fix(v4): a manage request with too few values, or no room, is refused (#169)

v4_manage_serve took its arguments with v4_dstack_pop and never checked the
stack's depth: a request with too few values acted on stale ring values, and
one whose results did not fit dropped the oldest silently -- around the D-16
guards. Now each request's arity (manage.h) is checked first: fewer values
than it takes, or no room for what it leaves, is error 12 with the stack as it
was -- as a block request is refused (blocks.c).

test_manage.c, written first and seen to fail (12 of 13 requests at both cell
widths; NODE-BORN passed only because the stale value read was below 1): every
request with one value fewer than it takes, and NODE-ME and CAPSULE-CELL on a
stack with no room -- refused, the stack unchanged. After: 78 checks, 0
failures at 32 and 64 bits; all v4 tests at 64 bits, 40 programs, 1,871,313
checks, 0 failures (logs/20261112-152602/v4-test).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017RzZgrf7V7Zk2iczBXDRbm
This commit is contained in:
Claude
2026-11-12 15:26:02 +00:00
parent c28cc0191b
commit c642af541b
3 changed files with 49 additions and 0 deletions
+36
View File
@@ -93,6 +93,42 @@ int main(void)
CHECK(v4_manage_enter(&m, 10, h, 0) == 0 && v4_manage_enter(&m, 0, h, 0) == 0, "a number that is live, or 0, is not entered again");
CHECK(v4_manage_serve(&m, h, V4_REQ_NODE_ME) == 1 && pop(h) == (v4_cell)h, "NODE-ME is her place");
/* #169: a request with fewer values than it takes, or with no room for what it leaves, is
* refused, error 12, the stack as it was -- as a block request is (v4/system/blocks.c). The
* values a request takes and leaves, from manage.h. */
{
static const struct { v4_cell req; unsigned in, out; const char *name; } arity[] = {
{ V4_REQ_NODE_ME, 0, 1, "NODE-ME" }, { V4_REQ_NODE_BORN, 1, 1, "NODE-BORN" },
{ V4_REQ_NODE_WIRE, 4, 1, "NODE-WIRE" }, { V4_REQ_NODE_UNWIRE, 2, 0, "NODE-UNWIRE" },
{ V4_REQ_NODE_SLEEP, 1, 0, "NODE-SLEEP" }, { V4_REQ_NODE_WAKE, 1, 0, "NODE-WAKE" },
{ V4_REQ_NODE_KILL, 1, 0, "NODE-KILL" }, { V4_REQ_NODE_PARITY, 2, 0, "NODE-PARITY" },
{ V4_REQ_CAPSULE_OPEN, 2, 1, "CAPSULE-OPEN" }, { V4_REQ_CAPSULE_CELL, 0, 2, "CAPSULE-CELL" },
{ V4_REQ_CAPSULE_LINE, 1, 1, "CAPSULE-LINE" }, { V4_REQ_NODE_CHILD, 2, 1, "NODE-CHILD" },
{ V4_REQ_NODE_PLACE, 1, 1, "NODE-PLACE" },
};
unsigned k, j;
for (k = 0; k < sizeof arity / sizeof arity[0]; k++) {
unsigned have;
if (arity[k].in > 0) { /* one value fewer than it takes */
v4_dstack_reset(&N(h)->ds);
for (j = 0; j + 1 < arity[k].in; j++) push(h, 1);
have = depth(h);
CHECK(refused(h, arity[k].req) && depth(h) == have,
"%s with %u value(s), not %u: refused, the stack as it was (depth %u)",
arity[k].name, have, arity[k].in, depth(h));
}
if (arity[k].out > arity[k].in) { /* no room for what it leaves */
v4_dstack_reset(&N(h)->ds);
while (depth(h) + (arity[k].out - arity[k].in) <= V4_DATA_DEPTH) push(h, 1);
have = depth(h);
CHECK(refused(h, arity[k].req) && depth(h) == have,
"%s on a stack with no room for its %u: refused, the stack as it was (depth %u of %u)",
arity[k].name, arity[k].out - arity[k].in, depth(h), have);
}
}
v4_dstack_reset(&N(h)->ds);
}
/* births */
r = birth(h, 11);
CHECK(r >= 0 && place_of(h, 11) == r && child(h, 10, 0) == 11 && child(h, 10, 1) == 0, "Hera births 11: the registry has it, with her as its parent");