Path: blob/main/contrib/llvm-project/clang/lib/Sema/SemaBoundsSafety.cpp
35234 views
//===-- SemaBoundsSafety.cpp - Bounds Safety specific routines-*- C++ -*---===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7/// \file8/// This file declares semantic analysis functions specific to `-fbounds-safety`9/// (Bounds Safety) and also its attributes when used without `-fbounds-safety`10/// (e.g. `counted_by`)11///12//===----------------------------------------------------------------------===//13#include "clang/Sema/Sema.h"1415namespace clang {1617static CountAttributedType::DynamicCountPointerKind18getCountAttrKind(bool CountInBytes, bool OrNull) {19if (CountInBytes)20return OrNull ? CountAttributedType::SizedByOrNull21: CountAttributedType::SizedBy;22return OrNull ? CountAttributedType::CountedByOrNull23: CountAttributedType::CountedBy;24}2526static const RecordDecl *GetEnclosingNamedOrTopAnonRecord(const FieldDecl *FD) {27const auto *RD = FD->getParent();28// An unnamed struct is anonymous struct only if it's not instantiated.29// However, the struct may not be fully processed yet to determine30// whether it's anonymous or not. In that case, this function treats it as31// an anonymous struct and tries to find a named parent.32while (RD && (RD->isAnonymousStructOrUnion() ||33(!RD->isCompleteDefinition() && RD->getName().empty()))) {34const auto *Parent = dyn_cast<RecordDecl>(RD->getParent());35if (!Parent)36break;37RD = Parent;38}39return RD;40}4142enum class CountedByInvalidPointeeTypeKind {43INCOMPLETE,44SIZELESS,45FUNCTION,46FLEXIBLE_ARRAY_MEMBER,47VALID,48};4950bool Sema::CheckCountedByAttrOnField(51FieldDecl *FD, Expr *E,52llvm::SmallVectorImpl<TypeCoupledDeclRefInfo> &Decls, bool CountInBytes,53bool OrNull) {54// Check the context the attribute is used in5556unsigned Kind = getCountAttrKind(CountInBytes, OrNull);5758if (FD->getParent()->isUnion()) {59Diag(FD->getBeginLoc(), diag::err_count_attr_in_union)60<< Kind << FD->getSourceRange();61return true;62}6364const auto FieldTy = FD->getType();65if (FieldTy->isArrayType() && (CountInBytes || OrNull)) {66Diag(FD->getBeginLoc(),67diag::err_count_attr_not_on_ptr_or_flexible_array_member)68<< Kind << FD->getLocation() << /* suggest counted_by */ 1;69return true;70}71if (!FieldTy->isArrayType() && !FieldTy->isPointerType()) {72Diag(FD->getBeginLoc(),73diag::err_count_attr_not_on_ptr_or_flexible_array_member)74<< Kind << FD->getLocation() << /* do not suggest counted_by */ 0;75return true;76}7778LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =79LangOptions::StrictFlexArraysLevelKind::IncompleteOnly;80if (FieldTy->isArrayType() &&81!Decl::isFlexibleArrayMemberLike(getASTContext(), FD, FieldTy,82StrictFlexArraysLevel, true)) {83Diag(FD->getBeginLoc(),84diag::err_counted_by_attr_on_array_not_flexible_array_member)85<< Kind << FD->getLocation();86return true;87}8889CountedByInvalidPointeeTypeKind InvalidTypeKind =90CountedByInvalidPointeeTypeKind::VALID;91QualType PointeeTy;92int SelectPtrOrArr = 0;93if (FieldTy->isPointerType()) {94PointeeTy = FieldTy->getPointeeType();95SelectPtrOrArr = 0;96} else {97assert(FieldTy->isArrayType());98const ArrayType *AT = getASTContext().getAsArrayType(FieldTy);99PointeeTy = AT->getElementType();100SelectPtrOrArr = 1;101}102// Note: The `Decl::isFlexibleArrayMemberLike` check earlier on means103// only `PointeeTy->isStructureTypeWithFlexibleArrayMember()` is reachable104// when `FieldTy->isArrayType()`.105bool ShouldWarn = false;106if (PointeeTy->isIncompleteType() && !CountInBytes) {107InvalidTypeKind = CountedByInvalidPointeeTypeKind::INCOMPLETE;108} else if (PointeeTy->isSizelessType()) {109InvalidTypeKind = CountedByInvalidPointeeTypeKind::SIZELESS;110} else if (PointeeTy->isFunctionType()) {111InvalidTypeKind = CountedByInvalidPointeeTypeKind::FUNCTION;112} else if (PointeeTy->isStructureTypeWithFlexibleArrayMember()) {113if (FieldTy->isArrayType() && !getLangOpts().BoundsSafety) {114// This is a workaround for the Linux kernel that has already adopted115// `counted_by` on a FAM where the pointee is a struct with a FAM. This116// should be an error because computing the bounds of the array cannot be117// done correctly without manually traversing every struct object in the118// array at runtime. To allow the code to be built this error is119// downgraded to a warning.120ShouldWarn = true;121}122InvalidTypeKind = CountedByInvalidPointeeTypeKind::FLEXIBLE_ARRAY_MEMBER;123}124125if (InvalidTypeKind != CountedByInvalidPointeeTypeKind::VALID) {126unsigned DiagID = ShouldWarn127? diag::warn_counted_by_attr_elt_type_unknown_size128: diag::err_counted_by_attr_pointee_unknown_size;129Diag(FD->getBeginLoc(), DiagID)130<< SelectPtrOrArr << PointeeTy << (int)InvalidTypeKind131<< (ShouldWarn ? 1 : 0) << Kind << FD->getSourceRange();132return true;133}134135// Check the expression136137if (!E->getType()->isIntegerType() || E->getType()->isBooleanType()) {138Diag(E->getBeginLoc(), diag::err_count_attr_argument_not_integer)139<< Kind << E->getSourceRange();140return true;141}142143auto *DRE = dyn_cast<DeclRefExpr>(E);144if (!DRE) {145Diag(E->getBeginLoc(),146diag::err_count_attr_only_support_simple_decl_reference)147<< Kind << E->getSourceRange();148return true;149}150151auto *CountDecl = DRE->getDecl();152FieldDecl *CountFD = dyn_cast<FieldDecl>(CountDecl);153if (auto *IFD = dyn_cast<IndirectFieldDecl>(CountDecl)) {154CountFD = IFD->getAnonField();155}156if (!CountFD) {157Diag(E->getBeginLoc(), diag::err_count_attr_must_be_in_structure)158<< CountDecl << Kind << E->getSourceRange();159160Diag(CountDecl->getBeginLoc(),161diag::note_flexible_array_counted_by_attr_field)162<< CountDecl << CountDecl->getSourceRange();163return true;164}165166if (FD->getParent() != CountFD->getParent()) {167if (CountFD->getParent()->isUnion()) {168Diag(CountFD->getBeginLoc(), diag::err_count_attr_refer_to_union)169<< Kind << CountFD->getSourceRange();170return true;171}172// Whether CountRD is an anonymous struct is not determined at this173// point. Thus, an additional diagnostic in case it's not anonymous struct174// is done later in `Parser::ParseStructDeclaration`.175auto *RD = GetEnclosingNamedOrTopAnonRecord(FD);176auto *CountRD = GetEnclosingNamedOrTopAnonRecord(CountFD);177178if (RD != CountRD) {179Diag(E->getBeginLoc(), diag::err_count_attr_param_not_in_same_struct)180<< CountFD << Kind << FieldTy->isArrayType() << E->getSourceRange();181Diag(CountFD->getBeginLoc(),182diag::note_flexible_array_counted_by_attr_field)183<< CountFD << CountFD->getSourceRange();184return true;185}186}187188Decls.push_back(TypeCoupledDeclRefInfo(CountFD, /*IsDref*/ false));189return false;190}191192} // namespace clang193194195