xref: /freebsd-src/contrib/llvm-project/clang/lib/Sema/SemaBoundsSafety.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
1*0fca6ea1SDimitry Andric //===-- SemaBoundsSafety.cpp - Bounds Safety specific routines-*- C++ -*---===//
2*0fca6ea1SDimitry Andric //
3*0fca6ea1SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*0fca6ea1SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*0fca6ea1SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*0fca6ea1SDimitry Andric //
7*0fca6ea1SDimitry Andric //===----------------------------------------------------------------------===//
8*0fca6ea1SDimitry Andric /// \file
9*0fca6ea1SDimitry Andric /// This file declares semantic analysis functions specific to `-fbounds-safety`
10*0fca6ea1SDimitry Andric /// (Bounds Safety) and also its attributes when used without `-fbounds-safety`
11*0fca6ea1SDimitry Andric /// (e.g. `counted_by`)
12*0fca6ea1SDimitry Andric ///
13*0fca6ea1SDimitry Andric //===----------------------------------------------------------------------===//
14*0fca6ea1SDimitry Andric #include "clang/Sema/Sema.h"
15*0fca6ea1SDimitry Andric 
16*0fca6ea1SDimitry Andric namespace clang {
17*0fca6ea1SDimitry Andric 
18*0fca6ea1SDimitry Andric static CountAttributedType::DynamicCountPointerKind
19*0fca6ea1SDimitry Andric getCountAttrKind(bool CountInBytes, bool OrNull) {
20*0fca6ea1SDimitry Andric   if (CountInBytes)
21*0fca6ea1SDimitry Andric     return OrNull ? CountAttributedType::SizedByOrNull
22*0fca6ea1SDimitry Andric                   : CountAttributedType::SizedBy;
23*0fca6ea1SDimitry Andric   return OrNull ? CountAttributedType::CountedByOrNull
24*0fca6ea1SDimitry Andric                 : CountAttributedType::CountedBy;
25*0fca6ea1SDimitry Andric }
26*0fca6ea1SDimitry Andric 
27*0fca6ea1SDimitry Andric static const RecordDecl *GetEnclosingNamedOrTopAnonRecord(const FieldDecl *FD) {
28*0fca6ea1SDimitry Andric   const auto *RD = FD->getParent();
29*0fca6ea1SDimitry Andric   // An unnamed struct is anonymous struct only if it's not instantiated.
30*0fca6ea1SDimitry Andric   // However, the struct may not be fully processed yet to determine
31*0fca6ea1SDimitry Andric   // whether it's anonymous or not. In that case, this function treats it as
32*0fca6ea1SDimitry Andric   // an anonymous struct and tries to find a named parent.
33*0fca6ea1SDimitry Andric   while (RD && (RD->isAnonymousStructOrUnion() ||
34*0fca6ea1SDimitry Andric                 (!RD->isCompleteDefinition() && RD->getName().empty()))) {
35*0fca6ea1SDimitry Andric     const auto *Parent = dyn_cast<RecordDecl>(RD->getParent());
36*0fca6ea1SDimitry Andric     if (!Parent)
37*0fca6ea1SDimitry Andric       break;
38*0fca6ea1SDimitry Andric     RD = Parent;
39*0fca6ea1SDimitry Andric   }
40*0fca6ea1SDimitry Andric   return RD;
41*0fca6ea1SDimitry Andric }
42*0fca6ea1SDimitry Andric 
43*0fca6ea1SDimitry Andric enum class CountedByInvalidPointeeTypeKind {
44*0fca6ea1SDimitry Andric   INCOMPLETE,
45*0fca6ea1SDimitry Andric   SIZELESS,
46*0fca6ea1SDimitry Andric   FUNCTION,
47*0fca6ea1SDimitry Andric   FLEXIBLE_ARRAY_MEMBER,
48*0fca6ea1SDimitry Andric   VALID,
49*0fca6ea1SDimitry Andric };
50*0fca6ea1SDimitry Andric 
51*0fca6ea1SDimitry Andric bool Sema::CheckCountedByAttrOnField(
52*0fca6ea1SDimitry Andric     FieldDecl *FD, Expr *E,
53*0fca6ea1SDimitry Andric     llvm::SmallVectorImpl<TypeCoupledDeclRefInfo> &Decls, bool CountInBytes,
54*0fca6ea1SDimitry Andric     bool OrNull) {
55*0fca6ea1SDimitry Andric   // Check the context the attribute is used in
56*0fca6ea1SDimitry Andric 
57*0fca6ea1SDimitry Andric   unsigned Kind = getCountAttrKind(CountInBytes, OrNull);
58*0fca6ea1SDimitry Andric 
59*0fca6ea1SDimitry Andric   if (FD->getParent()->isUnion()) {
60*0fca6ea1SDimitry Andric     Diag(FD->getBeginLoc(), diag::err_count_attr_in_union)
61*0fca6ea1SDimitry Andric         << Kind << FD->getSourceRange();
62*0fca6ea1SDimitry Andric     return true;
63*0fca6ea1SDimitry Andric   }
64*0fca6ea1SDimitry Andric 
65*0fca6ea1SDimitry Andric   const auto FieldTy = FD->getType();
66*0fca6ea1SDimitry Andric   if (FieldTy->isArrayType() && (CountInBytes || OrNull)) {
67*0fca6ea1SDimitry Andric     Diag(FD->getBeginLoc(),
68*0fca6ea1SDimitry Andric          diag::err_count_attr_not_on_ptr_or_flexible_array_member)
69*0fca6ea1SDimitry Andric         << Kind << FD->getLocation() << /* suggest counted_by */ 1;
70*0fca6ea1SDimitry Andric     return true;
71*0fca6ea1SDimitry Andric   }
72*0fca6ea1SDimitry Andric   if (!FieldTy->isArrayType() && !FieldTy->isPointerType()) {
73*0fca6ea1SDimitry Andric     Diag(FD->getBeginLoc(),
74*0fca6ea1SDimitry Andric          diag::err_count_attr_not_on_ptr_or_flexible_array_member)
75*0fca6ea1SDimitry Andric         << Kind << FD->getLocation() << /* do not suggest counted_by */ 0;
76*0fca6ea1SDimitry Andric     return true;
77*0fca6ea1SDimitry Andric   }
78*0fca6ea1SDimitry Andric 
79*0fca6ea1SDimitry Andric   LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
80*0fca6ea1SDimitry Andric       LangOptions::StrictFlexArraysLevelKind::IncompleteOnly;
81*0fca6ea1SDimitry Andric   if (FieldTy->isArrayType() &&
82*0fca6ea1SDimitry Andric       !Decl::isFlexibleArrayMemberLike(getASTContext(), FD, FieldTy,
83*0fca6ea1SDimitry Andric                                        StrictFlexArraysLevel, true)) {
84*0fca6ea1SDimitry Andric     Diag(FD->getBeginLoc(),
85*0fca6ea1SDimitry Andric          diag::err_counted_by_attr_on_array_not_flexible_array_member)
86*0fca6ea1SDimitry Andric         << Kind << FD->getLocation();
87*0fca6ea1SDimitry Andric     return true;
88*0fca6ea1SDimitry Andric   }
89*0fca6ea1SDimitry Andric 
90*0fca6ea1SDimitry Andric   CountedByInvalidPointeeTypeKind InvalidTypeKind =
91*0fca6ea1SDimitry Andric       CountedByInvalidPointeeTypeKind::VALID;
92*0fca6ea1SDimitry Andric   QualType PointeeTy;
93*0fca6ea1SDimitry Andric   int SelectPtrOrArr = 0;
94*0fca6ea1SDimitry Andric   if (FieldTy->isPointerType()) {
95*0fca6ea1SDimitry Andric     PointeeTy = FieldTy->getPointeeType();
96*0fca6ea1SDimitry Andric     SelectPtrOrArr = 0;
97*0fca6ea1SDimitry Andric   } else {
98*0fca6ea1SDimitry Andric     assert(FieldTy->isArrayType());
99*0fca6ea1SDimitry Andric     const ArrayType *AT = getASTContext().getAsArrayType(FieldTy);
100*0fca6ea1SDimitry Andric     PointeeTy = AT->getElementType();
101*0fca6ea1SDimitry Andric     SelectPtrOrArr = 1;
102*0fca6ea1SDimitry Andric   }
103*0fca6ea1SDimitry Andric   // Note: The `Decl::isFlexibleArrayMemberLike` check earlier on means
104*0fca6ea1SDimitry Andric   // only `PointeeTy->isStructureTypeWithFlexibleArrayMember()` is reachable
105*0fca6ea1SDimitry Andric   // when `FieldTy->isArrayType()`.
106*0fca6ea1SDimitry Andric   bool ShouldWarn = false;
107*0fca6ea1SDimitry Andric   if (PointeeTy->isIncompleteType() && !CountInBytes) {
108*0fca6ea1SDimitry Andric     InvalidTypeKind = CountedByInvalidPointeeTypeKind::INCOMPLETE;
109*0fca6ea1SDimitry Andric   } else if (PointeeTy->isSizelessType()) {
110*0fca6ea1SDimitry Andric     InvalidTypeKind = CountedByInvalidPointeeTypeKind::SIZELESS;
111*0fca6ea1SDimitry Andric   } else if (PointeeTy->isFunctionType()) {
112*0fca6ea1SDimitry Andric     InvalidTypeKind = CountedByInvalidPointeeTypeKind::FUNCTION;
113*0fca6ea1SDimitry Andric   } else if (PointeeTy->isStructureTypeWithFlexibleArrayMember()) {
114*0fca6ea1SDimitry Andric     if (FieldTy->isArrayType() && !getLangOpts().BoundsSafety) {
115*0fca6ea1SDimitry Andric       // This is a workaround for the Linux kernel that has already adopted
116*0fca6ea1SDimitry Andric       // `counted_by` on a FAM where the pointee is a struct with a FAM. This
117*0fca6ea1SDimitry Andric       // should be an error because computing the bounds of the array cannot be
118*0fca6ea1SDimitry Andric       // done correctly without manually traversing every struct object in the
119*0fca6ea1SDimitry Andric       // array at runtime. To allow the code to be built this error is
120*0fca6ea1SDimitry Andric       // downgraded to a warning.
121*0fca6ea1SDimitry Andric       ShouldWarn = true;
122*0fca6ea1SDimitry Andric     }
123*0fca6ea1SDimitry Andric     InvalidTypeKind = CountedByInvalidPointeeTypeKind::FLEXIBLE_ARRAY_MEMBER;
124*0fca6ea1SDimitry Andric   }
125*0fca6ea1SDimitry Andric 
126*0fca6ea1SDimitry Andric   if (InvalidTypeKind != CountedByInvalidPointeeTypeKind::VALID) {
127*0fca6ea1SDimitry Andric     unsigned DiagID = ShouldWarn
128*0fca6ea1SDimitry Andric                           ? diag::warn_counted_by_attr_elt_type_unknown_size
129*0fca6ea1SDimitry Andric                           : diag::err_counted_by_attr_pointee_unknown_size;
130*0fca6ea1SDimitry Andric     Diag(FD->getBeginLoc(), DiagID)
131*0fca6ea1SDimitry Andric         << SelectPtrOrArr << PointeeTy << (int)InvalidTypeKind
132*0fca6ea1SDimitry Andric         << (ShouldWarn ? 1 : 0) << Kind << FD->getSourceRange();
133*0fca6ea1SDimitry Andric     return true;
134*0fca6ea1SDimitry Andric   }
135*0fca6ea1SDimitry Andric 
136*0fca6ea1SDimitry Andric   // Check the expression
137*0fca6ea1SDimitry Andric 
138*0fca6ea1SDimitry Andric   if (!E->getType()->isIntegerType() || E->getType()->isBooleanType()) {
139*0fca6ea1SDimitry Andric     Diag(E->getBeginLoc(), diag::err_count_attr_argument_not_integer)
140*0fca6ea1SDimitry Andric         << Kind << E->getSourceRange();
141*0fca6ea1SDimitry Andric     return true;
142*0fca6ea1SDimitry Andric   }
143*0fca6ea1SDimitry Andric 
144*0fca6ea1SDimitry Andric   auto *DRE = dyn_cast<DeclRefExpr>(E);
145*0fca6ea1SDimitry Andric   if (!DRE) {
146*0fca6ea1SDimitry Andric     Diag(E->getBeginLoc(),
147*0fca6ea1SDimitry Andric          diag::err_count_attr_only_support_simple_decl_reference)
148*0fca6ea1SDimitry Andric         << Kind << E->getSourceRange();
149*0fca6ea1SDimitry Andric     return true;
150*0fca6ea1SDimitry Andric   }
151*0fca6ea1SDimitry Andric 
152*0fca6ea1SDimitry Andric   auto *CountDecl = DRE->getDecl();
153*0fca6ea1SDimitry Andric   FieldDecl *CountFD = dyn_cast<FieldDecl>(CountDecl);
154*0fca6ea1SDimitry Andric   if (auto *IFD = dyn_cast<IndirectFieldDecl>(CountDecl)) {
155*0fca6ea1SDimitry Andric     CountFD = IFD->getAnonField();
156*0fca6ea1SDimitry Andric   }
157*0fca6ea1SDimitry Andric   if (!CountFD) {
158*0fca6ea1SDimitry Andric     Diag(E->getBeginLoc(), diag::err_count_attr_must_be_in_structure)
159*0fca6ea1SDimitry Andric         << CountDecl << Kind << E->getSourceRange();
160*0fca6ea1SDimitry Andric 
161*0fca6ea1SDimitry Andric     Diag(CountDecl->getBeginLoc(),
162*0fca6ea1SDimitry Andric          diag::note_flexible_array_counted_by_attr_field)
163*0fca6ea1SDimitry Andric         << CountDecl << CountDecl->getSourceRange();
164*0fca6ea1SDimitry Andric     return true;
165*0fca6ea1SDimitry Andric   }
166*0fca6ea1SDimitry Andric 
167*0fca6ea1SDimitry Andric   if (FD->getParent() != CountFD->getParent()) {
168*0fca6ea1SDimitry Andric     if (CountFD->getParent()->isUnion()) {
169*0fca6ea1SDimitry Andric       Diag(CountFD->getBeginLoc(), diag::err_count_attr_refer_to_union)
170*0fca6ea1SDimitry Andric           << Kind << CountFD->getSourceRange();
171*0fca6ea1SDimitry Andric       return true;
172*0fca6ea1SDimitry Andric     }
173*0fca6ea1SDimitry Andric     // Whether CountRD is an anonymous struct is not determined at this
174*0fca6ea1SDimitry Andric     // point. Thus, an additional diagnostic in case it's not anonymous struct
175*0fca6ea1SDimitry Andric     // is done later in `Parser::ParseStructDeclaration`.
176*0fca6ea1SDimitry Andric     auto *RD = GetEnclosingNamedOrTopAnonRecord(FD);
177*0fca6ea1SDimitry Andric     auto *CountRD = GetEnclosingNamedOrTopAnonRecord(CountFD);
178*0fca6ea1SDimitry Andric 
179*0fca6ea1SDimitry Andric     if (RD != CountRD) {
180*0fca6ea1SDimitry Andric       Diag(E->getBeginLoc(), diag::err_count_attr_param_not_in_same_struct)
181*0fca6ea1SDimitry Andric           << CountFD << Kind << FieldTy->isArrayType() << E->getSourceRange();
182*0fca6ea1SDimitry Andric       Diag(CountFD->getBeginLoc(),
183*0fca6ea1SDimitry Andric            diag::note_flexible_array_counted_by_attr_field)
184*0fca6ea1SDimitry Andric           << CountFD << CountFD->getSourceRange();
185*0fca6ea1SDimitry Andric       return true;
186*0fca6ea1SDimitry Andric     }
187*0fca6ea1SDimitry Andric   }
188*0fca6ea1SDimitry Andric 
189*0fca6ea1SDimitry Andric   Decls.push_back(TypeCoupledDeclRefInfo(CountFD, /*IsDref*/ false));
190*0fca6ea1SDimitry Andric   return false;
191*0fca6ea1SDimitry Andric }
192*0fca6ea1SDimitry Andric 
193*0fca6ea1SDimitry Andric } // namespace clang
194