Skip to content

[Clang][counted_by] Don't treat a __bdos argument as an array if it isn't #125298

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 25 additions & 6 deletions clang/lib/CodeGen/CGBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<int64_t> 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;
Expand Down Expand Up @@ -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);
Expand Down
85 changes: 85 additions & 0 deletions clang/test/CodeGen/attr-counted-by-bug.c
Original file line number Diff line number Diff line change
@@ -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);
}