@@ -5444,9 +5444,318 @@ static void applyGadgets(const Decl *D, FixableGadgetList FixableGadgets,
54445444  }
54455445}
54465446
5447+ //  Checks if Self and Other are the same member bases. This supports only very
5448+ //  simple forms of member bases.
5449+ static  bool  isSameMemberBase (const  Expr *Self, const  Expr *Other) {
5450+   for  (;;) {
5451+     if  (Self == Other)
5452+       return  true ;
5453+ 
5454+     const  auto  *SelfICE = dyn_cast<ImplicitCastExpr>(Self);
5455+     const  auto  *OtherICE = dyn_cast<ImplicitCastExpr>(Other);
5456+     if  (SelfICE && OtherICE && SelfICE->getCastKind () == CK_LValueToRValue &&
5457+         OtherICE->getCastKind () == CK_LValueToRValue) {
5458+       Self = SelfICE->getSubExpr ();
5459+       Other = OtherICE->getSubExpr ();
5460+     }
5461+ 
5462+     const  auto  *SelfDRE = dyn_cast<DeclRefExpr>(Self);
5463+     const  auto  *OtherDRE = dyn_cast<DeclRefExpr>(Other);
5464+     if  (SelfDRE && OtherDRE)
5465+       return  SelfDRE->getDecl () == OtherDRE->getDecl ();
5466+ 
5467+     const  auto  *SelfME = dyn_cast<MemberExpr>(Self);
5468+     const  auto  *OtherME = dyn_cast<MemberExpr>(Other);
5469+     if  (!SelfME || !OtherME ||
5470+         SelfME->getMemberDecl () != OtherME->getMemberDecl ()) {
5471+       return  false ;
5472+     }
5473+ 
5474+     Self = SelfME->getBase ();
5475+     Other = OtherME->getBase ();
5476+   }
5477+ }
5478+ 
5479+ using  DependentDeclSetTy = llvm::SmallPtrSet<const  ValueDecl *, 4 >;
5480+ 
5481+ //  Gets the set/closure of bounds-attributed pointers and counts that belong to
5482+ //  the same group. Consider the following example:
5483+ //    int a, b, c;
5484+ //    int *__counted_by(a + b) p;
5485+ //    int *__counted_by(b - c) q;
5486+ //  Passing any of the variables above as `InitVD`, the function should return
5487+ //  the closure `{a, b, c, p, q}`.
5488+ static  DependentDeclSetTy GetBoundsAttributedClosure (const  ValueDecl *InitVD) {
5489+   DependentDeclSetTy Set;
5490+ 
5491+   llvm::SmallVector<const  ValueDecl *, 4 > WorkList;
5492+   WorkList.push_back (InitVD);
5493+ 
5494+   while  (!WorkList.empty ()) {
5495+     const  ValueDecl *CurVD = WorkList.pop_back_val ();
5496+     bool  Inserted = Set.insert (CurVD).second ;
5497+     if  (!Inserted)
5498+       continue ;
5499+ 
5500+     //  If CurVD is a dependent decl, add the pointers that depend on CurVD.
5501+     for  (const  auto  *Attr : CurVD->specific_attrs <DependerDeclsAttr>()) {
5502+       for  (const  Decl *D : Attr->dependerDecls ()) {
5503+         if  (const  auto  *VD = dyn_cast<ValueDecl>(D))
5504+           WorkList.push_back (VD);
5505+       }
5506+     }
5507+ 
5508+     //  If CurVD is a bounds-attributed pointer (or pointer to it), add its
5509+     //  dependent decls.
5510+     QualType Ty = CurVD->getType ();
5511+     const  auto  *BAT = Ty->getAs <BoundsAttributedType>();
5512+     if  (!BAT && Ty->isPointerType ())
5513+       BAT = Ty->getPointeeType ()->getAs <BoundsAttributedType>();
5514+     if  (BAT) {
5515+       for  (const  auto  &DI : BAT->dependent_decls ())
5516+         WorkList.push_back (DI.getDecl ());
5517+     }
5518+   }
5519+ 
5520+   return  Set;
5521+ }
5522+ 
5523+ //  Bounds-attributed pointer or dependent count.
5524+ struct  BoundsAttributedObject  {
5525+   const  ValueDecl *Decl = nullptr ;
5526+   const  Expr *MemberBase = nullptr ;
5527+   int  DerefLevel = 0 ;
5528+ };
5529+ 
5530+ static  std::optional<BoundsAttributedObject>
5531+ getBoundsAttributedObject (const  Expr *E) {
5532+   E = E->IgnoreParenCasts ();
5533+ 
5534+   int  DerefLevel = 0 ;
5535+   while  (const  auto  *UO = dyn_cast<UnaryOperator>(E)) {
5536+     if  (UO->getOpcode () == UO_Deref)
5537+       DerefLevel++;
5538+     else  if  (UO->getOpcode () == UO_AddrOf)
5539+       DerefLevel--;
5540+     else 
5541+       break ;
5542+     E = UO->getSubExpr ()->IgnoreParenCasts ();
5543+   }
5544+   assert (DerefLevel >= 0 );
5545+ 
5546+   const  ValueDecl *Decl;
5547+   const  Expr *MemberBase = nullptr ;
5548+ 
5549+   if  (const  auto  *DRE = dyn_cast<DeclRefExpr>(E))
5550+     Decl = DRE->getDecl ();
5551+   else  if  (const  auto  *ME = dyn_cast<MemberExpr>(E)) {
5552+     Decl = ME->getMemberDecl ();
5553+     MemberBase = ME->getBase ();
5554+   } else 
5555+     return  std::nullopt ;
5556+ 
5557+   QualType Ty = Decl->getType ();
5558+   bool  IsBoundsAttributedPointer =
5559+       Ty->isBoundsAttributedType () ||
5560+       (Ty->isPointerType () && Ty->getPointeeType ()->isBoundsAttributedType ());
5561+   if  (IsBoundsAttributedPointer || Decl->isDependentCount ())
5562+     return  {{Decl, MemberBase, DerefLevel}};
5563+ 
5564+   return  std::nullopt ;
5565+ }
5566+ 
5567+ struct  BoundsAttributedAssignmentGroup  {
5568+   DependentDeclSetTy DeclClosure;
5569+   llvm::SmallVector<const  BinaryOperator *, 4 > Assignments;
5570+   llvm::SmallVector<BoundsAttributedObject, 4 > AssignedObjects;
5571+   using  DeclUseTy = std::pair<const  ValueDecl *, const  Expr *>;
5572+   const  Expr *MemberBase = nullptr ;
5573+ 
5574+   void  Init (const  BoundsAttributedObject &Object) {
5575+     DeclClosure = GetBoundsAttributedClosure (Object.Decl );
5576+     MemberBase = Object.MemberBase ;
5577+   }
5578+ 
5579+   void  Clear () {
5580+     DeclClosure.clear ();
5581+     Assignments.clear ();
5582+     AssignedObjects.clear ();
5583+     MemberBase = nullptr ;
5584+   }
5585+ 
5586+   bool  Empty () const  {
5587+     return  DeclClosure.empty ();
5588+   }
5589+ 
5590+   bool  IsPartOfGroup (const  BoundsAttributedObject &Object) const  {
5591+     if  (!DeclClosure.contains (Object.Decl ))
5592+       return  false ;
5593+     if  (MemberBase)
5594+       return  Object.MemberBase  &&
5595+              isSameMemberBase (MemberBase, Object.MemberBase );
5596+     return  true ;
5597+   }
5598+ 
5599+   void  AddAssignment (const  BoundsAttributedObject &Object,
5600+                      const  BinaryOperator *BO) {
5601+     Assignments.push_back (BO);
5602+     AssignedObjects.push_back (Object);
5603+   }
5604+ };
5605+ 
5606+ //  Visitor that is responsible for finding bounds-attributed assignment groups
5607+ //  and other assignments to bounds-attributed objects that we consider too
5608+ //  complex to analyze.
5609+ // 
5610+ //  Bounds-attributed groups must be inside of a CompoundStmt:
5611+ //    void foo(int *__counted_by(count) p, int count) {
5612+ //      p = ...;
5613+ //      count = ...;
5614+ //    }
5615+ //  Here, the group consists of both assignments to p and count.  Note that the
5616+ //  function body is a CompoundStmt.
5617+ // 
5618+ //  We consider assignments that are not simple statements inside of a
5619+ //  CompoundStmt and modify bounds-attributed objects as too complex, and we give
5620+ //  up verifying them:
5621+ //    void foo(int *__counted_by(count) p, int count) {
5622+ //      q = p = ...;
5623+ //            ^ too complex assignment to __counted_by() pointer
5624+ //      n = count += ...;
5625+ //                ^ too complex assignment to dependent count
5626+ //    }
5627+ struct  BoundsAttributedGroupFinder 
5628+     : public ConstStmtVisitor<BoundsAttributedGroupFinder> {
5629+   using  GroupHandlerTy = void (const  BoundsAttributedAssignmentGroup &Group);
5630+   using  AssignHandlerTy = void (const  Expr *, const  ValueDecl *);
5631+   std::function<GroupHandlerTy> GroupHandler;
5632+   std::function<AssignHandlerTy> TooComplexAssignHandler;
5633+   BoundsAttributedAssignmentGroup CurGroup;
5634+ 
5635+   explicit  BoundsAttributedGroupFinder (
5636+       std::function<GroupHandlerTy> GroupHandler,
5637+       std::function<AssignHandlerTy> TooComplexAssignHandler)
5638+       : GroupHandler(std::move(GroupHandler)),
5639+         TooComplexAssignHandler(std::move(TooComplexAssignHandler)) {}
5640+ 
5641+   void  VisitChildren (const  Stmt *S) {
5642+     for  (const  Stmt *Child : S->children ())
5643+       Visit (Child);
5644+   }
5645+ 
5646+   void  VisitStmt (const  Stmt *S) { VisitChildren (S); }
5647+ 
5648+   void  VisitCompoundStmt (const  CompoundStmt *CS) {
5649+     for  (const  Stmt *Child : CS->children ()) {
5650+       const  Stmt *E = Child;
5651+ 
5652+       //  See through `ExprWithCleanups`. Clang will attach those nodes when C++
5653+       //  temporary object needs to be materialized. In our case, this can
5654+       //  happen when we create a temporary span with `sp.first()`. Then, the
5655+       //  structure is going to be:
5656+       //    CompoundStmt
5657+       //    `-ExprWithCleanups
5658+       //      `-BinaryOperator ...
5659+       if  (const  auto  *EWC = dyn_cast<ExprWithCleanups>(E))
5660+         E = EWC->getSubExpr ();
5661+ 
5662+       const  auto  *BO = dyn_cast<BinaryOperator>(E);
5663+       if  (BO && BO->getOpcode () == BO_Assign)
5664+         HandleAssignInCompound (BO);
5665+       else  {
5666+         FinishGroup ();
5667+         Visit (Child);
5668+       }
5669+     }
5670+ 
5671+     FinishGroup ();
5672+   }
5673+ 
5674+   void  HandleAssignInCompound (const  BinaryOperator *AssignOp) {
5675+     const  auto  ObjectOpt = getBoundsAttributedObject (AssignOp->getLHS ());
5676+     if  (!ObjectOpt.has_value ()) {
5677+       FinishGroup ();
5678+       VisitChildren (AssignOp);
5679+       return ;
5680+     }
5681+ 
5682+     if  (!CurGroup.IsPartOfGroup (*ObjectOpt)) {
5683+       FinishGroup ();
5684+       CurGroup.Init (*ObjectOpt);
5685+     }
5686+ 
5687+     CurGroup.AddAssignment (*ObjectOpt, AssignOp);
5688+     VisitChildren (AssignOp->getRHS ());
5689+   }
5690+ 
5691+   void  FinishGroup () {
5692+     if  (CurGroup.Empty ())
5693+       return ;
5694+     GroupHandler (CurGroup);
5695+     CurGroup.Clear ();
5696+   }
5697+ 
5698+   //  Handle statements that might modify bounds-attributed pointer/count, but
5699+   //  are not directly in a CompoundStmt.
5700+ 
5701+   void  VisitBinaryOperator (const  BinaryOperator *BO) {
5702+     VisitChildren (BO);
5703+ 
5704+     if  (BO->isAssignmentOp ())
5705+       CheckTooComplexAssign (BO, BO->getLHS ());
5706+   }
5707+ 
5708+   void  VisitUnaryOperator (const  UnaryOperator *UO) {
5709+     VisitChildren (UO);
5710+ 
5711+     if  (UO->isIncrementDecrementOp ())
5712+       CheckTooComplexAssign (UO, UO->getSubExpr ());
5713+   }
5714+ 
5715+   void  CheckTooComplexAssign (const  Expr *E, const  Expr *Sub) {
5716+     const  auto  DA = getBoundsAttributedObject (Sub);
5717+     if  (DA.has_value ())
5718+       TooComplexAssignHandler (E, DA->Decl );
5719+   }
5720+ };
5721+ 
5722+ static  void 
5723+ diagnoseTooComplexAssignToBoundsAttributed (const  Expr *E, const  ValueDecl *VD,
5724+                                            UnsafeBufferUsageHandler &Handler,
5725+                                            ASTContext &Ctx) {
5726+   //  Don't diagnose pointer arithmetic, since -Wunsafe-buffer-usage already does
5727+   //  it.
5728+   bool  IsPtrArith = false ;
5729+   if  (E->getType ()->isPointerType ()) {
5730+     if  (const  auto  *BO = dyn_cast<BinaryOperator>(E))
5731+       IsPtrArith = BO->isCompoundAssignmentOp ();
5732+     else  if  (const  auto  *UO = dyn_cast<UnaryOperator>(E)) {
5733+       assert (UO->isIncrementDecrementOp ());
5734+       IsPtrArith = true ;
5735+     }
5736+   }
5737+   if  (!IsPtrArith)
5738+     Handler.handleTooComplexCountAttributedAssign (
5739+         E, VD, /* IsRelatedToDecl=*/ false , Ctx);
5740+ }
5741+ 
5742+ static  void  checkBoundsSafetyAssignments (const  Stmt *S,
5743+                                          UnsafeBufferUsageHandler &Handler,
5744+                                          ASTContext &Ctx) {
5745+   BoundsAttributedGroupFinder Finder (
5746+       [&](const  BoundsAttributedAssignmentGroup &Group) {
5747+         //  TODO: Check group constraints.
5748+       },
5749+       [&](const  Expr *E, const  ValueDecl *VD) {
5750+         diagnoseTooComplexAssignToBoundsAttributed (E, VD, Handler, Ctx);
5751+       });
5752+   Finder.Visit (S);
5753+ }
5754+ 
54475755void  clang::checkUnsafeBufferUsage (const  Decl *D,
54485756                                   UnsafeBufferUsageHandler &Handler,
5449-                                    bool  EmitSuggestions) {
5757+                                    bool  EmitSuggestions,
5758+                                    bool  BoundsSafetyAttributes) {
54505759#ifndef  NDEBUG
54515760  Handler.clearDebugNotes ();
54525761#endif 
@@ -5492,6 +5801,11 @@ void clang::checkUnsafeBufferUsage(const Decl *D,
54925801  for  (Stmt *S : Stmts) {
54935802    findGadgets (S, D->getASTContext (), Handler, EmitSuggestions, FixableGadgets,
54945803                WarningGadgets, Tracker);
5804+ 
5805+     //  Run the bounds-safety assignment analysis if the attributes are enabled,
5806+     //  otherwise don't waste cycles.
5807+     if  (BoundsSafetyAttributes)
5808+       checkBoundsSafetyAssignments (S, Handler, D->getASTContext ());
54955809  }
54965810  applyGadgets (D, std::move (FixableGadgets), std::move (WarningGadgets),
54975811               std::move (Tracker), Handler, EmitSuggestions);
0 commit comments