@@ -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>
@@ -5663,7 +5673,7 @@ struct BoundsAttributedAssignmentGroup {
56635673 DependentDeclSetTy DeclClosure;
56645674 llvm::SmallVector<const BinaryOperator *, 4 > Assignments;
56655675 llvm::SmallVector<BoundsAttributedObject, 4 > AssignedObjects;
5666- using DeclUseTy = std::pair <const ValueDecl *, const Expr *> ;
5676+ llvm::SmallDenseMap <const ValueDecl *, const Expr *, 4 > Uses ;
56675677 const Expr *MemberBase = nullptr ;
56685678
56695679 void init (const BoundsAttributedObject &Object) {
@@ -5675,6 +5685,7 @@ struct BoundsAttributedAssignmentGroup {
56755685 DeclClosure.clear ();
56765686 Assignments.clear ();
56775687 AssignedObjects.clear ();
5688+ Uses.clear ();
56785689 MemberBase = nullptr ;
56795690 }
56805691
@@ -5816,6 +5827,22 @@ struct BoundsAttributedGroupFinder
58165827 if (DA.has_value ())
58175828 TooComplexAssignHandler (E, DA->Decl );
58185829 }
5830+
5831+ // Collect uses of decls belonging to the current group by visiting all DREs
5832+ // and MemberExprs.
5833+
5834+ void addUseIfPartOfCurGroup (const Expr *E) {
5835+ const auto DA = getBoundsAttributedObject (E);
5836+ if (DA.has_value () && CurGroup.isPartOfGroup (*DA))
5837+ CurGroup.Uses .insert ({DA->Decl , E});
5838+ }
5839+
5840+ void VisitDeclRefExpr (const DeclRefExpr *DRE) { addUseIfPartOfCurGroup (DRE); }
5841+
5842+ void VisitMemberExpr (const MemberExpr *ME) {
5843+ addUseIfPartOfCurGroup (ME);
5844+ Visit (ME->getBase ());
5845+ }
58195846};
58205847
58215848// Checks if the bounds-attributed group does not assign to implicitly
@@ -5947,6 +5974,52 @@ static bool checkMissingAndDuplicatedAssignments(
59475974 return IsGroupSafe;
59485975}
59495976
5977+ // Checks if the bounds-attributed group has assignments to objects that are
5978+ // also used in the same group. In those cases, the correctness of the group
5979+ // might depend on the order of assignments. We conservatively disallow such
5980+ // assignments.
5981+ //
5982+ // In the example below, the bounds-check in `sp.first()` uses the value of `b`
5983+ // before the later update, which can lead to OOB if `b` was less than 42.
5984+ // void foo(int *__counted_by(a + b) p, int a, int b, std::span<int> sp) {
5985+ // p = sp.first(b + 42).data();
5986+ // b = 42; // b is assigned and used
5987+ // a = b;
5988+ // }
5989+ static bool checkAssignedAndUsed (const BoundsAttributedAssignmentGroup &Group,
5990+ UnsafeBufferUsageHandler &Handler,
5991+ ASTContext &Ctx) {
5992+ const auto &Uses = Group.Uses ;
5993+ if (Uses.empty ())
5994+ return true ;
5995+
5996+ bool IsGroupSafe = true ;
5997+
5998+ for (size_t I = 0 , N = Group.AssignedObjects .size (); I < N; ++I) {
5999+ const BoundsAttributedObject &LHSObj = Group.AssignedObjects [I];
6000+ const BinaryOperator *Assign = Group.Assignments [I];
6001+
6002+ // Ignore self assignments, because they don't matter, since the value stays
6003+ // the same.
6004+ const auto RHSObj = getBoundsAttributedObject (Assign->getRHS ());
6005+ bool IsSelfAssign = RHSObj.has_value () && *RHSObj == LHSObj;
6006+ if (IsSelfAssign)
6007+ continue ;
6008+
6009+ const ValueDecl *VD = LHSObj.Decl ;
6010+ const auto It = Uses.find (VD);
6011+ if (It == Uses.end ())
6012+ continue ;
6013+
6014+ const Expr *Use = It->second ;
6015+ Handler.handleAssignedAndUsed (Assign, Use, VD,
6016+ /* IsRelatedToDecl=*/ false , Ctx);
6017+ IsGroupSafe = false ;
6018+ }
6019+
6020+ return IsGroupSafe;
6021+ }
6022+
59506023// Checks if the bounds-attributed group is safe. This function returns false
59516024// iff the assignment group is unsafe and diagnostics have been emitted.
59526025static bool
@@ -5956,6 +6029,8 @@ checkBoundsAttributedGroup(const BoundsAttributedAssignmentGroup &Group,
59566029 return false ;
59576030 if (!checkMissingAndDuplicatedAssignments (Group, Handler, Ctx))
59586031 return false ;
6032+ if (!checkAssignedAndUsed (Group, Handler, Ctx))
6033+ return false ;
59596034 // TODO: Add more checks.
59606035 return true ;
59616036}
0 commit comments