1234287Sdim//== ObjCContainersChecker.cpp - Path sensitive checker for CFArray *- C++ -*=// 2234287Sdim// 3234287Sdim// The LLVM Compiler Infrastructure 4234287Sdim// 5234287Sdim// This file is distributed under the University of Illinois Open Source 6234287Sdim// License. See LICENSE.TXT for details. 7234287Sdim// 8234287Sdim//===----------------------------------------------------------------------===// 9234287Sdim// 10234287Sdim// Performs path sensitive checks of Core Foundation static containers like 11234287Sdim// CFArray. 12234287Sdim// 1) Check for buffer overflows: 13234287Sdim// In CFArrayGetArrayAtIndex( myArray, index), if the index is outside the 14234287Sdim// index space of theArray (0 to N-1 inclusive (where N is the count of 15234287Sdim// theArray), the behavior is undefined. 16234287Sdim// 17234287Sdim//===----------------------------------------------------------------------===// 18234287Sdim 19234287Sdim#include "ClangSACheckers.h" 20249423Sdim#include "clang/AST/ParentMap.h" 21249423Sdim#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 22234287Sdim#include "clang/StaticAnalyzer/Core/Checker.h" 23234287Sdim#include "clang/StaticAnalyzer/Core/CheckerManager.h" 24234287Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 25234287Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" 26234287Sdim 27234287Sdimusing namespace clang; 28234287Sdimusing namespace ento; 29234287Sdim 30234287Sdimnamespace { 31234287Sdimclass ObjCContainersChecker : public Checker< check::PreStmt<CallExpr>, 32234287Sdim check::PostStmt<CallExpr> > { 33234287Sdim mutable OwningPtr<BugType> BT; 34234287Sdim inline void initBugType() const { 35234287Sdim if (!BT) 36234287Sdim BT.reset(new BugType("CFArray API", 37234287Sdim categories::CoreFoundationObjectiveC)); 38234287Sdim } 39234287Sdim 40234287Sdim inline SymbolRef getArraySym(const Expr *E, CheckerContext &C) const { 41234287Sdim SVal ArrayRef = C.getState()->getSVal(E, C.getLocationContext()); 42234287Sdim SymbolRef ArraySym = ArrayRef.getAsSymbol(); 43234287Sdim return ArraySym; 44234287Sdim } 45234287Sdim 46234287Sdim void addSizeInfo(const Expr *Array, const Expr *Size, 47234287Sdim CheckerContext &C) const; 48234287Sdim 49234287Sdimpublic: 50234287Sdim /// A tag to id this checker. 51234287Sdim static void *getTag() { static int Tag; return &Tag; } 52234287Sdim 53234287Sdim void checkPostStmt(const CallExpr *CE, CheckerContext &C) const; 54234287Sdim void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; 55234287Sdim}; 56234287Sdim} // end anonymous namespace 57234287Sdim 58243830Sdim// ProgramState trait - a map from array symbol to its state. 59243830SdimREGISTER_MAP_WITH_PROGRAMSTATE(ArraySizeMap, SymbolRef, DefinedSVal) 60234287Sdim 61234287Sdimvoid ObjCContainersChecker::addSizeInfo(const Expr *Array, const Expr *Size, 62234287Sdim CheckerContext &C) const { 63234287Sdim ProgramStateRef State = C.getState(); 64234287Sdim SVal SizeV = State->getSVal(Size, C.getLocationContext()); 65234287Sdim // Undefined is reported by another checker. 66234287Sdim if (SizeV.isUnknownOrUndef()) 67234287Sdim return; 68234287Sdim 69234287Sdim // Get the ArrayRef symbol. 70234287Sdim SVal ArrayRef = State->getSVal(Array, C.getLocationContext()); 71234287Sdim SymbolRef ArraySym = ArrayRef.getAsSymbol(); 72234287Sdim if (!ArraySym) 73234287Sdim return; 74234287Sdim 75249423Sdim C.addTransition( 76249423Sdim State->set<ArraySizeMap>(ArraySym, SizeV.castAs<DefinedSVal>())); 77234287Sdim return; 78234287Sdim} 79234287Sdim 80234287Sdimvoid ObjCContainersChecker::checkPostStmt(const CallExpr *CE, 81234287Sdim CheckerContext &C) const { 82234287Sdim StringRef Name = C.getCalleeName(CE); 83234287Sdim if (Name.empty() || CE->getNumArgs() < 1) 84234287Sdim return; 85234287Sdim 86234287Sdim // Add array size information to the state. 87234287Sdim if (Name.equals("CFArrayCreate")) { 88234287Sdim if (CE->getNumArgs() < 3) 89234287Sdim return; 90234287Sdim // Note, we can visit the Create method in the post-visit because 91234287Sdim // the CFIndex parameter is passed in by value and will not be invalidated 92234287Sdim // by the call. 93234287Sdim addSizeInfo(CE, CE->getArg(2), C); 94234287Sdim return; 95234287Sdim } 96234287Sdim 97234287Sdim if (Name.equals("CFArrayGetCount")) { 98234287Sdim addSizeInfo(CE->getArg(0), CE, C); 99234287Sdim return; 100234287Sdim } 101234287Sdim} 102234287Sdim 103234287Sdimvoid ObjCContainersChecker::checkPreStmt(const CallExpr *CE, 104234287Sdim CheckerContext &C) const { 105234287Sdim StringRef Name = C.getCalleeName(CE); 106234287Sdim if (Name.empty() || CE->getNumArgs() < 2) 107234287Sdim return; 108234287Sdim 109234287Sdim // Check the array access. 110234287Sdim if (Name.equals("CFArrayGetValueAtIndex")) { 111234287Sdim ProgramStateRef State = C.getState(); 112234287Sdim // Retrieve the size. 113234287Sdim // Find out if we saw this array symbol before and have information about it. 114234287Sdim const Expr *ArrayExpr = CE->getArg(0); 115234287Sdim SymbolRef ArraySym = getArraySym(ArrayExpr, C); 116234287Sdim if (!ArraySym) 117234287Sdim return; 118234287Sdim 119234287Sdim const DefinedSVal *Size = State->get<ArraySizeMap>(ArraySym); 120234287Sdim 121234287Sdim if (!Size) 122234287Sdim return; 123234287Sdim 124234287Sdim // Get the index. 125234287Sdim const Expr *IdxExpr = CE->getArg(1); 126234287Sdim SVal IdxVal = State->getSVal(IdxExpr, C.getLocationContext()); 127234287Sdim if (IdxVal.isUnknownOrUndef()) 128234287Sdim return; 129249423Sdim DefinedSVal Idx = IdxVal.castAs<DefinedSVal>(); 130234287Sdim 131234287Sdim // Now, check if 'Idx in [0, Size-1]'. 132234287Sdim const QualType T = IdxExpr->getType(); 133234287Sdim ProgramStateRef StInBound = State->assumeInBound(Idx, *Size, true, T); 134234287Sdim ProgramStateRef StOutBound = State->assumeInBound(Idx, *Size, false, T); 135234287Sdim if (StOutBound && !StInBound) { 136234287Sdim ExplodedNode *N = C.generateSink(StOutBound); 137234287Sdim if (!N) 138234287Sdim return; 139234287Sdim initBugType(); 140234287Sdim BugReport *R = new BugReport(*BT, "Index is out of bounds", N); 141234287Sdim R->addRange(IdxExpr->getSourceRange()); 142243830Sdim C.emitReport(R); 143234287Sdim return; 144234287Sdim } 145234287Sdim } 146234287Sdim} 147234287Sdim 148234287Sdim/// Register checker. 149234287Sdimvoid ento::registerObjCContainersChecker(CheckerManager &mgr) { 150234287Sdim mgr.registerChecker<ObjCContainersChecker>(); 151234287Sdim} 152