@@ -5433,9 +5433,315 @@ static void applyGadgets(const Decl *D, FixableGadgetList FixableGadgets,
54335433 }
54345434}
54355435
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+ if (IsBoundsAttributedPointer || Decl->isDependentCount ())
5551+ return {{Decl, MemberBase, DerefLevel}};
5552+
5553+ return std::nullopt ;
5554+ }
5555+
5556+ struct BoundsAttributedAssignmentGroup {
5557+ DependentDeclSetTy DeclClosure;
5558+ llvm::SmallVector<const BinaryOperator *, 4 > Assignments;
5559+ llvm::SmallVector<BoundsAttributedObject, 4 > AssignedObjects;
5560+ using DeclUseTy = std::pair<const ValueDecl *, const Expr *>;
5561+ const Expr *MemberBase = nullptr ;
5562+
5563+ void Init (const BoundsAttributedObject &Object) {
5564+ DeclClosure = GetBoundsAttributedClosure (Object.Decl );
5565+ MemberBase = Object.MemberBase ;
5566+ }
5567+
5568+ void Clear () {
5569+ DeclClosure.clear ();
5570+ Assignments.clear ();
5571+ AssignedObjects.clear ();
5572+ MemberBase = nullptr ;
5573+ }
5574+
5575+ bool Empty () const {
5576+ return DeclClosure.empty ();
5577+ }
5578+
5579+ bool IsPartOfGroup (const BoundsAttributedObject &Object) const {
5580+ if (!DeclClosure.contains (Object.Decl ))
5581+ return false ;
5582+ if (MemberBase)
5583+ return Object.MemberBase &&
5584+ isSameMemberBase (MemberBase, Object.MemberBase );
5585+ return true ;
5586+ }
5587+
5588+ void AddAssignment (const BoundsAttributedObject &Object,
5589+ const BinaryOperator *BO) {
5590+ Assignments.push_back (BO);
5591+ AssignedObjects.push_back (Object);
5592+ }
5593+ };
5594+
5595+ // Visitor that is responsible for finding bounds-attributed assignment groups
5596+ // and standalone assignments to bounds-attributed objects.
5597+ //
5598+ // Bounds-attributed groups must be inside of a CompoundStmt:
5599+ // void foo(int *__counted_by(count) p, int count) {
5600+ // p = ...;
5601+ // count = ...;
5602+ // }
5603+ // Here, the group consists of both assignments to p and count. Note that the
5604+ // function body is a CompoundStmt.
5605+ //
5606+ // Standalone assignments are modifications of bounds-attributed objects that
5607+ // are not simple assignments directly in a CompoundStmt:
5608+ // void foo(int *__counted_by(count) p, int count) {
5609+ // q = p = ...;
5610+ // ^ standalone assignment to __counted_by() pointer
5611+ // n = count += ...;
5612+ // ^ standalone assignment to dependent count
5613+ // }
5614+ struct BoundsAttributedGroupFinder
5615+ : public ConstStmtVisitor<BoundsAttributedGroupFinder> {
5616+ using GroupHandlerTy = void (const BoundsAttributedAssignmentGroup &Group);
5617+ using AssignHandlerTy = void (const Expr *, const ValueDecl *);
5618+ std::function<GroupHandlerTy> GroupHandler;
5619+ std::function<AssignHandlerTy> BadStandaloneAssignHandler;
5620+ BoundsAttributedAssignmentGroup CurGroup;
5621+
5622+ explicit BoundsAttributedGroupFinder (
5623+ std::function<GroupHandlerTy> GroupHandler,
5624+ std::function<AssignHandlerTy> BadStandaloneAssignHandler)
5625+ : GroupHandler(std::move(GroupHandler)),
5626+ BadStandaloneAssignHandler(std::move(BadStandaloneAssignHandler)) {}
5627+
5628+ void VisitChildren (const Stmt *S) {
5629+ for (const Stmt *Child : S->children ())
5630+ Visit (Child);
5631+ }
5632+
5633+ void VisitStmt (const Stmt *S) { VisitChildren (S); }
5634+
5635+ void VisitCompoundStmt (const CompoundStmt *CS) {
5636+ for (const Stmt *Child : CS->children ()) {
5637+ const Stmt *E = Child;
5638+
5639+ // See through `ExprWithCleanups`. Clang will attach those nodes when C++
5640+ // temporary object needs to be materialized. In our case, this can
5641+ // happen when we create a temporary span with `sp.first()`. Then, the
5642+ // structure is going to be:
5643+ // CompoundStmt
5644+ // `-ExprWithCleanups
5645+ // `-BinaryOperator ...
5646+ if (const auto *EWC = dyn_cast<ExprWithCleanups>(E))
5647+ E = EWC->getSubExpr ();
5648+
5649+ const auto *BO = dyn_cast<BinaryOperator>(E);
5650+ if (BO && BO->getOpcode () == BO_Assign)
5651+ HandleAssignInCompound (BO);
5652+ else {
5653+ FinishGroup ();
5654+ Visit (Child);
5655+ }
5656+ }
5657+
5658+ FinishGroup ();
5659+ }
5660+
5661+ void HandleAssignInCompound (const BinaryOperator *AssignOp) {
5662+ const auto ObjectOpt = getBoundsAttributedObject (AssignOp->getLHS ());
5663+ if (!ObjectOpt.has_value ()) {
5664+ FinishGroup ();
5665+ VisitChildren (AssignOp);
5666+ return ;
5667+ }
5668+
5669+ if (!CurGroup.IsPartOfGroup (*ObjectOpt)) {
5670+ FinishGroup ();
5671+ CurGroup.Init (*ObjectOpt);
5672+ }
5673+
5674+ CurGroup.AddAssignment (*ObjectOpt, AssignOp);
5675+ VisitChildren (AssignOp->getRHS ());
5676+ }
5677+
5678+ void FinishGroup () {
5679+ if (CurGroup.Empty ())
5680+ return ;
5681+ GroupHandler (CurGroup);
5682+ CurGroup.Clear ();
5683+ }
5684+
5685+ // Handle statements that might modify bounds-attributed pointer/count, but
5686+ // are not directly in a CompoundStmt.
5687+
5688+ void VisitBinaryOperator (const BinaryOperator *BO) {
5689+ VisitChildren (BO);
5690+
5691+ if (BO->isAssignmentOp ())
5692+ CheckBadStandaloneAssign (BO, BO->getLHS ());
5693+ }
5694+
5695+ void VisitUnaryOperator (const UnaryOperator *UO) {
5696+ VisitChildren (UO);
5697+
5698+ if (UO->isIncrementDecrementOp ())
5699+ CheckBadStandaloneAssign (UO, UO->getSubExpr ());
5700+ }
5701+
5702+ void CheckBadStandaloneAssign (const Expr *E, const Expr *Sub) {
5703+ const auto DA = getBoundsAttributedObject (Sub);
5704+ if (DA.has_value ())
5705+ BadStandaloneAssignHandler (E, DA->Decl );
5706+ }
5707+ };
5708+
5709+ static void
5710+ diagnoseStandaloneAssignToBoundsAttributed (const Expr *E, const ValueDecl *VD,
5711+ UnsafeBufferUsageHandler &Handler,
5712+ ASTContext &Ctx) {
5713+ // Don't diagnose pointer arithmetic, since -Wunsafe-buffer-usage already does
5714+ // it.
5715+ bool IsPtrArith = false ;
5716+ if (E->getType ()->isPointerType ()) {
5717+ if (const auto *BO = dyn_cast<BinaryOperator>(E))
5718+ IsPtrArith = BO->isCompoundAssignmentOp ();
5719+ else if (const auto *UO = dyn_cast<UnaryOperator>(E)) {
5720+ assert (UO->isIncrementDecrementOp ());
5721+ IsPtrArith = true ;
5722+ }
5723+ }
5724+ if (!IsPtrArith)
5725+ Handler.handleStandaloneAssign (E, VD, /* IsRelatedToDecl=*/ false , Ctx);
5726+ }
5727+
5728+ static void checkBoundsSafetyAssignments (const Stmt *S,
5729+ UnsafeBufferUsageHandler &Handler,
5730+ ASTContext &Ctx) {
5731+ BoundsAttributedGroupFinder Finder (
5732+ [&](const BoundsAttributedAssignmentGroup &Group) {
5733+ // TODO: Check group constraints.
5734+ },
5735+ [&](const Expr *E, const ValueDecl *VD) {
5736+ diagnoseStandaloneAssignToBoundsAttributed (E, VD, Handler, Ctx);
5737+ });
5738+ Finder.Visit (S);
5739+ }
5740+
54365741void clang::checkUnsafeBufferUsage (const Decl *D,
54375742 UnsafeBufferUsageHandler &Handler,
5438- bool EmitSuggestions) {
5743+ bool EmitSuggestions,
5744+ bool BoundsSafetyAttributes) {
54395745#ifndef NDEBUG
54405746 Handler.clearDebugNotes ();
54415747#endif
@@ -5481,6 +5787,11 @@ void clang::checkUnsafeBufferUsage(const Decl *D,
54815787 for (Stmt *S : Stmts) {
54825788 findGadgets (S, D->getASTContext (), Handler, EmitSuggestions, FixableGadgets,
54835789 WarningGadgets, Tracker);
5790+
5791+ // Run the bounds-safety assignment analysis if the attributes are enabled,
5792+ // otherwise don't waste cycles.
5793+ if (BoundsSafetyAttributes)
5794+ checkBoundsSafetyAssignments (S, Handler, D->getASTContext ());
54845795 }
54855796 applyGadgets (D, std::move (FixableGadgets), std::move (WarningGadgets),
54865797 std::move (Tracker), Handler, EmitSuggestions);
0 commit comments