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/BugReporter/BugType.h" 16 #include "clang/StaticAnalyzer/Core/Checker.h" 17 #include "clang/StaticAnalyzer/Core/CheckerManager.h" 18 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.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 23 using namespace clang; 24 using namespace ento; 25 26 namespace { 27 28 struct 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 58 class StreamChecker : public Checker<eval::Call, 59 check::DeadSymbols > { 60 mutable IdentifierInfo *II_fopen, *II_tmpfile, *II_fclose, *II_fread, 61 *II_fwrite, 62 *II_fseek, *II_ftell, *II_rewind, *II_fgetpos, *II_fsetpos, 63 *II_clearerr, *II_feof, *II_ferror, *II_fileno; 64 mutable std::unique_ptr<BuiltinBug> BT_nullfp, BT_illegalwhence, 65 BT_doubleclose, BT_ResourceLeak; 66 67 public: 68 StreamChecker() 69 : II_fopen(nullptr), II_tmpfile(nullptr), II_fclose(nullptr), 70 II_fread(nullptr), II_fwrite(nullptr), II_fseek(nullptr), 71 II_ftell(nullptr), II_rewind(nullptr), II_fgetpos(nullptr), 72 II_fsetpos(nullptr), II_clearerr(nullptr), II_feof(nullptr), 73 II_ferror(nullptr), II_fileno(nullptr) {} 74 75 bool evalCall(const CallExpr *CE, CheckerContext &C) const; 76 void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; 77 78 private: 79 void Fopen(CheckerContext &C, const CallExpr *CE) const; 80 void Tmpfile(CheckerContext &C, const CallExpr *CE) const; 81 void Fclose(CheckerContext &C, const CallExpr *CE) const; 82 void Fread(CheckerContext &C, const CallExpr *CE) const; 83 void Fwrite(CheckerContext &C, const CallExpr *CE) const; 84 void Fseek(CheckerContext &C, const CallExpr *CE) const; 85 void Ftell(CheckerContext &C, const CallExpr *CE) const; 86 void Rewind(CheckerContext &C, const CallExpr *CE) const; 87 void Fgetpos(CheckerContext &C, const CallExpr *CE) const; 88 void Fsetpos(CheckerContext &C, const CallExpr *CE) const; 89 void Clearerr(CheckerContext &C, const CallExpr *CE) const; 90 void Feof(CheckerContext &C, const CallExpr *CE) const; 91 void Ferror(CheckerContext &C, const CallExpr *CE) const; 92 void Fileno(CheckerContext &C, const CallExpr *CE) const; 93 94 void OpenFileAux(CheckerContext &C, const CallExpr *CE) const; 95 96 ProgramStateRef CheckNullStream(SVal SV, ProgramStateRef state, 97 CheckerContext &C) const; 98 ProgramStateRef CheckDoubleClose(const CallExpr *CE, ProgramStateRef state, 99 CheckerContext &C) const; 100 }; 101 102 } // end anonymous namespace 103 104 REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState) 105 106 107 bool StreamChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { 108 const FunctionDecl *FD = C.getCalleeDecl(CE); 109 if (!FD || FD->getKind() != Decl::Function) 110 return false; 111 112 ASTContext &Ctx = C.getASTContext(); 113 if (!II_fopen) 114 II_fopen = &Ctx.Idents.get("fopen"); 115 if (!II_tmpfile) 116 II_tmpfile = &Ctx.Idents.get("tmpfile"); 117 if (!II_fclose) 118 II_fclose = &Ctx.Idents.get("fclose"); 119 if (!II_fread) 120 II_fread = &Ctx.Idents.get("fread"); 121 if (!II_fwrite) 122 II_fwrite = &Ctx.Idents.get("fwrite"); 123 if (!II_fseek) 124 II_fseek = &Ctx.Idents.get("fseek"); 125 if (!II_ftell) 126 II_ftell = &Ctx.Idents.get("ftell"); 127 if (!II_rewind) 128 II_rewind = &Ctx.Idents.get("rewind"); 129 if (!II_fgetpos) 130 II_fgetpos = &Ctx.Idents.get("fgetpos"); 131 if (!II_fsetpos) 132 II_fsetpos = &Ctx.Idents.get("fsetpos"); 133 if (!II_clearerr) 134 II_clearerr = &Ctx.Idents.get("clearerr"); 135 if (!II_feof) 136 II_feof = &Ctx.Idents.get("feof"); 137 if (!II_ferror) 138 II_ferror = &Ctx.Idents.get("ferror"); 139 if (!II_fileno) 140 II_fileno = &Ctx.Idents.get("fileno"); 141 142 if (FD->getIdentifier() == II_fopen) { 143 Fopen(C, CE); 144 return true; 145 } 146 if (FD->getIdentifier() == II_tmpfile) { 147 Tmpfile(C, CE); 148 return true; 149 } 150 if (FD->getIdentifier() == II_fclose) { 151 Fclose(C, CE); 152 return true; 153 } 154 if (FD->getIdentifier() == II_fread) { 155 Fread(C, CE); 156 return true; 157 } 158 if (FD->getIdentifier() == II_fwrite) { 159 Fwrite(C, CE); 160 return true; 161 } 162 if (FD->getIdentifier() == II_fseek) { 163 Fseek(C, CE); 164 return true; 165 } 166 if (FD->getIdentifier() == II_ftell) { 167 Ftell(C, CE); 168 return true; 169 } 170 if (FD->getIdentifier() == II_rewind) { 171 Rewind(C, CE); 172 return true; 173 } 174 if (FD->getIdentifier() == II_fgetpos) { 175 Fgetpos(C, CE); 176 return true; 177 } 178 if (FD->getIdentifier() == II_fsetpos) { 179 Fsetpos(C, CE); 180 return true; 181 } 182 if (FD->getIdentifier() == II_clearerr) { 183 Clearerr(C, CE); 184 return true; 185 } 186 if (FD->getIdentifier() == II_feof) { 187 Feof(C, CE); 188 return true; 189 } 190 if (FD->getIdentifier() == II_ferror) { 191 Ferror(C, CE); 192 return true; 193 } 194 if (FD->getIdentifier() == II_fileno) { 195 Fileno(C, CE); 196 return true; 197 } 198 199 return false; 200 } 201 202 void StreamChecker::Fopen(CheckerContext &C, const CallExpr *CE) const { 203 OpenFileAux(C, CE); 204 } 205 206 void StreamChecker::Tmpfile(CheckerContext &C, const CallExpr *CE) const { 207 OpenFileAux(C, CE); 208 } 209 210 void StreamChecker::OpenFileAux(CheckerContext &C, const CallExpr *CE) const { 211 ProgramStateRef state = C.getState(); 212 SValBuilder &svalBuilder = C.getSValBuilder(); 213 const LocationContext *LCtx = C.getPredecessor()->getLocationContext(); 214 DefinedSVal RetVal = svalBuilder.conjureSymbolVal(nullptr, CE, LCtx, 215 C.blockCount()) 216 .castAs<DefinedSVal>(); 217 state = state->BindExpr(CE, C.getLocationContext(), RetVal); 218 219 ConstraintManager &CM = C.getConstraintManager(); 220 // Bifurcate the state into two: one with a valid FILE* pointer, the other 221 // with a NULL. 222 ProgramStateRef stateNotNull, stateNull; 223 std::tie(stateNotNull, stateNull) = CM.assumeDual(state, RetVal); 224 225 if (SymbolRef Sym = RetVal.getAsSymbol()) { 226 // if RetVal is not NULL, set the symbol's state to Opened. 227 stateNotNull = 228 stateNotNull->set<StreamMap>(Sym,StreamState::getOpened(CE)); 229 stateNull = 230 stateNull->set<StreamMap>(Sym, StreamState::getOpenFailed(CE)); 231 232 C.addTransition(stateNotNull); 233 C.addTransition(stateNull); 234 } 235 } 236 237 void StreamChecker::Fclose(CheckerContext &C, const CallExpr *CE) const { 238 ProgramStateRef state = CheckDoubleClose(CE, C.getState(), C); 239 if (state) 240 C.addTransition(state); 241 } 242 243 void StreamChecker::Fread(CheckerContext &C, const CallExpr *CE) const { 244 ProgramStateRef state = C.getState(); 245 if (!CheckNullStream(state->getSVal(CE->getArg(3), C.getLocationContext()), 246 state, C)) 247 return; 248 } 249 250 void StreamChecker::Fwrite(CheckerContext &C, const CallExpr *CE) const { 251 ProgramStateRef state = C.getState(); 252 if (!CheckNullStream(state->getSVal(CE->getArg(3), C.getLocationContext()), 253 state, C)) 254 return; 255 } 256 257 void StreamChecker::Fseek(CheckerContext &C, const CallExpr *CE) const { 258 ProgramStateRef state = C.getState(); 259 if (!(state = CheckNullStream(state->getSVal(CE->getArg(0), 260 C.getLocationContext()), state, C))) 261 return; 262 // Check the legality of the 'whence' argument of 'fseek'. 263 SVal Whence = state->getSVal(CE->getArg(2), C.getLocationContext()); 264 Optional<nonloc::ConcreteInt> CI = Whence.getAs<nonloc::ConcreteInt>(); 265 266 if (!CI) 267 return; 268 269 int64_t x = CI->getValue().getSExtValue(); 270 if (x >= 0 && x <= 2) 271 return; 272 273 if (ExplodedNode *N = C.generateNonFatalErrorNode(state)) { 274 if (!BT_illegalwhence) 275 BT_illegalwhence.reset( 276 new BuiltinBug(this, "Illegal whence argument", 277 "The whence argument to fseek() should be " 278 "SEEK_SET, SEEK_END, or SEEK_CUR.")); 279 C.emitReport(llvm::make_unique<BugReport>( 280 *BT_illegalwhence, BT_illegalwhence->getDescription(), N)); 281 } 282 } 283 284 void StreamChecker::Ftell(CheckerContext &C, const CallExpr *CE) const { 285 ProgramStateRef state = C.getState(); 286 if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()), 287 state, C)) 288 return; 289 } 290 291 void StreamChecker::Rewind(CheckerContext &C, const CallExpr *CE) const { 292 ProgramStateRef state = C.getState(); 293 if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()), 294 state, C)) 295 return; 296 } 297 298 void StreamChecker::Fgetpos(CheckerContext &C, const CallExpr *CE) const { 299 ProgramStateRef state = C.getState(); 300 if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()), 301 state, C)) 302 return; 303 } 304 305 void StreamChecker::Fsetpos(CheckerContext &C, const CallExpr *CE) const { 306 ProgramStateRef state = C.getState(); 307 if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()), 308 state, C)) 309 return; 310 } 311 312 void StreamChecker::Clearerr(CheckerContext &C, const CallExpr *CE) const { 313 ProgramStateRef state = C.getState(); 314 if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()), 315 state, C)) 316 return; 317 } 318 319 void StreamChecker::Feof(CheckerContext &C, const CallExpr *CE) const { 320 ProgramStateRef state = C.getState(); 321 if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()), 322 state, C)) 323 return; 324 } 325 326 void StreamChecker::Ferror(CheckerContext &C, const CallExpr *CE) const { 327 ProgramStateRef state = C.getState(); 328 if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()), 329 state, C)) 330 return; 331 } 332 333 void StreamChecker::Fileno(CheckerContext &C, const CallExpr *CE) const { 334 ProgramStateRef state = C.getState(); 335 if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()), 336 state, C)) 337 return; 338 } 339 340 ProgramStateRef StreamChecker::CheckNullStream(SVal SV, ProgramStateRef state, 341 CheckerContext &C) const { 342 Optional<DefinedSVal> DV = SV.getAs<DefinedSVal>(); 343 if (!DV) 344 return nullptr; 345 346 ConstraintManager &CM = C.getConstraintManager(); 347 ProgramStateRef stateNotNull, stateNull; 348 std::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV); 349 350 if (!stateNotNull && stateNull) { 351 if (ExplodedNode *N = C.generateErrorNode(stateNull)) { 352 if (!BT_nullfp) 353 BT_nullfp.reset(new BuiltinBug(this, "NULL stream pointer", 354 "Stream pointer might be NULL.")); 355 C.emitReport(llvm::make_unique<BugReport>( 356 *BT_nullfp, BT_nullfp->getDescription(), N)); 357 } 358 return nullptr; 359 } 360 return stateNotNull; 361 } 362 363 ProgramStateRef StreamChecker::CheckDoubleClose(const CallExpr *CE, 364 ProgramStateRef state, 365 CheckerContext &C) const { 366 SymbolRef Sym = 367 state->getSVal(CE->getArg(0), C.getLocationContext()).getAsSymbol(); 368 if (!Sym) 369 return state; 370 371 const StreamState *SS = state->get<StreamMap>(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.generateErrorNode(); 381 if (N) { 382 if (!BT_doubleclose) 383 BT_doubleclose.reset(new BuiltinBug( 384 this, "Double fclose", "Try to close a file Descriptor already" 385 " closed. Cause undefined behaviour.")); 386 C.emitReport(llvm::make_unique<BugReport>( 387 *BT_doubleclose, BT_doubleclose->getDescription(), N)); 388 } 389 return nullptr; 390 } 391 392 // Close the File Descriptor. 393 return state->set<StreamMap>(Sym, StreamState::getClosed(CE)); 394 } 395 396 void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper, 397 CheckerContext &C) const { 398 // TODO: Clean up the state. 399 for (SymbolReaper::dead_iterator I = SymReaper.dead_begin(), 400 E = SymReaper.dead_end(); I != E; ++I) { 401 SymbolRef Sym = *I; 402 ProgramStateRef state = C.getState(); 403 const StreamState *SS = state->get<StreamMap>(Sym); 404 if (!SS) 405 continue; 406 407 if (SS->isOpened()) { 408 ExplodedNode *N = C.generateErrorNode(); 409 if (N) { 410 if (!BT_ResourceLeak) 411 BT_ResourceLeak.reset(new BuiltinBug( 412 this, "Resource Leak", 413 "Opened File never closed. Potential Resource leak.")); 414 C.emitReport(llvm::make_unique<BugReport>( 415 *BT_ResourceLeak, BT_ResourceLeak->getDescription(), N)); 416 } 417 } 418 } 419 } 420 421 void ento::registerStreamChecker(CheckerManager &mgr) { 422 mgr.registerChecker<StreamChecker>(); 423 } 424