ValistChecker.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426
  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. DefaultBool ChecksEnabled[CK_NumCheckKinds];
  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 = {
  88. {{"vfprintf", 3}, 2},
  89. {{"vfscanf", 3}, 2},
  90. {{"vprintf", 2}, 1},
  91. {{"vscanf", 2}, 1},
  92. {{"vsnprintf", 4}, 3},
  93. {{"vsprintf", 3}, 2},
  94. {{"vsscanf", 3}, 2},
  95. {{"vfwprintf", 3}, 2},
  96. {{"vfwscanf", 3}, 2},
  97. {{"vwprintf", 2}, 1},
  98. {{"vwscanf", 2}, 1},
  99. {{"vswprintf", 4}, 3},
  100. // vswprintf is the wide version of vsnprintf,
  101. // vsprintf has no wide version
  102. {{"vswscanf", 3}, 2}};
  103. const CallDescription
  104. ValistChecker::VaStart("__builtin_va_start", /*Args=*/2, /*Params=*/1),
  105. ValistChecker::VaCopy("__builtin_va_copy", 2),
  106. ValistChecker::VaEnd("__builtin_va_end", 1);
  107. } // end anonymous namespace
  108. void ValistChecker::checkPreCall(const CallEvent &Call,
  109. CheckerContext &C) const {
  110. if (!Call.isGlobalCFunction())
  111. return;
  112. if (VaStart.matches(Call))
  113. checkVAListStartCall(Call, C, false);
  114. else if (VaCopy.matches(Call))
  115. checkVAListStartCall(Call, C, true);
  116. else if (VaEnd.matches(Call))
  117. checkVAListEndCall(Call, C);
  118. else {
  119. for (auto FuncInfo : VAListAccepters) {
  120. if (!FuncInfo.Func.matches(Call))
  121. continue;
  122. bool Symbolic;
  123. const MemRegion *VAList =
  124. getVAListAsRegion(Call.getArgSVal(FuncInfo.VAListPos),
  125. Call.getArgExpr(FuncInfo.VAListPos), Symbolic, C);
  126. if (!VAList)
  127. return;
  128. if (C.getState()->contains<InitializedVALists>(VAList))
  129. return;
  130. // We did not see va_start call, but the source of the region is unknown.
  131. // Be conservative and assume the best.
  132. if (Symbolic)
  133. return;
  134. SmallString<80> Errmsg("Function '");
  135. Errmsg += FuncInfo.Func.getFunctionName();
  136. Errmsg += "' is called with an uninitialized va_list argument";
  137. reportUninitializedAccess(VAList, Errmsg.c_str(), C);
  138. break;
  139. }
  140. }
  141. }
  142. const MemRegion *ValistChecker::getVAListAsRegion(SVal SV, const Expr *E,
  143. bool &IsSymbolic,
  144. CheckerContext &C) const {
  145. const MemRegion *Reg = SV.getAsRegion();
  146. if (!Reg)
  147. return nullptr;
  148. // TODO: In the future this should be abstracted away by the analyzer.
  149. bool VaListModelledAsArray = false;
  150. if (const auto *Cast = dyn_cast<CastExpr>(E)) {
  151. QualType Ty = Cast->getType();
  152. VaListModelledAsArray =
  153. Ty->isPointerType() && Ty->getPointeeType()->isRecordType();
  154. }
  155. if (const auto *DeclReg = Reg->getAs<DeclRegion>()) {
  156. if (isa<ParmVarDecl>(DeclReg->getDecl()))
  157. Reg = C.getState()->getSVal(SV.castAs<Loc>()).getAsRegion();
  158. }
  159. IsSymbolic = Reg && Reg->getAs<SymbolicRegion>();
  160. // Some VarRegion based VA lists reach here as ElementRegions.
  161. const auto *EReg = dyn_cast_or_null<ElementRegion>(Reg);
  162. return (EReg && VaListModelledAsArray) ? EReg->getSuperRegion() : Reg;
  163. }
  164. void ValistChecker::checkPreStmt(const VAArgExpr *VAA,
  165. CheckerContext &C) const {
  166. ProgramStateRef State = C.getState();
  167. const Expr *VASubExpr = VAA->getSubExpr();
  168. SVal VAListSVal = C.getSVal(VASubExpr);
  169. bool Symbolic;
  170. const MemRegion *VAList =
  171. getVAListAsRegion(VAListSVal, VASubExpr, Symbolic, C);
  172. if (!VAList)
  173. return;
  174. if (Symbolic)
  175. return;
  176. if (!State->contains<InitializedVALists>(VAList))
  177. reportUninitializedAccess(
  178. VAList, "va_arg() is called on an uninitialized va_list", C);
  179. }
  180. void ValistChecker::checkDeadSymbols(SymbolReaper &SR,
  181. CheckerContext &C) const {
  182. ProgramStateRef State = C.getState();
  183. InitializedVAListsTy TrackedVALists = State->get<InitializedVALists>();
  184. RegionVector LeakedVALists;
  185. for (auto Reg : TrackedVALists) {
  186. if (SR.isLiveRegion(Reg))
  187. continue;
  188. LeakedVALists.push_back(Reg);
  189. State = State->remove<InitializedVALists>(Reg);
  190. }
  191. if (ExplodedNode *N = C.addTransition(State))
  192. reportLeakedVALists(LeakedVALists, "Initialized va_list", " is leaked", C,
  193. N);
  194. }
  195. // This function traverses the exploded graph backwards and finds the node where
  196. // the va_list is initialized. That node is used for uniquing the bug paths.
  197. // It is not likely that there are several different va_lists that belongs to
  198. // different stack frames, so that case is not yet handled.
  199. const ExplodedNode *
  200. ValistChecker::getStartCallSite(const ExplodedNode *N,
  201. const MemRegion *Reg) const {
  202. const LocationContext *LeakContext = N->getLocationContext();
  203. const ExplodedNode *StartCallNode = N;
  204. bool FoundInitializedState = false;
  205. while (N) {
  206. ProgramStateRef State = N->getState();
  207. if (!State->contains<InitializedVALists>(Reg)) {
  208. if (FoundInitializedState)
  209. break;
  210. } else {
  211. FoundInitializedState = true;
  212. }
  213. const LocationContext *NContext = N->getLocationContext();
  214. if (NContext == LeakContext || NContext->isParentOf(LeakContext))
  215. StartCallNode = N;
  216. N = N->pred_empty() ? nullptr : *(N->pred_begin());
  217. }
  218. return StartCallNode;
  219. }
  220. void ValistChecker::reportUninitializedAccess(const MemRegion *VAList,
  221. StringRef Msg,
  222. CheckerContext &C) const {
  223. if (!ChecksEnabled[CK_Uninitialized])
  224. return;
  225. if (ExplodedNode *N = C.generateErrorNode()) {
  226. if (!BT_uninitaccess)
  227. BT_uninitaccess.reset(new BugType(CheckNames[CK_Uninitialized],
  228. "Uninitialized va_list",
  229. categories::MemoryError));
  230. auto R = std::make_unique<PathSensitiveBugReport>(*BT_uninitaccess, Msg, N);
  231. R->markInteresting(VAList);
  232. R->addVisitor(std::make_unique<ValistBugVisitor>(VAList));
  233. C.emitReport(std::move(R));
  234. }
  235. }
  236. void ValistChecker::reportLeakedVALists(const RegionVector &LeakedVALists,
  237. StringRef Msg1, StringRef Msg2,
  238. CheckerContext &C, ExplodedNode *N,
  239. bool ReportUninit) const {
  240. if (!(ChecksEnabled[CK_Unterminated] ||
  241. (ChecksEnabled[CK_Uninitialized] && ReportUninit)))
  242. return;
  243. for (auto Reg : LeakedVALists) {
  244. if (!BT_leakedvalist) {
  245. // FIXME: maybe creating a new check name for this type of bug is a better
  246. // solution.
  247. BT_leakedvalist.reset(
  248. new BugType(CheckNames[CK_Unterminated].getName().empty()
  249. ? CheckNames[CK_Uninitialized]
  250. : CheckNames[CK_Unterminated],
  251. "Leaked va_list", categories::MemoryError,
  252. /*SuppressOnSink=*/true));
  253. }
  254. const ExplodedNode *StartNode = getStartCallSite(N, Reg);
  255. PathDiagnosticLocation LocUsedForUniqueing;
  256. if (const Stmt *StartCallStmt = StartNode->getStmtForDiagnostics())
  257. LocUsedForUniqueing = PathDiagnosticLocation::createBegin(
  258. StartCallStmt, C.getSourceManager(), StartNode->getLocationContext());
  259. SmallString<100> Buf;
  260. llvm::raw_svector_ostream OS(Buf);
  261. OS << Msg1;
  262. std::string VariableName = Reg->getDescriptiveName();
  263. if (!VariableName.empty())
  264. OS << " " << VariableName;
  265. OS << Msg2;
  266. auto R = std::make_unique<PathSensitiveBugReport>(
  267. *BT_leakedvalist, OS.str(), N, LocUsedForUniqueing,
  268. StartNode->getLocationContext()->getDecl());
  269. R->markInteresting(Reg);
  270. R->addVisitor(std::make_unique<ValistBugVisitor>(Reg, true));
  271. C.emitReport(std::move(R));
  272. }
  273. }
  274. void ValistChecker::checkVAListStartCall(const CallEvent &Call,
  275. CheckerContext &C, bool IsCopy) const {
  276. bool Symbolic;
  277. const MemRegion *VAList =
  278. getVAListAsRegion(Call.getArgSVal(0), Call.getArgExpr(0), Symbolic, C);
  279. if (!VAList)
  280. return;
  281. ProgramStateRef State = C.getState();
  282. if (IsCopy) {
  283. const MemRegion *Arg2 =
  284. getVAListAsRegion(Call.getArgSVal(1), Call.getArgExpr(1), Symbolic, C);
  285. if (Arg2) {
  286. if (ChecksEnabled[CK_CopyToSelf] && VAList == Arg2) {
  287. RegionVector LeakedVALists{VAList};
  288. if (ExplodedNode *N = C.addTransition(State))
  289. reportLeakedVALists(LeakedVALists, "va_list",
  290. " is copied onto itself", C, N, true);
  291. return;
  292. } else if (!State->contains<InitializedVALists>(Arg2) && !Symbolic) {
  293. if (State->contains<InitializedVALists>(VAList)) {
  294. State = State->remove<InitializedVALists>(VAList);
  295. RegionVector LeakedVALists{VAList};
  296. if (ExplodedNode *N = C.addTransition(State))
  297. reportLeakedVALists(LeakedVALists, "Initialized va_list",
  298. " is overwritten by an uninitialized one", C, N,
  299. true);
  300. } else {
  301. reportUninitializedAccess(Arg2, "Uninitialized va_list is copied", C);
  302. }
  303. return;
  304. }
  305. }
  306. }
  307. if (State->contains<InitializedVALists>(VAList)) {
  308. RegionVector LeakedVALists{VAList};
  309. if (ExplodedNode *N = C.addTransition(State))
  310. reportLeakedVALists(LeakedVALists, "Initialized va_list",
  311. " is initialized again", C, N);
  312. return;
  313. }
  314. State = State->add<InitializedVALists>(VAList);
  315. C.addTransition(State);
  316. }
  317. void ValistChecker::checkVAListEndCall(const CallEvent &Call,
  318. CheckerContext &C) const {
  319. bool Symbolic;
  320. const MemRegion *VAList =
  321. getVAListAsRegion(Call.getArgSVal(0), Call.getArgExpr(0), Symbolic, C);
  322. if (!VAList)
  323. return;
  324. // We did not see va_start call, but the source of the region is unknown.
  325. // Be conservative and assume the best.
  326. if (Symbolic)
  327. return;
  328. if (!C.getState()->contains<InitializedVALists>(VAList)) {
  329. reportUninitializedAccess(
  330. VAList, "va_end() is called on an uninitialized va_list", C);
  331. return;
  332. }
  333. ProgramStateRef State = C.getState();
  334. State = State->remove<InitializedVALists>(VAList);
  335. C.addTransition(State);
  336. }
  337. PathDiagnosticPieceRef ValistChecker::ValistBugVisitor::VisitNode(
  338. const ExplodedNode *N, BugReporterContext &BRC, PathSensitiveBugReport &) {
  339. ProgramStateRef State = N->getState();
  340. ProgramStateRef StatePrev = N->getFirstPred()->getState();
  341. const Stmt *S = N->getStmtForDiagnostics();
  342. if (!S)
  343. return nullptr;
  344. StringRef Msg;
  345. if (State->contains<InitializedVALists>(Reg) &&
  346. !StatePrev->contains<InitializedVALists>(Reg))
  347. Msg = "Initialized va_list";
  348. else if (!State->contains<InitializedVALists>(Reg) &&
  349. StatePrev->contains<InitializedVALists>(Reg))
  350. Msg = "Ended va_list";
  351. if (Msg.empty())
  352. return nullptr;
  353. PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
  354. N->getLocationContext());
  355. return std::make_shared<PathDiagnosticEventPiece>(Pos, Msg, true);
  356. }
  357. void ento::registerValistBase(CheckerManager &mgr) {
  358. mgr.registerChecker<ValistChecker>();
  359. }
  360. bool ento::shouldRegisterValistBase(const CheckerManager &mgr) {
  361. return true;
  362. }
  363. #define REGISTER_CHECKER(name) \
  364. void ento::register##name##Checker(CheckerManager &mgr) { \
  365. ValistChecker *checker = mgr.getChecker<ValistChecker>(); \
  366. checker->ChecksEnabled[ValistChecker::CK_##name] = true; \
  367. checker->CheckNames[ValistChecker::CK_##name] = \
  368. mgr.getCurrentCheckerName(); \
  369. } \
  370. \
  371. bool ento::shouldRegister##name##Checker(const CheckerManager &mgr) { \
  372. return true; \
  373. }
  374. REGISTER_CHECKER(Uninitialized)
  375. REGISTER_CHECKER(Unterminated)
  376. REGISTER_CHECKER(CopyToSelf)