UnsafeBufferUsage.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695
  1. //===- UnsafeBufferUsage.cpp - Replace pointers with modern 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. #include "clang/Analysis/Analyses/UnsafeBufferUsage.h"
  9. #include "clang/AST/RecursiveASTVisitor.h"
  10. #include "clang/ASTMatchers/ASTMatchFinder.h"
  11. #include "llvm/ADT/SmallVector.h"
  12. #include <memory>
  13. #include <optional>
  14. using namespace llvm;
  15. using namespace clang;
  16. using namespace ast_matchers;
  17. namespace clang::ast_matchers {
  18. // A `RecursiveASTVisitor` that traverses all descendants of a given node "n"
  19. // except for those belonging to a different callable of "n".
  20. class MatchDescendantVisitor
  21. : public RecursiveASTVisitor<MatchDescendantVisitor> {
  22. public:
  23. typedef RecursiveASTVisitor<MatchDescendantVisitor> VisitorBase;
  24. // Creates an AST visitor that matches `Matcher` on all
  25. // descendants of a given node "n" except for the ones
  26. // belonging to a different callable of "n".
  27. MatchDescendantVisitor(const internal::DynTypedMatcher *Matcher,
  28. internal::ASTMatchFinder *Finder,
  29. internal::BoundNodesTreeBuilder *Builder,
  30. internal::ASTMatchFinder::BindKind Bind)
  31. : Matcher(Matcher), Finder(Finder), Builder(Builder), Bind(Bind),
  32. Matches(false) {}
  33. // Returns true if a match is found in a subtree of `DynNode`, which belongs
  34. // to the same callable of `DynNode`.
  35. bool findMatch(const DynTypedNode &DynNode) {
  36. Matches = false;
  37. if (const Stmt *StmtNode = DynNode.get<Stmt>()) {
  38. TraverseStmt(const_cast<Stmt *>(StmtNode));
  39. *Builder = ResultBindings;
  40. return Matches;
  41. }
  42. return false;
  43. }
  44. // The following are overriding methods from the base visitor class.
  45. // They are public only to allow CRTP to work. They are *not *part
  46. // of the public API of this class.
  47. // For the matchers so far used in safe buffers, we only need to match
  48. // `Stmt`s. To override more as needed.
  49. bool TraverseDecl(Decl *Node) {
  50. if (!Node)
  51. return true;
  52. if (!match(*Node))
  53. return false;
  54. // To skip callables:
  55. if (isa<FunctionDecl, BlockDecl, ObjCMethodDecl>(Node))
  56. return true;
  57. // Traverse descendants
  58. return VisitorBase::TraverseDecl(Node);
  59. }
  60. bool TraverseStmt(Stmt *Node, DataRecursionQueue *Queue = nullptr) {
  61. if (!Node)
  62. return true;
  63. if (!match(*Node))
  64. return false;
  65. // To skip callables:
  66. if (isa<LambdaExpr>(Node))
  67. return true;
  68. return VisitorBase::TraverseStmt(Node);
  69. }
  70. bool shouldVisitTemplateInstantiations() const { return true; }
  71. bool shouldVisitImplicitCode() const {
  72. // TODO: let's ignore implicit code for now
  73. return false;
  74. }
  75. private:
  76. // Sets 'Matched' to true if 'Matcher' matches 'Node'
  77. //
  78. // Returns 'true' if traversal should continue after this function
  79. // returns, i.e. if no match is found or 'Bind' is 'BK_All'.
  80. template <typename T> bool match(const T &Node) {
  81. internal::BoundNodesTreeBuilder RecursiveBuilder(*Builder);
  82. if (Matcher->matches(DynTypedNode::create(Node), Finder,
  83. &RecursiveBuilder)) {
  84. ResultBindings.addMatch(RecursiveBuilder);
  85. Matches = true;
  86. if (Bind != internal::ASTMatchFinder::BK_All)
  87. return false; // Abort as soon as a match is found.
  88. }
  89. return true;
  90. }
  91. const internal::DynTypedMatcher *const Matcher;
  92. internal::ASTMatchFinder *const Finder;
  93. internal::BoundNodesTreeBuilder *const Builder;
  94. internal::BoundNodesTreeBuilder ResultBindings;
  95. const internal::ASTMatchFinder::BindKind Bind;
  96. bool Matches;
  97. };
  98. AST_MATCHER_P(Stmt, forEveryDescendant, internal::Matcher<Stmt>, innerMatcher) {
  99. const DynTypedMatcher &DTM = static_cast<DynTypedMatcher>(innerMatcher);
  100. MatchDescendantVisitor Visitor(&DTM, Finder, Builder, ASTMatchFinder::BK_All);
  101. return Visitor.findMatch(DynTypedNode::create(Node));
  102. }
  103. } // namespace clang::ast_matchers
  104. namespace {
  105. // Because the analysis revolves around variables and their types, we'll need to
  106. // track uses of variables (aka DeclRefExprs).
  107. using DeclUseList = SmallVector<const DeclRefExpr *, 1>;
  108. // Convenience typedef.
  109. using FixItList = SmallVector<FixItHint, 4>;
  110. // Defined below.
  111. class Strategy;
  112. } // namespace
  113. // Because we're dealing with raw pointers, let's define what we mean by that.
  114. static auto hasPointerType() {
  115. return hasType(hasCanonicalType(pointerType()));
  116. }
  117. static auto hasArrayType() {
  118. return hasType(hasCanonicalType(arrayType()));
  119. }
  120. namespace {
  121. /// Gadget is an individual operation in the code that may be of interest to
  122. /// this analysis. Each (non-abstract) subclass corresponds to a specific
  123. /// rigid AST structure that constitutes an operation on a pointer-type object.
  124. /// Discovery of a gadget in the code corresponds to claiming that we understand
  125. /// what this part of code is doing well enough to potentially improve it.
  126. /// Gadgets can be warning (immediately deserving a warning) or fixable (not
  127. /// always deserving a warning per se, but requires our attention to identify
  128. /// it warrants a fixit).
  129. class Gadget {
  130. public:
  131. enum class Kind {
  132. #define GADGET(x) x,
  133. #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
  134. };
  135. /// Common type of ASTMatchers used for discovering gadgets.
  136. /// Useful for implementing the static matcher() methods
  137. /// that are expected from all non-abstract subclasses.
  138. using Matcher = decltype(stmt());
  139. Gadget(Kind K) : K(K) {}
  140. Kind getKind() const { return K; }
  141. virtual bool isWarningGadget() const = 0;
  142. virtual const Stmt *getBaseStmt() const = 0;
  143. /// Returns the list of pointer-type variables on which this gadget performs
  144. /// its operation. Typically, there's only one variable. This isn't a list
  145. /// of all DeclRefExprs in the gadget's AST!
  146. virtual DeclUseList getClaimedVarUseSites() const = 0;
  147. virtual ~Gadget() = default;
  148. private:
  149. Kind K;
  150. };
  151. /// Warning gadgets correspond to unsafe code patterns that warrants
  152. /// an immediate warning.
  153. class WarningGadget : public Gadget {
  154. public:
  155. WarningGadget(Kind K) : Gadget(K) {}
  156. static bool classof(const Gadget *G) { return G->isWarningGadget(); }
  157. bool isWarningGadget() const final { return true; }
  158. };
  159. /// Fixable gadgets correspond to code patterns that aren't always unsafe but need to be
  160. /// properly recognized in order to emit fixes. For example, if a raw pointer-type
  161. /// variable is replaced by a safe C++ container, every use of such variable must be
  162. /// carefully considered and possibly updated.
  163. class FixableGadget : public Gadget {
  164. public:
  165. FixableGadget(Kind K) : Gadget(K) {}
  166. static bool classof(const Gadget *G) { return !G->isWarningGadget(); }
  167. bool isWarningGadget() const final { return false; }
  168. /// Returns a fixit that would fix the current gadget according to
  169. /// the current strategy. Returns None if the fix cannot be produced;
  170. /// returns an empty list if no fixes are necessary.
  171. virtual std::optional<FixItList> getFixits(const Strategy &) const {
  172. return std::nullopt;
  173. }
  174. };
  175. using FixableGadgetList = std::vector<std::unique_ptr<FixableGadget>>;
  176. using WarningGadgetList = std::vector<std::unique_ptr<WarningGadget>>;
  177. /// An increment of a pointer-type value is unsafe as it may run the pointer
  178. /// out of bounds.
  179. class IncrementGadget : public WarningGadget {
  180. static constexpr const char *const OpTag = "op";
  181. const UnaryOperator *Op;
  182. public:
  183. IncrementGadget(const MatchFinder::MatchResult &Result)
  184. : WarningGadget(Kind::Increment),
  185. Op(Result.Nodes.getNodeAs<UnaryOperator>(OpTag)) {}
  186. static bool classof(const Gadget *G) {
  187. return G->getKind() == Kind::Increment;
  188. }
  189. static Matcher matcher() {
  190. return stmt(unaryOperator(
  191. hasOperatorName("++"),
  192. hasUnaryOperand(ignoringParenImpCasts(hasPointerType()))
  193. ).bind(OpTag));
  194. }
  195. const UnaryOperator *getBaseStmt() const override { return Op; }
  196. DeclUseList getClaimedVarUseSites() const override {
  197. SmallVector<const DeclRefExpr *, 2> Uses;
  198. if (const auto *DRE =
  199. dyn_cast<DeclRefExpr>(Op->getSubExpr()->IgnoreParenImpCasts())) {
  200. Uses.push_back(DRE);
  201. }
  202. return std::move(Uses);
  203. }
  204. };
  205. /// A decrement of a pointer-type value is unsafe as it may run the pointer
  206. /// out of bounds.
  207. class DecrementGadget : public WarningGadget {
  208. static constexpr const char *const OpTag = "op";
  209. const UnaryOperator *Op;
  210. public:
  211. DecrementGadget(const MatchFinder::MatchResult &Result)
  212. : WarningGadget(Kind::Decrement),
  213. Op(Result.Nodes.getNodeAs<UnaryOperator>(OpTag)) {}
  214. static bool classof(const Gadget *G) {
  215. return G->getKind() == Kind::Decrement;
  216. }
  217. static Matcher matcher() {
  218. return stmt(unaryOperator(
  219. hasOperatorName("--"),
  220. hasUnaryOperand(ignoringParenImpCasts(hasPointerType()))
  221. ).bind(OpTag));
  222. }
  223. const UnaryOperator *getBaseStmt() const override { return Op; }
  224. DeclUseList getClaimedVarUseSites() const override {
  225. if (const auto *DRE =
  226. dyn_cast<DeclRefExpr>(Op->getSubExpr()->IgnoreParenImpCasts())) {
  227. return {DRE};
  228. }
  229. return {};
  230. }
  231. };
  232. /// Array subscript expressions on raw pointers as if they're arrays. Unsafe as
  233. /// it doesn't have any bounds checks for the array.
  234. class ArraySubscriptGadget : public WarningGadget {
  235. static constexpr const char *const ArraySubscrTag = "arraySubscr";
  236. const ArraySubscriptExpr *ASE;
  237. public:
  238. ArraySubscriptGadget(const MatchFinder::MatchResult &Result)
  239. : WarningGadget(Kind::ArraySubscript),
  240. ASE(Result.Nodes.getNodeAs<ArraySubscriptExpr>(ArraySubscrTag)) {}
  241. static bool classof(const Gadget *G) {
  242. return G->getKind() == Kind::ArraySubscript;
  243. }
  244. static Matcher matcher() {
  245. // FIXME: What if the index is integer literal 0? Should this be
  246. // a safe gadget in this case?
  247. // clang-format off
  248. return stmt(arraySubscriptExpr(
  249. hasBase(ignoringParenImpCasts(
  250. anyOf(hasPointerType(), hasArrayType()))),
  251. unless(hasIndex(integerLiteral(equals(0)))))
  252. .bind(ArraySubscrTag));
  253. // clang-format on
  254. }
  255. const ArraySubscriptExpr *getBaseStmt() const override { return ASE; }
  256. DeclUseList getClaimedVarUseSites() const override {
  257. if (const auto *DRE =
  258. dyn_cast<DeclRefExpr>(ASE->getBase()->IgnoreParenImpCasts())) {
  259. return {DRE};
  260. }
  261. return {};
  262. }
  263. };
  264. /// A pointer arithmetic expression of one of the forms:
  265. /// \code
  266. /// ptr + n | n + ptr | ptr - n | ptr += n | ptr -= n
  267. /// \endcode
  268. class PointerArithmeticGadget : public WarningGadget {
  269. static constexpr const char *const PointerArithmeticTag = "ptrAdd";
  270. static constexpr const char *const PointerArithmeticPointerTag = "ptrAddPtr";
  271. const BinaryOperator *PA; // pointer arithmetic expression
  272. const Expr * Ptr; // the pointer expression in `PA`
  273. public:
  274. PointerArithmeticGadget(const MatchFinder::MatchResult &Result)
  275. : WarningGadget(Kind::PointerArithmetic),
  276. PA(Result.Nodes.getNodeAs<BinaryOperator>(PointerArithmeticTag)),
  277. Ptr(Result.Nodes.getNodeAs<Expr>(PointerArithmeticPointerTag)) {}
  278. static bool classof(const Gadget *G) {
  279. return G->getKind() == Kind::PointerArithmetic;
  280. }
  281. static Matcher matcher() {
  282. auto HasIntegerType = anyOf(
  283. hasType(isInteger()), hasType(enumType()));
  284. auto PtrAtRight = allOf(hasOperatorName("+"),
  285. hasRHS(expr(hasPointerType()).bind(PointerArithmeticPointerTag)),
  286. hasLHS(HasIntegerType));
  287. auto PtrAtLeft = allOf(
  288. anyOf(hasOperatorName("+"), hasOperatorName("-"),
  289. hasOperatorName("+="), hasOperatorName("-=")),
  290. hasLHS(expr(hasPointerType()).bind(PointerArithmeticPointerTag)),
  291. hasRHS(HasIntegerType));
  292. return stmt(binaryOperator(anyOf(PtrAtLeft, PtrAtRight)).bind(PointerArithmeticTag));
  293. }
  294. const Stmt *getBaseStmt() const override { return PA; }
  295. DeclUseList getClaimedVarUseSites() const override {
  296. if (const auto *DRE =
  297. dyn_cast<DeclRefExpr>(Ptr->IgnoreParenImpCasts())) {
  298. return {DRE};
  299. }
  300. return {};
  301. }
  302. // FIXME: pointer adding zero should be fine
  303. //FIXME: this gadge will need a fix-it
  304. };
  305. } // namespace
  306. namespace {
  307. // An auxiliary tracking facility for the fixit analysis. It helps connect
  308. // declarations to its and make sure we've covered all uses with our analysis
  309. // before we try to fix the declaration.
  310. class DeclUseTracker {
  311. using UseSetTy = SmallSet<const DeclRefExpr *, 16>;
  312. using DefMapTy = DenseMap<const VarDecl *, const DeclStmt *>;
  313. // Allocate on the heap for easier move.
  314. std::unique_ptr<UseSetTy> Uses{std::make_unique<UseSetTy>()};
  315. DefMapTy Defs{};
  316. public:
  317. DeclUseTracker() = default;
  318. DeclUseTracker(const DeclUseTracker &) = delete; // Let's avoid copies.
  319. DeclUseTracker(DeclUseTracker &&) = default;
  320. DeclUseTracker &operator=(DeclUseTracker &&) = default;
  321. // Start tracking a freshly discovered DRE.
  322. void discoverUse(const DeclRefExpr *DRE) { Uses->insert(DRE); }
  323. // Stop tracking the DRE as it's been fully figured out.
  324. void claimUse(const DeclRefExpr *DRE) {
  325. assert(Uses->count(DRE) &&
  326. "DRE not found or claimed by multiple matchers!");
  327. Uses->erase(DRE);
  328. }
  329. // A variable is unclaimed if at least one use is unclaimed.
  330. bool hasUnclaimedUses(const VarDecl *VD) const {
  331. // FIXME: Can this be less linear? Maybe maintain a map from VDs to DREs?
  332. return any_of(*Uses, [VD](const DeclRefExpr *DRE) {
  333. return DRE->getDecl()->getCanonicalDecl() == VD->getCanonicalDecl();
  334. });
  335. }
  336. void discoverDecl(const DeclStmt *DS) {
  337. for (const Decl *D : DS->decls()) {
  338. if (const auto *VD = dyn_cast<VarDecl>(D)) {
  339. // FIXME: Assertion temporarily disabled due to a bug in
  340. // ASTMatcher internal behavior in presence of GNU
  341. // statement-expressions. We need to properly investigate this
  342. // because it can screw up our algorithm in other ways.
  343. // assert(Defs.count(VD) == 0 && "Definition already discovered!");
  344. Defs[VD] = DS;
  345. }
  346. }
  347. }
  348. const DeclStmt *lookupDecl(const VarDecl *VD) const {
  349. auto It = Defs.find(VD);
  350. assert(It != Defs.end() && "Definition never discovered!");
  351. return It->second;
  352. }
  353. };
  354. } // namespace
  355. namespace {
  356. // Strategy is a map from variables to the way we plan to emit fixes for
  357. // these variables. It is figured out gradually by trying different fixes
  358. // for different variables depending on gadgets in which these variables
  359. // participate.
  360. class Strategy {
  361. public:
  362. enum class Kind {
  363. Wontfix, // We don't plan to emit a fixit for this variable.
  364. Span, // We recommend replacing the variable with std::span.
  365. Iterator, // We recommend replacing the variable with std::span::iterator.
  366. Array, // We recommend replacing the variable with std::array.
  367. Vector // We recommend replacing the variable with std::vector.
  368. };
  369. private:
  370. using MapTy = llvm::DenseMap<const VarDecl *, Kind>;
  371. MapTy Map;
  372. public:
  373. Strategy() = default;
  374. Strategy(const Strategy &) = delete; // Let's avoid copies.
  375. Strategy(Strategy &&) = default;
  376. void set(const VarDecl *VD, Kind K) {
  377. Map[VD] = K;
  378. }
  379. Kind lookup(const VarDecl *VD) const {
  380. auto I = Map.find(VD);
  381. if (I == Map.end())
  382. return Kind::Wontfix;
  383. return I->second;
  384. }
  385. };
  386. } // namespace
  387. /// Scan the function and return a list of gadgets found with provided kits.
  388. static std::tuple<FixableGadgetList, WarningGadgetList, DeclUseTracker> findGadgets(const Decl *D) {
  389. struct GadgetFinderCallback : MatchFinder::MatchCallback {
  390. FixableGadgetList FixableGadgets;
  391. WarningGadgetList WarningGadgets;
  392. DeclUseTracker Tracker;
  393. void run(const MatchFinder::MatchResult &Result) override {
  394. // In debug mode, assert that we've found exactly one gadget.
  395. // This helps us avoid conflicts in .bind() tags.
  396. #if NDEBUG
  397. #define NEXT return
  398. #else
  399. [[maybe_unused]] int numFound = 0;
  400. #define NEXT ++numFound
  401. #endif
  402. if (const auto *DRE = Result.Nodes.getNodeAs<DeclRefExpr>("any_dre")) {
  403. Tracker.discoverUse(DRE);
  404. NEXT;
  405. }
  406. if (const auto *DS = Result.Nodes.getNodeAs<DeclStmt>("any_ds")) {
  407. Tracker.discoverDecl(DS);
  408. NEXT;
  409. }
  410. // Figure out which matcher we've found, and call the appropriate
  411. // subclass constructor.
  412. // FIXME: Can we do this more logarithmically?
  413. #define FIXABLE_GADGET(name) \
  414. if (Result.Nodes.getNodeAs<Stmt>(#name)) { \
  415. FixableGadgets.push_back(std::make_unique<name ## Gadget>(Result)); \
  416. NEXT; \
  417. }
  418. #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
  419. #define WARNING_GADGET(name) \
  420. if (Result.Nodes.getNodeAs<Stmt>(#name)) { \
  421. WarningGadgets.push_back(std::make_unique<name ## Gadget>(Result)); \
  422. NEXT; \
  423. }
  424. #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
  425. assert(numFound >= 1 && "Gadgets not found in match result!");
  426. assert(numFound <= 1 && "Conflicting bind tags in gadgets!");
  427. }
  428. };
  429. MatchFinder M;
  430. GadgetFinderCallback CB;
  431. // clang-format off
  432. M.addMatcher(
  433. stmt(forEveryDescendant(
  434. stmt(anyOf(
  435. // Add Gadget::matcher() for every gadget in the registry.
  436. #define GADGET(x) \
  437. x ## Gadget::matcher().bind(#x),
  438. #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
  439. // In parallel, match all DeclRefExprs so that to find out
  440. // whether there are any uncovered by gadgets.
  441. declRefExpr(anyOf(hasPointerType(), hasArrayType()),
  442. to(varDecl())).bind("any_dre"),
  443. // Also match DeclStmts because we'll need them when fixing
  444. // their underlying VarDecls that otherwise don't have
  445. // any backreferences to DeclStmts.
  446. declStmt().bind("any_ds")
  447. ))
  448. // FIXME: Idiomatically there should be a forCallable(equalsNode(D))
  449. // here, to make sure that the statement actually belongs to the
  450. // function and not to a nested function. However, forCallable uses
  451. // ParentMap which can't be used before the AST is fully constructed.
  452. // The original problem doesn't sound like it needs ParentMap though,
  453. // maybe there's a more direct solution?
  454. )),
  455. &CB
  456. );
  457. // clang-format on
  458. M.match(*D->getBody(), D->getASTContext());
  459. // Gadgets "claim" variables they're responsible for. Once this loop finishes,
  460. // the tracker will only track DREs that weren't claimed by any gadgets,
  461. // i.e. not understood by the analysis.
  462. for (const auto &G : CB.FixableGadgets) {
  463. for (const auto *DRE : G->getClaimedVarUseSites()) {
  464. CB.Tracker.claimUse(DRE);
  465. }
  466. }
  467. return {std::move(CB.FixableGadgets), std::move(CB.WarningGadgets), std::move(CB.Tracker)};
  468. }
  469. struct WarningGadgetSets {
  470. std::map<const VarDecl *, std::set<std::unique_ptr<WarningGadget>>> byVar;
  471. // These Gadgets are not related to pointer variables (e. g. temporaries).
  472. llvm::SmallVector<std::unique_ptr<WarningGadget>, 16> noVar;
  473. };
  474. static WarningGadgetSets
  475. groupWarningGadgetsByVar(WarningGadgetList &&AllUnsafeOperations) {
  476. WarningGadgetSets result;
  477. // If some gadgets cover more than one
  478. // variable, they'll appear more than once in the map.
  479. for (auto &G : AllUnsafeOperations) {
  480. DeclUseList ClaimedVarUseSites = G->getClaimedVarUseSites();
  481. bool AssociatedWithVarDecl = false;
  482. for (const DeclRefExpr *DRE : ClaimedVarUseSites) {
  483. if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
  484. result.byVar[VD].emplace(std::move(G));
  485. AssociatedWithVarDecl = true;
  486. }
  487. }
  488. if (!AssociatedWithVarDecl) {
  489. result.noVar.emplace_back(std::move(G));
  490. continue;
  491. }
  492. }
  493. return result;
  494. }
  495. struct FixableGadgetSets {
  496. std::map<const VarDecl *, std::set<std::unique_ptr<FixableGadget>>> byVar;
  497. };
  498. static FixableGadgetSets
  499. groupFixablesByVar(FixableGadgetList &&AllFixableOperations) {
  500. FixableGadgetSets FixablesForUnsafeVars;
  501. for (auto &F : AllFixableOperations) {
  502. DeclUseList DREs = F->getClaimedVarUseSites();
  503. for (const DeclRefExpr *DRE : DREs) {
  504. if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
  505. FixablesForUnsafeVars.byVar[VD].emplace(std::move(F));
  506. }
  507. }
  508. }
  509. return FixablesForUnsafeVars;
  510. }
  511. static std::map<const VarDecl *, FixItList>
  512. getFixIts(FixableGadgetSets &FixablesForUnsafeVars, const Strategy &S) {
  513. std::map<const VarDecl *, FixItList> FixItsForVariable;
  514. for (const auto &[VD, Fixables] : FixablesForUnsafeVars.byVar) {
  515. // TODO fixVariable - fixit for the variable itself
  516. bool ImpossibleToFix = false;
  517. llvm::SmallVector<FixItHint, 16> FixItsForVD;
  518. for (const auto &F : Fixables) {
  519. llvm::Optional<FixItList> Fixits = F->getFixits(S);
  520. if (!Fixits) {
  521. ImpossibleToFix = true;
  522. break;
  523. } else {
  524. const FixItList CorrectFixes = Fixits.value();
  525. FixItsForVD.insert(FixItsForVD.end(), CorrectFixes.begin(),
  526. CorrectFixes.end());
  527. }
  528. }
  529. if (ImpossibleToFix)
  530. FixItsForVariable.erase(VD);
  531. else
  532. FixItsForVariable[VD].insert(FixItsForVariable[VD].end(),
  533. FixItsForVD.begin(), FixItsForVD.end());
  534. }
  535. return FixItsForVariable;
  536. }
  537. static Strategy
  538. getNaiveStrategy(const llvm::SmallVectorImpl<const VarDecl *> &UnsafeVars) {
  539. Strategy S;
  540. for (const VarDecl *VD : UnsafeVars) {
  541. S.set(VD, Strategy::Kind::Span);
  542. }
  543. return S;
  544. }
  545. void clang::checkUnsafeBufferUsage(const Decl *D,
  546. UnsafeBufferUsageHandler &Handler) {
  547. assert(D && D->getBody());
  548. WarningGadgetSets UnsafeOps;
  549. FixableGadgetSets FixablesForUnsafeVars;
  550. DeclUseTracker Tracker;
  551. {
  552. auto [FixableGadgets, WarningGadgets, TrackerRes] = findGadgets(D);
  553. UnsafeOps = groupWarningGadgetsByVar(std::move(WarningGadgets));
  554. FixablesForUnsafeVars = groupFixablesByVar(std::move(FixableGadgets));
  555. Tracker = std::move(TrackerRes);
  556. }
  557. // Filter out non-local vars and vars with unclaimed DeclRefExpr-s.
  558. for (auto it = FixablesForUnsafeVars.byVar.cbegin();
  559. it != FixablesForUnsafeVars.byVar.cend();) {
  560. // FIXME: Support ParmVarDecl as well.
  561. if (!it->first->isLocalVarDecl() || Tracker.hasUnclaimedUses(it->first)) {
  562. it = FixablesForUnsafeVars.byVar.erase(it);
  563. } else {
  564. ++it;
  565. }
  566. }
  567. llvm::SmallVector<const VarDecl *, 16> UnsafeVars;
  568. for (const auto &[VD, ignore] : FixablesForUnsafeVars.byVar)
  569. UnsafeVars.push_back(VD);
  570. Strategy NaiveStrategy = getNaiveStrategy(UnsafeVars);
  571. std::map<const VarDecl *, FixItList> FixItsForVariable =
  572. getFixIts(FixablesForUnsafeVars, NaiveStrategy);
  573. // FIXME Detect overlapping FixIts.
  574. for (const auto &G : UnsafeOps.noVar) {
  575. Handler.handleUnsafeOperation(G->getBaseStmt(), /*IsRelatedToDecl=*/false);
  576. }
  577. for (const auto &[VD, WarningGadgets] : UnsafeOps.byVar) {
  578. auto FixItsIt = FixItsForVariable.find(VD);
  579. Handler.handleFixableVariable(VD, FixItsIt != FixItsForVariable.end()
  580. ? std::move(FixItsIt->second)
  581. : FixItList{});
  582. for (const auto &G : WarningGadgets) {
  583. Handler.handleUnsafeOperation(G->getBaseStmt(), /*IsRelatedToDecl=*/true);
  584. }
  585. }
  586. }