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/BugReporter/BugType.h" 00016 #include "clang/StaticAnalyzer/Core/Checker.h" 00017 #include "clang/StaticAnalyzer/Core/CheckerManager.h" 00018 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.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 mutable IdentifierInfo *II_fopen, *II_tmpfile, *II_fclose, *II_fread, 00062 *II_fwrite, 00063 *II_fseek, *II_ftell, *II_rewind, *II_fgetpos, *II_fsetpos, 00064 *II_clearerr, *II_feof, *II_ferror, *II_fileno; 00065 mutable std::unique_ptr<BuiltinBug> BT_nullfp, BT_illegalwhence, 00066 BT_doubleclose, BT_ResourceLeak; 00067 00068 public: 00069 StreamChecker() 00070 : II_fopen(nullptr), II_tmpfile(nullptr), II_fclose(nullptr), 00071 II_fread(nullptr), II_fwrite(nullptr), II_fseek(nullptr), 00072 II_ftell(nullptr), II_rewind(nullptr), II_fgetpos(nullptr), 00073 II_fsetpos(nullptr), II_clearerr(nullptr), II_feof(nullptr), 00074 II_ferror(nullptr), II_fileno(nullptr) {} 00075 00076 bool evalCall(const CallExpr *CE, CheckerContext &C) const; 00077 void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; 00078 00079 private: 00080 void Fopen(CheckerContext &C, const CallExpr *CE) const; 00081 void Tmpfile(CheckerContext &C, const CallExpr *CE) const; 00082 void Fclose(CheckerContext &C, const CallExpr *CE) const; 00083 void Fread(CheckerContext &C, const CallExpr *CE) const; 00084 void Fwrite(CheckerContext &C, const CallExpr *CE) const; 00085 void Fseek(CheckerContext &C, const CallExpr *CE) const; 00086 void Ftell(CheckerContext &C, const CallExpr *CE) const; 00087 void Rewind(CheckerContext &C, const CallExpr *CE) const; 00088 void Fgetpos(CheckerContext &C, const CallExpr *CE) const; 00089 void Fsetpos(CheckerContext &C, const CallExpr *CE) const; 00090 void Clearerr(CheckerContext &C, const CallExpr *CE) const; 00091 void Feof(CheckerContext &C, const CallExpr *CE) const; 00092 void Ferror(CheckerContext &C, const CallExpr *CE) const; 00093 void Fileno(CheckerContext &C, const CallExpr *CE) const; 00094 00095 void OpenFileAux(CheckerContext &C, const CallExpr *CE) const; 00096 00097 ProgramStateRef CheckNullStream(SVal SV, ProgramStateRef state, 00098 CheckerContext &C) const; 00099 ProgramStateRef CheckDoubleClose(const CallExpr *CE, ProgramStateRef state, 00100 CheckerContext &C) const; 00101 }; 00102 00103 } // end anonymous namespace 00104 00105 REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState) 00106 00107 00108 bool StreamChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { 00109 const FunctionDecl *FD = C.getCalleeDecl(CE); 00110 if (!FD || FD->getKind() != Decl::Function) 00111 return false; 00112 00113 ASTContext &Ctx = C.getASTContext(); 00114 if (!II_fopen) 00115 II_fopen = &Ctx.Idents.get("fopen"); 00116 if (!II_tmpfile) 00117 II_tmpfile = &Ctx.Idents.get("tmpfile"); 00118 if (!II_fclose) 00119 II_fclose = &Ctx.Idents.get("fclose"); 00120 if (!II_fread) 00121 II_fread = &Ctx.Idents.get("fread"); 00122 if (!II_fwrite) 00123 II_fwrite = &Ctx.Idents.get("fwrite"); 00124 if (!II_fseek) 00125 II_fseek = &Ctx.Idents.get("fseek"); 00126 if (!II_ftell) 00127 II_ftell = &Ctx.Idents.get("ftell"); 00128 if (!II_rewind) 00129 II_rewind = &Ctx.Idents.get("rewind"); 00130 if (!II_fgetpos) 00131 II_fgetpos = &Ctx.Idents.get("fgetpos"); 00132 if (!II_fsetpos) 00133 II_fsetpos = &Ctx.Idents.get("fsetpos"); 00134 if (!II_clearerr) 00135 II_clearerr = &Ctx.Idents.get("clearerr"); 00136 if (!II_feof) 00137 II_feof = &Ctx.Idents.get("feof"); 00138 if (!II_ferror) 00139 II_ferror = &Ctx.Idents.get("ferror"); 00140 if (!II_fileno) 00141 II_fileno = &Ctx.Idents.get("fileno"); 00142 00143 if (FD->getIdentifier() == II_fopen) { 00144 Fopen(C, CE); 00145 return true; 00146 } 00147 if (FD->getIdentifier() == II_tmpfile) { 00148 Tmpfile(C, CE); 00149 return true; 00150 } 00151 if (FD->getIdentifier() == II_fclose) { 00152 Fclose(C, CE); 00153 return true; 00154 } 00155 if (FD->getIdentifier() == II_fread) { 00156 Fread(C, CE); 00157 return true; 00158 } 00159 if (FD->getIdentifier() == II_fwrite) { 00160 Fwrite(C, CE); 00161 return true; 00162 } 00163 if (FD->getIdentifier() == II_fseek) { 00164 Fseek(C, CE); 00165 return true; 00166 } 00167 if (FD->getIdentifier() == II_ftell) { 00168 Ftell(C, CE); 00169 return true; 00170 } 00171 if (FD->getIdentifier() == II_rewind) { 00172 Rewind(C, CE); 00173 return true; 00174 } 00175 if (FD->getIdentifier() == II_fgetpos) { 00176 Fgetpos(C, CE); 00177 return true; 00178 } 00179 if (FD->getIdentifier() == II_fsetpos) { 00180 Fsetpos(C, CE); 00181 return true; 00182 } 00183 if (FD->getIdentifier() == II_clearerr) { 00184 Clearerr(C, CE); 00185 return true; 00186 } 00187 if (FD->getIdentifier() == II_feof) { 00188 Feof(C, CE); 00189 return true; 00190 } 00191 if (FD->getIdentifier() == II_ferror) { 00192 Ferror(C, CE); 00193 return true; 00194 } 00195 if (FD->getIdentifier() == II_fileno) { 00196 Fileno(C, CE); 00197 return true; 00198 } 00199 00200 return false; 00201 } 00202 00203 void StreamChecker::Fopen(CheckerContext &C, const CallExpr *CE) const { 00204 OpenFileAux(C, CE); 00205 } 00206 00207 void StreamChecker::Tmpfile(CheckerContext &C, const CallExpr *CE) const { 00208 OpenFileAux(C, CE); 00209 } 00210 00211 void StreamChecker::OpenFileAux(CheckerContext &C, const CallExpr *CE) const { 00212 ProgramStateRef state = C.getState(); 00213 SValBuilder &svalBuilder = C.getSValBuilder(); 00214 const LocationContext *LCtx = C.getPredecessor()->getLocationContext(); 00215 DefinedSVal RetVal = svalBuilder.conjureSymbolVal(nullptr, CE, LCtx, 00216 C.blockCount()) 00217 .castAs<DefinedSVal>(); 00218 state = state->BindExpr(CE, C.getLocationContext(), RetVal); 00219 00220 ConstraintManager &CM = C.getConstraintManager(); 00221 // Bifurcate the state into two: one with a valid FILE* pointer, the other 00222 // with a NULL. 00223 ProgramStateRef stateNotNull, stateNull; 00224 std::tie(stateNotNull, stateNull) = CM.assumeDual(state, RetVal); 00225 00226 if (SymbolRef Sym = RetVal.getAsSymbol()) { 00227 // if RetVal is not NULL, set the symbol's state to Opened. 00228 stateNotNull = 00229 stateNotNull->set<StreamMap>(Sym,StreamState::getOpened(CE)); 00230 stateNull = 00231 stateNull->set<StreamMap>(Sym, StreamState::getOpenFailed(CE)); 00232 00233 C.addTransition(stateNotNull); 00234 C.addTransition(stateNull); 00235 } 00236 } 00237 00238 void StreamChecker::Fclose(CheckerContext &C, const CallExpr *CE) const { 00239 ProgramStateRef state = CheckDoubleClose(CE, C.getState(), C); 00240 if (state) 00241 C.addTransition(state); 00242 } 00243 00244 void StreamChecker::Fread(CheckerContext &C, const CallExpr *CE) const { 00245 ProgramStateRef state = C.getState(); 00246 if (!CheckNullStream(state->getSVal(CE->getArg(3), C.getLocationContext()), 00247 state, C)) 00248 return; 00249 } 00250 00251 void StreamChecker::Fwrite(CheckerContext &C, const CallExpr *CE) const { 00252 ProgramStateRef state = C.getState(); 00253 if (!CheckNullStream(state->getSVal(CE->getArg(3), C.getLocationContext()), 00254 state, C)) 00255 return; 00256 } 00257 00258 void StreamChecker::Fseek(CheckerContext &C, const CallExpr *CE) const { 00259 ProgramStateRef state = C.getState(); 00260 if (!(state = CheckNullStream(state->getSVal(CE->getArg(0), 00261 C.getLocationContext()), state, C))) 00262 return; 00263 // Check the legality of the 'whence' argument of 'fseek'. 00264 SVal Whence = state->getSVal(CE->getArg(2), C.getLocationContext()); 00265 Optional<nonloc::ConcreteInt> CI = Whence.getAs<nonloc::ConcreteInt>(); 00266 00267 if (!CI) 00268 return; 00269 00270 int64_t x = CI->getValue().getSExtValue(); 00271 if (x >= 0 && x <= 2) 00272 return; 00273 00274 if (ExplodedNode *N = C.addTransition(state)) { 00275 if (!BT_illegalwhence) 00276 BT_illegalwhence.reset( 00277 new BuiltinBug(this, "Illegal whence argument", 00278 "The whence argument to fseek() should be " 00279 "SEEK_SET, SEEK_END, or SEEK_CUR.")); 00280 BugReport *R = new BugReport(*BT_illegalwhence, 00281 BT_illegalwhence->getDescription(), N); 00282 C.emitReport(R); 00283 } 00284 } 00285 00286 void StreamChecker::Ftell(CheckerContext &C, const CallExpr *CE) const { 00287 ProgramStateRef state = C.getState(); 00288 if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()), 00289 state, C)) 00290 return; 00291 } 00292 00293 void StreamChecker::Rewind(CheckerContext &C, const CallExpr *CE) const { 00294 ProgramStateRef state = C.getState(); 00295 if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()), 00296 state, C)) 00297 return; 00298 } 00299 00300 void StreamChecker::Fgetpos(CheckerContext &C, const CallExpr *CE) const { 00301 ProgramStateRef state = C.getState(); 00302 if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()), 00303 state, C)) 00304 return; 00305 } 00306 00307 void StreamChecker::Fsetpos(CheckerContext &C, const CallExpr *CE) const { 00308 ProgramStateRef state = C.getState(); 00309 if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()), 00310 state, C)) 00311 return; 00312 } 00313 00314 void StreamChecker::Clearerr(CheckerContext &C, const CallExpr *CE) const { 00315 ProgramStateRef state = C.getState(); 00316 if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()), 00317 state, C)) 00318 return; 00319 } 00320 00321 void StreamChecker::Feof(CheckerContext &C, const CallExpr *CE) const { 00322 ProgramStateRef state = C.getState(); 00323 if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()), 00324 state, C)) 00325 return; 00326 } 00327 00328 void StreamChecker::Ferror(CheckerContext &C, const CallExpr *CE) const { 00329 ProgramStateRef state = C.getState(); 00330 if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()), 00331 state, C)) 00332 return; 00333 } 00334 00335 void StreamChecker::Fileno(CheckerContext &C, const CallExpr *CE) const { 00336 ProgramStateRef state = C.getState(); 00337 if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()), 00338 state, C)) 00339 return; 00340 } 00341 00342 ProgramStateRef StreamChecker::CheckNullStream(SVal SV, ProgramStateRef state, 00343 CheckerContext &C) const { 00344 Optional<DefinedSVal> DV = SV.getAs<DefinedSVal>(); 00345 if (!DV) 00346 return nullptr; 00347 00348 ConstraintManager &CM = C.getConstraintManager(); 00349 ProgramStateRef stateNotNull, stateNull; 00350 std::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV); 00351 00352 if (!stateNotNull && stateNull) { 00353 if (ExplodedNode *N = C.generateSink(stateNull)) { 00354 if (!BT_nullfp) 00355 BT_nullfp.reset(new BuiltinBug(this, "NULL stream pointer", 00356 "Stream pointer might be NULL.")); 00357 BugReport *R =new BugReport(*BT_nullfp, BT_nullfp->getDescription(), N); 00358 C.emitReport(R); 00359 } 00360 return nullptr; 00361 } 00362 return stateNotNull; 00363 } 00364 00365 ProgramStateRef StreamChecker::CheckDoubleClose(const CallExpr *CE, 00366 ProgramStateRef state, 00367 CheckerContext &C) const { 00368 SymbolRef Sym = 00369 state->getSVal(CE->getArg(0), C.getLocationContext()).getAsSymbol(); 00370 if (!Sym) 00371 return state; 00372 00373 const StreamState *SS = state->get<StreamMap>(Sym); 00374 00375 // If the file stream is not tracked, return. 00376 if (!SS) 00377 return state; 00378 00379 // Check: Double close a File Descriptor could cause undefined behaviour. 00380 // Conforming to man-pages 00381 if (SS->isClosed()) { 00382 ExplodedNode *N = C.generateSink(); 00383 if (N) { 00384 if (!BT_doubleclose) 00385 BT_doubleclose.reset(new BuiltinBug( 00386 this, "Double fclose", "Try to close a file Descriptor already" 00387 " closed. Cause undefined behaviour.")); 00388 BugReport *R = new BugReport(*BT_doubleclose, 00389 BT_doubleclose->getDescription(), N); 00390 C.emitReport(R); 00391 } 00392 return nullptr; 00393 } 00394 00395 // Close the File Descriptor. 00396 return state->set<StreamMap>(Sym, StreamState::getClosed(CE)); 00397 } 00398 00399 void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper, 00400 CheckerContext &C) const { 00401 // TODO: Clean up the state. 00402 for (SymbolReaper::dead_iterator I = SymReaper.dead_begin(), 00403 E = SymReaper.dead_end(); I != E; ++I) { 00404 SymbolRef Sym = *I; 00405 ProgramStateRef state = C.getState(); 00406 const StreamState *SS = state->get<StreamMap>(Sym); 00407 if (!SS) 00408 continue; 00409 00410 if (SS->isOpened()) { 00411 ExplodedNode *N = C.generateSink(); 00412 if (N) { 00413 if (!BT_ResourceLeak) 00414 BT_ResourceLeak.reset(new BuiltinBug( 00415 this, "Resource Leak", 00416 "Opened File never closed. Potential Resource leak.")); 00417 BugReport *R = new BugReport(*BT_ResourceLeak, 00418 BT_ResourceLeak->getDescription(), N); 00419 C.emitReport(R); 00420 } 00421 } 00422 } 00423 } 00424 00425 void ento::registerStreamChecker(CheckerManager &mgr) { 00426 mgr.registerChecker<StreamChecker>(); 00427 }