Skip to content

Commit be33180

Browse files
authored
[Safe Buffers][BoundsSafety] Initial bounds attributes support for FAMs (#10254) (#10285)
Enable parsing and syntax-level checking of bound attributes in flexible array member declarations. (rdar://139293326)
1 parent b5cb04b commit be33180

File tree

4 files changed

+91
-5
lines changed

4 files changed

+91
-5
lines changed

clang/lib/Sema/SemaType.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10698,12 +10698,17 @@ class CountArgChecker : public TreeTransform<CountArgChecker> {
1069810698
if (auto CF = ConstantFoldOrNull(E))
1069910699
return CF;
1070010700

10701+
const Expr *Base = E->getBase();
10702+
1070110703
// For structs/classes in C++, referring to a field creates a MemberExpr
1070210704
// instead of DeclRefExpr. To support other parts of bounds-safety logic,
1070310705
// replace the MemberExpr by DeclRefExpr.
10704-
// TODO: Check if this works for FAMs.
1070510706
// TODO: Add support for MemberExpr (rdar://134311605).
10706-
if (!IsArray && SemaRef.getLangOpts().CPlusPlus) {
10707+
if (SemaRef.getLangOpts().CPlusPlus &&
10708+
// The MemberExprs that are allowed in C++ but not in C are those having
10709+
// `this->` implicitly or explicitly as their bases.
10710+
(Base->isImplicitCXXThis() ||
10711+
isa<CXXThisExpr>(Base->IgnoreParenImpCasts()))) {
1070710712
ValueDecl *VD = E->getMemberDecl();
1070810713
bool IsNewVD = Visited.insert(VD).second;
1070910714
if (IsNewVD)
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
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+
};
42+
43+
class FAM_THIS_ARROW_ARROW {
44+
T *tp;
45+
int fam[__counted_by(this->tp->count)]; // expected-error{{arrow notation not allowed for struct member in count parameter}}
46+
};
47+
48+
class FAM_THIS_ARROW_DOT {
49+
T t;
50+
int fam[__counted_by(this->t.count)]; // dot-expressions in counted-by is ok for FAMs
51+
};
52+
53+
class FAM_ARITHMETIC {
54+
int count;
55+
int offset;
56+
int fam[__counted_by(count - offset)]; // ok
57+
};
58+
59+
class FAM_THIS_PTR_ARITHMETIC {
60+
int count;
61+
int fam[__counted_by((this + 1)->count)]; // expected-error{{arrow notation not allowed for struct member in count parameter}}
62+
};
63+
64+
class FAM_THIS_PTR_DEREFERENCE {
65+
int count;
66+
int fam[__counted_by((*this).count)]; // expected-error{{invalid argument expression to bounds attribute}}
67+
};

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)