416 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8/// \file9/// 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 18namespace clang {19 20static CountAttributedType::DynamicCountPointerKind21getCountAttrKind(bool CountInBytes, bool OrNull) {22 if (CountInBytes)23 return OrNull ? CountAttributedType::SizedByOrNull24 : CountAttributedType::SizedBy;25 return OrNull ? CountAttributedType::CountedByOrNull26 : CountAttributedType::CountedBy;27}28 29static 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 determine33 // whether it's anonymous or not. In that case, this function treats it as34 // 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 45enum class CountedByInvalidPointeeTypeKind {46 INCOMPLETE,47 SIZELESS,48 FUNCTION,49 FLEXIBLE_ARRAY_MEMBER,50 VALID,51};52 53bool Sema::CheckCountedByAttrOnField(FieldDecl *FD, Expr *E, bool CountInBytes,54 bool OrNull) {55 // Check the context the attribute is used in56 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 means104 // only `PointeeTy->isStructureTypeWithFlexibleArrayMember()` is reachable105 // when `FieldTy->isArrayType()`.106 bool ShouldWarn = false;107 if (PointeeTy->isAlwaysIncompleteType() && !CountInBytes) {108 // In general using `counted_by` or `counted_by_or_null` on109 // pointers where the pointee is an incomplete type are problematic. This is110 // because it isn't possible to compute the pointer's bounds without knowing111 // the pointee type size. At the same time it is common to forward declare112 // 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 from123 // being incomplete in places where bounds checks are needed the following124 // 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 later128 // on in the translation unit) the attribute is allowed by this method129 // but later uses of the FieldDecl are checked that the pointee type130 // is complete see `BoundsSafetyCheckAssignmentToCountAttrPtr`,131 // `BoundsSafetyCheckInitialization`, and132 // `BoundsSafetyCheckUseOfCountAttrPtr`133 //134 // * When the pointee type is always an incomplete type (e.g.135 // `void` in strict C mode) the attribute is disallowed by this method136 // because we know the type can never be completed so there's no reason137 // to allow it.138 //139 // Exception: void has an implicit size of 1 byte for pointer arithmetic140 // (following GNU convention). Therefore, counted_by on void* is allowed141 // and behaves equivalently to sized_by (treating the count as bytes).142 bool IsVoidPtr = PointeeTy->isVoidType();143 if (IsVoidPtr) {144 // Emit a warning that this is a GNU extension.145 Diag(FD->getBeginLoc(), diag::ext_gnu_counted_by_void_ptr) << Kind;146 Diag(FD->getBeginLoc(), diag::note_gnu_counted_by_void_ptr_use_sized_by)147 << Kind;148 assert(InvalidTypeKind == CountedByInvalidPointeeTypeKind::VALID);149 } else {150 InvalidTypeKind = CountedByInvalidPointeeTypeKind::INCOMPLETE;151 }152 } else if (PointeeTy->isSizelessType()) {153 InvalidTypeKind = CountedByInvalidPointeeTypeKind::SIZELESS;154 } else if (PointeeTy->isFunctionType()) {155 InvalidTypeKind = CountedByInvalidPointeeTypeKind::FUNCTION;156 } else if (PointeeTy->isStructureTypeWithFlexibleArrayMember()) {157 if (FieldTy->isArrayType() && !getLangOpts().BoundsSafety) {158 // This is a workaround for the Linux kernel that has already adopted159 // `counted_by` on a FAM where the pointee is a struct with a FAM. This160 // should be an error because computing the bounds of the array cannot be161 // done correctly without manually traversing every struct object in the162 // array at runtime. To allow the code to be built this error is163 // downgraded to a warning.164 ShouldWarn = true;165 }166 InvalidTypeKind = CountedByInvalidPointeeTypeKind::FLEXIBLE_ARRAY_MEMBER;167 }168 169 if (InvalidTypeKind != CountedByInvalidPointeeTypeKind::VALID) {170 unsigned DiagID = ShouldWarn171 ? diag::warn_counted_by_attr_elt_type_unknown_size172 : diag::err_counted_by_attr_pointee_unknown_size;173 Diag(FD->getBeginLoc(), DiagID)174 << SelectPtrOrArr << PointeeTy << (int)InvalidTypeKind175 << (ShouldWarn ? 1 : 0) << Kind << FD->getSourceRange();176 return true;177 }178 179 // Check the expression180 181 if (!E->getType()->isIntegerType() || E->getType()->isBooleanType()) {182 Diag(E->getBeginLoc(), diag::err_count_attr_argument_not_integer)183 << Kind << E->getSourceRange();184 return true;185 }186 187 auto *DRE = dyn_cast<DeclRefExpr>(E);188 if (!DRE) {189 Diag(E->getBeginLoc(),190 diag::err_count_attr_only_support_simple_decl_reference)191 << Kind << E->getSourceRange();192 return true;193 }194 195 auto *CountDecl = DRE->getDecl();196 FieldDecl *CountFD = dyn_cast<FieldDecl>(CountDecl);197 if (auto *IFD = dyn_cast<IndirectFieldDecl>(CountDecl)) {198 CountFD = IFD->getAnonField();199 }200 if (!CountFD) {201 Diag(E->getBeginLoc(), diag::err_count_attr_must_be_in_structure)202 << CountDecl << Kind << E->getSourceRange();203 204 Diag(CountDecl->getBeginLoc(),205 diag::note_flexible_array_counted_by_attr_field)206 << CountDecl << CountDecl->getSourceRange();207 return true;208 }209 210 if (FD->getParent() != CountFD->getParent()) {211 if (CountFD->getParent()->isUnion()) {212 Diag(CountFD->getBeginLoc(), diag::err_count_attr_refer_to_union)213 << Kind << CountFD->getSourceRange();214 return true;215 }216 // Whether CountRD is an anonymous struct is not determined at this217 // point. Thus, an additional diagnostic in case it's not anonymous struct218 // is done later in `Parser::ParseStructDeclaration`.219 auto *RD = GetEnclosingNamedOrTopAnonRecord(FD);220 auto *CountRD = GetEnclosingNamedOrTopAnonRecord(CountFD);221 222 if (RD != CountRD) {223 Diag(E->getBeginLoc(), diag::err_count_attr_param_not_in_same_struct)224 << CountFD << Kind << FieldTy->isArrayType() << E->getSourceRange();225 Diag(CountFD->getBeginLoc(),226 diag::note_flexible_array_counted_by_attr_field)227 << CountFD << CountFD->getSourceRange();228 return true;229 }230 }231 return false;232}233 234static void EmitIncompleteCountedByPointeeNotes(Sema &S,235 const CountAttributedType *CATy,236 NamedDecl *IncompleteTyDecl) {237 assert(IncompleteTyDecl == nullptr || isa<TypeDecl>(IncompleteTyDecl));238 239 if (IncompleteTyDecl) {240 // Suggest completing the pointee type if its a named typed (i.e.241 // IncompleteTyDecl isn't nullptr). Suggest this first as it is more likely242 // to be the correct fix.243 //244 // Note the `IncompleteTyDecl` type is the underlying type which might not245 // be the same as `CATy->getPointeeType()` which could be a typedef.246 //247 // The diagnostic printed will be at the location of the underlying type but248 // the diagnostic text will print the type of `CATy->getPointeeType()` which249 // could be a typedef name rather than the underlying type. This is ok250 // though because the diagnostic will print the underlying type name too.251 S.Diag(IncompleteTyDecl->getBeginLoc(),252 diag::note_counted_by_consider_completing_pointee_ty)253 << CATy->getPointeeType();254 }255 256 // Suggest using __sized_by(_or_null) instead of __counted_by(_or_null) as257 // __sized_by(_or_null) doesn't have the complete type restriction.258 //259 // We use the source range of the expression on the CountAttributedType as an260 // approximation for the source range of the attribute. This isn't quite right261 // but isn't easy to fix right now.262 //263 // TODO: Implement logic to find the relevant TypeLoc for the attribute and264 // get the SourceRange from that (#113582).265 //266 // TODO: We should emit a fix-it here.267 SourceRange AttrSrcRange = CATy->getCountExpr()->getSourceRange();268 S.Diag(AttrSrcRange.getBegin(), diag::note_counted_by_consider_using_sized_by)269 << CATy->isOrNull() << AttrSrcRange;270}271 272static std::tuple<const CountAttributedType *, QualType>273GetCountedByAttrOnIncompletePointee(QualType Ty, NamedDecl **ND) {274 auto *CATy = Ty->getAs<CountAttributedType>();275 // Incomplete pointee type is only a problem for276 // counted_by/counted_by_or_null277 if (!CATy || CATy->isCountInBytes())278 return {};279 280 auto PointeeTy = CATy->getPointeeType();281 if (PointeeTy.isNull()) {282 // Reachable if `CountAttributedType` wraps an IncompleteArrayType283 return {};284 }285 286 if (!PointeeTy->isIncompleteType(ND))287 return {};288 289 if (PointeeTy->isVoidType())290 return {};291 292 return {CATy, PointeeTy};293}294 295/// Perform Checks for assigning to a `__counted_by` or296/// `__counted_by_or_null` pointer type \param LHSTy where the pointee type297/// is incomplete which is invalid.298///299/// \param S The Sema instance.300/// \param LHSTy The type being assigned to. Checks will only be performed if301/// the type is a `counted_by` or `counted_by_or_null ` pointer.302/// \param RHSExpr The expression being assigned from.303/// \param Action The type assignment being performed304/// \param Loc The SourceLocation to use for error diagnostics305/// \param Assignee The ValueDecl being assigned. This is used to compute306/// the name of the assignee. If the assignee isn't known this can307/// be set to nullptr.308/// \param ShowFullyQualifiedAssigneeName If set to true when using \p309/// Assignee to compute the name of the assignee use the fully310/// qualified name, otherwise use the unqualified name.311///312/// \returns True iff no diagnostic where emitted, false otherwise.313static bool CheckAssignmentToCountAttrPtrWithIncompletePointeeTy(314 Sema &S, QualType LHSTy, Expr *RHSExpr, AssignmentAction Action,315 SourceLocation Loc, const ValueDecl *Assignee,316 bool ShowFullyQualifiedAssigneeName) {317 NamedDecl *IncompleteTyDecl = nullptr;318 auto [CATy, PointeeTy] =319 GetCountedByAttrOnIncompletePointee(LHSTy, &IncompleteTyDecl);320 if (!CATy)321 return true;322 323 std::string AssigneeStr;324 if (Assignee) {325 if (ShowFullyQualifiedAssigneeName) {326 AssigneeStr = Assignee->getQualifiedNameAsString();327 } else {328 AssigneeStr = Assignee->getNameAsString();329 }330 }331 332 S.Diag(Loc, diag::err_counted_by_on_incomplete_type_on_assign)333 << static_cast<int>(Action) << AssigneeStr << (AssigneeStr.size() > 0)334 << isa<ImplicitValueInitExpr>(RHSExpr) << LHSTy335 << CATy->getAttributeName(/*WithMacroPrefix=*/true) << PointeeTy336 << CATy->isOrNull() << RHSExpr->getSourceRange();337 338 EmitIncompleteCountedByPointeeNotes(S, CATy, IncompleteTyDecl);339 return false; // check failed340}341 342bool Sema::BoundsSafetyCheckAssignmentToCountAttrPtr(343 QualType LHSTy, Expr *RHSExpr, AssignmentAction Action, SourceLocation Loc,344 const ValueDecl *Assignee, bool ShowFullyQualifiedAssigneeName) {345 return CheckAssignmentToCountAttrPtrWithIncompletePointeeTy(346 *this, LHSTy, RHSExpr, Action, Loc, Assignee,347 ShowFullyQualifiedAssigneeName);348}349 350bool Sema::BoundsSafetyCheckInitialization(const InitializedEntity &Entity,351 const InitializationKind &Kind,352 AssignmentAction Action,353 QualType LHSType, Expr *RHSExpr) {354 auto SL = Kind.getLocation();355 356 // Note: We don't call `BoundsSafetyCheckAssignmentToCountAttrPtr` here357 // because we need conditionalize what is checked. In downstream358 // Clang `counted_by` is supported on variable definitions and in that359 // implementation an error diagnostic will be emitted on the variable360 // definition if the pointee is an incomplete type. To avoid warning about the361 // same problem twice (once when the variable is defined, once when Sema362 // checks the initializer) we skip checking the initializer if it's a363 // variable.364 if (Action == AssignmentAction::Initializing &&365 Entity.getKind() != InitializedEntity::EK_Variable) {366 367 if (!CheckAssignmentToCountAttrPtrWithIncompletePointeeTy(368 *this, LHSType, RHSExpr, Action, SL,369 dyn_cast_or_null<ValueDecl>(Entity.getDecl()),370 /*ShowFullQualifiedAssigneeName=*/true)) {371 return false;372 }373 }374 375 return true;376}377 378bool Sema::BoundsSafetyCheckUseOfCountAttrPtr(const Expr *E) {379 QualType T = E->getType();380 if (!T->isPointerType())381 return true;382 383 NamedDecl *IncompleteTyDecl = nullptr;384 auto [CATy, PointeeTy] =385 GetCountedByAttrOnIncompletePointee(T, &IncompleteTyDecl);386 if (!CATy)387 return true;388 389 // Generate a string for the diagnostic that describes the "use".390 // The string is specialized for direct calls to produce a better391 // diagnostic.392 SmallString<64> UseStr;393 bool IsDirectCall = false;394 if (const auto *CE = dyn_cast<CallExpr>(E->IgnoreParens())) {395 if (const auto *FD = CE->getDirectCallee()) {396 UseStr = FD->getName();397 IsDirectCall = true;398 }399 }400 401 if (!IsDirectCall) {402 llvm::raw_svector_ostream SS(UseStr);403 E->printPretty(SS, nullptr, getPrintingPolicy());404 }405 406 Diag(E->getBeginLoc(), diag::err_counted_by_on_incomplete_type_on_use)407 << IsDirectCall << UseStr << T << PointeeTy408 << CATy->getAttributeName(/*WithMacroPrefix=*/true) << CATy->isOrNull()409 << E->getSourceRange();410 411 EmitIncompleteCountedByPointeeNotes(*this, CATy, IncompleteTyDecl);412 return false;413}414 415} // namespace clang416