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