clang 23.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 treated as anonymous struct at this point.
32 // A 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() || RD->getName().empty())) {
36 const auto *Parent = dyn_cast<RecordDecl>(RD->getParent());
37 if (!Parent)
38 break;
39 RD = Parent;
40 }
41 return RD;
42}
43
51
52bool Sema::CheckCountedByAttrOnField(FieldDecl *FD, Expr *E, bool CountInBytes,
53 bool OrNull) {
54 // Check the context the attribute is used in
55
56 unsigned Kind = getCountAttrKind(CountInBytes, OrNull);
57
58 if (FD->getParent()->isUnion()) {
59 Diag(FD->getBeginLoc(), diag::err_count_attr_in_union)
60 << Kind << FD->getSourceRange();
61 return true;
62 }
63
64 const auto FieldTy = FD->getType();
65 if (FieldTy->isArrayType() && (CountInBytes || OrNull)) {
66 Diag(FD->getBeginLoc(),
67 diag::err_count_attr_not_on_ptr_or_flexible_array_member)
68 << Kind << FD->getLocation() << /* suggest counted_by */ 1;
69 return true;
70 }
71 if (!FieldTy->isArrayType() && !FieldTy->isPointerType()) {
72 Diag(FD->getBeginLoc(),
73 diag::err_count_attr_not_on_ptr_or_flexible_array_member)
74 << Kind << FD->getLocation() << /* do not suggest counted_by */ 0;
75 return true;
76 }
77
78 LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
80 if (FieldTy->isArrayType() &&
82 StrictFlexArraysLevel, true)) {
83 Diag(FD->getBeginLoc(),
84 diag::err_counted_by_attr_on_array_not_flexible_array_member)
85 << Kind << FD->getLocation();
86 return true;
87 }
88
89 CountedByInvalidPointeeTypeKind InvalidTypeKind =
91 QualType PointeeTy;
92 int SelectPtrOrArr = 0;
93 if (FieldTy->isPointerType()) {
94 PointeeTy = FieldTy->getPointeeType();
95 SelectPtrOrArr = 0;
96 } else {
97 assert(FieldTy->isArrayType());
98 const ArrayType *AT = getASTContext().getAsArrayType(FieldTy);
99 PointeeTy = AT->getElementType();
100 SelectPtrOrArr = 1;
101 }
102 // Note: The `Decl::isFlexibleArrayMemberLike` check earlier on means
103 // only `PointeeTy->isStructureTypeWithFlexibleArrayMember()` is reachable
104 // when `FieldTy->isArrayType()`.
105 bool ShouldWarn = false;
106 if (PointeeTy->isAlwaysIncompleteType() && !CountInBytes) {
107 // In general using `counted_by` or `counted_by_or_null` on
108 // pointers where the pointee is an incomplete type are problematic. This is
109 // because it isn't possible to compute the pointer's bounds without knowing
110 // the pointee type size. At the same time it is common to forward declare
111 // types in header files.
112 //
113 // E.g.:
114 //
115 // struct Handle;
116 // struct Wrapper {
117 // size_t count;
118 // struct Handle* __counted_by(count) handles;
119 // }
120 //
121 // To allow the above code pattern but still prevent the pointee type from
122 // being incomplete in places where bounds checks are needed the following
123 // scheme is used:
124 //
125 // * When the pointee type might not always be an incomplete type (i.e.
126 // a type that is currently incomplete but might be completed later
127 // on in the translation unit) the attribute is allowed by this method
128 // but later uses of the FieldDecl are checked that the pointee type
129 // is complete see `BoundsSafetyCheckAssignmentToCountAttrPtr`,
130 // `BoundsSafetyCheckInitialization`, and
131 // `BoundsSafetyCheckUseOfCountAttrPtr`
132 //
133 // * When the pointee type is always an incomplete type (e.g.
134 // `void` in strict C mode) the attribute is disallowed by this method
135 // because we know the type can never be completed so there's no reason
136 // to allow it.
137 //
138 // Exception: void has an implicit size of 1 byte for pointer arithmetic
139 // (following GNU convention). Therefore, counted_by on void* is allowed
140 // and behaves equivalently to sized_by (treating the count as bytes).
141 bool IsVoidPtr = PointeeTy->isVoidType();
142 if (IsVoidPtr) {
143 // Emit a warning that this is a GNU extension.
144 Diag(FD->getBeginLoc(), diag::ext_gnu_counted_by_void_ptr) << Kind;
145 Diag(FD->getBeginLoc(), diag::note_gnu_counted_by_void_ptr_use_sized_by)
146 << Kind;
147 assert(InvalidTypeKind == CountedByInvalidPointeeTypeKind::VALID);
148 } else {
150 }
151 } else if (PointeeTy->isSizelessType()) {
153 } else if (PointeeTy->isFunctionType()) {
155 } else if (PointeeTy->isStructureTypeWithFlexibleArrayMember()) {
156 if (FieldTy->isArrayType() && !getLangOpts().BoundsSafety) {
157 // This is a workaround for the Linux kernel that has already adopted
158 // `counted_by` on a FAM where the pointee is a struct with a FAM. This
159 // should be an error because computing the bounds of the array cannot be
160 // done correctly without manually traversing every struct object in the
161 // array at runtime. To allow the code to be built this error is
162 // downgraded to a warning.
163 ShouldWarn = true;
164 }
166 }
167
168 if (InvalidTypeKind != CountedByInvalidPointeeTypeKind::VALID) {
169 unsigned DiagID = ShouldWarn
170 ? diag::warn_counted_by_attr_elt_type_unknown_size
171 : diag::err_counted_by_attr_pointee_unknown_size;
172 Diag(FD->getBeginLoc(), DiagID)
173 << SelectPtrOrArr << PointeeTy << (int)InvalidTypeKind
174 << (ShouldWarn ? 1 : 0) << Kind << FD->getSourceRange();
175 return true;
176 }
177
178 // Check the expression
179
180 if (!E->getType()->isIntegerType() || E->getType()->isBooleanType()) {
181 Diag(E->getBeginLoc(), diag::err_count_attr_argument_not_integer)
182 << Kind << E->getSourceRange();
183 return true;
184 }
185
186 auto *DRE = dyn_cast<DeclRefExpr>(E);
187 if (!DRE) {
188 Diag(E->getBeginLoc(),
189 diag::err_count_attr_only_support_simple_decl_reference)
190 << Kind << E->getSourceRange();
191 return true;
192 }
193
194 auto *CountDecl = DRE->getDecl();
195 FieldDecl *CountFD = dyn_cast<FieldDecl>(CountDecl);
196 if (auto *IFD = dyn_cast<IndirectFieldDecl>(CountDecl)) {
197 CountFD = IFD->getAnonField();
198 }
199 if (!CountFD) {
200 Diag(E->getBeginLoc(), diag::err_count_attr_must_be_in_structure)
201 << CountDecl << Kind << E->getSourceRange();
202
203 Diag(CountDecl->getBeginLoc(),
204 diag::note_flexible_array_counted_by_attr_field)
205 << CountDecl << CountDecl->getSourceRange();
206 return true;
207 }
208
209 if (FD->getParent() != CountFD->getParent()) {
210 if (CountFD->getParent()->isUnion()) {
211 Diag(CountFD->getBeginLoc(), diag::err_count_attr_refer_to_union)
212 << Kind << CountFD->getSourceRange();
213 return true;
214 }
215 // Whether CountRD is an anonymous struct is not determined at this
216 // point. Thus, an additional diagnostic in case it's not anonymous struct
217 // is done later in `Parser::ParseStructDeclaration`.
219 auto *CountRD = GetEnclosingNamedOrTopAnonRecord(CountFD);
220
221 if (RD != CountRD) {
222 Diag(E->getBeginLoc(), diag::err_count_attr_param_not_in_same_struct)
223 << CountFD << Kind << FieldTy->isArrayType() << E->getSourceRange();
224 Diag(CountFD->getBeginLoc(),
225 diag::note_flexible_array_counted_by_attr_field)
226 << CountFD << CountFD->getSourceRange();
227 return true;
228 }
229 }
230 return false;
231}
232
234 const CountAttributedType *CATy,
235 NamedDecl *IncompleteTyDecl) {
236 assert(IncompleteTyDecl == nullptr || isa<TypeDecl>(IncompleteTyDecl));
237
238 if (IncompleteTyDecl) {
239 // Suggest completing the pointee type if its a named typed (i.e.
240 // IncompleteTyDecl isn't nullptr). Suggest this first as it is more likely
241 // to be the correct fix.
242 //
243 // Note the `IncompleteTyDecl` type is the underlying type which might not
244 // be the same as `CATy->getPointeeType()` which could be a typedef.
245 //
246 // The diagnostic printed will be at the location of the underlying type but
247 // the diagnostic text will print the type of `CATy->getPointeeType()` which
248 // could be a typedef name rather than the underlying type. This is ok
249 // though because the diagnostic will print the underlying type name too.
250 S.Diag(IncompleteTyDecl->getBeginLoc(),
251 diag::note_counted_by_consider_completing_pointee_ty)
252 << CATy->getPointeeType();
253 }
254
255 // Suggest using __sized_by(_or_null) instead of __counted_by(_or_null) as
256 // __sized_by(_or_null) doesn't have the complete type restriction.
257 //
258 // We use the source range of the expression on the CountAttributedType as an
259 // approximation for the source range of the attribute. This isn't quite right
260 // but isn't easy to fix right now.
261 //
262 // TODO: Implement logic to find the relevant TypeLoc for the attribute and
263 // get the SourceRange from that (#113582).
264 //
265 // TODO: We should emit a fix-it here.
266 SourceRange AttrSrcRange = CATy->getCountExpr()->getSourceRange();
267 S.Diag(AttrSrcRange.getBegin(), diag::note_counted_by_consider_using_sized_by)
268 << CATy->isOrNull() << AttrSrcRange;
269}
270
271static std::tuple<const CountAttributedType *, QualType>
273 auto *CATy = Ty->getAs<CountAttributedType>();
274 // Incomplete pointee type is only a problem for
275 // counted_by/counted_by_or_null
276 if (!CATy || CATy->isCountInBytes())
277 return {};
278
279 auto PointeeTy = CATy->getPointeeType();
280 if (PointeeTy.isNull()) {
281 // Reachable if `CountAttributedType` wraps an IncompleteArrayType
282 return {};
283 }
284
285 if (!PointeeTy->isIncompleteType(ND))
286 return {};
287
288 if (PointeeTy->isVoidType())
289 return {};
290
291 return {CATy, PointeeTy};
292}
293
294/// Perform Checks for assigning to a `__counted_by` or
295/// `__counted_by_or_null` pointer type \param LHSTy where the pointee type
296/// is incomplete which is invalid.
297///
298/// \param S The Sema instance.
299/// \param LHSTy The type being assigned to. Checks will only be performed if
300/// the type is a `counted_by` or `counted_by_or_null ` pointer.
301/// \param RHSExpr The expression being assigned from.
302/// \param Action The type assignment being performed
303/// \param Loc The SourceLocation to use for error diagnostics
304/// \param Assignee The ValueDecl being assigned. This is used to compute
305/// the name of the assignee. If the assignee isn't known this can
306/// be set to nullptr.
307/// \param ShowFullyQualifiedAssigneeName If set to true when using \p
308/// Assignee to compute the name of the assignee use the fully
309/// qualified name, otherwise use the unqualified name.
310///
311/// \returns True iff no diagnostic where emitted, false otherwise.
313 Sema &S, QualType LHSTy, Expr *RHSExpr, AssignmentAction Action,
314 SourceLocation Loc, const ValueDecl *Assignee,
315 bool ShowFullyQualifiedAssigneeName) {
316 NamedDecl *IncompleteTyDecl = nullptr;
317 auto [CATy, PointeeTy] =
318 GetCountedByAttrOnIncompletePointee(LHSTy, &IncompleteTyDecl);
319 if (!CATy)
320 return true;
321
322 std::string AssigneeStr;
323 if (Assignee) {
324 if (ShowFullyQualifiedAssigneeName) {
325 AssigneeStr = Assignee->getQualifiedNameAsString();
326 } else {
327 AssigneeStr = Assignee->getNameAsString();
328 }
329 }
330
331 S.Diag(Loc, diag::err_counted_by_on_incomplete_type_on_assign)
332 << static_cast<int>(Action) << AssigneeStr << (AssigneeStr.size() > 0)
333 << isa<ImplicitValueInitExpr>(RHSExpr) << LHSTy
334 << CATy->getAttributeName(/*WithMacroPrefix=*/true) << PointeeTy
335 << CATy->isOrNull() << RHSExpr->getSourceRange();
336
337 EmitIncompleteCountedByPointeeNotes(S, CATy, IncompleteTyDecl);
338 return false; // check failed
339}
340
342 QualType LHSTy, Expr *RHSExpr, AssignmentAction Action, SourceLocation Loc,
343 const ValueDecl *Assignee, bool ShowFullyQualifiedAssigneeName) {
345 *this, LHSTy, RHSExpr, Action, Loc, Assignee,
346 ShowFullyQualifiedAssigneeName);
347}
348
350 const InitializationKind &Kind,
351 AssignmentAction Action,
352 QualType LHSType, Expr *RHSExpr) {
353 auto SL = Kind.getLocation();
354
355 // Note: We don't call `BoundsSafetyCheckAssignmentToCountAttrPtr` here
356 // because we need conditionalize what is checked. In downstream
357 // Clang `counted_by` is supported on variable definitions and in that
358 // implementation an error diagnostic will be emitted on the variable
359 // definition if the pointee is an incomplete type. To avoid warning about the
360 // same problem twice (once when the variable is defined, once when Sema
361 // checks the initializer) we skip checking the initializer if it's a
362 // variable.
363 if (Action == AssignmentAction::Initializing &&
365
367 *this, LHSType, RHSExpr, Action, SL,
368 dyn_cast_or_null<ValueDecl>(Entity.getDecl()),
369 /*ShowFullQualifiedAssigneeName=*/true)) {
370 return false;
371 }
372 }
373
374 return true;
375}
376
378 QualType T = E->getType();
379 if (!T->isPointerType())
380 return true;
381
382 NamedDecl *IncompleteTyDecl = nullptr;
383 auto [CATy, PointeeTy] =
384 GetCountedByAttrOnIncompletePointee(T, &IncompleteTyDecl);
385 if (!CATy)
386 return true;
387
388 // Generate a string for the diagnostic that describes the "use".
389 // The string is specialized for direct calls to produce a better
390 // diagnostic.
391 SmallString<64> UseStr;
392 bool IsDirectCall = false;
393 if (const auto *CE = dyn_cast<CallExpr>(E->IgnoreParens())) {
394 if (const auto *FD = CE->getDirectCallee()) {
395 UseStr = FD->getName();
396 IsDirectCall = true;
397 }
398 }
399
400 if (!IsDirectCall) {
401 llvm::raw_svector_ostream SS(UseStr);
402 E->printPretty(SS, nullptr, getPrintingPolicy());
403 }
404
405 Diag(E->getBeginLoc(), diag::err_counted_by_on_incomplete_type_on_use)
406 << IsDirectCall << UseStr << T << PointeeTy
407 << CATy->getAttributeName(/*WithMacroPrefix=*/true) << CATy->isOrNull()
408 << E->getSourceRange();
409
410 EmitIncompleteCountedByPointeeNotes(*this, CATy, IncompleteTyDecl);
411 return false;
412}
413
414} // 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:3784
QualType getElementType() const
Definition TypeBase.h:3796
Represents a sugar type with __counted_by or __sized_by annotations, including their _or_null variant...
Definition TypeBase.h:3498
Expr * getCountExpr() const
Definition TypeBase.h:3524
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:460
SourceLocation getLocation() const
Definition DeclBase.h:447
SourceLocation getBeginLoc() const LLVM_READONLY
Definition DeclBase.h:439
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:3093
QualType getType() const
Definition Expr.h:144
Represents a member of a struct/union/class.
Definition Decl.h:3178
SourceRange getSourceRange() const override LLVM_READONLY
Source range that this declaration covers.
Definition Decl.cpp:4821
const RecordDecl * getParent() const
Returns the parent of this field declaration, which is the struct in which this field is defined.
Definition Decl.h:3414
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:1681
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:4343
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:868
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:939
PrintingPolicy getPrintingPolicy() const
Retrieve a suitable printing policy for diagnostics.
Definition Sema.h:1212
const LangOptions & getLangOpts() const
Definition Sema.h:932
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:343
SourceLocation getBeginLoc() const LLVM_READONLY
Definition Stmt.cpp:355
bool isUnion() const
Definition Decl.h:3946
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:2661
bool isVoidType() const
Definition TypeBase.h:9048
bool isBooleanType() const
Definition TypeBase.h:9185
bool isAlwaysIncompleteType() const
Definition Type.cpp:2607
bool isIntegerType() const
isIntegerType() does not include complex integers (a GCC extension).
Definition TypeBase.h:9092
QualType getPointeeType() const
If this is a pointer, ObjC object pointer, or block pointer, this returns the respective pointee.
Definition Type.cpp:789
bool isFunctionType() const
Definition TypeBase.h:8678
bool isStructureTypeWithFlexibleArrayMember() const
Definition Type.cpp:721
const T * getAs() const
Member-template getAs<specific type>'.
Definition TypeBase.h:9275
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)
AssignmentAction
Definition Sema.h:216