clang API Documentation

StreamChecker.cpp
Go to the documentation of this file.
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 }