Skip to content

Commit 855ad82

Browse files
committed
[analyzer] Correctly add assumptions based on array bounds.
Also simplify the constraints generated by the checker. Differential Revision: https://reviews.llvm.org/D23112 llvm-svn: 279425
1 parent 5f8419d commit 855ad82

File tree

2 files changed

+84
-21
lines changed

2 files changed

+84
-21
lines changed

clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp

Lines changed: 67 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
1818
#include "clang/StaticAnalyzer/Core/Checker.h"
1919
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
20+
#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h"
2021
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
2122
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
2223
#include "llvm/ADT/SmallString.h"
@@ -81,6 +82,42 @@ static SVal computeExtentBegin(SValBuilder &svalBuilder,
8182
}
8283
}
8384

85+
// TODO: once the constraint manager is smart enough to handle non simplified
86+
// symbolic expressions remove this function. Note that this can not be used in
87+
// the constraint manager as is, since this does not handle overflows. It is
88+
// safe to assume, however, that memory offsets will not overflow.
89+
static std::pair<NonLoc, nonloc::ConcreteInt>
90+
getSimplifiedOffsets(NonLoc offset, nonloc::ConcreteInt extent,
91+
SValBuilder &svalBuilder) {
92+
Optional<nonloc::SymbolVal> SymVal = offset.getAs<nonloc::SymbolVal>();
93+
if (SymVal && SymVal->isExpression()) {
94+
if (const SymIntExpr *SIE = dyn_cast<SymIntExpr>(SymVal->getSymbol())) {
95+
llvm::APSInt constant =
96+
APSIntType(extent.getValue()).convert(SIE->getRHS());
97+
switch (SIE->getOpcode()) {
98+
case BO_Mul:
99+
// The constant should never be 0 here, since it the result of scaling
100+
// based on the size of a type which is never 0.
101+
if ((extent.getValue() % constant) != 0)
102+
return std::pair<NonLoc, nonloc::ConcreteInt>(offset, extent);
103+
else
104+
return getSimplifiedOffsets(
105+
nonloc::SymbolVal(SIE->getLHS()),
106+
svalBuilder.makeIntVal(extent.getValue() / constant),
107+
svalBuilder);
108+
case BO_Add:
109+
return getSimplifiedOffsets(
110+
nonloc::SymbolVal(SIE->getLHS()),
111+
svalBuilder.makeIntVal(extent.getValue() - constant), svalBuilder);
112+
default:
113+
break;
114+
}
115+
}
116+
}
117+
118+
return std::pair<NonLoc, nonloc::ConcreteInt>(offset, extent);
119+
}
120+
84121
void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad,
85122
const Stmt* LoadS,
86123
CheckerContext &checkerContext) const {
@@ -104,16 +141,26 @@ void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad,
104141
if (!rawOffset.getRegion())
105142
return;
106143

144+
NonLoc rawOffsetVal = rawOffset.getByteOffset();
145+
107146
// CHECK LOWER BOUND: Is byteOffset < extent begin?
108147
// If so, we are doing a load/store
109148
// before the first valid offset in the memory region.
110149

111150
SVal extentBegin = computeExtentBegin(svalBuilder, rawOffset.getRegion());
112151

113152
if (Optional<NonLoc> NV = extentBegin.getAs<NonLoc>()) {
114-
SVal lowerBound =
115-
svalBuilder.evalBinOpNN(state, BO_LT, rawOffset.getByteOffset(), *NV,
116-
svalBuilder.getConditionType());
153+
if (NV->getAs<nonloc::ConcreteInt>()) {
154+
std::pair<NonLoc, nonloc::ConcreteInt> simplifiedOffsets =
155+
getSimplifiedOffsets(rawOffset.getByteOffset(),
156+
NV->castAs<nonloc::ConcreteInt>(),
157+
svalBuilder);
158+
rawOffsetVal = simplifiedOffsets.first;
159+
*NV = simplifiedOffsets.second;
160+
}
161+
162+
SVal lowerBound = svalBuilder.evalBinOpNN(state, BO_LT, rawOffsetVal, *NV,
163+
svalBuilder.getConditionType());
117164

118165
Optional<NonLoc> lowerBoundToCheck = lowerBound.getAs<NonLoc>();
119166
if (!lowerBoundToCheck)
@@ -142,10 +189,18 @@ void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad,
142189
if (!extentVal.getAs<NonLoc>())
143190
break;
144191

145-
SVal upperbound
146-
= svalBuilder.evalBinOpNN(state, BO_GE, rawOffset.getByteOffset(),
147-
extentVal.castAs<NonLoc>(),
148-
svalBuilder.getConditionType());
192+
if (extentVal.getAs<nonloc::ConcreteInt>()) {
193+
std::pair<NonLoc, nonloc::ConcreteInt> simplifiedOffsets =
194+
getSimplifiedOffsets(rawOffset.getByteOffset(),
195+
extentVal.castAs<nonloc::ConcreteInt>(),
196+
svalBuilder);
197+
rawOffsetVal = simplifiedOffsets.first;
198+
extentVal = simplifiedOffsets.second;
199+
}
200+
201+
SVal upperbound = svalBuilder.evalBinOpNN(state, BO_GE, rawOffsetVal,
202+
extentVal.castAs<NonLoc>(),
203+
svalBuilder.getConditionType());
149204

150205
Optional<NonLoc> upperboundToCheck = upperbound.getAs<NonLoc>();
151206
if (!upperboundToCheck)
@@ -157,13 +212,13 @@ void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad,
157212

158213
// If we are under constrained and the index variables are tainted, report.
159214
if (state_exceedsUpperBound && state_withinUpperBound) {
160-
if (state->isTainted(rawOffset.getByteOffset()))
215+
if (state->isTainted(rawOffset.getByteOffset())) {
161216
reportOOB(checkerContext, state_exceedsUpperBound, OOB_Tainted);
162217
return;
163-
}
164-
165-
// If we are constrained enough to definitely exceed the upper bound, report.
166-
if (state_exceedsUpperBound) {
218+
}
219+
} else if (state_exceedsUpperBound) {
220+
// If we are constrained enough to definitely exceed the upper bound,
221+
// report.
167222
assert(!state_withinUpperBound);
168223
reportOOB(checkerContext, state_exceedsUpperBound, OOB_Excedes);
169224
return;

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

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
// RUN: %clang_cc1 -Wno-array-bounds -analyze -analyzer-checker=core,alpha.security.ArrayBoundV2 -verify %s
1+
// RUN: %clang_cc1 -Wno-array-bounds -analyze -analyzer-checker=core,alpha.security.ArrayBoundV2,debug.ExprInspection -verify %s
2+
3+
void clang_analyzer_eval(int);
24

35
// Tests doing an out-of-bounds access after the end of an array using:
46
// - constant integer index
@@ -128,22 +130,22 @@ void test2_multi_ok(int x) {
128130
buf[0][0] = 1; // no-warning
129131
}
130132

131-
// *** FIXME ***
132-
// We don't get a warning here yet because our symbolic constraint solving
133-
// doesn't handle: (symbol * constant) < constant
134133
void test3(int x) {
135134
int buf[100];
136135
if (x < 0)
137-
buf[x] = 1;
136+
buf[x] = 1; // expected-warning{{Out of bound memory access}}
138137
}
139138

140-
// *** FIXME ***
141-
// We don't get a warning here yet because our symbolic constraint solving
142-
// doesn't handle: (symbol * constant) < constant
143139
void test4(int x) {
144140
int buf[100];
145141
if (x > 99)
146-
buf[x] = 1;
142+
buf[x] = 1; // expected-warning{{Out of bound memory access}}
143+
}
144+
145+
void test_assume_after_access(unsigned long x) {
146+
int buf[100];
147+
buf[x] = 1;
148+
clang_analyzer_eval(x <= 99); // expected-warning{{TRUE}}
147149
}
148150

149151
// Don't warn when indexing below the start of a symbolic region's whose
@@ -166,3 +168,9 @@ void test_extern_void() {
166168
p[1] = 42; // no-warning
167169
}
168170

171+
void test_assume_after_access2(unsigned long x) {
172+
char buf[100];
173+
buf[x] = 1;
174+
clang_analyzer_eval(x <= 99); // expected-warning{{TRUE}}
175+
}
176+

0 commit comments

Comments
 (0)