Files
chimera-gfx-Public/docs/runtime/phase-0.9c-output-channel-feasibility.md
Chimera GFX release export a6037502d7
phase0-ci / build-and-audit (push) Successful in 2m14s
Publish Chimera GFX source
2026-09-03 03:27:14 +02:00

269 lines
10 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Phase 0.9C output-channel feasibility
Status: **HOST CONTRACT ONLY — NO CURRENT OUTPUT CHANNEL**.
This document evaluates the four requested architectures in order. It defines
a finite host protocol because D1 is conceptually coherent, but it does not
claim that hardened elfldr, the controlled Payload Manager, or any target
implements that protocol. No production runtime was changed.
## D1 — caller-owned bounded result buffer
### Current architecture
The current `payload_args_t.payloadout` is not a caller-owned observer result
buffer:
1. hardened elfldr maps the argument page **inside the child process**
(`elfldr.c:273-287`);
2. `payloadout` is `args_mapping + 0x300`
(`elfldr.c:349-367`);
3. after detach, elfldr clears its tracked cleanup bitmap and does not read
`payloadout` (`elfldr.c:538-545`);
4. on process exit, the controlled waiter discards the `waitpid` status
(`ps5_controlled.c:36-59`); and
5. the controlled connection writes only fixed loader text
(`socksrv.c:361-365`).
The current field is therefore child-owned mapped storage whose useful
post-exit lifetime and consumption are absent. It cannot be relabelled as D1.
### Future concept
A D1 implementation could be reviewed only if a future loader revision:
- allocates exactly 4096 bytes under caller ownership;
- zero-initializes that buffer;
- makes one bounded view available for one execution;
- retains ownership outside the observer process;
- binds it to a 128-bit execution nonce and 128-bit request ID;
- prevents concurrent or reused writers;
- obtains a proven normal return/exit indication;
- validates the observer body before accepting it;
- records cleanup outcome after the observer can no longer write;
- writes the final checksum and completion marker last;
- reads it once and rejects duplicate consumption; and
- destroys the transient object without filesystem, listener, log, or
persistent shared-memory state.
That design requires a loader ABI and implementation change in a later phase.
It also depends on the startup/exit contract that Phase 0.9C did not prove.
Classification:
`CONCEPT_FEASIBLE_REQUIRES_LOADER_CHANGE_AND_EXIT_PROOF`.
## D2 — existing request/response channel
The controlled manager's transport interface contains exactly:
```text
connect_loopback
send_all
close_socket
```
Source: controlled manager `include/verified_launcher.h:38-42` and
`src/ps5_launcher.c:18-69`. `verified_launcher.c:153-183` sends the header and
artifact bytes, then closes the socket. There is no receive callback, response
buffer, length framing, result deadline, or result validation.
The elfldr side can write a fixed text line after the child is reaped, but the
controlled manager has already closed and never reads it. The text carries no
nonce, request ID, process status, capability data, checksum, truncation flag,
or cleanup status. It also cannot distinguish a normal exit from a reaped
crash because the waiter discards status.
Classification: `REJECTED_SEND_ONLY_NO_RESULT_RECEIVE`.
## D3 — loader-owned fixed status record
Hardened elfldr has transient local variables for child PID, watchdog return,
and `waitpid` status, but no persistent or returned per-request status record.
The current `wait_reaped` discards the status and the connection response is
fixed.
A small loader-owned record would still require:
- per-request storage and nonce binding;
- preservation of actual wait status;
- an exact normal/error/crash mapping;
- a proven way for the observer to supply bounded data;
- a manager receive operation and response framing; and
- lifecycle rules preventing stale reuse or a race between completion and
cleanup.
Those are production loader/manager changes and were forbidden in this task.
Classification:
`UNPROVEN_REQUIRES_LOADER_STATE_AND_PROPAGATION_CHANGE`.
## D4 — process exit status
`waitpid` supplies an integer status to the loader, but
`ps5_controlled.c:36-59` does not retain or return it. The manager receives no
status. The same fixed text can follow a normal exit or a crash, and the
watchdog result distinguishes only its own high-level completion/timeout path.
Even if later propagated, a conventional exit status has too little space for
the required observations and needs an exact mapping for success, unsupported,
observer error, crash, signal, timeout, and cleanup failure. None exists.
Classification: `REJECTED_WAIT_STATUS_DISCARDED_AND_AMBIGUOUS`.
## Rejected output mechanisms
The following remain inadmissible:
- target filesystem or target logs;
- a new socket, listener, server, debug service, or callback;
- notification, klog, stdout, stderr, crash dump, or audit log;
- kernel buffer or persistent shared memory;
- autoload or service state;
- graphics, VideoOut, SDL, GNM, or RetroArch;
- timing or another covert channel; and
- the legacy stdio route.
The controlled route passes `stdio=-1` at `socksrv.c:139-145`. Stdio
duplication occurs only for `stdio > 0` at `elfldr.c:499-520`.
## Host-only D1 protocol
The protocol model is `tests/phase09c_feasibility_model.py`. It performs no
filesystem, network, compiler, or target operation.
### Ownership and completion
The conceptual write sequence is:
```text
caller:
allocate and zero exactly 4096 bytes
bind nonce, request ID, exact artifact hash and deadline
observer:
fill fixed fields and at most 3840 body bytes
write explicit status and observed/unsupported bitmaps
write body SHA-256
return through the still-unproven safe ABI
caller after proven return and cleanup:
validate request binding and body
write cleanup status
compute final result SHA-256
write completion marker last
consumer:
validate once
reject stale, duplicate, incomplete, timed-out or inconsistent records
```
The caller-finalization step is essential: an observer cannot truthfully
attest loader cleanup that occurs only after it returns. The current loader has
no such step.
### Fixed framing
All integers are unsigned big-endian. There are no pointers, offsets supplied
by the observer, variable headers, dynamic growth, or nested lengths.
| Offset | Size | Field |
|---:|---:|---|
| 0 | 8 | magic `CHG09C01` |
| 8 | 2 | protocol version `1` |
| 10 | 2 | header size `256` |
| 12 | 4 | maximum output size `4096` |
| 16 | 4 | actual output size, inclusive of header |
| 20 | 4 | observer version |
| 24 | 4 | status |
| 28 | 4 | flags; bit 0 means truncation |
| 32 | 4 | caller-recorded cleanup status |
| 36 | 4 | reserved zero |
| 40 | 8 | requested capability bitmap |
| 48 | 8 | observed capability bitmap |
| 56 | 8 | unsupported capability bitmap |
| 64 | 8 | monotonic deadline in nanoseconds |
| 72 | 16 | execution nonce |
| 88 | 16 | request ID |
| 104 | 8 | firmware source 1, canonical NUL-padded ASCII |
| 112 | 8 | firmware source 2, canonical NUL-padded ASCII |
| 120 | 32 | exact future observer artifact SHA-256 |
| 152 | 32 | SHA-256 of body bytes |
| 184 | 32 | SHA-256 of header and body with this field and completion zero |
| 216 | 8 | completion marker `COMPLETE`, written last |
| 224 | 32 | reserved zero |
| 256 | 0–3840 | bounded body |
| actual–4096 | remainder | required zero |
`actual_output_size` must be in `[256, 4096]`; subtraction and addition are
checked before any slice is accepted. The body size is exactly
`actual_output_size - 256`. A body larger than 3840 bytes is rejected unless
the model deliberately produces a truncated record, and every truncated
record is blocked.
### Status and bitmap rules
- only explicit `SUCCESS` can yield a valid completed record;
- observer error, timeout, firmware conflict, or any unknown status is
blocked;
- `observed & unsupported` must be zero;
- `observed | unsupported` must exactly equal the requested bitmap;
- unrequested bits are forbidden;
- explicit unsupported bits preserve evidence but do not prove the
corresponding capability;
- an empty successful body is valid only when the requested bitmap is also
fully accounted for;
- an empty observer-error body is still failure, never empty success.
### Fail-closed validation
The consumer rejects:
- wrong magic, unknown protocol version, header size, or maximum;
- invalid or overflowed actual size;
- nonzero reserved or unused bytes;
- missing completion marker;
- stale nonce or request ID;
- duplicate consumption;
- observer version, deadline, artifact hash, or firmware binding mismatch;
- absent second firmware source or conflicting firmware sources;
- expired deadline;
- body or final checksum failure;
- unknown flags or truncation;
- incomplete or failed cleanup;
- observer failure;
- incomplete, overlapping, or unrequested capability bitmaps.
The completion marker is not itself an integrity proof. It is accepted only
after both checksums and every binding validate.
## Firmware binding
Source 1 is SDK `kernel_get_fw_version()` at `crt/kernel.c:148-170`. It reads
the `sdk_ps5_ver` field from the `libSceLibcInternal` process parameter and is
not independent device evidence. The SDK export stubs contain the symbol name
`sceKernelGetProsperoSystemSwVersion` at
`sce_stubs/libkernel_web.c:2358-2360`, but no reviewed public header,
signature, layout, semantics, side-effect contract, or firmware-9.60 runtime
evidence accompanies that name. A symbol name is not an ABI and is not source
2.
A future source 2 must be independently sourced, publicly reviewable or
locally cryptographically bound, current for the same execution, exact rather
than normalized from an ambiguous value, and bound into the same nonce and
request record. Absence yields `BLOCKED_FIRMWARE_SOURCE_2_ABSENT`; disagreement
yields `BLOCKED_FIRMWARE_CONFLICT`. Source 1 is never silently preferred.
## Feasibility decision
The 4096-byte D1 framing is internally closed and host-testable. That proves
only the data contract. It does not prove:
- a caller-owned target buffer;
- a shared mapping or copy-out ABI;
- an observer write boundary;
- a safe return or process exit;
- caller finalization after real cleanup;
- manager delivery; or
- firmware 9.60 runtime behavior.
No current bounded non-persistent output channel exists. The output result is
therefore `BLOCKED_NO_BOUNDED_OUTPUT_CHANNEL` despite the passing host model.