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
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` in strict C mode) the attribute is disallowed by this method
136 // because we know the type can never be completed so there's no reason
137 // to allow it.
138 //
139 // Exception: void has an implicit size of 1 byte for pointer arithmetic
140 // (following GNU convention). Therefore, counted_by on void* is allowed
141 // and behaves equivalently to sized_by (treating the count as bytes).
142 bool IsVoidPtr = PointeeTy->isVoidType();
143 if (IsVoidPtr) {
144 // Emit a warning that this is a GNU extension.
145 Diag(FD->getBeginLoc(), diag::ext_gnu_counted_by_void_ptr) << Kind;
146 Diag(FD->getBeginLoc(), diag::note_gnu_counted_by_void_ptr_use_sized_by)
147 << Kind;
148 assert(InvalidTypeKind == CountedByInvalidPointeeTypeKind::VALID);
149 } else {
151 }
152 } else if (PointeeTy->isSizelessType()) {
154 } else if (PointeeTy->isFunctionType()) {
156 } else if (PointeeTy->isStructureTypeWithFlexibleArrayMember()) {
157 if (FieldTy->isArrayType() && !getLangOpts().BoundsSafety) {
158 // This is a workaround for the Linux kernel that has already adopted
159 // `counted_by` on a FAM where the pointee is a struct with a FAM. This
160 // should be an error because computing the bounds of the array cannot be
161 // done correctly without manually traversing every struct object in the
162 // array at runtime. To allow the code to be built this error is
163 // downgraded to a warning.
164 ShouldWarn = true;
165 }
167 }
168
169 if (InvalidTypeKind != CountedByInvalidPointeeTypeKind::VALID) {
170 unsigned DiagID = ShouldWarn
171 ? diag::warn_counted_by_attr_elt_type_unknown_size
172 : diag::err_counted_by_attr_pointee_unknown_size;
173 Diag(FD->getBeginLoc(), DiagID)
174 << SelectPtrOrArr << PointeeTy << (int)InvalidTypeKind
175 << (ShouldWarn ? 1 : 0) << Kind << FD->getSourceRange();
176 return true;
177 }
178
179 // Check the expression
180
181 if (!E->getType()->isIntegerType() || E->getType()->isBooleanType()) {
182 Diag(E->getBeginLoc(), diag::err_count_attr_argument_not_integer)
183 << Kind << E->getSourceRange();
184 return true;
185 }
186
187 auto *DRE = dyn_cast<DeclRefExpr>(E);
188 if (!DRE) {
189 Diag(E->getBeginLoc(),
190 diag::err_count_attr_only_support_simple_decl_reference)
191 << Kind << E->getSourceRange();
192 return true;
193 }
194
195 auto *CountDecl = DRE->getDecl();
196 FieldDecl *CountFD = dyn_cast<FieldDecl>(CountDecl);
197 if (auto *IFD = dyn_cast<IndirectFieldDecl>(CountDecl)) {
198 CountFD = IFD->getAnonField();
199 }
200 if (!CountFD) {
201 Diag(E->getBeginLoc(), diag::err_count_attr_must_be_in_structure)
202 << CountDecl << Kind << E->getSourceRange();
203
204 Diag(CountDecl->getBeginLoc(),
205 diag::note_flexible_array_counted_by_attr_field)
206 << CountDecl << CountDecl->getSourceRange();
207 return true;
208 }
209
210 if (FD->getParent() != CountFD->getParent()) {
211 if (CountFD->getParent()->isUnion()) {
212 Diag(CountFD->getBeginLoc(), diag::err_count_attr_refer_to_union)
213 << Kind << CountFD->getSourceRange();
214 return true;
215 }
216 // Whether CountRD is an anonymous struct is not determined at this
217 // point. Thus, an additional diagnostic in case it's not anonymous struct
218 // is done later in `Parser::ParseStructDeclaration`.
220 auto *CountRD = GetEnclosingNamedOrTopAnonRecord(CountFD);
221
222 if (RD != CountRD) {
223 Diag(E->getBeginLoc(), diag::err_count_attr_param_not_in_same_struct)
224 << CountFD << Kind << FieldTy->isArrayType() << E->getSourceRange();
225 Diag(CountFD->getBeginLoc(),
226 diag::note_flexible_array_counted_by_attr_field)
227 << CountFD << CountFD->getSourceRange();
228 return true;
229 }
230 }
231 return false;
232}
233
235 const CountAttributedType *CATy,
236 NamedDecl *IncompleteTyDecl) {
237 assert(IncompleteTyDecl == nullptr || isa<TypeDecl>(IncompleteTyDecl));
238
239 if (IncompleteTyDecl) {
240 // Suggest completing the pointee type if its a named typed (i.e.
241 // IncompleteTyDecl isn't nullptr). Suggest this first as it is more likely
242 // to be the correct fix.
243 //
244 // Note the `IncompleteTyDecl` type is the underlying type which might not
245 // be the same as `CATy->getPointeeType()` which could be a typedef.
246 //
247 // The diagnostic printed will be at the location of the underlying type but
248 // the diagnostic text will print the type of `CATy->getPointeeType()` which
249 // could be a typedef name rather than the underlying type. This is ok
250 // though because the diagnostic will print the underlying type name too.
251 S.Diag(IncompleteTyDecl->getBeginLoc(),
252 diag::note_counted_by_consider_completing_pointee_ty)
253 << CATy->getPointeeType();
254 }
255
256 // Suggest using __sized_by(_or_null) instead of __counted_by(_or_null) as
257 // __sized_by(_or_null) doesn't have the complete type restriction.
258 //
259 // We use the source range of the expression on the CountAttributedType as an
260 // approximation for the source range of the attribute. This isn't quite right
261 // but isn't easy to fix right now.
262 //
263 // TODO: Implement logic to find the relevant TypeLoc for the attribute and
264 // get the SourceRange from that (#113582).
265 //
266 // TODO: We should emit a fix-it here.
267 SourceRange AttrSrcRange = CATy->getCountExpr()->getSourceRange();
268 S.Diag(AttrSrcRange.getBegin(), diag::note_counted_by_consider_using_sized_by)
269 << CATy->isOrNull() << AttrSrcRange;
270}
271
272static std::tuple<const CountAttributedType *, QualType>
274 auto *CATy = Ty->getAs<CountAttributedType>();
275 // Incomplete pointee type is only a problem for
276 // counted_by/counted_by_or_null
277 if (!CATy || CATy->isCountInBytes())
278 return {};
279
280 auto PointeeTy = CATy->getPointeeType();
281 if (PointeeTy.isNull()) {
282 // Reachable if `CountAttributedType` wraps an IncompleteArrayType
283 return {};
284 }
285
286 if (!PointeeTy->isIncompleteType(ND))
287 return {};
288
289 if (PointeeTy->isVoidType())
290 return {};
291
292 return {CATy, PointeeTy};
293}
294
295/// Perform Checks for assigning to a `__counted_by` or
296/// `__counted_by_or_null` pointer type \param LHSTy where the pointee type
297/// is incomplete which is invalid.
298///
299/// \param S The Sema instance.
300/// \param LHSTy The type being assigned to. Checks will only be performed if
301/// the type is a `counted_by` or `counted_by_or_null ` pointer.
302/// \param RHSExpr The expression being assigned from.
303/// \param Action The type assignment being performed
304/// \param Loc The SourceLocation to use for error diagnostics
305/// \param Assignee The ValueDecl being assigned. This is used to compute
306/// the name of the assignee. If the assignee isn't known this can
307/// be set to nullptr.
308/// \param ShowFullyQualifiedAssigneeName If set to true when using \p
309/// Assignee to compute the name of the assignee use the fully
310/// qualified name, otherwise use the unqualified name.
311///
312/// \returns True iff no diagnostic where emitted, false otherwise.
314 Sema &S, QualType LHSTy, Expr *RHSExpr, AssignmentAction Action,
315 SourceLocation Loc, const ValueDecl *Assignee,
316 bool ShowFullyQualifiedAssigneeName) {
317 NamedDecl *IncompleteTyDecl = nullptr;
318 auto [CATy, PointeeTy] =
319 GetCountedByAttrOnIncompletePointee(LHSTy, &IncompleteTyDecl);
320 if (!CATy)
321 return true;
322
323 std::string AssigneeStr;
324 if (Assignee) {
325 if (ShowFullyQualifiedAssigneeName) {
326 AssigneeStr = Assignee->getQualifiedNameAsString();
327 } else {
328 AssigneeStr = Assignee->getNameAsString();
329 }
330 }
331
332 S.Diag(Loc, diag::err_counted_by_on_incomplete_type_on_assign)
333 << static_cast<int>(Action) << AssigneeStr << (AssigneeStr.size() > 0)
334 << isa<ImplicitValueInitExpr>(RHSExpr) << LHSTy
335 << CATy->getAttributeName(/*WithMacroPrefix=*/true) << PointeeTy
336 << CATy->isOrNull() << RHSExpr->getSourceRange();
337
338 EmitIncompleteCountedByPointeeNotes(S, CATy, IncompleteTyDecl);
339 return false; // check failed
340}
341
343 QualType LHSTy, Expr *RHSExpr, AssignmentAction Action, SourceLocation Loc,
344 const ValueDecl *Assignee, bool ShowFullyQualifiedAssigneeName) {
346 *this, LHSTy, RHSExpr, Action, Loc, Assignee,
347 ShowFullyQualifiedAssigneeName);
348}
349
351 const InitializationKind &Kind,
352 AssignmentAction Action,
353 QualType LHSType, Expr *RHSExpr) {
354 auto SL = Kind.getLocation();
355
356 // Note: We don't call `BoundsSafetyCheckAssignmentToCountAttrPtr` here
357 // because we need conditionalize what is checked. In downstream
358 // Clang `counted_by` is supported on variable definitions and in that
359 // implementation an error diagnostic will be emitted on the variable
360 // definition if the pointee is an incomplete type. To avoid warning about the
361 // same problem twice (once when the variable is defined, once when Sema
362 // checks the initializer) we skip checking the initializer if it's a
363 // variable.
364 if (Action == AssignmentAction::Initializing &&
366
368 *this, LHSType, RHSExpr, Action, SL,
369 dyn_cast_or_null<ValueDecl>(Entity.getDecl()),
370 /*ShowFullQualifiedAssigneeName=*/true)) {
371 return false;
372 }
373 }
374
375 return true;
376}
377
379 QualType T = E->getType();
380 if (!T->isPointerType())
381 return true;
382
383 NamedDecl *IncompleteTyDecl = nullptr;
384 auto [CATy, PointeeTy] =
385 GetCountedByAttrOnIncompletePointee(T, &IncompleteTyDecl);
386 if (!CATy)
387 return true;
388
389 // Generate a string for the diagnostic that describes the "use".
390 // The string is specialized for direct calls to produce a better
391 // diagnostic.
392 SmallString<64> UseStr;
393 bool IsDirectCall = false;
394 if (const auto *CE = dyn_cast<CallExpr>(E->IgnoreParens())) {
395 if (const auto *FD = CE->getDirectCallee()) {
396 UseStr = FD->getName();
397 IsDirectCall = true;
398 }
399 }
400
401 if (!IsDirectCall) {
402 llvm::raw_svector_ostream SS(UseStr);
403 E->printPretty(SS, nullptr, getPrintingPolicy());
404 }
405
406 Diag(E->getBeginLoc(), diag::err_counted_by_on_incomplete_type_on_use)
407 << IsDirectCall << UseStr << T << PointeeTy
408 << CATy->getAttributeName(/*WithMacroPrefix=*/true) << CATy->isOrNull()
409 << E->getSourceRange();
410
411 EmitIncompleteCountedByPointeeNotes(*this, CATy, IncompleteTyDecl);
412 return false;
413}
414
415} // namespace clang
const ArrayType * getAsArrayType(QualType T) const
Type Query functions.
Represents an array type, per C99 6.7.5.2 - Array Declarators.
Definition TypeBase.h:3722
QualType getElementType() const
Definition TypeBase.h:3734
Represents a sugar type with __counted_by or __sized_by annotations, including their _or_null variant...
Definition TypeBase.h:3436
Expr * getCountExpr() const
Definition TypeBase.h:3462
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:459
SourceLocation getLocation() const
Definition DeclBase.h:439
SourceLocation getBeginLoc() const LLVM_READONLY
Definition DeclBase.h:431
SourceLocation getBeginLoc() const LLVM_READONLY
Definition Decl.h:831
This represents one expression.
Definition Expr.h:112
Expr * IgnoreParens() LLVM_READONLY
Skip past any parentheses which might surround this expression until reaching a fixed point.
Definition Expr.cpp:3081
QualType getType() const
Definition Expr.h:144
Represents a member of a struct/union/class.
Definition Decl.h:3160
SourceRange getSourceRange() const override LLVM_READONLY
Source range that this declaration covers.
Definition Decl.cpp:4820
const RecordDecl * getParent() const
Returns the parent of this field declaration, which is the struct in which this field is defined.
Definition Decl.h:3396
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.
@ 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:274
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:317
A (possibly-)qualified type.
Definition TypeBase.h:937
Represents a struct/union/class.
Definition Decl.h:4312
SemaDiagnosticBuilder Diag(SourceLocation Loc, unsigned DiagID)
Emit a diagnostic.
Definition SemaBase.cpp:61
Sema - This implements semantic analysis and AST building for C.
Definition Sema.h:854
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:925
PrintingPolicy getPrintingPolicy() const
Retrieve a suitable printing policy for diagnostics.
Definition Sema.h:1191
const LangOptions & getLangOpts() const
Definition Sema.h:918
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
void printPretty(raw_ostream &OS, PrinterHelper *Helper, const PrintingPolicy &Policy, unsigned Indentation=0, StringRef NewlineSymbol="\n", const ASTContext *Context=nullptr) const
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
Definition Stmt.cpp:338
SourceLocation getBeginLoc() const LLVM_READONLY
Definition Stmt.cpp:350
bool isUnion() const
Definition Decl.h:3922
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:2567
bool isVoidType() const
Definition TypeBase.h:8871
bool isBooleanType() const
Definition TypeBase.h:9001
bool isAlwaysIncompleteType() const
Definition Type.cpp:2515
bool isIntegerType() const
isIntegerType() does not include complex integers (a GCC extension).
Definition TypeBase.h:8915
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:8511
bool isStructureTypeWithFlexibleArrayMember() const
Definition Type.cpp:684
const T * getAs() const
Member-template getAs<specific type>'.
Definition TypeBase.h:9091
Represent the declaration of a variable (in which case it is an lvalue) a function (in which case it ...
Definition Decl.h:712
QualType getType() const
Definition Decl.h:723
The JSON file list parser is used to communicate input to InstallAPI.
bool isa(CodeGen::Address addr)
Definition Address.h:330
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)
const FunctionProtoType * T
AssignmentAction
Definition Sema.h:214