1 //== ArrayBoundCheckerV2.cpp ------------------------------------*- C++ -*--==//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // This file defines ArrayBoundCheckerV2, which is a path-sensitive check
11 // which looks for an out-of-bound array element access.
13 //===----------------------------------------------------------------------===//
15 #include "ExprEngineInternalChecks.h"
16 #include "clang/StaticAnalyzer/BugReporter/BugType.h"
17 #include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h"
18 #include "clang/StaticAnalyzer/PathSensitive/ExprEngine.h"
19 #include "clang/AST/CharUnits.h"
21 using namespace clang
;
25 class ArrayBoundCheckerV2
:
26 public CheckerVisitor
<ArrayBoundCheckerV2
> {
29 enum OOB_Kind
{ OOB_Precedes
, OOB_Excedes
};
31 void reportOOB(CheckerContext
&C
, const GRState
*errorState
,
35 ArrayBoundCheckerV2() : BT(0) {}
36 static void *getTag() { static int x
= 0; return &x
; }
37 void visitLocation(CheckerContext
&C
, const Stmt
*S
, SVal l
);
40 // FIXME: Eventually replace RegionRawOffset with this class.
41 class RegionRawOffsetV2
{
43 const SubRegion
*baseRegion
;
47 : baseRegion(0), byteOffset(UnknownVal()) {}
50 RegionRawOffsetV2(const SubRegion
* base
, SVal offset
)
51 : baseRegion(base
), byteOffset(offset
) {}
53 NonLoc
getByteOffset() const { return cast
<NonLoc
>(byteOffset
); }
54 const SubRegion
*getRegion() const { return baseRegion
; }
56 static RegionRawOffsetV2
computeOffset(const GRState
*state
,
57 SValBuilder
&svalBuilder
,
61 void dumpToStream(llvm::raw_ostream
& os
) const;
65 void ento::RegisterArrayBoundCheckerV2(ExprEngine
&Eng
) {
66 Eng
.registerCheck(new ArrayBoundCheckerV2());
69 void ArrayBoundCheckerV2::visitLocation(CheckerContext
&checkerContext
,
73 // NOTE: Instead of using GRState::assumeInBound(), we are prototyping
74 // some new logic here that reasons directly about memory region extents.
75 // Once that logic is more mature, we can bring it back to assumeInBound()
76 // for all clients to use.
78 // The algorithm we are using here for bounds checking is to see if the
79 // memory access is within the extent of the base region. Since we
80 // have some flexibility in defining the base region, we can achieve
81 // various levels of conservatism in our buffer overflow checking.
82 const GRState
*state
= checkerContext
.getState();
83 const GRState
*originalState
= state
;
85 SValBuilder
&svalBuilder
= checkerContext
.getSValBuilder();
86 const RegionRawOffsetV2
&rawOffset
=
87 RegionRawOffsetV2::computeOffset(state
, svalBuilder
, location
);
89 if (!rawOffset
.getRegion())
92 // CHECK LOWER BOUND: Is byteOffset < 0? If so, we are doing a load/store
93 // before the first valid offset in the memory region.
96 = svalBuilder
.evalBinOpNN(state
, BO_LT
, rawOffset
.getByteOffset(),
97 svalBuilder
.makeZeroArrayIndex(),
98 svalBuilder
.getConditionType());
100 NonLoc
*lowerBoundToCheck
= dyn_cast
<NonLoc
>(&lowerBound
);
101 if (!lowerBoundToCheck
)
104 const GRState
*state_precedesLowerBound
, *state_withinLowerBound
;
105 llvm::tie(state_precedesLowerBound
, state_withinLowerBound
) =
106 state
->assume(*lowerBoundToCheck
);
108 // Are we constrained enough to definitely precede the lower bound?
109 if (state_precedesLowerBound
&& !state_withinLowerBound
) {
110 reportOOB(checkerContext
, state_precedesLowerBound
, OOB_Precedes
);
114 // Otherwise, assume the constraint of the lower bound.
115 assert(state_withinLowerBound
);
116 state
= state_withinLowerBound
;
119 // CHECK UPPER BOUND: Is byteOffset >= extent(baseRegion)? If so,
120 // we are doing a load/store after the last valid offset.
121 DefinedOrUnknownSVal extentVal
=
122 rawOffset
.getRegion()->getExtent(svalBuilder
);
123 if (!isa
<NonLoc
>(extentVal
))
127 = svalBuilder
.evalBinOpNN(state
, BO_GE
, rawOffset
.getByteOffset(),
128 cast
<NonLoc
>(extentVal
),
129 svalBuilder
.getConditionType());
131 NonLoc
*upperboundToCheck
= dyn_cast
<NonLoc
>(&upperbound
);
132 if (!upperboundToCheck
)
135 const GRState
*state_exceedsUpperBound
, *state_withinUpperBound
;
136 llvm::tie(state_exceedsUpperBound
, state_withinUpperBound
) =
137 state
->assume(*upperboundToCheck
);
139 // Are we constrained enough to definitely exceed the upper bound?
140 if (state_exceedsUpperBound
&& !state_withinUpperBound
) {
141 reportOOB(checkerContext
, state_exceedsUpperBound
, OOB_Excedes
);
145 assert(state_withinUpperBound
);
146 state
= state_withinUpperBound
;
150 if (state
!= originalState
)
151 checkerContext
.generateNode(state
);
154 void ArrayBoundCheckerV2::reportOOB(CheckerContext
&checkerContext
,
155 const GRState
*errorState
,
158 ExplodedNode
*errorNode
= checkerContext
.generateSink(errorState
);
163 BT
= new BuiltinBug("Out-of-bound access");
165 // FIXME: This diagnostics are preliminary. We should get far better
166 // diagnostics for explaining buffer overruns.
168 llvm::SmallString
<256> buf
;
169 llvm::raw_svector_ostream
os(buf
);
170 os
<< "Out of bound memory access "
171 << (kind
== OOB_Precedes
? "(accessed memory precedes memory block)"
172 : "(access exceeds upper limit of memory block)");
174 checkerContext
.EmitReport(new RangedBugReport(*BT
, os
.str(), errorNode
));
177 void RegionRawOffsetV2::dump() const {
178 dumpToStream(llvm::errs());
181 void RegionRawOffsetV2::dumpToStream(llvm::raw_ostream
& os
) const {
182 os
<< "raw_offset_v2{" << getRegion() << ',' << getByteOffset() << '}';
185 // FIXME: Merge with the implementation of the same method in Store.cpp
186 static bool IsCompleteType(ASTContext
&Ctx
, QualType Ty
) {
187 if (const RecordType
*RT
= Ty
->getAs
<RecordType
>()) {
188 const RecordDecl
*D
= RT
->getDecl();
189 if (!D
->getDefinition())
197 // Lazily computes a value to be used by 'computeOffset'. If 'val'
198 // is unknown or undefined, we lazily substitute '0'. Otherwise,
200 static inline SVal
getValue(SVal val
, SValBuilder
&svalBuilder
) {
201 return isa
<UndefinedVal
>(val
) ? svalBuilder
.makeArrayIndex(0) : val
;
204 // Scale a base value by a scaling factor, and return the scaled
205 // value as an SVal. Used by 'computeOffset'.
206 static inline SVal
scaleValue(const GRState
*state
,
207 NonLoc baseVal
, CharUnits scaling
,
209 return sb
.evalBinOpNN(state
, BO_Mul
, baseVal
,
210 sb
.makeArrayIndex(scaling
.getQuantity()),
211 sb
.getArrayIndexType());
214 // Add an SVal to another, treating unknown and undefined values as
215 // summing to UnknownVal. Used by 'computeOffset'.
216 static SVal
addValue(const GRState
*state
, SVal x
, SVal y
,
217 SValBuilder
&svalBuilder
) {
218 // We treat UnknownVals and UndefinedVals the same here because we
219 // only care about computing offsets.
220 if (x
.isUnknownOrUndef() || y
.isUnknownOrUndef())
223 return svalBuilder
.evalBinOpNN(state
, BO_Add
,
224 cast
<NonLoc
>(x
), cast
<NonLoc
>(y
),
225 svalBuilder
.getArrayIndexType());
228 /// Compute a raw byte offset from a base region. Used for array bounds
230 RegionRawOffsetV2
RegionRawOffsetV2::computeOffset(const GRState
*state
,
231 SValBuilder
&svalBuilder
,
234 const MemRegion
*region
= location
.getAsRegion();
235 SVal offset
= UndefinedVal();
238 switch (region
->getKind()) {
240 if (const SubRegion
*subReg
= dyn_cast
<SubRegion
>(region
))
241 if (!offset
.isUnknownOrUndef())
242 return RegionRawOffsetV2(subReg
, offset
);
243 return RegionRawOffsetV2();
245 case MemRegion::ElementRegionKind
: {
246 const ElementRegion
*elemReg
= cast
<ElementRegion
>(region
);
247 SVal index
= elemReg
->getIndex();
248 if (!isa
<NonLoc
>(index
))
249 return RegionRawOffsetV2();
250 QualType elemType
= elemReg
->getElementType();
251 // If the element is an incomplete type, go no further.
252 ASTContext
&astContext
= svalBuilder
.getContext();
253 if (!IsCompleteType(astContext
, elemType
))
254 return RegionRawOffsetV2();
256 // Update the offset.
257 offset
= addValue(state
,
258 getValue(offset
, svalBuilder
),
261 astContext
.getTypeSizeInChars(elemType
),
265 if (offset
.isUnknownOrUndef())
266 return RegionRawOffsetV2();
268 region
= elemReg
->getSuperRegion();
273 return RegionRawOffsetV2();