StreamChecker.cpp 15 KB

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