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;
3239using namespace taint ;
3340using 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+
35167namespace {
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