@@ -5539,9 +5539,322 @@ static void applyGadgets(const Decl *D, FixableGadgetList FixableGadgets,
55395539  }
55405540}
55415541
5542+ //  Checks if Self and Other are the same member bases. This supports only very
5543+ //  simple forms of member bases.
5544+ static  bool  isSameMemberBase (const  Expr *Self, const  Expr *Other) {
5545+   for  (;;) {
5546+     if  (Self == Other)
5547+       return  true ;
5548+ 
5549+     const  auto  *SelfICE = dyn_cast<ImplicitCastExpr>(Self);
5550+     const  auto  *OtherICE = dyn_cast<ImplicitCastExpr>(Other);
5551+     if  (SelfICE && OtherICE && SelfICE->getCastKind () == CK_LValueToRValue &&
5552+         OtherICE->getCastKind () == CK_LValueToRValue) {
5553+       Self = SelfICE->getSubExpr ();
5554+       Other = OtherICE->getSubExpr ();
5555+     }
5556+ 
5557+     const  auto  *SelfDRE = dyn_cast<DeclRefExpr>(Self);
5558+     const  auto  *OtherDRE = dyn_cast<DeclRefExpr>(Other);
5559+     if  (SelfDRE && OtherDRE)
5560+       return  SelfDRE->getDecl () == OtherDRE->getDecl ();
5561+ 
5562+     const  auto  *SelfME = dyn_cast<MemberExpr>(Self);
5563+     const  auto  *OtherME = dyn_cast<MemberExpr>(Other);
5564+     if  (!SelfME || !OtherME ||
5565+         SelfME->getMemberDecl () != OtherME->getMemberDecl ()) {
5566+       return  false ;
5567+     }
5568+ 
5569+     Self = SelfME->getBase ();
5570+     Other = OtherME->getBase ();
5571+   }
5572+ }
5573+ 
5574+ using  DependentDeclSetTy = llvm::SmallPtrSet<const  ValueDecl *, 4 >;
5575+ 
5576+ //  Gets the set/closure of bounds-attributed pointers and counts that belong to
5577+ //  the same group. Consider the following example:
5578+ //    int a, b, c;
5579+ //    int *__counted_by(a + b) p;
5580+ //    int *__counted_by(b - c) q;
5581+ //  Passing any of the variables above as `InitVD`, the function should return
5582+ //  the closure `{a, b, c, p, q}`.
5583+ static  DependentDeclSetTy getBoundsAttributedClosure (const  ValueDecl *InitVD) {
5584+   DependentDeclSetTy Set;
5585+ 
5586+   llvm::SmallVector<const  ValueDecl *, 4 > WorkList;
5587+   WorkList.push_back (InitVD);
5588+ 
5589+   while  (!WorkList.empty ()) {
5590+     const  ValueDecl *CurVD = WorkList.pop_back_val ();
5591+     bool  Inserted = Set.insert (CurVD).second ;
5592+     if  (!Inserted)
5593+       continue ;
5594+ 
5595+     //  If CurVD is a dependent decl, add the pointers that depend on CurVD.
5596+     for  (const  auto  *Attr : CurVD->specific_attrs <DependerDeclsAttr>()) {
5597+       for  (const  Decl *D : Attr->dependerDecls ()) {
5598+         if  (const  auto  *VD = dyn_cast<ValueDecl>(D))
5599+           WorkList.push_back (VD);
5600+       }
5601+     }
5602+ 
5603+     //  If CurVD is a bounds-attributed pointer (or pointer to it), add its
5604+     //  dependent decls.
5605+     QualType Ty = CurVD->getType ();
5606+     const  auto  *BAT = Ty->getAs <BoundsAttributedType>();
5607+     if  (!BAT && Ty->isPointerType ())
5608+       BAT = Ty->getPointeeType ()->getAs <BoundsAttributedType>();
5609+     if  (BAT) {
5610+       for  (const  auto  &DI : BAT->dependent_decls ())
5611+         WorkList.push_back (DI.getDecl ());
5612+     }
5613+   }
5614+ 
5615+   return  Set;
5616+ }
5617+ 
5618+ //  Bounds-attributed pointer or dependent count.
5619+ struct  BoundsAttributedObject  {
5620+   const  ValueDecl *Decl = nullptr ;
5621+   const  Expr *MemberBase = nullptr ;
5622+   int  DerefLevel = 0 ;
5623+ };
5624+ 
5625+ static  std::optional<BoundsAttributedObject>
5626+ getBoundsAttributedObject (const  Expr *E) {
5627+   E = E->IgnoreParenCasts ();
5628+ 
5629+   int  DerefLevel = 0 ;
5630+   while  (const  auto  *UO = dyn_cast<UnaryOperator>(E)) {
5631+     if  (UO->getOpcode () == UO_Deref)
5632+       DerefLevel++;
5633+     else  if  (UO->getOpcode () == UO_AddrOf)
5634+       DerefLevel--;
5635+     else 
5636+       break ;
5637+     E = UO->getSubExpr ()->IgnoreParenCasts ();
5638+   }
5639+   assert (DerefLevel >= 0 );
5640+ 
5641+   const  ValueDecl *Decl;
5642+   const  Expr *MemberBase = nullptr ;
5643+ 
5644+   if  (const  auto  *DRE = dyn_cast<DeclRefExpr>(E))
5645+     Decl = DRE->getDecl ();
5646+   else  if  (const  auto  *ME = dyn_cast<MemberExpr>(E)) {
5647+     Decl = ME->getMemberDecl ();
5648+     MemberBase = ME->getBase ();
5649+   } else 
5650+     return  std::nullopt ;
5651+ 
5652+   QualType Ty = Decl->getType ();
5653+   bool  IsBoundsAttributedPointer =
5654+       Ty->isBoundsAttributedType () ||
5655+       (Ty->isPointerType () && Ty->getPointeeType ()->isBoundsAttributedType ());
5656+   if  (IsBoundsAttributedPointer || Decl->isDependentCount ())
5657+     return  {{Decl, MemberBase, DerefLevel}};
5658+ 
5659+   return  std::nullopt ;
5660+ }
5661+ 
5662+ struct  BoundsAttributedAssignmentGroup  {
5663+   DependentDeclSetTy DeclClosure;
5664+   llvm::SmallVector<const  BinaryOperator *, 4 > Assignments;
5665+   llvm::SmallVector<BoundsAttributedObject, 4 > AssignedObjects;
5666+   using  DeclUseTy = std::pair<const  ValueDecl *, const  Expr *>;
5667+   const  Expr *MemberBase = nullptr ;
5668+ 
5669+   void  init (const  BoundsAttributedObject &Object) {
5670+     DeclClosure = getBoundsAttributedClosure (Object.Decl );
5671+     MemberBase = Object.MemberBase ;
5672+   }
5673+ 
5674+   void  clear () {
5675+     DeclClosure.clear ();
5676+     Assignments.clear ();
5677+     AssignedObjects.clear ();
5678+     MemberBase = nullptr ;
5679+   }
5680+ 
5681+   bool  empty () const  {
5682+     return  DeclClosure.empty ();
5683+   }
5684+ 
5685+   bool  isPartOfGroup (const  BoundsAttributedObject &Object) const  {
5686+     if  (!DeclClosure.contains (Object.Decl ))
5687+       return  false ;
5688+     if  (MemberBase)
5689+       return  Object.MemberBase  &&
5690+              isSameMemberBase (MemberBase, Object.MemberBase );
5691+     return  true ;
5692+   }
5693+ 
5694+   void  addAssignment (const  BoundsAttributedObject &Object,
5695+                      const  BinaryOperator *BO) {
5696+     Assignments.push_back (BO);
5697+     AssignedObjects.push_back (Object);
5698+   }
5699+ };
5700+ 
5701+ //  Visitor that is responsible for finding bounds-attributed assignment groups
5702+ //  and other assignments that can modify bounds-attributed objects.
5703+ // 
5704+ //  A bounds-attributed group must be a group of assignments that are children
5705+ //  of a CompoundStmt:
5706+ //    void foo(int *__counted_by(count) p, int count) {
5707+ //      p = ...;
5708+ //      count = ...;
5709+ //    }
5710+ //  Here, the group consists of both assignments to p and count.  Note that the
5711+ //  function body is a CompoundStmt.
5712+ // 
5713+ //  We consider assignments to bounds-attributed objects that are not simple
5714+ //  statements or not children of a CompoundStmt as too complex, and we give up
5715+ //  trying to put them in a bounds-attributed group:
5716+ //    void foo(int *__counted_by(count) p, int count) {
5717+ //      q = p = ...;
5718+ //            ^ too complex assignment to __counted_by() pointer
5719+ //      n = count += ...;
5720+ //                ^ too complex assignment to dependent count
5721+ //    }
5722+ //  Note that while those assignments are descendants of a CompoundStmt, they
5723+ //  are not its direct children.
5724+ struct  BoundsAttributedGroupFinder 
5725+     : public ConstStmtVisitor<BoundsAttributedGroupFinder> {
5726+   using  GroupHandlerTy = void (const  BoundsAttributedAssignmentGroup &Group);
5727+   using  AssignHandlerTy = void (const  Expr *, const  ValueDecl *);
5728+   std::function<GroupHandlerTy> GroupHandler;
5729+   std::function<AssignHandlerTy> TooComplexAssignHandler;
5730+   BoundsAttributedAssignmentGroup CurGroup;
5731+ 
5732+   explicit  BoundsAttributedGroupFinder (
5733+       std::function<GroupHandlerTy> GroupHandler,
5734+       std::function<AssignHandlerTy> TooComplexAssignHandler)
5735+       : GroupHandler(std::move(GroupHandler)),
5736+         TooComplexAssignHandler(std::move(TooComplexAssignHandler)) {}
5737+ 
5738+   void  VisitChildren (const  Stmt *S) {
5739+     for  (const  Stmt *Child : S->children ()) {
5740+       if  (Child)
5741+         Visit (Child);
5742+     }
5743+   }
5744+ 
5745+   void  VisitStmt (const  Stmt *S) { VisitChildren (S); }
5746+ 
5747+   void  VisitCompoundStmt (const  CompoundStmt *CS) {
5748+     for  (const  Stmt *Child : CS->children ()) {
5749+       const  Stmt *E = Child;
5750+ 
5751+       //  See through `ExprWithCleanups`. Clang will attach those nodes when C++
5752+       //  temporary object needs to be materialized. In our case, this can
5753+       //  happen when we create a temporary span with `sp.first()`. Then, the
5754+       //  structure is going to be:
5755+       //    CompoundStmt
5756+       //    `-ExprWithCleanups
5757+       //      `-BinaryOperator ...
5758+       if  (const  auto  *EWC = dyn_cast<ExprWithCleanups>(E))
5759+         E = EWC->getSubExpr ();
5760+ 
5761+       const  auto  *BO = dyn_cast<BinaryOperator>(E);
5762+       if  (BO && BO->getOpcode () == BO_Assign)
5763+         handleAssignInCompound (BO);
5764+       else  {
5765+         finishGroup ();
5766+         Visit (Child);
5767+       }
5768+     }
5769+ 
5770+     finishGroup ();
5771+   }
5772+ 
5773+   void  handleAssignInCompound (const  BinaryOperator *AssignOp) {
5774+     const  auto  ObjectOpt = getBoundsAttributedObject (AssignOp->getLHS ());
5775+     if  (!ObjectOpt.has_value ()) {
5776+       finishGroup ();
5777+       VisitChildren (AssignOp);
5778+       return ;
5779+     }
5780+ 
5781+     if  (!CurGroup.isPartOfGroup (*ObjectOpt)) {
5782+       finishGroup ();
5783+       CurGroup.init (*ObjectOpt);
5784+     }
5785+ 
5786+     CurGroup.addAssignment (*ObjectOpt, AssignOp);
5787+     VisitChildren (AssignOp->getRHS ());
5788+   }
5789+ 
5790+   void  finishGroup () {
5791+     if  (CurGroup.empty ())
5792+       return ;
5793+     GroupHandler (CurGroup);
5794+     CurGroup.clear ();
5795+   }
5796+ 
5797+   //  Handle statements that might modify bounds-attributed pointer/count, but
5798+   //  are not children of a CompoundStmt.
5799+ 
5800+   void  VisitBinaryOperator (const  BinaryOperator *BO) {
5801+     VisitChildren (BO);
5802+ 
5803+     if  (BO->isAssignmentOp ())
5804+       checkTooComplexAssign (BO, BO->getLHS ());
5805+   }
5806+ 
5807+   void  VisitUnaryOperator (const  UnaryOperator *UO) {
5808+     VisitChildren (UO);
5809+ 
5810+     if  (UO->isIncrementDecrementOp ())
5811+       checkTooComplexAssign (UO, UO->getSubExpr ());
5812+   }
5813+ 
5814+   void  checkTooComplexAssign (const  Expr *E, const  Expr *Sub) {
5815+     const  auto  DA = getBoundsAttributedObject (Sub);
5816+     if  (DA.has_value ())
5817+       TooComplexAssignHandler (E, DA->Decl );
5818+   }
5819+ };
5820+ 
5821+ static  void 
5822+ diagnoseTooComplexAssignToBoundsAttributed (const  Expr *E, const  ValueDecl *VD,
5823+                                            UnsafeBufferUsageHandler &Handler,
5824+                                            ASTContext &Ctx) {
5825+   //  Don't diagnose pointer arithmetic, since -Wunsafe-buffer-usage already does
5826+   //  it.
5827+   bool  IsPtrArith = false ;
5828+   if  (E->getType ()->isPointerType ()) {
5829+     if  (const  auto  *BO = dyn_cast<BinaryOperator>(E))
5830+       IsPtrArith = BO->isCompoundAssignmentOp ();
5831+     else  if  (const  auto  *UO = dyn_cast<UnaryOperator>(E)) {
5832+       assert (UO->isIncrementDecrementOp ());
5833+       IsPtrArith = true ;
5834+     }
5835+   }
5836+   if  (!IsPtrArith)
5837+     Handler.handleTooComplexCountAttributedAssign (
5838+         E, VD, /* IsRelatedToDecl=*/ false , Ctx);
5839+ }
5840+ 
5841+ static  void  checkBoundsSafetyAssignments (const  Stmt *S,
5842+                                          UnsafeBufferUsageHandler &Handler,
5843+                                          ASTContext &Ctx) {
5844+   BoundsAttributedGroupFinder Finder (
5845+       [&](const  BoundsAttributedAssignmentGroup &Group) {
5846+         //  TODO: Check group constraints.
5847+       },
5848+       [&](const  Expr *E, const  ValueDecl *VD) {
5849+         diagnoseTooComplexAssignToBoundsAttributed (E, VD, Handler, Ctx);
5850+       });
5851+   Finder.Visit (S);
5852+ }
5853+ 
55425854void  clang::checkUnsafeBufferUsage (const  Decl *D,
55435855                                   UnsafeBufferUsageHandler &Handler,
5544-                                    bool  EmitSuggestions) {
5856+                                    bool  EmitSuggestions,
5857+                                    bool  BoundsSafetyAttributesEnabled) {
55455858#ifndef  NDEBUG
55465859  Handler.clearDebugNotes ();
55475860#endif 
@@ -5587,6 +5900,11 @@ void clang::checkUnsafeBufferUsage(const Decl *D,
55875900  for  (Stmt *S : Stmts) {
55885901    findGadgets (S, D->getASTContext (), Handler, EmitSuggestions, FixableGadgets,
55895902                WarningGadgets, Tracker);
5903+ 
5904+     //  Run the bounds-safety assignment analysis if the attributes are enabled,
5905+     //  otherwise don't waste cycles.
5906+     if  (BoundsSafetyAttributesEnabled)
5907+       checkBoundsSafetyAssignments (S, Handler, D->getASTContext ());
55905908  }
55915909  applyGadgets (D, std::move (FixableGadgets), std::move (WarningGadgets),
55925910               std::move (Tracker), Handler, EmitSuggestions);
0 commit comments