xref: /llvm-project/clang/lib/Sema/SemaBoundsSafety.cpp (revision 1119a0805009501691538a28aeb9bb18b3ff911f)
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(FieldDecl *FD, Expr *E, bool CountInBytes,
52                                      bool OrNull) {
53   // Check the context the attribute is used in
54 
55   unsigned Kind = getCountAttrKind(CountInBytes, OrNull);
56 
57   if (FD->getParent()->isUnion()) {
58     Diag(FD->getBeginLoc(), diag::err_count_attr_in_union)
59         << Kind << FD->getSourceRange();
60     return true;
61   }
62 
63   const auto FieldTy = FD->getType();
64   if (FieldTy->isArrayType() && (CountInBytes || OrNull)) {
65     Diag(FD->getBeginLoc(),
66          diag::err_count_attr_not_on_ptr_or_flexible_array_member)
67         << Kind << FD->getLocation() << /* suggest counted_by */ 1;
68     return true;
69   }
70   if (!FieldTy->isArrayType() && !FieldTy->isPointerType()) {
71     Diag(FD->getBeginLoc(),
72          diag::err_count_attr_not_on_ptr_or_flexible_array_member)
73         << Kind << FD->getLocation() << /* do not suggest counted_by */ 0;
74     return true;
75   }
76 
77   LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
78       LangOptions::StrictFlexArraysLevelKind::IncompleteOnly;
79   if (FieldTy->isArrayType() &&
80       !Decl::isFlexibleArrayMemberLike(getASTContext(), FD, FieldTy,
81                                        StrictFlexArraysLevel, true)) {
82     Diag(FD->getBeginLoc(),
83          diag::err_counted_by_attr_on_array_not_flexible_array_member)
84         << Kind << FD->getLocation();
85     return true;
86   }
87 
88   CountedByInvalidPointeeTypeKind InvalidTypeKind =
89       CountedByInvalidPointeeTypeKind::VALID;
90   QualType PointeeTy;
91   int SelectPtrOrArr = 0;
92   if (FieldTy->isPointerType()) {
93     PointeeTy = FieldTy->getPointeeType();
94     SelectPtrOrArr = 0;
95   } else {
96     assert(FieldTy->isArrayType());
97     const ArrayType *AT = getASTContext().getAsArrayType(FieldTy);
98     PointeeTy = AT->getElementType();
99     SelectPtrOrArr = 1;
100   }
101   // Note: The `Decl::isFlexibleArrayMemberLike` check earlier on means
102   // only `PointeeTy->isStructureTypeWithFlexibleArrayMember()` is reachable
103   // when `FieldTy->isArrayType()`.
104   bool ShouldWarn = false;
105   if (PointeeTy->isIncompleteType() && !CountInBytes) {
106     InvalidTypeKind = CountedByInvalidPointeeTypeKind::INCOMPLETE;
107   } else if (PointeeTy->isSizelessType()) {
108     InvalidTypeKind = CountedByInvalidPointeeTypeKind::SIZELESS;
109   } else if (PointeeTy->isFunctionType()) {
110     InvalidTypeKind = CountedByInvalidPointeeTypeKind::FUNCTION;
111   } else if (PointeeTy->isStructureTypeWithFlexibleArrayMember()) {
112     if (FieldTy->isArrayType() && !getLangOpts().BoundsSafety) {
113       // This is a workaround for the Linux kernel that has already adopted
114       // `counted_by` on a FAM where the pointee is a struct with a FAM. This
115       // should be an error because computing the bounds of the array cannot be
116       // done correctly without manually traversing every struct object in the
117       // array at runtime. To allow the code to be built this error is
118       // downgraded to a warning.
119       ShouldWarn = true;
120     }
121     InvalidTypeKind = CountedByInvalidPointeeTypeKind::FLEXIBLE_ARRAY_MEMBER;
122   }
123 
124   if (InvalidTypeKind != CountedByInvalidPointeeTypeKind::VALID) {
125     unsigned DiagID = ShouldWarn
126                           ? diag::warn_counted_by_attr_elt_type_unknown_size
127                           : diag::err_counted_by_attr_pointee_unknown_size;
128     Diag(FD->getBeginLoc(), DiagID)
129         << SelectPtrOrArr << PointeeTy << (int)InvalidTypeKind
130         << (ShouldWarn ? 1 : 0) << Kind << FD->getSourceRange();
131     return true;
132   }
133 
134   // Check the expression
135 
136   if (!E->getType()->isIntegerType() || E->getType()->isBooleanType()) {
137     Diag(E->getBeginLoc(), diag::err_count_attr_argument_not_integer)
138         << Kind << E->getSourceRange();
139     return true;
140   }
141 
142   auto *DRE = dyn_cast<DeclRefExpr>(E);
143   if (!DRE) {
144     Diag(E->getBeginLoc(),
145          diag::err_count_attr_only_support_simple_decl_reference)
146         << Kind << E->getSourceRange();
147     return true;
148   }
149 
150   auto *CountDecl = DRE->getDecl();
151   FieldDecl *CountFD = dyn_cast<FieldDecl>(CountDecl);
152   if (auto *IFD = dyn_cast<IndirectFieldDecl>(CountDecl)) {
153     CountFD = IFD->getAnonField();
154   }
155   if (!CountFD) {
156     Diag(E->getBeginLoc(), diag::err_count_attr_must_be_in_structure)
157         << CountDecl << Kind << E->getSourceRange();
158 
159     Diag(CountDecl->getBeginLoc(),
160          diag::note_flexible_array_counted_by_attr_field)
161         << CountDecl << CountDecl->getSourceRange();
162     return true;
163   }
164 
165   if (FD->getParent() != CountFD->getParent()) {
166     if (CountFD->getParent()->isUnion()) {
167       Diag(CountFD->getBeginLoc(), diag::err_count_attr_refer_to_union)
168           << Kind << CountFD->getSourceRange();
169       return true;
170     }
171     // Whether CountRD is an anonymous struct is not determined at this
172     // point. Thus, an additional diagnostic in case it's not anonymous struct
173     // is done later in `Parser::ParseStructDeclaration`.
174     auto *RD = GetEnclosingNamedOrTopAnonRecord(FD);
175     auto *CountRD = GetEnclosingNamedOrTopAnonRecord(CountFD);
176 
177     if (RD != CountRD) {
178       Diag(E->getBeginLoc(), diag::err_count_attr_param_not_in_same_struct)
179           << CountFD << Kind << FieldTy->isArrayType() << E->getSourceRange();
180       Diag(CountFD->getBeginLoc(),
181            diag::note_flexible_array_counted_by_attr_field)
182           << CountFD << CountFD->getSourceRange();
183       return true;
184     }
185   }
186   return false;
187 }
188 
189 } // namespace clang
190