Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/clang/lib/Sema/SemaBoundsSafety.cpp
35234 views
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>(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(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(getASTContext(), FD, FieldTy,
83
StrictFlexArraysLevel, 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(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(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(E->getBeginLoc(), diag::err_count_attr_argument_not_integer)
140
<< Kind << E->getSourceRange();
141
return true;
142
}
143
144
auto *DRE = dyn_cast<DeclRefExpr>(E);
145
if (!DRE) {
146
Diag(E->getBeginLoc(),
147
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>(CountDecl);
154
if (auto *IFD = dyn_cast<IndirectFieldDecl>(CountDecl)) {
155
CountFD = IFD->getAnonField();
156
}
157
if (!CountFD) {
158
Diag(E->getBeginLoc(), diag::err_count_attr_must_be_in_structure)
159
<< CountDecl << Kind << E->getSourceRange();
160
161
Diag(CountDecl->getBeginLoc(),
162
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(CountFD->getBeginLoc(), 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(CountFD);
178
179
if (RD != CountRD) {
180
Diag(E->getBeginLoc(), diag::err_count_attr_param_not_in_same_struct)
181
<< CountFD << Kind << FieldTy->isArrayType() << E->getSourceRange();
182
Diag(CountFD->getBeginLoc(),
183
diag::note_flexible_array_counted_by_attr_field)
184
<< CountFD << CountFD->getSourceRange();
185
return true;
186
}
187
}
188
189
Decls.push_back(TypeCoupledDeclRefInfo(CountFD, /*IsDref*/ false));
190
return false;
191
}
192
193
} // namespace clang
194
195