Home | History | Annotate | Download | only in Checkers
      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/Checker.h"
     16 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
     17 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
     18 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.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 #include "llvm/ADT/ImmutableMap.h"
     23 
     24 using namespace clang;
     25 using namespace ento;
     26 
     27 namespace {
     28 
     29 struct StreamState {
     30   enum Kind { Opened, Closed, OpenFailed, Escaped } K;
     31   const Stmt *S;
     32 
     33   StreamState(Kind k, const Stmt *s) : K(k), S(s) {}
     34 
     35   bool isOpened() const { return K == Opened; }
     36   bool isClosed() const { return K == Closed; }
     37   //bool isOpenFailed() const { return K == OpenFailed; }
     38   //bool isEscaped() const { return K == Escaped; }
     39 
     40   bool operator==(const StreamState &X) const {
     41     return K == X.K && S == X.S;
     42   }
     43 
     44   static StreamState getOpened(const Stmt *s) { return StreamState(Opened, s); }
     45   static StreamState getClosed(const Stmt *s) { return StreamState(Closed, s); }
     46   static StreamState getOpenFailed(const Stmt *s) {
     47     return StreamState(OpenFailed, s);
     48   }
     49   static StreamState getEscaped(const Stmt *s) {
     50     return StreamState(Escaped, s);
     51   }
     52 
     53   void Profile(llvm::FoldingSetNodeID &ID) const {
     54     ID.AddInteger(K);
     55     ID.AddPointer(S);
     56   }
     57 };
     58 
     59 class StreamChecker : public Checker<eval::Call,
     60                                        check::DeadSymbols,
     61                                        check::EndPath,
     62                                        check::PreStmt<ReturnStmt> > {
     63   mutable IdentifierInfo *II_fopen, *II_tmpfile, *II_fclose, *II_fread,
     64                  *II_fwrite,
     65                  *II_fseek, *II_ftell, *II_rewind, *II_fgetpos, *II_fsetpos,
     66                  *II_clearerr, *II_feof, *II_ferror, *II_fileno;
     67   mutable OwningPtr<BuiltinBug> BT_nullfp, BT_illegalwhence,
     68                                       BT_doubleclose, BT_ResourceLeak;
     69 
     70 public:
     71   StreamChecker()
     72     : II_fopen(0), II_tmpfile(0) ,II_fclose(0), II_fread(0), II_fwrite(0),
     73       II_fseek(0), II_ftell(0), II_rewind(0), II_fgetpos(0), II_fsetpos(0),
     74       II_clearerr(0), II_feof(0), II_ferror(0), II_fileno(0) {}
     75 
     76   bool evalCall(const CallExpr *CE, CheckerContext &C) const;
     77   void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
     78   void checkEndPath(CheckerContext &Ctx) const;
     79   void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const;
     80 
     81 private:
     82   void Fopen(CheckerContext &C, const CallExpr *CE) const;
     83   void Tmpfile(CheckerContext &C, const CallExpr *CE) const;
     84   void Fclose(CheckerContext &C, const CallExpr *CE) const;
     85   void Fread(CheckerContext &C, const CallExpr *CE) const;
     86   void Fwrite(CheckerContext &C, const CallExpr *CE) const;
     87   void Fseek(CheckerContext &C, const CallExpr *CE) const;
     88   void Ftell(CheckerContext &C, const CallExpr *CE) const;
     89   void Rewind(CheckerContext &C, const CallExpr *CE) const;
     90   void Fgetpos(CheckerContext &C, const CallExpr *CE) const;
     91   void Fsetpos(CheckerContext &C, const CallExpr *CE) const;
     92   void Clearerr(CheckerContext &C, const CallExpr *CE) const;
     93   void Feof(CheckerContext &C, const CallExpr *CE) const;
     94   void Ferror(CheckerContext &C, const CallExpr *CE) const;
     95   void Fileno(CheckerContext &C, const CallExpr *CE) const;
     96 
     97   void OpenFileAux(CheckerContext &C, const CallExpr *CE) const;
     98 
     99   ProgramStateRef CheckNullStream(SVal SV, ProgramStateRef state,
    100                                  CheckerContext &C) const;
    101   ProgramStateRef CheckDoubleClose(const CallExpr *CE, ProgramStateRef state,
    102                                  CheckerContext &C) const;
    103 };
    104 
    105 } // end anonymous namespace
    106 
    107 namespace clang {
    108 namespace ento {
    109   template <>
    110   struct ProgramStateTrait<StreamState>
    111     : public ProgramStatePartialTrait<llvm::ImmutableMap<SymbolRef, StreamState> > {
    112     static void *GDMIndex() { static int x; return &x; }
    113   };
    114 }
    115 }
    116 
    117 bool StreamChecker::evalCall(const CallExpr *CE, CheckerContext &C) const {
    118   const FunctionDecl *FD = C.getCalleeDecl(CE);
    119   if (!FD)
    120     return false;
    121 
    122   ASTContext &Ctx = C.getASTContext();
    123   if (!II_fopen)
    124     II_fopen = &Ctx.Idents.get("fopen");
    125   if (!II_tmpfile)
    126     II_tmpfile = &Ctx.Idents.get("tmpfile");
    127   if (!II_fclose)
    128     II_fclose = &Ctx.Idents.get("fclose");
    129   if (!II_fread)
    130     II_fread = &Ctx.Idents.get("fread");
    131   if (!II_fwrite)
    132     II_fwrite = &Ctx.Idents.get("fwrite");
    133   if (!II_fseek)
    134     II_fseek = &Ctx.Idents.get("fseek");
    135   if (!II_ftell)
    136     II_ftell = &Ctx.Idents.get("ftell");
    137   if (!II_rewind)
    138     II_rewind = &Ctx.Idents.get("rewind");
    139   if (!II_fgetpos)
    140     II_fgetpos = &Ctx.Idents.get("fgetpos");
    141   if (!II_fsetpos)
    142     II_fsetpos = &Ctx.Idents.get("fsetpos");
    143   if (!II_clearerr)
    144     II_clearerr = &Ctx.Idents.get("clearerr");
    145   if (!II_feof)
    146     II_feof = &Ctx.Idents.get("feof");
    147   if (!II_ferror)
    148     II_ferror = &Ctx.Idents.get("ferror");
    149   if (!II_fileno)
    150     II_fileno = &Ctx.Idents.get("fileno");
    151 
    152   if (FD->getIdentifier() == II_fopen) {
    153     Fopen(C, CE);
    154     return true;
    155   }
    156   if (FD->getIdentifier() == II_tmpfile) {
    157     Tmpfile(C, CE);
    158     return true;
    159   }
    160   if (FD->getIdentifier() == II_fclose) {
    161     Fclose(C, CE);
    162     return true;
    163   }
    164   if (FD->getIdentifier() == II_fread) {
    165     Fread(C, CE);
    166     return true;
    167   }
    168   if (FD->getIdentifier() == II_fwrite) {
    169     Fwrite(C, CE);
    170     return true;
    171   }
    172   if (FD->getIdentifier() == II_fseek) {
    173     Fseek(C, CE);
    174     return true;
    175   }
    176   if (FD->getIdentifier() == II_ftell) {
    177     Ftell(C, CE);
    178     return true;
    179   }
    180   if (FD->getIdentifier() == II_rewind) {
    181     Rewind(C, CE);
    182     return true;
    183   }
    184   if (FD->getIdentifier() == II_fgetpos) {
    185     Fgetpos(C, CE);
    186     return true;
    187   }
    188   if (FD->getIdentifier() == II_fsetpos) {
    189     Fsetpos(C, CE);
    190     return true;
    191   }
    192   if (FD->getIdentifier() == II_clearerr) {
    193     Clearerr(C, CE);
    194     return true;
    195   }
    196   if (FD->getIdentifier() == II_feof) {
    197     Feof(C, CE);
    198     return true;
    199   }
    200   if (FD->getIdentifier() == II_ferror) {
    201     Ferror(C, CE);
    202     return true;
    203   }
    204   if (FD->getIdentifier() == II_fileno) {
    205     Fileno(C, CE);
    206     return true;
    207   }
    208 
    209   return false;
    210 }
    211 
    212 void StreamChecker::Fopen(CheckerContext &C, const CallExpr *CE) const {
    213   OpenFileAux(C, CE);
    214 }
    215 
    216 void StreamChecker::Tmpfile(CheckerContext &C, const CallExpr *CE) const {
    217   OpenFileAux(C, CE);
    218 }
    219 
    220 void StreamChecker::OpenFileAux(CheckerContext &C, const CallExpr *CE) const {
    221   ProgramStateRef state = C.getState();
    222   unsigned Count = C.getCurrentBlockCount();
    223   SValBuilder &svalBuilder = C.getSValBuilder();
    224   const LocationContext *LCtx = C.getPredecessor()->getLocationContext();
    225   DefinedSVal RetVal =
    226     cast<DefinedSVal>(svalBuilder.getConjuredSymbolVal(0, CE, LCtx, Count));
    227   state = state->BindExpr(CE, C.getLocationContext(), RetVal);
    228 
    229   ConstraintManager &CM = C.getConstraintManager();
    230   // Bifurcate the state into two: one with a valid FILE* pointer, the other
    231   // with a NULL.
    232   ProgramStateRef stateNotNull, stateNull;
    233   llvm::tie(stateNotNull, stateNull) = CM.assumeDual(state, RetVal);
    234 
    235   if (SymbolRef Sym = RetVal.getAsSymbol()) {
    236     // if RetVal is not NULL, set the symbol's state to Opened.
    237     stateNotNull =
    238       stateNotNull->set<StreamState>(Sym,StreamState::getOpened(CE));
    239     stateNull =
    240       stateNull->set<StreamState>(Sym, StreamState::getOpenFailed(CE));
    241 
    242     C.addTransition(stateNotNull);
    243     C.addTransition(stateNull);
    244   }
    245 }
    246 
    247 void StreamChecker::Fclose(CheckerContext &C, const CallExpr *CE) const {
    248   ProgramStateRef state = CheckDoubleClose(CE, C.getState(), C);
    249   if (state)
    250     C.addTransition(state);
    251 }
    252 
    253 void StreamChecker::Fread(CheckerContext &C, const CallExpr *CE) const {
    254   ProgramStateRef state = C.getState();
    255   if (!CheckNullStream(state->getSVal(CE->getArg(3), C.getLocationContext()),
    256                        state, C))
    257     return;
    258 }
    259 
    260 void StreamChecker::Fwrite(CheckerContext &C, const CallExpr *CE) const {
    261   ProgramStateRef state = C.getState();
    262   if (!CheckNullStream(state->getSVal(CE->getArg(3), C.getLocationContext()),
    263                        state, C))
    264     return;
    265 }
    266 
    267 void StreamChecker::Fseek(CheckerContext &C, const CallExpr *CE) const {
    268   ProgramStateRef state = C.getState();
    269   if (!(state = CheckNullStream(state->getSVal(CE->getArg(0),
    270                                                C.getLocationContext()), state, C)))
    271     return;
    272   // Check the legality of the 'whence' argument of 'fseek'.
    273   SVal Whence = state->getSVal(CE->getArg(2), C.getLocationContext());
    274   const nonloc::ConcreteInt *CI = dyn_cast<nonloc::ConcreteInt>(&Whence);
    275 
    276   if (!CI)
    277     return;
    278 
    279   int64_t x = CI->getValue().getSExtValue();
    280   if (x >= 0 && x <= 2)
    281     return;
    282 
    283   if (ExplodedNode *N = C.addTransition(state)) {
    284     if (!BT_illegalwhence)
    285       BT_illegalwhence.reset(new BuiltinBug("Illegal whence argument",
    286 					"The whence argument to fseek() should be "
    287 					"SEEK_SET, SEEK_END, or SEEK_CUR."));
    288     BugReport *R = new BugReport(*BT_illegalwhence,
    289 				 BT_illegalwhence->getDescription(), N);
    290     C.EmitReport(R);
    291   }
    292 }
    293 
    294 void StreamChecker::Ftell(CheckerContext &C, const CallExpr *CE) const {
    295   ProgramStateRef state = C.getState();
    296   if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()),
    297                        state, C))
    298     return;
    299 }
    300 
    301 void StreamChecker::Rewind(CheckerContext &C, const CallExpr *CE) const {
    302   ProgramStateRef state = C.getState();
    303   if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()),
    304                        state, C))
    305     return;
    306 }
    307 
    308 void StreamChecker::Fgetpos(CheckerContext &C, const CallExpr *CE) const {
    309   ProgramStateRef state = C.getState();
    310   if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()),
    311                        state, C))
    312     return;
    313 }
    314 
    315 void StreamChecker::Fsetpos(CheckerContext &C, const CallExpr *CE) const {
    316   ProgramStateRef state = C.getState();
    317   if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()),
    318                        state, C))
    319     return;
    320 }
    321 
    322 void StreamChecker::Clearerr(CheckerContext &C, const CallExpr *CE) const {
    323   ProgramStateRef state = C.getState();
    324   if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()),
    325                        state, C))
    326     return;
    327 }
    328 
    329 void StreamChecker::Feof(CheckerContext &C, const CallExpr *CE) const {
    330   ProgramStateRef state = C.getState();
    331   if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()),
    332                        state, C))
    333     return;
    334 }
    335 
    336 void StreamChecker::Ferror(CheckerContext &C, const CallExpr *CE) const {
    337   ProgramStateRef state = C.getState();
    338   if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()),
    339                        state, C))
    340     return;
    341 }
    342 
    343 void StreamChecker::Fileno(CheckerContext &C, const CallExpr *CE) const {
    344   ProgramStateRef state = C.getState();
    345   if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()),
    346                        state, C))
    347     return;
    348 }
    349 
    350 ProgramStateRef StreamChecker::CheckNullStream(SVal SV, ProgramStateRef state,
    351                                     CheckerContext &C) const {
    352   const DefinedSVal *DV = dyn_cast<DefinedSVal>(&SV);
    353   if (!DV)
    354     return 0;
    355 
    356   ConstraintManager &CM = C.getConstraintManager();
    357   ProgramStateRef stateNotNull, stateNull;
    358   llvm::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV);
    359 
    360   if (!stateNotNull && stateNull) {
    361     if (ExplodedNode *N = C.generateSink(stateNull)) {
    362       if (!BT_nullfp)
    363         BT_nullfp.reset(new BuiltinBug("NULL stream pointer",
    364                                      "Stream pointer might be NULL."));
    365       BugReport *R =new BugReport(*BT_nullfp, BT_nullfp->getDescription(), N);
    366       C.EmitReport(R);
    367     }
    368     return 0;
    369   }
    370   return stateNotNull;
    371 }
    372 
    373 ProgramStateRef StreamChecker::CheckDoubleClose(const CallExpr *CE,
    374                                                ProgramStateRef state,
    375                                                CheckerContext &C) const {
    376   SymbolRef Sym =
    377     state->getSVal(CE->getArg(0), C.getLocationContext()).getAsSymbol();
    378   if (!Sym)
    379     return state;
    380 
    381   const StreamState *SS = state->get<StreamState>(Sym);
    382 
    383   // If the file stream is not tracked, return.
    384   if (!SS)
    385     return state;
    386 
    387   // Check: Double close a File Descriptor could cause undefined behaviour.
    388   // Conforming to man-pages
    389   if (SS->isClosed()) {
    390     ExplodedNode *N = C.generateSink();
    391     if (N) {
    392       if (!BT_doubleclose)
    393         BT_doubleclose.reset(new BuiltinBug("Double fclose",
    394                                         "Try to close a file Descriptor already"
    395                                         " closed. Cause undefined behaviour."));
    396       BugReport *R = new BugReport(*BT_doubleclose,
    397                                    BT_doubleclose->getDescription(), N);
    398       C.EmitReport(R);
    399     }
    400     return NULL;
    401   }
    402 
    403   // Close the File Descriptor.
    404   return state->set<StreamState>(Sym, StreamState::getClosed(CE));
    405 }
    406 
    407 void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper,
    408                                      CheckerContext &C) const {
    409   for (SymbolReaper::dead_iterator I = SymReaper.dead_begin(),
    410          E = SymReaper.dead_end(); I != E; ++I) {
    411     SymbolRef Sym = *I;
    412     ProgramStateRef state = C.getState();
    413     const StreamState *SS = state->get<StreamState>(Sym);
    414     if (!SS)
    415       return;
    416 
    417     if (SS->isOpened()) {
    418       ExplodedNode *N = C.generateSink();
    419       if (N) {
    420         if (!BT_ResourceLeak)
    421           BT_ResourceLeak.reset(new BuiltinBug("Resource Leak",
    422                          "Opened File never closed. Potential Resource leak."));
    423         BugReport *R = new BugReport(*BT_ResourceLeak,
    424                                      BT_ResourceLeak->getDescription(), N);
    425         C.EmitReport(R);
    426       }
    427     }
    428   }
    429 }
    430 
    431 void StreamChecker::checkEndPath(CheckerContext &Ctx) const {
    432   ProgramStateRef state = Ctx.getState();
    433   typedef llvm::ImmutableMap<SymbolRef, StreamState> SymMap;
    434   SymMap M = state->get<StreamState>();
    435 
    436   for (SymMap::iterator I = M.begin(), E = M.end(); I != E; ++I) {
    437     StreamState SS = I->second;
    438     if (SS.isOpened()) {
    439       ExplodedNode *N = Ctx.addTransition(state);
    440       if (N) {
    441         if (!BT_ResourceLeak)
    442           BT_ResourceLeak.reset(new BuiltinBug("Resource Leak",
    443                          "Opened File never closed. Potential Resource leak."));
    444         BugReport *R = new BugReport(*BT_ResourceLeak,
    445                                      BT_ResourceLeak->getDescription(), N);
    446         Ctx.EmitReport(R);
    447       }
    448     }
    449   }
    450 }
    451 
    452 void StreamChecker::checkPreStmt(const ReturnStmt *S, CheckerContext &C) const {
    453   const Expr *RetE = S->getRetValue();
    454   if (!RetE)
    455     return;
    456 
    457   ProgramStateRef state = C.getState();
    458   SymbolRef Sym = state->getSVal(RetE, C.getLocationContext()).getAsSymbol();
    459 
    460   if (!Sym)
    461     return;
    462 
    463   const StreamState *SS = state->get<StreamState>(Sym);
    464   if(!SS)
    465     return;
    466 
    467   if (SS->isOpened())
    468     state = state->set<StreamState>(Sym, StreamState::getEscaped(S));
    469 
    470   C.addTransition(state);
    471 }
    472 
    473 void ento::registerStreamChecker(CheckerManager &mgr) {
    474   mgr.registerChecker<StreamChecker>();
    475 }
    476