@@ -5433,9 +5433,316 @@ static void applyGadgets(const Decl *D, FixableGadgetList FixableGadgets,
5433
5433
}
5434
5434
}
5435
5435
5436
+ // Checks if Self and Other are the same member bases. This supports only very
5437
+ // simple forms of member bases.
5438
+ static bool isSameMemberBase (const Expr *Self, const Expr *Other) {
5439
+ for (;;) {
5440
+ if (Self == Other)
5441
+ return true ;
5442
+
5443
+ const auto *SelfICE = dyn_cast<ImplicitCastExpr>(Self);
5444
+ const auto *OtherICE = dyn_cast<ImplicitCastExpr>(Other);
5445
+ if (SelfICE && OtherICE && SelfICE->getCastKind () == CK_LValueToRValue &&
5446
+ OtherICE->getCastKind () == CK_LValueToRValue) {
5447
+ Self = SelfICE->getSubExpr ();
5448
+ Other = OtherICE->getSubExpr ();
5449
+ }
5450
+
5451
+ const auto *SelfDRE = dyn_cast<DeclRefExpr>(Self);
5452
+ const auto *OtherDRE = dyn_cast<DeclRefExpr>(Other);
5453
+ if (SelfDRE && OtherDRE)
5454
+ return SelfDRE->getDecl () == OtherDRE->getDecl ();
5455
+
5456
+ const auto *SelfME = dyn_cast<MemberExpr>(Self);
5457
+ const auto *OtherME = dyn_cast<MemberExpr>(Other);
5458
+ if (!SelfME || !OtherME ||
5459
+ SelfME->getMemberDecl () != OtherME->getMemberDecl ()) {
5460
+ return false ;
5461
+ }
5462
+
5463
+ Self = SelfME->getBase ();
5464
+ Other = OtherME->getBase ();
5465
+ }
5466
+ }
5467
+
5468
+ using DependentDeclSetTy = llvm::SmallPtrSet<const ValueDecl *, 4 >;
5469
+
5470
+ // Gets the set/closure of bounds-attributed pointers and counts that belong to
5471
+ // the same group. Consider the following example:
5472
+ // int a, b, c;
5473
+ // int *__counted_by(a + b) p;
5474
+ // int *__counted_by(b - c) q;
5475
+ // Passing any of the variables above as `InitVD`, the function should return
5476
+ // the closure `{a, b, c, p, q}`.
5477
+ static DependentDeclSetTy GetBoundsAttributedClosure (const ValueDecl *InitVD) {
5478
+ DependentDeclSetTy Set;
5479
+
5480
+ llvm::SmallVector<const ValueDecl *, 4 > WorkList;
5481
+ WorkList.push_back (InitVD);
5482
+
5483
+ while (!WorkList.empty ()) {
5484
+ const ValueDecl *CurVD = WorkList.pop_back_val ();
5485
+ bool Inserted = Set.insert (CurVD).second ;
5486
+ if (!Inserted)
5487
+ continue ;
5488
+
5489
+ // If CurVD is a dependent decl, add the pointers that depend on CurVD.
5490
+ for (const auto *Attr : CurVD->specific_attrs <DependerDeclsAttr>()) {
5491
+ for (const Decl *D : Attr->dependerDecls ()) {
5492
+ if (const auto *VD = dyn_cast<ValueDecl>(D))
5493
+ WorkList.push_back (VD);
5494
+ }
5495
+ }
5496
+
5497
+ // If CurVD is a bounds-attributed pointer (or pointer to it), add its
5498
+ // dependent decls.
5499
+ QualType Ty = CurVD->getType ();
5500
+ const auto *BAT = Ty->getAs <BoundsAttributedType>();
5501
+ if (!BAT && Ty->isPointerType ())
5502
+ BAT = Ty->getPointeeType ()->getAs <BoundsAttributedType>();
5503
+ if (BAT) {
5504
+ for (const auto &DI : BAT->dependent_decls ())
5505
+ WorkList.push_back (DI.getDecl ());
5506
+ }
5507
+ }
5508
+
5509
+ return Set;
5510
+ }
5511
+
5512
+ // Bounds-attributed pointer or dependent count.
5513
+ struct BoundsAttributedObject {
5514
+ const ValueDecl *Decl = nullptr ;
5515
+ const Expr *MemberBase = nullptr ;
5516
+ int DerefLevel = 0 ;
5517
+ };
5518
+
5519
+ static std::optional<BoundsAttributedObject>
5520
+ getBoundsAttributedObject (const Expr *E) {
5521
+ E = E->IgnoreParenCasts ();
5522
+
5523
+ int DerefLevel = 0 ;
5524
+ while (const auto *UO = dyn_cast<UnaryOperator>(E)) {
5525
+ if (UO->getOpcode () == UO_Deref)
5526
+ DerefLevel++;
5527
+ else if (UO->getOpcode () == UO_AddrOf)
5528
+ DerefLevel--;
5529
+ else
5530
+ break ;
5531
+ E = UO->getSubExpr ()->IgnoreParenCasts ();
5532
+ }
5533
+ assert (DerefLevel >= 0 );
5534
+
5535
+ const ValueDecl *Decl;
5536
+ const Expr *MemberBase = nullptr ;
5537
+
5538
+ if (const auto *DRE = dyn_cast<DeclRefExpr>(E))
5539
+ Decl = DRE->getDecl ();
5540
+ else if (const auto *ME = dyn_cast<MemberExpr>(E)) {
5541
+ Decl = ME->getMemberDecl ();
5542
+ MemberBase = ME->getBase ();
5543
+ } else
5544
+ return std::nullopt ;
5545
+
5546
+ QualType Ty = Decl->getType ();
5547
+ bool IsBoundsAttributedPointer =
5548
+ Ty->isBoundsAttributedType () ||
5549
+ (Ty->isPointerType () && Ty->getPointeeType ()->isBoundsAttributedType ());
5550
+ bool IsDependentDecl = Decl->hasAttr <DependerDeclsAttr>();
5551
+ if (IsBoundsAttributedPointer || IsDependentDecl)
5552
+ return {{Decl, MemberBase, DerefLevel}};
5553
+
5554
+ return std::nullopt ;
5555
+ }
5556
+
5557
+ struct BoundsAttributedAssignmentGroup {
5558
+ DependentDeclSetTy DeclClosure;
5559
+ llvm::SmallVector<const BinaryOperator *, 4 > Assignments;
5560
+ llvm::SmallVector<BoundsAttributedObject, 4 > AssignedObjects;
5561
+ using DeclUseTy = std::pair<const ValueDecl *, const Expr *>;
5562
+ const Expr *MemberBase = nullptr ;
5563
+
5564
+ void Init (const BoundsAttributedObject &Object) {
5565
+ DeclClosure = GetBoundsAttributedClosure (Object.Decl );
5566
+ MemberBase = Object.MemberBase ;
5567
+ }
5568
+
5569
+ void Clear () {
5570
+ DeclClosure.clear ();
5571
+ Assignments.clear ();
5572
+ AssignedObjects.clear ();
5573
+ MemberBase = nullptr ;
5574
+ }
5575
+
5576
+ bool Empty () const {
5577
+ return DeclClosure.empty ();
5578
+ }
5579
+
5580
+ bool IsPartOfGroup (const BoundsAttributedObject &Object) const {
5581
+ if (!DeclClosure.contains (Object.Decl ))
5582
+ return false ;
5583
+ if (MemberBase)
5584
+ return Object.MemberBase &&
5585
+ isSameMemberBase (MemberBase, Object.MemberBase );
5586
+ return true ;
5587
+ }
5588
+
5589
+ void AddAssignment (const BoundsAttributedObject &Object,
5590
+ const BinaryOperator *BO) {
5591
+ Assignments.push_back (BO);
5592
+ AssignedObjects.push_back (Object);
5593
+ }
5594
+ };
5595
+
5596
+ // Visitor that is responsible for finding bounds-attributed assignment groups
5597
+ // and standalone assignments to bounds-attributed objects.
5598
+ //
5599
+ // Bounds-attributed groups must be inside of a CompoundStmt:
5600
+ // void foo(int *__counted_by(count) p, int count) {
5601
+ // p = ...;
5602
+ // count = ...;
5603
+ // }
5604
+ // Here, the group consists of both assignments to p and count. Note that the
5605
+ // function body is a CompoundStmt.
5606
+ //
5607
+ // Standalone assignments are modifications of bounds-attributed objects that
5608
+ // are not simple assignments directly in a CompoundStmt:
5609
+ // void foo(int *__counted_by(count) p, int count) {
5610
+ // q = p = ...;
5611
+ // ^ standalone assignment to __counted_by() pointer
5612
+ // n = count += ...;
5613
+ // ^ standalone assignment to dependent count
5614
+ // }
5615
+ struct BoundsAttributedGroupFinder
5616
+ : public ConstStmtVisitor<BoundsAttributedGroupFinder> {
5617
+ using GroupHandlerTy = void (const BoundsAttributedAssignmentGroup &Group);
5618
+ using AssignHandlerTy = void (const Expr *, const ValueDecl *);
5619
+ std::function<GroupHandlerTy> GroupHandler;
5620
+ std::function<AssignHandlerTy> BadStandaloneAssignHandler;
5621
+ BoundsAttributedAssignmentGroup CurGroup;
5622
+
5623
+ explicit BoundsAttributedGroupFinder (
5624
+ std::function<GroupHandlerTy> GroupHandler,
5625
+ std::function<AssignHandlerTy> BadStandaloneAssignHandler)
5626
+ : GroupHandler(std::move(GroupHandler)),
5627
+ BadStandaloneAssignHandler(std::move(BadStandaloneAssignHandler)) {}
5628
+
5629
+ void VisitChildren (const Stmt *S) {
5630
+ for (const Stmt *Child : S->children ())
5631
+ Visit (Child);
5632
+ }
5633
+
5634
+ void VisitStmt (const Stmt *S) { VisitChildren (S); }
5635
+
5636
+ void VisitCompoundStmt (const CompoundStmt *CS) {
5637
+ for (const Stmt *Child : CS->children ()) {
5638
+ const Stmt *E = Child;
5639
+
5640
+ // See through `ExprWithCleanups`. Clang will attach those nodes when C++
5641
+ // temporary object needs to be materialized. In our case, this can
5642
+ // happen when we create a temporary span with `sp.first()`. Then, the
5643
+ // structure is going to be:
5644
+ // CompoundStmt
5645
+ // `-ExprWithCleanups
5646
+ // `-BinaryOperator ...
5647
+ if (const auto *EWC = dyn_cast<ExprWithCleanups>(E))
5648
+ E = EWC->getSubExpr ();
5649
+
5650
+ const auto *BO = dyn_cast<BinaryOperator>(E);
5651
+ if (BO && BO->getOpcode () == BO_Assign)
5652
+ HandleAssignInCompound (BO);
5653
+ else {
5654
+ FinishGroup ();
5655
+ Visit (Child);
5656
+ }
5657
+ }
5658
+
5659
+ FinishGroup ();
5660
+ }
5661
+
5662
+ void HandleAssignInCompound (const BinaryOperator *AssignOp) {
5663
+ const auto ObjectOpt = getBoundsAttributedObject (AssignOp->getLHS ());
5664
+ if (!ObjectOpt.has_value ()) {
5665
+ FinishGroup ();
5666
+ VisitChildren (AssignOp);
5667
+ return ;
5668
+ }
5669
+
5670
+ if (!CurGroup.IsPartOfGroup (*ObjectOpt)) {
5671
+ FinishGroup ();
5672
+ CurGroup.Init (*ObjectOpt);
5673
+ }
5674
+
5675
+ CurGroup.AddAssignment (*ObjectOpt, AssignOp);
5676
+ VisitChildren (AssignOp->getRHS ());
5677
+ }
5678
+
5679
+ void FinishGroup () {
5680
+ if (CurGroup.Empty ())
5681
+ return ;
5682
+ GroupHandler (CurGroup);
5683
+ CurGroup.Clear ();
5684
+ }
5685
+
5686
+ // Handle statements that might modify bounds-attributed pointer/count, but
5687
+ // are not directly in a CompoundStmt.
5688
+
5689
+ void VisitBinaryOperator (const BinaryOperator *BO) {
5690
+ VisitChildren (BO);
5691
+
5692
+ if (BO->isAssignmentOp ())
5693
+ CheckBadStandaloneAssign (BO, BO->getLHS ());
5694
+ }
5695
+
5696
+ void VisitUnaryOperator (const UnaryOperator *UO) {
5697
+ VisitChildren (UO);
5698
+
5699
+ if (UO->isIncrementDecrementOp ())
5700
+ CheckBadStandaloneAssign (UO, UO->getSubExpr ());
5701
+ }
5702
+
5703
+ void CheckBadStandaloneAssign (const Expr *E, const Expr *Sub) {
5704
+ const auto DA = getBoundsAttributedObject (Sub);
5705
+ if (DA.has_value ())
5706
+ BadStandaloneAssignHandler (E, DA->Decl );
5707
+ }
5708
+ };
5709
+
5710
+ static void
5711
+ diagnoseStandaloneAssignToBoundsAttributed (const Expr *E, const ValueDecl *VD,
5712
+ UnsafeBufferUsageHandler &Handler,
5713
+ ASTContext &Ctx) {
5714
+ // Don't diagnose pointer arithmetic, since -Wunsafe-buffer-usage already does
5715
+ // it.
5716
+ bool IsPtrArith = false ;
5717
+ if (E->getType ()->isPointerType ()) {
5718
+ if (const auto *BO = dyn_cast<BinaryOperator>(E))
5719
+ IsPtrArith = BO->isCompoundAssignmentOp ();
5720
+ else if (const auto *UO = dyn_cast<UnaryOperator>(E)) {
5721
+ assert (UO->isIncrementDecrementOp ());
5722
+ IsPtrArith = true ;
5723
+ }
5724
+ }
5725
+ if (!IsPtrArith)
5726
+ Handler.handleStandaloneAssign (E, VD, /* IsRelatedToDecl=*/ false , Ctx);
5727
+ }
5728
+
5729
+ static void checkBoundsSafetyAssignments (const Stmt *S,
5730
+ UnsafeBufferUsageHandler &Handler,
5731
+ ASTContext &Ctx) {
5732
+ BoundsAttributedGroupFinder Finder (
5733
+ [&](const BoundsAttributedAssignmentGroup &Group) {
5734
+ // TODO: Check group constraints.
5735
+ },
5736
+ [&](const Expr *E, const ValueDecl *VD) {
5737
+ diagnoseStandaloneAssignToBoundsAttributed (E, VD, Handler, Ctx);
5738
+ });
5739
+ Finder.Visit (S);
5740
+ }
5741
+
5436
5742
void clang::checkUnsafeBufferUsage (const Decl *D,
5437
5743
UnsafeBufferUsageHandler &Handler,
5438
- bool EmitSuggestions) {
5744
+ bool EmitSuggestions,
5745
+ bool BoundsSafetyAttributes) {
5439
5746
#ifndef NDEBUG
5440
5747
Handler.clearDebugNotes ();
5441
5748
#endif
@@ -5481,6 +5788,11 @@ void clang::checkUnsafeBufferUsage(const Decl *D,
5481
5788
for (Stmt *S : Stmts) {
5482
5789
findGadgets (S, D->getASTContext (), Handler, EmitSuggestions, FixableGadgets,
5483
5790
WarningGadgets, Tracker);
5791
+
5792
+ // Run the bounds-safety assignment analysis if the attributes are enabled,
5793
+ // otherwise don't waste cycles.
5794
+ if (BoundsSafetyAttributes)
5795
+ checkBoundsSafetyAssignments (S, Handler, D->getASTContext ());
5484
5796
}
5485
5797
applyGadgets (D, std::move (FixableGadgets), std::move (WarningGadgets),
5486
5798
std::move (Tracker), Handler, EmitSuggestions);
0 commit comments