@@ -53,21 +53,17 @@ class ArrayBoundCheckerV2 :
53
53
class RegionRawOffsetV2 {
54
54
private:
55
55
const SubRegion *baseRegion;
56
- SVal byteOffset;
57
-
58
- RegionRawOffsetV2 ()
59
- : baseRegion(nullptr ), byteOffset(UnknownVal()) {}
56
+ NonLoc byteOffset;
60
57
61
58
public:
62
59
RegionRawOffsetV2 (const SubRegion *base, NonLoc offset)
63
60
: baseRegion(base), byteOffset(offset) { assert (base); }
64
61
65
- NonLoc getByteOffset () const { return byteOffset. castAs <NonLoc>() ; }
62
+ NonLoc getByteOffset () const { return byteOffset; }
66
63
const SubRegion *getRegion () const { return baseRegion; }
67
64
68
- static RegionRawOffsetV2 computeOffset (ProgramStateRef state,
69
- SValBuilder &svalBuilder,
70
- SVal location);
65
+ static std::optional<RegionRawOffsetV2>
66
+ computeOffset (ProgramStateRef State, SValBuilder &SVB, SVal Location);
71
67
72
68
void dump () const ;
73
69
void dumpToStream (raw_ostream &os) const ;
@@ -169,16 +165,16 @@ void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad,
169
165
ProgramStateRef state = checkerContext.getState ();
170
166
171
167
SValBuilder &svalBuilder = checkerContext.getSValBuilder ();
172
- const RegionRawOffsetV2 &rawOffset =
173
- RegionRawOffsetV2::computeOffset (state, svalBuilder, location);
168
+ const std::optional< RegionRawOffsetV2> &RawOffset =
169
+ RegionRawOffsetV2::computeOffset (state, svalBuilder, location);
174
170
175
- if (!rawOffset. getRegion () )
171
+ if (!RawOffset )
176
172
return ;
177
173
178
- NonLoc ByteOffset = rawOffset. getByteOffset ();
174
+ NonLoc ByteOffset = RawOffset-> getByteOffset ();
179
175
180
176
// CHECK LOWER BOUND
181
- const MemSpaceRegion *SR = rawOffset. getRegion ()->getMemorySpace ();
177
+ const MemSpaceRegion *SR = RawOffset-> getRegion ()->getMemorySpace ();
182
178
if (!llvm::isa<UnknownSpaceRegion>(SR)) {
183
179
// A pointer to UnknownSpaceRegion may point to the middle of
184
180
// an allocated region.
@@ -199,7 +195,7 @@ void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad,
199
195
200
196
// CHECK UPPER BOUND
201
197
DefinedOrUnknownSVal Size =
202
- getDynamicExtent (state, rawOffset. getRegion (), svalBuilder);
198
+ getDynamicExtent (state, RawOffset-> getRegion (), svalBuilder);
203
199
if (auto KnownSize = Size.getAs <NonLoc>()) {
204
200
auto [state_withinUpperBound, state_exceedsUpperBound] =
205
201
compareValueToThreshold (state, ByteOffset, *KnownSize, svalBuilder);
@@ -307,84 +303,55 @@ void RegionRawOffsetV2::dumpToStream(raw_ostream &os) const {
307
303
}
308
304
#endif
309
305
310
- // Lazily computes a value to be used by 'computeOffset'. If 'val'
311
- // is unknown or undefined, we lazily substitute '0'. Otherwise,
312
- // return 'val'.
313
- static inline SVal getValue (SVal val, SValBuilder &svalBuilder) {
314
- return val.isUndef () ? svalBuilder.makeZeroArrayIndex () : val;
315
- }
316
-
317
- // Scale a base value by a scaling factor, and return the scaled
318
- // value as an SVal. Used by 'computeOffset'.
319
- static inline SVal scaleValue (ProgramStateRef state,
320
- NonLoc baseVal, CharUnits scaling,
321
- SValBuilder &sb) {
322
- return sb.evalBinOpNN (state, BO_Mul, baseVal,
323
- sb.makeArrayIndex (scaling.getQuantity ()),
324
- sb.getArrayIndexType ());
325
- }
326
-
327
- // Add an SVal to another, treating unknown and undefined values as
328
- // summing to UnknownVal. Used by 'computeOffset'.
329
- static SVal addValue (ProgramStateRef state, SVal x, SVal y,
330
- SValBuilder &svalBuilder) {
331
- // We treat UnknownVals and UndefinedVals the same here because we
332
- // only care about computing offsets.
333
- if (x.isUnknownOrUndef () || y.isUnknownOrUndef ())
334
- return UnknownVal ();
335
-
336
- return svalBuilder.evalBinOpNN (state, BO_Add, x.castAs <NonLoc>(),
337
- y.castAs <NonLoc>(),
338
- svalBuilder.getArrayIndexType ());
339
- }
340
-
341
- // / Compute a raw byte offset from a base region. Used for array bounds
342
- // / checking.
343
- RegionRawOffsetV2 RegionRawOffsetV2::computeOffset (ProgramStateRef state,
344
- SValBuilder &svalBuilder,
345
- SVal location)
346
- {
347
- const MemRegion *region = location.getAsRegion ();
348
- SVal offset = UndefinedVal ();
349
-
350
- while (region) {
351
- switch (region->getKind ()) {
352
- default : {
353
- if (const SubRegion *subReg = dyn_cast<SubRegion>(region)) {
354
- if (auto Offset = getValue (offset, svalBuilder).getAs <NonLoc>())
355
- return RegionRawOffsetV2 (subReg, *Offset);
356
- }
357
- return RegionRawOffsetV2 ();
358
- }
359
- case MemRegion::ElementRegionKind: {
360
- const ElementRegion *elemReg = cast<ElementRegion>(region);
361
- SVal index = elemReg->getIndex ();
362
- if (!isa<NonLoc>(index))
363
- return RegionRawOffsetV2 ();
364
- QualType elemType = elemReg->getElementType ();
306
+ // / For a given Location that can be represented as a symbolic expression
307
+ // / Arr[Idx] (or perhaps Arr[Idx1][Idx2] etc.), return the parent memory block
308
+ // / Arr and the distance of Location from the beginning of Arr (expressed in a
309
+ // / NonLoc that specifies the number of CharUnits). Returns nullopt when these
310
+ // / cannot be determined.
311
+ std::optional<RegionRawOffsetV2>
312
+ RegionRawOffsetV2::computeOffset (ProgramStateRef State, SValBuilder &SVB,
313
+ SVal Location) {
314
+ QualType T = SVB.getArrayIndexType ();
315
+ auto Calc = [&SVB, State, T](BinaryOperatorKind Op, NonLoc LHS, NonLoc RHS) {
316
+ // We will use this utility to add and multiply values.
317
+ return SVB.evalBinOpNN (State, Op, LHS, RHS, T).getAs <NonLoc>();
318
+ };
319
+
320
+ const MemRegion *Region = Location.getAsRegion ();
321
+ NonLoc Offset = SVB.makeZeroArrayIndex ();
322
+
323
+ while (Region) {
324
+ if (const auto *ERegion = dyn_cast<ElementRegion>(Region)) {
325
+ if (const auto Index = ERegion->getIndex ().getAs <NonLoc>()) {
326
+ QualType ElemType = ERegion->getElementType ();
365
327
// If the element is an incomplete type, go no further.
366
- ASTContext &astContext = svalBuilder.getContext ();
367
- if (elemType->isIncompleteType ())
368
- return RegionRawOffsetV2 ();
369
-
370
- // Update the offset.
371
- offset = addValue (state,
372
- getValue (offset, svalBuilder),
373
- scaleValue (state,
374
- index.castAs <NonLoc>(),
375
- astContext.getTypeSizeInChars (elemType),
376
- svalBuilder),
377
- svalBuilder);
378
-
379
- if (offset.isUnknownOrUndef ())
380
- return RegionRawOffsetV2 ();
381
-
382
- region = elemReg->getSuperRegion ();
383
- continue ;
328
+ if (ElemType->isIncompleteType ())
329
+ return std::nullopt;
330
+
331
+ // Perform Offset += Index * sizeof(ElemType); then continue the offset
332
+ // calculations with SuperRegion:
333
+ NonLoc Size = SVB.makeArrayIndex (
334
+ SVB.getContext ().getTypeSizeInChars (ElemType).getQuantity ());
335
+ if (auto Delta = Calc (BO_Mul, *Index, Size)) {
336
+ if (auto NewOffset = Calc (BO_Add, Offset, *Delta)) {
337
+ Offset = *NewOffset;
338
+ Region = ERegion->getSuperRegion ();
339
+ continue ;
340
+ }
341
+ }
384
342
}
343
+ } else if (const auto *SRegion = dyn_cast<SubRegion>(Region)) {
344
+ // NOTE: The dyn_cast<>() is expected to succeed, it'd be very surprising
345
+ // to see a MemSpaceRegion at this point.
346
+ // FIXME: We may return with {<Region>, 0} even if we didn't handle any
347
+ // ElementRegion layers. I think that this behavior was introduced
348
+ // accidentally by 8a4c760c204546aba566e302f299f7ed2e00e287 in 2011, so
349
+ // it may be useful to review it in the future.
350
+ return RegionRawOffsetV2 (SRegion, Offset);
385
351
}
352
+ return std::nullopt;
386
353
}
387
- return RegionRawOffsetV2 () ;
354
+ return std::nullopt ;
388
355
}
389
356
390
357
void ento::registerArrayBoundCheckerV2 (CheckerManager &mgr) {
0 commit comments