Skip to content

Commit 6e6ed6f

Browse files
committed
Implement without using eagerly-assume tags
1 parent 310bb3a commit 6e6ed6f

File tree

2 files changed

+58
-28
lines changed

2 files changed

+58
-28
lines changed

clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp

Lines changed: 56 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,11 @@
2828
#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h"
2929
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
3030
#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h"
31+
#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
3132
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
33+
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h"
3234
#include "llvm/ADT/DenseMap.h"
35+
#include "llvm/ADT/STLExtras.h"
3336
#include "llvm/Support/FormatVariadic.h"
3437
#include "llvm/Support/raw_ostream.h"
3538
#include <optional>
@@ -42,17 +45,36 @@ using llvm::formatv;
4245
namespace {
4346

4447
class SuppressReportsHavingWeakLoopAssumption : public BugReporterVisitor {
48+
public:
49+
SuppressReportsHavingWeakLoopAssumption(const PathSensitiveBugReport &R)
50+
: OrigCursor(R.getErrorNode()) {}
51+
4552
private:
4653
friend class BugReporterVisitor;
54+
55+
// The exploded node we are currently visiting, but in the original exploded
56+
// graph. This graph is not trimmed, unlike the one we usually visit.
57+
const ExplodedNode *OrigCursor;
58+
4759
llvm::DenseSet<const Stmt *> LoopsSeen;
48-
llvm::DenseMap<const Expr *, unsigned> EagerlyAssumedTrue;
49-
llvm::DenseMap<const Expr *, unsigned> EagerlyAssumedFalse;
60+
llvm::DenseMap<const Expr *, unsigned> NumTimesTakenTrueDecision;
61+
llvm::DenseMap<const Expr *, unsigned> NumTimesTakenFalseDecision;
5062

5163
void Profile(llvm::FoldingSetNodeID &ID) const final {
5264
static const bool Tag = 0;
5365
ID.AddPointer(&Tag);
5466
}
5567

68+
static const ExplodedNode *selectMatchingPred(const ExplodedNode *OrigSucc,
69+
unsigned SoughtPredId) {
70+
auto MatchesSoughtId = [SoughtPredId](const ExplodedNode *N) {
71+
return N->getID() == SoughtPredId;
72+
};
73+
auto It = llvm::find_if(OrigSucc->preds(), MatchesSoughtId);
74+
assert(It != OrigSucc->pred_end());
75+
return *It;
76+
}
77+
5678
static bool isLoopAndNonNull(const Stmt *S) {
5779
return isa_and_nonnull<ForStmt, WhileStmt, CXXForRangeStmt, DoStmt>(S);
5880
}
@@ -74,21 +96,33 @@ class SuppressReportsHavingWeakLoopAssumption : public BugReporterVisitor {
7496
PathDiagnosticPieceRef VisitNode(const ExplodedNode *Succ,
7597
BugReporterContext &BRC,
7698
PathSensitiveBugReport &BR) final {
99+
OrigCursor = selectMatchingPred(OrigCursor, Succ->getID());
100+
assert(OrigCursor->getID() == Succ->getID());
101+
77102
registerLoopStatements(Succ);
78103

79104
auto AsPostStmt = Succ->getLocationAs<PostStmt>();
80105
const Expr *CurrExpr =
81106
AsPostStmt ? dyn_cast<Expr>(AsPostStmt->getStmt()) : nullptr;
82107

83-
const auto *Tag = Succ->getLocation().getTag();
84-
if (!Tag)
108+
bool IsSplittingTwoWays = OrigCursor->succ_size() == 2;
109+
110+
if (!CurrExpr || !IsSplittingTwoWays)
85111
return nullptr;
86112

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);
113+
const ExplodedNode *Next = Succ->getFirstSucc();
114+
SValBuilder &SVB = Succ->getState()->getStateManager().getSValBuilder();
115+
auto Query = SVB.evalCast(Succ->getSVal(CurrExpr), SVB.getConditionType(),
116+
CurrExpr->getType())
117+
.getAs<DefinedOrUnknownSVal>();
118+
if (!Query)
119+
return nullptr;
120+
121+
ProgramStateRef TakenTrueBranch = Next->getState()->assume(*Query, true);
122+
registerOccurrence(TakenTrueBranch ? NumTimesTakenTrueDecision
123+
: NumTimesTakenFalseDecision,
124+
CurrExpr);
125+
92126
return nullptr;
93127
}
94128

@@ -110,30 +144,26 @@ class SuppressReportsHavingWeakLoopAssumption : public BugReporterVisitor {
110144

111145
void trySuppressReport(PathSensitiveBugReport &R, const Stmt *Loop,
112146
const Expr *CondSubExpr) const {
113-
auto NumEagerlyTrue = EagerlyAssumedTrue.lookup(CondSubExpr);
114-
auto NumEagerlyFalse = EagerlyAssumedFalse.lookup(CondSubExpr);
147+
unsigned NumTimesTakenLoopBody =
148+
NumTimesTakenTrueDecision.lookup(CondSubExpr);
149+
unsigned NumTimesNotTakenLoopBody =
150+
NumTimesTakenFalseDecision.lookup(CondSubExpr);
151+
152+
// Adjust for do-while loops, where the loop body is always taken.
153+
if (isa<DoStmt>(Loop))
154+
NumTimesTakenLoopBody += 1;
115155

116156
// Suppress the report if it avoided a loop with an eager assumption.
117-
if (NumEagerlyTrue == 0 && NumEagerlyFalse == 1) {
157+
if (NumTimesTakenLoopBody == 0 && NumTimesNotTakenLoopBody == 1) {
158+
// llvm::errs() << "eagerly decided never taking the loop body\n";
118159
R.markInvalid("eagerly decided never taking the loop body", CondSubExpr);
119160
return;
120161
}
121162

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-
134163
// Suppress the report if it iterates with eager assumptions 3 or more
135164
// times.
136-
if (NumEagerlyTrue > 2 && NumEagerlyFalse == 0) {
165+
if (NumTimesTakenLoopBody > 2 && NumTimesNotTakenLoopBody == 0) {
166+
// llvm::errs() << "eagerly taken the loop body at least 2 times\n";
137167
R.markInvalid("eagerly taken the loop body at least 2 times",
138168
CondSubExpr);
139169
}
@@ -854,7 +884,7 @@ void ArrayBoundCheckerV2::reportOOB(CheckerContext &C,
854884
if (Extent)
855885
markPartsInteresting(*BR, ErrorState, *Extent, IsTaintBug);
856886

857-
BR->addVisitor<SuppressReportsHavingWeakLoopAssumption>();
887+
BR->addVisitor<SuppressReportsHavingWeakLoopAssumption>(*BR);
858888
C.emitReport(std::move(BR));
859889
}
860890

clang/test/Analysis/out-of-bounds.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,7 @@ void loop_suppress_in_third_iteration_logical_and(int len, int flag) {
279279
// short-circuiting operator '&&'.
280280
// I have seen a real-world FP that looks like this, but it is much rarer
281281
// than the basic setup.
282-
buf[i] = 1; // expected-warning{{Out of bound access to memory}}
282+
buf[i] = 1; // no-warning
283283
}
284284
}
285285

@@ -311,7 +311,7 @@ void loop_suppress_in_third_iteration_cast(int len) {
311311
// assumption. There are already many differences between analysis with and
312312
// without eager assumptions, so it would be pointless to write more
313313
// complicated code to eliminate these rare differences.
314-
buf[i] = 1; // eagerlyassume-warning{{Out of bound access to memory}}
314+
buf[i] = 1; // no-warning
315315
}
316316
}
317317

0 commit comments

Comments
 (0)