brintos

brintos / llvm-project-archived public Read only

0
0
Text · 16.1 KiB · de9adf8 Raw
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