wcc/check+wcc_ww/check: reject overlong array literal — frame-smash class (#71)
An array literal with more elements than the declared [N] passed the per-element accept-if-fits checks in both stages and cgen then stored every element at its natural offset, writing past the slot: local frames smashed silently (the repeat form [1,2,3...] into [2]int wrote at the saved BP), module DATA corrupted neighbours. All four declaration contexts (local let, module let, def, struct-field literal) funnel through one choke point per stage — arrlit_init_fits (check.c) / checkarrlitfits (check.ww) — which now pre-counts the literal (skipping the ... marker) and rejects count > N naming both counts. cstage clet's blanket has_arr_repeat bypass is narrowed to non-array declared targets: repeat literals into arrays now run the same overlong + #130 range checks wwstage's checkletassign always ran (the bypass let [2]u8 = [999...] dodge the range check cstage-only). checkarrlitfits also recurses into NESTED array-literal elements (declared elem node N_TARRAY): cstage catches the nested shape through its typed-literal assignability net, which wwstage's untyped elements have no analog of — [2][2]int = [[1,2,3],[4,5]] at module scope silently emitted corrupted DATA (1,2,4,5) and the struct-field twin likewise. Recursion through the one choke point closes any depth; a named-alias element type still bypasses — task #16. alen==0/nil-length stays exempt ([0]/[_] sentinel conflation and un-inferred [_] in def/struct-field — task #11); a non-INTLIT length child (def-named [N]) is exempt in wwstage — task #13; under-long literals keep their current accept (Hare rejects — task #10); wwstage's overlong accept at assign/call-arg/return position (cstage already rejects) is task #12; exact-fit bare-int nested cs-reject/ ww-accept divergence is pre-existing — task #17.
This commit is contained in:
@@ -411,6 +411,27 @@ arrlit_init_fits(Checker *c, Type *dt, Node *rhs)
|
||||
&& strcmp(e->str, "...") == 0)
|
||||
continue;
|
||||
count++;
|
||||
}
|
||||
/* #71: more elements than the declared [N] passed every per-element
|
||||
* check below and then smashed the frame at cgen (each element is
|
||||
* stored at its natural offset — the overflow clobbered neighbours
|
||||
* and even the saved BP). Reject loud before the element walk.
|
||||
* alen==0 stays exempt: 0 doubles as the [_] infer sentinel ([0]
|
||||
* vs [_] conflation, and [_] in def/struct-field never infers —
|
||||
* task #11), and the let paths patch the real length in before
|
||||
* reaching here. Under-long (count < N, no `...`) stays accepted
|
||||
* as before; Hare rejects it — task #10. */
|
||||
if (u->kind == TY_ARRAY && u->alen != SIZE_UNDEFINED && u->alen > 0
|
||||
&& count > u->alen) {
|
||||
err(c, rhs->pos, "array literal has %llu elements "
|
||||
"but declared array holds %llu",
|
||||
(unsigned long long)count, (unsigned long long)u->alen);
|
||||
return 0;
|
||||
}
|
||||
for (Node *e = rhs->list; e; e = e->next) {
|
||||
if (e->kind == N_FIELD && e->str
|
||||
&& strcmp(e->str, "...") == 0)
|
||||
continue;
|
||||
Node *ev = e;
|
||||
while (ev && ev->kind == N_CAST) ev = ev->lhs;
|
||||
u64 v;
|
||||
@@ -2086,7 +2107,15 @@ clet(Checker *c, Node *n)
|
||||
* "flexible" length — the last value fills the remaining slots.
|
||||
* The literal's type carries the explicit-element count, which
|
||||
* may not match the declared length. Trust the declared type
|
||||
* when the marker is present. */
|
||||
* when the marker is present.
|
||||
*
|
||||
* #71: NOT when the declared type is an array — there the repeat
|
||||
* only relaxes the length upward (explicit count <= N, fill the
|
||||
* rest); arrlit_init_fits skips the marker and runs the overlong
|
||||
* reject + per-element range checks. The blanket bypass let
|
||||
* `[2]int = [1,2,3...]` write past the slot (saved-BP clobber)
|
||||
* and `[2]u8 = [999...]` skip the #130 range check — wwstage's
|
||||
* checkletassign already runs both unconditionally. */
|
||||
int has_arr_repeat = 0;
|
||||
if (n->rhs && n->rhs->kind == N_ARRLIT) {
|
||||
for (Node *e = n->rhs->list; e; e = e->next)
|
||||
@@ -2096,6 +2125,12 @@ clet(Checker *c, Node *n)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (has_arr_repeat && declared) {
|
||||
Type *du = (declared->kind == TY_NAMED) ? declared->under
|
||||
: declared;
|
||||
if (du && du->kind == TY_ARRAY)
|
||||
has_arr_repeat = 0;
|
||||
}
|
||||
/* #45: alloc([], n) defers element type to the let-init context
|
||||
* (Hare-style). cexpr's alloc-slice branch synthesizes
|
||||
* ([]u8 | nomem) with no LHS context; when the let declares []T,
|
||||
|
||||
Reference in New Issue
Block a user