Skip to content

Commit d674b28

Browse files
committed
A bitfield width must not exceed the bit width of its underlying type
C requires 1 <= width <= sizeof(type)*CHAR_BIT). This must also be consistent with what the printer can emit, or the type->string->type round trip fuzzer would break.
1 parent 7bf599e commit d674b28

3 files changed

Lines changed: 46 additions & 2 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1515

1616
- Bitfield member layout now records the storage-unit base in `member->offset` with `bit_offset` relative to that unit, instead of pointing `member->offset` at the bitfield's own (possibly mid-unit) byte with a `% 8` bit remainder. Callers that read or write a whole storage unit, like Affix's marshaller and pinned bitfield magic, always stay within the aggregate's bounds now; previously a bitfield whose low bit was not byte-aligned could drive a unit-sized access one byte past the end of the struct (heap-buffer-overflow caught by ASan).
1717
- JIT executable memory is now flushed with `FlushInstructionCache` whenever built on Windows, including clang builds.
18+
- Bitfield widths are validated against the underlying type's bit size (1 to `sizeof(type) * 8`), so widths that C allows for 128-bit types (e.g. `sint128:93`) no longer get rejected after a type→string→type round trip, while still-invalid widths (larger than the type) are refused. A flat cap of 64 had made the `fuzz_roundtrip` harness abort on `{fuzz_bf:sint128:93,...}` because the printer emitted a width the parser would not accept again.
1819

1920
## [0.2.1] - 2026-08-04
2021

src/core/signature.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,12 @@ static infix_struct_member * parse_aggregate_members(parser_state * state, char
294294
size_t width_val = 0;
295295
if (!parse_size_t(state, &width_val))
296296
return nullptr; // Error set by parse_size_t
297-
if (width_val > 64) {
297+
size_t type_bits = member_type->size * 8;
298+
// Unresolved named reference (size 0) or a huge base type: cap at the
299+
// uint8_t storage limit so the width is never truncated.
300+
if (type_bits == 0 || type_bits > 255)
301+
type_bits = 255;
302+
if (width_val > type_bits) {
298303
_infix_set_parser_error(state, INFIX_CODE_TYPE_TOO_LARGE);
299304
return nullptr;
300305
}

t/011_bitfields.c

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ void bitfield_handler(infix_reverse_t * ctx, void * ret, void ** args) {
3939
}
4040

4141
TEST {
42-
plan(2);
42+
plan(3);
4343
subtest("Basic Bitfields") {
4444
plan(8);
4545
infix_arena_t * arena = NULL;
@@ -111,4 +111,42 @@ TEST {
111111

112112
infix_arena_destroy(arena);
113113
}
114+
subtest("Wide Bitfield Round Trip") {
115+
// Regression (fuzz_roundtrip crash 9421a219): bitfield widths up to the
116+
// underlying type's bit size must survive parse -> print -> re-parse.
117+
// A flat cap of 64 rejected valid 128-bit-type widths (e.g. `sint128:93`).
118+
plan(13);
119+
const char * sigs[] = {
120+
"{fuzz_bf:sint128:93,fuzz:*void}",
121+
"{a:sint128:128}",
122+
"{a:sint64:64}",
123+
"{a:sint8:8}",
124+
};
125+
for (size_t i = 0; i < sizeof(sigs) / sizeof(sigs[0]); ++i) {
126+
infix_type * t = NULL;
127+
infix_arena_t * a = NULL;
128+
infix_status s = infix_type_from_signature(&t, &a, sigs[i], NULL);
129+
ok(s == INFIX_SUCCESS, "Parsed %s", sigs[i]);
130+
if (s == INFIX_SUCCESS) {
131+
char buf[512];
132+
infix_status p = infix_type_print(buf, sizeof(buf), t, INFIX_DIALECT_SIGNATURE);
133+
ok(p == INFIX_SUCCESS, "Printed %s", sigs[i]);
134+
infix_type * t2 = NULL;
135+
infix_arena_t * a2 = NULL;
136+
infix_status r = infix_type_from_signature(&t2, &a2, buf, NULL);
137+
ok(r == INFIX_SUCCESS, "Re-parsed printed output %s", sigs[i]);
138+
if (a2)
139+
infix_arena_destroy(a2);
140+
}
141+
if (a)
142+
infix_arena_destroy(a);
143+
}
144+
// Over-wide bitfields must still be rejected.
145+
infix_type * bad = NULL;
146+
infix_arena_t * bad_arena = NULL;
147+
infix_status bs = infix_type_from_signature(&bad, &bad_arena, "{a:sint64:65}", NULL);
148+
ok(bs != INFIX_SUCCESS, "Rejected width beyond underlying type ({a:sint64:65})");
149+
if (bad_arena)
150+
infix_arena_destroy(bad_arena);
151+
}
114152
}

0 commit comments

Comments
 (0)