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
18namespace clang {
19
20static CountAttributedType::DynamicCountPointerKind
21getCountAttrKind(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
29static 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>(Val: RD->getParent());
38 if (!Parent)
39 break;
40 RD = Parent;
41 }
42 return RD;
43}
44
45enum class CountedByInvalidPointeeTypeKind {
46 INCOMPLETE,
47 SIZELESS,
48 FUNCTION,
49 FLEXIBLE_ARRAY_MEMBER,
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(Loc: FD->getBeginLoc(), DiagID: 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(Loc: FD->getBeginLoc(),
68 DiagID: 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(Loc: FD->getBeginLoc(),
74 DiagID: 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(Context: getASTContext(), D: FD, Ty: FieldTy,
83 StrictFlexArraysLevel, IgnoreTemplateOrMacroSubstitution: true)) {
84 Diag(Loc: FD->getBeginLoc(),
85 DiagID: 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(T: 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(Loc: FD->getBeginLoc(), DiagID: diag::ext_gnu_counted_by_void_ptr) << Kind;
146 Diag(Loc: FD->getBeginLoc(), DiagID: diag::note_gnu_counted_by_void_ptr_use_sized_by)
147 << Kind;
148 assert(InvalidTypeKind == CountedByInvalidPointeeTypeKind::VALID);
149 } else {
150 InvalidTypeKind = CountedByInvalidPointeeTypeKind::INCOMPLETE;
151 }
152 } else if (PointeeTy->isSizelessType()) {
153 InvalidTypeKind = CountedByInvalidPointeeTypeKind::SIZELESS;
154 } else if (PointeeTy->isFunctionType()) {
155 InvalidTypeKind = CountedByInvalidPointeeTypeKind::FUNCTION;
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 }
166 InvalidTypeKind = CountedByInvalidPointeeTypeKind::FLEXIBLE_ARRAY_MEMBER;
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(Loc: 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(Loc: E->getBeginLoc(), DiagID: diag::err_count_attr_argument_not_integer)
183 << Kind << E->getSourceRange();
184 return true;
185 }
186
187 auto *DRE = dyn_cast<DeclRefExpr>(Val: E);
188 if (!DRE) {
189 Diag(Loc: E->getBeginLoc(),
190 DiagID: 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>(Val: CountDecl);
197 if (auto *IFD = dyn_cast<IndirectFieldDecl>(Val: CountDecl)) {
198 CountFD = IFD->getAnonField();
199 }
200 if (!CountFD) {
201 Diag(Loc: E->getBeginLoc(), DiagID: diag::err_count_attr_must_be_in_structure)
202 << CountDecl << Kind << E->getSourceRange();
203
204 Diag(Loc: CountDecl->getBeginLoc(),
205 DiagID: 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(Loc: CountFD->getBeginLoc(), DiagID: 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`.
219 auto *RD = GetEnclosingNamedOrTopAnonRecord(FD);
220 auto *CountRD = GetEnclosingNamedOrTopAnonRecord(FD: CountFD);
221
222 if (RD != CountRD) {
223 Diag(Loc: E->getBeginLoc(), DiagID: diag::err_count_attr_param_not_in_same_struct)
224 << CountFD << Kind << FieldTy->isArrayType() << E->getSourceRange();
225 Diag(Loc: CountFD->getBeginLoc(),
226 DiagID: diag::note_flexible_array_counted_by_attr_field)
227 << CountFD << CountFD->getSourceRange();
228 return true;
229 }
230 }
231 return false;
232}
233
234static void EmitIncompleteCountedByPointeeNotes(Sema &S,
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(Loc: IncompleteTyDecl->getBeginLoc(),
252 DiagID: 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(Loc: AttrSrcRange.getBegin(), DiagID: diag::note_counted_by_consider_using_sized_by)
269 << CATy->isOrNull() << AttrSrcRange;
270}
271
272static std::tuple<const CountAttributedType *, QualType>
273GetCountedByAttrOnIncompletePointee(QualType Ty, NamedDecl **ND) {
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(Def: 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.
313static bool CheckAssignmentToCountAttrPtrWithIncompletePointeeTy(
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(Ty: LHSTy, ND: &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, DiagID: diag::err_counted_by_on_incomplete_type_on_assign)
333 << static_cast<int>(Action) << AssigneeStr << (AssigneeStr.size() > 0)
334 << isa<ImplicitValueInitExpr>(Val: 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
342bool Sema::BoundsSafetyCheckAssignmentToCountAttrPtr(
343 QualType LHSTy, Expr *RHSExpr, AssignmentAction Action, SourceLocation Loc,
344 const ValueDecl *Assignee, bool ShowFullyQualifiedAssigneeName) {
345 return CheckAssignmentToCountAttrPtrWithIncompletePointeeTy(
346 S&: *this, LHSTy, RHSExpr, Action, Loc, Assignee,
347 ShowFullyQualifiedAssigneeName);
348}
349
350bool Sema::BoundsSafetyCheckInitialization(const InitializedEntity &Entity,
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 &&
365 Entity.getKind() != InitializedEntity::EK_Variable) {
366
367 if (!CheckAssignmentToCountAttrPtrWithIncompletePointeeTy(
368 S&: *this, LHSTy: LHSType, RHSExpr, Action, Loc: SL,
369 Assignee: dyn_cast_or_null<ValueDecl>(Val: Entity.getDecl()),
370 /*ShowFullQualifiedAssigneeName=*/ShowFullyQualifiedAssigneeName: true)) {
371 return false;
372 }
373 }
374
375 return true;
376}
377
378bool Sema::BoundsSafetyCheckUseOfCountAttrPtr(const Expr *E) {
379 QualType T = E->getType();
380 if (!T->isPointerType())
381 return true;
382
383 NamedDecl *IncompleteTyDecl = nullptr;
384 auto [CATy, PointeeTy] =
385 GetCountedByAttrOnIncompletePointee(Ty: T, ND: &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>(Val: 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(OS&: SS, Helper: nullptr, Policy: getPrintingPolicy());
404 }
405
406 Diag(Loc: E->getBeginLoc(), DiagID: 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(S&: *this, CATy, IncompleteTyDecl);
412 return false;
413}
414
415} // namespace clang
416