clang API Documentation

ArrayBoundCheckerV2.cpp
Go to the documentation of this file.
00001 //== ArrayBoundCheckerV2.cpp ------------------------------------*- C++ -*--==//
00002 //
00003 //                     The LLVM Compiler Infrastructure
00004 //
00005 // This file is distributed under the University of Illinois Open Source
00006 // License. See LICENSE.TXT for details.
00007 //
00008 //===----------------------------------------------------------------------===//
00009 //
00010 // This file defines ArrayBoundCheckerV2, which is a path-sensitive check
00011 // which looks for an out-of-bound array element access.
00012 //
00013 //===----------------------------------------------------------------------===//
00014 
00015 #include "ClangSACheckers.h"
00016 #include "clang/StaticAnalyzer/Core/Checker.h"
00017 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
00018 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
00019 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
00020 #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
00021 #include "clang/AST/CharUnits.h"
00022 #include "llvm/ADT/SmallString.h"
00023 #include "llvm/ADT/STLExtras.h"
00024 
00025 using namespace clang;
00026 using namespace ento;
00027 
00028 namespace {
00029 class ArrayBoundCheckerV2 : 
00030     public Checker<check::Location> {
00031   mutable OwningPtr<BuiltinBug> BT;
00032       
00033   enum OOB_Kind { OOB_Precedes, OOB_Excedes, OOB_Tainted };
00034   
00035   void reportOOB(CheckerContext &C, ProgramStateRef errorState,
00036                  OOB_Kind kind) const;
00037       
00038 public:
00039   void checkLocation(SVal l, bool isLoad, const Stmt*S,
00040                      CheckerContext &C) const;
00041 };
00042 
00043 // FIXME: Eventually replace RegionRawOffset with this class.
00044 class RegionRawOffsetV2 {
00045 private:
00046   const SubRegion *baseRegion;
00047   SVal byteOffset;
00048   
00049   RegionRawOffsetV2()
00050     : baseRegion(0), byteOffset(UnknownVal()) {}
00051 
00052 public:
00053   RegionRawOffsetV2(const SubRegion* base, SVal offset)
00054     : baseRegion(base), byteOffset(offset) {}
00055 
00056   NonLoc getByteOffset() const { return cast<NonLoc>(byteOffset); }
00057   const SubRegion *getRegion() const { return baseRegion; }
00058   
00059   static RegionRawOffsetV2 computeOffset(ProgramStateRef state,
00060                                          SValBuilder &svalBuilder,
00061                                          SVal location);
00062 
00063   void dump() const;
00064   void dumpToStream(raw_ostream &os) const;
00065 };
00066 }
00067 
00068 static SVal computeExtentBegin(SValBuilder &svalBuilder, 
00069                                const MemRegion *region) {
00070   while (true)
00071     switch (region->getKind()) {
00072       default:
00073         return svalBuilder.makeZeroArrayIndex();        
00074       case MemRegion::SymbolicRegionKind:
00075         // FIXME: improve this later by tracking symbolic lower bounds
00076         // for symbolic regions.
00077         return UnknownVal();
00078       case MemRegion::ElementRegionKind:
00079         region = cast<SubRegion>(region)->getSuperRegion();
00080         continue;
00081     }
00082 }
00083 
00084 void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad,
00085                                         const Stmt* LoadS,
00086                                         CheckerContext &checkerContext) const {
00087 
00088   // NOTE: Instead of using ProgramState::assumeInBound(), we are prototyping
00089   // some new logic here that reasons directly about memory region extents.
00090   // Once that logic is more mature, we can bring it back to assumeInBound()
00091   // for all clients to use.
00092   //
00093   // The algorithm we are using here for bounds checking is to see if the
00094   // memory access is within the extent of the base region.  Since we
00095   // have some flexibility in defining the base region, we can achieve
00096   // various levels of conservatism in our buffer overflow checking.
00097   ProgramStateRef state = checkerContext.getState();  
00098   ProgramStateRef originalState = state;
00099 
00100   SValBuilder &svalBuilder = checkerContext.getSValBuilder();
00101   const RegionRawOffsetV2 &rawOffset = 
00102     RegionRawOffsetV2::computeOffset(state, svalBuilder, location);
00103 
00104   if (!rawOffset.getRegion())
00105     return;
00106 
00107   // CHECK LOWER BOUND: Is byteOffset < extent begin?  
00108   //  If so, we are doing a load/store
00109   //  before the first valid offset in the memory region.
00110 
00111   SVal extentBegin = computeExtentBegin(svalBuilder, rawOffset.getRegion());
00112   
00113   if (isa<NonLoc>(extentBegin)) {
00114     SVal lowerBound
00115       = svalBuilder.evalBinOpNN(state, BO_LT, rawOffset.getByteOffset(),
00116                                 cast<NonLoc>(extentBegin),
00117                                 svalBuilder.getConditionType());
00118 
00119     NonLoc *lowerBoundToCheck = dyn_cast<NonLoc>(&lowerBound);
00120     if (!lowerBoundToCheck)
00121       return;
00122     
00123     ProgramStateRef state_precedesLowerBound, state_withinLowerBound;
00124     llvm::tie(state_precedesLowerBound, state_withinLowerBound) =
00125       state->assume(*lowerBoundToCheck);
00126 
00127     // Are we constrained enough to definitely precede the lower bound?
00128     if (state_precedesLowerBound && !state_withinLowerBound) {
00129       reportOOB(checkerContext, state_precedesLowerBound, OOB_Precedes);
00130       return;
00131     }
00132   
00133     // Otherwise, assume the constraint of the lower bound.
00134     assert(state_withinLowerBound);
00135     state = state_withinLowerBound;
00136   }
00137   
00138   do {
00139     // CHECK UPPER BOUND: Is byteOffset >= extent(baseRegion)?  If so,
00140     // we are doing a load/store after the last valid offset.
00141     DefinedOrUnknownSVal extentVal =
00142       rawOffset.getRegion()->getExtent(svalBuilder);
00143     if (!isa<NonLoc>(extentVal))
00144       break;
00145 
00146     SVal upperbound
00147       = svalBuilder.evalBinOpNN(state, BO_GE, rawOffset.getByteOffset(),
00148                                 cast<NonLoc>(extentVal),
00149                                 svalBuilder.getConditionType());
00150   
00151     NonLoc *upperboundToCheck = dyn_cast<NonLoc>(&upperbound);
00152     if (!upperboundToCheck)
00153       break;
00154   
00155     ProgramStateRef state_exceedsUpperBound, state_withinUpperBound;
00156     llvm::tie(state_exceedsUpperBound, state_withinUpperBound) =
00157       state->assume(*upperboundToCheck);
00158 
00159     // If we are under constrained and the index variables are tainted, report.
00160     if (state_exceedsUpperBound && state_withinUpperBound) {
00161       if (state->isTainted(rawOffset.getByteOffset()))
00162         reportOOB(checkerContext, state_exceedsUpperBound, OOB_Tainted);
00163         return;
00164     }
00165   
00166     // If we are constrained enough to definitely exceed the upper bound, report.
00167     if (state_exceedsUpperBound) {
00168       assert(!state_withinUpperBound);
00169       reportOOB(checkerContext, state_exceedsUpperBound, OOB_Excedes);
00170       return;
00171     }
00172   
00173     assert(state_withinUpperBound);
00174     state = state_withinUpperBound;
00175   }
00176   while (false);
00177   
00178   if (state != originalState)
00179     checkerContext.addTransition(state);
00180 }
00181 
00182 void ArrayBoundCheckerV2::reportOOB(CheckerContext &checkerContext,
00183                                     ProgramStateRef errorState,
00184                                     OOB_Kind kind) const {
00185   
00186   ExplodedNode *errorNode = checkerContext.generateSink(errorState);
00187   if (!errorNode)
00188     return;
00189 
00190   if (!BT)
00191     BT.reset(new BuiltinBug("Out-of-bound access"));
00192 
00193   // FIXME: This diagnostics are preliminary.  We should get far better
00194   // diagnostics for explaining buffer overruns.
00195 
00196   SmallString<256> buf;
00197   llvm::raw_svector_ostream os(buf);
00198   os << "Out of bound memory access ";
00199   switch (kind) {
00200   case OOB_Precedes:
00201     os << "(accessed memory precedes memory block)";
00202     break;
00203   case OOB_Excedes:
00204     os << "(access exceeds upper limit of memory block)";
00205     break;
00206   case OOB_Tainted:
00207     os << "(index is tainted)";
00208     break;
00209   }
00210 
00211   checkerContext.EmitReport(new BugReport(*BT, os.str(), errorNode));
00212 }
00213 
00214 void RegionRawOffsetV2::dump() const {
00215   dumpToStream(llvm::errs());
00216 }
00217 
00218 void RegionRawOffsetV2::dumpToStream(raw_ostream &os) const {
00219   os << "raw_offset_v2{" << getRegion() << ',' << getByteOffset() << '}';
00220 }
00221 
00222 // FIXME: Merge with the implementation of the same method in Store.cpp
00223 static bool IsCompleteType(ASTContext &Ctx, QualType Ty) {
00224   if (const RecordType *RT = Ty->getAs<RecordType>()) {
00225     const RecordDecl *D = RT->getDecl();
00226     if (!D->getDefinition())
00227       return false;
00228   }
00229 
00230   return true;
00231 }
00232 
00233 
00234 // Lazily computes a value to be used by 'computeOffset'.  If 'val'
00235 // is unknown or undefined, we lazily substitute '0'.  Otherwise,
00236 // return 'val'.
00237 static inline SVal getValue(SVal val, SValBuilder &svalBuilder) {
00238   return isa<UndefinedVal>(val) ? svalBuilder.makeArrayIndex(0) : val;
00239 }
00240 
00241 // Scale a base value by a scaling factor, and return the scaled
00242 // value as an SVal.  Used by 'computeOffset'.
00243 static inline SVal scaleValue(ProgramStateRef state,
00244                               NonLoc baseVal, CharUnits scaling,
00245                               SValBuilder &sb) {
00246   return sb.evalBinOpNN(state, BO_Mul, baseVal,
00247                         sb.makeArrayIndex(scaling.getQuantity()),
00248                         sb.getArrayIndexType());
00249 }
00250 
00251 // Add an SVal to another, treating unknown and undefined values as
00252 // summing to UnknownVal.  Used by 'computeOffset'.
00253 static SVal addValue(ProgramStateRef state, SVal x, SVal y,
00254                      SValBuilder &svalBuilder) {
00255   // We treat UnknownVals and UndefinedVals the same here because we
00256   // only care about computing offsets.
00257   if (x.isUnknownOrUndef() || y.isUnknownOrUndef())
00258     return UnknownVal();
00259   
00260   return svalBuilder.evalBinOpNN(state, BO_Add,                                 
00261                                  cast<NonLoc>(x), cast<NonLoc>(y),
00262                                  svalBuilder.getArrayIndexType());
00263 }
00264 
00265 /// Compute a raw byte offset from a base region.  Used for array bounds
00266 /// checking.
00267 RegionRawOffsetV2 RegionRawOffsetV2::computeOffset(ProgramStateRef state,
00268                                                    SValBuilder &svalBuilder,
00269                                                    SVal location)
00270 {
00271   const MemRegion *region = location.getAsRegion();
00272   SVal offset = UndefinedVal();
00273   
00274   while (region) {
00275     switch (region->getKind()) {
00276       default: {
00277         if (const SubRegion *subReg = dyn_cast<SubRegion>(region)) {
00278           offset = getValue(offset, svalBuilder);
00279           if (!offset.isUnknownOrUndef())
00280             return RegionRawOffsetV2(subReg, offset);
00281         }
00282         return RegionRawOffsetV2();
00283       }
00284       case MemRegion::ElementRegionKind: {
00285         const ElementRegion *elemReg = cast<ElementRegion>(region);
00286         SVal index = elemReg->getIndex();
00287         if (!isa<NonLoc>(index))
00288           return RegionRawOffsetV2();
00289         QualType elemType = elemReg->getElementType();
00290         // If the element is an incomplete type, go no further.
00291         ASTContext &astContext = svalBuilder.getContext();
00292         if (!IsCompleteType(astContext, elemType))
00293           return RegionRawOffsetV2();
00294         
00295         // Update the offset.
00296         offset = addValue(state,
00297                           getValue(offset, svalBuilder),
00298                           scaleValue(state,
00299                           cast<NonLoc>(index),
00300                           astContext.getTypeSizeInChars(elemType),
00301                           svalBuilder),
00302                           svalBuilder);
00303 
00304         if (offset.isUnknownOrUndef())
00305           return RegionRawOffsetV2();
00306 
00307         region = elemReg->getSuperRegion();
00308         continue;
00309       }
00310     }
00311   }
00312   return RegionRawOffsetV2();
00313 }
00314 
00315 
00316 void ento::registerArrayBoundCheckerV2(CheckerManager &mgr) {
00317   mgr.registerChecker<ArrayBoundCheckerV2>();
00318 }