@@ -57,8 +57,8 @@ class RegionRawOffsetV2 {
57
57
: baseRegion(nullptr ), byteOffset(UnknownVal()) {}
58
58
59
59
public:
60
- RegionRawOffsetV2 (const SubRegion* base, SVal offset)
61
- : baseRegion(base), byteOffset(offset) {}
60
+ RegionRawOffsetV2 (const SubRegion * base, NonLoc offset)
61
+ : baseRegion(base), byteOffset(offset) { assert (base); }
62
62
63
63
NonLoc getByteOffset () const { return byteOffset.castAs <NonLoc>(); }
64
64
const SubRegion *getRegion () const { return baseRegion; }
@@ -72,19 +72,12 @@ class RegionRawOffsetV2 {
72
72
};
73
73
}
74
74
75
- static SVal computeExtentBegin (SValBuilder &svalBuilder,
76
- const MemRegion *region) {
77
- const MemSpaceRegion *SR = region->getMemorySpace ();
78
- if (SR->getKind () == MemRegion::UnknownSpaceRegionKind)
79
- return UnknownVal ();
80
- else
81
- return svalBuilder.makeZeroArrayIndex ();
82
- }
83
-
84
75
// TODO: once the constraint manager is smart enough to handle non simplified
85
76
// symbolic expressions remove this function. Note that this can not be used in
86
77
// the constraint manager as is, since this does not handle overflows. It is
87
78
// safe to assume, however, that memory offsets will not overflow.
79
+ // NOTE: callers of this function need to be aware of the effects of overflows
80
+ // and signed<->unsigned conversions!
88
81
static std::pair<NonLoc, nonloc::ConcreteInt>
89
82
getSimplifiedOffsets (NonLoc offset, nonloc::ConcreteInt extent,
90
83
SValBuilder &svalBuilder) {
@@ -117,6 +110,38 @@ getSimplifiedOffsets(NonLoc offset, nonloc::ConcreteInt extent,
117
110
return std::pair<NonLoc, nonloc::ConcreteInt>(offset, extent);
118
111
}
119
112
113
+ // Evaluate the comparison Value < Threshold with the help of the custom
114
+ // simplification algorithm defined for this checker. Return a pair of states,
115
+ // where the first one corresponds to "value below threshold" and the second
116
+ // corresponds to "value at or above threshold". Returns {nullptr, nullptr} in
117
+ // the case when the evaluation fails.
118
+ static std::pair<ProgramStateRef, ProgramStateRef>
119
+ compareValueToThreshold (ProgramStateRef State, NonLoc Value, NonLoc Threshold,
120
+ SValBuilder &SVB) {
121
+ if (auto ConcreteThreshold = Threshold.getAs <nonloc::ConcreteInt>()) {
122
+ std::tie (Value, Threshold) = getSimplifiedOffsets (Value, *ConcreteThreshold, SVB);
123
+ }
124
+ if (auto ConcreteThreshold = Threshold.getAs <nonloc::ConcreteInt>()) {
125
+ QualType T = Value.getType (SVB.getContext ());
126
+ if (T->isUnsignedIntegerType () && ConcreteThreshold->getValue ().isNegative ()) {
127
+ // In this case we reduced the bound check to a comparison of the form
128
+ // (symbol or value with unsigned type) < (negative number)
129
+ // which is always false. We are handling these cases separately because
130
+ // evalBinOpNN can perform a signed->unsigned conversion that turns the
131
+ // negative number into a huge positive value and leads to wildly
132
+ // inaccurate conclusions.
133
+ return {nullptr , State};
134
+ }
135
+ }
136
+ auto BelowThreshold =
137
+ SVB.evalBinOpNN (State, BO_LT, Value, Threshold, SVB.getConditionType ()).getAs <NonLoc>();
138
+
139
+ if (BelowThreshold)
140
+ return State->assume (*BelowThreshold);
141
+
142
+ return {nullptr , nullptr };
143
+ }
144
+
120
145
void ArrayBoundCheckerV2::checkLocation (SVal location, bool isLoad,
121
146
const Stmt* LoadS,
122
147
CheckerContext &checkerContext) const {
@@ -139,95 +164,55 @@ void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad,
139
164
if (!rawOffset.getRegion ())
140
165
return ;
141
166
142
- NonLoc rawOffsetVal = rawOffset.getByteOffset ();
167
+ NonLoc ByteOffset = rawOffset.getByteOffset ();
143
168
144
- // CHECK LOWER BOUND: Is byteOffset < extent begin?
145
- // If so, we are doing a load/store
146
- // before the first valid offset in the memory region.
147
-
148
- SVal extentBegin = computeExtentBegin (svalBuilder, rawOffset.getRegion ());
149
-
150
- if (std::optional<NonLoc> NV = extentBegin.getAs <NonLoc>()) {
151
- if (auto ConcreteNV = NV->getAs <nonloc::ConcreteInt>()) {
152
- std::pair<NonLoc, nonloc::ConcreteInt> simplifiedOffsets =
153
- getSimplifiedOffsets (rawOffset.getByteOffset (), *ConcreteNV,
154
- svalBuilder);
155
- rawOffsetVal = simplifiedOffsets.first ;
156
- *NV = simplifiedOffsets.second ;
157
- }
169
+ // CHECK LOWER BOUND
170
+ const MemSpaceRegion *SR = rawOffset.getRegion ()->getMemorySpace ();
171
+ if (!llvm::isa<UnknownSpaceRegion>(SR)) {
172
+ // A pointer to UnknownSpaceRegion may point to the middle of
173
+ // an allocated region.
158
174
159
- SVal lowerBound = svalBuilder.evalBinOpNN (state, BO_LT, rawOffsetVal, *NV,
160
- svalBuilder.getConditionType ());
175
+ auto [state_precedesLowerBound, state_withinLowerBound] =
176
+ compareValueToThreshold (state, ByteOffset,
177
+ svalBuilder.makeZeroArrayIndex (), svalBuilder);
161
178
162
- std::optional<NonLoc> lowerBoundToCheck = lowerBound.getAs <NonLoc>();
163
- if (!lowerBoundToCheck)
164
- return ;
165
-
166
- ProgramStateRef state_precedesLowerBound, state_withinLowerBound;
167
- std::tie (state_precedesLowerBound, state_withinLowerBound) =
168
- state->assume (*lowerBoundToCheck);
169
-
170
- // Are we constrained enough to definitely precede the lower bound?
171
179
if (state_precedesLowerBound && !state_withinLowerBound) {
180
+ // We know that the index definitely precedes the lower bound.
172
181
reportOOB (checkerContext, state_precedesLowerBound, OOB_Precedes);
173
182
return ;
174
183
}
175
184
176
- // Otherwise, assume the constraint of the lower bound.
177
- assert (state_withinLowerBound);
178
- state = state_withinLowerBound;
185
+ if (state_withinLowerBound)
186
+ state = state_withinLowerBound;
179
187
}
180
188
181
- do {
182
- // CHECK UPPER BOUND: Is byteOffset >= size(baseRegion)? If so,
183
- // we are doing a load/store after the last valid offset.
184
- const MemRegion *MR = rawOffset.getRegion ();
185
- DefinedOrUnknownSVal Size = getDynamicExtent (state, MR, svalBuilder);
186
- if (!isa<NonLoc>(Size))
187
- break ;
188
-
189
- if (auto ConcreteSize = Size.getAs <nonloc::ConcreteInt>()) {
190
- std::pair<NonLoc, nonloc::ConcreteInt> simplifiedOffsets =
191
- getSimplifiedOffsets (rawOffset.getByteOffset (), *ConcreteSize,
192
- svalBuilder);
193
- rawOffsetVal = simplifiedOffsets.first ;
194
- Size = simplifiedOffsets.second ;
195
- }
196
-
197
- SVal upperbound = svalBuilder.evalBinOpNN (state, BO_GE, rawOffsetVal,
198
- Size.castAs <NonLoc>(),
199
- svalBuilder.getConditionType ());
200
-
201
- std::optional<NonLoc> upperboundToCheck = upperbound.getAs <NonLoc>();
202
- if (!upperboundToCheck)
203
- break ;
204
-
205
- ProgramStateRef state_exceedsUpperBound, state_withinUpperBound;
206
- std::tie (state_exceedsUpperBound, state_withinUpperBound) =
207
- state->assume (*upperboundToCheck);
208
-
209
- // If we are under constrained and the index variables are tainted, report.
210
- if (state_exceedsUpperBound && state_withinUpperBound) {
211
- SVal ByteOffset = rawOffset.getByteOffset ();
189
+ // CHECK UPPER BOUND
190
+ DefinedOrUnknownSVal Size =
191
+ getDynamicExtent (state, rawOffset.getRegion (), svalBuilder);
192
+ if (auto KnownSize = Size.getAs <NonLoc>()) {
193
+ auto [state_withinUpperBound, state_exceedsUpperBound] =
194
+ compareValueToThreshold (state, ByteOffset, *KnownSize, svalBuilder);
195
+
196
+ if (state_exceedsUpperBound) {
197
+ if (!state_withinUpperBound) {
198
+ // We know that the index definitely exceeds the upper bound.
199
+ reportOOB (checkerContext, state_exceedsUpperBound, OOB_Excedes);
200
+ return ;
201
+ }
212
202
if (isTainted (state, ByteOffset)) {
203
+ // Both cases are possible, but the index is tainted, so report.
213
204
reportTaintOOB (checkerContext, state_exceedsUpperBound, ByteOffset);
214
205
return ;
215
206
}
216
- } else if (state_exceedsUpperBound) {
217
- // If we are constrained enough to definitely exceed the upper bound,
218
- // report.
219
- assert (!state_withinUpperBound);
220
- reportOOB (checkerContext, state_exceedsUpperBound, OOB_Excedes);
221
- return ;
222
207
}
223
208
224
- assert (state_withinUpperBound);
225
- state = state_withinUpperBound;
209
+ if (state_withinUpperBound)
210
+ state = state_withinUpperBound;
226
211
}
227
- while (false );
228
212
229
213
checkerContext.addTransition (state);
230
214
}
215
+
231
216
void ArrayBoundCheckerV2::reportTaintOOB (CheckerContext &checkerContext,
232
217
ProgramStateRef errorState,
233
218
SVal TaintedSVal) const {
@@ -336,9 +321,8 @@ RegionRawOffsetV2 RegionRawOffsetV2::computeOffset(ProgramStateRef state,
336
321
switch (region->getKind ()) {
337
322
default : {
338
323
if (const SubRegion *subReg = dyn_cast<SubRegion>(region)) {
339
- offset = getValue (offset, svalBuilder);
340
- if (!offset.isUnknownOrUndef ())
341
- return RegionRawOffsetV2 (subReg, offset);
324
+ if (auto Offset = getValue (offset, svalBuilder).getAs <NonLoc>())
325
+ return RegionRawOffsetV2 (subReg, *Offset);
342
326
}
343
327
return RegionRawOffsetV2 ();
344
328
}
0 commit comments