Skip to content

Commit 7e00765

Browse files
author
MalavikaSamak
committed
[Wunsafe-buffer-usage] False positives for & expression indexing constant size array (arr[anything & 0])
Do not warn when a constant sized array is indexed with an expression that contains bitwise and operation involving constants and it always results in a bound safe access. (rdar://136684050)
1 parent 2ff4c25 commit 7e00765

File tree

2 files changed

+162
-8
lines changed

2 files changed

+162
-8
lines changed

clang/lib/Analysis/UnsafeBufferUsage.cpp

Lines changed: 119 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,118 @@ AST_MATCHER(CXXConstructExpr, isSafeSpanTwoParamConstruct) {
420420
return false;
421421
}
422422

423+
class MaxValueEval : public RecursiveASTVisitor<MaxValueEval> {
424+
425+
std::vector<llvm::APInt> val;
426+
ASTContext &Context;
427+
llvm::APInt Max;
428+
unsigned bit_width;
429+
430+
public:
431+
typedef RecursiveASTVisitor<MaxValueEval> VisitorBase;
432+
433+
explicit MaxValueEval(ASTContext &Ctx, const Expr *exp) : Context(Ctx) {
434+
bit_width = Ctx.getIntWidth(exp->getType());
435+
Max = llvm::APInt::getSignedMaxValue(bit_width);
436+
val.clear();
437+
}
438+
439+
bool findMatch(Expr *exp) {
440+
TraverseStmt(exp);
441+
return true;
442+
}
443+
444+
bool VisitDeclRefExpr(DeclRefExpr *dre) {
445+
val.push_back(Max);
446+
return false;
447+
}
448+
449+
bool VisitArraySubscriptExpr(ArraySubscriptExpr *E) {
450+
val.push_back(Max);
451+
return false;
452+
}
453+
454+
bool EvaluateExpression(Expr *exp) {
455+
Expr::EvalResult EVResult;
456+
if (exp->EvaluateAsInt(EVResult, Context)) {
457+
llvm::APSInt Result = EVResult.Val.getInt();
458+
val.push_back(Result);
459+
return true;
460+
}
461+
return false;
462+
}
463+
464+
bool VisitBinaryOperator(BinaryOperator *E) {
465+
466+
if (EvaluateExpression(E)) {
467+
return false;
468+
} else {
469+
TraverseStmt(E->getLHS());
470+
llvm::APInt LHS = val.back();
471+
val.pop_back();
472+
473+
TraverseStmt(E->getRHS());
474+
llvm::APInt RHS = val.back();
475+
val.pop_back();
476+
llvm::APInt Result = Max;
477+
478+
switch (E->getOpcode()) {
479+
case BO_And:
480+
case BO_AndAssign:
481+
Result = LHS & RHS;
482+
break;
483+
484+
case BO_Or:
485+
case BO_OrAssign:
486+
Result = LHS | RHS;
487+
break;
488+
489+
case BO_Shl:
490+
case BO_ShlAssign:
491+
if (RHS != Max.getLimitedValue())
492+
Result = LHS << RHS.getLimitedValue();
493+
break;
494+
495+
case BO_Shr:
496+
case BO_ShrAssign:
497+
if (RHS == Max.getLimitedValue())
498+
Result = LHS;
499+
else
500+
Result = LHS.getLimitedValue() >> RHS.getLimitedValue();
501+
break;
502+
503+
case BO_Rem:
504+
case BO_RemAssign:
505+
if (LHS.getLimitedValue() < RHS.getLimitedValue())
506+
Result = LHS;
507+
else
508+
Result = --RHS;
509+
break;
510+
511+
default:
512+
break;
513+
}
514+
val.push_back(Result);
515+
return false;
516+
}
517+
return true;
518+
}
519+
520+
bool VisitExpr(Expr *E) {
521+
if (EvaluateExpression(E)) {
522+
return false;
523+
}
524+
return VisitorBase::VisitExpr(E);
525+
}
526+
527+
APInt getValue() {
528+
if (val.size() == 1)
529+
return val[0];
530+
else // A pattern we didn't consider was encountered
531+
return Max;
532+
}
533+
};
534+
423535
AST_MATCHER(ArraySubscriptExpr, isSafeArraySubscript) {
424536
// FIXME: Proper solution:
425537
// - refactor Sema::CheckArrayAccess
@@ -439,14 +551,13 @@ AST_MATCHER(ArraySubscriptExpr, isSafeArraySubscript) {
439551
if (!CATy)
440552
return false;
441553

442-
if (const auto *IdxLit = dyn_cast<IntegerLiteral>(Node.getIdx())) {
443-
const APInt ArrIdx = IdxLit->getValue();
444-
// FIXME: ArrIdx.isNegative() we could immediately emit an error as that's a
445-
// bug
446-
if (ArrIdx.isNonNegative() &&
447-
ArrIdx.getLimitedValue() < CATy->getLimitedSize())
448-
return true;
449-
}
554+
MaxValueEval Vis(Finder->getASTContext(), Node.getIdx());
555+
Vis.findMatch(const_cast<Expr *>(Node.getIdx()));
556+
APInt result = Vis.getValue();
557+
558+
if (result.isNonNegative() &&
559+
result.getLimitedValue() < CATy->getLimitedSize())
560+
return true;
450561

451562
return false;
452563
}

clang/test/SemaCXX/warn-unsafe-buffer-usage-array.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,49 @@ void constant_idx_safe0(unsigned idx) {
3333
buffer[0] = 0;
3434
}
3535

36+
int array[10]; // expected-warning 3{{'array' is an unsafe buffer that does not perform bounds checks}}
37+
38+
void masked_idx1(unsigned long long idx) {
39+
// Bitwise and operation
40+
array[idx & 5] = 10; // no warning
41+
array[idx & 11 & 5] = 3; // no warning
42+
array[idx & 11] = 20; // expected-note{{used in buffer access here}}
43+
array[(idx & 9) | 8];
44+
array[idx &=5];
45+
}
46+
47+
void masked_idx2(unsigned long long idx, unsigned long long idx2) {
48+
array[idx] = 30; // expected-note{{used in buffer access here}}
49+
50+
// Remainder operation
51+
array[idx % 10];
52+
array[10 % idx]; // expected-note{{used in buffer access here}}
53+
array[9 % idx];
54+
array[idx % idx2]; // expected-note{{used in buffer access here}}
55+
array[idx %= 10];
56+
}
57+
58+
void masked_idx3(unsigned long long idx) {
59+
// Left shift operation <<
60+
array[2 << 1];
61+
array[8 << 1]; // expected-note{{used in buffer access here}}
62+
array[2 << idx]; // expected-note{{used in buffer access here}}
63+
array[idx << 2]; // expected-note{{used in buffer access here}}
64+
65+
// Right shift operation >>
66+
array[16 >> 1];
67+
array[8 >> idx];
68+
array[idx >> 63];
69+
}
70+
71+
int array2[5];
72+
73+
void masked_idx_false(unsigned long long idx) {
74+
array2[6 & 5]; // no warning
75+
array2[6 & idx & (idx + 1) & 5]; // no warning
76+
array2[6 & idx & 5 & (idx + 1) | 4]; // no warning
77+
}
78+
3679
void constant_idx_unsafe(unsigned idx) {
3780
int buffer[10]; // expected-warning{{'buffer' is an unsafe buffer that does not perform bounds checks}}
3881
// expected-note@-1{{change type of 'buffer' to 'std::array' to label it for hardening}}

0 commit comments

Comments
 (0)