Skip to content

Commit f5b5835

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 76e73ae commit f5b5835

File tree

2 files changed

+154
-0
lines changed

2 files changed

+154
-0
lines changed

clang/lib/Analysis/UnsafeBufferUsage.cpp

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,122 @@ AST_MATCHER(CXXConstructExpr, isSafeSpanTwoParamConstruct) {
431431
return false;
432432
}
433433

434+
class MaxValueEval : public ConstStmtVisitor<MaxValueEval, llvm::APInt> {
435+
436+
ASTContext &Context;
437+
llvm::APInt Max;
438+
unsigned bit_width;
439+
440+
public:
441+
typedef ConstStmtVisitor<MaxValueEval, llvm::APInt> VisitorBase;
442+
443+
explicit MaxValueEval(ASTContext &Ctx, const Expr *exp) : Context(Ctx) {
444+
bit_width = Ctx.getIntWidth(exp->getType());
445+
Max = llvm::APInt::getSignedMaxValue(bit_width);
446+
// val.clear();
447+
}
448+
449+
llvm::APInt findMatch(Expr *exp) { return TraverseStmt(exp); }
450+
451+
llvm::APInt TraverseStmt(Stmt *S) {
452+
if (Expr *E = dyn_cast<Expr>(S)) {
453+
Expr::EvalResult EVResult;
454+
if (ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) {
455+
return TraverseImplicitCastExpr(ICE);
456+
} else if (dyn_cast<DeclRefExpr>(E)) {
457+
return Max;
458+
} else if (dyn_cast<ArraySubscriptExpr>(E)) {
459+
return Max;
460+
} else if (BinaryOperator *BO = dyn_cast<BinaryOperator>(E)) {
461+
return TraverseBinaryOperator(BO);
462+
} else if (IntegerLiteral *IL = dyn_cast<IntegerLiteral>(E)) {
463+
return IL->getValue();
464+
}
465+
}
466+
return Max;
467+
}
468+
469+
llvm::APInt TraverseImplicitCastExpr(ImplicitCastExpr *E) {
470+
Expr::EvalResult EVResult;
471+
if (EvaluateExpression(E, EVResult)) {
472+
return EVResult.Val.getInt();
473+
} else {
474+
Expr *eExpr = E->getSubExpr();
475+
llvm::APInt subEValue = TraverseStmt(eExpr);
476+
switch (E->getCastKind()) {
477+
case CK_LValueToRValue:
478+
return subEValue;
479+
case CK_IntegralCast: {
480+
Expr *eExpr = E->getSubExpr();
481+
clang::IntegerLiteral *intLiteral = clang::IntegerLiteral::Create(
482+
Context, subEValue, eExpr->getType(), {});
483+
E->setSubExpr(intLiteral);
484+
bool evaluated = EvaluateExpression(E, EVResult);
485+
E->setSubExpr(eExpr);
486+
if (evaluated) {
487+
return EVResult.Val.getInt();
488+
}
489+
break;
490+
}
491+
default:
492+
break;
493+
}
494+
return Max;
495+
}
496+
}
497+
498+
bool EvaluateExpression(Expr *exp, Expr::EvalResult &EVResult) {
499+
if (exp->EvaluateAsInt(EVResult, Context)) {
500+
return true;
501+
}
502+
return false;
503+
}
504+
505+
llvm::APInt TraverseBinaryOperator(BinaryOperator *E) {
506+
unsigned bwidth = Context.getIntWidth(E->getType());
507+
508+
auto evaluateSubExpr = [&, bwidth](Expr *E) -> llvm::APInt {
509+
llvm::APInt Result = TraverseStmt(E);
510+
unsigned width = Result.getBitWidth();
511+
512+
// Fix the bit length.
513+
if (bwidth < width)
514+
Result = Result.trunc(bwidth);
515+
else if (bwidth > width)
516+
Result =
517+
APInt(bwidth, Result.getLimitedValue(), Result.isSignedIntN(width));
518+
return Result;
519+
};
520+
521+
Expr::EvalResult EVResult;
522+
if (EvaluateExpression(E, EVResult)) {
523+
return EVResult.Val.getInt();
524+
} else {
525+
Expr *LHSExpr = E->getLHS()->IgnoreParenCasts();
526+
Expr *RHSExpr = E->getRHS()->IgnoreParenCasts();
527+
528+
unsigned bwidth = Context.getIntWidth(E->getType());
529+
530+
llvm::APInt LHS = evaluateSubExpr(LHSExpr);
531+
llvm::APInt RHS = evaluateSubExpr(RHSExpr);
532+
533+
llvm::APInt Result = Max;
534+
535+
switch (E->getOpcode()) {
536+
case BO_And:
537+
case BO_AndAssign:
538+
Result = LHS & RHS;
539+
break;
540+
541+
default:
542+
break;
543+
}
544+
return Result;
545+
}
546+
return Max;
547+
}
548+
};
549+
434550
AST_MATCHER(ArraySubscriptExpr, isSafeArraySubscript) {
435551
// FIXME: Proper solution:
436552
// - refactor Sema::CheckArrayAccess
@@ -463,6 +579,13 @@ AST_MATCHER(ArraySubscriptExpr, isSafeArraySubscript) {
463579
if (ArrIdx.isNonNegative() && ArrIdx.getLimitedValue() < limit)
464580
return true;
465581
}
582+
583+
MaxValueEval Vis(Finder->getASTContext(), Node.getIdx());
584+
APInt result = Vis.findMatch(const_cast<Expr *>(Node.getIdx()));
585+
586+
if (result.isNonNegative() && result.getLimitedValue() < limit)
587+
return true;
588+
466589
return false;
467590
}
468591

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

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ void foo2(unsigned idx) {
1818

1919
struct Foo {
2020
int member_buffer[10];
21+
int x;
2122
};
23+
2224
void foo2(Foo& f, unsigned idx) {
2325
f.member_buffer[idx] = 0; // expected-warning{{unsafe buffer access}}
2426
}
@@ -33,6 +35,35 @@ void constant_idx_safe0(unsigned idx) {
3335
buffer[0] = 0;
3436
}
3537

38+
int array[10]; // expected-warning {{'array' is an unsafe buffer that does not perform bounds checks}}
39+
40+
void masked_idx1(unsigned long long idx, Foo f) {
41+
// Bitwise and operation
42+
array[idx & 5] = 10; // no warning
43+
array[idx & 11 & 5] = 3; // no warning
44+
array[idx & 11] = 20; // expected-note{{used in buffer access here}}
45+
array[idx &=5];
46+
array[f.x & 5];
47+
}
48+
49+
typedef unsigned long long uint64_t;
50+
typedef unsigned int uint32_t;
51+
typedef unsigned char uint8_t;
52+
53+
void type_conversions(uint64_t idx1, uint32_t idx2, uint8_t idx3) {
54+
array[(uint32_t)idx1 & 3];
55+
array[idx2 & 3];
56+
array[idx3 & 3];
57+
}
58+
59+
int array2[5];
60+
61+
void masked_idx_safe(unsigned long long idx) {
62+
array2[6 & 5]; // no warning
63+
array2[6 & idx & (idx + 1) & 5]; // no warning
64+
}
65+
66+
3667
void constant_idx_unsafe(unsigned idx) {
3768
int buffer[10]; // expected-warning{{'buffer' is an unsafe buffer that does not perform bounds checks}}
3869
// expected-note@-1{{change type of 'buffer' to 'std::array' to label it for hardening}}

0 commit comments

Comments
 (0)