1f4a2713aSLionel Sambuc //== ArrayBoundCheckerV2.cpp ------------------------------------*- C++ -*--==//
2f4a2713aSLionel Sambuc //
3f4a2713aSLionel Sambuc // The LLVM Compiler Infrastructure
4f4a2713aSLionel Sambuc //
5f4a2713aSLionel Sambuc // This file is distributed under the University of Illinois Open Source
6f4a2713aSLionel Sambuc // License. See LICENSE.TXT for details.
7f4a2713aSLionel Sambuc //
8f4a2713aSLionel Sambuc //===----------------------------------------------------------------------===//
9f4a2713aSLionel Sambuc //
10f4a2713aSLionel Sambuc // This file defines ArrayBoundCheckerV2, which is a path-sensitive check
11f4a2713aSLionel Sambuc // which looks for an out-of-bound array element access.
12f4a2713aSLionel Sambuc //
13f4a2713aSLionel Sambuc //===----------------------------------------------------------------------===//
14f4a2713aSLionel Sambuc
15f4a2713aSLionel Sambuc #include "ClangSACheckers.h"
16f4a2713aSLionel Sambuc #include "clang/AST/CharUnits.h"
17f4a2713aSLionel Sambuc #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
18f4a2713aSLionel Sambuc #include "clang/StaticAnalyzer/Core/Checker.h"
19f4a2713aSLionel Sambuc #include "clang/StaticAnalyzer/Core/CheckerManager.h"
20f4a2713aSLionel Sambuc #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
21f4a2713aSLionel Sambuc #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
22f4a2713aSLionel Sambuc #include "llvm/ADT/SmallString.h"
23f4a2713aSLionel Sambuc #include "llvm/Support/raw_ostream.h"
24f4a2713aSLionel Sambuc
25f4a2713aSLionel Sambuc using namespace clang;
26f4a2713aSLionel Sambuc using namespace ento;
27f4a2713aSLionel Sambuc
28f4a2713aSLionel Sambuc namespace {
29f4a2713aSLionel Sambuc class ArrayBoundCheckerV2 :
30f4a2713aSLionel Sambuc public Checker<check::Location> {
31*0a6a1f1dSLionel Sambuc mutable std::unique_ptr<BuiltinBug> BT;
32f4a2713aSLionel Sambuc
33f4a2713aSLionel Sambuc enum OOB_Kind { OOB_Precedes, OOB_Excedes, OOB_Tainted };
34f4a2713aSLionel Sambuc
35f4a2713aSLionel Sambuc void reportOOB(CheckerContext &C, ProgramStateRef errorState,
36f4a2713aSLionel Sambuc OOB_Kind kind) const;
37f4a2713aSLionel Sambuc
38f4a2713aSLionel Sambuc public:
39f4a2713aSLionel Sambuc void checkLocation(SVal l, bool isLoad, const Stmt*S,
40f4a2713aSLionel Sambuc CheckerContext &C) const;
41f4a2713aSLionel Sambuc };
42f4a2713aSLionel Sambuc
43f4a2713aSLionel Sambuc // FIXME: Eventually replace RegionRawOffset with this class.
44f4a2713aSLionel Sambuc class RegionRawOffsetV2 {
45f4a2713aSLionel Sambuc private:
46f4a2713aSLionel Sambuc const SubRegion *baseRegion;
47f4a2713aSLionel Sambuc SVal byteOffset;
48f4a2713aSLionel Sambuc
RegionRawOffsetV2()49f4a2713aSLionel Sambuc RegionRawOffsetV2()
50*0a6a1f1dSLionel Sambuc : baseRegion(nullptr), byteOffset(UnknownVal()) {}
51f4a2713aSLionel Sambuc
52f4a2713aSLionel Sambuc public:
RegionRawOffsetV2(const SubRegion * base,SVal offset)53f4a2713aSLionel Sambuc RegionRawOffsetV2(const SubRegion* base, SVal offset)
54f4a2713aSLionel Sambuc : baseRegion(base), byteOffset(offset) {}
55f4a2713aSLionel Sambuc
getByteOffset() const56f4a2713aSLionel Sambuc NonLoc getByteOffset() const { return byteOffset.castAs<NonLoc>(); }
getRegion() const57f4a2713aSLionel Sambuc const SubRegion *getRegion() const { return baseRegion; }
58f4a2713aSLionel Sambuc
59f4a2713aSLionel Sambuc static RegionRawOffsetV2 computeOffset(ProgramStateRef state,
60f4a2713aSLionel Sambuc SValBuilder &svalBuilder,
61f4a2713aSLionel Sambuc SVal location);
62f4a2713aSLionel Sambuc
63f4a2713aSLionel Sambuc void dump() const;
64f4a2713aSLionel Sambuc void dumpToStream(raw_ostream &os) const;
65f4a2713aSLionel Sambuc };
66f4a2713aSLionel Sambuc }
67f4a2713aSLionel Sambuc
computeExtentBegin(SValBuilder & svalBuilder,const MemRegion * region)68f4a2713aSLionel Sambuc static SVal computeExtentBegin(SValBuilder &svalBuilder,
69f4a2713aSLionel Sambuc const MemRegion *region) {
70f4a2713aSLionel Sambuc while (true)
71f4a2713aSLionel Sambuc switch (region->getKind()) {
72f4a2713aSLionel Sambuc default:
73f4a2713aSLionel Sambuc return svalBuilder.makeZeroArrayIndex();
74f4a2713aSLionel Sambuc case MemRegion::SymbolicRegionKind:
75f4a2713aSLionel Sambuc // FIXME: improve this later by tracking symbolic lower bounds
76f4a2713aSLionel Sambuc // for symbolic regions.
77f4a2713aSLionel Sambuc return UnknownVal();
78f4a2713aSLionel Sambuc case MemRegion::ElementRegionKind:
79f4a2713aSLionel Sambuc region = cast<SubRegion>(region)->getSuperRegion();
80f4a2713aSLionel Sambuc continue;
81f4a2713aSLionel Sambuc }
82f4a2713aSLionel Sambuc }
83f4a2713aSLionel Sambuc
checkLocation(SVal location,bool isLoad,const Stmt * LoadS,CheckerContext & checkerContext) const84f4a2713aSLionel Sambuc void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad,
85f4a2713aSLionel Sambuc const Stmt* LoadS,
86f4a2713aSLionel Sambuc CheckerContext &checkerContext) const {
87f4a2713aSLionel Sambuc
88f4a2713aSLionel Sambuc // NOTE: Instead of using ProgramState::assumeInBound(), we are prototyping
89f4a2713aSLionel Sambuc // some new logic here that reasons directly about memory region extents.
90f4a2713aSLionel Sambuc // Once that logic is more mature, we can bring it back to assumeInBound()
91f4a2713aSLionel Sambuc // for all clients to use.
92f4a2713aSLionel Sambuc //
93f4a2713aSLionel Sambuc // The algorithm we are using here for bounds checking is to see if the
94f4a2713aSLionel Sambuc // memory access is within the extent of the base region. Since we
95f4a2713aSLionel Sambuc // have some flexibility in defining the base region, we can achieve
96f4a2713aSLionel Sambuc // various levels of conservatism in our buffer overflow checking.
97f4a2713aSLionel Sambuc ProgramStateRef state = checkerContext.getState();
98f4a2713aSLionel Sambuc ProgramStateRef originalState = state;
99f4a2713aSLionel Sambuc
100f4a2713aSLionel Sambuc SValBuilder &svalBuilder = checkerContext.getSValBuilder();
101f4a2713aSLionel Sambuc const RegionRawOffsetV2 &rawOffset =
102f4a2713aSLionel Sambuc RegionRawOffsetV2::computeOffset(state, svalBuilder, location);
103f4a2713aSLionel Sambuc
104f4a2713aSLionel Sambuc if (!rawOffset.getRegion())
105f4a2713aSLionel Sambuc return;
106f4a2713aSLionel Sambuc
107f4a2713aSLionel Sambuc // CHECK LOWER BOUND: Is byteOffset < extent begin?
108f4a2713aSLionel Sambuc // If so, we are doing a load/store
109f4a2713aSLionel Sambuc // before the first valid offset in the memory region.
110f4a2713aSLionel Sambuc
111f4a2713aSLionel Sambuc SVal extentBegin = computeExtentBegin(svalBuilder, rawOffset.getRegion());
112f4a2713aSLionel Sambuc
113f4a2713aSLionel Sambuc if (Optional<NonLoc> NV = extentBegin.getAs<NonLoc>()) {
114f4a2713aSLionel Sambuc SVal lowerBound =
115f4a2713aSLionel Sambuc svalBuilder.evalBinOpNN(state, BO_LT, rawOffset.getByteOffset(), *NV,
116f4a2713aSLionel Sambuc svalBuilder.getConditionType());
117f4a2713aSLionel Sambuc
118f4a2713aSLionel Sambuc Optional<NonLoc> lowerBoundToCheck = lowerBound.getAs<NonLoc>();
119f4a2713aSLionel Sambuc if (!lowerBoundToCheck)
120f4a2713aSLionel Sambuc return;
121f4a2713aSLionel Sambuc
122f4a2713aSLionel Sambuc ProgramStateRef state_precedesLowerBound, state_withinLowerBound;
123*0a6a1f1dSLionel Sambuc std::tie(state_precedesLowerBound, state_withinLowerBound) =
124f4a2713aSLionel Sambuc state->assume(*lowerBoundToCheck);
125f4a2713aSLionel Sambuc
126f4a2713aSLionel Sambuc // Are we constrained enough to definitely precede the lower bound?
127f4a2713aSLionel Sambuc if (state_precedesLowerBound && !state_withinLowerBound) {
128f4a2713aSLionel Sambuc reportOOB(checkerContext, state_precedesLowerBound, OOB_Precedes);
129f4a2713aSLionel Sambuc return;
130f4a2713aSLionel Sambuc }
131f4a2713aSLionel Sambuc
132f4a2713aSLionel Sambuc // Otherwise, assume the constraint of the lower bound.
133f4a2713aSLionel Sambuc assert(state_withinLowerBound);
134f4a2713aSLionel Sambuc state = state_withinLowerBound;
135f4a2713aSLionel Sambuc }
136f4a2713aSLionel Sambuc
137f4a2713aSLionel Sambuc do {
138f4a2713aSLionel Sambuc // CHECK UPPER BOUND: Is byteOffset >= extent(baseRegion)? If so,
139f4a2713aSLionel Sambuc // we are doing a load/store after the last valid offset.
140f4a2713aSLionel Sambuc DefinedOrUnknownSVal extentVal =
141f4a2713aSLionel Sambuc rawOffset.getRegion()->getExtent(svalBuilder);
142f4a2713aSLionel Sambuc if (!extentVal.getAs<NonLoc>())
143f4a2713aSLionel Sambuc break;
144f4a2713aSLionel Sambuc
145f4a2713aSLionel Sambuc SVal upperbound
146f4a2713aSLionel Sambuc = svalBuilder.evalBinOpNN(state, BO_GE, rawOffset.getByteOffset(),
147f4a2713aSLionel Sambuc extentVal.castAs<NonLoc>(),
148f4a2713aSLionel Sambuc svalBuilder.getConditionType());
149f4a2713aSLionel Sambuc
150f4a2713aSLionel Sambuc Optional<NonLoc> upperboundToCheck = upperbound.getAs<NonLoc>();
151f4a2713aSLionel Sambuc if (!upperboundToCheck)
152f4a2713aSLionel Sambuc break;
153f4a2713aSLionel Sambuc
154f4a2713aSLionel Sambuc ProgramStateRef state_exceedsUpperBound, state_withinUpperBound;
155*0a6a1f1dSLionel Sambuc std::tie(state_exceedsUpperBound, state_withinUpperBound) =
156f4a2713aSLionel Sambuc state->assume(*upperboundToCheck);
157f4a2713aSLionel Sambuc
158f4a2713aSLionel Sambuc // If we are under constrained and the index variables are tainted, report.
159f4a2713aSLionel Sambuc if (state_exceedsUpperBound && state_withinUpperBound) {
160f4a2713aSLionel Sambuc if (state->isTainted(rawOffset.getByteOffset()))
161f4a2713aSLionel Sambuc reportOOB(checkerContext, state_exceedsUpperBound, OOB_Tainted);
162f4a2713aSLionel Sambuc return;
163f4a2713aSLionel Sambuc }
164f4a2713aSLionel Sambuc
165f4a2713aSLionel Sambuc // If we are constrained enough to definitely exceed the upper bound, report.
166f4a2713aSLionel Sambuc if (state_exceedsUpperBound) {
167f4a2713aSLionel Sambuc assert(!state_withinUpperBound);
168f4a2713aSLionel Sambuc reportOOB(checkerContext, state_exceedsUpperBound, OOB_Excedes);
169f4a2713aSLionel Sambuc return;
170f4a2713aSLionel Sambuc }
171f4a2713aSLionel Sambuc
172f4a2713aSLionel Sambuc assert(state_withinUpperBound);
173f4a2713aSLionel Sambuc state = state_withinUpperBound;
174f4a2713aSLionel Sambuc }
175f4a2713aSLionel Sambuc while (false);
176f4a2713aSLionel Sambuc
177f4a2713aSLionel Sambuc if (state != originalState)
178f4a2713aSLionel Sambuc checkerContext.addTransition(state);
179f4a2713aSLionel Sambuc }
180f4a2713aSLionel Sambuc
reportOOB(CheckerContext & checkerContext,ProgramStateRef errorState,OOB_Kind kind) const181f4a2713aSLionel Sambuc void ArrayBoundCheckerV2::reportOOB(CheckerContext &checkerContext,
182f4a2713aSLionel Sambuc ProgramStateRef errorState,
183f4a2713aSLionel Sambuc OOB_Kind kind) const {
184f4a2713aSLionel Sambuc
185f4a2713aSLionel Sambuc ExplodedNode *errorNode = checkerContext.generateSink(errorState);
186f4a2713aSLionel Sambuc if (!errorNode)
187f4a2713aSLionel Sambuc return;
188f4a2713aSLionel Sambuc
189f4a2713aSLionel Sambuc if (!BT)
190*0a6a1f1dSLionel Sambuc BT.reset(new BuiltinBug(this, "Out-of-bound access"));
191f4a2713aSLionel Sambuc
192f4a2713aSLionel Sambuc // FIXME: This diagnostics are preliminary. We should get far better
193f4a2713aSLionel Sambuc // diagnostics for explaining buffer overruns.
194f4a2713aSLionel Sambuc
195f4a2713aSLionel Sambuc SmallString<256> buf;
196f4a2713aSLionel Sambuc llvm::raw_svector_ostream os(buf);
197f4a2713aSLionel Sambuc os << "Out of bound memory access ";
198f4a2713aSLionel Sambuc switch (kind) {
199f4a2713aSLionel Sambuc case OOB_Precedes:
200f4a2713aSLionel Sambuc os << "(accessed memory precedes memory block)";
201f4a2713aSLionel Sambuc break;
202f4a2713aSLionel Sambuc case OOB_Excedes:
203f4a2713aSLionel Sambuc os << "(access exceeds upper limit of memory block)";
204f4a2713aSLionel Sambuc break;
205f4a2713aSLionel Sambuc case OOB_Tainted:
206f4a2713aSLionel Sambuc os << "(index is tainted)";
207f4a2713aSLionel Sambuc break;
208f4a2713aSLionel Sambuc }
209f4a2713aSLionel Sambuc
210f4a2713aSLionel Sambuc checkerContext.emitReport(new BugReport(*BT, os.str(), errorNode));
211f4a2713aSLionel Sambuc }
212f4a2713aSLionel Sambuc
dump() const213f4a2713aSLionel Sambuc void RegionRawOffsetV2::dump() const {
214f4a2713aSLionel Sambuc dumpToStream(llvm::errs());
215f4a2713aSLionel Sambuc }
216f4a2713aSLionel Sambuc
dumpToStream(raw_ostream & os) const217f4a2713aSLionel Sambuc void RegionRawOffsetV2::dumpToStream(raw_ostream &os) const {
218f4a2713aSLionel Sambuc os << "raw_offset_v2{" << getRegion() << ',' << getByteOffset() << '}';
219f4a2713aSLionel Sambuc }
220f4a2713aSLionel Sambuc
221f4a2713aSLionel Sambuc
222f4a2713aSLionel Sambuc // Lazily computes a value to be used by 'computeOffset'. If 'val'
223f4a2713aSLionel Sambuc // is unknown or undefined, we lazily substitute '0'. Otherwise,
224f4a2713aSLionel Sambuc // return 'val'.
getValue(SVal val,SValBuilder & svalBuilder)225f4a2713aSLionel Sambuc static inline SVal getValue(SVal val, SValBuilder &svalBuilder) {
226f4a2713aSLionel Sambuc return val.getAs<UndefinedVal>() ? svalBuilder.makeArrayIndex(0) : val;
227f4a2713aSLionel Sambuc }
228f4a2713aSLionel Sambuc
229f4a2713aSLionel Sambuc // Scale a base value by a scaling factor, and return the scaled
230f4a2713aSLionel Sambuc // value as an SVal. Used by 'computeOffset'.
scaleValue(ProgramStateRef state,NonLoc baseVal,CharUnits scaling,SValBuilder & sb)231f4a2713aSLionel Sambuc static inline SVal scaleValue(ProgramStateRef state,
232f4a2713aSLionel Sambuc NonLoc baseVal, CharUnits scaling,
233f4a2713aSLionel Sambuc SValBuilder &sb) {
234f4a2713aSLionel Sambuc return sb.evalBinOpNN(state, BO_Mul, baseVal,
235f4a2713aSLionel Sambuc sb.makeArrayIndex(scaling.getQuantity()),
236f4a2713aSLionel Sambuc sb.getArrayIndexType());
237f4a2713aSLionel Sambuc }
238f4a2713aSLionel Sambuc
239f4a2713aSLionel Sambuc // Add an SVal to another, treating unknown and undefined values as
240f4a2713aSLionel Sambuc // summing to UnknownVal. Used by 'computeOffset'.
addValue(ProgramStateRef state,SVal x,SVal y,SValBuilder & svalBuilder)241f4a2713aSLionel Sambuc static SVal addValue(ProgramStateRef state, SVal x, SVal y,
242f4a2713aSLionel Sambuc SValBuilder &svalBuilder) {
243f4a2713aSLionel Sambuc // We treat UnknownVals and UndefinedVals the same here because we
244f4a2713aSLionel Sambuc // only care about computing offsets.
245f4a2713aSLionel Sambuc if (x.isUnknownOrUndef() || y.isUnknownOrUndef())
246f4a2713aSLionel Sambuc return UnknownVal();
247f4a2713aSLionel Sambuc
248f4a2713aSLionel Sambuc return svalBuilder.evalBinOpNN(state, BO_Add, x.castAs<NonLoc>(),
249f4a2713aSLionel Sambuc y.castAs<NonLoc>(),
250f4a2713aSLionel Sambuc svalBuilder.getArrayIndexType());
251f4a2713aSLionel Sambuc }
252f4a2713aSLionel Sambuc
253f4a2713aSLionel Sambuc /// Compute a raw byte offset from a base region. Used for array bounds
254f4a2713aSLionel Sambuc /// checking.
computeOffset(ProgramStateRef state,SValBuilder & svalBuilder,SVal location)255f4a2713aSLionel Sambuc RegionRawOffsetV2 RegionRawOffsetV2::computeOffset(ProgramStateRef state,
256f4a2713aSLionel Sambuc SValBuilder &svalBuilder,
257f4a2713aSLionel Sambuc SVal location)
258f4a2713aSLionel Sambuc {
259f4a2713aSLionel Sambuc const MemRegion *region = location.getAsRegion();
260f4a2713aSLionel Sambuc SVal offset = UndefinedVal();
261f4a2713aSLionel Sambuc
262f4a2713aSLionel Sambuc while (region) {
263f4a2713aSLionel Sambuc switch (region->getKind()) {
264f4a2713aSLionel Sambuc default: {
265f4a2713aSLionel Sambuc if (const SubRegion *subReg = dyn_cast<SubRegion>(region)) {
266f4a2713aSLionel Sambuc offset = getValue(offset, svalBuilder);
267f4a2713aSLionel Sambuc if (!offset.isUnknownOrUndef())
268f4a2713aSLionel Sambuc return RegionRawOffsetV2(subReg, offset);
269f4a2713aSLionel Sambuc }
270f4a2713aSLionel Sambuc return RegionRawOffsetV2();
271f4a2713aSLionel Sambuc }
272f4a2713aSLionel Sambuc case MemRegion::ElementRegionKind: {
273f4a2713aSLionel Sambuc const ElementRegion *elemReg = cast<ElementRegion>(region);
274f4a2713aSLionel Sambuc SVal index = elemReg->getIndex();
275f4a2713aSLionel Sambuc if (!index.getAs<NonLoc>())
276f4a2713aSLionel Sambuc return RegionRawOffsetV2();
277f4a2713aSLionel Sambuc QualType elemType = elemReg->getElementType();
278f4a2713aSLionel Sambuc // If the element is an incomplete type, go no further.
279f4a2713aSLionel Sambuc ASTContext &astContext = svalBuilder.getContext();
280*0a6a1f1dSLionel Sambuc if (elemType->isIncompleteType())
281f4a2713aSLionel Sambuc return RegionRawOffsetV2();
282f4a2713aSLionel Sambuc
283f4a2713aSLionel Sambuc // Update the offset.
284f4a2713aSLionel Sambuc offset = addValue(state,
285f4a2713aSLionel Sambuc getValue(offset, svalBuilder),
286f4a2713aSLionel Sambuc scaleValue(state,
287f4a2713aSLionel Sambuc index.castAs<NonLoc>(),
288f4a2713aSLionel Sambuc astContext.getTypeSizeInChars(elemType),
289f4a2713aSLionel Sambuc svalBuilder),
290f4a2713aSLionel Sambuc svalBuilder);
291f4a2713aSLionel Sambuc
292f4a2713aSLionel Sambuc if (offset.isUnknownOrUndef())
293f4a2713aSLionel Sambuc return RegionRawOffsetV2();
294f4a2713aSLionel Sambuc
295f4a2713aSLionel Sambuc region = elemReg->getSuperRegion();
296f4a2713aSLionel Sambuc continue;
297f4a2713aSLionel Sambuc }
298f4a2713aSLionel Sambuc }
299f4a2713aSLionel Sambuc }
300f4a2713aSLionel Sambuc return RegionRawOffsetV2();
301f4a2713aSLionel Sambuc }
302f4a2713aSLionel Sambuc
registerArrayBoundCheckerV2(CheckerManager & mgr)303f4a2713aSLionel Sambuc void ento::registerArrayBoundCheckerV2(CheckerManager &mgr) {
304f4a2713aSLionel Sambuc mgr.registerChecker<ArrayBoundCheckerV2>();
305f4a2713aSLionel Sambuc }
306