StreamChecker.cpp revision 0c2e8c87f18e861cb48965784e20b9292fb70b60
1//===-- StreamChecker.cpp -----------------------------------------*- 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// This file defines checkers that model and check stream handling functions. 11// 12//===----------------------------------------------------------------------===// 13 14#include "GRExprEngineExperimentalChecks.h" 15#include "clang/Checker/BugReporter/BugType.h" 16#include "clang/Checker/PathSensitive/CheckerVisitor.h" 17#include "clang/Checker/PathSensitive/GRState.h" 18#include "clang/Checker/PathSensitive/GRStateTrait.h" 19#include "clang/Checker/PathSensitive/SymbolManager.h" 20#include "llvm/ADT/ImmutableMap.h" 21 22using namespace clang; 23 24namespace { 25 26class StreamChecker : public CheckerVisitor<StreamChecker> { 27 IdentifierInfo *II_fopen, *II_fread, *II_fwrite, 28 *II_fseek, *II_ftell, *II_rewind, *II_fgetpos, *II_fsetpos, 29 *II_clearerr, *II_feof, *II_ferror, *II_fileno; 30 BuiltinBug *BT_nullfp, *BT_illegalwhence; 31 32public: 33 StreamChecker() 34 : II_fopen(0), II_fread(0), II_fwrite(0), 35 II_fseek(0), II_ftell(0), II_rewind(0), II_fgetpos(0), II_fsetpos(0), 36 II_clearerr(0), II_feof(0), II_ferror(0), II_fileno(0), 37 BT_nullfp(0), BT_illegalwhence(0) {} 38 39 static void *getTag() { 40 static int x; 41 return &x; 42 } 43 44 virtual bool EvalCallExpr(CheckerContext &C, const CallExpr *CE); 45 46private: 47 void Fopen(CheckerContext &C, const CallExpr *CE); 48 void Fread(CheckerContext &C, const CallExpr *CE); 49 void Fwrite(CheckerContext &C, const CallExpr *CE); 50 void Fseek(CheckerContext &C, const CallExpr *CE); 51 void Ftell(CheckerContext &C, const CallExpr *CE); 52 void Rewind(CheckerContext &C, const CallExpr *CE); 53 void Fgetpos(CheckerContext &C, const CallExpr *CE); 54 void Fsetpos(CheckerContext &C, const CallExpr *CE); 55 void Clearerr(CheckerContext &C, const CallExpr *CE); 56 void Feof(CheckerContext &C, const CallExpr *CE); 57 void Ferror(CheckerContext &C, const CallExpr *CE); 58 void Fileno(CheckerContext &C, const CallExpr *CE); 59 60 // Return true indicates the stream pointer is NULL. 61 const GRState *CheckNullStream(SVal SV, const GRState *state, 62 CheckerContext &C); 63}; 64 65} // end anonymous namespace 66 67void clang::RegisterStreamChecker(GRExprEngine &Eng) { 68 Eng.registerCheck(new StreamChecker()); 69} 70 71bool StreamChecker::EvalCallExpr(CheckerContext &C, const CallExpr *CE) { 72 const GRState *state = C.getState(); 73 const Expr *Callee = CE->getCallee(); 74 SVal L = state->getSVal(Callee); 75 const FunctionDecl *FD = L.getAsFunctionDecl(); 76 if (!FD) 77 return false; 78 79 ASTContext &Ctx = C.getASTContext(); 80 if (!II_fopen) 81 II_fopen = &Ctx.Idents.get("fopen"); 82 if (!II_fread) 83 II_fread = &Ctx.Idents.get("fread"); 84 if (!II_fwrite) 85 II_fwrite = &Ctx.Idents.get("fwrite"); 86 if (!II_fseek) 87 II_fseek = &Ctx.Idents.get("fseek"); 88 if (!II_ftell) 89 II_ftell = &Ctx.Idents.get("ftell"); 90 if (!II_rewind) 91 II_rewind = &Ctx.Idents.get("rewind"); 92 if (!II_fgetpos) 93 II_fgetpos = &Ctx.Idents.get("fgetpos"); 94 if (!II_fsetpos) 95 II_fsetpos = &Ctx.Idents.get("fsetpos"); 96 if (!II_clearerr) 97 II_clearerr = &Ctx.Idents.get("clearerr"); 98 if (!II_feof) 99 II_feof = &Ctx.Idents.get("feof"); 100 if (!II_ferror) 101 II_ferror = &Ctx.Idents.get("ferror"); 102 if (!II_fileno) 103 II_fileno = &Ctx.Idents.get("fileno"); 104 105 if (FD->getIdentifier() == II_fopen) { 106 Fopen(C, CE); 107 return true; 108 } 109 if (FD->getIdentifier() == II_fread) { 110 Fread(C, CE); 111 return true; 112 } 113 if (FD->getIdentifier() == II_fwrite) { 114 Fwrite(C, CE); 115 return true; 116 } 117 if (FD->getIdentifier() == II_fseek) { 118 Fseek(C, CE); 119 return true; 120 } 121 if (FD->getIdentifier() == II_ftell) { 122 Ftell(C, CE); 123 return true; 124 } 125 if (FD->getIdentifier() == II_rewind) { 126 Rewind(C, CE); 127 return true; 128 } 129 if (FD->getIdentifier() == II_fgetpos) { 130 Fgetpos(C, CE); 131 return true; 132 } 133 if (FD->getIdentifier() == II_fsetpos) { 134 Fsetpos(C, CE); 135 return true; 136 } 137 if (FD->getIdentifier() == II_clearerr) { 138 Clearerr(C, CE); 139 return true; 140 } 141 if (FD->getIdentifier() == II_feof) { 142 Feof(C, CE); 143 return true; 144 } 145 if (FD->getIdentifier() == II_ferror) { 146 Ferror(C, CE); 147 return true; 148 } 149 if (FD->getIdentifier() == II_fileno) { 150 Fileno(C, CE); 151 return true; 152 } 153 154 return false; 155} 156 157void StreamChecker::Fopen(CheckerContext &C, const CallExpr *CE) { 158 const GRState *state = C.getState(); 159 unsigned Count = C.getNodeBuilder().getCurrentBlockCount(); 160 ValueManager &ValMgr = C.getValueManager(); 161 DefinedSVal RetVal = cast<DefinedSVal>(ValMgr.getConjuredSymbolVal(0, CE, 162 Count)); 163 state = state->BindExpr(CE, RetVal); 164 165 ConstraintManager &CM = C.getConstraintManager(); 166 // Bifurcate the state into two: one with a valid FILE* pointer, the other 167 // with a NULL. 168 const GRState *stateNotNull, *stateNull; 169 llvm::tie(stateNotNull, stateNull) = CM.AssumeDual(state, RetVal); 170 171 C.addTransition(stateNotNull); 172 C.addTransition(stateNull); 173} 174 175void StreamChecker::Fread(CheckerContext &C, const CallExpr *CE) { 176 const GRState *state = C.getState(); 177 if (!CheckNullStream(state->getSVal(CE->getArg(3)), state, C)) 178 return; 179} 180 181void StreamChecker::Fwrite(CheckerContext &C, const CallExpr *CE) { 182 const GRState *state = C.getState(); 183 if (!CheckNullStream(state->getSVal(CE->getArg(3)), state, C)) 184 return; 185} 186 187void StreamChecker::Fseek(CheckerContext &C, const CallExpr *CE) { 188 const GRState *state = C.getState(); 189 if (!(state = CheckNullStream(state->getSVal(CE->getArg(0)), state, C))) 190 return; 191 // Check the legality of the 'whence' argument of 'fseek'. 192 SVal Whence = state->getSVal(CE->getArg(2)); 193 bool WhenceIsLegal = true; 194 const nonloc::ConcreteInt *CI = dyn_cast<nonloc::ConcreteInt>(&Whence); 195 if (!CI) 196 WhenceIsLegal = false; 197 198 int64_t x = CI->getValue().getSExtValue(); 199 if (!(x == 0 || x == 1 || x == 2)) 200 WhenceIsLegal = false; 201 202 if (!WhenceIsLegal) { 203 if (ExplodedNode *N = C.GenerateSink(state)) { 204 if (!BT_illegalwhence) 205 BT_illegalwhence = new BuiltinBug("Illegal whence argument", 206 "The whence argument to fseek() should be " 207 "SEEK_SET, SEEK_END, or SEEK_CUR."); 208 BugReport *R = new BugReport(*BT_illegalwhence, 209 BT_illegalwhence->getDescription(), N); 210 C.EmitReport(R); 211 } 212 return; 213 } 214 215 C.addTransition(state); 216} 217 218void StreamChecker::Ftell(CheckerContext &C, const CallExpr *CE) { 219 const GRState *state = C.getState(); 220 if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C)) 221 return; 222} 223 224void StreamChecker::Rewind(CheckerContext &C, const CallExpr *CE) { 225 const GRState *state = C.getState(); 226 if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C)) 227 return; 228} 229 230void StreamChecker::Fgetpos(CheckerContext &C, const CallExpr *CE) { 231 const GRState *state = C.getState(); 232 if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C)) 233 return; 234} 235 236void StreamChecker::Fsetpos(CheckerContext &C, const CallExpr *CE) { 237 const GRState *state = C.getState(); 238 if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C)) 239 return; 240} 241 242void StreamChecker::Clearerr(CheckerContext &C, const CallExpr *CE) { 243 const GRState *state = C.getState(); 244 if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C)) 245 return; 246} 247 248void StreamChecker::Feof(CheckerContext &C, const CallExpr *CE) { 249 const GRState *state = C.getState(); 250 if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C)) 251 return; 252} 253 254void StreamChecker::Ferror(CheckerContext &C, const CallExpr *CE) { 255 const GRState *state = C.getState(); 256 if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C)) 257 return; 258} 259 260void StreamChecker::Fileno(CheckerContext &C, const CallExpr *CE) { 261 const GRState *state = C.getState(); 262 if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C)) 263 return; 264} 265 266const GRState *StreamChecker::CheckNullStream(SVal SV, const GRState *state, 267 CheckerContext &C) { 268 const DefinedSVal *DV = dyn_cast<DefinedSVal>(&SV); 269 if (!DV) 270 return false; 271 272 ConstraintManager &CM = C.getConstraintManager(); 273 const GRState *stateNotNull, *stateNull; 274 llvm::tie(stateNotNull, stateNull) = CM.AssumeDual(state, *DV); 275 276 if (!stateNotNull && stateNull) { 277 if (ExplodedNode *N = C.GenerateSink(stateNull)) { 278 if (!BT_nullfp) 279 BT_nullfp = new BuiltinBug("NULL stream pointer", 280 "Stream pointer might be NULL."); 281 BugReport *R =new BugReport(*BT_nullfp, BT_nullfp->getDescription(), N); 282 C.EmitReport(R); 283 } 284 return 0; 285 } 286 return stateNotNull; 287} 288