@@ -5375,9 +5375,322 @@ static void applyGadgets(const Decl *D, FixableGadgetList FixableGadgets,
53755375 }
53765376}
53775377
5378+ // Checks if Self and Other are the same member bases. This supports only very
5379+ // simple forms of member bases.
5380+ static bool isSameMemberBase (const Expr *Self, const Expr *Other) {
5381+ for (;;) {
5382+ if (Self == Other)
5383+ return true ;
5384+
5385+ const auto *SelfICE = dyn_cast<ImplicitCastExpr>(Self);
5386+ const auto *OtherICE = dyn_cast<ImplicitCastExpr>(Other);
5387+ if (SelfICE && OtherICE && SelfICE->getCastKind () == CK_LValueToRValue &&
5388+ OtherICE->getCastKind () == CK_LValueToRValue) {
5389+ Self = SelfICE->getSubExpr ();
5390+ Other = OtherICE->getSubExpr ();
5391+ }
5392+
5393+ const auto *SelfDRE = dyn_cast<DeclRefExpr>(Self);
5394+ const auto *OtherDRE = dyn_cast<DeclRefExpr>(Other);
5395+ if (SelfDRE && OtherDRE)
5396+ return SelfDRE->getDecl () == OtherDRE->getDecl ();
5397+
5398+ const auto *SelfME = dyn_cast<MemberExpr>(Self);
5399+ const auto *OtherME = dyn_cast<MemberExpr>(Other);
5400+ if (!SelfME || !OtherME ||
5401+ SelfME->getMemberDecl () != OtherME->getMemberDecl ()) {
5402+ return false ;
5403+ }
5404+
5405+ Self = SelfME->getBase ();
5406+ Other = OtherME->getBase ();
5407+ }
5408+ }
5409+
5410+ using DependentDeclSetTy = llvm::SmallPtrSet<const ValueDecl *, 4 >;
5411+
5412+ // Gets the set/closure of bounds-attributed pointers and counts that belong to
5413+ // the same group. Consider the following example:
5414+ // int a, b, c;
5415+ // int *__counted_by(a + b) p;
5416+ // int *__counted_by(b - c) q;
5417+ // Passing any of the variables above as `InitVD`, the function should return
5418+ // the closure `{a, b, c, p, q}`.
5419+ static DependentDeclSetTy getBoundsAttributedClosure (const ValueDecl *InitVD) {
5420+ DependentDeclSetTy Set;
5421+
5422+ llvm::SmallVector<const ValueDecl *, 4 > WorkList;
5423+ WorkList.push_back (InitVD);
5424+
5425+ while (!WorkList.empty ()) {
5426+ const ValueDecl *CurVD = WorkList.pop_back_val ();
5427+ bool Inserted = Set.insert (CurVD).second ;
5428+ if (!Inserted)
5429+ continue ;
5430+
5431+ // If CurVD is a dependent decl, add the pointers that depend on CurVD.
5432+ for (const auto *Attr : CurVD->specific_attrs <DependerDeclsAttr>()) {
5433+ for (const Decl *D : Attr->dependerDecls ()) {
5434+ if (const auto *VD = dyn_cast<ValueDecl>(D))
5435+ WorkList.push_back (VD);
5436+ }
5437+ }
5438+
5439+ // If CurVD is a bounds-attributed pointer (or pointer to it), add its
5440+ // dependent decls.
5441+ QualType Ty = CurVD->getType ();
5442+ const auto *BAT = Ty->getAs <BoundsAttributedType>();
5443+ if (!BAT && Ty->isPointerType ())
5444+ BAT = Ty->getPointeeType ()->getAs <BoundsAttributedType>();
5445+ if (BAT) {
5446+ for (const auto &DI : BAT->dependent_decls ())
5447+ WorkList.push_back (DI.getDecl ());
5448+ }
5449+ }
5450+
5451+ return Set;
5452+ }
5453+
5454+ // Bounds-attributed pointer or dependent count.
5455+ struct BoundsAttributedObject {
5456+ const ValueDecl *Decl = nullptr ;
5457+ const Expr *MemberBase = nullptr ;
5458+ int DerefLevel = 0 ;
5459+ };
5460+
5461+ static std::optional<BoundsAttributedObject>
5462+ getBoundsAttributedObject (const Expr *E) {
5463+ E = E->IgnoreParenCasts ();
5464+
5465+ int DerefLevel = 0 ;
5466+ while (const auto *UO = dyn_cast<UnaryOperator>(E)) {
5467+ if (UO->getOpcode () == UO_Deref)
5468+ DerefLevel++;
5469+ else if (UO->getOpcode () == UO_AddrOf)
5470+ DerefLevel--;
5471+ else
5472+ break ;
5473+ E = UO->getSubExpr ()->IgnoreParenCasts ();
5474+ }
5475+ assert (DerefLevel >= 0 );
5476+
5477+ const ValueDecl *Decl;
5478+ const Expr *MemberBase = nullptr ;
5479+
5480+ if (const auto *DRE = dyn_cast<DeclRefExpr>(E))
5481+ Decl = DRE->getDecl ();
5482+ else if (const auto *ME = dyn_cast<MemberExpr>(E)) {
5483+ Decl = ME->getMemberDecl ();
5484+ MemberBase = ME->getBase ();
5485+ } else
5486+ return std::nullopt ;
5487+
5488+ QualType Ty = Decl->getType ();
5489+ bool IsBoundsAttributedPointer =
5490+ Ty->isBoundsAttributedType () ||
5491+ (Ty->isPointerType () && Ty->getPointeeType ()->isBoundsAttributedType ());
5492+ if (IsBoundsAttributedPointer || Decl->isDependentCount ())
5493+ return {{Decl, MemberBase, DerefLevel}};
5494+
5495+ return std::nullopt ;
5496+ }
5497+
5498+ struct BoundsAttributedAssignmentGroup {
5499+ DependentDeclSetTy DeclClosure;
5500+ llvm::SmallVector<const BinaryOperator *, 4 > Assignments;
5501+ llvm::SmallVector<BoundsAttributedObject, 4 > AssignedObjects;
5502+ using DeclUseTy = std::pair<const ValueDecl *, const Expr *>;
5503+ const Expr *MemberBase = nullptr ;
5504+
5505+ void init (const BoundsAttributedObject &Object) {
5506+ DeclClosure = getBoundsAttributedClosure (Object.Decl );
5507+ MemberBase = Object.MemberBase ;
5508+ }
5509+
5510+ void clear () {
5511+ DeclClosure.clear ();
5512+ Assignments.clear ();
5513+ AssignedObjects.clear ();
5514+ MemberBase = nullptr ;
5515+ }
5516+
5517+ bool empty () const {
5518+ return DeclClosure.empty ();
5519+ }
5520+
5521+ bool isPartOfGroup (const BoundsAttributedObject &Object) const {
5522+ if (!DeclClosure.contains (Object.Decl ))
5523+ return false ;
5524+ if (MemberBase)
5525+ return Object.MemberBase &&
5526+ isSameMemberBase (MemberBase, Object.MemberBase );
5527+ return true ;
5528+ }
5529+
5530+ void addAssignment (const BoundsAttributedObject &Object,
5531+ const BinaryOperator *BO) {
5532+ Assignments.push_back (BO);
5533+ AssignedObjects.push_back (Object);
5534+ }
5535+ };
5536+
5537+ // Visitor that is responsible for finding bounds-attributed assignment groups
5538+ // and other assignments that can modify bounds-attributed objects.
5539+ //
5540+ // A bounds-attributed group must be a group of assignments that are children
5541+ // of a CompoundStmt:
5542+ // void foo(int *__counted_by(count) p, int count) {
5543+ // p = ...;
5544+ // count = ...;
5545+ // }
5546+ // Here, the group consists of both assignments to p and count. Note that the
5547+ // function body is a CompoundStmt.
5548+ //
5549+ // We consider assignments to bounds-attributed objects that are not simple
5550+ // statements or not children of a CompoundStmt as too complex, and we give up
5551+ // trying to put them in a bounds-attributed group:
5552+ // void foo(int *__counted_by(count) p, int count) {
5553+ // q = p = ...;
5554+ // ^ too complex assignment to __counted_by() pointer
5555+ // n = count += ...;
5556+ // ^ too complex assignment to dependent count
5557+ // }
5558+ // Note that while those assignments are descendants of a CompoundStmt, they
5559+ // are not its direct children.
5560+ struct BoundsAttributedGroupFinder
5561+ : public ConstStmtVisitor<BoundsAttributedGroupFinder> {
5562+ using GroupHandlerTy = void (const BoundsAttributedAssignmentGroup &Group);
5563+ using AssignHandlerTy = void (const Expr *, const ValueDecl *);
5564+ std::function<GroupHandlerTy> GroupHandler;
5565+ std::function<AssignHandlerTy> TooComplexAssignHandler;
5566+ BoundsAttributedAssignmentGroup CurGroup;
5567+
5568+ explicit BoundsAttributedGroupFinder (
5569+ std::function<GroupHandlerTy> GroupHandler,
5570+ std::function<AssignHandlerTy> TooComplexAssignHandler)
5571+ : GroupHandler(std::move(GroupHandler)),
5572+ TooComplexAssignHandler(std::move(TooComplexAssignHandler)) {}
5573+
5574+ void VisitChildren (const Stmt *S) {
5575+ for (const Stmt *Child : S->children ()) {
5576+ if (Child)
5577+ Visit (Child);
5578+ }
5579+ }
5580+
5581+ void VisitStmt (const Stmt *S) { VisitChildren (S); }
5582+
5583+ void VisitCompoundStmt (const CompoundStmt *CS) {
5584+ for (const Stmt *Child : CS->children ()) {
5585+ const Stmt *E = Child;
5586+
5587+ // See through `ExprWithCleanups`. Clang will attach those nodes when C++
5588+ // temporary object needs to be materialized. In our case, this can
5589+ // happen when we create a temporary span with `sp.first()`. Then, the
5590+ // structure is going to be:
5591+ // CompoundStmt
5592+ // `-ExprWithCleanups
5593+ // `-BinaryOperator ...
5594+ if (const auto *EWC = dyn_cast<ExprWithCleanups>(E))
5595+ E = EWC->getSubExpr ();
5596+
5597+ const auto *BO = dyn_cast<BinaryOperator>(E);
5598+ if (BO && BO->getOpcode () == BO_Assign)
5599+ handleAssignInCompound (BO);
5600+ else {
5601+ finishGroup ();
5602+ Visit (Child);
5603+ }
5604+ }
5605+
5606+ finishGroup ();
5607+ }
5608+
5609+ void handleAssignInCompound (const BinaryOperator *AssignOp) {
5610+ const auto ObjectOpt = getBoundsAttributedObject (AssignOp->getLHS ());
5611+ if (!ObjectOpt.has_value ()) {
5612+ finishGroup ();
5613+ VisitChildren (AssignOp);
5614+ return ;
5615+ }
5616+
5617+ if (!CurGroup.isPartOfGroup (*ObjectOpt)) {
5618+ finishGroup ();
5619+ CurGroup.init (*ObjectOpt);
5620+ }
5621+
5622+ CurGroup.addAssignment (*ObjectOpt, AssignOp);
5623+ VisitChildren (AssignOp->getRHS ());
5624+ }
5625+
5626+ void finishGroup () {
5627+ if (CurGroup.empty ())
5628+ return ;
5629+ GroupHandler (CurGroup);
5630+ CurGroup.clear ();
5631+ }
5632+
5633+ // Handle statements that might modify bounds-attributed pointer/count, but
5634+ // are not children of a CompoundStmt.
5635+
5636+ void VisitBinaryOperator (const BinaryOperator *BO) {
5637+ VisitChildren (BO);
5638+
5639+ if (BO->isAssignmentOp ())
5640+ checkTooComplexAssign (BO, BO->getLHS ());
5641+ }
5642+
5643+ void VisitUnaryOperator (const UnaryOperator *UO) {
5644+ VisitChildren (UO);
5645+
5646+ if (UO->isIncrementDecrementOp ())
5647+ checkTooComplexAssign (UO, UO->getSubExpr ());
5648+ }
5649+
5650+ void checkTooComplexAssign (const Expr *E, const Expr *Sub) {
5651+ const auto DA = getBoundsAttributedObject (Sub);
5652+ if (DA.has_value ())
5653+ TooComplexAssignHandler (E, DA->Decl );
5654+ }
5655+ };
5656+
5657+ static void
5658+ diagnoseTooComplexAssignToBoundsAttributed (const Expr *E, const ValueDecl *VD,
5659+ UnsafeBufferUsageHandler &Handler,
5660+ ASTContext &Ctx) {
5661+ // Don't diagnose pointer arithmetic, since -Wunsafe-buffer-usage already does
5662+ // it.
5663+ bool IsPtrArith = false ;
5664+ if (E->getType ()->isPointerType ()) {
5665+ if (const auto *BO = dyn_cast<BinaryOperator>(E))
5666+ IsPtrArith = BO->isCompoundAssignmentOp ();
5667+ else if (const auto *UO = dyn_cast<UnaryOperator>(E)) {
5668+ assert (UO->isIncrementDecrementOp ());
5669+ IsPtrArith = true ;
5670+ }
5671+ }
5672+ if (!IsPtrArith)
5673+ Handler.handleTooComplexCountAttributedAssign (
5674+ E, VD, /* IsRelatedToDecl=*/ false , Ctx);
5675+ }
5676+
5677+ static void checkBoundsSafetyAssignments (const Stmt *S,
5678+ UnsafeBufferUsageHandler &Handler,
5679+ ASTContext &Ctx) {
5680+ BoundsAttributedGroupFinder Finder (
5681+ [&](const BoundsAttributedAssignmentGroup &Group) {
5682+ // TODO: Check group constraints.
5683+ },
5684+ [&](const Expr *E, const ValueDecl *VD) {
5685+ diagnoseTooComplexAssignToBoundsAttributed (E, VD, Handler, Ctx);
5686+ });
5687+ Finder.Visit (S);
5688+ }
5689+
53785690void clang::checkUnsafeBufferUsage (const Decl *D,
53795691 UnsafeBufferUsageHandler &Handler,
5380- bool EmitSuggestions) {
5692+ bool EmitSuggestions,
5693+ bool BoundsSafetyAttributesEnabled) {
53815694#ifndef NDEBUG
53825695 Handler.clearDebugNotes ();
53835696#endif
@@ -5423,6 +5736,11 @@ void clang::checkUnsafeBufferUsage(const Decl *D,
54235736 for (Stmt *S : Stmts) {
54245737 findGadgets (S, D->getASTContext (), Handler, EmitSuggestions, FixableGadgets,
54255738 WarningGadgets, Tracker);
5739+
5740+ // Run the bounds-safety assignment analysis if the attributes are enabled,
5741+ // otherwise don't waste cycles.
5742+ if (BoundsSafetyAttributesEnabled)
5743+ checkBoundsSafetyAssignments (S, Handler, D->getASTContext ());
54265744 }
54275745 applyGadgets (D, std::move (FixableGadgets), std::move (WarningGadgets),
54285746 std::move (Tracker), Handler, EmitSuggestions);
0 commit comments