ObjCContainersChecker.cpp revision 256281
1156230Smux//== ObjCContainersChecker.cpp - Path sensitive checker for CFArray *- C++ -*=// 2156230Smux// 3156230Smux// The LLVM Compiler Infrastructure 4156230Smux// 5156230Smux// This file is distributed under the University of Illinois Open Source 6156230Smux// License. See LICENSE.TXT for details. 7156230Smux// 8156230Smux//===----------------------------------------------------------------------===// 9156230Smux// 10156230Smux// Performs path sensitive checks of Core Foundation static containers like 11156230Smux// CFArray. 12156230Smux// 1) Check for buffer overflows: 13156230Smux// In CFArrayGetArrayAtIndex( myArray, index), if the index is outside the 14156230Smux// index space of theArray (0 to N-1 inclusive (where N is the count of 15156230Smux// theArray), the behavior is undefined. 16156230Smux// 17156230Smux//===----------------------------------------------------------------------===// 18156230Smux 19156230Smux#include "ClangSACheckers.h" 20156230Smux#include "clang/AST/ParentMap.h" 21156230Smux#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 22156230Smux#include "clang/StaticAnalyzer/Core/Checker.h" 23156230Smux#include "clang/StaticAnalyzer/Core/CheckerManager.h" 24156230Smux#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 25156230Smux#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" 26156230Smux 27156230Smuxusing namespace clang; 28156230Smuxusing namespace ento; 29156230Smux 30156230Smuxnamespace { 31156230Smuxclass ObjCContainersChecker : public Checker< check::PreStmt<CallExpr>, 32156230Smux check::PostStmt<CallExpr> > { 33156230Smux mutable OwningPtr<BugType> BT; 34156230Smux inline void initBugType() const { 35156230Smux if (!BT) 36156230Smux BT.reset(new BugType("CFArray API", 37156230Smux categories::CoreFoundationObjectiveC)); 38156230Smux } 39156230Smux 40156230Smux inline SymbolRef getArraySym(const Expr *E, CheckerContext &C) const { 41156230Smux SVal ArrayRef = C.getState()->getSVal(E, C.getLocationContext()); 42156230Smux SymbolRef ArraySym = ArrayRef.getAsSymbol(); 43156230Smux return ArraySym; 44156230Smux } 45156230Smux 46156230Smux void addSizeInfo(const Expr *Array, const Expr *Size, 47156230Smux CheckerContext &C) const; 48156230Smux 49156230Smuxpublic: 50156230Smux /// A tag to id this checker. 51156230Smux static void *getTag() { static int Tag; return &Tag; } 52156230Smux 53156230Smux void checkPostStmt(const CallExpr *CE, CheckerContext &C) const; 54156230Smux void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; 55156230Smux}; 56156230Smux} // end anonymous namespace 57156230Smux 58156230Smux// ProgramState trait - a map from array symbol to its state. 59156230SmuxREGISTER_MAP_WITH_PROGRAMSTATE(ArraySizeMap, SymbolRef, DefinedSVal) 60156230Smux 61156230Smuxvoid ObjCContainersChecker::addSizeInfo(const Expr *Array, const Expr *Size, 62156230Smux CheckerContext &C) const { 63156230Smux ProgramStateRef State = C.getState(); 64156230Smux SVal SizeV = State->getSVal(Size, C.getLocationContext()); 65156230Smux // Undefined is reported by another checker. 66156230Smux if (SizeV.isUnknownOrUndef()) 67156230Smux return; 68156230Smux 69156230Smux // Get the ArrayRef symbol. 70156230Smux SVal ArrayRef = State->getSVal(Array, C.getLocationContext()); 71156230Smux SymbolRef ArraySym = ArrayRef.getAsSymbol(); 72156230Smux if (!ArraySym) 73156230Smux return; 74156230Smux 75156230Smux C.addTransition( 76156230Smux State->set<ArraySizeMap>(ArraySym, SizeV.castAs<DefinedSVal>())); 77156230Smux return; 78156230Smux} 79156230Smux 80156230Smuxvoid ObjCContainersChecker::checkPostStmt(const CallExpr *CE, 81156230Smux CheckerContext &C) const { 82156230Smux StringRef Name = C.getCalleeName(CE); 83156230Smux if (Name.empty() || CE->getNumArgs() < 1) 84156230Smux return; 85156230Smux 86156230Smux // Add array size information to the state. 87156230Smux if (Name.equals("CFArrayCreate")) { 88156230Smux if (CE->getNumArgs() < 3) 89156230Smux return; 90156230Smux // Note, we can visit the Create method in the post-visit because 91156230Smux // the CFIndex parameter is passed in by value and will not be invalidated 92156230Smux // by the call. 93156230Smux addSizeInfo(CE, CE->getArg(2), C); 94156230Smux return; 95156230Smux } 96156230Smux 97156230Smux if (Name.equals("CFArrayGetCount")) { 98156230Smux addSizeInfo(CE->getArg(0), CE, C); 99156230Smux return; 100186781Slulf } 101156230Smux} 102156230Smux 103156230Smuxvoid ObjCContainersChecker::checkPreStmt(const CallExpr *CE, 104156230Smux CheckerContext &C) const { 105156230Smux StringRef Name = C.getCalleeName(CE); 106156230Smux if (Name.empty() || CE->getNumArgs() < 2) 107156230Smux return; 108156230Smux 109156230Smux // Check the array access. 110156230Smux if (Name.equals("CFArrayGetValueAtIndex")) { 111156230Smux ProgramStateRef State = C.getState(); 112156230Smux // Retrieve the size. 113156230Smux // Find out if we saw this array symbol before and have information about it. 114156230Smux const Expr *ArrayExpr = CE->getArg(0); 115156230Smux SymbolRef ArraySym = getArraySym(ArrayExpr, C); 116156230Smux if (!ArraySym) 117156230Smux return; 118156230Smux 119156230Smux const DefinedSVal *Size = State->get<ArraySizeMap>(ArraySym); 120156230Smux 121156230Smux if (!Size) 122156230Smux return; 123156230Smux 124156230Smux // Get the index. 125156230Smux const Expr *IdxExpr = CE->getArg(1); 126156230Smux SVal IdxVal = State->getSVal(IdxExpr, C.getLocationContext()); 127156230Smux if (IdxVal.isUnknownOrUndef()) 128156230Smux return; 129156230Smux DefinedSVal Idx = IdxVal.castAs<DefinedSVal>(); 130156230Smux 131156230Smux // Now, check if 'Idx in [0, Size-1]'. 132156230Smux const QualType T = IdxExpr->getType(); 133156230Smux ProgramStateRef StInBound = State->assumeInBound(Idx, *Size, true, T); 134156230Smux ProgramStateRef StOutBound = State->assumeInBound(Idx, *Size, false, T); 135156230Smux if (StOutBound && !StInBound) { 136156230Smux ExplodedNode *N = C.generateSink(StOutBound); 137156230Smux if (!N) 138156230Smux return; 139156230Smux initBugType(); 140156230Smux BugReport *R = new BugReport(*BT, "Index is out of bounds", N); 141156230Smux R->addRange(IdxExpr->getSourceRange()); 142156230Smux C.emitReport(R); 143156230Smux return; 144156230Smux } 145156230Smux } 146156230Smux} 147156230Smux 148156230Smux/// Register checker. 149156230Smuxvoid ento::registerObjCContainersChecker(CheckerManager &mgr) { 150156230Smux mgr.registerChecker<ObjCContainersChecker>(); 151156230Smux} 152156230Smux