Skip to content

[Safe Buffers][BoundsSafety] Initial bounds attributes support for FAMs #10254

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 18, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions clang/lib/Sema/SemaType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10888,12 +10888,17 @@ class CountArgChecker : public TreeTransform<CountArgChecker> {
if (auto CF = ConstantFoldOrNull(E))
return CF;

const Expr *Base = E->getBase();

// For structs/classes in C++, referring to a field creates a MemberExpr
// instead of DeclRefExpr. To support other parts of bounds-safety logic,
// replace the MemberExpr by DeclRefExpr.
// TODO: Check if this works for FAMs.
// TODO: Add support for MemberExpr (rdar://134311605).
if (!IsArray && SemaRef.getLangOpts().CPlusPlus) {
if (SemaRef.getLangOpts().CPlusPlus &&
// The MemberExprs that are allowed in C++ but not in C are those having
// `this->` implicitly or explicitly as their bases.
(Base->isImplicitCXXThis() ||
isa<CXXThisExpr>(Base->IgnoreParenImpCasts()))) {
ValueDecl *VD = E->getMemberDecl();
bool IsNewVD = Visited.insert(VD).second;
if (IsNewVD)
Expand Down
67 changes: 67 additions & 0 deletions clang/test/BoundsSafety/Sema/counted-by-member-expr-cxx.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x c++ -verify %s

#include <ptrcheck.h>
#include <stddef.h>

typedef struct T {
size_t count;
} T;

class C {
T t;
T *tp;
size_t count;
int * __counted_by(count) p; // implicit `this->count` is ok
int * __counted_by(this->count) q; // explicit `this->count` is ok
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}}
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}}
};

// test for simple flexible array members:
typedef struct flexible {
size_t count;
int elems[__counted_by(count)];
} flex_t;

class FAM {
size_t count;
int fam[__counted_by(count)];
public:
FAM() {};
};

class FAM_DOT {
T t;
int fam[__counted_by(t.count)]; // dot-expressions in counted-by is ok for FAMs
};

class FAM_ARROW {
T *tp;
int fam[__counted_by(tp->count)]; // expected-error{{arrow notation not allowed for struct member in count parameter}}
};

class FAM_THIS_ARROW_ARROW {
T *tp;
int fam[__counted_by(this->tp->count)]; // expected-error{{arrow notation not allowed for struct member in count parameter}}
};

class FAM_THIS_ARROW_DOT {
T t;
int fam[__counted_by(this->t.count)]; // dot-expressions in counted-by is ok for FAMs
};

class FAM_ARITHMETIC {
int count;
int offset;
int fam[__counted_by(count - offset)]; // ok
};

class FAM_THIS_PTR_ARITHMETIC {
int count;
int fam[__counted_by((this + 1)->count)]; // expected-error{{arrow notation not allowed for struct member in count parameter}}
};

class FAM_THIS_PTR_DEREFERENCE {
int count;
int fam[__counted_by((*this).count)]; // expected-error{{invalid argument expression to bounds attribute}}
};
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
typedef unsigned size_t;
class MyClass {
size_t m;
int q[__counted_by(m)]; // expected-error{{arrow notation not allowed for struct member in count parameter}}
int q[__counted_by(m)];

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

namespace value_dependent_assertion_violation {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace std {

template<typename T>
struct span {
constexpr span(T *, size_t) {}
constexpr span(const T *, size_t) {}
};

} // namespace std
Expand Down Expand Up @@ -162,3 +162,17 @@ void test_span_ctor_warn(const char * p,
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}}
}
#pragma clang diagnostic pop

namespace test_fam {
class FAM {
public:
size_t count;
int fam[__counted_by(count)];
};

void test(const FAM *f) {
std::span<int> s{f->fam, f->count};
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}}
}

} // namespace test_fam