ValistChecker.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425
  1. //== ValistChecker.cpp - stdarg.h macro usage checker -----------*- C++ -*--==//
  2. //
  3. // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  4. // See https://llvm.org/LICENSE.txt for license information.
  5. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  6. //
  7. //===----------------------------------------------------------------------===//
  8. //
  9. // This defines checkers which detect usage of uninitialized va_list values
  10. // and va_start calls with no matching va_end.
  11. //
  12. //===----------------------------------------------------------------------===//
  13. #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
  14. #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
  15. #include "clang/StaticAnalyzer/Core/Checker.h"
  16. #include "clang/StaticAnalyzer/Core/CheckerManager.h"
  17. #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
  18. #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
  19. #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
  20. using namespace clang;
  21. using namespace ento;
  22. REGISTER_SET_WITH_PROGRAMSTATE(InitializedVALists, const MemRegion *)
  23. namespace {
  24. typedef SmallVector<const MemRegion *, 2> RegionVector;
  25. class ValistChecker : public Checker<check::PreCall, check::PreStmt<VAArgExpr>,
  26. check::DeadSymbols> {
  27. mutable std::unique_ptr<BugType> BT_leakedvalist, BT_uninitaccess;
  28. struct VAListAccepter {
  29. CallDescription Func;
  30. int VAListPos;
  31. };
  32. static const SmallVector<VAListAccepter, 15> VAListAccepters;
  33. static const CallDescription VaStart, VaEnd, VaCopy;
  34. public:
  35. enum CheckKind {
  36. CK_Uninitialized,
  37. CK_Unterminated,
  38. CK_CopyToSelf,
  39. CK_NumCheckKinds
  40. };
  41. bool ChecksEnabled[CK_NumCheckKinds] = {false};
  42. CheckerNameRef CheckNames[CK_NumCheckKinds];
  43. void checkPreStmt(const VAArgExpr *VAA, CheckerContext &C) const;
  44. void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
  45. void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
  46. private:
  47. const MemRegion *getVAListAsRegion(SVal SV, const Expr *VAExpr,
  48. bool &IsSymbolic, CheckerContext &C) const;
  49. const ExplodedNode *getStartCallSite(const ExplodedNode *N,
  50. const MemRegion *Reg) const;
  51. void reportUninitializedAccess(const MemRegion *VAList, StringRef Msg,
  52. CheckerContext &C) const;
  53. void reportLeakedVALists(const RegionVector &LeakedVALists, StringRef Msg1,
  54. StringRef Msg2, CheckerContext &C, ExplodedNode *N,
  55. bool ReportUninit = false) const;
  56. void checkVAListStartCall(const CallEvent &Call, CheckerContext &C,
  57. bool IsCopy) const;
  58. void checkVAListEndCall(const CallEvent &Call, CheckerContext &C) const;
  59. class ValistBugVisitor : public BugReporterVisitor {
  60. public:
  61. ValistBugVisitor(const MemRegion *Reg, bool IsLeak = false)
  62. : Reg(Reg), IsLeak(IsLeak) {}
  63. void Profile(llvm::FoldingSetNodeID &ID) const override {
  64. static int X = 0;
  65. ID.AddPointer(&X);
  66. ID.AddPointer(Reg);
  67. }
  68. PathDiagnosticPieceRef getEndPath(BugReporterContext &BRC,
  69. const ExplodedNode *EndPathNode,
  70. PathSensitiveBugReport &BR) override {
  71. if (!IsLeak)
  72. return nullptr;
  73. PathDiagnosticLocation L = BR.getLocation();
  74. // Do not add the statement itself as a range in case of leak.
  75. return std::make_shared<PathDiagnosticEventPiece>(L, BR.getDescription(),
  76. false);
  77. }
  78. PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
  79. BugReporterContext &BRC,
  80. PathSensitiveBugReport &BR) override;
  81. private:
  82. const MemRegion *Reg;
  83. bool IsLeak;
  84. };
  85. };
  86. const SmallVector<ValistChecker::VAListAccepter, 15>
  87. ValistChecker::VAListAccepters = {{{{"vfprintf"}, 3}, 2},
  88. {{{"vfscanf"}, 3}, 2},
  89. {{{"vprintf"}, 2}, 1},
  90. {{{"vscanf"}, 2}, 1},
  91. {{{"vsnprintf"}, 4}, 3},
  92. {{{"vsprintf"}, 3}, 2},
  93. {{{"vsscanf"}, 3}, 2},
  94. {{{"vfwprintf"}, 3}, 2},
  95. {{{"vfwscanf"}, 3}, 2},
  96. {{{"vwprintf"}, 2}, 1},
  97. {{{"vwscanf"}, 2}, 1},
  98. {{{"vswprintf"}, 4}, 3},
  99. // vswprintf is the wide version of
  100. // vsnprintf, vsprintf has no wide version
  101. {{{"vswscanf"}, 3}, 2}};
  102. const CallDescription ValistChecker::VaStart({"__builtin_va_start"}, /*Args=*/2,
  103. /*Params=*/1),
  104. ValistChecker::VaCopy({"__builtin_va_copy"}, 2),
  105. ValistChecker::VaEnd({"__builtin_va_end"}, 1);
  106. } // end anonymous namespace
  107. void ValistChecker::checkPreCall(const CallEvent &Call,
  108. CheckerContext &C) const {
  109. if (!Call.isGlobalCFunction())
  110. return;
  111. if (VaStart.matches(Call))
  112. checkVAListStartCall(Call, C, false);
  113. else if (VaCopy.matches(Call))
  114. checkVAListStartCall(Call, C, true);
  115. else if (VaEnd.matches(Call))
  116. checkVAListEndCall(Call, C);
  117. else {
  118. for (auto FuncInfo : VAListAccepters) {
  119. if (!FuncInfo.Func.matches(Call))
  120. continue;
  121. bool Symbolic;
  122. const MemRegion *VAList =
  123. getVAListAsRegion(Call.getArgSVal(FuncInfo.VAListPos),
  124. Call.getArgExpr(FuncInfo.VAListPos), Symbolic, C);
  125. if (!VAList)
  126. return;
  127. if (C.getState()->contains<InitializedVALists>(VAList))
  128. return;
  129. // We did not see va_start call, but the source of the region is unknown.
  130. // Be conservative and assume the best.
  131. if (Symbolic)
  132. return;
  133. SmallString<80> Errmsg("Function '");
  134. Errmsg += FuncInfo.Func.getFunctionName();
  135. Errmsg += "' is called with an uninitialized va_list argument";
  136. reportUninitializedAccess(VAList, Errmsg.c_str(), C);
  137. break;
  138. }
  139. }
  140. }
  141. const MemRegion *ValistChecker::getVAListAsRegion(SVal SV, const Expr *E,
  142. bool &IsSymbolic,
  143. CheckerContext &C) const {
  144. const MemRegion *Reg = SV.getAsRegion();
  145. if (!Reg)
  146. return nullptr;
  147. // TODO: In the future this should be abstracted away by the analyzer.
  148. bool VaListModelledAsArray = false;
  149. if (const auto *Cast = dyn_cast<CastExpr>(E)) {
  150. QualType Ty = Cast->getType();
  151. VaListModelledAsArray =
  152. Ty->isPointerType() && Ty->getPointeeType()->isRecordType();
  153. }
  154. if (const auto *DeclReg = Reg->getAs<DeclRegion>()) {
  155. if (isa<ParmVarDecl>(DeclReg->getDecl()))
  156. Reg = C.getState()->getSVal(SV.castAs<Loc>()).getAsRegion();
  157. }
  158. IsSymbolic = Reg && Reg->getBaseRegion()->getAs<SymbolicRegion>();
  159. // Some VarRegion based VA lists reach here as ElementRegions.
  160. const auto *EReg = dyn_cast_or_null<ElementRegion>(Reg);
  161. return (EReg && VaListModelledAsArray) ? EReg->getSuperRegion() : Reg;
  162. }
  163. void ValistChecker::checkPreStmt(const VAArgExpr *VAA,
  164. CheckerContext &C) const {
  165. ProgramStateRef State = C.getState();
  166. const Expr *VASubExpr = VAA->getSubExpr();
  167. SVal VAListSVal = C.getSVal(VASubExpr);
  168. bool Symbolic;
  169. const MemRegion *VAList =
  170. getVAListAsRegion(VAListSVal, VASubExpr, Symbolic, C);
  171. if (!VAList)
  172. return;
  173. if (Symbolic)
  174. return;
  175. if (!State->contains<InitializedVALists>(VAList))
  176. reportUninitializedAccess(
  177. VAList, "va_arg() is called on an uninitialized va_list", C);
  178. }
  179. void ValistChecker::checkDeadSymbols(SymbolReaper &SR,
  180. CheckerContext &C) const {
  181. ProgramStateRef State = C.getState();
  182. InitializedVAListsTy TrackedVALists = State->get<InitializedVALists>();
  183. RegionVector LeakedVALists;
  184. for (auto Reg : TrackedVALists) {
  185. if (SR.isLiveRegion(Reg))
  186. continue;
  187. LeakedVALists.push_back(Reg);
  188. State = State->remove<InitializedVALists>(Reg);
  189. }
  190. if (ExplodedNode *N = C.addTransition(State))
  191. reportLeakedVALists(LeakedVALists, "Initialized va_list", " is leaked", C,
  192. N);
  193. }
  194. // This function traverses the exploded graph backwards and finds the node where
  195. // the va_list is initialized. That node is used for uniquing the bug paths.
  196. // It is not likely that there are several different va_lists that belongs to
  197. // different stack frames, so that case is not yet handled.
  198. const ExplodedNode *
  199. ValistChecker::getStartCallSite(const ExplodedNode *N,
  200. const MemRegion *Reg) const {
  201. const LocationContext *LeakContext = N->getLocationContext();
  202. const ExplodedNode *StartCallNode = N;
  203. bool FoundInitializedState = false;
  204. while (N) {
  205. ProgramStateRef State = N->getState();
  206. if (!State->contains<InitializedVALists>(Reg)) {
  207. if (FoundInitializedState)
  208. break;
  209. } else {
  210. FoundInitializedState = true;
  211. }
  212. const LocationContext *NContext = N->getLocationContext();
  213. if (NContext == LeakContext || NContext->isParentOf(LeakContext))
  214. StartCallNode = N;
  215. N = N->pred_empty() ? nullptr : *(N->pred_begin());
  216. }
  217. return StartCallNode;
  218. }
  219. void ValistChecker::reportUninitializedAccess(const MemRegion *VAList,
  220. StringRef Msg,
  221. CheckerContext &C) const {
  222. if (!ChecksEnabled[CK_Uninitialized])
  223. return;
  224. if (ExplodedNode *N = C.generateErrorNode()) {
  225. if (!BT_uninitaccess)
  226. BT_uninitaccess.reset(new BugType(CheckNames[CK_Uninitialized],
  227. "Uninitialized va_list",
  228. categories::MemoryError));
  229. auto R = std::make_unique<PathSensitiveBugReport>(*BT_uninitaccess, Msg, N);
  230. R->markInteresting(VAList);
  231. R->addVisitor(std::make_unique<ValistBugVisitor>(VAList));
  232. C.emitReport(std::move(R));
  233. }
  234. }
  235. void ValistChecker::reportLeakedVALists(const RegionVector &LeakedVALists,
  236. StringRef Msg1, StringRef Msg2,
  237. CheckerContext &C, ExplodedNode *N,
  238. bool ReportUninit) const {
  239. if (!(ChecksEnabled[CK_Unterminated] ||
  240. (ChecksEnabled[CK_Uninitialized] && ReportUninit)))
  241. return;
  242. for (auto Reg : LeakedVALists) {
  243. if (!BT_leakedvalist) {
  244. // FIXME: maybe creating a new check name for this type of bug is a better
  245. // solution.
  246. BT_leakedvalist.reset(
  247. new BugType(CheckNames[CK_Unterminated].getName().empty()
  248. ? CheckNames[CK_Uninitialized]
  249. : CheckNames[CK_Unterminated],
  250. "Leaked va_list", categories::MemoryError,
  251. /*SuppressOnSink=*/true));
  252. }
  253. const ExplodedNode *StartNode = getStartCallSite(N, Reg);
  254. PathDiagnosticLocation LocUsedForUniqueing;
  255. if (const Stmt *StartCallStmt = StartNode->getStmtForDiagnostics())
  256. LocUsedForUniqueing = PathDiagnosticLocation::createBegin(
  257. StartCallStmt, C.getSourceManager(), StartNode->getLocationContext());
  258. SmallString<100> Buf;
  259. llvm::raw_svector_ostream OS(Buf);
  260. OS << Msg1;
  261. std::string VariableName = Reg->getDescriptiveName();
  262. if (!VariableName.empty())
  263. OS << " " << VariableName;
  264. OS << Msg2;
  265. auto R = std::make_unique<PathSensitiveBugReport>(
  266. *BT_leakedvalist, OS.str(), N, LocUsedForUniqueing,
  267. StartNode->getLocationContext()->getDecl());
  268. R->markInteresting(Reg);
  269. R->addVisitor(std::make_unique<ValistBugVisitor>(Reg, true));
  270. C.emitReport(std::move(R));
  271. }
  272. }
  273. void ValistChecker::checkVAListStartCall(const CallEvent &Call,
  274. CheckerContext &C, bool IsCopy) const {
  275. bool Symbolic;
  276. const MemRegion *VAList =
  277. getVAListAsRegion(Call.getArgSVal(0), Call.getArgExpr(0), Symbolic, C);
  278. if (!VAList)
  279. return;
  280. ProgramStateRef State = C.getState();
  281. if (IsCopy) {
  282. const MemRegion *Arg2 =
  283. getVAListAsRegion(Call.getArgSVal(1), Call.getArgExpr(1), Symbolic, C);
  284. if (Arg2) {
  285. if (ChecksEnabled[CK_CopyToSelf] && VAList == Arg2) {
  286. RegionVector LeakedVALists{VAList};
  287. if (ExplodedNode *N = C.addTransition(State))
  288. reportLeakedVALists(LeakedVALists, "va_list",
  289. " is copied onto itself", C, N, true);
  290. return;
  291. } else if (!State->contains<InitializedVALists>(Arg2) && !Symbolic) {
  292. if (State->contains<InitializedVALists>(VAList)) {
  293. State = State->remove<InitializedVALists>(VAList);
  294. RegionVector LeakedVALists{VAList};
  295. if (ExplodedNode *N = C.addTransition(State))
  296. reportLeakedVALists(LeakedVALists, "Initialized va_list",
  297. " is overwritten by an uninitialized one", C, N,
  298. true);
  299. } else {
  300. reportUninitializedAccess(Arg2, "Uninitialized va_list is copied", C);
  301. }
  302. return;
  303. }
  304. }
  305. }
  306. if (State->contains<InitializedVALists>(VAList)) {
  307. RegionVector LeakedVALists{VAList};
  308. if (ExplodedNode *N = C.addTransition(State))
  309. reportLeakedVALists(LeakedVALists, "Initialized va_list",
  310. " is initialized again", C, N);
  311. return;
  312. }
  313. State = State->add<InitializedVALists>(VAList);
  314. C.addTransition(State);
  315. }
  316. void ValistChecker::checkVAListEndCall(const CallEvent &Call,
  317. CheckerContext &C) const {
  318. bool Symbolic;
  319. const MemRegion *VAList =
  320. getVAListAsRegion(Call.getArgSVal(0), Call.getArgExpr(0), Symbolic, C);
  321. if (!VAList)
  322. return;
  323. // We did not see va_start call, but the source of the region is unknown.
  324. // Be conservative and assume the best.
  325. if (Symbolic)
  326. return;
  327. if (!C.getState()->contains<InitializedVALists>(VAList)) {
  328. reportUninitializedAccess(
  329. VAList, "va_end() is called on an uninitialized va_list", C);
  330. return;
  331. }
  332. ProgramStateRef State = C.getState();
  333. State = State->remove<InitializedVALists>(VAList);
  334. C.addTransition(State);
  335. }
  336. PathDiagnosticPieceRef ValistChecker::ValistBugVisitor::VisitNode(
  337. const ExplodedNode *N, BugReporterContext &BRC, PathSensitiveBugReport &) {
  338. ProgramStateRef State = N->getState();
  339. ProgramStateRef StatePrev = N->getFirstPred()->getState();
  340. const Stmt *S = N->getStmtForDiagnostics();
  341. if (!S)
  342. return nullptr;
  343. StringRef Msg;
  344. if (State->contains<InitializedVALists>(Reg) &&
  345. !StatePrev->contains<InitializedVALists>(Reg))
  346. Msg = "Initialized va_list";
  347. else if (!State->contains<InitializedVALists>(Reg) &&
  348. StatePrev->contains<InitializedVALists>(Reg))
  349. Msg = "Ended va_list";
  350. if (Msg.empty())
  351. return nullptr;
  352. PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
  353. N->getLocationContext());
  354. return std::make_shared<PathDiagnosticEventPiece>(Pos, Msg, true);
  355. }
  356. void ento::registerValistBase(CheckerManager &mgr) {
  357. mgr.registerChecker<ValistChecker>();
  358. }
  359. bool ento::shouldRegisterValistBase(const CheckerManager &mgr) {
  360. return true;
  361. }
  362. #define REGISTER_CHECKER(name) \
  363. void ento::register##name##Checker(CheckerManager &mgr) { \
  364. ValistChecker *checker = mgr.getChecker<ValistChecker>(); \
  365. checker->ChecksEnabled[ValistChecker::CK_##name] = true; \
  366. checker->CheckNames[ValistChecker::CK_##name] = \
  367. mgr.getCurrentCheckerName(); \
  368. } \
  369. \
  370. bool ento::shouldRegister##name##Checker(const CheckerManager &mgr) { \
  371. return true; \
  372. }
  373. REGISTER_CHECKER(Uninitialized)
  374. REGISTER_CHECKER(Unterminated)
  375. REGISTER_CHECKER(CopyToSelf)