Skip to content

Commit 310bb3a

Browse files
committed
Add SuppressReportsHavingWeakLoopAssumption visitor
1 parent c240537 commit 310bb3a

File tree

1 file changed

+134
-1
lines changed

1 file changed

+134
-1
lines changed

clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp

Lines changed: 134 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,25 @@
1111
//
1212
//===----------------------------------------------------------------------===//
1313

14+
#include "clang/AST/ASTContext.h"
1415
#include "clang/AST/CharUnits.h"
16+
#include "clang/AST/Expr.h"
1517
#include "clang/AST/ParentMapContext.h"
18+
#include "clang/AST/Stmt.h"
19+
#include "clang/ASTMatchers/ASTMatchFinder.h"
20+
#include "clang/ASTMatchers/ASTMatchers.h"
21+
#include "clang/Analysis/ProgramPoint.h"
1622
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
1723
#include "clang/StaticAnalyzer/Checkers/Taint.h"
24+
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h"
1825
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
1926
#include "clang/StaticAnalyzer/Core/Checker.h"
2027
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
2128
#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h"
2229
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
2330
#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h"
2431
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
25-
#include "llvm/ADT/SmallString.h"
32+
#include "llvm/ADT/DenseMap.h"
2633
#include "llvm/Support/FormatVariadic.h"
2734
#include "llvm/Support/raw_ostream.h"
2835
#include <optional>
@@ -32,6 +39,131 @@ using namespace ento;
3239
using namespace taint;
3340
using llvm::formatv;
3441

