@@ -5444,9 +5444,322 @@ static void applyGadgets(const Decl *D, FixableGadgetList FixableGadgets,
54445444 }
54455445}
54465446
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+
54475759void clang::checkUnsafeBufferUsage (const Decl *D,
54485760 UnsafeBufferUsageHandler &Handler,
5449- bool EmitSuggestions) {
5761+ bool EmitSuggestions,
5762+ bool BoundsSafetyAttributesEnabled) {
54505763#ifndef NDEBUG
54515764 Handler.clearDebugNotes ();
54525765#endif
@@ -5492,6 +5805,11 @@ void clang::checkUnsafeBufferUsage(const Decl *D,
54925805 for (Stmt *S : Stmts) {
54935806 findGadgets (S, D->getASTContext (), Handler, EmitSuggestions, FixableGadgets,
54945807 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 ());
54955813 }
54965814 applyGadgets (D, std::move (FixableGadgets), std::move (WarningGadgets),
54975815 std::move (Tracker), Handler, EmitSuggestions);
0 commit comments