Skip to content

Commit 80e593f

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 80e593f

File tree

2 files changed

+177
-8
lines changed

2 files changed

+177
-8
lines changed

clang/lib/Analysis/UnsafeBufferUsage.cpp

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

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-
}
558+
MaxValueEval Vis(Finder->getASTContext(), Node.getIdx());
559+
Vis.findMatch(const_cast<Expr*>(Node.getIdx()));
560+
APInt result = Vis.getValue();
561+
562+
if (result.isNonNegative() &&
563+
result.getLimitedValue() < CATy->getLimitedSize())
564+
return true;
450565

451566
return false;
452567
}
@@ -1146,6 +1261,10 @@ class ArraySubscriptGadget : public WarningGadget {
11461261
// clang-format on
11471262
}
11481263

1264+
const ArraySubscriptExpr* getASE() const{
1265+
return ASE;
1266+
}
1267+
11491268
void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
11501269
bool IsRelatedToDecl,
11511270
ASTContext &Ctx) const override {
@@ -3904,6 +4023,13 @@ void clang::checkUnsafeBufferUsage(const Decl *D,
39044023
: FixItList{},
39054024
D, NaiveStrategy);
39064025
for (const auto &G : WarningGadgets) {
4026+
//const Stmt *Operation = Handler;
4027+
if(const auto ASG = dyn_cast<ArraySubscriptGadget>(G)) {
4028+
const auto * AS = ASG->getASE();
4029+
MaxValueEval Vis(VD->getASTContext(), AS->getIdx());
4030+
Vis.findMatch(const_cast<Expr*>(AS->getIdx()));
4031+
APInt result = Vis.getValue();
4032+
}
39074033
G->handleUnsafeOperation(Handler, /*IsRelatedToDecl=*/true,
39084034
D->getASTContext());
39094035
}

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)