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 | |
18 | namespace clang { |
19 | |
20 | static CountAttributedType::DynamicCountPointerKind |
21 | getCountAttrKind(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 | |
29 | static 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>(RD->getParent()); |
38 | if (!Parent) |
39 | break; |
40 | RD = Parent; |
41 | } |
42 | return RD; |
43 | } |
44 | |
45 | enum class CountedByInvalidPointeeTypeKind { |
46 | INCOMPLETE, |
47 | SIZELESS, |
48 | FUNCTION, |
49 | FLEXIBLE_ARRAY_MEMBER, |
50 | VALID, |
51 | }; |
52 | |
53 | bool 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 = |
80 | LangOptions::StrictFlexArraysLevelKind::IncompleteOnly; |
81 | if (FieldTy->isArrayType() && |
82 | !Decl::isFlexibleArrayMemberLike(Context: getASTContext(), D: FD, Ty: FieldTy, |
83 | StrictFlexArraysLevel, IgnoreTemplateOrMacroSubstitution: 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 = |
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`) 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. |
137 | InvalidTypeKind = CountedByInvalidPointeeTypeKind::INCOMPLETE; |
138 | } else if (PointeeTy->isSizelessType()) { |
139 | InvalidTypeKind = CountedByInvalidPointeeTypeKind::SIZELESS; |
140 | } else if (PointeeTy->isFunctionType()) { |
141 | InvalidTypeKind = CountedByInvalidPointeeTypeKind::FUNCTION; |
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 | } |
152 | InvalidTypeKind = CountedByInvalidPointeeTypeKind::FLEXIBLE_ARRAY_MEMBER; |
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>(Val: 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>(Val: CountDecl); |
183 | if (auto *IFD = dyn_cast<IndirectFieldDecl>(Val: 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`. |
205 | auto *RD = GetEnclosingNamedOrTopAnonRecord(FD); |
206 | auto *CountRD = GetEnclosingNamedOrTopAnonRecord(FD: 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 | |
220 | static void EmitIncompleteCountedByPointeeNotes(Sema &S, |
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 | |
258 | static std::tuple<const CountAttributedType *, QualType> |
259 | GetCountedByAttrOnIncompletePointee(QualType Ty, NamedDecl **ND) { |
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. |
296 | static bool CheckAssignmentToCountAttrPtrWithIncompletePointeeTy( |
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 | |
325 | bool Sema::BoundsSafetyCheckAssignmentToCountAttrPtr( |
326 | QualType LHSTy, Expr *RHSExpr, AssignmentAction Action, SourceLocation Loc, |
327 | const ValueDecl *Assignee, bool ShowFullyQualifiedAssigneeName) { |
328 | return CheckAssignmentToCountAttrPtrWithIncompletePointeeTy( |
329 | S&: *this, LHSTy, RHSExpr, Action, Loc, Assignee, |
330 | ShowFullyQualifiedAssigneeName); |
331 | } |
332 | |
333 | bool Sema::BoundsSafetyCheckInitialization(const InitializedEntity &Entity, |
334 | const InitializationKind &Kind, |
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 && |
348 | Entity.getKind() != InitializedEntity::EK_Variable) { |
349 | |
350 | if (!CheckAssignmentToCountAttrPtrWithIncompletePointeeTy( |
351 | S&: *this, LHSTy: LHSType, RHSExpr, Action, Loc: SL, |
352 | Assignee: dyn_cast_or_null<ValueDecl>(Val: Entity.getDecl()), |
353 | /*ShowFullQualifiedAssigneeName=*/ShowFullyQualifiedAssigneeName: true)) { |
354 | return false; |
355 | } |
356 | } |
357 | |
358 | return true; |
359 | } |
360 | |
361 | bool Sema::BoundsSafetyCheckUseOfCountAttrPtr(const Expr *E) { |
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>(Val: 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 |
399 | |