diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index 11fa295dad952..a57fc8109fa1a 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -1319,12 +1319,37 @@ CodeGenFunction::emitCountedByMemberSize(const Expr *E, llvm::Value *EmittedE, // size_t field_offset = offsetof (struct s, field); Value *FieldOffset = nullptr; + llvm::ConstantInt *FieldBaseSize = nullptr; if (FlexibleArrayMemberFD != FD) { std::optional Offset = GetFieldOffset(Ctx, RD, FD); if (!Offset) return nullptr; FieldOffset = llvm::ConstantInt::get(ResType, *Offset / CharWidth, IsSigned); + + if (Idx) { + // From option (4): + // size_t field_base_size = sizeof (*ptr->field_array); + if (!FieldTy->isArrayType()) + // The field isn't an array. It could be a pointer, for example: + // + // struct { + // int count; + // char *string; + // int array[] __counted_by(count); + // } x; + // + // __builtin_dynamic_object_size(&x.string[42], 0); + // + // This __bdos isn't wanting the size of the struct, but the size of + // 'string's allocation, which __bdos isn't currently able to provide. + return nullptr; + + const ArrayType *ArrayTy = Ctx.getAsArrayType(FieldTy); + CharUnits BaseSize = Ctx.getTypeSizeInChars(ArrayTy->getElementType()); + FieldBaseSize = + llvm::ConstantInt::get(ResType, BaseSize.getQuantity(), IsSigned); + } } // size_t count = (size_t) ptr->count; @@ -1376,12 +1401,6 @@ CodeGenFunction::emitCountedByMemberSize(const Expr *E, llvm::Value *EmittedE, llvm::ConstantInt::get(ResType, Size.getKnownMinValue() / CharWidth); if (Idx) { // Option (4) '&ptr->field_array[idx]' - // size_t field_base_size = sizeof (*ptr->field_array); - const ArrayType *ArrayTy = Ctx.getAsArrayType(FieldTy); - CharUnits BaseSize = Ctx.getTypeSizeInChars(ArrayTy->getElementType()); - auto *FieldBaseSize = - llvm::ConstantInt::get(ResType, BaseSize.getQuantity(), IsSigned); - // field_offset += index * field_base_size; Value *Mul = Builder.CreateMul(Index, FieldBaseSize, "field_offset", !IsSigned, IsSigned); diff --git a/clang/test/CodeGen/attr-counted-by-bug.c b/clang/test/CodeGen/attr-counted-by-bug.c new file mode 100644 index 0000000000000..9d76edf30eece --- /dev/null +++ b/clang/test/CodeGen/attr-counted-by-bug.c @@ -0,0 +1,85 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 +// RUN: %clang_cc1 -triple aarch64-unknown-linux-gnu -DCOUNTED_BY -O2 -Wall -Wno-int-conversion -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 aarch64-unknown-linux-gnu -DCOUNTED_BY -O2 -Wall -Wno-int-conversion -fsanitize=array-bounds,object-size,local-bounds -fstrict-flex-arrays=3 -emit-llvm -o - %s | FileCheck --check-prefix=NO-SANITIZE-WITH-ATTR %s +// RUN: %clang_cc1 -triple aarch64-unknown-linux-gnu -O2 -Wall -Wno-int-conversion -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 aarch64-unknown-linux-gnu -O2 -Wall -Wno-int-conversion -fsanitize=array-bounds,object-size,local-bounds -fstrict-flex-arrays=3 -emit-llvm -o - %s | FileCheck --check-prefix=NO-SANITIZE-WITHOUT-ATTR %s + +// See https://github.com/llvm/llvm-project/pull/122198#issuecomment-2627868702 + +#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 + +#define __bdos(P) __builtin_dynamic_object_size(P, 0) + +typedef long unsigned int size_t; + +struct test1_struct { + int a; + char *b; + char c[] __counted_by(a); +} d; + +// SANITIZE-WITH-ATTR-LABEL: define dso_local i64 @test1( +// SANITIZE-WITH-ATTR-SAME: ) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// SANITIZE-WITH-ATTR-NEXT: [[ENTRY:.*:]] +// SANITIZE-WITH-ATTR-NEXT: ret i64 -1 +// +// NO-SANITIZE-WITH-ATTR-LABEL: define dso_local i64 @test1( +// NO-SANITIZE-WITH-ATTR-SAME: ) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// NO-SANITIZE-WITH-ATTR-NEXT: [[ENTRY:.*:]] +// NO-SANITIZE-WITH-ATTR-NEXT: ret i64 -1 +// +// SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test1( +// SANITIZE-WITHOUT-ATTR-SAME: ) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// SANITIZE-WITHOUT-ATTR-NEXT: [[ENTRY:.*:]] +// SANITIZE-WITHOUT-ATTR-NEXT: ret i64 -1 +// +// NO-SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test1( +// NO-SANITIZE-WITHOUT-ATTR-SAME: ) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// NO-SANITIZE-WITHOUT-ATTR-NEXT: [[ENTRY:.*:]] +// NO-SANITIZE-WITHOUT-ATTR-NEXT: ret i64 -1 +// +size_t test1(void) { + return __builtin_dynamic_object_size(d.b[4], 0); +} + +typedef struct { + char __padding[0]; +} spinlock_t; +struct { + int priv_len; + spinlock_t addr_list_lock; + char *dev_addr; + char priv[] __attribute__((__counted_by__(priv_len))); +} x; + +// SANITIZE-WITH-ATTR-LABEL: define dso_local i64 @test2( +// SANITIZE-WITH-ATTR-SAME: ) local_unnamed_addr #[[ATTR1:[0-9]+]] { +// SANITIZE-WITH-ATTR-NEXT: [[ENTRY:.*:]] +// SANITIZE-WITH-ATTR-NEXT: ret i64 -1 +// +// NO-SANITIZE-WITH-ATTR-LABEL: define dso_local i64 @test2( +// NO-SANITIZE-WITH-ATTR-SAME: ) local_unnamed_addr #[[ATTR1:[0-9]+]] { +// NO-SANITIZE-WITH-ATTR-NEXT: [[ENTRY:.*:]] +// NO-SANITIZE-WITH-ATTR-NEXT: ret i64 -1 +// +// SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test2( +// SANITIZE-WITHOUT-ATTR-SAME: ) local_unnamed_addr #[[ATTR1:[0-9]+]] { +// SANITIZE-WITHOUT-ATTR-NEXT: [[ENTRY:.*:]] +// SANITIZE-WITHOUT-ATTR-NEXT: ret i64 -1 +// +// NO-SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test2( +// NO-SANITIZE-WITHOUT-ATTR-SAME: ) local_unnamed_addr #[[ATTR1:[0-9]+]] { +// NO-SANITIZE-WITHOUT-ATTR-NEXT: [[ENTRY:.*:]] +// NO-SANITIZE-WITHOUT-ATTR-NEXT: ret i64 -1 +// +size_t test2() { + return __builtin_dynamic_object_size(&x.dev_addr[4], 1); +}