@@ -5539,9 +5539,322 @@ static void applyGadgets(const Decl *D, FixableGadgetList FixableGadgets,
55395539 }
55405540}
55415541
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+
55425854void clang::checkUnsafeBufferUsage (const Decl *D,
55435855 UnsafeBufferUsageHandler &Handler,
5544- bool EmitSuggestions) {
5856+ bool EmitSuggestions,
5857+ bool BoundsSafetyAttributesEnabled) {
55455858#ifndef NDEBUG
55465859 Handler.clearDebugNotes ();
55475860#endif
@@ -5587,6 +5900,11 @@ void clang::checkUnsafeBufferUsage(const Decl *D,
55875900 for (Stmt *S : Stmts) {
55885901 findGadgets (S, D->getASTContext (), Handler, EmitSuggestions, FixableGadgets,
55895902 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 ());
55905908 }
55915909 applyGadgets (D, std::move (FixableGadgets), std::move (WarningGadgets),
55925910 std::move (Tracker), Handler, EmitSuggestions);
0 commit comments