Skip to content

Commit a943c37

Browse files
authored
[BoundsSafety][-Wunsafe-buffer-usage] Add a safe pattern of span construction from bounds-attributed pointers (#9746) (#10027)
For a `__sized_by(n)` pointer `p`, constructing a std::span the way below is safe. `std::span<char>{(char *)p, n}` cherry-picked from downstream (rdar://141103910)
1 parent 0db2189 commit a943c37

File tree

2 files changed

+48
-2
lines changed

2 files changed

+48
-2
lines changed

clang/lib/Analysis/UnsafeBufferUsage.cpp

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -903,6 +903,7 @@ bool isSinglePointerArgumentSafe(ASTContext &Context, const Expr *Arg) {
903903
// `n`
904904
// 5. `std::span<T>{any, 0}`
905905
// 6. `std::span<T>{p, n}`, where `p` is a __counted_by(`n`)/__sized_by(`n`)
906+
// pointer OR `std::span<char>{(char*)p, n}`, where `p` is a __sized_by(`n`)
906907
// pointer.
907908
AST_MATCHER(CXXConstructExpr, isSafeSpanTwoParamConstruct) {
908909
assert(Node.getNumArgs() == 2 &&
@@ -972,9 +973,28 @@ AST_MATCHER(CXXConstructExpr, isSafeSpanTwoParamConstruct) {
972973
}
973974

974975
// Check form 6:
976+
bool isArg0CastToBytePtrType = false;
977+
978+
if (auto *CE = dyn_cast<CastExpr>(Arg0)) {
979+
if (auto DestTySize = Finder->getASTContext().getTypeSizeInCharsIfKnown(
980+
Arg0Ty->getPointeeType())) {
981+
if (!DestTySize->isOne())
982+
return false; // If the destination pointee type is NOT of one byte
983+
// size, pattern match fails.
984+
Arg0 = CE->getSubExpr()->IgnoreParenImpCasts();
985+
Arg0Ty = Arg0->getType();
986+
isArg0CastToBytePtrType = true;
987+
}
988+
}
989+
// Check pointer and count/size with respect to the count-attribute:
975990
if (const auto *CAT = Arg0Ty->getAs<CountAttributedType>()) {
976-
// Accept __sized_by() if the size of the pointee type is 1.
977-
if (CAT->isCountInBytes()) {
991+
// For the pattern of `std::span<char>{(char *) p, n}`, p must NOT be a
992+
// __counted_by pointer.
993+
if (!CAT->isCountInBytes() && isArg0CastToBytePtrType)
994+
return false;
995+
// If `Arg0` is not a cast and is a sized_by pointer, its pointee type size
996+
// must be one byte:
997+
if (CAT->isCountInBytes() && !isArg0CastToBytePtrType) {
978998
std::optional<CharUnits> SizeOpt =
979999
Finder->getASTContext().getTypeSizeInCharsIfKnown(
9801000
CAT->getPointeeType());

clang/test/SemaCXX/warn-unsafe-buffer-usage-in-container-span-construct-count-attributed-pointer.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,32 @@ void span_from_sb_int(sb_int *i, sb_char *c) {
102102
std::span<char>{c->p, c->size};
103103
}
104104

105+
void span_from_sb_void(void * __sized_by(n) p, size_t n) {
106+
std::span<char>{(char *) p, n};
107+
std::span<unsigned char>{(unsigned char *) p, n};
108+
std::span<int>{(int *) p, n}; // expected-warning{{the two-parameter std::span construction is unsafe}}
109+
std::span<char>{(char *) p, n+1}; // expected-warning{{the two-parameter std::span construction is unsafe}}
110+
}
111+
112+
class SpanFromSbVoidMemberTest {
113+
void * __sized_by(n) p;
114+
size_t n;
115+
116+
void test() {
117+
std::span<char>{(char *) p, n};
118+
std::span<unsigned char>{(unsigned char *) p, n};
119+
std::span<int>{(int *) p, n}; // expected-warning{{the two-parameter std::span construction is unsafe}}
120+
std::span<char>{(char *) p, n+1}; // expected-warning{{the two-parameter std::span construction is unsafe}}
121+
}
122+
123+
void test(SpanFromSbVoidMemberTest t) {
124+
std::span<char>{(char *) t.p, t.n};
125+
std::span<unsigned char>{(unsigned char *) t.p, t.n};
126+
std::span<int>{(int *) t.p, t.n}; // expected-warning{{the two-parameter std::span construction is unsafe}}
127+
std::span<char>{(char *) t.p, t.n+1}; // expected-warning{{the two-parameter std::span construction is unsafe}}
128+
}
129+
};
130+
105131
void span_from_output_parm(int * __counted_by(n) *cb_p, size_t n,
106132
int * __counted_by(*m) *cb_q, size_t *m,
107133
int * __counted_by(*l) cb_z, size_t *l,

0 commit comments

Comments
 (0)