10 KiB
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:
- hardened elfldr maps the argument page inside the child process
(
elfldr.c:273-287); payloadoutisargs_mapping + 0x300(elfldr.c:349-367);- after detach, elfldr clears its tracked cleanup bitmap and does not read
payloadout(elfldr.c:538-545); - on process exit, the controlled waiter discards the
waitpidstatus (ps5_controlled.c:36-59); and - 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:
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:
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
SUCCESScan yield a valid completed record; - observer error, timeout, firmware conflict, or any unknown status is blocked;
observed & unsupportedmust be zero;observed | unsupportedmust 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.