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/Sema/Sema.h" |
15 | |
16 | namespace clang { |
17 | |
18 | static CountAttributedType::DynamicCountPointerKind |
19 | getCountAttrKind(bool CountInBytes, bool OrNull) { |
20 | if (CountInBytes) |
21 | return OrNull ? CountAttributedType::SizedByOrNull |
22 | : CountAttributedType::SizedBy; |
23 | return OrNull ? CountAttributedType::CountedByOrNull |
24 | : CountAttributedType::CountedBy; |
25 | } |
26 | |
27 | static const RecordDecl *GetEnclosingNamedOrTopAnonRecord(const FieldDecl *FD) { |
28 | const auto *RD = FD->getParent(); |
29 | // An unnamed struct is anonymous struct only if it's not instantiated. |
30 | // However, the struct may not be fully processed yet to determine |
31 | // whether it's anonymous or not. In that case, this function treats it as |
32 | // an anonymous struct and tries to find a named parent. |
33 | while (RD && (RD->isAnonymousStructOrUnion() || |
34 | (!RD->isCompleteDefinition() && RD->getName().empty()))) { |
35 | const auto *Parent = dyn_cast<RecordDecl>(Val: RD->getParent()); |
36 | if (!Parent) |
37 | break; |
38 | RD = Parent; |
39 | } |
40 | return RD; |
41 | } |
42 | |
43 | enum class CountedByInvalidPointeeTypeKind { |
44 | INCOMPLETE, |
45 | SIZELESS, |
46 | FUNCTION, |
47 | FLEXIBLE_ARRAY_MEMBER, |
48 | VALID, |
49 | }; |
50 | |
51 | bool Sema::CheckCountedByAttrOnField( |
52 | FieldDecl *FD, Expr *E, |
53 | llvm::SmallVectorImpl<TypeCoupledDeclRefInfo> &Decls, 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->isIncompleteType() && !CountInBytes) { |
108 | InvalidTypeKind = CountedByInvalidPointeeTypeKind::INCOMPLETE; |
109 | } else if (PointeeTy->isSizelessType()) { |
110 | InvalidTypeKind = CountedByInvalidPointeeTypeKind::SIZELESS; |
111 | } else if (PointeeTy->isFunctionType()) { |
112 | InvalidTypeKind = CountedByInvalidPointeeTypeKind::FUNCTION; |
113 | } else if (PointeeTy->isStructureTypeWithFlexibleArrayMember()) { |
114 | if (FieldTy->isArrayType() && !getLangOpts().BoundsSafety) { |
115 | // This is a workaround for the Linux kernel that has already adopted |
116 | // `counted_by` on a FAM where the pointee is a struct with a FAM. This |
117 | // should be an error because computing the bounds of the array cannot be |
118 | // done correctly without manually traversing every struct object in the |
119 | // array at runtime. To allow the code to be built this error is |
120 | // downgraded to a warning. |
121 | ShouldWarn = true; |
122 | } |
123 | InvalidTypeKind = CountedByInvalidPointeeTypeKind::FLEXIBLE_ARRAY_MEMBER; |
124 | } |
125 | |
126 | if (InvalidTypeKind != CountedByInvalidPointeeTypeKind::VALID) { |
127 | unsigned DiagID = ShouldWarn |
128 | ? diag::warn_counted_by_attr_elt_type_unknown_size |
129 | : diag::err_counted_by_attr_pointee_unknown_size; |
130 | Diag(Loc: FD->getBeginLoc(), DiagID) |
131 | << SelectPtrOrArr << PointeeTy << (int)InvalidTypeKind |
132 | << (ShouldWarn ? 1 : 0) << Kind << FD->getSourceRange(); |
133 | return true; |
134 | } |
135 | |
136 | // Check the expression |
137 | |
138 | if (!E->getType()->isIntegerType() || E->getType()->isBooleanType()) { |
139 | Diag(Loc: E->getBeginLoc(), DiagID: diag::err_count_attr_argument_not_integer) |
140 | << Kind << E->getSourceRange(); |
141 | return true; |
142 | } |
143 | |
144 | auto *DRE = dyn_cast<DeclRefExpr>(Val: E); |
145 | if (!DRE) { |
146 | Diag(Loc: E->getBeginLoc(), |
147 | DiagID: diag::err_count_attr_only_support_simple_decl_reference) |
148 | << Kind << E->getSourceRange(); |
149 | return true; |
150 | } |
151 | |
152 | auto *CountDecl = DRE->getDecl(); |
153 | FieldDecl *CountFD = dyn_cast<FieldDecl>(Val: CountDecl); |
154 | if (auto *IFD = dyn_cast<IndirectFieldDecl>(Val: CountDecl)) { |
155 | CountFD = IFD->getAnonField(); |
156 | } |
157 | if (!CountFD) { |
158 | Diag(Loc: E->getBeginLoc(), DiagID: diag::err_count_attr_must_be_in_structure) |
159 | << CountDecl << Kind << E->getSourceRange(); |
160 | |
161 | Diag(Loc: CountDecl->getBeginLoc(), |
162 | DiagID: diag::note_flexible_array_counted_by_attr_field) |
163 | << CountDecl << CountDecl->getSourceRange(); |
164 | return true; |
165 | } |
166 | |
167 | if (FD->getParent() != CountFD->getParent()) { |
168 | if (CountFD->getParent()->isUnion()) { |
169 | Diag(Loc: CountFD->getBeginLoc(), DiagID: diag::err_count_attr_refer_to_union) |
170 | << Kind << CountFD->getSourceRange(); |
171 | return true; |
172 | } |
173 | // Whether CountRD is an anonymous struct is not determined at this |
174 | // point. Thus, an additional diagnostic in case it's not anonymous struct |
175 | // is done later in `Parser::ParseStructDeclaration`. |
176 | auto *RD = GetEnclosingNamedOrTopAnonRecord(FD); |
177 | auto *CountRD = GetEnclosingNamedOrTopAnonRecord(FD: CountFD); |
178 | |
179 | if (RD != CountRD) { |
180 | Diag(Loc: E->getBeginLoc(), DiagID: diag::err_count_attr_param_not_in_same_struct) |
181 | << CountFD << Kind << FieldTy->isArrayType() << E->getSourceRange(); |
182 | Diag(Loc: CountFD->getBeginLoc(), |
183 | DiagID: diag::note_flexible_array_counted_by_attr_field) |
184 | << CountFD << CountFD->getSourceRange(); |
185 | return true; |
186 | } |
187 | } |
188 | |
189 | Decls.push_back(Elt: TypeCoupledDeclRefInfo(CountFD, /*IsDref*/ false)); |
190 | return false; |
191 | } |
192 | |
193 | } // namespace clang |
194 | |