ulfius: client_address carries the whole peer sockaddr; forgectrl.auth asserts the loopback report is accepted

The dual-stack listener reports every peer as a sockaddr_in6; ulfius
2.7.15 copied sixteen bytes of it, so forgectrl's loopback-only cooling
channel refused the controller's every report (403 loopback only) and
the engine never saw a run or an armed window. The recipe carries the
patch: a sockaddr_storage allocation and a copy of the family's length,
in the dispatcher and in ulfius_copy_request.

forgetest: forgectrl.auth asserts POST /cool/state from loopback -> 200
beside the LAN 403, and covers src/peer.*. BRINGUP item 21 and the
campaign log record how the campaign on dev 20260824215906 found it.
This commit is contained in:
ScottW514
2026-08-24 19:02:52 -04:00
parent 4d3d173445
commit 243320f65d
5 changed files with 147 additions and 5 deletions
+21 -2
View File
@@ -1586,8 +1586,27 @@ Open items only. Anything closed is in `CAMPAIGN-LOG.md`.
remove and on the probe unwind, the empty-ring run request logs at ERR
level again (it was the only kernel-log trace of the fault), and
`image.health` asserts the SDMA clock enable count directly. The ecspi2
`dmas` stay deleted. Left: build, flash, the item-16 drill and the
campaign.
`dmas` stay deleted. Bench-proven on dev 20260824215906: the clock count
reads 1, the probe reports MOTION OK, `cnc/free` reads the ring less its
gap, and the campaign ran every kernel, forgectrl, logs and motion test
green.
That campaign then stopped on `cooling.fans-quiet-after-motion`: M8
raised no fan duty because forgectrl had accepted no cooling report
from the controller at all (`report_age_s` -1). The dual-stack listener
of the second round reports every peer as a `sockaddr_in6`, and ulfius
2.7.15 copies the peer into `client_address` as `sizeof(struct
sockaddr)`, 16 bytes; the mapped-loopback bytes the check reads lie
beyond the copy, so `POST /cool/state` from 127.0.0.1 got `403 loopback
only` (fail-safe: the engine treats silence as a stand-down, so nothing
fired, but no run profile and no armed window either). The fix: the
image patches ulfius to allocate a `sockaddr_storage` and copy the
family's length (`meta-forgefirm/recipes-extended/ulfius`), the peer
check lives in `src/peer.c` with a host unit test (`auth_peer_test`,
including the truncated-copy case, which fails closed), and
`forgectrl.auth` asserts that the loopback peer is accepted as well as
that a LAN peer is refused. Left: build, flash, the item-16 drill and
the campaign.
**Deliberately not gated:** an armed GRBL job after an underrun cuts at the
stale origin unless homing is required (GRBL mode permits unhomed cutting; the
+41
View File
@@ -3934,6 +3934,47 @@ tests and the 270 forgetest tests pass. Owed: the image, then on the
bench `clk_enable_count` reading 1, `MOTION OK` from the probe,
`cnc/free` at 33521664 idle, a GRBL job, and the campaign.
## 2026-08-24: the clocks proven, and the listener that heard nobody
Dev 20260824215906, built with the SDMA clock fix, on the bench: `sdma`
`clk_enable_count` 1, the supervisor's probe `MOTION OK` (p2p x=3390
y=1720), `/mode` verified, `cnc/free` 33521664 at idle, position 0.
Campaign `c-20260824223050-0356` (36 unattended, the fixture in the loop):
`image.health` passed in seconds with its new clock assertion, and every
kernel, forgectrl, logs and motion test passed, `motion.liveness-probe`
and `motion.button-hold-resume` among them. `cooling.flow-verify` passed.
`cooling.fans-quiet-after-motion` failed: `M8 did not raise the fan duty
off idle`.
The engine had heard nothing. `/cool/status` showed `report_age_s` -1 for
the controller the supervisor had just respawned, and a hand-sent
`POST /cool/state?mode=idle` from 127.0.0.1 answered `403 loopback only`.
The listener is dual-stack since the second kernel round (`:::8080`), so
every peer arrives as a `sockaddr_in6`, the IPv4 client as
`::ffff:127.0.0.1`. forgectrl's check handles that spelling; ulfius 2.7.15
does not hand it over: `src/ulfius.c` allocates and copies
`client_address` as `sizeof(struct sockaddr)`, 16 bytes, which holds the
family, the port, the flow label and eight address bytes. The mapped
prefix and the 127 sit at bytes 10 to 12 of the address, past the copy,
in heap the check should never have read. Every report since dev
20260824200726 was refused the same way; nothing ran the cooling tests on
those images until now. The direction was safe: the engine treats silence
as a stand-down, so no run profile, no armed window, no fire.
The fix and its proof so far: the image carries a ulfius patch (a
`sockaddr_storage` allocation, a copy of the family's length, in the
dispatcher and in `ulfius_copy_request`); the recipe builds it clean under
ulfius's own `-Werror -Wconversion`. The peer check moved into
`forgectrl/src/peer.c` unchanged in meaning, with `tests/auth_peer_test.c`
in CI: 127/8, `::1` and mapped 127/8 pass; LAN addresses in both families,
a mapped LAN address, link-local, unspecified, `AF_UNIX`, NULL and a
`::ffff:127.0.0.1` cut to sixteen bytes are refused. forgectrl cross-builds
under `-Werror`. `forgectrl.auth` now asserts the loopback acceptance
(200) next to the LAN refusal (403), so a listener that truncates the
peer fails the catalog on the first forgectrl test rather than the first
cooling one. Owed: the image, the loopback report accepted on the bench,
the campaign.
## Superseded status notes
### Shared machine services — remaining polish, as listed 2026-08-13