1//== ObjCContainersChecker.cpp - Path sensitive checker for CFArray *- C++ -*=// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file is distributed under the University of Illinois Open Source 6// License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9// 10// Performs path sensitive checks of Core Foundation static containers like 11// CFArray. 12// 1) Check for buffer overflows: 13// In CFArrayGetArrayAtIndex( myArray, index), if the index is outside the 14// index space of theArray (0 to N-1 inclusive (where N is the count of 15// theArray), the behavior is undefined. 16// 17//===----------------------------------------------------------------------===// 18 19#include "ClangSACheckers.h" 20#include "clang/StaticAnalyzer/Core/Checker.h" 21#include "clang/StaticAnalyzer/Core/CheckerManager.h" 22#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 23#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" 24#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 25#include "clang/AST/ParentMap.h" 26 27using namespace clang; 28using namespace ento; 29 30namespace { 31class ObjCContainersChecker : public Checker< check::PreStmt<CallExpr>, 32 check::PostStmt<CallExpr> > { 33 mutable OwningPtr<BugType> BT; 34 inline void initBugType() const { 35 if (!BT) 36 BT.reset(new BugType("CFArray API", 37 categories::CoreFoundationObjectiveC)); 38 } 39 40 inline SymbolRef getArraySym(const Expr *E, CheckerContext &C) const { 41 SVal ArrayRef = C.getState()->getSVal(E, C.getLocationContext()); 42 SymbolRef ArraySym = ArrayRef.getAsSymbol(); 43 return ArraySym; 44 } 45 46 void addSizeInfo(const Expr *Array, const Expr *Size, 47 CheckerContext &C) const; 48 49public: 50 /// A tag to id this checker. 51 static void *getTag() { static int Tag; return &Tag; } 52 53 void checkPostStmt(const CallExpr *CE, CheckerContext &C) const; 54 void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; 55}; 56} // end anonymous namespace 57 58// ProgramState trait - a map from array symbol to it's state. 59typedef llvm::ImmutableMap<SymbolRef, DefinedSVal> ArraySizeM; 60 61namespace { struct ArraySizeMap {}; } 62namespace clang { namespace ento { 63template<> struct ProgramStateTrait<ArraySizeMap> 64 : public ProgramStatePartialTrait<ArraySizeM > { 65 static void *GDMIndex() { return ObjCContainersChecker::getTag(); } 66}; 67}} 68 69void ObjCContainersChecker::addSizeInfo(const Expr *Array, const Expr *Size, 70 CheckerContext &C) const { 71 ProgramStateRef State = C.getState(); 72 SVal SizeV = State->getSVal(Size, C.getLocationContext()); 73 // Undefined is reported by another checker. 74 if (SizeV.isUnknownOrUndef()) 75 return; 76 77 // Get the ArrayRef symbol. 78 SVal ArrayRef = State->getSVal(Array, C.getLocationContext()); 79 SymbolRef ArraySym = ArrayRef.getAsSymbol(); 80 if (!ArraySym) 81 return; 82 83 C.addTransition(State->set<ArraySizeMap>(ArraySym, cast<DefinedSVal>(SizeV))); 84 return; 85} 86 87void ObjCContainersChecker::checkPostStmt(const CallExpr *CE, 88 CheckerContext &C) const { 89 StringRef Name = C.getCalleeName(CE); 90 if (Name.empty() || CE->getNumArgs() < 1) 91 return; 92 93 // Add array size information to the state. 94 if (Name.equals("CFArrayCreate")) { 95 if (CE->getNumArgs() < 3) 96 return; 97 // Note, we can visit the Create method in the post-visit because 98 // the CFIndex parameter is passed in by value and will not be invalidated 99 // by the call. 100 addSizeInfo(CE, CE->getArg(2), C); 101 return; 102 } 103 104 if (Name.equals("CFArrayGetCount")) { 105 addSizeInfo(CE->getArg(0), CE, C); 106 return; 107 } 108} 109 110void ObjCContainersChecker::checkPreStmt(const CallExpr *CE, 111 CheckerContext &C) const { 112 StringRef Name = C.getCalleeName(CE); 113 if (Name.empty() || CE->getNumArgs() < 2) 114 return; 115 116 // Check the array access. 117 if (Name.equals("CFArrayGetValueAtIndex")) { 118 ProgramStateRef State = C.getState(); 119 // Retrieve the size. 120 // Find out if we saw this array symbol before and have information about it. 121 const Expr *ArrayExpr = CE->getArg(0); 122 SymbolRef ArraySym = getArraySym(ArrayExpr, C); 123 if (!ArraySym) 124 return; 125 126 const DefinedSVal *Size = State->get<ArraySizeMap>(ArraySym); 127 128 if (!Size) 129 return; 130 131 // Get the index. 132 const Expr *IdxExpr = CE->getArg(1); 133 SVal IdxVal = State->getSVal(IdxExpr, C.getLocationContext()); 134 if (IdxVal.isUnknownOrUndef()) 135 return; 136 DefinedSVal Idx = cast<DefinedSVal>(IdxVal); 137 138 // Now, check if 'Idx in [0, Size-1]'. 139 const QualType T = IdxExpr->getType(); 140 ProgramStateRef StInBound = State->assumeInBound(Idx, *Size, true, T); 141 ProgramStateRef StOutBound = State->assumeInBound(Idx, *Size, false, T); 142 if (StOutBound && !StInBound) { 143 ExplodedNode *N = C.generateSink(StOutBound); 144 if (!N) 145 return; 146 initBugType(); 147 BugReport *R = new BugReport(*BT, "Index is out of bounds", N); 148 R->addRange(IdxExpr->getSourceRange()); 149 C.EmitReport(R); 150 return; 151 } 152 } 153} 154 155/// Register checker. 156void ento::registerObjCContainersChecker(CheckerManager &mgr) { 157 mgr.registerChecker<ObjCContainersChecker>(); 158} 159