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