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