StreamChecker.cpp revision ba37d3b2ef37c3591a4f673215d78cb9cc928de3
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 "ExprEngineExperimentalChecks.h" 15#include "clang/StaticAnalyzer/BugReporter/BugType.h" 16#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" 17#include "clang/StaticAnalyzer/PathSensitive/GRState.h" 18#include "clang/StaticAnalyzer/PathSensitive/GRStateTrait.h" 19#include "clang/StaticAnalyzer/PathSensitive/SymbolManager.h" 20#include "llvm/ADT/ImmutableMap.h" 21 22using namespace clang; 23using namespace ento; 24 25namespace { 26 27struct StreamState { 28 enum Kind { Opened, Closed, OpenFailed, Escaped } K; 29 const Stmt *S; 30 31 StreamState(Kind k, const Stmt *s) : K(k), S(s) {} 32 33 bool isOpened() const { return K == Opened; } 34 bool isClosed() const { return K == Closed; } 35 //bool isOpenFailed() const { return K == OpenFailed; } 36 //bool isEscaped() const { return K == Escaped; } 37 38 bool operator==(const StreamState &X) const { 39 return K == X.K && S == X.S; 40 } 41 42 static StreamState getOpened(const Stmt *s) { return StreamState(Opened, s); } 43 static StreamState getClosed(const Stmt *s) { return StreamState(Closed, s); } 44 static StreamState getOpenFailed(const Stmt *s) { 45 return StreamState(OpenFailed, s); 46 } 47 static StreamState getEscaped(const Stmt *s) { 48 return StreamState(Escaped, s); 49 } 50 51 void Profile(llvm::FoldingSetNodeID &ID) const { 52 ID.AddInteger(K); 53 ID.AddPointer(S); 54 } 55}; 56 57class StreamChecker : public CheckerVisitor<StreamChecker> { 58 IdentifierInfo *II_fopen, *II_tmpfile, *II_fclose, *II_fread, *II_fwrite, 59 *II_fseek, *II_ftell, *II_rewind, *II_fgetpos, *II_fsetpos, 60 *II_clearerr, *II_feof, *II_ferror, *II_fileno; 61 BuiltinBug *BT_nullfp, *BT_illegalwhence, *BT_doubleclose, *BT_ResourceLeak; 62 63public: 64 StreamChecker() 65 : II_fopen(0), II_tmpfile(0) ,II_fclose(0), II_fread(0), II_fwrite(0), 66 II_fseek(0), II_ftell(0), II_rewind(0), II_fgetpos(0), II_fsetpos(0), 67 II_clearerr(0), II_feof(0), II_ferror(0), II_fileno(0), 68 BT_nullfp(0), BT_illegalwhence(0), BT_doubleclose(0), 69 BT_ResourceLeak(0) {} 70 71 static void *getTag() { 72 static int x; 73 return &x; 74 } 75 76 virtual bool evalCallExpr(CheckerContext &C, const CallExpr *CE); 77 void evalDeadSymbols(CheckerContext &C, SymbolReaper &SymReaper); 78 void evalEndPath(EndOfFunctionNodeBuilder &B, void *tag, ExprEngine &Eng); 79 void PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *S); 80 81private: 82 void Fopen(CheckerContext &C, const CallExpr *CE); 83 void Tmpfile(CheckerContext &C, const CallExpr *CE); 84 void Fclose(CheckerContext &C, const CallExpr *CE); 85 void Fread(CheckerContext &C, const CallExpr *CE); 86 void Fwrite(CheckerContext &C, const CallExpr *CE); 87 void Fseek(CheckerContext &C, const CallExpr *CE); 88 void Ftell(CheckerContext &C, const CallExpr *CE); 89 void Rewind(CheckerContext &C, const CallExpr *CE); 90 void Fgetpos(CheckerContext &C, const CallExpr *CE); 91 void Fsetpos(CheckerContext &C, const CallExpr *CE); 92 void Clearerr(CheckerContext &C, const CallExpr *CE); 93 void Feof(CheckerContext &C, const CallExpr *CE); 94 void Ferror(CheckerContext &C, const CallExpr *CE); 95 void Fileno(CheckerContext &C, const CallExpr *CE); 96 97 void OpenFileAux(CheckerContext &C, const CallExpr *CE); 98 99 const GRState *CheckNullStream(SVal SV, const GRState *state, 100 CheckerContext &C); 101 const GRState *CheckDoubleClose(const CallExpr *CE, const GRState *state, 102 CheckerContext &C); 103}; 104 105} // end anonymous namespace 106 107namespace clang { 108namespace ento { 109 template <> 110 struct GRStateTrait<StreamState> 111 : public GRStatePartialTrait<llvm::ImmutableMap<SymbolRef, StreamState> > { 112 static void *GDMIndex() { return StreamChecker::getTag(); } 113 }; 114} 115} 116 117void ento::RegisterStreamChecker(ExprEngine &Eng) { 118 Eng.registerCheck(new StreamChecker()); 119} 120 121bool StreamChecker::evalCallExpr(CheckerContext &C, const CallExpr *CE) { 122 const GRState *state = C.getState(); 123 const Expr *Callee = CE->getCallee(); 124 SVal L = state->getSVal(Callee); 125 const FunctionDecl *FD = L.getAsFunctionDecl(); 126 if (!FD) 127 return false; 128 129 ASTContext &Ctx = C.getASTContext(); 130 if (!II_fopen) 131 II_fopen = &Ctx.Idents.get("fopen"); 132 if (!II_tmpfile) 133 II_tmpfile = &Ctx.Idents.get("tmpfile"); 134 if (!II_fclose) 135 II_fclose = &Ctx.Idents.get("fclose"); 136 if (!II_fread) 137 II_fread = &Ctx.Idents.get("fread"); 138 if (!II_fwrite) 139 II_fwrite = &Ctx.Idents.get("fwrite"); 140 if (!II_fseek) 141 II_fseek = &Ctx.Idents.get("fseek"); 142 if (!II_ftell) 143 II_ftell = &Ctx.Idents.get("ftell"); 144 if (!II_rewind) 145 II_rewind = &Ctx.Idents.get("rewind"); 146 if (!II_fgetpos) 147 II_fgetpos = &Ctx.Idents.get("fgetpos"); 148 if (!II_fsetpos) 149 II_fsetpos = &Ctx.Idents.get("fsetpos"); 150 if (!II_clearerr) 151 II_clearerr = &Ctx.Idents.get("clearerr"); 152 if (!II_feof) 153 II_feof = &Ctx.Idents.get("feof"); 154 if (!II_ferror) 155 II_ferror = &Ctx.Idents.get("ferror"); 156 if (!II_fileno) 157 II_fileno = &Ctx.Idents.get("fileno"); 158 159 if (FD->getIdentifier() == II_fopen) { 160 Fopen(C, CE); 161 return true; 162 } 163 if (FD->getIdentifier() == II_tmpfile) { 164 Tmpfile(C, CE); 165 return true; 166 } 167 if (FD->getIdentifier() == II_fclose) { 168 Fclose(C, CE); 169 return true; 170 } 171 if (FD->getIdentifier() == II_fread) { 172 Fread(C, CE); 173 return true; 174 } 175 if (FD->getIdentifier() == II_fwrite) { 176 Fwrite(C, CE); 177 return true; 178 } 179 if (FD->getIdentifier() == II_fseek) { 180 Fseek(C, CE); 181 return true; 182 } 183 if (FD->getIdentifier() == II_ftell) { 184 Ftell(C, CE); 185 return true; 186 } 187 if (FD->getIdentifier() == II_rewind) { 188 Rewind(C, CE); 189 return true; 190 } 191 if (FD->getIdentifier() == II_fgetpos) { 192 Fgetpos(C, CE); 193 return true; 194 } 195 if (FD->getIdentifier() == II_fsetpos) { 196 Fsetpos(C, CE); 197 return true; 198 } 199 if (FD->getIdentifier() == II_clearerr) { 200 Clearerr(C, CE); 201 return true; 202 } 203 if (FD->getIdentifier() == II_feof) { 204 Feof(C, CE); 205 return true; 206 } 207 if (FD->getIdentifier() == II_ferror) { 208 Ferror(C, CE); 209 return true; 210 } 211 if (FD->getIdentifier() == II_fileno) { 212 Fileno(C, CE); 213 return true; 214 } 215 216 return false; 217} 218 219void StreamChecker::Fopen(CheckerContext &C, const CallExpr *CE) { 220 OpenFileAux(C, CE); 221} 222 223void StreamChecker::Tmpfile(CheckerContext &C, const CallExpr *CE) { 224 OpenFileAux(C, CE); 225} 226 227void StreamChecker::OpenFileAux(CheckerContext &C, const CallExpr *CE) { 228 const GRState *state = C.getState(); 229 unsigned Count = C.getNodeBuilder().getCurrentBlockCount(); 230 SValBuilder &svalBuilder = C.getSValBuilder(); 231 DefinedSVal RetVal = 232 cast<DefinedSVal>(svalBuilder.getConjuredSymbolVal(0, CE, Count)); 233 state = state->BindExpr(CE, RetVal); 234 235 ConstraintManager &CM = C.getConstraintManager(); 236 // Bifurcate the state into two: one with a valid FILE* pointer, the other 237 // with a NULL. 238 const GRState *stateNotNull, *stateNull; 239 llvm::tie(stateNotNull, stateNull) = CM.assumeDual(state, RetVal); 240 241 if (SymbolRef Sym = RetVal.getAsSymbol()) { 242 // if RetVal is not NULL, set the symbol's state to Opened. 243 stateNotNull = 244 stateNotNull->set<StreamState>(Sym,StreamState::getOpened(CE)); 245 stateNull = 246 stateNull->set<StreamState>(Sym, StreamState::getOpenFailed(CE)); 247 248 C.addTransition(stateNotNull); 249 C.addTransition(stateNull); 250 } 251} 252 253void StreamChecker::Fclose(CheckerContext &C, const CallExpr *CE) { 254 const GRState *state = CheckDoubleClose(CE, C.getState(), C); 255 if (state) 256 C.addTransition(state); 257} 258 259void StreamChecker::Fread(CheckerContext &C, const CallExpr *CE) { 260 const GRState *state = C.getState(); 261 if (!CheckNullStream(state->getSVal(CE->getArg(3)), state, C)) 262 return; 263} 264 265void StreamChecker::Fwrite(CheckerContext &C, const CallExpr *CE) { 266 const GRState *state = C.getState(); 267 if (!CheckNullStream(state->getSVal(CE->getArg(3)), state, C)) 268 return; 269} 270 271void StreamChecker::Fseek(CheckerContext &C, const CallExpr *CE) { 272 const GRState *state = C.getState(); 273 if (!(state = CheckNullStream(state->getSVal(CE->getArg(0)), state, C))) 274 return; 275 // Check the legality of the 'whence' argument of 'fseek'. 276 SVal Whence = state->getSVal(CE->getArg(2)); 277 const nonloc::ConcreteInt *CI = dyn_cast<nonloc::ConcreteInt>(&Whence); 278 279 if (!CI) 280 return; 281 282 int64_t x = CI->getValue().getSExtValue(); 283 if (x >= 0 && x <= 2) 284 return; 285 286 if (ExplodedNode *N = C.generateNode(state)) { 287 if (!BT_illegalwhence) 288 BT_illegalwhence = new BuiltinBug("Illegal whence argument", 289 "The whence argument to fseek() should be " 290 "SEEK_SET, SEEK_END, or SEEK_CUR."); 291 BugReport *R = new BugReport(*BT_illegalwhence, 292 BT_illegalwhence->getDescription(), N); 293 C.EmitReport(R); 294 } 295} 296 297void StreamChecker::Ftell(CheckerContext &C, const CallExpr *CE) { 298 const GRState *state = C.getState(); 299 if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C)) 300 return; 301} 302 303void StreamChecker::Rewind(CheckerContext &C, const CallExpr *CE) { 304 const GRState *state = C.getState(); 305 if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C)) 306 return; 307} 308 309void StreamChecker::Fgetpos(CheckerContext &C, const CallExpr *CE) { 310 const GRState *state = C.getState(); 311 if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C)) 312 return; 313} 314 315void StreamChecker::Fsetpos(CheckerContext &C, const CallExpr *CE) { 316 const GRState *state = C.getState(); 317 if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C)) 318 return; 319} 320 321void StreamChecker::Clearerr(CheckerContext &C, const CallExpr *CE) { 322 const GRState *state = C.getState(); 323 if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C)) 324 return; 325} 326 327void StreamChecker::Feof(CheckerContext &C, const CallExpr *CE) { 328 const GRState *state = C.getState(); 329 if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C)) 330 return; 331} 332 333void StreamChecker::Ferror(CheckerContext &C, const CallExpr *CE) { 334 const GRState *state = C.getState(); 335 if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C)) 336 return; 337} 338 339void StreamChecker::Fileno(CheckerContext &C, const CallExpr *CE) { 340 const GRState *state = C.getState(); 341 if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C)) 342 return; 343} 344 345const GRState *StreamChecker::CheckNullStream(SVal SV, const GRState *state, 346 CheckerContext &C) { 347 const DefinedSVal *DV = dyn_cast<DefinedSVal>(&SV); 348 if (!DV) 349 return 0; 350 351 ConstraintManager &CM = C.getConstraintManager(); 352 const GRState *stateNotNull, *stateNull; 353 llvm::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV); 354 355 if (!stateNotNull && stateNull) { 356 if (ExplodedNode *N = C.generateSink(stateNull)) { 357 if (!BT_nullfp) 358 BT_nullfp = new BuiltinBug("NULL stream pointer", 359 "Stream pointer might be NULL."); 360 BugReport *R =new BugReport(*BT_nullfp, BT_nullfp->getDescription(), N); 361 C.EmitReport(R); 362 } 363 return 0; 364 } 365 return stateNotNull; 366} 367 368const GRState *StreamChecker::CheckDoubleClose(const CallExpr *CE, 369 const GRState *state, 370 CheckerContext &C) { 371 SymbolRef Sym = state->getSVal(CE->getArg(0)).getAsSymbol(); 372 if (!Sym) 373 return state; 374 375 const StreamState *SS = state->get<StreamState>(Sym); 376 377 // If the file stream is not tracked, return. 378 if (!SS) 379 return state; 380 381 // Check: Double close a File Descriptor could cause undefined behaviour. 382 // Conforming to man-pages 383 if (SS->isClosed()) { 384 ExplodedNode *N = C.generateSink(); 385 if (N) { 386 if (!BT_doubleclose) 387 BT_doubleclose = new BuiltinBug("Double fclose", 388 "Try to close a file Descriptor already" 389 " closed. Cause undefined behaviour."); 390 BugReport *R = new BugReport(*BT_doubleclose, 391 BT_doubleclose->getDescription(), N); 392 C.EmitReport(R); 393 } 394 return NULL; 395 } 396 397 // Close the File Descriptor. 398 return state->set<StreamState>(Sym, StreamState::getClosed(CE)); 399} 400 401void StreamChecker::evalDeadSymbols(CheckerContext &C,SymbolReaper &SymReaper) { 402 for (SymbolReaper::dead_iterator I = SymReaper.dead_begin(), 403 E = SymReaper.dead_end(); I != E; ++I) { 404 SymbolRef Sym = *I; 405 const GRState *state = C.getState(); 406 const StreamState *SS = state->get<StreamState>(Sym); 407 if (!SS) 408 return; 409 410 if (SS->isOpened()) { 411 ExplodedNode *N = C.generateSink(); 412 if (N) { 413 if (!BT_ResourceLeak) 414 BT_ResourceLeak = new BuiltinBug("Resource Leak", 415 "Opened File never closed. Potential Resource leak."); 416 BugReport *R = new BugReport(*BT_ResourceLeak, 417 BT_ResourceLeak->getDescription(), N); 418 C.EmitReport(R); 419 } 420 } 421 } 422} 423 424void StreamChecker::evalEndPath(EndOfFunctionNodeBuilder &B, void *tag, 425 ExprEngine &Eng) { 426 const GRState *state = B.getState(); 427 typedef llvm::ImmutableMap<SymbolRef, StreamState> SymMap; 428 SymMap M = state->get<StreamState>(); 429 430 for (SymMap::iterator I = M.begin(), E = M.end(); I != E; ++I) { 431 StreamState SS = I->second; 432 if (SS.isOpened()) { 433 ExplodedNode *N = B.generateNode(state, tag, B.getPredecessor()); 434 if (N) { 435 if (!BT_ResourceLeak) 436 BT_ResourceLeak = new BuiltinBug("Resource Leak", 437 "Opened File never closed. Potential Resource leak."); 438 BugReport *R = new BugReport(*BT_ResourceLeak, 439 BT_ResourceLeak->getDescription(), N); 440 Eng.getBugReporter().EmitReport(R); 441 } 442 } 443 } 444} 445 446void StreamChecker::PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *S) { 447 const Expr *RetE = S->getRetValue(); 448 if (!RetE) 449 return; 450 451 const GRState *state = C.getState(); 452 SymbolRef Sym = state->getSVal(RetE).getAsSymbol(); 453 454 if (!Sym) 455 return; 456 457 const StreamState *SS = state->get<StreamState>(Sym); 458 if(!SS) 459 return; 460 461 if (SS->isOpened()) 462 state = state->set<StreamState>(Sym, StreamState::getEscaped(S)); 463 464 C.addTransition(state); 465} 466