xref: /minix3/external/bsd/llvm/dist/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
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