clang API Documentation
00001 //== ObjCContainersChecker.cpp - Path sensitive checker for CFArray *- 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 // Performs path sensitive checks of Core Foundation static containers like 00011 // CFArray. 00012 // 1) Check for buffer overflows: 00013 // In CFArrayGetArrayAtIndex( myArray, index), if the index is outside the 00014 // index space of theArray (0 to N-1 inclusive (where N is the count of 00015 // theArray), the behavior is undefined. 00016 // 00017 //===----------------------------------------------------------------------===// 00018 00019 #include "ClangSACheckers.h" 00020 #include "clang/StaticAnalyzer/Core/Checker.h" 00021 #include "clang/StaticAnalyzer/Core/CheckerManager.h" 00022 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 00023 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" 00024 #include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" 00025 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 00026 #include "clang/AST/ParentMap.h" 00027 00028 using namespace clang; 00029 using namespace ento; 00030 00031 namespace { 00032 class ObjCContainersChecker : public Checker< check::PreStmt<CallExpr>, 00033 check::PostStmt<CallExpr> > { 00034 mutable OwningPtr<BugType> BT; 00035 inline void initBugType() const { 00036 if (!BT) 00037 BT.reset(new BugType("CFArray API", 00038 categories::CoreFoundationObjectiveC)); 00039 } 00040 00041 inline SymbolRef getArraySym(const Expr *E, CheckerContext &C) const { 00042 SVal ArrayRef = C.getState()->getSVal(E, C.getLocationContext()); 00043 SymbolRef ArraySym = ArrayRef.getAsSymbol(); 00044 return ArraySym; 00045 } 00046 00047 void addSizeInfo(const Expr *Array, const Expr *Size, 00048 CheckerContext &C) const; 00049 00050 public: 00051 /// A tag to id this checker. 00052 static void *getTag() { static int Tag; return &Tag; } 00053 00054 void checkPostStmt(const CallExpr *CE, CheckerContext &C) const; 00055 void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; 00056 }; 00057 } // end anonymous namespace 00058 00059 // ProgramState trait - a map from array symbol to it's state. 00060 typedef llvm::ImmutableMap<SymbolRef, DefinedSVal> ArraySizeM; 00061 00062 namespace { struct ArraySizeMap {}; } 00063 namespace clang { namespace ento { 00064 template<> struct ProgramStateTrait<ArraySizeMap> 00065 : public ProgramStatePartialTrait<ArraySizeM > { 00066 static void *GDMIndex() { return ObjCContainersChecker::getTag(); } 00067 }; 00068 }} 00069 00070 void ObjCContainersChecker::addSizeInfo(const Expr *Array, const Expr *Size, 00071 CheckerContext &C) const { 00072 ProgramStateRef State = C.getState(); 00073 SVal SizeV = State->getSVal(Size, C.getLocationContext()); 00074 // Undefined is reported by another checker. 00075 if (SizeV.isUnknownOrUndef()) 00076 return; 00077 00078 // Get the ArrayRef symbol. 00079 SVal ArrayRef = State->getSVal(Array, C.getLocationContext()); 00080 SymbolRef ArraySym = ArrayRef.getAsSymbol(); 00081 if (!ArraySym) 00082 return; 00083 00084 C.addTransition(State->set<ArraySizeMap>(ArraySym, cast<DefinedSVal>(SizeV))); 00085 return; 00086 } 00087 00088 void ObjCContainersChecker::checkPostStmt(const CallExpr *CE, 00089 CheckerContext &C) const { 00090 StringRef Name = C.getCalleeName(CE); 00091 if (Name.empty() || CE->getNumArgs() < 1) 00092 return; 00093 00094 // Add array size information to the state. 00095 if (Name.equals("CFArrayCreate")) { 00096 if (CE->getNumArgs() < 3) 00097 return; 00098 // Note, we can visit the Create method in the post-visit because 00099 // the CFIndex parameter is passed in by value and will not be invalidated 00100 // by the call. 00101 addSizeInfo(CE, CE->getArg(2), C); 00102 return; 00103 } 00104 00105 if (Name.equals("CFArrayGetCount")) { 00106 addSizeInfo(CE->getArg(0), CE, C); 00107 return; 00108 } 00109 } 00110 00111 void ObjCContainersChecker::checkPreStmt(const CallExpr *CE, 00112 CheckerContext &C) const { 00113 StringRef Name = C.getCalleeName(CE); 00114 if (Name.empty() || CE->getNumArgs() < 2) 00115 return; 00116 00117 // Check the array access. 00118 if (Name.equals("CFArrayGetValueAtIndex")) { 00119 ProgramStateRef State = C.getState(); 00120 // Retrieve the size. 00121 // Find out if we saw this array symbol before and have information about it. 00122 const Expr *ArrayExpr = CE->getArg(0); 00123 SymbolRef ArraySym = getArraySym(ArrayExpr, C); 00124 if (!ArraySym) 00125 return; 00126 00127 const DefinedSVal *Size = State->get<ArraySizeMap>(ArraySym); 00128 00129 if (!Size) 00130 return; 00131 00132 // Get the index. 00133 const Expr *IdxExpr = CE->getArg(1); 00134 SVal IdxVal = State->getSVal(IdxExpr, C.getLocationContext()); 00135 if (IdxVal.isUnknownOrUndef()) 00136 return; 00137 DefinedSVal Idx = cast<DefinedSVal>(IdxVal); 00138 00139 // Now, check if 'Idx in [0, Size-1]'. 00140 const QualType T = IdxExpr->getType(); 00141 ProgramStateRef StInBound = State->assumeInBound(Idx, *Size, true, T); 00142 ProgramStateRef StOutBound = State->assumeInBound(Idx, *Size, false, T); 00143 if (StOutBound && !StInBound) { 00144 ExplodedNode *N = C.generateSink(StOutBound); 00145 if (!N) 00146 return; 00147 initBugType(); 00148 BugReport *R = new BugReport(*BT, "Index is out of bounds", N); 00149 R->addRange(IdxExpr->getSourceRange()); 00150 C.EmitReport(R); 00151 return; 00152 } 00153 } 00154 } 00155 00156 /// Register checker. 00157 void ento::registerObjCContainersChecker(CheckerManager &mgr) { 00158 mgr.registerChecker<ObjCContainersChecker>(); 00159 }