@@ -5539,9 +5539,322 @@ static void applyGadgets(const Decl *D, FixableGadgetList FixableGadgets,
5539
5539
}
5540
5540
}
5541
5541
5542
+ // Checks if Self and Other are the same member bases. This supports only very
5543
+ // simple forms of member bases.
5544
+ static bool isSameMemberBase (const Expr *Self, const Expr *Other) {
5545
+ for (;;) {
5546
+ if (Self == Other)
5547
+ return true ;
5548
+
5549
+ const auto *SelfICE = dyn_cast<ImplicitCastExpr>(Self);
5550
+ const auto *OtherICE = dyn_cast<ImplicitCastExpr>(Other);
5551
+ if (SelfICE && OtherICE && SelfICE->getCastKind () == CK_LValueToRValue &&
5552
+ OtherICE->getCastKind () == CK_LValueToRValue) {
5553
+ Self = SelfICE->getSubExpr ();
5554
+ Other = OtherICE->getSubExpr ();
5555
+ }
5556
+
5557
+ const auto *SelfDRE = dyn_cast<DeclRefExpr>(Self);
5558
+ const auto *OtherDRE = dyn_cast<DeclRefExpr>(Other);
5559
+ if (SelfDRE && OtherDRE)
5560
+ return SelfDRE->getDecl () == OtherDRE->getDecl ();
5561
+
5562
+ const auto *SelfME = dyn_cast<MemberExpr>(Self);
5563
+ const auto *OtherME = dyn_cast<MemberExpr>(Other);
5564
+ if (!SelfME || !OtherME ||
5565
+ SelfME->getMemberDecl () != OtherME->getMemberDecl ()) {
5566
+ return false ;
5567
+ }
5568
+
5569
+ Self = SelfME->getBase ();
5570
+ Other = OtherME->getBase ();
5571
+ }
5572
+ }
5573
+
5574
+ using DependentDeclSetTy = llvm::SmallPtrSet<const ValueDecl *, 4 >;
5575
+
5576
+ // Gets the set/closure of bounds-attributed pointers and counts that belong to
5577
+ // the same group. Consider the following example:
5578
+ // int a, b, c;
5579
+ // int *__counted_by(a + b) p;
5580
+ // int *__counted_by(b - c) q;
5581
+ // Passing any of the variables above as `InitVD`, the function should return
5582
+ // the closure `{a, b, c, p, q}`.
5583
+ static DependentDeclSetTy getBoundsAttributedClosure (const ValueDecl *InitVD) {
5584
+ DependentDeclSetTy Set;
5585
+
5586
+ llvm::SmallVector<const ValueDecl *, 4 > WorkList;
5587
+ WorkList.push_back (InitVD);
5588
+
5589
+ while (!WorkList.empty ()) {
5590
+ const ValueDecl *CurVD = WorkList.pop_back_val ();
5591
+ bool Inserted = Set.insert (CurVD).second ;
5592
+ if (!Inserted)
5593
+ continue ;
5594
+
5595
+ // If CurVD is a dependent decl, add the pointers that depend on CurVD.
5596
+ for (const auto *Attr : CurVD->specific_attrs <DependerDeclsAttr>()) {
5597
+ for (const Decl *D : Attr->dependerDecls ()) {
5598
+ if (const auto *VD = dyn_cast<ValueDecl>(D))
5599
+ WorkList.push_back (VD);
5600
+ }
5601
+ }
5602
+
5603
+ // If CurVD is a bounds-attributed pointer (or pointer to it), add its
5604
+ // dependent decls.
5605
+ QualType Ty = CurVD->getType ();
5606
+ const auto *BAT = Ty->getAs <BoundsAttributedType>();
5607
+ if (!BAT && Ty->isPointerType ())
5608
+ BAT = Ty->getPointeeType ()->getAs <BoundsAttributedType>();
5609
+ if (BAT) {
5610
+ for (const auto &DI : BAT->dependent_decls ())
5611
+ WorkList.push_back (DI.getDecl ());
5612
+ }
5613
+ }
5614
+
5615
+ return Set;
5616
+ }
5617
+
5618
+ // Bounds-attributed pointer or dependent count.
5619
+ struct BoundsAttributedObject {
5620
+ const ValueDecl *Decl = nullptr ;
5621
+ const Expr *MemberBase = nullptr ;
5622
+ int DerefLevel = 0 ;
5623
+ };
5624
+
5625
+ static std::optional<BoundsAttributedObject>
5626
+ getBoundsAttributedObject (const Expr *E) {
5627
+ E = E->IgnoreParenCasts ();
5628
+
5629
+ int DerefLevel = 0 ;
5630
+ while (const auto *UO = dyn_cast<UnaryOperator>(E)) {
5631
+ if (UO->getOpcode () == UO_Deref)
5632
+ DerefLevel++;
5633
+ else if (UO->getOpcode () == UO_AddrOf)
5634
+ DerefLevel--;
5635
+ else
5636
+ break ;
5637
+ E = UO->getSubExpr ()->IgnoreParenCasts ();
5638
+ }
5639
+ assert (DerefLevel >= 0 );
5640
+
5641
+ const ValueDecl *Decl;
5642
+ const Expr *MemberBase = nullptr ;
5643
+
5644
+ if (const auto *DRE = dyn_cast<DeclRefExpr>(E))
5645
+ Decl = DRE->getDecl ();
5646
+ else if (const auto *ME = dyn_cast<MemberExpr>(E)) {
5647
+ Decl = ME->getMemberDecl ();
5648
+ MemberBase = ME->getBase ();
5649
+ } else
5650
+ return std::nullopt ;
5651
+
5652
+ QualType Ty = Decl->getType ();
5653
+ bool IsBoundsAttributedPointer =
5654
+ Ty->isBoundsAttributedType () ||
5655
+ (Ty->isPointerType () && Ty->getPointeeType ()->isBoundsAttributedType ());
5656
+ if (IsBoundsAttributedPointer || Decl->isDependentCount ())
5657
+ return {{Decl, MemberBase, DerefLevel}};
5658
+
5659
+ return std::nullopt ;
5660
+ }
5661
+
5662
+ struct BoundsAttributedAssignmentGroup {
5663
+ DependentDeclSetTy DeclClosure;
5664
+ llvm::SmallVector<const BinaryOperator *, 4 > Assignments;
5665
+ llvm::SmallVector<BoundsAttributedObject, 4 > AssignedObjects;
5666
+ using DeclUseTy = std::pair<const ValueDecl *, const Expr *>;
5667
+ const Expr *MemberBase = nullptr ;
5668
+
5669
+ void init (const BoundsAttributedObject &Object) {
5670
+ DeclClosure = getBoundsAttributedClosure (Object.Decl );
5671
+ MemberBase = Object.MemberBase ;
5672
+ }
5673
+
5674
+ void clear () {
5675
+ DeclClosure.clear ();
5676
+ Assignments.clear ();
5677
+ AssignedObjects.clear ();
5678
+ MemberBase = nullptr ;
5679
+ }
5680
+
5681
+ bool empty () const {
5682
+ return DeclClosure.empty ();
5683
+ }
5684
+
5685
+ bool isPartOfGroup (const BoundsAttributedObject &Object) const {
5686
+ if (!DeclClosure.contains (Object.Decl ))
5687
+ return false ;
5688
+ if (MemberBase)
5689
+ return Object.MemberBase &&
5690
+ isSameMemberBase (MemberBase, Object.MemberBase );
5691
+ return true ;
5692
+ }
5693
+
5694
+ void addAssignment (const BoundsAttributedObject &Object,
5695
+ const BinaryOperator *BO) {
5696
+ Assignments.push_back (BO);
5697
+ AssignedObjects.push_back (Object);
5698
+ }
5699
+ };
5700
+
5701
+ // Visitor that is responsible for finding bounds-attributed assignment groups
5702
+ // and other assignments that can modify bounds-attributed objects.
5703
+ //
5704
+ // A bounds-attributed group must be a group of assignments that are children
5705
+ // of a CompoundStmt:
5706
+ // void foo(int *__counted_by(count) p, int count) {
5707
+ // p = ...;
5708
+ // count = ...;
5709
+ // }
5710
+ // Here, the group consists of both assignments to p and count. Note that the
5711
+ // function body is a CompoundStmt.
5712
+ //
5713
+ // We consider assignments to bounds-attributed objects that are not simple
5714
+ // statements or not children of a CompoundStmt as too complex, and we give up
5715
+ // trying to put them in a bounds-attributed group:
5716
+ // void foo(int *__counted_by(count) p, int count) {
5717
+ // q = p = ...;
5718
+ // ^ too complex assignment to __counted_by() pointer
5719
+ // n = count += ...;
5720
+ // ^ too complex assignment to dependent count
5721
+ // }
5722
+ // Note that while those assignments are descendants of a CompoundStmt, they
5723
+ // are not its direct children.
5724
+ struct BoundsAttributedGroupFinder
5725
+ : public ConstStmtVisitor<BoundsAttributedGroupFinder> {
5726
+ using GroupHandlerTy = void (const BoundsAttributedAssignmentGroup &Group);
5727
+ using AssignHandlerTy = void (const Expr *, const ValueDecl *);
5728
+ std::function<GroupHandlerTy> GroupHandler;
5729
+ std::function<AssignHandlerTy> TooComplexAssignHandler;
5730
+ BoundsAttributedAssignmentGroup CurGroup;
5731
+
5732
+ explicit BoundsAttributedGroupFinder (
5733
+ std::function<GroupHandlerTy> GroupHandler,
5734
+ std::function<AssignHandlerTy> TooComplexAssignHandler)
5735
+ : GroupHandler(std::move(GroupHandler)),
5736
+ TooComplexAssignHandler(std::move(TooComplexAssignHandler)) {}
5737
+
5738
+ void VisitChildren (const Stmt *S) {
5739
+ for (const Stmt *Child : S->children ()) {
5740
+ if (Child)
5741
+ Visit (Child);
5742
+ }
5743
+ }
5744
+
5745
+ void VisitStmt (const Stmt *S) { VisitChildren (S); }
5746
+
5747
+ void VisitCompoundStmt (const CompoundStmt *CS) {
5748
+ for (const Stmt *Child : CS->children ()) {
5749
+ const Stmt *E = Child;
5750
+
5751
+ // See through `ExprWithCleanups`. Clang will attach those nodes when C++
5752
+ // temporary object needs to be materialized. In our case, this can
5753
+ // happen when we create a temporary span with `sp.first()`. Then, the
5754
+ // structure is going to be:
5755
+ // CompoundStmt
5756
+ // `-ExprWithCleanups
5757
+ // `-BinaryOperator ...
5758
+ if (const auto *EWC = dyn_cast<ExprWithCleanups>(E))
5759
+ E = EWC->getSubExpr ();
5760
+
5761
+ const auto *BO = dyn_cast<BinaryOperator>(E);
5762
+ if (BO && BO->getOpcode () == BO_Assign)
5763
+ handleAssignInCompound (BO);
5764
+ else {
5765
+ finishGroup ();
5766
+ Visit (Child);
5767
+ }
5768
+ }
5769
+
5770
+ finishGroup ();
5771
+ }
5772
+
5773
+ void handleAssignInCompound (const BinaryOperator *AssignOp) {
5774
+ const auto ObjectOpt = getBoundsAttributedObject (AssignOp->getLHS ());
5775
+ if (!ObjectOpt.has_value ()) {
5776
+ finishGroup ();
5777
+ VisitChildren (AssignOp);
5778
+ return ;
5779
+ }
5780
+
5781
+ if (!CurGroup.isPartOfGroup (*ObjectOpt)) {
5782
+ finishGroup ();
5783
+ CurGroup.init (*ObjectOpt);
5784
+ }
5785
+
5786
+ CurGroup.addAssignment (*ObjectOpt, AssignOp);
5787
+ VisitChildren (AssignOp->getRHS ());
5788
+ }
5789
+
5790
+ void finishGroup () {
5791
+ if (CurGroup.empty ())
5792
+ return ;
5793
+ GroupHandler (CurGroup);
5794
+ CurGroup.clear ();
5795
+ }
5796
+
5797
+ // Handle statements that might modify bounds-attributed pointer/count, but
5798
+ // are not children of a CompoundStmt.
5799
+
5800
+ void VisitBinaryOperator (const BinaryOperator *BO) {
5801
+ VisitChildren (BO);
5802
+
5803
+ if (BO->isAssignmentOp ())
5804
+ checkTooComplexAssign (BO, BO->getLHS ());
5805
+ }
5806
+
5807
+ void VisitUnaryOperator (const UnaryOperator *UO) {
5808
+ VisitChildren (UO);
5809
+
5810
+ if (UO->isIncrementDecrementOp ())
5811
+ checkTooComplexAssign (UO, UO->getSubExpr ());
5812
+ }
5813
+
5814
+ void checkTooComplexAssign (const Expr *E, const Expr *Sub) {
5815
+ const auto DA = getBoundsAttributedObject (Sub);
5816
+ if (DA.has_value ())
5817
+ TooComplexAssignHandler (E, DA->Decl );
5818
+ }
5819
+ };
5820
+
5821
+ static void
5822
+ diagnoseTooComplexAssignToBoundsAttributed (const Expr *E, const ValueDecl *VD,
5823
+ UnsafeBufferUsageHandler &Handler,
5824
+ ASTContext &Ctx) {
5825
+ // Don't diagnose pointer arithmetic, since -Wunsafe-buffer-usage already does
5826
+ // it.
5827
+ bool IsPtrArith = false ;
5828
+ if (E->getType ()->isPointerType ()) {
5829
+ if (const auto *BO = dyn_cast<BinaryOperator>(E))
5830
+ IsPtrArith = BO->isCompoundAssignmentOp ();
5831
+ else if (const auto *UO = dyn_cast<UnaryOperator>(E)) {
5832
+ assert (UO->isIncrementDecrementOp ());
5833
+ IsPtrArith = true ;
5834
+ }
5835
+ }
5836
+ if (!IsPtrArith)
5837
+ Handler.handleTooComplexCountAttributedAssign (
5838
+ E, VD, /* IsRelatedToDecl=*/ false , Ctx);
5839
+ }
5840
+
5841
+ static void checkBoundsSafetyAssignments (const Stmt *S,
5842
+ UnsafeBufferUsageHandler &Handler,
5843
+ ASTContext &Ctx) {
5844
+ BoundsAttributedGroupFinder Finder (
5845
+ [&](const BoundsAttributedAssignmentGroup &Group) {
5846
+ // TODO: Check group constraints.
5847
+ },
5848
+ [&](const Expr *E, const ValueDecl *VD) {
5849
+ diagnoseTooComplexAssignToBoundsAttributed (E, VD, Handler, Ctx);
5850
+ });
5851
+ Finder.Visit (S);
5852
+ }
5853
+
5542
5854
void clang::checkUnsafeBufferUsage (const Decl *D,
5543
5855
UnsafeBufferUsageHandler &Handler,
5544
- bool EmitSuggestions) {
5856
+ bool EmitSuggestions,
5857
+ bool BoundsSafetyAttributesEnabled) {
5545
5858
#ifndef NDEBUG
5546
5859
Handler.clearDebugNotes ();
5547
5860
#endif
@@ -5587,6 +5900,11 @@ void clang::checkUnsafeBufferUsage(const Decl *D,
5587
5900
for (Stmt *S : Stmts) {
5588
5901
findGadgets (S, D->getASTContext (), Handler, EmitSuggestions, FixableGadgets,
5589
5902
WarningGadgets, Tracker);
5903
+
5904
+ // Run the bounds-safety assignment analysis if the attributes are enabled,
5905
+ // otherwise don't waste cycles.
5906
+ if (BoundsSafetyAttributesEnabled)
5907
+ checkBoundsSafetyAssignments (S, Handler, D->getASTContext ());
5590
5908
}
5591
5909
applyGadgets (D, std::move (FixableGadgets), std::move (WarningGadgets),
5592
5910
std::move (Tracker), Handler, EmitSuggestions);
0 commit comments