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 "ClangSACheckers.h"
16 #include "clang/StaticAnalyzer/Core/Checker.h"
17 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
18 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
19 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
20 #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
21 #include "clang/AST/CharUnits.h"
23 using namespace clang;
27 class ArrayBoundCheckerV2 :
28 public Checker<check::Location> {
29 mutable llvm::OwningPtr<BuiltinBug> BT;
31 enum OOB_Kind { OOB_Precedes, OOB_Excedes };
33 void reportOOB(CheckerContext &C, const ProgramState *errorState,
37 void checkLocation(SVal l, bool isLoad, const Stmt*S,
38 CheckerContext &C) const;
41 // FIXME: Eventually replace RegionRawOffset with this class.
42 class RegionRawOffsetV2 {
44 const SubRegion *baseRegion;
48 : baseRegion(0), byteOffset(UnknownVal()) {}
51 RegionRawOffsetV2(const SubRegion* base, SVal offset)
52 : baseRegion(base), byteOffset(offset) {}
54 NonLoc getByteOffset() const { return cast<NonLoc>(byteOffset); }
55 const SubRegion *getRegion() const { return baseRegion; }
57 static RegionRawOffsetV2 computeOffset(const ProgramState *state,
58 SValBuilder &svalBuilder,
62 void dumpToStream(raw_ostream &os) const;
66 static SVal computeExtentBegin(SValBuilder &svalBuilder,
67 const MemRegion *region) {
69 switch (region->getKind()) {
71 return svalBuilder.makeZeroArrayIndex();
72 case MemRegion::SymbolicRegionKind:
73 // FIXME: improve this later by tracking symbolic lower bounds
74 // for symbolic regions.
76 case MemRegion::ElementRegionKind:
77 region = cast<SubRegion>(region)->getSuperRegion();
82 void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad,
84 CheckerContext &checkerContext) const {
86 // NOTE: Instead of using ProgramState::assumeInBound(), we are prototyping
87 // some new logic here that reasons directly about memory region extents.
88 // Once that logic is more mature, we can bring it back to assumeInBound()
89 // for all clients to use.
91 // The algorithm we are using here for bounds checking is to see if the
92 // memory access is within the extent of the base region. Since we
93 // have some flexibility in defining the base region, we can achieve
94 // various levels of conservatism in our buffer overflow checking.
95 const ProgramState *state = checkerContext.getState();
96 const ProgramState *originalState = state;
98 SValBuilder &svalBuilder = checkerContext.getSValBuilder();
99 const RegionRawOffsetV2 &rawOffset =
100 RegionRawOffsetV2::computeOffset(state, svalBuilder, location);
102 if (!rawOffset.getRegion())
105 // CHECK LOWER BOUND: Is byteOffset < extent begin?
106 // If so, we are doing a load/store
107 // before the first valid offset in the memory region.
109 SVal extentBegin = computeExtentBegin(svalBuilder, rawOffset.getRegion());
111 if (isa<NonLoc>(extentBegin)) {
113 = svalBuilder.evalBinOpNN(state, BO_LT, rawOffset.getByteOffset(),
114 cast<NonLoc>(extentBegin),
115 svalBuilder.getConditionType());
117 NonLoc *lowerBoundToCheck = dyn_cast<NonLoc>(&lowerBound);
118 if (!lowerBoundToCheck)
121 const ProgramState *state_precedesLowerBound, *state_withinLowerBound;
122 llvm::tie(state_precedesLowerBound, state_withinLowerBound) =
123 state->assume(*lowerBoundToCheck);
125 // Are we constrained enough to definitely precede the lower bound?
126 if (state_precedesLowerBound && !state_withinLowerBound) {
127 reportOOB(checkerContext, state_precedesLowerBound, OOB_Precedes);
131 // Otherwise, assume the constraint of the lower bound.
132 assert(state_withinLowerBound);
133 state = state_withinLowerBound;
137 // CHECK UPPER BOUND: Is byteOffset >= extent(baseRegion)? If so,
138 // we are doing a load/store after the last valid offset.
139 DefinedOrUnknownSVal extentVal =
140 rawOffset.getRegion()->getExtent(svalBuilder);
141 if (!isa<NonLoc>(extentVal))
145 = svalBuilder.evalBinOpNN(state, BO_GE, rawOffset.getByteOffset(),
146 cast<NonLoc>(extentVal),
147 svalBuilder.getConditionType());
149 NonLoc *upperboundToCheck = dyn_cast<NonLoc>(&upperbound);
150 if (!upperboundToCheck)
153 const ProgramState *state_exceedsUpperBound, *state_withinUpperBound;
154 llvm::tie(state_exceedsUpperBound, state_withinUpperBound) =
155 state->assume(*upperboundToCheck);
157 // Are we constrained enough to definitely exceed the upper bound?
158 if (state_exceedsUpperBound && !state_withinUpperBound) {
159 reportOOB(checkerContext, state_exceedsUpperBound, OOB_Excedes);
163 assert(state_withinUpperBound);
164 state = state_withinUpperBound;
168 if (state != originalState)
169 checkerContext.generateNode(state);
172 void ArrayBoundCheckerV2::reportOOB(CheckerContext &checkerContext,
173 const ProgramState *errorState,
174 OOB_Kind kind) const {
176 ExplodedNode *errorNode = checkerContext.generateSink(errorState);
181 BT.reset(new BuiltinBug("Out-of-bound access"));
183 // FIXME: This diagnostics are preliminary. We should get far better
184 // diagnostics for explaining buffer overruns.
186 llvm::SmallString<256> buf;
187 llvm::raw_svector_ostream os(buf);
188 os << "Out of bound memory access "
189 << (kind == OOB_Precedes ? "(accessed memory precedes memory block)"
190 : "(access exceeds upper limit of memory block)");
192 checkerContext.EmitReport(new BugReport(*BT, os.str(), errorNode));
195 void RegionRawOffsetV2::dump() const {
196 dumpToStream(llvm::errs());
199 void RegionRawOffsetV2::dumpToStream(raw_ostream &os) const {
200 os << "raw_offset_v2{" << getRegion() << ',' << getByteOffset() << '}';
203 // FIXME: Merge with the implementation of the same method in Store.cpp
204 static bool IsCompleteType(ASTContext &Ctx, QualType Ty) {
205 if (const RecordType *RT = Ty->getAs<RecordType>()) {
206 const RecordDecl *D = RT->getDecl();
207 if (!D->getDefinition())
215 // Lazily computes a value to be used by 'computeOffset'. If 'val'
216 // is unknown or undefined, we lazily substitute '0'. Otherwise,
218 static inline SVal getValue(SVal val, SValBuilder &svalBuilder) {
219 return isa<UndefinedVal>(val) ? svalBuilder.makeArrayIndex(0) : val;
222 // Scale a base value by a scaling factor, and return the scaled
223 // value as an SVal. Used by 'computeOffset'.
224 static inline SVal scaleValue(const ProgramState *state,
225 NonLoc baseVal, CharUnits scaling,
227 return sb.evalBinOpNN(state, BO_Mul, baseVal,
228 sb.makeArrayIndex(scaling.getQuantity()),
229 sb.getArrayIndexType());
232 // Add an SVal to another, treating unknown and undefined values as
233 // summing to UnknownVal. Used by 'computeOffset'.
234 static SVal addValue(const ProgramState *state, SVal x, SVal y,
235 SValBuilder &svalBuilder) {
236 // We treat UnknownVals and UndefinedVals the same here because we
237 // only care about computing offsets.
238 if (x.isUnknownOrUndef() || y.isUnknownOrUndef())
241 return svalBuilder.evalBinOpNN(state, BO_Add,
242 cast<NonLoc>(x), cast<NonLoc>(y),
243 svalBuilder.getArrayIndexType());
246 /// Compute a raw byte offset from a base region. Used for array bounds
248 RegionRawOffsetV2 RegionRawOffsetV2::computeOffset(const ProgramState *state,
249 SValBuilder &svalBuilder,
252 const MemRegion *region = location.getAsRegion();
253 SVal offset = UndefinedVal();
256 switch (region->getKind()) {
258 if (const SubRegion *subReg = dyn_cast<SubRegion>(region)) {
259 offset = getValue(offset, svalBuilder);
260 if (!offset.isUnknownOrUndef())
261 return RegionRawOffsetV2(subReg, offset);
263 return RegionRawOffsetV2();
265 case MemRegion::ElementRegionKind: {
266 const ElementRegion *elemReg = cast<ElementRegion>(region);
267 SVal index = elemReg->getIndex();
268 if (!isa<NonLoc>(index))
269 return RegionRawOffsetV2();
270 QualType elemType = elemReg->getElementType();
271 // If the element is an incomplete type, go no further.
272 ASTContext &astContext = svalBuilder.getContext();
273 if (!IsCompleteType(astContext, elemType))
274 return RegionRawOffsetV2();
276 // Update the offset.
277 offset = addValue(state,
278 getValue(offset, svalBuilder),
281 astContext.getTypeSizeInChars(elemType),
285 if (offset.isUnknownOrUndef())
286 return RegionRawOffsetV2();
288 region = elemReg->getSuperRegion();
293 return RegionRawOffsetV2();
297 void ento::registerArrayBoundCheckerV2(CheckerManager &mgr) {
298 mgr.registerChecker<ArrayBoundCheckerV2>();