docs: append REGRESSION + REMEDY record for task-8 ps2d read-to-string regression

Root cause: std::fs::read_to_string loops forever on kernel scheme
/scheme/serio/present (serio kread for HandleKind::Present never
produces EOF). Fix: bounded single-byte read in ps2d main.rs
(commit aeb2df8e on submodule/base branch). 14/14 host tests green.
QEMU gate expected: ps2d startup log BEFORE notify-wait ticks,
no kill line.
This commit is contained in:
2026-08-05 14:22:28 +03:00
parent 47a22479bc
commit cab22e5a4a
@@ -92,3 +92,61 @@ Single command (no build needed):
- No `src/scheme/irq.rs` cpu- path handling touched
- No retries/warning noise on absence
- No recipe files modified
---
## REGRESSION + REMEDY RECORD (2026-08-05, QEMU verification)
### Root cause
`std::fs::read_to_string("/scheme/serio/present")` in
`local/sources/base/drivers/input/ps2d/src/main.rs:38` loops forever on
the i8042-present path. The kernel's `SerioScheme::kread` for
`HandleKind::Present` (kernel `src/scheme/serio.rs:146-148`) always
returns the verdict bytes (`b"1\n"` or `b"0\n"`) on every read without
ever producing a zero-byte EOF. `read_to_string` in Rust std reads
until `read()` returns 0, so it enters an infinite read loop — growing
the accumulator string by 3 bytes per iteration — and never returns to
caller code. ps2d blocks in `read_to_string` before any log output or
`daemon.ready()` can execute, so init's 10s notify-wait timeout kills it
with zero diagnostic output ("'ps2d' did not notify readiness within 10s;
killing and skipping").
The absent path (kernel verdict "0\n") is equally affected but was not
caught because the QEMU configs used for absent-path testing happened to
not exercise `read_to_string` (or the absent-path tests were host-only
verdict logic tests, not QEMU boots). The host test suite (14/14 GREEN)
tests the verdict DECISION logic in isolation — it does not exercise the
actual `read_to_string` syscall path on a running Redox kernel.
### Remedy
Commit `aeb2df8e` on `submodule/base`:
- **`drivers/input/ps2d/src/main.rs`** — Replaced `std::fs::read_to_string`
with a bounded single-byte read (`read_serio_present()`): opens
`/scheme/serio/present`, reads at most 2 bytes into a stack buffer,
returns the first byte. Only the first byte matters (b'1' = present,
b'0' = absent); the '\n' is discarded.
- Added a `log::info!` on the present path BEFORE `InputProducer` opens
("ps2d: PS/2 controller present; starting serio device handshake") so
future boot logs show ps2d's progress through the startup sequence
before any scheme I/O blocking point.
Host tests: 14/14 GREEN (`cargo test --manifest-path
local/sources/kernel/host-tests/i8042-verdict/Cargo.toml`). All verdict
decision-table tests pass unchanged — the fix only changes the I/O
mechanism, not the decision logic.
### Expected QEMU gate observable
On the i8042-present path, the orchestrator's QEMU gate should see:
- **BEFORE** the first `notify-wait 'ps2d' tick` (within ~50ms of
"Started PS/2 driver"): the line
`ps2d: PS/2 controller present; starting serio device handshake`,
followed by `ps2d: registered producer handle, listening on serio/0
(keyboard) and serio/1 (mouse)`.
- **ABSENT**: the kill line `'ps2d' did not notify readiness within 10s;
killing and skipping`.
- Keyboard input on `/scheme/serio/0` works in QEMU after ps2d's
successful notify.