42+
namespace {
43+
44+
class SuppressReportsHavingWeakLoopAssumption : public BugReporterVisitor {
45+
private:
46+
friend class BugReporterVisitor;
47+
llvm::DenseSet<const Stmt *> LoopsSeen;
48+
llvm::DenseMap<const Expr *, unsigned> EagerlyAssumedTrue;
49+
llvm::DenseMap<const Expr *, unsigned> EagerlyAssumedFalse;
50+
51+
void Profile(llvm::FoldingSetNodeID &ID) const final {
52+
static const bool Tag = 0;
53+
ID.AddPointer(&Tag);
54+
}
55+
56+
static bool isLoopAndNonNull(const Stmt *S) {
57+
return isa_and_nonnull<ForStmt, WhileStmt, CXXForRangeStmt, DoStmt>(S);
58+
}
59+
60+
void registerLoopStatements(const ExplodedNode *N) {
61+
if (auto Entrance = N->getLocation().getAs<BlockEntrance>()) {
62+
const Stmt *TermStmt = Entrance->getBlock()->getTerminatorStmt();
63+
if (isLoopAndNonNull(TermStmt))
64+
LoopsSeen.insert(TermStmt);
65+
}
66+
}
67+
68+
void registerOccurrence(llvm::DenseMap<const Expr *, unsigned> &Map,
69+
const Expr *Key) {
70+
if (auto [Place, Inserted] = Map.try_emplace(Key, 1); !Inserted)
71+
++Place->second;
72+
}
73+
74+
PathDiagnosticPieceRef VisitNode(const ExplodedNode *Succ,
75+
BugReporterContext &BRC,
76+
PathSensitiveBugReport &BR) final {
77+
registerLoopStatements(Succ);
78+
79+
auto AsPostStmt = Succ->getLocationAs<PostStmt>();
80+
const Expr *CurrExpr =
81+
AsPostStmt ? dyn_cast<Expr>(AsPostStmt->getStmt()) : nullptr;
82+
83+
const auto *Tag = Succ->getLocation().getTag();
84+
if (!Tag)
85+
return nullptr;
86+
87+
StringRef TagDesc = Tag->getTagDescription();
88+
if (TagDesc == "ExprEngine : Eagerly Assume True")
89+
registerOccurrence(EagerlyAssumedTrue, CurrExpr);
90+
if (TagDesc == "ExprEngine : Eagerly Assume False")
91+
registerOccurrence(EagerlyAssumedFalse, CurrExpr);
92+
return nullptr;
93+
}
94+
95+
static const Expr *getCond(const Stmt *S) {
96+
assert(isLoopAndNonNull(S));
97+
switch (S->getStmtClass()) {
98+
case Stmt::StmtClass::ForStmtClass:
99+
return cast<ForStmt>(S)->getCond();
100+
case Stmt::StmtClass::WhileStmtClass:
101+
return cast<WhileStmt>(S)->getCond();
102+
case Stmt::StmtClass::CXXForRangeStmtClass:
103+
return cast<CXXForRangeStmt>(S)->getCond();
104+
case Stmt::StmtClass::DoStmtClass:
105+
return cast<DoStmt>(S)->getCond();
106+
default:
107+
return nullptr;
108+
}
109+
}
110+
111+
void trySuppressReport(PathSensitiveBugReport &R, const Stmt *Loop,
112+
const Expr *CondSubExpr) const {
113+
auto NumEagerlyTrue = EagerlyAssumedTrue.lookup(CondSubExpr);
114+
auto NumEagerlyFalse = EagerlyAssumedFalse.lookup(CondSubExpr);
115+
116+
// Suppress the report if it avoided a loop with an eager assumption.
117+
if (NumEagerlyTrue == 0 && NumEagerlyFalse == 1) {
118+
R.markInvalid("eagerly decided never taking the loop body", CondSubExpr);
119+
return;
120+
}
121+
122+
// Account for do-while loops where the body is already "taken"
123+
// unconditionally for the first round.
124+
if (isa<DoStmt>(Loop)) {
125+
// Suppress the report if we have taken the loop body for the second time
126+
// with an eager assumption.
127+
if (NumEagerlyTrue > 1 && NumEagerlyFalse == 0) {
128+
R.markInvalid("eagerly taken the do-while body at least 2 times",
129+
CondSubExpr);
130+
}
131+
return;
132+
}
133+
134+
// Suppress the report if it iterates with eager assumptions 3 or more
135+
// times.
136+
if (NumEagerlyTrue > 2 && NumEagerlyFalse == 0) {
137+
R.markInvalid("eagerly taken the loop body at least 2 times",
138+
CondSubExpr);
139+
}
140+
}
141+
142+
void finalizeVisitor(BugReporterContext &BRC, const ExplodedNode *,
143+
PathSensitiveBugReport &R) final {
144+
ASTContext &Ctx = BRC.getASTContext();
145+
146+
// Go over all the loops we have seen (either avoided or entered), and check
147+
// if the condition of such loops were eagerly decided.
148+
for (const Stmt *Loop : LoopsSeen) {
149+
if (const Expr *Cond = getCond(Loop)) {
150+
// Let's try all sub-exprs to cover cases that use short-circuiting.
151+
// We need to check if any sub-expression of the condition was eagerly
152+
// decided, because the '&&' and '||' logical operators could
153+
// short-circuit, thus the expression on which we recorded the
154+
// "eager decision" was a sub-expression of the Loop condition.
155+
using namespace ast_matchers;
156+
for (auto Binding : match(findAll(expr().bind("e")), *Cond, Ctx)) {
157+
trySuppressReport(R, Loop, Binding.getNodeAs<Expr>("e"));
158+
if (!R.isValid())
159+
return;
160+
}
161+
}
162+
}
163+
}
164+
};
165+
} // namespace
166+
35167
namespace {
36168
/// If `E` is a "clean" array subscript expression, return the type of the
37169
/// accessed element. If the base of the subscript expression is modified by
@@ -722,6 +854,7 @@ void ArrayBoundCheckerV2::reportOOB(CheckerContext &C,
722854
if (Extent)
723855
markPartsInteresting(*BR, ErrorState, *Extent, IsTaintBug);
724856

857+
BR->addVisitor<SuppressReportsHavingWeakLoopAssumption>();
725858
C.emitReport(std::move(BR));
726859
}
727860

0 commit comments

Comments
 (0)