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