w6c_ww/cgen: uniform tinfo esz for global str/slice addr-of + store (fix #11)
Three sibling arms of the #10 global-str/slice INDEX miscompile (23670d7,
the READ path) shared the identical N_TARRAY/N_TPTR tnode-KIND whitelist in
their global-ident resolution arm and were still LIVE and silently cs!=ww:
- cgun `&s[1]` / `&g[1]` (cgenexpr.ww N_INDEX addr-of) — a global str
(tnode N_TNAME) / slice (N_TSLICE) matched neither arm, so esz stayed at
the default 8 and the base fell to the complex-base fallback: a wide
{ptr,len,cap} header + 8-byte stride instead of MOVQ name(SB) (.ptr) +
ADDQ.
- cgassign `g[1] = v` store AND `g[1] OP= v` compound (two arms) — same
whitelist; a global slice store emitted a full-word MOVQ at an 8-byte
stride: an 8-BYTE OUT-OF-BOUNDS WRITE past a 1-byte element (memory
corruption) instead of MOVB at .ptr+1.
cstage (cmd/w6c/cgen.c) is the runtime-correct reference and was already
uniform across all three: esz off idx_eff(base->type)->sub->size and the
base load gated by is_arr (TY_ARRAY -> LEAQ name(SB), every other -> MOVQ
name(SB), since a str/slice's .ptr IS the symbol's first word). Align the
wwstage UP to that, mirroring the just-landed cgindex template (#10): resolve
esz via elemsizeofc with no kind gate, dispatch the base by N_TARRAY ? LEAQ :
MOVQ name(SB). The store/compound arms also resolve elemtn exactly like their
local branch (element node for ARRAY/SLICE/PTR; nil for str so tnodestoreop
picks MOVB) so a global []str store routes to the 3-word header store and the
compound arm's str/slice hard-error still fires.
Close-by-construction: the global element base/stride is now computed off the
resolved type at every wwstage index site — read (cgindex, #10), addr-of
(cgun), store + compound (cgassign) — with no remaining tnode-kind whitelist.
cgslice/cgbaselen already resolved via elemsizeofc.
803_globalidx_run extends from 9 to 18 rows: global str/slice addr-of (read
back through the pointer), global slice store AND compound store `g[i] OP= v`
(the distinct third fixed arm, with adjacent-element addends as the OOB-write
guard on both), a WIDTH>1 signed variant of each (esz=4 stride/store-width pin),
and local addr-of/store regression pins. Runtime (cstage build+run) + cs==ww
byte-id per row. combined.ww embeds (w6c + wwdump) regenerate.
This commit is contained in:
@@ -23170,17 +23170,26 @@ fn cgun(c: *cgen, n: *node) void = {
|
||||
if (tn.kind == nkind.N_TARRAY) { isarr = true; };
|
||||
};
|
||||
} else {
|
||||
// #11: addr-of twin of the #10 cgindex read
|
||||
// fix. Dispatch esz + base load off the
|
||||
// global's RESOLVED type, NOT an N_TARRAY/
|
||||
// N_TPTR kind whitelist — a global str (tnode
|
||||
// N_TNAME) / slice (N_TSLICE) matched NEITHER
|
||||
// old arm, so esz stayed at the default 8 and
|
||||
// the base fell to the complex-base fallback,
|
||||
// yielding a wide-stride &s[i]. cstage's
|
||||
// TK_AMP N_INDEX (cmd/w6c/cgen.c) is uniform:
|
||||
// esz=bu->sub->size, base is_arr?LEAQ:MOVQ
|
||||
// name(SB) (a str/slice's .ptr IS the symbol's
|
||||
// first word). Align UP, mirroring cgindex.
|
||||
let tn: *node = letvartnode(c, base.str);
|
||||
if (tn != nil) {
|
||||
globalname = base.str;
|
||||
esz = elemsizeofc(c, tn);
|
||||
if (tn.kind == nkind.N_TARRAY) {
|
||||
isglobalarr = true;
|
||||
globalname = base.str;
|
||||
esz = elemsizeofc(c, tn);
|
||||
};
|
||||
if (tn.kind == nkind.N_TPTR) {
|
||||
} else {
|
||||
isglobalptr = true;
|
||||
globalname = base.str;
|
||||
esz = elemsizeofc(c, tn);
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -24797,19 +24806,34 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
if (bk == nkind.N_TPTR) { elemtn = btn.lhs; };
|
||||
};
|
||||
} else {
|
||||
// #11: store/compound twin of the #10 cgindex
|
||||
// read fix. A global str/slice element store hit
|
||||
// the same kind whitelist — N_TNAME (str) /
|
||||
// N_TSLICE matched NEITHER arm, so esz stayed 8
|
||||
// and the store emitted a full-word MOVQ — an
|
||||
// 8-byte OUT-OF-BOUNDS write past a 1-byte
|
||||
// element — instead of MOVB. cstage
|
||||
// (cmd/w6c/cgen.c N_INDEX store) dispatches esz
|
||||
// off idx_eff->sub->size + the elem-kind flags
|
||||
// off eff->sub uniformly, base is_arr?LEAQ:MOVQ
|
||||
// name(SB). Align UP and resolve elemtn exactly
|
||||
// like the local branch above (element node for
|
||||
// ARRAY/SLICE/PTR; nil for str so tnodestoreop
|
||||
// picks MOVB on the store arm, and the compound
|
||||
// arm's str/slice hard-error still fires on a
|
||||
// []str element).
|
||||
let tn: *node = letvartnode(c, bn);
|
||||
if (tn != nil) {
|
||||
if (tn.kind == nkind.N_TARRAY) {
|
||||
globalname = bn;
|
||||
esz = elemsizeofc(c, tn);
|
||||
let bk: nkind = tn.kind;
|
||||
if (bk == nkind.N_TARRAY) {
|
||||
isglobalarr = true;
|
||||
globalname = bn;
|
||||
esz = elemsizeofc(c, tn);
|
||||
elemtn = tn.lhs;
|
||||
};
|
||||
if (tn.kind == nkind.N_TPTR) {
|
||||
} else {
|
||||
isglobalptr = true;
|
||||
globalname = bn;
|
||||
esz = elemsizeofc(c, tn);
|
||||
elemtn = tn.lhs;
|
||||
if (bk == nkind.N_TSLICE) { elemtn = tn.lhs; };
|
||||
if (bk == nkind.N_TPTR) { elemtn = tn.lhs; };
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -25229,19 +25253,34 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
if (bk == nkind.N_TPTR) { elemtn = btn.lhs; };
|
||||
};
|
||||
} else {
|
||||
// #11: store/compound twin of the #10 cgindex
|
||||
// read fix. A global str/slice element store hit
|
||||
// the same kind whitelist — N_TNAME (str) /
|
||||
// N_TSLICE matched NEITHER arm, so esz stayed 8
|
||||
// and the store emitted a full-word MOVQ — an
|
||||
// 8-byte OUT-OF-BOUNDS write past a 1-byte
|
||||
// element — instead of MOVB. cstage
|
||||
// (cmd/w6c/cgen.c N_INDEX store) dispatches esz
|
||||
// off idx_eff->sub->size + the elem-kind flags
|
||||
// off eff->sub uniformly, base is_arr?LEAQ:MOVQ
|
||||
// name(SB). Align UP and resolve elemtn exactly
|
||||
// like the local branch above (element node for
|
||||
// ARRAY/SLICE/PTR; nil for str so tnodestoreop
|
||||
// picks MOVB on the store arm, and the compound
|
||||
// arm's str/slice hard-error still fires on a
|
||||
// []str element).
|
||||
let tn: *node = letvartnode(c, bn);
|
||||
if (tn != nil) {
|
||||
if (tn.kind == nkind.N_TARRAY) {
|
||||
globalname = bn;
|
||||
esz = elemsizeofc(c, tn);
|
||||
let bk: nkind = tn.kind;
|
||||
if (bk == nkind.N_TARRAY) {
|
||||
isglobalarr = true;
|
||||
globalname = bn;
|
||||
esz = elemsizeofc(c, tn);
|
||||
elemtn = tn.lhs;
|
||||
};
|
||||
if (tn.kind == nkind.N_TPTR) {
|
||||
} else {
|
||||
isglobalptr = true;
|
||||
globalname = bn;
|
||||
esz = elemsizeofc(c, tn);
|
||||
elemtn = tn.lhs;
|
||||
if (bk == nkind.N_TSLICE) { elemtn = tn.lhs; };
|
||||
if (bk == nkind.N_TPTR) { elemtn = tn.lhs; };
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -3446,17 +3446,26 @@ fn cgun(c: *cgen, n: *node) void = {
|
||||
if (tn.kind == nkind.N_TARRAY) { isarr = true; };
|
||||
};
|
||||
} else {
|
||||
// #11: addr-of twin of the #10 cgindex read
|
||||
// fix. Dispatch esz + base load off the
|
||||
// global's RESOLVED type, NOT an N_TARRAY/
|
||||
// N_TPTR kind whitelist — a global str (tnode
|
||||
// N_TNAME) / slice (N_TSLICE) matched NEITHER
|
||||
// old arm, so esz stayed at the default 8 and
|
||||
// the base fell to the complex-base fallback,
|
||||
// yielding a wide-stride &s[i]. cstage's
|
||||
// TK_AMP N_INDEX (cmd/w6c/cgen.c) is uniform:
|
||||
// esz=bu->sub->size, base is_arr?LEAQ:MOVQ
|
||||
// name(SB) (a str/slice's .ptr IS the symbol's
|
||||
// first word). Align UP, mirroring cgindex.
|
||||
let tn: *node = letvartnode(c, base.str);
|
||||
if (tn != nil) {
|
||||
globalname = base.str;
|
||||
esz = elemsizeofc(c, tn);
|
||||
if (tn.kind == nkind.N_TARRAY) {
|
||||
isglobalarr = true;
|
||||
globalname = base.str;
|
||||
esz = elemsizeofc(c, tn);
|
||||
};
|
||||
if (tn.kind == nkind.N_TPTR) {
|
||||
} else {
|
||||
isglobalptr = true;
|
||||
globalname = base.str;
|
||||
esz = elemsizeofc(c, tn);
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -5073,19 +5082,34 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
if (bk == nkind.N_TPTR) { elemtn = btn.lhs; };
|
||||
};
|
||||
} else {
|
||||
// #11: store/compound twin of the #10 cgindex
|
||||
// read fix. A global str/slice element store hit
|
||||
// the same kind whitelist — N_TNAME (str) /
|
||||
// N_TSLICE matched NEITHER arm, so esz stayed 8
|
||||
// and the store emitted a full-word MOVQ — an
|
||||
// 8-byte OUT-OF-BOUNDS write past a 1-byte
|
||||
// element — instead of MOVB. cstage
|
||||
// (cmd/w6c/cgen.c N_INDEX store) dispatches esz
|
||||
// off idx_eff->sub->size + the elem-kind flags
|
||||
// off eff->sub uniformly, base is_arr?LEAQ:MOVQ
|
||||
// name(SB). Align UP and resolve elemtn exactly
|
||||
// like the local branch above (element node for
|
||||
// ARRAY/SLICE/PTR; nil for str so tnodestoreop
|
||||
// picks MOVB on the store arm, and the compound
|
||||
// arm's str/slice hard-error still fires on a
|
||||
// []str element).
|
||||
let tn: *node = letvartnode(c, bn);
|
||||
if (tn != nil) {
|
||||
if (tn.kind == nkind.N_TARRAY) {
|
||||
globalname = bn;
|
||||
esz = elemsizeofc(c, tn);
|
||||
let bk: nkind = tn.kind;
|
||||
if (bk == nkind.N_TARRAY) {
|
||||
isglobalarr = true;
|
||||
globalname = bn;
|
||||
esz = elemsizeofc(c, tn);
|
||||
elemtn = tn.lhs;
|
||||
};
|
||||
if (tn.kind == nkind.N_TPTR) {
|
||||
} else {
|
||||
isglobalptr = true;
|
||||
globalname = bn;
|
||||
esz = elemsizeofc(c, tn);
|
||||
elemtn = tn.lhs;
|
||||
if (bk == nkind.N_TSLICE) { elemtn = tn.lhs; };
|
||||
if (bk == nkind.N_TPTR) { elemtn = tn.lhs; };
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -5505,19 +5529,34 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
if (bk == nkind.N_TPTR) { elemtn = btn.lhs; };
|
||||
};
|
||||
} else {
|
||||
// #11: store/compound twin of the #10 cgindex
|
||||
// read fix. A global str/slice element store hit
|
||||
// the same kind whitelist — N_TNAME (str) /
|
||||
// N_TSLICE matched NEITHER arm, so esz stayed 8
|
||||
// and the store emitted a full-word MOVQ — an
|
||||
// 8-byte OUT-OF-BOUNDS write past a 1-byte
|
||||
// element — instead of MOVB. cstage
|
||||
// (cmd/w6c/cgen.c N_INDEX store) dispatches esz
|
||||
// off idx_eff->sub->size + the elem-kind flags
|
||||
// off eff->sub uniformly, base is_arr?LEAQ:MOVQ
|
||||
// name(SB). Align UP and resolve elemtn exactly
|
||||
// like the local branch above (element node for
|
||||
// ARRAY/SLICE/PTR; nil for str so tnodestoreop
|
||||
// picks MOVB on the store arm, and the compound
|
||||
// arm's str/slice hard-error still fires on a
|
||||
// []str element).
|
||||
let tn: *node = letvartnode(c, bn);
|
||||
if (tn != nil) {
|
||||
if (tn.kind == nkind.N_TARRAY) {
|
||||
globalname = bn;
|
||||
esz = elemsizeofc(c, tn);
|
||||
let bk: nkind = tn.kind;
|
||||
if (bk == nkind.N_TARRAY) {
|
||||
isglobalarr = true;
|
||||
globalname = bn;
|
||||
esz = elemsizeofc(c, tn);
|
||||
elemtn = tn.lhs;
|
||||
};
|
||||
if (tn.kind == nkind.N_TPTR) {
|
||||
} else {
|
||||
isglobalptr = true;
|
||||
globalname = bn;
|
||||
esz = elemsizeofc(c, tn);
|
||||
elemtn = tn.lhs;
|
||||
if (bk == nkind.N_TSLICE) { elemtn = tn.lhs; };
|
||||
if (bk == nkind.N_TPTR) { elemtn = tn.lhs; };
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -23170,17 +23170,26 @@ fn cgun(c: *cgen, n: *node) void = {
|
||||
if (tn.kind == nkind.N_TARRAY) { isarr = true; };
|
||||
};
|
||||
} else {
|
||||
// #11: addr-of twin of the #10 cgindex read
|
||||
// fix. Dispatch esz + base load off the
|
||||
// global's RESOLVED type, NOT an N_TARRAY/
|
||||
// N_TPTR kind whitelist — a global str (tnode
|
||||
// N_TNAME) / slice (N_TSLICE) matched NEITHER
|
||||
// old arm, so esz stayed at the default 8 and
|
||||
// the base fell to the complex-base fallback,
|
||||
// yielding a wide-stride &s[i]. cstage's
|
||||
// TK_AMP N_INDEX (cmd/w6c/cgen.c) is uniform:
|
||||
// esz=bu->sub->size, base is_arr?LEAQ:MOVQ
|
||||
// name(SB) (a str/slice's .ptr IS the symbol's
|
||||
// first word). Align UP, mirroring cgindex.
|
||||
let tn: *node = letvartnode(c, base.str);
|
||||
if (tn != nil) {
|
||||
globalname = base.str;
|
||||
esz = elemsizeofc(c, tn);
|
||||
if (tn.kind == nkind.N_TARRAY) {
|
||||
isglobalarr = true;
|
||||
globalname = base.str;
|
||||
esz = elemsizeofc(c, tn);
|
||||
};
|
||||
if (tn.kind == nkind.N_TPTR) {
|
||||
} else {
|
||||
isglobalptr = true;
|
||||
globalname = base.str;
|
||||
esz = elemsizeofc(c, tn);
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -24797,19 +24806,34 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
if (bk == nkind.N_TPTR) { elemtn = btn.lhs; };
|
||||
};
|
||||
} else {
|
||||
// #11: store/compound twin of the #10 cgindex
|
||||
// read fix. A global str/slice element store hit
|
||||
// the same kind whitelist — N_TNAME (str) /
|
||||
// N_TSLICE matched NEITHER arm, so esz stayed 8
|
||||
// and the store emitted a full-word MOVQ — an
|
||||
// 8-byte OUT-OF-BOUNDS write past a 1-byte
|
||||
// element — instead of MOVB. cstage
|
||||
// (cmd/w6c/cgen.c N_INDEX store) dispatches esz
|
||||
// off idx_eff->sub->size + the elem-kind flags
|
||||
// off eff->sub uniformly, base is_arr?LEAQ:MOVQ
|
||||
// name(SB). Align UP and resolve elemtn exactly
|
||||
// like the local branch above (element node for
|
||||
// ARRAY/SLICE/PTR; nil for str so tnodestoreop
|
||||
// picks MOVB on the store arm, and the compound
|
||||
// arm's str/slice hard-error still fires on a
|
||||
// []str element).
|
||||
let tn: *node = letvartnode(c, bn);
|
||||
if (tn != nil) {
|
||||
if (tn.kind == nkind.N_TARRAY) {
|
||||
globalname = bn;
|
||||
esz = elemsizeofc(c, tn);
|
||||
let bk: nkind = tn.kind;
|
||||
if (bk == nkind.N_TARRAY) {
|
||||
isglobalarr = true;
|
||||
globalname = bn;
|
||||
esz = elemsizeofc(c, tn);
|
||||
elemtn = tn.lhs;
|
||||
};
|
||||
if (tn.kind == nkind.N_TPTR) {
|
||||
} else {
|
||||
isglobalptr = true;
|
||||
globalname = bn;
|
||||
esz = elemsizeofc(c, tn);
|
||||
elemtn = tn.lhs;
|
||||
if (bk == nkind.N_TSLICE) { elemtn = tn.lhs; };
|
||||
if (bk == nkind.N_TPTR) { elemtn = tn.lhs; };
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -25229,19 +25253,34 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
if (bk == nkind.N_TPTR) { elemtn = btn.lhs; };
|
||||
};
|
||||
} else {
|
||||
// #11: store/compound twin of the #10 cgindex
|
||||
// read fix. A global str/slice element store hit
|
||||
// the same kind whitelist — N_TNAME (str) /
|
||||
// N_TSLICE matched NEITHER arm, so esz stayed 8
|
||||
// and the store emitted a full-word MOVQ — an
|
||||
// 8-byte OUT-OF-BOUNDS write past a 1-byte
|
||||
// element — instead of MOVB. cstage
|
||||
// (cmd/w6c/cgen.c N_INDEX store) dispatches esz
|
||||
// off idx_eff->sub->size + the elem-kind flags
|
||||
// off eff->sub uniformly, base is_arr?LEAQ:MOVQ
|
||||
// name(SB). Align UP and resolve elemtn exactly
|
||||
// like the local branch above (element node for
|
||||
// ARRAY/SLICE/PTR; nil for str so tnodestoreop
|
||||
// picks MOVB on the store arm, and the compound
|
||||
// arm's str/slice hard-error still fires on a
|
||||
// []str element).
|
||||
let tn: *node = letvartnode(c, bn);
|
||||
if (tn != nil) {
|
||||
if (tn.kind == nkind.N_TARRAY) {
|
||||
globalname = bn;
|
||||
esz = elemsizeofc(c, tn);
|
||||
let bk: nkind = tn.kind;
|
||||
if (bk == nkind.N_TARRAY) {
|
||||
isglobalarr = true;
|
||||
globalname = bn;
|
||||
esz = elemsizeofc(c, tn);
|
||||
elemtn = tn.lhs;
|
||||
};
|
||||
if (tn.kind == nkind.N_TPTR) {
|
||||
} else {
|
||||
isglobalptr = true;
|
||||
globalname = bn;
|
||||
esz = elemsizeofc(c, tn);
|
||||
elemtn = tn.lhs;
|
||||
if (bk == nkind.N_TSLICE) { elemtn = tn.lhs; };
|
||||
if (bk == nkind.N_TPTR) { elemtn = tn.lhs; };
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
/*
|
||||
* 803_globalidx_run — BUG #10. Runtime + cs==ww byte-id net for INDEXING
|
||||
* a GLOBAL `str` / GLOBAL slice (`s[i]` / `g[i]` where s/g are module-level
|
||||
* lets).
|
||||
* 803_globalidx_run — BUG #10 + #11. Runtime + cs==ww byte-id net for
|
||||
* the GLOBAL `str` / GLOBAL slice index family (`s/g` are module-level lets):
|
||||
* #10 — the READ `s[i]` / `g[i]` (cgindex).
|
||||
* #11 — the ADDR-OF `&s[i]` / `&g[i]` (cgun) and the STORE `g[i] = v`
|
||||
* (cgassign). Same root as #10 (the N_TARRAY/N_TPTR kind whitelist
|
||||
* in the global-resolution arm), three more sibling arms; the store
|
||||
* miscompile is an 8-byte OUT-OF-BOUNDS write (full-word MOVQ instead
|
||||
* of MOVB), so the store rows below also assert ADJACENT elements stay
|
||||
* uncorrupted.
|
||||
*
|
||||
* THE BUG (wwstage WRONG, cstage correct — a rule-10 divergence):
|
||||
* wwstage `cgindex` (selfhost/cmd/wcc/cgenexpr.ww) dispatched the element
|
||||
@@ -113,6 +119,119 @@ static const struct row rows[] = {
|
||||
" g = a;\n"
|
||||
" return g[1] - g[0];\n"
|
||||
"};\n", 25, 1 },
|
||||
/* #11 ADDR-OF, global str: &s[1] then read the byte through the
|
||||
* pointer == 'B' == 66. Pre-#11 cgun materialised the wide {ptr,len,
|
||||
* cap} header + an 8-byte stride (IMULQ $8 + LEAQ s(SB)) instead of
|
||||
* MOVQ s(SB) (.ptr) + ADDQ; FULL byte-id (string-literal DATA is
|
||||
* stage-identical, same as the gstr READ rows). */
|
||||
{ "gstr_addr",
|
||||
"package main;\n"
|
||||
"let s: str = \"ABC\";\n"
|
||||
"export fn main() i32 = { let p: *u8 = &s[1]; return (*p): i32; };\n",
|
||||
66, 0 },
|
||||
/* #11 ADDR-OF, global slice []u8: &g[1] read back == 20. Bare
|
||||
* `let g: []u8;` decl → orthogonal zero-header DATAW, so TEXT-only
|
||||
* (same data gap as gslice_mid; the addr codegen is all TEXT). */
|
||||
{ "gslice_addr",
|
||||
"package main;\n"
|
||||
"let g: []u8;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let a: [3]u8 = [10u8, 20u8, 30u8];\n"
|
||||
" g = a;\n"
|
||||
" let p: *u8 = &g[1];\n"
|
||||
" return (*p): i32;\n"
|
||||
"};\n", 20, 1 },
|
||||
/* #11 ADDR-OF, global slice WIDTH>1: &g[1] over []i32, *p == 20.
|
||||
* Pins esz=4 stride on the addr path — a stride-8 regression lands
|
||||
* &g[2] (*p == 30) and re-diverges the TEXT (IMULQ $4 vs IMULQ $8).
|
||||
* Same bare-decl DATAW gap → TEXT-only. */
|
||||
{ "gislice_addr",
|
||||
"package main;\n"
|
||||
"let g: []i32;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let a: [3]i32 = [-5, 20, 30];\n"
|
||||
" g = a;\n"
|
||||
" let p: *i32 = &g[1];\n"
|
||||
" return *p;\n"
|
||||
"};\n", 20, 1 },
|
||||
/* #11 STORE, global slice []u8: g[1] = 99, then g[1]+g[0]+g[2] ==
|
||||
* 99 + 10 + 30 == 139. The g[0]/g[2] addends are the OOB-WRITE GUARD:
|
||||
* pre-#11 the store was a full-word MOVQ at an 8-byte stride, writing
|
||||
* 99 at ptr+8 (8-byte OOB) and leaving g[1] == 20 (sum 60, AND clobbered
|
||||
* memory). The fix stores MOVB at ptr+1; adjacent bytes intact. TEXT-
|
||||
* only (bare-decl DATAW gap). */
|
||||
{ "gslice_store",
|
||||
"package main;\n"
|
||||
"let g: []u8;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let a: [3]u8 = [10u8, 20u8, 30u8];\n"
|
||||
" g = a;\n"
|
||||
" g[1] = 99u8;\n"
|
||||
" return g[1]: i32 + g[0]: i32 + g[2]: i32;\n"
|
||||
"};\n", 139, 1 },
|
||||
/* #11 STORE, global slice WIDTH>1 SIGNED: g[1] = 42, then
|
||||
* g[1]+g[0]+g[2] == 42 + (-5) + 30 == 67. Pins esz=4 store WIDTH +
|
||||
* stride: a full-word/stride-8 regression writes 8 bytes at ptr+8
|
||||
* (OOB) and re-diverges the TEXT (IMULQ $4 + MOVL vs IMULQ $8 + MOVQ).
|
||||
* g[0]/g[2] are the adjacency guard. TEXT-only. */
|
||||
{ "gislice_store",
|
||||
"package main;\n"
|
||||
"let g: []i32;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let a: [3]i32 = [-5, 20, 30];\n"
|
||||
" g = a;\n"
|
||||
" g[1] = 42;\n"
|
||||
" return g[1] + g[0] + g[2];\n"
|
||||
"};\n", 67, 1 },
|
||||
/* #11 COMPOUND STORE, global slice []u8: g[1] += 79 → 20+79 == 99,
|
||||
* then g[1]+g[0]+g[2] == 99+10+30 == 139. Exercises the THIRD fixed
|
||||
* arm (cgenexpr.ww cgassign n.op != TK_ASSIGN), a distinct code path
|
||||
* from the plain-store rows above: it load-combines-stores in place.
|
||||
* Pre-#11 it hit the same kind whitelist → esz=8 + a full-word RMW: an
|
||||
* 8-byte OOB load AND store at ptr+8, leaving g[1] == 20 (sum 60) and
|
||||
* clobbering adjacent memory. The g[0]/g[2] addends are the OOB guard.
|
||||
* TEXT-only (bare-decl DATAW gap). */
|
||||
{ "gslice_compound",
|
||||
"package main;\n"
|
||||
"let g: []u8;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let a: [3]u8 = [10u8, 20u8, 30u8];\n"
|
||||
" g = a;\n"
|
||||
" g[1] += 79u8;\n"
|
||||
" return g[1]: i32 + g[0]: i32 + g[2]: i32;\n"
|
||||
"};\n", 139, 1 },
|
||||
/* #11 COMPOUND STORE, global slice WIDTH>1 SIGNED: g[1] += 47 →
|
||||
* (-5)+47 == 42, then g[1]+g[0]+g[2] == 42+(-25)+30 == 47. Pins the
|
||||
* compound arm's esz=4 load/store WIDTH + stride on the global path —
|
||||
* a stride-8/full-word RMW regression reads+writes 8 bytes at ptr+8
|
||||
* (OOB) and re-diverges the TEXT (IMULQ $4 + MOVL/MOVSXD vs IMULQ $8 +
|
||||
* MOVQ). g[0]/g[2] are the adjacency guard. TEXT-only. */
|
||||
{ "gislice_compound",
|
||||
"package main;\n"
|
||||
"let g: []i32;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let a: [3]i32 = [-25, -5, 30];\n"
|
||||
" g = a;\n"
|
||||
" g[1] += 47;\n"
|
||||
" return g[1] + g[0] + g[2];\n"
|
||||
"};\n", 47, 1 },
|
||||
/* REGRESSION PIN: local str ADDR-OF (already clean pre-#11) — the
|
||||
* fix must not perturb the local path. &s[1] read back == 66. FULL. */
|
||||
{ "lstr_addr",
|
||||
"package main;\n"
|
||||
"export fn main() i32 = { let s: str = \"ABC\"; let p: *u8 = &s[1]; return (*p): i32; };\n",
|
||||
66, 0 },
|
||||
/* REGRESSION PIN: local slice STORE (already clean) — g[1] = 99 then
|
||||
* g[1]+g[0]+g[2] == 139, adjacency intact. FULL byte-id (a local slice
|
||||
* carries no bare-decl data gap). */
|
||||
{ "lslice_store",
|
||||
"package main;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let a: [3]u8 = [10u8, 20u8, 30u8];\n"
|
||||
" let g: []u8 = a;\n"
|
||||
" g[1] = 99u8;\n"
|
||||
" return g[1]: i32 + g[0]: i32 + g[2]: i32;\n"
|
||||
"};\n", 139, 0 },
|
||||
/* REGRESSION PIN: local str index (already byte-id-clean pre-fix) —
|
||||
* the fix must not perturb the local path. s[1] == 'B' == 66. */
|
||||
{ "lstr_mid",
|
||||
|
||||
Reference in New Issue
Block a user