Skip to content

Commit

Permalink
[BoundsSafety][-Wunsafe-buffer-usage] Add a safe pattern of span cons…
Browse files Browse the repository at this point in the history
…truction from bounds-attributed pointers

Cherry-picked from downstream.

(rdar://141103910)
  • Loading branch information
ziqingluo-90 committed Dec 19, 2024
1 parent 1a588e5 commit 0031494
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 2 deletions.
24 changes: 22 additions & 2 deletions clang/lib/Analysis/UnsafeBufferUsage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -847,6 +847,7 @@ bool isCountAttributedPointerArgumentSafe(ASTContext &Context,
// `n`
// 5. `std::span<T>{any, 0}`
// 6. `std::span<T>{p, n}`, where `p` is a __counted_by(`n`)/__sized_by(`n`)
// pointer OR `std::span<char>{(char*)p, n}`, where `p` is a __sized_by(`n`)
// pointer.
AST_MATCHER(CXXConstructExpr, isSafeSpanTwoParamConstruct) {
assert(Node.getNumArgs() == 2 &&
Expand Down Expand Up @@ -916,9 +917,28 @@ AST_MATCHER(CXXConstructExpr, isSafeSpanTwoParamConstruct) {
}

// Check form 6:
bool isArg0CastToBytePtrType = false;

if (auto *CE = dyn_cast<CastExpr>(Arg0)) {
if (auto DestTySize = Finder->getASTContext().getTypeSizeInCharsIfKnown(
Arg0Ty->getPointeeType())) {
if (!DestTySize->isOne())
return false; // If the destination pointee type is NOT of one byte
// size, pattern match fails.
Arg0 = CE->getSubExpr()->IgnoreParenImpCasts();
Arg0Ty = Arg0->getType();
isArg0CastToBytePtrType = true;
}
}
// Check pointer and count/size with respect to the count-attribute:
if (const auto *CAT = Arg0Ty->getAs<CountAttributedType>()) {
// Accept __sized_by() if the size of the pointee type is 1.
if (CAT->isCountInBytes()) {
// For the pattern of `std::span<char>{(char *) p, n}`, p must NOT be a
// __counted_by pointer.
if (!CAT->isCountInBytes() && isArg0CastToBytePtrType)
return false;
// If `Arg0` is not a cast and is a sized_by pointer, its pointee type size
// must be one byte:
if (CAT->isCountInBytes() && !isArg0CastToBytePtrType) {
std::optional<CharUnits> SizeOpt =
Finder->getASTContext().getTypeSizeInCharsIfKnown(
CAT->getPointeeType());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,32 @@ void span_from_sb_int(sb_int *i, sb_char *c) {
std::span<char>{c->p, c->size};
}

void span_from_sb_void(void * __sized_by(n) p, size_t n) {
std::span<char>{(char *) p, n};
std::span<unsigned char>{(unsigned char *) p, n};
std::span<int>{(int *) p, n}; // expected-warning{{the two-parameter std::span construction is unsafe}}
std::span<char>{(char *) p, n+1}; // expected-warning{{the two-parameter std::span construction is unsafe}}
}

class SpanFromSbVoidMemberTest {
void * __sized_by(n) p;
size_t n;

void test() {
std::span<char>{(char *) p, n};
std::span<unsigned char>{(unsigned char *) p, n};
std::span<int>{(int *) p, n}; // expected-warning{{the two-parameter std::span construction is unsafe}}
std::span<char>{(char *) p, n+1}; // expected-warning{{the two-parameter std::span construction is unsafe}}
}

void test(SpanFromSbVoidMemberTest t) {
std::span<char>{(char *) t.p, t.n};
std::span<unsigned char>{(unsigned char *) t.p, t.n};
std::span<int>{(int *) t.p, t.n}; // expected-warning{{the two-parameter std::span construction is unsafe}}
std::span<char>{(char *) t.p, t.n+1}; // expected-warning{{the two-parameter std::span construction is unsafe}}
}
};

void span_from_output_parm(int * __counted_by(n) *cb_p, size_t n,
int * __counted_by(*m) *cb_q, size_t *m,
int * __counted_by(*l) cb_z, size_t *l,
Expand Down

0 comments on commit 0031494

Please sign in to comment.