Skip to content

Commit 1195d74

Browse files
committed
[Safe Buffers][BoundsSafety] Initial bounds attributes support for FAMs
Enable parsing and syntax-level checking of bound attributes in flexible array member declarations. (rdar://139293326)
1 parent 3bb030e commit 1195d74

File tree

4 files changed

+67
-5
lines changed

4 files changed

+67
-5
lines changed

clang/lib/Sema/SemaType.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10893,7 +10893,14 @@ class CountArgChecker : public TreeTransform<CountArgChecker> {
1089310893
// replace the MemberExpr by DeclRefExpr.
1089410894
// TODO: Check if this works for FAMs.
1089510895
// TODO: Add support for MemberExpr (rdar://134311605).
10896-
if (!IsArray && SemaRef.getLangOpts().CPlusPlus) {
10896+
if (SemaRef.getLangOpts().CPlusPlus) {
10897+
// The MemberExprs that are allowed in C++ but not in C are those having
10898+
// `this->` implicitly or explicitly as their bases:
10899+
const Expr *Base = E->getBase();
10900+
10901+
if (!(Base->isImplicitCXXThis() ||
10902+
isa<CXXThisExpr>(Base->IgnoreParenImpCasts())))
10903+
goto BOUNDS_SAFETY_CHECK_FAM_OR_ARROW_FOR_MEMBER;
1089710904
ValueDecl *VD = E->getMemberDecl();
1089810905
bool IsNewVD = Visited.insert(VD).second;
1089910906
if (IsNewVD)
@@ -10904,7 +10911,7 @@ class CountArgChecker : public TreeTransform<CountArgChecker> {
1090410911
VD->getType(), VK_LValue);
1090510912
return DRE;
1090610913
}
10907-
10914+
BOUNDS_SAFETY_CHECK_FAM_OR_ARROW_FOR_MEMBER:
1090810915
if (!IsArray) {
1090910916
SemaRef.Diag(
1091010917
E->getExprLoc(),
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x c++ -verify %s
2+
3+
#include <ptrcheck.h>
4+
#include <stddef.h>
5+
6+
typedef struct T {
7+
size_t count;
8+
} T;
9+
10+
class C {
11+
T t;
12+
T *tp;
13+
size_t count;
14+
int * __counted_by(count) p; // implicit `this->count` is ok
15+
int * __counted_by(this->count) q; // explicit `this->count` is ok
16+
int * __counted_by(t.count) x; // expected-error{{invalid argument expression to bounds attribute}} expected-note{{nested struct member in count parameter only supported for flexible array members}}
17+
int * __counted_by(tp->count) y; // expected-error{{invalid argument expression to bounds attribute}} expected-note{{nested struct member in count parameter only supported for flexible array members}}
18+
};
19+
20+
// test for simple flexible array members:
21+
typedef struct flexible {
22+
size_t count;
23+
int elems[__counted_by(count)];
24+
} flex_t;
25+
26+
class FAM {
27+
size_t count;
28+
int fam[__counted_by(count)];
29+
public:
30+
FAM() {};
31+
};
32+
33+
class FAM_DOT {
34+
T t;
35+
int fam[__counted_by(t.count)]; // dot-expressions in counted-by is ok for FAMs
36+
};
37+
38+
class FAM_ARROW {
39+
T *tp;
40+
int fam[__counted_by(tp->count)]; // expected-error{{arrow notation not allowed for struct member in count parameter}}
41+
};

clang/test/BoundsSafety/Sema/unsafe-buffer-usage-interop-crash.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
typedef unsigned size_t;
44
class MyClass {
55
size_t m;
6-
int q[__counted_by(m)]; // expected-error{{arrow notation not allowed for struct member in count parameter}}
6+
int q[__counted_by(m)];
77

8-
// The error is because we do not have FAM support right now. Previously, this example will crash.
8+
// Previously, this example will crash.
99
};
1010

1111
namespace value_dependent_assertion_violation {

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

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ namespace std {
88

99
template<typename T>
1010
struct span {
11-
constexpr span(T *, size_t) {}
11+
constexpr span(const T *, size_t) {}
1212
};
1313

1414
} // namespace std
@@ -162,3 +162,17 @@ void test_span_ctor_warn(const char * p,
162162
std::span S2{wp, wcslen(wp)}; // expected-warning{{passing 'const wchar_t *' to parameter of incompatible type 'const wchar_t * __terminated_by(0)' (aka 'const wchar_t *') is an unsafe operation}}
163163
}
164164
#pragma clang diagnostic pop
165+
166+
namespace test_fam {
167+
class FAM {
168+
public:
169+
size_t count;
170+
int fam[__counted_by(count)];
171+
};
172+
173+
void test(const FAM *f) {
174+
std::span<int> s{f->fam, f->count};
175+
std::span<int> s2{f->fam, f->count + 1}; // expected-warning{{the two-parameter std::span construction is unsafe as it can introduce mismatch between buffer size and the bound information}}
176+
}
177+
178+
} // namespace test_fam

0 commit comments

Comments
 (0)