clang 22.0.0git
SemaBoundsSafety.cpp
Go to the documentation of this file.
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"
16#include "clang/Sema/Sema.h"
17
18namespace clang {
19
21getCountAttrKind(bool CountInBytes, bool OrNull) {
22 if (CountInBytes)
27}
28
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
50 VALID,
51};
52
53bool 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 =
81 if (FieldTy->isArrayType() &&
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 =
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.
138 } else if (PointeeTy->isSizelessType()) {
140 } else if (PointeeTy->isFunctionType()) {
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 }
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`.
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
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
258static std::tuple<const CountAttributedType *, QualType>
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.
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
326 QualType LHSTy, Expr *RHSExpr, AssignmentAction Action, SourceLocation Loc,
327 const ValueDecl *Assignee, bool ShowFullyQualifiedAssigneeName) {
329 *this, LHSTy, RHSExpr, Action, Loc, Assignee,
330 ShowFullyQualifiedAssigneeName);
331}
332
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 &&
349
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
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
NodeId Parent
Definition: ASTDiff.cpp:191
enum clang::sema::@1840::IndirectLocalPathEntry::EntryKind Kind
Expr * E
SourceLocation Loc
Definition: SemaObjC.cpp:754
__device__ int
const ArrayType * getAsArrayType(QualType T) const
Type Query functions.
Represents an array type, per C99 6.7.5.2 - Array Declarators.
Definition: TypeBase.h:3738
QualType getElementType() const
Definition: TypeBase.h:3750
Represents a sugar type with __counted_by or __sized_by annotations, including their _or_null variant...
Definition: TypeBase.h:3454
Expr * getCountExpr() const
Definition: TypeBase.h:3480
static bool isFlexibleArrayMemberLike(const ASTContext &Context, const Decl *D, QualType Ty, LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel, bool IgnoreTemplateOrMacroSubstitution)
Whether it resembles a flexible array member.
Definition: DeclBase.cpp:437
SourceLocation getLocation() const
Definition: DeclBase.h:439
SourceLocation getBeginLoc() const LLVM_READONLY
Definition: DeclBase.h:431
SourceLocation getBeginLoc() const LLVM_READONLY
Definition: Decl.h:830
This represents one expression.
Definition: Expr.h:112
Represents a member of a struct/union/class.
Definition: Decl.h:3157
SourceRange getSourceRange() const override LLVM_READONLY
Source range that this declaration covers.
Definition: Decl.cpp:4767
const RecordDecl * getParent() const
Returns the parent of this field declaration, which is the struct in which this field is defined.
Definition: Decl.h:3393
Describes the kind of initialization being performed, along with location information for tokens rela...
Describes an entity that is being initialized.
EntityKind getKind() const
Determine the kind of initialization.
ValueDecl * getDecl() const
Retrieve the variable, parameter, or field being initialized.
Definition: SemaInit.cpp:3701
@ EK_Variable
The entity being initialized is a variable.
@ IncompleteOnly
Any trailing array member of undefined size is a FAM.
This represents a decl that may have a name.
Definition: Decl.h:273
std::string getQualifiedNameAsString() const
Definition: Decl.cpp:1680
std::string getNameAsString() const
Get a human-readable name for the declaration, even if it is one of the special kinds of names (C++ c...
Definition: Decl.h:316
A (possibly-)qualified type.
Definition: TypeBase.h:937
Represents a struct/union/class.
Definition: Decl.h:4309
SemaDiagnosticBuilder Diag(SourceLocation Loc, unsigned DiagID, bool DeferHint=false)
Emit a diagnostic.
Definition: SemaBase.cpp:61
Sema - This implements semantic analysis and AST building for C.
Definition: Sema.h:850
bool BoundsSafetyCheckAssignmentToCountAttrPtr(QualType LHSTy, Expr *RHSExpr, AssignmentAction Action, SourceLocation Loc, const ValueDecl *Assignee, bool ShowFullyQualifiedAssigneeName)
Perform Bounds Safety Semantic checks for assigning to a __counted_by or __counted_by_or_null pointer...
bool BoundsSafetyCheckUseOfCountAttrPtr(const Expr *E)
Perform Bounds Safety semantic checks for uses of invalid uses counted_by or counted_by_or_null point...
ASTContext & getASTContext() const
Definition: Sema.h:918
PrintingPolicy getPrintingPolicy() const
Retrieve a suitable printing policy for diagnostics.
Definition: Sema.h:1184
const LangOptions & getLangOpts() const
Definition: Sema.h:911
bool CheckCountedByAttrOnField(FieldDecl *FD, Expr *E, bool CountInBytes, bool OrNull)
Check if applying the specified attribute variant from the "counted by" family of attributes to Field...
bool BoundsSafetyCheckInitialization(const InitializedEntity &Entity, const InitializationKind &Kind, AssignmentAction Action, QualType LHSType, Expr *RHSExpr)
Perform Bounds Safety Semantic checks for initializing a Bounds Safety pointer.
Encodes a location in the source.
A trivial tuple used to represent a source range.
SourceLocation getBegin() const
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
Definition: Stmt.cpp:334
bool isUnion() const
Definition: Decl.h:3919
bool isSizelessType() const
As an extension, we classify types as one of "sized" or "sizeless"; every type is one or the other.
Definition: Type.cpp:2572
bool isAlwaysIncompleteType() const
Definition: Type.cpp:2520
bool isPointerType() const
Definition: TypeBase.h:8580
QualType getPointeeType() const
If this is a pointer, ObjC object pointer, or block pointer, this returns the respective pointee.
Definition: Type.cpp:752
bool isFunctionType() const
Definition: TypeBase.h:8576
bool isStructureTypeWithFlexibleArrayMember() const
Definition: Type.cpp:684
const T * getAs() const
Member-template getAs<specific type>'.
Definition: TypeBase.h:9159
Represent the declaration of a variable (in which case it is an lvalue) a function (in which case it ...
Definition: Decl.h:711
QualType getType() const
Definition: Decl.h:722
The JSON file list parser is used to communicate input to InstallAPI.
static CountAttributedType::DynamicCountPointerKind getCountAttrKind(bool CountInBytes, bool OrNull)
CountedByInvalidPointeeTypeKind
static bool CheckAssignmentToCountAttrPtrWithIncompletePointeeTy(Sema &S, QualType LHSTy, Expr *RHSExpr, AssignmentAction Action, SourceLocation Loc, const ValueDecl *Assignee, bool ShowFullyQualifiedAssigneeName)
Perform Checks for assigning to a __counted_by or __counted_by_or_null pointer type.
static const RecordDecl * GetEnclosingNamedOrTopAnonRecord(const FieldDecl *FD)
static void EmitIncompleteCountedByPointeeNotes(Sema &S, const CountAttributedType *CATy, NamedDecl *IncompleteTyDecl)
static std::tuple< const CountAttributedType *, QualType > GetCountedByAttrOnIncompletePointee(QualType Ty, NamedDecl **ND)
AssignmentAction
Definition: Sema.h:213
const FunctionProtoType * T