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