https://github.com/Z3rox-dev updated https://github.com/llvm/llvm-project/pull/181508
>From 4112a0f6b81f1f8a8cb1179d950cc62330014524 Mon Sep 17 00:00:00 2001 From: "Giovanni B." <[email protected]> Date: Sat, 14 Feb 2026 21:59:04 +0100 Subject: [PATCH 1/3] [Clang][CodeGen] Clip bitfield storage on zero-width boundary with #pragma pack --- clang/lib/CodeGen/CGRecordLayoutBuilder.cpp | 23 +++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/clang/lib/CodeGen/CGRecordLayoutBuilder.cpp b/clang/lib/CodeGen/CGRecordLayoutBuilder.cpp index e9205c68c2812..d47929fb1cde3 100644 --- a/clang/lib/CodeGen/CGRecordLayoutBuilder.cpp +++ b/clang/lib/CodeGen/CGRecordLayoutBuilder.cpp @@ -424,6 +424,29 @@ CGRecordLowering::accumulateBitFields(bool isNonVirtualBaseType, for (; Field != FieldEnd && Field->isBitField(); ++Field) { // Zero-width bitfields end runs. if (Field->isZeroLengthBitField()) { + // If the current run's storage extends beyond this zero-width + // bitfield, clip it to prevent overlap with subsequent members. + // This can happen with #pragma pack when the packed alignment is + // smaller than the bitfield's formal type, causing the storage + // unit to extend into the next run's territory. + if (Run != FieldEnd) { + uint64_t BitOffset = getFieldBitOffset(*Field); + if (Tail > BitOffset) { + CharUnits RunBytes = bitsToCharUnits(BitOffset) - + bitsToCharUnits(StartBitOffset); + llvm::Type *ClippedType = getByteArrayType(RunBytes); + // Walk backward through Members to find and replace the + // storage member for the current run. + CharUnits RunStart = bitsToCharUnits(StartBitOffset); + for (size_t I = Members.size(); I > 0; --I) { + if (Members[I - 1].Data && + Members[I - 1].Offset == RunStart) { + Members[I - 1].Data = ClippedType; + break; + } + } + } + } Run = FieldEnd; continue; } >From d1ea93f499ccb3c4a72fc64befbc61ce834eb297 Mon Sep 17 00:00:00 2001 From: "Giovanni B." <[email protected]> Date: Sat, 14 Feb 2026 21:59:27 +0100 Subject: [PATCH 2/3] Add regression test for #181505 --- .../CodeGen/ms_struct-pack-bitfield-clip.c | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 clang/test/CodeGen/ms_struct-pack-bitfield-clip.c diff --git a/clang/test/CodeGen/ms_struct-pack-bitfield-clip.c b/clang/test/CodeGen/ms_struct-pack-bitfield-clip.c new file mode 100644 index 0000000000000..dc49cbb1f183a --- /dev/null +++ b/clang/test/CodeGen/ms_struct-pack-bitfield-clip.c @@ -0,0 +1,62 @@ +// RUN: %clang_cc1 -emit-llvm-only -triple x86_64-w64-windows-gnu %s +// RUN: %clang_cc1 -emit-llvm-only -triple i686-w64-windows-gnu %s +// RUN: %clang_cc1 -emit-llvm-only -triple i386-apple-darwin9 %s + +// Regression test: when #pragma pack(N) with N < alignof(int) is combined +// with ms_struct layout and a zero-width bitfield, the bitfield storage unit +// could extend past the zero-width bitfield boundary, overlapping the next +// member's storage and triggering the "Bitfield access unit is not clipped" +// assertion in checkBitfieldClipping(). + +// Minimal case: pack(1) + char + bitfield + :0 + bitfield +#pragma pack(1) +struct S0 { + char f1; + unsigned f3 : 1; + unsigned : 0; + unsigned f4 : 1; +} __attribute__((__ms_struct__)); +struct S0 s0; + +// Variation: pack(1) with more complex bitfield layout (original CSmith case) +struct S1 { + signed f0 : 28; + const signed char f1; + const volatile signed f2 : 25; + const unsigned f3 : 23; + unsigned : 0; + unsigned f4 : 12; + volatile unsigned f5 : 29; +} __attribute__((__ms_struct__)); +struct S1 s1; + +// Variation: pack(2) with short-sized first member +#pragma pack(2) +struct S2 { + short f1; + unsigned f3 : 1; + unsigned : 0; + unsigned f4 : 1; +} __attribute__((__ms_struct__)); +struct S2 s2; + +// Variation: pack(1), multiple zero-width bitfields +#pragma pack(1) +struct S3 { + char f1; + unsigned f3 : 1; + unsigned : 0; + unsigned f4 : 1; + unsigned : 0; + unsigned f5 : 1; +} __attribute__((__ms_struct__)); +struct S3 s3; + +// Variation: pack(1), zero-width of different type +struct S4 { + char f1; + unsigned f3 : 1; + short : 0; + unsigned f4 : 1; +} __attribute__((__ms_struct__)); +struct S4 s4; >From e41a5625a99f7fa47d5498f5cedfaafbb823695a Mon Sep 17 00:00:00 2001 From: "Giovanni B." <[email protected]> Date: Sat, 14 Feb 2026 23:09:45 +0100 Subject: [PATCH 3/3] Address review: add RunBytes zero guard and assert on member lookup --- clang/lib/CodeGen/CGRecordLayoutBuilder.cpp | 24 +++++++++++++-------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/clang/lib/CodeGen/CGRecordLayoutBuilder.cpp b/clang/lib/CodeGen/CGRecordLayoutBuilder.cpp index d47929fb1cde3..f7b3e0dcb8b23 100644 --- a/clang/lib/CodeGen/CGRecordLayoutBuilder.cpp +++ b/clang/lib/CodeGen/CGRecordLayoutBuilder.cpp @@ -434,16 +434,22 @@ CGRecordLowering::accumulateBitFields(bool isNonVirtualBaseType, if (Tail > BitOffset) { CharUnits RunBytes = bitsToCharUnits(BitOffset) - bitsToCharUnits(StartBitOffset); - llvm::Type *ClippedType = getByteArrayType(RunBytes); - // Walk backward through Members to find and replace the - // storage member for the current run. - CharUnits RunStart = bitsToCharUnits(StartBitOffset); - for (size_t I = Members.size(); I > 0; --I) { - if (Members[I - 1].Data && - Members[I - 1].Offset == RunStart) { - Members[I - 1].Data = ClippedType; - break; + if (!RunBytes.isZero()) { + llvm::Type *ClippedType = getByteArrayType(RunBytes); + // Walk backward through Members to find and replace the + // storage member for the current run. + CharUnits RunStart = bitsToCharUnits(StartBitOffset); + bool Found = false; + for (size_t I = Members.size(); I > 0; --I) { + if (Members[I - 1].Data && + Members[I - 1].Offset == RunStart) { + Members[I - 1].Data = ClippedType; + Found = true; + break; + } } + assert(Found && + "Failed to find storage member to clip"); } } } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
