clang API Documentation
00001 //===-- StreamChecker.cpp -----------------------------------------*- C++ -*--// 00002 // 00003 // The LLVM Compiler Infrastructure 00004 // 00005 // This file is distributed under the University of Illinois Open Source 00006 // License. See LICENSE.TXT for details. 00007 // 00008 //===----------------------------------------------------------------------===// 00009 // 00010 // This file defines checkers that model and check stream handling functions. 00011 // 00012 //===----------------------------------------------------------------------===// 00013 00014 #include "ClangSACheckers.h" 00015 #include "clang/StaticAnalyzer/Core/Checker.h" 00016 #include "clang/StaticAnalyzer/Core/CheckerManager.h" 00017 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 00018 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 00019 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" 00020 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" 00021 #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" 00022 #include "llvm/ADT/ImmutableMap.h" 00023 00024 using namespace clang; 00025 using namespace ento; 00026 00027 namespace { 00028 00029 struct StreamState { 00030 enum Kind { Opened, Closed, OpenFailed, Escaped } K; 00031 const Stmt *S; 00032 00033 StreamState(Kind k, const Stmt *s) : K(k), S(s) {} 00034 00035 bool isOpened() const { return K == Opened; } 00036 bool isClosed() const { return K == Closed; } 00037 //bool isOpenFailed() const { return K == OpenFailed; } 00038 //bool isEscaped() const { return K == Escaped; } 00039 00040 bool operator==(const StreamState &X) const { 00041 return K == X.K && S == X.S; 00042 } 00043 00044 static StreamState getOpened(const Stmt *s) { return StreamState(Opened, s); } 00045 static StreamState getClosed(const Stmt *s) { return StreamState(Closed, s); } 00046 static StreamState getOpenFailed(const Stmt *s) { 00047 return StreamState(OpenFailed, s); 00048 } 00049 static StreamState getEscaped(const Stmt *s) { 00050 return StreamState(Escaped, s); 00051 } 00052 00053 void Profile(llvm::FoldingSetNodeID &ID) const { 00054 ID.AddInteger(K); 00055 ID.AddPointer(S); 00056 } 00057 }; 00058 00059 class StreamChecker : public Checker<eval::Call, 00060 check::DeadSymbols, 00061 check::EndPath, 00062 check::PreStmt<ReturnStmt> > { 00063 mutable IdentifierInfo *II_fopen, *II_tmpfile, *II_fclose, *II_fread, 00064 *II_fwrite, 00065 *II_fseek, *II_ftell, *II_rewind, *II_fgetpos, *II_fsetpos, 00066 *II_clearerr, *II_feof, *II_ferror, *II_fileno; 00067 mutable OwningPtr<BuiltinBug> BT_nullfp, BT_illegalwhence, 00068 BT_doubleclose, BT_ResourceLeak; 00069 00070 public: 00071 StreamChecker() 00072 : II_fopen(0), II_tmpfile(0) ,II_fclose(0), II_fread(0), II_fwrite(0), 00073 II_fseek(0), II_ftell(0), II_rewind(0), II_fgetpos(0), II_fsetpos(0), 00074 II_clearerr(0), II_feof(0), II_ferror(0), II_fileno(0) {} 00075 00076 bool evalCall(const CallExpr *CE, CheckerContext &C) const; 00077 void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; 00078 void checkEndPath(CheckerContext &Ctx) const; 00079 void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const; 00080 00081 private: 00082 void Fopen(CheckerContext &C, const CallExpr *CE) const; 00083 void Tmpfile(CheckerContext &C, const CallExpr *CE) const; 00084 void Fclose(CheckerContext &C, const CallExpr *CE) const; 00085 void Fread(CheckerContext &C, const CallExpr *CE) const; 00086 void Fwrite(CheckerContext &C, const CallExpr *CE) const; 00087 void Fseek(CheckerContext &C, const CallExpr *CE) const; 00088 void Ftell(CheckerContext &C, const CallExpr *CE) const; 00089 void Rewind(CheckerContext &C, const CallExpr *CE) const; 00090 void Fgetpos(CheckerContext &C, const CallExpr *CE) const; 00091 void Fsetpos(CheckerContext &C, const CallExpr *CE) const; 00092 void Clearerr(CheckerContext &C, const CallExpr *CE) const; 00093 void Feof(CheckerContext &C, const CallExpr *CE) const; 00094 void Ferror(CheckerContext &C, const CallExpr *CE) const; 00095 void Fileno(CheckerContext &C, const CallExpr *CE) const; 00096 00097 void OpenFileAux(CheckerContext &C, const CallExpr *CE) const; 00098 00099 ProgramStateRef CheckNullStream(SVal SV, ProgramStateRef state, 00100 CheckerContext &C) const; 00101 ProgramStateRef CheckDoubleClose(const CallExpr *CE, ProgramStateRef state, 00102 CheckerContext &C) const; 00103 }; 00104 00105 } // end anonymous namespace 00106 00107 namespace clang { 00108 namespace ento { 00109 template <> 00110 struct ProgramStateTrait<StreamState> 00111 : public ProgramStatePartialTrait<llvm::ImmutableMap<SymbolRef, StreamState> > { 00112 static void *GDMIndex() { static int x; return &x; } 00113 }; 00114 } 00115 } 00116 00117 bool StreamChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { 00118 const FunctionDecl *FD = C.getCalleeDecl(CE); 00119 if (!FD) 00120 return false; 00121 00122 ASTContext &Ctx = C.getASTContext(); 00123 if (!II_fopen) 00124 II_fopen = &Ctx.Idents.get("fopen"); 00125 if (!II_tmpfile) 00126 II_tmpfile = &Ctx.Idents.get("tmpfile"); 00127 if (!II_fclose) 00128 II_fclose = &Ctx.Idents.get("fclose"); 00129 if (!II_fread) 00130 II_fread = &Ctx.Idents.get("fread"); 00131 if (!II_fwrite) 00132 II_fwrite = &Ctx.Idents.get("fwrite"); 00133 if (!II_fseek) 00134 II_fseek = &Ctx.Idents.get("fseek"); 00135 if (!II_ftell) 00136 II_ftell = &Ctx.Idents.get("ftell"); 00137 if (!II_rewind) 00138 II_rewind = &Ctx.Idents.get("rewind"); 00139 if (!II_fgetpos) 00140 II_fgetpos = &Ctx.Idents.get("fgetpos"); 00141 if (!II_fsetpos) 00142 II_fsetpos = &Ctx.Idents.get("fsetpos"); 00143 if (!II_clearerr) 00144 II_clearerr = &Ctx.Idents.get("clearerr"); 00145 if (!II_feof) 00146 II_feof = &Ctx.Idents.get("feof"); 00147 if (!II_ferror) 00148 II_ferror = &Ctx.Idents.get("ferror"); 00149 if (!II_fileno) 00150 II_fileno = &Ctx.Idents.get("fileno"); 00151 00152 if (FD->getIdentifier() == II_fopen) { 00153 Fopen(C, CE); 00154 return true; 00155 } 00156 if (FD->getIdentifier() == II_tmpfile) { 00157 Tmpfile(C, CE); 00158 return true; 00159 } 00160 if (FD->getIdentifier() == II_fclose) { 00161 Fclose(C, CE); 00162 return true; 00163 } 00164 if (FD->getIdentifier() == II_fread) { 00165 Fread(C, CE); 00166 return true; 00167 } 00168 if (FD->getIdentifier() == II_fwrite) { 00169 Fwrite(C, CE); 00170 return true; 00171 } 00172 if (FD->getIdentifier() == II_fseek) { 00173 Fseek(C, CE); 00174 return true; 00175 } 00176 if (FD->getIdentifier() == II_ftell) { 00177 Ftell(C, CE); 00178 return true; 00179 } 00180 if (FD->getIdentifier() == II_rewind) { 00181 Rewind(C, CE); 00182 return true; 00183 } 00184 if (FD->getIdentifier() == II_fgetpos) { 00185 Fgetpos(C, CE); 00186 return true; 00187 } 00188 if (FD->getIdentifier() == II_fsetpos) { 00189 Fsetpos(C, CE); 00190 return true; 00191 } 00192 if (FD->getIdentifier() == II_clearerr) { 00193 Clearerr(C, CE); 00194 return true; 00195 } 00196 if (FD->getIdentifier() == II_feof) { 00197 Feof(C, CE); 00198 return true; 00199 } 00200 if (FD->getIdentifier() == II_ferror) { 00201 Ferror(C, CE); 00202 return true; 00203 } 00204 if (FD->getIdentifier() == II_fileno) { 00205 Fileno(C, CE); 00206 return true; 00207 } 00208 00209 return false; 00210 } 00211 00212 void StreamChecker::Fopen(CheckerContext &C, const CallExpr *CE) const { 00213 OpenFileAux(C, CE); 00214 } 00215 00216 void StreamChecker::Tmpfile(CheckerContext &C, const CallExpr *CE) const { 00217 OpenFileAux(C, CE); 00218 } 00219 00220 void StreamChecker::OpenFileAux(CheckerContext &C, const CallExpr *CE) const { 00221 ProgramStateRef state = C.getState(); 00222 unsigned Count = C.getCurrentBlockCount(); 00223 SValBuilder &svalBuilder = C.getSValBuilder(); 00224 const LocationContext *LCtx = C.getPredecessor()->getLocationContext(); 00225 DefinedSVal RetVal = 00226 cast<DefinedSVal>(svalBuilder.getConjuredSymbolVal(0, CE, LCtx, Count)); 00227 state = state->BindExpr(CE, C.getLocationContext(), RetVal); 00228 00229 ConstraintManager &CM = C.getConstraintManager(); 00230 // Bifurcate the state into two: one with a valid FILE* pointer, the other 00231 // with a NULL. 00232 ProgramStateRef stateNotNull, stateNull; 00233 llvm::tie(stateNotNull, stateNull) = CM.assumeDual(state, RetVal); 00234 00235 if (SymbolRef Sym = RetVal.getAsSymbol()) { 00236 // if RetVal is not NULL, set the symbol's state to Opened. 00237 stateNotNull = 00238 stateNotNull->set<StreamState>(Sym,StreamState::getOpened(CE)); 00239 stateNull = 00240 stateNull->set<StreamState>(Sym, StreamState::getOpenFailed(CE)); 00241 00242 C.addTransition(stateNotNull); 00243 C.addTransition(stateNull); 00244 } 00245 } 00246 00247 void StreamChecker::Fclose(CheckerContext &C, const CallExpr *CE) const { 00248 ProgramStateRef state = CheckDoubleClose(CE, C.getState(), C); 00249 if (state) 00250 C.addTransition(state); 00251 } 00252 00253 void StreamChecker::Fread(CheckerContext &C, const CallExpr *CE) const { 00254 ProgramStateRef state = C.getState(); 00255 if (!CheckNullStream(state->getSVal(CE->getArg(3), C.getLocationContext()), 00256 state, C)) 00257 return; 00258 } 00259 00260 void StreamChecker::Fwrite(CheckerContext &C, const CallExpr *CE) const { 00261 ProgramStateRef state = C.getState(); 00262 if (!CheckNullStream(state->getSVal(CE->getArg(3), C.getLocationContext()), 00263 state, C)) 00264 return; 00265 } 00266 00267 void StreamChecker::Fseek(CheckerContext &C, const CallExpr *CE) const { 00268 ProgramStateRef state = C.getState(); 00269 if (!(state = CheckNullStream(state->getSVal(CE->getArg(0), 00270 C.getLocationContext()), state, C))) 00271 return; 00272 // Check the legality of the 'whence' argument of 'fseek'. 00273 SVal Whence = state->getSVal(CE->getArg(2), C.getLocationContext()); 00274 const nonloc::ConcreteInt *CI = dyn_cast<nonloc::ConcreteInt>(&Whence); 00275 00276 if (!CI) 00277 return; 00278 00279 int64_t x = CI->getValue().getSExtValue(); 00280 if (x >= 0 && x <= 2) 00281 return; 00282 00283 if (ExplodedNode *N = C.addTransition(state)) { 00284 if (!BT_illegalwhence) 00285 BT_illegalwhence.reset(new BuiltinBug("Illegal whence argument", 00286 "The whence argument to fseek() should be " 00287 "SEEK_SET, SEEK_END, or SEEK_CUR.")); 00288 BugReport *R = new BugReport(*BT_illegalwhence, 00289 BT_illegalwhence->getDescription(), N); 00290 C.EmitReport(R); 00291 } 00292 } 00293 00294 void StreamChecker::Ftell(CheckerContext &C, const CallExpr *CE) const { 00295 ProgramStateRef state = C.getState(); 00296 if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()), 00297 state, C)) 00298 return; 00299 } 00300 00301 void StreamChecker::Rewind(CheckerContext &C, const CallExpr *CE) const { 00302 ProgramStateRef state = C.getState(); 00303 if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()), 00304 state, C)) 00305 return; 00306 } 00307 00308 void StreamChecker::Fgetpos(CheckerContext &C, const CallExpr *CE) const { 00309 ProgramStateRef state = C.getState(); 00310 if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()), 00311 state, C)) 00312 return; 00313 } 00314 00315 void StreamChecker::Fsetpos(CheckerContext &C, const CallExpr *CE) const { 00316 ProgramStateRef state = C.getState(); 00317 if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()), 00318 state, C)) 00319 return; 00320 } 00321 00322 void StreamChecker::Clearerr(CheckerContext &C, const CallExpr *CE) const { 00323 ProgramStateRef state = C.getState(); 00324 if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()), 00325 state, C)) 00326 return; 00327 } 00328 00329 void StreamChecker::Feof(CheckerContext &C, const CallExpr *CE) const { 00330 ProgramStateRef state = C.getState(); 00331 if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()), 00332 state, C)) 00333 return; 00334 } 00335 00336 void StreamChecker::Ferror(CheckerContext &C, const CallExpr *CE) const { 00337 ProgramStateRef state = C.getState(); 00338 if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()), 00339 state, C)) 00340 return; 00341 } 00342 00343 void StreamChecker::Fileno(CheckerContext &C, const CallExpr *CE) const { 00344 ProgramStateRef state = C.getState(); 00345 if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()), 00346 state, C)) 00347 return; 00348 } 00349 00350 ProgramStateRef StreamChecker::CheckNullStream(SVal SV, ProgramStateRef state, 00351 CheckerContext &C) const { 00352 const DefinedSVal *DV = dyn_cast<DefinedSVal>(&SV); 00353 if (!DV) 00354 return 0; 00355 00356 ConstraintManager &CM = C.getConstraintManager(); 00357 ProgramStateRef stateNotNull, stateNull; 00358 llvm::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV); 00359 00360 if (!stateNotNull && stateNull) { 00361 if (ExplodedNode *N = C.generateSink(stateNull)) { 00362 if (!BT_nullfp) 00363 BT_nullfp.reset(new BuiltinBug("NULL stream pointer", 00364 "Stream pointer might be NULL.")); 00365 BugReport *R =new BugReport(*BT_nullfp, BT_nullfp->getDescription(), N); 00366 C.EmitReport(R); 00367 } 00368 return 0; 00369 } 00370 return stateNotNull; 00371 } 00372 00373 ProgramStateRef StreamChecker::CheckDoubleClose(const CallExpr *CE, 00374 ProgramStateRef state, 00375 CheckerContext &C) const { 00376 SymbolRef Sym = 00377 state->getSVal(CE->getArg(0), C.getLocationContext()).getAsSymbol(); 00378 if (!Sym) 00379 return state; 00380 00381 const StreamState *SS = state->get<StreamState>(Sym); 00382 00383 // If the file stream is not tracked, return. 00384 if (!SS) 00385 return state; 00386 00387 // Check: Double close a File Descriptor could cause undefined behaviour. 00388 // Conforming to man-pages 00389 if (SS->isClosed()) { 00390 ExplodedNode *N = C.generateSink(); 00391 if (N) { 00392 if (!BT_doubleclose) 00393 BT_doubleclose.reset(new BuiltinBug("Double fclose", 00394 "Try to close a file Descriptor already" 00395 " closed. Cause undefined behaviour.")); 00396 BugReport *R = new BugReport(*BT_doubleclose, 00397 BT_doubleclose->getDescription(), N); 00398 C.EmitReport(R); 00399 } 00400 return NULL; 00401 } 00402 00403 // Close the File Descriptor. 00404 return state->set<StreamState>(Sym, StreamState::getClosed(CE)); 00405 } 00406 00407 void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper, 00408 CheckerContext &C) const { 00409 for (SymbolReaper::dead_iterator I = SymReaper.dead_begin(), 00410 E = SymReaper.dead_end(); I != E; ++I) { 00411 SymbolRef Sym = *I; 00412 ProgramStateRef state = C.getState(); 00413 const StreamState *SS = state->get<StreamState>(Sym); 00414 if (!SS) 00415 return; 00416 00417 if (SS->isOpened()) { 00418 ExplodedNode *N = C.generateSink(); 00419 if (N) { 00420 if (!BT_ResourceLeak) 00421 BT_ResourceLeak.reset(new BuiltinBug("Resource Leak", 00422 "Opened File never closed. Potential Resource leak.")); 00423 BugReport *R = new BugReport(*BT_ResourceLeak, 00424 BT_ResourceLeak->getDescription(), N); 00425 C.EmitReport(R); 00426 } 00427 } 00428 } 00429 } 00430 00431 void StreamChecker::checkEndPath(CheckerContext &Ctx) const { 00432 ProgramStateRef state = Ctx.getState(); 00433 typedef llvm::ImmutableMap<SymbolRef, StreamState> SymMap; 00434 SymMap M = state->get<StreamState>(); 00435 00436 for (SymMap::iterator I = M.begin(), E = M.end(); I != E; ++I) { 00437 StreamState SS = I->second; 00438 if (SS.isOpened()) { 00439 ExplodedNode *N = Ctx.addTransition(state); 00440 if (N) { 00441 if (!BT_ResourceLeak) 00442 BT_ResourceLeak.reset(new BuiltinBug("Resource Leak", 00443 "Opened File never closed. Potential Resource leak.")); 00444 BugReport *R = new BugReport(*BT_ResourceLeak, 00445 BT_ResourceLeak->getDescription(), N); 00446 Ctx.EmitReport(R); 00447 } 00448 } 00449 } 00450 } 00451 00452 void StreamChecker::checkPreStmt(const ReturnStmt *S, CheckerContext &C) const { 00453 const Expr *RetE = S->getRetValue(); 00454 if (!RetE) 00455 return; 00456 00457 ProgramStateRef state = C.getState(); 00458 SymbolRef Sym = state->getSVal(RetE, C.getLocationContext()).getAsSymbol(); 00459 00460 if (!Sym) 00461 return; 00462 00463 const StreamState *SS = state->get<StreamState>(Sym); 00464 if(!SS) 00465 return; 00466 00467 if (SS->isOpened()) 00468 state = state->set<StreamState>(Sym, StreamState::getEscaped(S)); 00469 00470 C.addTransition(state); 00471 } 00472 00473 void ento::registerStreamChecker(CheckerManager &mgr) { 00474 mgr.registerChecker<StreamChecker>(); 00475 }