Files
chimera-gfx-Public/docs/runtime/phase-0.9c-output-channel-feasibility.md
T
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

10 KiB
Raw Permalink Blame History

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:

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 03840 bounded body
actual4096 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.