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(C.getSVal(CE->getArg(3)), state, C)) 246 return; 247 } 248 249 void StreamChecker::Fwrite(CheckerContext &C, const CallExpr *CE) const { 250 ProgramStateRef state = C.getState(); 251 if (!CheckNullStream(C.getSVal(CE->getArg(3)), state, C)) 252 return; 253 } 254 255 void StreamChecker::Fseek(CheckerContext &C, const CallExpr *CE) const { 256 ProgramStateRef state = C.getState(); 257 if (!(state = CheckNullStream(C.getSVal(CE->getArg(0)), state, C))) 258 return; 259 // Check the legality of the 'whence' argument of 'fseek'. 260 SVal Whence = state->getSVal(CE->getArg(2), C.getLocationContext()); 261 Optional<nonloc::ConcreteInt> CI = Whence.getAs<nonloc::ConcreteInt>(); 262 263 if (!CI) 264 return; 265 266 int64_t x = CI->getValue().getSExtValue(); 267 if (x >= 0 && x <= 2) 268 return; 269 270 if (ExplodedNode *N = C.generateNonFatalErrorNode(state)) { 271 if (!BT_illegalwhence) 272 BT_illegalwhence.reset( 273 new BuiltinBug(this, "Illegal whence argument", 274 "The whence argument to fseek() should be " 275 "SEEK_SET, SEEK_END, or SEEK_CUR.")); 276 C.emitReport(llvm::make_unique<BugReport>( 277 *BT_illegalwhence, BT_illegalwhence->getDescription(), N)); 278 } 279 } 280 281 void StreamChecker::Ftell(CheckerContext &C, const CallExpr *CE) const { 282 ProgramStateRef state = C.getState(); 283 if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C)) 284 return; 285 } 286 287 void StreamChecker::Rewind(CheckerContext &C, const CallExpr *CE) const { 288 ProgramStateRef state = C.getState(); 289 if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C)) 290 return; 291 } 292 293 void StreamChecker::Fgetpos(CheckerContext &C, const CallExpr *CE) const { 294 ProgramStateRef state = C.getState(); 295 if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C)) 296 return; 297 } 298 299 void StreamChecker::Fsetpos(CheckerContext &C, const CallExpr *CE) const { 300 ProgramStateRef state = C.getState(); 301 if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C)) 302 return; 303 } 304 305 void StreamChecker::Clearerr(CheckerContext &C, const CallExpr *CE) const { 306 ProgramStateRef state = C.getState(); 307 if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C)) 308 return; 309 } 310 311 void StreamChecker::Feof(CheckerContext &C, const CallExpr *CE) const { 312 ProgramStateRef state = C.getState(); 313 if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C)) 314 return; 315 } 316 317 void StreamChecker::Ferror(CheckerContext &C, const CallExpr *CE) const { 318 ProgramStateRef state = C.getState(); 319 if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C)) 320 return; 321 } 322 323 void StreamChecker::Fileno(CheckerContext &C, const CallExpr *CE) const { 324 ProgramStateRef state = C.getState(); 325 if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C)) 326 return; 327 } 328 329 ProgramStateRef StreamChecker::CheckNullStream(SVal SV, ProgramStateRef state, 330 CheckerContext &C) const { 331 Optional<DefinedSVal> DV = SV.getAs<DefinedSVal>(); 332 if (!DV) 333 return nullptr; 334 335 ConstraintManager &CM = C.getConstraintManager(); 336 ProgramStateRef stateNotNull, stateNull; 337 std::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV); 338 339 if (!stateNotNull && stateNull) { 340 if (ExplodedNode *N = C.generateErrorNode(stateNull)) { 341 if (!BT_nullfp) 342 BT_nullfp.reset(new BuiltinBug(this, "NULL stream pointer", 343 "Stream pointer might be NULL.")); 344 C.emitReport(llvm::make_unique<BugReport>( 345 *BT_nullfp, BT_nullfp->getDescription(), N)); 346 } 347 return nullptr; 348 } 349 return stateNotNull; 350 } 351 352 ProgramStateRef StreamChecker::CheckDoubleClose(const CallExpr *CE, 353 ProgramStateRef state, 354 CheckerContext &C) const { 355 SymbolRef Sym = C.getSVal(CE->getArg(0)).getAsSymbol(); 356 if (!Sym) 357 return state; 358 359 const StreamState *SS = state->get<StreamMap>(Sym); 360 361 // If the file stream is not tracked, return. 362 if (!SS) 363 return state; 364 365 // Check: Double close a File Descriptor could cause undefined behaviour. 366 // Conforming to man-pages 367 if (SS->isClosed()) { 368 ExplodedNode *N = C.generateErrorNode(); 369 if (N) { 370 if (!BT_doubleclose) 371 BT_doubleclose.reset(new BuiltinBug( 372 this, "Double fclose", "Try to close a file Descriptor already" 373 " closed. Cause undefined behaviour.")); 374 C.emitReport(llvm::make_unique<BugReport>( 375 *BT_doubleclose, BT_doubleclose->getDescription(), N)); 376 } 377 return nullptr; 378 } 379 380 // Close the File Descriptor. 381 return state->set<StreamMap>(Sym, StreamState::getClosed(CE)); 382 } 383 384 void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper, 385 CheckerContext &C) const { 386 // TODO: Clean up the state. 387 for (SymbolReaper::dead_iterator I = SymReaper.dead_begin(), 388 E = SymReaper.dead_end(); I != E; ++I) { 389 SymbolRef Sym = *I; 390 ProgramStateRef state = C.getState(); 391 const StreamState *SS = state->get<StreamMap>(Sym); 392 if (!SS) 393 continue; 394 395 if (SS->isOpened()) { 396 ExplodedNode *N = C.generateErrorNode(); 397 if (N) { 398 if (!BT_ResourceLeak) 399 BT_ResourceLeak.reset(new BuiltinBug( 400 this, "Resource Leak", 401 "Opened File never closed. Potential Resource leak.")); 402 C.emitReport(llvm::make_unique<BugReport>( 403 *BT_ResourceLeak, BT_ResourceLeak->getDescription(), N)); 404 } 405 } 406 } 407 } 408 409 void ento::registerStreamChecker(CheckerManager &mgr) { 410 mgr.registerChecker<StreamChecker>(); 411 } 412