StreamChecker.cpp revision 302408
11897Swollman//===-- StreamChecker.cpp -----------------------------------------*- C++ -*--// 21897Swollman// 31897Swollman// The LLVM Compiler Infrastructure 41897Swollman// 51897Swollman// This file is distributed under the University of Illinois Open Source 61897Swollman// License. See LICENSE.TXT for details. 71897Swollman// 812798Swpaul//===----------------------------------------------------------------------===// 91897Swollman// 101897Swollman// This file defines checkers that model and check stream handling functions. 111897Swollman// 1212798Swpaul//===----------------------------------------------------------------------===// 131897Swollman 141897Swollman#include "ClangSACheckers.h" 151897Swollman#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 1612798Swpaul#include "clang/StaticAnalyzer/Core/Checker.h" 171897Swollman#include "clang/StaticAnalyzer/Core/CheckerManager.h" 181897Swollman#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 191897Swollman#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" 2012798Swpaul#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" 211897Swollman#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" 221897Swollman#include "llvm/ADT/ImmutableMap.h" 231897Swollman 2412798Swpaulusing namespace clang; 251897Swollmanusing namespace ento; 261897Swollman 271897Swollmannamespace { 281897Swollman 2912798Swpaulstruct StreamState { 301897Swollman enum Kind { Opened, Closed, OpenFailed, Escaped } K; 3112798Swpaul const Stmt *S; 3212798Swpaul 3312798Swpaul StreamState(Kind k, const Stmt *s) : K(k), S(s) {} 3412798Swpaul 3512798Swpaul bool isOpened() const { return K == Opened; } 3612798Swpaul bool isClosed() const { return K == Closed; } 3712798Swpaul //bool isOpenFailed() const { return K == OpenFailed; } 3812798Swpaul //bool isEscaped() const { return K == Escaped; } 3912798Swpaul 4012798Swpaul bool operator==(const StreamState &X) const { 4112798Swpaul return K == X.K && S == X.S; 4212798Swpaul } 4312798Swpaul 4412798Swpaul static StreamState getOpened(const Stmt *s) { return StreamState(Opened, s); } 4512798Swpaul static StreamState getClosed(const Stmt *s) { return StreamState(Closed, s); } 4612798Swpaul static StreamState getOpenFailed(const Stmt *s) { 4712798Swpaul return StreamState(OpenFailed, s); 4812798Swpaul } 4912798Swpaul static StreamState getEscaped(const Stmt *s) { 5012798Swpaul return StreamState(Escaped, s); 5112798Swpaul } 5212798Swpaul 5312798Swpaul void Profile(llvm::FoldingSetNodeID &ID) const { 5412798Swpaul ID.AddInteger(K); 5512798Swpaul ID.AddPointer(S); 5612798Swpaul } 5712798Swpaul}; 5812798Swpaul 5912798Swpaulclass StreamChecker : public Checker<eval::Call, 6012798Swpaul check::DeadSymbols > { 6112798Swpaul mutable IdentifierInfo *II_fopen, *II_tmpfile, *II_fclose, *II_fread, 6212798Swpaul *II_fwrite, 631897Swollman *II_fseek, *II_ftell, *II_rewind, *II_fgetpos, *II_fsetpos, 6412798Swpaul *II_clearerr, *II_feof, *II_ferror, *II_fileno; 651897Swollman mutable std::unique_ptr<BuiltinBug> BT_nullfp, BT_illegalwhence, 661897Swollman BT_doubleclose, BT_ResourceLeak; 671897Swollman 6812798Swpaulpublic: 691897Swollman StreamChecker() 701897Swollman : II_fopen(nullptr), II_tmpfile(nullptr), II_fclose(nullptr), 711897Swollman II_fread(nullptr), II_fwrite(nullptr), II_fseek(nullptr), 7212798Swpaul II_ftell(nullptr), II_rewind(nullptr), II_fgetpos(nullptr), 731897Swollman II_fsetpos(nullptr), II_clearerr(nullptr), II_feof(nullptr), 741897Swollman II_ferror(nullptr), II_fileno(nullptr) {} 751897Swollman 761897Swollman bool evalCall(const CallExpr *CE, CheckerContext &C) const; 771897Swollman void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; 781897Swollman 791897Swollmanprivate: 801897Swollman void Fopen(CheckerContext &C, const CallExpr *CE) const; 811897Swollman void Tmpfile(CheckerContext &C, const CallExpr *CE) const; 821897Swollman void Fclose(CheckerContext &C, const CallExpr *CE) const; 831897Swollman void Fread(CheckerContext &C, const CallExpr *CE) const; 841897Swollman void Fwrite(CheckerContext &C, const CallExpr *CE) const; 851897Swollman void Fseek(CheckerContext &C, const CallExpr *CE) const; 861897Swollman void Ftell(CheckerContext &C, const CallExpr *CE) const; 871897Swollman void Rewind(CheckerContext &C, const CallExpr *CE) const; 881897Swollman void Fgetpos(CheckerContext &C, const CallExpr *CE) const; 891897Swollman void Fsetpos(CheckerContext &C, const CallExpr *CE) const; 901897Swollman void Clearerr(CheckerContext &C, const CallExpr *CE) const; 911897Swollman void Feof(CheckerContext &C, const CallExpr *CE) const; 921897Swollman void Ferror(CheckerContext &C, const CallExpr *CE) const; 931897Swollman void Fileno(CheckerContext &C, const CallExpr *CE) const; 941897Swollman 951897Swollman void OpenFileAux(CheckerContext &C, const CallExpr *CE) const; 961897Swollman 971897Swollman ProgramStateRef CheckNullStream(SVal SV, ProgramStateRef state, 9812798Swpaul CheckerContext &C) const; 991897Swollman ProgramStateRef CheckDoubleClose(const CallExpr *CE, ProgramStateRef state, 1001897Swollman CheckerContext &C) const; 1011897Swollman}; 10212798Swpaul 1031897Swollman} // end anonymous namespace 1041897Swollman 1051897SwollmanREGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState) 1061897Swollman 1071897Swollman 1081897Swollmanbool StreamChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { 1091897Swollman const FunctionDecl *FD = C.getCalleeDecl(CE); 1101897Swollman if (!FD || FD->getKind() != Decl::Function) 1111897Swollman return false; 1121897Swollman 1131897Swollman ASTContext &Ctx = C.getASTContext(); 1141897Swollman if (!II_fopen) 11512798Swpaul II_fopen = &Ctx.Idents.get("fopen"); 1161897Swollman if (!II_tmpfile) 1171897Swollman II_tmpfile = &Ctx.Idents.get("tmpfile"); 1181897Swollman if (!II_fclose) 1191897Swollman II_fclose = &Ctx.Idents.get("fclose"); 1201897Swollman if (!II_fread) 1211897Swollman II_fread = &Ctx.Idents.get("fread"); 1221897Swollman if (!II_fwrite) 1231897Swollman II_fwrite = &Ctx.Idents.get("fwrite"); 1241897Swollman if (!II_fseek) 12512798Swpaul II_fseek = &Ctx.Idents.get("fseek"); 1261897Swollman if (!II_ftell) 1271897Swollman II_ftell = &Ctx.Idents.get("ftell"); 1281897Swollman if (!II_rewind) 1291897Swollman II_rewind = &Ctx.Idents.get("rewind"); 1301897Swollman if (!II_fgetpos) 1311897Swollman II_fgetpos = &Ctx.Idents.get("fgetpos"); 1321897Swollman if (!II_fsetpos) 1331897Swollman II_fsetpos = &Ctx.Idents.get("fsetpos"); 134 if (!II_clearerr) 135 II_clearerr = &Ctx.Idents.get("clearerr"); 136 if (!II_feof) 137 II_feof = &Ctx.Idents.get("feof"); 138 if (!II_ferror) 139 II_ferror = &Ctx.Idents.get("ferror"); 140 if (!II_fileno) 141 II_fileno = &Ctx.Idents.get("fileno"); 142 143 if (FD->getIdentifier() == II_fopen) { 144 Fopen(C, CE); 145 return true; 146 } 147 if (FD->getIdentifier() == II_tmpfile) { 148 Tmpfile(C, CE); 149 return true; 150 } 151 if (FD->getIdentifier() == II_fclose) { 152 Fclose(C, CE); 153 return true; 154 } 155 if (FD->getIdentifier() == II_fread) { 156 Fread(C, CE); 157 return true; 158 } 159 if (FD->getIdentifier() == II_fwrite) { 160 Fwrite(C, CE); 161 return true; 162 } 163 if (FD->getIdentifier() == II_fseek) { 164 Fseek(C, CE); 165 return true; 166 } 167 if (FD->getIdentifier() == II_ftell) { 168 Ftell(C, CE); 169 return true; 170 } 171 if (FD->getIdentifier() == II_rewind) { 172 Rewind(C, CE); 173 return true; 174 } 175 if (FD->getIdentifier() == II_fgetpos) { 176 Fgetpos(C, CE); 177 return true; 178 } 179 if (FD->getIdentifier() == II_fsetpos) { 180 Fsetpos(C, CE); 181 return true; 182 } 183 if (FD->getIdentifier() == II_clearerr) { 184 Clearerr(C, CE); 185 return true; 186 } 187 if (FD->getIdentifier() == II_feof) { 188 Feof(C, CE); 189 return true; 190 } 191 if (FD->getIdentifier() == II_ferror) { 192 Ferror(C, CE); 193 return true; 194 } 195 if (FD->getIdentifier() == II_fileno) { 196 Fileno(C, CE); 197 return true; 198 } 199 200 return false; 201} 202 203void StreamChecker::Fopen(CheckerContext &C, const CallExpr *CE) const { 204 OpenFileAux(C, CE); 205} 206 207void StreamChecker::Tmpfile(CheckerContext &C, const CallExpr *CE) const { 208 OpenFileAux(C, CE); 209} 210 211void StreamChecker::OpenFileAux(CheckerContext &C, const CallExpr *CE) const { 212 ProgramStateRef state = C.getState(); 213 SValBuilder &svalBuilder = C.getSValBuilder(); 214 const LocationContext *LCtx = C.getPredecessor()->getLocationContext(); 215 DefinedSVal RetVal = svalBuilder.conjureSymbolVal(nullptr, CE, LCtx, 216 C.blockCount()) 217 .castAs<DefinedSVal>(); 218 state = state->BindExpr(CE, C.getLocationContext(), RetVal); 219 220 ConstraintManager &CM = C.getConstraintManager(); 221 // Bifurcate the state into two: one with a valid FILE* pointer, the other 222 // with a NULL. 223 ProgramStateRef stateNotNull, stateNull; 224 std::tie(stateNotNull, stateNull) = CM.assumeDual(state, RetVal); 225 226 if (SymbolRef Sym = RetVal.getAsSymbol()) { 227 // if RetVal is not NULL, set the symbol's state to Opened. 228 stateNotNull = 229 stateNotNull->set<StreamMap>(Sym,StreamState::getOpened(CE)); 230 stateNull = 231 stateNull->set<StreamMap>(Sym, StreamState::getOpenFailed(CE)); 232 233 C.addTransition(stateNotNull); 234 C.addTransition(stateNull); 235 } 236} 237 238void StreamChecker::Fclose(CheckerContext &C, const CallExpr *CE) const { 239 ProgramStateRef state = CheckDoubleClose(CE, C.getState(), C); 240 if (state) 241 C.addTransition(state); 242} 243 244void StreamChecker::Fread(CheckerContext &C, const CallExpr *CE) const { 245 ProgramStateRef state = C.getState(); 246 if (!CheckNullStream(state->getSVal(CE->getArg(3), C.getLocationContext()), 247 state, C)) 248 return; 249} 250 251void StreamChecker::Fwrite(CheckerContext &C, const CallExpr *CE) const { 252 ProgramStateRef state = C.getState(); 253 if (!CheckNullStream(state->getSVal(CE->getArg(3), C.getLocationContext()), 254 state, C)) 255 return; 256} 257 258void StreamChecker::Fseek(CheckerContext &C, const CallExpr *CE) const { 259 ProgramStateRef state = C.getState(); 260 if (!(state = CheckNullStream(state->getSVal(CE->getArg(0), 261 C.getLocationContext()), state, C))) 262 return; 263 // Check the legality of the 'whence' argument of 'fseek'. 264 SVal Whence = state->getSVal(CE->getArg(2), C.getLocationContext()); 265 Optional<nonloc::ConcreteInt> CI = Whence.getAs<nonloc::ConcreteInt>(); 266 267 if (!CI) 268 return; 269 270 int64_t x = CI->getValue().getSExtValue(); 271 if (x >= 0 && x <= 2) 272 return; 273 274 if (ExplodedNode *N = C.generateNonFatalErrorNode(state)) { 275 if (!BT_illegalwhence) 276 BT_illegalwhence.reset( 277 new BuiltinBug(this, "Illegal whence argument", 278 "The whence argument to fseek() should be " 279 "SEEK_SET, SEEK_END, or SEEK_CUR.")); 280 C.emitReport(llvm::make_unique<BugReport>( 281 *BT_illegalwhence, BT_illegalwhence->getDescription(), N)); 282 } 283} 284 285void StreamChecker::Ftell(CheckerContext &C, const CallExpr *CE) const { 286 ProgramStateRef state = C.getState(); 287 if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()), 288 state, C)) 289 return; 290} 291 292void StreamChecker::Rewind(CheckerContext &C, const CallExpr *CE) const { 293 ProgramStateRef state = C.getState(); 294 if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()), 295 state, C)) 296 return; 297} 298 299void StreamChecker::Fgetpos(CheckerContext &C, const CallExpr *CE) const { 300 ProgramStateRef state = C.getState(); 301 if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()), 302 state, C)) 303 return; 304} 305 306void StreamChecker::Fsetpos(CheckerContext &C, const CallExpr *CE) const { 307 ProgramStateRef state = C.getState(); 308 if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()), 309 state, C)) 310 return; 311} 312 313void StreamChecker::Clearerr(CheckerContext &C, const CallExpr *CE) const { 314 ProgramStateRef state = C.getState(); 315 if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()), 316 state, C)) 317 return; 318} 319 320void StreamChecker::Feof(CheckerContext &C, const CallExpr *CE) const { 321 ProgramStateRef state = C.getState(); 322 if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()), 323 state, C)) 324 return; 325} 326 327void StreamChecker::Ferror(CheckerContext &C, const CallExpr *CE) const { 328 ProgramStateRef state = C.getState(); 329 if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()), 330 state, C)) 331 return; 332} 333 334void StreamChecker::Fileno(CheckerContext &C, const CallExpr *CE) const { 335 ProgramStateRef state = C.getState(); 336 if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()), 337 state, C)) 338 return; 339} 340 341ProgramStateRef StreamChecker::CheckNullStream(SVal SV, ProgramStateRef state, 342 CheckerContext &C) const { 343 Optional<DefinedSVal> DV = SV.getAs<DefinedSVal>(); 344 if (!DV) 345 return nullptr; 346 347 ConstraintManager &CM = C.getConstraintManager(); 348 ProgramStateRef stateNotNull, stateNull; 349 std::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV); 350 351 if (!stateNotNull && stateNull) { 352 if (ExplodedNode *N = C.generateErrorNode(stateNull)) { 353 if (!BT_nullfp) 354 BT_nullfp.reset(new BuiltinBug(this, "NULL stream pointer", 355 "Stream pointer might be NULL.")); 356 C.emitReport(llvm::make_unique<BugReport>( 357 *BT_nullfp, BT_nullfp->getDescription(), N)); 358 } 359 return nullptr; 360 } 361 return stateNotNull; 362} 363 364ProgramStateRef StreamChecker::CheckDoubleClose(const CallExpr *CE, 365 ProgramStateRef state, 366 CheckerContext &C) const { 367 SymbolRef Sym = 368 state->getSVal(CE->getArg(0), C.getLocationContext()).getAsSymbol(); 369 if (!Sym) 370 return state; 371 372 const StreamState *SS = state->get<StreamMap>(Sym); 373 374 // If the file stream is not tracked, return. 375 if (!SS) 376 return state; 377 378 // Check: Double close a File Descriptor could cause undefined behaviour. 379 // Conforming to man-pages 380 if (SS->isClosed()) { 381 ExplodedNode *N = C.generateErrorNode(); 382 if (N) { 383 if (!BT_doubleclose) 384 BT_doubleclose.reset(new BuiltinBug( 385 this, "Double fclose", "Try to close a file Descriptor already" 386 " closed. Cause undefined behaviour.")); 387 C.emitReport(llvm::make_unique<BugReport>( 388 *BT_doubleclose, BT_doubleclose->getDescription(), N)); 389 } 390 return nullptr; 391 } 392 393 // Close the File Descriptor. 394 return state->set<StreamMap>(Sym, StreamState::getClosed(CE)); 395} 396 397void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper, 398 CheckerContext &C) const { 399 // TODO: Clean up the state. 400 for (SymbolReaper::dead_iterator I = SymReaper.dead_begin(), 401 E = SymReaper.dead_end(); I != E; ++I) { 402 SymbolRef Sym = *I; 403 ProgramStateRef state = C.getState(); 404 const StreamState *SS = state->get<StreamMap>(Sym); 405 if (!SS) 406 continue; 407 408 if (SS->isOpened()) { 409 ExplodedNode *N = C.generateErrorNode(); 410 if (N) { 411 if (!BT_ResourceLeak) 412 BT_ResourceLeak.reset(new BuiltinBug( 413 this, "Resource Leak", 414 "Opened File never closed. Potential Resource leak.")); 415 C.emitReport(llvm::make_unique<BugReport>( 416 *BT_ResourceLeak, BT_ResourceLeak->getDescription(), N)); 417 } 418 } 419 } 420} 421 422void ento::registerStreamChecker(CheckerManager &mgr) { 423 mgr.registerChecker<StreamChecker>(); 424} 425