mirror of
https://github.com/llvm/llvm-project.git
synced 2025-05-02 15:06:06 +00:00
Revert "[Clang] Implement the 'counted_by' attribute" (#68603)
This reverts commit 9a954c693573281407f6ee3f4eb1b16cc545033d, which
causes clang crashes when compiling with `-fsanitize=bounds`. See
9a954c6935 (commitcomment-129529574)
for details.
This commit is contained in:
parent
08b20d8385
commit
67b675ee55
@ -139,11 +139,6 @@ C Language Changes
|
||||
- ``structs``, ``unions``, and ``arrays`` that are const may now be used as
|
||||
constant expressions. This change is more consistent with the behavior of
|
||||
GCC.
|
||||
- Clang now supports the C-only attribute ``counted_by``. When applied to a
|
||||
struct's flexible array member, it points to the struct field that holds the
|
||||
number of elements in the flexible array member. This information can improve
|
||||
the results of the array bound sanitizer and the
|
||||
``__builtin_dynamic_object_size`` builtin.
|
||||
|
||||
C23 Feature Support
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
@ -4302,30 +4302,6 @@ public:
|
||||
return field_begin() == field_end();
|
||||
}
|
||||
|
||||
FieldDecl *getLastField() {
|
||||
FieldDecl *FD = nullptr;
|
||||
for (FieldDecl *Field : fields())
|
||||
FD = Field;
|
||||
return FD;
|
||||
}
|
||||
const FieldDecl *getLastField() const {
|
||||
return const_cast<RecordDecl *>(this)->getLastField();
|
||||
}
|
||||
|
||||
template <typename Functor>
|
||||
const FieldDecl *findFieldIf(Functor &Pred) const {
|
||||
for (const Decl *D : decls()) {
|
||||
if (const auto *FD = dyn_cast<FieldDecl>(D); FD && Pred(FD))
|
||||
return FD;
|
||||
|
||||
if (const auto *RD = dyn_cast<RecordDecl>(D))
|
||||
if (const FieldDecl *FD = RD->findFieldIf(Pred))
|
||||
return FD;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/// Note that the definition of this type is now complete.
|
||||
virtual void completeDefinition();
|
||||
|
||||
|
@ -18,7 +18,6 @@
|
||||
#include "clang/AST/DeclarationName.h"
|
||||
#include "clang/Basic/IdentifierTable.h"
|
||||
#include "clang/Basic/LLVM.h"
|
||||
#include "clang/Basic/LangOptions.h"
|
||||
#include "clang/Basic/SourceLocation.h"
|
||||
#include "clang/Basic/Specifiers.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
@ -478,15 +477,6 @@ public:
|
||||
// Return true if this is a FileContext Decl.
|
||||
bool isFileContextDecl() const;
|
||||
|
||||
/// Whether it resembles a flexible array member. This is a static member
|
||||
/// because we want to be able to call it with a nullptr. That allows us to
|
||||
/// perform non-Decl specific checks based on the object's type and strict
|
||||
/// flex array level.
|
||||
static bool isFlexibleArrayMemberLike(
|
||||
ASTContext &Context, const Decl *D, QualType Ty,
|
||||
LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel,
|
||||
bool IgnoreTemplateOrMacroSubstitution);
|
||||
|
||||
ASTContext &getASTContext() const LLVM_READONLY;
|
||||
|
||||
/// Helper to get the language options from the ASTContext.
|
||||
|
@ -4246,21 +4246,3 @@ def AvailableOnlyInDefaultEvalMethod : InheritableAttr {
|
||||
let Subjects = SubjectList<[TypedefName], ErrorDiag>;
|
||||
let Documentation = [Undocumented];
|
||||
}
|
||||
|
||||
def CountedBy : InheritableAttr {
|
||||
let Spellings = [Clang<"counted_by">];
|
||||
let Subjects = SubjectList<[Field]>;
|
||||
let Args = [IdentifierArgument<"CountedByField">];
|
||||
let Documentation = [CountedByDocs];
|
||||
let LangOpts = [COnly];
|
||||
// FIXME: This is ugly. Let using a DeclArgument would be nice, but a Decl
|
||||
// isn't yet available due to the fact that we're still parsing the
|
||||
// structure. Maybe that code could be changed sometime in the future.
|
||||
code AdditionalMembers = [{
|
||||
private:
|
||||
SourceRange CountedByFieldLoc;
|
||||
public:
|
||||
SourceRange getCountedByFieldLoc() const { return CountedByFieldLoc; }
|
||||
void setCountedByFieldLoc(SourceRange Loc) { CountedByFieldLoc = Loc; }
|
||||
}];
|
||||
}
|
||||
|
@ -7275,69 +7275,3 @@ relative ordering of values is important. For example:
|
||||
attribute, they default to the value ``65535``.
|
||||
}];
|
||||
}
|
||||
|
||||
def CountedByDocs : Documentation {
|
||||
let Category = DocCatField;
|
||||
let Content = [{
|
||||
Clang supports the ``counted_by`` attribute on the flexible array member of a
|
||||
structure in C. The argument for the attribute is the name of a field member in
|
||||
the same structure holding the count of elements in the flexible array. This
|
||||
information can be used to improve the results of the array bound sanitizer and
|
||||
the ``__builtin_dynamic_object_size`` builtin.
|
||||
|
||||
For example, the following code:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
struct bar;
|
||||
|
||||
struct foo {
|
||||
size_t count;
|
||||
char other;
|
||||
struct bar *array[] __attribute__((counted_by(count)));
|
||||
};
|
||||
|
||||
specifies that the flexible array member ``array`` has the number of elements
|
||||
allocated for it stored in ``count``. This establishes a relationship between
|
||||
``array`` and ``count``. Specifically, ``p->array`` must have at least
|
||||
``p->count`` number of elements available. It's the user's responsibility to
|
||||
ensure that this relationship is maintained through changes to the structure.
|
||||
|
||||
In the following example, the allocated array erroneously has fewer elements
|
||||
than what's specified by ``p->count``. This would result in an out-of-bounds
|
||||
access not being detected.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
#define SIZE_INCR 42
|
||||
|
||||
struct foo *p;
|
||||
|
||||
void foo_alloc(size_t count) {
|
||||
p = malloc(MAX(sizeof(struct foo),
|
||||
offsetof(struct foo, array[0]) + count * sizeof(struct bar *)));
|
||||
p->count = count + SIZE_INCR;
|
||||
}
|
||||
|
||||
The next example updates ``p->count``, breaking the relationship requirement
|
||||
that ``p->array`` must have at least ``p->count`` number of elements available:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
#define SIZE_INCR 42
|
||||
|
||||
struct foo *p;
|
||||
|
||||
void foo_alloc(size_t count) {
|
||||
p = malloc(MAX(sizeof(struct foo),
|
||||
offsetof(struct foo, array[0]) + count * sizeof(struct bar *)));
|
||||
p->count = count;
|
||||
}
|
||||
|
||||
void use_foo(int index) {
|
||||
p->count += SIZE_INCR + 1; /* 'count' is now larger than the number of elements of 'array'. */
|
||||
p->array[index] = 0; /* the sanitizer can't properly check if this is an out-of-bounds access. */
|
||||
}
|
||||
|
||||
}];
|
||||
}
|
||||
|
@ -6389,21 +6389,6 @@ def warn_superclass_variable_sized_type_not_at_end : Warning<
|
||||
"field %0 can overwrite instance variable %1 with variable sized type %2"
|
||||
" in superclass %3">, InGroup<ObjCFlexibleArray>;
|
||||
|
||||
def err_counted_by_attr_not_on_flexible_array_member : Error<
|
||||
"'counted_by' only applies to flexible array members">;
|
||||
def err_flexible_array_counted_by_attr_field_not_found : Error<
|
||||
"field %0 in 'counted_by' not found">;
|
||||
def err_flexible_array_counted_by_attr_field_not_found_suggest : Error<
|
||||
"field %0 in 'counted_by' not found; did you mean %1?">;
|
||||
def err_flexible_array_counted_by_attr_field_not_found_in_struct : Error<
|
||||
"field %0 in 'counted_by' is not found in struct">;
|
||||
def err_flexible_array_counted_by_attr_refers_to_self : Error<
|
||||
"field %0 in 'counted_by' cannot refer to the flexible array">;
|
||||
def err_flexible_array_counted_by_attr_field_not_integer : Error<
|
||||
"field %0 in 'counted_by' is not a non-boolean integer type">;
|
||||
def note_flexible_array_counted_by_attr_field : Note<
|
||||
"field %0 declared here">;
|
||||
|
||||
let CategoryName = "ARC Semantic Issue" in {
|
||||
|
||||
// ARC-mode diagnostics.
|
||||
|
@ -4795,8 +4795,6 @@ public:
|
||||
bool CheckAlwaysInlineAttr(const Stmt *OrigSt, const Stmt *CurSt,
|
||||
const AttributeCommonInfo &A);
|
||||
|
||||
bool CheckCountedByAttr(Scope *Scope, const FieldDecl *FD);
|
||||
|
||||
/// Adjust the calling convention of a method to be the ABI default if it
|
||||
/// wasn't specified explicitly. This handles method types formed from
|
||||
/// function type typedefs and typename template arguments.
|
||||
|
@ -8978,10 +8978,6 @@ class AttrImporter {
|
||||
public:
|
||||
AttrImporter(ASTImporter &I) : Importer(I), NImporter(I) {}
|
||||
|
||||
// Useful for accessing the imported attribute.
|
||||
template <typename T> T *castAttrAs() { return cast<T>(ToAttr); }
|
||||
template <typename T> const T *castAttrAs() const { return cast<T>(ToAttr); }
|
||||
|
||||
// Create an "importer" for an attribute parameter.
|
||||
// Result of the 'value()' of that object is to be passed to the function
|
||||
// 'importAttr', in the order that is expected by the attribute class.
|
||||
@ -9188,15 +9184,6 @@ Expected<Attr *> ASTImporter::Import(const Attr *FromAttr) {
|
||||
From->args_size());
|
||||
break;
|
||||
}
|
||||
case attr::CountedBy: {
|
||||
AI.cloneAttr(FromAttr);
|
||||
const auto *CBA = cast<CountedByAttr>(FromAttr);
|
||||
Expected<SourceRange> SR = Import(CBA->getCountedByFieldLoc()).get();
|
||||
if (!SR)
|
||||
return SR.takeError();
|
||||
AI.castAttrAs<CountedByAttr>()->setCountedByFieldLoc(SR.get());
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
// The default branch works for attributes that have no arguments to import.
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include "clang/AST/Type.h"
|
||||
#include "clang/Basic/IdentifierTable.h"
|
||||
#include "clang/Basic/LLVM.h"
|
||||
#include "clang/Basic/LangOptions.h"
|
||||
#include "clang/Basic/Module.h"
|
||||
#include "clang/Basic/ObjCRuntime.h"
|
||||
#include "clang/Basic/PartialDiagnostic.h"
|
||||
@ -410,81 +411,6 @@ bool Decl::isFileContextDecl() const {
|
||||
return DC && DC->isFileContext();
|
||||
}
|
||||
|
||||
bool Decl::isFlexibleArrayMemberLike(
|
||||
ASTContext &Ctx, const Decl *D, QualType Ty,
|
||||
LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel,
|
||||
bool IgnoreTemplateOrMacroSubstitution) {
|
||||
// For compatibility with existing code, we treat arrays of length 0 or
|
||||
// 1 as flexible array members.
|
||||
const auto *CAT = Ctx.getAsConstantArrayType(Ty);
|
||||
if (CAT) {
|
||||
using FAMKind = LangOptions::StrictFlexArraysLevelKind;
|
||||
|
||||
llvm::APInt Size = CAT->getSize();
|
||||
FAMKind StrictFlexArraysLevel =
|
||||
Ctx.getLangOpts().getStrictFlexArraysLevel();
|
||||
|
||||
if (StrictFlexArraysLevel == FAMKind::IncompleteOnly)
|
||||
return false;
|
||||
|
||||
// GCC extension, only allowed to represent a FAM.
|
||||
if (Size.isZero())
|
||||
return true;
|
||||
|
||||
if (StrictFlexArraysLevel == FAMKind::ZeroOrIncomplete && Size.uge(1))
|
||||
return false;
|
||||
|
||||
if (StrictFlexArraysLevel == FAMKind::OneZeroOrIncomplete && Size.uge(2))
|
||||
return false;
|
||||
} else if (!Ctx.getAsIncompleteArrayType(Ty)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (const auto *OID = dyn_cast_if_present<ObjCIvarDecl>(D))
|
||||
return OID->getNextIvar() == nullptr;
|
||||
|
||||
const auto *FD = dyn_cast_if_present<FieldDecl>(D);
|
||||
if (!FD)
|
||||
return false;
|
||||
|
||||
if (CAT) {
|
||||
// GCC treats an array memeber of a union as an FAM if the size is one or
|
||||
// zero.
|
||||
llvm::APInt Size = CAT->getSize();
|
||||
if (FD->getParent()->isUnion() && (Size.isZero() || Size.isOne()))
|
||||
return true;
|
||||
}
|
||||
|
||||
// Don't consider sizes resulting from macro expansions or template argument
|
||||
// substitution to form C89 tail-padded arrays.
|
||||
if (IgnoreTemplateOrMacroSubstitution) {
|
||||
TypeSourceInfo *TInfo = FD->getTypeSourceInfo();
|
||||
while (TInfo) {
|
||||
TypeLoc TL = TInfo->getTypeLoc();
|
||||
|
||||
// Look through typedefs.
|
||||
if (TypedefTypeLoc TTL = TL.getAsAdjusted<TypedefTypeLoc>()) {
|
||||
const TypedefNameDecl *TDL = TTL.getTypedefNameDecl();
|
||||
TInfo = TDL->getTypeSourceInfo();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (auto CTL = TL.getAs<ConstantArrayTypeLoc>()) {
|
||||
const Expr *SizeExpr = dyn_cast<IntegerLiteral>(CTL.getSizeExpr());
|
||||
if (!SizeExpr || SizeExpr->getExprLoc().isMacroID())
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Test that the field is the last in the structure.
|
||||
RecordDecl::field_iterator FI(
|
||||
DeclContext::decl_iterator(const_cast<FieldDecl *>(FD)));
|
||||
return ++FI == FD->getParent()->field_end();
|
||||
}
|
||||
|
||||
TranslationUnitDecl *Decl::getTranslationUnitDecl() {
|
||||
if (auto *TUD = dyn_cast<TranslationUnitDecl>(this))
|
||||
return TUD;
|
||||
|
@ -205,22 +205,85 @@ bool Expr::isKnownToHaveBooleanValue(bool Semantic) const {
|
||||
}
|
||||
|
||||
bool Expr::isFlexibleArrayMemberLike(
|
||||
ASTContext &Ctx,
|
||||
ASTContext &Context,
|
||||
LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel,
|
||||
bool IgnoreTemplateOrMacroSubstitution) const {
|
||||
|
||||
// For compatibility with existing code, we treat arrays of length 0 or
|
||||
// 1 as flexible array members.
|
||||
const auto *CAT = Context.getAsConstantArrayType(getType());
|
||||
if (CAT) {
|
||||
llvm::APInt Size = CAT->getSize();
|
||||
|
||||
using FAMKind = LangOptions::StrictFlexArraysLevelKind;
|
||||
|
||||
if (StrictFlexArraysLevel == FAMKind::IncompleteOnly)
|
||||
return false;
|
||||
|
||||
// GCC extension, only allowed to represent a FAM.
|
||||
if (Size == 0)
|
||||
return true;
|
||||
|
||||
if (StrictFlexArraysLevel == FAMKind::ZeroOrIncomplete && Size.uge(1))
|
||||
return false;
|
||||
|
||||
if (StrictFlexArraysLevel == FAMKind::OneZeroOrIncomplete && Size.uge(2))
|
||||
return false;
|
||||
} else if (!Context.getAsIncompleteArrayType(getType()))
|
||||
return false;
|
||||
|
||||
const Expr *E = IgnoreParens();
|
||||
const Decl *D = nullptr;
|
||||
|
||||
if (const auto *ME = dyn_cast<MemberExpr>(E))
|
||||
D = ME->getMemberDecl();
|
||||
else if (const auto *DRE = dyn_cast<DeclRefExpr>(E))
|
||||
D = DRE->getDecl();
|
||||
const NamedDecl *ND = nullptr;
|
||||
if (const auto *DRE = dyn_cast<DeclRefExpr>(E))
|
||||
ND = DRE->getDecl();
|
||||
else if (const auto *ME = dyn_cast<MemberExpr>(E))
|
||||
ND = ME->getMemberDecl();
|
||||
else if (const auto *IRE = dyn_cast<ObjCIvarRefExpr>(E))
|
||||
D = IRE->getDecl();
|
||||
return IRE->getDecl()->getNextIvar() == nullptr;
|
||||
|
||||
return Decl::isFlexibleArrayMemberLike(Ctx, D, E->getType(),
|
||||
StrictFlexArraysLevel,
|
||||
IgnoreTemplateOrMacroSubstitution);
|
||||
if (!ND)
|
||||
return false;
|
||||
|
||||
// A flexible array member must be the last member in the class.
|
||||
// FIXME: If the base type of the member expr is not FD->getParent(),
|
||||
// this should not be treated as a flexible array member access.
|
||||
if (const auto *FD = dyn_cast<FieldDecl>(ND)) {
|
||||
// GCC treats an array memeber of a union as an FAM if the size is one or
|
||||
// zero.
|
||||
if (CAT) {
|
||||
llvm::APInt Size = CAT->getSize();
|
||||
if (FD->getParent()->isUnion() && (Size.isZero() || Size.isOne()))
|
||||
return true;
|
||||
}
|
||||
|
||||
// Don't consider sizes resulting from macro expansions or template argument
|
||||
// substitution to form C89 tail-padded arrays.
|
||||
if (IgnoreTemplateOrMacroSubstitution) {
|
||||
TypeSourceInfo *TInfo = FD->getTypeSourceInfo();
|
||||
while (TInfo) {
|
||||
TypeLoc TL = TInfo->getTypeLoc();
|
||||
// Look through typedefs.
|
||||
if (TypedefTypeLoc TTL = TL.getAsAdjusted<TypedefTypeLoc>()) {
|
||||
const TypedefNameDecl *TDL = TTL.getTypedefNameDecl();
|
||||
TInfo = TDL->getTypeSourceInfo();
|
||||
continue;
|
||||
}
|
||||
if (ConstantArrayTypeLoc CTL = TL.getAs<ConstantArrayTypeLoc>()) {
|
||||
const Expr *SizeExpr = dyn_cast<IntegerLiteral>(CTL.getSizeExpr());
|
||||
if (!SizeExpr || SizeExpr->getExprLoc().isMacroID())
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
RecordDecl::field_iterator FI(
|
||||
DeclContext::decl_iterator(const_cast<FieldDecl *>(FD)));
|
||||
return ++FI == FD->getParent()->field_end();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const ValueDecl *
|
||||
|
@ -853,57 +853,6 @@ CodeGenFunction::emitBuiltinObjectSize(const Expr *E, unsigned Type,
|
||||
}
|
||||
}
|
||||
|
||||
if (IsDynamic) {
|
||||
LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
|
||||
getLangOpts().getStrictFlexArraysLevel();
|
||||
const Expr *Base = E->IgnoreParenImpCasts();
|
||||
|
||||
if (FieldDecl *FD = FindCountedByField(Base, StrictFlexArraysLevel)) {
|
||||
const auto *ME = dyn_cast<MemberExpr>(Base);
|
||||
llvm::Value *ObjectSize = nullptr;
|
||||
|
||||
if (!ME) {
|
||||
const auto *DRE = dyn_cast<DeclRefExpr>(Base);
|
||||
ValueDecl *VD = nullptr;
|
||||
|
||||
ObjectSize = ConstantInt::get(
|
||||
ResType,
|
||||
getContext().getTypeSize(DRE->getType()->getPointeeType()) / 8,
|
||||
true);
|
||||
|
||||
if (auto *RD = DRE->getType()->getPointeeType()->getAsRecordDecl())
|
||||
VD = RD->getLastField();
|
||||
|
||||
Expr *ICE = ImplicitCastExpr::Create(
|
||||
getContext(), DRE->getType(), CK_LValueToRValue,
|
||||
const_cast<Expr *>(cast<Expr>(DRE)), nullptr, VK_PRValue,
|
||||
FPOptionsOverride());
|
||||
ME = MemberExpr::CreateImplicit(getContext(), ICE, true, VD,
|
||||
VD->getType(), VK_LValue, OK_Ordinary);
|
||||
}
|
||||
|
||||
// At this point, we know that \p ME is a flexible array member.
|
||||
const auto *ArrayTy = getContext().getAsArrayType(ME->getType());
|
||||
unsigned Size = getContext().getTypeSize(ArrayTy->getElementType());
|
||||
|
||||
llvm::Value *CountField =
|
||||
EmitAnyExprToTemp(MemberExpr::CreateImplicit(
|
||||
getContext(), const_cast<Expr *>(ME->getBase()),
|
||||
ME->isArrow(), FD, FD->getType(), VK_LValue,
|
||||
OK_Ordinary))
|
||||
.getScalarVal();
|
||||
|
||||
llvm::Value *Mul = Builder.CreateMul(
|
||||
CountField, llvm::ConstantInt::get(CountField->getType(), Size / 8));
|
||||
Mul = Builder.CreateZExtOrTrunc(Mul, ResType);
|
||||
|
||||
if (ObjectSize)
|
||||
return Builder.CreateAdd(ObjectSize, Mul);
|
||||
|
||||
return Mul;
|
||||
}
|
||||
}
|
||||
|
||||
// LLVM can't handle Type=3 appropriately, and __builtin_object_size shouldn't
|
||||
// evaluate E for side-effects. In either case, we shouldn't lower to
|
||||
// @llvm.objectsize.
|
||||
|
@ -30,7 +30,6 @@
|
||||
#include "clang/Basic/CodeGenOptions.h"
|
||||
#include "clang/Basic/SourceManager.h"
|
||||
#include "llvm/ADT/Hashing.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/ADT/StringExtras.h"
|
||||
#include "llvm/IR/DataLayout.h"
|
||||
#include "llvm/IR/Intrinsics.h"
|
||||
@ -932,31 +931,16 @@ static llvm::Value *getArrayIndexingBound(CodeGenFunction &CGF,
|
||||
if (CE->getCastKind() == CK_ArrayToPointerDecay &&
|
||||
!CE->getSubExpr()->isFlexibleArrayMemberLike(CGF.getContext(),
|
||||
StrictFlexArraysLevel)) {
|
||||
CodeGenFunction::SanitizerScope SanScope(&CGF);
|
||||
|
||||
IndexedType = CE->getSubExpr()->getType();
|
||||
const ArrayType *AT = IndexedType->castAsArrayTypeUnsafe();
|
||||
if (const auto *CAT = dyn_cast<ConstantArrayType>(AT))
|
||||
return CGF.Builder.getInt(CAT->getSize());
|
||||
|
||||
if (const auto *VAT = dyn_cast<VariableArrayType>(AT))
|
||||
else if (const auto *VAT = dyn_cast<VariableArrayType>(AT))
|
||||
return CGF.getVLASize(VAT).NumElts;
|
||||
// Ignore pass_object_size here. It's not applicable on decayed pointers.
|
||||
}
|
||||
|
||||
if (FieldDecl *FD = CGF.FindCountedByField(Base, StrictFlexArraysLevel)) {
|
||||
const auto *ME = dyn_cast<MemberExpr>(CE->getSubExpr());
|
||||
IndexedType = Base->getType();
|
||||
return CGF
|
||||
.EmitAnyExprToTemp(MemberExpr::CreateImplicit(
|
||||
CGF.getContext(), const_cast<Expr *>(ME->getBase()),
|
||||
ME->isArrow(), FD, FD->getType(), VK_LValue, OK_Ordinary))
|
||||
.getScalarVal();
|
||||
}
|
||||
}
|
||||
|
||||
CodeGenFunction::SanitizerScope SanScope(&CGF);
|
||||
|
||||
QualType EltTy{Base->getType()->getPointeeOrArrayElementType(), 0};
|
||||
if (llvm::Value *POS = CGF.LoadPassedObjectSize(Base, EltTy)) {
|
||||
IndexedType = Base->getType();
|
||||
@ -966,53 +950,13 @@ static llvm::Value *getArrayIndexingBound(CodeGenFunction &CGF,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FieldDecl *CodeGenFunction::FindCountedByField(
|
||||
const Expr *Base,
|
||||
LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel) {
|
||||
const ValueDecl *VD = nullptr;
|
||||
|
||||
Base = Base->IgnoreParenImpCasts();
|
||||
|
||||
if (const auto *ME = dyn_cast<MemberExpr>(Base)) {
|
||||
VD = dyn_cast<ValueDecl>(ME->getMemberDecl());
|
||||
} else if (const auto *DRE = dyn_cast<DeclRefExpr>(Base)) {
|
||||
// Pointing to the full structure.
|
||||
VD = dyn_cast<ValueDecl>(DRE->getDecl());
|
||||
|
||||
QualType Ty = VD->getType();
|
||||
if (Ty->isPointerType())
|
||||
Ty = Ty->getPointeeType();
|
||||
|
||||
if (const auto *RD = Ty->getAsRecordDecl())
|
||||
VD = RD->getLastField();
|
||||
} else if (const auto *CE = dyn_cast<CastExpr>(Base)) {
|
||||
if (const auto *ME = dyn_cast<MemberExpr>(CE->getSubExpr()))
|
||||
VD = dyn_cast<ValueDecl>(ME->getMemberDecl());
|
||||
}
|
||||
|
||||
const auto *FD = dyn_cast_if_present<FieldDecl>(VD);
|
||||
if (!FD || !FD->getParent() ||
|
||||
!Decl::isFlexibleArrayMemberLike(getContext(), FD, FD->getType(),
|
||||
StrictFlexArraysLevel, true))
|
||||
return nullptr;
|
||||
|
||||
const auto *CBA = FD->getAttr<CountedByAttr>();
|
||||
if (!CBA)
|
||||
return nullptr;
|
||||
|
||||
StringRef FieldName = CBA->getCountedByField()->getName();
|
||||
auto It =
|
||||
llvm::find_if(FD->getParent()->fields(), [&](const FieldDecl *Field) {
|
||||
return FieldName == Field->getName();
|
||||
});
|
||||
return It != FD->getParent()->field_end() ? *It : nullptr;
|
||||
}
|
||||
|
||||
void CodeGenFunction::EmitBoundsCheck(const Expr *E, const Expr *Base,
|
||||
llvm::Value *Index, QualType IndexType,
|
||||
bool Accessed) {
|
||||
assert(SanOpts.has(SanitizerKind::ArrayBounds) &&
|
||||
"should not be called unless adding bounds checks");
|
||||
SanitizerScope SanScope(this);
|
||||
|
||||
const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
|
||||
getLangOpts().getStrictFlexArraysLevel();
|
||||
|
||||
@ -1022,8 +966,6 @@ void CodeGenFunction::EmitBoundsCheck(const Expr *E, const Expr *Base,
|
||||
if (!Bound)
|
||||
return;
|
||||
|
||||
SanitizerScope SanScope(this);
|
||||
|
||||
bool IndexSigned = IndexType->isSignedIntegerOrEnumerationType();
|
||||
llvm::Value *IndexVal = Builder.CreateIntCast(Index, SizeTy, IndexSigned);
|
||||
llvm::Value *BoundVal = Builder.CreateIntCast(Bound, SizeTy, false);
|
||||
|
@ -3022,12 +3022,6 @@ public:
|
||||
void EmitBoundsCheck(const Expr *E, const Expr *Base, llvm::Value *Index,
|
||||
QualType IndexType, bool Accessed);
|
||||
|
||||
/// Find the FieldDecl specified in a FAM's "counted_by" attribute. Returns
|
||||
/// \p nullptr if either the attribute or the field doesn't exist.
|
||||
FieldDecl *FindCountedByField(
|
||||
const Expr *Base,
|
||||
LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel);
|
||||
|
||||
llvm::Value *EmitScalarPrePostIncDec(const UnaryOperator *E, LValue LV,
|
||||
bool isInc, bool isPre);
|
||||
ComplexPairTy EmitComplexPrePostIncDec(const UnaryOperator *E, LValue LV,
|
||||
|
@ -18091,18 +18091,6 @@ void Sema::ActOnTagFinishDefinition(Scope *S, Decl *TagD,
|
||||
[](const FieldDecl *FD) { return FD->isBitField(); }))
|
||||
Diag(BraceRange.getBegin(), diag::warn_pragma_align_not_xl_compatible);
|
||||
}
|
||||
|
||||
// Check the "counted_by" attribute to ensure that the count field exists in
|
||||
// the struct.
|
||||
if (const auto *RD = dyn_cast<RecordDecl>(Tag)) {
|
||||
auto Pred = [](const Decl *D) {
|
||||
if (const auto *FD = dyn_cast<FieldDecl>(D))
|
||||
return FD->hasAttr<CountedByAttr>();
|
||||
return false;
|
||||
};
|
||||
if (const FieldDecl *FD = RD->findFieldIf(Pred))
|
||||
CheckCountedByAttr(S, FD);
|
||||
}
|
||||
}
|
||||
|
||||
void Sema::ActOnObjCContainerFinishDefinition() {
|
||||
|
@ -8361,134 +8361,6 @@ static void handleZeroCallUsedRegsAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
|
||||
D->addAttr(ZeroCallUsedRegsAttr::Create(S.Context, Kind, AL));
|
||||
}
|
||||
|
||||
static void handleCountedByAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
|
||||
if (!AL.isArgIdent(0)) {
|
||||
S.Diag(AL.getLoc(), diag::err_attribute_argument_type)
|
||||
<< AL << AANT_ArgumentIdentifier;
|
||||
return;
|
||||
}
|
||||
|
||||
IdentifierLoc *IL = AL.getArgAsIdent(0);
|
||||
CountedByAttr *CBA =
|
||||
::new (S.Context) CountedByAttr(S.Context, AL, IL->Ident);
|
||||
CBA->setCountedByFieldLoc(IL->Loc);
|
||||
D->addAttr(CBA);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// Callback to only accept typo corrections that are for field members of
|
||||
// the given struct or union.
|
||||
class FieldDeclValidatorCCC final : public CorrectionCandidateCallback {
|
||||
public:
|
||||
explicit FieldDeclValidatorCCC(const RecordDecl *RD) : Record(RD) {}
|
||||
|
||||
bool ValidateCandidate(const TypoCorrection &candidate) override {
|
||||
FieldDecl *FD = candidate.getCorrectionDeclAs<FieldDecl>();
|
||||
return FD && FD->getDeclContext()->getRedeclContext()->Equals(Record);
|
||||
}
|
||||
|
||||
std::unique_ptr<CorrectionCandidateCallback> clone() override {
|
||||
return std::make_unique<FieldDeclValidatorCCC>(*this);
|
||||
}
|
||||
|
||||
private:
|
||||
const RecordDecl *Record;
|
||||
};
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
bool Sema::CheckCountedByAttr(Scope *S, const FieldDecl *FD) {
|
||||
const RecordDecl *RD = FD->getParent();
|
||||
const auto *CBA = FD->getAttr<CountedByAttr>();
|
||||
const IdentifierInfo *FieldName = CBA->getCountedByField();
|
||||
|
||||
auto Pred = [&](const Decl *D) {
|
||||
if (const auto *Field = dyn_cast<FieldDecl>(D))
|
||||
return Field->getName() == FieldName->getName();
|
||||
return false;
|
||||
};
|
||||
const FieldDecl *Field = RD->findFieldIf(Pred);
|
||||
|
||||
if (!Field) {
|
||||
// The "counted_by" field needs to exist within the struct.
|
||||
DeclarationNameInfo NameInfo(FieldName,
|
||||
CBA->getCountedByFieldLoc().getBegin());
|
||||
LookupResult Result(*this, NameInfo, Sema::LookupOrdinaryName);
|
||||
|
||||
LookupName(Result, S);
|
||||
if (Result.getResultKind() == LookupResult::Found) {
|
||||
SourceRange SR = CBA->getCountedByFieldLoc();
|
||||
Diag(SR.getBegin(),
|
||||
diag::err_flexible_array_counted_by_attr_field_not_found_in_struct)
|
||||
<< CBA->getCountedByField() << SR;
|
||||
|
||||
SR = Result.getAsSingle<NamedDecl>()->getSourceRange();
|
||||
Diag(SR.getBegin(), diag::note_var_declared_here)
|
||||
<< Result.getAsSingle<NamedDecl>() << SR;
|
||||
} else {
|
||||
SourceRange SR = CBA->getCountedByFieldLoc();
|
||||
FieldDeclValidatorCCC CCC(RD);
|
||||
|
||||
if (TypoCorrection Corrected =
|
||||
CorrectTypo(NameInfo, Sema::LookupMemberName, /*Scope=*/nullptr,
|
||||
/*SS=*/nullptr, CCC, Sema::CTK_ErrorRecovery,
|
||||
const_cast<RecordDecl *>(RD))) {
|
||||
diagnoseTypo(
|
||||
Corrected,
|
||||
PDiag(
|
||||
diag::
|
||||
err_flexible_array_counted_by_attr_field_not_found_suggest)
|
||||
<< FieldName);
|
||||
} else {
|
||||
Diag(SR.getBegin(),
|
||||
diag::err_flexible_array_counted_by_attr_field_not_found)
|
||||
<< CBA->getCountedByField() << SR;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
|
||||
Context.getLangOpts().getStrictFlexArraysLevel();
|
||||
|
||||
if (!Decl::isFlexibleArrayMemberLike(Context, FD, FD->getType(),
|
||||
StrictFlexArraysLevel, true)) {
|
||||
// The "counted_by" attribute must be on a flexible array member.
|
||||
SourceRange SR = FD->getLocation();
|
||||
Diag(SR.getBegin(),
|
||||
diag::err_counted_by_attr_not_on_flexible_array_member)
|
||||
<< SR;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Field->hasAttr<CountedByAttr>()) {
|
||||
// The "counted_by" field can't point to the flexible array member.
|
||||
SourceRange SR = CBA->getCountedByFieldLoc();
|
||||
Diag(SR.getBegin(),
|
||||
diag::err_flexible_array_counted_by_attr_refers_to_self)
|
||||
<< CBA->getCountedByField() << SR;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!Field->getType()->isIntegerType() ||
|
||||
Field->getType()->isBooleanType()) {
|
||||
// The "counted_by" field must have an integer type.
|
||||
SourceRange SR = Field->getLocation();
|
||||
Diag(SR.getBegin(),
|
||||
diag::err_flexible_array_counted_by_attr_field_not_integer)
|
||||
<< Field << SR;
|
||||
|
||||
SR = CBA->getCountedByFieldLoc();
|
||||
Diag(SR.getBegin(), diag::note_flexible_array_counted_by_attr_field)
|
||||
<< CBA->getCountedByField() << SR;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void handleFunctionReturnThunksAttr(Sema &S, Decl *D,
|
||||
const ParsedAttr &AL) {
|
||||
StringRef KindStr;
|
||||
@ -9442,10 +9314,6 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
|
||||
handleAvailableOnlyInDefaultEvalMethod(S, D, AL);
|
||||
break;
|
||||
|
||||
case ParsedAttr::AT_CountedBy:
|
||||
handleCountedByAttr(S, D, AL);
|
||||
break;
|
||||
|
||||
// Microsoft attributes:
|
||||
case ParsedAttr::AT_LayoutVersion:
|
||||
handleLayoutVersion(S, D, AL);
|
||||
|
@ -1,227 +0,0 @@
|
||||
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 3
|
||||
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -DCOUNTED_BY -O2 -Wall -fsanitize=array-bounds,object-size,local-bounds -fstrict-flex-arrays=3 -emit-llvm -o - %s | FileCheck --check-prefix=SANITIZE-WITH-ATTR %s
|
||||
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -DCOUNTED_BY -O2 -Wall -fstrict-flex-arrays=3 -emit-llvm -o - %s | FileCheck --check-prefix=NO-SANITIZE-WITH-ATTR %s
|
||||
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -O2 -Wall -fsanitize=array-bounds,object-size,local-bounds -fstrict-flex-arrays=3 -emit-llvm -o - %s | FileCheck --check-prefix=SANITIZE-WITHOUT-ATTR %s
|
||||
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -O2 -Wall -fstrict-flex-arrays=3 -emit-llvm -o - %s | FileCheck --check-prefix=NO-SANITIZE-WITHOUT-ATTR %s
|
||||
|
||||
#if !__has_attribute(counted_by)
|
||||
#error "has attribute broken"
|
||||
#endif
|
||||
|
||||
#ifdef COUNTED_BY
|
||||
#define __counted_by(member) __attribute__((__counted_by__(member)))
|
||||
#else
|
||||
#define __counted_by(member)
|
||||
#endif
|
||||
|
||||
typedef long unsigned int size_t;
|
||||
|
||||
struct annotated {
|
||||
unsigned long flags;
|
||||
int count;
|
||||
int array[] __counted_by(count);
|
||||
};
|
||||
|
||||
// SANITIZE-WITH-ATTR-LABEL: define dso_local void @test1(
|
||||
// SANITIZE-WITH-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]], i32 noundef [[VAL:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] {
|
||||
// SANITIZE-WITH-ATTR-NEXT: entry:
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_ANNOTATED:%.*]], ptr [[P]], i64 0, i32 1
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT]], align 8, !tbaa [[TBAA2:![0-9]+]]
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[TMP1:%.*]] = sext i32 [[INDEX]] to i64, !nosanitize !6
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[TMP2:%.*]] = zext i32 [[TMP0]] to i64, !nosanitize !6
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[TMP3:%.*]] = icmp ult i64 [[TMP1]], [[TMP2]], !nosanitize !6
|
||||
// SANITIZE-WITH-ATTR-NEXT: br i1 [[TMP3]], label [[CONT7:%.*]], label [[HANDLER_OUT_OF_BOUNDS:%.*]], !prof [[PROF7:![0-9]+]], !nosanitize !6
|
||||
// SANITIZE-WITH-ATTR: handler.out_of_bounds:
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[TMP4:%.*]] = zext i32 [[INDEX]] to i64, !nosanitize !6
|
||||
// SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB2:[0-9]+]], i64 [[TMP4]]) #[[ATTR2:[0-9]+]], !nosanitize !6
|
||||
// SANITIZE-WITH-ATTR-NEXT: unreachable, !nosanitize !6
|
||||
// SANITIZE-WITH-ATTR: cont7:
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [[STRUCT_ANNOTATED]], ptr [[P]], i64 0, i32 2, i64 [[TMP1]]
|
||||
// SANITIZE-WITH-ATTR-NEXT: store i32 [[VAL]], ptr [[ARRAYIDX]], align 4, !tbaa [[TBAA2]]
|
||||
// SANITIZE-WITH-ATTR-NEXT: ret void
|
||||
//
|
||||
// NO-SANITIZE-WITH-ATTR-LABEL: define dso_local void @test1(
|
||||
// NO-SANITIZE-WITH-ATTR-SAME: ptr nocapture noundef writeonly [[P:%.*]], i32 noundef [[INDEX:%.*]], i32 noundef [[VAL:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] {
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: entry:
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [[STRUCT_ANNOTATED:%.*]], ptr [[P]], i64 0, i32 2, i64 [[IDXPROM]]
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: store i32 [[VAL]], ptr [[ARRAYIDX]], align 4, !tbaa [[TBAA2:![0-9]+]]
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: ret void
|
||||
//
|
||||
// SANITIZE-WITHOUT-ATTR-LABEL: define dso_local void @test1(
|
||||
// SANITIZE-WITHOUT-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]], i32 noundef [[VAL:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] {
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: entry:
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [[STRUCT_ANNOTATED:%.*]], ptr [[P]], i64 0, i32 2, i64 [[IDXPROM]]
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: store i32 [[VAL]], ptr [[ARRAYIDX]], align 4, !tbaa [[TBAA2:![0-9]+]]
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: ret void
|
||||
//
|
||||
// NO-SANITIZE-WITHOUT-ATTR-LABEL: define dso_local void @test1(
|
||||
// NO-SANITIZE-WITHOUT-ATTR-SAME: ptr nocapture noundef writeonly [[P:%.*]], i32 noundef [[INDEX:%.*]], i32 noundef [[VAL:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] {
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: entry:
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [[STRUCT_ANNOTATED:%.*]], ptr [[P]], i64 0, i32 2, i64 [[IDXPROM]]
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: store i32 [[VAL]], ptr [[ARRAYIDX]], align 4, !tbaa [[TBAA2:![0-9]+]]
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: ret void
|
||||
//
|
||||
void test1(struct annotated *p, int index, int val) {
|
||||
p->array[index] = val;
|
||||
}
|
||||
|
||||
// SANITIZE-WITH-ATTR-LABEL: define dso_local void @test2(
|
||||
// SANITIZE-WITH-ATTR-SAME: ptr noundef [[P:%.*]], i64 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// SANITIZE-WITH-ATTR-NEXT: entry:
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_ANNOTATED:%.*]], ptr [[P]], i64 0, i32 1
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT]], align 8, !tbaa [[TBAA2]]
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[TMP1:%.*]] = zext i32 [[TMP0]] to i64, !nosanitize !6
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[TMP2:%.*]] = icmp ugt i64 [[TMP1]], [[INDEX]], !nosanitize !6
|
||||
// SANITIZE-WITH-ATTR-NEXT: br i1 [[TMP2]], label [[CONT12:%.*]], label [[HANDLER_OUT_OF_BOUNDS:%.*]], !prof [[PROF7]], !nosanitize !6
|
||||
// SANITIZE-WITH-ATTR: handler.out_of_bounds:
|
||||
// SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB4:[0-9]+]], i64 [[INDEX]]) #[[ATTR2]], !nosanitize !6
|
||||
// SANITIZE-WITH-ATTR-NEXT: unreachable, !nosanitize !6
|
||||
// SANITIZE-WITH-ATTR: cont12:
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [[STRUCT_ANNOTATED]], ptr [[P]], i64 0, i32 2, i64 [[INDEX]]
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[TMP3:%.*]] = shl i32 [[TMP0]], 2
|
||||
// SANITIZE-WITH-ATTR-NEXT: store i32 [[TMP3]], ptr [[ARRAYIDX]], align 4, !tbaa [[TBAA2]]
|
||||
// SANITIZE-WITH-ATTR-NEXT: ret void
|
||||
//
|
||||
// NO-SANITIZE-WITH-ATTR-LABEL: define dso_local void @test2(
|
||||
// NO-SANITIZE-WITH-ATTR-SAME: ptr nocapture noundef [[P:%.*]], i64 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR1:[0-9]+]] {
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: entry:
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_ANNOTATED:%.*]], ptr [[P]], i64 0, i32 1
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT]], align 8, !tbaa [[TBAA2]]
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP1:%.*]] = shl i32 [[TMP0]], 2
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [[STRUCT_ANNOTATED]], ptr [[P]], i64 0, i32 2, i64 [[INDEX]]
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: store i32 [[TMP1]], ptr [[ARRAYIDX]], align 4, !tbaa [[TBAA2]]
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: ret void
|
||||
//
|
||||
// SANITIZE-WITHOUT-ATTR-LABEL: define dso_local void @test2(
|
||||
// SANITIZE-WITHOUT-ATTR-SAME: ptr noundef [[P:%.*]], i64 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: entry:
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [[STRUCT_ANNOTATED:%.*]], ptr [[P]], i64 0, i32 2, i64 [[INDEX]]
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: store i32 -1, ptr [[ARRAYIDX]], align 4, !tbaa [[TBAA2]]
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: ret void
|
||||
//
|
||||
// NO-SANITIZE-WITHOUT-ATTR-LABEL: define dso_local void @test2(
|
||||
// NO-SANITIZE-WITHOUT-ATTR-SAME: ptr noundef [[P:%.*]], i64 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR1:[0-9]+]] {
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: entry:
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [[STRUCT_ANNOTATED:%.*]], ptr [[P]], i64 0, i32 2, i64 [[INDEX]]
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: store i32 -1, ptr [[ARRAYIDX]], align 4, !tbaa [[TBAA2]]
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: ret void
|
||||
//
|
||||
void test2(struct annotated *p, size_t index) {
|
||||
p->array[index] = __builtin_dynamic_object_size(p->array, 1);
|
||||
}
|
||||
|
||||
// SANITIZE-WITH-ATTR-LABEL: define dso_local void @test3(
|
||||
// SANITIZE-WITH-ATTR-SAME: ptr noundef [[P:%.*]], i64 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// SANITIZE-WITH-ATTR-NEXT: entry:
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_ANNOTATED:%.*]], ptr [[P]], i64 0, i32 1
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT]], align 8, !tbaa [[TBAA2]]
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[TMP1:%.*]] = zext i32 [[TMP0]] to i64, !nosanitize !6
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[TMP2:%.*]] = icmp ugt i64 [[TMP1]], [[INDEX]], !nosanitize !6
|
||||
// SANITIZE-WITH-ATTR-NEXT: br i1 [[TMP2]], label [[CONT12:%.*]], label [[HANDLER_OUT_OF_BOUNDS:%.*]], !prof [[PROF7]], !nosanitize !6
|
||||
// SANITIZE-WITH-ATTR: handler.out_of_bounds:
|
||||
// SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB5:[0-9]+]], i64 [[INDEX]]) #[[ATTR2]], !nosanitize !6
|
||||
// SANITIZE-WITH-ATTR-NEXT: unreachable, !nosanitize !6
|
||||
// SANITIZE-WITH-ATTR: cont12:
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [[STRUCT_ANNOTATED]], ptr [[P]], i64 0, i32 2, i64 [[INDEX]]
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[TMP3:%.*]] = shl i32 [[TMP0]], 2
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[CONV:%.*]] = add i32 [[TMP3]], 16
|
||||
// SANITIZE-WITH-ATTR-NEXT: store i32 [[CONV]], ptr [[ARRAYIDX]], align 4, !tbaa [[TBAA2]]
|
||||
// SANITIZE-WITH-ATTR-NEXT: ret void
|
||||
//
|
||||
// NO-SANITIZE-WITH-ATTR-LABEL: define dso_local void @test3(
|
||||
// NO-SANITIZE-WITH-ATTR-SAME: ptr nocapture noundef [[P:%.*]], i64 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR1]] {
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: entry:
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[COUNT:%.*]] = getelementptr inbounds [[STRUCT_ANNOTATED:%.*]], ptr [[P]], i64 0, i32 1
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT]], align 8, !tbaa [[TBAA2]]
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP1:%.*]] = shl i32 [[TMP0]], 2
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[CONV:%.*]] = add i32 [[TMP1]], 16
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [[STRUCT_ANNOTATED]], ptr [[P]], i64 0, i32 2, i64 [[INDEX]]
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: store i32 [[CONV]], ptr [[ARRAYIDX]], align 4, !tbaa [[TBAA2]]
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: ret void
|
||||
//
|
||||
// SANITIZE-WITHOUT-ATTR-LABEL: define dso_local void @test3(
|
||||
// SANITIZE-WITHOUT-ATTR-SAME: ptr noundef [[P:%.*]], i64 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: entry:
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [[STRUCT_ANNOTATED:%.*]], ptr [[P]], i64 0, i32 2, i64 [[INDEX]]
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: store i32 -1, ptr [[ARRAYIDX]], align 4, !tbaa [[TBAA2]]
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: ret void
|
||||
//
|
||||
// NO-SANITIZE-WITHOUT-ATTR-LABEL: define dso_local void @test3(
|
||||
// NO-SANITIZE-WITHOUT-ATTR-SAME: ptr noundef [[P:%.*]], i64 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR1]] {
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: entry:
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [[STRUCT_ANNOTATED:%.*]], ptr [[P]], i64 0, i32 2, i64 [[INDEX]]
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: store i32 -1, ptr [[ARRAYIDX]], align 4, !tbaa [[TBAA2]]
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: ret void
|
||||
//
|
||||
void test3(struct annotated *p, size_t index) {
|
||||
// This test differs from 'test2' by checking bdos on the whole array and not
|
||||
// just the FAM.
|
||||
p->array[index] = __builtin_dynamic_object_size(p, 1);
|
||||
}
|
||||
|
||||
struct annotated_with_anon_struct {
|
||||
unsigned long flags;
|
||||
struct {
|
||||
unsigned char count;
|
||||
int array[] __counted_by(count);
|
||||
};
|
||||
};
|
||||
|
||||
// SANITIZE-WITH-ATTR-LABEL: define dso_local void @test4(
|
||||
// SANITIZE-WITH-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// SANITIZE-WITH-ATTR-NEXT: entry:
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[TMP0:%.*]] = getelementptr inbounds [[STRUCT_ANNOTATED_WITH_ANON_STRUCT:%.*]], ptr [[P]], i64 0, i32 1
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[TMP1:%.*]] = load i8, ptr [[TMP0]], align 8, !tbaa [[TBAA8:![0-9]+]]
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[TMP2:%.*]] = sext i32 [[INDEX]] to i64, !nosanitize !6
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[TMP3:%.*]] = zext i8 [[TMP1]] to i64, !nosanitize !6
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[TMP4:%.*]] = icmp ult i64 [[TMP2]], [[TMP3]], !nosanitize !6
|
||||
// SANITIZE-WITH-ATTR-NEXT: br i1 [[TMP4]], label [[CONT18:%.*]], label [[HANDLER_OUT_OF_BOUNDS:%.*]], !prof [[PROF7]], !nosanitize !6
|
||||
// SANITIZE-WITH-ATTR: handler.out_of_bounds:
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[TMP5:%.*]] = zext i32 [[INDEX]] to i64, !nosanitize !6
|
||||
// SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB6:[0-9]+]], i64 [[TMP5]]) #[[ATTR2]], !nosanitize !6
|
||||
// SANITIZE-WITH-ATTR-NEXT: unreachable, !nosanitize !6
|
||||
// SANITIZE-WITH-ATTR: cont18:
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[ARRAY:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 12
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [0 x i32], ptr [[ARRAY]], i64 0, i64 [[TMP2]]
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[TMP6:%.*]] = shl i8 [[TMP1]], 2
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[CONV:%.*]] = zext i8 [[TMP6]] to i32
|
||||
// SANITIZE-WITH-ATTR-NEXT: store i32 [[CONV]], ptr [[ARRAYIDX]], align 4, !tbaa [[TBAA2]]
|
||||
// SANITIZE-WITH-ATTR-NEXT: ret void
|
||||
//
|
||||
// NO-SANITIZE-WITH-ATTR-LABEL: define dso_local void @test4(
|
||||
// NO-SANITIZE-WITH-ATTR-SAME: ptr nocapture noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR1]] {
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: entry:
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP0:%.*]] = getelementptr inbounds [[STRUCT_ANNOTATED_WITH_ANON_STRUCT:%.*]], ptr [[P]], i64 0, i32 1
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP1:%.*]] = load i8, ptr [[TMP0]], align 8, !tbaa [[TBAA6:![0-9]+]]
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP2:%.*]] = shl i8 [[TMP1]], 2
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[CONV:%.*]] = zext i8 [[TMP2]] to i32
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[ARRAY:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 12
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [0 x i32], ptr [[ARRAY]], i64 0, i64 [[IDXPROM]]
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: store i32 [[CONV]], ptr [[ARRAYIDX]], align 4, !tbaa [[TBAA2]]
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: ret void
|
||||
//
|
||||
// SANITIZE-WITHOUT-ATTR-LABEL: define dso_local void @test4(
|
||||
// SANITIZE-WITHOUT-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: entry:
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: [[ARRAY:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 12
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [0 x i32], ptr [[ARRAY]], i64 0, i64 [[IDXPROM]]
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: store i32 -1, ptr [[ARRAYIDX]], align 4, !tbaa [[TBAA2]]
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: ret void
|
||||
//
|
||||
// NO-SANITIZE-WITHOUT-ATTR-LABEL: define dso_local void @test4(
|
||||
// NO-SANITIZE-WITHOUT-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR1]] {
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: entry:
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: [[ARRAY:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 12
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [0 x i32], ptr [[ARRAY]], i64 0, i64 [[IDXPROM]]
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: store i32 -1, ptr [[ARRAYIDX]], align 4, !tbaa [[TBAA2]]
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: ret void
|
||||
//
|
||||
void test4(struct annotated_with_anon_struct *p, int index) {
|
||||
p->array[index] = __builtin_dynamic_object_size(p->array, 1);
|
||||
}
|
@ -56,7 +56,6 @@
|
||||
// CHECK-NEXT: ConsumableAutoCast (SubjectMatchRule_record)
|
||||
// CHECK-NEXT: ConsumableSetOnRead (SubjectMatchRule_record)
|
||||
// CHECK-NEXT: Convergent (SubjectMatchRule_function)
|
||||
// CHECK-NEXT: CountedBy (SubjectMatchRule_field)
|
||||
// CHECK-NEXT: DLLExport (SubjectMatchRule_function, SubjectMatchRule_variable, SubjectMatchRule_record, SubjectMatchRule_objc_interface)
|
||||
// CHECK-NEXT: DLLImport (SubjectMatchRule_function, SubjectMatchRule_variable, SubjectMatchRule_record, SubjectMatchRule_objc_interface)
|
||||
// CHECK-NEXT: Destructor (SubjectMatchRule_function)
|
||||
|
@ -1,42 +0,0 @@
|
||||
// RUN: %clang_cc1 -fstrict-flex-arrays=3 -fsyntax-only -verify %s
|
||||
|
||||
#define __counted_by(f) __attribute__((counted_by(f)))
|
||||
|
||||
struct bar;
|
||||
|
||||
struct not_found {
|
||||
int count;
|
||||
struct bar *fam[] __counted_by(bork); // expected-error {{field 'bork' in 'counted_by' not found}}
|
||||
};
|
||||
|
||||
struct not_found_suggest {
|
||||
int bork; // expected-note {{'bork' declared here}}
|
||||
struct bar *fam[] __counted_by(blork); // expected-error {{field 'blork' in 'counted_by' not found; did you mean 'bork'?}}
|
||||
};
|
||||
|
||||
int global; // expected-note {{variable 'global' is declared here}}
|
||||
|
||||
struct found_outside_of_struct {
|
||||
int bork;
|
||||
struct bar *fam[] __counted_by(global); // expected-error {{field 'global' in 'counted_by' is not found in struct}}
|
||||
};
|
||||
|
||||
struct self_referrential {
|
||||
int bork;
|
||||
struct bar *self[] __counted_by(self); // expected-error {{field 'self' in 'counted_by' cannot refer to the flexible array}}
|
||||
};
|
||||
|
||||
struct non_int {
|
||||
double non_integer; // expected-error {{field 'non_integer' in 'counted_by' is not a non-boolean integer type}}
|
||||
struct bar *fam[] __counted_by(non_integer); // expected-note {{field 'non_integer' declared here}}
|
||||
};
|
||||
|
||||
struct array_of_ints {
|
||||
int non_integer[2]; // expected-error {{field 'non_integer' in 'counted_by' is not a non-boolean integer type}}
|
||||
struct bar *fam[] __counted_by(non_integer); // expected-note {{field 'non_integer' declared here}}
|
||||
};
|
||||
|
||||
struct not_a_fam {
|
||||
double non_integer;
|
||||
struct bar *non_fam __counted_by(non_integer); // expected-error {{'counted_by' only applies to flexible array members}}
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user