xref: /freebsd/contrib/llvm-project/clang/lib/Sema/SemaBoundsSafety.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
1 //===-- SemaBoundsSafety.cpp - Bounds Safety specific routines-*- C++ -*---===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 /// \file
9 /// This file declares semantic analysis functions specific to `-fbounds-safety`
10 /// (Bounds Safety) and also its attributes when used without `-fbounds-safety`
11 /// (e.g. `counted_by`)
12 ///
13 //===----------------------------------------------------------------------===//
14 #include "clang/Lex/Lexer.h"
15 #include "clang/Sema/Initialization.h"
16 #include "clang/Sema/Sema.h"
17 
18 namespace clang {
19 
20 static CountAttributedType::DynamicCountPointerKind
getCountAttrKind(bool CountInBytes,bool OrNull)21 getCountAttrKind(bool CountInBytes, bool OrNull) {
22   if (CountInBytes)
23     return OrNull ? CountAttributedType::SizedByOrNull
24                   : CountAttributedType::SizedBy;
25   return OrNull ? CountAttributedType::CountedByOrNull
26                 : CountAttributedType::CountedBy;
27 }
28 
GetEnclosingNamedOrTopAnonRecord(const FieldDecl * FD)29 static const RecordDecl *GetEnclosingNamedOrTopAnonRecord(const FieldDecl *FD) {
30   const auto *RD = FD->getParent();
31   // An unnamed struct is anonymous struct only if it's not instantiated.
32   // However, the struct may not be fully processed yet to determine
33   // whether it's anonymous or not. In that case, this function treats it as
34   // an anonymous struct and tries to find a named parent.
35   while (RD && (RD->isAnonymousStructOrUnion() ||
36                 (!RD->isCompleteDefinition() && RD->getName().empty()))) {
37     const auto *Parent = dyn_cast<RecordDecl>(RD->getParent());
38     if (!Parent)
39       break;
40     RD = Parent;
41   }
42   return RD;
43 }
44 
45 enum class CountedByInvalidPointeeTypeKind {
46   INCOMPLETE,
47   SIZELESS,
48   FUNCTION,
49   FLEXIBLE_ARRAY_MEMBER,
50   VALID,
51 };
52 
CheckCountedByAttrOnField(FieldDecl * FD,Expr * E,bool CountInBytes,bool OrNull)53 bool Sema::CheckCountedByAttrOnField(FieldDecl *FD, Expr *E, bool CountInBytes,
54                                      bool OrNull) {
55   // Check the context the attribute is used in
56 
57   unsigned Kind = getCountAttrKind(CountInBytes, OrNull);
58 
59   if (FD->getParent()->isUnion()) {
60     Diag(FD->getBeginLoc(), diag::err_count_attr_in_union)
61         << Kind << FD->getSourceRange();
62     return true;
63   }
64 
65   const auto FieldTy = FD->getType();
66   if (FieldTy->isArrayType() && (CountInBytes || OrNull)) {
67     Diag(FD->getBeginLoc(),
68          diag::err_count_attr_not_on_ptr_or_flexible_array_member)
69         << Kind << FD->getLocation() << /* suggest counted_by */ 1;
70     return true;
71   }
72   if (!FieldTy->isArrayType() && !FieldTy->isPointerType()) {
73     Diag(FD->getBeginLoc(),
74          diag::err_count_attr_not_on_ptr_or_flexible_array_member)
75         << Kind << FD->getLocation() << /* do not suggest counted_by */ 0;
76     return true;
77   }
78 
79   LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
80       LangOptions::StrictFlexArraysLevelKind::IncompleteOnly;
81   if (FieldTy->isArrayType() &&
82       !Decl::isFlexibleArrayMemberLike(getASTContext(), FD, FieldTy,
83                                        StrictFlexArraysLevel, true)) {
84     Diag(FD->getBeginLoc(),
85          diag::err_counted_by_attr_on_array_not_flexible_array_member)
86         << Kind << FD->getLocation();
87     return true;
88   }
89 
90   CountedByInvalidPointeeTypeKind InvalidTypeKind =
91       CountedByInvalidPointeeTypeKind::VALID;
92   QualType PointeeTy;
93   int SelectPtrOrArr = 0;
94   if (FieldTy->isPointerType()) {
95     PointeeTy = FieldTy->getPointeeType();
96     SelectPtrOrArr = 0;
97   } else {
98     assert(FieldTy->isArrayType());
99     const ArrayType *AT = getASTContext().getAsArrayType(FieldTy);
100     PointeeTy = AT->getElementType();
101     SelectPtrOrArr = 1;
102   }
103   // Note: The `Decl::isFlexibleArrayMemberLike` check earlier on means
104   // only `PointeeTy->isStructureTypeWithFlexibleArrayMember()` is reachable
105   // when `FieldTy->isArrayType()`.
106   bool ShouldWarn = false;
107   if (PointeeTy->isAlwaysIncompleteType() && !CountInBytes) {
108     // In general using `counted_by` or `counted_by_or_null` on
109     // pointers where the pointee is an incomplete type are problematic. This is
110     // because it isn't possible to compute the pointer's bounds without knowing
111     // the pointee type size. At the same time it is common to forward declare
112     // types in header files.
113     //
114     // E.g.:
115     //
116     // struct Handle;
117     // struct Wrapper {
118     //   size_t size;
119     //   struct Handle* __counted_by(count) handles;
120     // }
121     //
122     // To allow the above code pattern but still prevent the pointee type from
123     // being incomplete in places where bounds checks are needed the following
124     // scheme is used:
125     //
126     // * When the pointee type might not always be an incomplete type (i.e.
127     // a type that is currently incomplete but might be completed later
128     // on in the translation unit) the attribute is allowed by this method
129     // but later uses of the FieldDecl are checked that the pointee type
130     // is complete see `BoundsSafetyCheckAssignmentToCountAttrPtr`,
131     // `BoundsSafetyCheckInitialization`, and
132     // `BoundsSafetyCheckUseOfCountAttrPtr`
133     //
134     // * When the pointee type is always an incomplete type (e.g.
135     // `void`) the attribute is disallowed by this method because we know the
136     // type can never be completed so there's no reason to allow it.
137     InvalidTypeKind = CountedByInvalidPointeeTypeKind::INCOMPLETE;
138   } else if (PointeeTy->isSizelessType()) {
139     InvalidTypeKind = CountedByInvalidPointeeTypeKind::SIZELESS;
140   } else if (PointeeTy->isFunctionType()) {
141     InvalidTypeKind = CountedByInvalidPointeeTypeKind::FUNCTION;
142   } else if (PointeeTy->isStructureTypeWithFlexibleArrayMember()) {
143     if (FieldTy->isArrayType() && !getLangOpts().BoundsSafety) {
144       // This is a workaround for the Linux kernel that has already adopted
145       // `counted_by` on a FAM where the pointee is a struct with a FAM. This
146       // should be an error because computing the bounds of the array cannot be
147       // done correctly without manually traversing every struct object in the
148       // array at runtime. To allow the code to be built this error is
149       // downgraded to a warning.
150       ShouldWarn = true;
151     }
152     InvalidTypeKind = CountedByInvalidPointeeTypeKind::FLEXIBLE_ARRAY_MEMBER;
153   }
154 
155   if (InvalidTypeKind != CountedByInvalidPointeeTypeKind::VALID) {
156     unsigned DiagID = ShouldWarn
157                           ? diag::warn_counted_by_attr_elt_type_unknown_size
158                           : diag::err_counted_by_attr_pointee_unknown_size;
159     Diag(FD->getBeginLoc(), DiagID)
160         << SelectPtrOrArr << PointeeTy << (int)InvalidTypeKind
161         << (ShouldWarn ? 1 : 0) << Kind << FD->getSourceRange();
162     return true;
163   }
164 
165   // Check the expression
166 
167   if (!E->getType()->isIntegerType() || E->getType()->isBooleanType()) {
168     Diag(E->getBeginLoc(), diag::err_count_attr_argument_not_integer)
169         << Kind << E->getSourceRange();
170     return true;
171   }
172 
173   auto *DRE = dyn_cast<DeclRefExpr>(E);
174   if (!DRE) {
175     Diag(E->getBeginLoc(),
176          diag::err_count_attr_only_support_simple_decl_reference)
177         << Kind << E->getSourceRange();
178     return true;
179   }
180 
181   auto *CountDecl = DRE->getDecl();
182   FieldDecl *CountFD = dyn_cast<FieldDecl>(CountDecl);
183   if (auto *IFD = dyn_cast<IndirectFieldDecl>(CountDecl)) {
184     CountFD = IFD->getAnonField();
185   }
186   if (!CountFD) {
187     Diag(E->getBeginLoc(), diag::err_count_attr_must_be_in_structure)
188         << CountDecl << Kind << E->getSourceRange();
189 
190     Diag(CountDecl->getBeginLoc(),
191          diag::note_flexible_array_counted_by_attr_field)
192         << CountDecl << CountDecl->getSourceRange();
193     return true;
194   }
195 
196   if (FD->getParent() != CountFD->getParent()) {
197     if (CountFD->getParent()->isUnion()) {
198       Diag(CountFD->getBeginLoc(), diag::err_count_attr_refer_to_union)
199           << Kind << CountFD->getSourceRange();
200       return true;
201     }
202     // Whether CountRD is an anonymous struct is not determined at this
203     // point. Thus, an additional diagnostic in case it's not anonymous struct
204     // is done later in `Parser::ParseStructDeclaration`.
205     auto *RD = GetEnclosingNamedOrTopAnonRecord(FD);
206     auto *CountRD = GetEnclosingNamedOrTopAnonRecord(CountFD);
207 
208     if (RD != CountRD) {
209       Diag(E->getBeginLoc(), diag::err_count_attr_param_not_in_same_struct)
210           << CountFD << Kind << FieldTy->isArrayType() << E->getSourceRange();
211       Diag(CountFD->getBeginLoc(),
212            diag::note_flexible_array_counted_by_attr_field)
213           << CountFD << CountFD->getSourceRange();
214       return true;
215     }
216   }
217   return false;
218 }
219 
EmitIncompleteCountedByPointeeNotes(Sema & S,const CountAttributedType * CATy,NamedDecl * IncompleteTyDecl)220 static void EmitIncompleteCountedByPointeeNotes(Sema &S,
221                                                 const CountAttributedType *CATy,
222                                                 NamedDecl *IncompleteTyDecl) {
223   assert(IncompleteTyDecl == nullptr || isa<TypeDecl>(IncompleteTyDecl));
224 
225   if (IncompleteTyDecl) {
226     // Suggest completing the pointee type if its a named typed (i.e.
227     // IncompleteTyDecl isn't nullptr). Suggest this first as it is more likely
228     // to be the correct fix.
229     //
230     // Note the `IncompleteTyDecl` type is the underlying type which might not
231     // be the same as `CATy->getPointeeType()` which could be a typedef.
232     //
233     // The diagnostic printed will be at the location of the underlying type but
234     // the diagnostic text will print the type of `CATy->getPointeeType()` which
235     // could be a typedef name rather than the underlying type. This is ok
236     // though because the diagnostic will print the underlying type name too.
237     S.Diag(IncompleteTyDecl->getBeginLoc(),
238            diag::note_counted_by_consider_completing_pointee_ty)
239         << CATy->getPointeeType();
240   }
241 
242   // Suggest using __sized_by(_or_null) instead of __counted_by(_or_null) as
243   // __sized_by(_or_null) doesn't have the complete type restriction.
244   //
245   // We use the source range of the expression on the CountAttributedType as an
246   // approximation for the source range of the attribute. This isn't quite right
247   // but isn't easy to fix right now.
248   //
249   // TODO: Implement logic to find the relevant TypeLoc for the attribute and
250   // get the SourceRange from that (#113582).
251   //
252   // TODO: We should emit a fix-it here.
253   SourceRange AttrSrcRange = CATy->getCountExpr()->getSourceRange();
254   S.Diag(AttrSrcRange.getBegin(), diag::note_counted_by_consider_using_sized_by)
255       << CATy->isOrNull() << AttrSrcRange;
256 }
257 
258 static std::tuple<const CountAttributedType *, QualType>
GetCountedByAttrOnIncompletePointee(QualType Ty,NamedDecl ** ND)259 GetCountedByAttrOnIncompletePointee(QualType Ty, NamedDecl **ND) {
260   auto *CATy = Ty->getAs<CountAttributedType>();
261   // Incomplete pointee type is only a problem for
262   // counted_by/counted_by_or_null
263   if (!CATy || CATy->isCountInBytes())
264     return {};
265 
266   auto PointeeTy = CATy->getPointeeType();
267   if (PointeeTy.isNull()) {
268     // Reachable if `CountAttributedType` wraps an IncompleteArrayType
269     return {};
270   }
271 
272   if (!PointeeTy->isIncompleteType(ND))
273     return {};
274 
275   return {CATy, PointeeTy};
276 }
277 
278 /// Perform Checks for assigning to a `__counted_by` or
279 /// `__counted_by_or_null` pointer type \param LHSTy where the pointee type
280 /// is incomplete which is invalid.
281 ///
282 /// \param S The Sema instance.
283 /// \param LHSTy The type being assigned to. Checks will only be performed if
284 ///              the type is a `counted_by` or `counted_by_or_null ` pointer.
285 /// \param RHSExpr The expression being assigned from.
286 /// \param Action The type assignment being performed
287 /// \param Loc The SourceLocation to use for error diagnostics
288 /// \param Assignee The ValueDecl being assigned. This is used to compute
289 ///        the name of the assignee. If the assignee isn't known this can
290 ///        be set to nullptr.
291 /// \param ShowFullyQualifiedAssigneeName If set to true when using \p
292 ///        Assignee to compute the name of the assignee use the fully
293 ///        qualified name, otherwise use the unqualified name.
294 ///
295 /// \returns True iff no diagnostic where emitted, false otherwise.
CheckAssignmentToCountAttrPtrWithIncompletePointeeTy(Sema & S,QualType LHSTy,Expr * RHSExpr,AssignmentAction Action,SourceLocation Loc,const ValueDecl * Assignee,bool ShowFullyQualifiedAssigneeName)296 static bool CheckAssignmentToCountAttrPtrWithIncompletePointeeTy(
297     Sema &S, QualType LHSTy, Expr *RHSExpr, AssignmentAction Action,
298     SourceLocation Loc, const ValueDecl *Assignee,
299     bool ShowFullyQualifiedAssigneeName) {
300   NamedDecl *IncompleteTyDecl = nullptr;
301   auto [CATy, PointeeTy] =
302       GetCountedByAttrOnIncompletePointee(LHSTy, &IncompleteTyDecl);
303   if (!CATy)
304     return true;
305 
306   std::string AssigneeStr;
307   if (Assignee) {
308     if (ShowFullyQualifiedAssigneeName) {
309       AssigneeStr = Assignee->getQualifiedNameAsString();
310     } else {
311       AssigneeStr = Assignee->getNameAsString();
312     }
313   }
314 
315   S.Diag(Loc, diag::err_counted_by_on_incomplete_type_on_assign)
316       << static_cast<int>(Action) << AssigneeStr << (AssigneeStr.size() > 0)
317       << isa<ImplicitValueInitExpr>(RHSExpr) << LHSTy
318       << CATy->getAttributeName(/*WithMacroPrefix=*/true) << PointeeTy
319       << CATy->isOrNull() << RHSExpr->getSourceRange();
320 
321   EmitIncompleteCountedByPointeeNotes(S, CATy, IncompleteTyDecl);
322   return false; // check failed
323 }
324 
BoundsSafetyCheckAssignmentToCountAttrPtr(QualType LHSTy,Expr * RHSExpr,AssignmentAction Action,SourceLocation Loc,const ValueDecl * Assignee,bool ShowFullyQualifiedAssigneeName)325 bool Sema::BoundsSafetyCheckAssignmentToCountAttrPtr(
326     QualType LHSTy, Expr *RHSExpr, AssignmentAction Action, SourceLocation Loc,
327     const ValueDecl *Assignee, bool ShowFullyQualifiedAssigneeName) {
328   return CheckAssignmentToCountAttrPtrWithIncompletePointeeTy(
329       *this, LHSTy, RHSExpr, Action, Loc, Assignee,
330       ShowFullyQualifiedAssigneeName);
331 }
332 
BoundsSafetyCheckInitialization(const InitializedEntity & Entity,const InitializationKind & Kind,AssignmentAction Action,QualType LHSType,Expr * RHSExpr)333 bool Sema::BoundsSafetyCheckInitialization(const InitializedEntity &Entity,
334                                            const InitializationKind &Kind,
335                                            AssignmentAction Action,
336                                            QualType LHSType, Expr *RHSExpr) {
337   auto SL = Kind.getLocation();
338 
339   // Note: We don't call `BoundsSafetyCheckAssignmentToCountAttrPtr` here
340   // because we need conditionalize what is checked. In downstream
341   // Clang `counted_by` is supported on variable definitions and in that
342   // implementation an error diagnostic will be emitted on the variable
343   // definition if the pointee is an incomplete type. To avoid warning about the
344   // same problem twice (once when the variable is defined, once when Sema
345   // checks the initializer) we skip checking the initializer if it's a
346   // variable.
347   if (Action == AssignmentAction::Initializing &&
348       Entity.getKind() != InitializedEntity::EK_Variable) {
349 
350     if (!CheckAssignmentToCountAttrPtrWithIncompletePointeeTy(
351             *this, LHSType, RHSExpr, Action, SL,
352             dyn_cast_or_null<ValueDecl>(Entity.getDecl()),
353             /*ShowFullQualifiedAssigneeName=*/true)) {
354       return false;
355     }
356   }
357 
358   return true;
359 }
360 
BoundsSafetyCheckUseOfCountAttrPtr(const Expr * E)361 bool Sema::BoundsSafetyCheckUseOfCountAttrPtr(const Expr *E) {
362   QualType T = E->getType();
363   if (!T->isPointerType())
364     return true;
365 
366   NamedDecl *IncompleteTyDecl = nullptr;
367   auto [CATy, PointeeTy] =
368       GetCountedByAttrOnIncompletePointee(T, &IncompleteTyDecl);
369   if (!CATy)
370     return true;
371 
372   // Generate a string for the diagnostic that describes the "use".
373   // The string is specialized for direct calls to produce a better
374   // diagnostic.
375   SmallString<64> UseStr;
376   bool IsDirectCall = false;
377   if (const auto *CE = dyn_cast<CallExpr>(E->IgnoreParens())) {
378     if (const auto *FD = CE->getDirectCallee()) {
379       UseStr = FD->getName();
380       IsDirectCall = true;
381     }
382   }
383 
384   if (!IsDirectCall) {
385     llvm::raw_svector_ostream SS(UseStr);
386     E->printPretty(SS, nullptr, getPrintingPolicy());
387   }
388 
389   Diag(E->getBeginLoc(), diag::err_counted_by_on_incomplete_type_on_use)
390       << IsDirectCall << UseStr << T << PointeeTy
391       << CATy->getAttributeName(/*WithMacroPrefix=*/true) << CATy->isOrNull()
392       << E->getSourceRange();
393 
394   EmitIncompleteCountedByPointeeNotes(*this, CATy, IncompleteTyDecl);
395   return false;
396 }
397 
398 } // namespace clang
399