selfhost/cmd/wcc/cgen: size amalloc slots to struct, not sizeof(str)@16

Nine sites used hardcoded byte counts sized for str=16. With str's
in-memory size invariant about to grow under #1, the next-pointer or
field write would land past the slot and corrupt the next bump
allocation — selfhost/CLAUDE.md flags this exact pattern. Over-alloc
by 8B is harmless under the bump allocator, so bumping the constants
is correct at str=16 too.

Sites: fnret, enumtype, modent (×4), ffi, strlit, enummember slot
sizes; loopendbuf, loopcontbuf, yieldbuf LOOP_MAX strides.

Latent bug found by str-size-hang-debug worker via PC trace on a
str=24 probe: fnretlookup spun forever because the frnext write
fell into the string heap, forming a cycle. Fix verified at str=16
(130/130 + 994/995) and probed at str=24 (994 still green; further
graduation work tracked by #1).
This commit is contained in:
2026-05-19 22:09:47 +09:00
parent 61705fb39e
commit 65db360b91
3 changed files with 72 additions and 36 deletions

View File

@@ -19854,7 +19854,8 @@ fn collectenums(c: *cgen, file: *node) void = {
let body: *node = d.lhs;
if (body != nil) {
if (body.kind == nkind.N_TENUM) {
let et: *enumtype = amalloc(c.a, 64u64): *enumtype;
// #35: sizeof(enumtype) = 72 under str=24 ({str,str,*node,*enummember,*enumtype}); amalloc < struct corrupts the next slot.
let et: *enumtype = amalloc(c.a, 80u64): *enumtype;
et.ename = d.str;
et.emod = d.nmod;
et.storage = body.lhs;
@@ -19873,7 +19874,8 @@ fn collectenums(c: *cgen, file: *node) void = {
};
};
prev = val;
let em: *enummember = amalloc(c.a, 32u64): *enummember;
// #35: sizeof(enummember) = 40 under str=24 ({str,u64,*enummember}).
let em: *enummember = amalloc(c.a, 48u64): *enummember;
em.mname = m.str;
em.mval = val;
em.emnext = nil;
@@ -20162,10 +20164,14 @@ fn cgeninit(c: *cgen, a: *arena) void = {
// persist across cgfn calls within one file. cgfile resets them
// at the start of each compilation unit.
c.looptop = 0;
c.loopendbuf = amalloc(a, (LOOP_MAX: u64) * 16u64): *str;
c.loopcontbuf = amalloc(a, (LOOP_MAX: u64) * 16u64): *str;
// #35: per-slot stride is sizeof(str); the bare 16 is the str=16
// width and undershoots under str=24, so indices past (LOOP_MAX*16)/24
// = 10 would spill into the next amalloc. Bumped to 24/slot; +8/slot
// over-alloc under str=16 is harmless under the bump arena.
c.loopendbuf = amalloc(a, (LOOP_MAX: u64) * 24u64): *str;
c.loopcontbuf = amalloc(a, (LOOP_MAX: u64) * 24u64): *str;
c.yieldtop = 0;
c.yieldbuf = amalloc(a, (LOOP_MAX: u64) * 16u64): *str;
c.yieldbuf = amalloc(a, (LOOP_MAX: u64) * 24u64): *str;
c.defertop = 0;
c.deferbuf = amalloc(a, (DEFER_MAX: u64) * 8u64): **node;
};
@@ -20521,7 +20527,8 @@ fn internstrlit(c: *cgen, bytes: str) str = {
let lab: str;
lab.ptr = p;
lab.len = total;
let nw: *strlit = amalloc(c.a, 48u64): *strlit;
// #35: sizeof(strlit) = 56 under str=24 ({str,str,*strlit}).
let nw: *strlit = amalloc(c.a, 64u64): *strlit;
nw.label = lab;
nw.bytes = bytes;
nw.slnext = c.strlits;
@@ -21250,7 +21257,8 @@ fn collectfnrets(c: *cgen, file: *node) void = {
let d: *node = file.list;
for (d != nil) {
if (d.kind == nkind.N_FNDECL) {
let f: *fnret = amalloc(c.a, 64u64): *fnret;
// #35: sizeof(fnret) = 72 under str=24 ({str,str,*node,*node,*fnret}); was the trigger that hung w6c_ww on the hangtest — frnext overflowed into the next amalloc, breaking fnretlookup's chain walk.
let f: *fnret = amalloc(c.a, 80u64): *fnret;
f.fname = d.str;
f.fmod = d.nmod;
f.rtype = d.lhs;
@@ -21490,6 +21498,9 @@ type modent = struct {
mnext: *modent,
};
// #35: sizeof(modent) = 56 under str=24 ({str,str,*modent}); each
// of the four amallocs below grew from 48 to 64 to keep mnext inside
// the slot.
fn collectmods(c: *cgen, file: *node) void = {
c.mods = nil;
if (file == nil) { return; };
@@ -21513,7 +21524,7 @@ fn collectmods(c: *cgen, file: *node) void = {
};
if (!isffi) {
if (!streq(d.str, "main")) {
let m: *modent = amalloc(c.a, 48u64): *modent;
let m: *modent = amalloc(c.a, 64u64): *modent;
m.mname = d.str;
m.nmod = d.nmod;
m.mnext = c.mods;
@@ -21525,7 +21536,7 @@ fn collectmods(c: *cgen, file: *node) void = {
if (d.kind == nkind.N_DEF) {
if (d.exported == 0) {
if (d.nmod.len > 0) {
let m: *modent = amalloc(c.a, 48u64): *modent;
let m: *modent = amalloc(c.a, 64u64): *modent;
m.mname = d.str;
m.nmod = d.nmod;
m.mnext = c.mods;
@@ -21536,7 +21547,7 @@ fn collectmods(c: *cgen, file: *node) void = {
if (d.kind == nkind.N_TYPEDECL) {
if (d.exported == 0) {
if (d.nmod.len > 0) {
let m: *modent = amalloc(c.a, 48u64): *modent;
let m: *modent = amalloc(c.a, 64u64): *modent;
m.mname = d.str;
m.nmod = d.nmod;
m.mnext = c.mods;
@@ -21547,7 +21558,7 @@ fn collectmods(c: *cgen, file: *node) void = {
if (d.kind == nkind.N_LET) {
if (d.exported == 0) {
if (d.nmod.len > 0) {
let m: *modent = amalloc(c.a, 48u64): *modent;
let m: *modent = amalloc(c.a, 64u64): *modent;
m.mname = d.str;
m.nmod = d.nmod;
m.mnext = c.mods;
@@ -21653,7 +21664,8 @@ fn fficollect(c: *cgen, file: *node) void = {
let symnode: *node = a.list;
if (symnode != nil) {
if (symnode.kind == nkind.N_STRLIT) {
let f: *ffi = amalloc(c.a, 48u64): *ffi;
// #35: sizeof(ffi) = 56 under str=24 ({str,str,*ffi}).
let f: *ffi = amalloc(c.a, 64u64): *ffi;
f.ident = d.str;
f.symbol = symnode.str;
f.fnext = c.ffis;