Skip to content

Commit b185b85

Browse files
authored
[Clang] Improve Sema diagnostic performance for __builtin_counted_by_ref (#116719)
Implement the sema checks with a placeholder. We then check for that placeholder in all of the places we care to emit a diagnostic. Fixes: #115520
1 parent 991154d commit b185b85

File tree

7 files changed

+113
-111
lines changed

7 files changed

+113
-111
lines changed

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6670,14 +6670,15 @@ def warn_counted_by_attr_elt_type_unknown_size :
66706670
// __builtin_counted_by_ref diagnostics:
66716671
def err_builtin_counted_by_ref_must_be_flex_array_member : Error<
66726672
"'__builtin_counted_by_ref' argument must reference a flexible array member">;
6673+
def err_builtin_counted_by_ref_has_side_effects : Error<
6674+
"'__builtin_counted_by_ref' argument cannot have side-effects">;
6675+
66736676
def err_builtin_counted_by_ref_cannot_leak_reference : Error<
6674-
"value returned by '__builtin_counted_by_ref' cannot be assigned to a "
6675-
"variable, have its address taken, or passed into or returned from a function">;
6676-
def err_builtin_counted_by_ref_invalid_lhs_use : Error<
6677+
"value returned by '__builtin_counted_by_ref' cannot be %select{assigned to a "
6678+
"variable|passed into a function|returned from a function}0">;
6679+
def err_builtin_counted_by_ref_invalid_use : Error<
66776680
"value returned by '__builtin_counted_by_ref' cannot be used in "
66786681
"%select{an array subscript|a binary}0 expression">;
6679-
def err_builtin_counted_by_ref_has_side_effects : Error<
6680-
"'__builtin_counted_by_ref' argument cannot have side-effects">;
66816682

66826683
let CategoryName = "ARC Semantic Issue" in {
66836684

clang/include/clang/Sema/Sema.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2524,6 +2524,17 @@ class Sema final : public SemaBase {
25242524

25252525
bool BuiltinNonDeterministicValue(CallExpr *TheCall);
25262526

2527+
enum BuiltinCountedByRefKind {
2528+
AssignmentKind,
2529+
InitializerKind,
2530+
FunctionArgKind,
2531+
ReturnArgKind,
2532+
ArraySubscriptKind,
2533+
BinaryExprKind,
2534+
};
2535+
2536+
bool CheckInvalidBuiltinCountedByRef(const Expr *E,
2537+
BuiltinCountedByRefKind K);
25272538
bool BuiltinCountedByRef(CallExpr *TheCall);
25282539

25292540
// Matrix builtin handling.

clang/lib/Sema/SemaChecking.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5664,6 +5664,45 @@ bool Sema::BuiltinCountedByRef(CallExpr *TheCall) {
56645664
return false;
56655665
}
56665666

5667+
/// The result of __builtin_counted_by_ref cannot be assigned to a variable.
5668+
/// It allows leaking and modification of bounds safety information.
5669+
bool Sema::CheckInvalidBuiltinCountedByRef(const Expr *E,
5670+
BuiltinCountedByRefKind K) {
5671+
const CallExpr *CE =
5672+
E ? dyn_cast<CallExpr>(E->IgnoreParenImpCasts()) : nullptr;
5673+
if (!CE || CE->getBuiltinCallee() != Builtin::BI__builtin_counted_by_ref)
5674+
return false;
5675+
5676+
switch (K) {
5677+
case AssignmentKind:
5678+
case InitializerKind:
5679+
Diag(E->getExprLoc(),
5680+
diag::err_builtin_counted_by_ref_cannot_leak_reference)
5681+
<< 0 << E->getSourceRange();
5682+
break;
5683+
case FunctionArgKind:
5684+
Diag(E->getExprLoc(),
5685+
diag::err_builtin_counted_by_ref_cannot_leak_reference)
5686+
<< 1 << E->getSourceRange();
5687+
break;
5688+
case ReturnArgKind:
5689+
Diag(E->getExprLoc(),
5690+
diag::err_builtin_counted_by_ref_cannot_leak_reference)
5691+
<< 2 << E->getSourceRange();
5692+
break;
5693+
case ArraySubscriptKind:
5694+
Diag(E->getExprLoc(), diag::err_builtin_counted_by_ref_invalid_use)
5695+
<< 0 << E->getSourceRange();
5696+
break;
5697+
case BinaryExprKind:
5698+
Diag(E->getExprLoc(), diag::err_builtin_counted_by_ref_invalid_use)
5699+
<< 1 << E->getSourceRange();
5700+
break;
5701+
}
5702+
5703+
return true;
5704+
}
5705+
56675706
namespace {
56685707

56695708
class UncoveredArgHandler {

clang/lib/Sema/SemaDecl.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14699,6 +14699,8 @@ void Sema::FinalizeDeclaration(Decl *ThisDecl) {
1469914699
}
1470014700
}
1470114701

14702+
CheckInvalidBuiltinCountedByRef(VD->getInit(), InitializerKind);
14703+
1470214704
checkAttributesAfterMerging(*this, *VD);
1470314705

1470414706
if (VD->isStaticLocal())

clang/lib/Sema/SemaExpr.cpp

Lines changed: 14 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -4897,6 +4897,8 @@ ExprResult Sema::ActOnArraySubscriptExpr(Scope *S, Expr *base,
48974897
return ExprError();
48984898
}
48994899

4900+
CheckInvalidBuiltinCountedByRef(base, ArraySubscriptKind);
4901+
49004902
// Handle any non-overload placeholder types in the base and index
49014903
// expressions. We can't handle overloads here because the other
49024904
// operand might be an overloadable type, in which case the overload
@@ -6489,6 +6491,12 @@ ExprResult Sema::BuildCallExpr(Scope *Scope, Expr *Fn, SourceLocation LParenLoc,
64896491
if (CheckArgsForPlaceholders(ArgExprs))
64906492
return ExprError();
64916493

6494+
// The result of __builtin_counted_by_ref cannot be used as a function
6495+
// argument. It allows leaking and modification of bounds safety information.
6496+
for (const Expr *Arg : ArgExprs)
6497+
if (CheckInvalidBuiltinCountedByRef(Arg, FunctionArgKind))
6498+
return ExprError();
6499+
64926500
if (getLangOpts().CPlusPlus) {
64936501
// If this is a pseudo-destructor expression, build the call immediately.
64946502
if (isa<CXXPseudoDestructorExpr>(Fn)) {
@@ -9199,38 +9207,6 @@ Sema::CheckAssignmentConstraints(QualType LHSType, ExprResult &RHS,
91999207
LHSType = Context.getCanonicalType(LHSType).getUnqualifiedType();
92009208
RHSType = Context.getCanonicalType(RHSType).getUnqualifiedType();
92019209

9202-
// __builtin_counted_by_ref cannot be assigned to a variable, used in
9203-
// function call, or in a return.
9204-
auto FindBuiltinCountedByRefExpr = [&](Expr *E) -> CallExpr * {
9205-
struct BuiltinCountedByRefVisitor : DynamicRecursiveASTVisitor {
9206-
CallExpr *TheCall = nullptr;
9207-
bool VisitCallExpr(CallExpr *CE) override {
9208-
if (CE->getBuiltinCallee() == Builtin::BI__builtin_counted_by_ref) {
9209-
TheCall = CE;
9210-
return false;
9211-
}
9212-
return true;
9213-
}
9214-
bool
9215-
VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *UE) override {
9216-
// A UnaryExprOrTypeTraitExpr---e.g. sizeof, __alignof, etc.---isn't
9217-
// the same as a CallExpr, so if we find a __builtin_counted_by_ref()
9218-
// call in one, ignore it.
9219-
return false;
9220-
}
9221-
} V;
9222-
V.TraverseStmt(E);
9223-
return V.TheCall;
9224-
};
9225-
static llvm::SmallPtrSet<CallExpr *, 4> Diagnosed;
9226-
if (auto *CE = FindBuiltinCountedByRefExpr(RHS.get());
9227-
CE && !Diagnosed.count(CE)) {
9228-
Diagnosed.insert(CE);
9229-
Diag(CE->getExprLoc(),
9230-
diag::err_builtin_counted_by_ref_cannot_leak_reference)
9231-
<< CE->getSourceRange();
9232-
}
9233-
92349210
// Common case: no conversion required.
92359211
if (LHSType == RHSType) {
92369212
Kind = CK_NoOp;
@@ -13781,42 +13757,6 @@ QualType Sema::CheckAssignmentOperands(Expr *LHSExpr, ExprResult &RHS,
1378113757
ConvTy = CheckAssignmentConstraints(Loc, LHSType, RHSType);
1378213758
}
1378313759

13784-
// __builtin_counted_by_ref can't be used in a binary expression or array
13785-
// subscript on the LHS.
13786-
int DiagOption = -1;
13787-
auto FindInvalidUseOfBoundsSafetyCounter = [&](Expr *E) -> CallExpr * {
13788-
struct BuiltinCountedByRefVisitor : DynamicRecursiveASTVisitor {
13789-
CallExpr *CE = nullptr;
13790-
bool InvalidUse = false;
13791-
int Option = -1;
13792-
13793-
bool VisitCallExpr(CallExpr *E) override {
13794-
if (E->getBuiltinCallee() == Builtin::BI__builtin_counted_by_ref) {
13795-
CE = E;
13796-
return false;
13797-
}
13798-
return true;
13799-
}
13800-
13801-
bool VisitArraySubscriptExpr(ArraySubscriptExpr *E) override {
13802-
InvalidUse = true;
13803-
Option = 0; // report 'array expression' in diagnostic.
13804-
return true;
13805-
}
13806-
bool VisitBinaryOperator(BinaryOperator *E) override {
13807-
InvalidUse = true;
13808-
Option = 1; // report 'binary expression' in diagnostic.
13809-
return true;
13810-
}
13811-
} V;
13812-
V.TraverseStmt(E);
13813-
DiagOption = V.Option;
13814-
return V.InvalidUse ? V.CE : nullptr;
13815-
};
13816-
if (auto *CE = FindInvalidUseOfBoundsSafetyCounter(LHSExpr))
13817-
Diag(CE->getExprLoc(), diag::err_builtin_counted_by_ref_invalid_lhs_use)
13818-
<< DiagOption << CE->getSourceRange();
13819-
1382013760
if (DiagnoseAssignmentResult(ConvTy, Loc, LHSType, RHSType, RHS.get(),
1382113761
AssignmentAction::Assigning))
1382213762
return QualType();
@@ -15277,6 +15217,12 @@ ExprResult Sema::ActOnBinOp(Scope *S, SourceLocation TokLoc,
1527715217
if (Kind == tok::TokenKind::slash)
1527815218
DetectPrecisionLossInComplexDivision(*this, TokLoc, LHSExpr);
1527915219

15220+
BuiltinCountedByRefKind K =
15221+
BinaryOperator::isAssignmentOp(Opc) ? AssignmentKind : BinaryExprKind;
15222+
15223+
CheckInvalidBuiltinCountedByRef(LHSExpr, K);
15224+
CheckInvalidBuiltinCountedByRef(RHSExpr, K);
15225+
1528015226
return BuildBinOp(S, TokLoc, Opc, LHSExpr, RHSExpr);
1528115227
}
1528215228

clang/lib/Sema/SemaStmt.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3778,6 +3778,8 @@ Sema::ActOnReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp,
37783778
<< FSI->getFirstCoroutineStmtKeyword();
37793779
}
37803780

3781+
CheckInvalidBuiltinCountedByRef(RetVal.get(), ReturnArgKind);
3782+
37813783
StmtResult R =
37823784
BuildReturnStmt(ReturnLoc, RetVal.get(), /*AllowRecovery=*/true);
37833785
if (R.isInvalid() || ExprEvalContexts.back().isDiscardedStatementContext())

clang/test/Sema/builtin-counted-by-ref.c

Lines changed: 39 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -11,62 +11,63 @@ struct fam_struct {
1111
int array[] __attribute__((counted_by(count)));
1212
};
1313

14-
void test1(struct fam_struct *ptr, int size, int idx) {
15-
size_t size_of = sizeof(__builtin_counted_by_ref(ptr->array)); // ok
16-
17-
*__builtin_counted_by_ref(ptr->array) = size; // ok
14+
void g(char *);
15+
void h(char);
1816

19-
{
20-
size_t __ignored_assignment;
21-
*_Generic(__builtin_counted_by_ref(ptr->array),
22-
void *: &__ignored_assignment,
23-
default: __builtin_counted_by_ref(ptr->array)) = 42; // ok
24-
}
17+
void test1(struct fam_struct *ptr, int size, int idx) {
18+
size_t size_of = sizeof(__builtin_counted_by_ref(ptr->array)); // ok
19+
int align_of = __alignof(__builtin_counted_by_ref(ptr->array)); // ok
20+
size_t __ignored_assignment;
21+
22+
*__builtin_counted_by_ref(ptr->array) = size; // ok
23+
*_Generic(__builtin_counted_by_ref(ptr->array),
24+
void *: &__ignored_assignment,
25+
default: __builtin_counted_by_ref(ptr->array)) = 42; // ok
26+
h(*__builtin_counted_by_ref(ptr->array)); // ok
2527
}
2628

2729
void test2(struct fam_struct *ptr, int idx) {
28-
__builtin_counted_by_ref(); // expected-error {{too few arguments to function call, expected 1, have 0}}
29-
__builtin_counted_by_ref(ptr->array, ptr->x, ptr->count); // expected-error {{too many arguments to function call, expected 1, have 3}}
30+
__builtin_counted_by_ref(); // expected-error {{too few arguments to function call, expected 1, have 0}}
31+
__builtin_counted_by_ref(ptr->array, ptr->x, ptr->count); // expected-error {{too many arguments to function call, expected 1, have 3}}
3032
}
3133

3234
void test3(struct fam_struct *ptr, int idx) {
33-
__builtin_counted_by_ref(&ptr->array[0]); // expected-error {{'__builtin_counted_by_ref' argument must reference a flexible array member}}
34-
__builtin_counted_by_ref(&ptr->array[idx]); // expected-error {{'__builtin_counted_by_ref' argument must reference a flexible array member}}
35-
__builtin_counted_by_ref(&ptr->array); // expected-error {{'__builtin_counted_by_ref' argument must reference a flexible array member}}
36-
__builtin_counted_by_ref(ptr->x); // expected-error {{'__builtin_counted_by_ref' argument must reference a flexible array member}}
37-
__builtin_counted_by_ref(&ptr->x); // expected-error {{'__builtin_counted_by_ref' argument must reference a flexible array member}}
38-
__builtin_counted_by_ref(global_array); // expected-error {{'__builtin_counted_by_ref' argument must reference a flexible array member}}
39-
__builtin_counted_by_ref(global_int); // expected-error {{'__builtin_counted_by_ref' argument must reference a flexible array member}}
40-
__builtin_counted_by_ref(&global_int); // expected-error {{'__builtin_counted_by_ref' argument must reference a flexible array member}}
35+
__builtin_counted_by_ref(&ptr->array[0]); // expected-error {{'__builtin_counted_by_ref' argument must reference a flexible array member}}
36+
__builtin_counted_by_ref(&ptr->array[idx]); // expected-error {{'__builtin_counted_by_ref' argument must reference a flexible array member}}
37+
__builtin_counted_by_ref(&ptr->array); // expected-error {{'__builtin_counted_by_ref' argument must reference a flexible array member}}
38+
__builtin_counted_by_ref(ptr->x); // expected-error {{'__builtin_counted_by_ref' argument must reference a flexible array member}}
39+
__builtin_counted_by_ref(&ptr->x); // expected-error {{'__builtin_counted_by_ref' argument must reference a flexible array member}}
40+
__builtin_counted_by_ref(global_array); // expected-error {{'__builtin_counted_by_ref' argument must reference a flexible array member}}
41+
__builtin_counted_by_ref(global_int); // expected-error {{'__builtin_counted_by_ref' argument must reference a flexible array member}}
42+
__builtin_counted_by_ref(&global_int); // expected-error {{'__builtin_counted_by_ref' argument must reference a flexible array member}}
4143
}
4244

4345
void test4(struct fam_struct *ptr, int idx) {
44-
__builtin_counted_by_ref(ptr++->array); // expected-error {{'__builtin_counted_by_ref' argument cannot have side-effects}}
45-
__builtin_counted_by_ref(&ptr->array[idx++]); // expected-error {{'__builtin_counted_by_ref' argument cannot have side-effects}}
46+
__builtin_counted_by_ref(ptr++->array); // expected-error {{'__builtin_counted_by_ref' argument cannot have side-effects}}
47+
__builtin_counted_by_ref(&ptr->array[idx++]); // expected-error {{'__builtin_counted_by_ref' argument cannot have side-effects}}
4648
}
4749

48-
void foo(char *);
49-
5050
void *test5(struct fam_struct *ptr, int size, int idx) {
51-
char *ref = __builtin_counted_by_ref(ptr->array); // expected-error {{value returned by '__builtin_counted_by_ref' cannot be assigned to a variable, have its address taken, or passed into or returned from a function}}
51+
char *ref = __builtin_counted_by_ref(ptr->array); // expected-error {{value returned by '__builtin_counted_by_ref' cannot be assigned to a variable}}
52+
char *int_ptr;
53+
char *p;
5254

53-
ref = __builtin_counted_by_ref(ptr->array); // expected-error {{value returned by '__builtin_counted_by_ref' cannot be assigned to a variable, have its address taken, or passed into or returned from a function}}
54-
ref = (char *)(int *)(42 + &*__builtin_counted_by_ref(ptr->array)); // expected-error {{value returned by '__builtin_counted_by_ref' cannot be assigned to a variable, have its address taken, or passed into or returned from a function}}
55-
foo(__builtin_counted_by_ref(ptr->array)); // expected-error {{value returned by '__builtin_counted_by_ref' cannot be assigned to a variable, have its address taken, or passed into or returned from a function}}
56-
foo(ref = __builtin_counted_by_ref(ptr->array)); // expected-error {{value returned by '__builtin_counted_by_ref' cannot be assigned to a variable, have its address taken, or passed into or returned from a function}}
55+
ref = __builtin_counted_by_ref(ptr->array); // expected-error {{value returned by '__builtin_counted_by_ref' cannot be assigned to a variable}}
56+
g(__builtin_counted_by_ref(ptr->array)); // expected-error {{value returned by '__builtin_counted_by_ref' cannot be passed into a function}}
57+
g(ref = __builtin_counted_by_ref(ptr->array)); // expected-error {{value returned by '__builtin_counted_by_ref' cannot be assigned to a variable}}
5758

58-
if ((ref = __builtin_counted_by_ref(ptr->array))) // expected-error {{value returned by '__builtin_counted_by_ref' cannot be assigned to a variable, have its address taken, or passed into or returned from a function}}
59+
if ((ref = __builtin_counted_by_ref(ptr->array))) // expected-error {{value returned by '__builtin_counted_by_ref' cannot be assigned to a variable}}
5960
;
6061

61-
for (char *p = __builtin_counted_by_ref(ptr->array); p && *p; ++p) // expected-error {{value returned by '__builtin_counted_by_ref' cannot be assigned to a variable, have its address taken, or passed into or returned from a function}}
62+
for (p = __builtin_counted_by_ref(ptr->array); p && *p; ++p) // expected-error {{value returned by '__builtin_counted_by_ref' cannot be assigned to a variable}}
6263
;
6364

64-
return __builtin_counted_by_ref(ptr->array); // expected-error {{value returned by '__builtin_counted_by_ref' cannot be assigned to a variable, have its address taken, or passed into or returned from a function}}
65+
return __builtin_counted_by_ref(ptr->array); // expected-error {{value returned by '__builtin_counted_by_ref' cannot be returned from a function}}
6566
}
6667

6768
void test6(struct fam_struct *ptr, int size, int idx) {
68-
*(__builtin_counted_by_ref(ptr->array) + 4) = 37; // expected-error {{value returned by '__builtin_counted_by_ref' cannot be used in a binary expression}}
69-
__builtin_counted_by_ref(ptr->array)[3] = 37; // expected-error {{value returned by '__builtin_counted_by_ref' cannot be used in an array subscript expression}}
69+
*(__builtin_counted_by_ref(ptr->array) + 4) = 37; // expected-error {{value returned by '__builtin_counted_by_ref' cannot be used in a binary expression}}
70+
__builtin_counted_by_ref(ptr->array)[3] = 37; // expected-error {{value returned by '__builtin_counted_by_ref' cannot be used in an array subscript expression}}
7071
}
7172

7273
struct non_fam_struct {
@@ -77,10 +78,10 @@ struct non_fam_struct {
7778
};
7879

7980
void *test7(struct non_fam_struct *ptr, int size) {
80-
*__builtin_counted_by_ref(ptr->array) = size // expected-error {{'__builtin_counted_by_ref' argument must reference a flexible array member}}
81-
*__builtin_counted_by_ref(&ptr->array[0]) = size; // expected-error {{'__builtin_counted_by_ref' argument must reference a flexible array member}}
82-
*__builtin_counted_by_ref(ptr->pointer) = size; // expected-error {{'__builtin_counted_by_ref' argument must reference a flexible array member}}
83-
*__builtin_counted_by_ref(&ptr->pointer[0]) = size; // expected-error {{'__builtin_counted_by_ref' argument must reference a flexible array member}}
81+
*__builtin_counted_by_ref(ptr->array) = size // expected-error {{'__builtin_counted_by_ref' argument must reference a flexible array member}}
82+
*__builtin_counted_by_ref(&ptr->array[0]) = size; // expected-error {{'__builtin_counted_by_ref' argument must reference a flexible array member}}
83+
*__builtin_counted_by_ref(ptr->pointer) = size; // expected-error {{'__builtin_counted_by_ref' argument must reference a flexible array member}}
84+
*__builtin_counted_by_ref(&ptr->pointer[0]) = size; // expected-error {{'__builtin_counted_by_ref' argument must reference a flexible array member}}
8485
}
8586

8687
struct char_count {

0 commit comments

Comments
 (0)