@@ -5620,6 +5620,16 @@ struct BoundsAttributedObject {
56205620 const ValueDecl *Decl = nullptr ;
56215621 const Expr *MemberBase = nullptr ;
56225622 int DerefLevel = 0 ;
5623+
5624+ bool operator ==(const BoundsAttributedObject &Other) const {
5625+ if (Other.Decl != Decl || Other.DerefLevel != DerefLevel)
5626+ return false ;
5627+ if (Other.MemberBase == MemberBase)
5628+ return true ;
5629+ if (Other.MemberBase == nullptr || MemberBase == nullptr )
5630+ return false ;
5631+ return isSameMemberBase (Other.MemberBase , MemberBase);
5632+ }
56235633};
56245634
56255635static std::optional<BoundsAttributedObject>
@@ -5664,6 +5674,7 @@ struct BoundsAttributedAssignmentGroup {
56645674 llvm::SmallVector<const BinaryOperator *, 4 > Assignments;
56655675 llvm::SmallVector<BoundsAttributedObject, 4 > AssignedObjects;
56665676 using DeclUseTy = std::pair<const ValueDecl *, const Expr *>;
5677+ llvm::SmallVector<DeclUseTy, 4 > Uses;
56675678 const Expr *MemberBase = nullptr ;
56685679
56695680 void init (const BoundsAttributedObject &Object) {
@@ -5675,6 +5686,7 @@ struct BoundsAttributedAssignmentGroup {
56755686 DeclClosure.clear ();
56765687 Assignments.clear ();
56775688 AssignedObjects.clear ();
5689+ Uses.clear ();
56785690 MemberBase = nullptr ;
56795691 }
56805692
@@ -5696,6 +5708,10 @@ struct BoundsAttributedAssignmentGroup {
56965708 Assignments.push_back (BO);
56975709 AssignedObjects.push_back (Object);
56985710 }
5711+
5712+ void addUse (const BoundsAttributedObject &Object, const Expr *E) {
5713+ Uses.emplace_back (Object.Decl , E);
5714+ }
56995715};
57005716
57015717// Visitor that is responsible for finding bounds-attributed assignment groups
@@ -5816,6 +5832,21 @@ struct BoundsAttributedGroupFinder
58165832 if (DA.has_value ())
58175833 TooComplexAssignHandler (E, DA->Decl );
58185834 }
5835+
5836+ // Collect uses.
5837+
5838+ void addUseIfPartOfCurGroup (const Expr *E) {
5839+ const auto DA = getBoundsAttributedObject (E);
5840+ if (DA.has_value () && CurGroup.isPartOfGroup (*DA))
5841+ CurGroup.addUse (*DA, E);
5842+ }
5843+
5844+ void VisitDeclRefExpr (const DeclRefExpr *DRE) { addUseIfPartOfCurGroup (DRE); }
5845+
5846+ void VisitMemberExpr (const MemberExpr *ME) {
5847+ addUseIfPartOfCurGroup (ME);
5848+ Visit (ME->getBase ());
5849+ }
58195850};
58205851
58215852// Checks if the bounds-attributed group does not assign to implicitly
@@ -5947,6 +5978,55 @@ static bool checkMissingAndDuplicatedAssignments(
59475978 return IsGroupSafe;
59485979}
59495980
5981+ // Checks if the bounds-attributed group has assignments to objects that are
5982+ // also used in the same group. In those cases, the correctness of the group
5983+ // might depend on the order of assignments. We conservatively disallow such
5984+ // assignments.
5985+ //
5986+ // In the example below, the bounds-check in `sp.first()` uses the value of `b`
5987+ // before the later update, which can lead to OOB if `b` was less than 42.
5988+ // void foo(int *__counted_by(a + b) p, int a, int b, std::span<int> sp) {
5989+ // p = sp.first(b + 42).data();
5990+ // b = 42; // b is assigned and used
5991+ // a = b;
5992+ // }
5993+ static bool checkAssignedAndUsed (const BoundsAttributedAssignmentGroup &Group,
5994+ UnsafeBufferUsageHandler &Handler,
5995+ ASTContext &Ctx) {
5996+ if (Group.Uses .empty ())
5997+ return true ;
5998+
5999+ llvm::SmallDenseMap<const ValueDecl *, const BinaryOperator *, 4 > Assigns;
6000+ for (size_t I = 0 , N = Group.AssignedObjects .size (); I < N; ++I) {
6001+ const BoundsAttributedObject &LHSObj = Group.AssignedObjects [I];
6002+ const BinaryOperator *Assign = Group.Assignments [I];
6003+
6004+ // Ignore self assignments, because they don't matter, since the value stays
6005+ // the same.
6006+ const auto RHSObj = getBoundsAttributedObject (Assign->getRHS ());
6007+ bool IsSelfAssign = RHSObj.has_value () && *RHSObj == LHSObj;
6008+ if (IsSelfAssign)
6009+ continue ;
6010+
6011+ const ValueDecl *VD = LHSObj.Decl ;
6012+ [[maybe_unused]] bool Inserted = Assigns.insert ({VD, Assign}).second ;
6013+ assert (Inserted);
6014+ }
6015+
6016+ bool IsGroupSafe = true ;
6017+
6018+ for (const auto [VD, Use] : Group.Uses ) {
6019+ const auto It = Assigns.find (VD);
6020+ if (It != Assigns.end ()) {
6021+ Handler.handleAssignedAndUsed (It->second , Use, VD,
6022+ /* IsRelatedToDecl=*/ false , Ctx);
6023+ IsGroupSafe = false ;
6024+ }
6025+ }
6026+
6027+ return IsGroupSafe;
6028+ }
6029+
59506030// Checks if the bounds-attributed group is safe. This function returns false
59516031// iff the assignment group is unsafe and diagnostics have been emitted.
59526032static bool
@@ -5956,6 +6036,8 @@ checkBoundsAttributedGroup(const BoundsAttributedAssignmentGroup &Group,
59566036 return false ;
59576037 if (!checkMissingAndDuplicatedAssignments (Group, Handler, Ctx))
59586038 return false ;
6039+ if (!checkAssignedAndUsed (Group, Handler, Ctx))
6040+ return false ;
59596041 // TODO: Add more checks.
59606042 return true ;
59616043}
0 commit comments