@@ -5457,6 +5457,16 @@ struct BoundsAttributedObject {
54575457 const ValueDecl *Decl = nullptr ;
54585458 const Expr *MemberBase = nullptr ;
54595459 int DerefLevel = 0 ;
5460+
5461+ bool operator ==(const BoundsAttributedObject &Other) const {
5462+ if (Other.Decl != Decl || Other.DerefLevel != DerefLevel)
5463+ return false ;
5464+ if (Other.MemberBase == MemberBase)
5465+ return true ;
5466+ if (Other.MemberBase == nullptr || MemberBase == nullptr )
5467+ return false ;
5468+ return isSameMemberBase (Other.MemberBase , MemberBase);
5469+ }
54605470};
54615471
54625472static std::optional<BoundsAttributedObject>
@@ -5500,7 +5510,7 @@ struct BoundsAttributedAssignmentGroup {
55005510 DependentDeclSetTy DeclClosure;
55015511 llvm::SmallVector<const BinaryOperator *, 4 > Assignments;
55025512 llvm::SmallVector<BoundsAttributedObject, 4 > AssignedObjects;
5503- using DeclUseTy = std::pair <const ValueDecl *, const Expr *> ;
5513+ llvm::SmallDenseMap <const ValueDecl *, const Expr *, 4 > Uses ;
55045514 const Expr *MemberBase = nullptr ;
55055515
55065516 void init (const BoundsAttributedObject &Object) {
@@ -5512,6 +5522,7 @@ struct BoundsAttributedAssignmentGroup {
55125522 DeclClosure.clear ();
55135523 Assignments.clear ();
55145524 AssignedObjects.clear ();
5525+ Uses.clear ();
55155526 MemberBase = nullptr ;
55165527 }
55175528
@@ -5653,6 +5664,22 @@ struct BoundsAttributedGroupFinder
56535664 if (DA.has_value ())
56545665 TooComplexAssignHandler (E, DA->Decl );
56555666 }
5667+
5668+ // Collect uses of decls belonging to the current group by visiting all DREs
5669+ // and MemberExprs.
5670+
5671+ void addUseIfPartOfCurGroup (const Expr *E) {
5672+ const auto DA = getBoundsAttributedObject (E);
5673+ if (DA.has_value () && CurGroup.isPartOfGroup (*DA))
5674+ CurGroup.Uses .insert ({DA->Decl , E});
5675+ }
5676+
5677+ void VisitDeclRefExpr (const DeclRefExpr *DRE) { addUseIfPartOfCurGroup (DRE); }
5678+
5679+ void VisitMemberExpr (const MemberExpr *ME) {
5680+ addUseIfPartOfCurGroup (ME);
5681+ Visit (ME->getBase ());
5682+ }
56565683};
56575684
56585685// Checks if the bounds-attributed group does not assign to implicitly
@@ -5784,6 +5811,52 @@ static bool checkMissingAndDuplicatedAssignments(
57845811 return IsGroupSafe;
57855812}
57865813
5814+ // Checks if the bounds-attributed group has assignments to objects that are
5815+ // also used in the same group. In those cases, the correctness of the group
5816+ // might depend on the order of assignments. We conservatively disallow such
5817+ // assignments.
5818+ //
5819+ // In the example below, the bounds-check in `sp.first()` uses the value of `b`
5820+ // before the later update, which can lead to OOB if `b` was less than 42.
5821+ // void foo(int *__counted_by(a + b) p, int a, int b, std::span<int> sp) {
5822+ // p = sp.first(b + 42).data();
5823+ // b = 42; // b is assigned and used
5824+ // a = b;
5825+ // }
5826+ static bool checkAssignedAndUsed (const BoundsAttributedAssignmentGroup &Group,
5827+ UnsafeBufferUsageHandler &Handler,
5828+ ASTContext &Ctx) {
5829+ const auto &Uses = Group.Uses ;
5830+ if (Uses.empty ())
5831+ return true ;
5832+
5833+ bool IsGroupSafe = true ;
5834+
5835+ for (size_t I = 0 , N = Group.AssignedObjects .size (); I < N; ++I) {
5836+ const BoundsAttributedObject &LHSObj = Group.AssignedObjects [I];
5837+ const BinaryOperator *Assign = Group.Assignments [I];
5838+
5839+ // Ignore self assignments, because they don't matter, since the value stays
5840+ // the same.
5841+ const auto RHSObj = getBoundsAttributedObject (Assign->getRHS ());
5842+ bool IsSelfAssign = RHSObj.has_value () && *RHSObj == LHSObj;
5843+ if (IsSelfAssign)
5844+ continue ;
5845+
5846+ const ValueDecl *VD = LHSObj.Decl ;
5847+ const auto It = Uses.find (VD);
5848+ if (It == Uses.end ())
5849+ continue ;
5850+
5851+ const Expr *Use = It->second ;
5852+ Handler.handleAssignedAndUsed (Assign, Use, VD,
5853+ /* IsRelatedToDecl=*/ false , Ctx);
5854+ IsGroupSafe = false ;
5855+ }
5856+
5857+ return IsGroupSafe;
5858+ }
5859+
57875860// Checks if the bounds-attributed group is safe. This function returns false
57885861// iff the assignment group is unsafe and diagnostics have been emitted.
57895862static bool
@@ -5793,6 +5866,8 @@ checkBoundsAttributedGroup(const BoundsAttributedAssignmentGroup &Group,
57935866 return false ;
57945867 if (!checkMissingAndDuplicatedAssignments (Group, Handler, Ctx))
57955868 return false ;
5869+ if (!checkAssignedAndUsed (Group, Handler, Ctx))
5870+ return false ;
57965871 // TODO: Add more checks.
57975872 return true ;
57985873}
0 commit comments