From fee37cd9b538ad6629ed1e0b646df7a43a03f044 Mon Sep 17 00:00:00 2001 From: Chimera GFX release export Date: Thu, 3 Sep 2026 02:53:36 +0200 Subject: [PATCH] Publish Chimera GFX source --- .clang-format | 7 + .clang-tidy | 13 + .dockerignore | 11 + .editorconfig | 23 + .gitattributes | 8 + .gitea/workflows/ci.yml | 60 + .gitea/workflows/managed-validation.yml | 117 + .gitignore | 60 + .gitleaks.toml | 13 + ARCHITECTURE.md | 438 + CMakeLists.txt | 1293 ++ CMakePresets.json | 49 + CONTRIBUTING.md | 46 + FIRMWARE_COMPATIBILITY.md | 271 + LICENSE | 676 + PUBLIC-SOURCE-MANIFEST.sha256 | 823 + README.md | 124 + RESEARCH.md | 162 + ROADMAP.md | 225 + SAFETY.md | 610 + SECURITY.md | 41 + TEST_PLAN.md | 1101 ++ THIRD_PARTY_NOTICES.md | 25 + adapters/retroarch/README.md | 11 + adapters/retroarch/retroarch_adapter.c | 28 + adapters/sdl2/README.md | 11 + adapters/sdl2/sdl2_adapter.c | 28 + cmake/README.md | 15 + cmake/chimera-gfx-config.cmake.in | 3 + docs/DECISIONS.md | 16 + docs/PUBLICATION_READINESS.md | 40 + docs/adr/0001-standalone-repository.md | 15 + docs/adr/0002-export-names-are-not-abi.md | 21 + docs/adr/0003-fail-closed-probe.md | 20 + docs/adr/0004-license.md | 15 + docs/adr/0005-versioned-resource-lifecycle.md | 24 + docs/adr/0006-phase1-sdl-videoout-boundary.md | 29 + docs/adr/0007-artifact-provenance.md | 15 + docs/adr/0008-firmware-9.60-probe-build.md | 39 + .../0009-block-unproven-minimal-startup.md | 43 + docs/adr/0010-controlled-runtime-effects.md | 45 + ...011-phase07-hardened-controlled-runtime.md | 49 + .../adr/0012-phase10j-first-frame-identity.md | 38 + .../phase-0.8-bounded-observation-template.md | 131 + .../phase-0.9-backup-creation-template.md | 69 + .../phase-0.9-observation-template.md | 64 + .../phase-0.9-one-shot-execution-template.md | 64 + docs/approvals/phase-0.9-staging-template.md | 63 + docs/approvals/phase-0.9-switch-template.md | 67 + .../phase-0.9b-observer-execution-template.md | 76 + ...se-0.9e-r-y2jb-deployed-use-attestation.md | 57 + .../phase-1.0aa-offline-fake-adapter.md | 25 + .../phase-1.0ab-live-adapter-feasibility.md | 23 + docs/approvals/phase-1.0ac-dormant-adapter.md | 24 + .../phase-1.0b-device-smoke-template.md | 50 + .../phase-1.0cz-launch-canary-template.md | 10 + .../phase-1.0do-snapshot-runner-template.json | 1 + ...phase-1.0dr-inventory-runner-template.json | 1 + .../phase-1.0dt-metadata-runner-template.json | 1 + .../phase-1.0e-one-shot-result-test.md | 28 + .../phase-1.0f-device-test-template.md | 24 + .../phase-1.0g-one-shot-runner-template.md | 34 + .../phase-1.0h-device-test-template.md | 23 + ...phase-1.0k-write-diag-one-shot-template.md | 38 + ...phase-1.0n-write-free-one-shot-template.md | 39 + .../phase-1.0t-shsrv-metadata-collection.md | 32 + docs/approvals/phase-1.0v-shsrv-collector.md | 35 + docs/approvals/phase-1.0w-shsrv-client.md | 43 + .../phase-1.0x-inactive-transport.md | 47 + docs/approvals/phase-1.0y-shsrv-framing.md | 33 + docs/approvals/phase-1.0z-passive-batch.md | 27 + .../phase07-hardened-installation-request.md | 39 + ...07-lifecycle-transfer-execution-request.md | 41 + .../probe-9.60-transfer-execution.md | 94 + .../external-evidence-integrity-2026-07-29.md | 54 + docs/evidence/phase0-build-2026-07-16.md | 41 + docs/evidence/phase0-build-2026-07-17.md | 76 + .../phase0.5-startup-offline-2026-07-17.md | 67 + .../phase07-offline-build-2026-07-17.md | 61 + .../evidence/probe-9.60-offline-2026-07-17.md | 147 + docs/phase1/APPROVAL_PACKAGE.md | 53 + docs/phase1/FIRMWARE_ABI_CHECKLIST.md | 39 + docs/phase1/HARDWARE_TEST_PLAN.md | 58 + docs/phase1/README.md | 13 + docs/phase1/VIDEOOUT_CLEAR_EXPERIMENT.md | 62 + docs/retroarch/phase-1.0a-build-results.md | 169 + docs/retroarch/phase-1.0a-driver-status.md | 55 + .../phase-1.0a-next-device-smoke-test.md | 65 + .../phase-1.0a-pacbrew-sdl-analysis.md | 56 + docs/retroarch/phase-1.0a-port-plan.md | 70 + .../phase-1.0a-ps4-reference-delta.md | 29 + .../retroarch/phase-1.0a-upstream-analysis.md | 67 + ...-1.0aa-offline-fake-adapter-integration.md | 70 + ...-1.0ab-offline-live-adapter-feasibility.md | 79 + ...hase-1.0ab-timeout-and-cleanup-contract.md | 50 + .../phase-1.0ac-fake-syscall-contract.md | 24 + .../phase-1.0ac-offline-dormant-adapter.md | 55 + ...hase-1.0ad-inactive-activation-contract.md | 24 + ...ae-minimal-bigapp-launcher-architecture.md | 79 + ...se-1.0af-offline-bigapp-lifecycle-model.md | 25 + .../phase-1.0ag-bounded-elf-contract.md | 26 + ...phase-1.0ah-dynamic-relocation-contract.md | 28 + ...phase-1.0ai-offline-mapping-transaction.md | 25 + .../phase-1.0aj-offline-primitive-audit.md | 30 + .../phase-1.0ak-offline-hybrid-composition.md | 23 + .../phase-1.0al-offline-mdbg-copy-audit.md | 24 + .../phase-1.0am-offline-bounded-copy-model.md | 22 + ...e-1.0an-offline-service-lifecycle-audit.md | 25 + ...e-1.0ao-offline-worker-supervisor-model.md | 22 + ...-1.0ap-offline-worker-feasibility-audit.md | 26 + ...hase-1.0aq-offline-worker-result-record.md | 23 + ...hase-1.0ar-offline-result-channel-model.md | 21 + ...e-1.0as-offline-channel-primitive-audit.md | 21 + .../phase-1.0at-offline-fd-deadline-model.md | 20 + .../phase-1.0au-live-channel-feasibility.md | 21 + ...se-1.0av-launch-context-canary-contract.md | 28 + .../phase-1.0aw-canary-source-delta-audit.md | 28 + .../phase-1.0ax-canary-protocol-model.md | 24 + .../phase-1.0ay-target-source-base.md | 20 + docs/retroarch/phase-1.0az-host-av-source.md | 30 + .../phase-1.0b-device-risk-assessment.md | 49 + .../phase-1.0b-linker-and-wx-analysis.md | 62 + .../phase-1.0b-persistent-write-audit.md | 47 + .../phase-1.0b-proposed-one-shot-test.md | 49 + .../phase-1.0b-runtime-and-exit-contract.md | 65 + .../phase-1.0b-smoke-candidate-design.md | 81 + ...ase-1.0ba-target-profile-callsite-audit.md | 24 + ...1.0bb-source-only-launch-canary-profile.md | 21 + ...se-1.0bc-cross-build-prerequisite-audit.md | 22 + ...e-1.0bd-dormant-sdl-materializer-policy.md | 19 + .../phase-1.0be-fake-only-sdl-materializer.md | 15 + ...e-1.0bf-live-sdl-adapter-boundary-audit.md | 22 + ...hase-1.0bg-dormant-sdl-request-compiler.md | 15 + .../phase-1.0bh-bounded-sdl-executor.md | 16 + ....0bi-real-facade-and-tool-install-audit.md | 21 + ...se-1.0bj-exact-host-tool-install-result.md | 18 + ...k-bounded-real-facade-host-fixture-gate.md | 17 + ...0bl-fixture-import-failure-and-new-gate.md | 16 + .../phase-1.0bm-real-facade-fixture-result.md | 16 + ...aterializer-preflight-patch-chain-audit.md | 20 + ...se-1.0bo-patch-chain-remediation-result.md | 18 + ...1.0bp-one-shot-sdl-materialization-gate.md | 17 + .../phase-1.0bq-inline-invocation-failure.md | 16 + ...e-1.0br-script-entrypoint-one-shot-gate.md | 15 + .../phase-1.0bs-read-only-status-timeout.md | 14 + ...e-1.0bt-corrected-timeout-one-shot-gate.md | 13 + ...us-timeout-and-windows-git-fixture-gate.md | 15 + ...ture-result-and-request-correction-gate.md | 16 + ...orrected-sdl-materializer-one-shot-gate.md | 15 + ...iles-timeout-and-read-only-fixture-gate.md | 15 + ...tage-fixture-failure-and-file-hash-gate.md | 13 + ...hase-1.0bz-exact-sdl-stage-cleanup-gate.md | 15 + .../phase-1.0c-device-smoke-result.md | 153 + ...esult-and-hash-verifier-correction-gate.md | 15 + ...verified-sdl-materializer-one-shot-gate.md | 14 + ...e-timeout-and-exact-output-cleanup-gate.md | 16 + ...t-and-configure-timeout-correction-gate.md | 15 + ...onfigure-sdl-materializer-one-shot-gate.md | 11 + ...ated-configure-timeout-and-cleanup-gate.md | 14 + ...native-wsl-sdl-materializer-design-gate.md | 15 + ...tive-wsl-sdl-materializer-one-shot-gate.md | 11 + ...-native-sdk-bottleneck-and-cleanup-gate.md | 16 + ...hase-1.0cj-native-sdk-stage-design-gate.md | 14 + ...tive-sdk-sdl-materializer-one-shot-gate.md | 11 + ...sdk-configure-progress-and-cleanup-gate.md | 15 + ...sured-configure-timeout-correction-gate.md | 11 + ...d-native-sdl-materializer-one-shot-gate.md | 10 + ...success-build-output-limit-cleanup-gate.md | 12 + ....0cp-build-output-limit-correction-gate.md | 9 + ...cq-full-output-native-sdl-one-shot-gate.md | 10 + ...e-1.0cr-audited-sdl-archive-export-gate.md | 17 + ...ase-1.0cs-export-parent-correction-gate.md | 13 + .../phase-1.0ct-sdl-archive-export-result.md | 14 + ...hase-1.0cu-exact-sdl-header-export-gate.md | 14 + ...-1.0cv-launch-canary-offline-build-gate.md | 15 + ...hase-1.0cw-launch-canary-artifact-audit.md | 21 + ...0cx-isolated-reproducibility-build-gate.md | 15 + ...cy-launch-canary-reproducibility-result.md | 15 + ...ase-1.0cz-inactive-launch-canary-runner.md | 23 + docs/retroarch/phase-1.0d-crt-entry-canary.md | 38 + .../phase-1.0d-early-diagnostic-design.md | 52 + .../phase-1.0d-loader-static-model.md | 35 + .../phase-1.0d-loader-to-entry-analysis.md | 55 + .../phase-1.0d-next-device-test-ladder.md | 44 + .../phase-1.0d-startup-import-closure.md | 66 + ...ase-1.0db-post-cz-differential-analysis.md | 40 + ...e-1.0dc-inactive-bigapp-comparison-gate.md | 26 + ...imal-launcher-source-prerequisite-audit.md | 41 + ...1.0de-independent-abi-provenance-search.md | 27 + ...-1.0df-inactive-title-presence-observer.md | 25 + ...se-1.0dg-title-presence-primitive-audit.md | 29 + .../phase-1.0dh-hash-bound-snapshot-query.md | 21 + .../phase-1.0di-live-acquisition-boundary.md | 25 + docs/retroarch/phase-1.0dj-di-t2-result.md | 21 + .../phase-1.0dk-port-status-result.md | 18 + ...ase-1.0dl-official-shsrv-artifact-audit.md | 23 + .../phase-1.0dm-bounded-snapshot-observer.md | 27 + .../phase-1.0dn-inactive-snapshot-receiver.md | 15 + .../phase-1.0do-inactive-snapshot-runner.md | 15 + docs/retroarch/phase-1.0do-snapshot-result.md | 19 + .../phase-1.0dp-listener-status-result.md | 15 + ...phase-1.0dq-bounded-fake00000-inventory.md | 22 + .../retroarch/phase-1.0dr-inventory-result.md | 15 + .../phase-1.0ds-bounded-fake00000-metadata.md | 14 + docs/retroarch/phase-1.0dt-metadata-result.md | 15 + .../phase-1.0du-exact-package-stat.md | 7 + .../phase-1.0dv-package-stat-result.md | 14 + .../phase-1.0dw-exact-package-readback.md | 9 + ...hase-1.0dx-official-package-correlation.md | 20 + .../phase-1.0dx-package-readback-result.md | 16 + .../phase-1.0dy-upstream-websrv-audit.md | 17 + .../phase-1.0dy-websrv-listener-result.md | 13 + .../phase-1.0dz-direct-launcher-decision.md | 50 + .../phase-1.0e-device-observations.md | 27 + .../phase-1.0e-inherited-result-channel.md | 104 + docs/retroarch/phase-1.0e-next-device-test.md | 38 + .../phase-1.0ea-expanded-launch-evidence.md | 59 + .../phase-1.0eb-public-launch-matrix.md | 65 + docs/retroarch/phase-1.0f-startup-interval.md | 93 + docs/retroarch/phase-1.0g-device-result.md | 37 + docs/retroarch/phase-1.0g-one-shot-runner.md | 54 + docs/retroarch/phase-1.0h-device-result.md | 50 + docs/retroarch/phase-1.0h-startup-args.md | 66 + .../phase-1.0i-flip-and-write-analysis.md | 139 + .../phase-1.0j-write-firewall-diagnostic.md | 107 + .../phase-1.0k-write-diag-one-shot-runner.md | 76 + ...ase-1.0l-write-firewall-result-analysis.md | 64 + .../phase-1.0m-write-free-defaults.md | 63 + .../phase-1.0n-inactive-one-shot-runner.md | 40 + .../phase-1.0o-write-free-device-result.md | 53 + .../phase-1.0p-terminal-ordering-analysis.md | 24 + .../phase-1.0p-videoout-submit-analysis.md | 74 + .../phase-1.0q-public-videoout-evidence.md | 109 + .../phase-1.0r-launch-context-comparison.md | 126 + ...se-1.0r-sdl2main-and-packaging-analysis.md | 81 + .../phase-1.0s-bigapp-launch-contract.md | 104 + .../phase-1.0s-hbldr-shsrv-provenance.md | 107 + ...phase-1.0t-inactive-shsrv-identity-gate.md | 130 + ...ase-1.0u-local-shsrv-artifact-inventory.md | 78 + ...-1.0v-inactive-one-shot-shsrv-collector.md | 105 + ...review-and-inactive-client-architecture.md | 95 + .../phase-1.0x-inactive-injected-transport.md | 95 + .../phase-1.0y-offline-shsrv-framing-audit.md | 143 + ...ase-1.0z-offline-passive-batch-contract.md | 75 + docs/reviews/phase0-final-2026-07-17.md | 51 + docs/runtime/controlled-runtime-policy.md | 87 + docs/runtime/kernelwrite-proof-matrix.md | 93 + docs/runtime/loader-contract.md | 220 + docs/runtime/minimal-crt-feasibility.md | 131 + docs/runtime/payload-manager-policy.md | 72 + docs/runtime/phase-0.5-review.md | 48 + docs/runtime/phase-0.6-loader-audit.md | 122 + docs/runtime/phase-0.6-review.md | 58 + docs/runtime/phase-0.7-hardening.md | 79 + docs/runtime/phase-0.8-read-only-preflight.md | 137 + docs/runtime/phase-0.8-remediation.md | 289 + .../phase-0.9-anti-brick-threat-model.md | 188 + ...ase-0.9-installation-transaction-design.md | 201 + ...hase-0.9-recovery-and-rollback-contract.md | 106 + .../phase-0.9b-bounded-observer-design.md | 133 + .../phase-0.9b-observer-limitations.md | 108 + .../phase-0.9b-observer-result-contract.md | 114 + .../phase-0.9b-observer-static-audit.md | 152 + docs/runtime/phase-0.9c-capability-closure.md | 103 + .../phase-0.9c-output-channel-feasibility.md | 268 + docs/runtime/phase-0.9c-side-effect-model.md | 108 + .../phase-0.9c-startup-exit-feasibility.md | 289 + docs/runtime/phase-0.9c-static-audit.md | 147 + .../phase-0.9d-bootstrap-recovery-chain.md | 107 + ...ase-0.9d-existing-stack-endpoint-matrix.md | 123 + ...hase-0.9d-independent-recovery-analysis.md | 71 + .../phase-0.9d-off-device-backup-contract.md | 81 + .../runtime/phase-0.9d-operational-windows.md | 91 + .../phase-0.9d-readback-feasibility.md | 115 + .../phase-0.9e-bootstrap-provenance.md | 109 + ...ase-0.9e-future-rescue-payload-contract.md | 64 + .../phase-0.9e-independent-rescue-chain.md | 80 + .../phase-0.9e-loader-9020-protocol.md | 63 + .../phase-0.9e-output-architecture-options.md | 30 + .../phase-0.9e-r-official-hostsender-audit.md | 56 + ...ase-0.9e-r-official-release-correlation.md | 125 + .../phase-0.9e-r-port9020-source-audit.md | 148 + docs/runtime/phase-0.9e-r-provenance-gaps.md | 63 + .../phase-0.9e-r-release-source-binding.md | 93 + ...se-0.9e-r2-community-backup-correlation.md | 70 + ...phase-0.9e-r2-final-provenance-decision.md | 67 + ...phase-0.9e-r2-inner-archive-correlation.md | 68 + ...phase-0.9e-r2-local-download-provenance.md | 78 + ...hase-0.9e-r2-siecaf-structural-analysis.md | 70 + .../phase-0.9e-reboot-and-crash-model.md | 57 + docs/runtime/startup-callgraph.md | 198 + include/chimera/gfx/adapters/retroarch.h | 34 + include/chimera/gfx/adapters/sdl2.h | 34 + include/chimera/gfx/chimera_gfx.h | 201 + manifests/artifact-denylist.json | 21 + manifests/artifact-denylist.schema.json | 46 + manifests/artifact-manifest.schema.json | 87 + .../chimera-elfldr-phase07-fw-9.60.json | 38 + ...ra-gfx-capability-probe-0.1.0-fw-9.60.json | 39 + ...imera-gfx-capability-probe-0.1.0-none.json | 40 + ...a-gfx-lifecycle-probe-phase07-fw-9.60.json | 38 + ...-gfx-phase1-videoout-clear-0.1.0-none.json | 42 + ...imera-payload-manager-phase07-fw-9.60.json | 38 + .../controlled-runtime-profile.schema.json | 237 + manifests/ps5_gnm_symbols.json | 44 + manifests/retroarch/phase-1.0a-artifacts.json | 661 + manifests/retroarch/phase-1.0a-build.json | 223 + .../phase-1.0aa-offline-fake-adapter.json | 115 + .../phase-1.0ab-live-adapter-feasibility.json | 120 + .../phase-1.0ac-dormant-adapter.json | 110 + .../phase-1.0ad-inactive-activation.json | 60 + .../phase-1.0ae-launcher-architecture.json | 72 + .../phase-1.0af-bigapp-lifecycle-model.json | 57 + .../phase-1.0ag-bounded-elf-contract.json | 64 + .../phase-1.0ah-dynamic-contract.json | 90 + .../retroarch/phase-1.0ai-mapping-model.json | 65 + .../phase-1.0aj-primitive-audit.json | 60 + .../phase-1.0ak-hybrid-composition.json | 57 + .../phase-1.0al-mdbg-copy-audit.json | 53 + .../phase-1.0am-bounded-copy-model.json | 60 + .../phase-1.0an-service-lifecycle-audit.json | 59 + .../phase-1.0ao-worker-supervisor-model.json | 59 + .../phase-1.0ap-worker-feasibility-audit.json | 72 + .../phase-1.0aq-worker-result-record.json | 75 + .../phase-1.0ar-result-channel-model.json | 62 + .../phase-1.0as-channel-primitive-audit.json | 74 + .../phase-1.0at-fd-deadline-model.json | 68 + .../phase-1.0au-live-channel-feasibility.json | 84 + ...-1.0av-launch-context-canary-contract.json | 87 + ...phase-1.0aw-canary-source-delta-audit.json | 112 + .../phase-1.0ax-canary-protocol-model.json | 89 + .../phase-1.0ay-target-source-base.json | 62 + .../retroarch/phase-1.0az-host-av-source.json | 68 + manifests/retroarch/phase-1.0b-artifact.json | 443 + manifests/retroarch/phase-1.0b-build.json | 178 + .../phase-1.0b-runtime-contract.json | 159 + ...e-1.0ba-target-profile-callsite-audit.json | 49 + ...0bb-source-only-launch-canary-profile.json | 52 + ...-1.0bc-cross-build-prerequisite-audit.json | 48 + ...1.0bd-dormant-sdl-materializer-policy.json | 47 + ...hase-1.0be-fake-only-sdl-materializer.json | 46 + ...1.0bf-live-sdl-adapter-boundary-audit.json | 54 + ...se-1.0bg-dormant-sdl-request-compiler.json | 48 + .../phase-1.0bh-bounded-sdl-executor.json | 48 + ...bi-real-facade-and-tool-install-audit.json | 55 + ...-1.0bj-exact-host-tool-install-result.json | 42 + ...bounded-real-facade-host-fixture-gate.json | 62 + ...l-fixture-import-failure-and-new-gate.json | 51 + ...hase-1.0bm-real-facade-fixture-result.json | 47 + ...erializer-preflight-patch-chain-audit.json | 52 + ...-1.0bo-patch-chain-remediation-result.json | 59 + ...0bp-one-shot-sdl-materialization-gate.json | 56 + ...phase-1.0bq-inline-invocation-failure.json | 43 + ...1.0br-script-entrypoint-one-shot-gate.json | 51 + .../phase-1.0bs-read-only-status-timeout.json | 51 + ...1.0bt-corrected-timeout-one-shot-gate.json | 52 + ...-timeout-and-windows-git-fixture-gate.json | 61 + ...re-result-and-request-correction-gate.json | 52 + ...rected-sdl-materializer-one-shot-gate.json | 54 + ...es-timeout-and-read-only-fixture-gate.json | 74 + ...ge-fixture-failure-and-file-hash-gate.json | 54 + ...se-1.0bz-exact-sdl-stage-cleanup-gate.json | 59 + .../phase-1.0c-device-smoke-result.json | 183 + ...ult-and-hash-verifier-correction-gate.json | 55 + ...rified-sdl-materializer-one-shot-gate.json | 55 + ...timeout-and-exact-output-cleanup-gate.json | 67 + ...and-configure-timeout-correction-gate.json | 46 + ...figure-sdl-materializer-one-shot-gate.json | 48 + ...ed-configure-timeout-and-cleanup-gate.json | 52 + ...tive-wsl-sdl-materializer-design-gate.json | 53 + ...ve-wsl-sdl-materializer-one-shot-gate.json | 49 + ...ative-sdk-bottleneck-and-cleanup-gate.json | 53 + ...se-1.0cj-native-sdk-stage-design-gate.json | 52 + ...ve-sdk-sdl-materializer-one-shot-gate.json | 49 + ...k-configure-progress-and-cleanup-gate.json | 49 + ...red-configure-timeout-correction-gate.json | 46 + ...native-sdl-materializer-one-shot-gate.json | 46 + ...ccess-build-output-limit-cleanup-gate.json | 45 + ...cp-build-output-limit-correction-gate.json | 30 + ...-full-output-native-sdl-one-shot-gate.json | 11 + ...1.0cr-audited-sdl-archive-export-gate.json | 12 + ...e-1.0cs-export-parent-correction-gate.json | 12 + ...phase-1.0ct-sdl-archive-export-result.json | 11 + ...se-1.0cu-exact-sdl-header-export-gate.json | 12 + ....0cv-launch-canary-offline-build-gate.json | 12 + ...se-1.0cw-launch-canary-artifact-audit.json | 12 + ...x-isolated-reproducibility-build-gate.json | 12 + ...-launch-canary-reproducibility-result.json | 12 + ...e-1.0cz-launch-canary-one-shot-active.json | 14 + ...e-1.0cz-launch-canary-one-shot-runner.json | 14 + ...hase-1.0cz-one-shot-approval-template.json | 31 + .../retroarch/phase-1.0d-canary-artifact.json | 128 + .../phase-1.0d-early-diag-artifact.json | 202 + .../phase-1.0d-loader-model-results.json | 125 + ...hase-1.0da-raw-elfldr-baseline-result.json | 16 + ...e-1.0db-post-cz-differential-analysis.json | 75 + ...1.0dc-inactive-bigapp-comparison-gate.json | 87 + ...al-launcher-source-prerequisite-audit.json | 97 + ...0de-independent-abi-provenance-search.json | 70 + ....0df-inactive-title-presence-observer.json | 82 + ...-1.0dg-title-presence-primitive-audit.json | 76 + ...phase-1.0dh-hash-bound-snapshot-query.json | 57 + ...phase-1.0di-live-acquisition-boundary.json | 80 + .../retroarch/phase-1.0dj-di-t2-result.json | 53 + .../phase-1.0dk-port-status-result.json | 44 + ...e-1.0dl-official-shsrv-artifact-audit.json | 58 + ...phase-1.0dm-bounded-snapshot-observer.json | 63 + ...hase-1.0dn-inactive-snapshot-receiver.json | 11 + .../phase-1.0do-inactive-snapshot-runner.json | 1 + .../phase-1.0do-snapshot-result.json | 15 + .../phase-1.0dp-listener-status-result.json | 19 + .../phase-1.0dq-inventory-observer.json | 1 + ...phase-1.0dr-inactive-inventory-runner.json | 1 + .../phase-1.0dr-inventory-result.json | 14 + .../phase-1.0ds-metadata-observer.json | 1 + .../phase-1.0dt-inactive-metadata-runner.json | 1 + .../phase-1.0dt-metadata-result.json | 15 + .../retroarch/phase-1.0du-package-stat.json | 1 + ...se-1.0dv-inactive-package-stat-runner.json | 1 + .../phase-1.0dv-package-stat-result.json | 15 + .../phase-1.0dw-package-readback.json | 1 + ....0dx-inactive-package-readback-runner.json | 1 + .../phase-1.0dx-package-readback-result.json | 13 + .../phase-1.0dy-upstream-websrv-audit.json | 10 + .../phase-1.0dy-websrv-listener-result.json | 17 + .../phase-1.0dz-direct-launcher-decision.json | 56 + .../retroarch/phase-1.0e-result-channel.json | 327 + .../phase-1.0ea-expanded-launch-evidence.json | 63 + .../phase-1.0eb-public-launch-matrix.json | 93 + .../phase-1.0f-startup-interval.json | 247 + .../retroarch/phase-1.0g-device-result.json | 106 + ...phase-1.0g-one-shot-approval-template.json | 31 + .../retroarch/phase-1.0g-one-shot-runner.json | 111 + .../retroarch/phase-1.0h-device-result.json | 128 + .../retroarch/phase-1.0h-startup-args.json | 163 + .../phase-1.0i-flip-and-write-analysis.json | 149 + .../phase-1.0j-write-firewall-diagnostic.json | 165 + ...phase-1.0k-one-shot-approval-template.json | 31 + ...phase-1.0k-write-diag-one-shot-runner.json | 130 + ...e-1.0l-write-firewall-result-analysis.json | 137 + .../phase-1.0m-write-free-defaults.json | 158 + ...phase-1.0n-one-shot-approval-template.json | 31 + ...phase-1.0n-write-free-one-shot-runner.json | 134 + .../phase-1.0o-write-free-device-result.json | 144 + .../phase-1.0p-videoout-submit-analysis.json | 127 + .../phase-1.0q-public-videoout-evidence.json | 129 + .../phase-1.0r-launch-context-analysis.json | 112 + .../phase-1.0s-launcher-provenance.json | 162 + .../phase-1.0t-shsrv-identity-gate.json | 132 + .../phase-1.0u-local-shsrv-inventory.json | 119 + .../phase-1.0v-inactive-shsrv-collector.json | 117 + ...ase-1.0w-inactive-client-architecture.json | 124 + .../phase-1.0x-inactive-transport.json | 145 + .../retroarch/phase-1.0y-shsrv-framing.json | 145 + .../retroarch/phase-1.0z-passive-batch.json | 129 + manifests/retroarch/upstreams.json | 72 + .../controlled-ps5-runtime-profile.json | 133 + .../runtime/kernelwrite-proof-matrix.json | 77 + .../minimal-startup-artifact-decision.json | 41 + .../runtime/phase-0.5-startup-audit.json | 1401 ++ .../phase-0.6-loader-runtime-audit.json | 541 + .../phase-0.7-kernelwrite-proof-matrix.json | 174 + .../runtime/phase-0.7-offline-audit.json | 12457 ++++++++++++++++ .../phase-0.8-read-only-preflight.json | 232 + manifests/runtime/phase-0.8-remediation.json | 734 + .../runtime/phase-0.9-anti-brick-design.json | 491 + .../phase-0.9b-observation-plan.schema.json | 267 + manifests/runtime/phase-0.9b-observer.json | 372 + manifests/runtime/phase-0.9c-feasibility.json | 416 + .../phase-0.9c-feasibility.schema.json | 336 + .../phase-0.9d-existing-stack-readback.json | 428 + ...e-0.9d-existing-stack-readback.schema.json | 155 + .../phase-0.9e-bootstrap-provenance.json | 685 + ...hase-0.9e-bootstrap-provenance.schema.json | 181 + .../runtime/phase-0.9e-loader-protocol.json | 79 + .../phase-0.9e-loader-protocol.schema.json | 125 + .../runtime/phase-0.9e-r-port9020-audit.json | 105 + .../phase-0.9e-r-port9020-audit.schema.json | 42 + .../phase-0.9e-r-release-correlation.json | 446 + ...ase-0.9e-r-release-correlation.schema.json | 90 + .../phase-0.9e-r2-inner-correlation.json | 477 + .../phase-0.9e-r2-siecaf-fingerprints.json | 4420 ++++++ manifests/sbom.spdx.json | 136 + manifests/upstreams.lock.json | 83 + packaging/Dockerfile | 52 + packaging/README.md | 15 + .../patches/sdl2-phase1-video-only.patch | 57 + .../phase07/README-installation-review.md | 19 + packaging/phase07/README-rollback.md | 25 + packaging/phase09b/SHA256SUMS.txt | 10 + packaging/phase09c/SHA256SUMS.txt | 11 + packaging/phase09e/SHA256SUMS.txt | 24 + packaging/phase09er/SHA256SUMS.txt | 22 + packaging/phase09er2/SHA256SUMS.txt | 21 + packaging/retroarch/phase10a/SHA256SUMS.txt | 6 + packaging/retroarch/phase10b/SHA256SUMS.txt | 4 + packaging/retroarch/phase10c/SHA256SUMS.txt | 5 + packaging/retroarch/phase10d/SHA256SUMS.txt | 4 + packaging/retroarch/phase10e/SHA256SUMS.txt | 6 + packaging/retroarch/phase10f/SHA256SUMS.txt | 4 + packaging/retroarch/phase10g/SHA256SUMS.txt | 5 + packaging/retroarch/phase10h/SHA256SUMS.txt | 4 + packaging/retroarch/phase10j/SHA256SUMS.txt | 3 + packaging/retroarch/phase10k/SHA256SUMS.txt | 4 + packaging/retroarch/phase10m/SHA256SUMS.txt | 3 + packaging/retroarch/phase10n/SHA256SUMS.txt | 4 + samples/capability_probe/main.c | 44 + samples/clear_screen/README.md | 7 + samples/lifecycle_probe/main.c | 30 + samples/phase1_videoout_clear/main.c | 88 + src/backends/mock/mock_backend.c | 139 + src/backends/ps5/firmware_gate.c | 17 + src/backends/ps5/firmware_gate.h | 8 + src/backends/ps5/generated_probe_symbols.inc | 22 + src/backends/ps5/probe.h | 30 + src/backends/ps5/probe_logic.c | 83 + src/backends/ps5/probe_platform.c | 58 + src/backends/ps5/probe_platform.h | 18 + src/backends/ps5/ps5_backend.c | 10 + tests/phase09b_observer_model.py | 140 + tests/phase09c_feasibility_model.py | 579 + tests/phase10w_fake_transport.py | 53 + tests/test_adapters.c | 62 + tests/test_artifact_audit.py | 60 + tests/test_core.c | 108 + tests/test_execution_policy.py | 327 + tests/test_firmware_gate.c | 27 + tests/test_manifest_tools.py | 93 + tests/test_minimal_startup_block.py | 53 + tests/test_mock_integration.c | 169 + tests/test_phase05_audit.py | 71 + tests/test_phase06_audit.py | 70 + tests/test_phase07_audit.py | 160 + tests/test_phase08_preflight.py | 110 + tests/test_phase08_remediation.py | 193 + tests/test_phase09_transaction.py | 763 + tests/test_phase09b_observer_audit.py | 302 + tests/test_phase09c_feasibility.py | 199 + tests/test_phase09c_protocol.py | 362 + tests/test_phase09d_readback.py | 405 + tests/test_phase09e_bootstrap.py | 302 + tests/test_phase09er2_correlation.py | 404 + tests/test_phase09er_provenance.py | 323 + tests/test_phase10aa_offline_fake_batch.py | 226 + .../test_phase10ab_nonblocking_trace_model.py | 120 + tests/test_phase10ac_dormant_adapter.py | 267 + tests/test_phase10ad_activation_contract.py | 95 + .../test_phase10af_bigapp_lifecycle_model.py | 108 + tests/test_phase10ag_bounded_elf.py | 108 + tests/test_phase10ah_dynamic_contract.py | 120 + tests/test_phase10ai_mapping_model.py | 133 + tests/test_phase10ak_hybrid_composition.py | 120 + tests/test_phase10am_bounded_copy_model.py | 103 + .../test_phase10ao_worker_supervisor_model.py | 105 + tests/test_phase10aq_worker_result_record.py | 103 + tests/test_phase10ar_result_channel_model.py | 96 + tests/test_phase10at_fd_deadline_model.py | 117 + tests/test_phase10av_launch_context_canary.py | 113 + tests/test_phase10ax_canary_protocol_model.py | 119 + tests/test_phase10dc_bigapp_gate_contract.py | 39 + .../test_phase10df_title_observer_contract.py | 35 + tests/test_phase10dh_snapshot_query.py | 37 + tests/test_phase10dm_snapshot_protocol.py | 25 + tests/test_phase10dn_snapshot_receiver.py | 34 + ...test_phase10do_one_shot_snapshot_runner.py | 28 + tests/test_phase10dq_inventory_protocol.py | 23 + tests/test_phase10dr_inventory_runner.py | 24 + tests/test_phase10ds_metadata_protocol.py | 22 + tests/test_phase10dt_metadata_runner.py | 18 + tests/test_phase10dv_package_stat_runner.py | 12 + .../test_phase10dx_package_readback_runner.py | 12 + tests/test_phase10t_shsrv_transcript.py | 128 + tests/test_phase10v_shsrv_collector_model.py | 202 + tests/test_phase10w_shsrv_client_policy.py | 269 + tests/test_phase10x_inactive_transport.py | 284 + tests/test_phase10y_shsrv_framing_model.py | 157 + tests/test_phase10z_passive_batch_contract.py | 206 + tests/test_probe.c | 121 + tests/test_retroarch_phase10a.py | 205 + tests/test_retroarch_phase10aa.py | 61 + tests/test_retroarch_phase10ab.py | 49 + tests/test_retroarch_phase10ac.py | 69 + tests/test_retroarch_phase10ad.py | 43 + tests/test_retroarch_phase10ae.py | 41 + tests/test_retroarch_phase10af.py | 42 + tests/test_retroarch_phase10ag.py | 44 + tests/test_retroarch_phase10ah.py | 44 + tests/test_retroarch_phase10ai.py | 43 + tests/test_retroarch_phase10aj.py | 42 + tests/test_retroarch_phase10ak.py | 44 + tests/test_retroarch_phase10al.py | 44 + tests/test_retroarch_phase10am.py | 47 + tests/test_retroarch_phase10an.py | 45 + tests/test_retroarch_phase10ao.py | 46 + tests/test_retroarch_phase10ap.py | 45 + tests/test_retroarch_phase10aq.py | 46 + tests/test_retroarch_phase10ar.py | 48 + tests/test_retroarch_phase10as.py | 46 + tests/test_retroarch_phase10at.py | 49 + tests/test_retroarch_phase10au.py | 51 + tests/test_retroarch_phase10av.py | 54 + tests/test_retroarch_phase10aw.py | 58 + tests/test_retroarch_phase10ax.py | 50 + tests/test_retroarch_phase10ay.py | 47 + tests/test_retroarch_phase10az.py | 48 + tests/test_retroarch_phase10b.py | 216 + tests/test_retroarch_phase10ba.py | 49 + tests/test_retroarch_phase10bb.py | 39 + tests/test_retroarch_phase10bc.py | 46 + tests/test_retroarch_phase10bd.py | 41 + tests/test_retroarch_phase10be.py | 55 + tests/test_retroarch_phase10bf.py | 44 + tests/test_retroarch_phase10bg.py | 47 + tests/test_retroarch_phase10bh.py | 40 + tests/test_retroarch_phase10bi.py | 38 + tests/test_retroarch_phase10bj.py | 50 + tests/test_retroarch_phase10bk.py | 50 + tests/test_retroarch_phase10bl.py | 52 + tests/test_retroarch_phase10bm.py | 43 + tests/test_retroarch_phase10bn.py | 46 + tests/test_retroarch_phase10bo.py | 50 + tests/test_retroarch_phase10bp.py | 51 + tests/test_retroarch_phase10bq.py | 42 + tests/test_retroarch_phase10br.py | 50 + tests/test_retroarch_phase10bs.py | 47 + tests/test_retroarch_phase10bt.py | 45 + tests/test_retroarch_phase10bu.py | 52 + tests/test_retroarch_phase10bv.py | 54 + tests/test_retroarch_phase10bw.py | 59 + tests/test_retroarch_phase10bx.py | 51 + tests/test_retroarch_phase10by.py | 52 + tests/test_retroarch_phase10bz.py | 49 + tests/test_retroarch_phase10ca.py | 52 + tests/test_retroarch_phase10cb.py | 56 + tests/test_retroarch_phase10cc.py | 55 + tests/test_retroarch_phase10cd.py | 52 + tests/test_retroarch_phase10ce.py | 45 + tests/test_retroarch_phase10cf.py | 46 + tests/test_retroarch_phase10cg.py | 49 + tests/test_retroarch_phase10ch.py | 44 + tests/test_retroarch_phase10ci.py | 46 + tests/test_retroarch_phase10cj.py | 47 + tests/test_retroarch_phase10ck.py | 44 + tests/test_retroarch_phase10cl.py | 40 + tests/test_retroarch_phase10cm.py | 43 + tests/test_retroarch_phase10cn.py | 39 + tests/test_retroarch_phase10co.py | 26 + tests/test_retroarch_phase10cp.py | 16 + tests/test_retroarch_phase10cq.py | 14 + tests/test_retroarch_phase10cr.py | 15 + tests/test_retroarch_phase10cs.py | 14 + tests/test_retroarch_phase10ct.py | 14 + tests/test_retroarch_phase10cu.py | 15 + tests/test_retroarch_phase10cv.py | 15 + tests/test_retroarch_phase10cw.py | 14 + tests/test_retroarch_phase10cx.py | 15 + tests/test_retroarch_phase10cy.py | 14 + tests/test_retroarch_phase10cz.py | 17 + tests/test_retroarch_phase10d.py | 330 + tests/test_retroarch_phase10db.py | 29 + tests/test_retroarch_phase10dc.py | 12 + tests/test_retroarch_phase10dd.py | 20 + tests/test_retroarch_phase10de.py | 16 + tests/test_retroarch_phase10df.py | 12 + tests/test_retroarch_phase10dg.py | 18 + tests/test_retroarch_phase10di.py | 18 + tests/test_retroarch_phase10dj.py | 19 + tests/test_retroarch_phase10dk.py | 18 + tests/test_retroarch_phase10dl.py | 18 + tests/test_retroarch_phase10dm.py | 17 + tests/test_retroarch_phase10dn.py | 16 + tests/test_retroarch_phase10dz.py | 46 + tests/test_retroarch_phase10e.py | 276 + tests/test_retroarch_phase10ea.py | 47 + tests/test_retroarch_phase10eb.py | 49 + tests/test_retroarch_phase10f.py | 190 + tests/test_retroarch_phase10g.py | 195 + tests/test_retroarch_phase10g_result.py | 72 + tests/test_retroarch_phase10h.py | 158 + tests/test_retroarch_phase10h_result.py | 100 + tests/test_retroarch_phase10i.py | 118 + tests/test_retroarch_phase10j.py | 138 + tests/test_retroarch_phase10k.py | 143 + tests/test_retroarch_phase10l.py | 119 + tests/test_retroarch_phase10m.py | 144 + tests/test_retroarch_phase10n.py | 142 + tests/test_retroarch_phase10o.py | 137 + tests/test_retroarch_phase10p.py | 141 + tests/test_retroarch_phase10q.py | 141 + tests/test_retroarch_phase10r.py | 128 + tests/test_retroarch_phase10s.py | 132 + tests/test_retroarch_phase10t.py | 130 + tests/test_retroarch_phase10u.py | 114 + tests/test_retroarch_phase10v.py | 126 + tests/test_retroarch_phase10w.py | 128 + tests/test_retroarch_phase10x.py | 126 + tests/test_retroarch_phase10y.py | 120 + tests/test_retroarch_phase10z.py | 71 + tests/test_siecaf_header_parser.py | 324 + tools/audit_phase07_artifacts.py | 390 + tools/audit_phase0_safety.py | 135 + tools/audit_ps5_artifacts.py | 225 + tools/audit_ps5_loader_runtime.py | 573 + tools/audit_ps5_sdk_runtime.py | 193 + tools/audit_ps5_startup_feasibility.py | 612 + tools/bootstrap-ps5-sdk.ps1 | 52 + tools/build-firmware-probe.sh | 91 + tools/build-host.ps1 | 40 + tools/build-phase1-videoout.sh | 97 + tools/build_phase10dm_observer.py | 22 + tools/build_phase10dq_inventory.py | 21 + tools/build_phase10ds_metadata.py | 16 + tools/build_phase10du_package_stat.py | 12 + tools/build_phase10dw_package_readback.py | 12 + tools/check_artifact_execution_policy.py | 341 + tools/check_format.py | 36 + tools/export-public-source.sh | 68 + tools/generate_artifact_manifest.py | 96 + tools/generate_probe_symbols.py | 41 + tools/inspect_siecaf_header.py | 509 + tools/package_phase07_review.py | 253 + tools/phase10aa_offline_fake_batch.py | 287 + tools/phase10ab_nonblocking_trace_model.py | 218 + tools/phase10ac_dormant_adapter.py | 348 + tools/phase10ad_activation_contract.py | 128 + tools/phase10af_bigapp_lifecycle_model.py | 213 + tools/phase10ag_bounded_elf.py | 148 + tools/phase10ah_dynamic_contract.py | 175 + tools/phase10ai_mapping_model.py | 239 + tools/phase10ak_hybrid_composition.py | 214 + tools/phase10am_bounded_copy_model.py | 197 + tools/phase10ao_worker_supervisor_model.py | 188 + tools/phase10aq_worker_result_record.py | 127 + tools/phase10ar_result_channel_model.py | 105 + tools/phase10at_fd_deadline_model.py | 205 + tools/phase10av_launch_context_canary.py | 179 + tools/phase10ax_canary_protocol_model.py | 189 + tools/phase10dc_bigapp_gate_contract.py | 138 + tools/phase10df_title_observer_contract.py | 136 + tools/phase10dh_snapshot_query.py | 96 + tools/phase10dm_snapshot_protocol.py | 35 + tools/phase10dn_snapshot_receiver.py | 41 + tools/phase10do_one_shot_snapshot_runner.py | 65 + tools/phase10dq_inventory_protocol.py | 33 + tools/phase10dr_inventory_runner.py | 47 + tools/phase10ds_metadata_protocol.py | 28 + tools/phase10dt_metadata_runner.py | 43 + tools/phase10dv_package_stat_runner.py | 38 + tools/phase10dx_package_readback_runner.py | 53 + tools/phase10t_shsrv_transcript.py | 160 + tools/phase10v_shsrv_collector_model.py | 207 + tools/phase10w_shsrv_client_policy.py | 180 + tools/phase10x_inactive_transport.py | 229 + tools/phase10y_shsrv_framing_model.py | 204 + tools/phase10z_passive_batch_contract.py | 240 + tools/scan_secrets.py | 61 + tools/simulate_phase09_transaction.py | 1008 ++ tools/validate_phase08_remediation.py | 618 + tools/validate_phase09b_observer_audit.py | 464 + tools/validate_phase09c_feasibility.py | 687 + tools/validate_phase09d_readback.py | 759 + tools/validate_phase09e_bootstrap.py | 798 + tools/validate_phase09er2_correlation.py | 658 + tools/validate_phase09er_provenance.py | 621 + tools/validate_retroarch_phase10a.py | 326 + tools/validate_retroarch_phase10aa.py | 204 + tools/validate_retroarch_phase10ab.py | 146 + tools/validate_retroarch_phase10ac.py | 198 + tools/validate_retroarch_phase10ad.py | 64 + tools/validate_retroarch_phase10ae.py | 54 + tools/validate_retroarch_phase10af.py | 47 + tools/validate_retroarch_phase10ag.py | 47 + tools/validate_retroarch_phase10ah.py | 57 + tools/validate_retroarch_phase10ai.py | 47 + tools/validate_retroarch_phase10aj.py | 52 + tools/validate_retroarch_phase10ak.py | 48 + tools/validate_retroarch_phase10al.py | 46 + tools/validate_retroarch_phase10am.py | 48 + tools/validate_retroarch_phase10an.py | 46 + tools/validate_retroarch_phase10ao.py | 48 + tools/validate_retroarch_phase10ap.py | 54 + tools/validate_retroarch_phase10aq.py | 47 + tools/validate_retroarch_phase10ar.py | 48 + tools/validate_retroarch_phase10as.py | 54 + tools/validate_retroarch_phase10at.py | 37 + tools/validate_retroarch_phase10au.py | 62 + tools/validate_retroarch_phase10av.py | 41 + tools/validate_retroarch_phase10aw.py | 80 + tools/validate_retroarch_phase10ax.py | 39 + tools/validate_retroarch_phase10ay.py | 55 + tools/validate_retroarch_phase10az.py | 66 + tools/validate_retroarch_phase10b.py | 558 + tools/validate_retroarch_phase10ba.py | 49 + tools/validate_retroarch_phase10bb.py | 47 + tools/validate_retroarch_phase10bd.py | 45 + tools/validate_retroarch_phase10d.py | 326 + tools/validate_retroarch_phase10dc.py | 35 + tools/validate_retroarch_phase10df.py | 31 + tools/validate_retroarch_phase10e.py | 382 + tools/validate_retroarch_phase10f.py | 262 + tools/validate_retroarch_phase10g.py | 253 + tools/validate_retroarch_phase10g_result.py | 154 + tools/validate_retroarch_phase10h.py | 234 + tools/validate_retroarch_phase10h_result.py | 161 + tools/validate_retroarch_phase10i.py | 191 + tools/validate_retroarch_phase10j.py | 216 + tools/validate_retroarch_phase10k.py | 242 + tools/validate_retroarch_phase10l.py | 236 + tools/validate_retroarch_phase10m.py | 259 + tools/validate_retroarch_phase10n.py | 241 + tools/validate_retroarch_phase10o.py | 240 + tools/validate_retroarch_phase10p.py | 256 + tools/validate_retroarch_phase10q.py | 284 + tools/validate_retroarch_phase10r.py | 319 + tools/validate_retroarch_phase10s.py | 316 + tools/validate_retroarch_phase10t.py | 339 + tools/validate_retroarch_phase10u.py | 243 + tools/validate_retroarch_phase10v.py | 268 + tools/validate_retroarch_phase10w.py | 281 + tools/validate_retroarch_phase10x.py | 291 + tools/validate_retroarch_phase10y.py | 336 + tools/validate_retroarch_phase10z.py | 230 + tools/verify_artifact_manifest.py | 71 + tools/verify_manifests.py | 338 + 824 files changed, 100072 insertions(+) create mode 100644 .clang-format create mode 100644 .clang-tidy create mode 100644 .dockerignore create mode 100644 .editorconfig create mode 100644 .gitattributes create mode 100644 .gitea/workflows/ci.yml create mode 100644 .gitea/workflows/managed-validation.yml create mode 100644 .gitignore create mode 100644 .gitleaks.toml create mode 100644 ARCHITECTURE.md create mode 100644 CMakeLists.txt create mode 100644 CMakePresets.json create mode 100644 CONTRIBUTING.md create mode 100644 FIRMWARE_COMPATIBILITY.md create mode 100644 LICENSE create mode 100644 PUBLIC-SOURCE-MANIFEST.sha256 create mode 100644 README.md create mode 100644 RESEARCH.md create mode 100644 ROADMAP.md create mode 100644 SAFETY.md create mode 100644 SECURITY.md create mode 100644 TEST_PLAN.md create mode 100644 THIRD_PARTY_NOTICES.md create mode 100644 adapters/retroarch/README.md create mode 100644 adapters/retroarch/retroarch_adapter.c create mode 100644 adapters/sdl2/README.md create mode 100644 adapters/sdl2/sdl2_adapter.c create mode 100644 cmake/README.md create mode 100644 cmake/chimera-gfx-config.cmake.in create mode 100644 docs/DECISIONS.md create mode 100644 docs/PUBLICATION_READINESS.md create mode 100644 docs/adr/0001-standalone-repository.md create mode 100644 docs/adr/0002-export-names-are-not-abi.md create mode 100644 docs/adr/0003-fail-closed-probe.md create mode 100644 docs/adr/0004-license.md create mode 100644 docs/adr/0005-versioned-resource-lifecycle.md create mode 100644 docs/adr/0006-phase1-sdl-videoout-boundary.md create mode 100644 docs/adr/0007-artifact-provenance.md create mode 100644 docs/adr/0008-firmware-9.60-probe-build.md create mode 100644 docs/adr/0009-block-unproven-minimal-startup.md create mode 100644 docs/adr/0010-controlled-runtime-effects.md create mode 100644 docs/adr/0011-phase07-hardened-controlled-runtime.md create mode 100644 docs/adr/0012-phase10j-first-frame-identity.md create mode 100644 docs/approvals/phase-0.8-bounded-observation-template.md create mode 100644 docs/approvals/phase-0.9-backup-creation-template.md create mode 100644 docs/approvals/phase-0.9-observation-template.md create mode 100644 docs/approvals/phase-0.9-one-shot-execution-template.md create mode 100644 docs/approvals/phase-0.9-staging-template.md create mode 100644 docs/approvals/phase-0.9-switch-template.md create mode 100644 docs/approvals/phase-0.9b-observer-execution-template.md create mode 100644 docs/approvals/phase-0.9e-r-y2jb-deployed-use-attestation.md create mode 100644 docs/approvals/phase-1.0aa-offline-fake-adapter.md create mode 100644 docs/approvals/phase-1.0ab-live-adapter-feasibility.md create mode 100644 docs/approvals/phase-1.0ac-dormant-adapter.md create mode 100644 docs/approvals/phase-1.0b-device-smoke-template.md create mode 100644 docs/approvals/phase-1.0cz-launch-canary-template.md create mode 100644 docs/approvals/phase-1.0do-snapshot-runner-template.json create mode 100644 docs/approvals/phase-1.0dr-inventory-runner-template.json create mode 100644 docs/approvals/phase-1.0dt-metadata-runner-template.json create mode 100644 docs/approvals/phase-1.0e-one-shot-result-test.md create mode 100644 docs/approvals/phase-1.0f-device-test-template.md create mode 100644 docs/approvals/phase-1.0g-one-shot-runner-template.md create mode 100644 docs/approvals/phase-1.0h-device-test-template.md create mode 100644 docs/approvals/phase-1.0k-write-diag-one-shot-template.md create mode 100644 docs/approvals/phase-1.0n-write-free-one-shot-template.md create mode 100644 docs/approvals/phase-1.0t-shsrv-metadata-collection.md create mode 100644 docs/approvals/phase-1.0v-shsrv-collector.md create mode 100644 docs/approvals/phase-1.0w-shsrv-client.md create mode 100644 docs/approvals/phase-1.0x-inactive-transport.md create mode 100644 docs/approvals/phase-1.0y-shsrv-framing.md create mode 100644 docs/approvals/phase-1.0z-passive-batch.md create mode 100644 docs/approvals/phase07-hardened-installation-request.md create mode 100644 docs/approvals/phase07-lifecycle-transfer-execution-request.md create mode 100644 docs/approvals/probe-9.60-transfer-execution.md create mode 100644 docs/evidence/external-evidence-integrity-2026-07-29.md create mode 100644 docs/evidence/phase0-build-2026-07-16.md create mode 100644 docs/evidence/phase0-build-2026-07-17.md create mode 100644 docs/evidence/phase0.5-startup-offline-2026-07-17.md create mode 100644 docs/evidence/phase07-offline-build-2026-07-17.md create mode 100644 docs/evidence/probe-9.60-offline-2026-07-17.md create mode 100644 docs/phase1/APPROVAL_PACKAGE.md create mode 100644 docs/phase1/FIRMWARE_ABI_CHECKLIST.md create mode 100644 docs/phase1/HARDWARE_TEST_PLAN.md create mode 100644 docs/phase1/README.md create mode 100644 docs/phase1/VIDEOOUT_CLEAR_EXPERIMENT.md create mode 100644 docs/retroarch/phase-1.0a-build-results.md create mode 100644 docs/retroarch/phase-1.0a-driver-status.md create mode 100644 docs/retroarch/phase-1.0a-next-device-smoke-test.md create mode 100644 docs/retroarch/phase-1.0a-pacbrew-sdl-analysis.md create mode 100644 docs/retroarch/phase-1.0a-port-plan.md create mode 100644 docs/retroarch/phase-1.0a-ps4-reference-delta.md create mode 100644 docs/retroarch/phase-1.0a-upstream-analysis.md create mode 100644 docs/retroarch/phase-1.0aa-offline-fake-adapter-integration.md create mode 100644 docs/retroarch/phase-1.0ab-offline-live-adapter-feasibility.md create mode 100644 docs/retroarch/phase-1.0ab-timeout-and-cleanup-contract.md create mode 100644 docs/retroarch/phase-1.0ac-fake-syscall-contract.md create mode 100644 docs/retroarch/phase-1.0ac-offline-dormant-adapter.md create mode 100644 docs/retroarch/phase-1.0ad-inactive-activation-contract.md create mode 100644 docs/retroarch/phase-1.0ae-minimal-bigapp-launcher-architecture.md create mode 100644 docs/retroarch/phase-1.0af-offline-bigapp-lifecycle-model.md create mode 100644 docs/retroarch/phase-1.0ag-bounded-elf-contract.md create mode 100644 docs/retroarch/phase-1.0ah-dynamic-relocation-contract.md create mode 100644 docs/retroarch/phase-1.0ai-offline-mapping-transaction.md create mode 100644 docs/retroarch/phase-1.0aj-offline-primitive-audit.md create mode 100644 docs/retroarch/phase-1.0ak-offline-hybrid-composition.md create mode 100644 docs/retroarch/phase-1.0al-offline-mdbg-copy-audit.md create mode 100644 docs/retroarch/phase-1.0am-offline-bounded-copy-model.md create mode 100644 docs/retroarch/phase-1.0an-offline-service-lifecycle-audit.md create mode 100644 docs/retroarch/phase-1.0ao-offline-worker-supervisor-model.md create mode 100644 docs/retroarch/phase-1.0ap-offline-worker-feasibility-audit.md create mode 100644 docs/retroarch/phase-1.0aq-offline-worker-result-record.md create mode 100644 docs/retroarch/phase-1.0ar-offline-result-channel-model.md create mode 100644 docs/retroarch/phase-1.0as-offline-channel-primitive-audit.md create mode 100644 docs/retroarch/phase-1.0at-offline-fd-deadline-model.md create mode 100644 docs/retroarch/phase-1.0au-live-channel-feasibility.md create mode 100644 docs/retroarch/phase-1.0av-launch-context-canary-contract.md create mode 100644 docs/retroarch/phase-1.0aw-canary-source-delta-audit.md create mode 100644 docs/retroarch/phase-1.0ax-canary-protocol-model.md create mode 100644 docs/retroarch/phase-1.0ay-target-source-base.md create mode 100644 docs/retroarch/phase-1.0az-host-av-source.md create mode 100644 docs/retroarch/phase-1.0b-device-risk-assessment.md create mode 100644 docs/retroarch/phase-1.0b-linker-and-wx-analysis.md create mode 100644 docs/retroarch/phase-1.0b-persistent-write-audit.md create mode 100644 docs/retroarch/phase-1.0b-proposed-one-shot-test.md create mode 100644 docs/retroarch/phase-1.0b-runtime-and-exit-contract.md create mode 100644 docs/retroarch/phase-1.0b-smoke-candidate-design.md create mode 100644 docs/retroarch/phase-1.0ba-target-profile-callsite-audit.md create mode 100644 docs/retroarch/phase-1.0bb-source-only-launch-canary-profile.md create mode 100644 docs/retroarch/phase-1.0bc-cross-build-prerequisite-audit.md create mode 100644 docs/retroarch/phase-1.0bd-dormant-sdl-materializer-policy.md create mode 100644 docs/retroarch/phase-1.0be-fake-only-sdl-materializer.md create mode 100644 docs/retroarch/phase-1.0bf-live-sdl-adapter-boundary-audit.md create mode 100644 docs/retroarch/phase-1.0bg-dormant-sdl-request-compiler.md create mode 100644 docs/retroarch/phase-1.0bh-bounded-sdl-executor.md create mode 100644 docs/retroarch/phase-1.0bi-real-facade-and-tool-install-audit.md create mode 100644 docs/retroarch/phase-1.0bj-exact-host-tool-install-result.md create mode 100644 docs/retroarch/phase-1.0bk-bounded-real-facade-host-fixture-gate.md create mode 100644 docs/retroarch/phase-1.0bl-fixture-import-failure-and-new-gate.md create mode 100644 docs/retroarch/phase-1.0bm-real-facade-fixture-result.md create mode 100644 docs/retroarch/phase-1.0bn-materializer-preflight-patch-chain-audit.md create mode 100644 docs/retroarch/phase-1.0bo-patch-chain-remediation-result.md create mode 100644 docs/retroarch/phase-1.0bp-one-shot-sdl-materialization-gate.md create mode 100644 docs/retroarch/phase-1.0bq-inline-invocation-failure.md create mode 100644 docs/retroarch/phase-1.0br-script-entrypoint-one-shot-gate.md create mode 100644 docs/retroarch/phase-1.0bs-read-only-status-timeout.md create mode 100644 docs/retroarch/phase-1.0bt-corrected-timeout-one-shot-gate.md create mode 100644 docs/retroarch/phase-1.0bu-wsl-git-status-timeout-and-windows-git-fixture-gate.md create mode 100644 docs/retroarch/phase-1.0bv-windows-git-fixture-result-and-request-correction-gate.md create mode 100644 docs/retroarch/phase-1.0bw-corrected-sdl-materializer-one-shot-gate.md create mode 100644 docs/retroarch/phase-1.0bx-changed-files-timeout-and-read-only-fixture-gate.md create mode 100644 docs/retroarch/phase-1.0by-windows-git-stage-fixture-failure-and-file-hash-gate.md create mode 100644 docs/retroarch/phase-1.0bz-exact-sdl-stage-cleanup-gate.md create mode 100644 docs/retroarch/phase-1.0c-device-smoke-result.md create mode 100644 docs/retroarch/phase-1.0ca-cleanup-result-and-hash-verifier-correction-gate.md create mode 100644 docs/retroarch/phase-1.0cb-hash-verified-sdl-materializer-one-shot-gate.md create mode 100644 docs/retroarch/phase-1.0cc-cmake-timeout-and-exact-output-cleanup-gate.md create mode 100644 docs/retroarch/phase-1.0cd-cleanup-result-and-configure-timeout-correction-gate.md create mode 100644 docs/retroarch/phase-1.0ce-extended-configure-sdl-materializer-one-shot-gate.md create mode 100644 docs/retroarch/phase-1.0cf-repeated-configure-timeout-and-cleanup-gate.md create mode 100644 docs/retroarch/phase-1.0cg-native-wsl-sdl-materializer-design-gate.md create mode 100644 docs/retroarch/phase-1.0ch-native-wsl-sdl-materializer-one-shot-gate.md create mode 100644 docs/retroarch/phase-1.0ci-native-sdk-bottleneck-and-cleanup-gate.md create mode 100644 docs/retroarch/phase-1.0cj-native-sdk-stage-design-gate.md create mode 100644 docs/retroarch/phase-1.0ck-native-sdk-sdl-materializer-one-shot-gate.md create mode 100644 docs/retroarch/phase-1.0cl-native-sdk-configure-progress-and-cleanup-gate.md create mode 100644 docs/retroarch/phase-1.0cm-measured-configure-timeout-correction-gate.md create mode 100644 docs/retroarch/phase-1.0cn-measured-native-sdl-materializer-one-shot-gate.md create mode 100644 docs/retroarch/phase-1.0co-configure-success-build-output-limit-cleanup-gate.md create mode 100644 docs/retroarch/phase-1.0cp-build-output-limit-correction-gate.md create mode 100644 docs/retroarch/phase-1.0cq-full-output-native-sdl-one-shot-gate.md create mode 100644 docs/retroarch/phase-1.0cr-audited-sdl-archive-export-gate.md create mode 100644 docs/retroarch/phase-1.0cs-export-parent-correction-gate.md create mode 100644 docs/retroarch/phase-1.0ct-sdl-archive-export-result.md create mode 100644 docs/retroarch/phase-1.0cu-exact-sdl-header-export-gate.md create mode 100644 docs/retroarch/phase-1.0cv-launch-canary-offline-build-gate.md create mode 100644 docs/retroarch/phase-1.0cw-launch-canary-artifact-audit.md create mode 100644 docs/retroarch/phase-1.0cx-isolated-reproducibility-build-gate.md create mode 100644 docs/retroarch/phase-1.0cy-launch-canary-reproducibility-result.md create mode 100644 docs/retroarch/phase-1.0cz-inactive-launch-canary-runner.md create mode 100644 docs/retroarch/phase-1.0d-crt-entry-canary.md create mode 100644 docs/retroarch/phase-1.0d-early-diagnostic-design.md create mode 100644 docs/retroarch/phase-1.0d-loader-static-model.md create mode 100644 docs/retroarch/phase-1.0d-loader-to-entry-analysis.md create mode 100644 docs/retroarch/phase-1.0d-next-device-test-ladder.md create mode 100644 docs/retroarch/phase-1.0d-startup-import-closure.md create mode 100644 docs/retroarch/phase-1.0db-post-cz-differential-analysis.md create mode 100644 docs/retroarch/phase-1.0dc-inactive-bigapp-comparison-gate.md create mode 100644 docs/retroarch/phase-1.0dd-minimal-launcher-source-prerequisite-audit.md create mode 100644 docs/retroarch/phase-1.0de-independent-abi-provenance-search.md create mode 100644 docs/retroarch/phase-1.0df-inactive-title-presence-observer.md create mode 100644 docs/retroarch/phase-1.0dg-title-presence-primitive-audit.md create mode 100644 docs/retroarch/phase-1.0dh-hash-bound-snapshot-query.md create mode 100644 docs/retroarch/phase-1.0di-live-acquisition-boundary.md create mode 100644 docs/retroarch/phase-1.0dj-di-t2-result.md create mode 100644 docs/retroarch/phase-1.0dk-port-status-result.md create mode 100644 docs/retroarch/phase-1.0dl-official-shsrv-artifact-audit.md create mode 100644 docs/retroarch/phase-1.0dm-bounded-snapshot-observer.md create mode 100644 docs/retroarch/phase-1.0dn-inactive-snapshot-receiver.md create mode 100644 docs/retroarch/phase-1.0do-inactive-snapshot-runner.md create mode 100644 docs/retroarch/phase-1.0do-snapshot-result.md create mode 100644 docs/retroarch/phase-1.0dp-listener-status-result.md create mode 100644 docs/retroarch/phase-1.0dq-bounded-fake00000-inventory.md create mode 100644 docs/retroarch/phase-1.0dr-inventory-result.md create mode 100644 docs/retroarch/phase-1.0ds-bounded-fake00000-metadata.md create mode 100644 docs/retroarch/phase-1.0dt-metadata-result.md create mode 100644 docs/retroarch/phase-1.0du-exact-package-stat.md create mode 100644 docs/retroarch/phase-1.0dv-package-stat-result.md create mode 100644 docs/retroarch/phase-1.0dw-exact-package-readback.md create mode 100644 docs/retroarch/phase-1.0dx-official-package-correlation.md create mode 100644 docs/retroarch/phase-1.0dx-package-readback-result.md create mode 100644 docs/retroarch/phase-1.0dy-upstream-websrv-audit.md create mode 100644 docs/retroarch/phase-1.0dy-websrv-listener-result.md create mode 100644 docs/retroarch/phase-1.0dz-direct-launcher-decision.md create mode 100644 docs/retroarch/phase-1.0e-device-observations.md create mode 100644 docs/retroarch/phase-1.0e-inherited-result-channel.md create mode 100644 docs/retroarch/phase-1.0e-next-device-test.md create mode 100644 docs/retroarch/phase-1.0ea-expanded-launch-evidence.md create mode 100644 docs/retroarch/phase-1.0eb-public-launch-matrix.md create mode 100644 docs/retroarch/phase-1.0f-startup-interval.md create mode 100644 docs/retroarch/phase-1.0g-device-result.md create mode 100644 docs/retroarch/phase-1.0g-one-shot-runner.md create mode 100644 docs/retroarch/phase-1.0h-device-result.md create mode 100644 docs/retroarch/phase-1.0h-startup-args.md create mode 100644 docs/retroarch/phase-1.0i-flip-and-write-analysis.md create mode 100644 docs/retroarch/phase-1.0j-write-firewall-diagnostic.md create mode 100644 docs/retroarch/phase-1.0k-write-diag-one-shot-runner.md create mode 100644 docs/retroarch/phase-1.0l-write-firewall-result-analysis.md create mode 100644 docs/retroarch/phase-1.0m-write-free-defaults.md create mode 100644 docs/retroarch/phase-1.0n-inactive-one-shot-runner.md create mode 100644 docs/retroarch/phase-1.0o-write-free-device-result.md create mode 100644 docs/retroarch/phase-1.0p-terminal-ordering-analysis.md create mode 100644 docs/retroarch/phase-1.0p-videoout-submit-analysis.md create mode 100644 docs/retroarch/phase-1.0q-public-videoout-evidence.md create mode 100644 docs/retroarch/phase-1.0r-launch-context-comparison.md create mode 100644 docs/retroarch/phase-1.0r-sdl2main-and-packaging-analysis.md create mode 100644 docs/retroarch/phase-1.0s-bigapp-launch-contract.md create mode 100644 docs/retroarch/phase-1.0s-hbldr-shsrv-provenance.md create mode 100644 docs/retroarch/phase-1.0t-inactive-shsrv-identity-gate.md create mode 100644 docs/retroarch/phase-1.0u-local-shsrv-artifact-inventory.md create mode 100644 docs/retroarch/phase-1.0v-inactive-one-shot-shsrv-collector.md create mode 100644 docs/retroarch/phase-1.0w-self-review-and-inactive-client-architecture.md create mode 100644 docs/retroarch/phase-1.0x-inactive-injected-transport.md create mode 100644 docs/retroarch/phase-1.0y-offline-shsrv-framing-audit.md create mode 100644 docs/retroarch/phase-1.0z-offline-passive-batch-contract.md create mode 100644 docs/reviews/phase0-final-2026-07-17.md create mode 100644 docs/runtime/controlled-runtime-policy.md create mode 100644 docs/runtime/kernelwrite-proof-matrix.md create mode 100644 docs/runtime/loader-contract.md create mode 100644 docs/runtime/minimal-crt-feasibility.md create mode 100644 docs/runtime/payload-manager-policy.md create mode 100644 docs/runtime/phase-0.5-review.md create mode 100644 docs/runtime/phase-0.6-loader-audit.md create mode 100644 docs/runtime/phase-0.6-review.md create mode 100644 docs/runtime/phase-0.7-hardening.md create mode 100644 docs/runtime/phase-0.8-read-only-preflight.md create mode 100644 docs/runtime/phase-0.8-remediation.md create mode 100644 docs/runtime/phase-0.9-anti-brick-threat-model.md create mode 100644 docs/runtime/phase-0.9-installation-transaction-design.md create mode 100644 docs/runtime/phase-0.9-recovery-and-rollback-contract.md create mode 100644 docs/runtime/phase-0.9b-bounded-observer-design.md create mode 100644 docs/runtime/phase-0.9b-observer-limitations.md create mode 100644 docs/runtime/phase-0.9b-observer-result-contract.md create mode 100644 docs/runtime/phase-0.9b-observer-static-audit.md create mode 100644 docs/runtime/phase-0.9c-capability-closure.md create mode 100644 docs/runtime/phase-0.9c-output-channel-feasibility.md create mode 100644 docs/runtime/phase-0.9c-side-effect-model.md create mode 100644 docs/runtime/phase-0.9c-startup-exit-feasibility.md create mode 100644 docs/runtime/phase-0.9c-static-audit.md create mode 100644 docs/runtime/phase-0.9d-bootstrap-recovery-chain.md create mode 100644 docs/runtime/phase-0.9d-existing-stack-endpoint-matrix.md create mode 100644 docs/runtime/phase-0.9d-independent-recovery-analysis.md create mode 100644 docs/runtime/phase-0.9d-off-device-backup-contract.md create mode 100644 docs/runtime/phase-0.9d-operational-windows.md create mode 100644 docs/runtime/phase-0.9d-readback-feasibility.md create mode 100644 docs/runtime/phase-0.9e-bootstrap-provenance.md create mode 100644 docs/runtime/phase-0.9e-future-rescue-payload-contract.md create mode 100644 docs/runtime/phase-0.9e-independent-rescue-chain.md create mode 100644 docs/runtime/phase-0.9e-loader-9020-protocol.md create mode 100644 docs/runtime/phase-0.9e-output-architecture-options.md create mode 100644 docs/runtime/phase-0.9e-r-official-hostsender-audit.md create mode 100644 docs/runtime/phase-0.9e-r-official-release-correlation.md create mode 100644 docs/runtime/phase-0.9e-r-port9020-source-audit.md create mode 100644 docs/runtime/phase-0.9e-r-provenance-gaps.md create mode 100644 docs/runtime/phase-0.9e-r-release-source-binding.md create mode 100644 docs/runtime/phase-0.9e-r2-community-backup-correlation.md create mode 100644 docs/runtime/phase-0.9e-r2-final-provenance-decision.md create mode 100644 docs/runtime/phase-0.9e-r2-inner-archive-correlation.md create mode 100644 docs/runtime/phase-0.9e-r2-local-download-provenance.md create mode 100644 docs/runtime/phase-0.9e-r2-siecaf-structural-analysis.md create mode 100644 docs/runtime/phase-0.9e-reboot-and-crash-model.md create mode 100644 docs/runtime/startup-callgraph.md create mode 100644 include/chimera/gfx/adapters/retroarch.h create mode 100644 include/chimera/gfx/adapters/sdl2.h create mode 100644 include/chimera/gfx/chimera_gfx.h create mode 100644 manifests/artifact-denylist.json create mode 100644 manifests/artifact-denylist.schema.json create mode 100644 manifests/artifact-manifest.schema.json create mode 100644 manifests/artifacts/chimera-elfldr-phase07-fw-9.60.json create mode 100644 manifests/artifacts/chimera-gfx-capability-probe-0.1.0-fw-9.60.json create mode 100644 manifests/artifacts/chimera-gfx-capability-probe-0.1.0-none.json create mode 100644 manifests/artifacts/chimera-gfx-lifecycle-probe-phase07-fw-9.60.json create mode 100644 manifests/artifacts/chimera-gfx-phase1-videoout-clear-0.1.0-none.json create mode 100644 manifests/artifacts/chimera-payload-manager-phase07-fw-9.60.json create mode 100644 manifests/controlled-runtime-profile.schema.json create mode 100644 manifests/ps5_gnm_symbols.json create mode 100644 manifests/retroarch/phase-1.0a-artifacts.json create mode 100644 manifests/retroarch/phase-1.0a-build.json create mode 100644 manifests/retroarch/phase-1.0aa-offline-fake-adapter.json create mode 100644 manifests/retroarch/phase-1.0ab-live-adapter-feasibility.json create mode 100644 manifests/retroarch/phase-1.0ac-dormant-adapter.json create mode 100644 manifests/retroarch/phase-1.0ad-inactive-activation.json create mode 100644 manifests/retroarch/phase-1.0ae-launcher-architecture.json create mode 100644 manifests/retroarch/phase-1.0af-bigapp-lifecycle-model.json create mode 100644 manifests/retroarch/phase-1.0ag-bounded-elf-contract.json create mode 100644 manifests/retroarch/phase-1.0ah-dynamic-contract.json create mode 100644 manifests/retroarch/phase-1.0ai-mapping-model.json create mode 100644 manifests/retroarch/phase-1.0aj-primitive-audit.json create mode 100644 manifests/retroarch/phase-1.0ak-hybrid-composition.json create mode 100644 manifests/retroarch/phase-1.0al-mdbg-copy-audit.json create mode 100644 manifests/retroarch/phase-1.0am-bounded-copy-model.json create mode 100644 manifests/retroarch/phase-1.0an-service-lifecycle-audit.json create mode 100644 manifests/retroarch/phase-1.0ao-worker-supervisor-model.json create mode 100644 manifests/retroarch/phase-1.0ap-worker-feasibility-audit.json create mode 100644 manifests/retroarch/phase-1.0aq-worker-result-record.json create mode 100644 manifests/retroarch/phase-1.0ar-result-channel-model.json create mode 100644 manifests/retroarch/phase-1.0as-channel-primitive-audit.json create mode 100644 manifests/retroarch/phase-1.0at-fd-deadline-model.json create mode 100644 manifests/retroarch/phase-1.0au-live-channel-feasibility.json create mode 100644 manifests/retroarch/phase-1.0av-launch-context-canary-contract.json create mode 100644 manifests/retroarch/phase-1.0aw-canary-source-delta-audit.json create mode 100644 manifests/retroarch/phase-1.0ax-canary-protocol-model.json create mode 100644 manifests/retroarch/phase-1.0ay-target-source-base.json create mode 100644 manifests/retroarch/phase-1.0az-host-av-source.json create mode 100644 manifests/retroarch/phase-1.0b-artifact.json create mode 100644 manifests/retroarch/phase-1.0b-build.json create mode 100644 manifests/retroarch/phase-1.0b-runtime-contract.json create mode 100644 manifests/retroarch/phase-1.0ba-target-profile-callsite-audit.json create mode 100644 manifests/retroarch/phase-1.0bb-source-only-launch-canary-profile.json create mode 100644 manifests/retroarch/phase-1.0bc-cross-build-prerequisite-audit.json create mode 100644 manifests/retroarch/phase-1.0bd-dormant-sdl-materializer-policy.json create mode 100644 manifests/retroarch/phase-1.0be-fake-only-sdl-materializer.json create mode 100644 manifests/retroarch/phase-1.0bf-live-sdl-adapter-boundary-audit.json create mode 100644 manifests/retroarch/phase-1.0bg-dormant-sdl-request-compiler.json create mode 100644 manifests/retroarch/phase-1.0bh-bounded-sdl-executor.json create mode 100644 manifests/retroarch/phase-1.0bi-real-facade-and-tool-install-audit.json create mode 100644 manifests/retroarch/phase-1.0bj-exact-host-tool-install-result.json create mode 100644 manifests/retroarch/phase-1.0bk-bounded-real-facade-host-fixture-gate.json create mode 100644 manifests/retroarch/phase-1.0bl-fixture-import-failure-and-new-gate.json create mode 100644 manifests/retroarch/phase-1.0bm-real-facade-fixture-result.json create mode 100644 manifests/retroarch/phase-1.0bn-materializer-preflight-patch-chain-audit.json create mode 100644 manifests/retroarch/phase-1.0bo-patch-chain-remediation-result.json create mode 100644 manifests/retroarch/phase-1.0bp-one-shot-sdl-materialization-gate.json create mode 100644 manifests/retroarch/phase-1.0bq-inline-invocation-failure.json create mode 100644 manifests/retroarch/phase-1.0br-script-entrypoint-one-shot-gate.json create mode 100644 manifests/retroarch/phase-1.0bs-read-only-status-timeout.json create mode 100644 manifests/retroarch/phase-1.0bt-corrected-timeout-one-shot-gate.json create mode 100644 manifests/retroarch/phase-1.0bu-wsl-git-status-timeout-and-windows-git-fixture-gate.json create mode 100644 manifests/retroarch/phase-1.0bv-windows-git-fixture-result-and-request-correction-gate.json create mode 100644 manifests/retroarch/phase-1.0bw-corrected-sdl-materializer-one-shot-gate.json create mode 100644 manifests/retroarch/phase-1.0bx-changed-files-timeout-and-read-only-fixture-gate.json create mode 100644 manifests/retroarch/phase-1.0by-windows-git-stage-fixture-failure-and-file-hash-gate.json create mode 100644 manifests/retroarch/phase-1.0bz-exact-sdl-stage-cleanup-gate.json create mode 100644 manifests/retroarch/phase-1.0c-device-smoke-result.json create mode 100644 manifests/retroarch/phase-1.0ca-cleanup-result-and-hash-verifier-correction-gate.json create mode 100644 manifests/retroarch/phase-1.0cb-hash-verified-sdl-materializer-one-shot-gate.json create mode 100644 manifests/retroarch/phase-1.0cc-cmake-timeout-and-exact-output-cleanup-gate.json create mode 100644 manifests/retroarch/phase-1.0cd-cleanup-result-and-configure-timeout-correction-gate.json create mode 100644 manifests/retroarch/phase-1.0ce-extended-configure-sdl-materializer-one-shot-gate.json create mode 100644 manifests/retroarch/phase-1.0cf-repeated-configure-timeout-and-cleanup-gate.json create mode 100644 manifests/retroarch/phase-1.0cg-native-wsl-sdl-materializer-design-gate.json create mode 100644 manifests/retroarch/phase-1.0ch-native-wsl-sdl-materializer-one-shot-gate.json create mode 100644 manifests/retroarch/phase-1.0ci-native-sdk-bottleneck-and-cleanup-gate.json create mode 100644 manifests/retroarch/phase-1.0cj-native-sdk-stage-design-gate.json create mode 100644 manifests/retroarch/phase-1.0ck-native-sdk-sdl-materializer-one-shot-gate.json create mode 100644 manifests/retroarch/phase-1.0cl-native-sdk-configure-progress-and-cleanup-gate.json create mode 100644 manifests/retroarch/phase-1.0cm-measured-configure-timeout-correction-gate.json create mode 100644 manifests/retroarch/phase-1.0cn-measured-native-sdl-materializer-one-shot-gate.json create mode 100644 manifests/retroarch/phase-1.0co-configure-success-build-output-limit-cleanup-gate.json create mode 100644 manifests/retroarch/phase-1.0cp-build-output-limit-correction-gate.json create mode 100644 manifests/retroarch/phase-1.0cq-full-output-native-sdl-one-shot-gate.json create mode 100644 manifests/retroarch/phase-1.0cr-audited-sdl-archive-export-gate.json create mode 100644 manifests/retroarch/phase-1.0cs-export-parent-correction-gate.json create mode 100644 manifests/retroarch/phase-1.0ct-sdl-archive-export-result.json create mode 100644 manifests/retroarch/phase-1.0cu-exact-sdl-header-export-gate.json create mode 100644 manifests/retroarch/phase-1.0cv-launch-canary-offline-build-gate.json create mode 100644 manifests/retroarch/phase-1.0cw-launch-canary-artifact-audit.json create mode 100644 manifests/retroarch/phase-1.0cx-isolated-reproducibility-build-gate.json create mode 100644 manifests/retroarch/phase-1.0cy-launch-canary-reproducibility-result.json create mode 100644 manifests/retroarch/phase-1.0cz-launch-canary-one-shot-active.json create mode 100644 manifests/retroarch/phase-1.0cz-launch-canary-one-shot-runner.json create mode 100644 manifests/retroarch/phase-1.0cz-one-shot-approval-template.json create mode 100644 manifests/retroarch/phase-1.0d-canary-artifact.json create mode 100644 manifests/retroarch/phase-1.0d-early-diag-artifact.json create mode 100644 manifests/retroarch/phase-1.0d-loader-model-results.json create mode 100644 manifests/retroarch/phase-1.0da-raw-elfldr-baseline-result.json create mode 100644 manifests/retroarch/phase-1.0db-post-cz-differential-analysis.json create mode 100644 manifests/retroarch/phase-1.0dc-inactive-bigapp-comparison-gate.json create mode 100644 manifests/retroarch/phase-1.0dd-minimal-launcher-source-prerequisite-audit.json create mode 100644 manifests/retroarch/phase-1.0de-independent-abi-provenance-search.json create mode 100644 manifests/retroarch/phase-1.0df-inactive-title-presence-observer.json create mode 100644 manifests/retroarch/phase-1.0dg-title-presence-primitive-audit.json create mode 100644 manifests/retroarch/phase-1.0dh-hash-bound-snapshot-query.json create mode 100644 manifests/retroarch/phase-1.0di-live-acquisition-boundary.json create mode 100644 manifests/retroarch/phase-1.0dj-di-t2-result.json create mode 100644 manifests/retroarch/phase-1.0dk-port-status-result.json create mode 100644 manifests/retroarch/phase-1.0dl-official-shsrv-artifact-audit.json create mode 100644 manifests/retroarch/phase-1.0dm-bounded-snapshot-observer.json create mode 100644 manifests/retroarch/phase-1.0dn-inactive-snapshot-receiver.json create mode 100644 manifests/retroarch/phase-1.0do-inactive-snapshot-runner.json create mode 100644 manifests/retroarch/phase-1.0do-snapshot-result.json create mode 100644 manifests/retroarch/phase-1.0dp-listener-status-result.json create mode 100644 manifests/retroarch/phase-1.0dq-inventory-observer.json create mode 100644 manifests/retroarch/phase-1.0dr-inactive-inventory-runner.json create mode 100644 manifests/retroarch/phase-1.0dr-inventory-result.json create mode 100644 manifests/retroarch/phase-1.0ds-metadata-observer.json create mode 100644 manifests/retroarch/phase-1.0dt-inactive-metadata-runner.json create mode 100644 manifests/retroarch/phase-1.0dt-metadata-result.json create mode 100644 manifests/retroarch/phase-1.0du-package-stat.json create mode 100644 manifests/retroarch/phase-1.0dv-inactive-package-stat-runner.json create mode 100644 manifests/retroarch/phase-1.0dv-package-stat-result.json create mode 100644 manifests/retroarch/phase-1.0dw-package-readback.json create mode 100644 manifests/retroarch/phase-1.0dx-inactive-package-readback-runner.json create mode 100644 manifests/retroarch/phase-1.0dx-package-readback-result.json create mode 100644 manifests/retroarch/phase-1.0dy-upstream-websrv-audit.json create mode 100644 manifests/retroarch/phase-1.0dy-websrv-listener-result.json create mode 100644 manifests/retroarch/phase-1.0dz-direct-launcher-decision.json create mode 100644 manifests/retroarch/phase-1.0e-result-channel.json create mode 100644 manifests/retroarch/phase-1.0ea-expanded-launch-evidence.json create mode 100644 manifests/retroarch/phase-1.0eb-public-launch-matrix.json create mode 100644 manifests/retroarch/phase-1.0f-startup-interval.json create mode 100644 manifests/retroarch/phase-1.0g-device-result.json create mode 100644 manifests/retroarch/phase-1.0g-one-shot-approval-template.json create mode 100644 manifests/retroarch/phase-1.0g-one-shot-runner.json create mode 100644 manifests/retroarch/phase-1.0h-device-result.json create mode 100644 manifests/retroarch/phase-1.0h-startup-args.json create mode 100644 manifests/retroarch/phase-1.0i-flip-and-write-analysis.json create mode 100644 manifests/retroarch/phase-1.0j-write-firewall-diagnostic.json create mode 100644 manifests/retroarch/phase-1.0k-one-shot-approval-template.json create mode 100644 manifests/retroarch/phase-1.0k-write-diag-one-shot-runner.json create mode 100644 manifests/retroarch/phase-1.0l-write-firewall-result-analysis.json create mode 100644 manifests/retroarch/phase-1.0m-write-free-defaults.json create mode 100644 manifests/retroarch/phase-1.0n-one-shot-approval-template.json create mode 100644 manifests/retroarch/phase-1.0n-write-free-one-shot-runner.json create mode 100644 manifests/retroarch/phase-1.0o-write-free-device-result.json create mode 100644 manifests/retroarch/phase-1.0p-videoout-submit-analysis.json create mode 100644 manifests/retroarch/phase-1.0q-public-videoout-evidence.json create mode 100644 manifests/retroarch/phase-1.0r-launch-context-analysis.json create mode 100644 manifests/retroarch/phase-1.0s-launcher-provenance.json create mode 100644 manifests/retroarch/phase-1.0t-shsrv-identity-gate.json create mode 100644 manifests/retroarch/phase-1.0u-local-shsrv-inventory.json create mode 100644 manifests/retroarch/phase-1.0v-inactive-shsrv-collector.json create mode 100644 manifests/retroarch/phase-1.0w-inactive-client-architecture.json create mode 100644 manifests/retroarch/phase-1.0x-inactive-transport.json create mode 100644 manifests/retroarch/phase-1.0y-shsrv-framing.json create mode 100644 manifests/retroarch/phase-1.0z-passive-batch.json create mode 100644 manifests/retroarch/upstreams.json create mode 100644 manifests/runtime/controlled-ps5-runtime-profile.json create mode 100644 manifests/runtime/kernelwrite-proof-matrix.json create mode 100644 manifests/runtime/minimal-startup-artifact-decision.json create mode 100644 manifests/runtime/phase-0.5-startup-audit.json create mode 100644 manifests/runtime/phase-0.6-loader-runtime-audit.json create mode 100644 manifests/runtime/phase-0.7-kernelwrite-proof-matrix.json create mode 100644 manifests/runtime/phase-0.7-offline-audit.json create mode 100644 manifests/runtime/phase-0.8-read-only-preflight.json create mode 100644 manifests/runtime/phase-0.8-remediation.json create mode 100644 manifests/runtime/phase-0.9-anti-brick-design.json create mode 100644 manifests/runtime/phase-0.9b-observation-plan.schema.json create mode 100644 manifests/runtime/phase-0.9b-observer.json create mode 100644 manifests/runtime/phase-0.9c-feasibility.json create mode 100644 manifests/runtime/phase-0.9c-feasibility.schema.json create mode 100644 manifests/runtime/phase-0.9d-existing-stack-readback.json create mode 100644 manifests/runtime/phase-0.9d-existing-stack-readback.schema.json create mode 100644 manifests/runtime/phase-0.9e-bootstrap-provenance.json create mode 100644 manifests/runtime/phase-0.9e-bootstrap-provenance.schema.json create mode 100644 manifests/runtime/phase-0.9e-loader-protocol.json create mode 100644 manifests/runtime/phase-0.9e-loader-protocol.schema.json create mode 100644 manifests/runtime/phase-0.9e-r-port9020-audit.json create mode 100644 manifests/runtime/phase-0.9e-r-port9020-audit.schema.json create mode 100644 manifests/runtime/phase-0.9e-r-release-correlation.json create mode 100644 manifests/runtime/phase-0.9e-r-release-correlation.schema.json create mode 100644 manifests/runtime/phase-0.9e-r2-inner-correlation.json create mode 100644 manifests/runtime/phase-0.9e-r2-siecaf-fingerprints.json create mode 100644 manifests/sbom.spdx.json create mode 100644 manifests/upstreams.lock.json create mode 100644 packaging/Dockerfile create mode 100644 packaging/README.md create mode 100644 packaging/patches/sdl2-phase1-video-only.patch create mode 100644 packaging/phase07/README-installation-review.md create mode 100644 packaging/phase07/README-rollback.md create mode 100644 packaging/phase09b/SHA256SUMS.txt create mode 100644 packaging/phase09c/SHA256SUMS.txt create mode 100644 packaging/phase09e/SHA256SUMS.txt create mode 100644 packaging/phase09er/SHA256SUMS.txt create mode 100644 packaging/phase09er2/SHA256SUMS.txt create mode 100644 packaging/retroarch/phase10a/SHA256SUMS.txt create mode 100644 packaging/retroarch/phase10b/SHA256SUMS.txt create mode 100644 packaging/retroarch/phase10c/SHA256SUMS.txt create mode 100644 packaging/retroarch/phase10d/SHA256SUMS.txt create mode 100644 packaging/retroarch/phase10e/SHA256SUMS.txt create mode 100644 packaging/retroarch/phase10f/SHA256SUMS.txt create mode 100644 packaging/retroarch/phase10g/SHA256SUMS.txt create mode 100644 packaging/retroarch/phase10h/SHA256SUMS.txt create mode 100644 packaging/retroarch/phase10j/SHA256SUMS.txt create mode 100644 packaging/retroarch/phase10k/SHA256SUMS.txt create mode 100644 packaging/retroarch/phase10m/SHA256SUMS.txt create mode 100644 packaging/retroarch/phase10n/SHA256SUMS.txt create mode 100644 samples/capability_probe/main.c create mode 100644 samples/clear_screen/README.md create mode 100644 samples/lifecycle_probe/main.c create mode 100644 samples/phase1_videoout_clear/main.c create mode 100644 src/backends/mock/mock_backend.c create mode 100644 src/backends/ps5/firmware_gate.c create mode 100644 src/backends/ps5/firmware_gate.h create mode 100644 src/backends/ps5/generated_probe_symbols.inc create mode 100644 src/backends/ps5/probe.h create mode 100644 src/backends/ps5/probe_logic.c create mode 100644 src/backends/ps5/probe_platform.c create mode 100644 src/backends/ps5/probe_platform.h create mode 100644 src/backends/ps5/ps5_backend.c create mode 100644 tests/phase09b_observer_model.py create mode 100644 tests/phase09c_feasibility_model.py create mode 100644 tests/phase10w_fake_transport.py create mode 100644 tests/test_adapters.c create mode 100644 tests/test_artifact_audit.py create mode 100644 tests/test_core.c create mode 100644 tests/test_execution_policy.py create mode 100644 tests/test_firmware_gate.c create mode 100644 tests/test_manifest_tools.py create mode 100644 tests/test_minimal_startup_block.py create mode 100644 tests/test_mock_integration.c create mode 100644 tests/test_phase05_audit.py create mode 100644 tests/test_phase06_audit.py create mode 100644 tests/test_phase07_audit.py create mode 100644 tests/test_phase08_preflight.py create mode 100644 tests/test_phase08_remediation.py create mode 100644 tests/test_phase09_transaction.py create mode 100644 tests/test_phase09b_observer_audit.py create mode 100644 tests/test_phase09c_feasibility.py create mode 100644 tests/test_phase09c_protocol.py create mode 100644 tests/test_phase09d_readback.py create mode 100644 tests/test_phase09e_bootstrap.py create mode 100644 tests/test_phase09er2_correlation.py create mode 100644 tests/test_phase09er_provenance.py create mode 100644 tests/test_phase10aa_offline_fake_batch.py create mode 100644 tests/test_phase10ab_nonblocking_trace_model.py create mode 100644 tests/test_phase10ac_dormant_adapter.py create mode 100644 tests/test_phase10ad_activation_contract.py create mode 100644 tests/test_phase10af_bigapp_lifecycle_model.py create mode 100644 tests/test_phase10ag_bounded_elf.py create mode 100644 tests/test_phase10ah_dynamic_contract.py create mode 100644 tests/test_phase10ai_mapping_model.py create mode 100644 tests/test_phase10ak_hybrid_composition.py create mode 100644 tests/test_phase10am_bounded_copy_model.py create mode 100644 tests/test_phase10ao_worker_supervisor_model.py create mode 100644 tests/test_phase10aq_worker_result_record.py create mode 100644 tests/test_phase10ar_result_channel_model.py create mode 100644 tests/test_phase10at_fd_deadline_model.py create mode 100644 tests/test_phase10av_launch_context_canary.py create mode 100644 tests/test_phase10ax_canary_protocol_model.py create mode 100644 tests/test_phase10dc_bigapp_gate_contract.py create mode 100644 tests/test_phase10df_title_observer_contract.py create mode 100644 tests/test_phase10dh_snapshot_query.py create mode 100644 tests/test_phase10dm_snapshot_protocol.py create mode 100644 tests/test_phase10dn_snapshot_receiver.py create mode 100644 tests/test_phase10do_one_shot_snapshot_runner.py create mode 100644 tests/test_phase10dq_inventory_protocol.py create mode 100644 tests/test_phase10dr_inventory_runner.py create mode 100644 tests/test_phase10ds_metadata_protocol.py create mode 100644 tests/test_phase10dt_metadata_runner.py create mode 100644 tests/test_phase10dv_package_stat_runner.py create mode 100644 tests/test_phase10dx_package_readback_runner.py create mode 100644 tests/test_phase10t_shsrv_transcript.py create mode 100644 tests/test_phase10v_shsrv_collector_model.py create mode 100644 tests/test_phase10w_shsrv_client_policy.py create mode 100644 tests/test_phase10x_inactive_transport.py create mode 100644 tests/test_phase10y_shsrv_framing_model.py create mode 100644 tests/test_phase10z_passive_batch_contract.py create mode 100644 tests/test_probe.c create mode 100644 tests/test_retroarch_phase10a.py create mode 100644 tests/test_retroarch_phase10aa.py create mode 100644 tests/test_retroarch_phase10ab.py create mode 100644 tests/test_retroarch_phase10ac.py create mode 100644 tests/test_retroarch_phase10ad.py create mode 100644 tests/test_retroarch_phase10ae.py create mode 100644 tests/test_retroarch_phase10af.py create mode 100644 tests/test_retroarch_phase10ag.py create mode 100644 tests/test_retroarch_phase10ah.py create mode 100644 tests/test_retroarch_phase10ai.py create mode 100644 tests/test_retroarch_phase10aj.py create mode 100644 tests/test_retroarch_phase10ak.py create mode 100644 tests/test_retroarch_phase10al.py create mode 100644 tests/test_retroarch_phase10am.py create mode 100644 tests/test_retroarch_phase10an.py create mode 100644 tests/test_retroarch_phase10ao.py create mode 100644 tests/test_retroarch_phase10ap.py create mode 100644 tests/test_retroarch_phase10aq.py create mode 100644 tests/test_retroarch_phase10ar.py create mode 100644 tests/test_retroarch_phase10as.py create mode 100644 tests/test_retroarch_phase10at.py create mode 100644 tests/test_retroarch_phase10au.py create mode 100644 tests/test_retroarch_phase10av.py create mode 100644 tests/test_retroarch_phase10aw.py create mode 100644 tests/test_retroarch_phase10ax.py create mode 100644 tests/test_retroarch_phase10ay.py create mode 100644 tests/test_retroarch_phase10az.py create mode 100644 tests/test_retroarch_phase10b.py create mode 100644 tests/test_retroarch_phase10ba.py create mode 100644 tests/test_retroarch_phase10bb.py create mode 100644 tests/test_retroarch_phase10bc.py create mode 100644 tests/test_retroarch_phase10bd.py create mode 100644 tests/test_retroarch_phase10be.py create mode 100644 tests/test_retroarch_phase10bf.py create mode 100644 tests/test_retroarch_phase10bg.py create mode 100644 tests/test_retroarch_phase10bh.py create mode 100644 tests/test_retroarch_phase10bi.py create mode 100644 tests/test_retroarch_phase10bj.py create mode 100644 tests/test_retroarch_phase10bk.py create mode 100644 tests/test_retroarch_phase10bl.py create mode 100644 tests/test_retroarch_phase10bm.py create mode 100644 tests/test_retroarch_phase10bn.py create mode 100644 tests/test_retroarch_phase10bo.py create mode 100644 tests/test_retroarch_phase10bp.py create mode 100644 tests/test_retroarch_phase10bq.py create mode 100644 tests/test_retroarch_phase10br.py create mode 100644 tests/test_retroarch_phase10bs.py create mode 100644 tests/test_retroarch_phase10bt.py create mode 100644 tests/test_retroarch_phase10bu.py create mode 100644 tests/test_retroarch_phase10bv.py create mode 100644 tests/test_retroarch_phase10bw.py create mode 100644 tests/test_retroarch_phase10bx.py create mode 100644 tests/test_retroarch_phase10by.py create mode 100644 tests/test_retroarch_phase10bz.py create mode 100644 tests/test_retroarch_phase10ca.py create mode 100644 tests/test_retroarch_phase10cb.py create mode 100644 tests/test_retroarch_phase10cc.py create mode 100644 tests/test_retroarch_phase10cd.py create mode 100644 tests/test_retroarch_phase10ce.py create mode 100644 tests/test_retroarch_phase10cf.py create mode 100644 tests/test_retroarch_phase10cg.py create mode 100644 tests/test_retroarch_phase10ch.py create mode 100644 tests/test_retroarch_phase10ci.py create mode 100644 tests/test_retroarch_phase10cj.py create mode 100644 tests/test_retroarch_phase10ck.py create mode 100644 tests/test_retroarch_phase10cl.py create mode 100644 tests/test_retroarch_phase10cm.py create mode 100644 tests/test_retroarch_phase10cn.py create mode 100644 tests/test_retroarch_phase10co.py create mode 100644 tests/test_retroarch_phase10cp.py create mode 100644 tests/test_retroarch_phase10cq.py create mode 100644 tests/test_retroarch_phase10cr.py create mode 100644 tests/test_retroarch_phase10cs.py create mode 100644 tests/test_retroarch_phase10ct.py create mode 100644 tests/test_retroarch_phase10cu.py create mode 100644 tests/test_retroarch_phase10cv.py create mode 100644 tests/test_retroarch_phase10cw.py create mode 100644 tests/test_retroarch_phase10cx.py create mode 100644 tests/test_retroarch_phase10cy.py create mode 100644 tests/test_retroarch_phase10cz.py create mode 100644 tests/test_retroarch_phase10d.py create mode 100644 tests/test_retroarch_phase10db.py create mode 100644 tests/test_retroarch_phase10dc.py create mode 100644 tests/test_retroarch_phase10dd.py create mode 100644 tests/test_retroarch_phase10de.py create mode 100644 tests/test_retroarch_phase10df.py create mode 100644 tests/test_retroarch_phase10dg.py create mode 100644 tests/test_retroarch_phase10di.py create mode 100644 tests/test_retroarch_phase10dj.py create mode 100644 tests/test_retroarch_phase10dk.py create mode 100644 tests/test_retroarch_phase10dl.py create mode 100644 tests/test_retroarch_phase10dm.py create mode 100644 tests/test_retroarch_phase10dn.py create mode 100644 tests/test_retroarch_phase10dz.py create mode 100644 tests/test_retroarch_phase10e.py create mode 100644 tests/test_retroarch_phase10ea.py create mode 100644 tests/test_retroarch_phase10eb.py create mode 100644 tests/test_retroarch_phase10f.py create mode 100644 tests/test_retroarch_phase10g.py create mode 100644 tests/test_retroarch_phase10g_result.py create mode 100644 tests/test_retroarch_phase10h.py create mode 100644 tests/test_retroarch_phase10h_result.py create mode 100644 tests/test_retroarch_phase10i.py create mode 100644 tests/test_retroarch_phase10j.py create mode 100644 tests/test_retroarch_phase10k.py create mode 100644 tests/test_retroarch_phase10l.py create mode 100644 tests/test_retroarch_phase10m.py create mode 100644 tests/test_retroarch_phase10n.py create mode 100644 tests/test_retroarch_phase10o.py create mode 100644 tests/test_retroarch_phase10p.py create mode 100644 tests/test_retroarch_phase10q.py create mode 100644 tests/test_retroarch_phase10r.py create mode 100644 tests/test_retroarch_phase10s.py create mode 100644 tests/test_retroarch_phase10t.py create mode 100644 tests/test_retroarch_phase10u.py create mode 100644 tests/test_retroarch_phase10v.py create mode 100644 tests/test_retroarch_phase10w.py create mode 100644 tests/test_retroarch_phase10x.py create mode 100644 tests/test_retroarch_phase10y.py create mode 100644 tests/test_retroarch_phase10z.py create mode 100644 tests/test_siecaf_header_parser.py create mode 100644 tools/audit_phase07_artifacts.py create mode 100644 tools/audit_phase0_safety.py create mode 100644 tools/audit_ps5_artifacts.py create mode 100644 tools/audit_ps5_loader_runtime.py create mode 100755 tools/audit_ps5_sdk_runtime.py create mode 100644 tools/audit_ps5_startup_feasibility.py create mode 100644 tools/bootstrap-ps5-sdk.ps1 create mode 100755 tools/build-firmware-probe.sh create mode 100644 tools/build-host.ps1 create mode 100644 tools/build-phase1-videoout.sh create mode 100644 tools/build_phase10dm_observer.py create mode 100644 tools/build_phase10dq_inventory.py create mode 100644 tools/build_phase10ds_metadata.py create mode 100644 tools/build_phase10du_package_stat.py create mode 100644 tools/build_phase10dw_package_readback.py create mode 100644 tools/check_artifact_execution_policy.py create mode 100644 tools/check_format.py create mode 100755 tools/export-public-source.sh create mode 100644 tools/generate_artifact_manifest.py create mode 100644 tools/generate_probe_symbols.py create mode 100644 tools/inspect_siecaf_header.py create mode 100644 tools/package_phase07_review.py create mode 100644 tools/phase10aa_offline_fake_batch.py create mode 100644 tools/phase10ab_nonblocking_trace_model.py create mode 100644 tools/phase10ac_dormant_adapter.py create mode 100644 tools/phase10ad_activation_contract.py create mode 100644 tools/phase10af_bigapp_lifecycle_model.py create mode 100644 tools/phase10ag_bounded_elf.py create mode 100644 tools/phase10ah_dynamic_contract.py create mode 100644 tools/phase10ai_mapping_model.py create mode 100644 tools/phase10ak_hybrid_composition.py create mode 100644 tools/phase10am_bounded_copy_model.py create mode 100644 tools/phase10ao_worker_supervisor_model.py create mode 100644 tools/phase10aq_worker_result_record.py create mode 100644 tools/phase10ar_result_channel_model.py create mode 100644 tools/phase10at_fd_deadline_model.py create mode 100644 tools/phase10av_launch_context_canary.py create mode 100644 tools/phase10ax_canary_protocol_model.py create mode 100644 tools/phase10dc_bigapp_gate_contract.py create mode 100644 tools/phase10df_title_observer_contract.py create mode 100644 tools/phase10dh_snapshot_query.py create mode 100644 tools/phase10dm_snapshot_protocol.py create mode 100644 tools/phase10dn_snapshot_receiver.py create mode 100644 tools/phase10do_one_shot_snapshot_runner.py create mode 100644 tools/phase10dq_inventory_protocol.py create mode 100644 tools/phase10dr_inventory_runner.py create mode 100644 tools/phase10ds_metadata_protocol.py create mode 100644 tools/phase10dt_metadata_runner.py create mode 100644 tools/phase10dv_package_stat_runner.py create mode 100644 tools/phase10dx_package_readback_runner.py create mode 100644 tools/phase10t_shsrv_transcript.py create mode 100644 tools/phase10v_shsrv_collector_model.py create mode 100644 tools/phase10w_shsrv_client_policy.py create mode 100644 tools/phase10x_inactive_transport.py create mode 100644 tools/phase10y_shsrv_framing_model.py create mode 100644 tools/phase10z_passive_batch_contract.py create mode 100644 tools/scan_secrets.py create mode 100644 tools/simulate_phase09_transaction.py create mode 100644 tools/validate_phase08_remediation.py create mode 100644 tools/validate_phase09b_observer_audit.py create mode 100644 tools/validate_phase09c_feasibility.py create mode 100644 tools/validate_phase09d_readback.py create mode 100644 tools/validate_phase09e_bootstrap.py create mode 100644 tools/validate_phase09er2_correlation.py create mode 100644 tools/validate_phase09er_provenance.py create mode 100644 tools/validate_retroarch_phase10a.py create mode 100644 tools/validate_retroarch_phase10aa.py create mode 100644 tools/validate_retroarch_phase10ab.py create mode 100644 tools/validate_retroarch_phase10ac.py create mode 100644 tools/validate_retroarch_phase10ad.py create mode 100644 tools/validate_retroarch_phase10ae.py create mode 100644 tools/validate_retroarch_phase10af.py create mode 100644 tools/validate_retroarch_phase10ag.py create mode 100644 tools/validate_retroarch_phase10ah.py create mode 100644 tools/validate_retroarch_phase10ai.py create mode 100644 tools/validate_retroarch_phase10aj.py create mode 100644 tools/validate_retroarch_phase10ak.py create mode 100644 tools/validate_retroarch_phase10al.py create mode 100644 tools/validate_retroarch_phase10am.py create mode 100644 tools/validate_retroarch_phase10an.py create mode 100644 tools/validate_retroarch_phase10ao.py create mode 100644 tools/validate_retroarch_phase10ap.py create mode 100644 tools/validate_retroarch_phase10aq.py create mode 100644 tools/validate_retroarch_phase10ar.py create mode 100644 tools/validate_retroarch_phase10as.py create mode 100644 tools/validate_retroarch_phase10at.py create mode 100644 tools/validate_retroarch_phase10au.py create mode 100644 tools/validate_retroarch_phase10av.py create mode 100644 tools/validate_retroarch_phase10aw.py create mode 100644 tools/validate_retroarch_phase10ax.py create mode 100644 tools/validate_retroarch_phase10ay.py create mode 100644 tools/validate_retroarch_phase10az.py create mode 100644 tools/validate_retroarch_phase10b.py create mode 100644 tools/validate_retroarch_phase10ba.py create mode 100644 tools/validate_retroarch_phase10bb.py create mode 100644 tools/validate_retroarch_phase10bd.py create mode 100644 tools/validate_retroarch_phase10d.py create mode 100644 tools/validate_retroarch_phase10dc.py create mode 100644 tools/validate_retroarch_phase10df.py create mode 100644 tools/validate_retroarch_phase10e.py create mode 100644 tools/validate_retroarch_phase10f.py create mode 100644 tools/validate_retroarch_phase10g.py create mode 100644 tools/validate_retroarch_phase10g_result.py create mode 100644 tools/validate_retroarch_phase10h.py create mode 100644 tools/validate_retroarch_phase10h_result.py create mode 100644 tools/validate_retroarch_phase10i.py create mode 100644 tools/validate_retroarch_phase10j.py create mode 100644 tools/validate_retroarch_phase10k.py create mode 100644 tools/validate_retroarch_phase10l.py create mode 100644 tools/validate_retroarch_phase10m.py create mode 100644 tools/validate_retroarch_phase10n.py create mode 100644 tools/validate_retroarch_phase10o.py create mode 100644 tools/validate_retroarch_phase10p.py create mode 100644 tools/validate_retroarch_phase10q.py create mode 100644 tools/validate_retroarch_phase10r.py create mode 100644 tools/validate_retroarch_phase10s.py create mode 100644 tools/validate_retroarch_phase10t.py create mode 100644 tools/validate_retroarch_phase10u.py create mode 100644 tools/validate_retroarch_phase10v.py create mode 100644 tools/validate_retroarch_phase10w.py create mode 100644 tools/validate_retroarch_phase10x.py create mode 100644 tools/validate_retroarch_phase10y.py create mode 100644 tools/validate_retroarch_phase10z.py create mode 100644 tools/verify_artifact_manifest.py create mode 100644 tools/verify_manifests.py diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..96de680 --- /dev/null +++ b/.clang-format @@ -0,0 +1,7 @@ +BasedOnStyle: LLVM +IndentWidth: 4 +ContinuationIndentWidth: 4 +ColumnLimit: 80 +DerivePointerAlignment: false +PointerAlignment: Right +SortIncludes: CaseSensitive diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 0000000..01c5c0a --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,13 @@ +Checks: >- + -*, + clang-analyzer-*, + -clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling, + bugprone-sizeof-expression, + bugprone-suspicious-memory-comparison, + bugprone-suspicious-missing-comma, + bugprone-suspicious-realloc-usage, + performance-*, + portability-* +WarningsAsErrors: '*' +HeaderFilterRegex: '(include/chimera|src|samples|tests)/.*' +FormatStyle: file diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..f737bb9 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,11 @@ +.git +.gitea +build +build-* +work +out +.vs +.vscode +.idea +*.log +*.elf diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..92055b4 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,23 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.{c,h}] +indent_style = space +indent_size = 4 + +[*.{cmake,json,md,yml,yaml}] +indent_style = space +indent_size = 2 + +[*.{py,ps1}] +indent_style = space +indent_size = 4 + +[CMakeLists.txt] +indent_style = space +indent_size = 2 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..e4bf206 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,8 @@ +* text=auto eol=lf + +*.png binary +*.jpg binary +*.jpeg binary +*.zip binary +*.elf binary +*.patch -whitespace diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml new file mode 100644 index 0000000..35d2574 --- /dev/null +++ b/.gitea/workflows/ci.yml @@ -0,0 +1,60 @@ +name: phase0-ci + +on: + push: + branches: [main] + pull_request: + workflow_dispatch: + +concurrency: + group: phase0-ci-${{ gitea.repository }}-${{ gitea.ref }} + cancel-in-progress: true + +jobs: + build-and-audit: + if: ${{ gitea.event_name != 'pull_request' || gitea.event.pull_request.head.repo.full_name == gitea.repository }} + runs-on: ubuntu-latest + container: + image: ubuntu:26.04@sha256:651ba3fe3a830441e3deaf70fafac40d808a6bd2800a6f2c43130055159f23e6 + steps: + - name: Install build tools + run: >- + apt-get update && apt-get install -y --no-install-recommends + ca-certificates + bsdextrautils + clang-18 + clang-format-18 + clang-tidy-18 + cmake + curl + git + lld-18 + llvm-18-dev + ninja-build + nodejs + python3 + unzip + + - name: Check out repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + with: + fetch-depth: 0 + + - name: Host build and policy tests + run: | + cmake -S . -B build/host -G Ninja -DCMAKE_C_COMPILER=clang-18 -DCHIMERA_GFX_ENABLE_CLANG_TIDY=ON -DCHIMERA_GFX_REGISTER_EXTERNAL_EVIDENCE_VALIDATORS=OFF -DBUILD_TESTING=ON + cmake --build build/host + ctest --test-dir build/host --output-on-failure + + - name: Fetch and verify locked public PS5 SDK + run: | + curl -fL --retry 3 --output /tmp/ps5-payload-sdk.zip https://github.com/ps5-payload-dev/sdk/releases/download/v0.41/ps5-payload-sdk.zip + echo "ebfb0acb5260511951a80e17db41650c62d20a8caf8659a230b928dc85005984 /tmp/ps5-payload-sdk.zip" | sha256sum --check --strict + unzip -q /tmp/ps5-payload-sdk.zip -d /opt + + - name: Compile all safe Phase-0 PS5 targets + env: + LLVM_CONFIG: /usr/bin/llvm-config-18 + run: | + cmake -S . -B build/ps5 -G Ninja -DCMAKE_TOOLCHAIN_FILE=/opt/ps5-payload-sdk/toolchain/prospero.cmake -DCHIMERA_GFX_BUILD_PS5_PROBE=ON -DCHIMERA_GFX_PS5_ALLOWED_FIRMWARE=NONE -DBUILD_TESTING=OFF + cmake --build build/ps5 diff --git a/.gitea/workflows/managed-validation.yml b/.gitea/workflows/managed-validation.yml new file mode 100644 index 0000000..3ee15ba --- /dev/null +++ b/.gitea/workflows/managed-validation.yml @@ -0,0 +1,117 @@ +name: Managed validation + +on: + workflow_dispatch: + inputs: + profile: + description: Allowlisted validation profile + required: true + default: full + type: choice + options: [test, lint, typecheck, build, security, full] + +permissions: + contents: read + +concurrency: + group: managed-validation-${{ gitea.repository }}-${{ gitea.ref }} + cancel-in-progress: true + +jobs: + full: + name: full + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: Validate repository with a bounded profile + shell: bash + env: + REQUESTED_PROFILE: ${{ inputs.profile }} + run: | + set -euo pipefail + profile="${REQUESTED_PROFILE:-full}" + case "${profile}" in + test|lint|typecheck|build|security|full) ;; + *) echo "Profile is not allowlisted" >&2; exit 2 ;; + esac + + git diff --check + if git grep -nE '^(<<<<<<< |=======$|>>>>>>> )' -- . ':!*.lock' ':!*.patch'; then + echo "Unresolved merge markers detected" >&2 + exit 1 + fi + + if [[ -f pyproject.toml || -f requirements.txt ]]; then + # Compile only tracked Python sources. Running compileall after a + # Node install would otherwise traverse node_modules and turn a + # lightweight baseline into a large runner workload. + git ls-files -z '*.py' | xargs -0 -r python -m py_compile + if [[ -f uv.lock ]]; then + python -m venv "${RUNNER_TEMP}/managed-uv" + uv_python="${RUNNER_TEMP}/managed-uv/bin/python" + "${uv_python}" -m pip install --disable-pip-version-check uv==0.10.0 + managed_uv="${RUNNER_TEMP}/managed-uv/bin/uv" + export UV_PROJECT_ENVIRONMENT="${RUNNER_TEMP}/managed-project-venv" + "${managed_uv}" sync --locked + export PATH="${UV_PROJECT_ENVIRONMENT}/bin:${PATH}" + if [[ "${profile}" == test || "${profile}" == full ]]; then + if "${managed_uv}" run python -c 'import pytest' 2>/dev/null; then + "${managed_uv}" run python -m pytest + fi + fi + if [[ "${profile}" == lint || "${profile}" == full ]]; then + if "${managed_uv}" run python -c 'import ruff' 2>/dev/null; then + "${managed_uv}" run python -m ruff check . + fi + fi + elif [[ -f requirements.txt ]]; then + python -m venv "${RUNNER_TEMP}/managed-python" + managed_python="${RUNNER_TEMP}/managed-python/bin/python" + "${managed_python}" -m pip install --disable-pip-version-check -r requirements.txt + export PATH="${RUNNER_TEMP}/managed-python/bin:${PATH}" + if [[ "${profile}" == test || "${profile}" == full ]]; then + if "${managed_python}" -c 'import pytest' 2>/dev/null; then + "${managed_python}" -m pytest + fi + fi + fi + fi + + # Prepare Python before invoking Node scripts. Polyglot repositories + # commonly delegate their test script to Python and need the managed + # virtual environment to be active first. + if [[ -f package.json ]]; then + corepack enable + if [[ -f pnpm-lock.yaml ]]; then + pnpm install --frozen-lockfile + [[ "${profile}" == test || "${profile}" == full ]] && pnpm --if-present test + [[ "${profile}" == lint || "${profile}" == full ]] && pnpm --if-present lint + [[ "${profile}" == typecheck || "${profile}" == full ]] && pnpm --if-present typecheck + [[ "${profile}" == build || "${profile}" == full ]] && pnpm --if-present build + elif [[ -f package-lock.json ]]; then + npm ci + [[ "${profile}" == test || "${profile}" == full ]] && npm run --if-present test + [[ "${profile}" == lint || "${profile}" == full ]] && npm run --if-present lint + if [[ "${profile}" == typecheck || "${profile}" == full ]]; then + npm run --if-present typecheck + fi + [[ "${profile}" == build || "${profile}" == full ]] && npm run --if-present build + fi + fi + + if [[ -f go.mod ]]; then + if [[ "${profile}" == test || "${profile}" == build || "${profile}" == full ]]; then + go test ./... + fi + fi + if [[ -f Cargo.toml ]]; then + if [[ "${profile}" == test || "${profile}" == build || "${profile}" == full ]]; then + cargo test --locked + fi + fi + if compgen -G '*.sln' >/dev/null; then + if [[ "${profile}" == test || "${profile}" == build || "${profile}" == full ]]; then + dotnet test --configuration Release + fi + fi diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46fa9f9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,60 @@ +# Build products +/build/ +/build-*/ +/out/ +/outputs/ +/CMakeFiles/ +CMakeCache.txt +cmake_install.cmake +CTestTestfile.cmake +compile_commands.json +.ninja_* +build.ninja +*.elf +*.o +*.obj +*.a +*.lib +*.pdb +*.dSYM/ +__pycache__/ +*.py[cod] + +# Test, coverage and diagnostic output +/Testing/ +/coverage/ +*.gcda +*.gcno +*.profraw +*.profdata +core +core.* +crash-*.json +*.log + +# Local tools, downloads and upstream research clones +/work/ +/tmp/ +/temp/ +/artifacts/ + +# Local AI/editor workspace state +.codex/ +.claude/ +.agents/ +.idea/ +.vscode/ +.vs/ +.DS_Store +Thumbs.db + +# Credentials and local configuration +.env +.env.* +!.env.example +*.key +*.pem +*.p12 +*.pfx +credentials* +secrets* diff --git a/.gitleaks.toml b/.gitleaks.toml new file mode 100644 index 0000000..bab7e45 --- /dev/null +++ b/.gitleaks.toml @@ -0,0 +1,13 @@ +[extend] +useDefault = true + +# These files contain published SHA-256 provenance values. Gitleaks' generic +# key rule mistakes the JSON field names for credentials; the values are +# one-way digests, not authentication material. Keep the allowlist path-bound. +[[allowlists]] +description = "Published provenance SHA-256 values" +paths = [ + '''^manifests/runtime/phase-0\.9e-r-release-correlation\.json$''', + '''^manifests/retroarch/phase-1\.0bz-exact-sdl-stage-cleanup-gate\.json$''', + '''^manifests/retroarch/phase-1\.0ca-cleanup-result-and-hash-verifier-correction-gate\.json$''' +] diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md new file mode 100644 index 0000000..74aad6c --- /dev/null +++ b/ARCHITECTURE.md @@ -0,0 +1,438 @@ +# Architecture + +## Scope and trust boundaries + +`libchimera-gfx` is split so that host-testable policy and state validation do +not depend on PS5 headers or undocumented ABI assumptions. + +```text +RetroArch adapter (Phase 3) ----+ + +--> public C API --> validated core +SDL2 renderer (Phase 4) --------+ | + +--> mock backend + +--> PS5 backend (refuses) + +Phase-0 capability sample --> probe logic --> symbol resolver --> system loader + ^ | + | +-- no resolved pointer escapes + +-- generated read-only symbol table +``` + +The capability probe is intentionally separate from the normal context API. +Discovering exports does not make the PS5 backend usable and cannot set a +rendering capability flag. + +## Public API contract + +The public API is C11 and uses opaque handles, explicit structure sizes, and an +API version. This supports compatible growth without copying upstream or +proprietary structures into the interface. + +Phase-0 guarantees: + +1. `chimera_gfx_create()` creates only the mock backend; the separately + compiled PS5 backend always returns `SAFETY_POLICY`. +2. Any rendering-request flag fails with `CHIMERA_GFX_STATUS_SAFETY_POLICY`. +3. Mock capabilities claim only a host-memory lifecycle and present model, + always together with `NON_RENDERING` and `HOST_TEST_ONLY`. +4. Context destruction accepts `NULL` and otherwise returns `RESOURCE_BUSY` + until every child surface and texture is destroyed. +5. Unknown backend IDs, API versions, flags, or undersized structures fail + before backend code is entered. + +The contract includes opaque surface and texture handles, validated RGBA8 +upload, a deterministic mock present serial, and cleanup status. It does not +yet include buffers, queues, shaders, fences, swapchains, display ownership, +hardware contexts, or hardware presentation. + +## Backend contract + +Backends receive only validated inputs. A backend must populate capability +flags from evidence, never from platform names or compile-time assumptions. +Future mutating operations must include bounded lifetimes, timeout behavior, +idempotent cleanup, and a safe fallback before entering the public API. + +The mock backend is deterministic and advertises only: + +- backend availability; +- non-rendering operation; +- host-test support; +- bounded surface and texture lifecycle; +- packed host-memory upload and non-graphical present-state tracking. + +It allocates no display or GPU resource. A present validates ownership, +dimensions, format, and upload state, then records a serial and FNV-1a content +hash for test inspection. + +## PS5 discovery boundary + +The PS5 probe has two layers: + +- portable logic that iterates an immutable symbol-name table and records only + `resolved: true|false`; +- a PS5 loader shim limited to `dlopen`, `dlsym`, and `dlclose`. + +Resolved addresses are compared with `NULL`, then discarded. They are never +logged, returned, cast to a function type, or invoked. The probe does not call +even apparently read-only exports because their signatures and ABI remain +unverified. + +## Phase-0.5 startup boundary + +The stock SDK v0.41 `crt1.o` is outside the trusted runtime boundary: its +reachable `_start` graph performs kernel credential and syscall-bound writes +and initializes a module-capable rtld. A compiler trace proves that +`-nostartfiles -nodefaultlibs` can omit that object, but the exact public loader +caller needed to prove entry stack, argument ownership, safe return, cleanup, +and crash behavior is absent from the pinned local evidence. + +Accordingly, `CHIMERA_GFX_BUILD_PS5_MINIMAL_STARTUP=ON` always fails +configuration. There is no candidate source or artifact. The design may be +reopened only after the evidence listed in +`docs/runtime/minimal-crt-feasibility.md` is available and reviewed. + +Artifact execution policy is a separate, fail-closed boundary. Static +eligibility requires `execution_eligible=true` and a SHA-256 absent from the +permanent denylist. Passing that layer never grants execution authority. + +## Phase-0.6 controlled runtime boundary + +Phase 0.6 identified the exact installed Payload Manager v0.3.1 and +`ps5-payload-dev/elfldr` v0.23. The normal SDK CRT is acceptable for design +only when every temporary effect is documented and bounded; `__patch_init` +alone is no longer an automatic blocker. + +The exact chain is not bounded: ptrace single-step loops and detached payload +runtime have no deadline, cleanup and credential failure paths are unresolved, +Payload Manager launch is not SHA-256-bound, and manager upload writes +persistent storage. The historical Phase-0.6 profile therefore remained +`execution_eligible=false` and no Phase-0.6 lifecycle artifact exists. + +Future consumers must validate the artifact manifest, permanent denylist, +controlled runtime profile, exact device firmware, exact local bytes, and +budgets together. The static gate always returns +`execution_authorized=false`. See ADR-0010. + +## Phase-0.7 hardened deployment boundary + +Phase 0.7 preserves the Phase-0.6 record and creates new private hardened +runtime artifacts. elfldr now bounds ptrace and child lifetime, restores and +checks all credential fields, centralizes cleanup, reaps every controlled +child, and rehashes received payload bytes. The controlled Payload Manager +hashes and streams one no-follow file descriptor and is compiled for one exact +firmware/artifact identity. + +The lifecycle payload uses normal SDK v0.41 startup, sends one notification, +and calls `_exit`. It contains no GNM, VideoOut, SDL, payload-network, retry, +or autoload path. Complete source/map/disassembly callgraphs remain part of the +audit because static linkage means imports alone are insufficient evidence. + +The current architecture state is +`READY_FOR_HARDENED_RUNTIME_DEPLOYMENT` offline only. It permits packaging and +review; it does not permit connection, installation, transfer, or execution. +See ADR-0011. + +The candidate runtime module name `libSceGnmDriver.sprx` is an inference from +the public stub filename and the SDK's documented dynamic-loading convention; +it is not treated as proven on any firmware. + +## Phase-0.9A anti-brick design boundary + +Phase 0.9A adds only an offline transaction contract and a virtual host +simulator. It does not add an installer, PS5 path, device transport, target +artifact or production runtime code. The simulator holds logical objects, +mounts, hashes and writes in memory and marks every result as non-hardware +evidence. + +Hardened elfldr and controlled Payload Manager have independent transactions, +backups, target mappings, approvals, switches and rollbacks. No combined action +or component order is inferred. The lifecycle probe is excluded. In-place +overwrite and a two-rename interval with a missing live name are forbidden. +All fourteen PS5 power-loss boundaries and all filesystem atomicity and +durability properties remain `UNPROVEN`; any one blocks installation. See +`docs/runtime/phase-0.9-installation-transaction-design.md`. + +## Phase-0.9B bounded observer boundary + +Phase 0.9B stops before target implementation. The normal SDK v0.41 `_start` +graph reaches `__patch_init()` before `main` and therefore violates this +phase's kernelwrite-free requirement. `-nostartfiles -nodefaultlibs` can omit +that graph, but the bare-entry return/exit, crash and loader-cleanup contract +is still unproven. + +The hardened controlled route also passes `stdio=-1` to the payload. The +controlled manager provides connect, send and close operations but no receive +operation; the loader's fixed exit response carries no payload data. Therefore +there is no proven non-persistent observer output channel. No observer source, +target, ELF, map or artifact package exists. Host-only mocks validate the +result contract and fail-closed BLOCKED record, not PS5 behavior. See +`docs/runtime/phase-0.9b-bounded-observer-design.md`. + +## Phase-0.9C observer execution-feasibility boundary + +Phase 0.9C closes the offline design question with +`BLOCKED_MULTIPLE_FOUNDATIONAL_CONTRACTS`. The loader sets `RIP`, `RDI`, and a +synthetic return address, but it does not prove entry stack alignment or a +safe post-return continuation. Normal SDK startup reaches `__patch_init`; +omitting that runtime also removes the only reviewed syscall, import, time, +heap, and libc initialization path. No safe normal/error/deadline exit and +joint cleanup contract remains. + +The host-only D1 model defines a fixed 4096-byte caller-owned record with +nonce/request binding, two firmware fields, capability bitmaps, checksums, +deadline, cleanup status, and a final completion marker. Hardened elfldr does +not own such a buffer: its current `payloadout` lives in the child mapping. +The controlled manager connects, sends, and closes without receiving; the +loader discards wait status. The protocol is therefore a future contract, not +a target implementation or output channel. No observer source, target, +artifact, map, package, device operation, or authorization exists. + +## Phase-0.9D existing-stack readback boundary + +Phase 0.9D does not revisit target startup or create an observer. It audits the +already present elfldr and Payload Manager request surfaces. Their file reads +feed listing, metadata, hash-before-launch, or host-to-elfldr transmission; no +route frames and returns an arbitrary file to the host. The result is +`BLOCKED_NO_READBACK_PATH`. + +The host backup state machine therefore remains a contract only: two separate +exclusive local files, exact byte counts, close/reopen/hash, and full +size/hash/byte comparison. Matching copies could establish +`OFF_DEVICE_BACKUP_VALID`, never `RECOVERY_PROVEN`. Current live component +paths, external exploit repeatability, durable restoration, and power-loss +recovery remain unproven. See +`docs/runtime/phase-0.9d-readback-feasibility.md`. + +## Phase-0.9E external bootstrap boundary + +The architecture before hardened elfldr is not locally source-bound. The +bounded inventory found an exact Y2JB-named ZIP and byte-exact opaque `SIECAF` +member, but no acceptable parser, exact-used deployment evidence, port-9020 +listener source/binary, or exact sender implementation. The port-9020 material +in the elfldr README describes only a host stream into a pre-existing +rudimentary loader; it is not that loader's protocol or implementation. + +Consequently host-to-memory, filesystem staging, entrypoint selection, +duplex output, crash cleanup, post-reboot restart, and independence from +elfldr and Payload Manager remain `UNPROVEN`. Phase 0.9E adds no parser, +emulator, target code, client, rescue payload, or artifact. Its fail-closed +classification is `BOOTSTRAP_IMPLEMENTATION_MISSING`. + +## Phase-0.9E-R official-release correlation boundary + +The local outer Y2JB ZIP is not byte-identical to any current official +`Gezine/Y2JB` release asset: all official sizes and GitHub SHA-256 digests +differ. Official tag source establishes a dynamic Remote JS Loader that seeks +port 50000 and evaluates bounded JavaScript, while 9020 appears only in sender +examples. The embedded port-9021 elfldr has no source or generator in release +1.6. These source facts do not bind the local opaque `SIECAF` backup or prove +deployed behavior. Classification remains `LOCAL_BACKUP_NOT_CORRELATED`; no +Phase-0.9F design gate opens. + +## Phase-0.9E-R2 inner-correlation boundary + +The local MediaFire URL and object page are now metadata-bound, but their +maker/source is not. A read-only host parser, bound to the public fixed-width +SIECAF header layout, fingerprints header, metadata, and hash tables without +decrypting or extracting content. The local inner differs from both the +mandatory official Y2JB 1.6 4.03 inner and one date/size-motivated community +autoloader inner in complete bytes and normalized structure. Classification is +`LOCAL_BACKUP_UNCORRELATED`; the parser and fingerprints create no bootstrap, +target, recovery, deployment, or runtime evidence. + +## Adapter boundaries + +### RetroArch + +Phase 1.0A is implemented in a separate private `chimera-retroarch` fork. It +links the real RetroArch frontend/runloop, a PS5 platform frontend and a +deterministic static software smoke core. The headless profile uses null +drivers; the second profile links RGUI and RetroArch's SDL2 video, input and +audio drivers to the public PS5 SDL fork. Neither profile uses +`libchimera-gfx`, GNM, a hardware-rendered libretro context or dynamic cores. +Target artifacts remain ignored local outputs with execution eligibility +disabled. Later hardware-rendered cores still require proven context, +synchronization, memory-ownership and shader contracts. + +Phase 1.0E adds a narrowly scoped result path to the Phase-1.0D diagnostic +profile. Hardened elfldr's legacy raw-ELF route duplicates the accepted socket +to payload stdout, so the payload can emit fixed 64-byte checksummed D-stage +frames without creating a target-side socket or connection. The host performs +one send and a write-half-close, then bounded receive on that same connection. +This contract does not apply to the controlled route (`stdio=-1`), grants no +device authority, and leaves the SDK CRT, SDL/VideoOut path and GNM boundary +unchanged. + +Phase 1.0I adds no runtime component. It binds the consumed H trace to the +existing RetroArch/SDL source, linker map and disassembly. The architecture +boundary remains at the failed first VideoOut submit: the exact call tuple is +known, while errno, argument semantics, visible presentation and cleanup are +not. The write-firewall status contains more state than the transmitted D12 +frame, so future observability must expose the exact operation before another +artifact can be reviewed. + +Phase 1.0P similarly adds no runtime component. It binds the consumed O result +to the exact M source, map, dynamic relocations and disassembly. The submit +failure site and D12-before-D04 source order are proven, while VideoOut +argument/layout semantics and the root cause remain unresolved. A future +protocol needs a distinct post-D04 terminal; accepting the earlier D12 would +confuse a shutdown request with lifecycle completion. + +Phase 1.0Q adds only public-source provenance. The PS5 submit declarations, +opaque records and constants all trace to one SDL commit lineage; the SDK has +export names only and the relevant official ports consume that same SDL fork. +PS4's similar API is retained as analogue evidence, never promoted to a PS5 +contract. The architecture therefore remains blocked before any argument or +ownership-state experiment. + +Phase 1.0R adds no runtime path. It proves that the pinned PS5 SDL2main does +not create an application or display context: its only pre-entry action is +splash hiding and its exit action occurs after `SDL_main` returns. SDL's video +backend already performs the splash action before VideoOut open in the tested +artifact. Direct and Payload Manager transfers converge on the same hardened +elfldr spawn routine. PacBrew is packaging metadata, and the distinct +`hbldr`/shsrv launcher implementation remains unbound; it cannot be promoted +to a target fix. + +Phase 1.0S binds that official implementation and proves an architectural +difference: hbldr substitutes an ELF into a SystemService-launched BigApp, +whereas raw elfldr substitutes it into SceSpZeroConf. It does not prove that +the BigApp supplies usable VideoOut ownership on firmware 9.60. The available +hbldr path is also outside the Chimera execution model because it requires a +device file and includes BigApp termination, runtime kernel/ptrace mutation, +unbounded waits and, in current releases, possible persistent fake-app setup +under `/system_ex`. It remains research evidence, never a runtime dependency. + +Phase 1.0T adds only an offline sanitization and classification boundary for +text that an operator might supply later. It has no socket implementation. +The model drops serial, model, temperature, frequency, raw transcript and +unknown paths; it fingerprints `help` command names and labels `sum` as a +non-cryptographic 16-bit checksum. Its output schema hard-codes +`exact_identity=false`. Any future collector remains a separately reviewed, +inactive design problem because merely connecting already spawns shsrv's +shell and emits sensitive greeting data. + +Phase 1.0U adds no runtime component. It records a bounded local artifact +inventory and distinguishes official source, a host telnet wrapper and an +unpinned package recipe from a deployed target binary. No candidate was found +inside the declared scope. That negative result cannot be generalized to the +whole host or device and cannot satisfy the launch-context identity gate. + +Phase 1.0V adds a host-only byte-stream model, not a transport. An incremental +Telnet filter feeds the existing Phase-1.0T sanitizer under fixed byte/chunk +limits and a one-shot state machine. The CLI exposes only an explicit offline +stdin mode and literal metadata paths. Logical buffer clearing limits retained +application state but does not prove physical memory erasure. No network or +device boundary is crossed. + +Phase 1.0W hardens the collector's Telnet, chunk, path and metadata edges and +places an immutable policy object above it. The policy consumes two synthetic +matching records and produces a frozen session-plan value. A test-only fake +transport models one open/command/receive/close sequence. Neither component +contains a live transport, persistence path or executable CLI, so the network +boundary remains a separately blocked implementation concern. + +Phase 1.0X places exclusive host evidence and one-shot orchestration around +that immutable plan. The adapter and monotonic clock are dependency-injected; +the repository provides only fakes. Receipt creation precedes adapter open, +and sanitized output follows collector completion. Both files use exclusive +leaf creation, file flush, close and reopen verification. There is deliberately +no command-byte layer or live prompt boundary: those contracts remain blocked +until exact source framing is established. File flush does not prove containing +directory durability, and pre/post deadline checks cannot preempt a blocking +adapter implementation. + +Phase 1.0Y separates protocol evidence into legacy raw and current +`libtelnet`/NVT source families. The framing model is deliberately independent +of X orchestration: it transforms only supplied synthetic bytes and reports +prompt candidates without creating boundaries for a live adapter. This avoids +silently binding current-source CRLF and negotiation behavior to an unknown +deployed version. A later passive contract can use plain LF, which both audited +families accept, while treating all received negotiation and incomplete output +as bounded failure. + +Phase 1.0Z implements that later layer as a pure data contract. An immutable, +target-free object contains one ASCII/LF batch and its exact completion policy. +The receive accumulator consumes supplied chunks, rejects IAC and delegates +sanitization to V, but it cannot seal on prompt or EOF. An externally injected +synthetic hard-deadline event is the only sealing boundary. This separation +keeps byte formatting and completeness checks independently testable without +quietly introducing a network adapter, clock or live completion claim. + +Phase 1.0AA composes X evidence and Z framing behind a closed fake boundary. +Unlike X's abstract injected adapter, AA accepts only exact built-in fake types; +there is no protocol a live implementation can satisfy. A synthetic event +advances an explicit fake clock, while receipt, one-batch send, deadline seal, +close and sanitized output form a testable order. This proves model composition +only, not socket preemption, deployment identity or firmware behavior. + +Phase 1.0AB separates OS feasibility from implementation. It binds the local +Python `_socket`, `select`, `socket.py`, `selectors.py` and monotonic-clock +identities, then validates only synthetic syscall ordering. The required future +architecture is one nonblocking descriptor, readiness before every I/O call, +an explicit partial-send offset, a single absolute deadline and `finally`-based +local cleanup. Runtime source makes that sequence feasible but cannot turn +selector timeouts into a hard scheduling guarantee or attest remote cleanup. + +Phase 1.0AC realizes that ordering behind a dormant, target-free component. +Only the exact built-in fake facade and fake clock are accepted, so no live +implementation can satisfy an injected protocol. The adapter composes the Z +batch/result contract with synthetic create, readiness, partial progress, +deadline and close outcomes. This is integration-test infrastructure only: it +adds no socket factory, address model, activation path or firmware evidence. + +Phase 1.0AU closes the subsequent result-channel source-feasibility question. +The official SDK exposes every primitive signature needed by the AT ownership +model, but official shsrv composes none of them into a bounded channel. Its +worker closes inherited descriptors and its service restarts automatically. +The architecture therefore permits only a target-free canary contract next; +live channel code, a target build and every device action remain outside the +boundary. + +Phase 1.0AV turns the unresolved launch-context claim into a causal offline +contract. It holds payload and protocol identity constant while varying only +the launcher identity, and requires independent one-shot approvals. Results +are comparable only after submit, D04 and a distinct cleanup terminal. The +contract deliberately cannot interpret a zero submit return as presentation; +it contains no target implementation or activation surface. + +Phase 1.0AW maps that contract onto exact historical source. A future single +ELF needs a new magic and a D14 terminal guarded by completed RetroArch teardown, +an empty initialized mask, valid cleanup order and an independent cleanup-error +counter. Official v0.7 can duplicate raw stdout into its BigApp, but that source +candidate does not make its unbounded and mutating launcher admissible. + +Phase 1.0AX freezes the proposed wire and cleanup semantics independently of +target code. D14 reuses the 64-byte frame's raw/result/auxiliary fields to bind +initialized, cleaned, cleanup-error and `rarch_main` state. Only canonical D14 +after D07 and D04 is terminal. This host reference cannot establish that a +future RetroArch build or firmware implements the same behavior. + +Phase 1.0AY fixes repository lineage as another trust boundary. The selected N +tip is an exact descendant of M with unchanged diagnostic target sources and an +inactive runner. Future source work must occur in a separate worktree so the +historical loader checkout and consumed-run records are not rewritten. + +### SDL2 + +The future SDL2 integration will be a renderer backend that can satisfy +`SDL_RENDERER_ACCELERATED`; it will not replace the existing PS5 window, +VideoOut, audio, input, IME, or filesystem backends. This separation avoids +forking unrelated platform support. The compiled scaffold reports acceleration +unavailable and requires the existing software fallback. + +## Offline Phase-1 boundary + +The VideoOut candidate is a separate CMake option that is off by default and +valid only under the PS5 toolchain. Project code calls public SDL2 APIs; a +staged Zlib-licensed overlay removes upstream keyboard/IME initialization. +SDL retains ownership of its public-source VideoOut declarations and layouts. +The target does not link SDL2main, so the firmware gate runs before SDL or +system-service initialization. See ADR-0006 and `docs/phase1/`. + +## Failure model + +All unknown firmware, missing exports, version mismatches, partial +initialization, or unsupported requests fail closed. Later hardware phases must +add bounded waits, watchdog-visible progress, crash logs without secrets or +addresses, and cleanup that can be audited independently of the success path. diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..25cbe6c --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,1293 @@ +cmake_minimum_required(VERSION 3.21) + +project(chimera-gfx VERSION 0.1.0 LANGUAGES C) + +include(CTest) +include(GNUInstallDirs) +include(CMakePackageConfigHelpers) + +option(CHIMERA_GFX_BUILD_PS5_PROBE + "Compile the discovery-only PS5 probe; never deploy or execute it" OFF) +option(CHIMERA_GFX_BUILD_PHASE1_VIDEOOUT_CLEAR + "Compile the gated SDL2 VideoOut clear candidate; never deploy or run it" + OFF) +option(CHIMERA_GFX_BUILD_PS5_MINIMAL_STARTUP + "Build the Phase-0.5 freestanding PS5 startup audit candidate" OFF) +option(CHIMERA_GFX_BUILD_PS5_LIFECYCLE_PROBE + "Build the Phase-0.7 firmware-9.60 controlled lifecycle probe" OFF) +option(CHIMERA_GFX_WARNINGS_AS_ERRORS "Treat project warnings as errors" ON) +option(CHIMERA_GFX_ENABLE_CLANG_TIDY "Run clang-tidy while compiling" OFF) +option(CHIMERA_GFX_RUN_BRANCH_LOCAL_HISTORICAL_VALIDATORS + "Run validators that intentionally require their exact historical branch" + OFF) +option(CHIMERA_GFX_REGISTER_EXTERNAL_EVIDENCE_VALIDATORS + "Register validators requiring phase-specific sibling/evidence workspaces" + OFF) +set(CHIMERA_GFX_PS5_ALLOWED_FIRMWARE "NONE" CACHE STRING + "Exact firmware gate embedded in a PS5 probe build; NONE fails closed") +set(CHIMERA_GFX_PS5_PROBE_FIRMWARE_ALLOWLIST "9.60") +set(CHIMERA_GFX_PHASE1_SDL_BUILD "" CACHE PATH + "Existing cross-compiled pinned SDL2 build used only by the Phase-1 candidate") + +set(CMAKE_C_STANDARD 11) +set(CMAKE_C_STANDARD_REQUIRED ON) +set(CMAKE_C_EXTENSIONS OFF) + +if(CHIMERA_GFX_BUILD_PS5_MINIMAL_STARTUP) + message(FATAL_ERROR + "BLOCKED: no pinned public loader caller proves stack, return, payloadout, " + "post-return cleanup, or crash semantics; no minimal PS5 ELF may be built") +endif() + +function(chimera_gfx_project_warnings target) + if(MSVC) + target_compile_options(${target} PRIVATE /W4) + if(CHIMERA_GFX_WARNINGS_AS_ERRORS) + target_compile_options(${target} PRIVATE /WX) + endif() + else() + target_compile_options(${target} PRIVATE + -Wall -Wextra -Wpedantic -Wconversion -Wshadow) + if(CHIMERA_GFX_WARNINGS_AS_ERRORS) + target_compile_options(${target} PRIVATE -Werror) + endif() + endif() + if(CHIMERA_GFX_ENABLE_CLANG_TIDY) + find_program(CHIMERA_GFX_CLANG_TIDY NAMES clang-tidy-18 clang-tidy REQUIRED) + set_property(TARGET ${target} PROPERTY C_CLANG_TIDY + "${CHIMERA_GFX_CLANG_TIDY};--config-file=${CMAKE_CURRENT_SOURCE_DIR}/.clang-tidy") + endif() +endfunction() + +function(chimera_gfx_add_external_evidence_test) + if(CHIMERA_GFX_REGISTER_EXTERNAL_EVIDENCE_VALIDATORS) + add_test(${ARGV}) + endif() +endfunction() + +if(CHIMERA_GFX_BUILD_PS5_LIFECYCLE_PROBE) + if(NOT PS5) + message(FATAL_ERROR + "The lifecycle probe requires the public PS5 Payload SDK toolchain") + endif() + if(NOT CHIMERA_GFX_PS5_ALLOWED_FIRMWARE STREQUAL "9.60") + message(FATAL_ERROR + "The lifecycle probe is build-allowlisted only for exact firmware 9.60") + endif() + + add_executable(chimera-gfx-lifecycle-probe + samples/lifecycle_probe/main.c) + target_compile_definitions(chimera-gfx-lifecycle-probe PRIVATE + "CHIMERA_GFX_PS5_ALLOWED_FIRMWARE=\"9.60\"" + "CHIMERA_GFX_LIFECYCLE_BUILD_ID=\"phase07-fw960-v1\"") + target_link_options(chimera-gfx-lifecycle-probe PRIVATE + "-nodefaultlibs" + "-Wl,-Map,chimera-gfx-lifecycle-probe.map") + target_link_libraries(chimera-gfx-lifecycle-probe PRIVATE + c + kernel_web + SceLibcInternal) + set_target_properties(chimera-gfx-lifecycle-probe PROPERTIES + OUTPUT_NAME "chimera-gfx-lifecycle-probe.elf") + chimera_gfx_project_warnings(chimera-gfx-lifecycle-probe) +endif() + +add_library(chimera-gfx STATIC + src/core/context.c + src/core/resources.c + src/core/status.c + src/backends/mock/mock_backend.c + src/backends/ps5/ps5_backend.c) +add_library(chimera-gfx::chimera-gfx ALIAS chimera-gfx) +target_include_directories(chimera-gfx + PUBLIC + $ + $) +chimera_gfx_project_warnings(chimera-gfx) + +add_library(chimera-gfx-probe-logic STATIC + src/backends/ps5/probe_logic.c) +target_include_directories(chimera-gfx-probe-logic + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_CURRENT_SOURCE_DIR}/src/backends/ps5) +target_link_libraries(chimera-gfx-probe-logic PUBLIC chimera-gfx) +chimera_gfx_project_warnings(chimera-gfx-probe-logic) + +add_library(chimera-gfx-firmware-gate STATIC + src/backends/ps5/firmware_gate.c) +target_include_directories(chimera-gfx-firmware-gate PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/src/backends/ps5) +set_target_properties(chimera-gfx-firmware-gate PROPERTIES + INTERPROCEDURAL_OPTIMIZATION FALSE) +chimera_gfx_project_warnings(chimera-gfx-firmware-gate) + +add_library(chimera-gfx-retroarch-scaffold STATIC + adapters/retroarch/retroarch_adapter.c) +target_include_directories(chimera-gfx-retroarch-scaffold PUBLIC + $ + $) +target_link_libraries(chimera-gfx-retroarch-scaffold PUBLIC chimera-gfx) +chimera_gfx_project_warnings(chimera-gfx-retroarch-scaffold) + +add_library(chimera-gfx-sdl2-scaffold STATIC + adapters/sdl2/sdl2_adapter.c) +target_include_directories(chimera-gfx-sdl2-scaffold PUBLIC + $ + $) +target_link_libraries(chimera-gfx-sdl2-scaffold PUBLIC chimera-gfx) +chimera_gfx_project_warnings(chimera-gfx-sdl2-scaffold) + +if(CHIMERA_GFX_BUILD_PS5_PROBE) + if(NOT PS5) + message(FATAL_ERROR + "CHIMERA_GFX_BUILD_PS5_PROBE requires the public PS5 Payload SDK toolchain") + endif() + if(NOT CHIMERA_GFX_PS5_ALLOWED_FIRMWARE STREQUAL "NONE" AND + NOT CHIMERA_GFX_PS5_ALLOWED_FIRMWARE IN_LIST + CHIMERA_GFX_PS5_PROBE_FIRMWARE_ALLOWLIST) + message(FATAL_ERROR + "Probe firmware is not in the reviewed exact allowlist: 9.60") + endif() + + add_executable(chimera-gfx-capability-probe + samples/capability_probe/main.c + src/backends/ps5/probe_platform.c) + target_include_directories(chimera-gfx-capability-probe PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/src/backends/ps5) + target_link_libraries(chimera-gfx-capability-probe PRIVATE + chimera-gfx + chimera-gfx-probe-logic + chimera-gfx-firmware-gate) + target_compile_definitions(chimera-gfx-capability-probe PRIVATE + "CHIMERA_GFX_PS5_ALLOWED_FIRMWARE=\"${CHIMERA_GFX_PS5_ALLOWED_FIRMWARE}\"") + set_target_properties(chimera-gfx-capability-probe PROPERTIES + OUTPUT_NAME "chimera-gfx-capability-probe.elf") + chimera_gfx_project_warnings(chimera-gfx-capability-probe) +endif() + +if(CHIMERA_GFX_BUILD_PHASE1_VIDEOOUT_CLEAR) + if(NOT PS5) + message(FATAL_ERROR + "The Phase-1 VideoOut candidate requires the public PS5 toolchain") + endif() + if(NOT CHIMERA_GFX_PS5_ALLOWED_FIRMWARE MATCHES "^[A-Za-z0-9._-]+$") + message(FATAL_ERROR "Firmware gate contains unsupported characters") + endif() + if(NOT CHIMERA_GFX_PS5_ALLOWED_FIRMWARE STREQUAL "NONE") + message(FATAL_ERROR + "The Phase-1 VideoOut candidate must remain fail-closed at NONE") + endif() + if(NOT IS_ABSOLUTE "${CHIMERA_GFX_PHASE1_SDL_BUILD}") + message(FATAL_ERROR "CHIMERA_GFX_PHASE1_SDL_BUILD must be absolute") + endif() + if(NOT EXISTS "${CHIMERA_GFX_PHASE1_SDL_BUILD}/libSDL2.a") + message(FATAL_ERROR "Pinned static SDL2 build was not found") + endif() + + add_library(chimera-gfx-phase1-sdl2 STATIC IMPORTED) + set_target_properties(chimera-gfx-phase1-sdl2 PROPERTIES + IMPORTED_LOCATION "${CHIMERA_GFX_PHASE1_SDL_BUILD}/libSDL2.a" + INTERFACE_INCLUDE_DIRECTORIES + "${CHIMERA_GFX_PHASE1_SDL_BUILD}/include;${CHIMERA_GFX_PHASE1_SDL_BUILD}/include/SDL2;${CHIMERA_GFX_PHASE1_SDL_BUILD}/include-config-release;${CHIMERA_GFX_PHASE1_SDL_BUILD}/include-config-release/SDL2") + + add_executable(chimera-gfx-phase1-videoout-clear + samples/phase1_videoout_clear/main.c) + target_link_libraries(chimera-gfx-phase1-videoout-clear PRIVATE + chimera-gfx-firmware-gate + chimera-gfx-phase1-sdl2 + SceSystemService + SceVideoOut) + target_compile_definitions(chimera-gfx-phase1-videoout-clear PRIVATE + "CHIMERA_GFX_PS5_ALLOWED_FIRMWARE=\"${CHIMERA_GFX_PS5_ALLOWED_FIRMWARE}\"") + set_target_properties(chimera-gfx-phase1-videoout-clear PROPERTIES + OUTPUT_NAME "chimera-gfx-phase1-videoout-clear.elf") + chimera_gfx_project_warnings(chimera-gfx-phase1-videoout-clear) +endif() + +if(BUILD_TESTING AND NOT CMAKE_CROSSCOMPILING) + find_package(Python3 3.10 REQUIRED COMPONENTS Interpreter) + find_program(CHIMERA_GFX_CLANG_FORMAT NAMES clang-format-18 clang-format) + + add_executable(chimera-gfx-core-tests tests/test_core.c) + target_link_libraries(chimera-gfx-core-tests PRIVATE chimera-gfx) + chimera_gfx_project_warnings(chimera-gfx-core-tests) + add_test(NAME core_api COMMAND chimera-gfx-core-tests) + + add_executable(chimera-gfx-probe-tests tests/test_probe.c) + target_link_libraries(chimera-gfx-probe-tests PRIVATE chimera-gfx-probe-logic) + chimera_gfx_project_warnings(chimera-gfx-probe-tests) + add_test(NAME probe_logic COMMAND chimera-gfx-probe-tests) + + add_executable(chimera-gfx-mock-integration-tests + tests/test_mock_integration.c) + target_link_libraries(chimera-gfx-mock-integration-tests PRIVATE chimera-gfx) + chimera_gfx_project_warnings(chimera-gfx-mock-integration-tests) + add_test(NAME mock_integration COMMAND chimera-gfx-mock-integration-tests) + + add_executable(chimera-gfx-adapter-scaffold-tests + tests/test_adapters.c) + target_link_libraries(chimera-gfx-adapter-scaffold-tests PRIVATE + chimera-gfx-retroarch-scaffold + chimera-gfx-sdl2-scaffold) + chimera_gfx_project_warnings(chimera-gfx-adapter-scaffold-tests) + add_test(NAME adapter_scaffolds COMMAND chimera-gfx-adapter-scaffold-tests) + + add_executable(chimera-gfx-firmware-gate-tests + tests/test_firmware_gate.c) + target_link_libraries(chimera-gfx-firmware-gate-tests PRIVATE + chimera-gfx-firmware-gate) + chimera_gfx_project_warnings(chimera-gfx-firmware-gate-tests) + add_test(NAME firmware_gate COMMAND chimera-gfx-firmware-gate-tests) + + add_test(NAME generated_probe_symbols + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/generate_probe_symbols.py + --manifest ${CMAKE_CURRENT_SOURCE_DIR}/manifests/ps5_gnm_symbols.json + --output ${CMAKE_CURRENT_SOURCE_DIR}/src/backends/ps5/generated_probe_symbols.inc + --check) + add_test(NAME manifest_policy + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/verify_manifests.py + --locks ${CMAKE_CURRENT_SOURCE_DIR}/manifests/upstreams.lock.json + --symbols ${CMAKE_CURRENT_SOURCE_DIR}/manifests/ps5_gnm_symbols.json + --sbom ${CMAKE_CURRENT_SOURCE_DIR}/manifests/sbom.spdx.json + --artifact-schema + ${CMAKE_CURRENT_SOURCE_DIR}/manifests/artifact-manifest.schema.json + --denylist-schema + ${CMAKE_CURRENT_SOURCE_DIR}/manifests/artifact-denylist.schema.json + --denylist + ${CMAKE_CURRENT_SOURCE_DIR}/manifests/artifact-denylist.json + --runtime-profile-schema + ${CMAKE_CURRENT_SOURCE_DIR}/manifests/controlled-runtime-profile.schema.json + --runtime-profile + ${CMAKE_CURRENT_SOURCE_DIR}/manifests/runtime/controlled-ps5-runtime-profile.json + --phase06-audit + ${CMAKE_CURRENT_SOURCE_DIR}/manifests/runtime/phase-0.6-loader-runtime-audit.json + --phase07-audit + ${CMAKE_CURRENT_SOURCE_DIR}/manifests/runtime/phase-0.7-offline-audit.json + --phase07-proof + ${CMAKE_CURRENT_SOURCE_DIR}/manifests/runtime/phase-0.7-kernelwrite-proof-matrix.json) + add_test(NAME artifact_manifest_tools + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_manifest_tools.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME artifact_audit_logic + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_artifact_audit.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME artifact_execution_policy + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_execution_policy.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME minimal_startup_build_block + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_minimal_startup_block.py + --root ${CMAKE_CURRENT_SOURCE_DIR} + --cmake ${CMAKE_COMMAND} + --compiler ${CMAKE_C_COMPILER}) + add_test(NAME phase05_audit_records + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_phase05_audit.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME phase06_loader_audit_records + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_phase06_audit.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME phase07_offline_audit_records + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_phase07_audit.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME phase08_read_only_preflight_records + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_phase08_preflight.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME phase08_remediation_records + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_phase08_remediation.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME phase09_anti_brick_design + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_phase09_transaction.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + chimera_gfx_add_external_evidence_test(NAME phase09b_observer_audit + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_phase09b_observer_audit.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME phase09c_protocol + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_phase09c_protocol.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + chimera_gfx_add_external_evidence_test(NAME phase09c_feasibility + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_phase09c_feasibility.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + if(CHIMERA_GFX_RUN_BRANCH_LOCAL_HISTORICAL_VALIDATORS) + add_test(NAME phase09d_readback + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_phase09d_readback.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME phase09e_bootstrap_validation + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/validate_phase09e_bootstrap.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME phase09er_provenance_validation + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/validate_phase09er_provenance.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME phase09er2_correlation_validation + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/validate_phase09er2_correlation.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + endif() + add_test(NAME phase09e_bootstrap_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_phase09e_bootstrap.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME phase09er_provenance_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_phase09er_provenance.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME phase09er2_correlation_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_phase09er2_correlation.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME phase09er2_siecaf_parser + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_siecaf_header_parser.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10a_validation + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/validate_retroarch_phase10a.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10a_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10a.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10b_validation + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/validate_retroarch_phase10b.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10b_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10b.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10d_validation + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/validate_retroarch_phase10d.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10d_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10d.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10e_validation + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/validate_retroarch_phase10e.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10e_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10e.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10f_validation + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/validate_retroarch_phase10f.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10f_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10f.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10g_validation + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/validate_retroarch_phase10g.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10g_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10g.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10g_result_validation + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/validate_retroarch_phase10g_result.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10g_result_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10g_result.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10h_validation + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/validate_retroarch_phase10h.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10h_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10h.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10h_result_validation + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/validate_retroarch_phase10h_result.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10h_result_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10h_result.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10i_validation + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/validate_retroarch_phase10i.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10i_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10i.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10j_validation + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/validate_retroarch_phase10j.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10j_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10j.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + chimera_gfx_add_external_evidence_test(NAME retroarch_phase10k_validation + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/validate_retroarch_phase10k.py + --root ${CMAKE_CURRENT_SOURCE_DIR} + --retroarch-root ${CMAKE_CURRENT_SOURCE_DIR}/../chimera-retroarch) + add_test(NAME retroarch_phase10k_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10k.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + chimera_gfx_add_external_evidence_test(NAME retroarch_phase10l_validation + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/validate_retroarch_phase10l.py + --root ${CMAKE_CURRENT_SOURCE_DIR} + --retroarch-root ${CMAKE_CURRENT_SOURCE_DIR}/../chimera-retroarch) + add_test(NAME retroarch_phase10l_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10l.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + chimera_gfx_add_external_evidence_test(NAME retroarch_phase10m_validation + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/validate_retroarch_phase10m.py + --root ${CMAKE_CURRENT_SOURCE_DIR} + --retroarch-root ${CMAKE_CURRENT_SOURCE_DIR}/../chimera-retroarch) + add_test(NAME retroarch_phase10m_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10m.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + chimera_gfx_add_external_evidence_test(NAME retroarch_phase10n_validation + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/validate_retroarch_phase10n.py + --root ${CMAKE_CURRENT_SOURCE_DIR} + --retroarch-root ${CMAKE_CURRENT_SOURCE_DIR}/../chimera-retroarch) + add_test(NAME retroarch_phase10n_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10n.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + chimera_gfx_add_external_evidence_test(NAME retroarch_phase10o_validation + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/validate_retroarch_phase10o.py + --root ${CMAKE_CURRENT_SOURCE_DIR} + --retroarch-root ${CMAKE_CURRENT_SOURCE_DIR}/../chimera-retroarch) + add_test(NAME retroarch_phase10o_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10o.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + chimera_gfx_add_external_evidence_test(NAME retroarch_phase10p_validation + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/validate_retroarch_phase10p.py + --root ${CMAKE_CURRENT_SOURCE_DIR} + --retroarch-root ${CMAKE_CURRENT_SOURCE_DIR}/../chimera-retroarch + --sdk-root ${CMAKE_CURRENT_SOURCE_DIR}/work/upstream/sdk + --sdl-root ${CMAKE_CURRENT_SOURCE_DIR}/work/upstream/SDL) + add_test(NAME retroarch_phase10p_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10p.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + chimera_gfx_add_external_evidence_test(NAME retroarch_phase10q_validation + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/validate_retroarch_phase10q.py + --root ${CMAKE_CURRENT_SOURCE_DIR} + --sdk-root ${CMAKE_CURRENT_SOURCE_DIR}/work/upstream/sdk + --sdl-root ${CMAKE_CURRENT_SOURCE_DIR}/work/upstream/SDL) + add_test(NAME retroarch_phase10q_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10q.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + chimera_gfx_add_external_evidence_test(NAME retroarch_phase10r_validation + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/validate_retroarch_phase10r.py + --root ${CMAKE_CURRENT_SOURCE_DIR} + --retroarch-root ${CMAKE_CURRENT_SOURCE_DIR}/../chimera-retroarch + --sdk-root ${CMAKE_CURRENT_SOURCE_DIR}/work/upstream/sdk + --sdl-root ${CMAKE_CURRENT_SOURCE_DIR}/work/upstream/SDL + --elfldr-root ${CMAKE_CURRENT_SOURCE_DIR}/../chimera-elfldr + --manager-root ${CMAKE_CURRENT_SOURCE_DIR}/../chimera-ps5-payload-manager + --pacbrew-root ${CMAKE_CURRENT_SOURCE_DIR}/work/upstream/pacbrew-repo) + add_test(NAME retroarch_phase10r_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10r.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + chimera_gfx_add_external_evidence_test(NAME retroarch_phase10s_validation + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/validate_retroarch_phase10s.py + --root ${CMAKE_CURRENT_SOURCE_DIR} + --shsrv-root ${CMAKE_CURRENT_SOURCE_DIR}/work/upstream/shsrv + --shsrv-v07-root ${CMAKE_CURRENT_SOURCE_DIR}/work/upstream/shsrv-v0.7 + --pacbrew-root ${CMAKE_CURRENT_SOURCE_DIR}/work/upstream/pacbrew-repo) + add_test(NAME retroarch_phase10s_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10s.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + chimera_gfx_add_external_evidence_test(NAME retroarch_phase10t_validation + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/validate_retroarch_phase10t.py + --root ${CMAKE_CURRENT_SOURCE_DIR} + --shsrv-root ${CMAKE_CURRENT_SOURCE_DIR}/work/upstream/shsrv + --shsrv-v07-root ${CMAKE_CURRENT_SOURCE_DIR}/work/upstream/shsrv-v0.7) + add_test(NAME retroarch_phase10t_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10t.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME phase10t_shsrv_transcript + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_phase10t_shsrv_transcript.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + chimera_gfx_add_external_evidence_test(NAME retroarch_phase10u_validation + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/validate_retroarch_phase10u.py + --root ${CMAKE_CURRENT_SOURCE_DIR} + --shsrv-root ${CMAKE_CURRENT_SOURCE_DIR}/work/upstream/shsrv + --shsrv-v07-root ${CMAKE_CURRENT_SOURCE_DIR}/work/upstream/shsrv-v0.7 + --pacbrew-root ${CMAKE_CURRENT_SOURCE_DIR}/work/upstream/pacbrew-repo) + add_test(NAME retroarch_phase10u_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10u.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10v_validation + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/validate_retroarch_phase10v.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10v_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10v.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME phase10v_shsrv_collector_model + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_phase10v_shsrv_collector_model.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10w_validation + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/validate_retroarch_phase10w.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10w_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10w.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME phase10w_shsrv_client_policy + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_phase10w_shsrv_client_policy.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + chimera_gfx_add_external_evidence_test(NAME retroarch_phase10x_validation + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/validate_retroarch_phase10x.py + --root ${CMAKE_CURRENT_SOURCE_DIR} + --shsrv-root ${CMAKE_CURRENT_SOURCE_DIR}/work/upstream/shsrv) + add_test(NAME retroarch_phase10x_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10x.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME phase10x_inactive_transport + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_phase10x_inactive_transport.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + chimera_gfx_add_external_evidence_test(NAME retroarch_phase10y_validation + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/validate_retroarch_phase10y.py + --root ${CMAKE_CURRENT_SOURCE_DIR} + --shsrv-root ${CMAKE_CURRENT_SOURCE_DIR}/work/upstream/shsrv) + add_test(NAME retroarch_phase10y_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10y.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME phase10y_shsrv_framing_model + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_phase10y_shsrv_framing_model.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10z_validation + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/validate_retroarch_phase10z.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10z_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10z.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME phase10z_passive_batch_contract + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_phase10z_passive_batch_contract.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10aa_validation + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/validate_retroarch_phase10aa.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10aa_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10aa.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME phase10aa_offline_fake_batch + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_phase10aa_offline_fake_batch.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10ab_validation + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/validate_retroarch_phase10ab.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10ab_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10ab.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME phase10ab_nonblocking_trace_model + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_phase10ab_nonblocking_trace_model.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10ac_validation + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/validate_retroarch_phase10ac.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10ac_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10ac.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME phase10ac_dormant_adapter + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_phase10ac_dormant_adapter.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME phase10ad_activation_contract + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_phase10ad_activation_contract.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10ad_validation + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/validate_retroarch_phase10ad.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10ad_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10ad.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + chimera_gfx_add_external_evidence_test(NAME retroarch_phase10ae_validation + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/validate_retroarch_phase10ae.py + --root ${CMAKE_CURRENT_SOURCE_DIR} + --shsrv-root ${CMAKE_CURRENT_SOURCE_DIR}/work/upstream/shsrv + --shsrv-v07-root ${CMAKE_CURRENT_SOURCE_DIR}/work/upstream/shsrv-v0.7) + add_test(NAME retroarch_phase10ae_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10ae.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME phase10af_bigapp_lifecycle_model + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_phase10af_bigapp_lifecycle_model.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10af_validation + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/validate_retroarch_phase10af.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10af_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10af.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME phase10ag_bounded_elf + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_phase10ag_bounded_elf.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10ag_validation + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/validate_retroarch_phase10ag.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10ag_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10ag.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME phase10ah_dynamic_contract + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_phase10ah_dynamic_contract.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + chimera_gfx_add_external_evidence_test(NAME retroarch_phase10ah_validation + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/validate_retroarch_phase10ah.py + --root ${CMAKE_CURRENT_SOURCE_DIR} + --shsrv-v07-root ${CMAKE_CURRENT_SOURCE_DIR}/work/upstream/shsrv-v0.7 + --sdk-root ${CMAKE_CURRENT_SOURCE_DIR}/work/upstream/sdk) + add_test(NAME retroarch_phase10ah_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10ah.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME phase10ai_mapping_model + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_phase10ai_mapping_model.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10ai_validation + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/validate_retroarch_phase10ai.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10ai_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10ai.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + chimera_gfx_add_external_evidence_test(NAME retroarch_phase10aj_validation + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/validate_retroarch_phase10aj.py + --root ${CMAKE_CURRENT_SOURCE_DIR} + --shsrv-v07-root ${CMAKE_CURRENT_SOURCE_DIR}/work/upstream/shsrv-v0.7 + --hardened-elfldr-root ${CMAKE_CURRENT_SOURCE_DIR}/../chimera-elfldr) + add_test(NAME retroarch_phase10aj_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10aj.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME phase10ak_hybrid_composition + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_phase10ak_hybrid_composition.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10ak_validation + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/validate_retroarch_phase10ak.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10ak_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10ak.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + chimera_gfx_add_external_evidence_test(NAME retroarch_phase10al_validation + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/validate_retroarch_phase10al.py + --root ${CMAKE_CURRENT_SOURCE_DIR} + --sdk-root ${CMAKE_CURRENT_SOURCE_DIR}/work/upstream/sdk) + add_test(NAME retroarch_phase10al_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10al.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME phase10am_bounded_copy_model + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_phase10am_bounded_copy_model.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10am_validation + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/validate_retroarch_phase10am.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10am_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10am.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + chimera_gfx_add_external_evidence_test(NAME retroarch_phase10an_validation + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/validate_retroarch_phase10an.py + --root ${CMAKE_CURRENT_SOURCE_DIR} + --hardened-elfldr-root ${CMAKE_CURRENT_SOURCE_DIR}/../chimera-elfldr) + add_test(NAME retroarch_phase10an_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10an.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME phase10ao_worker_supervisor_model + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_phase10ao_worker_supervisor_model.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10ao_validation + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/validate_retroarch_phase10ao.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10ao_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10ao.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + chimera_gfx_add_external_evidence_test(NAME retroarch_phase10ap_validation + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/validate_retroarch_phase10ap.py + --root ${CMAKE_CURRENT_SOURCE_DIR} + --sdk-root ${CMAKE_CURRENT_SOURCE_DIR}/work/upstream/sdk + --shsrv-root ${CMAKE_CURRENT_SOURCE_DIR}/work/upstream/shsrv-v0.7) + add_test(NAME retroarch_phase10ap_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10ap.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME phase10aq_worker_result_record + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_phase10aq_worker_result_record.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10aq_validation + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/validate_retroarch_phase10aq.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10aq_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10aq.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME phase10ar_result_channel_model + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_phase10ar_result_channel_model.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10ar_validation + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/validate_retroarch_phase10ar.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10ar_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10ar.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + chimera_gfx_add_external_evidence_test(NAME retroarch_phase10as_validation + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/validate_retroarch_phase10as.py + --root ${CMAKE_CURRENT_SOURCE_DIR} + --sdk-root ${CMAKE_CURRENT_SOURCE_DIR}/work/upstream/sdk + --shsrv-root ${CMAKE_CURRENT_SOURCE_DIR}/work/upstream/shsrv-v0.7) + add_test(NAME retroarch_phase10as_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10as.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME phase10at_fd_deadline_model + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_phase10at_fd_deadline_model.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10at_validation + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/validate_retroarch_phase10at.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10at_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10at.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + chimera_gfx_add_external_evidence_test(NAME retroarch_phase10au_validation + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/validate_retroarch_phase10au.py + --root ${CMAKE_CURRENT_SOURCE_DIR} + --sdk-root ${CMAKE_CURRENT_SOURCE_DIR}/work/upstream/sdk + --shsrv-root ${CMAKE_CURRENT_SOURCE_DIR}/work/upstream/shsrv-v0.7) + add_test(NAME retroarch_phase10au_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10au.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME phase10av_launch_context_canary + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_phase10av_launch_context_canary.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10av_validation + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/validate_retroarch_phase10av.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10av_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10av.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + chimera_gfx_add_external_evidence_test(NAME retroarch_phase10aw_validation + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/validate_retroarch_phase10aw.py + --root ${CMAKE_CURRENT_SOURCE_DIR} + --retroarch-root ${CMAKE_CURRENT_SOURCE_DIR}/../chimera-retroarch + --shsrv-root ${CMAKE_CURRENT_SOURCE_DIR}/work/upstream/shsrv-v0.7) + add_test(NAME retroarch_phase10aw_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10aw.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME phase10ax_canary_protocol_model + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_phase10ax_canary_protocol_model.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10ax_validation + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/validate_retroarch_phase10ax.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10ax_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10ax.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + chimera_gfx_add_external_evidence_test(NAME retroarch_phase10ay_validation + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/validate_retroarch_phase10ay.py + --root ${CMAKE_CURRENT_SOURCE_DIR} + --retroarch-root ${CMAKE_CURRENT_SOURCE_DIR}/../chimera-retroarch) + add_test(NAME retroarch_phase10ay_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10ay.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + chimera_gfx_add_external_evidence_test(NAME retroarch_phase10az_validation + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/validate_retroarch_phase10az.py + --root ${CMAKE_CURRENT_SOURCE_DIR} + --retroarch-root ${CMAKE_CURRENT_SOURCE_DIR}/../chimera-retroarch-phase10az) + add_test(NAME retroarch_phase10az_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10az.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + chimera_gfx_add_external_evidence_test(NAME retroarch_phase10ba_validation + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/validate_retroarch_phase10ba.py + --root ${CMAKE_CURRENT_SOURCE_DIR} + --retroarch-root ${CMAKE_CURRENT_SOURCE_DIR}/../chimera-retroarch-phase10az) + add_test(NAME retroarch_phase10ba_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10ba.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + chimera_gfx_add_external_evidence_test(NAME retroarch_phase10bb_validation + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/validate_retroarch_phase10bb.py + --root ${CMAKE_CURRENT_SOURCE_DIR} + --retroarch-root ${CMAKE_CURRENT_SOURCE_DIR}/../chimera-retroarch-phase10az) + add_test(NAME retroarch_phase10bb_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10bb.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10bc_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10bc.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + chimera_gfx_add_external_evidence_test(NAME retroarch_phase10bd_validation + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/validate_retroarch_phase10bd.py + --root ${CMAKE_CURRENT_SOURCE_DIR} + --retroarch-root ${CMAKE_CURRENT_SOURCE_DIR}/../chimera-retroarch-phase10az) + add_test(NAME retroarch_phase10bd_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10bd.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10be_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10be.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10bf_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10bf.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10bg_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10bg.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10bh_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10bh.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10bi_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10bi.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10bj_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10bj.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10bk_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10bk.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10bl_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10bl.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10bm_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10bm.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10bn_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10bn.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10bo_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10bo.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10bp_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10bp.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10bq_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10bq.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10br_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10br.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10bs_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10bs.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10bt_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10bt.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10bu_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10bu.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10bv_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10bv.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10bw_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10bw.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10bx_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10bx.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10by_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10by.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10bz_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10bz.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10ca_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10ca.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10cb_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10cb.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10cc_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10cc.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10cd_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10cd.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10ce_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10ce.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10cf_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10cf.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10cg_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10cg.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10ch_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10ch.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10ci_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10ci.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10cj_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10cj.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10ck_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10ck.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10cl_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10cl.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10cm_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10cm.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10cn_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10cn.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10co_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10co.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10cp_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10cp.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10cq_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10cq.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10cr_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10cr.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10cs_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10cs.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10ct_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10ct.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10cu_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10cu.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10cv_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10cv.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10cw_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10cw.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10cx_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10cx.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10cy_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10cy.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10cz_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10cz.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10db_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10db.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME phase10dc_bigapp_gate_contract + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_phase10dc_bigapp_gate_contract.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10dc_validation + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/validate_retroarch_phase10dc.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10dc_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10dc.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10dd_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10dd.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10de_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10de.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME phase10df_title_observer_contract + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_phase10df_title_observer_contract.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10df_validation + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/validate_retroarch_phase10df.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10df_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10df.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10dg_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10dg.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME phase10dh_snapshot_query + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_phase10dh_snapshot_query.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10di_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10di.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10dj_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10dj.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10dk_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10dk.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10dl_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10dl.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME phase10dm_snapshot_protocol + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_phase10dm_snapshot_protocol.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10dm_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10dm.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME phase10dn_snapshot_receiver + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_phase10dn_snapshot_receiver.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10dn_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10dn.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10dz_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10dz.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10ea_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10ea.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME retroarch_phase10eb_guardrails + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_retroarch_phase10eb.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME phase10do_one_shot_snapshot_runner + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_phase10do_one_shot_snapshot_runner.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME phase10dq_inventory_protocol + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_phase10dq_inventory_protocol.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME phase10dr_inventory_runner + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_phase10dr_inventory_runner.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME phase10ds_metadata_protocol + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_phase10ds_metadata_protocol.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME phase10dt_metadata_runner + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_phase10dt_metadata_runner.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME phase10dv_package_stat_runner + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_phase10dv_package_stat_runner.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME phase10dx_package_readback_runner + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_phase10dx_package_readback_runner.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME phase0_safety_audit + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/audit_phase0_safety.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME repository_secret_scan + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/scan_secrets.py + --root ${CMAKE_CURRENT_SOURCE_DIR}) + if(CHIMERA_GFX_CLANG_FORMAT) + add_test(NAME source_format + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tools/check_format.py + --root ${CMAKE_CURRENT_SOURCE_DIR} + --clang-format ${CHIMERA_GFX_CLANG_FORMAT}) + endif() +endif() + +install(TARGETS + chimera-gfx + chimera-gfx-retroarch-scaffold + chimera-gfx-sdl2-scaffold + EXPORT chimera-gfx-targets + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) +install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) +install(EXPORT chimera-gfx-targets + FILE chimera-gfx-targets.cmake + NAMESPACE chimera-gfx:: + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/chimera-gfx) + +configure_package_config_file( + cmake/chimera-gfx-config.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/chimera-gfx-config.cmake + INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/chimera-gfx) +write_basic_package_version_file( + ${CMAKE_CURRENT_BINARY_DIR}/chimera-gfx-config-version.cmake + COMPATIBILITY SameMinorVersion) +install(FILES + ${CMAKE_CURRENT_BINARY_DIR}/chimera-gfx-config.cmake + ${CMAKE_CURRENT_BINARY_DIR}/chimera-gfx-config-version.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/chimera-gfx) diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 0000000..7eb3411 --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,49 @@ +{ + "version": 6, + "cmakeMinimumRequired": { + "major": 3, + "minor": 21, + "patch": 0 + }, + "configurePresets": [ + { + "name": "host-debug", + "displayName": "Host debug build", + "generator": "Ninja", + "binaryDir": "${sourceDir}/build/host-debug", + "cacheVariables": { + "BUILD_TESTING": "ON", + "CHIMERA_GFX_REGISTER_EXTERNAL_EVIDENCE_VALIDATORS": "OFF", + "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" + } + }, + { + "name": "host-release", + "displayName": "Host release build", + "generator": "Ninja", + "binaryDir": "${sourceDir}/build/host-release", + "cacheVariables": { + "BUILD_TESTING": "ON", + "CHIMERA_GFX_REGISTER_EXTERNAL_EVIDENCE_VALIDATORS": "OFF", + "CMAKE_BUILD_TYPE": "Release" + } + } + ], + "buildPresets": [ + {"name": "host-debug", "configurePreset": "host-debug"}, + {"name": "host-release", "configurePreset": "host-release"} + ], + "testPresets": [ + { + "name": "host-debug", + "configurePreset": "host-debug", + "output": {"outputOnFailure": true} + }, + { + "name": "host-release", + "configurePreset": "host-release", + "output": {"outputOnFailure": true} + } + ] +} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..0013a13 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,46 @@ +# Contributing + +All contributions must preserve the safety boundary in `SAFETY.md`. + +## Workflow + +1. Start from an issue or ADR-sized question. +2. Update `RESEARCH.md` when a claim or source changes; label facts, + inferences, and unknowns. +3. Add or update host tests before adding a backend behavior. +4. Keep commits small: documentation/evidence, portable logic, and platform + behavior should be independently reviewable. +5. Run the host build, full CTest suite, generated-file and manifest checks, + safety audit, format/static analysis, and secret scan. +6. Do not add deployment commands or hardware execution to CI. +7. Never weaken `execution_eligible`, remove a permanent denylist entry, or + bypass the Phase-0.5 minimal-startup configure failure. + +## Source requirements + +Use primary, public, open-source technical sources. Do not contribute +proprietary SDK content, leaked or decrypted headers/binaries, exploit code, +DRM bypass, direct register/MMIO access, firmware patches, or clock/SMU/fan +control. ROMs, BIOS images, and copyrighted game content are outside scope. + +Guessed ABI declarations are not accepted. A symbol name is not a function +signature. When evidence is incomplete, record an unknown instead of adding a +plausible-looking definition. + +## Style and compatibility + +- C11, four-space indentation, no compiler extensions in the public API. +- Public structures begin with `struct_size`; API changes require tests. +- Destroy child handles before their context; cleanup failures must be visible. +- Use SPDX identifiers in new source files. +- Keep logs deterministic and free of addresses, credentials, user paths, and + content filenames. +- Warnings are errors in project-owned code. + +## Hardware changes + +A pull request can prepare a disabled experiment, but review or merge does not +authorize console execution. Each hardware test requires explicit approval in +the active task and must follow the template in `TEST_PLAN.md`. +Startup changes additionally require the exact loader-caller evidence listed +in `docs/runtime/minimal-crt-feasibility.md` before an ELF may be built. diff --git a/FIRMWARE_COMPATIBILITY.md b/FIRMWARE_COMPATIBILITY.md new file mode 100644 index 0000000..cb5b5c0 --- /dev/null +++ b/FIRMWARE_COMPATIBILITY.md @@ -0,0 +1,271 @@ +# Firmware compatibility + +Firmware `9.60` remains the only exact firmware in scope. Separately approved +Phase-1.0D runs have proven SDK CRT/main/notification execution on that device, +but they do not prove the Phase-0 capability probe, GNM, general VideoOut, +rendering, cleanup or compatibility of any other artifact. On 2026-07-22 the +exact Phase-1.0E result artifact received and consumed one single-use +authorization; all authorization fields are again false. + +| Firmware | Discovery build | Probe executed | Result | Evidence | Rendering | +|---|---:|---:|---|---|---:| +| _none_ | no | no | unknown | no hardware authorization | prohibited | +| `9.60` | yes, offline only | no | unknown; SDK CRT execution blocker | `docs/evidence/probe-9.60-offline-2026-07-17.md` | prohibited | +| `9.60` Phase 0.6 lifecycle | no artifact built | no | blocked: exact loader has unbounded/unknown effects | `docs/runtime/phase-0.6-loader-audit.md` | prohibited | +| `9.60` Phase 0.7 lifecycle | yes, offline only | no | hardened runtime ready for a separately approved installation; hardware behavior unproven | `docs/runtime/phase-0.7-hardening.md` | prohibited | +| `9.60` Phase 0.8 preflight | no new artifact | no | read-only on-device preflight blocked: collector side-effect freedom and current rollback state unproven | `docs/runtime/phase-0.8-read-only-preflight.md` | prohibited | +| `9.60` Phase 0.9A anti-brick | no target artifact | no | offline design only; filesystem atomicity, durability and independent recovery unproven | `docs/runtime/phase-0.9-installation-transaction-design.md` | prohibited | +| `9.60` Phase 0.9B observer | no target artifact | no | blocked before build: startup/exit ABI and non-persistent output channel unproven | `docs/runtime/phase-0.9b-bounded-observer-design.md` | prohibited | +| `9.60` Phase 0.9C feasibility | no target artifact | no | blocked: startup/exit/cleanup, current bounded output, firmware source two, and observation effects remain unproven | `docs/runtime/phase-0.9c-static-audit.md` | prohibited | +| `9.60` Phase 0.9D readback | no target artifact | no | blocked: existing stack has no PS5-to-host file response; live paths and recovery remain unproven | `docs/runtime/phase-0.9d-readback-feasibility.md` | prohibited | +| `9.60` Phase 0.9E-R provenance | no target artifact | no | local outer Y2JB backup does not match any current official asset; official source has no 9020 listener implementation | `docs/runtime/phase-0.9e-r-official-release-correlation.md` | prohibited | +| `9.60` Phase 0.9E-R2 inner provenance | no target artifact | no | local inner differs from the mandatory official and selected community inners; MediaFire maker/source and deployment remain unbound | `docs/runtime/phase-0.9e-r2-final-provenance-decision.md` | prohibited | +| `9.60` Phase 1.0D RUN A/B | yes, exact one-shot artifacts | yes, two separately authorized runs | C1 proves SDK CRT/main/notification; RUN-B stage remains unclassified because its notification was unreadable | `docs/retroarch/phase-1.0e-device-observations.md` | unproven | +| `9.60` Phase 1.0E result channel | yes, exact one-shot artifact | yes, one authorized run | inherited stdout returned valid D00-D02 with platform result `0`; remote EOF before D03; permission consumed | `docs/retroarch/phase-1.0e-inherited-result-channel.md` | unproven beyond D02 | +| `9.60` Phase 1.0F interval diagnostic | yes, offline exact artifact | no | I00-I14 source interval prepared and statically audited; no device authorization | `docs/retroarch/phase-1.0f-startup-interval.md` | unproven beyond D02 | +| `9.60` Phase 1.0G interval run | exact one-shot artifact | yes, consumed | valid through I03; deterministic no-argument/no-menu help exit before I04 | `docs/retroarch/phase-1.0g-device-result.md` | SDL/VideoOut unproven | +| `9.60` Phase 1.0H startup args | yes, offline exact artifact | no | scoped argc-2 correction built and statically audited; no device authorization | `docs/retroarch/phase-1.0h-startup-args.md` | unproven beyond I03 | +| `9.60` Phase 1.0H one-shot run | exact one-shot artifact | yes, consumed | I04 and SDL/VideoOut reached; buffers registered; first flip submit and SDL init returned `-1` | `docs/retroarch/phase-1.0h-device-result.md` | visible flip unproven | +| `9.60` Phase 1.0I postmortem | no new artifact | no new run | offline source/map/disassembly binds submit `(handle,0,1,0)`; exact E118 operation, submit errno and root cause remain unproven | `docs/retroarch/phase-1.0i-flip-and-write-analysis.md` | no new rendering evidence | +| `9.60` Phase 1.0J-1.0O diagnostics/results | exact offline artifacts and consumed one-shot runs where recorded | only separately authorized J/K/O attempts | first blocked write was `MKDIR`; write-free M reached VideoOut but first flip still returned `-1`; all permissions consumed | `docs/retroarch/` Phase-1.0J through O records | visible presentation and cleanup unproven | +| `9.60` Phase 1.0P-1.0S analysis | no new target artifact | no new run | public VideoOut semantics and a source-proven launch-context fix remain unavailable; hbldr/shsrv is only a source candidate | `docs/retroarch/` Phase-1.0P through S records | parameter or launcher change blocked | +| `9.60` Phase 1.0T-1.0AY offline route design | host-only models and source audits | no device request | deployed shsrv identity remains unknown; result-channel, launch-context, protocol and exact source-base work are offline only | `docs/retroarch/` Phase-1.0T through AY records | no firmware/rendering evidence | + +## Rules + +- A firmware row is added only for an exact, reproducible identifier. +- Build allowlisting is not runtime compatibility evidence or execution + permission. +- `symbol present` means only that lookup returned non-null on that observation. +- Presence does not establish signature, semantics, or rendering compatibility. +- Failure on an unlisted firmware is the expected fail-closed behavior. +- Compatibility does not transfer between firmware revisions. +- Every observation must reference the source commit, manifest hash, SDK lock, + approval, and redacted crash/probe log. + +The build cache variable `CHIMERA_GFX_PS5_ALLOWED_FIRMWARE` defaults to `NONE`. +Changing it is a deliberate build action, not a repository compatibility claim +and not permission to execute the artifact. + +The default capability-probe and every VideoOut review artifact embed `NONE`. +The 9.60 capability artifact has its own digest, manifest, and offline review. +It remains execution-blocked because the pinned SDK payload CRT writes kernel +process state before `main`. VideoOut is not allowlisted for 9.60 and also +remains blocked by the unproven bounded-wait requirement in +`docs/phase1/HARDWARE_TEST_PLAN.md`. + +Phase 0.5 did not create a firmware-9.60 startup artifact: the exact loader +caller, return, cleanup, crash, and pre-entry contract is unproven. The older +9.60 probe hash `4be1c17b4964f2b68c39b5145bc4af4619c32512d60269ecf5c39728b390fa63` +is permanently denylisted independent of future firmware or approval records. + +Phase 0.6 exactly matched the installed Payload Manager v0.3.1 and elfldr +v0.23, but the firmware identifier is still user-attested rather than +device-attested. The lifecycle remains blocked by unbounded ptrace/payload +runtime, unproven return and cleanup, incomplete restoration, absent +launch-time hash enforcement, and the manager's persistent upload path. +`__patch_init` is now a documented payload-process-local effect and is not by +itself the blocker. + +Phase 0.7 uses new private hardened elfldr and controlled Payload Manager +artifacts rather than reclassifying the old binaries. Exact firmware 9.60 is +explicitly confirmed by Jens but has not been queried from the device in this +task. Offline compiler, linker, test, disassembly, callgraph, and +reproducibility evidence supports +`READY_FOR_HARDENED_RUNTIME_DEPLOYMENT`. This is not an installed, transferred, +executed, or compatibility-tested state, and it authorizes no console action. + +Phase 0.8 preserves that static Phase-0.7 decision but blocks the operational +read-only preflight before any connection. The stock Payload Manager HTTP +interface mutates in-process state, and no alternative collector currently +proves freedom from atime, audit, cache, metadata, service, or logging side +effects. Firmware 9.60 remains user-attested rather than confirmed by two +current device sources. + +Phase 0.9A does not add firmware evidence. Its virtual host model cannot prove +PS5 filesystem identity, atomic switch, file or directory durability, +power-loss behavior, quiescence or independent recovery. Every such property +remains `UNPROVEN`, all device authorizations remain false, and no target +artifact is built. + +Phase 0.9B also adds no firmware evidence. The normal SDK startup path violates +the phase's kernelwrite-free rule, a freestanding return/exit cleanup contract +is unproven, and the hash-bound controlled loader route does not return payload +output. The observer build stops before source or artifact creation. Firmware +9.60 remains user-attested and runtime-unproven. + +Phase 0.9C identifies SDK `kernel_get_fw_version()` as source 1, but that +function reads a `libSceLibcInternal` process-parameter SDK field and has no +runtime observation in this phase. The SDK stub name +`sceKernelGetProsperoSystemSwVersion` has no accepted public signature, +semantics, side-effect contract, or firmware-9.60 evidence and is not source +2. The host result record can bind two exact values to a nonce, request ID and +artifact hash, but no target output implementation exists. Firmware agreement +therefore remains `BLOCKED_FIRMWARE_SOURCE_INCOMPLETE`. + +Phase 0.9D adds no firmware evidence and performs no device request. Its local +source audit finds only host-to-PS5 upload/launch and internal read paths, not a +binary-safe PS5-to-host component download. Configured, packaged, and +historical reference paths are not current live-path evidence. Firmware 9.60 +therefore remains user-attested and runtime behavior remains `UNPROVEN`. + +Phase 0.9E also adds no firmware evidence. The local Y2JB-named backup +candidate covers firmware 4.03 through 12.40 only by filename claim; its opaque +`SIECAF` content cannot be tied to the exact firmware-9.60 deployment or to an +auditable port-9020 listener. No bootstrap, restart, host-to-memory, output, +filesystem, autoload, crash, or reboot property was tested on hardware. +Firmware 9.60 remains user-attested and runtime behavior remains `UNPROVEN`. + +Phase 0.9E-R confirms only that official release 1.6 source permits Lapse's +version-selection path through firmware 10.01. Because the local outer backup +matches no current official asset, that source logic is not bound to the +deployed bytes and is not runtime compatibility evidence for firmware 9.60. + +Phase 0.9E-R2 adds no firmware evidence. Full host-side inner and SIECAF +comparisons are negative, and the MediaFire page exposes no source binding. +Even a positive structural or byte match would not prove current deployment. +Firmware 9.60 remains user-attested and runtime behavior remains `UNPROVEN`. + +Phase-1.0D RUN A changes one narrow firmware statement: the operator visibly +observed the exact C1 marker, proving that hardened elfldr reached the normal +SDK CRT, `main` and the notification ABI on the firmware-9.60 console for that +exact canary hash. RUN B produced one unreadable notification and therefore +does not identify a D-stage. Phase-1.0E RUN C adds exact artifact-bound evidence +for D00-D02 and the inherited result channel. D02 reported platform result `0`; +remote EOF occurred before D03. It proves neither SDL/VideoOut nor rendering, +terminal status, cleanup or safe exit. Every authorization is false. +Phase 1.0F adds no firmware evidence: its I00-I14 artifact exists only as an +offline build and has never been transferred, received from or executed on a +device. +Phase 1.0G adds exact artifact-bound firmware evidence through I03. Captured +ordinary stdout proves the build took RetroArch's no-argument/no-menu help path +and exited before I04. It adds no SDL, VideoOut, rendering, terminal cleanup or +safe-exit evidence. Its one-shot permission is consumed. +Phase 1.0H adds no firmware evidence. It is an offline, reproducible correction +for that source-bound argument defect and remains transfer- and +execution-ineligible. Its modeled I04 boundary and all later stages are +unproven on firmware 9.60. +The later exact H permission was consumed once. It proves I04, I13/I14, D03, +a positive D05 VideoOut handle and D06 buffer-registration result `0`. D07 and +D04 both returned `-1`; no successful visible flip, runloop, full cleanup or +safe exit is proven. No retry occurred. + +Phase 1.0M adds no firmware evidence. It removes the source-bound playlist +directory probe/`mkdir` from write-free builds and produces a reproducible, +statically audited artifact, but that new hash has never been transferred or +executed. I04, SDL, VideoOut, flip behavior, visible output and cleanup remain +unproven on firmware 9.60. + +Phase 1.0N adds no firmware evidence. It prepares only an inactive, +manifest-selected host runner for the unchanged Phase-1.0M hash. Fake-socket +tests and static hash binding do not prove runtime behavior on firmware 9.60; +all target, transfer, execution and reception authorizations remain false. + +The separately authorized Phase-1.0O attempt adds exact firmware-9.60 evidence +for the M hash: I04 and SDL video entry were reached, VideoOut opened, buffer +registration returned `0`, and the first flip submit returned `-1` with saved +errno `0`. No D13/write-firewall frame occurred. D12 reported runtime +failure/E104 and D04 reported SDL init `-1`. Visible presentation, successful +termination and cleanup remain unproven; the permission is consumed. + +Phase 1.0P adds no firmware evidence. Its offline source/map/disassembly audit +proves the exact first-submit tuple and failure site, but the local public +sources do not prove the argument semantics, opaque attributes, flip-master +or active-app requirements, return-code meaning, visible output or cleanup. +The VideoOut root cause remains unresolved and no new device action is +authorized. + +Phase 1.0Q adds no firmware evidence. Its public-source search establishes +that the current PS5 VideoOut declarations have one SDL lineage and no +independent public PS5 corroboration. PS4 analogues and host source scans do +not establish firmware-9.60 argument, layout, ownership or error semantics. + +Phase 1.0R adds no firmware evidence. Source proves only that SDL2main is a +lifecycle wrapper and that the tested artifact already hides the splash before +VideoOut open. It does not prove the firmware-9.60 active-app, flip-owner, +launcher, process, visibility or cleanup contract. The observed LNC log remains +a non-unique correlation, not a firmware interpretation or root cause. + +Phase 1.0S adds no firmware evidence. Official shsrv source proves that hbldr +creates a BigApp-based process context, but source cannot prove that the route +works on this firmware-9.60 device, grants flip ownership, or fixes the exact +submit failure. The deployed shsrv version, target fake-app state, visibility, +crash and cleanup behavior remain unobserved. + +Phase 1.0T adds no firmware evidence. It statically proves that the audited +shsrv source emits firmware, serial and telemetry on connection and that its +existing identity-related commands cannot return an exact deployed SHA-256. +No port-2323 connection occurred, so the deployed shsrv family, compile +metadata, device paths and all firmware-9.60 runtime effects remain unobserved. + +Phase 1.0U adds no firmware evidence. Its bounded host inventory found no +original shsrv target binary or receipt to hash. Official source and packaging +metadata do not establish what runs on firmware 9.60, and no device identity, +path, version, greeting or behavior was observed. + +Phase 1.0V adds no firmware evidence. Its Telnet and sanitization behavior is a +synthetic host model fed from stdin. It neither connects to nor observes the +firmware-9.60 device, and it does not prove the live greeting, prompt, +negotiation, timeout, disconnect, shell cleanup or deployed shsrv identity. + +Phase 1.0W adds no firmware evidence. Its remediated collector, session policy +and fake transport use synthetic host input and an explicit synthetic clock. +Source-bound port 2323 and exact firmware text `9.60` are policy constraints, +not proof of a listener, negotiation, device identity, deployed bytes or +runtime behavior. + +Phase 1.0X also adds no firmware evidence. Its receipt/output operations and +deadline/cleanup state are exercised only against injected fake adapters. The +official shsrv source describes a dynamic `PWD` prompt and automatic greeting, +but does not bind the deployed bytes or live Telnet stream on firmware 9.60. + +Phase 1.0Y adds source-history evidence only. Official tags v0.7-v0.8 use raw +framing and v0.9-v0.19 use `libtelnet`/NVT, but no evidence selects which +family, revision or modified binary runs on firmware 9.60. Synthetic framing +tests therefore do not establish live line endings, prompt completion, echo, +short-write behavior or disconnect cleanup. + +Phase 1.0Z adds no firmware evidence. Its one-batch formatter and deadline-only +accumulator run exclusively on synthetic host inputs. The tests show that the +contract tolerates modeled LF/CRLF output and rejects incomplete data; they do +not prove a listener, exact deployed shsrv bytes, live deadline behavior, +filesystem effects, cleanup or restart on firmware 9.60. + +Phase 1.0AA adds no firmware evidence. Its exact fake adapter, synthetic clock, +exclusive temporary host files and scripted events exercise only local model +composition. Passing receipt/send/deadline/close tests cannot establish a live +listener, OS timeout behavior, target identity, shell cleanup or any behavior +on firmware 9.60. + +Phase 1.0AB adds no firmware evidence. Local Windows Python runtime identities +and synthetic nonblocking traces describe only a possible host implementation. +They do not observe port state, packet delivery, shsrv cleanup or any behavior +on firmware 9.60. + +Phase 1.0AC adds no firmware evidence. Its dormant adapter accepts only exact +built-in fake syscall steps and a synthetic clock. Passing its lifecycle and +cleanup cases does not establish a listener, deployed shsrv identity, network +timing, remote cleanup, launch context or any firmware-9.60 behavior. + +Phase 1.0AU adds no firmware evidence. Exact official headers establish source +signatures for pipe, descriptor inheritance, nonblocking reads, monotonic time, +termination and reap. They do not establish their runtime behavior on firmware +9.60, and current official shsrv does not contain the bounded composition. + +Phase 1.0AV adds no firmware evidence. Its two synthetic arms define how a +future exact-payload comparison would isolate launcher identity, but neither +arm has target source, an artifact, approval or device result. A modeled return +difference cannot prove a visible flip or firmware behavior. + +Phase 1.0AW adds no firmware evidence. It proves only that official v0.7 source +passes raw stdout into the replaced BigApp and identifies the source changes a +future canary would need. No deployed identity, bounded launcher, target bytes +or firmware-9.60 result exists. + +Phase 1.0AX adds no firmware evidence. Its valid D07/D04/D14 traces are +synthetic bytes and its cleanup predicate is a host reference. No target source, +artifact, device result or visible output implements that reference. + +Phase 1.0AY adds no firmware evidence. Selecting an exact private Git commit +establishes only source lineage; it creates no target source delta, artifact or +firmware-9.60 observation. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..c154efd --- /dev/null +++ b/LICENSE @@ -0,0 +1,676 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. + + diff --git a/PUBLIC-SOURCE-MANIFEST.sha256 b/PUBLIC-SOURCE-MANIFEST.sha256 new file mode 100644 index 0000000..7095056 --- /dev/null +++ b/PUBLIC-SOURCE-MANIFEST.sha256 @@ -0,0 +1,823 @@ +e5581d3995f5b65624acc550d0af4a76b6dca6d12e5d3dc7765346ba9adc26e5 .clang-format +703c1765cfa88b9ae775aea2218a05beba963f2cfb9f134717c18a4230dbf703 .clang-tidy +984d4bba9c66a83e3ea9fa671d889781de0c9964f0a94e803909c9288b90be46 .dockerignore +18ed8d2c4f47423be504385dc162054e9982364fcd25d6e8cb32a63b417dcbe0 .editorconfig +40b049570098cbfa24b48c5f69436f250ee25a08370153104c8c2527763f84a2 .gitattributes +02b95d59e72562ea17409a0e3644cb76ea47662ccdc8f5ac284574654945accd .gitea/workflows/ci.yml +5019f29fe156ad1e1672198bcc2bb83a86947778a172a80d229b7dd6f092b976 .gitea/workflows/managed-validation.yml +1c1d1dbd105b9ee07e1d64d5a31f7ab905a2b9ff06acde2d3fb35bf78af4e95a .gitignore +ea0dfd53e693b39df4588542f03733db05d95cb34222922dddf6bd7612a726cf .gitleaks.toml +7b479cff9b881cb7989450869e96b6b746c6049b9eac1a1c4ae1f0afac45ad69 ARCHITECTURE.md +82bdaa28bba2f2053b768c8e2737c1cb93947c9c4bf28a3e96c06c9d321d9789 CMakeLists.txt +93b4eab27651241c4b5ffddac717dc151339955e7b5d2c736e7ac0c1ed1e0a9a CMakePresets.json +aa426f28c3d3db8991b51c16e36c804daa0fe10038d38dd034bbc8ac5608b799 CONTRIBUTING.md +9e008af5f21e61aeb05635f4bad8816c0c5f571324e7751c686ae1436c83f3de FIRMWARE_COMPATIBILITY.md +7fe14716ed31cc71fc3bf9af293c368d464a853a70ea86e9995388f6b9e929e5 LICENSE +af26a708c6905da54d831f76f3c8006340fe8d4fd616ae7848954211169c4768 README.md +81e505bff73602c7b5b9cbb4bf9946591277a5896bb2fd68e4b27a8bc787afc9 RESEARCH.md +99374945ca60263212a1b5669cc7e229882a2980a076a5708273a66358c57de9 ROADMAP.md +7446e0bad0ade04fbb2e2fa71909aaab77f895866d3c1dfa6185ccb3c64efe86 SAFETY.md +40d67873455d191ae0d1f9805dabedf20622d100ecfc06ca84aa987c3e2afa92 SECURITY.md +3d2dfc653e341b4b512a89dbb38231631c51872add4cb027f0aeee7d2b4ab06b TEST_PLAN.md +7a7e8823edd5d7f7ded8c162fea18e47aa8a06696a7cfb8290fa08a5570abb07 THIRD_PARTY_NOTICES.md +a70eae3c717b69ecd2a1014c99432faafd81600616fc09c02608d5af849205f7 adapters/retroarch/README.md +de15249d0bc354d843d4ad7f59a6cb122230b8fe128cd6d9e74b81a7141427d6 adapters/retroarch/retroarch_adapter.c +5ed138d0506c85973f9c8d56d8822b93de839a39deecd7fc45e35df4ab747862 adapters/sdl2/README.md +e2a540bb61bfed8665bd7dd7a2bf2c39ff539ecc95d496eb6a9295fd662d987a adapters/sdl2/sdl2_adapter.c +a5ca7ad5adaa14b3fc808da1909a4318320ac9dbe113eb8bd30b7cce9f4fdf4f cmake/README.md +61bf26f2631737849491e48f818febc0fe2a7a3e756dc3d7dbcd1e1b4d60eac5 cmake/chimera-gfx-config.cmake.in +adf50105994b1d95ac1030d23ead8a20952818f703fe5216587f78e6871b9aab docs/DECISIONS.md +1d4a884fca5da62429fbd95a44414c830ec48a35c30a1416ee339e214431d131 docs/PUBLICATION_READINESS.md +a92e6eed8e7ccd73dfdd26a258377a76c52f32b2cbd4d64fbc506395c3b333b6 docs/adr/0001-standalone-repository.md +111c1890d8570d3ea64e4c716aabcf0a5e5d2745228955744bffce47c253068c docs/adr/0002-export-names-are-not-abi.md +f97ba38da2c4bfd3cf19853195110ac48ad59728e241bac47d8c21e00d1d3c26 docs/adr/0003-fail-closed-probe.md +46a9971ce8378172ad2d0beb8e8571f46f0c282052c242ee55fc072dfe177088 docs/adr/0004-license.md +8ec477bd10e5f6c72fa417087d891b5666917f9a6873397313e7a880d4de040b docs/adr/0005-versioned-resource-lifecycle.md +3722750cb3899dcda3589008b94449b12bfd4d4232200e366da779b4f5560e86 docs/adr/0006-phase1-sdl-videoout-boundary.md +585c14113274608d6ab578a6fd5a775bd750c4e8a947ec0174f17d75399bf20c docs/adr/0007-artifact-provenance.md +b3494b872144b9c636bb665be84e8dc3d7c1819ffd597b79c72a5c931cd5656e docs/adr/0008-firmware-9.60-probe-build.md +f7b61c292e51af9632fff385f7ebcc75a5384cf16e7b44fb24066b7cb97b59cb docs/adr/0009-block-unproven-minimal-startup.md +06e2248addc9d66ef5d4705ec518e6f1faa40b4633a51468cce7edd740ca4ccd docs/adr/0010-controlled-runtime-effects.md +299ce9147cab884c50038372fc913b9c603bc5cb99ca0ad52b9ca3e4745f5ed7 docs/adr/0011-phase07-hardened-controlled-runtime.md +aec275591d56a564cd77b022c73246456b42be3aef2def6c1fa702dd89111a97 docs/adr/0012-phase10j-first-frame-identity.md +e18abf9b7cec2d17c9716092e63dac235579b4bd8c80c04ca77b0588981b9f72 docs/approvals/phase-0.8-bounded-observation-template.md +cad136ac07edeac7cf85a7c48e1b6558683ea1b9a9110ebeb8dd79cc8dc419c3 docs/approvals/phase-0.9-backup-creation-template.md +9793af46530f33a3b1fc6f713ddd06a0f4f8b913a442a4a22df2fe1dc4d45f3f docs/approvals/phase-0.9-observation-template.md +b9376da3d539226930785c2ff5412fdce591e809e6d710dd4177f62c44e5ec76 docs/approvals/phase-0.9-one-shot-execution-template.md +b4987f172b56241495577119d38a5c69b7a38a715f61a9973fbcf2beb8b5fc62 docs/approvals/phase-0.9-staging-template.md +ea4e41d138952780160271eba814c7c4ba9b4bfe4c5d15982d4fe582304655bb docs/approvals/phase-0.9-switch-template.md +65b781814e501aea5a78276db658b820d130f643d84e38aee1792c56ef9c52ea docs/approvals/phase-0.9b-observer-execution-template.md +530cd9c8c62bff167c942d4195cedc12d81f948c8e72885bbe8e50105ff76fd9 docs/approvals/phase-0.9e-r-y2jb-deployed-use-attestation.md +5504229082cbe743fcc09a028a36fd28efc4a5e7aeb56a77c79a5ba34244ee9c docs/approvals/phase-1.0aa-offline-fake-adapter.md +dc1b8f657bd949f3d072e00e715229332438ad142f73d4f55da5f02402e3fa8e docs/approvals/phase-1.0ab-live-adapter-feasibility.md +2d4b79b11fa0c1cd4fde39fc82f4ee8ceb99daadab95a2f9dd2f98e8f704fb0a docs/approvals/phase-1.0ac-dormant-adapter.md +104beb0f59d4130e0faddd43c8ecce01509936839df2d891aa992266eba7b1e0 docs/approvals/phase-1.0b-device-smoke-template.md +d825666a600c0462f27cdaf558c5bce8080aba0c8e0bd8d18b54e5b8dab4fb72 docs/approvals/phase-1.0cz-launch-canary-template.md +4961884183ca8ff1ace22d61ab65e831fcb4127d1655a6178f936d83ff7bfc19 docs/approvals/phase-1.0do-snapshot-runner-template.json +15551279dfc71a9dbdce555d999f7370cb9f401869acdcd752a60787b092ed1a docs/approvals/phase-1.0dr-inventory-runner-template.json +1f5655faf888a1ababc5a5eb0e46e7fb13b21993f8d68b151114c482ee01ebf8 docs/approvals/phase-1.0dt-metadata-runner-template.json +1f085ed324802901cf5cb6663bc6d3b4a2a6f65dd350d694e9a525d1c2b7e30b docs/approvals/phase-1.0e-one-shot-result-test.md +66bbe6c71133bd050a3d87ef59e0e452a6c580e9986fe121043844e751940304 docs/approvals/phase-1.0f-device-test-template.md +c83f17317532a07e077f0e4fe81356252d0e84e73367abbd9699125f2c558b13 docs/approvals/phase-1.0g-one-shot-runner-template.md +b4d5306ac2630fa893135ccbf3990e30df219cbec6f6a47c4579c8e1a1790a7a docs/approvals/phase-1.0h-device-test-template.md +b53d609fc2d3ae3bf87c466b585416600d5b264dc98afabafb398d0e766f8638 docs/approvals/phase-1.0k-write-diag-one-shot-template.md +a149dbff78367bab20f3c7dd78a18ad922742ffb5d6e1d7285362052c2d325cd docs/approvals/phase-1.0n-write-free-one-shot-template.md +16a8757bbd396d0b03b6e1c898c0a74f9fae795da9a1fc622e01d1dd2f849ed5 docs/approvals/phase-1.0t-shsrv-metadata-collection.md +f750e8732a785f707b23b47f93664a4994aae48ca091f7e037f36fb788971266 docs/approvals/phase-1.0v-shsrv-collector.md +d5abc3eaf0391d1999396f90962b6e7847eed9b56ac37c6bd7ff5b0be92c8716 docs/approvals/phase-1.0w-shsrv-client.md +03df44f177c5291aff622c89f2ee771ec53377e9b021d3e79b0d33efdc52cf91 docs/approvals/phase-1.0x-inactive-transport.md +5c035120900fe234a2fb53bf291cb9788e2a1de033a2ac33acb2b5e882f6bdef docs/approvals/phase-1.0y-shsrv-framing.md +c0643ffb2f24f0ce43ab0df4556e1a7704bf60c8141f789101d9561bbacbce5d docs/approvals/phase-1.0z-passive-batch.md +5ad3d388f71f9ac2aeb2f39e4600d692f922c9c85700862931c0ad46ee29c3ee docs/approvals/phase07-hardened-installation-request.md +b74189c8abaf9ca200e9b67329ecac55040af2779b433c4680d555c6aea48d69 docs/approvals/phase07-lifecycle-transfer-execution-request.md +232016be1ce7d582b5249b4506e004199539602226bd0015521c6463efa2ae57 docs/approvals/probe-9.60-transfer-execution.md +f300406bfe357f0e5cbf852d46248a1e7a54ece4cdff484b078191ae9fe15a9d docs/evidence/external-evidence-integrity-2026-07-29.md +daa95cec45040afdb6a0c69bf04574f7ded0f76894e506d7d54355ca3773439f docs/evidence/phase0-build-2026-07-16.md +9304cc107fe5423d72fc2615174d0a8d8ff1106c3d52b527d81b732ad6edd51d docs/evidence/phase0-build-2026-07-17.md +fa4a80353b19c6be947e98c2143fed05779fa47f2f70d1599bb82901e17e1204 docs/evidence/phase0.5-startup-offline-2026-07-17.md +5d4c821491c12d717237c97b99c582251e2ecce80b629aa30c88344bb31d6445 docs/evidence/phase07-offline-build-2026-07-17.md +2bc642945a4cda8cf4f782f94afd49849e27f5785327ae37aa010cd4d4180ed5 docs/evidence/probe-9.60-offline-2026-07-17.md +bd043f7e480f7c83a60bc73e2a58cb03a760fa2e543bba7d99c340661aa7d2f4 docs/phase1/APPROVAL_PACKAGE.md +e23066ddab4fef7d682c374b9639dfd1f1cf241f7b45f100cc7fbde21c0fef75 docs/phase1/FIRMWARE_ABI_CHECKLIST.md +baa8142ff42009966a0ce7f4ca77f819c680a51a9b3083346abedc5892bf63af docs/phase1/HARDWARE_TEST_PLAN.md +3501dbdddbfc9f1818f95f00b92275aed21cab8b56aee5e65daa3e71405eb1f7 docs/phase1/README.md +7aa7f5637d5594a7bc23fb92dfba17185f4269fc8ecf34d0691829a6f476cb33 docs/phase1/VIDEOOUT_CLEAR_EXPERIMENT.md +ef2c2a2356dce0409ee2a3b78c683e57f0c064e3f6de10ec271ae44f92682077 docs/retroarch/phase-1.0a-build-results.md +c6ba1b2db5397234d1fe0dcaf74a19a8d62db75f811e61cda3e0d876bdd89553 docs/retroarch/phase-1.0a-driver-status.md +0acb3a025bb10bae6da75be6150d3c7547792e5d83fd8544a73e5a1d5ab68b0d docs/retroarch/phase-1.0a-next-device-smoke-test.md +069b60eb414e144b1e4ca03ca814656afc94e85afe87163710b874e6df88aedc docs/retroarch/phase-1.0a-pacbrew-sdl-analysis.md +e95686b41616bab34cccfd37969385c14ab36ccff17fae17b604a48d8a5e8193 docs/retroarch/phase-1.0a-port-plan.md +15ebed8c48c1919cce4a7ede061a6a5bf696bf2f6ff765fcbf04f0b32e4b8803 docs/retroarch/phase-1.0a-ps4-reference-delta.md +9ba81f2eb4b43d51606b33af55f523fb28b791d157c88a0efb4bac127bebb97e docs/retroarch/phase-1.0a-upstream-analysis.md +12290c7062134e524f19d1666505e95ff13ace7000b3fe22e52be54af5eb9e1b docs/retroarch/phase-1.0aa-offline-fake-adapter-integration.md +39f0bab00fb7055ac0e5f7fb0ca59c33e2b028db04c11ed38dbfe7675fd37c37 docs/retroarch/phase-1.0ab-offline-live-adapter-feasibility.md +9c87ff0e5d4dc6ec5961000d7fe60a749d6da8b337fb959a2597041430fdc8f7 docs/retroarch/phase-1.0ab-timeout-and-cleanup-contract.md +d18132c1dcb5f4ec21d5cdd8b2bedf81aefec1edf15877a4b6c9f7de4470b66b docs/retroarch/phase-1.0ac-fake-syscall-contract.md +bb3c1b21c2548f362befc20e09465084041b218151ce1410f1c543fc7b99edd4 docs/retroarch/phase-1.0ac-offline-dormant-adapter.md +6cc0b2dc8140e6860929e543df820d4d9905c8720a366b70cd1a008f7548d93e docs/retroarch/phase-1.0ad-inactive-activation-contract.md +8dd8e89a4f5850ec9fe02e90562066e0e639b78ab74a4b1b7d44947ae1db989b docs/retroarch/phase-1.0ae-minimal-bigapp-launcher-architecture.md +cf78a730c1a9bb41167e5b955d9bffa0bbe6e301cd7f4fd717498e8e294386a7 docs/retroarch/phase-1.0af-offline-bigapp-lifecycle-model.md +8c0a964c0d1343cd62c245d218b0f1dd69c1f723bd803421d019b963063815a7 docs/retroarch/phase-1.0ag-bounded-elf-contract.md +49019682cb278e7d9ed1a7ecf08fcec4863a46afbd49edeb406c6c59de50fc87 docs/retroarch/phase-1.0ah-dynamic-relocation-contract.md +a0547c34101760e07d670ee53ba4fbf022320ca63ab12e428ac1a6d6d3cd3a2e docs/retroarch/phase-1.0ai-offline-mapping-transaction.md +4f7d945127800e985ae775c5ecb2970f174bb92653eb60998179a313b31afe8d docs/retroarch/phase-1.0aj-offline-primitive-audit.md +0057f9dafe295b165b5b3bb5998ffc8310071de1b43ec6d5b3d5ebe007b068a8 docs/retroarch/phase-1.0ak-offline-hybrid-composition.md +ffbc58d32896f80c4de173f5a32d2d7a53086dda74bf8f74b69ff1086bbd2789 docs/retroarch/phase-1.0al-offline-mdbg-copy-audit.md +0ee16aa477b54b3e257bf409a7f89ddd3c416651b151c2b5a571899a446a71f3 docs/retroarch/phase-1.0am-offline-bounded-copy-model.md +fd29ce0799da7c48bbf6a9e1ef9bb94a25b79b7c4e12246029c33fdfe7e90c8b docs/retroarch/phase-1.0an-offline-service-lifecycle-audit.md +847930203f2f7bbc51cd3de5cef6e56ac9c95714a7017c811c2bd8b938540c0b docs/retroarch/phase-1.0ao-offline-worker-supervisor-model.md +2c4d18b6ae3115f25a92603caf9544194b23f50782252cc1313f3479ffe7c0b2 docs/retroarch/phase-1.0ap-offline-worker-feasibility-audit.md +2ec07965bcb32494ea0d4fc8068f2ad2411a683e7470aeec30ed0e0c2dc63fad docs/retroarch/phase-1.0aq-offline-worker-result-record.md +ffa1485f0244ccac112a282b7d40d4fd3d21a67e4782b0bdac34b9eda839c627 docs/retroarch/phase-1.0ar-offline-result-channel-model.md +89ed6e00c2df946ba2beb86f9eab103ae3e79404b8e45772f5c66585def03fb0 docs/retroarch/phase-1.0as-offline-channel-primitive-audit.md +787ffc3825a74ff3b874189a325adf54891bb501d60e6635ea711bf4aba049cf docs/retroarch/phase-1.0at-offline-fd-deadline-model.md +a61d86a2950e16cdddd7211d668e08d684a95cb43c3bbd7a2af9726328c77ab0 docs/retroarch/phase-1.0au-live-channel-feasibility.md +5007989ce1cc715f7541ca6b0c12b45e9c71b2527ad0bba7dfd9654fe5e002b0 docs/retroarch/phase-1.0av-launch-context-canary-contract.md +d26f2157a1998009ae3b0d655432e7e0f641654cf4ca2e0e2a855ddd2bd66323 docs/retroarch/phase-1.0aw-canary-source-delta-audit.md +490f0d4d40f5b041d495fa443b46f80c5393fbb0a635b46cdf5203b8eea92edc docs/retroarch/phase-1.0ax-canary-protocol-model.md +38c7c606ea0eadc4f75162dad2d7e8faf55d21bf6e9056589f71eb0f3f2645c0 docs/retroarch/phase-1.0ay-target-source-base.md +09ec776a2b20c62d1f16b0b9e774d66c86659c774881294a6ef9d28f339fb4d9 docs/retroarch/phase-1.0az-host-av-source.md +5015aca6628dc0b93742fccee9a8173cb8b16718eec8f957e9fe851769b48c29 docs/retroarch/phase-1.0b-device-risk-assessment.md +b74290d32402b8b57896dc3488a4cc8a76d39f4b4cd7e41daf812f8d86aa08c9 docs/retroarch/phase-1.0b-linker-and-wx-analysis.md +34370caa210e18fee5676def8f80dcb2ce56b7d27d747b40d808828327ac825d docs/retroarch/phase-1.0b-persistent-write-audit.md +e31ca5aed239b90e5a295bdae7239405903f779ec75b8160f2efdf94f1f6167c docs/retroarch/phase-1.0b-proposed-one-shot-test.md +b5505b0e076ae6eb643f734dca8b5b0e2a42eef33bdeb4f5c3ad77baa8a80d6e docs/retroarch/phase-1.0b-runtime-and-exit-contract.md +1c64c57d5eea338f0942cb87d80b8ce32babf32d61193c06bca432265b167b37 docs/retroarch/phase-1.0b-smoke-candidate-design.md +529d0179911ab516ca6c339bd8a3537049033666de4083205bf341eddb0c0bf2 docs/retroarch/phase-1.0ba-target-profile-callsite-audit.md +a9f485ff7a17be6147ae306c0f835aaa5e7b3af1ea8016257d1cde163a0292b1 docs/retroarch/phase-1.0bb-source-only-launch-canary-profile.md +d4848c343b801efcef4e559fe4e3bb1d6fa19c3f676d52f08e96b232d4bdd317 docs/retroarch/phase-1.0bc-cross-build-prerequisite-audit.md +937c129f95f2098f80514c3192748da91f7192cb1b32f81abc36d4de88541d74 docs/retroarch/phase-1.0bd-dormant-sdl-materializer-policy.md +0bfd1956813b0762d2cf58de7c60fb8bbd88fc77437ceaac2c10421bf023830c docs/retroarch/phase-1.0be-fake-only-sdl-materializer.md +fb8006ebe45ad37259bd2cfe15448a818dd128c716b1c5b61e4613c4f03825ec docs/retroarch/phase-1.0bf-live-sdl-adapter-boundary-audit.md +cdd878630ed3efa7fdb23127ce82d11f94e8a32bf8cadd092d1fe3f912f47a65 docs/retroarch/phase-1.0bg-dormant-sdl-request-compiler.md +b6b1f6f8fd0a4ce86123e0170b4ff7000a895557c81e2690209b5462c25c1bc5 docs/retroarch/phase-1.0bh-bounded-sdl-executor.md +859304a469d44fa939db56a57e761e4975e27aa0d61960858ebd2b668d17b030 docs/retroarch/phase-1.0bi-real-facade-and-tool-install-audit.md +8c7a101f66e9d56cee4f09a67e3e983a1600bbfcc2d4dc2ee5a7b2357d99dae6 docs/retroarch/phase-1.0bj-exact-host-tool-install-result.md +49bd69bf84aa1404a3fef08c49e0047e675bca2a0727a5308ae6b64596fd6144 docs/retroarch/phase-1.0bk-bounded-real-facade-host-fixture-gate.md +e8c3caf90b02c8fb41074f7af791578e73f83d2090e4ecf67eafc7cfffd1c1ae docs/retroarch/phase-1.0bl-fixture-import-failure-and-new-gate.md +c48641c52dc563d4a5f1a45e3f87d4c6187f8d96ce33c1d9ce894cc9ef5fadd0 docs/retroarch/phase-1.0bm-real-facade-fixture-result.md +7215afaa02718ee0ec81f08110617b4ec921ba9d3a6919569eed471f82113f2b docs/retroarch/phase-1.0bn-materializer-preflight-patch-chain-audit.md +f7eb3bff8eae70b0718d595ea7de32ead6741b856914d757b0b5ae3358e36b9e docs/retroarch/phase-1.0bo-patch-chain-remediation-result.md +6e95488acc2c0e0253ed86179d40cc1cad3aace9ca8093c2022b08d5eb982fa0 docs/retroarch/phase-1.0bp-one-shot-sdl-materialization-gate.md +d12a1eb9dd845157c6f354bb7946b60b99b00d7ae994598c3a0cb6ba752db760 docs/retroarch/phase-1.0bq-inline-invocation-failure.md +2a9bd55f9faf43680b6db3cd59dc73f4468e0e284b19c2661d667ba8fac58fff docs/retroarch/phase-1.0br-script-entrypoint-one-shot-gate.md +dc75f6a87bf09dbda2ba7650818d314e3dce7f04624aa45f4768af44dd0d9157 docs/retroarch/phase-1.0bs-read-only-status-timeout.md +9da8d096f4a3b31ded83faeb7bf8243ff79090b9f72072eebedecc67a15b165d docs/retroarch/phase-1.0bt-corrected-timeout-one-shot-gate.md +01e6f8dbac3944d9f523e9111ac516babd842fd82cf81bf4ce6faf22b81df225 docs/retroarch/phase-1.0bu-wsl-git-status-timeout-and-windows-git-fixture-gate.md +74d7f6bff7dc293b263e06c8e333997d52c51285840c7173892e9cbb6f91593e docs/retroarch/phase-1.0bv-windows-git-fixture-result-and-request-correction-gate.md +6fa0c47c25b70a1c68339244065a5029edff6749363f00438e08ba89b9a4a9ed docs/retroarch/phase-1.0bw-corrected-sdl-materializer-one-shot-gate.md +2f45aa803c78eeaec9659ac732527280071a482c6e527c487d82b5bd9236523f docs/retroarch/phase-1.0bx-changed-files-timeout-and-read-only-fixture-gate.md +9238b84bf6008256759d17521bbb474a5219fd8f147e6c14011eac2479884b90 docs/retroarch/phase-1.0by-windows-git-stage-fixture-failure-and-file-hash-gate.md +84ba9cdaf4399a921f6e99615d830c857d2d6edb7446cd2bb928ac645baa14ec docs/retroarch/phase-1.0bz-exact-sdl-stage-cleanup-gate.md +49ae3623fef1f9d6291141262cfd536c5da6100ee586a365f32b5f2788190e96 docs/retroarch/phase-1.0c-device-smoke-result.md +a65f1c3962e121812786dee0ffbb8075b2e1a4762ec7860f4869fea07be3fdb3 docs/retroarch/phase-1.0ca-cleanup-result-and-hash-verifier-correction-gate.md +de38d557ec8d27d36060c4948e8cdf3a3e5783481fbdbd51d1c9654262d9def7 docs/retroarch/phase-1.0cb-hash-verified-sdl-materializer-one-shot-gate.md +c3906fccc007538c1af16c31cec923c660eb9252bd0bc06cca6622540b089fe9 docs/retroarch/phase-1.0cc-cmake-timeout-and-exact-output-cleanup-gate.md +7bc8e9be7d5bcf0ae86ecc81f3ea44d1b7a381b98e5b51baad6f61e0f12e18ee docs/retroarch/phase-1.0cd-cleanup-result-and-configure-timeout-correction-gate.md +86c4612dac26856eae1e6e7003ea62f73eb5e91b822345c72d688f6c023af801 docs/retroarch/phase-1.0ce-extended-configure-sdl-materializer-one-shot-gate.md +76be79071f727f80598d31991582f4a0f0d452bdc0f61bfbeabea86c8fb8011e docs/retroarch/phase-1.0cf-repeated-configure-timeout-and-cleanup-gate.md +354d81acc6fe933e9173d71003fb3ed3db3b211595c37b91220da4ba29b6051a docs/retroarch/phase-1.0cg-native-wsl-sdl-materializer-design-gate.md +75e84bfe41b6cbfc49bfb0a60e333895575b5939ec3a3ef5251bc8ad9de219c6 docs/retroarch/phase-1.0ch-native-wsl-sdl-materializer-one-shot-gate.md +22494e511d8b3286d494d60b1503c511ef2c7175ca229f7866b05f1a06e773be docs/retroarch/phase-1.0ci-native-sdk-bottleneck-and-cleanup-gate.md +5771c2c19584ea1701625dcfeafa2d0cba72c9dd26a018eebc05fb6f03f91565 docs/retroarch/phase-1.0cj-native-sdk-stage-design-gate.md +f58e9aee796151c1a67e21a472f553eae33c3e72a05a2c7398a56f7e89d1f5a6 docs/retroarch/phase-1.0ck-native-sdk-sdl-materializer-one-shot-gate.md +a0fc6e9c3ca8966f3ac304abc769345d33c1c4aa59b386f70948caf728cd8cf9 docs/retroarch/phase-1.0cl-native-sdk-configure-progress-and-cleanup-gate.md +516ed0355034c58c8bf595e6535fac9a53c6517c1c9458db6ba2a00fbbe2c5af docs/retroarch/phase-1.0cm-measured-configure-timeout-correction-gate.md +bc30af8fa3604448bf46728f72e79301e4ed4583910a64b64d17e3797e800b94 docs/retroarch/phase-1.0cn-measured-native-sdl-materializer-one-shot-gate.md +e6df2ee057620f4629d4ffcf8294a878965582a54b5c3bd547e7fb92b3ea91ad docs/retroarch/phase-1.0co-configure-success-build-output-limit-cleanup-gate.md +d35b77688e0284df00e40a031c64288abdba7ed8c286d004fd7bc20592b8cb99 docs/retroarch/phase-1.0cp-build-output-limit-correction-gate.md +8e4f8d5c0136133d7fffa9a2cc080ed53a3367bfdfc3b4820ef151bff346ef65 docs/retroarch/phase-1.0cq-full-output-native-sdl-one-shot-gate.md +3ffc2d6cf4014f81a049d1da9dc7b458e57edacf5a8253348e00818a1bd8f44b docs/retroarch/phase-1.0cr-audited-sdl-archive-export-gate.md +3351d4275da1fc31e0059bef554b9909bceca8efc019f96ff52cb96c0ce22579 docs/retroarch/phase-1.0cs-export-parent-correction-gate.md +ada914d4117acf181544477887206466a8d5bb04c59cbe5aff001968c2d2b835 docs/retroarch/phase-1.0ct-sdl-archive-export-result.md +af8cd79c521f0fce620a9eb761ebc95bd2f3f4bfea565a3b32e6e6e919c12bcb docs/retroarch/phase-1.0cu-exact-sdl-header-export-gate.md +0252173b805880f4fd864b73b79284fd89fc37f9e19e356ceac7ff64dc1c9ee9 docs/retroarch/phase-1.0cv-launch-canary-offline-build-gate.md +13149aed840b070e59cec1deffc8f3f43d0dda0fe4f75e5598807016b75d64aa docs/retroarch/phase-1.0cw-launch-canary-artifact-audit.md +017dfb7d36094ea159d894aa9c14567d1e9a2db87d7bfb6279144d72b2ba70b9 docs/retroarch/phase-1.0cx-isolated-reproducibility-build-gate.md +e5c90154336da5afc89f54ed804943747e8f715e3c394c9a3a475f2bdeb103c9 docs/retroarch/phase-1.0cy-launch-canary-reproducibility-result.md +2bf57f963a7f1281d55755c5dd5293140347d2cd0a22b446607d29a47d70d50d docs/retroarch/phase-1.0cz-inactive-launch-canary-runner.md +21e12364bfe942a174f5a00997dd389c004e731fadd9c9a1f796a8300b9bf863 docs/retroarch/phase-1.0d-crt-entry-canary.md +513282e0c04211831e0202b0beea3320a4e92a3804bffd2858d1d58f47163231 docs/retroarch/phase-1.0d-early-diagnostic-design.md +3b4543a38fe8e978014dac8eea78e7b8e42f64e1ce9cd8d8753133db27bd9edd docs/retroarch/phase-1.0d-loader-static-model.md +1ffaecfcb46c8a428717aadf4d67b6ba324a8bf27749193ef838cab2a9f72372 docs/retroarch/phase-1.0d-loader-to-entry-analysis.md +e254b5ad5578cf17a99ac2af8410aa034e3211d0c26c2fbc3576e60cac561980 docs/retroarch/phase-1.0d-next-device-test-ladder.md +5f456adffcc2769312a0c5db72f24e02657de1a918ff15d8ba02a9c127024a74 docs/retroarch/phase-1.0d-startup-import-closure.md +1c06cdb914a0be1001e580b2f1340d4de72eb50e8c49156dfa914d9afe06e2e7 docs/retroarch/phase-1.0db-post-cz-differential-analysis.md +4388ebce6ada766b087d0a203ddd4aefc548d840532c2c670977a1ab8de9b04f docs/retroarch/phase-1.0dc-inactive-bigapp-comparison-gate.md +f5a3c8d8ba5eae4b9ba3e207d44b5594bc4e9c3a5e76ef44a4c65804df21ff92 docs/retroarch/phase-1.0dd-minimal-launcher-source-prerequisite-audit.md +c1b528bcb26017826d7d4eb2859bb5640ae715f21d6d04cd8def4458d1157c91 docs/retroarch/phase-1.0de-independent-abi-provenance-search.md +0601340177d3877b2617d65f16cfb501a6b886a08610c78b66bb4b833c1e9684 docs/retroarch/phase-1.0df-inactive-title-presence-observer.md +6b2737c96153b69ae88a019a5f8703e857ee69726afd0d8e89f9444f773546bb docs/retroarch/phase-1.0dg-title-presence-primitive-audit.md +f3b04baf12a0995f66a5e041d17170553ab6a1f4b2ff04e2bb1d6dfc4bed51f0 docs/retroarch/phase-1.0dh-hash-bound-snapshot-query.md +0f10dba8db8f6632920b41a011f4f7fc564ad0c542677069f6a995484b162575 docs/retroarch/phase-1.0di-live-acquisition-boundary.md +2fc4553ac748f4180bb3bacd9ad59da033c525e84b3fab6e499308935766abd1 docs/retroarch/phase-1.0dj-di-t2-result.md +3cd253161d9f9e30d66060e9ca8b88a680428d1f65fb54f2b7f138ab25ed4564 docs/retroarch/phase-1.0dk-port-status-result.md +8c3d49373697ac6d285c6cc1f281aaa1b1ea2d31cbe9312c10698f412acd6de9 docs/retroarch/phase-1.0dl-official-shsrv-artifact-audit.md +6005967a871ccc5f7daddafc96c988837f11addaf8720c8fdf48d56287a5e739 docs/retroarch/phase-1.0dm-bounded-snapshot-observer.md +6ac0bd089b740ae8dab1899fe673a1f29d4ede4e156456a6237fbbde11e014f8 docs/retroarch/phase-1.0dn-inactive-snapshot-receiver.md +1a9dacff044f9d07c8aee9fd1165a40572de74bd193945f02ce724b4e9d2361e docs/retroarch/phase-1.0do-inactive-snapshot-runner.md +0742f0a36e138e83d31432dfc7d79b5f37ed848ef7c1ddc4ac42675656ecb0ba docs/retroarch/phase-1.0do-snapshot-result.md +ccd174358fc3a75747079f132c483ea9d1f4b578c0494be8617425d47efd307c docs/retroarch/phase-1.0dp-listener-status-result.md +6035916a2b8bca82e763e4733c2547a63bdf036278bb5dd22f93b7395001192b docs/retroarch/phase-1.0dq-bounded-fake00000-inventory.md +046dfcb1f314c5ef17ca2ebe7dd7365c6e7e07e9c8609a5a362b117705609a5b docs/retroarch/phase-1.0dr-inventory-result.md +8be5db151b8c7d9a2da092b7a6429ca9266226fe060d5b08079ead896639c5a0 docs/retroarch/phase-1.0ds-bounded-fake00000-metadata.md +3e824ca6d3566da6768313e45cbf80e610aceced36f5462a7543bd7601c0ebc8 docs/retroarch/phase-1.0dt-metadata-result.md +80e5ca91e652c61e44d2130cd691ca4d710323863cbe9f3bdb6a7edc27992cf8 docs/retroarch/phase-1.0du-exact-package-stat.md +aec2ce9b966aabe5fddd9d75ddd225cb213fb0ac5b49b07dee9940e260e1a62a docs/retroarch/phase-1.0dv-package-stat-result.md +425c90cdc5fed883bed07d9b5698a676968fdc69526ea61d25705d70b95b19e2 docs/retroarch/phase-1.0dw-exact-package-readback.md +9155fb6f7f7442b8ab324283843bb4acfc8e92f9d5703e4d86b8b700de005938 docs/retroarch/phase-1.0dx-official-package-correlation.md +2ffc2b2fe95c5799eb16966233f955fc25e2dc9fa5bc6ac4fa1312e9fb490745 docs/retroarch/phase-1.0dx-package-readback-result.md +ff259d12d4cfd2a4472b216f7c7ebcdfe27b9578e43fe79d96670840274eed8f docs/retroarch/phase-1.0dy-upstream-websrv-audit.md +3dbb3eb66244a25cd8079ff9ca06a850817eb472775858e5197e3015771a1df0 docs/retroarch/phase-1.0dy-websrv-listener-result.md +cf16f0236a617c4697b17287d2d0769504e19cda6f50241e672e93a714084983 docs/retroarch/phase-1.0dz-direct-launcher-decision.md +d4ec002b7370c09ab6f36f587661704280769d3cc1e7f4c77d2dbcc71d0ca0cf docs/retroarch/phase-1.0e-device-observations.md +1c185d12e60deedda833bfaa9e0ce55946bf55d4f11149351c8ff58b089d5b44 docs/retroarch/phase-1.0e-inherited-result-channel.md +b9d069e019ce3ac095bb7b0abd3777b5bd19a26bd79bdcd491e628d24ab96b14 docs/retroarch/phase-1.0e-next-device-test.md +228e06d48643f5faa6f2cae8cace40d010ddb3f0a76bfaaaa1a1ab22896a512e docs/retroarch/phase-1.0ea-expanded-launch-evidence.md +b66aef08168fb0e5ad7869e593304af219f7a0a0ba4a3ba220328f178c9d2135 docs/retroarch/phase-1.0eb-public-launch-matrix.md +fa08bf2d97a014dd87df8f92f7a67f428a9aa26d4b946407d1ff7f160415f68b docs/retroarch/phase-1.0f-startup-interval.md +ca9f346e3ca521459afed554913e66a9862f3d27930d0ae535eb493f71b45955 docs/retroarch/phase-1.0g-device-result.md +a53790b14a7fd6f251937570501943a63970d2deb1872765b6dd99c5cd0d7998 docs/retroarch/phase-1.0g-one-shot-runner.md +18dc3333de9cea654c711b934af19f82936ec9f4ef87234a4a46543330abbd03 docs/retroarch/phase-1.0h-device-result.md +a13196de45802bc5b248e41324a88630709b20730a4dea1149f251dbfa293cdc docs/retroarch/phase-1.0h-startup-args.md +4bd329ac2f0ef7da0660706ce51ceecdd3cfabc9ab69942fd95f880f5c82330e docs/retroarch/phase-1.0i-flip-and-write-analysis.md +cf0170391eee2144d10bb888397482a7f343fdf48ce90821b02078b3d522ba57 docs/retroarch/phase-1.0j-write-firewall-diagnostic.md +11714d28d240eefcb480f3fb6ea0b2b443b94dd338d485116e855fae13a24c59 docs/retroarch/phase-1.0k-write-diag-one-shot-runner.md +4c1f10e895bb273800a7709ae1e227b0c695f827054ee42d2eb68818cfc981f0 docs/retroarch/phase-1.0l-write-firewall-result-analysis.md +a8767a48fe8829d9105e484062dd368b5431ec20bcc9c25c228d3bbcef19fc1d docs/retroarch/phase-1.0m-write-free-defaults.md +4f1e540712b257d677b95eca822a44be837bd1f10e8ba05d3b0aece7ae9d81a9 docs/retroarch/phase-1.0n-inactive-one-shot-runner.md +b177c9c86c87d1e46fa3b72748fa45d39384518993fdbce582432d8c40980d76 docs/retroarch/phase-1.0o-write-free-device-result.md +2aaee1b64f4f7072f241ee001c411c00c91caee328455ac2f3ba2f3295bf4dd8 docs/retroarch/phase-1.0p-terminal-ordering-analysis.md +19955a833cdee3ea45a2ee8bd69859c6b8aed4f45430d384d1ab32b0cf07df22 docs/retroarch/phase-1.0p-videoout-submit-analysis.md +e03a063eff4ab5b20e3bc67248eb2a458363cbb95f772e47607cae2e78b4d7ea docs/retroarch/phase-1.0q-public-videoout-evidence.md +2c13fe97dbdb0fc3067fda6f8ae22f4925eda0bea85925088f0c61a438dc77f1 docs/retroarch/phase-1.0r-launch-context-comparison.md +1bf68b8ab52218a08bc0d012dbf2f6c5616f05f33f3cb81ec27b8a1c9b2d4316 docs/retroarch/phase-1.0r-sdl2main-and-packaging-analysis.md +7b6e6e5f13679e7509f5c5bb2d22b00f7c52a556ad392b3e0ee0e934570720f6 docs/retroarch/phase-1.0s-bigapp-launch-contract.md +303c4433c385774776896052c41d2fc1a70423c3ac822a0ffa1931f7fa5f699c docs/retroarch/phase-1.0s-hbldr-shsrv-provenance.md +12214413ee5c8b4a3d96bbd3fb829a6007043f21e627c01159f440afdb5dfcce docs/retroarch/phase-1.0t-inactive-shsrv-identity-gate.md +76750a5c480ef5f361624d29100a9aeef00cdbddd253cbe08db3fc939152de49 docs/retroarch/phase-1.0u-local-shsrv-artifact-inventory.md +fcc59e096bcc14ac13b7b38cf71f5f643d69d90c7bd01658ed2bea562f0ee894 docs/retroarch/phase-1.0v-inactive-one-shot-shsrv-collector.md +9f941d306423ab952c15f71dd17630b2a7f717245ebba715108d975a91159593 docs/retroarch/phase-1.0w-self-review-and-inactive-client-architecture.md +bbef44998625c9aeb5ca3eed3d80c2e0e875d4f1f348f461c038414f01c833da docs/retroarch/phase-1.0x-inactive-injected-transport.md +74fbdcdb30b3b6b324b1bab3f2481adfba13097276eff379e42c60ad4625fdee docs/retroarch/phase-1.0y-offline-shsrv-framing-audit.md +ca3d644e121c4256b88d4e56fc2b95a731f95f90549646e3ae022f5125e44ad7 docs/retroarch/phase-1.0z-offline-passive-batch-contract.md +605bebac65c3043639f230fe32e0d9fa0e15c0a2e49fb193c8d01f4868a9fa64 docs/reviews/phase0-final-2026-07-17.md +9fe84b247e32ca3d59117683fc63f0efcb1872e9803413400cf939b72f08e050 docs/runtime/controlled-runtime-policy.md +82aaca085fb27843f9c1cbdb380d01732a98e3082bbad1a0c186fcb551cfe0b0 docs/runtime/kernelwrite-proof-matrix.md +e49440e9b92872dddeb222f689606761eecdbb96e5b7314994790de5e79ad6bf docs/runtime/loader-contract.md +73f55bf8b343e3dff0ea03f7e9987cf436c92daeb68f5f9309e2591536675dbf docs/runtime/minimal-crt-feasibility.md +0e809b29057759f309be6f5a0dc5adc6f19917297f2482867e6e03820f409004 docs/runtime/payload-manager-policy.md +714fe8598d9160a17b22216adbf03ecd63871fb9a437a49996295a9911c77ebe docs/runtime/phase-0.5-review.md +19631e2e77987b052bd070e08e2b58d080aa8edb1a2858c1edf90ceec7a705aa docs/runtime/phase-0.6-loader-audit.md +aedac5a7b7f10cdedeeeb35fc7e8befc5f931d0e84024609cc6169811892d333 docs/runtime/phase-0.6-review.md +1eb862f27eaca716b83d62cf6e98cd62c7a62e9fb3f725367e2177ab13ba497a docs/runtime/phase-0.7-hardening.md +3fbe086175a6048176075f447ec1482074928e3b5282db97ea2169395fe1d508 docs/runtime/phase-0.8-read-only-preflight.md +6133656597a608780f9cb73c4974ed8237a250daf3cb8b77f6745a1e6aacbb8a docs/runtime/phase-0.8-remediation.md +cf78fb86681002741eb042347176844583fac086ccd83b649f1b1ffbac0ae8a4 docs/runtime/phase-0.9-anti-brick-threat-model.md +d4e8f4e39e76f8e7fde85285ee245ae7443a7430d50c2b3aa1ee25cb4c836602 docs/runtime/phase-0.9-installation-transaction-design.md +5a03c0d13dec167e56192e6af14900208040ff94a64e63a89323f3c6a1757561 docs/runtime/phase-0.9-recovery-and-rollback-contract.md +7b6d26d4bc1b62f8dea4905caef78c9341c98139e78024ee4b7dfe680ced737c docs/runtime/phase-0.9b-bounded-observer-design.md +3aaa44de37e892657e8a31230e9fee22f00bd7865103d1bc8abef93591ee2984 docs/runtime/phase-0.9b-observer-limitations.md +be7c2ca486b36f3299a1e30d28b0ca95ca1f0589e272c05fb6999c608f437108 docs/runtime/phase-0.9b-observer-result-contract.md +e5d41cc9801bdf491c1423fb9cd36efbb84327e812a5624a46497ee323632998 docs/runtime/phase-0.9b-observer-static-audit.md +b1e5ec2501870800c260517401199d5a83f48c5222e9f8671d44db68d55ac49f docs/runtime/phase-0.9c-capability-closure.md +7e2d32865d747ffaa370bb01103f6aca95dfa77c8225784ef7be7196f3863cb0 docs/runtime/phase-0.9c-output-channel-feasibility.md +8c67055409ef766a6be9f5409b5349650f48f2f80ac3253e2867373e21d753fd docs/runtime/phase-0.9c-side-effect-model.md +23a2f4b7312219afc6cb2d97773fdc33c9d222a8eb21a1ba8bd4c8ed7280b03e docs/runtime/phase-0.9c-startup-exit-feasibility.md +8dfd5da1149f3af0af9d5a70b096ad6e49ab93d99ed85e986083ecefc3f40a03 docs/runtime/phase-0.9c-static-audit.md +947ca08ed995696b4f139e735f2249db4b30033d7c08ad5f1e70769ba5ba2be8 docs/runtime/phase-0.9d-bootstrap-recovery-chain.md +bbc1e606dd7c36668f24bd4a89697d7dcae191857143ffb8d4625bff1a14c400 docs/runtime/phase-0.9d-existing-stack-endpoint-matrix.md +f1018ffed4e8ffd737fc549c79b87aba22e56133172f8344df3efe6b9ef18bfb docs/runtime/phase-0.9d-independent-recovery-analysis.md +d26cf866eb43f75b9a6fa902738b27eccd3d305e8e991030628b327d1e4dbacc docs/runtime/phase-0.9d-off-device-backup-contract.md +398aa48132e856f4a6ee2ea5b14ca3fdc38e76006cc59e0f15cf2f0ea8ab8970 docs/runtime/phase-0.9d-operational-windows.md +f733e6c2cea3d03b7509dd95cc4b36dd0da546eba87c5b5aea271d4e330bcce3 docs/runtime/phase-0.9d-readback-feasibility.md +4cbfff8517f03960e98a5922da0f3645c99426d742989500635cb22719e635f4 docs/runtime/phase-0.9e-bootstrap-provenance.md +ef7e144a37a0d771e4249401104b84cbd0ad5c0611d0f92a5de6599a9568e23b docs/runtime/phase-0.9e-future-rescue-payload-contract.md +90e008313386a70966a191a62a45d7bfe9f64a12f408cfbfea41f2a095d283fc docs/runtime/phase-0.9e-independent-rescue-chain.md +ac9fd1462280f3b6a8fb465c547ff0ec20384901df3da8a51b45a43b39dd8a61 docs/runtime/phase-0.9e-loader-9020-protocol.md +ad97d281dc722c89de7618a74702ff94470003d246ce08228ea77b6690d4cf30 docs/runtime/phase-0.9e-output-architecture-options.md +4bc6960014dd91873cc3448631220e23637f8bca663106b74005716bdf2fc2eb docs/runtime/phase-0.9e-r-official-hostsender-audit.md +3ae48d9f2100b025945e1491692840f5e025af19fba1d042e89a3fa7f92ebd53 docs/runtime/phase-0.9e-r-official-release-correlation.md +9aaa11ee2e69a37f14bb94f88b47fc5c0c3655298d25d7eb78884dfcb7e7e106 docs/runtime/phase-0.9e-r-port9020-source-audit.md +9b2ad3c4ef4ce7dd6e503e4a02796112575883efde5eda07ca1ab093288e8c0c docs/runtime/phase-0.9e-r-provenance-gaps.md +b358f3ad9dda971a0484e4efb9e7218dc810e1b884b9c7a239c9ce3ba323ae0f docs/runtime/phase-0.9e-r-release-source-binding.md +6d3b63dd02fc521d1d1132df4fadecdeb4a87fdc378b8a553ce67e94dc9d048c docs/runtime/phase-0.9e-r2-community-backup-correlation.md +e19f7ecf5f665b7f4a7ac4425fe12548d5a837a0b667b73f7576b8f1b0762580 docs/runtime/phase-0.9e-r2-final-provenance-decision.md +860220412196e3c10a357f85427492e03e30e8287df872cefe846d9b0eee594e docs/runtime/phase-0.9e-r2-inner-archive-correlation.md +04ca2d4f7d5c3f841593b2cbe2f1665678ab08de2f6a6ac0dfb228366ba4fc98 docs/runtime/phase-0.9e-r2-local-download-provenance.md +a906f43cecbda0cf82d41af197dd6d04dec89f7116b541a8db1cf39e9dcef553 docs/runtime/phase-0.9e-r2-siecaf-structural-analysis.md +35cf63a6816d43188dcfd48bff6166ad13b2592301df172ab75a9bc6f680f870 docs/runtime/phase-0.9e-reboot-and-crash-model.md +6a922f766bac2a378c5c1f30efa1f652dca9341dbfb9d75c040279f8f4fa7393 docs/runtime/startup-callgraph.md +27dac7577376a0e0eb9db2664757a9d15932788ec595b53101bc32c09c5f5a55 include/chimera/gfx/adapters/retroarch.h +5cd48c985ce271f3c5604a0a75b79febe5d22c04b69da20e9050c0692f58a5a5 include/chimera/gfx/adapters/sdl2.h +8bfb4371f301d9ebe6d94ae8a936ee1fba9184750457650bac705325953a108b include/chimera/gfx/chimera_gfx.h +e9603b0e3792781ad5b511afb22ef61e3d4fd4c5a16bf928f8609193bcd97783 manifests/artifact-denylist.json +78d0d28da552550e4b7dabc5b9c25347fb2618c4664b7a554b87c6de69cd6c96 manifests/artifact-denylist.schema.json +749a850f2f2b98a3a99f63ef543b5725fd695212a3fa7aa013ed73c8ca1f87cd manifests/artifact-manifest.schema.json +f609057f40b5aefd008c1af7140cf3d7134a0c10753e827ce31055ddbc1e9eb0 manifests/artifacts/chimera-elfldr-phase07-fw-9.60.json +dea9097eb5a2580fc2d8b5891466c1f6d9a55e5f475b78b045b23c611c9b148f manifests/artifacts/chimera-gfx-capability-probe-0.1.0-fw-9.60.json +c0cc13ddcf090a940e94d4fbe7217c005811984299602bd2bc2e899da851f161 manifests/artifacts/chimera-gfx-capability-probe-0.1.0-none.json +1ca5e970cb0285bf9a31c4e3e6fa7f7e25d8a62bba2a0a34550410b189e95971 manifests/artifacts/chimera-gfx-lifecycle-probe-phase07-fw-9.60.json +8513ae052c27d9a42b49bc7e7a6be4ee06d093301309d70ef643a8f03a1e3be4 manifests/artifacts/chimera-gfx-phase1-videoout-clear-0.1.0-none.json +e9686874bc76d3637b5622734290a5adcc36aa7a1faac2482a1e8f954843a88b manifests/artifacts/chimera-payload-manager-phase07-fw-9.60.json +4ab3844528defc1c986dbfbc585014943760aec674f0f0000498cde55e1aaa6f manifests/controlled-runtime-profile.schema.json +86c22c80d3a882c836f79e27262d5b97e2a334b6feb651b8b5dcc382d760c974 manifests/ps5_gnm_symbols.json +8f1a8edaf50c09a51baf9bd4f76a09140191c500f6852ef7dc83f2c24d6d7eac manifests/retroarch/phase-1.0a-artifacts.json +cbe2ff6a8c5e8c7a80d3b132cafb0ed55762b10bcb306b1177038db57fed22fb manifests/retroarch/phase-1.0a-build.json +800ca442351dc146f5bc67723d4296f36de13bd0b6ae19dcb640fe9eb6cc7db1 manifests/retroarch/phase-1.0aa-offline-fake-adapter.json +e6027123ac9e399ddfb68a235830997caee6c5d4910939288b69e9ff9a1edf3f manifests/retroarch/phase-1.0ab-live-adapter-feasibility.json +0053358fc05c3be6adc7a92b263fd904061d34cfd3b28820792930337f9c731c manifests/retroarch/phase-1.0ac-dormant-adapter.json +0fcc592d0b7bbe0fd2edd04fc234bd3f57bb989618ad19867925b20320a4d34d manifests/retroarch/phase-1.0ad-inactive-activation.json +eeebff316a84b184bb802485e0ed5ad42016f3236ba44c04e718c9fb08ca680d manifests/retroarch/phase-1.0ae-launcher-architecture.json +c0e74c0e828168faede5f3db3e9964b6191d6cd9d9834dc3926e0807feee273c manifests/retroarch/phase-1.0af-bigapp-lifecycle-model.json +4178adf244650d7359cf400492a8d8404b60296a5aee5fc88df4a32adf61bdf1 manifests/retroarch/phase-1.0ag-bounded-elf-contract.json +ecb38325bcfdda905a5909f82ca690a174806d0108a45e716b4f8f5a58da168d manifests/retroarch/phase-1.0ah-dynamic-contract.json +c0d19a8a4df45f2311861c1cd1e7ab30d262c97ddc5d317e6229a3ec27d415f5 manifests/retroarch/phase-1.0ai-mapping-model.json +2883c11d3e798ca6f838bb5f8978f2b95a1598630d15a7d9b0d3b35ed3338144 manifests/retroarch/phase-1.0aj-primitive-audit.json +3322d42eb18718f1de7c85d043e68f37a9c5b5632f0d9565fd3f7b6d2ffd3d00 manifests/retroarch/phase-1.0ak-hybrid-composition.json +4a143c0fb2aae98ee0c7452326f8a2fd72afc24d79867282b82ab77cb58c188a manifests/retroarch/phase-1.0al-mdbg-copy-audit.json +c02d59b2d3ced8fb1e50700cd85b60f69f99be9c8ff9559ac4e18512e4992a96 manifests/retroarch/phase-1.0am-bounded-copy-model.json +6754ce238b4e696c76e8fceee0fe6d3347b09568b3f883ec4a1ac52a9f7e83fc manifests/retroarch/phase-1.0an-service-lifecycle-audit.json +46dd4144a4392cbc08a1f1b38087c8858cdff0e3c3935fc16a51e85eff1a812c manifests/retroarch/phase-1.0ao-worker-supervisor-model.json +f2e7d5e28df5b294d055378ec239120d13fb8ba0f895a1f7ef981a739a19d423 manifests/retroarch/phase-1.0ap-worker-feasibility-audit.json +04d5e26c3dcb54ddd029e477e329d1824fae3ab851752cac35c3b450975e7cfc manifests/retroarch/phase-1.0aq-worker-result-record.json +86f101a48aadd1586594f3b4cbbd6d84a01b8cf51c664dd8512c8bab50e00f22 manifests/retroarch/phase-1.0ar-result-channel-model.json +750764e8ea177afb0fc7c21d7c4f1441bd90bb539917c2506ecb61f8aa39fc2a manifests/retroarch/phase-1.0as-channel-primitive-audit.json +17584b4ee70ecca138b8fbddf03526b6070daa6ccb4aa366231457b5272df9da manifests/retroarch/phase-1.0at-fd-deadline-model.json +10687d050b1354efd7edbbd0e6739255278dc5edc85e3312ea85497f9052ceef manifests/retroarch/phase-1.0au-live-channel-feasibility.json +364e1ca4c052f69165ed2c38d3a20b6729fe8cb192199f3afc9ebe706a14b391 manifests/retroarch/phase-1.0av-launch-context-canary-contract.json +425bf66d12e1b9c0e9a26790ea58372ad5736ab6886028c57c4a1be241927a68 manifests/retroarch/phase-1.0aw-canary-source-delta-audit.json +ed9208ea371c36a6025334dc58b5261b4e754cf97bc6b6962bce26b4fe445ddc manifests/retroarch/phase-1.0ax-canary-protocol-model.json +c9d161f1cd7053f32480d9c9f8b31902107d5af78518cbfaeb5d08676fc63a2e manifests/retroarch/phase-1.0ay-target-source-base.json +0dee988ed67b7f8ea81bc80f0b2fe22818196868510794c68560b5a910624a79 manifests/retroarch/phase-1.0az-host-av-source.json +24779e3572f71084d157dbcfc83cf15f55999a15acdffcfc02043ec48bcaa730 manifests/retroarch/phase-1.0b-artifact.json +8ee4305bc2d7e5d75d3f7a4b0b335de39c68932ca49c199310cda9f2279dd69f manifests/retroarch/phase-1.0b-build.json +e3f1941e9218d72299010bf9481931ffd70abd2e0b0f1c69561e7db05befb951 manifests/retroarch/phase-1.0b-runtime-contract.json +5a028550751b20555855de3f0cc538f91e7d574533a40b48b76442ee5e42cb22 manifests/retroarch/phase-1.0ba-target-profile-callsite-audit.json +201b08ac542b94d4c7c47e3f45fdaceb2f54d7da11b5f5c8007a8ec162c3daf3 manifests/retroarch/phase-1.0bb-source-only-launch-canary-profile.json +8b7f2793fb1cce79cf701efee01af307b5c7ccce98e6d0506913929b319207b3 manifests/retroarch/phase-1.0bc-cross-build-prerequisite-audit.json +2f4ca8b0e50da928bba52ea6a7b49333ba6682bea336bfb7d289e87179f81be0 manifests/retroarch/phase-1.0bd-dormant-sdl-materializer-policy.json +c5e502a4b2d1f3fc873df4d0be3c08a8444d381c7bb734e152deea9aa0d14874 manifests/retroarch/phase-1.0be-fake-only-sdl-materializer.json +cb02fabb01770738a1322715648da4adabbbf064af759c92a4f90eb141e1ca03 manifests/retroarch/phase-1.0bf-live-sdl-adapter-boundary-audit.json +14373371cbf032ce139507c8ab0d4204e5da382dfb4a20be4dfd14d95af07efc manifests/retroarch/phase-1.0bg-dormant-sdl-request-compiler.json +d64f17854e1ae7c2f6f183a5f4ea33f9bce9340c89c0638bfad171688bf7554b manifests/retroarch/phase-1.0bh-bounded-sdl-executor.json +0f53877e27aec5a432b7d5d650f4f4203d7419d4d0a220c27de9913095452267 manifests/retroarch/phase-1.0bi-real-facade-and-tool-install-audit.json +aa4d6ee9df8e2fe67ce1d30d3bb62d5c7593f43770a86e0ea8c1f0f8122176fa manifests/retroarch/phase-1.0bj-exact-host-tool-install-result.json +fb2e32e17e9d9984cb6054ca2d057f162a208364de77720c39834c45227fead0 manifests/retroarch/phase-1.0bk-bounded-real-facade-host-fixture-gate.json +f4b2b98bcba59ec161f52297ef4998ff4db782957806883e55a479ffc3d2a437 manifests/retroarch/phase-1.0bl-fixture-import-failure-and-new-gate.json +ace70d11be0a30caf4e93a4a7b92d1f9627645284c9a9b6c92bbfd86f9e86664 manifests/retroarch/phase-1.0bm-real-facade-fixture-result.json +de3606c7f4c5f029c160d4508e82b38067302a72aff02cfdb7cde3741672ed49 manifests/retroarch/phase-1.0bn-materializer-preflight-patch-chain-audit.json +7f65395e0a91ccaff90319d018d09d15eed71c2de0383e491ef991e1ff5b5ce6 manifests/retroarch/phase-1.0bo-patch-chain-remediation-result.json +833be486e1c90fe917d5872904823bb0c673814fd18d54dca57f5f08a7ea812f manifests/retroarch/phase-1.0bp-one-shot-sdl-materialization-gate.json +fe80f9a526c0ac928d50b64de71673ab2f93a34aba223bfd9ae79a08183c5ab1 manifests/retroarch/phase-1.0bq-inline-invocation-failure.json +0bf54f5566abd876d1041879f430e355431bdfdf3b4144b9427531305d16fa00 manifests/retroarch/phase-1.0br-script-entrypoint-one-shot-gate.json +fa923e5f33967606c65c585eb1a1273fd7e64faf1650776e33717f73f1861987 manifests/retroarch/phase-1.0bs-read-only-status-timeout.json +313e95ccc61cdb6005e39a7cb4c4ab82cc9e4e791889e90e8bea5f1a826fed84 manifests/retroarch/phase-1.0bt-corrected-timeout-one-shot-gate.json +4085201165f16771b793a5e9866d90992589080da841b44847d7761b68e78b46 manifests/retroarch/phase-1.0bu-wsl-git-status-timeout-and-windows-git-fixture-gate.json +fb326bcb93ea777991918bf06956613136639f8fffe8b68b697e0d75c7d9d852 manifests/retroarch/phase-1.0bv-windows-git-fixture-result-and-request-correction-gate.json +82f7f20d6cf829ce3a7621ade6ca5124e12c35a79898d4cb1eb9df93d1643f4d manifests/retroarch/phase-1.0bw-corrected-sdl-materializer-one-shot-gate.json +ac40bda2d1893d09a86f47d09475c43d608360f2d04fc1192a382c24f9bb9adc manifests/retroarch/phase-1.0bx-changed-files-timeout-and-read-only-fixture-gate.json +d44773c113c716d6fc158aa34673c6d676b13d50742def7e453c90f175647486 manifests/retroarch/phase-1.0by-windows-git-stage-fixture-failure-and-file-hash-gate.json +06ddaafc41354cc03ecbf5ad8efab9f9ebaee7534ea9e152344d71b6bfacd686 manifests/retroarch/phase-1.0bz-exact-sdl-stage-cleanup-gate.json +63b62ca819431f739d45be14c7453c66f23b78ae5ca52ece28ffef911d38f671 manifests/retroarch/phase-1.0c-device-smoke-result.json +960b248cd57c1b82c1c81f02376ce37a49e0ca7b871ab63ca33326302608d8e3 manifests/retroarch/phase-1.0ca-cleanup-result-and-hash-verifier-correction-gate.json +e4894b904e01eb2c3b191d1696679f5d02e5c7026be3c37eaf218ff0296c13bb manifests/retroarch/phase-1.0cb-hash-verified-sdl-materializer-one-shot-gate.json +52262a2f86664b1493b8a5c5d738a217a090b39b1f05907106483c18017fa6f4 manifests/retroarch/phase-1.0cc-cmake-timeout-and-exact-output-cleanup-gate.json +cd491f229682cbdcaa0866f84109bb3a1a831c0634679d02926b4d9e4a9a737b manifests/retroarch/phase-1.0cd-cleanup-result-and-configure-timeout-correction-gate.json +9ef6f65bd4641f6b5d1e2e5facd7ea5642d51b7da7b208be99f9df30ed0e0187 manifests/retroarch/phase-1.0ce-extended-configure-sdl-materializer-one-shot-gate.json +1ba00ed257d680ab229fa39ca11b6e7b4070c3ce52aab347e039607ede51ebf9 manifests/retroarch/phase-1.0cf-repeated-configure-timeout-and-cleanup-gate.json +1be804144320648ba42bc2fecb8e80dced8c3422dca40f350b5077989fdea9cf manifests/retroarch/phase-1.0cg-native-wsl-sdl-materializer-design-gate.json +9b03963689440e30d73f847abaa55d137fca8366e5f07ec2ef57831ba8d11e3a manifests/retroarch/phase-1.0ch-native-wsl-sdl-materializer-one-shot-gate.json +3b469a4363ded25ac8a0055e504e87c2b667a5c035be145e112b01e2ecf5742c manifests/retroarch/phase-1.0ci-native-sdk-bottleneck-and-cleanup-gate.json +c330690cb3df7c5858a21fd450a3106df281838e81b8abfb6dca234bd1942c9b manifests/retroarch/phase-1.0cj-native-sdk-stage-design-gate.json +667807cb1ebb2f18779d41978abe02ba1dc60185e0f7554d37df8b5584de8b1e manifests/retroarch/phase-1.0ck-native-sdk-sdl-materializer-one-shot-gate.json +0a94ffbb3554ed55d63d5e138d03479d9bbdaf14948051bf4bd7db7f07b6136f manifests/retroarch/phase-1.0cl-native-sdk-configure-progress-and-cleanup-gate.json +861fc229b4066089610ee7e1982512a775f391772bae24fd35603743cc6ba996 manifests/retroarch/phase-1.0cm-measured-configure-timeout-correction-gate.json +b6535723c1f129366c71324a9d38e76940977816e1410d388a478bdbc175bac3 manifests/retroarch/phase-1.0cn-measured-native-sdl-materializer-one-shot-gate.json +3dba9f8b230e034f16c11ba22b4aeffe2f8dabafc4233a4218da8b577b5479e3 manifests/retroarch/phase-1.0co-configure-success-build-output-limit-cleanup-gate.json +3f3af1e73ecb060d2869fdaebb18f1055a61183037542e3dc202bcadf501094f manifests/retroarch/phase-1.0cp-build-output-limit-correction-gate.json +8d120585502993c93d5475ce31b1f1b484611bc649cb61fa4055d09a6fffa593 manifests/retroarch/phase-1.0cq-full-output-native-sdl-one-shot-gate.json +b2206270e5004a9588b9da3b5210dd8df076bee2beefee349075f491e468bb98 manifests/retroarch/phase-1.0cr-audited-sdl-archive-export-gate.json +260937d3b5626305f256925535c0d4aaa10db4dc32bbf45fcba014eddac9b545 manifests/retroarch/phase-1.0cs-export-parent-correction-gate.json +9e979b08288b77a6f8dc5a32ede41e59eda1e465768f3ec75c16207522bbd4fc manifests/retroarch/phase-1.0ct-sdl-archive-export-result.json +a1a864ad0a330f7e119464b6149d6f4831c7f6f5d5972f2ee3ff20ac9a93af65 manifests/retroarch/phase-1.0cu-exact-sdl-header-export-gate.json +3ff666f2157511d76b525ee271f931bce2d2d35c72d4178dd87096089305b809 manifests/retroarch/phase-1.0cv-launch-canary-offline-build-gate.json +de31fe618c8fda49cb7bb0a1d056a44c0f0745d1249fdfdfe9cdff6d7facd849 manifests/retroarch/phase-1.0cw-launch-canary-artifact-audit.json +e063ea356b8b2b114c26f69680ab677817b0696e23898dbcd6fb8e99dd20d399 manifests/retroarch/phase-1.0cx-isolated-reproducibility-build-gate.json +850d0a2860ebb53ff69bcfd3cb0ac268809d470bb208d16db18bf76a63fc3c36 manifests/retroarch/phase-1.0cy-launch-canary-reproducibility-result.json +da52b5a7787be14f8e0b182132bab2744fd6e1946eab94676bd26877077c490c manifests/retroarch/phase-1.0cz-launch-canary-one-shot-active.json +c0eaf1e4c3f9f02f3446cf651cb046634252aae4ec36bb06d5e5be8a02a482ba manifests/retroarch/phase-1.0cz-launch-canary-one-shot-runner.json +ebedd74cf0ad8e7eb087c1ce1df659698d02e1956e07dd842a0cea526eef5818 manifests/retroarch/phase-1.0cz-one-shot-approval-template.json +f5ef0be57ee523683409926d9b51f7da2774414b9ec0d8465a16aecead247134 manifests/retroarch/phase-1.0d-canary-artifact.json +e6f9fb7ce4fe21b4b7632c52b26d600f54f631849c69da95b477728e665e4c52 manifests/retroarch/phase-1.0d-early-diag-artifact.json +24168ea339a5e1234f8405b81029090eff00f17ebfa226dcf0325a2d4e19ed87 manifests/retroarch/phase-1.0d-loader-model-results.json +f745c8762d1b88743751df1af625b3a08e452dff8d2a16091abd9eed82647e50 manifests/retroarch/phase-1.0da-raw-elfldr-baseline-result.json +dc5ce0c80ae771c35f16ea51b3ef4e2369133dc150b3f39bc39a81805d6fd6c7 manifests/retroarch/phase-1.0db-post-cz-differential-analysis.json +729bc5c19d46966049a5ee1863df4efa3aa736dc90cf23c120fff8159c2e76f4 manifests/retroarch/phase-1.0dc-inactive-bigapp-comparison-gate.json +aa0657e1b51f457a65f134e533799f5bee58560fcc7f8761f6539811f75ef6ee manifests/retroarch/phase-1.0dd-minimal-launcher-source-prerequisite-audit.json +0fd33ad39747cab0b7ff22488363a36b774dc0e516df89c604216824ad9eef75 manifests/retroarch/phase-1.0de-independent-abi-provenance-search.json +b0cce485969e20ba607311afc9b9d566d135a1cf0ab1ac240ef95c69f9bcba5f manifests/retroarch/phase-1.0df-inactive-title-presence-observer.json +506a93ec2c710ef0881dc8d73899ab391a9ab848cc01fd110f9f59e9a8b1b2b2 manifests/retroarch/phase-1.0dg-title-presence-primitive-audit.json +47313c6693f50f21cf086e66681e118ed6556493813f263c6e81eb6e6ebf08fe manifests/retroarch/phase-1.0dh-hash-bound-snapshot-query.json +d70f01d4180a25a57c78c2de17134817c5b04be53533a95be6112a17b487b101 manifests/retroarch/phase-1.0di-live-acquisition-boundary.json +746e3843561dfd04bfe9b734d515d581bf4d62f5880a85a34364059b6ddb0798 manifests/retroarch/phase-1.0dj-di-t2-result.json +981c0b4d68b24351279cac97d7493cd46c4d5e441dad007b0de44a3deabba600 manifests/retroarch/phase-1.0dk-port-status-result.json +1f40fde01dab9f2211420d3545d49cb645f5b4a1897a2544793f482fec6cd330 manifests/retroarch/phase-1.0dl-official-shsrv-artifact-audit.json +e622860221cb14819e53cfdf06adcaa73cfdcd84b84476cec3c33ec94247e195 manifests/retroarch/phase-1.0dm-bounded-snapshot-observer.json +1714fbbf6a0027dda58d4e7ba60f8bded6dc1d0f39b8533b5bc51b5445d43d0b manifests/retroarch/phase-1.0dn-inactive-snapshot-receiver.json +4961884183ca8ff1ace22d61ab65e831fcb4127d1655a6178f936d83ff7bfc19 manifests/retroarch/phase-1.0do-inactive-snapshot-runner.json +d7c3f224f1fd0033eaf393177be694c18b40769d176966acf84910ffafad9c20 manifests/retroarch/phase-1.0do-snapshot-result.json +d906133bf58c94433950cdcfb97a3faf08a489816e9d107ee36ac615344036f3 manifests/retroarch/phase-1.0dp-listener-status-result.json +416bc37d71f1cfa4bad878c6a62cf54bfb3f8adae9ae8a753a35a3fdf5d6090b manifests/retroarch/phase-1.0dq-inventory-observer.json +15551279dfc71a9dbdce555d999f7370cb9f401869acdcd752a60787b092ed1a manifests/retroarch/phase-1.0dr-inactive-inventory-runner.json +2f21095849779bfd912180baebd6456f20bd9c851d04da03d77974634671fd60 manifests/retroarch/phase-1.0dr-inventory-result.json +d236090b3d708b4b1a3cbd63dbbcfeaf8ea0644f426d8b253ea4fd2b43811c8f manifests/retroarch/phase-1.0ds-metadata-observer.json +1f5655faf888a1ababc5a5eb0e46e7fb13b21993f8d68b151114c482ee01ebf8 manifests/retroarch/phase-1.0dt-inactive-metadata-runner.json +1c0c34761665eb2fc99faff0a4aca76e45994682d731408af8bc7d3323826be2 manifests/retroarch/phase-1.0dt-metadata-result.json +f733d3cd241595b290dd7d17d974758a0ac5119c8eb81445ef4b23ca1d8e4fd6 manifests/retroarch/phase-1.0du-package-stat.json +eebb773917ae80f1b962ab371a10b3df8376e1bd34d531cc7ad6cb1e791a8b3b manifests/retroarch/phase-1.0dv-inactive-package-stat-runner.json +ae15c7ac53e646288f96135e2da451152616bf214ccceb84b546444f54f46806 manifests/retroarch/phase-1.0dv-package-stat-result.json +f16c7eca137c1504a2be3b5eeda07a4473533902f5feffb7bc3a51602ab5a495 manifests/retroarch/phase-1.0dw-package-readback.json +0527d11ff28f27cebadee13f7ed5e0e0004ab2aaf475858d3cfcd5e22fe15b9f manifests/retroarch/phase-1.0dx-inactive-package-readback-runner.json +c45c99d75e2b9a664cbfb9f72b0e4e653a9881287b523e618d663f68018b5fa5 manifests/retroarch/phase-1.0dx-package-readback-result.json +22186efb400f3ad98f5238696462ab3f22f1476183ab20fc46ba7917a151dd09 manifests/retroarch/phase-1.0dy-upstream-websrv-audit.json +b96002e2479e3fca267ca07c6d8cb95245dee56d06689b10db984ecc497589d2 manifests/retroarch/phase-1.0dy-websrv-listener-result.json +410860fb773435849f0a8cce8dcc2eeb2353b8443b6dce56435a8e039aeac2c4 manifests/retroarch/phase-1.0dz-direct-launcher-decision.json +38961a3ea6ef498e80678d70556b4053f3612915858c22234e5201231eddba7a manifests/retroarch/phase-1.0e-result-channel.json +33722cae870ef6a229a833b4f5fbe57d31e5a604fc9ecf0a44108ac20f35cc44 manifests/retroarch/phase-1.0ea-expanded-launch-evidence.json +ddef7de517d4f5d8d5e4b8f292aeecc20421e2571ec52eba1195baeab9156df6 manifests/retroarch/phase-1.0eb-public-launch-matrix.json +159dfa77b781ef0eefb01b840952afa196d922b3e305cc790a8aa2e18a1ade8d manifests/retroarch/phase-1.0f-startup-interval.json +670ea54567dce43b4ab0324713992f5bc90ffd95997d1bee2fcf8c9702e928ea manifests/retroarch/phase-1.0g-device-result.json +effcd84daf9d245aed4e9f7917048b174b7ab092d40b3fefef9ba480c664be1b manifests/retroarch/phase-1.0g-one-shot-approval-template.json +b1627c2c5056e30a48fcb7898219759c8bd9d3a37405900c6e47863f138907e9 manifests/retroarch/phase-1.0g-one-shot-runner.json +28a9748503010cfa4a3cb0c96ce01ba69218750489011eb39dd6a7bbdb1370bb manifests/retroarch/phase-1.0h-device-result.json +aa8973452bed8f667514f9cd0f77cb777214b8dad68f56e7d63a64386427d9c9 manifests/retroarch/phase-1.0h-startup-args.json +612367070ddf114c3b4e633afdb237914f63a96f60dba2e5969622854620e03f manifests/retroarch/phase-1.0i-flip-and-write-analysis.json +7323a8f965f3bb117a88b4fc8ef7234e980e3f2f88a2962a15718a15b31d0c83 manifests/retroarch/phase-1.0j-write-firewall-diagnostic.json +e59c8944d6e428b6c7d5cc395230536c8d4c62ac4902c7273d95c5065146e043 manifests/retroarch/phase-1.0k-one-shot-approval-template.json +a72ce22a9d49c572137b4161f5c2ead363446f411fa33b1ee10e7716dcb0566b manifests/retroarch/phase-1.0k-write-diag-one-shot-runner.json +4b54b6a23ff9afb1960f29ffed1ce18cca601f661e37f36fc53364cf5e16b0ac manifests/retroarch/phase-1.0l-write-firewall-result-analysis.json +661482c9c73a1c40944d2ccf7c62ec8fadee061dbc1e9b746e13f2aef886a167 manifests/retroarch/phase-1.0m-write-free-defaults.json +ac6917d246a589879ddc159ee23c73b9b6630ca4bb29b6acb25020a3eee4ce9c manifests/retroarch/phase-1.0n-one-shot-approval-template.json +cb0f4ac8fe8baf697855c7b146310fe6ec58c98dd940cf3af7fc7246a8434aa2 manifests/retroarch/phase-1.0n-write-free-one-shot-runner.json +a92650732bc2fd4509970eb8c731e06ba1b91eff5c1bb7f9e2b844ac58ca8537 manifests/retroarch/phase-1.0o-write-free-device-result.json +dc5ef42c5e058883f86d1312da2b2b1cccf987029d4947f7fed7a722ba1118f2 manifests/retroarch/phase-1.0p-videoout-submit-analysis.json +c4b7de21ae1a738a46c03aad74613edf49b07125669ce5112c879d31cf8de24e manifests/retroarch/phase-1.0q-public-videoout-evidence.json +89a80e6f9619b2fd77de3b1542c0c9997fdc06917661bc5ed4042f458493a1e6 manifests/retroarch/phase-1.0r-launch-context-analysis.json +257f49dd4214d7599069772aff9efd82b1593d919abe9e27f1aebdc3e2faa723 manifests/retroarch/phase-1.0s-launcher-provenance.json +55b09535401f1c7d0149b67951b10fe8a90bbd17f94036dbcb1d166a8a7f1f10 manifests/retroarch/phase-1.0t-shsrv-identity-gate.json +397d5db1e19cef7d64f058a7f9f2312d7e93a3426640b48452ab633b0e2368d7 manifests/retroarch/phase-1.0u-local-shsrv-inventory.json +f898a4df1e1c03db89bdecf79fda3c17c331fd7a1254bd13ab1107d371ed1d9f manifests/retroarch/phase-1.0v-inactive-shsrv-collector.json +1fe676cbc4651e0289043fe770ff08a749d1c304fcceee39cc191242ca17c7af manifests/retroarch/phase-1.0w-inactive-client-architecture.json +3c96e6fc35032eba436136009d0daf13cfb0ebb84085d279d5a7956b5ac3f311 manifests/retroarch/phase-1.0x-inactive-transport.json +4f0f8e37280c493d4b05741e213b029c23e8a83b6e8ad0e4559a8baf73e1a2da manifests/retroarch/phase-1.0y-shsrv-framing.json +d5d1ba0e18a1f8530c7f4e60ee69db0cf74b0bd1d6c0b9683258c4ef2b689085 manifests/retroarch/phase-1.0z-passive-batch.json +8e7074fbbc875678909cf98666d5c35cd0f7662519363aa1c7c699dcff5b5e00 manifests/retroarch/upstreams.json +ddba34f78a750482fa910f03fcd43341d3a9407b2c4082f4d8ad5957bcc76dea manifests/runtime/controlled-ps5-runtime-profile.json +aefd1d1e663be617ed1042c6a1d68b9f21dd0c137f0c601d15cdcbfdf03dadb7 manifests/runtime/kernelwrite-proof-matrix.json +b04dd224c00af0c7228c582f6ba3ca55ef03e9453287f233a0c4ff2e545092fc manifests/runtime/minimal-startup-artifact-decision.json +d0e8202c1a07e4104476cadf6c14a1dea2d724b1d97495dddcdf05858f6c8d4a manifests/runtime/phase-0.5-startup-audit.json +8cabbdad5cd79b15d7268586a41a7db8f000a26c4047593bda4521c99e8d1811 manifests/runtime/phase-0.6-loader-runtime-audit.json +68cbb2d3ffbf5490ae52e13e822ddd12b0344317986dea8e16cea91e90ec4a3e manifests/runtime/phase-0.7-kernelwrite-proof-matrix.json +28229d54e64dc9896c991715715df086257bda0b18010fe3714ba18023c1759b manifests/runtime/phase-0.7-offline-audit.json +47d7f452f8799979fe99b3e6d56859f03544112725bf7e5b349eba5ed81b3322 manifests/runtime/phase-0.8-read-only-preflight.json +a9dafed8c83722c43709dcf90ea117c21d6f996ff8fe233810ed08aa20cbe071 manifests/runtime/phase-0.8-remediation.json +39fd7c70cae998d9d74e7caf1ff3c19f9f76f7de2e5a50bc09baede4ba53e9e9 manifests/runtime/phase-0.9-anti-brick-design.json +efcea3b0001ef5b2da65c372ceb93ee2fec09c9331b2e4cbb6008212504c0918 manifests/runtime/phase-0.9b-observation-plan.schema.json +104c4a667ad17f9827fc7276852c6faeef96effb8e3561a5ebc19a62c7d51634 manifests/runtime/phase-0.9b-observer.json +84eb737ff3486d0c5d8b5ecf06809f93bc573497da4919595b4c1817fd34247c manifests/runtime/phase-0.9c-feasibility.json +31cca2363069789dc0da7561204ae0bf02b39b616655bc5eb3302505549cd101 manifests/runtime/phase-0.9c-feasibility.schema.json +86e5aaf034685dbe058b71ffeec645b682f0a8cc7d249e8ac0397155233991de manifests/runtime/phase-0.9d-existing-stack-readback.json +d1c60b9ec80f27df7368b53d13bc1f0efafd7bd551e972a3cc9f59f94a43db80 manifests/runtime/phase-0.9d-existing-stack-readback.schema.json +5dfa9bfe2ae751b2f0ea0e03c60c1a4471a35452389cf471456e6c54eb4601cf manifests/runtime/phase-0.9e-bootstrap-provenance.json +00b15c2ad3ff91aade8f0355803248d2388193fbbd78c0f86043601daf2c9df9 manifests/runtime/phase-0.9e-bootstrap-provenance.schema.json +a7ca8b4e8072cb60ad4cd4869f6c508e8014059c8d81ad3b94a8d9e8cd0db4cb manifests/runtime/phase-0.9e-loader-protocol.json +f51d0d3fe7712991beff4a3db60ef2dee9889dfb18f512e8ffd1e8090f6e644a manifests/runtime/phase-0.9e-loader-protocol.schema.json +ea84f1885ad4286670a401cbf73e9a371cdcd03a1f247908b90ea27553ee820e manifests/runtime/phase-0.9e-r-port9020-audit.json +8ace6eaef67765fd5ec9d7ce511536a84b39d2464fb25e50f8f7d1111ef96477 manifests/runtime/phase-0.9e-r-port9020-audit.schema.json +3c71bc0651f468c87910a78d1cc5463eec6ce8352059797c77aefdb4da1cf1ff manifests/runtime/phase-0.9e-r-release-correlation.json +0bc4df92ad14b7a42901697ac129a0f8f9261c9e73efb81fbfdcb2746689af52 manifests/runtime/phase-0.9e-r-release-correlation.schema.json +68713fd95c5d3662354380bb14be7461029449d119f2dedacfe463ea58668780 manifests/runtime/phase-0.9e-r2-inner-correlation.json +18f47668c69501e26080b93fb17f5610c7037a10b971f0ee9059b66a81f98c4a manifests/runtime/phase-0.9e-r2-siecaf-fingerprints.json +6b7a07df79d81d246fb7c28a34e997ae53d76a2fe3b24a577e244f8f9fedf9b3 manifests/sbom.spdx.json +7d1c817a0af968763e29edefcc79ea12ff213582d487bfbbe76e0daa87d7b85a manifests/upstreams.lock.json +8df5e97bef1771c9d7e12ae6fb370441f5ba1c6264e6b1b5411b63a7f4c9f33b packaging/Dockerfile +5da245c2190344d11a583aadcdee733aca2afa1c53ffd55cca4d6410f86d3ce7 packaging/README.md +25ac5acfba0406feca9bc98c7221d391e22750ea98a4f8bfd18b8811cb9aa1c8 packaging/patches/sdl2-phase1-video-only.patch +26211a596cbda55e523f1dbc0a742854110ef120bbe3bf9e7b34dfae2e1f97a2 packaging/phase07/README-installation-review.md +f47b934cc748b0d32fd640963c610bfe5dd60061da4b801307b5bc0ef59cd916 packaging/phase07/README-rollback.md +15332856199f90c50ebf556a6b7c248b1e1b493803a5ad3b160d1b37f531697c packaging/phase09b/SHA256SUMS.txt +318be72b5d0d57cbd7f7a3767527d5a09305f328b75a05e0ff9a43d677d2b48e packaging/phase09c/SHA256SUMS.txt +bb27384a4a88b7b588323e8f0246b2765c019b3739c41e3ba798c9a36d4d47b4 packaging/phase09e/SHA256SUMS.txt +50d766c63d6b9d56cb4bfc4573ba789fb5fac7690434f77284e9cc6557d4db8b packaging/phase09er/SHA256SUMS.txt +fe0896ea39c671861f4d2850678edb097adae36e49c7735554ce08913a401686 packaging/phase09er2/SHA256SUMS.txt +b9b600a256d21f856c94e7d64be40b1f493e57e1e68fb146ca092c6cc984002c packaging/retroarch/phase10a/SHA256SUMS.txt +18eb49c600810c724e36df4bcea25b3f0e9e58c755ed97618127a570b029de29 packaging/retroarch/phase10b/SHA256SUMS.txt +d634dd78b29265b10805053f619d6307e292cd2ff9a0d9e0e77e535d6c07c570 packaging/retroarch/phase10c/SHA256SUMS.txt +21dbae8c7728995df0765bcd0ba3ff054778149a8dbbd53dc1129f65750a9ee8 packaging/retroarch/phase10d/SHA256SUMS.txt +c3673efc2816de41440e8da91a436185ae0852872f6ae8e01bba15ab1dc93369 packaging/retroarch/phase10e/SHA256SUMS.txt +63bc1b7912df0ae2ac750684ba3dad65bc97cc9a1b7b92b35f0fdad66a2b5f24 packaging/retroarch/phase10f/SHA256SUMS.txt +4fe39356f6abf09cc67452b402469deab2d7c7c17a21b768259086b92c5395a3 packaging/retroarch/phase10g/SHA256SUMS.txt +d837ec91b36a975a30378fdcf998e0375a85fef613f251bf09f7a0e5bacd5aa4 packaging/retroarch/phase10h/SHA256SUMS.txt +0ac35d7ab28b9c27268989450dd3aa6415508410f813be49be0439960a86e168 packaging/retroarch/phase10j/SHA256SUMS.txt +053a5f7eb89ded685939611472d033952fe66f2e5e1c739b392f9455fdc04c9d packaging/retroarch/phase10k/SHA256SUMS.txt +9f2ed5f4237459624c9f70670434e64e5c36c86eefde29b8d9266328cc1582d3 packaging/retroarch/phase10m/SHA256SUMS.txt +e63fff5dbf7537efc82889b59f88fab18487f4cd93e031cab3b4f02131bec5f9 packaging/retroarch/phase10n/SHA256SUMS.txt +b1a0c3c0da844616d361131b36adc172cb2b980df94d73778dcf723c6479b240 samples/capability_probe/main.c +1791b0abbca0f59e296c2ec5f12b325ae99db76ca26e8580a4229fb2d654fd85 samples/clear_screen/README.md +1ae7df1fe921ccab2a252f77975d3d441ef7725e34535b024580c0d4a242d766 samples/lifecycle_probe/main.c +b18f0789c5a9fbfdb9814aa540e7e5fefd567aeefb03cf79fd5e9711e644e1b7 samples/phase1_videoout_clear/main.c +b12ec726e142e4b10c98d371c1c804864c5c37dfb070576fc8b51f9b74218238 src/backends/mock/mock_backend.c +45e16afad30eb49638877fcdc9e4f62af2343204407c5f95cbea911b2abcb6ab src/backends/ps5/firmware_gate.c +bfbfd48e9bb595fce781e77b4359f2a4ed6d7f268ac211e3b1a7b6df73b2e280 src/backends/ps5/firmware_gate.h +8c123c36ba52298a2cfe508d91c78b5489d4c1d043774742d13895fd76de1ba4 src/backends/ps5/generated_probe_symbols.inc +1649b4387f58e92753b7a8b530a762e20adb157f7ce55d0a5f5c56822c48b17f src/backends/ps5/probe.h +3ae29e1322ad569240dd8e5ac3bc141d9880c2b77641b02429cca4472ec19c77 src/backends/ps5/probe_logic.c +50adec6ca971a7a7a9540861e6a93f7902578b0b8b24f1c9d2ac42b348b4d3bf src/backends/ps5/probe_platform.c +1126fdae32cffc494ac3308807987675fd5086cb62a15396c0ad55acdb629b4e src/backends/ps5/probe_platform.h +ce83fc5e5a25f1b9ba4e26e1c7b46fea0539b47fdc1499ffc4d86ecaf9e7bb1c src/backends/ps5/ps5_backend.c +31545d7d1d6a54fe99d420afbd7ba8b66ed9017dfbf4697fe99874ea7816f34f tests/phase09b_observer_model.py +144c5ab0b33a0ef4cdc6fcfaa52a576f090af61e79a5706f60ec21cb02d9686f tests/phase09c_feasibility_model.py +e2f074e844102177f45fb0f97254108ee641db00931bb037574dee82111f316f tests/phase10w_fake_transport.py +00653d250c102303e9bad525e9a40f9e0b074aea8d35135bd40620f4110ef7b1 tests/test_adapters.c +51b137a732d0dea5bcb1a71ef8ab222107acb0be9162e34856301a6b81ffdb18 tests/test_artifact_audit.py +86ca41d02c38ba1daa69571dd3f4e8b66cfa6848cf824aec0dd9deac557aee90 tests/test_core.c +a9f5e65c34e5175da2b4fd78a801e55b865aa472b0c08ea1c3dbc39f8f607bca tests/test_execution_policy.py +8399c395b9e3c9c5e40f58f84d14fb215feb66eb5013cc781995f949203216b6 tests/test_firmware_gate.c +a305e244c641bae3e42cda81444294703562e5c48e944478a84d0fb66df71db7 tests/test_manifest_tools.py +c82bd968b80a7c213498021151d8711a454192d07ae96a03762734155ce9e50a tests/test_minimal_startup_block.py +48d2cefe09d2da9f3c6f4f5c26512fb0013190678c78578c431b2fdd632584c4 tests/test_mock_integration.c +6ab05b7e1c4945b2f98afa2d642cf30735030bfdb641a25f330dda6aae949c0a tests/test_phase05_audit.py +60a6e4b0e9edb58943e09bfa9cfdb286ad9ac02c9542597f7a3ad0646007e8ba tests/test_phase06_audit.py +5a51528e60721c9579d5ceb345f33b571071159d5adb8b255252c4ae2befdced tests/test_phase07_audit.py +8a4ad7c70de28ffe3148fd3fd1f68c36a872c53c691c9068e1ff163970863c48 tests/test_phase08_preflight.py +0721b8c75bfeaff3e58095cd2e302cf14f3304d0031bde3bacc3774685d1fb55 tests/test_phase08_remediation.py +e1ae56d0d9cd3a181675bdbb2197a84216a8e014a2fb758c3e357c7386c463b9 tests/test_phase09_transaction.py +38f49333b92b23eec3a82ee832e602baf3cd7fff9d6b93d04864e61d209942f3 tests/test_phase09b_observer_audit.py +bd910012b5e2d6d79d9067e29410d30ac53f834c3c93ee4e60495f11543f7c9f tests/test_phase09c_feasibility.py +abc73d05a1a72e60ce588917fac5e183091175d5ad85ec680fa9f3672c1dd8b5 tests/test_phase09c_protocol.py +840060bb2a00b9130a923dea7da610df9432d26c47d38bac2575573d24b5b8d4 tests/test_phase09d_readback.py +a458d1e71711b4fc856f6bd869429a9e08f45358bec6bc28bb97a3c7c903e79c tests/test_phase09e_bootstrap.py +8c013795d2706b1d1c2f3f097701667f51e5d3d3a2eaa592ed1836f9c143f633 tests/test_phase09er2_correlation.py +71d0a87e4127afe28aa184855b7f16fd1fdd242d65dba0a924260ae36908ebe6 tests/test_phase09er_provenance.py +18a5470a651fcbe4b23f2a68cba499dd19d3da15a229623c054be0b598025699 tests/test_phase10aa_offline_fake_batch.py +39597e991b15bcfa9aa28cb2f68c87ed048c38482c6dbf81a28c56ccfceb0a48 tests/test_phase10ab_nonblocking_trace_model.py +9c8c611dbab5e43df71d523169d9a1bf7579ed1918943df7531bf75ef4b9abfc tests/test_phase10ac_dormant_adapter.py +5800f8bc393921b31fcf0df3bd853b5936ec2ad5a3fef100505ab04af9729d15 tests/test_phase10ad_activation_contract.py +d112a5202fd3520a407256ebb64e1b06e37f76a52d7e6cfe95d2410ae4c834a1 tests/test_phase10af_bigapp_lifecycle_model.py +774afc1151044f4362bcfcd92fad97286d23477046fb4b542a4cf739723b0c36 tests/test_phase10ag_bounded_elf.py +d78eb2441e091bb98586aae36a4e0b1aca24ea8a62feb27103c1e1461b4aa415 tests/test_phase10ah_dynamic_contract.py +d5454bcb6f249fccf99d6883a4248d00c6daa5dcac7ff640e9e72418378e6529 tests/test_phase10ai_mapping_model.py +8f76fb3f9d91ed96e2a19c4b3386915f306187a1f0ee2e842d24351c282ef026 tests/test_phase10ak_hybrid_composition.py +6f9f97134efb83e50881fae813cb0119d4da2f17fda979ffe841209e8e5c8895 tests/test_phase10am_bounded_copy_model.py +fe90f44d4f8da27f5fc0c8110551647d039257a2e3f3d9d10d56603c1275bfa9 tests/test_phase10ao_worker_supervisor_model.py +774c1eaee561b4058d8069d263f07d30a2e575dd8a063edabb73cc0065193ded tests/test_phase10aq_worker_result_record.py +a8dfc17daf3d05dade537aacd4cd60482958a30a527c7bfc60ae9cba5355e1b4 tests/test_phase10ar_result_channel_model.py +ca365b93b955d24b5ce3653cb20cef1fccddc91eca8d6382a56220accecdeafe tests/test_phase10at_fd_deadline_model.py +095aec531c123a634e8267c7f97c64ea95b8ff603a2d7c6d4e996e2bbb51508d tests/test_phase10av_launch_context_canary.py +9d7adb3f2a35b7af1f671aa967f4f247ed82f562ce2c9139d5c36e174ecb5306 tests/test_phase10ax_canary_protocol_model.py +ecef8f126e2245d707a776b4fd02aad13553e6e79515287e1002d2855a8db2d2 tests/test_phase10dc_bigapp_gate_contract.py +3760fa60e66857b24bf507f196060ec9e4d8b96ebce3843ab02fc185c0f8c0b8 tests/test_phase10df_title_observer_contract.py +1215d7750ad9b767a77530c37c24a4cc603c9f969c7dbadec1632a7f2a26b0d1 tests/test_phase10dh_snapshot_query.py +5bfffdf92733cd9e05adf8d00a8f4c3234fec00f5b5f4ffc56104eee8be11777 tests/test_phase10dm_snapshot_protocol.py +5fceb7fe4429a77772c6b234ebb57bd9eb658cd81053f767b9d9e250d6feb580 tests/test_phase10dn_snapshot_receiver.py +cbb7569d1f0e29ab150a8e940c8325e7476a5426170d3b37854570ce432de5de tests/test_phase10do_one_shot_snapshot_runner.py +33f1bbc18903d63ee5aff38b3b16774cb5e95560ad2a73c147bd1ec03a7283ff tests/test_phase10dq_inventory_protocol.py +b34cafdb93296f7e1d3218d65fb840f720cc405fa9b4201f7573f3f2483f9407 tests/test_phase10dr_inventory_runner.py +2ef718cdb36d0199bb958ba645f14c269ec2bd01baa992ad4b88686ee89a9099 tests/test_phase10ds_metadata_protocol.py +f9501e4fac385e088ab3b0b008ada815a108c3ab3f800dae399178139a29f108 tests/test_phase10dt_metadata_runner.py +4d950a56efe3531d80b01b1ea8ab20415f600bad2734e41708183a2c7d9ef940 tests/test_phase10dv_package_stat_runner.py +61346236e3caf73a4d9f3ba1a2b13be1cab00df28ec1aea75d9455b8c6cf6bd4 tests/test_phase10dx_package_readback_runner.py +fdfb0146c75f65c507f801706ca49b0362b254dd0150ace0abfa73c7e0b3a85b tests/test_phase10t_shsrv_transcript.py +25157158560702b9a3167dd8575fe5e0053e57facdd9e76672d3bb699f10c05a tests/test_phase10v_shsrv_collector_model.py +c1f55c9668776befd682ae05ea18a6f007ea726804eab0f42e0ffbd523abd215 tests/test_phase10w_shsrv_client_policy.py +19709ed6ab456be428d262b3f0afb4f6f577b34e4db80e66a73ed63d9bf2cd43 tests/test_phase10x_inactive_transport.py +802742450d65b237c0865e5820a8523131391988ca9eadd206766fb51693ca95 tests/test_phase10y_shsrv_framing_model.py +7706cc212a0fc683eb32acaef26ecaa64cbe5784ea6aeeed7e29ade11612490c tests/test_phase10z_passive_batch_contract.py +9b75e5dd130ab463917cf54dd3462bfda5fcf56826cec32fa22db9ce0d7629f5 tests/test_probe.c +db1f24e53fffeae0a9a1594ca51e2f4f385b283d45d38fe687f5ca1dbb07726c tests/test_retroarch_phase10a.py +604697167eb023c7ad399b9f2b01e359947c0db9c31de2b98907edd062cac226 tests/test_retroarch_phase10aa.py +bf66aab2c6efe90672fe09b9fcebf34a8803230b50b4e80f6d6175d7bf4c6cc5 tests/test_retroarch_phase10ab.py +5a73c0d997a6224abdc7dd8a748d46fabe2d37cd436d005d30e9090c66c95d2d tests/test_retroarch_phase10ac.py +054f4a452f05e0c4b0e8cfac6b1b0060e173569a1e1a486d782cb59fd095050a tests/test_retroarch_phase10ad.py +5419a9ace55e84c959ba9346a16044df1a8526654b25e76674ace09dd2106965 tests/test_retroarch_phase10ae.py +d8186a0ac570f7de218ea4b19840d61273ca2f6c91cefde23e65f8c8da494082 tests/test_retroarch_phase10af.py +40b012da64d8b9db9dbd05145ff91dea5797efe39b4705eddfd19edc83633707 tests/test_retroarch_phase10ag.py +d15c1fe5641dbc94c296fd5b44febfa93bbb6b87db6a3635b1becb03386d19e2 tests/test_retroarch_phase10ah.py +1db69f54d59f902aa3a2e14415765c9b32d67b89ff4979675d7555c24fe06a45 tests/test_retroarch_phase10ai.py +1db322e7272f8f3cd9c5ce0416040d2a0fa39ec6c711ceff27e9782fcaeb407a tests/test_retroarch_phase10aj.py +e9964da392acfce8a8e8e55c46c72bba20f5907fdc509e206d32f55fc386f69d tests/test_retroarch_phase10ak.py +27a1f1820a7d3a18a8952c509aeb7b7a77bc449774fd208ec96a4e96e914153e tests/test_retroarch_phase10al.py +11e4ef3dddacba6c32cfa03a55b4de652f023eedac2ebe2cd9dd778b3562e1e4 tests/test_retroarch_phase10am.py +fb6a193be0b9c7592f0ee01e27ad2af4f7c73050bea2d3e8c11016f02206eedd tests/test_retroarch_phase10an.py +5e5f4c166816a094ad4d58d328d5c77a027630a0dcc1bc8a917819daacaa0849 tests/test_retroarch_phase10ao.py +0067417aa508db2e883843b17d3167dd2709b2ca57a92fca5bec6f9aed77a0f1 tests/test_retroarch_phase10ap.py +cafbecd799e83ae493a22e9c80573154978c052b1caa812203e32bb78e07545b tests/test_retroarch_phase10aq.py +a60d1fcf0178208e62ea6c309f15292b59bdc1e8a7f2759067edda855f0dfbb3 tests/test_retroarch_phase10ar.py +08b10cd9d8577faf534dd51a68e98ec8e618e7226eb8cf14e8827bfb59357e0d tests/test_retroarch_phase10as.py +ef755634399b3e95ac935223b203e04c401d8b8685a4fe28557643c3cd4fe2f8 tests/test_retroarch_phase10at.py +3ca082442d0a86abb7df532bec8f9fb015ab05075860e0272f05c6285309530d tests/test_retroarch_phase10au.py +f833fadb2c8f3bef55c81365913d83ec999906b62d550e434308e6e25d92cb0a tests/test_retroarch_phase10av.py +d716211934aa42c0f3e71207648ee04f1cdb22ff226b7b32a135906c29f0f5da tests/test_retroarch_phase10aw.py +589d10cca4d809b727b16d5b7a1bb2aed34b258daa4c2d28ee6888f96fb78167 tests/test_retroarch_phase10ax.py +7bd02713b38ab3989e0a055d5f1a17796a85befd46116a2ae78a877ee446e20f tests/test_retroarch_phase10ay.py +b8220454a887736d0daf4f94ce2549bf9c885524960b2926e4b813156f64e53f tests/test_retroarch_phase10az.py +843d347899ad2fadcf840cfbb3e9b0b9fa67a3e0943e6752b994238ed28194f4 tests/test_retroarch_phase10b.py +27fa35227652c2c2abb0a1f5f59f6ac67f09cfbd897f527a896dfac7ebb2ced2 tests/test_retroarch_phase10ba.py +9c5624ce945d2af4d670a34ea4025561b9a92ba037bec7ef6a55b98c73ff139f tests/test_retroarch_phase10bb.py +8fd5352804efb57daa785f4d9bab36f77713ed12f197e2bea5284755a2c1e440 tests/test_retroarch_phase10bc.py +840bc7a537f37743682735eb25355bdc1ee27cef8c7556618356490cb62e5513 tests/test_retroarch_phase10bd.py +27f9ea8a5f8f84e1c84a0bf6f62455e67f555437a1bef70b750318ed73507d8c tests/test_retroarch_phase10be.py +da5bbfa7f0ecb642d4ca5c2a3f32a5688602ce662397e83c16ee7f6c4e2c043b tests/test_retroarch_phase10bf.py +9f845988d081d2846a3cf8709fba58eb51c387dc088df93e58025b295261a4bd tests/test_retroarch_phase10bg.py +296ffe0cfb9551cefb3a4c503f94e48db360f42f66f8757910cfb293c98e001f tests/test_retroarch_phase10bh.py +35560a7d3ee1aebe74e62211553abb3b497b1a3e114833fbcd5f5da3587c11c3 tests/test_retroarch_phase10bi.py +8f3f7176d12705c52df8f0b9d70d97c61b543ae1b1e951e58c0ecd32df93c992 tests/test_retroarch_phase10bj.py +73cc51e4233a79eefd7e82aac4068a25b8d1c7c2a949db83e5c5b9ac983591df tests/test_retroarch_phase10bk.py +8187c74eab446d1d02efb36386aa694957051af36dbdbd145a62457113249abf tests/test_retroarch_phase10bl.py +bc0a39a35c1b55b44aa5180e1d3432a7a0313a135057b8ced478de4ef7694b35 tests/test_retroarch_phase10bm.py +6d41c40bd61ca73e486a639a9fad0a9c066861860c17c9c9b2929fc36eafe1e8 tests/test_retroarch_phase10bn.py +bebbe4cd0a62b048760c46da8d114d4c984055f16a181a58dbf6c59ccc739b2a tests/test_retroarch_phase10bo.py +78f2357d8d1a37b60aedfb993966f240c8e3c35bc8d841c2ea6f53b9ef3be5f4 tests/test_retroarch_phase10bp.py +4bb880cd9f76e21471b9b299ca4e9bb239276ed7cdcac77a6d8b32eec58fa125 tests/test_retroarch_phase10bq.py +a463ee38718138a52a1fc84aba96595e4ddfe7928b17dbc46f357694086b4d36 tests/test_retroarch_phase10br.py +63c1ce5714a815b9e30f11d4a905715cc3189972f8096f8972a2a7dbedc4069b tests/test_retroarch_phase10bs.py +42fff30438830cf28e498cdc6254416719c53bc514308214d869342a7958a586 tests/test_retroarch_phase10bt.py +91912291937314eb8c10b926e0d300be435852244e0e45d05f3dc855826750c9 tests/test_retroarch_phase10bu.py +a5398751b35db977c040f10576445aa3634d78f51596917ba70fae7e92a3b0b7 tests/test_retroarch_phase10bv.py +d186b0f2b80a0e744caf808fdb9538ebbfc0dd812053ff71b206c53d39153abc tests/test_retroarch_phase10bw.py +a503d6908081ba51bacd3532340614b13b8b9c06648047e197ded51d7c19af78 tests/test_retroarch_phase10bx.py +d304ca6c54e58bc86f6395111747681d303eaabba8c39c74e041e81ccb663e72 tests/test_retroarch_phase10by.py +059a1ae5981389cc2eabe16e035b3f2e348a758603509f90ded50fb020747e28 tests/test_retroarch_phase10bz.py +59fd653fd55b9e2183d36e3073ea0d4d8a6b9066e0faea44644903d711845713 tests/test_retroarch_phase10ca.py +a7b308ba734872396dbe2c8fb4f65bfe853649a848d4a8dc8bc64a751b479699 tests/test_retroarch_phase10cb.py +731de085e2b32db646626b315b6ceece1a5b2afc4862fa8836a2636d51e6522e tests/test_retroarch_phase10cc.py +3ce8b17df36a5b64dff7766cfef30a14f99b6953115e6b075393f638d9e280a5 tests/test_retroarch_phase10cd.py +dce0c5739a308eb55000fd06518db036a1712e09ddd0009705ad2d407f81f7fe tests/test_retroarch_phase10ce.py +87e485a65242f2ccf88cfd0232b98daddf799b11f92c64f353a9cca7cdd1d53a tests/test_retroarch_phase10cf.py +84cd491cd51524701d3296e48a04010033614a52f4a657543264e3627df5bac1 tests/test_retroarch_phase10cg.py +61f7b8c08fdc1d6666073643a93b7fc9ca8eaf3c6c0f61503e0b953860eae180 tests/test_retroarch_phase10ch.py +2ce97a0c0d1c6ed9a3558b2423c1e27e3d3360fc1611b3b93c9aafe9209ac6ba tests/test_retroarch_phase10ci.py +9693a45763687fa0a3da43416bf20bc70db155742d57a8657db1095b087f9c07 tests/test_retroarch_phase10cj.py +19e20a8432d5c9af206b24551b334d06a14c3da7ecc81fea5763a0a64c23898c tests/test_retroarch_phase10ck.py +7efb1804466bf0ac4112a27aadf6d30213b3b6bfb2be92976756a2459fe7e2c2 tests/test_retroarch_phase10cl.py +4c7d1c30bf3ebacea50562d89bf39819196c8671fa268a823b8b8e20c18ef9fc tests/test_retroarch_phase10cm.py +0cfe172f0a9d38671fefd39e174751ee94ba7270e9f651f25c8c983e8bdd704b tests/test_retroarch_phase10cn.py +005bb665194f7669d24e78ef19901df41e7f5099e7cb81abc8058b6b6c69baa1 tests/test_retroarch_phase10co.py +a78a91a776e31e1bf471a1b0f2101a01ec8013b2e74d35045b8a95f537edbc7a tests/test_retroarch_phase10cp.py +121bd9b70f0eb729fa434728dcf71ccd1cbeadfe97fa7d20244c6e1c173aa808 tests/test_retroarch_phase10cq.py +de77a8629895d41345919dc2ed65bb356527d13ace0100c907a4aac706c79caa tests/test_retroarch_phase10cr.py +659cc7451635ca3b5fe78ceb8af2b6f0d00725aacccb6fd63611818439dde95e tests/test_retroarch_phase10cs.py +c34ebcfe235db49cb0db5a9d6d67807749c2500a14c6a03ef0d735a494fed797 tests/test_retroarch_phase10ct.py +297d0efd0f1f949f6a2602c05d1b7bda1bbecb99feb90deab975677cfc445203 tests/test_retroarch_phase10cu.py +2d5be765d5082ea7e847205e7da638e398a17750fc416baa428eb85bacec3dfe tests/test_retroarch_phase10cv.py +a5873e20783004ec187af2c959fcfb57dcf363f9d4108bd8c6cd186f5a459cbb tests/test_retroarch_phase10cw.py +5b177be12ce6e454f8294d57f494b172321395becf02b2b7cb0d78d74303fb34 tests/test_retroarch_phase10cx.py +ca82ec5e63f005c91c17d6d0ffbfc54f6ece138ff2d02d83bfb1814c616b831a tests/test_retroarch_phase10cy.py +8fdde81f139651398212613c3ac811d93f4342d21e8d041f7f85a296c203a0ba tests/test_retroarch_phase10cz.py +9737d19f992e8259d51fab1e1cb88b3ab28db3b45226b601ec485d040be01623 tests/test_retroarch_phase10d.py +e3b8719b9defcaa779f9ef32a7764bc3cc3b85b319e255cecec9b51826b20a5c tests/test_retroarch_phase10db.py +3d1e579c6e21ce073ae2b25078ae697580561cde199b6d5e0821ce41baebde7f tests/test_retroarch_phase10dc.py +14a3427669964fb012a590b4f8311fbecf8c562a831648cdd528bdf818c059c4 tests/test_retroarch_phase10dd.py +c6f0aa744234f8c6dcdb6eac3ee52ae39b155704545fda7ffdfda7af80940303 tests/test_retroarch_phase10de.py +ca14f35e69692d6a0aaa2b4957b6c8432db6d17b8bb771e4c20c4b5b56284768 tests/test_retroarch_phase10df.py +4e23d2fe055a69677ed3fc79524a79d4865622fe7d6965c360ba6dc9b56e02c6 tests/test_retroarch_phase10dg.py +2df3032db4d6f72b0cea178a054ad51a4f05b48a53061a5fff010cd07cfc7f9a tests/test_retroarch_phase10di.py +6096020b8e1d3b2daa7157ce99efe9d1de62a1a2272f6fa7b7221a29da9b0e87 tests/test_retroarch_phase10dj.py +df611a6f444b162b9b2c93ae3b002bc61776451aff0cde01d1bfa3efd0155b3d tests/test_retroarch_phase10dk.py +ce187acc66b649481957d94b675941b196e190a3c144f55fd4228fc2ca7001c6 tests/test_retroarch_phase10dl.py +46b11621aad630501d6e5aa8ae4cf6ecd0ad12cc5a847d3e1ba0ab0e26f2e7d8 tests/test_retroarch_phase10dm.py +ade663284982f0ce098a70cee4984f314678a7aadf0d0a0688ed6245bfbac113 tests/test_retroarch_phase10dn.py +ba5a1cc897ab81787cb8e1f3ff88103a70398c6db72292f625db6a624e86f320 tests/test_retroarch_phase10dz.py +1fd17f2135b88815f356764b9ddbd617151c681c85c1fe74f7de20ba0f71fdab tests/test_retroarch_phase10e.py +56ddb0dd92eb0685980296c38bfd0b512593977a30e3834bf38049451ff8587e tests/test_retroarch_phase10ea.py +b67efe56403c9e43edb469941b46a5bb277c44af8bb84a40779f98b99e3ea3d9 tests/test_retroarch_phase10eb.py +ffa05661c5441ddf3d7cf86a711973f128b2cf8ea91e2350aaa9a76be8bf8b06 tests/test_retroarch_phase10f.py +914bbf1604f5fef01ebd94039ce6d1135ac69fb5199242325c671dfdd4f48c5c tests/test_retroarch_phase10g.py +1133d7b5ec08d3c372e9c1f486835b1d5d69eb730a9fea8eee12903c65e1a4f2 tests/test_retroarch_phase10g_result.py +0c85f61a8a3934bb8904234ab97a675a8e86b4dcb863fc899578f773caf2973f tests/test_retroarch_phase10h.py +ac77c73859f191e63e1499dd88054160d2caa7e17eb290554e36cd600b0f4041 tests/test_retroarch_phase10h_result.py +6cfcf46bd2854a986077d94dc1348d6dd8d1542835b3187a621ffc909746cb77 tests/test_retroarch_phase10i.py +fd1a7556b5f7e54f2faa7e334df84a713f70ff390cc8ec6b8658dee5fb8ae592 tests/test_retroarch_phase10j.py +f49e68f9c1ec816d63b01729650938ebe39ceed5650b8f233b4ff20c5228c280 tests/test_retroarch_phase10k.py +7c9f5fdc718b1909c01baae81c430656befc9d12733a3994d997b9bb785a5601 tests/test_retroarch_phase10l.py +5041695fd882c96f1c0a475a6dfc56d42b99fc66cc525298abaad7cc07efeee5 tests/test_retroarch_phase10m.py +442545472bd43dcba89a2ce87bbfa1f4e9079a3d653f1dd4cc91664350c1694e tests/test_retroarch_phase10n.py +30f116bc6236ccb7643a8aa82cec16204ba0b890b406b43a85f717c7983e21b5 tests/test_retroarch_phase10o.py +a49c49aaa9b41f716eba531956b13f708c20c3f10ee3899cd840f1d4c04236e5 tests/test_retroarch_phase10p.py +e0cc4d907326b56d092b55a539f8288f75b9e92488def1dc13fd3b75c47c0d6d tests/test_retroarch_phase10q.py +5cc20dc708fb1ffe877ee16602ce2d79f09758d1c4791f67cc03e091897c63a5 tests/test_retroarch_phase10r.py +80ca9a684ba9859d1399faf337b63a0c6995ddf59ff408b009760ee966793e92 tests/test_retroarch_phase10s.py +581dc379049e161793ed9fc73f457d299e985927f505a97df19ae6bffe9cbfbe tests/test_retroarch_phase10t.py +03b0365ef5445a541d270ed20fff24546c234927be864a0c501e4f6b3288973e tests/test_retroarch_phase10u.py +de927a641b586aaa9678c7b86104fb03bfba87f3b5cf6e6e00726fdcea3f5280 tests/test_retroarch_phase10v.py +62d6a1fd3a815e16744e2946b80b30d580a98315875caf548af0446ac7713a5d tests/test_retroarch_phase10w.py +70d30bd40ae91578a53886be07d92f0f3e32182445f2c75cc76ca210fbe872b0 tests/test_retroarch_phase10x.py +0eb1e254f48a3fcbd8808d4ea5754fb41d598c4303a874a355f75c49792cf370 tests/test_retroarch_phase10y.py +b4dcef0d162584e046714e708072d7168872ebdf8edb22e45f19af7daf245d3a tests/test_retroarch_phase10z.py +953aa89ac468a14fa3f8168599a162e737c60828276d0a24862484a7dfeb0b78 tests/test_siecaf_header_parser.py +85ac29a7966959df6d32e8b34767041c3ad1b8ae1b6cd9af1e1410b1f8999c52 tools/audit_phase07_artifacts.py +857723841da449e8387059d01d3d0af2e4a6f2f9f3d210ee9bf717f39093ce99 tools/audit_phase0_safety.py +c7f0be413dcd16fb13277bc9dfa60f619ef14aaddaf7599bd769a4864c32ff6f tools/audit_ps5_artifacts.py +d1d42236df6697eb82f99bf9b2f2c27b3b264bb1cfbcdd68ce4b48e0993d0fca tools/audit_ps5_loader_runtime.py +e2b2abeba5b682aebc0efd3d16769b0d8c540c8c17bffe38d1773772f73ec607 tools/audit_ps5_sdk_runtime.py +4e15adf9e4f2b500cc563420da596dd8b3d149e0983840273e0743dbe30d4d4a tools/audit_ps5_startup_feasibility.py +7f5689fe2f1f6d0070f13ddb10abd651bb723714f7310e41d2e13a772f28353b tools/bootstrap-ps5-sdk.ps1 +1a42349ee47f12a932fb88c913573d5afc208bf775a9ee77ae831c3e84e1b0d6 tools/build-firmware-probe.sh +3dc8abeb81244fb5b817215c043284b0ccc736e1468cde02eca82863c7e13a11 tools/build-host.ps1 +d6b872d118e0c62f01b3576aa0926ffe775155dc7836e7623237f4d0dd49aca2 tools/build-phase1-videoout.sh +7d10ff0fd31ff9b0db25b10f7a0a49640cfc8f40474e04e31ef2b8843adee30f tools/build_phase10dm_observer.py +cf017a25c2fbacce003fae2aea99a26f3a03354e6206ef910e3357ef1335e659 tools/build_phase10dq_inventory.py +ac5800d50cf58a16af9fbe1ad252c72ba22398ce21e95e4499a514f31dc1913d tools/build_phase10ds_metadata.py +3c67ba564414170a5df9594de190124941131b898b5b32e11d17a3912e52821d tools/build_phase10du_package_stat.py +c4e693d95e492bbf9cfb7cb2444ddc3048b89fa12c4afd03996e7a5580c81bda tools/build_phase10dw_package_readback.py +3d03ad950e1fc95e8f7b3144d5a94df8f890408b175abb1f18c63b901656527e tools/check_artifact_execution_policy.py +640e8fa19776ff47ce0542de66c3997361d3b71544ab36b2bc4051a386d5aad7 tools/check_format.py +277bf1fabf77967f071103f320a485c0e6c7d8e69b3129471e2e3300ef166d92 tools/export-public-source.sh +9031659644a64be079211bfb867e5a993e49b9612a7bae6ad034065ff0a2e47b tools/generate_artifact_manifest.py +52c908a63423327fa6fb7b8e68cbc40fabee25ee0598a0b578c363203273a33c tools/generate_probe_symbols.py +c34574ba024b863d3ce1ad97ba555b56894a21836d7b418b0d5140b3cb65fab3 tools/inspect_siecaf_header.py +710dca22e3522336f45f76a0fe96c8746622d6b7bb168ad2e1e5490321d9538b tools/package_phase07_review.py +8e1cac255f85d2cd14baf8fbc27d631c9b607fc7d19fc57c65462089f0574055 tools/phase10aa_offline_fake_batch.py +7d1aa32d49b91b1e5cf3a085dda033767bdf17ab34389ff044f7403f86287959 tools/phase10ab_nonblocking_trace_model.py +6f28926b59fd9afa6de1ff36d4fa9b013d7e027c9adc0bffd9445d7c89acf939 tools/phase10ac_dormant_adapter.py +b28c80a34bc30ae2d4db8df1360c8d5ce3515de8b91d2bba409a0a404da4b79a tools/phase10ad_activation_contract.py +00c8bed501715d737ad1328f3d4c98fe16f69c061016a939ecb3774d9c1f28f3 tools/phase10af_bigapp_lifecycle_model.py +748c93eb5c9275c992e4b471747ff0dbbcf71cd5b795f44d47f2c82e19691270 tools/phase10ag_bounded_elf.py +51ffa5d8acc475f1edae48f104d27c48ba2725459b217326694f1361552aee3c tools/phase10ah_dynamic_contract.py +26c01c09086f04650dc8834205439e21a9fbe5f92ec78d15a769dbb09487fac0 tools/phase10ai_mapping_model.py +35115ccdc201f7c6040eb0d0638d4ece7f36ddc1386ed8c4352f7c73426d3751 tools/phase10ak_hybrid_composition.py +025976fe8736caea6ee33ae107f97411ff2f5981b16f7005098eb9314b1ddeb3 tools/phase10am_bounded_copy_model.py +9093142ba53790cc6faee38d1c53d2b5221946a60f37c72cc3e24299e56a1b0c tools/phase10ao_worker_supervisor_model.py +e05a754165e78eb42ac46f5783efdb73da015a547c87628384f8eb24f95ff28b tools/phase10aq_worker_result_record.py +416a17addde1ecf114f2f9924287c3358c9a10c84bed251c008d2aee6e4dd1c8 tools/phase10ar_result_channel_model.py +341f90e559c3611b9921c21a906baa01c82a49597d886c53fddd6fa3e0903e2d tools/phase10at_fd_deadline_model.py +a03cd687e9e5d9d61cce768ebc662babe11bebe1972eaf18f6ead4dd616db5d4 tools/phase10av_launch_context_canary.py +faa43bc3b767208bfa2e4cd7bf804384bf497bb5651f6d0aa945b1b300649a02 tools/phase10ax_canary_protocol_model.py +b8e965f343a0c3df5cf7026b68f547a81dad08d4b2e0934e4f9721de8b99010f tools/phase10dc_bigapp_gate_contract.py +2679f03617241abf37ca547141a2a51e3f89b75fec957a1da4f13b904d931f2d tools/phase10df_title_observer_contract.py +0e35c097cebd4213f8ff50ce16dafb4af60bc0576b29f8ea52789acb5fa6183d tools/phase10dh_snapshot_query.py +00b29d72b6274fe0ab07b6524c0bbecf76b9f15f59421c9255ca93f24d38c177 tools/phase10dm_snapshot_protocol.py +99f6195347c1b5340a45b33b035a642f8f3cb3cb50ea6be9604108b5a641ef6a tools/phase10dn_snapshot_receiver.py +d70733fc87492e3008ff521cdf525bcd84b2782690f3d3cadbe50febb4ac32ae tools/phase10do_one_shot_snapshot_runner.py +a465a225042d44270b10b499f0b2f37fea680155bc207f3135909559587c18ac tools/phase10dq_inventory_protocol.py +0ee3f4c3b6f3ee7f87a89f4aa560837665172c1af3dcc595144fd08eb5a46e3d tools/phase10dr_inventory_runner.py +210c9e38d7409cab451c0bf0fca04b49243d07bdf43fc59b2b61c50da59a4007 tools/phase10ds_metadata_protocol.py +eeda75cd3b56d07d24ff1cd6daa6c9f1af5c233fdee2adca7d0ddc991fc56808 tools/phase10dt_metadata_runner.py +69cbb715651bc3fcb4c769632961c6791a3d4ce456b695a16e973006d9e04f03 tools/phase10dv_package_stat_runner.py +558d101c4f95c2999223035fc648d4b7a3576672cf5068b0864658e0a36fb01e tools/phase10dx_package_readback_runner.py +4701a057a98b4874e49e1bcf11db9a9a3a105e48f2c25e42796bff10f238f7c2 tools/phase10t_shsrv_transcript.py +f8a306dafee5d135919bec5afda789dd741e57f39803b7683fb8747c186db25c tools/phase10v_shsrv_collector_model.py +747d23c88f2722e8e8846599c3ac1dae3826eb3fca881caaad36b251f30f3592 tools/phase10w_shsrv_client_policy.py +568d7578482ecf2fcd9e29085b2eb9d8705fc699611508acdf22afd30f2ddd23 tools/phase10x_inactive_transport.py +5081898ec86be52900670be2f9949a20b9abb7781a6b04d5337178a8340775d4 tools/phase10y_shsrv_framing_model.py +0728c2be7f368e0a7f4b68efe86f6e0c5c2f50704a41d0e1992b0bfec19dde06 tools/phase10z_passive_batch_contract.py +bf03a5c321d0e5e4f262220e9074f0e4aacb39a840e2ecd5ae5510b91b66b7ad tools/scan_secrets.py +f8074515d2e2de7e921d4daf59699bb100eada303169ce6f33547e14c908707e tools/simulate_phase09_transaction.py +2e76cc74e639376c0f2f88fb8e175e3134bfa5d18b236d39df2fee31cb61075e tools/validate_phase08_remediation.py +e7bf50c3437a8749b56a5986efce1b6e768a6faa024560cd7261d508f92d8844 tools/validate_phase09b_observer_audit.py +ab1e078bd35d9abc923621e21a07f4f2544debdac71e7532d54fb200cee1eb13 tools/validate_phase09c_feasibility.py +2ddfd7c78bf1122b8d5705925b111afa6204713dffeab2fb4a398cd82944f5c4 tools/validate_phase09d_readback.py +4425bd6a4aff14569ca1426772afba3a53e91d63194f8d98c51491acff40986c tools/validate_phase09e_bootstrap.py +345b1359cd23d7ae46b6e96962b0a74b40de5d76539a5a93aa58784abb4af1a6 tools/validate_phase09er2_correlation.py +7a4751031fcffd32b04d692a7eb180a11be8ad114131862a8717119b0363b918 tools/validate_phase09er_provenance.py +84d9dc52d1558c3a92ef70922350f153d0fe5296db45dc696f8adebf28dfd0d5 tools/validate_retroarch_phase10a.py +472efd01edaf4adfc4ed6eefcc6afe33c0d8e8e6d5c3cad82687e2bceb500a2c tools/validate_retroarch_phase10aa.py +c3baec66024fd6e1e235fb8211cb79d021053f81302c29148617072e9a542d84 tools/validate_retroarch_phase10ab.py +d96450e3099505b79803f7d9b4658a6a37e5807bbcafb149b037fe4131eb0342 tools/validate_retroarch_phase10ac.py +af0db2c20b1e11f7f3f1fdeb7cbe867a2c075e5b50816c77ff8f245a1925083c tools/validate_retroarch_phase10ad.py +99ced5c158e541d86b6d0fb4bfd0e46e24f7ac4ebc3be779712909690189224d tools/validate_retroarch_phase10ae.py +dba0ce0e4642c20e97b86ff160dc2c45c96425bb302ec7d384e7bbc76c38941a tools/validate_retroarch_phase10af.py +834060368a5a5739e025e651368f81e6ed2dbd7fdb98ca46f6577ecdb93ff8ac tools/validate_retroarch_phase10ag.py +38ee0cf4f8abd234eb6b75d1acaf2b85349e37c5767bbb8ef80e87f6065e5449 tools/validate_retroarch_phase10ah.py +6f954f903780902f92ada9d63e7595f71f46cbc9a8eb5cc72688174b88ffd51c tools/validate_retroarch_phase10ai.py +407284e2ca23d9b9b2841dad915bf46c9e023b3248925f6139e5bb54feb52d4e tools/validate_retroarch_phase10aj.py +7c789a62dc4aee94616c97ae783a8d7c48df70cc0c7dc908bc71bd413d8ba8ce tools/validate_retroarch_phase10ak.py +51c443b37eb0605e0427d1ec5e2651959996c0d09ebc274ffbf98cd09a1cdb78 tools/validate_retroarch_phase10al.py +648180288ee418e5846a59d6b4ecca680a6f72ed6883bac40d9a956b21e13429 tools/validate_retroarch_phase10am.py +c29710d10ca9b722a6869475cef6afd8002e86d1cf59cfcb7f2e655721e7186f tools/validate_retroarch_phase10an.py +009501a4d31a5750f52590a9a1a93b6cf0d1e90bfd6cb6e607d54ae937601099 tools/validate_retroarch_phase10ao.py +4c512802cd0c96332240dc175637ed5b7608863081bff23074cb6e4141f4e60b tools/validate_retroarch_phase10ap.py +3844c1eb92b99ef29e3087d682234e47889f64769697f02daa972fb2e5cec5b8 tools/validate_retroarch_phase10aq.py +efc86fb3c9ca39140979bc545449709d91140378c77e99171634bd52ee50ce54 tools/validate_retroarch_phase10ar.py +dd0a1547efac0213b929edf24505b8c077c58d4b535d47adccfd2af26309bb21 tools/validate_retroarch_phase10as.py +d0e8b5737b3fd507e303cea8d204601001e4d482b82e37119f443212c70f0b77 tools/validate_retroarch_phase10at.py +53f24bc4f9d9ba79c9adce95b014f7818b95c94cc819c163a8dce2d66566897f tools/validate_retroarch_phase10au.py +5b791d63e52b79c09ae7013c60c23a554ae1c9a15d4d1803d2f036957f29751b tools/validate_retroarch_phase10av.py +f0a5bb4fa950e553b4a5321fb53a7cc2e1dd544e757ad0370898c1f77d312a71 tools/validate_retroarch_phase10aw.py +e95865f7a5dcd7fe2c3b8760dd5dfcd642080613fb47387b5480232dfef07a0e tools/validate_retroarch_phase10ax.py +214e2eaef44ce7f3f8a621de60bebc8342b9080984fcee8b39d564ccdbb2ec1c tools/validate_retroarch_phase10ay.py +731bb068c47142461fdd51e55a551277f00454e56c8c87714060beaa030a196a tools/validate_retroarch_phase10az.py +cb2bfedace580c349119aca814f30d5152f70691290e866a537d330da03a04c8 tools/validate_retroarch_phase10b.py +39ac367b38cd9a2fd952efb991cdef03c877542a040768f9ffccc7ab38b31dec tools/validate_retroarch_phase10ba.py +1e6fd33f843a3b604def6299e4eca6c36c71def837d851b2adb197930e2212ef tools/validate_retroarch_phase10bb.py +d130c216435e69a6190b5ab3544941b5b5c9b585bab99d9fa0b23a5cca748277 tools/validate_retroarch_phase10bd.py +55250ccf93f7454f7c78b6e36cf2db21d52da56b5f35342e863aaa96fb0dab0f tools/validate_retroarch_phase10d.py +50c9a70f717b9d7f7b5cbf5e7ab4912f472bff4d981009367e0b93c0e2137b07 tools/validate_retroarch_phase10dc.py +ed58e5ced3a4e1d10402c5cd34979bf99132a1fedc3b69e14f6ea1f6dd9232dd tools/validate_retroarch_phase10df.py +1a53af97335f89146c34fa2e4de54240a5edf2a9c51bef29d64692328c2caa11 tools/validate_retroarch_phase10e.py +5fd8c2e8d1b5b342853e9bc24bbb740b500a274685b5593b88060bd1c8d2d636 tools/validate_retroarch_phase10f.py +c0972689bab5326ef57d48902ade2cb694bb33d3e21970107e9c68208e6e4895 tools/validate_retroarch_phase10g.py +c089596521cd1591b16cbc358b867e4676e3fbf5725846a34f89cd11ee31ea99 tools/validate_retroarch_phase10g_result.py +19c482436f36074b01ccb4964c3329812bf31eef84232f747eeb5dfa438d154c tools/validate_retroarch_phase10h.py +07396152097295fdf60157de2cacc441fcdc7a555e97594717613829a0cf60ee tools/validate_retroarch_phase10h_result.py +962075aefd4055389a609cda06e1a4e978e6386e0c524da8856559a01a95faec tools/validate_retroarch_phase10i.py +b0746656525e3fc562fe103b4d3d69a4a7ea15816f5406cfd5325b7e8146ea18 tools/validate_retroarch_phase10j.py +ea4fb2f0378dee60fbdf07a384d6972579ada196e538696486adfde99a900aa1 tools/validate_retroarch_phase10k.py +afb46f7a0f6f854373b9ec1d9a2fafb278bd458f67af143f1e71cf2b8fe06376 tools/validate_retroarch_phase10l.py +5ad7d83e02c968ab46c6ddab71b5544367eeabe53e7552ff144f57e54719fd61 tools/validate_retroarch_phase10m.py +38af9b6603ba343ceb7ab8af412987459aec14a5040057ea465b2ecd1b2bbddb tools/validate_retroarch_phase10n.py +583c618f912ac8431ca313b05a2532b9f4f32ab440855df9dba51d8743656d5f tools/validate_retroarch_phase10o.py +49f450660f9ed0accbc8e573001695c0f5622c2744ae807da05623a966bb0e50 tools/validate_retroarch_phase10p.py +e3ded400fd57b54fb196f854815f5e2cab4118f022676acadeebcbd75ef3ee9c tools/validate_retroarch_phase10q.py +7590c786e1bc4c54ade56e754d2987b1faeaa0ffd63ecceab9edbe350b37e9f2 tools/validate_retroarch_phase10r.py +3930d1435008b47fd41941290d6083b104af3076e29d26988966242a97c525ea tools/validate_retroarch_phase10s.py +4554b140aac177f535545e2a98f57cec40346a5d804a0d53986083de0105f30a tools/validate_retroarch_phase10t.py +3e6e41f3cfca7417f3565e584b7d3bfd3a436c34264488d3fdd2214f8bf5585f tools/validate_retroarch_phase10u.py +ed7055473214a461d288532d4080597dd4343cbc0957dd003fa82c6b8d1baaac tools/validate_retroarch_phase10v.py +acca7980d15e5477f9e32ce1652272d764557ae22dd9fd2793b6fe19f9211fce tools/validate_retroarch_phase10w.py +0278cd5877aa3d0246a60c8db3298cc76695566cf04e62d79e930e5fe8741e26 tools/validate_retroarch_phase10x.py +aecb092a9c85b14db0c9f7924c9f24cdf57897d236bc8ef3085515436b2e696a tools/validate_retroarch_phase10y.py +5148f436ec589b833781e5a534128029ff71dfa2cb1c0bfbb475221643be84cc tools/validate_retroarch_phase10z.py +bea78ee5ddafe80de428e7fbb33b90419e3e486069cd65abc2cea470463d4b25 tools/verify_artifact_manifest.py +a0501216bc5c5efc257baf86f4d57d2e2f8cce236ea56df440ab1651de48d89e tools/verify_manifests.py diff --git a/README.md b/README.md new file mode 100644 index 0000000..18153a1 --- /dev/null +++ b/README.md @@ -0,0 +1,124 @@ +# Chimera GFX + +Chimera GFX is an experimental, open-source graphics abstraction for native +PlayStation 5 homebrew. It provides a small C11 API, a deterministic host +backend, and disabled-by-default platform adapters so graphics code can be +designed and tested without a console. + +The project is useful today for: + +- developing and testing renderer-independent code on a normal workstation; +- reviewing a capability-honest PS5 backend without enabling device actions; +- integrating the API boundary with SDL2 or RetroArch scaffolding; and +- reproducing the project's safety, provenance, and compatibility checks. + +It is research software, not a finished PS5 graphics driver. There is no GNM +renderer, shader compiler, GPU allocator, deployment command, automatic +startup, or supported device-execution workflow in this repository. + +## Current status + +| Area | Status | +|---|---| +| Public C API | Implemented and versioned | +| Host mock backend | Implemented and covered by tests | +| PS5 capability probe | Compile-only and fail-closed by default | +| SDL2 / RetroArch adapters | Offline scaffolding; not a supported runtime | +| Hardware-accelerated rendering | Not implemented | +| Device deployment or execution | Intentionally absent | + +The default firmware identifier is `NONE`. A firmware allowlist permits an +offline build only; it is not a compatibility claim or permission to transfer +or execute an artifact. See [SAFETY.md](SAFETY.md) and +[FIRMWARE_COMPATIBILITY.md](FIRMWARE_COMPATIBILITY.md). + +## Build and test on a workstation + +Requirements: + +- CMake 3.21 or newer; +- Ninja; +- a C11 compiler; and +- Python 3.10 or newer. + +```sh +cmake --preset host-debug +cmake --build --preset host-debug +ctest --preset host-debug +``` + +The host build is deterministic and does not communicate with a console. +Windows users can run the equivalent helper: + +```powershell +./tools/build-host.ps1 +``` + +## Use the API + +Include the public header: + +```c +#include +``` + +The API models contexts, capabilities, surfaces, textures, uploads, presents, +errors, and ordered cleanup. Application code should query capabilities, +validate every result, and destroy child resources before their context. + +The mock backend is the supported starting point for application development. +It performs host-memory state transitions only and produces no real graphics +output. The public declarations and lifecycle rules live in +[`include/chimera/gfx/chimera_gfx.h`](include/chimera/gfx/chimera_gfx.h); the +tests are executable usage examples. + +## Optional PS5 compile check + +The locked public SDK reference and checksum are recorded in +[`manifests/upstreams.lock.json`](manifests/upstreams.lock.json). After placing +that SDK in an ignored local workspace, a compile-only probe can be configured: + +```sh +cmake -S . -B build-ps5 \ + -DCMAKE_TOOLCHAIN_FILE="$PS5_PAYLOAD_SDK/toolchain/prospero.cmake" \ + -DCHIMERA_GFX_BUILD_PS5_PROBE=ON +cmake --build build-ps5 +``` + +Do not run the produced ELF. Building does not authorize transfer or execution, +and the repository deliberately contains no deploy, upload, boot, or run target. + +## Repository map + +- `include/chimera/gfx/` — stable public API and adapter interfaces +- `src/core/` — backend-independent validation and lifecycle logic +- `src/backends/mock/` — deterministic host backend +- `src/backends/ps5/` — fail-closed capability-probe implementation +- `samples/` — disabled or host-reviewable examples +- `adapters/` — SDL2 and RetroArch integration boundaries +- `tests/` — host tests and policy checks +- `manifests/` — upstream pins, provenance, and historical decisions +- `docs/` — architecture decisions, research history, and safety evidence +- `tools/` — reproducibility, validation, and audit helpers + +For design context, read [ARCHITECTURE.md](ARCHITECTURE.md). Contributors +should start with [CONTRIBUTING.md](CONTRIBUTING.md), and security reports +should follow [SECURITY.md](SECURITY.md). + +## Scope and safety + +Chimera GFX accepts only public, redistributable technical information. Do not +contribute proprietary SDK material, leaked headers or binaries, exploit code, +DRM bypasses, firmware dumps, credentials, private network data, or copyrighted +game content. + +Historical manifests document why experimental paths are blocked. They are not +instructions or active approvals. Every tracked execution authorization is +consumed or false, and generated artifacts, device captures, SDK archives, and +local operator records remain outside Git. + +## License + +Chimera GFX is licensed under +[GPL-3.0-or-later](LICENSE). Third-party references and their license evidence +are listed in [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md). diff --git a/RESEARCH.md b/RESEARCH.md new file mode 100644 index 0000000..086066d --- /dev/null +++ b/RESEARCH.md @@ -0,0 +1,162 @@ +# Research record + +Evidence cutoff: **2026-07-17**. Only primary upstream repositories and release +metadata are used below. Each statement is labelled `FACT`, `INFERENCE`, or +`UNKNOWN`. + +## Evidence snapshot + +### PS5 Payload SDK + +- **FACT:** release `v0.41` maps to commit + `d2e2e585740362976a39fdd5ccf390f199a7bc37` and was published on + 2026-06-28. [Release](https://github.com/ps5-payload-dev/sdk/releases/tag/v0.41) +- **FACT:** GitHub's `releases/latest` endpoint still resolved to v0.41 on + 2026-07-17. +- **FACT:** GitHub release metadata reports `ps5-payload-sdk.zip` SHA-256 + `ebfb0acb5260511951a80e17db41650c62d20a8caf8659a230b928dc85005984`. +- **FACT:** `sce_stubs/libSceGnmDriver.c` contains 158 export stubs, including + submit, draw, dispatch, compute-queue, embedded-shader, resource-registration, + and synchronization-related names. The file is assembly labels only and does + not establish C signatures. [Pinned stub](https://github.com/ps5-payload-dev/sdk/blob/d2e2e585740362976a39fdd5ccf390f199a7bc37/sce_stubs/libSceGnmDriver.c) +- **FACT:** the pinned SDK tree has no public GNM header and no GNM/GPU sample. + Its only GNM-named source files are the driver stub sets. +- **FACT:** the SDK's `hello_dlfcn` sample demonstrates `dlopen`, `dlsym`, and + `dlclose`; `hello_so` demonstrates shared-object linking/loading. + [Dynamic-loading sample](https://github.com/ps5-payload-dev/sdk/blob/d2e2e585740362976a39fdd5ccf390f199a7bc37/samples/hello_dlfcn/main.c) +- **FACT:** the pinned payload CRT calls `__patch_init` before `main`; + `crt/patch.c` writes process credential capability/attribute fields and + syscall-address bounds through SDK kernel read/write primitives. + [Pinned CRT startup](https://github.com/ps5-payload-dev/sdk/blob/d2e2e585740362976a39fdd5ccf390f199a7bc37/crt/crt.c) + [Pinned patch code](https://github.com/ps5-payload-dev/sdk/blob/d2e2e585740362976a39fdd5ccf390f199a7bc37/crt/patch.c) +- **FACT:** the pinned rtld's SPRX path can call + `sceKernelLoadStartModule`/`sceKernelStopUnloadModule`; the SPRX-specific + `init` and `fini` callbacks are empty in SDK source, but the system calls' + internal side effects are not established. + [Pinned SPRX loader](https://github.com/ps5-payload-dev/sdk/blob/d2e2e585740362976a39fdd5ccf390f199a7bc37/crt/rtld_sprx.c) +- **FACT:** `crt/Makefile` partially links 12 source objects into `crt1.o`; the + other six installed CRT-named files are empty archives at v0.41. +- **FACT:** the `prospero-clang` wrapper omits `crt1.o` for `-nostartfiles` and + omits libc/kernel/Sce default libraries for `-nodefaultlibs`. An offline + `-###` trace confirms both suppressions without producing an ELF. +- **FACT:** the SDK README points to external ELF-loader projects, but the exact + loader used for firmware 9.60 and its caller source are not part of the SDK or + the pinned local evidence set. +- **FACT:** unless otherwise marked, the SDK is GPLv3-or-later; FreeBSD headers + retain their BSD licenses. [SDK README](https://github.com/ps5-payload-dev/sdk/blob/d2e2e585740362976a39fdd5ccf390f199a7bc37/README.md) + +### PS5 SDL2 fork + +- **FACT:** inspected commit + `0baf4ac49382b537ba449901b5b6d0d189bb1fbb`, dated 2026-07-07. + [Commit](https://github.com/ps5-payload-dev/SDL/commit/0baf4ac49382b537ba449901b5b6d0d189bb1fbb) +- **FACT:** `src/video/ps5` implements a CPU framebuffer/VideoOut path and an + OSMesa context path. VideoOut registration and flip occur in the SDL backend, + not in `chimera-gfx` Phase 0. [PS5 video](https://github.com/ps5-payload-dev/SDL/blob/0baf4ac49382b537ba449901b5b6d0d189bb1fbb/src/video/ps5/SDL_ps5video.c) +- **FACT:** that backend owns direct-memory allocation, buffer tiling, + registration, flip submission, event waiting, and cleanup. Its public source + declares opaque VideoOut structures with `junk` fields; those layouts are not + copied into this project. +- **FACT:** SDL is Zlib-licensed at the pinned commit. The PacBrew recipe's + `license=('LGPL')` metadata conflicts with upstream `LICENSE.txt`; this + project follows the primary upstream license file. +- **FACT:** the native audio backend forces 48 kHz and uses AudioOut. + [PS5 audio](https://github.com/ps5-payload-dev/SDL/blob/0baf4ac49382b537ba449901b5b6d0d189bb1fbb/src/audio/ps5/SDL_ps5audio.c) +- **FACT:** native PS5 source directories exist for joystick, keyboard/IME, + filesystem, audio, video, and main; joystick code includes vibration and + light-bar operations. [PS5 joystick](https://github.com/ps5-payload-dev/SDL/blob/0baf4ac49382b537ba449901b5b6d0d189bb1fbb/src/joystick/ps5/SDL_ps5joystick.c) + +### PacBrew + +- **FACT:** inspected commit + `c2abcfcb60f569128abd0e8e70ad03a67bee5ea7`, dated 2026-07-07. + [Repository](https://github.com/ps5-payload-dev/pacbrew-repo/tree/c2abcfcb60f569128abd0e8e70ad03a67bee5ea7) +- **FACT:** the SDL2 recipe enables `SDL_OPENGL` and `SDL_LOADSO` but follows an + unpinned Git source with `sha256sums=('SKIP')`; this project therefore does + not treat that recipe as reproducible pinning. + [SDL2 recipe](https://github.com/ps5-payload-dev/pacbrew-repo/blob/c2abcfcb60f569128abd0e8e70ad03a67bee5ea7/SDL2/PKGBUILD) +- **FACT:** the Mesa 22.1.7 recipe enables OSMesa and gallium `swrast`, while + Vulkan, EGL, GBM, GLX, and GLES are disabled. + [Mesa recipe](https://github.com/ps5-payload-dev/pacbrew-repo/blob/c2abcfcb60f569128abd0e8e70ad03a67bee5ea7/mesa/PKGBUILD) +- **FACT:** package recipes exist for FBNeo, LakeSnes, Mednafen, DevilutionX, + and EDuke32. Their presence proves port work, not native GPU acceleration. + +### RetroArch + +- **FACT:** inspected commit + `32ee70cef5d4bdc32a4ca3b3b261209ce74b6e81`, dated 2026-07-16. + [Commit](https://github.com/libretro/RetroArch/commit/32ee70cef5d4bdc32a4ca3b3b261209ce74b6e81) +- **FACT:** the tree contains an SDL2 video driver and dynamic-library support + used for libretro cores. [SDL2 driver](https://github.com/libretro/RetroArch/blob/32ee70cef5d4bdc32a4ca3b3b261209ce74b6e81/gfx/drivers/sdl2_gfx.c) +- **FACT:** `Makefile.orbis`, `platform_orbis.c`, and an Orbis context driver are + available as PS4 integration references. +- **INFERENCE:** those PS4 files are useful for frontend lifecycle and build + concepts only. Their GPU assumptions must not be transferred to PS5. + +### PS5 Linux + +- **FACT:** inspected `ps5-linux-loader` commit + `8e7dd40df6144bed6194d165d48aa9468a7e13f3`, dated 2026-07-10. + [Repository](https://github.com/ps5-linux/ps5-linux-loader/tree/8e7dd40df6144bed6194d165d48aa9468a7e13f3) +- **FACT:** the loader targets a Linux kernel/initramfs boot flow and references + patched Linux components. +- **INFERENCE:** Linux amdgpu/RADV progress demonstrates that the hardware can + be driven under a custom Linux stack; it does not provide a native PS5 + userland ABI or a reusable driver implementation for this project. + +## ABI evidence table + +| Class | Area | Primary evidence | What the evidence supports | Policy | +|---|---|---|---|---| +| FACT | Export names | SDK `libSceGnmDriver.c` | names exist in the v0.41 stub set | resolve only | +| FACT | Target ISA | public SDK toolchain | compile target is x86-64 PS5 userland | does not prove a function ABI | +| INFERENCE | Runtime module name | stub filename plus SDK `.sprx` convention | `libSceGnmDriver.sprx` is a candidate | load only behind gates | +| UNKNOWN | GNM signatures | no pinned public header | no callable prototype is proven | never call | +| UNKNOWN | GNM structures | no pinned public definition | no layout is proven | never construct | +| UNKNOWN | Firmware stability | no public compatibility matrix | 9.60 is a build identifier, not known compatible firmware | 9.60 build only; never infer compatibility | +| FACT | SDK payload startup | pinned `crt/crt.c` and `crt/patch.c` | pre-main kernel process-state writes occur | execution blocked | +| FACT/UNKNOWN | SPRX loader lifecycle | pinned rtld source/system-module implementation boundary | load/start and stop/unload are requested; system internals unknown | offline audit only | +| UNKNOWN | Embedded-shader semantics | export names only | no identifier or binary contract | never call | +| UNKNOWN | Submission/sync semantics | export names only | no ownership or timeout contract | never call | +| FACT | SDL2 public API | pinned SDL headers and `LICENSE.txt` | application API and Zlib terms at that commit | Phase-1 candidate only | +| FACT | Pinned SDL implementation | pinned PS5 SDL source | exact source-level VideoOut sequence at that commit | indirect through SDL only | +| UNKNOWN | SDL runtime behavior | no approved observation | firmware compatibility, timeout, and cleanup on PS5 | do not execute | +| FACT/UNKNOWN | minimal startup | compiler can omit stock CRT; exact loader caller is absent | source-level omission only, not safe return | do not build an ELF | + +The complete Phase-0 lookup subset and per-symbol evidence are in +`manifests/ps5_gnm_symbols.json`. Symbol presence never upgrades ABI confidence. + +## Unknowns register + +| ID | Unknown | Why it blocks progress | Evidence needed | +|---|---|---|---| +| U-001 | exact signatures for every candidate GNM export | a wrong call can corrupt memory immediately | acceptable public header/source or independently validated ABI evidence | +| U-002 | command-buffer and packet formats | cannot safely build GPU work | public, licensed format evidence plus review | +| U-003 | GPU-visible memory allocation and cache rules | resources may alias or be incoherent | proven userland allocation contract | +| U-004 | resource registration ownership/lifetime | cleanup and crash safety are unknown | proven signatures and lifecycle evidence | +| U-005 | queue, fence, and timeout semantics | a wait or queue action could hang | bounded synchronization contract | +| U-006 | VideoOut/GNM buffer compatibility | presentation ownership is unknown | separate, minimal Phase-1 evidence | +| U-007 | embedded shader identifiers and binary contract | cannot safely bind a shader | licensed public evidence and test plan | +| U-008 | runtime module name across firmware | even discovery may fail | approved hardware observation per firmware | +| U-009 | system-module initialization side effects | SDK call chain is known; firmware module internals are not public | acceptable public evidence plus separately approved observation | +| U-010 | supported firmware set | no hardware evidence exists | one manually approved probe at a time | +| U-011 | bounded SDL flip-event wait | safe timeout/cleanup cannot be guaranteed | licensed bounded-wait change or proven supervisor semantics | +| U-012 | SDL cleanup after forced process termination | recovery behavior is unknown | approved observation or public lifecycle evidence | +| U-013 | kernelwrite-free PS5 payload startup | SDK v0.41 CRT patches kernel process state before `main`; Phase 0.5 cannot prove a safe replacement | exact pinned loader caller plus complete entry/return/cleanup/crash audit | +| U-014 | loader changes before `_start` | incoming args already expose kernel access, but the creation path is absent | exact loader source and configuration used for firmware 9.60 | + +Unknowns are closed only by updating this file, the relevant ADR, and the +firmware matrix with a primary source or an explicitly approved observation. + +## Assumptions register + +Assumptions are design choices, not compatibility claims. + +| ID | Assumption | Scope | Falsification or review trigger | +|---|---|---|---| +| A-001 | `libSceGnmDriver.sprx` is a useful runtime candidate | discovery-only probe | approved lookup fails or a public source establishes another module | +| A-002 | Mock limits of 4096 pixels and 16 live resources are sufficient for lifecycle tests | host tests only | an adapter test requires a larger deterministic bound | +| A-003 | RGBA8 is enough to stabilize the initial public upload contract | mock and software-frame planning | RetroArch/SDL integration proves another minimum format is required | +| A-004 | The existing SDL CPU-framebuffer route is safer than duplicating its opaque VideoOut layout | first Phase-1 design | official public VideoOut headers and bounded lifecycle become available | +| A-005 | 1920x1080 and one fixed frame minimize first-test state | disabled Phase-1 candidate | exact firmware/display evidence requires a different supported mode | +| A-006 | A firmware-specific rebuild must receive a new approval because its digest changes | all hardware candidates | never relaxed; enforced artifact-provenance rule | diff --git a/ROADMAP.md b/ROADMAP.md new file mode 100644 index 0000000..517158c --- /dev/null +++ b/ROADMAP.md @@ -0,0 +1,225 @@ +# Roadmap and gates + +Progress is gate-based. A later phase may be designed and compiled offline, +but no hardware behavior may be transferred or executed before its gate. + +## Phase 0 — research and non-rendering implementation (offline complete) + +- [x] Standalone repository boundary +- [x] Primary-source research snapshot and upstream pins +- [x] Threat model, ABI evidence table, and unknowns register +- [x] Versioned context, capability, resource, present-model, and cleanup API +- [x] Deterministic non-rendering mock and fail-closed PS5 backend +- [x] Compilable RetroArch and SDL2 adapter interfaces +- [x] Host unit/integration tests and static safety audit +- [x] Checksummed SDK bootstrap and pinned container recipe +- [x] Read-only capability manifest and compile-only probe +- [x] SPDX SBOM and artifact-manifest schema/tooling +- [x] Independent final review record (`docs/reviews/phase0-final-2026-07-17.md`) +- [x] Exact 9.60 discovery-only offline build authorization and runtime audit +- [x] Identify SDK v0.41 pre-main kernelwrite execution blocker +- [x] Phase-0.5 stock CRT, compiler, linker, loader-contract, and transitive + object audit +- [x] Permanent artifact denylist and fail-closed Payload Manager policy gate +- [x] Block minimal startup construction because safe loader return is unproven +- [x] Phase-0.6 exact installed Payload Manager/elfldr identity and source audit +- [x] Controlled runtime profile and fail-closed exact-firmware/hash/budget gate +- [x] Stop lifecycle-probe construction on persistent and unbounded effects +- [x] Phase-0.7 hardened elfldr with bounded ptrace, complete restoration, + cleanup, watchdog, and receiver-side hash/denylist enforcement +- [x] Phase-0.7 controlled Payload Manager with same-FD hash-to-stream, + loopback-only versioned transport, and atomic non-autoload upload +- [x] Phase-0.7 exact 9.60 lifecycle probe, double clean cross-build, linker + map, disassembly, complete callgraphs, and machine proof matrix +- [x] Hash-bound installation-review and rollback preparation without console + contact +- [x] Phase-0.9D existing-stack endpoint, flag, path, readback, and recovery + audit; no PS5-to-host file route found +- [x] Phase-0.9E bounded bootstrap provenance search and opaque candidate audit +- [x] Phase-0.9E-R official release/tag/source/sender audit; local backup + classified `LOCAL_BACKUP_NOT_CORRELATED` +- [x] Phase-0.9E-R2 inner, SIECAF, community, browser, and exact MediaFire + correlation; local backup classified `LOCAL_BACKUP_UNCORRELATED` +- [ ] Exact-used Y2JB/other host package and port-9020 listener provenance +- [ ] Independent host-to-memory, output, restart, and live-file contracts +- [ ] Phase-0.9F offline rescue-payload design gate opened +- [x] Two exact, separately approved Phase-1.0D one-shot executions recorded; + C1 proven and RUN-B D-stage unclassified +- [x] Phase-1.0E inherited-stdout result channel built, host-tested and audited + and exercised once with exact authorization; D00-D02 proven, incomplete + before D03, authorization consumed +- [x] Phase-1.0F I00-I14 startup-interval artifact built reproducibly and + audited offline; no transfer, execution or result reception authorized +- [x] Phase-1.0G manifest-only one-shot host runner prepared and fake-socket + tested; durable pre-connect attempt receipt, no device authorization +- [x] One exact Phase-1.0G run consumed; D00-D02/I00-I03 and deterministic + no-argument/no-menu exit proven, no retry or device write +- [x] Phase-1.0H minimal `-v` startup-argument correction built twice and + audited offline; exact artifact remains device-ineligible +- [x] One exact Phase-1.0H run consumed; I04/SDL/VideoOut/buffer registration + proven, first flip submit `-1`, no retry +- [x] Phase-1.0I offline source/map/disassembly postmortem; exact submit tuple + proven, write operation and submit errno remain unobserved + +Exit evidence: host tests pass, safe PS5 targets compile, generated files are +current, no deploy target exists, Git is clean, private origin is synchronized, +and no secrets or unreviewed binary artifacts are tracked. + +## Phase 1 — controlled presentation experiments + +Gate: explicit artifact-specific hardware authorization plus all controls in +`SAFETY.md` and `docs/phase1/`. + +Offline preparation: + +- [x] Minimal SDL2 CPU-framebuffer/VideoOut clear design +- [x] Pinned SDL build with reviewed video-only overlay +- [x] Fail-closed compile target and artifact-manifest workflow +- [x] Firmware/ABI checklist and cleanup/rollback plan +- [ ] Proven bounded flip wait or safe process supervisor +- [x] Exact firmware supplied for an offline discovery build (`9.60`) +- [x] Bounded hardened lifecycle prepared offline with only the explicit + removable controlled-artifact write budget (Phase 0.7) +- [ ] Hardened runtime separately approved, installed, and hash-verified +- [ ] Successful separately approved discovery probe +- [ ] Artifact-specific VideoOut execution approval + +After those gates, perform one CPU-filled frame through the existing SDL +VideoOut route. Only then may a separate GNM resource/synchronization proof be +designed, with every ABI reviewed independently. Embedded shaders remain an +evidence and licensing question, not an implementation assumption. + +## Phase 2 — native 2D blitter + +Texture upload, fullscreen primitive, nearest/bilinear sampling, aspect and +integer scaling, overlays, buffering, and fences. Each feature requires a mock +contract and bounded-failure test first. + +## Phase 3 — RetroArch adapter + +- [x] Phase-1.0A real PS5 headless frontend with static deterministic smoke core +- [x] Phase-1.0A SDL2/RGUI software profile linked offline +- [x] Host ASan/UBSan smoke-core integration and static target audit +- [x] Phase-1.0D CRT canary reached visible C1 on firmware 9.60 +- [x] Phase-1.0D early diagnostic produced one unreadable notification; + exact stage and graphics progress remain unproven +- [x] Phase-1.0E machine-readable result-channel candidate prepared offline +- [x] Separately authorized one-shot Phase-1.0E result-channel device test; + D02 platform result `0`, EOF before D03, no retry/reconnect +- [x] Phase-1.0F stream-only D02-to-D03 interval diagnostic prepared offline; + exact artifact is device-ineligible pending separate future review +- [x] Phase-1.0G fail-closed one-shot runner prepared offline; tracked manifest + and approval template are inactive and contain no target +- [x] Phase-1.0G result identifies the pre-SDL configuration exit before I04; + follow-up artifact requires a new offline design and exact permission +- [x] Phase-1.0H startup wrapper now models argc 2 without content/menu/config; + H runner requires exact dual authorization and currently grants none +- [x] Phase-1.0H runtime reached SDL VideoOut but failed its first flip submit; + visible presentation, runloop and cleanup remain unproven +- [x] Phase-1.0I bounds the E118 candidates and records the frame-zero + inconsistency without claiming a root cause or authorizing a retest +- [x] Phase-1.0M removes the source-bound playlist `mkdir` while preserving + the global write firewall and reproducible artifact audit +- [x] Phase-1.0N binds the M artifact to an inactive one-shot runner contract +- [x] Phase-1.0O proves M reaches I04 and reproduces the VideoOut flip-submit + failure with result `-1` and saved errno `0`; authorization is consumed +- [x] Phase-1.0P proves the exact submit boundary and deterministic D12/D04 + order while keeping the VideoOut ABI/root cause fail-closed +- [x] Phase-1.0Q finds only one PS5 VideoOut declaration lineage and blocks + parameter experiments for lack of independent public ABI evidence +- [x] Phase-1.0R proves SDL2main adds no app/display registration, binds direct + and manager launches to the same elfldr constructor, and blocks target + changes because the separate hbldr/shsrv context remains unproven +- [x] Phase-1.0S binds official hbldr/shsrv: it proves BigApp substitution but + blocks route reuse because deployed identity, firmware behavior and safe + bounded device operation remain unproven +- [x] Phase-1.0T proves the existing shsrv shell cannot provide exact deployed + identity, records unavoidable greeting effects, and designs an inactive + redacting metadata gate without a network client +- [x] Phase-1.0U performs the bounded local artifact inventory; no original + shsrv target, receipt or transfer log is found, so direct host hashing is + unavailable and global absence is not claimed +- [x] Phase-1.0V implements a bounded offline one-shot Telnet/sanitization + model with inactive approval and no network transport +- [x] Phase-1.0W self-reviews/remediates the collector and proves an inactive + dual-record client policy plus fake one-shot transport +- [x] Phase-1.0X implements offline-only injected transport orchestration, + consumed-attempt receipts and exclusive sanitized output; no real + connection is authorized +- [x] Phase-1.0Y audits prompt/Telnet/completion framing, identifies the raw + and libtelnet/NVT source families and models both offline; deployed + identity and exact completion stay unproven +- [x] Phase-1.0Z implements an offline passive, source-family-tolerant + LF-batch contract with deadline-only sealing, strict completeness and no + Telnet command emission or live transport +- [x] Phase-1.0AA integrates Z bytes, synthetic deadline and exclusive X + evidence through an exact built-in fake adapter only +- [x] Phase-1.0AB audits the exact local runtime and models receipt, + nonblocking connect, complete send, bounded receive, deadline and close; + hard scheduling and remote cleanup remain partial/unproven +- [x] Phase-1.0AC implements a dormant target-free adapter around an exact + built-in fake syscall facade; no socket import, address or activation + path exists +- [x] Phase-1.0AD defines an inactive numeric-target and activation-record + contract with exact launcher/payload/approval hashes and no live ability +- [x] Phase-1.0AE selects the nonpersistent official shsrv v0.7 lineage and + rejects v0.19 fake-app/remount behavior; target implementation is blocked +- [x] Phase-1.0AF implements an injected host-only BigApp lifecycle model with + bounded ticks, unique-child correlation and exhaustive failure cleanup +- [x] Phase-1.0AG implements a bytes-only bounded ELF64 admission contract; + the historical M bytes are absent and therefore not newly admitted +- [x] Phase-1.0AH bounds the loader/CRT relocation split and exact per-artifact + DT_NEEDED inventory without mapping or loading anything +- [x] Phase-1.0AI models allocation, exact copy/BSS, RELATIVE application, + final permissions, sync, commit and full-region rollback +- [x] Phase-1.0AJ binds the v0.7 and hardened elfldr primitive sources, + rejects direct v0.7 loader reuse and records the remaining composition + and cleanup-ownership gaps +- [x] Phase-1.0AK models hybrid primitive composition, every temporary JIT + resource and fail-closed child termination after cleanup failure +- [x] Phase-1.0AL audits exact SDK `mdbg_copyin` partial-copy and credential + restoration semantics and blocks direct reuse +- [x] Phase-1.0AM models bounded exact-progress copying, independent all-field + restoration, child cleanup and compromised-service containment +- [x] Phase-1.0AN binds service fail-stop behavior and proves restart ownership, + `PT_IO` hard preemption and exact progress remain absent +- [x] Phase-1.0AO models one-shot worker preemption under an explicit supervisor + with no automatic restart, retry or real capability +- [x] Phase-1.0AP audits current official SDK/shsrv worker creation, termination, + identity and result-channel evidence; only creation is a source candidate +- [x] Phase-1.0AQ implements a fixed 128-byte worker-result record with + precommitted nonce, attempt, PID pair and monotonic generation +- [x] Phase-1.0AR models exclusive single-writer framing, every partial-read + split, deadline/EOF, overflow and containment without live transport +- [x] Phase-1.0AS audits public pipe/poll, rfork FD semantics, close ownership, + nonblocking reads and monotonic deadline evidence +- [x] Phase-1.0AT models RFFDG inheritance, exclusive pipe-end close order, + EINTR and one absolute deadline through fake operations only +- [x] Phase-1.0AU reassesses live channel feasibility: public primitive + signatures are complete, but official safe composition and runtime proof + remain absent +- [x] Phase-1.0AV defines a target-free launch-context A/B canary with identical + payload bytes, separate approvals and a distinct post-D04 terminal +- [x] Phase-1.0AW binds the exact canary source delta, v0.7 raw stdout candidate + and blocking launcher effects; no artifact is permitted +- [x] Phase-1.0AX implements byte-exact CHD10AV1/D14 framing and the cleanup + predicate for host tests only +- [x] Phase-1.0AY selects the exact inactive Phase-1.0N source base and requires + a separate worktree, preserving the current checkout +- [ ] Implement and host-test the AV source structure in that isolated worktree + without a target profile, cross-build or artifact +- [ ] First static emulator core with legally redistributable test content +- [ ] Dynamic core loading contract and implementation + +Hardware-rendered libretro contexts remain disabled. + +## Phase 4 — SDL2 accelerated renderer + +Reusable SDL renderer backend and simple shaders, without replacing unrelated +native SDL platform facilities. + +## Phase 5 — separately evaluated context expansion + +Consider a broader hardware-render context or small OpenGL subset only after +the 2D path is stable. A full Vulkan or Mesa driver is explicitly not an early +goal. diff --git a/SAFETY.md b/SAFETY.md new file mode 100644 index 0000000..eef5fd2 --- /dev/null +++ b/SAFETY.md @@ -0,0 +1,610 @@ +# Safety and threat model + +## Non-negotiable boundary + +This project uses only userland behavior and public, open-source information. +It excludes kernel and hypervisor code, exploit development, DRM bypass, +proprietary SDK material, leaked or decrypted headers/binaries, direct MMIO or +register writes, clock/SMU/fan control, boost settings, and firmware patches. + +No payload or ELF may be transferred to or executed on a PS5 without explicit +approval from Jens in the active task. There is no boot-time or automatic +execution path. + +Documented temporary process/kernel runtime changes made by the exact public +loader or SDK may be classified as expected volatile, restored by the loader, +or payload-process-local. They are not automatically unsafe. Persistent writes +and unbounded or unknown effects remain hard blockers. + +## Assets and adversaries + +Protected assets include the console's stability, display availability, user +data, network credentials, private repository credentials, and the accuracy of +the project's compatibility claims. + +Relevant failure or threat sources: + +- an incorrect guessed ABI that corrupts stack or memory; +- a command submission that hangs the GPU or display path; +- unbounded waits or incomplete cleanup after partial initialization; +- symbol presence being mistaken for compatible semantics; +- firmware drift; +- malicious or compromised upstream artifacts; +- secrets entering logs, manifests, commits, or remote URLs; +- a build or CI target silently becoming a deployment path. + +## Phase-0 controls + +| Risk | Control | Verification | +|---|---|---| +| ABI guess is called | No GNM declarations or calls; symbol addresses never escape | `phase0_safety_audit` test | +| GPU work is submitted | No submit/draw/dispatch/flip call sites | source allowlist audit | +| GPU memory changes | No GPU allocator, mapper, resource registration, or command buffer | source and build-graph audit | +| Probe runs accidentally | target disabled by default; runtime acknowledgement; firmware gate | CMake tests and code review | +| Unknown firmware runs | default is `NONE`; discovery build accepts only exact `9.60` | unit test, CMake gate, compatibility file | +| Automatic deployment | no deploy/test/run target; output is compile artifact only | CMake audit | +| Supply-chain substitution | immutable upstream commit and release SHA-256 | lock manifest and bootstrap script | +| Secret disclosure | secret filename ignores, no credential scripts, boolean-only logs | repository scan before push | +| Adapter accidentally claims hardware | compiled queries report unavailable and reject hardware requests | adapter tests | +| Resource lifecycle leaks | context refuses destruction while child handles exist | mock integration tests | +| Phase-1 target enters normal build | separate option defaults off and requires PS5 plus explicit SDL path | configure and source audit | +| Pre-gate SDL side effect | candidate does not link SDL2main; gate precedes `SDL_Init` | source-order audit and ELF imports | +| SDK CRT changes kernel state before `main` | classify exact effects; permanent legacy artifact remains blocked | pinned-source runtime audits and ADR-0010 | + +## Probe side-effect statement + +After its firmware gate, project code requests only system module loading, +symbol lookup, boolean logging, and module unloading. It does not request a GNM +operation. The pinned SDK source proves that `dlopen` can call +`sceKernelLoadStartModule` and that `dlclose` can call +`sceKernelStopUnloadModule`; module start/stop internals remain unknown. + +More importantly, SDK v0.41's payload CRT runs before `main`. Its startup calls +`__patch_init`, which writes process credential capability/attribute fields and +syscall-address limits through the SDK's kernel read/write primitives. The CRT +also sets libc `__isthreaded`, initializes syscall/kernel/log/rtld state, may +load `libSceSysmodule.sprx`, allocates loader bookkeeping, relocates the +payload, and runs constructors. Normal termination runs payload destructors; +project `dlclose` requests module stop/unload only when this open loaded it. +There is no guaranteed cleanup after a hang, crash, partial load, or failed +stop/unload. + +Therefore **no GPU mutation or rendering operation is requested by project +code**. The existing ELF remains permanently ineligible by artifact-specific +denylist, independently of the corrected Phase-0.6 classification model. + +The firmware-9.60 capability artifact with SHA-256 +`4be1c17b4964f2b68c39b5145bc4af4619c32512d60269ecf5c39728b390fa63` +is permanently blocked in `manifests/artifact-denylist.json`. Its manifest +states `execution_eligible=false`, and all new manifests default to the same +false value. `tools/check_artifact_execution_policy.py` must be the first +static gate used by repository tooling and Payload Manager integrations: +missing or false eligibility, malformed input, changed bytes, or a denylist +match returns `DENY`. A pass is not execution authorization. + +## Phase-0.5 startup result + +The stock v0.41 CRT is `UNSAFE`. Omitting it is mechanically possible, but the +loader caller and its return, cleanup, crash, and pre-entry behavior are +`UNPROVEN`. No minimal startup source or ELF exists, and the corresponding +CMake option fails closed. See `docs/runtime/` and +`manifests/runtime/phase-0.5-startup-audit.json`. + +## Phase-0.6 historical lifecycle result + +The installed Payload Manager v0.3.1 and elfldr v0.23 are exact public-release +matches. Temporary QA flag, credential, ptrace, mapping, and SDK startup +effects are classified individually. The lifecycle still fails closed because +it has `PERSISTENT_WRITE` and `UNBOUNDED_OR_UNKNOWN` effects: unbounded ptrace +loops, no detached-child watchdog, unresolved return/reaping/cleanup, +incomplete credential restoration, no launch-time hash check, and a manager +upload path that writes `/data/pldmgr/payloads`. + +That result remains immutable evidence for the unmodified public-release +chain. It is not the current runtime profile. + +## Phase-0.7 offline deployment-readiness result + +The hardened elfldr and controlled Payload Manager close the Phase-0.6 +implementation blockers with bounded ptrace steps, complete checked credential +restoration, centralized cleanup, a two-second kill/reap watchdog, exact +same-file-descriptor hashing, and receiver-side digest/denylist enforcement. +The lifecycle ELF is a normal SDK v0.41 payload that sends one notification +and calls `_exit`. + +The current `controlled-ps5-runtime` profile binds exact firmware 9.60, SDK, +source commits, hashes, sizes, expected effects, a removable controlled +artifact-directory write budget, no payload network, 2000 ms maximum runtime, +and no retry. It is `READY_FOR_HARDENED_RUNTIME_DEPLOYMENT` only: +`installed=false`, `execution_authorized=false`, `transferred=false`, and +`executed=false`. + +No PS5 connection, transfer, installation, or execution was used to reach +this decision. Hardware behavior remains unproven. The normal CRT +`__patch_init` path is process-local but means the lifecycle is not a +kernelwrite-free artifact. The old blocked hash remains permanently denied. +See `docs/runtime/phase-0.7-hardening.md` and +`manifests/runtime/phase-0.7-offline-audit.json`. + +## Phase-0.8 read-only preflight result + +The offline collector-admissibility review is +`READ_ONLY_PREFLIGHT_BLOCKED`. The exact Payload Manager v0.3.1 HTTP handler +sets an in-process active flag for every non-`OPTIONS` request. +`/autoload_status` additionally sets `autoload_triggered` and reads the +autoload configuration. Direct filesystem collection has no pinned collector +or proof excluding atime, audit-log, cache, metadata, service-state, or +on-device-log changes. + +No on-device session was started and no PS5 connection was made. Current +firmware, live object identities, listeners, startup state, and rollback +backups remain unobserved. The exact stock Payload Manager backup is therefore +a hard open gate. Installation, lifecycle, execution, and automatic retry +remain unauthorized. See `docs/runtime/phase-0.8-read-only-preflight.md` and +`manifests/runtime/phase-0.8-read-only-preflight.json`. + +## Phase-0.9A anti-brick design result + +The offline anti-brick layer is `DESIGN_ONLY`. It models exact pre-image +identity, separate reopened backup, inactive staging, quiescence, an atomic +switch, post-switch verification and separately authorized rollback. No such +PS5 switch or recovery primitive is claimed. All fourteen interruption +boundaries remain `UNPROVEN`, so the design is not installation-suitable. + +The host simulator performs only in-memory logical writes and cannot connect, +transfer, install, execute, open a target artifact or start a compiler. Its +synthetic atomic primitive is fault-test scaffolding, not hardware evidence. +The byte-exact on-device Payload Manager backup and recovery independent of +both elfldr and Payload Manager remain catastrophic hard gates. No +authorization is granted and automatic retry remains false. + +## Phase-0.9B observer feasibility result + +The offline result is `BLOCKED`. Normal SDK v0.41 startup is not +kernelwrite-free because `_start()` reaches `__patch_init()` before `main`. +Omitting the CRT is mechanically possible, but safe custom entry, return/exit, +crash handling and loader cleanup remain `UNPROVEN`. + +The hash-bound hardened elfldr route deliberately gives the payload no +stdout/stderr socket, and the controlled Payload Manager transport does not +receive payload output. Notifications, klog and stock manager HTTP are not a +proven bounded non-persistent result channel. No observer source, target, ELF, +linker map or package was created. All device authorizations remain false, and +no PS5 action occurred. See +`docs/runtime/phase-0.9b-observer-static-audit.md`. + +## Phase-0.9C execution-feasibility result + +The offline classification is +`BLOCKED_MULTIPLE_FOUNDATIONAL_CONTRACTS`. Source review proves that a normal +SDK entry is side-effecting before `main`; a freestanding entry has no closed +stack/BSS/relocation/TLS, read, monotonic-time, return, exit, or cleanup +contract. A normal `ret` resumes an unproven SceSpZeroConf continuation, and a +watchdog kill is not accepted as safe exit. + +The 4096-byte D1 result format passes deterministic host tests, but no current +caller-owned target buffer, copy-out, cleanup finalizer, or manager delivery +exists. Firmware source two is absent; an SDK export name alone is not an ABI. +Filesystem and platform reads may still affect metadata, atime, audit, caches, +counters, service/security state, open bookkeeping, accounting, object +lifetime, or races. No target source, ELF, map or package was built, and no PS5 +action or authorization occurred. See +`docs/runtime/phase-0.9c-static-audit.md`. + +## Phase-0.9D existing-stack readback result + +The offline decision is `BLOCKED_NO_READBACK_PATH`. The full and controlled +Payload Manager profiles contain uploads, installs, deletes, launch-time file +reads, directory/configuration observations, and process actions, but no +binary-safe file-download response. Hardened elfldr returns fixed status text; +its controlled manager transport sends and closes without receiving component +bytes. + +The full-profile `server_active_flag` is process-local and only suppresses a +fallback notification in audited source. It is `LOW_VOLATILE`, but has no +in-process reset and must be explicitly accepted in any future permission. +`/autoload_status` is excluded because it sets `autoload_triggered` and can +alter worker timing. No PS5 was contacted, no file was transferred, and no +target, observer, client, backup, package, staging action, or authorization was +created. See `docs/runtime/phase-0.9d-readback-feasibility.md`. + +## Phase-0.9E bootstrap provenance gate + +The exact external exploit/host and rudimentary port-9020 listener must be +available and provenance-bound before they can be considered an independent +rescue executor. A matching filename, an opaque console backup, a public +upstream, a generic sender command, or a conceptual dependency graph is not +enough. Unknown filesystem staging or autoload effects are brick-relevant and +remain hard stops. + +No Phase-0.9F target design is authorized while the receive, mapping, +entrypoint, live-file, output, cleanup, crash, and reboot contracts are +missing. Phase 0.9E permits only local hashing, source/binary inspection, +schemas, manifests, and host-only policy tests. It permits no network socket, +device client, transfer, target build, payload, install, execution, autoload, +retry, or recovery action. + +Phase 0.9E-R permits only bounded official `Gezine/Y2JB` GitHub acquisition +and private-origin push. Official metadata excludes every current release +asset from matching the local backup, and official source contains no +port-9020 listener implementation. The ignored official source tree is +inspection-only; downloaded code was not executed. No outer association may +be promoted to opaque inner provenance, and neither an operator attestation +nor host tests are runtime evidence. All device, build, transfer, execution, +installation, lifecycle, autoload, write, and retry authorizations remain +false. + +Phase 0.9E-R2 permits only host-side hashing, bounded browser metadata copies, +the exact MediaFire object page, one mandatory official backup, and +source-motivated community assets. Downloaded archives are static evidence: +never execute or restore them, never track them, and remove temporary copies. +The SIECAF parser is read-only and may inspect only public fixed-width +structural metadata; it may not decrypt, guess keys, extract, write, or invoke +`ps5-bar-tool`. MediaFire metadata, structural similarity, and an inner match +would each remain non-runtime evidence. The actual result is +`LOCAL_BACKUP_UNCORRELATED`, so Phase 0.9F and every authorization remain +blocked. + +## Phase-1.0E inherited result boundary + +Prior RUN A and RUN B permissions were artifact-specific and consumed. RUN A +visibly reached C1; RUN B produced an unreadable notification and no exact +D-stage may be inferred. Phase 1.0E may record those observations and build a +new execution-ineligible diagnostic candidate offline. Its later exact +one-shot authorization was consumed on 2026-07-22. + +The only added target operation is one nonblocking `send` attempt per reached +D-stage through stdout inherited from hardened elfldr's legacy raw-ELF +connection. It may not open a target socket, connect, listen, read requests, +retry, write the device filesystem, install or activate autoload. The host may +model one connect/send/write-half-close/bounded receive with fake sockets, but +no real device use occurs without new exact artifact-specific permission that +also names result reception. EOF, timeout, partial frames and send failure are +terminal, never retry triggers. The normal SDK CRT effects remain explicit; +this result channel is not side-effect-free. RUN C used the permitted single +connection and validated D00-D02 before remote EOF. It did not retry or +reconnect. D03, SDL, VideoOut, rendering, terminal status and cleanup remain +unproven. All authorization fields and artifact eligibility are now false. + +## Phase-1.0F startup interval boundary + +Phase 1.0F may prepare an exact artifact offline to place I00-I14 checkpoints +inside the source interval after D02 and through entry to D03. These +checkpoints may only reuse the existing nonblocking, signal-suppressed inherited +stdout send. They do not send notifications and may not add a socket, connect, +listener, receive path, address, port, filesystem write, install, autoload, +retry or reconnect. + +The distinct `CHD10F01` parser remains offline-only: the live CLI is not +activated for it and must continue to reject the ineligible manifest. Normal +SDK CRT patch effects and the existing SDL/VideoOut imports remain explicit; +the artifact is not side-effect-free and static success is not hardware +evidence. No prior authorization carries forward. Device connection, transfer, +result reception and execution each require a new exact permission. + +## Phase-1.0G one-shot runner boundary + +Phase 1.0G may prepare and host-test a manifest-driven runner, but its tracked +manifest and approval template remain inactive. The interval protocol cannot +be selected through a free command-line switch. Activation requires both an +exact active manifest and a separate untracked local approval whose artifact, +firmware, target, protocol, limits and unique run identifier agree. + +The runner must rehash the actual artifact before transport and exclusively +create and `fsync` a consumed-attempt receipt before opening a socket. This +fail-closed receipt prevents a host crash from silently making the same +permission reusable. Retry, reconnect, resume and trace overwrite remain +forbidden. The current repository contains no active target, run identifier or +authorization; no device action is allowed by Phase 1.0G itself. + +The later exact Phase-1.0G authorization was consumed by one attempt. Valid +frames ended at I03 and ordinary stdout proved the deliberate no-argument, +no-menu help/exit path before I04. There was no retry or reconnect. This result +does not authorize another action and does not prove SDL, VideoOut, rendering +or terminal cleanup. + +## Phase-1.0H startup-argument boundary + +Phase 1.0H may correct only the proven `!HAVE_MENU && argc == 1` exit. The +reviewed profile adds RetroArch's existing verbose flag, generating `-v`, and +must retain null content/config/core paths, static contentless core, write +firewall, bounded runtime and the inherited one-send-per-stage stream. It must +allow H only through an exact active manifest plus separate matching local +approval, never a free protocol switch. No target, run ID, retry, reconnect, +installation, autoload or device-write authority may be tracked by default. + +The exact H artifact is offline evidence only. It remains transfer-, execution- +and installation-ineligible, and all authorizations are false. Phase-1.0G +authority was consumed and cannot authorize H. A later action requires a new +exact artifact-specific permission; static success does not prove I04, SDL, +VideoOut, rendering, terminal status or cleanup. + +The later exact Phase-1.0H authorization was consumed once. Its trace reached +I04, SDL2 video, VideoOut open and buffer registration, then the first flip +submit returned `-1` and SDL init returned `-1`. The diagnostic pattern was +copied into mapped display memory before the failed submit. D12 also records a +write-firewall rejection during configuration parsing; the exact requested +operation is unobserved. No retry is authorized. Do not infer a visible frame, +runloop entry, complete cleanup or safe exit. + +## Phase-1.0I offline postmortem boundary + +Phase 1.0I may inspect only the consumed H trace, exact source, ignored local +artifact/map and disassembly. It proves the submitted tuple +`(handle, 0, 1, 0)` and narrows E118 to linked `OPEN` or `STREAM` wrappers. It +does not know the exact blocked operation, original submit `errno`, VideoOut +argument semantics or root cause. The diagnostic/normal frame-zero/frame-one +mismatch is a source candidate only. + +No target build, artifact, device client, connection, transfer, execution, +result reception or retry belongs to Phase 1.0I. A later offline design must +capture errno before reporting, transmit the exact write operation and stop +before I04 on a firewall shutdown. Selecting a different frame ID requires a +new ADR, new artifact audit and separate future authorization. + +## Phase-1.0O consumed write-free result + +The exact M artifact was transferred and executed once. The exclusive receipt +was created before the only connection; there was one send, zero retries and +zero reconnects. No installation, autoload, persistent staging or device +filesystem write occurred. The diagnostic pattern did mutate mapped display +memory and one VideoOut flip submit was attempted, as bounded by the approval. + +The run passed I04 with no D13, then reproduced the first flip-submit failure. +D07 reports `-1` and saved errno `0`; E104 is the overlay's generic +`framebuffer_fail` label after that failure, not proof of an allocation fault. +The subsequent D04 after terminal-flagged D12 prevents a successful terminal +classification. Do not infer visible output, event-wait behavior, complete +cleanup or safe exit. The authorization is consumed and no action carries. + +## Phase-1.0P offline VideoOut analysis + +Phase 1.0P performs source, map, relocation and disassembly inspection only. +It proves the consumed artifact called `sceVideoOutSubmitFlip(handle,0,1,0)` +and received `-1` with saved errno `0`. It does not prove the semantic ABI, +opaque buffer contract, flip ownership, visible presentation or cleanup. + +An exported symbol name is not permission to call a status or flip-master +function. No argument experiment, target build, parser relaxation or device +action is permitted by this analysis. The D12-before-D04 order is +source-deterministic, and D12 remains a shutdown request rather than proof of +lifecycle completion. + +## Phase-1.0Q public evidence boundary + +The bounded official-source inventory found no independent PS5 VideoOut ABI. +SDK v0.41 provides export names only; the PS5 declarations and opaque records +originate together in one SDL lineage, and relevant official ports consume +that same fork. OpenOrbis defines only the analogous PS4 contract. + +Do not promote source repetition, a successful registration return, a PS4 +constant or an exported status/flip-master name into PS5 semantic proof. No +submit parameter, buffer layout, ownership state or error interpretation may +change from Phase 1.0Q, and no target or device action is authorized. + +## Phase-1.0R launch-context boundary + +The exact PS5 SDL2main adds no application ID, title identity, LNC setup, +VideoOut ownership or process creation. Its `LoadExec("exit")` occurs only +after `SDL_main` returns, and the tested RetroArch path already performs SDL's +splash-hide call before VideoOut open. Direct and Payload Manager raw-ELF +routes use the same hardened elfldr process constructor. + +Do not treat PacBrew packaging, `homebrew.js` path/argument descriptors, +LakeSnes documentation, or the non-unique LNC log as proof of a different +working display context. The exact hbldr/shsrv launcher and runtime active-app +state are unbound. Keep +`NO_SOURCE_PROVEN_LAUNCH_CONTEXT_FIX_TARGET_CHANGE_BLOCKED`: no SDL2main, +LNC/SystemService, submit, VideoOut, target-build, transfer or execution +change is authorized. + +## Phase-1.0S hbldr/shsrv boundary + +Official source proves that hbldr is materially different from raw elfldr: it +launches a BigApp through SystemService and replaces the resulting process +with a device-resident ELF. This is source evidence for a launch-context +difference, not proof of VideoOut permission, visible output, safe cleanup or +firmware-9.60 behavior. The exact deployed shsrv identity is unknown. + +Never invoke or copy the existing route under this gate. It may kill the +running BigApp, performs kernel/ptrace process changes, lacks a hard deadline, +requires prior target staging, and current versions may remount `/system_ex` +and create persistent `FAKE00000` content without an atomic write, rollback or +power-loss protocol. Keep +`BIGAPP_CONTEXT_SOURCE_PROVEN_DEPLOYED_IDENTITY_UNPROVEN_DEVICE_PATH_BLOCKED`. +No port-2323 connection, shsrv request/deployment, hbldr command, target build, +device file, app termination, remount, transfer or execution is authorized. + +## Phase-1.0T inactive shsrv identity boundary + +The current shsrv source spawns a shell for every accepted connection and its +greeting automatically queries and transmits model, serial number, firmware, +temperatures and CPU frequency. Therefore a nominally read-only `help` request +is not side-effect-free and risks disclosing a device identifier. Phase 1.0T +contains no connector and authorizes no connection. + +The offline parser must receive an already supplied transcript on stdin. It +must never persist raw input, serial, model or telemetry, and may retain file +metadata only for an independently supplied literal absolute path. `help` +identifies at most a source family; `stat` is metadata only; `sum` is a weak +16-bit rotating checksum and may cause atime/cache/accounting effects. None +can prove an exact binary. Wildcards, path discovery, content commands, +`hbldr`, launch, writes, signals, mounts, retry and reconnect remain forbidden. +All future windows are `DESIGNED_NOT_ACTIVE` and require separate exact review. + +## Phase-1.0U bounded local inventory + +Phase 1.0U found no deployed shsrv candidate within the declared Chimera, +attachment, known-download and ZIP-entry-name scope. This is not a global host +or device absence claim. The official source checkout, host telnet wrapper and +PacBrew recipe remain non-deployed references and must never be substituted +for exact installed bytes. No discovered script or binary was executed. + +The inventory grants no connection, shell command, target build, transfer, +execution, installation, write, retry or launch-context experiment. Only the +next offline inactive collector-design phase may proceed. A future live +collector remains blocked behind a new exact approval and the Phase-1.0T +serial/telemetry side-effect acceptance. + +## Phase-1.0V inactive collector-model boundary + +Phase 1.0V is an offline stdin model with no network transport. It bounds raw +and sanitized input to 65,536 bytes, permits at most 256 chunks, rejects +incomplete Telnet controls and invalid UTF-8, and seals after one result. It +retains only the Phase-1.0T sanitized record. The internal bytearray is cleared, +but physical memory erasure is not proven. + +The tracked activation and approval remain empty and false. Do not add an +address, port, command, socket, connect/send path, automatic reply, persistent +raw transcript, retry, reconnect, resume or fallback under this phase. Host +model success is not device, prompt, cleanup or deployed-identity evidence. + +## Phase-1.0W reviewed inactive client architecture + +Self-review remediates doubled-IAC state, empty-chunk accounting, path +allowlisting, firmware/compile metadata validation and numeric-parser error +normalization. Physical memory erasure remains unproven. The new policy accepts +only synthetic dual records and returns a frozen data plan; the fake transport +has no network primitives. + +Keep the tracked activation and approval empty. Port 2323, one connection, +ten-second deadline and T2/T3 command tokens are future constraints, not +authority. A consumed receipt, exclusive sanitized output, monotonic deadline, +network transport, Telnet reply/prompt contract and deterministic close remain +missing. Do not connect, render shell lines or issue a request under this phase. + +## Phase-1.0X inactive injected transport + +The X orchestrator accepts only an already validated immutable session plan, +an injected adapter, an injected monotonic clock and a caller-owned host +evidence directory. Repository tests supply only fake adapters. The consumed +receipt is exclusively created and file-flushed before adapter open. Output is +sanitized, exclusively created and bound to the reopened receipt hash. There +is no overwrite, delete, raw-transcript persistence, target persistence, retry +or second open. + +Do not interpret file `fsync` as directory-entry durability. A partial file is +invalid and deliberately not cleaned up; the run remains consumed. Deadline +checks surround adapter boundaries but cannot preempt a blocking real adapter. +Exact prompt and Telnet framing, OS socket timeouts, live close behavior and +deployed shsrv identity remain blockers. No real adapter, address, port, +connection, request or authority may be added under X. + +## Phase-1.0Y offline shsrv framing + +The Y model accepts synthetic bytes only. It distinguishes raw v0.7-v0.8 from +`libtelnet`/NVT v0.9-v0.19, never opens a transport and never formats a command. +Neither audited family proactively negotiates or server-echoes input. Current +source rejects unsupported `WILL`/`DO`; legacy source passes Telnet controls +into the shell. Future Chimera input must therefore emit no IAC commands. + +A terminal `$ ` is not a live completion proof. `PWD` is not forcibly +overwritten, the external Telnet client's local echo is undefined, wire chunks +are arbitrary, and current pipe/socket handlers do not complete short writes. +The server also has no bounded session deadline. Preserve deadline-based +partial-result rejection, keep all authority false, and do not add a real +network adapter under Y. + +## Phase-1.0Z offline passive batch + +The Z contract produces one target-free ASCII/LF byte batch from a validated W +plan. It permits only `help`, or ordered `stat` and `sum` for one normalized +literal path. NUL, CR, IAC and shell separators cannot enter the batch. It has +no CLI, address, transport, clock or file output and authorizes no device use. + +Incoming IAC fails closed; the model never sends a Telnet reply. Prompt text +and remote EOF are not completion boundaries. Only an explicit synthetic +hard-deadline event can seal, and incomplete help/stat/sum output remains +invalid. This does not prove live timeout preemption. A future T3 read may +still cause atime, cache, accounting and scheduler effects, while the automatic +sensitive greeting and shell/connection state require explicit acceptance. +Keep all authority false and add no real adapter under Z. + +## Phase-1.0AA offline fake-adapter integration + +AA accepts only exact built-in fake adapter, fake clock and fake evidence-store +types. Subclasses and arbitrary injected implementations are rejected, so the +module cannot be repurposed as a live transport boundary. It creates an +exclusive consumed receipt before fake open, permits one complete Z batch, +seals only at a valid synthetic hard deadline and closes exactly once. + +EOF, blocked receive, early or missing deadline, data at/after deadline, IAC, +partial output and evidence collisions are failures. A receipt remains after a +failed fake attempt; no cleanup deletes it. Logical event-buffer clearing does +not prove physical erasure. Host `fsync` does not prove directory-entry +durability, and the synthetic clock cannot prove OS preemption. Keep every +authorization false and do not add a network adapter or device action under AA. + +## Phase-1.0AB offline live-adapter feasibility + +AB binds the local Python 3.13.2 Windows socket/select implementation but adds +no network import or adapter. Its trace model accepts synthetic operations only +and requires receipt-before-creation, nonblocking-before-connect, readiness and +`SO_ERROR`, positive bounded send/receive progress, deadline-only sealing, +sanitization, local close and then output. + +Selector timeout is a maximum requested wait, not proof that host scheduling +cannot overshoot. Local `close` is not proof of remote shsrv/process cleanup. +Numeric-address parsing and the exact accepted Windows pending-connect error set +still require implementation review. Keep live implementation, socket creation, +DNS, target retention, connection and request as hard stops under AB. + +## Phase-1.0AC offline dormant-adapter boundary + +AC executes only against the exact built-in fake syscall facade and fake +clock. A precommitted receipt marker must precede synthetic creation; +nonblocking setup precedes synthetic connect; partial progress is explicit; +EOF, zero progress and data at the deadline fail. One fake local close is +attempted after every successfully opened path. Unused fake events are logically discarded, +which does not prove physical erasure or remote cleanup. + +The module has no live adapter protocol, socket/selector import, DNS, address, +CLI, real clock or file output. All authorization remains false. AC permits +only a later offline inactive numeric-target/activation design; it does not +permit a transport implementation, connection, request or device action. + +## Controls required before Phase 1 + +Phase 1 cannot begin until all of the following are recorded in a new ADR: + +1. explicit hardware-test approval; +2. an allowlisted firmware and reproducible console identification method; +3. proven function signatures and data layouts from acceptable public sources; +4. a bounded timeout and operator recovery plan; +5. independently reviewed cleanup and crash-log paths; +6. a one-step-at-a-time test case with an SDL software/VideoOut fallback; +7. an explicit statement of exactly which buffer or GPU state may mutate. + +Approval for one test does not authorize later tests. + +## Timeout, watchdog, cleanup, and logs + +The Phase-0 loop is statically bounded by the 21-entry manifest and always +attempts module cleanup after lookup begins. Dynamic-loader calls do not expose +a documented cancellation API, so an in-process forced timeout would risk +leaking loader state. The default `NONE` gate blocks the project-requested +module open. The 9.60 build gate does not remove the SDK CRT blocker and grants +no execution authority. + +Before any approved hardware observation, a separate supervisor design must +define a wall-clock deadline, progress events, operator-visible failure state, +and a recovery action that does not kill a thread while it owns loader or GPU +state. The probe already emits deterministic JSON-line stage events suitable +for a redacted crash/timeout log; it never emits addresses. Later GPU phases +must add explicit per-operation deadlines and prove cleanup for each partially +completed state transition. + +## Disabled Phase-1 candidate + +The candidate source contains one SDL window-surface update, but it is outside +every default target and automated test. Its default `NONE` build exits before +SDL initialization. Project code contains no direct VideoOut or GNM prototype. +The pinned SDL backend's flip-event wait has no proven finite timeout; this is +recorded as a hardware blocker rather than hidden behind a forced thread kill. +See `docs/phase1/HARDWARE_TEST_PLAN.md`. + +## Incident rule + +On an unexpected return code, missing cleanup confirmation, display anomaly, +hang, reset, or firmware mismatch: stop, preserve non-sensitive logs, mark the +compatibility entry as failed or unknown, and do not retry automatically. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..763d2c8 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,41 @@ +# Security Policy + +## Supported code + +Security fixes target the current `main` branch. Historical research and +experiment branches are evidence, not supported release channels. + +## Reporting a vulnerability + +Report suspected vulnerabilities privately to +[`security@itworx.tech`](mailto:security@itworx.tech). Do not +publish exploit chains, console-specific privileged addresses, credentials, +private network details, signing material, proprietary SDK or firmware +material, copyrighted dumps, payload delivery details that expose a live +target, or unredacted crash dumps in a public issue. + +Include the affected component and commit, firmware or adapter boundary, a +minimal reproduction using synthetic inputs where possible, expected and +observed behaviour, and likely impact. Note whether the issue affects input +validation, ownership, lifecycle cleanup, firmware gating, artifact +provenance, hashing, resource bounds, or a documented safety boundary. + +## Supported security boundary + +Chimera GFX treats platform adapters, firmware assumptions, and native memory +boundaries as untrusted until explicitly validated. Contributions must preserve +bounded parsing, fail-closed compatibility checks, write-free diagnostics by +default, and the release requirements in `SAFETY.md`, `AGENTS.md`, +`FIRMWARE_COMPATIBILITY.md`, and the accepted ADRs. They must not silently add +deployment, automatic startup, proprietary dependencies, kernel or hypervisor +functionality, DRM bypasses, unbounded hardware access, or unsupported +compatibility claims. + +Generated ELF files, core dumps, local build trees, device captures, +credentials, and operator-specific infrastructure are not source artifacts and +must not be committed. + +## Disclosure + +Coordinate remediation and disclosure with the repository owner before +publishing details that would materially increase exploitation risk. diff --git a/TEST_PLAN.md b/TEST_PLAN.md new file mode 100644 index 0000000..a4fbf9d --- /dev/null +++ b/TEST_PLAN.md @@ -0,0 +1,1101 @@ +# Test plan + +## Test classes + +### Host unit tests + +- API rejects null, undersized, wrong-version, unknown-backend, unknown-flag, + and render-request configurations. +- mock context lifecycle and capabilities are deterministic. +- surface/texture validation, upload, present ordering, ownership, limits, and + resource-busy cleanup are deterministic; +- adapter queries never claim acceleration or hardware contexts; +- probe logic handles complete, partial, and failed resolution without + exposing addresses or invoking callbacks as functions. +- repeated cleanup is safe where the API contract permits it. + +### Static policy tests + +- generated symbol include matches the JSON manifest byte-for-byte; +- no GNM identifier appears as a C call expression; +- no submit, draw, dispatch, flip, queue-map, resource-registration, MMIO, or + firmware-patch operation is called by project C sources; +- the PS5 platform shim uses only the approved dynamic-loader surface; +- no build target contains `deploy`, `upload`, `send`, `run-ps5`, or boot hooks; +- the discovery-only firmware allowlist is exactly `["9.60"]`, the CMake + default remains `NONE`, and Phase-1 rejects every non-`NONE` gate; +- Phase-1 firmware validation precedes `SDL_Init` and present; +- the Phase-1 target is off by default and never links SDL2main. + +### Reproducibility checks + +- SDK release download matches the locked SHA-256 and size; +- generated files are current; +- clean host configure/build/test works without network access; +- Docker base is pinned by digest and direct tool packages are version-pinned; +- PS5 compile uses the locked SDK and produces an ELF without contacting a PS5. +- artifact generation binds the byte hash and clean source commit; verification + rejects changed bytes or execution claims; +- artifact consumers reject `execution_eligible=false`, malformed inputs, and + permanent denylist hashes; +- the Phase-0.5 minimal-startup option fails configuration while its loader + caller contract is unproven; +- Phase-0.6 exact loader/manager identities, binary audit, call graph, effect + classes, and blocked artifact state validate offline; +- Phase-0.7 hardened loader, controlled manager, and lifecycle probe have + byte-identical double clean builds, exact import/DT_NEEDED inventories, + complete call-edge inventories, and a passing static deployment gate; +- Phase-0.8 records a blocked offline preflight whenever exact permission, + collector side-effect freedom, two-source firmware identity, current live + topology, or byte-exact rollback evidence is absent; tests reject any + invented on-device action, authorization, or completed dataset; +- Phase-0.8R rehashes the three immutable Phase-0.8 records, keeps every + authorization and retry field false, preserves the permanent denylist and + hard Payload Manager-backup gate, and rejects promotion of HTTP routes, + OPTIONS, host evidence, the permission template, or RetroArch work; +- Phase-0.9A validates 20 anti-brick invariants, two separate component + transactions, five empty authorization templates, all 14 power-loss + boundaries, fail-closed approval transitions and every declared virtual + fault while preserving `READ_ONLY_PREFLIGHT_BLOCKED`; +- Phase-0.9A simulator results always identify themselves as host-only, + perform no filesystem/network/target I/O, never claim PS5 evidence, and stop + before execution even when the synthetic switch primitive is enabled; +- Phase-0.9B validates the blocked startup/exit and output-channel gates, + exercises the proposed result contract entirely in memory, and rejects any + observer source, target artifact, authorization or fabricated artifact audit; +- Phase-0.9C validates the complete startup/exit model, prohibited normal-CRT + effects, incomplete freestanding dependency closure, absence of a safe exit + terminal, all four output architectures, fixed 4096-byte D1 framing, + nonce/request/checksum/deadline/completion/cleanup rules, firmware-source + conflict and absence, observation side effects, capability blockers, exact + source and immutable hashes, and the absence of target source, ELF, map or + lifecycle/install/autoload package; +- hardened launcher tests cover deadlines, step budgets, credential restore, + cleanup, child termination/reaping, receiver-side hashing, and denylisting; +- controlled-manager tests cover same-FD hashing/streaming, symlinks, + truncation, mismatch, denylisting, TOCTOU, loopback-only transport, and + negative compile-time artifact configuration; +- every potentially eligible artifact requires an exact controlled runtime + profile and firmware argument; missing/mismatched/hard-effect profiles fail; +- SPDX inventory and artifact schema validate without network access. +- the compiled probe has no direct Sce imports and the disabled Phase-1 ELF has + exactly the reviewed 15-name Sce import set with no GNM/keyboard/IME imports. + +## Commands + +Host: + +```sh +cmake --preset host-debug +cmake --build --preset host-debug +ctest --preset host-debug +``` + +The default host suite is standalone and uses only tracked repository inputs. +Validators that require exact sibling repositories, ignored linker maps/ELFs, +or pinned `work/upstream` checkouts are deliberately excluded from that suite. +Register them only in a prepared, phase-specific evidence workspace: + +```sh +cmake -S . -B build/external-evidence \ + -DBUILD_TESTING=ON \ + -DCHIMERA_GFX_REGISTER_EXTERNAL_EVIDENCE_VALIDATORS=ON +cmake --build build/external-evidence +ctest --test-dir build/external-evidence -N +``` + +Run only the named validator whose exact sibling commits and ignored artifacts +were prepared, using its phase command below or `ctest -R `. +Historical validators intentionally bind different RetroArch revisions and +cannot be treated as one shared-worktree suite. Missing, dirty, mismatched, or +incomplete external evidence must fail the selected validator. Leaving them +unregistered never promotes an evidence claim; the corresponding artifact and +device authorization fields remain false. The current workspace disposition is +recorded in `docs/evidence/external-evidence-integrity-2026-07-29.md`. + +PS5 compile-only: + +```sh +cmake -S . -B build-ps5 \ + -DCMAKE_TOOLCHAIN_FILE="$PS5_PAYLOAD_SDK/toolchain/prospero.cmake" \ + -DCHIMERA_GFX_BUILD_PS5_PROBE=ON +cmake --build build-ps5 --target chimera-gfx-capability-probe +``` + +The expected default artifact contains a firmware gate value of `NONE` and +cannot enter symbol discovery. No test command in this repository executes it. + +Exact 9.60 offline build from WSL/Linux and a clean tree: + +```sh +bash tools/build-firmware-probe.sh 9.60 +``` + +The script builds only `chimera-gfx-capability-probe`, forces +`CHIMERA_GFX_BUILD_PHASE1_VIDEOOUT_CLEAR=OFF`, verifies the exact six undefined +imports, audits the pinned SDK startup/loader/cleanup chain, and marks the +artifact execution-ineligible. It does not connect to or execute on a PS5. + +Phase-0.5 startup audit (WSL/Linux, offline, produces JSON only): + +```sh +python3 tools/audit_ps5_startup_feasibility.py \ + --root . \ + --sdk-source work/upstream/sdk \ + --sdk-install work/toolchains/ps5-payload-sdk-v0.41 \ + --objdump /usr/bin/llvm-objdump-18 \ + --readelf /usr/bin/readelf \ + --output manifests/runtime/phase-0.5-startup-audit.json +``` + +The command invokes the compiler only with `-###`; it does not compile or link +an ELF. Repeating it must leave the JSON byte-identical. Enabling +`CHIMERA_GFX_BUILD_PS5_MINIMAL_STARTUP` is a negative test and must fail before +any target is created. + +Phase-0.6 exact loader/runtime audit (WSL/Linux, offline): + +```sh +python3 tools/audit_ps5_loader_runtime.py \ + --root . \ + --loader-source work/upstream/elfldr-v0.23 \ + --loader-asset work/upstream/release-assets/elfldr-ps5-v0.23.elf \ + --payload-manager-source work/upstream/pldmgr-v0.3.1 \ + --sdk-source work/upstream/sdk \ + --readelf /usr/bin/readelf \ + --objdump /usr/bin/llvm-objdump-18 \ + --output manifests/runtime/phase-0.6-loader-runtime-audit.json +``` + +Run it twice from clean exact checkouts and compare output SHA-256. It only +reads public source and an ignored release asset, then writes JSON. It does not +connect to a PS5. Because the decision is blocked, no lifecycle compile, +linker map, probe disassembly, double ELF build, transfer, or execution occurs. + +Phase-0.7 full offline audit: + +```sh +python3 tools/audit_phase07_artifacts.py \ + --root . \ + --loader-root ../chimera-elfldr \ + --manager-root ../chimera-ps5-payload-manager \ + --sdk-install work/toolchains/ps5-payload-sdk-v0.41 \ + --output manifests/runtime/phase-0.7-offline-audit.json +``` + +The tool performs two clean builds of all three artifacts, requires +byte-identical ELF output, records full imports, `DT_NEEDED`, relocations, +sections, linker maps, disassembly, and reachable call edges, and rejects +source identity drift. Full reports are written below ignored +`outputs/phase07/audit/`; the machine audit and all report hashes are +committed. It never contacts or executes on a PS5. + +The static policy check must pass for the exact local lifecycle bytes while +continuing to report `execution_authorized=false`: + +```sh +python3 tools/check_artifact_execution_policy.py \ + --manifest manifests/artifacts/chimera-gfx-lifecycle-probe-phase07-fw-9.60.json \ + --denylist manifests/artifact-denylist.json \ + --artifact outputs/phase07/artifacts/chimera-gfx-lifecycle-probe.elf \ + --runtime-profile manifests/runtime/controlled-ps5-runtime-profile.json \ + --firmware 9.60 +``` + +Wrong firmware, altered bytes, blocked hash, missing hardened identity, +installed/executed state drift, widened budgets, or a hard effect must fail. + +After all source and evidence are committed and the tracked worktree is clean, +create the deterministic offline review and rollback archives: + +```sh +python3 tools/package_phase07_review.py --root . +``` + +The command accepts only the exact reviewed hashes, embeds the clean +repository HEAD, and writes below ignored `outputs/phase07/packages/`. It +contains no PS5 connection, transfer, installation, or execution operation. + +Phase-0.8 read-only preflight record: + +```sh +python3 tests/test_phase08_preflight.py --root . +``` + +This is an offline record-validation command. It does not contact the PS5. +The current expected result is `READ_ONLY_PREFLIGHT_BLOCKED`: no exact +permission record or admissible no-side-effect collector exists, and current +device and rollback evidence is absent. Do not use the stock Payload Manager +HTTP routes as a Phase-0.8 collector. + +Phase-0.8R remediation and regression guardrails: + +```sh +python3 tools/validate_phase08_remediation.py --root . +python3 tests/test_phase08_remediation.py --root . +``` + +When the ignored exact Payload Manager v0.3.1 source checkout is already +present locally, its commit and relevant source-file hashes can also be +revalidated without network access: + +```sh +python3 tools/validate_phase08_remediation.py \ + --root . \ + --require-local-source +``` + +These commands only read repository evidence and run in-memory negative +variants. They do not build a target, contact a remote or PS5, create an +artifact, or claim hardware safety. + +Phase-0.9A offline anti-brick design and simulator: + +```sh +python3 tests/test_phase09_transaction.py --root . +python3 tools/simulate_phase09_transaction.py \ + --component hardened_elfldr \ + --run-fault-suite +python3 tools/simulate_phase09_transaction.py \ + --component controlled_payload_manager \ + --run-fault-suite +``` + +The simulator uses virtual objects held in memory. It has no output-file, +device, network, compiler, transfer, install or execution operation. The +default transaction stops because PS5 switch atomicity and durability are +unproven. Its synthetic atomic model exists only inside the fault suite and +does not establish PS5 behavior. + +Phase-0.9B blocked bounded-observer audit: + +```sh +python3 tools/validate_phase09b_observer_audit.py --root . +python3 tests/test_phase09b_observer_audit.py --root . +``` + +These commands read pinned local source evidence and run an in-memory host +model. They fail if an observer source, target, ELF, map, install/lifecycle +package, device address, read path, true authorization field or false positive +artifact audit appears. They do not compile target code, contact a PS5, +transfer, execute, install, create a backup, change services or start +RetroArch/graphics work. + +Phase-0.9C execution-feasibility closure: + +```sh +python3 tests/test_phase09c_protocol.py --root . +python3 tools/validate_phase09c_feasibility.py --root . +python3 tests/test_phase09c_feasibility.py --root . +``` + +These commands use local source trees and finite host-only models. They do not +compile or assemble target code, open a device path, use a network operation, +create an ELF/map/package, or authorize a later action. The expected result is +`BLOCKED_MULTIPLE_FOUNDATIONAL_CONTRACTS`; a passing host protocol does not +establish a target startup, exit, output, firmware, cleanup, or side-effect +contract. + +Phase-0.9D existing-stack readback and recovery audit: + +```sh +python3 tools/validate_phase09d_readback.py --root . +python3 tests/test_phase09d_readback.py --root . +``` + +The validator rehashes the five clean pinned source trees, immutable Phase +records, and permanent denylist. The 30 negative guardrails reject mutating, +launching, autoload-triggering, incompletely framed, retrying, or +recovery-promoting variants. They create no target code or artifact, make no +network request, and do not prove device behavior. The expected decision is +`BLOCKED_NO_READBACK_PATH`. + +Phase-0.9E external-bootstrap provenance audit: + +```sh +python3 tools/validate_phase09e_bootstrap.py --root . +python3 tests/test_phase09e_bootstrap.py --root . +``` + +The validator rehashes the bounded local Y2JB-named candidate archive, its +opaque `SIECAF` member, the clean pinned source trees, immutable historical +records, source-bound post-bootstrap artifacts, and the permanent denylist. +The 20 guardrails prohibit invented provenance, protocol promotion, target +source or artifacts, live replacement, autoload, automatic retry, and false +reboot or host-to-memory claims. The tests open no network port, create no +protocol client or emulator, and provide no device evidence. The expected +classification is `BOOTSTRAP_IMPLEMENTATION_MISSING`. + +Phase-0.9E-R official-release correlation: + +```sh +python3 tools/validate_phase09er_provenance.py --root . +python3 tests/test_phase09er_provenance.py --root . +``` + +The validator rehashes the unchanged local outer ZIP and opaque inner member, +the official detached 1.6 source tree, immutable historical records, the +permanent denylist, deliverables, and their checksum inventory. The 22 +guardrails reject name/size-only matches, mirror provenance, opaque-content +promotion, invented embedded-source identity, reference-only port promotion, +duplex overclaims, target output, authorization, retry, and large tracked +assets. They perform no network or device action. Expected decisions are +`OFFICIAL_RELEASE_NO_MATCH`, `PORT_9020_REFERENCE_ONLY`, and +`LOCAL_BACKUP_NOT_CORRELATED`. + +Phase-0.9E-R2 inner and MediaFire correlation: + +```sh +python3 tools/validate_phase09er2_correlation.py --root . +python3 tests/test_phase09er2_correlation.py --root . +python3 tests/test_siecaf_header_parser.py --root . +``` + +The validator rehashes the unchanged local outer and inner, all immutable +historical manifests, the denylist, generated SIECAF record fingerprints, and +the checksum inventory. It recomputes the local SIECAF parse and all +normalized record hashes without needing downloaded evidence to remain. The 24 +phase guardrails and 18 parser cases cover repacking, complete byte-match +requirements, malformed and overflowing tables, ranges, duplicate/overlap +reporting, redaction, large-file exclusion, authorization, retry, target +absence, and runtime-evidence boundaries. Expected results are +`INNER_ARCHIVE_HASH_MISMATCH`, `SIECAF_LAYOUT_DIFFERENT`, and +`LOCAL_BACKUP_UNCORRELATED`. + +The Phase-0.9D and Phase-0.9E validators deliberately require their original +branch and original root-document checksums. CTest therefore leaves those two +branch-local historical validators disabled on later phases; the current +Phase-0.9E-R2 validator rehashes their immutable manifests instead. They can be +registered only on the matching historical checkout with +`CHIMERA_GFX_RUN_BRANCH_LOCAL_HISTORICAL_VALIDATORS=ON`. + +Phase-1.0E inherited result-channel validation: + +```sh +python3 tools/validate_retroarch_phase10e.py --root . \ + --retroarch-root ../chimera-retroarch \ + --loader-root ../chimera-elfldr +python3 tests/test_retroarch_phase10e.py --root . +``` + +In `chimera-retroarch`, run `make -f Makefile.ps5 test-ps5-port`, then perform +two clean `PS5_PROFILE=result-diag` builds and compare both ELF and linker map +byte-for-byte. Static audit must prove W^X, empty init/fini arrays, absent TLS, +the complete import set, an import delta of exactly `send`, and the reachable +SDK `_start -> __patch_init` path. The validator additionally checks the +ignored RUN-C trace's exact size/hash and its one-connect/no-retry D00-D02 +record when that local evidence is present. These commands are offline and do +not authorize or perform any new connection, transfer, result reception or +execution. + +Phase-1.0F offline startup-interval validation: + +```sh +python3 tools/validate_retroarch_phase10f.py --root . \ + --retroarch-root ../chimera-retroarch +python3 tests/test_retroarch_phase10f.py --root . +``` + +In `chimera-retroarch`, run `make -f Makefile.ps5 test-ps5-port`, then perform +two clean `PS5_PROFILE=interval-diag` builds from the manifest-bound commit and +compare ELF and linker map byte-for-byte. Audit both copies for identical ELF +model, imports, relocations and segment permissions. Disassembly must bind all +I00-I14 callsites to the stream-only wrappers and the single existing `send` +relocation. These commands never authorize a connection, transfer, result +reception or execution. + +Phase-1.0G offline one-shot-runner validation: + +```sh +python3 tools/validate_retroarch_phase10g.py --root . \ + --retroarch-root ../chimera-retroarch +python3 tests/test_retroarch_phase10g.py --root . +``` + +In `chimera-retroarch`, `make -f Makefile.ps5 test-ps5-port` uses only injected +fake sockets. It verifies exact manifest/approval binding, actual artifact +rehashing, manifest-only F-protocol selection, exclusive trace creation and a +durable consumed-attempt receipt created before the mocked transport. Negative +tests prove that inactive, mismatched, reused, retrying or reconnecting inputs +do not reach transport. These are host-policy tests, not device evidence. + +Consumed Phase-1.0G result validation: + +```sh +python3 tools/validate_retroarch_phase10g_result.py --root . +python3 tests/test_retroarch_phase10g_result.py --root . +``` + +These checks bind the immutable local trace hash, exact seven-frame sequence, +ordinary stdout hash, one-shot counters and consumed authorization. They reject +invented I04/terminal progress, retries, reconnects, active future authority or +claims that SDL/VideoOut and cleanup were reached. + +Phase-1.0H offline startup-argument validation: + +```sh +python3 tools/validate_retroarch_phase10h.py --root . \ + --retroarch-root ../chimera-retroarch +python3 tests/test_retroarch_phase10h.py --root . +``` + +In `chimera-retroarch`, run `make -f Makefile.ps5 test-ps5-port`, then perform +two clean `PS5_PROFILE=startup-args-diag` builds and compare the ELF and map +byte-for-byte. Audit both ELFs for H magic, flag mask `0x07`, unchanged +imports/modules/relocations, no RWX, manifest-only dual-gated H selection and +identical normalized disassembly. These are host/static checks, not device +evidence. + +Consumed Phase-1.0H result validation: + +```sh +python3 tools/validate_retroarch_phase10h_result.py --root . +python3 tests/test_retroarch_phase10h_result.py --root . +``` + +These checks bind the 25-frame order, exact raw results, transport counters, +trace/receipt hashes and consumed authority. Negative cases reject invented +D11/flip success, retry, reconnect, visibility, cleanup and write-operation +claims. + +Phase-1.0I offline postmortem validation: + +```sh +python3 tools/validate_retroarch_phase10i.py --root . +python3 tests/test_retroarch_phase10i.py --root . +``` + +These checks bind the consumed artifact/map identities, three linked blocking +wrappers, exact submit tuple and failure branch. Mutation cases reject an +invented write operation, errno, successful flip, event wait, root cause, +active authorization, target action or device-test readiness. They are static +host checks and create no target artifact or hardware evidence. + +Phase-1.0J offline diagnostic artifact validation: + +```sh +python3 tools/validate_retroarch_phase10j.py --root . +python3 tests/test_retroarch_phase10j.py --root . +``` + +Build the sibling RetroArch `write-diag` profile twice from clean state and +compare the ELF and map byte-for-byte. Audit the ignored output for exact J +magic and D13 fields, an immediate stop before I04, errno capture before any +reporting, one frame-index source, no RWX, empty init/fini arrays, no TLS, the +exact imports/modules/relocations and identical normalized disassembly. The +GFX validator binds those results while mutation cases reject a live J runner, +socket/receive capability, late stop, successful write, repeated submit, +active authorization or hardware-proof claim. No run, upload or deploy target +exists. + +Phase-1.0K inactive one-shot runner validation: + +```sh +python3 tools/validate_retroarch_phase10k.py \ + --root . --retroarch-root ../chimera-retroarch +python3 tests/test_retroarch_phase10k.py --root . +``` + +These host-only checks bind the unchanged J artifact, exact 30-entry wire +table and host-runner commit. Mutation tests reject active authorization, +artifact eligibility, a tracked target/run ID, D13 renumbering, a free protocol +selector, retry, multiple connections, a late consumption receipt and any +claim that fake-socket results are hardware evidence. The tracked manifest and +approval template remain inactive and cannot open a socket. + +Phase-1.0L consumed write-firewall result validation: + +```sh +python3 tools/validate_retroarch_phase10l.py \ + --root . --retroarch-root ../chimera-retroarch +python3 tests/test_retroarch_phase10l.py --root . +``` + +These host-only checks bind the consumed K receipt and ignored trace identities, +the exact nine-frame terminal sequence, `D13(6,1)` as the first and only blocked +`MKDIR`, and the source/map/disassembly route through `config_set_defaults`. +Mutation cases reject an invented operation, second write, I04/SDL progress, +retry, reconnect, runtime path, cleanup proof or reusable authority. No target +artifact is built or tracked. + +Phase-1.0M offline write-free defaults validation: + +```sh +python3 tools/validate_retroarch_phase10m.py \ + --root . --retroarch-root ../chimera-retroarch +python3 tests/test_retroarch_phase10m.py --root . +``` + +Build the sibling RetroArch `write-diag` profile twice from clean state. The +checks bind the exact source guard, retained in-memory path derivation, all 17 +write-wrapper link options, exact ignored ELF/map hashes, identical normalized +disassembly, imports, modules, relocations and no-RWX result. Mutation cases +reject a weakened firewall, lost path derivation, invented runtime progress, +artifact eligibility, active authority or a device action. No run, upload or +deploy target exists. + +Phase-1.0N inactive one-shot runner validation: + +```sh +python3 tools/validate_retroarch_phase10n.py \ + --root . --retroarch-root ../chimera-retroarch +python3 tests/test_retroarch_phase10n.py --root . +``` + +These host-only checks bind the exact M artifact to the exact N runner commit +and source hash. Mutation cases reject active eligibility or authority, a +Phase-K scope, target/run fields, altered wire ordering, a free protocol +selector, retries and widened attempt counts. The sibling fake-socket suite +proves parser and gate behavior only. No target build or device action occurs. + +Phase-1.0O consumed result validation: + +```sh +python3 tools/validate_retroarch_phase10o.py \ + --root . --retroarch-root ../chimera-retroarch +python3 tests/test_retroarch_phase10o.py --root . +``` + +These offline checks bind the consumed run ID, authorization/receipt/trace +hashes, exact one-shot counters, 25-frame sequence and source-bounded +interpretation. Mutations reject reusable authority, retry/reconnect, an +invented D13 or successful flip, invented errno, successful runner-terminal +classification, visible presentation and cleanup. They never reconnect to the +device and do not treat the result as authority for another run. + +Phase-1.0P offline analysis validation: + +```sh +python3 tools/validate_retroarch_phase10p.py \ + --root . --retroarch-root ../chimera-retroarch \ + --sdk-root work/upstream/sdk --sdl-root work/upstream/SDL +python3 tests/test_retroarch_phase10p.py --root . +``` + +These host-only checks bind the O artifact/trace identities, exact submit +register tuple, SDK/SDL source commits and deterministic D12/D04 ordering. +Mutations reject invented success or errno, semantic ABI promotion, new +VideoOut calls, parser relaxation, parameter experiments and active authority. +They build no target and perform no network or device operation. + +Phase-1.0Q public evidence validation: + +```sh +python3 tools/validate_retroarch_phase10q.py \ + --root . --sdk-root work/upstream/sdk --sdl-root work/upstream/SDL +python3 tests/test_retroarch_phase10q.py --root . +``` + +These offline tests validate the already-recorded source identities and do not +access the network. Mutations reject mirrors, executed downloads, invented SDK +headers or independent sources, PS4-to-PS5 promotion, semantic promotion, +parameter changes, new VideoOut calls and every authorization. + +Phase-1.0R launch-context validation: + +```sh +python3 tools/validate_retroarch_phase10r.py \ + --root . --retroarch-root ../chimera-retroarch \ + --sdk-root work/upstream/sdk --sdl-root work/upstream/SDL \ + --elfldr-root ../chimera-elfldr \ + --manager-root ../chimera-ps5-payload-manager \ + --pacbrew-root work/upstream/pacbrew-repo +python3 tests/test_retroarch_phase10r.py --root . +``` + +These host-only checks rehash the exact wrapper, CRT, SDL video, elfldr, +manager, RetroArch makefile, artifact and linker map. They verify the actual +source tokens and repository commits, reject invented SDL2main registration, +a distinct manager process, packaging-as-launcher claims, LNC-log root-cause +promotion and every target/device authorization. They open no network socket +and build no target. + +Phase-1.0S launcher-provenance validation: + +```sh +python3 tools/validate_retroarch_phase10s.py \ + --root . --shsrv-root work/upstream/shsrv \ + --shsrv-v07-root work/upstream/shsrv-v0.7 \ + --pacbrew-root work/upstream/pacbrew-repo +python3 tests/test_retroarch_phase10s.py --root . +``` + +These offline tests bind the clean official v0.7 and v0.19 commits, Git trees, +file hashes, BigApp calls, persistent-write branch and PacBrew provenance +limit. Mutations reject upstream-as-deployed promotion, VideoOut/runtime +promotion, hidden staging, remount or fake-app effects, invented deadlines, +route reuse and every target/device authorization. They do not build or run +the downloaded source and perform no network or device operation. + +Phase-1.0T inactive identity-gate validation: + +```sh +python3 tools/validate_retroarch_phase10t.py \ + --root . --shsrv-root work/upstream/shsrv \ + --shsrv-v07-root work/upstream/shsrv-v0.7 +python3 tests/test_retroarch_phase10t.py --root . +python3 tests/test_phase10t_shsrv_transcript.py --root . +``` + +These host-only tests bind the exact public shsrv source identities and the +connection-to-shell/greeting effects. Mutation tests reject active authority, +targets, retry, hidden serial transmission, exact-identity promotion and +relaxed command or redaction policy. Synthetic transcript tests prove that +sensitive values and unknown paths are discarded and that all source-family, +metadata and weak-checksum combinations remain non-exact. They open no socket, +write no transcript and perform no device operation. + +Phase-1.0U bounded local-inventory validation: + +```sh +python3 tools/validate_retroarch_phase10u.py \ + --root . --shsrv-root work/upstream/shsrv \ + --shsrv-v07-root work/upstream/shsrv-v0.7 \ + --pacbrew-root work/upstream/pacbrew-repo +python3 tests/test_retroarch_phase10u.py --root . +``` + +These host-only checks rehash the official wrapper and PacBrew recipe, bind the +clean source commits and preserve the distinction between reference objects +and deployed identity. Mutations reject global-absence claims, execution of a +discovered sender, active authority and launch-context experimentation. They +perform no search, network or device operation during CTest; the bounded +inventory result is an immutable audit record. + +Phase-1.0V inactive collector-model validation: + +```sh +python3 tools/validate_retroarch_phase10v.py --root . +python3 tests/test_retroarch_phase10v.py --root . +python3 tests/test_phase10v_shsrv_collector_model.py --root . +``` + +These offline tests enforce the absence of network imports and live CLI +arguments, exact source hashes, inactive activation, fixed bounds and +non-promoted memory/device claims. Synthetic byte streams cover fragmented +Telnet negotiation, subnegotiation, serial/telemetry redaction, oversize and +chunk rejection, invalid UTF-8, incomplete control state, abort, one-shot +sealing and literal-path filtering. No socket or device operation is used. + +Phase-1.0W self-review and inactive client-architecture validation: + +```sh +python3 tools/validate_retroarch_phase10v.py --root . +python3 tests/test_phase10v_shsrv_collector_model.py --root . +python3 tools/validate_retroarch_phase10w.py --root . +python3 tests/test_retroarch_phase10w.py --root . +python3 tests/test_phase10w_shsrv_client_policy.py --root . +python3 tools/validate_retroarch_phase10x.py --root . \ + --shsrv-root work/upstream/shsrv +python3 tests/test_retroarch_phase10x.py --root . +python3 tests/test_phase10x_inactive_transport.py --root . +python3 tools/validate_retroarch_phase10y.py --root . \ + --shsrv-root work/upstream/shsrv +python3 tests/test_retroarch_phase10y.py --root . +python3 tests/test_phase10y_shsrv_framing_model.py --root . +``` + +The V rerun proves the reviewed model's corrected hash and 21 edge cases. The +W tests bind the data-only policy/fake-transport hashes, inactive records, +source-bound port, dual-record match, approval lifetime, side-effect acceptance +and one-shot command windows. Mutations reject targets in tracked state, +second opens, command/target injection, retry, execution authority, missing +effects, unknown approval fields, listener/policy mismatch, expired approval +and invented live components. No socket is opened. + +The X tests bind the W policy and remediated collector to an injected-only +orchestrator. Eighteen mutation guardrails preserve inactive records, false +authority, missing live framing and explicit durability/deadline limitations. +Eighteen fault tests verify consumed-before-open ordering, exclusive files, +close/reopen/hash validation, one-shot command order, sanitization, deadline +and adapter failures, deterministic fake close and absence of socket/CLI code. +They use no network and provide no hardware evidence. + +The Y validator binds every official v0.9-v0.19 tag to the empty-option +`libtelnet` family and keeps v0.7-v0.8 raw. Eighteen mutation guardrails reject +authority, activation, family collapse, invented echo/negotiation, prompt +promotion and live implementation. Twenty-two synthetic framing cases cover +raw pass-through, NVT EOL, IAC escaping, unsupported-option replies, +fragmentation, subnegotiation, prompt ambiguity and hard size bounds. + +Phase-1.0Z passive-batch validation: + +```sh +python3 tools/validate_retroarch_phase10z.py --root . +python3 tests/test_retroarch_phase10z.py --root . +python3 tests/test_phase10z_passive_batch_contract.py --root . +``` + +The validator binds V/W/X/Y and the Z model/test identities, exact inactive +state, false authority, one LF batch and synthetic-deadline-only completion. +Eighteen mutations reject activation, target retention, authority, retry, +reconnect, CR/IAC, multiple batches, prompt/EOF completion and hardware-proof +promotion. Twenty-five synthetic cases cover exact batch bytes, bounds, path +injection, IAC rejection, raw LF and CRLF transcripts, complete help and +stat/sum, partial rejection, abort and one-shot sealing. No network is used. + +Phase-1.0AA offline fake-adapter integration: + +```sh +python3 tools/validate_retroarch_phase10aa.py --root . +python3 tests/test_retroarch_phase10aa.py --root . +python3 tests/test_phase10aa_offline_fake_batch.py --root . +``` + +The validator hash-binds X, Z and the AA integration/tests, rejects network or +real-clock imports, and enforces exact fake types and inert authorization. +Twenty mutations prevent activation, target retention, live protocols, +authority, retries, subclass promotion, EOF/early-deadline completion and +hardware claims. Twenty-five integration cases cover receipt-before-open, +single batch, complete help/stat/sum, synthetic deadline, close on all paths, +exclusive evidence, sanitization and bounded fake-event failures. + +Phase-1.0AB offline live-adapter feasibility: + +```sh +python3 tools/validate_retroarch_phase10ab.py --root . +python3 tests/test_retroarch_phase10ab.py --root . +python3 tests/test_phase10ab_nonblocking_trace_model.py --root . +``` + +On the audited Windows host, additionally pass +`--python-root "C:\Program Files\Python313"` to rehash the four recorded local +runtime files. CI validates the immutable evidence record without assuming that +its own Python installation is the audited host. Twenty mutations reject +authorization, runtime/feasibility promotion, live imports and relaxed hard +stops. Twenty-five trace cases cover immediate/pending connect, `SO_ERROR`, +partial sends, interrupted/time-out waits, bounded receive, EOF, deadline races, +sanitization, close and output ordering. No socket is opened. + +Phase-1.0AC offline dormant-adapter validation: + +```sh +python3 tools/validate_retroarch_phase10ac.py --root . +python3 tests/test_retroarch_phase10ac.py --root . +python3 tests/test_phase10ac_dormant_adapter.py --root . +``` + +The validator hash-binds the dormant adapter and its tests, rejects live +imports, embedded addresses, activation and device authority, and requires the +exact closed fake types. Twenty mutations protect the inactive record. Thirty- +two synthetic cases cover create/nonblocking order, immediate and pending +connect, `SO_ERROR`, partial progress, interruptions, timeout/deadline races, +bounded reads, EOF, malformed output, one-close cleanup and result +sanitization. No socket or real clock is used. + +Phase-1.0AD inactive activation-contract validation: + +```sh +python3 tools/validate_retroarch_phase10ad.py --root . +python3 tests/test_retroarch_phase10ad.py --root . +python3 tests/test_phase10ad_activation_contract.py --root . +``` + +The validator hash-binds the contract, tests and documentation. The tracked +record is target-free and inactive. Synthetic candidate validation accepts +only canonical private numeric IPv4, source-bound port 2323, a five-minute +one-shot window and separate launcher, payload and approval hashes. It rejects +DNS names, public/special addresses, retry, reconnect, resume, device writes, +app termination and remount authority. No socket, clock, CLI or file output is +present. + +Phase-1.0AE minimal BigApp-launcher architecture validation: + +```sh +python3 tools/validate_retroarch_phase10ae.py --root . \ + --shsrv-root work/upstream/shsrv \ + --shsrv-v07-root work/upstream/shsrv-v0.7 +python3 tests/test_retroarch_phase10ae.py --root . +``` + +The external validator binds the official v0.19 and v0.7 commits, trees and +hbldr/elfldr/ptrace source hashes. It proves only the offline lineage facts: +v0.19 contains fake-app/remount behavior and v0.7 references the existing +`PPSA01659` title without those two mechanisms. Standard guardrails keep every +authority false, reject killing an existing BigApp and preserve all firmware, +VideoOut, cleanup and bounded-wait blockers. + +Phase-1.0AF offline BigApp lifecycle-model validation: + +```sh +python3 tools/validate_retroarch_phase10af.py --root . +python3 tests/test_retroarch_phase10af.py --root . +python3 tests/test_phase10af_bigapp_lifecycle_model.py --root . +``` + +The validator binds the AE decision, model and tests and rejects live imports, +target claims and authority. Failure injection covers a pre-existing BigApp, +every parent/child stage, unique-child timeout, replacement restoration, +cleanup failure, tick exhaustion, wrong ordering and unused operations. These +tests prove only the closed fake state machine. + +Phase-1.0AG bounded ELF admission validation: + +```sh +python3 tools/validate_retroarch_phase10ag.py --root . +python3 tests/test_retroarch_phase10ag.py --root . +python3 tests/test_phase10ag_bounded_elf.py --root . +``` + +The validator hash-binds AF, the bytes-only parser and its synthetic tests. +Malformed identity, hash, type, machine, header tables, file/memory ranges, +alignment, overlap, W+X, interpreter, entry and count cases fail closed. No +path, mapping or execution interface exists, and the missing historical M +bytes are never promoted from a size/hash reference into admitted evidence. + +Phase-1.0AH dynamic/relocation contract validation: + +```sh +python3 tools/validate_retroarch_phase10ah.py --root . \ + --shsrv-v07-root work/upstream/shsrv-v0.7 \ + --sdk-root work/upstream/sdk +python3 tests/test_retroarch_phase10ah.py --root . +python3 tests/test_phase10ah_dynamic_contract.py --root . +``` + +The external validator binds official shsrv v0.7 and SDK v0.41 source. Tests +cover exact ordered dependencies, allowlisting, dynamic termination, RW/aligned +targets, relative symbol/addend rules, GLOB_DAT rules, unknown types, missing +relative closure and inherited ELF/hash rejection. No mapping or module load +is performed. + +Phase-1.0AI mapping transaction validation: + +```sh +python3 tools/validate_retroarch_phase10ai.py --root . +python3 tests/test_retroarch_phase10ai.py --root . +python3 tests/test_phase10ai_mapping_model.py --root . +``` + +Failure injection covers reserve, mirror creation, every copy/BSS/relocation/ +protection/sync/release stage, atomic deadline preemption, cleanup failures, +W+X, page-rounded protection overlap, wrong bindings and unused operations. +Success retains only the synthetic new region; failure requires mirror release +and complete region unmap. No allocator or mapping API is called. + +Phase-1.0AJ primitive source audit: + +```sh +python3 tools/validate_retroarch_phase10aj.py --root . \ + --shsrv-v07-root work/upstream/shsrv-v0.7 \ + --hardened-elfldr-root ../chimera-elfldr +python3 tests/test_retroarch_phase10aj.py --root . +``` + +The validator hash-binds both source trees. Guardrails reject direct reuse of +the unbounded v0.7 loader, require the temporary-resource and cleanup-ownership +gaps to remain explicit, and keep all target and device authority false. + +Phase-1.0AK hybrid composition model: + +```sh +python3 tools/validate_retroarch_phase10ak.py --root . +python3 tests/test_retroarch_phase10ak.py --root . +python3 tests/test_phase10ak_hybrid_composition.py --root . +``` + +Failure injection covers every forward operation, reverse cleanup, cleanup +failure containment through child termination, failed terminal termination, +deadline preemption, invalid bounds/order and unused operations. The model has +no process, mapping, clock, network or device capability. + +Phase-1.0AL SDK mdbg copy audit: + +```sh +python3 tools/validate_retroarch_phase10al.py --root . \ + --sdk-root work/upstream/sdk +python3 tests/test_retroarch_phase10al.py --root . +``` + +The external validator binds the exact SDK commit, implementation and public +header. Guardrails preserve the observed restoration/progress gaps, reject +direct reuse and keep every target and device authorization false. + +Phase-1.0AM bounded copy/restore model: + +```sh +python3 tools/validate_retroarch_phase10am.py --root . +python3 tests/test_retroarch_phase10am.py --root . +python3 tests/test_phase10am_bounded_copy_model.py --root . +``` + +Tests cover exact multi-chunk completion, partial/error/zero/oversized progress, +both independent restore failures, capability-set failure, deadline and range +overflow, incomplete completion and terminal cleanup failure. No credential, +process, memory, clock, network or target interface exists in the model. + +Phase-1.0AN service lifecycle and copy audit: + +```sh +python3 tools/validate_retroarch_phase10an.py --root . \ + --hardened-elfldr-root ../chimera-elfldr +python3 tests/test_retroarch_phase10an.py --root . +``` + +The external validator binds the hardened fork and distinguishes fail-stop from +restart/recovery. Guardrails keep blocking `PT_IO` copy, automatic restart, +target implementation and every device authorization disabled. + +Phase-1.0AO worker supervisor model: + +```sh +python3 tools/validate_retroarch_phase10ao.py --root . +python3 tests/test_retroarch_phase10ao.py --root . +python3 tests/test_phase10ao_worker_supervisor_model.py --root . +``` + +Tests cover exact accepted results, deadline preemption, wrong identity, +partial/restore failure, ambiguous start, pre-start failure, terminal cleanup +failure, bounds and unused events. The model has no process, signal, clock, IPC, +network or target interface and performs no retry or automatic restart. + +Phase-1.0AP current-upstream worker feasibility audit: + +```sh +python3 tools/validate_retroarch_phase10ap.py --root . \ + --sdk-root work/upstream/sdk \ + --shsrv-root work/upstream/shsrv-v0.7 +python3 tests/test_retroarch_phase10ap.py --root . +``` + +The validator binds exact official commits and Git blobs without changing the +pinned checkouts. Guardrails distinguish a worker-creation source candidate +from runtime proof and keep preemption, result transport, target code, shsrv +code reuse and device authority blocked. + +Phase-1.0AQ fixed worker result record: + +```sh +python3 tools/validate_retroarch_phase10aq.py --root . +python3 tests/test_retroarch_phase10aq.py --root . +python3 tests/test_phase10aq_worker_result_record.py --root . +``` + +Tests cover exact success/failure records, every single-byte mutation, +truncation/extension/type errors, PID/nonce/generation/attempt mismatches, +partial progress, restore bits, rehashed semantic corruption and numeric bounds. +The parser has no random source, pipe, socket, process, clock or target ability. + +Phase-1.0AR exclusive result channel model: + +```sh +python3 tools/validate_retroarch_phase10ar.py --root . +python3 tests/test_retroarch_phase10ar.py --root . +python3 tests/test_phase10ar_result_channel_model.py --root . +``` + +Tests cover every two-part split, byte-at-a-time delivery, EOF, deadline, +silence, overflow, damaged/wrong-identity records, writer precommit, crossing +deadline, trailing events and invalid chunks. No live transport is opened. + +Phase-1.0AS channel primitive and FD ownership audit: + +```sh +python3 tools/validate_retroarch_phase10as.py --root . \ + --sdk-root work/upstream/sdk \ + --shsrv-root work/upstream/shsrv-v0.7 +python3 tests/test_retroarch_phase10as.py --root . +``` + +The validator binds exact official Git objects. Guardrails preserve the +`RFCFDG` close-all mismatch, absent exclusive close order, nonblocking callsite, +absolute deadline, bounded reap, target code and device authorization. + +Phase-1.0AT offline RFFDG FD/deadline model: + +```sh +python3 tools/validate_retroarch_phase10at.py --root . +python3 tests/test_retroarch_phase10at.py --root . +python3 tests/test_phase10at_fd_deadline_model.py --root . +``` + +Failure injection covers every setup boundary, exact parent/child end ownership, +partial records, EINTR, would-block, absolute deadline, EOF, overflow, damaged +or trailing records and terminal cleanup. All operations are synthetic. + +Phase-1.0AU live-channel feasibility reassessment: + +```sh +python3 tools/validate_retroarch_phase10au.py --root . \ + --sdk-root work/upstream/sdk \ + --shsrv-root work/upstream/shsrv-v0.7 +python3 tests/test_retroarch_phase10au.py --root . +``` + +The validator binds the exact official SDK headers and shsrv callsites. The +guardrails keep API declarations separate from firmware behavior and reject +the current `RFCFDG`, unbounded-wait and automatic-restart composition. + +Phase-1.0AV target-free launch-context canary: + +```sh +python3 tools/validate_retroarch_phase10av.py --root . +python3 tests/test_retroarch_phase10av.py --root . +python3 tests/test_phase10av_launch_context_canary.py --root . +``` + +Tests require identical payload identity, distinct launcher/run/approval +bindings, submit-before-D04-before-terminal ordering, complete cleanup and zero +retry/persistent-write counts. A return difference remains candidate-only. + +Phase-1.0AW canary source-delta and effect audit: + +```sh +python3 tools/validate_retroarch_phase10aw.py --root . \ + --retroarch-root ../chimera-retroarch \ + --shsrv-root work/upstream/shsrv-v0.7 +python3 tests/test_retroarch_phase10aw.py --root . +``` + +The validator binds the historical M source and official shsrv v0.7 Git +objects. Guardrails require a new magic, D14 cleanup predicate and byte-identical +payload while retaining every BigApp effect and live-result blocker. + +Phase-1.0AX host-only AV protocol model: + +```sh +python3 tools/validate_retroarch_phase10ax.py --root . +python3 tests/test_retroarch_phase10ax.py --root . +python3 tests/test_phase10ax_canary_protocol_model.py --root . +``` + +Tests cover canonical 64-byte frames, every single-byte mutation, semantic +stage types, duplicate/order errors, false cleanup claims, D12 terminal misuse +and every individual D14 cleanup predicate. + +Phase-1.0AY exact RetroArch source-base selection: + +```sh +python3 tools/validate_retroarch_phase10ay.py --root . \ + --retroarch-root ../chimera-retroarch +python3 tests/test_retroarch_phase10ay.py --root . +``` + +The validator proves that the private N tip descends from M and retains every +relevant target-source blob. Guardrails require an isolated worktree and forbid +target profile, cross-build, artifact and runner activation. + +Offline Phase-1 compile only: + +```sh +bash tools/build-phase1-videoout.sh +``` + +The script verifies the SDK and SDL inputs, applies only the reviewed video-only +overlay, disables SDL2main and unrelated subsystems, builds both fail-closed +ELFs, and runs the strict static import audit. Do not execute either output. + +## Future installation and hardware-test template (not authorized) + +Every approved hardware test must record: + +- task approval reference and operator; +- console firmware identifier and allowlist entry; +- source commit and toolchain lock digest; +- exact operation permitted; +- timeout and recovery procedure; +- expected logs and success/failure criteria; +- cleanup evidence; +- result and any anomaly. + +An empty or ambiguous field means the test does not run. diff --git a/THIRD_PARTY_NOTICES.md b/THIRD_PARTY_NOTICES.md new file mode 100644 index 0000000..fe1a62d --- /dev/null +++ b/THIRD_PARTY_NOTICES.md @@ -0,0 +1,25 @@ +# Third-party notices + +No third-party source or binary is vendored in this repository. + +| Project | Use | Pinned reference | License evidence | +|---|---|---|---| +| PS5 Payload SDK | external cross-toolchain and public stub evidence | v0.41 / `d2e2e585...` | GPLv3+ generally; BSD notices under `include/freebsd` | +| PS5 Payload Manager | exact installed runtime-control evidence; not redistributed | v0.3.1 / `cfbc70f30...` | GPLv3 | +| PS5 ELF Loader | exact installed loader evidence; not redistributed | v0.23 / `699e8bcff0...` | GPLv3-or-later | +| itsPLK ps5-elfldr | excluded comparison candidate; not installed or redistributed | v0.23.2 / `148b71c2fb...` | upstream GPL notice applies | +| PS5 SDL fork | external Phase-1 software-profile static link | `0baf4ac493...` / SDL 2.30.12 | Zlib (`LICENSE.txt`); reviewed overlay preserves this notice | +| RetroArch | separate private Phase-1 port fork; not vendored here | v1.22.2 / `69a4f0ea1...` | GPL-3.0-or-later (`COPYING` and source notices) | +| ps5-payload-shsrv | external protocol/source evidence; not built or redistributed | v0.7-v0.19 / `6f320637d...` current | GPL-3.0-or-later; bundled `libtelnet` files contain a public-domain dedication | +| PacBrew repository | build/port research | `c2abcfcb60...` | repository and per-package licenses apply | +| ps5-linux-loader | architecture research only | `8e7dd40df...` | upstream license/notices apply | +| actions/checkout | pinned Gitea CI checkout action | `11bd71901b...` | MIT | + +The SDK release ZIP is downloaded into an ignored local `work/` directory or a +container layer and is not redistributed by this repository. Redistributors +must review the SDK's included notices and corresponding-source obligations. + +`chimera-gfx` is GPL-3.0-or-later. This is compatible with linking the planned +adapter into GPLv3 RetroArch and with GPLv3-or-later SDK components, subject to +the normal GPL corresponding-source and notice requirements. This statement is +an engineering license choice, not legal advice. diff --git a/adapters/retroarch/README.md b/adapters/retroarch/README.md new file mode 100644 index 0000000..270faee --- /dev/null +++ b/adapters/retroarch/README.md @@ -0,0 +1,11 @@ +# RetroArch adapter boundary + +The compiled scaffold exposes a dependency-free query contract. It reports +RGUI and SDL software fallback as the intended integration path, but reports +software-frame binding not yet implemented and hardware contexts unavailable. +Every hardware-context request returns `SAFETY_POLICY`. + +The future driver lifecycle is: initialize a `chimera_gfx_context`, create one +surface, create/reuse textures for software core frames, upload a frame, present +through the selected backend, then destroy texture, surface, and context in +that order. No RetroArch private type or header enters the public library API. diff --git a/adapters/retroarch/retroarch_adapter.c b/adapters/retroarch/retroarch_adapter.c new file mode 100644 index 0000000..7b984cc --- /dev/null +++ b/adapters/retroarch/retroarch_adapter.c @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +#include + +chimera_gfx_status chimera_gfx_retroarch_query_scaffold( + chimera_gfx_retroarch_adapter_info *out_info) { + if (out_info == NULL || out_info->struct_size < sizeof(*out_info)) { + return CHIMERA_GFX_STATUS_INVALID_ARGUMENT; + } + out_info->struct_size = sizeof(*out_info); + out_info->api_version = CHIMERA_GFX_API_VERSION; + out_info->accepts_software_frames = 0u; + out_info->supports_hardware_contexts = 0u; + out_info->supports_rgui = 1u; + out_info->has_sdl_software_fallback = 1u; + return CHIMERA_GFX_STATUS_OK; +} + +chimera_gfx_status +chimera_gfx_retroarch_bind_scaffold(chimera_gfx_context *context, + uint32_t request_hardware_context) { + if (context == NULL) { + return CHIMERA_GFX_STATUS_INVALID_ARGUMENT; + } + if (request_hardware_context != 0u) { + return CHIMERA_GFX_STATUS_SAFETY_POLICY; + } + return CHIMERA_GFX_STATUS_UNSUPPORTED; +} diff --git a/adapters/sdl2/README.md b/adapters/sdl2/README.md new file mode 100644 index 0000000..b835abc --- /dev/null +++ b/adapters/sdl2/README.md @@ -0,0 +1,11 @@ +# SDL2 renderer adapter boundary + +The compiled scaffold reports that no accelerated renderer exists and that the +current PS5 window, VideoOut, input, and audio facilities must remain owned by +SDL. Acceleration requests return `SAFETY_POLICY`; non-accelerated creation is +`UNSUPPORTED` until the real adapter phase. + +The future renderer will translate SDL textures and presents to the stable +`libchimera-gfx` resource lifecycle without replacing SDL's platform backends. +The Phase-1 clear-frame sample is evidence for the existing SDL VideoOut route, +not an accelerated renderer implementation. diff --git a/adapters/sdl2/sdl2_adapter.c b/adapters/sdl2/sdl2_adapter.c new file mode 100644 index 0000000..566b664 --- /dev/null +++ b/adapters/sdl2/sdl2_adapter.c @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +#include + +chimera_gfx_status +chimera_gfx_sdl2_query_scaffold(chimera_gfx_sdl2_adapter_info *out_info) { + if (out_info == NULL || out_info->struct_size < sizeof(*out_info)) { + return CHIMERA_GFX_STATUS_INVALID_ARGUMENT; + } + out_info->struct_size = sizeof(*out_info); + out_info->api_version = CHIMERA_GFX_API_VERSION; + out_info->accelerated_renderer_available = 0u; + out_info->reuses_ps5_window_backend = 1u; + out_info->reuses_ps5_input_audio = 1u; + out_info->software_fallback_required = 1u; + return CHIMERA_GFX_STATUS_OK; +} + +chimera_gfx_status +chimera_gfx_sdl2_create_renderer_scaffold(chimera_gfx_context *context, + uint32_t request_acceleration) { + if (context == NULL) { + return CHIMERA_GFX_STATUS_INVALID_ARGUMENT; + } + if (request_acceleration != 0u) { + return CHIMERA_GFX_STATUS_SAFETY_POLICY; + } + return CHIMERA_GFX_STATUS_UNSUPPORTED; +} diff --git a/cmake/README.md b/cmake/README.md new file mode 100644 index 0000000..6490b0c --- /dev/null +++ b/cmake/README.md @@ -0,0 +1,15 @@ +# CMake helpers + +Project-owned CMake modules are placed here as they become necessary. Phase 0 +keeps policy in the top-level build so the enabled target graph is easy to +audit. + +The Phase-1 VideoOut option imports an already verified static SDL build by +absolute path. It never downloads, patches, or executes a dependency during +CMake configuration; orchestration and pin checks live in +`tools/build-phase1-videoout.sh`. + +The installed config exports the core as `chimera-gfx::chimera-gfx` and the +capability-honest interfaces as +`chimera-gfx::chimera-gfx-retroarch-scaffold` and +`chimera-gfx::chimera-gfx-sdl2-scaffold`. diff --git a/cmake/chimera-gfx-config.cmake.in b/cmake/chimera-gfx-config.cmake.in new file mode 100644 index 0000000..15e070b --- /dev/null +++ b/cmake/chimera-gfx-config.cmake.in @@ -0,0 +1,3 @@ +@PACKAGE_INIT@ + +include("${CMAKE_CURRENT_LIST_DIR}/chimera-gfx-targets.cmake") diff --git a/docs/DECISIONS.md b/docs/DECISIONS.md new file mode 100644 index 0000000..8b9e710 --- /dev/null +++ b/docs/DECISIONS.md @@ -0,0 +1,16 @@ +# Decision log + +| Date | Decision | ADR | Status | +|---|---|---|---| +| 2026-07-16 | Create an independent `chimera-gfx` repository | ADR-0001 | accepted | +| 2026-07-16 | Treat GNM export names as discovery evidence only | ADR-0002 | accepted | +| 2026-07-16 | Keep the Phase-0 probe non-rendering and fail-closed | ADR-0003 | accepted | +| 2026-07-16 | Use GPL-3.0-or-later for project code | ADR-0004 | accepted | +| 2026-07-17 | Define the resource lifecycle through a non-rendering mock model | ADR-0005 | accepted | +| 2026-07-17 | Keep the first VideoOut experiment behind pinned SDL2 | ADR-0006 | accepted | +| 2026-07-17 | Bind every candidate to a machine-readable artifact manifest | ADR-0007 | accepted | +| 2026-07-17 | Allowlist 9.60 for probe builds while blocking SDK-CRT execution | ADR-0008 | accepted | +| 2026-07-17 | Block unproven minimal startup and permanently deny the legacy ELF | ADR-0009 | accepted | + +New decisions that change safety, ABI, firmware, licensing, or adapter +boundaries require a numbered ADR and an update here. diff --git a/docs/PUBLICATION_READINESS.md b/docs/PUBLICATION_READINESS.md new file mode 100644 index 0000000..2ef789f --- /dev/null +++ b/docs/PUBLICATION_READINESS.md @@ -0,0 +1,40 @@ +# Publication readiness + +The current source tip is prepared for public review. The repository remains +private until the history and security-contact decisions below are confirmed. + +## Completed + +- GPL-3.0-or-later license, contribution guidance, security policy, third-party + notices, and a user-oriented README are present. +- Development branches are consolidated into one reviewed candidate branch. +- No generated ELF, SDK archive, crash dump, device capture, or other binary + release artifact is tracked. +- Current source paths and manifests contain no private LAN repository URL or + operator-specific filesystem path. +- Host builds, policy tests, JSON validation, large-object review, and current + plus all-ref secret scans are part of the publication review. +- CI actions, container images, SDK downloads, and source revisions are pinned; + downloaded SDK bytes are verified before use. Distribution packages follow + the security-updated repository attached to the pinned base image rather than + stale exact package revisions. +- `SECURITY.md` publishes a fixed private reporting address. +- Pull requests from public forks cannot run on the self-hosted CI runner. +- `tools/export-public-source.sh` creates a parentless source candidate, strips + machine-local agent instructions, and rejects private deployment markers, + forbidden secret files, generated binaries, and oversized files. + +## Decisions required before changing visibility + +1. **Release policy.** Recommended: publish reviewed source only. Do not attach + runnable PS5 ELF artifacts; if that policy changes later, require reproducible + builds, checksums, corresponding source, and a separate safety review. + +Historical commits contain an old private LAN URL, operator-specific paths, and +author email metadata. Keep that canonical history private and publish only the +parentless export from a reviewed commit. Deleting branches is not a substitute +for this export. + +Generated payloads, crash dumps, SDK archives, local hardware captures, and +unredacted operator records are not public source artifacts and must remain +outside Git. diff --git a/docs/adr/0001-standalone-repository.md b/docs/adr/0001-standalone-repository.md new file mode 100644 index 0000000..3da7e01 --- /dev/null +++ b/docs/adr/0001-standalone-repository.md @@ -0,0 +1,15 @@ +# ADR-0001: Standalone repository + +- Status: accepted +- Date: 2026-07-16 + +## Decision + +`chimera-gfx` is a new repository and workspace, not a subdirectory, worktree, +submodule, or branch of the existing Chimera project. + +## Consequences + +It has an independent Git history, private Gitea origin, license, CI, release +policy, dependencies, and risk boundary. No path or build step refers to the +existing Chimera repository. diff --git a/docs/adr/0002-export-names-are-not-abi.md b/docs/adr/0002-export-names-are-not-abi.md new file mode 100644 index 0000000..ce42f11 --- /dev/null +++ b/docs/adr/0002-export-names-are-not-abi.md @@ -0,0 +1,21 @@ +# ADR-0002: Export names are not ABI definitions + +- Status: accepted +- Date: 2026-07-16 + +## Context + +The public PS5 Payload SDK v0.41 contains GNM export stubs but no GNM headers, +structure definitions, or GPU samples. + +## Decision + +An export name proves only that the pinned stub set exposes that name. No C +prototype, calling convention detail, structure layout, ownership rule, or +semantic behavior is inferred from it. Phase 0 may resolve a name and record a +boolean, but may not call the result. + +## Consequences + +Even `sceGnmAreSubmitsAllowed` is not called. Each future ABI must be accepted +through a new evidence record before use. diff --git a/docs/adr/0003-fail-closed-probe.md b/docs/adr/0003-fail-closed-probe.md new file mode 100644 index 0000000..eadcb8f --- /dev/null +++ b/docs/adr/0003-fail-closed-probe.md @@ -0,0 +1,20 @@ +# ADR-0003: Fail-closed capability probe + +- Status: accepted +- Date: 2026-07-16 + +## Decision + +The PS5 probe is disabled in ordinary builds. A probe build embeds exactly one +approved firmware identifier, defaulting to `NONE`. Runtime requires an exact +firmware match and the literal `--acknowledge-read-only-probe` argument before +module loading. The checked-in firmware matrix is empty. + +The platform shim may call only `dlopen`, `dlsym`, and `dlclose`. Logs contain +symbol names and booleans, never addresses. There is no deploy target. + +## Consequences + +Default artifacts cannot discover symbols on hardware. A deliberately gated +artifact still requires separate human authorization before transfer or +execution. Loader-internal side effects remain an explicitly recorded unknown. diff --git a/docs/adr/0004-license.md b/docs/adr/0004-license.md new file mode 100644 index 0000000..ea28e64 --- /dev/null +++ b/docs/adr/0004-license.md @@ -0,0 +1,15 @@ +# ADR-0004: GPL-3.0-or-later + +- Status: accepted +- Date: 2026-07-16 + +## Decision + +Project-owned code is licensed GPL-3.0-or-later. + +## Rationale + +The planned RetroArch adapter targets a GPLv3 project and the PS5 Payload SDK is +generally GPLv3-or-later. A common GPL-compatible project license keeps the +combined distribution boundary understandable. Third-party code is not +vendored and retains its own notices. diff --git a/docs/adr/0005-versioned-resource-lifecycle.md b/docs/adr/0005-versioned-resource-lifecycle.md new file mode 100644 index 0000000..bf9a238 --- /dev/null +++ b/docs/adr/0005-versioned-resource-lifecycle.md @@ -0,0 +1,24 @@ +# ADR-0005: Versioned resource lifecycle and host present model + +- Status: accepted +- Date: 2026-07-17 + +## Decision + +API version 2 includes opaque contexts, surfaces, and textures, plus sized +descriptors for texture upload and present. Context destruction returns +`RESOURCE_BUSY` while child handles exist. The version-1 `chimera_gfx_destroy` +symbol remains as a void compatibility wrapper for childless contexts. The +mock backend implements the complete ownership model in host memory and records +deterministic present serials and content hashes. + +The mock present is a state transition, not a graphical operation. It creates +no window, accesses no display, and advertises both `NON_RENDERING` and +`HOST_TEST_ONLY`. The PS5 backend is a separate compiled unit that refuses +context creation with `SAFETY_POLICY`. + +## Consequences + +Adapter work can compile and test lifecycle assumptions before a hardware ABI +exists. Future backends must preserve validation and cleanup semantics, but +must not inherit mock capabilities without evidence. diff --git a/docs/adr/0006-phase1-sdl-videoout-boundary.md b/docs/adr/0006-phase1-sdl-videoout-boundary.md new file mode 100644 index 0000000..04156f7 --- /dev/null +++ b/docs/adr/0006-phase1-sdl-videoout-boundary.md @@ -0,0 +1,29 @@ +# ADR-0006: Phase-1 VideoOut experiment stays behind SDL2 + +- Status: accepted +- Date: 2026-07-17 + +## Context + +The public SDK v0.41 exports VideoOut stubs but supplies no official VideoOut +headers. The pinned PS5 SDL2 fork contains a working framebuffer route, but its +backend declares opaque layouts with fields named `junk`. Those declarations +are public source evidence of the upstream implementation, not a sufficiently +proven ABI for duplication in `chimera-gfx`. + +## Decision + +The first presentation candidate calls only public SDL2 APIs. A reviewed +Zlib-licensed build overlay removes SDL's unrelated keyboard/IME initialization +from this candidate. Project code does not copy a VideoOut prototype or data +layout. The target is disabled by default, requires the PS5 toolchain and an +explicit SDL build path, and embeds the same `NONE` firmware gate as the probe. + +The candidate does not link SDL2main because that wrapper hides the splash +screen before application-level firmware validation. + +## Consequences + +The upstream SDL backend remains the owner of VideoOut, direct memory, tiling, +flip, and cleanup. Its unbounded flip wait is a known hardware-test blocker; +successful offline compilation is not runtime approval or ABI proof. diff --git a/docs/adr/0007-artifact-provenance.md b/docs/adr/0007-artifact-provenance.md new file mode 100644 index 0000000..1aa9c5e --- /dev/null +++ b/docs/adr/0007-artifact-provenance.md @@ -0,0 +1,15 @@ +# ADR-0007: Artifact provenance is machine-readable and fail-closed + +- Status: accepted +- Date: 2026-07-17 + +## Decision + +Every review or hardware candidate uses artifact-manifest schema version 1. +The manifest binds filename, size, SHA-256, target, clean source commit, +toolchain pins, optional SDL commit, firmware gate, and explicit non-execution +state. Generation and verification are separate tested tools. + +An artifact with firmware identifier `NONE` is never allowlisted. Any rebuild, +including a firmware-specific rebuild, creates a new digest and therefore +requires a new artifact-specific approval. diff --git a/docs/adr/0008-firmware-9.60-probe-build.md b/docs/adr/0008-firmware-9.60-probe-build.md new file mode 100644 index 0000000..94b7a11 --- /dev/null +++ b/docs/adr/0008-firmware-9.60-probe-build.md @@ -0,0 +1,39 @@ +# ADR-0008: Firmware 9.60 is build-allowlisted; SDK-CRT execution is blocked + +- Status: accepted +- Date: 2026-07-17 + +## Context + +Jens reported exact PS5 firmware `9.60` and authorized an offline-only, +non-rendering capability-probe build and audit. Transfer, connection, execution, +VideoOut, framebuffer/GPU/GNM mutation, draw, dispatch, submit, flip, MMIO, +register writes, and kernel/firmware changes remain prohibited. + +The pinned public SDK v0.41 source was audited beyond project `main`. Its +payload startup calls `__patch_init` before `main`. That function changes the +current process's kernel credential fields and syscall-address bounds using the +SDK kernel read/write path. The SDK rtld also ensures `libSceSysmodule.sprx` is +available during startup. A later project `dlopen` can load/start +`libSceGnmDriver.sprx`; `dlclose` can stop/unload it. + +## Decision + +The discovery manifest and probe configure gate accept exactly `9.60`; the +default remains `NONE`. This allowlist entry authorizes only a reproducible +offline build. Phase-1 VideoOut rejects every non-`NONE` firmware gate. + +The resulting 9.60 ELF is named `offline-audit-only`, records zero transfer and +execution, and is not execution-eligible. No literal execution approval is +offered while the linked SDK CRT performs kernel writes before the project gate. + +## Consequences + +- Project code still invokes no resolved GNM pointer and contains no rendering, + submit, draw, dispatch, flip, or GPU-memory operation. +- The artifact is useful for deterministic compilation, import/disassembly + review, hashing, and future startup research only. +- A new ADR and a new hash-bound build are required after a public, + kernelwrite-free startup/loader route is proven. +- Transfer or execution of this artifact would violate the current safety + policy even if Jens later supplied a generic execution approval. diff --git a/docs/adr/0009-block-unproven-minimal-startup.md b/docs/adr/0009-block-unproven-minimal-startup.md new file mode 100644 index 0000000..e92d6de --- /dev/null +++ b/docs/adr/0009-block-unproven-minimal-startup.md @@ -0,0 +1,43 @@ +# ADR-0009: Block unproven minimal startup and permanently deny legacy ELF + +Status: accepted +Date: 2026-07-17 + +## Context + +SDK v0.41's stock `crt1.o` performs prohibited kernel credential and +syscall-bound writes before project `main`. Phase 0.5 tested whether omitting +the CRT could support a deterministic freestanding entry. Compiler and linker +evidence proves omission is mechanically possible, but no exact pinned loader +caller is locally available to prove stack, argument ownership, safe return, +post-return cleanup, crash handling, or pre-entry process changes. + +The already-built firmware-9.60 capability probe has SHA-256 +`4be1c17b4964f2b68c39b5145bc4af4619c32512d60269ecf5c39728b390fa63` +and must remain permanently blocked. + +## Decision + +Do not create a minimal-startup source or PS5 ELF while the caller contract is +unproven. Keep `CHIMERA_GFX_BUILD_PS5_MINIMAL_STARTUP` as an unconditional +configure-time failure with the evidence blocker stated in its message. + +Require every artifact manifest to state `execution_eligible` explicitly. New +manifests default to false. Maintain a permanent SHA-256 denylist and a +fail-closed policy tool that refuses false eligibility, malformed records, +changed bytes, or a denylist match. Passing this static layer does not grant +execution authority. + +## Consequences + +- no Phase-0.5 PS5 ELF, linker map, artifact disassembly, or artifact hash + exists; +- stock CRT evidence remains available for review without making a safe-runtime + claim; +- the legacy firmware-9.60 ELF cannot become eligible through a later manifest + edit or approval record; +- a Payload Manager can consume the JSON policy tool, but the manager's own + implementation remains outside this repository and cannot be claimed + reviewed here; +- work may continue through mock/software backends or a separately scoped + Linux-on-PS5 backend. diff --git a/docs/adr/0010-controlled-runtime-effects.md b/docs/adr/0010-controlled-runtime-effects.md new file mode 100644 index 0000000..26dfe40 --- /dev/null +++ b/docs/adr/0010-controlled-runtime-effects.md @@ -0,0 +1,45 @@ +# ADR-0010: Controlled runtime effects and Phase-0.6 gate + +Status: accepted on 2026-07-17. + +## Context + +ADR-0009 asked whether a fully kernelwrite-free startup could be proven while +the exact loader was absent. The current task corrects the safety model: +documented, bounded, volatile runtime changes may be acceptable. Persistent +writes and unbounded or unknown effects remain hard blockers. + +The installed Payload Manager and elfldr were subsequently identified exactly. +Their public source exposes both acceptable volatile effects and unresolved +unbounded paths. + +## Decision + +Classify each lifecycle effect as exactly one of: + +- `EXPECTED_VOLATILE_RUNTIME_EFFECT`; +- `RESTORED_BY_LOADER`; +- `PAYLOAD_PROCESS_LOCAL`; +- `PERSISTENT_WRITE`; +- `UNBOUNDED_OR_UNKNOWN`. + +Only the final two are categorical blockers. `__patch_init` is classified as +payload-process-local and is no longer an automatic blocker. + +Every potentially eligible artifact must also have a profile named exactly +`controlled-ps5-runtime`, with exact firmware/loader/SDK/artifact identity, +an explicit list of expected volatile effects, zero persistent and filesystem +writes, no payload network, a maximum 2000 ms runtime, no retry, no hard +effects, and no hard blockers. Both the profile and static gate explicitly set +`execution_authorized=false`; static eligibility never grants execution +authority. + +## Consequences + +Phase 0.6 remains blocked because the exact chain has unbounded ptrace loops, +no payload watchdog, unresolved termination/cleanup, incomplete credential +restoration, no launch-time hash enforcement, and a persistent manager upload +path. No lifecycle source or ELF is built. + +ADR-0009 and its Phase-0.5 evidence remain historical records. The permanent +artifact denylist is unchanged. diff --git a/docs/adr/0011-phase07-hardened-controlled-runtime.md b/docs/adr/0011-phase07-hardened-controlled-runtime.md new file mode 100644 index 0000000..8baece3 --- /dev/null +++ b/docs/adr/0011-phase07-hardened-controlled-runtime.md @@ -0,0 +1,49 @@ +# ADR-0011: Phase-0.7 hardened controlled runtime + +- Status: accepted for offline deployment preparation +- Date: 2026-07-17 +- Decision: `READY_FOR_HARDENED_RUNTIME_DEPLOYMENT` + +## Context + +Phase 0.6 identified exact upstream loader and Payload Manager versions but +found unbounded ptrace completion, incomplete credential restoration, missing +reaping/watchdog behavior, and a path-based unhashed launch route. Those +findings were implementation inputs, not permanent platform blockers. + +Jens explicitly confirmed exact firmware 9.60. Independent device attestation +is therefore not a Phase-0.7 blocker. The controlled upload below +`/data/pldmgr/payloads/chimera-controlled` is an allowed, removable +application write and is not a firmware write. + +## Decision + +Use private GPL-3.0 hardening forks based on: + +- elfldr `699e8bcff03e91e8d6ca6eba281af25c5a58d8c2`; +- Payload Manager `cfbc70f30f419b09bf2b52283f7409e2d3117ee1`; +- PS5 Payload SDK `d2e2e585740362976a39fdd5ccf390f199a7bc37`. + +The controlled route is a versioned loopback-only protocol with exact +firmware, artifact ID, size, SHA-256, timeout, and no-retry metadata. elfldr +rehashes received bytes, enforces the permanent denylist, applies bounded +ptrace steps and cleanup, and reaps the child through a two-second watchdog. +The controlled manager is compiled for one exact lifecycle artifact and opens, +hashes, rewinds, and streams one no-follow file descriptor. + +The lifecycle probe uses normal SDK v0.41 startup, performs one +`sceKernelSendNotificationRequest`, and calls `_exit`; it does not return +through `payload_terminate`. + +## Consequences + +The three ELFs are eligible for a later, separately authorized hardened +runtime installation. This decision does not authorize installation, +transfer, or execution. The previously blocked SHA-256 +`4be1c17b4964f2b68c39b5145bc4af4619c32512d60269ecf5c39728b390fa63` +remains permanently denied. + +Normal SDK startup still reaches documented process-local patch and runtime +initialization. The existing elfldr bootstrap still enables volatile QA flags. +Both facts are explicit expected runtime effects; neither is silently claimed +kernelwrite-free. diff --git a/docs/adr/0012-phase10j-first-frame-identity.md b/docs/adr/0012-phase10j-first-frame-identity.md new file mode 100644 index 0000000..b3e15b6 --- /dev/null +++ b/docs/adr/0012-phase10j-first-frame-identity.md @@ -0,0 +1,38 @@ +# ADR-0012: Phase-1.0J first-frame identity + +- Status: accepted for offline diagnostic construction +- Date: 2026-07-22 +- Decision: `FIRST_FRAME_INDEX_ZERO_SOURCE_CONSISTENCY_ONLY` + +## Context + +The consumed Phase-1.0H run submitted the diagnostic buffer with +`sceVideoOutSubmitFlip(handle, 0, 1, 0)` and received `-1`. The diagnostic +helper used buffer zero, while the patched normal SDL update path started its +frame counter at one. The public PS5 SDL source at commit +`0baf4ac49382b537ba449901b5b6d0d189bb1fbb` starts that normal counter at +zero. Phase 1.0I classified the mismatch as a strong source candidate, not a +proven firmware root cause. + +## Decision + +The Phase-1.0J SDL overlay defines one compile-time source of truth, +`CHIMERA_PS5_FIRST_FRAME_INDEX`, with value zero. The early diagnostic copy, +the early diagnostic submit and the normal update counter all derive their +initial index from that macro. + +Zero is selected because it preserves the already-audited diagnostic tuple +and agrees with the public SDL source. It is not selected on the basis of an +unpublished ABI or an inferred firmware requirement. + +## Consequences + +The internal zero-versus-one inconsistency is removed and can be checked in +source, linker-map and disassembly evidence. This decision does not prove that +buffer zero is accepted on firmware 9.60, that the fourth flip argument has +the required semantics, or that the old mismatch caused the Phase-1.0H +failure. + +Phase 1.0J remains an offline artifact phase. A separately reviewed exact +artifact and new explicit permission would be required before any connection, +transfer, result reception or execution. diff --git a/docs/approvals/phase-0.8-bounded-observation-template.md b/docs/approvals/phase-0.8-bounded-observation-template.md new file mode 100644 index 0000000..dd76af2 --- /dev/null +++ b/docs/approvals/phase-0.8-bounded-observation-template.md @@ -0,0 +1,131 @@ +# Phase 0.8 future bounded-observation permission template + +Status: **TEMPLATE ONLY — NOT AUTHORIZED**. + +This file is not permission. Empty, `null`, zero, missing, ambiguous, expired, +or inconsistent input means `STOP`. Copying, editing, signing, or committing +the template does not itself authorize a connection, transfer, or execution. +Authorization can exist only through a new exact statement from Jens in the +active task after the proposed method and its complete effect contract have +been reviewed. + +The template may describe only a separately named bounded-observation phase. +It cannot authorize installation, rollback creation, lifecycle execution, +autoload, retry, graphics work, or RetroArch work. + +## Machine-readable default state + +The validator requires every authority value below to remain `false` in the +repository template and every request-specific field to remain unfilled. + + +```json +{ + "template_only": true, + "authorized": false, + "execution_authorized": false, + "transfer_authorized": false, + "installation_authorized": false, + "lifecycle_authorized": false, + "automatic_retry": false, + "required_fields": { + "exact_user_statement": null, + "authorization_date": null, + "expiration_time": null, + "device_identity": null, + "exact_purpose": null, + "exact_observations": null, + "method_or_collector_id": null, + "source_commit": null, + "collector_file_size": null, + "collector_sha256": null, + "firmware_gate": null, + "maximum_runtime_ms": null, + "maximum_execution_count": null, + "maximum_transfer_count": null, + "network_behavior": null, + "output_channel": null, + "allowed_volatile_effects": null, + "prohibited_persistent_effects": null, + "stop_criteria": null, + "cleanup_requirements": null, + "reporting_requirements": null, + "explicit_installation_exclusion": null, + "explicit_lifecycle_probe_exclusion": null, + "explicit_autoload_and_retry_exclusion": null, + "explicit_graphics_and_retroarch_exclusion": null, + "revocation_method": null, + "manual_confirmation_template_does_not_authorize": null + }, + "fixed_exclusions": { + "installation": true, + "lifecycle_probe": true, + "autoload": true, + "automatic_retry": true, + "gnm": true, + "videoout": true, + "sdl": true, + "retroarch": true + } +} +``` + + +## Required human review fields + +Every field below must be supplied in a later review package. Do not fill it +in this repository template. + +- Exact user statement: +- Authorization date and timezone: +- Expiration time and timezone: +- Device identity: +- Exact purpose: +- Exact observations: +- Method or collector ID: +- Source repository and commit: +- Collector filename: +- Collector byte size: +- Collector SHA-256: +- Exact firmware gate: +- Maximum runtime: +- Maximum execution count: +- Maximum transfer count: +- Network behavior, addresses, ports, direction, and connection count: +- Output channel and maximum output: +- Exhaustive allowed volatile effects: +- Exhaustive forbidden persistent and functional effects: +- STOP criteria: +- Cleanup requirements: +- Reporting and raw-evidence requirements: +- Explicit installation exclusion: +- Explicit lifecycle-probe exclusion: +- Explicit autoload and retry exclusion: +- Explicit GNM, VideoOut, SDL, and RetroArch exclusion: +- Revocation method and immediate-stop behavior: +- Manual confirmation that this template is not authorization: + +No artifact ID, filename, size, SHA-256, path, ABI, syscall, loader contract, +transport, or execution mechanism is implied by these empty fields. + +## Mandatory future effect boundary + +A future request must enumerate every allowed category-C volatile effect +before authorization. All category-A persistent mutations and category-B +functional mutations remain prohibited unless a different, explicitly +mutating phase is designed and separately authorized. + +Unknown behavior, an unspecified effect, an identity mismatch, a partial +dataset, a timeout, an expired authorization, a count overrun, an unexpected +log, or cleanup uncertainty means immediate `STOP` with no automatic retry. + +The approved observation count, transfer count, and execution count—if any— +must be literal bounded integers. No value carries into rollback preparation, +installation, lifecycle testing, graphics, or RetroArch. + +## Revocation + +Jens may revoke a later authorization at any time before or during the +authorized window. Revocation, ambiguity, interruption, or conflicting +instructions causes fail-closed stop. Revocation never triggers cleanup that +was not already explicitly reviewed and authorized. diff --git a/docs/approvals/phase-0.9-backup-creation-template.md b/docs/approvals/phase-0.9-backup-creation-template.md new file mode 100644 index 0000000..930a807 --- /dev/null +++ b/docs/approvals/phase-0.9-backup-creation-template.md @@ -0,0 +1,69 @@ +# Phase 0.9 future backup-creation approval template + +Status: **TEMPLATE ONLY — NOT AUTHORIZED**. + +This template is for one possible future backup-creation change window for one +component. It cannot authorize observation collection, staging, switch, +installation, execution, lifecycle, autoload, retry or a second component. + + +```json +{ + "template_only": true, + "template_action": "backup_creation", + "authorized": false, + "installation_authorized": false, + "execution_authorized": false, + "transfer_authorized": false, + "lifecycle_authorized": false, + "automatic_retry": false, + "required_fields": { + "exact_user_statement": null, + "active_task_id": null, + "authorization_issued_at": null, + "authorization_expires_at": null, + "device_identity": null, + "firmware_exact": null, + "component": null, + "exact_action": null, + "maximum_action_count": null, + "maximum_runtime_ms": null, + "live_path": null, + "live_mount_id": null, + "live_object_id": null, + "live_type": null, + "live_size": null, + "live_sha256": null, + "backup_path": null, + "backup_mount_id": null, + "minimum_free_bytes_and_metadata_reserve": null, + "reopen_and_rehash_contract": null, + "target_mapping": null, + "independent_recovery_executor": null, + "second_independent_recovery_path": null, + "allowed_persistent_effects": null, + "forbidden_effects": null, + "cleanup_contract": null, + "stop_conditions": null, + "revocation_method": null, + "reviewer": null + }, + "fixed_exclusions": { + "staging": true, + "switch": true, + "installation": true, + "execution": true, + "second_component": true, + "lifecycle_probe": true, + "autoload": true, + "automatic_retry": true, + "graphics": true, + "retroarch": true + } +} +``` + + +The exact current live identity and separate backup destination must be filled +from later admissible evidence. A stock reference hash does not fill this +template. The Payload Manager backup hard gate remains open. diff --git a/docs/approvals/phase-0.9-observation-template.md b/docs/approvals/phase-0.9-observation-template.md new file mode 100644 index 0000000..c54173f --- /dev/null +++ b/docs/approvals/phase-0.9-observation-template.md @@ -0,0 +1,64 @@ +# Phase 0.9 future observation approval template + +Status: **TEMPLATE ONLY — NOT AUTHORIZED**. + +This template never grants permission. It is bound only to a possible future +observation action and cannot authorize backup creation, staging, switch, +installation, lifecycle, execution, autoload, retry, graphics or RetroArch. +Every request-specific value is deliberately empty. + + +```json +{ + "template_only": true, + "template_action": "observation", + "authorized": false, + "installation_authorized": false, + "execution_authorized": false, + "transfer_authorized": false, + "lifecycle_authorized": false, + "automatic_retry": false, + "required_fields": { + "exact_user_statement": null, + "active_task_id": null, + "authorization_issued_at": null, + "authorization_expires_at": null, + "device_identity": null, + "firmware_exact": null, + "firmware_source_one": null, + "firmware_source_two": null, + "component": null, + "exact_action": null, + "maximum_action_count": null, + "maximum_runtime_ms": null, + "collector_or_artifact_id": null, + "collector_or_artifact_size": null, + "collector_or_artifact_sha256": null, + "source_commit": null, + "allowed_observations": null, + "allowed_volatile_effects": null, + "forbidden_effects": null, + "output_contract": null, + "cleanup_contract": null, + "stop_conditions": null, + "revocation_method": null, + "reviewer": null + }, + "fixed_exclusions": { + "backup_creation": true, + "staging": true, + "switch": true, + "installation": true, + "execution": true, + "lifecycle_probe": true, + "autoload": true, + "automatic_retry": true, + "graphics": true, + "retroarch": true + } +} +``` + + +Empty, ambiguous, expired, mismatched or unreviewed data means `STOP`. A copy, +signature, edit or commit of this file is not approval. diff --git a/docs/approvals/phase-0.9-one-shot-execution-template.md b/docs/approvals/phase-0.9-one-shot-execution-template.md new file mode 100644 index 0000000..40024d7 --- /dev/null +++ b/docs/approvals/phase-0.9-one-shot-execution-template.md @@ -0,0 +1,64 @@ +# Phase 0.9 future one-shot execution approval template + +Status: **TEMPLATE ONLY — NOT AUTHORIZED**. + +This template can be considered only after a separately approved switch, +durable post-switch identity verification and human review. It is bound to one +component and one manual execution. It does not authorize transfer, +installation, lifecycle-probe execution, autoload, retry or another component. + + +```json +{ + "template_only": true, + "template_action": "one_shot_execution", + "authorized": false, + "installation_authorized": false, + "execution_authorized": false, + "transfer_authorized": false, + "lifecycle_authorized": false, + "automatic_retry": false, + "required_fields": { + "exact_user_statement": null, + "active_task_id": null, + "authorization_issued_at": null, + "authorization_expires_at": null, + "device_identity": null, + "firmware_exact": null, + "component": null, + "exact_action": null, + "maximum_execution_count": null, + "maximum_runtime_ms": null, + "live_artifact_id": null, + "live_source_commit": null, + "live_size": null, + "live_sha256": null, + "live_mount_id": null, + "live_object_id": null, + "post_switch_verification_evidence": null, + "verified_backup_identity": null, + "independent_recovery_executor": null, + "success_criteria": null, + "stop_conditions": null, + "cleanup_contract": null, + "reporting_contract": null, + "revocation_method": null, + "reviewer": null + }, + "fixed_exclusions": { + "transfer": true, + "installation": true, + "second_execution": true, + "second_component": true, + "lifecycle_probe": true, + "autoload": true, + "automatic_retry": true, + "graphics": true, + "retroarch": true + } +} +``` + + +The template itself is not a request and is not permission. Phase 0.9A does +not ask for execution approval. diff --git a/docs/approvals/phase-0.9-staging-template.md b/docs/approvals/phase-0.9-staging-template.md new file mode 100644 index 0000000..f197aa7 --- /dev/null +++ b/docs/approvals/phase-0.9-staging-template.md @@ -0,0 +1,63 @@ +# Phase 0.9 future staging approval template + +Status: **TEMPLATE ONLY — NOT AUTHORIZED**. + +This template could bind one future inactive candidate-staging action. It +cannot authorize a live switch, installation, execution, lifecycle, autoload, +retry or a second component. + + +```json +{ + "template_only": true, + "template_action": "staging", + "authorized": false, + "installation_authorized": false, + "execution_authorized": false, + "transfer_authorized": false, + "lifecycle_authorized": false, + "automatic_retry": false, + "required_fields": { + "exact_user_statement": null, + "active_task_id": null, + "authorization_issued_at": null, + "authorization_expires_at": null, + "device_identity": null, + "firmware_exact": null, + "component": null, + "exact_action": null, + "maximum_action_count": null, + "maximum_runtime_ms": null, + "candidate_artifact_id": null, + "candidate_source_commit": null, + "candidate_size": null, + "candidate_sha256": null, + "staging_path": null, + "staging_mount_id": null, + "staging_inactive_proof": null, + "verified_backup_identity": null, + "verified_backup_sha256": null, + "candidate_reopen_and_rehash_contract": null, + "allowed_persistent_effects": null, + "forbidden_effects": null, + "cleanup_contract": null, + "stop_conditions": null, + "revocation_method": null, + "reviewer": null + }, + "fixed_exclusions": { + "live_switch": true, + "installation": true, + "execution": true, + "second_component": true, + "lifecycle_probe": true, + "autoload": true, + "automatic_retry": true, + "graphics": true, + "retroarch": true + } +} +``` + + +A candidate hash or offline build record alone never authorizes staging. diff --git a/docs/approvals/phase-0.9-switch-template.md b/docs/approvals/phase-0.9-switch-template.md new file mode 100644 index 0000000..53f911f --- /dev/null +++ b/docs/approvals/phase-0.9-switch-template.md @@ -0,0 +1,67 @@ +# Phase 0.9 future live-switch approval template + +Status: **TEMPLATE ONLY — NOT AUTHORIZED**. + +This template is limited to one future switch of one separately staged +component. It cannot authorize staging, execution, lifecycle, rollback, +autoload, retry or a second component. + + +```json +{ + "template_only": true, + "template_action": "live_switch", + "authorized": false, + "installation_authorized": false, + "execution_authorized": false, + "transfer_authorized": false, + "lifecycle_authorized": false, + "automatic_retry": false, + "required_fields": { + "exact_user_statement": null, + "active_task_id": null, + "authorization_issued_at": null, + "authorization_expires_at": null, + "device_identity": null, + "firmware_exact": null, + "component": null, + "exact_action": null, + "maximum_action_count": null, + "maximum_runtime_ms": null, + "live_path_mount_object_size_sha256": null, + "backup_path_mount_object_size_sha256": null, + "candidate_path_mount_object_size_sha256": null, + "target_quiescence_evidence": null, + "autoload_and_retry_disabled_evidence": null, + "atomic_switch_primitive_identity": null, + "atomicity_evidence": null, + "file_durability_evidence": null, + "directory_durability_evidence": null, + "power_loss_evidence": null, + "post_switch_verification_contract": null, + "independent_recovery_executor": null, + "rollback_target_mapping": null, + "allowed_persistent_effects": null, + "forbidden_effects": null, + "stop_conditions": null, + "revocation_method": null, + "reviewer": null + }, + "fixed_exclusions": { + "staging": true, + "execution": true, + "second_component": true, + "lifecycle_probe": true, + "autoload": true, + "automatic_retry": true, + "in_place_overwrite": true, + "two_step_rename_gap": true, + "graphics": true, + "retroarch": true + } +} +``` + + +While atomicity or durability is `UNPROVEN`, this template must remain empty +and no switch request may be made. diff --git a/docs/approvals/phase-0.9b-observer-execution-template.md b/docs/approvals/phase-0.9b-observer-execution-template.md new file mode 100644 index 0000000..be6b20c --- /dev/null +++ b/docs/approvals/phase-0.9b-observer-execution-template.md @@ -0,0 +1,76 @@ +# Phase 0.9B future observer execution template + +Status: **TEMPLATE ONLY — NOT AUTHORIZED — BLOCKED**. + +This file cannot authorize an action. There is no observer artifact to bind, +and the startup/exit and output-channel gates are blocked. Every +request-specific value is deliberately empty. Editing, signing, copying, +committing or pushing this template does not grant permission. + + +```json +{ + "template_only": true, + "phase": "0.9B", + "action": "one_shot_observer_execution", + "status": "BLOCKED", + "authorized": false, + "transfer_authorized": false, + "execution_authorized": false, + "installation_authorized": false, + "lifecycle_authorized": false, + "autoload_authorized": false, + "backup_creation_authorized": false, + "automatic_retry": false, + "observer_only": true, + "required_fields": { + "exact_user_statement": null, + "active_task_id": null, + "authorization_issued_at": null, + "authorization_expires_at": null, + "device_identity": null, + "firmware_exact": null, + "firmware_source_one": null, + "firmware_source_two": null, + "observer_build_id": null, + "observer_source_commit": null, + "observer_size": null, + "observer_sha256": null, + "toolchain_identity": null, + "runtime_path": null, + "allowed_observations": null, + "allowed_read_paths": null, + "output_channel": null, + "maximum_runtime_ms": null, + "maximum_execution_count": null, + "stop_criteria": null, + "reviewer": null + }, + "fixed_exclusions": { + "device_address": true, + "installation": true, + "lifecycle": true, + "autoload": true, + "automatic_retry": true, + "backup_creation": true, + "file_mutation": true, + "process_or_service_mutation": true, + "listener_creation": true, + "kernelwrite": true, + "graphics": true, + "sdl": true, + "retroarch": true + }, + "blocking_facts": [ + "OBSERVER_STARTUP_OR_EXIT_ABI_UNPROVEN", + "NO_PROVEN_NON_PERSISTENT_OUTPUT_CHANNEL", + "OBSERVER_ARTIFACT_ABSENT" + ] +} +``` + + +A future request would need a new active-task authorization bound to one exact +artifact, device, firmware, output channel, observation plan, runtime, +one execution and expiration. That later request still could not authorize +installation, lifecycle, backup creation, autoload or retry. diff --git a/docs/approvals/phase-0.9e-r-y2jb-deployed-use-attestation.md b/docs/approvals/phase-0.9e-r-y2jb-deployed-use-attestation.md new file mode 100644 index 0000000..7de53e0 --- /dev/null +++ b/docs/approvals/phase-0.9e-r-y2jb-deployed-use-attestation.md @@ -0,0 +1,57 @@ +# Phase 0.9E-R Y2JB deployed-use operator attestation + +Status: `UNATTESTED_TEMPLATE` + +This template records a future operator statement. It is not device evidence, +runtime verification, permission, or approval. Do not add secrets, PS5 +addresses, account identifiers, signed download URLs, or credentials. + +```yaml +schema_version: 1 +phase: PHASE_0_9E_R_Y2JB_DEPLOYED_USE_ATTESTATION +attested: false +attestation_date: null +operator: null + +outer_zip: + file_name: null + sha256: null + size: null + obtained_from: null + installation_date_exact_or_estimated: null + installation_date_is_estimate: null + restore_or_install_method: null + firmware_at_restore_or_install: null + +subsequent_changes: + another_y2jb_backup_applied: null + download0_dat_replaced_separately: null + youtube_or_appdata_changed: null + external_autoloader_used: null + external_autoloader_identity: null + +host_sender: + tool_name: null + path_or_source: null + version_or_commit: null + sha256: null + default_port: null + ports_actually_selected: [] + +classification_when_completed: OPERATOR_ATTESTED_DEPLOYED_USE +runtime_verified: false + +device_action_authorized: false +target_build_authorized: false +execution_authorized: false +installation_authorized: false +transfer_authorized: false +lifecycle_authorized: false +autoload_authorized: false +device_write_authorized: false +automatic_retry: false +``` + +An incomplete or completed statement never changes `runtime_verified` and +never authorizes a device action. Any later use requires a separate review and +artifact-specific authorization. diff --git a/docs/approvals/phase-1.0aa-offline-fake-adapter.md b/docs/approvals/phase-1.0aa-offline-fake-adapter.md new file mode 100644 index 0000000..4b311ac --- /dev/null +++ b/docs/approvals/phase-1.0aa-offline-fake-adapter.md @@ -0,0 +1,25 @@ +# Phase 1.0AA offline fake-adapter approval record + +This tracked record is deliberately inactive and authorizes no live action. + +- `active=false` +- `attested=false` +- `run_id=null` +- `target_address=null` +- `target_port=null` +- `window=null` +- `ps5_connection_authorized=false` +- `device_request_authorized=false` +- `result_receive_authorized=false` +- `target_build_authorized=false` +- `device_transfer_authorized=false` +- `device_execution_authorized=false` +- `installation_authorized=false` +- `autoload_authorized=false` +- `device_write_authorized=false` +- `automatic_retry=false` +- `reconnect_authorized=false` +- `resume_authorized=false` + +The fake integration cannot consume a live approval and exposes no live +adapter. A later phase and new exact permission would be required first. diff --git a/docs/approvals/phase-1.0ab-live-adapter-feasibility.md b/docs/approvals/phase-1.0ab-live-adapter-feasibility.md new file mode 100644 index 0000000..416700f --- /dev/null +++ b/docs/approvals/phase-1.0ab-live-adapter-feasibility.md @@ -0,0 +1,23 @@ +# Phase 1.0AB live-adapter feasibility approval record + +This tracked record is inactive and grants no network or device authority. + +- `active=false` +- `attested=false` +- `target_address=null` +- `target_port=null` +- `run_id=null` +- `ps5_connection_authorized=false` +- `device_request_authorized=false` +- `result_receive_authorized=false` +- `target_build_authorized=false` +- `device_transfer_authorized=false` +- `device_execution_authorized=false` +- `installation_authorized=false` +- `autoload_authorized=false` +- `device_write_authorized=false` +- `automatic_retry=false` +- `reconnect_authorized=false` +- `resume_authorized=false` + +The trace model cannot connect and cannot consume an approval. diff --git a/docs/approvals/phase-1.0ac-dormant-adapter.md b/docs/approvals/phase-1.0ac-dormant-adapter.md new file mode 100644 index 0000000..b00c497 --- /dev/null +++ b/docs/approvals/phase-1.0ac-dormant-adapter.md @@ -0,0 +1,24 @@ +# Phase 1.0AC inactive record + +This is not a device approval. + +```text +active=false +target_address=null +target_port=null +run_id=null +ps5_connection_authorized=false +device_request_authorized=false +result_receive_authorized=false +device_transfer_authorized=false +device_execution_authorized=false +installation_authorized=false +autoload_authorized=false +device_write_authorized=false +automatic_retry=false +reconnect_authorized=false +resume_authorized=false +``` + +Phase 1.0AC contains only host-side synthetic tests. No value in this file can +activate a transport or authorize a later action. diff --git a/docs/approvals/phase-1.0b-device-smoke-template.md b/docs/approvals/phase-1.0b-device-smoke-template.md new file mode 100644 index 0000000..c880862 --- /dev/null +++ b/docs/approvals/phase-1.0b-device-smoke-template.md @@ -0,0 +1,50 @@ +# Phase 1.0B device-smoke approval template + +This template is intentionally empty and non-authorizing. + +```yaml +authorized: false +transfer_authorized: false +execution_authorized: false +installation_authorized: false +autoload_authorized: false +automatic_retry: false + +artifact: + label: retroarch_ps5_software_smoke.elf + sha256: null + size: null + chimera_retroarch_source_commit: null + build_manifest_sha256: null + +operator_observation: + firmware_text: null + firmware_expected: "9.60" + observed_by: null + observed_at: null + +window: + maximum_transfers: 1 + maximum_executions: 1 + maximum_runtime_ms: 60000 + live_replacement: false + retry: false + autoload: false + installation: false + +expected_visible_phases: + - S07_RUNNING + - S08_SHUTDOWN_REQUESTED +expected_shutdown: + - OPTIONS_HOLD_2000_MS + - RUNTIME_LIMIT_60000_MS + - FRAME_LIMIT_3600 + +accepted_remaining_risks: [] +operator_stop_criteria_reviewed: false +``` + +An edited copy is not valid unless every identity is exact and a new active +task explicitly grants that artifact-specific transfer and execution. This +template is not an execution package and contains no address, sender or +device command. diff --git a/docs/approvals/phase-1.0cz-launch-canary-template.md b/docs/approvals/phase-1.0cz-launch-canary-template.md new file mode 100644 index 0000000..57b097d --- /dev/null +++ b/docs/approvals/phase-1.0cz-launch-canary-template.md @@ -0,0 +1,10 @@ +# Phase 1.0CZ launch-canary approval template + +This tracked document is intentionally inactive and is not permission. + +An active approval must be supplied separately in the active task by Jens and +must name the exact SHA-256 +`8dadce9d9faaef21ea129a3d216c768eea9a3ca9bf8ecb8d852e376b58a9bf95`, +firmware 9.60, the raw-elfldr baseline route, target, port, unique run ID, +timeout, one transfer, one execution and one bounded result reception. It must +also state no installation, autoload, device write, retry, reconnect or reboot. diff --git a/docs/approvals/phase-1.0do-snapshot-runner-template.json b/docs/approvals/phase-1.0do-snapshot-runner-template.json new file mode 100644 index 0000000..1c260d8 --- /dev/null +++ b/docs/approvals/phase-1.0do-snapshot-runner-template.json @@ -0,0 +1 @@ +{"active":false,"run_id":null,"target":null,"port":9021,"artifact_size":109896,"artifact_sha256":"147b5bede0f0b5b7d2be903bc72ff0d0541a2cdc28eae7d86b6bf95e1978ebdf","snapshot_path":null,"receipt_path":null,"not_before":null,"not_after":null,"one_connection":false,"one_transfer":false,"one_execution":false,"result_receive":false,"target_file_read":false,"device_write":false,"installation":false,"autoload":false,"retry":false,"reconnect":false} diff --git a/docs/approvals/phase-1.0dr-inventory-runner-template.json b/docs/approvals/phase-1.0dr-inventory-runner-template.json new file mode 100644 index 0000000..e833cfa --- /dev/null +++ b/docs/approvals/phase-1.0dr-inventory-runner-template.json @@ -0,0 +1 @@ +{"active":false,"run_id":null,"target":null,"port":9021,"artifact_size":110032,"artifact_sha256":"914fce06a490ad048fdd0a85ae117858e8904b47c72054bf12fbfebc213a6db8","output_path":null,"receipt_path":null,"not_before":null,"not_after":null,"one_connection":false,"one_transfer":false,"one_execution":false,"result_receive":false,"directory_inventory":false,"possible_atime_effect_acknowledged":false,"device_file_content_read":false,"persistent_device_write":false,"installation":false,"autoload":false,"retry":false,"reconnect":false} diff --git a/docs/approvals/phase-1.0dt-metadata-runner-template.json b/docs/approvals/phase-1.0dt-metadata-runner-template.json new file mode 100644 index 0000000..8e7c799 --- /dev/null +++ b/docs/approvals/phase-1.0dt-metadata-runner-template.json @@ -0,0 +1 @@ +{"active":false,"run_id":null,"target":null,"port":9021,"artifact_size":109928,"artifact_sha256":"077307b98e44f566fa1db82b08cd5e71bd56bd9792826fc7254965fa768c0dc7","output_path":null,"receipt_path":null,"not_before":null,"not_after":null,"one_connection":false,"one_transfer":false,"one_execution":false,"result_receive":false,"four_exact_metadata_reads":false,"possible_atime_effect_acknowledged":false,"app_pkg_read":false,"backup_read":false,"persistent_device_write":false,"installation":false,"autoload":false,"retry":false,"reconnect":false} diff --git a/docs/approvals/phase-1.0e-one-shot-result-test.md b/docs/approvals/phase-1.0e-one-shot-result-test.md new file mode 100644 index 0000000..fed1632 --- /dev/null +++ b/docs/approvals/phase-1.0e-one-shot-result-test.md @@ -0,0 +1,28 @@ +# Phase 1.0E one-shot result test approval + +Status: **CONSUMED — NO FURTHER DEVICE ACTION AUTHORIZED**. + +On 2026-07-22 the repository owner issued an exact, artifact-bound approval. +The immediately preceding proposal bound that confirmation to: + +- `retroarch_ps5_result_diag.elf`; +- 1,844,880 bytes; +- SHA-256 + `1049c78099a60b472a3fb0e2999e3393b6ad76337a28532a7e53872e7772dedf`; +- exact firmware 9.60 and the separately confirmed current session address; +- one connection, one direct in-memory raw-ELF transfer, one execution; +- result reception through the same inherited connection; and +- no retry, reconnect, installation, autoload or device write. + +The bounded host deadline is 75 seconds. The address is held only in the local +ignored approval record and is not committed. The first connection attempt +consumes this permission regardless of send, execution or result success. +Timeout, EOF, reset, parser failure or any exception stops the run without a +second connection. + +This approval does not apply to any other artifact or later device action. + +The attempt used one connection, transfer and execution and received validated +D00-D02 frames before remote EOF. No retry or reconnect occurred. All device, +transfer, execution and result-receive authorization fields were reset to +`false`; the artifact is no longer transfer- or execution-eligible. diff --git a/docs/approvals/phase-1.0f-device-test-template.md b/docs/approvals/phase-1.0f-device-test-template.md new file mode 100644 index 0000000..d04ee84 --- /dev/null +++ b/docs/approvals/phase-1.0f-device-test-template.md @@ -0,0 +1,24 @@ +# Phase 1.0F device-test template — inactive + +This is a non-authorizing review template for the offline Phase-1.0F artifact. +It is not permission to connect, transfer, receive results or execute. + +```text +artifact=retroarch_ps5_interval_diag.elf +size=1845152 +sha256=e8bfc01c61bfb14b5814280a6e5442f1a5ad05ace5439d1c09e7e5ee00cd0055 +firmware=9.60 +ps5_connection_authorized=false +device_transfer_authorized=false +device_execution_authorized=false +result_receive_authorized=false +installation_authorized=false +autoload_authorized=false +device_write_authorized=false +automatic_retry=false +``` + +Any future authorization must be a new explicit statement in the active task, +repeat the exact name, size and SHA-256, set the intended actions explicitly, +and bound connection, transfer, execution, reception and observation counts. +No authority carries from RUN A, RUN B or RUN C. diff --git a/docs/approvals/phase-1.0g-one-shot-runner-template.md b/docs/approvals/phase-1.0g-one-shot-runner-template.md new file mode 100644 index 0000000..34547ea --- /dev/null +++ b/docs/approvals/phase-1.0g-one-shot-runner-template.md @@ -0,0 +1,34 @@ +# Phase 1.0G one-shot approval template — inactive + +This is a review checklist, not an authorization. The tracked JSON template is +intentionally inactive and must never be edited into a reusable project-wide +permission. + +Current values: + +```text +authorized=false +consumed=false +ps5_connection_authorized=false +device_transfer_authorized=false +device_execution_authorized=false +result_receive_authorized=false +installation_authorized=false +autoload_authorized=false +device_write_authorized=false +automatic_retry=false +protocol_activation_authorized=false +run_id=null +target=null +port=null +``` + +A future approval, if Jens chooses to issue one, must name the exact artifact +name, size and SHA-256; firmware; one target; protocol `CHD10F01`; timeout; +unique run identifier; result reception; and one connection, transfer, +execution and receive. It must separately keep installation, autoload, device +write, retry, reconnect, resume and automatic reboot false. + +The active record must stay outside tracked repository paths, carry an exact +approval reference, and be reviewed together with the active execution +manifest. No prior RUN A, B or C permission carries forward. diff --git a/docs/approvals/phase-1.0h-device-test-template.md b/docs/approvals/phase-1.0h-device-test-template.md new file mode 100644 index 0000000..967f96f --- /dev/null +++ b/docs/approvals/phase-1.0h-device-test-template.md @@ -0,0 +1,23 @@ +# Phase 1.0H device-test template — inactive + +This non-authorizing template identifies the offline Phase-1.0H artifact. It +is not permission to connect, transfer, receive results or execute. + +```text +artifact=retroarch_ps5_startup_args_diag.elf +size=1845152 +sha256=822f2cf1f4d33a514d2bdd88fde40ad580dda5d85f537362ef6dff2eafcb56b6 +firmware=9.60 +ps5_connection_authorized=false +device_transfer_authorized=false +device_execution_authorized=false +result_receive_authorized=false +installation_authorized=false +autoload_authorized=false +device_write_authorized=false +automatic_retry=false +``` + +Any possible device test requires a new active-task statement naming this +exact artifact, size and hash and separately bounding connection, transfer, +execution, receive and observation. This template grants nothing. diff --git a/docs/approvals/phase-1.0k-write-diag-one-shot-template.md b/docs/approvals/phase-1.0k-write-diag-one-shot-template.md new file mode 100644 index 0000000..7fae66c --- /dev/null +++ b/docs/approvals/phase-1.0k-write-diag-one-shot-template.md @@ -0,0 +1,38 @@ +# Phase 1.0K one-shot approval template — inactive + +This is a review checklist, not authorization. Its tracked JSON companion is +permanently inactive and contains no device address, port or run ID. + +Current state: + +```text +authorized=false +consumed=false +ps5_connection_authorized=false +device_transfer_authorized=false +device_execution_authorized=false +result_receive_authorized=false +installation_authorized=false +autoload_authorized=false +device_write_authorized=false +automatic_retry=false +protocol_activation_authorized=false +run_id=null +target=null +port=null +``` + +Any later approval must be newly issued by Jens and bind exactly: + +- `retroarch_ps5_write_diag.elf`; +- size `1845208`; +- SHA-256 `6ff0f7ea391da5f15ea43512a871078133e896a6900ae9f8f3fa75711abb8009`; +- firmware `9.60`; +- protocol `CHD10J01`, version 1, 64-byte frames; +- one explicit target, port, timeout and unique run ID; +- exactly one connection, transfer, execution and result reception. + +It must keep installation, autoload, device write, retry, reconnect, resume and +automatic reboot false. The active approval must remain outside tracked +repository paths and agree byte-for-byte with a separately reviewed active +manifest. This template grants nothing and prior permissions do not carry. diff --git a/docs/approvals/phase-1.0n-write-free-one-shot-template.md b/docs/approvals/phase-1.0n-write-free-one-shot-template.md new file mode 100644 index 0000000..0184ea5 --- /dev/null +++ b/docs/approvals/phase-1.0n-write-free-one-shot-template.md @@ -0,0 +1,39 @@ +# Phase 1.0N one-shot approval template — inactive + +This is a review checklist, not authorization. Its tracked JSON companion is +permanently inactive and contains no device address, port or run ID. + +Current state: + +```text +authorized=false +consumed=false +ps5_connection_authorized=false +device_transfer_authorized=false +device_execution_authorized=false +result_receive_authorized=false +installation_authorized=false +autoload_authorized=false +device_write_authorized=false +automatic_retry=false +protocol_activation_authorized=false +run_id=null +target=null +port=null +``` + +Any later approval must be newly issued by Jens and bind exactly: + +- `retroarch_ps5_write_diag.elf`; +- size `1845208`; +- SHA-256 `c99a0856309a357ad2667d89b4924e4063ad214cae09c8a419457b0732f583cd`; +- firmware `9.60`; +- protocol `CHD10J01`, version 1, 64-byte frames; +- scope `EXACT_ONE_SHOT_PHASE_1_0N`; +- one explicit target, port, timeout and unique run ID; +- exactly one connection, transfer, execution and result reception. + +It must keep installation, autoload, device write, retry, reconnect, resume and +automatic reboot false. The approval must remain outside tracked repository +paths and agree with a separately reviewed active manifest. This template +grants nothing; consumed Phase-1.0K and all earlier permissions do not carry. diff --git a/docs/approvals/phase-1.0t-shsrv-metadata-collection.md b/docs/approvals/phase-1.0t-shsrv-metadata-collection.md new file mode 100644 index 0000000..2d9fabb --- /dev/null +++ b/docs/approvals/phase-1.0t-shsrv-metadata-collection.md @@ -0,0 +1,32 @@ +# Phase 1.0T shsrv metadata collection approval + +Status: `INACTIVE_TEMPLATE` + +This document is deliberately unapproved. It is not a command list, device +client, or permission to connect. + +```text +attested=false +active=false +ps5_connection_authorized=false +device_request_authorized=false +result_receive_authorized=false +process_side_effects_accepted=false +automatic_serial_query_accepted=false +automatic_telemetry_query_accepted=false +serial_redaction_contract_accepted=false +automatic_retry=false +reconnect_authorized=false +target_address=null +listener_already_running_attested=false +window=null +exact_literal_path=null +commands=[] +expires_at=null +``` + +Any future approval must name exactly one window, one connection, the exact +commands, a short deadline, and whether the unavoidable spawned shell and +automatic serial/telemetry reads are accepted. Acceptance does not authorize +hbldr, target staging, file writes, app termination, remount, execution, +autoload, retry or any other device action. diff --git a/docs/approvals/phase-1.0v-shsrv-collector.md b/docs/approvals/phase-1.0v-shsrv-collector.md new file mode 100644 index 0000000..e202eee --- /dev/null +++ b/docs/approvals/phase-1.0v-shsrv-collector.md @@ -0,0 +1,35 @@ +# Phase 1.0V shsrv collector approval + +Status: `INACTIVE_TEMPLATE` + +This template grants no authority and cannot activate the offline model. + +```text +attested=false +active=false +collector_source_sha256=null +ps5_connection_authorized=false +device_request_authorized=false +result_receive_authorized=false +automatic_serial_query_accepted=false +automatic_telemetry_query_accepted=false +spawned_shell_effects_accepted=false +sanitized_output_only_accepted=false +physical_memory_erasure_unproven_accepted=false +automatic_retry=false +reconnect_authorized=false +target_address=null +target_port=null +listener_already_running_attested=false +window=null +exact_literal_path=null +commands=[] +deadline_seconds=null +run_id=null +expires_at=null +``` + +Any future live collector requires a new phase because Phase 1.0V contains no +network transport. Filling this template does not add one and does not +authorize hbldr, file content reads, writes, execution, transfer, installation, +autoload, path discovery, retry or reconnect. diff --git a/docs/approvals/phase-1.0w-shsrv-client.md b/docs/approvals/phase-1.0w-shsrv-client.md new file mode 100644 index 0000000..029d1d2 --- /dev/null +++ b/docs/approvals/phase-1.0w-shsrv-client.md @@ -0,0 +1,43 @@ +# Phase 1.0W shsrv client approval + +Status: `INACTIVE_TEMPLATE` + +There is no live client in Phase 1.0W. This template cannot activate the +policy model or create network capability. + +```text +attested=false +active=false +policy_sha256=null +collector_sha256=null +run_id=null +target_address=null +target_port=null +window=null +exact_literal_path=null +commands=[] +deadline_seconds=null +expires_at=null +listener_already_running_attested=false +ps5_connection_authorized=false +device_request_authorized=false +result_receive_authorized=false +spawned_shell_effects_accepted=false +automatic_serial_query_accepted=false +automatic_telemetry_query_accepted=false +sanitized_output_only_accepted=false +physical_memory_erasure_unproven_accepted=false +target_build_authorized=false +device_transfer_authorized=false +device_execution_authorized=false +installation_authorized=false +autoload_authorized=false +device_write_authorized=false +automatic_retry=false +reconnect_authorized=false +resume_authorized=false +fallback_authorized=false +``` + +A later phase must create and audit a separate inactive transport before any +approval can be considered. No field in this template authorizes a connection. diff --git a/docs/approvals/phase-1.0x-inactive-transport.md b/docs/approvals/phase-1.0x-inactive-transport.md new file mode 100644 index 0000000..f535f4e --- /dev/null +++ b/docs/approvals/phase-1.0x-inactive-transport.md @@ -0,0 +1,47 @@ +# Phase 1.0X inactive transport approval + +Status: `INACTIVE_TEMPLATE` + +Phase 1.0X has no live network adapter. This record is deliberately inert and +cannot authorize or activate a connection. + +```text +attested=false +active=false +policy_sha256=null +collector_sha256=null +transport_sha256=null +run_id=null +target_address=null +target_port=null +window=null +exact_literal_path=null +commands=[] +deadline_seconds=null +expires_at=null +listener_already_running_attested=false +ps5_connection_authorized=false +device_request_authorized=false +result_receive_authorized=false +spawned_shell_effects_accepted=false +automatic_serial_query_accepted=false +automatic_telemetry_query_accepted=false +sanitized_output_only_accepted=false +physical_memory_erasure_unproven_accepted=false +directory_entry_durability_unproven_accepted=false +blocking_call_preemption_unproven_accepted=false +target_build_authorized=false +device_transfer_authorized=false +device_execution_authorized=false +installation_authorized=false +autoload_authorized=false +device_write_authorized=false +automatic_retry=false +reconnect_authorized=false +resume_authorized=false +fallback_authorized=false +``` + +No value may be filled under Phase 1.0X. A later phase must first prove exact +prompt/Telnet framing and implement an independently reviewed inactive network +adapter. That work still would not inherit authority from this template. diff --git a/docs/approvals/phase-1.0y-shsrv-framing.md b/docs/approvals/phase-1.0y-shsrv-framing.md new file mode 100644 index 0000000..616c427 --- /dev/null +++ b/docs/approvals/phase-1.0y-shsrv-framing.md @@ -0,0 +1,33 @@ +# Phase 1.0Y shsrv framing approval + +Status: `INACTIVE_TEMPLATE` + +This phase is source audit and synthetic modeling only. It contains no live +transport to activate. + +```text +attested=false +active=false +framing_model_sha256=null +source_family=null +run_id=null +target_address=null +target_port=null +commands=[] +ps5_connection_authorized=false +device_request_authorized=false +result_receive_authorized=false +target_build_authorized=false +device_transfer_authorized=false +device_execution_authorized=false +installation_authorized=false +autoload_authorized=false +device_write_authorized=false +automatic_retry=false +reconnect_authorized=false +resume_authorized=false +fallback_authorized=false +``` + +No field may be filled under Phase 1.0Y. Official-source framing is not proof +of the deployed shsrv family, live prompt boundaries or firmware behavior. diff --git a/docs/approvals/phase-1.0z-passive-batch.md b/docs/approvals/phase-1.0z-passive-batch.md new file mode 100644 index 0000000..cbc35dd --- /dev/null +++ b/docs/approvals/phase-1.0z-passive-batch.md @@ -0,0 +1,27 @@ +# Phase 1.0Z passive-batch approval record + +This tracked record is deliberately inactive. It is not permission to connect, +send, receive or execute anything. + +- `active=false` +- `attested=false` +- `run_id=null` +- `target_address=null` +- `target_port=null` +- `window=null` +- `exact_literal_path=null` +- `ps5_connection_authorized=false` +- `device_request_authorized=false` +- `result_receive_authorized=false` +- `target_build_authorized=false` +- `device_transfer_authorized=false` +- `device_execution_authorized=false` +- `installation_authorized=false` +- `autoload_authorized=false` +- `device_write_authorized=false` +- `automatic_retry=false` +- `reconnect_authorized=false` +- `resume_authorized=false` + +Any later action requires a new exact artifact/session-specific approval and a +separate reviewed live-adapter phase. Nothing in this template activates one. diff --git a/docs/approvals/phase07-hardened-installation-request.md b/docs/approvals/phase07-hardened-installation-request.md new file mode 100644 index 0000000..5910a75 --- /dev/null +++ b/docs/approvals/phase07-hardened-installation-request.md @@ -0,0 +1,39 @@ +# Phase-0.7 installation authorization packet + +Status: **NOT AUTHORIZED**. This file prepares a later installation request; +it grants no authority and contains no transfer or execution command. + +## Hash-bound installation set + +- elfldr: + `63e810982471eb40cae3a20aa9df9a0a02892f420e429874fae4e99aa400b561` + (397000 bytes) +- controlled Payload Manager: + `8fecf8241a46246eddbd21e8bb4d875f5d76f1f4f4c6a11384df1f131aa5e5b1` + (99560 bytes) +- lifecycle probe: + `bfb4a5cc768e162fe4c2fddf41c3978e152722918a39085277fd172cb95a7182` + (112680 bytes) +- firmware: exact `9.60` +- automatic retry: `false` +- lifecycle timeout: `2000 ms` + +The permanent blocked hash +`4be1c17b4964f2b68c39b5145bc4af4619c32512d60269ecf5c39728b390fa63` +is excluded and must still be rejected. + +## Exact later permission sentence + +> Ik geef toestemming om uitsluitend op mijn PS5 met exact firmware 9.60 de +> geharde elfldr met SHA-256 +> 63e810982471eb40cae3a20aa9df9a0a02892f420e429874fae4e99aa400b561 +> en de controlled Payload Manager met SHA-256 +> 8fecf8241a46246eddbd21e8bb4d875f5d76f1f4f4c6a11384df1f131aa5e5b1 +> éénmalig te installeren, nadat de bestaande bestanden hashgebonden zijn +> geback-upt. Deze toestemming omvat geen overdracht of uitvoering van de +> lifecycle-probe en geen automatische start. + +After that separate installation is verified, a second, artifact-specific +authorization would still be required to transfer and execute the lifecycle +probe. Installation permission must not be interpreted as execution +permission. diff --git a/docs/approvals/phase07-lifecycle-transfer-execution-request.md b/docs/approvals/phase07-lifecycle-transfer-execution-request.md new file mode 100644 index 0000000..62dc0e5 --- /dev/null +++ b/docs/approvals/phase07-lifecycle-transfer-execution-request.md @@ -0,0 +1,41 @@ +# Phase-0.7 lifecycle transfer and one-time execution request + +Status: **NOT AUTHORIZED**. Do not use this request until the hardened elfldr +and controlled Payload Manager have been separately authorized, installed, +hash-verified on-device, and recorded in a new active task. + +## Exact later execution subject + +- lifecycle probe SHA-256: + `bfb4a5cc768e162fe4c2fddf41c3978e152722918a39085277fd172cb95a7182` +- size: 112680 bytes +- artifact ID: `chimera-gfx-lifecycle-phase07-fw960-v1` +- firmware: exact `9.60` +- maximum runtime: 2000 ms +- automatic retry: `false` +- requested action count: one transfer and one execution + +## Preconditions + +- on-device hardened elfldr hash equals + `63e810982471eb40cae3a20aa9df9a0a02892f420e429874fae4e99aa400b561`; +- on-device controlled Payload Manager hash equals + `8fecf8241a46246eddbd21e8bb4d875f5d76f1f4f4c6a11384df1f131aa5e5b1`; +- the original installed components have hash-bound backups; +- the permanent blocked hash is rejected by both installed consumers; +- the static policy gate is rerun against the exact transferred bytes; +- no autoload or automatic retry is enabled. + +## Exact later permission sentence + +> Ik geef toestemming om uitsluitend op mijn PS5 met exact firmware 9.60 de +> lifecycle-probe met SHA-256 +> bfb4a5cc768e162fe4c2fddf41c3978e152722918a39085277fd172cb95a7182, +> 112680 bytes en artifact-ID +> chimera-gfx-lifecycle-phase07-fw960-v1 eenmalig over te dragen en eenmaal uit +> te voeren via de reeds geinstalleerde en exact geverifieerde geharde runtime, +> met een harde limiet van 2000 ms, zonder retry, autoload, VideoOut, GNM, SDL +> of netwerkgebruik door de probe. + +This text is a future request template only. Its presence in the repository is +not approval. diff --git a/docs/approvals/probe-9.60-transfer-execution.md b/docs/approvals/probe-9.60-transfer-execution.md new file mode 100644 index 0000000..5410e7f --- /dev/null +++ b/docs/approvals/probe-9.60-transfer-execution.md @@ -0,0 +1,94 @@ +# Firmware 9.60 probe transfer and one-time execution package + +Status: **BLOCKED — do not transfer and do not execute**. + +This is the separate review package requested after the offline build. It is +not an authorization request because the current artifact cannot satisfy the +project's no-kernel-change boundary. + +## Bound artifact + +- Filename: + `chimera-gfx-capability-probe-0.1.0-fw-9.60-offline-audit-only.elf` +- SHA-256: + `4be1c17b4964f2b68c39b5145bc4af4619c32512d60269ecf5c39728b390fa63` +- Size: 110424 bytes +- Source commit: `ba8f6a40cf37dff628254caa9b11d83a73957cf8` +- Firmware build gate: exact `9.60` +- Manifest: `manifests/artifacts/chimera-gfx-capability-probe-0.1.0-fw-9.60.json` +- Offline evidence: `docs/evidence/probe-9.60-offline-2026-07-17.md` +- Transfer / execution recorded: false / false +- Execution eligible: false +- Permanent denylist: `manifests/artifact-denylist.json` + +## Intended one-time project action + +If a future replacement artifact closes every blocker, the first action would +be a single manual, foreground capability probe with application arguments: + +```text +--firmware 9.60 --acknowledge-read-only-probe +``` + +Project code would open the candidate GNM module, perform exactly 21 `dlsym` +lookups, log names plus booleans, call none of the returned addresses, and +attempt `dlclose`. It would request no VideoOut, framebuffer, GNM command, +draw, dispatch, submit, flip, GPU-memory mutation, MMIO, or register operation. +There would be no retry or automatic boot path. + +## APIs and side effects under review + +Project path: `strcmp`, `chimera_gfx_firmware_gate_allows`, +`chimera_gfx_ps5_make_loader_ops`, `chimera_gfx_ps5_probe_symbols`, `dlopen`, +`open_module`, `dlerror`, `resolve_symbol`, `dlsym`, `log_line`, `snprintf`, +`write_log`, `fprintf`/`fwrite`, `close_module`, `dlclose`, and +`chimera_gfx_status_string`. + +Pinned SDK transitive path includes `__patch_init`, kernel credential and +syscall-permission writes, rtld initialization, possible +`sceKernelLoadStartModule` calls, SDK kernel reads while inspecting module +metadata, and possible `sceKernelStopUnloadModule` during cleanup. The complete +audited path is in the evidence document. + +## Blocking findings + +1. SDK v0.41 performs kernel process-state writes before `main`. The firmware + gate cannot prevent them. +2. No bounded/cancellable loader API or safe external supervisor has been + proven. A hang can prevent cleanup. +3. Module start/stop internals on firmware 9.60 are unknown. +4. The gate trusts a supplied string and does not attest firmware. +5. Runtime delivery of the required four arguments has not been observed. + +Likely failure outcomes range from a clean refusal or loader error to a payload +crash, persistent loaded-module/process state, or a console restart. GPU/display +effects are not requested, but driver-internal effects cannot be ruled out. + +## Timeout, cleanup, and recovery status + +No safe timeout or forced-cancellation procedure exists for this artifact. +Normal cleanup attempts `dlclose`; crash/hang cleanup and CRT kernel-state +restoration are not guaranteed. Therefore no transfer, launch, kill, retry, or +recovery procedure is approved. + +## Approval checklist + +- [x] Offline artifact hash and source commit recorded +- [x] Full imports and call chain audited +- [x] VideoOut/Phase-1 excluded from the build +- [ ] Public, kernelwrite-free startup/loader path proven +- [ ] Replacement artifact built and newly hashed +- [ ] Firmware attestation and argument delivery proven +- [ ] Bounded timeout and cleanup/recovery proven +- [ ] New artifact-specific transfer approval +- [ ] New artifact-specific one-time execution approval + +## Permission text + +There is deliberately **no valid permission text for this hash**. Generic or +future approval must not be interpreted as authorization for it. After the +blockers are closed, a newly named and newly hashed replacement needs a new +package and an exact permission sentence that names that replacement hash. + +ADR-0009 makes this hash permanently ineligible; closing future startup +blockers cannot rehabilitate these bytes. diff --git a/docs/evidence/external-evidence-integrity-2026-07-29.md b/docs/evidence/external-evidence-integrity-2026-07-29.md new file mode 100644 index 0000000..46c5662 --- /dev/null +++ b/docs/evidence/external-evidence-integrity-2026-07-29.md @@ -0,0 +1,54 @@ +# External evidence integrity check — 2026-07-29 + +## Decision + +`EXTERNAL_EVIDENCE_WORKSPACE_NOT_REPRODUCIBLE` + +This is a host-only integrity record. No PS5, network, transfer, target build, +installation, execution, result reception, autoload, or device write was used. +Every authorization remains false and automatic retry remains false. + +## Findings + +The standalone tracked-input suite passes, but the currently available sibling +workspace cannot reproduce every historical cross-repository validator: + +- Phase-0.9B and Phase-0.9C source digests do not match several committed + blobs at the commits named by their historical manifests; +- the expected historical bytes are not present in any local commit of the + affected sibling repositories; +- Phase-1.0L and Phase-1.0M require ignored sibling audit documents that are + absent from the current RetroArch checkout; +- Phase-1.0P and Phase-1.0R bind older RetroArch revisions and Phase-1.0R also + requires ignored Phase-1.0M ELF/map evidence that is absent; +- a single sibling worktree cannot simultaneously represent all historical + revisions. + +Worktree CRLF conversion is a separate source of false mismatches on Windows. +The historical validators and their checksum inventories are immutable and +were not rewritten. A future source-evidence contract should bind committed +Git blobs or explicitly normalized text, but that change must use a new schema +and record rather than altering the historical evidence package. Comparison +against committed blobs confirmed that several remaining mismatches are +genuine evidence-availability failures, not line-ending conversion. + +## Test boundary + +The default CTest suite uses tracked files only. Historical validators needing +sibling repositories, upstream trees, or ignored artifacts are registered only +with `CHIMERA_GFX_REGISTER_EXTERNAL_EVIDENCE_VALIDATORS=ON` and must be run one +at a time against the exact phase-specific workspace described in +`TEST_PLAN.md`. + +Unregistered does not mean passed. Missing or mismatched external evidence +remains fail-closed and cannot support a compatibility, hardware-safety, +execution, cleanup, or recovery claim. The historical records are preserved; +their missing bytes are not reconstructed or replaced by current source. + +## Recovery requirement + +To re-establish a historical external validation, supply the original exact +ignored evidence and source bytes, verify their SHA-256 values independently, +and bind them to a clean phase-specific checkout. If those bytes cannot be +recovered, the affected source-binding claim remains unavailable permanently; +no manifest hash may be rewritten merely to make a validator pass. diff --git a/docs/evidence/phase0-build-2026-07-16.md b/docs/evidence/phase0-build-2026-07-16.md new file mode 100644 index 0000000..b9a79da --- /dev/null +++ b/docs/evidence/phase0-build-2026-07-16.md @@ -0,0 +1,41 @@ +# Phase-0 build evidence — 2026-07-16 + +This record captures local compile evidence only. No PS5 connection, transfer, +or execution occurred. + +## Inputs + +- source workspace: standalone `chimera-gfx` +- PS5 Payload SDK: v0.41 / commit `d2e2e585740362976a39fdd5ccf390f199a7bc37` +- SDK ZIP SHA-256: + `ebfb0acb5260511951a80e17db41650c62d20a8caf8659a230b928dc85005984` +- host compiler: MSVC 19.42.34436.0 +- cross compiler: Ubuntu clang 18.1.8 (`20ubuntu8`) +- CMake: host 3.29.5-msvc4; cross 4.2.3 +- Ninja: 1.13.2 + +## Host result + +The Debug host library and test executables compiled with warnings as errors. +Clang-tidy completed during the WSL/Clang build. All 7 unit, manifest, +generated-file, safety, secret, and format checks passed. + +## PS5 compile result + +`chimera-gfx-capability-probe.elf` compiled as a 64-bit x86-64 PIE using the +public SDK toolchain, with `CHIMERA_GFX_PS5_ALLOWED_FIRMWARE=NONE` and warnings +as errors. Initial artifact SHA-256 before the final source split was +`618eadc14975eb3f20942ee2228007b2457c0f6c1b0e4daa72d17ae9feb64c5c`. +After splitting unused host allocation code out of the linked probe surface and +performing the final rebuild, the artifact SHA-256 is +`bc09865f6d26ba4fa86a5167b99841ad3ebee3629421a2cb4bae678bde63227f`. + +Read-only symbol inspection found: + +- GNM imports: 0 +- VideoOut imports: 0 +- loader entry points present: `dlopen`, `dlsym`, `dlclose` +- undefined allocation imports (`calloc`, `free`): 0 +- representative GNM names present only as embedded lookup strings + +This evidence does not claim runtime compatibility with any firmware. diff --git a/docs/evidence/phase0-build-2026-07-17.md b/docs/evidence/phase0-build-2026-07-17.md new file mode 100644 index 0000000..b0694b0 --- /dev/null +++ b/docs/evidence/phase0-build-2026-07-17.md @@ -0,0 +1,76 @@ +# Phase-0 and offline Phase-1 build evidence + +- Date: 2026-07-17 +- Source commit: `72292f2d5788ad643e9d1816ce28ab754ef739b5` +- Branch: `main` +- Firmware gate: `NONE` +- Hardware transfer/execution: **not performed** + +The source tree was clean when the two artifacts below were rebuilt. Generated +build directories and ELFs are ignored; only their machine-readable manifests +are tracked. + +## Host verification + +| Environment | Result | +|---|---| +| Windows, Visual Studio 2022 x64 Debug | 11/11 CTest tests passed | +| Windows, Visual Studio 2022 x64 Release | 11/11 CTest tests passed | +| WSL, Clang 18.1.8, clang-tidy 18, warnings as errors | 12/12 CTest tests passed | +| WSL, GCC 15.2.0, ASan + UBSan | 12/12 CTest tests passed | +| Installed core library consumer | compiled and exited 0 | +| Installed RetroArch/SDL2 scaffold consumer | compiled and exited 0 | + +The tests include API/error/lifecycle validation, cross-context ownership, +resource limits, deterministic mock upload/present state, adapter refusal, +firmware-gate negative cases, symbol-manifest generation, artifact-manifest +tamper rejection, format, safety policy, and secret scanning. + +The Clang ASan/UBSan variant was attempted but could not link because this WSL +installation lacks Clang 18's `compiler-rt` ASan archives. That attempt is not +counted as a passed test. The available GCC sanitizer runtime completed the +same 12-test suite successfully. + +Docker was not installed in the local environment, so the pinned Dockerfile +was reviewed but not built. Its host and Phase-0 cross-build commands were run +directly with the same pinned compiler/SDK inputs. No Gitea job is claimed as +passed merely from being queued. + +## Cross-build verification + +The clean PS5 compile used: + +- PS5 Payload SDK `v0.41`, commit + `d2e2e585740362976a39fdd5ccf390f199a7bc37`; +- PS5 SDL2 commit `0baf4ac49382b537ba449901b5b6d0d189bb1fbb`; +- the single reviewed keyboard/IME-removal overlay; +- SDL2main, audio, joystick, haptic, sensor, power, file, filesystem, locale, + misc, OpenGL, LoadSO, render, Vulkan, dummy/offscreen video, and HIDAPI + disabled for the Phase-1 candidate; +- warnings as errors and firmware identifier `NONE`. + +All core, adapter, firmware-gate, probe, and disabled Phase-1 targets compiled +and linked. Static `prospero-nm` inspection then passed the strict import audit. + +| Artifact | Size | SHA-256 | Static result | +|---|---:|---|---| +| `chimera-gfx-capability-probe.elf` | 110424 | `f0f74978ac62490ff2482386e9c4efa3ad6d6cc46e10d54c41cb8669b4449f36` | 0 direct Sce imports; 0 GNM imports | +| `chimera-gfx-phase1-videoout-clear.elf` | 1566368 | `2e826ac4ea2bffb626d82e370f98909bb508417d92b1ac3796e9f5a902e975c9` | exact reviewed 15-name Sce set; 0 GNM/keyboard/IME/UserService imports | + +The 15 Phase-1 imports are six direct-memory/equeue functions, one +SystemService function, and eight VideoOut functions listed in +`docs/phase1/VIDEOOUT_CLEAR_EXPERIMENT.md`. + +`llvm-objdump-18` confirmed the retained control-flow order in the Phase-1 +`main`: firmware gate at offset `0x78`, `SDL_SetMainReady` at `0xa9`, +`SDL_Init` at `0xb4`, and the sole `SDL_UpdateWindowSurface` at `0x1fc`. The +probe likewise reaches its firmware gate before loader construction and probe +logic. With embedded identifier `NONE`, both return before the later calls. + +The corresponding tracked manifests are: + +- `manifests/artifacts/chimera-gfx-capability-probe-0.1.0-none.json`; +- `manifests/artifacts/chimera-gfx-phase1-videoout-clear-0.1.0-none.json`. + +Both manifests verify against the local bytes and state `authorized: false`, +`transferred: false`, and `executed: false`. diff --git a/docs/evidence/phase0.5-startup-offline-2026-07-17.md b/docs/evidence/phase0.5-startup-offline-2026-07-17.md new file mode 100644 index 0000000..c6846a2 --- /dev/null +++ b/docs/evidence/phase0.5-startup-offline-2026-07-17.md @@ -0,0 +1,67 @@ +# Phase 0.5 startup offline evidence — 2026-07-17 + +## Outcome + +Decision: **BLOCKED**. The stock SDK v0.41 startup is `UNSAFE`, and the exact +loader caller needed to prove safe return from a replacement is absent. No +custom `_start` source and no PS5 ELF were created. + +No PS5 connection, transfer, execution, VideoOut open, module load, network +initialization, GNM operation, framebuffer mutation, credential change, +syscall-bound change, MMIO access, or firmware change occurred. + +## Offline startup audit + +`tools/audit_ps5_startup_feasibility.py` was run twice independently against +the pinned SDK source and install. Both JSON outputs were byte-identical: + +- startup audit SHA-256: + `d0e8202c1a07e4104476cadf6c14a1dea2d724b1d97495dddcdf05858f6c8d4a`; +- 12 transitive stock CRT sources enumerated; +- six other CRT-named installed files confirmed as empty archives; +- 31 functions and 182 call/tail-call edges reachable from stock `_start`; +- reachable prohibited set: `__patch_init`, `kernel_copyin`, `kernel_copyout`, + `kernel_set_ucred_caps`, and `kernel_set_ucred_attrs`; +- compiler `-###` trace confirmed that `-nostartfiles -nodefaultlibs` adds no + stock CRT or default library and produced no ELF; +- non-SDK local loader-contract hits: zero. + +## Policy verification + +The real legacy artifact, its updated manifest, and the permanent denylist were +evaluated together. The policy tool returned exit 2 with both +`ARTIFACT_PERMANENTLY_DENYLISTED` and +`MANIFEST_EXECUTION_INELIGIBLE`. + +JSON Schema validation passed for all three tracked artifact manifests and the +permanent denylist. + +## Host and static tests + +- Windows MSVC warnings-as-errors build: passed. +- Windows CTest: 14/14 passed. +- WSL Clang 18 plus clang-tidy build: passed. +- WSL Clang CTest including formatting: 15/15 passed. +- WSL GCC 15 ASan+UBSan build: passed. +- WSL GCC ASan+UBSan CTest: 15/15 passed. +- Python syntax compilation for every changed tool/test: passed. +- `git diff --check`: passed at review time. + +An initial Clang ASan configure did not compile a project source because the +local Clang install lacks `libclang_rt.asan*`. GCC's available ASan/UBSan +runtime was used successfully instead. The failed configure is an environment +toolchain limitation and was not reported as a passed test. + +## Bound hashes + +| Record | SHA-256 | +|---|---| +| permanent denylist | `e9603b0e3792781ad5b511afb22ef61e3d4fd4c5a16bf928f8609193bcd97783` | +| denylist schema | `78d0d28da552550e4b7dabc5b9c25347fb2618c4664b7a554b87c6de69cd6c96` | +| startup audit JSON | `d0e8202c1a07e4104476cadf6c14a1dea2d724b1d97495dddcdf05858f6c8d4a` | +| non-build decision | `b04dd224c00af0c7228c582f6ba3ca55ef03e9453287f233a0c4ff2e545092fc` | +| machine proof matrix | `aefd1d1e663be617ed1042c6a1d68b9f21dd0c137f0c601d15cdcbfdf03dadb7` | +| permanently blocked legacy ELF | `4be1c17b4964f2b68c39b5145bc4af4619c32512d60269ecf5c39728b390fa63` | + +The last hash names pre-existing bytes only. It is not a Phase-0.5 artifact and +must not be transferred or executed. diff --git a/docs/evidence/phase07-offline-build-2026-07-17.md b/docs/evidence/phase07-offline-build-2026-07-17.md new file mode 100644 index 0000000..5d1e1d9 --- /dev/null +++ b/docs/evidence/phase07-offline-build-2026-07-17.md @@ -0,0 +1,61 @@ +# Phase-0.7 offline build evidence — 2026-07-17 + +- Public SDK: v0.41, + `d2e2e585740362976a39fdd5ccf390f199a7bc37` +- Firmware build gate: exact `9.60` +- Host validation: MSVC 19.50 `/W4 /WX`; Clang 18 + warnings-as-errors/clang-tidy/format; GCC 15 ASan/UBSan +- Fuzz fallback: 100,000 deterministic ASan/UBSan inputs for both controlled + header and verified metadata +- Cross-build: all three targets with warnings-as-errors +- Reproducibility: two clean byte-identical builds per ELF +- Console actions: none + +The complete machine evidence is +`manifests/runtime/phase-0.7-offline-audit.json`. Full ignored reports and +artifacts are packaged below `outputs/phase07/`. + +## Exact outputs + +| Artifact | SHA-256 | Size | Undefined imports | `DT_NEEDED` | +|---|---|---:|---|---| +| hardened elfldr | `63e810982471eb40cae3a20aa9df9a0a02892f420e429874fae4e99aa400b561` | 397000 | none | `libSceLibcInternal.sprx`, `libSceNet.sprx`, `libkernel_web.sprx` | +| controlled Payload Manager | `8fecf8241a46246eddbd21e8bb4d875f5d76f1f4f4c6a11384df1f131aa5e5b1` | 99560 | none | `libSceLibcInternal.sprx`, `libSceNet.sprx`, `libkernel_web.sprx` | +| lifecycle probe | `bfb4a5cc768e162fe4c2fddf41c3978e152722918a39085277fd172cb95a7182` | 112680 | `_exit`, `sceKernelSendNotificationRequest` | `libSceLibcInternal.sprx`, `libkernel_web.sprx` | + +The lifecycle project's called functions are exactly one +`sceKernelSendNotificationRequest` followed by `_exit`. The normal SDK startup +is statically included and reaches `__patch_init` before `main`; that fact is +proven from source, linker map, disassembly, and the 499-edge lifecycle +callgraph rather than inferred from imports. The hardened loader audit records +1032 call edges and the controlled manager records 478. + +The lifecycle linker map also contains stock-SDK `__dlopen`, `__dlsym`, +`sceKernelLoadStartModule`, and `sceKernelStopUnloadModule` symbols. Direct +static reachability proves the rtld initialization path but cannot resolve all +116 indirect lifecycle edges. The audit therefore records linked symbols, +direct `_start` reachability where symbolization permits it, and every +unresolved indirect edge separately. No graphics/display-sensitive symbol is +linked in any of the three audited ELFs. + +## Final offline tests + +- Chimera GFX Windows host build: MSVC `/W4 /WX`, 16/16 CTest passed. +- Hardened elfldr: MSVC 1/1; Clang 18 plus clang-tidy 1/1; GCC 15 + ASan/UBSan 1/1; 100,000-input sanitizer fuzz pass. +- Controlled Payload Manager: MSVC 1/1; Clang 18 plus clang-tidy 2/2; GCC 15 + ASan/UBSan 2/2; 100,000-input sanitizer fuzz pass. +- All three PS5 cross-builds passed warnings-as-errors and reproduced the + exact hashes and sizes above. +- Negative lifecycle firmware `9.50` configure failed before creating a build + graph. +- Controlled-manager build without exact hash/size/artifact ID failed. +- Static execution-policy refusal suite, manifest schemas, Phase-0.5/0.6 + historical gates, Phase-0.7 proof matrix, safety audit, and secret scans + passed. + +The local Clang installation does not contain the optional +`libclang_rt.asan`/libFuzzer runtime archives. Clang warnings and clang-tidy +therefore run without sanitizers; GCC supplies the ASan/UBSan and deterministic +fuzz coverage. This is a host-tool packaging limitation, not a target +compiler, linker, or ABI blocker. diff --git a/docs/evidence/probe-9.60-offline-2026-07-17.md b/docs/evidence/probe-9.60-offline-2026-07-17.md new file mode 100644 index 0000000..b754f86 --- /dev/null +++ b/docs/evidence/probe-9.60-offline-2026-07-17.md @@ -0,0 +1,147 @@ +# Firmware 9.60 capability-probe offline evidence + +Date: 2026-07-17. No PS5 connection, transfer, deployment, or execution was +performed. + +## Artifact identity + +| Field | Value | +|---|---| +| Artifact | `chimera-gfx-capability-probe-0.1.0-fw-9.60-offline-audit-only.elf` | +| Size | 110424 bytes | +| SHA-256 | `4be1c17b4964f2b68c39b5145bc4af4619c32512d60269ecf5c39728b390fa63` | +| Source commit | `ba8f6a40cf37dff628254caa9b11d83a73957cf8` | +| SDK | PS5 Payload SDK v0.41, commit `d2e2e585740362976a39fdd5ccf390f199a7bc37` | +| Build type | Release, C11, warnings as errors | +| Phase-1 VideoOut | explicitly `OFF`; target not built or linked | +| Transfer / execution | false / false | +| Execution eligibility | **false** | + +Two clean builds around an audit-tool-only commit produced the same size and +SHA-256. The tracked manifest is +`manifests/artifacts/chimera-gfx-capability-probe-0.1.0-fw-9.60.json`. + +## Firmware gate + +The checked-in discovery allowlist is exactly `["9.60"]`; CMake still defaults +to `NONE`. Configure rejects `9.61`. The Phase-1 target rejects `9.60` and every +other non-`NONE` value. + +The ELF embeds `9.60` and requires exactly these application arguments: + +```text +--firmware 9.60 --acknowledge-read-only-probe +``` + +Disassembly places `chimera_gfx_firmware_gate_allows` at `main+0x7f`, before +`chimera_gfx_ps5_make_loader_ops` at `main+0xa5` and the probe call at +`main+0xb2`. This is only a comparison against an operator-supplied string; it +does not independently attest the console firmware. SDK startup occurs before +`main` and therefore before this gate. + +## Complete dynamic import inventory + +Undefined symbols (`prospero-nm -u`), exact set: + +- `__stderrp` +- `__stdoutp` +- `fprintf` +- `fwrite` +- `snprintf` +- `strcmp` + +`DT_NEEDED` modules (`llvm-readelf-18 --dynamic-table`), exact set: + +- `libkernel_web.sprx` +- `libSceLibcInternal.sprx` +- `libSceNet.sprx` + +There are zero direct `sce*`, GNM, VideoOut, SDL, draw, dispatch, submit, or +flip imports. `libSceVideoOut.sprx` text is present only because the SDK rtld +statically includes a general sysmodule-name table; there is no corresponding +import or call. `INIT_ARRAYSZ` and `FINI_ARRAYSZ` are both zero. + +## Project-requested functions + +Before the firmware gate, project `main` uses only argument checks, `strcmp`, +and a refusal log on error. After the gate, the complete project path is: + +1. `chimera_gfx_ps5_make_loader_ops`; +2. `chimera_gfx_ps5_probe_symbols`; +3. internal `log_line`/`write_log` callbacks for boolean JSON events; +4. loader callback `open_module`, which calls + `dlopen("libSceGnmDriver.sprx", RTLD_LAZY | RTLD_LOCAL)`; +5. loader callback `resolve_symbol` 21 times; it calls `dlerror`, `dlsym`, then + `dlerror`; every returned address is reduced to a boolean and discarded; +6. `snprintf` plus `fprintf`/compiler-selected `fwrite` for boolean JSON lines; +7. loader callback `close_module`, which calls `dlclose`; +8. `chimera_gfx_status_string` and a final summary log. + +No resolved GNM pointer is cast to a callable type or invoked. The 21 exact +names are machine-checked against `manifests/ps5_gnm_symbols.json`. + +## SDK startup and loader side effects + +Pinned-source review plus disassembly proves this pre-`main` success path: + +1. `_start` clears payload BSS. +2. `__crt_syscall_init`, `__kernel_init`, and `__klog_init` initialize SDK + state from loader-supplied arguments. +3. libc `__isthreaded` is set to one. +4. `__patch_init` reads current process credentials, calls + `kernel_set_ucred_caps` and `kernel_set_ucred_attrs`, and performs two + `kernel_copyin` writes that change the process syscall-address bounds. +5. `__rtld_init` initializes SPRX/SO/payload/dlfcn support and may call + `sceKernelLoadStartModule` for `libSceSysmodule.sprx` if it is absent. +6. payload dependencies are opened and relocations modify payload memory; + payload init/fini arrays themselves are empty. + +After the project gate, SDK `dlopen` first checks loaded modules through SDK +kernel reads. If GNM is absent it can call `sceKernelLoadStartModule`. It then +uses `kernel_copyout` to copy module metadata, symbol tables, and string tables +into allocated user memory. The pinned SPRX-specific `init` callback is empty, +but the internal behavior of the system load/start call is unknown. + +These facts mean the ELF is non-rendering at project level but is not globally +non-mutating. The pre-`main` kernel writes violate the project's userland-only +execution boundary. + +## Cleanup audit + +The bounded project loop attempts `dlclose` after all lookups and on lookup or +format failures. SDK `dlclose` calls rtld fini, close, and destroy. The pinned +SPRX `fini` callback is empty. If this open loaded the module, `sprx_close` +calls `sceKernelStopUnloadModule`, then frees copied tables and clears local +state. + +Cleanup is not guaranteed: + +- there is no documented cancellation or bounded timeout around loader calls; +- a crash or hang can bypass `dlclose`; +- a stop/unload failure can leave the module loaded while SDK bookkeeping is + destroyed; +- the CRT's credential/syscall-permission changes have no matching restoration + path in the pinned source; +- a `libSceSysmodule.sprx` load during rtld initialization has no observed + matching unload in this call chain. + +## Offline verification results + +- Windows MSVC Debug: 11/11 tests passed. +- WSL Clang 18 with clang-tidy and formatting: 12/12 tests passed. +- Strict artifact audit: six exact undefined symbols, three exact + `DT_NEEDED` modules, empty init/fini arrays, all 21 manifest names, zero + direct Sce/GNM imports. +- SDK runtime source audit: passed and concluded execution eligibility false. +- Negative configure tests: firmware `9.61` rejected; Phase-1 with `9.60` + rejected. +- Artifact-manifest digest/size verification: passed. +- Secret scan and `git diff --check`: passed. + +## Remaining blockers + +The exact system-module initialization effects, runtime argument delivery, +firmware attestation, loader timeout behavior, and partial-failure recovery are +unproven. More decisively, the SDK v0.41 CRT performs prohibited kernel writes +before the project firmware gate. This exact artifact must not be transferred +or executed. diff --git a/docs/phase1/APPROVAL_PACKAGE.md b/docs/phase1/APPROVAL_PACKAGE.md new file mode 100644 index 0000000..7308a13 --- /dev/null +++ b/docs/phase1/APPROVAL_PACKAGE.md @@ -0,0 +1,53 @@ +# Hardware approval package + +Status: **not authorized; offline review artifacts only**. + +## Current offline review artifacts + +| Artifact | Firmware | SHA-256 | Transfer/execution | +|---|---|---|---| +| `chimera-gfx-capability-probe.elf` | `NONE` | `f0f74978ac62490ff2482386e9c4efa3ad6d6cc46e10d54c41cb8669b4449f36` | false / false | +| `chimera-gfx-phase1-videoout-clear.elf` | `NONE` | `2e826ac4ea2bffb626d82e370f98909bb508417d92b1ac3796e9f5a902e975c9` | false / false | +| `chimera-gfx-capability-probe-0.1.0-fw-9.60-offline-audit-only.elf` | `9.60` | `4be1c17b4964f2b68c39b5145bc4af4619c32512d60269ecf5c39728b390fa63` | false / false; permanently denylisted | + +The two `NONE` artifacts are tied to source commit +`72292f2d5788ad643e9d1816ce28ab754ef739b5`. The offline test and import +evidence is in `docs/evidence/phase0-build-2026-07-17.md`. These exact artifacts +cannot enter the project-requested GNM module open or SDL initialization because +their firmware gate is `NONE`. The SDK payload CRT still initializes before +`main`. + +## First hardware gate + +The first console action, if it ever becomes safe and receives new permission, +must be the non-rendering capability probe, not the VideoOut experiment. +Project code would call only `dlopen`, `dlsym`, `dlclose`, and standard output +functions. It would resolve the 21 manifest names, record only booleans, invoke +no resolved pointer, and request no submit, draw, dispatch, flip, or GPU-memory +operation. + +Jens reported exact firmware `9.60` and authorized only its offline build and +audit. The pinned SDK v0.41 payload CRT performs prohibited kernel process-state +writes before `main`, so the resulting ELF is not execution-eligible even +though the project gate precedes `dlopen`. A kernelwrite-free startup route and +a newly built digest are required before execution can be considered. + +## VideoOut gate after successful discovery + +The later experiment would execute the exact SDL sequence and indirect API +inventory in `VIDEOOUT_CLEAR_EXPERIMENT.md`. Likely failures are a clean error, +payload crash, stuck payload, lost display ownership, or required controlled +console restart. Automatic retry is prohibited. The unbounded SDL flip wait is +an unresolved blocker even after symbol discovery. + +## Authorization state + +The 9.60 offline build authorization has been received. No transfer or execution +authorization has been received. No execution-permission wording is offered +while the SDK CRT blocker remains. The firmware-specific evidence and blocked +approval package must record the filename, SHA-256, exact source commit, all +imports, startup/loader/cleanup call chain, and offline tests. Approval never +carries forward to the VideoOut test. + +The completed firmware-specific package is +`docs/approvals/probe-9.60-transfer-execution.md`. diff --git a/docs/phase1/FIRMWARE_ABI_CHECKLIST.md b/docs/phase1/FIRMWARE_ABI_CHECKLIST.md new file mode 100644 index 0000000..9148f4c --- /dev/null +++ b/docs/phase1/FIRMWARE_ABI_CHECKLIST.md @@ -0,0 +1,39 @@ +# Firmware and ABI checklist + +## Firmware identity + +- [ ] Exact user-visible firmware identifier supplied by Jens +- [ ] Reproducible identification method documented without kernel access +- [ ] Identifier added to one candidate build only +- [ ] `FIRMWARE_COMPATIBILITY.md` row created as untested +- [ ] No compatibility inferred from another revision + +## Discovery gate + +- [ ] Capability-probe artifact manifest and SHA-256 reviewed +- [ ] Explicit artifact-specific execution permission recorded +- [ ] Probe resolves names only and invokes no resolved pointer +- [ ] Redacted result log archived +- [ ] Runtime module candidate confirmed or rejected for this firmware + +## SDL/VideoOut evidence + +- [x] SDL source commit pinned +- [x] Zlib license and overlay notice recorded +- [x] Project code uses public SDL2 APIs only +- [x] Keyboard/IME initialization removed from the staged build +- [x] Direct GNM imports forbidden +- [ ] Bounded flip-wait or safe supervisor proven +- [ ] Cleanup behavior observed on this exact firmware + +## Artifact review + +- [ ] Clean source commit matches manifest +- [ ] SDK and SDL commits match locks +- [ ] Firmware is not `NONE` +- [ ] ELF filename, size, and SHA-256 match +- [ ] Imports match the API inventory +- [ ] Offline host, static-analysis, cross-compile, and secret tests pass +- [ ] Transfer and execution remain false until separately approved + +Any unchecked runtime item blocks execution. diff --git a/docs/phase1/HARDWARE_TEST_PLAN.md b/docs/phase1/HARDWARE_TEST_PLAN.md new file mode 100644 index 0000000..487375b --- /dev/null +++ b/docs/phase1/HARDWARE_TEST_PLAN.md @@ -0,0 +1,58 @@ +# Phase-1 hardware test plan + +## Preconditions + +Every item must be complete before transfer: + +- exact firmware is recorded and allowlisted for one artifact; +- the discovery-only capability probe has already completed on that firmware; +- source commit, SDK/SDL commits, artifact SHA-256, and static imports match the + reviewed manifest; +- the console has no unsaved work and automatic retry/boot integration is off; +- an operator and observer have the redacted JSON-stage log visible; +- the bounded-wait blocker below has an accepted resolution. + +## Proposed timeline + +1. Verify digest again immediately before transfer. +2. Transfer manually using an existing approved userland loader workflow. +3. Start once, manually. No boot hook or retry. +4. Require `firmware_gate`, `video_init`, `window_create`, `surface_acquire`, + and `cpu_fill` within five seconds total. +5. Permit exactly one `single_present` and a one-second hold. +6. Require `cleanup` and process exit within five seconds after present. +7. Preserve logs and mark the firmware row pass, fail, or anomalous. + +## Watchdog and timeout + +The application stages are bounded except the pinned SDL call +`SDL_UpdateWindowSurface`, whose backend waits for a flip event without a +publicly proven finite timeout. A cooperative application thread cannot safely +cancel it. Force-killing a thread while it owns VideoOut or direct-memory state +is prohibited. + +Before authorization, one of these must be proven and separately reviewed: + +1. a public, licensed SDL/VideoOut change that supplies a bounded wait and + returns control for cleanup; or +2. a userland process supervisor whose termination semantics guarantee OS + cleanup of VideoOut, equeue, and direct-memory ownership. + +Until then, the operator wall-clock limit is a detection mechanism only, not a +safe watchdog, and the hardware test remains blocked. + +## Cleanup, rollback, and emergency stop + +Normal cleanup is `SDL_DestroyWindow` followed by `SDL_Quit`; the pinned +backend then closes VideoOut, releases direct memory, and deletes its equeue. +No second flip is attempted during cleanup. + +On any error, missing stage, display anomaly, or deadline: + +- do not retry; +- request normal userland process termination only if responsive; +- do not kill an individual worker thread; +- if display ownership does not return, use the console's normal controlled + restart procedure; never patch firmware, registers, clocks, or fans; +- after restart, use the existing SDL software path and mark the artifact and + firmware combination failed/unknown pending review. diff --git a/docs/phase1/README.md b/docs/phase1/README.md new file mode 100644 index 0000000..2e2ef14 --- /dev/null +++ b/docs/phase1/README.md @@ -0,0 +1,13 @@ +# Offline Phase-1 preparation + +No Phase-1 artifact has been transferred or executed. This directory contains +the review package for the first CPU-framebuffer/VideoOut experiment: + +- `VIDEOOUT_CLEAR_EXPERIMENT.md`: exact design and API inventory; +- `HARDWARE_TEST_PLAN.md`: staging, timeout, cleanup, rollback, and stop rules; +- `FIRMWARE_ABI_CHECKLIST.md`: evidence that must be completed per firmware; +- `APPROVAL_PACKAGE.md`: artifact-specific authorization gate. + +The compile target is off by default and the produced review artifact embeds +firmware `NONE`, so it refuses before `SDL_Init`. This preparation does not +authorize a hardware test. diff --git a/docs/phase1/VIDEOOUT_CLEAR_EXPERIMENT.md b/docs/phase1/VIDEOOUT_CLEAR_EXPERIMENT.md new file mode 100644 index 0000000..b4e4f55 --- /dev/null +++ b/docs/phase1/VIDEOOUT_CLEAR_EXPERIMENT.md @@ -0,0 +1,62 @@ +# Minimal VideoOut clear-frame experiment + +## Goal and non-goals + +Display exactly one fixed 1920x1080 solid frame through the existing pinned +PS5 SDL2 CPU-framebuffer backend, hold it for one second, and cleanly release +SDL. This experiment does not use `libchimera-gfx` hardware contexts, GNM, +shaders, command buffers, compute, custom tiling code, input, audio, OpenGL, +OSMesa, or a render loop. + +## Application sequence + +Project code performs the following calls only after exact compile-time and +runtime firmware identifiers match and the literal acknowledgement is present: + +1. `SDL_SetMainReady` +2. `SDL_Init(SDL_INIT_VIDEO)` +3. `SDL_CreateWindow` +4. `SDL_GetWindowSurface` +5. `SDL_MapRGBA` +6. `SDL_FillRect` +7. `SDL_UpdateWindowSurface` exactly once +8. `SDL_Delay(1000)` +9. `SDL_DestroyWindow` +10. `SDL_Quit` + +The fixed color is RGBA `(0x18, 0x2a, 0x41, 0xff)`. Logs contain stage names +and booleans only. + +## Indirect pinned-SDL behavior + +At commit `0baf4ac49382b537ba449901b5b6d0d189bb1fbb`, the reviewed PS5 video +backend indirectly uses these public export names: + +- initialization: `sceSystemServiceHideSplashScreen`, `sceVideoOutOpen`, + `sceKernelAllocateMainDirectMemory`, `sceKernelMapDirectMemory`, + `sceKernelCreateEqueue`, `sceVideoOutAddFlipEvent`, + `sceVideoOutSetFlipRate`, `sceVideoOutSetBufferAttribute2`, and + `sceVideoOutRegisterBuffers2`; +- single present: `sceVideoOutSubmitFlip` and `sceKernelWaitEqueue`; +- cleanup: `sceVideoOutDeleteFlipEvent`, `sceVideoOutClose`, + `sceKernelReleaseDirectMemory`, and `sceKernelDeleteEqueue`. + +These are indirect implementation observations, not independently proven +`chimera-gfx` ABI declarations. The overlay removes keyboard and IME setup. + +## Build boundary + +`tools/build-phase1-videoout.sh` verifies both upstream commits, stages the +reviewed SDL overlay, disables SDL2main plus unrelated SDL subsystems, and +cross-compiles the probe and candidate. It then statically requires the exact +15-name Sce import inventory above and zero GNM, keyboard, IME, or UserService +imports. It contains no upload, run, host, port, or boot command. The default +firmware identifier is `NONE`. + +## Known blocker + +The pinned SDL backend waits inside `sceKernelWaitEqueue` with no publicly +proven bounded timeout in this call path. Project code cannot safely cancel +that wait. The artifact is suitable for offline inspection, but not yet for +hardware authorization until `HARDWARE_TEST_PLAN.md` records an accepted +supervisor/recovery mechanism and an exact firmware. diff --git a/docs/retroarch/phase-1.0a-build-results.md b/docs/retroarch/phase-1.0a-build-results.md new file mode 100644 index 0000000..b11902e --- /dev/null +++ b/docs/retroarch/phase-1.0a-build-results.md @@ -0,0 +1,169 @@ +# Phase 1.0A build and artifact results + +## Result + +`RETROARCH_PS5_SOFTWARE_PORT_BUILT` + +Both outputs are real RetroArch v1.22.2 frontends built from fork commit +`ca1b45680577befc743e1c92fa40687e1b1745e7`. They link the upstream +frontend/runloop and the static `chimera_smokecore`; the software target also +links RGUI and the reviewed PS5 SDL2 software backends. They were not run. + +| Profile | Local output | Size | SHA-256 | Map SHA-256 | +| --- | --- | ---: | --- | --- | +| headless | `build/phase10a/final/retroarch_ps5_headless.elf` | 722392 | `fd595a826f64d18598be0b55e539bb524b33bd469b98f62c958ee50acb544628` | `fb935f7a768c91408a87290f03bad8ed59f5061de75232d5660957eff8ec017f` | +| software/RGUI | `build/phase10a/final/retroarch_ps5_software.elf` | 3318432 | `7beb09592404b5c1fb4161c632171d2901f3715db26e59458998690e8c49f3fc` | `1573a9951fa53bfa1bf304e0038e6f049f859fbfbe92f6a895196dc283c27597` | + +The paths are relative to the separate `chimera-retroarch` repository. The +ELFs and maps are ignored local build outputs, not files in `chimera-gfx` and +not transfer, execution or installation packages. + +## Builds and reproducibility + +Every target comparison used fork commit +`ca1b45680577befc743e1c92fa40687e1b1745e7`, SDK commit +`d2e2e585740362976a39fdd5ccf390f199a7bc37`, +`SOURCE_DATE_EPOCH=1763597828`, `TZ=UTC` and `LC_ALL=C`. +Each repetition invoked `make ... clean` to completion before a separate +`make ... -j4 all`. An earlier concurrent `make clean all` experiment was +rejected as a race and is not evidence. + +| Comparison | ELF | Linker map | +| --- | --- | --- | +| headless A versus B | byte-identical | byte-identical | +| software A versus B | byte-identical | byte-identical | + +Compiler flags include warnings-as-errors, `-O2`, no debug data, source-prefix +maps, section garbage collection, `--as-needed` and `--build-id=none`. The +cross compiler reports Clang 18.1.8 for `x86_64-sie-ps5`; GNU Make 4.4.1 was +used under WSL2. + +The static SDL2 archive is 3120572 bytes with SHA-256 +`353065505f54e71fa8f7fff41e090dce52f39fe2aaafab85c4653648fc5f1b56`. +It was built from commit `0baf4ac49382b537ba449901b5b6d0d189bb1fbb` +plus the 5195-byte reviewed overlay whose SHA-256 is +`b547260d8af40ce2360575ab7831c009036c1f35f8b525cad6fd376dc5ca9d6b`. + +## Host integration result + +The host harness compiled with GCC 15.2.0, `-Werror`, AddressSanitizer and +UndefinedBehaviorSanitizer, with leak detection enabled. It completed: + +- 600 frames; +- video FNV-1a-64 `43f920496eb5f435`; +- audio FNV-1a-64 `a48f47dc08c56625`; +- digital and analog input mapping: pass; +- Start-driven libretro shutdown callback: pass; +- init/deinit and ASan/UBSan/leak checks: pass; +- PS5 port structural validator: pass. + +This is host evidence for the core and source contracts, not PS5 runtime +evidence. + +## ELF audit + +Both files are ELF64 little-endian x86-64 System V PIE/DYN binaries, dynamically +linked and not stripped. Neither has an interpreter, build ID, GNU-stack +program header, ELF TLS segment, `.tdata` or `.tbss`. Both have 20 section +headers; `.init_array` and `.fini_array` exist with zero size. The full section +set is `.text`, unwind tables, dynamic symbol/hash/string/relocation tables, +`.data.rel.ro`, `.got`, `.rodata`, empty init/fini arrays, `.dynamic`, `.data`, +`.bss`, `.comment` and static symbol/string tables. + +| Field | Headless | Software/RGUI | +| --- | ---: | ---: | +| Entry point | `0x47d10` | `0x1c29c0` | +| Program headers | 4 | 4 | +| First LOAD | offset `0x4000`, size `0x4e570`, RWE | offset `0x4000`, size `0x1c93e0`, RWE | +| Other LOADs | two RW | two RW | +| `.rela.dyn` entries | 610 | 3373 | +| Dynamic symbols | 79 | 220 | +| Full symbols | 1387 | 7256 | +| Defined global symbols | 719 | 2403 | +| Undefined symbols/imports | 73 | 210 | + +The executable first LOAD segment is also writable because the pinned SDK +linker script co-locates text that way. This is a material W^X weakness and a +future execution risk; offline build success does not waive it. + +The linker maps prove inclusion of `frontend/drivers/platform_ps5.o` and +`cores/chimera_smokecore/chimera_smokecore.o` in both targets. The software +map additionally proves inclusion of `SDL_ps5video.c.o`, `SDL_ps5audio.c.o` +and `SDL_ps5joystick.c.o`, while PS5 keyboard and IME objects are absent from +the final link. + +## Dynamic dependencies and imports + +Headless `DT_NEEDED`: + +- `libkernel_web.sprx`; +- `libSceLibcInternal.sprx`. + +Software/RGUI additionally needs: + +- `libSceVideoOut.sprx`; +- `libScePad.sprx`; +- `libSceUserService.sprx`; +- `libSceSystemService.sprx`; +- `libSceAudioOut.sprx`. + +`--as-needed` removes the SDK's otherwise unconditional SceNet dependency. +There are no undefined socket/network, `dlopen`/`dlsym`, module-loader, GNM, +mount, reboot or console-shutdown symbols. The software target's complete Sce +API import set is AudioOut init/open/output/close; direct-memory and equeue +allocation/wait/release; Pad init/open/read/close plus linked-but-not-required +lightbar/vibration calls; UserService initialization/user queries; +`sceSystemServiceHideSplashScreen`; and VideoOut open/register/flip/event/ +close operations. Full ordered import lists are in +`manifests/retroarch/phase-1.0a-artifacts.json`. + +Generic RetroArch code keeps write-capable libc imports. Headless includes +`fopen`, `ftruncate`, `fwrite`, `mkdir`, `open`, `remove`, `rename` and +`write`; software also includes `setenv`. The fixed PS5 startup path blocks +configuration reads, supplies no content/config/SRAM/state/core/data path and +compiles the normal shutdown-save paths out. The claim is therefore +control-flow-specific, not global absence of write-capable code. + +## Entry, shutdown and static reachability + +Source and disassembly agree on this entry sequence: + +`_start` clears BSS, performs the inlined SDK `payload_init` sequence +(`__crt_syscall_init`, `__kernel_init`, `__klog_init`, `__isthreaded`, +`__patch_init`, `__rtld_init`), constructs the SDK payload runtime, calls +`main`, which tail-jumps to `rarch_main`, then runs SDK fini/close/destroy and +termination handling. RetroArch selects `frontend_ctx_ps5`, performs a +contentless static-core launch, enters `runloop_iterate`, and reaches the +linked `retro_run`. Start requests the normal RetroArch/core shutdown path; +`retro_deinit` and the PS5 frontend deinit/shutdown callbacks are linked. + +The SDK source and linked disassembly also prove that startup is not +kernelwrite-free: `__patch_init` reaches credential-capability/attribute +changes and syscall-bound writes through `kernel_copyin/out`. Those effects +are accepted for this authorized offline link milestone under the existing +ADR boundary, but remain unexecuted and hardware-unproven. + +## String and policy audit + +No personal absolute host path, credential, PS5/device address, install route, +Payload Manager modification, elfldr modification, lifecycle probe, deploy +client or execution helper was found. The generic RetroArch string pool keeps +`udp://127.0.0.1:`, an RTMP template, overlay “autoload” labels and downloader +localization even though networking, updater, dynamic cores and autoload +routes are compiled out. The software pool also yields byte-pattern false +positives `0.1.2.3` and `4.5.6.7`. SDK RTLD name tables contain module/dlsym +strings, but none is an undefined application import. + +Full local audit output for each target contains the file classification, +ELF/readobj dump, complete symbols and undefined symbols, relocations, +disassembly, startup/shutdown slices and strings under +`build/phase10a/audit/{headless,software}` in `chimera-retroarch`. + +The Chimera GFX secretscan passed across 658 text files and a clean archive of +all 21 fork-delta files passed separately. A whole-upstream RetroArch scan +also identified the pre-existing BearSSL sample private-key fixtures and +mbedTLS parser/writer source literals; these are official baseline dependency +test/source material, not additions or credentials introduced by this port. + +No PS5 connection, request, transfer, execution, installation, autoload or +device write occurred. diff --git a/docs/retroarch/phase-1.0a-driver-status.md b/docs/retroarch/phase-1.0a-driver-status.md new file mode 100644 index 0000000..2128c52 --- /dev/null +++ b/docs/retroarch/phase-1.0a-driver-status.md @@ -0,0 +1,55 @@ +# Phase 1.0A driver status + +| Component | Headless | Software/RGUI | Runtime classification | +| --- | --- | --- | --- | +| RetroArch frontend/runloop | Real linked code | Real linked code | Built offline | +| Platform frontend | `frontend_ctx_ps5` | `frontend_ctx_ps5` | Startup/return on device unproven | +| Video | `video_null` | RetroArch `sdl2` + SDL software + PS5 VideoOut | Linked, firmware runtime unproven | +| Menu | none | RGUI | Linked, display unproven | +| Input | `input_null` | RetroArch SDL + SDL PS5 Pad | Linked, one-controller runtime unproven | +| Joypad | null | SDL joypad over PS5 Pad | Buttons/axes source present; runtime unproven | +| Audio | `audio_null` | RetroArch SDL + PS5 AudioOut | 48 kHz stereo source present; runtime unproven | +| Core | static `chimera_smokecore` | static `chimera_smokecore` | Host-verified | +| Dynamic core | disabled | disabled | Not implemented | +| Config/VFS persistence | disabled default path | disabled default path | Generic write-capable code remains | +| Networking/updater | disabled | disabled | No network API imports expected | +| GNM/hardware context | absent | absent | Out of scope | + +## Platform services + +- lifecycle, contentless arguments and blocked config reads: + `frontend_ctx_ps5`; +- monotonic time and bounded sleeps: RetroArch/libretro-common POSIX paths + backed by the pinned SDK libc; no invented PS5 ABI; +- RetroArch worker threads: compiled out; the software profile may use SDL's + public pthread-backed audio primitives; +- logging: existing RetroArch stderr/stdout path only, with file logging off; +- VFS: generic code is linkable but the fixed PS5 wrapper supplies no content, + config, SRAM, state, core or data path; +- dynamic core loading and executable-memory policy: explicitly unsupported. + +## Smoke core contract + +- no content; +- 320x240 XRGB8888 at 60 Hz; +- 48 kHz deterministic stereo, 800 sample frames per video frame; +- D-pad and left analog stick offset the pattern; +- A toggles the background; +- Start requests clean libretro shutdown; +- no input means unbounded deterministic operation; +- no filesystem, networking, threads, JIT or frame-hot-path allocation. + +Host result for 600 no-input frames: + +- video FNV-1a-64: `43f920496eb5f435`; +- audio FNV-1a-64: `a48f47dc08c56625`; +- ASan/UBSan: pass; +- digital- and analog-input-dependent video change: pass; +- Start shutdown callback: pass. + +## Explicitly unsupported + +Persistent configuration, history, playlists, screenshots, saves, states, +content loading, dynamic cores, multiple controllers, haptics, touchpad, +gyro, lightbar, overlays, shaders and hardware-rendered cores are not silently +reported as working. diff --git a/docs/retroarch/phase-1.0a-next-device-smoke-test.md b/docs/retroarch/phase-1.0a-next-device-smoke-test.md new file mode 100644 index 0000000..1e88edf --- /dev/null +++ b/docs/retroarch/phase-1.0a-next-device-smoke-test.md @@ -0,0 +1,65 @@ +# Proposed first PS5 smoke test (not authorized) + +This is a review package only. It is not a sender, execution package or device +instruction and it grants no authority. + +## Candidate + +The first useful device test would use the exact audited +`retroarch_ps5_software.elf`, not the headless ELF. The headless static core +has no observable result channel and intentionally runs indefinitely with null +input. The software candidate can provide visible, audible and controller +evidence and can request a clean shutdown. + +Before any action, a later task must bind: + +- source commit `ca1b45680577befc743e1c92fa40687e1b1745e7`; +- the 3318432-byte ELF with SHA-256 + `7beb09592404b5c1fb4161c632171d2901f3715db26e59458998690e8c49f3fc`; +- firmware text `9.60`; +- a manual, non-autoload, non-installing one-shot loader route; +- an independently reviewed recovery path; +- a 100 ms flip timeout in the linked SDL object; +- no automatic retry; +- one explicit artifact-specific transfer authorization and one explicit + execution authorization. + +## Exact proposed one-shot scope + +1. Verify the local ELF hash against the reviewed manifest. +2. Verify autoload remains off and no live component will be replaced. +3. Transfer only that one ELF to a non-persistent one-shot loader path. +4. Execute once with no content and no arguments. +5. Observe whether a 320x240 moving pattern is presented through RGUI/SDL. +6. Observe bounded 48 kHz stereo test audio. +7. Check one controller: D-pad and left stick change pattern position and A + changes its background. +8. Press Start once and observe whether RetroArch returns cleanly. +9. Do not retry automatically. On crash, hang, missing output, timeout or + unexpected filesystem/service behaviour, stop and use the separately + reviewed recovery action. +10. Hash and preserve host-side logs/transcript; do not infer success for + unobserved subsystems. + +## Explicit technical effects to accept later + +The SDK CRT performs its previously documented bounded runtime initialization. +The software profile opens user/system service, VideoOut, Pad and AudioOut, +allocates heap/direct-memory buffers, creates SDL audio/thread primitives, +submits VideoOut flips, waits on an equeue with a 100 ms timeout, polls input +and writes audio. Logs, scheduler activity, process accounting and caches may +change. These are functional runtime effects, not a side-effect-free probe. + +## Remaining risks + +- loader entry/return and firmware-9.60 lifecycle remain hardware-unproven; +- VideoOut ownership or direct-memory assumptions may fail; +- the public SDL backend's partial-init cleanup has not been observed; +- a timeout may return an error but higher-level shutdown still needs proof; +- audio output is blocking and underrun/overrun statistics are absent; +- controller mappings/deadzone need device validation; +- generic RetroArch write-capable code remains in the ELF even though the + fixed first-run path blocks persistent writes; +- a crash or hang may require a reboot/new jailbreak session. + +No transfer or execution may occur on the basis of this document alone. diff --git a/docs/retroarch/phase-1.0a-pacbrew-sdl-analysis.md b/docs/retroarch/phase-1.0a-pacbrew-sdl-analysis.md new file mode 100644 index 0000000..1a61ecc --- /dev/null +++ b/docs/retroarch/phase-1.0a-pacbrew-sdl-analysis.md @@ -0,0 +1,56 @@ +# Phase 1.0A PacBrew and PS5 SDL analysis + +## Identities + +| Source | Identity | +| --- | --- | +| PacBrew repository | `c2abcfcb60f569128abd0e8e70ad03a67bee5ea7` | +| PS5 SDL fork | `0baf4ac49382b537ba449901b5b6d0d189bb1fbb` | +| SDL reported revision | `SDL-2.30.12-g0baf4ac4` | +| SDL license | Zlib | + +The PacBrew SDL2 recipe builds the PS5 fork and enables OpenGL and LOADSO. +Those two options are intentionally disabled here. PacBrew recipes were also +reviewed for SDL2_image, SDL2_mixer, SDL2_ttf, freetype, zlib, libpng, OpenAL, +elfldr and representative emulator ports. None is needed by the two Phase +1.0A binaries beyond SDL2 itself. + +## Native backend inventory + +| Area | PS5 SDL implementation | APIs/dependencies | Phase 1.0A use | +| --- | --- | --- | --- | +| Video | Software surface, tiled direct-memory buffers, double buffer, VideoOut flip/equeue | `SceVideoOut`, kernel equeue/direct memory, user/system service | Enabled | +| Render | SDL software renderer targeting the window framebuffer | SDL core software renderer | Enabled | +| Input | Up to four PS5 Pad users, buttons, axes, connect/disconnect polling | `ScePad`, `SceUserService` | Enabled for one controller | +| Audio | 48 kHz, mono/stereo, S16 or F32, bounded 256-2048 sample buffers | `SceAudioOut` | Enabled, RetroArch requests stereo | +| Keyboard/IME | PS5 keyboard and IME dialog | `SceKeyboard`, `SceImeDialog` | Removed by reviewed overlay | +| Filesystem | PS5 implementation exists | user service/filesystem | Disabled | +| OpenGL/OSMesa | Optional source exists | Mesa/OSMesa | Disabled | +| LoadSO | Optional | dynamic loader | Disabled | + +## Reviewed overlay + +The fork carries only a patch, not SDL source. The patch: + +- removes keyboard/IME initialization and event pumping; +- replaces twelve freshly created worker threads per frame with a checked + single-thread tile copy; +- rejects non-positive or non-tile-aligned framebuffer dimensions; +- passes a 100,000 microsecond timeout to `sceKernelWaitEqueue`; +- propagates copy and timeout errors to SDL. + +SDL is configured with video, render, events, joystick, threads, timers and +audio on; filesystem, file, loadso, OpenGL, Vulkan, haptic, sensor, locale, +misc, libsamplerate, disk audio and dummy audio are off. + +## Classification + +`PS5_SDL_PARTIAL_CANDIDATE` + +The source contains real PS5 video, controller and audio backends and the +software RetroArch profile links them. It is not `FULL` because firmware-9.60 +runtime behaviour, VideoOut ownership, error cleanup, disconnect recovery, +audio underrun/overrun reporting, exact flip timeout semantics and shutdown +after partial initialization have not been verified on hardware. + +Static build and host audits are not runtime evidence. diff --git a/docs/retroarch/phase-1.0a-port-plan.md b/docs/retroarch/phase-1.0a-port-plan.md new file mode 100644 index 0000000..203b516 --- /dev/null +++ b/docs/retroarch/phase-1.0a-port-plan.md @@ -0,0 +1,70 @@ +# Phase 1.0A native RetroArch PS5 port plan + +Date: 2026-07-19 + +## Outcome and boundary + +Phase 1.0A creates a real native RetroArch target in the separate +`chimera-retroarch` repository. It does not add RetroArch source to +`chimera-gfx`. The implementation has three profiles: + +| Profile | Purpose | Target result | +| --- | --- | --- | +| `ps5-headless-smokecore` | Real RetroArch frontend/runloop with null drivers | `retroarch_ps5_headless.elf` | +| `ps5-software-rgui-smokecore` | RGUI and SDL2 software video, Pad input and 48 kHz AudioOut | `retroarch_ps5_software.elf` | +| `host-smokecore-integration` | 600 deterministic libretro frames under ASan/UBSan | Host executable, removed after test | + +The target outputs are offline research artifacts. They are not installation +or execution packages and carry no device authority. + +## Layering + +1. Upstream RetroArch remains the generic frontend, runloop, menu and driver + registry. +2. The PS5 layer adds the platform frontend, compile-time profiles and static + smoke core. The software profile selects existing RetroArch SDL2 drivers. +3. Chimera owns dependency locks, reproducible builds, artifact audits, + manifests, guardrails and a later human-reviewed smoke-test proposal. + +## Work sequence + +1. Pin the official stable RetroArch release and source archive. +2. Add the PS5 frontend and a warnings-as-errors headless build. +3. Add and host-test a deterministic, contentless static libretro core. +4. Cross-link the real headless RetroArch ELF. +5. Build the pinned PS5 SDL fork with a reviewed overlay. +6. Cross-link RGUI plus software video, input and audio. +7. Perform two clean builds of every successful profile and compare bytes. +8. Audit ELF headers, dynamic dependencies, symbols, relocations, executable + segments, map files, startup/shutdown disassembly, strings and policy + markers. +9. Commit the port to its separate private branch and record only identities, + results and safety evidence here. + +## Compile-time safety policy + +- networking, achievements, updater, core download and dynamic cores are off; +- no GNM, OpenGL, Vulkan, shaders or hardware libretro context is present; +- configuration reads are blocked by the PS5 frontend; +- no content, core, SRAM, state, config or data path is supplied; +- Salamander config and the normal shutdown save event are compiled out; +- the static smoke core has no filesystem, network, JIT, thread or hot-path + allocation code; +- build rules contain no sender, deploy, install or run action. + +Generic RetroArch filesystem functions remain linkable and some libc write +imports remain in the ELFs. The safety claim is limited to the fixed default +launch control flow, not global absence of all write-capable library code. +Hardware behaviour on firmware 9.60 remains unproven. + +## Non-goals + +Dynamic libretro cores, persistent VFS paths, savestates, SRAM, playlists, +history, screenshots, multiple controllers, rumble, touchpad, gyro, shaders, +GNM acceleration, OpenGL/Vulkan contexts, packaging, installation and device +execution are outside Phase 1.0A. + +The five explicit offline source/build/audit/private-push authorizations are +recorded as true for this phase. PS5 connection, device transfer, device +execution, installation, lifecycle, autoload and device writes remain false; +`automatic_retry=false`. diff --git a/docs/retroarch/phase-1.0a-ps4-reference-delta.md b/docs/retroarch/phase-1.0a-ps4-reference-delta.md new file mode 100644 index 0000000..20687e7 --- /dev/null +++ b/docs/retroarch/phase-1.0a-ps4-reference-delta.md @@ -0,0 +1,29 @@ +# Phase 1.0A PS4 reference delta + +The official RetroArch Orbis port is a structural reference only. + +| Orbis concept | PS5 Phase 1.0A decision | Evidence/status | +| --- | --- | --- | +| `Makefile.orbis` platform build | New `Makefile.ps5` using the open PS5 Payload SDK | Implemented | +| Orbis frontend registry | New `frontend_ctx_ps5`, first for `__PS5__` | Implemented | +| Orbis platform paths | No device paths at all | Safer default; future VFS work | +| PS4 native input | RetroArch SDL2 input over the PS5 SDL Pad backend | Linked; runtime unproven | +| PS4 native audio | RetroArch SDL audio over PS5 SDL AudioOut | Linked; runtime unproven | +| PS4 video/context | PS5 SDL software framebuffer and VideoOut only | Linked; no GNM/context | +| Static core support | Global libretro symbols provided by `chimera_smokecore` | Implemented and audited | +| Console definitions | `__PS5__`, `__PROSPERO__`, `PS5`, `RARCH_CONSOLE` | Compile-time only | +| Packaging/SELF | No conversion or packaging | Explicitly excluded | + +Not reused from PS4: + +- ABI declarations or structure layouts; +- Orbis library or stub names; +- PS4 paths, title identifiers or package logic; +- PS4 controller/audio/video calls; +- Auth info, SELF generation or installation; +- PS4 runtime or cleanup assumptions. + +The PS5 ELF uses the public SDK v0.41 CRT and linker contract already audited +by Chimera. That startup has documented userland/kernel runtime effects and +adds SDK dynamic dependencies; it is not a PS4 ABI and is not claimed to be +side-effect free. Offline linking is authorized, device execution is not. diff --git a/docs/retroarch/phase-1.0a-upstream-analysis.md b/docs/retroarch/phase-1.0a-upstream-analysis.md new file mode 100644 index 0000000..835ac7c --- /dev/null +++ b/docs/retroarch/phase-1.0a-upstream-analysis.md @@ -0,0 +1,67 @@ +# Phase 1.0A upstream analysis + +## Selected RetroArch base + +| Field | Value | +| --- | --- | +| Official repository | `https://github.com/libretro/RetroArch` | +| Stable tag | `v1.22.2` | +| Commit | `69a4f0ea1e8aaf442ae4858f2e7f2b31a1776576` | +| Tree | `33babf9eb7699b5d571a3063ea21c3e488c159fe` | +| Commit date | `2025-11-20T00:17:08Z` | +| Source archive | official GitHub tag archive | +| Archive size | `71629881` bytes | +| Archive SHA-256 | `245ef18c8fa8fbd9fbb5eb25cf43e17c6aace2f95c1ed99873cbd794012bb232` | +| License | GPL-3.0-or-later (`COPYING` and source notices) | +| Acquisition date | `2026-07-19` | + +The local fork starts at that exact commit. Its `upstream` fetch remote names +the official repository and its push URL is disabled. Project pushes are +restricted to the private Gitea `origin`. + +## Areas inspected + +The analysis covered `Makefile.common`, `Makefile.griffin`, +`Makefile.orbis`, frontend and driver registries, null/dummy drivers, RGUI, +static core glue, config/path startup, task queues, time/sleep, threads, +dynamic loading, VFS and logging. Reference platform implementations included +Orbis, Vita, Switch, PSP and null. + +Relevant upstream mechanisms: + +- `frontend_ctx_drivers[]` selects the first compiled platform frontend; +- video, audio and input arrays fall back to their null drivers; +- `Makefile.common` conditionally adds SDL2 video, input/joypad and audio; +- a static build resolves the global libretro API symbols at link time; +- configuration defaults derive driver names from compiled feature macros; +- RGUI is the smallest software-oriented menu driver; +- generic file/VFS/task code is widely shared even when persistent features + are disabled. + +## Fork delta + +The port adds: + +- `Makefile.ps5` with explicit headless and software profiles; +- `frontend/drivers/platform_ps5.c`; +- `cores/chimera_smokecore/`; +- host tests and PS5 build validation; +- `pkg/ps5/` for the pinned SDL build and reviewed overlay; +- PS5 port documentation. + +Small generic-source changes register the platform, block static Salamander +config I/O and normal shutdown saves for this profile, and add headers or +fallback locals required by the unusually small feature matrix. No proprietary +header, NID, syscall, firmware offset or device path is introduced. + +## Static and dynamic core decisions + +The static smoke core is linked into both ELFs and exports the real libretro +entrypoints consumed by RetroArch. Dynamic loading is deliberately disabled: +there is no proven native PS5 shared-core file/relocation/unload contract yet. +A later milestone must separately specify format, symbol binding, executable +memory, directory/VFS policy, ABI compatibility, error cleanup and unload. + +No additional `libretro-samples` acquisition was needed because the required +deterministic input, video, audio and no-content behaviour was implemented in +the small GPL-compatible Chimera core and verified by a host harness. diff --git a/docs/retroarch/phase-1.0aa-offline-fake-adapter-integration.md b/docs/retroarch/phase-1.0aa-offline-fake-adapter-integration.md new file mode 100644 index 0000000..3a53a53 --- /dev/null +++ b/docs/retroarch/phase-1.0aa-offline-fake-adapter-integration.md @@ -0,0 +1,70 @@ +# Phase 1.0AA: offline fake-adapter integration + +Status: `OFFLINE_FAKE_BATCH_INTEGRATION_COMPLETE_LIVE_ADAPTER_BLOCKED` + +Date: 2026-07-22 + +Phase 1.0AA connects the Phase-1.0Z passive batch contract to the Phase-1.0X +exclusive evidence model using only an exact built-in fake adapter and exact +synthetic clock. It creates no live adapter, socket, address, CLI, target code +or target artifact. No PS5 action occurred. + +## Closed fake boundary + +`run_offline_fake_batch` rejects subclasses and arbitrary adapter objects. The +only accepted components are: + +- `OfflineFakeClock`, whose value changes only through explicit fake events; +- `OfflineFakeBatchAdapter`, which allows one fake open, one complete Z batch, + a bounded scripted event sequence and one fake close; +- `OfflineFakeEvidenceStore`, which exclusively creates local test evidence. + +There is deliberately no adapter protocol that a network implementation could +quietly satisfy. The fake adapter records only the SHA-256 of sent bytes, not a +target. Scripted event buffers are logically discarded during close; physical +memory erasure remains unproven because caller-owned immutable bytes may still +exist. + +## State and ordering + +```text +validate W plan -> build and revalidate Z batch + -> exclusive consumed receipt + -> fake open exactly once + -> fake send exactly one complete batch + -> zero or more bounded DATA events before deadline + -> HARD_DEADLINE at or after the synthetic deadline + -> Z completeness validation and sanitization + -> fake close exactly once + -> exclusive receipt-bound sanitized output +``` + +The consumed receipt includes run ID, window, batch size, batch SHA-256, +deadline and the exact Z contract hash. It does not include a target or raw +transcript. A failure after receipt creation leaves the receipt intact and +creates no sanitized output; no cleanup deletes it. + +Remote EOF, a blocked receive event, missing deadline, early deadline, data at +or after the deadline, IAC, partial output, a second fake send, a second fake +close and evidence collisions all fail closed. Prompt bytes remain ordinary +data and cannot seal a result. + +## What the tests prove + +The host tests prove internal ordering and rejection behavior of this exact +Python model. They do not prove: + +- a live listener or exact deployed shsrv binary; +- OS socket timeouts or preemption of a blocking call; +- live fragmentation, short sends/writes, disconnect or cleanup; +- firmware-9.60 behavior; +- absence of atime, cache, accounting, scheduler or shell-process effects; +- physical erasure or containing-directory durability. + +## Decision + +The fake integration is complete and remains host-only. A live adapter is +blocked and unauthorized. The next permitted step is an offline feasibility +review for a future bounded live adapter: OS timeout/preemption semantics, +short-send handling, exclusive evidence ordering and failure cleanup must be +specified without adding a socket, address or device action. diff --git a/docs/retroarch/phase-1.0ab-offline-live-adapter-feasibility.md b/docs/retroarch/phase-1.0ab-offline-live-adapter-feasibility.md new file mode 100644 index 0000000..df8adca --- /dev/null +++ b/docs/retroarch/phase-1.0ab-offline-live-adapter-feasibility.md @@ -0,0 +1,79 @@ +# Phase 1.0AB: offline live-adapter feasibility + +Status: `PARTIAL_FEASIBILITY_LIVE_IMPLEMENTATION_BLOCKED` + +Date: 2026-07-22 + +This phase audits the local Windows host runtime and models lifecycle ordering +with synthetic traces only. It adds no socket import, address, DNS, live +adapter, CLI or device action. + +## Bound local runtime + +| Object | Identity | +|---|---| +| Python | 3.13.2, MSC v.1942, 64-bit AMD64 | +| `socket.py` | 38,741 bytes; SHA-256 `523695ac3383799547b421b4fe18004de1e80181e97181b6d7a10533b47f4c49` | +| `selectors.py` | 20,060 bytes; SHA-256 `b3d6cebd4a3a03b4a614f12f171622ce4e4ba3295b9e8b89e2bde051003106eb` | +| `_socket.pyd` | 84,984 bytes; SHA-256 `8daefaff53e6956f5aea5279a7c71f17d8c63e2b0d54031c3b9e82fcb0fb84db` | +| `select.pyd` | 32,248 bytes; SHA-256 `baee284995b22d495fd12fa8378077e470978db1522c61bfb9af37fb827f33d1` | +| monotonic clock | `QueryPerformanceCounter()`, monotonic, non-adjustable, reported resolution `1e-07` seconds | +| default selector | `SelectSelector` on this runtime | + +These identities are local host evidence only. They are not portable to a +different Python build and say nothing about the PS5. + +## Source-bound findings + +The local `_socket` interface states that: + +- `setblocking(False)` is equivalent to timeout `0.0`; +- `connect_ex` returns an error number instead of raising for connection + results; +- `send` returns a byte count that may be smaller than the supplied buffer; +- `recv` returns at most the requested bytes and returns empty bytes after + remote close and drained data; +- `close` makes the local socket object unusable. + +The local `selectors.py` documents a maximum wait parameter. On Windows its +`SelectSelector` passes readers, writers and exceptional writers to +`select.select`, merges exceptional sockets into writable results, and returns +an empty readiness set after `InterruptedError`. Therefore a pending connect +must inspect `SO_ERROR` after writability, and every empty/interrupted return +must recompute the remaining monotonic budget. + +## Feasibility matrix + +| Part | Classification | Reason | +|---|---|---| +| receipt before socket creation | `FEASIBLE_FROM_EXISTING_HOST_MODEL` | X/AA exclusive evidence already models this order | +| numeric-address-only input | `DESIGN_REQUIRED` | DNS must be excluded; no address parser is added here | +| nonblocking mode before connect | `FEASIBLE_FROM_LOCAL_RUNTIME` | exact local `_socket` contract exists | +| pending connect completion | `PARTIAL` | readiness plus `SO_ERROR` is source-bound; exact accepted Windows error set still needs implementation review | +| complete batch send | `FEASIBLE_FROM_LOCAL_RUNTIME` | repeated readiness and explicit partial-send loop are available | +| bounded receive memory | `FEASIBLE_FROM_EXISTING_MODEL` | 65,536-byte collector bound already exists | +| hard wall-clock deadline | `PARTIAL` | monotonic remaining budgets are feasible; selector/scheduler overshoot cannot be proven impossible | +| prompt-independent completion | `FEASIBLE_FROM_Z` | only deadline sealing is accepted | +| EOF behavior | `FEASIBLE_FAIL_CLOSED` | empty receive must be failure, never completion | +| local descriptor cleanup | `FEASIBLE_BY_DESIGN` | unregister/selector close/socket close can run in `finally` | +| remote shell cleanup | `UNPROVEN` | local close does not attest remote exit or firmware behavior | +| retry/reconnect/resume | `EXCLUDED` | forbidden by contract | + +Overall classification: `PARTIAL_FEASIBILITY_LIVE_IMPLEMENTATION_BLOCKED`. + +## Existing-client comparison + +Earlier one-shot ELF runners used blocking `sendall`, write-half-close and +bounded receive for a different raw-ELF protocol. Their result manifests prove +those consumed runs only. They are not reusable for shsrv: Z sends a tiny shell +batch, must keep the write direction open while receiving, ignores prompts and +seals only at deadline. `sendall` also hides individual partial-send progress; +the future contract requires an explicit nonblocking send loop. + +## Decision + +The required sequence is implementable in principle on the exact local host, +but a hard scheduling bound and remote cleanup cannot be proven offline. A live +adapter remains blocked. The next permitted phase may build only a dormant, +target-free adapter around an injected syscall facade and fake OS results. It +must not import or instantiate a real socket until a later dedicated review. diff --git a/docs/retroarch/phase-1.0ab-timeout-and-cleanup-contract.md b/docs/retroarch/phase-1.0ab-timeout-and-cleanup-contract.md new file mode 100644 index 0000000..556d8e6 --- /dev/null +++ b/docs/retroarch/phase-1.0ab-timeout-and-cleanup-contract.md @@ -0,0 +1,50 @@ +# Phase 1.0AB timeout and cleanup contract + +This is a design contract, not live network code. + +## Future required algorithm + +1. Rehash policy, Z batch and approval before any capability creation. +2. Exclusively create, flush, close and reopen the consumed receipt. +3. Acquire one absolute monotonic deadline. +4. Create at most one stream socket and immediately set it nonblocking. +5. Start one numeric-address connect; never perform DNS. +6. For pending connect, wait for write/exception readiness using only the + recomputed remaining budget, then require `SO_ERROR == 0`. +7. Send the exact batch with an offset loop. Each call requires prior write + readiness; zero progress, excess count or error fails the attempt. +8. Switch to read readiness. Each read is bounded by both remaining collector + capacity and a small fixed chunk size. +9. Treat empty receive as remote EOF and fail immediately. +10. After every wait or operation, read monotonic time again. At or beyond the + deadline perform no further send or receive. +11. Ask Z to seal only because the hard deadline was reached. Partial or + malformed data remains invalid. +12. In `finally`, unregister if registered, close the selector and close the + local socket exactly once. Never retry, reconnect, resume or delete the + consumed receipt. +13. Create sanitized output only after successful Z sealing and local cleanup. + +## Conservative race rule + +When readiness and deadline coincide, the deadline wins. No additional bytes +are read or sent at `now >= deadline`. This can reject data already queued by +the OS, but it cannot silently extend the approved window. + +## Failure classifications + +| Event | Required result | +|---|---| +| connect error or nonzero `SO_ERROR` | fail, local cleanup | +| selector interruption | recompute remaining budget; no retry counter | +| selector timeout before absolute deadline | recompute; never seal early | +| partial send | advance offset only by reported positive count | +| zero send | fail | +| receive over 65,536 bytes | fail | +| remote EOF | fail | +| deadline without complete Z result | fail | +| cleanup exception | fail and retain receipt | +| output collision or short host write | fail and retain existing evidence | + +`close()` proves only local object closure. It does not prove TCP packet +delivery, remote shsrv exit, process cleanup or reboot recovery. diff --git a/docs/retroarch/phase-1.0ac-fake-syscall-contract.md b/docs/retroarch/phase-1.0ac-fake-syscall-contract.md new file mode 100644 index 0000000..df9e4f6 --- /dev/null +++ b/docs/retroarch/phase-1.0ac-fake-syscall-contract.md @@ -0,0 +1,24 @@ +# Phase 1.0AC fake-syscall contract + +This contract is executable only against the exact built-in fake facade. + +| Synthetic operation | Accepted result | Rule | +|---|---|---| +| `CREATE_STREAM` | `OK`, `ERROR` | exactly once after a precommitted receipt | +| `SET_NONBLOCKING` | `OK`, `ERROR` | immediately after create | +| `START_CONNECT` | `IMMEDIATE`, `PENDING`, `ERROR` | contains no target or address | +| `WAIT_WRITE` | `READY`, `INTERRUPTED`, `TIMEOUT`, `ERROR` | remaining fake budget is rechecked | +| `GET_SO_ERROR` | `ZERO`, `NONZERO`, `ERROR` | mandatory after pending readiness | +| `WRITE_BYTES` | `PROGRESS`, `ZERO`, `ERROR` | positive progress cannot exceed remainder | +| `WAIT_READ` | `READY`, `INTERRUPTED`, `TIMEOUT`, `ERROR` | deadline wins a simultaneous readiness event | +| `READ_BYTES` | `PROGRESS`, `EOF`, `ERROR` | EOF is failure; total input is bounded | +| local close | `OK`, `ERROR` | one call after ownership; none if create failed | + +The facade accepts at most 1,024 scripted steps. One step may advance the fake +clock by at most 60 seconds. These are model bounds, not proposed live values. +Unused synthetic steps are logically discarded at close; physical memory +erasure is not proven. + +There is deliberately no adapter protocol, inheritance hook, address field, +network import or live factory. A later phase cannot reinterpret this fake as +device authorization. diff --git a/docs/retroarch/phase-1.0ac-offline-dormant-adapter.md b/docs/retroarch/phase-1.0ac-offline-dormant-adapter.md new file mode 100644 index 0000000..4e55c84 --- /dev/null +++ b/docs/retroarch/phase-1.0ac-offline-dormant-adapter.md @@ -0,0 +1,55 @@ +# Phase 1.0AC: offline dormant adapter + +Status: `OFFLINE_DORMANT_FAKE_SYSCALL_ADAPTER_COMPLETE_LIVE_ADAPTER_BLOCKED` + +Date: 2026-07-22 + +Chimera GFX is a graphics/homebrew project. This phase adds host-side test +infrastructure for a possible future one-shot launcher observation; it is not +a security feature and it changes no RetroArch, SDL, VideoOut or target code. + +## Closed capability boundary + +`phase10ac_dormant_adapter.py` composes the Phase-1.0Z passive batch parser +with an exact built-in fake syscall facade. The facade consumes caller-created +synthetic outcomes. It cannot accept a live implementation and stores no +target. The module imports no socket, selector, DNS, real clock or filesystem +output interface and exposes no CLI. + +The adapter requires a precommitted-receipt marker before the first synthetic +create step. It then models: + +1. one create and immediate nonblocking setup; +2. immediate or pending connect completion; +3. readiness plus `SO_ERROR == 0` for a pending result; +4. an explicit partial-write loop for exactly one Phase-1.0Z batch; +5. bounded read readiness and at most 65,536 supplied bytes; +6. deadline-only result sealing; +7. one local close on success or any failure after successful create. + +Timeout and interrupted-wait events stay inside the same synthetic attempt. +They never create a retry, reconnect or resume. When readiness coincides with +the deadline, the deadline wins and no subsequent fake read or write occurs. +EOF, zero progress, excess progress, malformed data and cleanup failure all +invalidate the run. + +## Evidence boundary + +The 32 host tests cover immediate and pending connect paths, partial writes, +interrupted and timeout waits, exact deadline races, EOF, receive limits, +malformed results, cleanup and closed fake types. They prove only the Python +model's behavior. They do not prove: + +- a deployed shsrv identity or port-2323 behavior; +- Windows scheduler or real socket timing; +- a live connect, send, receive or remote-process cleanup; +- firmware-9.60 behavior; +- RetroArch launch context, SDL, VideoOut or rendering. + +## Decision + +The dormant fake-syscall adapter is complete. A live adapter remains blocked +and absent. The next permissible step is an offline-only Phase 1.0AD design +for numeric target validation and an inactive activation record. That phase +must still contain no socket creation, connection, device request or enabled +authorization. diff --git a/docs/retroarch/phase-1.0ad-inactive-activation-contract.md b/docs/retroarch/phase-1.0ad-inactive-activation-contract.md new file mode 100644 index 0000000..c552abf --- /dev/null +++ b/docs/retroarch/phase-1.0ad-inactive-activation-contract.md @@ -0,0 +1,24 @@ +# Phase 1.0AD: inactive activation contract + +Status: `INACTIVE_NUMERIC_TARGET_CONTRACT_COMPLETE_NO_LIVE_CAPABILITY` + +Date: 2026-07-29 + +This phase closes the data boundary left by Phase 1.0AC. It does not add a +network adapter, target source, launcher, socket, DNS lookup, clock, CLI or +device action. + +The tracked activation is entirely inactive and target-free. The host-only +contract can validate hypothetical future records, but validation neither +activates nor persists them. A candidate must use canonical private IPv4 text, +source-bound port 2323, a unique run ID, a window of at most five minutes and +separate SHA-256 identities for launcher, payload and untracked approval. + +Retry, reconnect, resume, device writes, app termination and system remounts +are rejected. The contract deliberately cannot express authorization for +those effects. + +The next useful phase is a separate offline architecture review of a minimal +BigApp launcher derived from official GPLv3 shsrv source. That review must +remove the general shell, persistent fake-app creation, remount behavior, +unbounded waits and arbitrary payload selection before target code is allowed. diff --git a/docs/retroarch/phase-1.0ae-minimal-bigapp-launcher-architecture.md b/docs/retroarch/phase-1.0ae-minimal-bigapp-launcher-architecture.md new file mode 100644 index 0000000..8e35ad2 --- /dev/null +++ b/docs/retroarch/phase-1.0ae-minimal-bigapp-launcher-architecture.md @@ -0,0 +1,79 @@ +# Phase 1.0AE: minimal BigApp launcher architecture + +Status: `V07_NONPERSISTENT_LINEAGE_SELECTED_TARGET_IMPLEMENTATION_BLOCKED` + +Date: 2026-07-29 + +## Objective + +Identify the shortest public-source route from the proven raw-ELF VideoOut +failure to one bounded launch-context experiment. This is an offline source +review. It adds no target source, artifact, socket, transfer or execution. + +## Source decision + +Official shsrv v0.19 is not an acceptable base. Its hbldr path can remount +`/system_ex`, persistently create `FAKE00000`, copy an executable, kill the +running BigApp and enter unbounded ptrace/wait loops. + +Official shsrv v0.7 is the selected reference lineage because it launches the +existing VideoPlayer WebApp title `PPSA01659` and contains no fake-app creation +or system-ex remount in its hbldr bundle. Selection is not authorization and +does not prove that the title exists, launches, owns VideoOut or behaves the +same way on firmware 9.60. + +## Required minimal callgraph + +The future design may model only this sequence: + +1. validate one exact payload hash and fixed argument vector; +2. query the foreground user; +3. require that no BigApp is currently running; +4. attach to the source-proven SystemService parent; +5. arm bounded fork and exec observation before launch; +6. request launch of the fixed existing `PPSA01659` title; +7. detach the parent on every path; +8. replace only the newly observed child with the exact payload; +9. restore every temporary instruction and credential mutation; +10. detach the child or terminate only that newly created child on failure; +11. emit a bounded inherited result and stop. + +The architecture must fail closed when a BigApp already exists. It must never +call `sceSystemServiceKillApp` as part of the experiment. + +## Removed upstream behavior + +- general Telnet shell and arbitrary command parsing; +- PATH search, arbitrary filesystem path and target-side ELF read; +- `FAKE00000`, `fakeapp_create_if_missing` and `remount_system_ex`; +- package installation, autoload and persistent writes; +- `hbdbg`, GDB wait and free-form arguments; +- arbitrary root/jail broadening unless separately proven indispensable and + exactly restored; +- unbounded `waitpid`, `pt_await_child`, `pt_await_exec`, `pt_call` and + `pt_syscall` loops; +- killing or replacing any pre-existing process; +- retry, reconnect and fallback title selection. + +## Unclosed contracts + +Target implementation remains blocked until an offline design proves: + +- a deadline-capable wait primitive for every wait/step boundary; +- unique child correlation without acting on an unrelated process; +- complete parent detach and child cleanup for every failure edge; +- exact restoration of breakpoint bytes, page protections, credentials, + environment, root and jail changes; +- bounded ELF size, headers, segments, relocations and allocation; +- exact firmware-9.60 availability and role of `PPSA01659`, or a fail-closed + observation that does not install or mutate it; +- accepted public evidence for every SystemService/UserService declaration; +- an explicit policy for the unavoidable kernel/ptrace effects. + +## Decision + +The persistent v0.19 route is rejected. v0.7 is the preferred public-source +reference for a nonpersistent BigApp experiment, but copying or compiling it +is still blocked. The next phase may implement only a host lifecycle model +with injected fake operations and exhaustive failure cleanup. It may not add +PS5 headers, target source, syscall numbers, a target build or device action. diff --git a/docs/retroarch/phase-1.0af-offline-bigapp-lifecycle-model.md b/docs/retroarch/phase-1.0af-offline-bigapp-lifecycle-model.md new file mode 100644 index 0000000..123fed3 --- /dev/null +++ b/docs/retroarch/phase-1.0af-offline-bigapp-lifecycle-model.md @@ -0,0 +1,25 @@ +# Phase 1.0AF: offline BigApp lifecycle model + +Status: `HOST_LIFECYCLE_MODEL_COMPLETE_TARGET_IMPLEMENTATION_BLOCKED` + +Date: 2026-07-29 + +The host-only state machine implements the lifecycle selected in Phase 1.0AE +using exact built-in fake events. It contains no PS5 headers, process calls, +syscalls, socket, real clock, CLI or filesystem output. + +The model refuses launch when a BigApp already exists. A successful synthetic +attempt must attach and arm the parent, request only fixed title `PPSA01659`, +observe one positive unique child identity, detach the parent, observe child +exec, replace one exact payload, restore temporary mutations, detach the child +and emit one result within 64 supplied ticks. + +Failure injection proves the modeled unwind order. An acquired parent is +detached. A newly observed child is the only process eligible for termination. +If replacement began, restoration precedes termination. Cleanup failure is a +hard error rather than a clean result. These are properties of the model, not +firmware or runtime evidence. + +Target implementation remains blocked. The next permitted work is a host-only +bounded ELF contract and validator so malformed or oversized payloads can be +rejected before any future launcher boundary. diff --git a/docs/retroarch/phase-1.0ag-bounded-elf-contract.md b/docs/retroarch/phase-1.0ag-bounded-elf-contract.md new file mode 100644 index 0000000..a84a427 --- /dev/null +++ b/docs/retroarch/phase-1.0ag-bounded-elf-contract.md @@ -0,0 +1,26 @@ +# Phase 1.0AG: bounded ELF admission + +Status: `BOUNDED_ELF_ADMISSION_IMPLEMENTED_HISTORICAL_ARTIFACT_BYTES_MISSING` + +Date: 2026-07-29 + +The bytes-only validator admits only an exact SHA-256-bound, little-endian +x86-64 `ET_DYN` payload of at most 2 MiB. All ELF, program-header, optional +section-header and load-segment ranges must be contained in the supplied +immutable byte string. + +At most 32 program headers, 256 section headers and eight load segments are +accepted. Load memory is limited to 64 MiB across a 128 MiB virtual span. +Segments must have power-of-two alignment no greater than 2 MiB, must not +overlap, and may never be both writable and executable. `PT_INTERP` is +forbidden and the entry must fall inside an executable load segment. + +The parser has no path or file interface and performs no mapping, relocation +or execution. Synthetic tests cover identity, hash, type, machine, bounds, +alignment, overlap, permissions, interpreter, entry and header-count failures. + +The consumed Phase-1.0M artifact is a size/hash reference only. Its exact bytes +are absent from the scanned workspace, so Phase 1.0AG does not claim to have +admitted it. A future canary must be rebuilt and validated from its exact bytes. +Before target mapping can be designed, the supported dynamic table and +relocation subset must be specified with the same bounds. diff --git a/docs/retroarch/phase-1.0ah-dynamic-relocation-contract.md b/docs/retroarch/phase-1.0ah-dynamic-relocation-contract.md new file mode 100644 index 0000000..fb3108b --- /dev/null +++ b/docs/retroarch/phase-1.0ah-dynamic-relocation-contract.md @@ -0,0 +1,28 @@ +# Phase 1.0AH: dynamic and relocation contract + +Status: `LOADER_CRT_RELOCATION_SPLIT_BOUNDED_TARGET_MAPPING_BLOCKED` + +Date: 2026-07-29 + +This bytes-only phase extends Phase 1.0AG with one bounded dynamic table and +up to four `SHT_RELA` sections containing at most 4,096 entries. It has no +file, mapping, module-loading or execution interface. + +The future launcher subset applies only `R_X86_64_RELATIVE`. Each target must +be eight-byte aligned and wholly inside an admitted RW load segment; the symbol +index must be zero and the addend must identify admitted image memory. + +`R_X86_64_GLOB_DAT` is admitted only as SDK-CRT work, with a nonzero symbol +index, zero addend and an RW target. Every other relocation type fails closed. +The exact ordered `DT_NEEDED` inventory must be supplied per artifact and may +contain only the seven modules already evidenced for Phase 1.0M. + +This distinction matters: official shsrv v0.7 applies RELATIVE relocations +while the pinned SDK v0.41 CRT loads `DT_NEEDED` modules and resolves dynamic +symbols. The latter is side-effecting runtime behavior, not a property proven +safe by this parser. + +The historical M counts (913 RELATIVE, 142 GLOB_DAT, seven modules) remain +reference metadata because the bytes are absent. Target mapping remains +blocked until allocation, copy, BSS zeroing, final page permissions and full +failure restoration are modeled with bounded fake operations. diff --git a/docs/retroarch/phase-1.0ai-offline-mapping-transaction.md b/docs/retroarch/phase-1.0ai-offline-mapping-transaction.md new file mode 100644 index 0000000..6e96f83 --- /dev/null +++ b/docs/retroarch/phase-1.0ai-offline-mapping-transaction.md @@ -0,0 +1,25 @@ +# Phase 1.0AI: offline mapping transaction + +Status: `HOST_MAPPING_ROLLBACK_MODEL_COMPLETE_TARGET_MAPPING_BLOCKED` + +Date: 2026-07-29 + +The host-only model consumes already admitted load metadata. It models one +new child region and one host mirror, exact file-byte copy and BSS zeroing per +segment, the admitted RELATIVE count, one mirror-to-child copy, final segment +protections, image sync and mirror release. + +Success retains the new synthetic child region only after every protection, +sync and mirror-release step succeeds. Failure releases an acquired mirror and +unmaps the complete newly reserved child region. A partial image is never a +valid outcome. Cleanup failure is a hard error. + +The supplied tick deadline is atomic: an operation that would cross the bound +is classified as not executed. This prevents ambiguous ownership when a +resource-creating operation meets the deadline. + +The model rejects W+X segments and overlapping page-rounded protection ranges. +It contains no allocator, memory mapping, process, ptrace or target interface. +The next phase must bind every required primitive to official source and prove +how new mappings and any temporary protection changes are undone on every +failure path before target code is considered. diff --git a/docs/retroarch/phase-1.0aj-offline-primitive-audit.md b/docs/retroarch/phase-1.0aj-offline-primitive-audit.md new file mode 100644 index 0000000..8c2dd45 --- /dev/null +++ b/docs/retroarch/phase-1.0aj-offline-primitive-audit.md @@ -0,0 +1,30 @@ +# Phase 1.0AJ: offline primitive source audit + +Status: `HYBRID_PRIMITIVE_REUSE_CANDIDATE_GAPS_REMAIN_TARGET_CODE_BLOCKED` + +Date: 2026-07-29 + +Official shsrv v0.7 remains useful only for the BigApp creation and replacement +lineage. Its loader implementation is not reusable: remote calls are unbounded, +and several alias-unmap, descriptor-close, host-unmap and rollback results are +discarded. + +The hash-bound hardened `chimera-elfldr` fork supplies better candidates for +remote execution and cleanup. Its ptrace calls have monotonic deadlines and a +step ceiling, registers are restored, credential restoration attempts every +field, and acquired breakpoints, descriptors, mappings and children are handled +by centralized cleanup with reported failure bits. + +This is not yet a complete BigApp loader. The hardened cleanup state does not +model every temporary JIT descriptor, alias and host mirror needed by the v0.7 +mapping lineage. It also clears ownership bits after failed cleanup, so a +composition must make child termination the fail-closed terminal owner rather +than treating failed release as successful release. BigApp identity/correlation, +partial remote copy, final protection, sync and full rollback remain unbound as +one transaction. + +The decision is therefore hybrid reuse by composition, not source copying: +retain v0.7 as provenance for BigApp creation, reuse only reviewed hardened +primitives, and first extend the Phase-1.0AI host model with every temporary +resource and cleanup failure. No target source, build or device action is +allowed by this audit. diff --git a/docs/retroarch/phase-1.0ak-offline-hybrid-composition.md b/docs/retroarch/phase-1.0ak-offline-hybrid-composition.md new file mode 100644 index 0000000..4204926 --- /dev/null +++ b/docs/retroarch/phase-1.0ak-offline-hybrid-composition.md @@ -0,0 +1,23 @@ +# Phase 1.0AK: offline hybrid primitive composition + +Status: `HOST_HYBRID_RESOURCE_OWNERSHIP_MODEL_COMPLETE_TARGET_CODE_BLOCKED` + +Date: 2026-07-29 + +This host-only model composes the lifecycle and mapping ownership needed by a +future BigApp route. It explicitly tracks the child, whole image region, host +mirror and every per-executable-segment JIT master descriptor, alias descriptor, +host alias and remote alias. + +Every forward operation is failure-injected. Acquired temporary resources are +released in reverse groups. A failed release is never treated as successful +release: once a child exists, bounded kill-and-reap is the terminal owner and +models operating-system reclamation. Failure of that terminal operation is a +hard error. A successful outcome retains only the child and committed image; +all temporary resources are closed. + +The model contains no process, mapping, target, clock, socket or device +interface. It does not prove that the source primitives compose on firmware +9.60. In particular, partial progress and credential restoration for the SDK +`mdbg_copyin` path still require a source-bound audit before target code can be +considered. diff --git a/docs/retroarch/phase-1.0al-offline-mdbg-copy-audit.md b/docs/retroarch/phase-1.0al-offline-mdbg-copy-audit.md new file mode 100644 index 0000000..7970e4f --- /dev/null +++ b/docs/retroarch/phase-1.0al-offline-mdbg-copy-audit.md @@ -0,0 +1,24 @@ +# Phase 1.0AL: offline mdbg copy and restoration audit + +Status: `SDK_MDBG_COPY_NOT_FAIL_CLOSED_DIRECT_REUSE_BLOCKED` + +Date: 2026-07-29 + +The exact pinned SDK v0.41 `mdbg_copyin` implementation is unsuitable for +direct reuse in the hybrid loader. It changes the service process auth ID +before changing capabilities, but a capability-set failure returns without +restoring that auth ID. Its normal restoration also returns immediately after +an auth-ID restore failure, so capability restoration is not attempted. + +The copy loop neither bounds iterations nor uses a monotonic deadline. It does +not reject a reported length larger than the remaining length, check pointer +arithmetic, or expose partial progress. A nonzero remote status with zero +progress can leave the syscall return value as zero, so return value zero does +not prove that the requested copy completed. Any earlier iterations may already +have mutated target memory. + +A replacement contract must report exact progress and restoration failures, +attempt every required restoration on every exit, and terminate/reap the child +after any partial copy or restoration failure. This audit authorizes only a +capability-free host model of that contract; it does not authorize target code, +a build, connection, transfer or execution. diff --git a/docs/retroarch/phase-1.0am-offline-bounded-copy-model.md b/docs/retroarch/phase-1.0am-offline-bounded-copy-model.md new file mode 100644 index 0000000..c9c265a --- /dev/null +++ b/docs/retroarch/phase-1.0am-offline-bounded-copy-model.md @@ -0,0 +1,22 @@ +# Phase 1.0AM: offline bounded copy and restore model + +Status: `HOST_EXACT_PROGRESS_AND_RESTORE_MODEL_COMPLETE_TARGET_CODE_BLOCKED` + +Date: 2026-07-29 + +The capability-free model replaces the unsafe semantics identified in Phase +1.0AL. It validates 64-bit source and destination ranges, bounds total size, +chunk count and synthetic ticks, rejects zero or oversized progress, and +accepts completion only at the exact requested byte count. + +Auth ID and capabilities are both backed up before modification. Every field +that changed is independently restored on every exit, with separate failure +bits. A partial copy requires child termination and reap. Any credential +restore failure additionally requires termination of the now-untrusted service; +failure of either terminal cleanup operation is a hard error. + +Successful exact copy keeps the child alive for subsequent loader stages. This +is only host-model behavior. The model imports no credential, process, memory, +clock, network or target interface and does not establish the undocumented +remote status semantics or firmware-9.60 behavior. Target implementation stays +blocked pending service-lifecycle ownership and source review. diff --git a/docs/retroarch/phase-1.0an-offline-service-lifecycle-audit.md b/docs/retroarch/phase-1.0an-offline-service-lifecycle-audit.md new file mode 100644 index 0000000..2d5d552 --- /dev/null +++ b/docs/retroarch/phase-1.0an-offline-service-lifecycle-audit.md @@ -0,0 +1,25 @@ +# Phase 1.0AN: offline service lifecycle and copy audit + +Status: `FAIL_STOP_PRESENT_RESTART_AND_BOUNDED_COPY_UNPROVEN_TARGET_CODE_BLOCKED` + +Date: 2026-07-29 + +The hardened elfldr fork has a real fail-stop for its ptrace credential scope. +If restoration of the temporary auth ID fails, a process-global latch rejects +all later ptrace calls. After the current request and its cleanup attempt, the +socket service calls `_exit(125)`. + +That proves process termination, not safe recovery. No reviewed source owns a +bounded restart, verifies the identity of a replacement service, or proves +cleanup when startup-level credential restoration fails. Automatic restart is +therefore forbidden. + +The alternative `pt_copyin` route is also insufficient. It issues one blocking +`PT_IO` request and does not validate returned descriptor progress. The source +has neither a hard deadline/preemption mechanism for that call nor proof that a +successful return means the exact requested byte count. It cannot replace the +unsafe SDK `mdbg_copyin` merely because it changes fewer credentials. + +The next work may only design a host-only preemption and service-owner +architecture. No target source, build, connection, transfer, restart or device +execution is authorized. diff --git a/docs/retroarch/phase-1.0ao-offline-worker-supervisor-model.md b/docs/retroarch/phase-1.0ao-offline-worker-supervisor-model.md new file mode 100644 index 0000000..87e6db6 --- /dev/null +++ b/docs/retroarch/phase-1.0ao-offline-worker-supervisor-model.md @@ -0,0 +1,22 @@ +# Phase 1.0AO: offline worker supervisor model + +Status: `HOST_PREEMPTION_OWNERSHIP_MODEL_COMPLETE_TARGET_FEASIBILITY_UNPROVEN` + +Date: 2026-07-29 + +The capability-free architecture isolates the potentially blocking copy and +temporary credentials in a one-shot worker. The persistent supervisor changes +no credentials. It accepts only a result bound to the exact worker identity, +exact requested byte count and zero restoration-failure bits. + +A deadline terminates and reaps the worker. Once copy start is attempted, every +deadline, malformed/wrong-identity result, partial copy, restoration failure or +ambiguous start additionally terminates and reaps the child. Any terminal +cleanup failure is a hard error. The supervisor performs no retry and never +automatically restarts a worker or service. + +This model does not prove that PS5 userland can create and uniquely identify +such a worker, preempt one blocked inside the relevant syscall, or return a +bounded authenticated result. It contains no process, signal, clock, IPC, +network or target interface. A source-feasibility audit is required before +target code can be considered. diff --git a/docs/retroarch/phase-1.0ap-offline-worker-feasibility-audit.md b/docs/retroarch/phase-1.0ap-offline-worker-feasibility-audit.md new file mode 100644 index 0000000..ca88ce2 --- /dev/null +++ b/docs/retroarch/phase-1.0ap-offline-worker-feasibility-audit.md @@ -0,0 +1,26 @@ +# Phase 1.0AP: current-upstream worker feasibility audit + +Status: `WORKER_CREATION_SOURCE_CANDIDATE_PREEMPTION_AND_RESULT_CHANNEL_BLOCKED` + +Date: 2026-07-29 + +The current official SDK and shsrv heads were fetched as Git objects without +changing either pinned worktree. The SDK declares `rfork_thread`, and current +shsrv uses it both for forked shell builtins and its raw ELF process path. A +returned PID makes worker creation a strong public-source candidate. + +That PID is not a generation-safe identity. There is no bounded worker-start +handshake or birth token. Official source uses `SIGKILL` and `waitpid`, but the +relevant waits are blocking and do not prove that a worker stuck in `mdbg` or +`PT_IO` can be terminated and reaped within a deadline on firmware 9.60. The +state of a separately mutated child after worker termination is also unknown. + +Shell pipes are generic byte streams, not the fixed, identity-bound result +record required by Phase 1.0AO. They provide no exact progress/restoration +fields, bounded receive or deadline sealing. Current shsrv code must not be +copied: its broader persistence and lifecycle effects remain rejected by prior +phases. + +The next allowed step is a capability-free fixed-result and PID-generation +contract. No target source, build, connection, transfer or execution is +authorized. diff --git a/docs/retroarch/phase-1.0aq-offline-worker-result-record.md b/docs/retroarch/phase-1.0aq-offline-worker-result-record.md new file mode 100644 index 0000000..82b2963 --- /dev/null +++ b/docs/retroarch/phase-1.0aq-offline-worker-result-record.md @@ -0,0 +1,23 @@ +# Phase 1.0AQ: offline fixed worker result record + +Status: `FIXED_RESULT_AND_GENERATION_IDENTITY_COMPLETE_TRANSPORT_BLOCKED` + +Date: 2026-07-29 + +The bytes-only protocol is one exact 128-byte little-endian record. It binds an +attempt ID, worker nonce, worker and child PID, monotonic generation, requested +and copied byte counts, status and credential-restoration failure bits. The +first 96 bytes are protected by a trailing SHA-256 digest. Reserved and flag +fields must be zero. + +PID alone is explicitly insufficient. A result is accepted only when every +identity field equals a precommit. Success additionally requires the exact +requested byte count and zero restoration failures. Failure records preserve +partial progress. Tests mutate every byte position and also reject rehashed +records with semantically invalid fields. + +SHA-256 provides integrity here, not authentication; identity comes from the +precommitted attempt and nonce. The model has no random source, pipe, socket, +process, clock or target capability. Exclusive writer ownership, partial reads, +EOF/deadline behavior and PS5 pipe atomicity remain unproven, so no transport or +target implementation is allowed. diff --git a/docs/retroarch/phase-1.0ar-offline-result-channel-model.md b/docs/retroarch/phase-1.0ar-offline-result-channel-model.md new file mode 100644 index 0000000..5ef3451 --- /dev/null +++ b/docs/retroarch/phase-1.0ar-offline-result-channel-model.md @@ -0,0 +1,21 @@ +# Phase 1.0AR: offline exclusive result channel model + +Status: `HOST_PARTIAL_READ_AND_DEADLINE_MODEL_COMPLETE_LIVE_CHANNEL_BLOCKED` + +Date: 2026-07-29 + +The transport-free model consumes synthetic chunks from exactly one +precommitted writer. It supports every two-part split and byte-at-a-time +delivery of the 128-byte Phase-1.0AQ record. Completion of the exact record, not +EOF, is the only success boundary. + +EOF or deadline before completion, silent incompleteness, overflow, digest or +identity mismatch, and any event after the exact record all fail closed. A read +that would cross the synthetic deadline is classified as not executed. Every +failed outcome requires the supervisor to contain worker and child. + +The model does not rely on a 128-byte atomic write: partial reads are expected. +It contains no pipe, descriptor, real clock, process or target interface. Live +descriptor inheritance, close ordering, nonblocking reads and a hard deadline +must be bound to reviewed public source before a channel implementation can be +considered. diff --git a/docs/retroarch/phase-1.0as-offline-channel-primitive-audit.md b/docs/retroarch/phase-1.0as-offline-channel-primitive-audit.md new file mode 100644 index 0000000..eff5e32 --- /dev/null +++ b/docs/retroarch/phase-1.0as-offline-channel-primitive-audit.md @@ -0,0 +1,21 @@ +# Phase 1.0AS: offline channel primitive and FD ownership audit + +Status: `PIPE_POLL_CANDIDATES_PRESENT_FD_INHERITANCE_AND_HARD_DEADLINE_BLOCKED` + +Date: 2026-07-29 + +Current official SDK declarations and shsrv call sites make `pipe` and `poll` +public-source candidates. The shsrv telnet loop uses a short relative poll, but +does not compose it with an absolute monotonic deadline, EINTR budget +recalculation or bounded worker reap. + +The decisive ownership mismatch is in worker creation. The official header +defines `RFFDG` as copying the descriptor table and `RFCFDG` as closing every +descriptor. Current shsrv workers use `RFCFDG`; a pre-created result pipe would +therefore not be inherited. No reviewed worker path uses `RFFDG` with exclusive +parent/child pipe-end closure or close-on-exec handling. + +Headers alone do not prove that a new composition is safe on firmware 9.60. +The next phase may model FD acquisition, inheritance, close ordering, EINTR and +an absolute deadline using fake operations only. No live channel, target source, +build or device action is authorized. diff --git a/docs/retroarch/phase-1.0at-offline-fd-deadline-model.md b/docs/retroarch/phase-1.0at-offline-fd-deadline-model.md new file mode 100644 index 0000000..887932a --- /dev/null +++ b/docs/retroarch/phase-1.0at-offline-fd-deadline-model.md @@ -0,0 +1,20 @@ +# Phase 1.0AT: offline RFFDG FD/deadline model + +Status: `OFFLINE_OWNERSHIP_MODEL_COMPLETE_LIVE_COMPOSITION_BLOCKED` + +Date: 2026-07-29 + +This phase composes the Phase-1.0AS source candidates in a fake-only ownership +transaction. A pipe exists before the modeled `RFFDG` worker copy. The parent +closes its write end, the worker closes its read end, and the parent assembles +one exact Phase-1.0AQ record through a nonblocking partial-read model. + +One 256-tick absolute budget covers setup, EINTR, would-block and data events. +Deadline, early EOF, overflow, a damaged record or any trailing read event +contains the worker. Every acquired pipe end is closed and every started worker +is terminated and reaped; a cleanup failure is itself a hard model error. + +The facade is scripted and imports no OS, process, clock, network or filesystem +capability. It does not establish a real `RFFDG` composition, hard wall-clock +preemption, bounded real reap or firmware-9.60 behavior. No target source, +cross-build, connection, transfer or execution is authorized. diff --git a/docs/retroarch/phase-1.0au-live-channel-feasibility.md b/docs/retroarch/phase-1.0au-live-channel-feasibility.md new file mode 100644 index 0000000..e5079e8 --- /dev/null +++ b/docs/retroarch/phase-1.0au-live-channel-feasibility.md @@ -0,0 +1,21 @@ +# Phase 1.0AU: offline live result-channel feasibility reassessment + +Status: `PRIMITIVE_SIGNATURES_COMPLETE_COMPOSITION_AND_RUNTIME_BLOCKED` + +Date: 2026-07-29 + +The current official SDK headers expose the complete source-level vocabulary +needed by the Phase-1.0AT model: `pipe`, `RFFDG`, `fcntl` with `O_NONBLOCK`, +`poll`, `CLOCK_MONOTONIC`, `kill`, and `waitpid` with `WNOHANG`. + +No audited official callsite composes that vocabulary into the required +channel. Current shsrv builtin workers use `RFCFDG`, which closes pre-created +descriptors. Its waits are not bounded by one absolute monotonic deadline and +the service has an automatic restart loop. Direct code reuse would violate the +one-shot, no-retry and explicit-supervisor boundaries. + +Headers are declarations, not firmware-9.60 runtime evidence. The AT fake +model is not proof of hard preemption, real descriptor cleanup, launch context, +VideoOut ownership or a visible flip. A live or target implementation remains +blocked. The next permitted step is only an offline, target-free canary +contract with a distinct post-result terminal state. diff --git a/docs/retroarch/phase-1.0av-launch-context-canary-contract.md b/docs/retroarch/phase-1.0av-launch-context-canary-contract.md new file mode 100644 index 0000000..832272e --- /dev/null +++ b/docs/retroarch/phase-1.0av-launch-context-canary-contract.md @@ -0,0 +1,28 @@ +# Phase 1.0AV: target-free launch-context canary contract + +Status: `OFFLINE_CAUSAL_PAIR_CONTRACT_COMPLETE_NO_ARTIFACT_OR_AUTHORITY` + +Date: 2026-07-29 + +AV defines the minimum causal comparison for the launch-context hypothesis. +Both arms must use the same exact future payload and `CHD10AV1` protocol. One +arm uses raw elfldr and the other a separately identified BigApp candidate. +Launcher hashes, run IDs and approval hashes must differ; neither approval can +carry to the other arm. Retry, reconnect, resume, installation and autoload are +forbidden. + +Each supplied result must prove submit, then D04, then a distinct later cleanup +terminal. Missing or early terminal state makes the pair incomparable. Identity +drift, a retry, a persistent-write count or an unscoped visible-output claim is +a hard contract failure. Graphics operations have a separate closed approval +gate and are not conflated with persistent writes. + +A changed submit return is classified only as a launch-context candidate. A +zero return is not interpreted as a visible flip, correct VideoOut semantics or +root cause. This module processes synthetic immutable values only. There is no +target source, artifact, address, transport, activation or device authority. + +Before any implementation can be considered, a separate offline audit must +bind the exact source delta, post-D04 terminal, launcher effects and result-path +feasibility. BigApp termination requires its own explicit artifact-specific +approval and is currently false. diff --git a/docs/retroarch/phase-1.0aw-canary-source-delta-audit.md b/docs/retroarch/phase-1.0aw-canary-source-delta-audit.md new file mode 100644 index 0000000..8cf9c66 --- /dev/null +++ b/docs/retroarch/phase-1.0aw-canary-source-delta-audit.md @@ -0,0 +1,28 @@ +# Phase 1.0AW: offline canary source-delta and effect audit + +Status: `SOURCE_DELTA_BOUNDED_RESULT_PATH_CANDIDATE_EFFECTS_BLOCK_ARTIFACT` + +Date: 2026-07-29 + +The historical Phase-1.0M bytes are absent and implement `CHD10J01`, where +D12 is terminal and D04 can follow it. They cannot satisfy AV and cannot be +reused. A new profile must build one ELF for both launch arms; launcher identity +must never become a compile define or payload-byte difference. + +The smallest source design adds `CHD10AV1` and D14. Submit-before-D04 ordering +is retained. D12 becomes shutdown-request evidence only. D14 may be emitted +once, through inherited stdout, only after `rarch_main` returns, S15 is set, +the initialized mask is empty, cleanup ordering has no errors and a new cleanup +failure counter is zero. The counter is necessary because the current +first-error field can retain E106 and mask a later E122 cleanup failure. + +Official shsrv v0.7 supplies a source candidate for the other arm: hbldr passes +stdout to `elfldr_exec`, which duplicates it into the replaced BigApp process, +and the v0.7 shell framing is raw. This does not prove a live binary channel. +The deployed identity is unknown and host completion remains unimplemented. + +The same v0.7 route reads a device-resident ELF, kills the running BigApp, +launches PPSA01659, waits indefinitely for child discovery, mutates kernel and +ptrace state, and changes root/jail state. Direct reuse is rejected. No source +was changed, no artifact was built, and no device action is authorized. Only a +host-tested, non-target source design may follow. diff --git a/docs/retroarch/phase-1.0ax-canary-protocol-model.md b/docs/retroarch/phase-1.0ax-canary-protocol-model.md new file mode 100644 index 0000000..bf3e37f --- /dev/null +++ b/docs/retroarch/phase-1.0ax-canary-protocol-model.md @@ -0,0 +1,24 @@ +# Phase 1.0AX: host-only AV protocol and cleanup model + +Status: `HOST_REFERENCE_MODEL_COMPLETE_TARGET_SOURCE_AND_ARTIFACT_BLOCKED` + +Date: 2026-07-29 + +AX turns AW's source requirements into an exact bytes-only reference model. +Frames remain 64-byte, big-endian and CRC-bound over the first 60 bytes, but +use `CHD10AV1`. Historical wire values are preserved and D14 is appended at +stage value 30. D12 cannot carry the terminal flag; only a final D14 can. + +D14 records the current initialized mask, cleaned mask, `rarch_main` result, +cleanup-order errors and the independent cleanup-failure count. It is generated +only when `rarch_main` returned, D04 was emitted, phase S15 was reached, no +subsystem remains initialized and both cleanup error counts are zero. + +The trace layer requires exactly one D07, D04 and final D14 in that order, +strictly increasing sequence values, at most one D12 and no post-terminal data. +All 64 single-byte mutations fail CRC or canonical parsing. Correctly +rechecksummed but false cleanup claims also fail. + +This is Python byte processing only. It creates no target profile, source, +artifact, socket, process, output file, address or authorization. Passing it +proves neither target equivalence nor firmware behavior. diff --git a/docs/retroarch/phase-1.0ay-target-source-base.md b/docs/retroarch/phase-1.0ay-target-source-base.md new file mode 100644 index 0000000..8100680 --- /dev/null +++ b/docs/retroarch/phase-1.0ay-target-source-base.md @@ -0,0 +1,20 @@ +# Phase 1.0AY: offline RetroArch target-source base reassessment + +Status: `EXACT_PHASE10N_BASE_SELECTED_HOST_SOURCE_PATCH_ONLY` + +Date: 2026-07-29 + +The local primary RetroArch checkout remains on the historical loader-diagnosis +branch. It is not switched or rewritten. The exact source base selected for the +next isolated worktree is private commit `606909706f91d7213751c245081333f56c2cce89`, +the inactive Phase-1.0N runner tip. + +That commit descends from Phase-1.0M, contains its artifact audit and preserves +all target-source blobs relevant to AX byte-for-byte. Its Makefile difference +adds only inactive N runner validation. This makes it the least-lossy base and +avoids replaying the completed M/N history onto the older checkout. + +The next patch is limited to host-tested source structure: cleanup-failure +accounting, `CHD10AV1`, D14 and its cleanup predicate. It may not add a target +profile, cross-build, artifact, launcher-specific define or runner activation. +Every hardware and build authorization remains false. diff --git a/docs/retroarch/phase-1.0az-host-av-source.md b/docs/retroarch/phase-1.0az-host-av-source.md new file mode 100644 index 0000000..48d2c13 --- /dev/null +++ b/docs/retroarch/phase-1.0az-host-av-source.md @@ -0,0 +1,30 @@ +# Phase 1.0AZ: host-tested AV source structure + +Status: `REMOTE_BOUND_HOST_TESTED_SOURCE_TARGET_REASSESSMENT_ONLY` + +Date: 2026-07-29 + +An isolated RetroArch worktree was created from the exact Phase-1.0N tip and +committed locally as `a3887edd90780426b3762d42b8e3b96f8ca6813d`. The change +implements the source structure selected by AY without creating a PS5 build +profile or artifact. + +The distinct stream magic is `CHD10AV1`; D14 is appended as stage 30 without +renumbering historical stages. Under the AV host define, D11 and D12 are +nonterminal. D14 is the sole terminal and is attempted at most once only after +a successfully written D04, reported `rarch_main` return, S15, an empty +initialized mask, zero cleanup-order errors and zero cleanup failures. +Duplicate or failed cleanup is counted independently from the sticky first +error, so an earlier runtime error cannot mask cleanup failure. + +The new ASan/UBSan host test passed, as did every C host test and the existing +Windows-side Phase-1.0I/J/K/M/N and structural validators. The combined WSL +target stopped only when an older validator invoked `git ls-files`: a linked +Windows worktree stores a Windows-absolute gitdir that WSL Git cannot resolve. + +The inherited HTTPS remote first failed authentication. The repository was then +pushed through the same private Gitea SSH route already configured for Chimera +GFX, and `origin/codex/ps5-phase10az-host-av-source` was fetched back as the +exact commit above. Remote source binding is therefore complete. Only an +offline target-profile reassessment may follow; no target build, ELF, +connection, transfer, execution or device action is authorized. diff --git a/docs/retroarch/phase-1.0b-device-risk-assessment.md b/docs/retroarch/phase-1.0b-device-risk-assessment.md new file mode 100644 index 0000000..c5781c6 --- /dev/null +++ b/docs/retroarch/phase-1.0b-device-risk-assessment.md @@ -0,0 +1,49 @@ +# Phase 1.0B device risk assessment + +Overall classification: +`RETROARCH_PS5_DEVICE_SMOKE_CANDIDATE_BUILT_WITH_DECLARED_RISKS`. + +## Hard boundaries satisfied offline + +- no PS5 connection, request, transfer or execution occurred; +- no installation, autoload, live replacement, sender or retry exists; +- no content/config file is required; +- persistent write attempts fail closed; +- the run requests shutdown at 60 seconds or 3,600 frames; +- Options held for two seconds provides an additional shutdown; +- the final ELF contains no SceNet, GNM, module-loader, installer, mount, + reboot, console-shutdown or other-payload launch import; +- SDL's dynamic API and runtime assertion-exit path are disabled, leaving one + smoke-owned post-teardown `_Exit` callsite; +- the primary ELF contains no permanent W+X load segment. + +The strings audit still sees shared RetroArch localization text for disabled +network, autoload and reboot menu entries, plus the generic +`udp://127.0.0.1:` template. These are inert data findings, not hidden +functionality: the complete undefined-symbol inventory has no networking +primitive, and no RetroArch network callgraph is linked. SDK runtime-loader +symbol-name strings remain separately because the pinned CRT resolves its +own loader at runtime. + +## Declared device-test risks + +| Risk | Class | Bounded mitigation | Residual | +|---|---|---|---| +| SDK CRT temporary kernel/process patches before `main` | session/integrity | exact SDK v0.41 source and controlled runtime identity | firmware-9.60 effect unobserved | +| SDK CRT runtime loader | session/initialization | exact source and DT_NEEDED inventory; no RetroArch dynamic-core path | CRT resolves `sceKernelLoadStartModule` at runtime and can conditionally load `libSceSysmodule`; this is not visible as an ELF import | +| `sceAudioOutOutput` is blocking | session hang | SDL worker, error counters, outer 60 s policy | no public timeout ABI; thread/join can still block | +| VideoOut/Pad/AudioOut open calls lack an evidenced timeout variant | session hang | single attempt, no retry, partial cleanup | underlying call can outlive outer policy | +| direct-memory mapping has no separately evidenced unmap in pinned SDL headers | cleanup | release direct memory and process exit | mapping cleanup before exit is incomplete/unproven | +| new RX/R/RW layout | child/session crash | source-bound loader flag handling and relocation audit | device behavior unobserved | +| `_Exit` in injected child | session termination | one smoke-owned call after teardown; SDL secondary exit paths compiled out | exact firmware behavior unobserved; SDK init-failure fallback still owns its separate `exit` path | +| write firewall relies partly on link wrapping | integrity | VFS guard plus import/disassembly/callgraph audit | libc-internal behavior is not intercepted | +| controller mapping differs from label conventions | operability | source-bound Options-to-SDL-Back-to-RetroArch-Select chain | on-device button observation absent | + +These are not brick-safety guarantees. A later operator must explicitly +accept possible black screen, missing audio/input, process crash, lost +jailbreak/host session, forced manual reboot after a hang, and no diagnostic +after video teardown. + +The missing byte-exact Payload Manager backup remains a separate installation +blocker. This candidate does not alter that gate because it is not an +installation proposal. diff --git a/docs/retroarch/phase-1.0b-linker-and-wx-analysis.md b/docs/retroarch/phase-1.0b-linker-and-wx-analysis.md new file mode 100644 index 0000000..8076d4b --- /dev/null +++ b/docs/retroarch/phase-1.0b-linker-and-wx-analysis.md @@ -0,0 +1,62 @@ +# Phase 1.0B linker and W^X analysis + +## SDK baseline + +PS5 Payload SDK v0.41 `ldscripts/elf_x86_64.x:20-25` emits three load +segments but deliberately marks text `0x7` (RWE), rodata `0x6` (RW) and data +`0x6` (RW). The Phase-1.0A software ELF consequently has a permanent RWE +text segment. + +## Loader evidence + +Both public elfldr `699e8bcff03e91e8d6ca6eba281af25c5a58d8c2` +and hardened elfldr `197623058f509eddde18868dafcb92fdcac66464`: + +- enumerate every `PT_LOAD`; +- initially reserve one RW image mapping; +- copy all segments and apply `R_X86_64_RELATIVE` relocations in the host + mirror; +- copy the completed image to the child; +- translate every `p_flags` bit through `PFLAGS`; +- use `kernel_mprotect` for executable segments and `pt_mprotect` otherwise. + +Relevant hardened source is `elfldr.c:54-56,144-263`. This supports multiple +load segments and a temporary relocation-write phase followed by final +per-segment protection. + +## Smoke layout + +The smoke-only `pkg/ps5/elf_x86_64_smoke_wx.x` preserves the SDK symbols and +0x4000 page alignment while arranging: + +- `.text` and `.plt`: RX; +- unwind tables and constant `.rodata`: R; +- dynamic symbol/hash/string/relocation tables, `.data.rel.ro`, GOT, + init/fini arrays, `.dynamic`, `.data` and `.bss`: RW. + +Runtime-relocated GOT and related metadata deliberately remain writable. +The final program-header audit must show no segment with both W and E. All +`R_X86_64_RELATIVE` destinations must lie in RW load ranges. The primary +candidate is accepted only if both checks pass twice byte-identically. + +## RELRO and self-modification + +The pinned SDK/loader contract does not implement a post-CRT RELRO transition. +Marking the GOT read-only before SDK runtime linking would be unsafe, so no +RELRO claim is made. No self-modifying RetroArch/smokecore code, JIT or +dynamic core loader is enabled or reachable. No production elfldr code was +changed. + +## Residual risk + +W^X is statically established, but the exact protected layout has not run on +firmware 9.60. Loader protection errors, an SDK runtime write to an +unexpected R-only section, or firmware-specific mapping behavior could end +the child/session. No persistent installation effect follows from the +artifact design, but that claim also remains unobserved. + +The SDK CRT's statically linked runtime loader is present independently of +RetroArch's feature set and may conditionally load `libSceSysmodule`. +The final import list has no module-loader symbol, but absent imports alone +are not used as proof that startup has no module effects; the CRT source and +startup disassembly are the controlling evidence. diff --git a/docs/retroarch/phase-1.0b-persistent-write-audit.md b/docs/retroarch/phase-1.0b-persistent-write-audit.md new file mode 100644 index 0000000..536e426 --- /dev/null +++ b/docs/retroarch/phase-1.0b-persistent-write-audit.md @@ -0,0 +1,47 @@ +# Phase 1.0B persistent-write audit + +Conclusion: persistent device writes are fail-closed in the selected smoke +paths; device execution remains unauthorized. + +## Layers + +1. The PS5 frontend blocks config reads, supplies no content/config/SRAM/state + path and selects a static no-content core. +2. RetroArch VFS accepts only `RETRO_VFS_FILE_ACCESS_READ`. Write, truncate, + remove, rename and mkdir return failure and trip `E118`. +3. Link-time wrappers reject write-capable `open/openat`, non-read-only + `fopen`, non-stdio `write/fwrite`, and all create, temp, rename, unlink, + remove, mkdir, rmdir, chmod, chown and truncate calls. +4. A rejected operation returns `EROFS`, stores the first operation in memory, + requests shutdown once and never retries. + +`stdout` and `stderr` writes remain allowed because the SDK CRT/RetroArch can +already use those process streams. They are not file-output authorization. +The ELF imports read-only filesystem primitives (`open`, `fopen`, `read`, +`fread`, `stat`, directory iteration); the frontend has no content/config +path, but normal runtime/module implementation can still use read primitives. + +## Static reachability result + +Final link garbage collection removes wrapper bodies for operations with no +remaining caller. Reachable application calls to `open`, `fopen` and `fwrite` +resolve to `__wrap_open`, `__wrap_fopen` and `__wrap_fwrite`; their only +forward edges are read-only opens or stdout/stderr. The underlying libc +imports remain because accepted paths forward to them. + +No final import is present for `rename`, `unlink`, `remove`, `mkdir`, `chmod`, +`chown`, `ftruncate`, `mkstemp`, `tmpfile` or `freopen`. Static absence alone +is not the safety claim: the VFS guards, wrappers, linker map and disassembly +together provide the offline evidence. + +## Explicit exclusions + +The profile disables or provides no route for config save, playlist/history, +SRAM, savestate, screenshot, recording, shader cache, autoconfig, remap, +database, temporary file, installation and autoload. It contains no device +path, IP address, sender, transfer or installation package. + +Limit: libc internals invoked inside an imported function are not intercepted +by linker wrapping. No application path can supply a writable file stream +because writable open modes are blocked, but this remains static reasoning, +not observed firmware behavior. diff --git a/docs/retroarch/phase-1.0b-proposed-one-shot-test.md b/docs/retroarch/phase-1.0b-proposed-one-shot-test.md new file mode 100644 index 0000000..84b9433 --- /dev/null +++ b/docs/retroarch/phase-1.0b-proposed-one-shot-test.md @@ -0,0 +1,49 @@ +# Proposed Phase 1.0B one-shot test + +Status: proposal only. Transfer and execution are not authorized. + +## Preconditions for a later review + +- bind approval to the complete ELF SHA-256, size, source commit and manifest + SHA-256; +- operator independently confirms firmware 9.60 in the PS5 UI; +- autoload remains disabled and no live path is selected or replaced; +- exactly one transfer and one execution are separately authorized; +- no retry, reinstall, backup, recovery, configuration or second payload is + combined with the window; +- operator accepts all risks in the device-risk assessment. + +## Expected visible sequence + +1. A software framebuffer appears with `CHIMERA RETROARCH PS5 SMOKE TEST`. +2. Initialization can pass rapidly through `S01`-`S06`; these phases are not + guaranteed to receive their own visible frame. +3. `S07` shows increasing monotonic time/frame count, VideoOut/flip index, + 320 x 240 source with destination pitch/dimensions, XRGB8888 format code, + controller buttons/sticks, AudioOut counters, core state and `E000`. +4. Holding Options for two seconds shows hold progress and requests `S08`. + Without input, 60,000 ms or 3,600 frames requests the same bounded path. +5. The final visible frame can show `S08` and its shutdown reason. +6. `S09`-`S15` run after the last present; `_Exit` ends the child. No visible + completion frame is promised. + +## Operator stop criteria + +Do not retry. Stop the window and record the last visible phase/error when: + +- no image appears; +- framebuffer bounds are `FAIL`; +- any error other than `E000` appears; +- flip count stops advancing; +- input state is implausible or Options does not progress; +- AudioOut submit time grows unexpectedly or frames stop; +- the screen freezes beyond 60 seconds; +- RetroArch exits before the expected phase; +- the jailbreak/host session disappears; +- any write-firewall trip is shown; +- any unexpected notification, install, autoload, network or filesystem + behavior is observed. + +If the child does not exit, the proposal permits no automated kill, retry or +reboot command. The operator may later decide manually whether a console +reboot is necessary, outside the authorization window. diff --git a/docs/retroarch/phase-1.0b-runtime-and-exit-contract.md b/docs/retroarch/phase-1.0b-runtime-and-exit-contract.md new file mode 100644 index 0000000..f5cd3a9 --- /dev/null +++ b/docs/retroarch/phase-1.0b-runtime-and-exit-contract.md @@ -0,0 +1,65 @@ +# Phase 1.0B runtime and exit contract + +Status: source-bound offline contract; firmware-9.60 behavior remains +unproven until a separately authorized one-shot run. + +## Stateful lifecycle + +The status record tracks platform, frontend, SDL, video, input, audio, core, +threads, buffers, direct memory and device handles. Initialization records +each subsystem once in a bounded stack. Cleanup may touch only an initialized +bit, runs once, and verifies reverse order. An order violation is sticky +`E120`, duplicate init is `E119`, duplicate cleanup is `E121`, and a cleanup +failure is `E122`. Host fault injection covers every partial-init boundary, +every per-subsystem cleanup failure and an out-of-order cleanup. + +Video cleanup stops future flips, destroys the software surface and event +queue, closes VideoOut, releases direct memory, then clears handles. Input +cleanup closes Pad before its state is cleared. Audio cleanup stops the SDL +worker before AudioOut close and records the elapsed cleanup time. The pinned +public SDL source exposes release of direct memory but no separately evidenced +direct-memory unmap primitive; this remains a declared cleanup uncertainty. + +## Exit alternatives + +| Option | Exact evidence | Destructors/cleanup | Continuation | Decision | +|---|---|---|---|---| +| return from `main` | SDK `crt/crt.c:133-146,153-170,196-220` | SDK `.fini` runs | a hijacked process returns through the synthetic address placed by elfldr | rejected | +| `payload_exit` | SDK `crt/crt.c:174-180`, `payload.h:45-51` | long-jumps into CRT | still reaches `payload_terminate`; hijacked route returns | rejected | +| libc `exit` | imported and used by SDK `payload_terminate` only for a non-hijacked process | libc exit semantics; would bypass the remaining SDK payload path when called directly | process termination expected | not selected | +| libc `_Exit` | public libc import in the final ELF; direct call from smoke `main` | all RetroArch/SDL cleanup runs first; no process destructors after call | no synthetic return | selected | + +The selected method is compile-time +`CHIMERA_PS5_EXIT_PROCESS_EXIT=1`. After `rarch_main` completes, +`main` sets `S15` and reaches one smoke-owned `_Exit(result)` call. +The smoke SDL overlay disables the dynamic API and sets +`SDL_ASSERT_LEVEL=0`, removing SDL's secondary `SDL_ExitProcess` callsites. +Disassembly and the undefined-symbol inventory bind this decision. It does +not reboot, launch another process, retry or write a device file. The SDK CRT +still imports `exit` for its own pre-`main` initialization-failure +termination; that separate startup path is not represented as the selected +smoke exit. + +## Loader relationship + +Hardened elfldr commit `197623058f509eddde18868dafcb92fdcac66464` +spawns a separate child, writes the pre-injection RIP to `rsp-8`, enters the +payload at `_start`, and on the controlled route waits under a bounded +watchdog (`elfldr.c:381-410,643-768`; +`socksrv.c:128-157`; `ps5_controlled.c:186-215`). `_Exit` terminates that +child instead of resuming the synthetic continuation. + +This is stronger source evidence than returning, but it is not device +evidence. Remaining exit risks are: + +- SDK CRT initialization and temporary runtime patches occur before `main`; +- SDK CRT startup resolves runtime-loader functions and may conditionally + load `libSceSysmodule` before entering RetroArch; +- firmware-9.60 libc `_Exit` behavior in this exact injected child is + unobserved; +- a blocked AudioOut call or SDL join can prevent reaching `_Exit`; +- the controlled loader route itself is not authorized or exercised here; +- ending the child can end the current host/jailbreak session, which is a + session risk rather than an installation guarantee. + +No execution-review bundle has been produced. diff --git a/docs/retroarch/phase-1.0b-smoke-candidate-design.md b/docs/retroarch/phase-1.0b-smoke-candidate-design.md new file mode 100644 index 0000000..f4067cc --- /dev/null +++ b/docs/retroarch/phase-1.0b-smoke-candidate-design.md @@ -0,0 +1,81 @@ +# Phase 1.0B bounded RetroArch smoke candidate + +Status: `BUILT_WITH_DECLARED_RISKS`; offline evidence only. Transfer, +installation and execution are not authorized. + +## Scope + +The candidate is the real RetroArch v1.22.2 frontend and runloop from the +separate `chimera-retroarch` fork. It statically binds `chimera_smokecore`, +uses the pinned PS5 SDL2 software framebuffer, and initializes VideoOut, one +Pad device and stereo AudioOut. It requires neither content nor a +configuration file. It is not a mini-frontend and contains no dynamic core. +The permitted existing smokecore software image is selected, so RGUI/menu +and their content browser are compiled out of this bounded variant. + +The logical profile is `ps5-software-rgui-smoke-bounded`; the make selector is +`PS5_PROFILE=software-smoke`, and the output label is +`retroarch_ps5_software_smoke.elf`. + +## Compile-time closure + +Enabled: + +- real RetroArch frontend and runloop; +- static Chimera smokecore, 320 x 240 XRGB8888 at 60 fps; +- SDL2 software video with double-buffered PS5 VideoOut; +- one SDL2 PS5 Pad and 15% deadzone; +- SDL2 PS5 48 kHz stereo AudioOut; +- on-screen bounded status record; +- 60,000 ms and 3,600-frame shutdown gates; +- post-teardown process `_Exit`; +- VFS and link-time libc write firewalls. + +Disabled or garbage-collected from the final ELF: + +- dynamic cores, content browser data, playlists/history, databases; +- networking, updater, achievements and command interfaces; +- screenshots, recording, shaders, filters and compressed content; +- SRAM, savestates, remap/autoconfig/config persistence; +- keyboard, IME, mouse, sensor, haptic, rumble and lightbar paths; +- GNM, OpenGL, Vulkan, RetroArch dynamic-core/module loading, installation, + autoload and retry. + +SDL's dynamic API and runtime assertions are disabled in its smoke-specific +static build. The pinned SDK CRT remains a necessary startup component: it +resolves its own runtime loader primitives and may conditionally load +`libSceSysmodule`. This source-bound startup behavior is declared separately +and is not confused with a RetroArch dynamic-core path. + +`HAVE_CONFIGFILE=0`, `HAVE_MENU=0` and `HAVE_RGUI=0` remove their read, +browser and save paths. Independently, the PS5 frontend sets +`RARCH_CTL_SET_BLOCK_CONFIG_READ`, provides no config path, and the smoke +firewall rejects every attempted write. + +## Visible record + +The static core draws `CHIMERA RETROARCH PS5 SMOKE TEST`, a build/manifest +label, phase, monotonic elapsed time, frame count, VideoOut and framebuffer +details, raw button/stick state, AudioOut counters, core state, shutdown +reason, first sticky error, write-firewall state and both hard limits. + +Phases are stable `S00` through `S15`; errors are stable `E000` and +`E101` through `E123`. `S07 RUNNING` and a final rendered +`S08 SHUTDOWN_REQUESTED` are expected on screen. `S09` through `S15` occur +during teardown after the final present and therefore remain in the +in-memory record rather than being promised as visible frames. + +## Bounded control + +The first condition wins: + +1. Options held for 2,000 ms; +2. 60,000 monotonic milliseconds; +3. 3,600 core frames; +4. a sticky runtime/write-firewall failure. + +The pinned SDL mapping maps the PS5 Options bit to SDL Back, which RetroArch +exposes to the core as `RETRO_DEVICE_ID_JOYPAD_SELECT`. Shutdown is requested +once. There is no retry, second run or automatic extension. + +No host test or static audit in this phase is hardware evidence. diff --git a/docs/retroarch/phase-1.0ba-target-profile-callsite-audit.md b/docs/retroarch/phase-1.0ba-target-profile-callsite-audit.md new file mode 100644 index 0000000..2f2649e --- /dev/null +++ b/docs/retroarch/phase-1.0ba-target-profile-callsite-audit.md @@ -0,0 +1,24 @@ +# Phase 1.0BA: offline AV target-profile and callsite audit + +Status: `EXACT_CALLSITE_AND_PROFILE_DELTA_SOURCE_PATCH_ALLOWED_NO_BUILD` + +Date: 2026-07-29 + +The remote-bound AZ commit leaves one exact integration point in `main`: after +`rarch_main` returns and S15 is set, but before `_Exit`. On the normal PS5 loop +return, `rarch_main` has already called `main_exit`; the PS5 frontend deinit +then removes its SDL, frontend and platform initialization markers. Early +initialization returns that bypass `main_exit` retain initialized bits and the +AZ predicate therefore rejects D14. + +The smallest profile is a derivative of `write-diag`: it retains the +contentless `-v` startup correction, write firewall, D13 operation evidence, +bounded runtime, inherited stdout, SDL diagnostic overlay and frame-zero +source. The only new target define is `CHIMERA_PS5_AV_DIAG`; its stream magic +takes precedence over the inherited J selection. Launcher identity remains +outside the payload so both future launch arms use one byte-exact ELF. + +The next source commit may add the call and a distinct `launch-canary` profile, +plus host/static tests. This audit does not permit a cross-build, ELF, runner +activation, connection, transfer, execution or PS5 action. A subsequent exact +source audit must authorize any artifact build separately. diff --git a/docs/retroarch/phase-1.0bb-source-only-launch-canary-profile.md b/docs/retroarch/phase-1.0bb-source-only-launch-canary-profile.md new file mode 100644 index 0000000..a5e8968 --- /dev/null +++ b/docs/retroarch/phase-1.0bb-source-only-launch-canary-profile.md @@ -0,0 +1,21 @@ +# Phase 1.0BB: source-only launch-canary profile + +Status: `REMOTE_BOUND_SOURCE_PROFILE_COMPLETE_BUILD_PREREQUISITE_AUDIT_ONLY` + +Date: 2026-07-29 + +RetroArch commit `b0f5178e5acb59172fb352098d2ba32e5c42831b` adds the +BA-authorized post-S15/pre-`_Exit` terminal call and a distinct +`launch-canary` profile. The profile inherits write-diag startup, SDL overlay, +write firewall, inherited stdout and bounded runtime behavior. No launcher +identity enters the payload bytes. + +All host C/sanitizer tests, existing parser/runner tests, historical source +validators, the BB mutation tests and PS5 structural validation passed. No +launch-canary ELF or map was produced. The source commit was pushed to and +read from the private Gitea origin. + +Only an offline prerequisite audit may now determine whether the exact pinned +SDK, SDL archive, linker script, write wrappers and expected object graph are +present and hashable. This phase does not authorize a cross-build, artifact, +runner activation or device action. diff --git a/docs/retroarch/phase-1.0bc-cross-build-prerequisite-audit.md b/docs/retroarch/phase-1.0bc-cross-build-prerequisite-audit.md new file mode 100644 index 0000000..5aa12cb --- /dev/null +++ b/docs/retroarch/phase-1.0bc-cross-build-prerequisite-audit.md @@ -0,0 +1,22 @@ +# Phase 1.0BC: offline launch-canary cross-build prerequisite audit + +Status: `PREREQUISITES_PARTIAL_EXACT_SDL_MATERIALIZATION_REQUIRED` + +Date: 2026-07-29 + +The installed public SDK v0.41, clean public SDL commit, reviewed diagnostic +patch and W^X linker script are present and hashable. The SDK release remains +bound to the upstream lock and the local wrapper resolves to Ubuntu Clang +18.1.8. + +The isolated RetroArch worktree has no `build/ps5-sdl2-write-diag/libSDL2.a`. +No matching archive exists elsewhere in the declared Chimera project scope. +WSL also has no CMake or Ninja command at this observation. Reusing an unknown +archive or silently selecting the unpatched SDL tree is forbidden. + +The next phase may add and host-test a target-free materializer that accepts +only SDL commit `0baf4ac49382b537ba449901b5b6d0d189bb1fbb`, patch SHA-256 +`aa1d3ebc...70c193` and the locked SDK. It must create outputs only in ignored +build storage and must stop before compiling RetroArch. This phase authorizes +neither SDL compilation nor a launch-canary cross-build, artifact or device +action. diff --git a/docs/retroarch/phase-1.0bd-dormant-sdl-materializer-policy.md b/docs/retroarch/phase-1.0bd-dormant-sdl-materializer-policy.md new file mode 100644 index 0000000..0e925fc --- /dev/null +++ b/docs/retroarch/phase-1.0bd-dormant-sdl-materializer-policy.md @@ -0,0 +1,19 @@ +# Phase 1.0BD: dormant exact SDL materializer policy + +Status: `REMOTE_BOUND_POLICY_COMPLETE_ADAPTER_SOURCE_ONLY` + +Date: 2026-07-29 + +RetroArch commit `c003c503ff0b747bd161c75ad7329a62d3880da0` adds a pure +policy for the missing SDL archive. It accepts only the BC-bound SDL commit, +diagnostic patch and SDK file hashes, requires clean and absent output state, +and returns one immutable Ninja/Release/SDL2-static plan affecting exactly the +three reviewed PS5 SDL files. + +The module has no process, network, clock, filesystem adapter or CLI. It cannot +materialize or compile anything, and its plan excludes both RetroArch building +and device actions. Static validation, identity mutations, prerequisite +mutations and the complete existing host matrix pass. + +The next phase may implement an injected host adapter and fake-adapter tests. +It may not yet invoke CMake/Ninja, build SDL or RetroArch, or act on a PS5. diff --git a/docs/retroarch/phase-1.0be-fake-only-sdl-materializer.md b/docs/retroarch/phase-1.0be-fake-only-sdl-materializer.md new file mode 100644 index 0000000..48e4734 --- /dev/null +++ b/docs/retroarch/phase-1.0be-fake-only-sdl-materializer.md @@ -0,0 +1,15 @@ +# Phase 1.0BE: fake-only SDL materializer adapter + +Status: `REMOTE_BOUND_FAKE_ADAPTER_COMPLETE_LIVE_ADAPTER_REASSESSMENT_ONLY` + +Date: 2026-07-29 + +RetroArch commit `1c874e725d3c4a51ffbc7b21e18b7548ffcf1b97` composes +the BD plan with an exact-type-checked built-in fake adapter. It proves the +identity, absence, detached-stage, patch-check, patch, changed-file, configure, +SDL-only build and archive-verification order. Every synthetic failure stops +before later operations; subclasses and changed-file drift are rejected. + +There is no subprocess, filesystem, network or device capability. Fake success +is not materialization or build evidence. Only an offline live-adapter source +reassessment may follow; SDL/RetroArch builds and device actions remain false. diff --git a/docs/retroarch/phase-1.0bf-live-sdl-adapter-boundary-audit.md b/docs/retroarch/phase-1.0bf-live-sdl-adapter-boundary-audit.md new file mode 100644 index 0000000..ef8f50c --- /dev/null +++ b/docs/retroarch/phase-1.0bf-live-sdl-adapter-boundary-audit.md @@ -0,0 +1,22 @@ +# Phase 1.0BF: offline live SDL adapter boundary audit + +Status: `LIVE_ADAPTER_CONTRACT_BOUND_SOURCE_ONLY_NO_INSTALL_OR_BUILD` + +Date: 2026-07-29 + +The BE operation order can be implemented by a dormant local adapter only if +all paths resolve beneath the declared RetroArch build root, every argv is a +fixed sequence passed without a shell, identity output is bounded, each process +has a hard timeout, and fresh stage/build/archive paths are required. One +failure consumes the attempt; there is no retry, fallback, resume or automatic +cleanup. + +The adapter may know only Git, CMake and Ninja operations needed for the exact +SDL archive. It may not contain apt/package installation, network, RetroArch +compilation, target address, device command or launch behavior. Patch checking +must precede patching, and the changed-file set must equal the three BD paths. + +Local Ubuntu metadata identifies official repository candidates CMake +`4.2.3-2ubuntu2` and ninja-build `1.13.2-1`; neither is installed. This audit +does not authorize installation. It authorizes dormant adapter source with +fake-process tests only and no invocation against the real workspace. diff --git a/docs/retroarch/phase-1.0bg-dormant-sdl-request-compiler.md b/docs/retroarch/phase-1.0bg-dormant-sdl-request-compiler.md new file mode 100644 index 0000000..7ef2596 --- /dev/null +++ b/docs/retroarch/phase-1.0bg-dormant-sdl-request-compiler.md @@ -0,0 +1,15 @@ +# Phase 1.0BG: dormant SDL materializer request compiler + +Status: `REMOTE_BOUND_REQUEST_GRAPH_COMPLETE_EXECUTOR_SOURCE_ONLY` + +Date: 2026-07-29 + +RetroArch commit `de23ac2e1debe8c538b2eb1ff973849ad6892946` converts the +reviewed plan into eight fixed Git/CMake argv tuples. Output paths are confined +below the RetroArch build root; identity output is capped and every request has +a timeout. The graph contains no shell, package manager, RetroArch build, +network, launcher or device operation. + +The component has no process or filesystem executor, so no command ran and no +archive exists. Only bounded executor source with injected fake facades may +follow. Tool installation, materialization and cross-building remain false. diff --git a/docs/retroarch/phase-1.0bh-bounded-sdl-executor.md b/docs/retroarch/phase-1.0bh-bounded-sdl-executor.md new file mode 100644 index 0000000..feb8c90 --- /dev/null +++ b/docs/retroarch/phase-1.0bh-bounded-sdl-executor.md @@ -0,0 +1,16 @@ +# Phase 1.0BH: bounded one-shot SDL executor + +Status: `REMOTE_BOUND_FAKE_FACADE_EXECUTOR_COMPLETE_REAL_FACADE_REASSESSMENT_ONLY` + +Date: 2026-07-29 + +RetroArch commit `aa5002e0794834f737f7a3a55571a29465d877df` adds a +one-shot executor over exact fake process/filesystem facades. Consumption +precedes preflight. Request order, exit status, UTF-8, output limits, source +commit, clean state, changed-file set and final archive presence are enforced. +Every tested success or failure prevents reuse. + +No real subprocess/filesystem implementation or CLI exists and no command was +run. Only a real-facade and official-tool-installation reassessment may follow; +installation, SDL materialization, RetroArch cross-build and device action +remain unauthorized. diff --git a/docs/retroarch/phase-1.0bi-real-facade-and-tool-install-audit.md b/docs/retroarch/phase-1.0bi-real-facade-and-tool-install-audit.md new file mode 100644 index 0000000..ffa088c --- /dev/null +++ b/docs/retroarch/phase-1.0bi-real-facade-and-tool-install-audit.md @@ -0,0 +1,21 @@ +# Phase 1.0BI: real facade and exact host-tool installation audit + +Status: `REAL_FACADE_SOURCE_AND_EXACT_TOOL_INSTALL_ALLOWED_NO_BUILD` + +Date: 2026-07-29 + +The bounded facade may use Linux `Popen` only with `shell=False`, a new process +session, nonblocking merged output, selectors, monotonic time, a per-request +byte cap and process-group termination on timeout/overflow. It may expose only +the eight BG requests and read-only output-existence checks. There is no CLI, +retry, cleanup, package manager, RetroArch operation, network or device path. + +The WSL environment is x86_64 with Python 3.14.4 and noninteractive sudo. +Official Ubuntu resolute metadata binds CMake `4.2.3-2ubuntu2` package SHA-256 +`87c496...becc61` and ninja-build `1.13.2-1` package SHA-256 +`533239...8f156`. Installation of only those exact versions is allowed and +must be verified from dpkg and executable hashes afterward. + +This phase authorizes dormant real-facade source and the exact two-package host +installation. It does not authorize invoking the materializer, compiling SDL +or RetroArch, producing an artifact or acting on a PS5. diff --git a/docs/retroarch/phase-1.0bj-exact-host-tool-install-result.md b/docs/retroarch/phase-1.0bj-exact-host-tool-install-result.md new file mode 100644 index 0000000..59fedd5 --- /dev/null +++ b/docs/retroarch/phase-1.0bj-exact-host-tool-install-result.md @@ -0,0 +1,18 @@ +# Phase 1.0BJ: exact host-tool installation result + +Status: `EXACT_HOST_TOOLS_INSTALLED_REAL_FACADE_DORMANT_NO_BUILD` + +Date: 2026-07-29 + +The single Phase-1.0BI package installation attempt completed successfully in +WSL. `dpkg-query` reports CMake and cmake-data `4.2.3-2ubuntu2` and +ninja-build `1.13.2-1`. The installed executable hashes are recorded in the BJ +manifest so later host work can fail closed if the tools change. + +This is host provenance only. It does not activate the real facade or the +materializer, compile SDL or RetroArch, produce a target artifact, or authorize +any PS5 connection, transfer, installation or execution. + +The same gate produced a dormant real facade at RetroArch commit +`c7511c4f2187794b6dbf56fb2c68b57137424f3b`. Its validation is source-only: +the process and filesystem implementations were not invoked. diff --git a/docs/retroarch/phase-1.0bk-bounded-real-facade-host-fixture-gate.md b/docs/retroarch/phase-1.0bk-bounded-real-facade-host-fixture-gate.md new file mode 100644 index 0000000..56cb46d --- /dev/null +++ b/docs/retroarch/phase-1.0bk-bounded-real-facade-host-fixture-gate.md @@ -0,0 +1,17 @@ +# Phase 1.0BK: bounded real-facade host-fixture gate + +Status: `EXACT_HOST_FIXTURES_AUTHORIZED_NO_MATERIALIZER` + +Date: 2026-07-29 + +Phase 1.0BJ installed and bound the exact host tools and added a dormant real +process/filesystem facade. This gate permits one local WSL test suite against +four exact Python fixtures: success, nonzero exit, output overflow and timeout. +Each fixture may be started once. They have no filesystem, network, SDL, +RetroArch or device operation. The timeout fixture exists solely to prove the +process-group termination path. + +This permission does not accept arbitrary commands and does not activate the +eight materializer requests. It does not authorize an SDL build, a RetroArch +target build, an artifact, a PS5 connection, transfer or execution. The suite +result must be recorded separately before any materializer gate is considered. diff --git a/docs/retroarch/phase-1.0bl-fixture-import-failure-and-new-gate.md b/docs/retroarch/phase-1.0bl-fixture-import-failure-and-new-gate.md new file mode 100644 index 0000000..521dfbd --- /dev/null +++ b/docs/retroarch/phase-1.0bl-fixture-import-failure-and-new-gate.md @@ -0,0 +1,16 @@ +# Phase 1.0BL: fixture import failure and new one-shot gate + +Status: `BK_CONSUMED_BEFORE_FIXTURES_CORRECTED_RUNNER_AUTHORIZED_ONCE` + +Date: 2026-07-29 + +The single BK suite attempt exited with code 1 while importing the first local +`tools` module. Python reported `ModuleNotFoundError: No module named 'tools'`. +The failure occurred before `main`, before construction of the facade and +before any of the four fixture processes. BK is consumed and is not retried. + +The runner now inserts its repository root in `sys.path`; its exact corrected +source and commit are bound below. BL grants one new suite attempt against the +same four BK fixtures. This is a new reviewed host-only permission, not an +automatic retry. No materializer request, SDL/RetroArch build, artifact or PS5 +action is authorized. diff --git a/docs/retroarch/phase-1.0bm-real-facade-fixture-result.md b/docs/retroarch/phase-1.0bm-real-facade-fixture-result.md new file mode 100644 index 0000000..00ce3de --- /dev/null +++ b/docs/retroarch/phase-1.0bm-real-facade-fixture-result.md @@ -0,0 +1,16 @@ +# Phase 1.0BM: real-facade fixture result + +Status: `REAL_FACADE_BOUNDARIES_PROVEN_ON_HOST_DORMANT_COMPOSITION_ALLOWED` + +Date: 2026-07-29 + +The single BL suite attempt completed successfully. Four exact processes were +started once: success returned `0`/`OK\n`, nonzero returned `7`/`E7\n`, the +65-byte output fixture was rejected at the 64-byte cap, and the sleeping +fixture was terminated at its one-second deadline. No materializer request, +SDL or RetroArch build, network operation or device action occurred. + +This host evidence permits source-only implementation of a new exact +one-shot composition between the reviewed request graph and the proven real +facades. The composition must remain dormant. Invocation of any materializer +request and every build or PS5 operation remain unauthorized. diff --git a/docs/retroarch/phase-1.0bn-materializer-preflight-patch-chain-audit.md b/docs/retroarch/phase-1.0bn-materializer-preflight-patch-chain-audit.md new file mode 100644 index 0000000..ffd967f --- /dev/null +++ b/docs/retroarch/phase-1.0bn-materializer-preflight-patch-chain-audit.md @@ -0,0 +1,20 @@ +# Phase 1.0BN: materializer preflight patch-chain audit + +Status: `MATERIALIZER_BLOCKED_PATCH_CHAIN_INCOMPLETE_OFFLINE_REBASE_ALLOWED` + +Date: 2026-07-29 + +The exact SDL commit is clean, the corrected output paths are absent, CMake, +Ninja, GNU patch, the SDK inputs and both patch files are present and hash +bound. The final preflight nevertheless rejects the current request graph: +`sdl2-ps5-early-diag.patch` does not apply directly to the clean SDL commit. +Its contexts assume the smoke-hardening overlay, while BG supplies only the +early-diag patch. The expected changed-file set also omits +`src/dynapi/SDL_dynapi.h`, and the CMake flags are narrower than the historical +reviewed early-diag build. + +No materializer request was invoked and no output path was created. This phase +permits only an offline temporary-worktree rebase of the two existing reviewed +overlays into one exact combined patch, plus source-only correction of the +policy/request/composition contracts. It does not authorize CMake, compilation, +the final materializer, RetroArch, an artifact or a PS5 action. diff --git a/docs/retroarch/phase-1.0bo-patch-chain-remediation-result.md b/docs/retroarch/phase-1.0bo-patch-chain-remediation-result.md new file mode 100644 index 0000000..a5b07a6 --- /dev/null +++ b/docs/retroarch/phase-1.0bo-patch-chain-remediation-result.md @@ -0,0 +1,18 @@ +# Phase 1.0BO: patch-chain remediation result + +Status: `COMPLETE_PATCH_CHAIN_BOUND_RUNNER_SOURCE_ALLOWED_NO_BUILD` + +Date: 2026-07-29 + +The first temporary checkout exposed CRLF and the second was incomplete; both +stopped before a valid overlay result. A third, quiet WSL checkout completed +with LF files. GNU patch `2.8` then passed dry-run and application for the +smoke-hardening overlay followed by the early-diag overlay. The final diff is +clean and changes exactly audio, dynapi, joystick and video. + +The corrected policy now binds both patch hashes and the patch executable. The +request graph performs both dry-run/apply pairs, uses the complete historical +early-diag CMake flags and `--clean-first`, and derives all output paths from +the policy. This phase permits a dormant exact one-shot runner source only. It +does not authorize invoking the materializer, CMake, compilation, RetroArch or +any PS5 operation. diff --git a/docs/retroarch/phase-1.0bp-one-shot-sdl-materialization-gate.md b/docs/retroarch/phase-1.0bp-one-shot-sdl-materialization-gate.md new file mode 100644 index 0000000..cf1b3ae --- /dev/null +++ b/docs/retroarch/phase-1.0bp-one-shot-sdl-materialization-gate.md @@ -0,0 +1,17 @@ +# Phase 1.0BP: one-shot SDL materialization gate + +Status: `EXACT_SDL_ONLY_MATERIALIZATION_AUTHORIZED_ONCE` + +Date: 2026-07-29 + +The BO runner, validator and source-only tests are hash bound. The pinned SDL +source is clean and the exact stage, build and archive paths are absent. BP +authorizes one invocation of the exact Python expression below. It may execute +only the ten reviewed requests and produce the detached SDL source, SDL build +tree and `libSDL2.a`. + +The attempt is consumed before hashing or process creation. Any mismatch, +timeout, overflow, nonzero result or missing archive stops the attempt. There +is no retry, resume or cleanup. The outputs are retained for audit. This gate +does not authorize building RetroArch, producing an ELF/map, connecting to a +PS5, transferring, installing or executing anything on a device. diff --git a/docs/retroarch/phase-1.0bq-inline-invocation-failure.md b/docs/retroarch/phase-1.0bq-inline-invocation-failure.md new file mode 100644 index 0000000..4328ef1 --- /dev/null +++ b/docs/retroarch/phase-1.0bq-inline-invocation-failure.md @@ -0,0 +1,16 @@ +# Phase 1.0BQ: inline invocation failure + +Status: `BP_CONSUMED_BEFORE_PYTHON_ENTRYPOINT_SOURCE_ALLOWED` + +Date: 2026-07-29 + +The single BP invocation exited in Bash parsing with an unexpected-token error +around the inline Python `join` expression. Python did not start, the runner was +not imported and all ten materializer requests remained uncalled. The exact +stage, build and archive paths are still absent. BP is consumed; it is not +retried. + +BQ permits only a tiny exact Python entrypoint source that imports `run_once`, +calls it once and prints its completed operation list. The source must contain +no arguments, shell, subprocess, filesystem, network, RetroArch or device +logic. No invocation or build is authorized by this phase. diff --git a/docs/retroarch/phase-1.0br-script-entrypoint-one-shot-gate.md b/docs/retroarch/phase-1.0br-script-entrypoint-one-shot-gate.md new file mode 100644 index 0000000..ad0d4ad --- /dev/null +++ b/docs/retroarch/phase-1.0br-script-entrypoint-one-shot-gate.md @@ -0,0 +1,15 @@ +# Phase 1.0BR: script-entrypoint one-shot gate + +Status: `EXACT_SCRIPT_ENTRYPOINT_AUTHORIZED_ONCE_SDL_ONLY` + +Date: 2026-07-29 + +The exact argument-free BQ entrypoint is source-audited and hash bound. It adds +only the repository import root, calls the already bound `run_once` exactly +once and prints JSON. The production output paths remain absent. + +BR grants one new materialization attempt through the script path, without an +inline shell expression. This is a separately reviewed permission, not a retry. +Any failure consumes it. No cleanup, resume or automatic retry is permitted. +The scope ends at the retained SDL archive; RetroArch and every PS5 action stay +unauthorized. diff --git a/docs/retroarch/phase-1.0bs-read-only-status-timeout.md b/docs/retroarch/phase-1.0bs-read-only-status-timeout.md new file mode 100644 index 0000000..eed3a71 --- /dev/null +++ b/docs/retroarch/phase-1.0bs-read-only-status-timeout.md @@ -0,0 +1,14 @@ +# Phase 1.0BS: read-only status timeout + +Status: `BR_CONSUMED_AT_STATUS_TIMEOUT_TIMEOUT_SOURCE_CORRECTION_ALLOWED` + +Date: 2026-07-29 + +BR entered the exact runner. `verify_sdl_commit` completed, then the bounded +real facade terminated `verify_sdl_clean` when WSL Git exceeded its ten-second +DrvFS deadline. No detached stage, CMake configure or build started and all +three output paths remain absent. BR is consumed and is not retried. + +BS permits only increasing the read-only status request deadline to 120 +seconds and adjusting its source tests. No other argv, output cap, request, +build or device authority changes. Invocation remains unauthorized. diff --git a/docs/retroarch/phase-1.0bt-corrected-timeout-one-shot-gate.md b/docs/retroarch/phase-1.0bt-corrected-timeout-one-shot-gate.md new file mode 100644 index 0000000..f0ec547 --- /dev/null +++ b/docs/retroarch/phase-1.0bt-corrected-timeout-one-shot-gate.md @@ -0,0 +1,13 @@ +# Phase 1.0BT: corrected-timeout one-shot gate + +Status: `EXACT_120_SECOND_STATUS_MATERIALIZATION_AUTHORIZED_ONCE` + +Date: 2026-07-29 + +The request graph differs from BR only in the read-only `verify_sdl_clean` +deadline, now 120 seconds with unchanged argv and 4,096-byte output cap. The +entrypoint and runner are unchanged and the exact outputs remain absent. + +BT grants one new, nonautomatic script attempt. It has no retry, resume or +cleanup. Authority ends at `libSDL2.a`; RetroArch artifacts and all PS5 actions +remain forbidden. diff --git a/docs/retroarch/phase-1.0bu-wsl-git-status-timeout-and-windows-git-fixture-gate.md b/docs/retroarch/phase-1.0bu-wsl-git-status-timeout-and-windows-git-fixture-gate.md new file mode 100644 index 0000000..1dd69e9 --- /dev/null +++ b/docs/retroarch/phase-1.0bu-wsl-git-status-timeout-and-windows-git-fixture-gate.md @@ -0,0 +1,15 @@ +# Phase 1.0BU: WSL Git status timeout and Windows Git fixture gate + +Status: `BT_CONSUMED_WSL_STATUS_UNSUITABLE_WINDOWS_GIT_READ_ONLY_FIXTURE_ALLOWED` + +Date: 2026-07-29 + +BT again completed the commit check and timed out in the unchanged read-only +status request, now after 120 seconds. The process group was terminated before +stage creation and all output paths remain absent. Repeated deadline increases +are not justified. + +The locally installed Windows Git `2.54.0.windows.1` is exact-hash bound and +has already returned the same checkout clean through PowerShell. BU permits one +WSL-to-Windows read-only fixture containing only `rev-parse HEAD` and `status +--porcelain`. It does not permit worktree creation, patches, CMake or a build. diff --git a/docs/retroarch/phase-1.0bv-windows-git-fixture-result-and-request-correction-gate.md b/docs/retroarch/phase-1.0bv-windows-git-fixture-result-and-request-correction-gate.md new file mode 100644 index 0000000..a7ff9e8 --- /dev/null +++ b/docs/retroarch/phase-1.0bv-windows-git-fixture-result-and-request-correction-gate.md @@ -0,0 +1,16 @@ +# Phase 1.0BV: Windows Git fixture result and request correction gate + +Status: `WINDOWS_GIT_READ_ONLY_FIXTURE_PASSED_REQUEST_SOURCE_CORRECTION_ALLOWED` + +Date: 2026-07-30 + +The single BU fixture completed both exact read-only commands. Windows Git, +invoked from WSL, returned the pinned SDL commit and an empty porcelain status +within the 30-second per-command bound. No worktree, patch, CMake, build or +device action was invoked. + +BV permits only an offline source correction. The first two materializer +requests may use the exact-hash-bound Windows Git executable and Windows SDL +source path. The detached LF worktree request may become quiet and use a +900-second bound, based on the previously observed roughly six-minute silent +checkout. The corrected materializer must not be invoked under this phase. diff --git a/docs/retroarch/phase-1.0bw-corrected-sdl-materializer-one-shot-gate.md b/docs/retroarch/phase-1.0bw-corrected-sdl-materializer-one-shot-gate.md new file mode 100644 index 0000000..ad8a45f --- /dev/null +++ b/docs/retroarch/phase-1.0bw-corrected-sdl-materializer-one-shot-gate.md @@ -0,0 +1,15 @@ +# Phase 1.0BW: corrected SDL materializer one-shot gate + +Status: `CORRECTED_REQUEST_GRAPH_BOUND_ONE_OFFLINE_SDL_MATERIALIZATION_ALLOWED` + +Date: 2026-07-30 + +The BV-authorized request correction is committed, pushed and bound below by +exact RetroArch commit and source hashes. Host tests and static validators pass. +The exact stage, build and archive outputs were rechecked absent. + +BW permits one invocation of the exact script entrypoint. Its scope ends after +materializing the patched SDL source and building `libSDL2.a`; it excludes the +RetroArch target, network access and every PS5 action. The attempt is consumed +when the entrypoint starts. Failure must stop without retry, cleanup or path +reuse until a later reviewed result gate. diff --git a/docs/retroarch/phase-1.0bx-changed-files-timeout-and-read-only-fixture-gate.md b/docs/retroarch/phase-1.0bx-changed-files-timeout-and-read-only-fixture-gate.md new file mode 100644 index 0000000..583548e --- /dev/null +++ b/docs/retroarch/phase-1.0bx-changed-files-timeout-and-read-only-fixture-gate.md @@ -0,0 +1,15 @@ +# Phase 1.0BX: changed-files timeout and read-only fixture gate + +Status: `BW_CONSUMED_CHANGED_FILES_TIMEOUT_WINDOWS_GIT_STAGE_FIXTURE_ALLOWED` + +Date: 2026-07-30 + +The single BW attempt completed both source checks, detached stage creation and +both patch check/apply pairs. It then timed out in WSL Git +`verify_changed_files` after ten seconds. The process group was terminated. +The stage exists; the build directory and archive do not. CMake and the SDL +build were not invoked. + +BX permits one exact read-only Windows Git `diff --name-only` fixture against +the existing stage. It permits no cleanup, output reuse, patching, CMake, +build, retry or device action. diff --git a/docs/retroarch/phase-1.0by-windows-git-stage-fixture-failure-and-file-hash-gate.md b/docs/retroarch/phase-1.0by-windows-git-stage-fixture-failure-and-file-hash-gate.md new file mode 100644 index 0000000..2e324be --- /dev/null +++ b/docs/retroarch/phase-1.0by-windows-git-stage-fixture-failure-and-file-hash-gate.md @@ -0,0 +1,13 @@ +# Phase 1.0BY: Windows Git stage fixture failure and file-hash gate + +Status: `BX_CONSUMED_CROSS_PLATFORM_WORKTREE_UNREADABLE_EXACT_FILE_HASH_FIXTURE_ALLOWED` + +Date: 2026-07-30 + +The single BX fixture failed with exit code 1 and +`fatal: not a git repository: (NULL)`. Windows Git therefore cannot audit the +WSL-created worktree through this route. No build or cleanup followed. + +BY permits one PowerShell read-only fixture: read the stage `.git` indirection +and calculate SHA-256 for the four exact patch target files. No directory +enumeration, Git invocation, cleanup, output reuse, CMake or build is allowed. diff --git a/docs/retroarch/phase-1.0bz-exact-sdl-stage-cleanup-gate.md b/docs/retroarch/phase-1.0bz-exact-sdl-stage-cleanup-gate.md new file mode 100644 index 0000000..c696ba4 --- /dev/null +++ b/docs/retroarch/phase-1.0bz-exact-sdl-stage-cleanup-gate.md @@ -0,0 +1,15 @@ +# Phase 1.0BZ: exact SDL stage cleanup gate + +Status: `PATCH_TARGET_HASHES_BOUND_EXACT_FAILED_STAGE_CLEANUP_ALLOWED_ONCE` + +Date: 2026-07-30 + +The BY fixture read the WSL `.git` indirection and hashed all four expected +patch targets. Their exact hashes are recorded in the manifest. The indirection +explains why Windows Git could not open this WSL-created worktree. + +BZ permits one forced WSL Git worktree removal for only the exact failed +`build/phase10bd/sdl-source` stage. This destructive cleanup is bounded to the +hash-recorded, dirty derived checkout; it does not include other temporary +worktrees, the clean SDL source, build outputs or any device. No retry or source +change is authorized. diff --git a/docs/retroarch/phase-1.0c-device-smoke-result.md b/docs/retroarch/phase-1.0c-device-smoke-result.md new file mode 100644 index 0000000..b402296 --- /dev/null +++ b/docs/retroarch/phase-1.0c-device-smoke-result.md @@ -0,0 +1,153 @@ +# Phase 1.0C PS5 device smoke result + +Date: 2026-07-19 + +Local window: 16:19–16:31 CEST + +Classification: `DEVICE_SMOKE_PARTIAL` + +## Result + +The exact Phase-1.0B candidate was rebuilt, reopened, and verified before +device contact: + +- artifact: `retroarch_ps5_software_smoke.elf`; +- size: 1,826,216 bytes; +- SHA-256: + `16c04afe26c490b5071388dc9a32524d09d50d181b1baffbbc54bd96f0c2c688`; +- linker-map SHA-256: + `21dfdaa44261f8d546fa9ea71102162dc880a8ca8037526019a04fece5b21313`; +- Phase-1.0B build-manifest SHA-256: + `8ee4305bc2d7e5d75d3f7a4b0b335de39c68932ca49c199310cda9f2279dd69f`. + +The complete offline preflight passed: both repositories and pinned upstreams +were clean and exact, the clean target rebuild reproduced the authorized +identity, the artifact remained stable across reopen and rehash, all 30 +Chimera GFX CTest cases passed, all 26 Phase-1.0B Python guardrails passed, +and the RetroArch host smoke, fault, sanitizer, import, relocation, segment, +exit-path, and persistent-write checks passed. + +No Phase-1.0C target, sender, installation, autoload, or device-management +code was created. + +## Authorization and attempts + +The operator confirmed all twelve device-preflight statements immediately +before the first connection. The first artifact-specific authorization +permitted one connection, transfer, execution attempt, and 75-second +observation. That transfer completed locally, but the operator's display was +off, so no screen evidence was obtained. The operator reported that the +console had not hung and still functioned normally. + +The operator then explicitly overrode the no-later-action boundary and issued +a new manual authorization for one additional run of the same hash-bound +artifact to the same session address. This was a separately initiated manual +action, not an automatic retry. The artifact and sender were reopened and +rehashed before the second connection. The second transfer also completed +locally. During the second 75-second window the operator observed no visible +change and reported that the console continued to respond normally. + +| Counter | Result | +|---|---:| +| TCP connections opened by the selected sender | 2 | +| complete local `sendall` operations | 2 | +| bytes offered per operation | 1,826,216 | +| loader execution attempts requested | 2 | +| executions independently observed | 0 | +| automatic retries | 0 | +| further attempts | 0 | + +The second action is recorded because it occurred, but it does not retroactively +change the scope of the first authorization. No third attempt is authorized or +performed. + +## Transport evidence + +The selected client was the existing official Y2JB source-controlled sender: + +- source: + `Gezine/Y2JB@0dbbf4e7e0203af7e5d101a3256c634edf4e3ba2:payload_sender.py`; +- sender SHA-256: + `8c87920c41dbdbd66b9f36ca9509f0d6bef9170f351dd97ff831cfb98e642ec6`; +- transport: one IPv4 TCP connection per invocation, explicit port 9021; +- local input: binary read of the selected ELF; +- send behavior: CPython `sendall`, which completes all local sends or raises; +- retry/resume: absent; +- response/acknowledgement: absent; +- close: after successful `sendall`. + +The audited hardened elfldr source at +`197623058f509eddde18868dafcb92fdcac66464` recognizes raw ELF magic, receives +the complete ELF extent into memory, and makes one spawn attempt without +filesystem staging, installation, rename, deletion, or live-file replacement. +That local source identity is transport evidence, not byte-exact proof of the +currently running device listener. + +Both sender invocations exited successfully and reported 1,826,216 locally +sent bytes. Because the raw route has no peer acknowledgement, remote receipt, +parse completion, child creation, entry, and exit are not independently proven. + +## Device observations + +| Observation | Result | +|---|---| +| anonymized device ID | `PS5-FW960-SESSION-A` | +| firmware source | operator confirmation from PS5 UI | +| raw firmware value | `9.60` | +| first visible phase | `UNOBSERVED` | +| last visible phase | `UNOBSERVED` | +| first error code | `UNOBSERVED` | +| write-firewall status | `UNOBSERVED` | +| video/title/frame/flip | no visible change observed | +| controller/digital/sticks | not observed | +| audio/submits | not observed | +| Options shutdown | not observed | +| automatic deadline | not observed | +| process exit | not observed | +| host observation windows | 75,235 ms and 75,405 ms | +| console response after attempts | normal according to operator | +| jailbreak/elfldr session | not independently tested after the attempts | +| reboot needed | no | +| unexpected persistent effect | none reported; not independently inspected | +| photo/video evidence | none | + +The first window supplied no visual evidence because the display was off. The +second window supplied a direct negative observation: no visible change +occurred while the console remained responsive. + +## Classification and remaining risk + +This result is `DEVICE_SMOKE_PARTIAL`, specifically: + +`NO_VISIBLE_OUTPUT; REMOTE_EXECUTION_AND_ENTRY_UNCONFIRMED; CONSOLE_RESPONSIVE` + +It is not a pass. The evidence does not prove `S00`, `S07`, any error code, +VideoOut, Pad, AudioOut, the runtime deadline, teardown, or `_Exit`. It is not +a session-recoverable failure because no crash, hang, lost session, or reboot +need was observed. It is not a transfer failure because both host-side +`sendall` operations completed, although the protocol cannot attest peer +receipt or execution. + +The remaining focused questions are: + +1. whether the deployed port-9021 listener is byte-equivalent to the audited + hardened source; +2. whether it accepted and parsed the complete ELF; +3. whether child creation or `_start` failed before the first visible frame; +4. whether VideoOut initialization failed without a visible reporting path; +5. whether the process ran and exited before the display showed a frame. + +No installation, autoload change, persistent staging, Payload Manager request, +live-file replacement, delete, cleanup write, automatic retry, or reboot +command was performed. + +## Next phase + +The exact proposed next implementation phase is: + +`PHASE 1.0D — OFFLINE LOADER-TO-ENTRY AND EARLIEST-VISIBLE-ERROR DIAGNOSIS` + +It must remain host/offline work until separately reviewed. It should focus +only on distinguishing loader receipt, child entry, and pre-first-frame +VideoOut failure using already public, bounded userland contracts. It must not +introduce installation, autoload, dynamic cores, GNM, or another device run. diff --git a/docs/retroarch/phase-1.0ca-cleanup-result-and-hash-verifier-correction-gate.md b/docs/retroarch/phase-1.0ca-cleanup-result-and-hash-verifier-correction-gate.md new file mode 100644 index 0000000..9ee1d31 --- /dev/null +++ b/docs/retroarch/phase-1.0ca-cleanup-result-and-hash-verifier-correction-gate.md @@ -0,0 +1,15 @@ +# Phase 1.0CA: cleanup result and hash-verifier correction gate + +Status: `EXACT_STAGE_CLEANUP_PASSED_HASH_VERIFIER_SOURCE_CORRECTION_ALLOWED` + +Date: 2026-07-30 + +The single BZ cleanup completed successfully. The exact stage and its worktree +metadata are absent, while the source checkout remains clean at the pinned SDL +commit. No other temporary worktree was removed. + +CA permits only an offline request-graph correction: replace the failing +worktree Git diff with exact SHA-256 verification of the four patch targets +using the hash-bound `/usr/bin/sha256sum`. The resulting hashes must equal the +BY fixture values in exact path order. Materializer invocation, output reuse, +CMake, build and device actions remain unauthorized. diff --git a/docs/retroarch/phase-1.0cb-hash-verified-sdl-materializer-one-shot-gate.md b/docs/retroarch/phase-1.0cb-hash-verified-sdl-materializer-one-shot-gate.md new file mode 100644 index 0000000..628304c --- /dev/null +++ b/docs/retroarch/phase-1.0cb-hash-verified-sdl-materializer-one-shot-gate.md @@ -0,0 +1,14 @@ +# Phase 1.0CB: hash-verified SDL materializer one-shot gate + +Status: `HASH_VERIFIER_BOUND_ONE_OFFLINE_SDL_MATERIALIZATION_ALLOWED` + +Date: 2026-07-30 + +The CA-authorized hash verifier is committed, pushed and bound by exact commit +and source hashes. Its host tests and validators pass. The exact stage, build +and archive outputs are absent after the separately authorized cleanup. + +CB permits one new invocation of the exact script entrypoint. The request graph +must verify all four patched files by SHA-256 before CMake. Scope ends at +`libSDL2.a`; RetroArch target build, network and device actions remain excluded. +The attempt is consumed at entry and may not retry or clean up on failure. diff --git a/docs/retroarch/phase-1.0cc-cmake-timeout-and-exact-output-cleanup-gate.md b/docs/retroarch/phase-1.0cc-cmake-timeout-and-exact-output-cleanup-gate.md new file mode 100644 index 0000000..ca2a40e --- /dev/null +++ b/docs/retroarch/phase-1.0cc-cmake-timeout-and-exact-output-cleanup-gate.md @@ -0,0 +1,16 @@ +# Phase 1.0CC: CMake timeout and exact output cleanup gate + +Status: `CB_CONSUMED_CONFIGURE_TIMEOUT_EXACT_DERIVED_OUTPUT_CLEANUP_ALLOWED` + +Date: 2026-07-30 + +CB passed the commit, clean-source, worktree, both patch chains and all four +patch-target hashes. CMake compiler identification then succeeded for C and +C++, but configure exceeded 120 seconds before producing `CMakeCache.txt`. +The archive is absent. + +CC permits one cleanup transaction over only the exact derived stage and +partial build directory. The stage must be removed through its originating WSL +Git worktree administration; the build directory may be recursively removed +only after its absolute path is verified beneath the exact Phase-1.0BD root. +No retry, source change, build or device action is authorized. diff --git a/docs/retroarch/phase-1.0cd-cleanup-result-and-configure-timeout-correction-gate.md b/docs/retroarch/phase-1.0cd-cleanup-result-and-configure-timeout-correction-gate.md new file mode 100644 index 0000000..f0bd934 --- /dev/null +++ b/docs/retroarch/phase-1.0cd-cleanup-result-and-configure-timeout-correction-gate.md @@ -0,0 +1,15 @@ +# Phase 1.0CD: cleanup result and configure-timeout correction gate + +Status: `EXACT_OUTPUT_CLEANUP_PASSED_CONFIGURE_TIMEOUT_CORRECTION_ALLOWED` + +Date: 2026-07-30 + +The one CC cleanup transaction removed only the registered failed stage and +partial build directory. Their worktree metadata is absent and the pinned SDL +source remains clean. + +CD permits one source-only change: increase +`cmake_configure_sdl_only.timeout_seconds` from 120 to 600. The prior configure +had already completed C and C++ compiler identification before its deadline. +All argv, output limits, later build timeout and safety exclusions remain +unchanged. Invocation and output reuse remain unauthorized. diff --git a/docs/retroarch/phase-1.0ce-extended-configure-sdl-materializer-one-shot-gate.md b/docs/retroarch/phase-1.0ce-extended-configure-sdl-materializer-one-shot-gate.md new file mode 100644 index 0000000..20dbde8 --- /dev/null +++ b/docs/retroarch/phase-1.0ce-extended-configure-sdl-materializer-one-shot-gate.md @@ -0,0 +1,11 @@ +# Phase 1.0CE: extended-configure SDL materializer one-shot gate + +Status: `EXTENDED_CONFIGURE_REQUEST_BOUND_ONE_OFFLINE_MATERIALIZATION_ALLOWED` + +Date: 2026-07-30 + +The CD-authorized timeout-only correction is committed, pushed and hash-bound. +Host tests and validators pass, and the exact stage and build outputs are +absent. CE permits one exact offline entrypoint invocation with a 600-second +CMake configure bound. Scope still ends at `libSDL2.a`; no RetroArch target, +network or PS5 action is authorized, and failure consumes the attempt. diff --git a/docs/retroarch/phase-1.0cf-repeated-configure-timeout-and-cleanup-gate.md b/docs/retroarch/phase-1.0cf-repeated-configure-timeout-and-cleanup-gate.md new file mode 100644 index 0000000..341cfa5 --- /dev/null +++ b/docs/retroarch/phase-1.0cf-repeated-configure-timeout-and-cleanup-gate.md @@ -0,0 +1,14 @@ +# Phase 1.0CF: repeated configure timeout and cleanup gate + +Status: `CE_CONSUMED_MOUNTED_BUILDROOT_UNSUITABLE_EXACT_CLEANUP_ALLOWED` + +Date: 2026-07-30 + +CE again passed all pre-CMake operations. CMake made successful compiler and +try-compile progress but timed out after 600 seconds without a cache or archive. +Further timeout increases on the mounted buildroot are rejected. + +CF permits one cleanup of only the exact derived stage and partial build, using +the same bounded methods as CC. It does not authorize another materializer +attempt. The next source phase may design and host-test a WSL-native stage/build +with exact archive export, but may not invoke it under CF. diff --git a/docs/retroarch/phase-1.0cg-native-wsl-sdl-materializer-design-gate.md b/docs/retroarch/phase-1.0cg-native-wsl-sdl-materializer-design-gate.md new file mode 100644 index 0000000..175540e --- /dev/null +++ b/docs/retroarch/phase-1.0cg-native-wsl-sdl-materializer-design-gate.md @@ -0,0 +1,15 @@ +# Phase 1.0CG: native WSL SDL materializer design gate + +Status: `MOUNTED_OUTPUTS_CLEAN_NATIVE_WSL_SOURCE_DESIGN_ALLOWED` + +Date: 2026-07-30 + +The CF cleanup removed the failed mounted stage and build. `/tmp` is a WSL +`tmpfs` with 8,226,119,680 available bytes, and the exact proposed root +`/tmp/chimera-gfx-phase10ch` is absent. + +CG permits source implementation and fake-host testing only. The materializer +may redirect its stage, build and intermediate archive to the exact native root +while retaining the pinned mounted source, SDK and patch inputs. No native root +may be created, no CMake/build may run, and no archive export, RetroArch target +or device action is authorized under CG. diff --git a/docs/retroarch/phase-1.0ch-native-wsl-sdl-materializer-one-shot-gate.md b/docs/retroarch/phase-1.0ch-native-wsl-sdl-materializer-one-shot-gate.md new file mode 100644 index 0000000..6367e72 --- /dev/null +++ b/docs/retroarch/phase-1.0ch-native-wsl-sdl-materializer-one-shot-gate.md @@ -0,0 +1,11 @@ +# Phase 1.0CH: native WSL SDL materializer one-shot gate + +Status: `NATIVE_WSL_REQUEST_GRAPH_BOUND_ONE_MATERIALIZATION_ALLOWED` + +Date: 2026-07-30 + +The CG-authorized native request graph is committed, pushed and hash-bound. +Host tests, validators and WSL import checks pass; the exact native root is +absent. CH permits one offline invocation that may create only that native root, +materialize the patched SDL worktree and build `libSDL2.a` there. Archive export, +RetroArch target build, network and PS5 actions remain unauthorized. diff --git a/docs/retroarch/phase-1.0ci-native-sdk-bottleneck-and-cleanup-gate.md b/docs/retroarch/phase-1.0ci-native-sdk-bottleneck-and-cleanup-gate.md new file mode 100644 index 0000000..5563229 --- /dev/null +++ b/docs/retroarch/phase-1.0ci-native-sdk-bottleneck-and-cleanup-gate.md @@ -0,0 +1,16 @@ +# Phase 1.0CI: native SDK bottleneck and cleanup gate + +Status: `CH_CONSUMED_MOUNTED_SDK_PROBE_BOTTLENECK_EXACT_NATIVE_CLEANUP_ALLOWED` + +Date: 2026-07-30 + +CH passed every preconfigure operation on the native WSL root. Configure then +progressed through successful symbol try-compiles but timed out after 600 +seconds. The scratch tree was native; compiler and sysroot remained mounted. +The pinned SDK is only 29,406,271 bytes, so native SDK staging is the next +bounded remediation candidate rather than another timeout increase. + +CI permits one cleanup of only `/tmp/chimera-gfx-phase10ch`, with its SDL +worktree removed through the originating Git administration before the exact +root is recursively removed. No retry, SDK copy, build or device action is +authorized in CI. diff --git a/docs/retroarch/phase-1.0cj-native-sdk-stage-design-gate.md b/docs/retroarch/phase-1.0cj-native-sdk-stage-design-gate.md new file mode 100644 index 0000000..aa04801 --- /dev/null +++ b/docs/retroarch/phase-1.0cj-native-sdk-stage-design-gate.md @@ -0,0 +1,14 @@ +# Phase 1.0CJ: native SDK stage design gate + +Status: `NATIVE_ROOT_CLEAN_FULL_SDK_TREE_BOUND_STAGE_DESIGN_ALLOWED` + +Date: 2026-07-30 + +The CI cleanup left the exact native root absent. The pinned SDK contains 2,923 +regular files, no symbolic links and 29,406,271 bytes. A normalized read-only +tar stream binds the entire tree, not merely selected files. + +CJ permits source implementation and fake tests for one exclusive native SDK +copy followed by normalized tree verification. The build must reference only +the verified native copy. No copy, CMake invocation, build, export, target or +device action is authorized under CJ. diff --git a/docs/retroarch/phase-1.0ck-native-sdk-sdl-materializer-one-shot-gate.md b/docs/retroarch/phase-1.0ck-native-sdk-sdl-materializer-one-shot-gate.md new file mode 100644 index 0000000..37ff61c --- /dev/null +++ b/docs/retroarch/phase-1.0ck-native-sdk-sdl-materializer-one-shot-gate.md @@ -0,0 +1,11 @@ +# Phase 1.0CK: native-SDK SDL materializer one-shot gate + +Status: `FULL_SDK_TREE_STAGE_BOUND_ONE_NATIVE_MATERIALIZATION_ALLOWED` + +Date: 2026-07-30 + +The CJ-authorized implementation is committed, pushed and hash-bound. The exact +native root is absent. CK permits one offline invocation: create and verify the +normalized SDK archive, extract it natively, materialize patched SDL and build +the native `libSDL2.a`. Export, RetroArch target build, network and PS5 actions +remain unauthorized. Failure consumes the attempt without retry or cleanup. diff --git a/docs/retroarch/phase-1.0cl-native-sdk-configure-progress-and-cleanup-gate.md b/docs/retroarch/phase-1.0cl-native-sdk-configure-progress-and-cleanup-gate.md new file mode 100644 index 0000000..41dba85 --- /dev/null +++ b/docs/retroarch/phase-1.0cl-native-sdk-configure-progress-and-cleanup-gate.md @@ -0,0 +1,15 @@ +# Phase 1.0CL: native-SDK configure progress and cleanup gate + +Status: `CK_CONSUMED_96_SUCCESSFUL_CHECKS_EXACT_NATIVE_CLEANUP_ALLOWED` + +Date: 2026-07-30 + +CK completed SDK staging, full-tree verification, extraction, SDL worktree and +patch verification. Native CMake then completed 96 checks and reached the +`strcasestr` probe before its 600-second deadline. Compiler and sysroot paths +were both native and successful; this is bounded progress, not a toolchain +failure. + +CL permits one cleanup of only the exact native root, removing its registered +SDL worktree first. No retry, timeout change, build, export or device action is +authorized under CL. diff --git a/docs/retroarch/phase-1.0cm-measured-configure-timeout-correction-gate.md b/docs/retroarch/phase-1.0cm-measured-configure-timeout-correction-gate.md new file mode 100644 index 0000000..dbb6ebe --- /dev/null +++ b/docs/retroarch/phase-1.0cm-measured-configure-timeout-correction-gate.md @@ -0,0 +1,11 @@ +# Phase 1.0CM: measured configure-timeout correction gate + +Status: `NATIVE_ROOT_CLEAN_MEASURED_1800_SECOND_SOURCE_CORRECTION_ALLOWED` + +Date: 2026-07-30 + +CL cleanup succeeded. CK completed 96 native compiler checks in 600 seconds; +SDL's remaining libc, math and platform checks make 1,800 seconds a measured +upper bound with margin. CM permits only changing +`cmake_configure_sdl_only.timeout_seconds` from 600 to 1,800. All commands, +other bounds and scopes remain unchanged. No invocation is authorized. diff --git a/docs/retroarch/phase-1.0cn-measured-native-sdl-materializer-one-shot-gate.md b/docs/retroarch/phase-1.0cn-measured-native-sdl-materializer-one-shot-gate.md new file mode 100644 index 0000000..a9cc102 --- /dev/null +++ b/docs/retroarch/phase-1.0cn-measured-native-sdl-materializer-one-shot-gate.md @@ -0,0 +1,10 @@ +# Phase 1.0CN: measured native SDL materializer one-shot gate + +Status: `MEASURED_1800_SECOND_NATIVE_REQUEST_BOUND_ONE_BUILD_ALLOWED` + +Date: 2026-07-30 + +The CM timeout-only correction is committed, tested, pushed and hash-bound; the +native root is absent. CN permits one offline native-SDK materializer invocation +with 1,800 seconds for configure and 600 seconds for `SDL2-static`. Archive +export, RetroArch target build, network and every PS5 action remain excluded. diff --git a/docs/retroarch/phase-1.0co-configure-success-build-output-limit-cleanup-gate.md b/docs/retroarch/phase-1.0co-configure-success-build-output-limit-cleanup-gate.md new file mode 100644 index 0000000..3fb1808 --- /dev/null +++ b/docs/retroarch/phase-1.0co-configure-success-build-output-limit-cleanup-gate.md @@ -0,0 +1,12 @@ +# Phase 1.0CO: configure success, build output-limit cleanup gate + +Status: `CN_CONFIGURE_PASSED_BUILD_OUTPUT_LIMIT_STOP_EXACT_CLEANUP_ALLOWED` + +Date: 2026-07-30 + +CN completed native SDK staging, SDL patching and full CMake configure. The +build then produced 46 object files before its merged output exceeded 65,536 +bytes and the process group was terminated. No archive exists. + +CO permits one cleanup of only the exact native root after registered worktree +removal. It authorizes no retry or source change. diff --git a/docs/retroarch/phase-1.0cp-build-output-limit-correction-gate.md b/docs/retroarch/phase-1.0cp-build-output-limit-correction-gate.md new file mode 100644 index 0000000..c3416a3 --- /dev/null +++ b/docs/retroarch/phase-1.0cp-build-output-limit-correction-gate.md @@ -0,0 +1,9 @@ +# Phase 1.0CP: build output-limit correction gate + +Status: `NATIVE_ROOT_CLEAN_BUILD_OUTPUT_LIMIT_SOURCE_CORRECTION_ALLOWED` + +Date: 2026-07-30 + +CO cleanup succeeded. CP permits only changing the SDL build merged-output +limit from 65,536 to 1,048,576 bytes. The configure and build timeouts, argv, +target and all scopes remain unchanged. No invocation is authorized. diff --git a/docs/retroarch/phase-1.0cq-full-output-native-sdl-one-shot-gate.md b/docs/retroarch/phase-1.0cq-full-output-native-sdl-one-shot-gate.md new file mode 100644 index 0000000..f52d6b6 --- /dev/null +++ b/docs/retroarch/phase-1.0cq-full-output-native-sdl-one-shot-gate.md @@ -0,0 +1,10 @@ +# Phase 1.0CQ: full-output native SDL one-shot gate + +Status: `CONFIGURE_PROVEN_FULL_BUILD_OUTPUT_BOUND_ONE_BUILD_ALLOWED` + +Date: 2026-07-30 + +The CP correction is committed and tested; the native root is absent. CQ +permits one native-SDK materializer invocation with the proven 1,800-second +configure bound and a 1,048,576-byte SDL-build output cap. Scope ends at the +native static archive; export, RetroArch target and PS5 actions remain excluded. diff --git a/docs/retroarch/phase-1.0cr-audited-sdl-archive-export-gate.md b/docs/retroarch/phase-1.0cr-audited-sdl-archive-export-gate.md new file mode 100644 index 0000000..bd0748e --- /dev/null +++ b/docs/retroarch/phase-1.0cr-audited-sdl-archive-export-gate.md @@ -0,0 +1,17 @@ +# Phase 1.0CR: audited SDL archive export gate + +Status: `NATIVE_SDL_ARCHIVE_AUDITED_ONE_EXCLUSIVE_EXPORT_ALLOWED` + +Date: 2026-07-30 + +The CQ one-shot completed all fifteen operations and produced a 2,412,290-byte +current `ar` archive with SHA-256 +`4ced0a042c0b8a70424e42a14cfe9b2c4d30a4cd73b661dc1fcf8ad967a7f7e1`. +Offline inspection found 143 members, all five expected PS5 backend objects, +all 26 expected undefined Chimera hooks and the expected public SDL entry +points. No RetroArch ELF or map exists. + +CR permits exactly one host-only export to the absent mounted destination. +The bound exporter verifies the source, uses exclusive leaf creation, flushes, +reopens and rehashes the destination. It may not remove the native source. +RetroArch target building, networking and every PS5 action remain excluded. diff --git a/docs/retroarch/phase-1.0cs-export-parent-correction-gate.md b/docs/retroarch/phase-1.0cs-export-parent-correction-gate.md new file mode 100644 index 0000000..f0fd8c2 --- /dev/null +++ b/docs/retroarch/phase-1.0cs-export-parent-correction-gate.md @@ -0,0 +1,13 @@ +# Phase 1.0CS: export-parent correction gate + +Status: `CR_REJECTED_PRE_MUTATION_EXACT_PARENT_AND_ONE_EXPORT_ALLOWED` + +Date: 2026-07-30 + +The CR attempt was consumed when the exporter found that the exact destination +parent was absent. It stopped before creating a destination leaf and no retry +occurred. CS binds the corrected exporter and permits creation of only +`build/phase10bd/sdl-build`, below the already existing `build/phase10bd`, +followed by one exclusive export of the same audited archive. + +No cleanup, RetroArch target build, network operation or PS5 action is allowed. diff --git a/docs/retroarch/phase-1.0ct-sdl-archive-export-result.md b/docs/retroarch/phase-1.0ct-sdl-archive-export-result.md new file mode 100644 index 0000000..4dd8ce9 --- /dev/null +++ b/docs/retroarch/phase-1.0ct-sdl-archive-export-result.md @@ -0,0 +1,14 @@ +# Phase 1.0CT: SDL archive export result + +Status: `EXACT_ARCHIVE_EXPORTED_TARGET_BUILD_BLOCKED_MISSING_HEADERS` + +Date: 2026-07-30 + +The CS one-shot exported the audited archive exclusively. The destination is +2,412,290 bytes and rehashes to the expected SHA-256 +`4ced0a042c0b8a70424e42a14cfe9b2c4d30a4cd73b661dc1fcf8ad967a7f7e1`. + +The subsequent Makefile audit found that a target build also consumes the +generated and public SDL include roots. Neither has been exported. A RetroArch +target build therefore remains blocked until both exact trees are enumerated, +hash-bound and copied under a new offline gate. diff --git a/docs/retroarch/phase-1.0cu-exact-sdl-header-export-gate.md b/docs/retroarch/phase-1.0cu-exact-sdl-header-export-gate.md new file mode 100644 index 0000000..1ea9d0a --- /dev/null +++ b/docs/retroarch/phase-1.0cu-exact-sdl-header-export-gate.md @@ -0,0 +1,14 @@ +# Phase 1.0CU: exact SDL header export gate + +Status: `HEADER_TREES_BOUND_ONE_HOST_EXPORT_ALLOWED` + +Date: 2026-07-30 + +CU binds both header roots consumed by `Makefile.ps5`. The public tree contains +78 regular files (2,400,049 bytes); the generated release configuration tree +contains one regular file (16,566 bytes). Their identities cover every sorted +UTF-8 relative path, byte length and file SHA-256. + +One host-only staged export is permitted. Existing destinations, symlinks, +identity drift and partial reuse are rejected. Cleanup, target building, +networking and every PS5 operation remain unauthorized. diff --git a/docs/retroarch/phase-1.0cv-launch-canary-offline-build-gate.md b/docs/retroarch/phase-1.0cv-launch-canary-offline-build-gate.md new file mode 100644 index 0000000..4078d82 --- /dev/null +++ b/docs/retroarch/phase-1.0cv-launch-canary-offline-build-gate.md @@ -0,0 +1,15 @@ +# Phase 1.0CV: launch-canary offline build gate + +Status: `COMPLETE_SDL_INPUT_BOUND_ONE_OFFLINE_TARGET_BUILD_ALLOWED` + +Date: 2026-07-30 + +The CU export completed and both destination header identities match. The +archive, headers, RetroArch commit, Makefile, linker script and normalized +native SDK are bound. No target ELF, map, object or dependency file exists. +A parse-only Make run produced the complete 121-command build plan. + +CV permits one offline `launch-canary` build with the exact recorded argv. +It does not permit `clean`, retry, network access, transfer, installation or +execution. Any resulting artifact remains ineligible until a later static +ELF/map/callgraph audit records its exact identity and safety properties. diff --git a/docs/retroarch/phase-1.0cw-launch-canary-artifact-audit.md b/docs/retroarch/phase-1.0cw-launch-canary-artifact-audit.md new file mode 100644 index 0000000..10acf20 --- /dev/null +++ b/docs/retroarch/phase-1.0cw-launch-canary-artifact-audit.md @@ -0,0 +1,21 @@ +# Phase 1.0CW: launch-canary artifact audit + +Status: `OFFLINE_ARTIFACT_BUILT_STATIC_AUDIT_PASS_REPRODUCIBILITY_UNPROVEN` + +Date: 2026-07-30 + +The one CV build completed and produced an ELF of 1,845,240 bytes with SHA-256 +`8dadce9d9faaef21ea129a3d216c768eea9a3ca9bf8ecb8d852e376b58a9bf95`. +Its map is 630,127 bytes with SHA-256 +`1cabe4d61b2eb2c1844a58883db6b8644c980759690242bef17f92e9c260b3cd`. + +The ELF is an x86-64 PIE with three load segments and no writable/executable +segment. It has the exact seven expected PS5 libraries, zero-sized init/fini +arrays, `CHD10AV1`, the D14 cleanup emitter and inherited diagnostic stream. +Only the live `open`, `fopen` and `fwrite` firewall wrappers survived section +garbage collection. There are no socket-construction or receive imports; the +single `send` import is the bounded inherited-descriptor result channel. + +This is one build, not reproducibility proof or firmware evidence. The artifact +remains transfer-, installation- and execution-ineligible. The next phase must +build independently in a fresh native tree and compare ELF and map bytes. diff --git a/docs/retroarch/phase-1.0cx-isolated-reproducibility-build-gate.md b/docs/retroarch/phase-1.0cx-isolated-reproducibility-build-gate.md new file mode 100644 index 0000000..f3a11b4 --- /dev/null +++ b/docs/retroarch/phase-1.0cx-isolated-reproducibility-build-gate.md @@ -0,0 +1,15 @@ +# Phase 1.0CX: isolated reproducibility build gate + +Status: `FIRST_ARTIFACT_AUDITED_ONE_ISOLATED_REBUILD_ALLOWED` + +Date: 2026-07-30 + +CX permits one independent build in the absent native root +`/tmp/chimera-gfx-phase10cx`. The bound builder exports only tracked bytes from +the exact target-source commit, recopies and rehashes the audited SDL package, +and performs a full object-free build. It succeeds only when both ELF and map +match the first build byte-for-byte. + +The source archive and native root are consumed evidence and may not be cleaned +or reused. No retry, networking, transfer, installation or PS5 execution is +authorized, and even a match does not itself make the artifact eligible. diff --git a/docs/retroarch/phase-1.0cy-launch-canary-reproducibility-result.md b/docs/retroarch/phase-1.0cy-launch-canary-reproducibility-result.md new file mode 100644 index 0000000..8e24dc8 --- /dev/null +++ b/docs/retroarch/phase-1.0cy-launch-canary-reproducibility-result.md @@ -0,0 +1,15 @@ +# Phase 1.0CY: launch-canary reproducibility result + +Status: `BYTE_EXACT_ELF_AND_MAP_REPRODUCIBILITY_PROVEN_OFFLINE` + +Date: 2026-07-30 + +The isolated CX build completed from a tracked-only source archive in a fresh +native root. It reused no object files and invoked neither network nor device +actions. Its 1,845,240-byte ELF and 630,127-byte map match the first build +byte-for-byte. + +This proves offline build reproducibility, not firmware behavior. The artifact +remains ineligible. Before any hardware approval can be considered, the host +runner must gain inactive, manifest-only `CHD10AV1` parsing with D14 as its sole +terminal and retain the existing consumed-before-connect one-shot rules. diff --git a/docs/retroarch/phase-1.0cz-inactive-launch-canary-runner.md b/docs/retroarch/phase-1.0cz-inactive-launch-canary-runner.md new file mode 100644 index 0000000..6a91e70 --- /dev/null +++ b/docs/retroarch/phase-1.0cz-inactive-launch-canary-runner.md @@ -0,0 +1,23 @@ +# Phase 1.0CZ: inactive launch-canary one-shot runner + +Status: `INACTIVE_ARTIFACT_SPECIFIC_HARDWARE_GATE` + +Date: 2026-07-30 + +CZ binds the reproducible 1,845,240-byte launch-canary ELF and adds no free +protocol selector. Only an exact active CZ manifest can select `CHD10AV1`; D14 +is the sole accepted terminal. The runner also requires exactly one ordered +D07, D04 and D14, exact RAW semantics for D07/D04, zero cleanup-error fields +in D14, at most one D12 and no bytes after terminal. The inherited raw-ELF route remains one connect, +one send, one write-half-close, bounded receive, consumed receipt before the +socket and exclusive trace creation. + +The tracked manifest and approval template are inactive. They contain no +target, port or run ID, all hardware authorizations are false, and artifact +eligibility is false. Editing the tracked template is not an approval. A real +attempt requires a new active-task statement from Jens naming this exact +artifact and scope, plus a separately matching untracked approval. + +This gate covers only the raw-elfldr baseline arm. It does not use shsrv/hbldr +and cannot prove the BigApp launch-context hypothesis. Work on a bounded +nonpersistent BigApp launcher remains a separate offline track. diff --git a/docs/retroarch/phase-1.0d-crt-entry-canary.md b/docs/retroarch/phase-1.0d-crt-entry-canary.md new file mode 100644 index 0000000..bdbbe7e --- /dev/null +++ b/docs/retroarch/phase-1.0d-crt-entry-canary.md @@ -0,0 +1,38 @@ +# Phase 1.0D CRT entry canary + +`chimera_ps5_crt_entry_canary.elf` is a non-RetroArch diagnostic artifact. It +uses the pinned normal SDK CRT, the same ET_DYN/W^X loader class, and the same +`_Exit` selection as the software-smoke profile. + +Its fixed behavior is: + +1. enter `main`; +2. attempt exactly one notification containing + `CHIMERA C1 — CRT MAIN REACHED`; +3. perform one relative `nanosleep` of two seconds, with no interrupted-sleep + retry; +4. call `_Exit(0)`. + +It contains no SDL, VideoOut, Pad, AudioOut, filesystem, networking, thread +creation, listener, retry, configuration, autoload, payload launch, or +installation implementation. Its complete undefined-symbol set is `_Exit`, +`memset`, `nanosleep`, and `sceKernelSendNotificationRequest`; DT_NEEDED is +only `libSceLibcInternal.sprx` and `libkernel_web.sprx`. + +The notification ABI is copied from the local lifecycle source at commit +`fe08300339a13f899fb78ea404ada381a5cba87c`, Git blob +`654e6659c9e6f2569ddccf1c9e46ea1c62c066f3`: 45 reserved bytes followed by +3,075 message bytes, passed as +`sceKernelSendNotificationRequest(0, &request, sizeof(request), 0)`. No +notification ABI was inferred from a symbol name. + +Static disassembly binds `main` at `0x0`, the notification indirect call at +`0x24b`, `_Exit` at `0x23`, and SDK `_start` at `0x290`. The ELF has three +LOAD segments (RX, R, RW), zero RWX segments, an empty init/fini array, no TLS, +144 relocations, and 2,512 BSS bytes. + +Two clean builds produced the same 106,344-byte SHA-256 +`c84c0fb43392ce17abc1e7e6be38b522f1b32a681fd516d684c3dbbf7b34b743`; +both maps produced +`96132ef015d1743606a86906c810b9e9b2832889a6d49863653b03dddb75cf07`. +The ELF is `execution_eligible=false`. diff --git a/docs/retroarch/phase-1.0d-early-diagnostic-design.md b/docs/retroarch/phase-1.0d-early-diagnostic-design.md new file mode 100644 index 0000000..f554486 --- /dev/null +++ b/docs/retroarch/phase-1.0d-early-diagnostic-design.md @@ -0,0 +1,52 @@ +# Phase 1.0D early RetroArch diagnostic + +`retroarch_ps5_early_diag.elf` is compile-time profile +`PS5_PROFILE=early-diag` with `CHIMERA_PS5_EARLY_DIAG=1`. It retains the real +RetroArch frontend, static chimera_smokecore, write firewall, bounded runtime, +W^X layout, reverse cleanup, no network, no content/configuration, no dynamic +core, no retry, no autoload, and `_Exit`. + +## Diagnostic ladder + +The fixed D00-D12 ladder covers RetroArch main, platform begin/result, SDL +begin/result, VideoOut open, buffers, first flip, Pad, AudioOut, core init, +runloop, and shutdown. Each stage owns one bit in an in-memory attempted mask, +so a notification is attempted at most once. Notification failure is recorded +as a raw error and never blocks the primary path. Text is built in a fixed +192-byte buffer; truncation is counted and no dynamic formatting allocation is +used. Login users are represented only by count and numeric IDs. + +`sceVideoOutSetBufferAttribute2` is `void` in the pinned SDL source. The +diagnostic stores `CHIMERA_PS5_DIAG_NO_RETURN_VALUE`; it does not fabricate a +success return. Consequently that exact boundary cannot receive a return-code +failure injection. Every representable boundary is injected by the host test. + +## Earliest frame + +After VideoOut open, direct-memory allocation/mapping, equeue/event setup, and +buffer registration—but before input, audio, core, and runloop—the SDL overlay +writes a full magenta frame, a fixed white rectangle, and an embedded black +pattern. It submits frame ID 0 once and waits once for at most 100,000 +microseconds. A failed first flip records D07 and enters bounded teardown; it +does not retry or initialize another buffer chain. + +Source callsites are `retroarch.c:6201` for D00 and the +`PS5_PresentEarlyDiagnosticFrame` hunk in +`pkg/ps5/sdl2-ps5-early-diag.patch` for the magenta frame. Disassembly binds +D00 at `main+0xd` (`0x3ccd`), the diagnostic frame helper at `0xfdf90`, its +first-flip call at `0xfe1ec`, the common notification call at `0x4776b`, and +`_Exit` at `0x3cef`. + +## Host fault model + +ASan/UBSan host tests cover notification failure, truncation and duplicate +suppression; user-service failure and no-login-user; VideoOut open; direct +allocation/map; registration; equeue/event; first flip; Pad; AudioOut; core; +deadline; every cleanup boundary; the write firewall; stable D-code; and +exactly one selected `_Exit` path. No test result is hardware evidence. + +Two clean target builds are byte-identical at 1,844,664 bytes and SHA-256 +`092bcabc0f8717a648030c592aa0dc900f878952fb14ee453851a29f298ea7ee`. +Their map hash is +`0d4d4614826fa4cbbf11e7374a2d2ecb574a00b55ad9deac17272f9504b254a0`. +The artifact is `execution_eligible=false`. diff --git a/docs/retroarch/phase-1.0d-loader-static-model.md b/docs/retroarch/phase-1.0d-loader-static-model.md new file mode 100644 index 0000000..2189eb0 --- /dev/null +++ b/docs/retroarch/phase-1.0d-loader-static-model.md @@ -0,0 +1,35 @@ +# Phase 1.0D hardened-elfldr static model + +The host-only model is in the separate chimera-retroarch fork at +`tools/ps5_elfldr_model.py`. Its authority is `elfldr.c` at commit +`197623058f509eddde18868dafcb92fdcac66464`, file SHA-256 +`9949f8e4037984d10f1f5aa498e4665593d1fac8a33614d7f2141349839bb803`. + +It reproduces source checks and data operations for the initial size/magic and +program-file bounds, ET_DYN/ET_EXEC, mapping range, PT_LOAD copies, relative +relocations, protection layout, entry mapping, synthetic saved RIP, RSP, RDI, +and 16 KiB argument-resource mapping. It inventories DT_NEEDED and undefined +symbols but correctly records that SDK startup—not `elfldr_load`—resolves +them. + +The source does not validate several expected ELF properties: class, data +encoding, machine, header entry sizes, segment overlap/alignment, and mapped +entrypoint are not loader rejection conditions. The model reports these as +source-validation gaps. It never invents stricter acceptance rules. + +| Input | SHA-256 | Result | +|---|---|---| +| Phase-1.0B smoke | `16c04afe26c490b5071388dc9a32524d09d50d181b1baffbbc54bd96f0c2c688` | ACCEPTED_BY_STATIC_MODEL | +| CRT canary | `c84c0fb43392ce17abc1e7e6be38b522f1b32a681fd516d684c3dbbf7b34b743` | ACCEPTED_BY_STATIC_MODEL | +| early diagnostic | `092bcabc0f8717a648030c592aa0dc900f878952fb14ee453851a29f298ea7ee` | ACCEPTED_BY_STATIC_MODEL | +| lifecycle reference | `bfb4a5cc768e162fe4c2fddf41c3978e152722918a39085277fd172cb95a7182` | ACCEPTED_BY_STATIC_MODEL | + +Negative host copies prove deterministic rejection for bad magic, unsupported +type, and out-of-file segment ranges. An unmapped entry yields +`MODEL_INCOMPLETE`, while a wrong machine remains accepted with an explicit +source-validation gap because the actual loader does not check it. + +`ACCEPTED_BY_STATIC_MODEL` means only “no deterministic rejection in the +modeled source path.” It is not proof of runtime allocation, kernel calls, +imports, firmware behavior, receipt, entry, execution, notification, cleanup, +or return. diff --git a/docs/retroarch/phase-1.0d-loader-to-entry-analysis.md b/docs/retroarch/phase-1.0d-loader-to-entry-analysis.md new file mode 100644 index 0000000..888b23c --- /dev/null +++ b/docs/retroarch/phase-1.0d-loader-to-entry-analysis.md @@ -0,0 +1,55 @@ +# Phase 1.0D loader-to-entry analysis + +Status: `RETROARCH_PS5_ENTRY_DIAGNOSTIC_LADDER_BUILT_OFFLINE`. +Nothing in this phase authorizes a PS5 connection, transfer, or execution. + +## Source-bound path + +The analysis binds hardened elfldr commit +`197623058f509eddde18868dafcb92fdcac66464`, SDK v0.41 commit +`d2e2e585740362976a39fdd5ccf390f199a7bc37`, and chimera-retroarch +commit `69b65858ffaee826d70f5c0df61013cd1b0e2048`. + +| Order | Source operation | Observable marker | Failure status | +|---:|---|---|---| +| 1 | elfldr sanity checks the received buffer | none | host receipt and this check remain unobservable | +| 2 | elfldr reserves/copies segments, applies relative relocations and protections | none | allocation, mapping, copy, protection, or sync can fail | +| 3 | elfldr creates `payload_args_t`, saves a synthetic return at `RSP-8`, sets entry RIP and passes args in RDI | none | resource setup and register writes can fail | +| 4 | SDK `_start` clears BSS | none | loader/stack/entry correctness is still required | +| 5 | `payload_init` calls `__crt_syscall_init`, `__kernel_init`, `__klog_init`, resolves and sets `__isthreaded` | none | any failed setup returns before `main` | +| 6 | `__patch_init` changes current-process credentials/capability attributes and the permitted syscall-address range | none | exact runtime success and firmware-9.60 behavior are unproven | +| 7 | `__rtld_init`, dependency opening, relocation and init-array handling | none | a missing module/import or relocation failure can stop before `main` | +| 8 | canary `main` or RetroArch `main` | C1 or D00 | first Phase-1.0D visible boundary | +| 9 | profile body and bounded teardown | D01-D12 for early diagnostic | raw boundary result is retained in memory | +| 10 | `_Exit` selected by the profile | D12 precedes it in the early profile | device exit behavior remains unproven | + +The earliest possible failure is therefore before `_start`: elfldr receipt, +validation, mapping, argument-resource construction, or register transfer. +After entry, the earliest application-independent failure is BSS/SDK CRT +initialization. Neither C1 nor D00 can distinguish those earlier steps. + +## Loader and artifact effects + +The existing loader itself performs process mapping, protection, ptrace-style +register operations, socket-overlap setup for the SDK argument resources, and +kernel/process changes in its broader execution path. The normal SDK CRT also +performs the bounded source-visible writes described above. Phase 1.0D does +not call any of this: it only builds and audits files offline. These effects +must not be described as harmless or absent in any later run authorization. + +No SDK CRT or hardened elfldr source was modified. No freestanding startup was +introduced. Static model acceptance is not proof of transport, receipt, +execution, firmware compatibility, visible notification, or safe exit. + +## Complexity comparison + +| Artifact | Bytes | Entry | PHDRs / LOADs | Relocations | Imports | DT_NEEDED | BSS bytes | Largest LOAD memory | +|---|---:|---:|---:|---:|---:|---:|---:|---:| +| Phase-1.0B smoke | 1,826,216 | `0xfdd70` | 4 / 3 | 1,053 | 140 | 7 | 697,232 | 903,360 | +| CRT canary | 106,344 | `0x290` | 4 / 3 | 144 | 4 | 2 | 2,512 | 35,312 | +| early diagnostic | 1,844,664 | `0xfec40` | 4 / 3 | 1,054 | 141 | 7 | 697,616 | 903,744 | +| lifecycle reference | 112,680 | `0x90` | 4 / 3 | 142 | 2 | 2 | not used as a Phase-1.0D claim | not used as a Phase-1.0D claim | + +The canary materially reduces size, imports, modules, and relocation volume. +The comparison can isolate classes of failure in a future authorized ladder; +it does not establish which class caused Phase 1.0C. diff --git a/docs/retroarch/phase-1.0d-next-device-test-ladder.md b/docs/retroarch/phase-1.0d-next-device-test-ladder.md new file mode 100644 index 0000000..e09fd1d --- /dev/null +++ b/docs/retroarch/phase-1.0d-next-device-test-ladder.md @@ -0,0 +1,44 @@ +# Phase 1.0D future device-test ladder + +Current state: + +```text +ps5_connection_authorized=false +device_transfer_authorized=false +device_execution_authorized=false +installation_authorized=false +autoload_authorized=false +device_write_authorized=false +automatic_retry=false +``` + +There is no transfer or execution package. The following is only a contract +for a later task. + +## RUN A + +A later authorization must name exactly +`chimera_ps5_crt_entry_canary.elf`, size 106,344 bytes, SHA-256 +`c84c0fb43392ce17abc1e7e6be38b522f1b32a681fd516d684c3dbbf7b34b743`, +the destination address supplied in that later task, one connection, one +direct in-memory transfer, one execution, and a bounded observation window. +It must continue to forbid install, autoload, staging, device files, live +replacement, cleanup, retry, reconnect, reboot, and any other payload. + +Visible `CHIMERA C1 — CRT MAIN REACHED` is the only RUN-A success marker. A +completed host send is not success. RUN A is never automatically repeated. + +## RUN B + +RUN B cannot be authorized merely because Phase 1.0D passed offline. It +requires visible RUN-A success, a separately recorded result, and a new +artifact-specific authorization naming +`retroarch_ps5_early_diag.elf`, size 1,844,664 bytes, SHA-256 +`092bcabc0f8717a648030c592aa0dc900f878952fb14ee453851a29f298ea7ee`. +It permits at most one connection, transfer, execution, and bounded +observation. The operator records the last visible D-code or the magenta-frame +state. + +RUN B has no retry, reconnect, second execution, third artifact, autoload, +installation, persistent staging, device write, or Phase-1.0B smoke rerun. +RUN A authority does not carry to RUN B. diff --git a/docs/retroarch/phase-1.0d-startup-import-closure.md b/docs/retroarch/phase-1.0d-startup-import-closure.md new file mode 100644 index 0000000..07c0b3f --- /dev/null +++ b/docs/retroarch/phase-1.0d-startup-import-closure.md @@ -0,0 +1,66 @@ +# Phase 1.0D startup and import closure + +## Pre-main closure + +The SDK `_start` order is BSS clear; syscall bridge; kernel argument bridge; +klog; `__isthreaded`; `__patch_init`; runtime linker; dependent library +opening/relocation; init array; then `main`. Both Phase-1.0D artifacts have +zero-byte init and fini arrays and no TLS sections or PT_TLS. Heap, signal, and +pthread behavior is not an application constructor here, but the runtime and +linked libraries remain able to resolve their normal facilities. + +`__patch_init` is not side-effect-free: it reads then changes current-process +capability/attribute bytes (including source comments for JIT shared memory +and ptrace) and writes lower/upper syscall-address bounds through kernel +copyin. The loader also prepares mappings and SDK resources before entry. +These are pre-main failure points with no C1/D00 marker. + +The SDK runtime linker can resolve and conditionally load +`/system/common/lib/libSceSysmodule.sprx`; exact runtime module availability +and whether that conditional path executes on firmware 9.60 are unproven. +Hardened elfldr does not resolve the artifact's dynamic symbols; the SDK +runtime path does. + +## Canary imports + +| Import | Class | Rationale | +|---|---|---| +| `memset` | CRT_PRE_MAIN / helper | request clearing may be compiler-lowered; SDK startup precedes it | +| `sceKernelSendNotificationRequest` | CANARY_MAIN | only C1 output | +| `nanosleep` | CANARY_MAIN | one two-second relative wait | +| `_Exit` | SHUTDOWN | selected terminal call | + +Required modules are `libkernel_web.sprx` and `libSceLibcInternal.sprx`. + +## Early-diagnostic groups + +The complete 141-symbol set is machine-readable in +`manifests/retroarch/phase-1.0d-early-diag-artifact.json`. Its meaningful +boundary grouping is: + +- CRT_PRE_MAIN: SDK-owned syscall/kernel/klog/patch/runtime-linker operations + are statically linked and may resolve libc/kernel facilities before D00. + No target marker precedes them. +- RETROARCH_PRE_VIDEO: libc/POSIX support, user service, system-service splash, + notification, clock and bounded platform status. +- VIDEO: direct memory, equeue, and every `sceVideoOut*` import. +- INPUT: `scePadInit`, `scePadOpen`, `scePadReadState`, `scePadClose`. +- AUDIO: `sceAudioOutInit`, `sceAudioOutOpen`, `sceAudioOutOutput`, + `sceAudioOutClose`. +- SHUTDOWN: `_Exit`, close/release/delete operations and bounded teardown. +- UNREACHABLE_IN_PROFILE: generic RetroArch file-write strings and code may + remain linked, but the profile routes mutating entrypoints through the + write firewall and disables content/configuration; no socket/SceNet import + is present. + +The import list alone cannot prove reachability. Conversely, absent imports do +not disprove statically linked behavior. The source profile, linker map, +disassembly, undefined-symbol inventory, and host fault model are considered +together. Generic localization strings such as netplay text and +`udp://127.0.0.1:` remain in the binary; there is no target network callgraph +or network import. SDK patch symbols also remain because normal CRT startup +was an explicit requirement. + +The earliest new marker that does not modify SDK CRT or loader architecture is +therefore C1/D00 at application `main`. A notification earlier than that would +require a separately reviewed CRT/loader change and is out of scope. diff --git a/docs/retroarch/phase-1.0db-post-cz-differential-analysis.md b/docs/retroarch/phase-1.0db-post-cz-differential-analysis.md new file mode 100644 index 0000000..bdb860a --- /dev/null +++ b/docs/retroarch/phase-1.0db-post-cz-differential-analysis.md @@ -0,0 +1,40 @@ +# Phase 1.0DB: post-CZ differential analysis + +Status: `RAW_CONTEXT_FAILURE_CONFIRMED_CAUSAL_BIGAPP_COMPARISON_NOT_READY` + +The consumed CZ run is a valid raw-elfldr baseline, but not a successful AV +run. It reached RetroArch and SDL video initialization, then +`sceVideoOutOpen(0xff, 0, 0, NULL)` returned signed `-2144796663` +(`0x80290009`). D06 and D07 were not reached. D04 returned `-1`, remote EOF +followed, and the distinct cleanup terminal D14 was absent. + +## Comparison with Phase 1.0O + +O and CZ both used the raw SceSpZeroConf-based elfldr constructor, but they did +not use identical artifact bytes and their runtime state was not captured as +identical. O obtained positive handle `1309671680`, registered buffers with +result `0`, then failed the first flip with `-1`. CZ failed earlier at open. +The difference therefore cannot identify a root cause or be attributed to the +new D14 path. Public evidence does not bind `0x80290009` to a stable semantic +name, so this repository records the raw value only. + +What the pair does prove is narrower: raw elfldr can reach the video path, but +it has not produced a usable, repeatable VideoOut/presentation lifecycle. A +third raw attempt would neither isolate the launch-context variable nor be +authorized. + +## BigApp decision + +Official shsrv v0.7 remains the source-proven comparison arm because it +replaces a SystemService-launched BigApp rather than SceSpZeroConf. It has not +been tested by Chimera and is not safe to invoke directly: it requires a +device-resident ELF, can terminate the active BigApp, has unbounded waits and +uses kernel/ptrace and root/jail mutations. Its deployed identity and live +result channel remain unproven. + +A causal comparison requires the exact unchanged CZ artifact, a bounded +BigApp launcher with no persistent writes or remounts, explicit app-termination +scope, bounded cleanup and a separately approved one-shot result channel. +Those prerequisites do not currently coexist in an artifact. No shsrv +connection, hbldr command, staging, execution, retry or other device action is +authorized by this analysis. diff --git a/docs/retroarch/phase-1.0dc-inactive-bigapp-comparison-gate.md b/docs/retroarch/phase-1.0dc-inactive-bigapp-comparison-gate.md new file mode 100644 index 0000000..b1151d7 --- /dev/null +++ b/docs/retroarch/phase-1.0dc-inactive-bigapp-comparison-gate.md @@ -0,0 +1,26 @@ +# Phase 1.0DC: inactive BigApp comparison gate + +Status: `INACTIVE_DUAL_ARTIFACT_BIGAPP_GATE_COMPLETE_TARGET_IMPLEMENTATION_BLOCKED` + +Phase 1.0DC turns the post-CZ gaps into one data-only admission contract. It +does not create target source, a launcher ELF, a network client, a socket, a +CLI or an active approval, and performs no device action. + +Any hypothetical candidate must bind two artifacts independently: a bounded +launcher no larger than 1 MiB and the unchanged CZ canary of 1,845,240 bytes +with SHA-256 `8dadce9d...a9bf95`. Firmware is exactly 9.60, the only title is +the existing v0.7 reference `PPSA01659`, and the result protocol remains +`CHD10AV1` with sole cleanup terminal D14. + +The contract additionally requires a fresh approval hash, a five-minute +window, an attestation that no BigApp is running, explicit acceptance of the +temporary kernel/ptrace effects, and static proof of bounded parent detach, +child cleanup and result-channel completion. It rejects terminating an +existing BigApp, persistent writes, `/system_ex` remount, installation, +autoload, retry, reconnect and fallback titles. + +The tracked instance is entirely inactive and target-free. No current +candidate can pass because there is no bounded launcher artifact and the live +result-channel composition remains unproven. Passing the pure validator in a +future phase would validate record shape only; it would not authorize or +perform execution. diff --git a/docs/retroarch/phase-1.0dd-minimal-launcher-source-prerequisite-audit.md b/docs/retroarch/phase-1.0dd-minimal-launcher-source-prerequisite-audit.md new file mode 100644 index 0000000..562605a --- /dev/null +++ b/docs/retroarch/phase-1.0dd-minimal-launcher-source-prerequisite-audit.md @@ -0,0 +1,41 @@ +# Phase 1.0DD: minimal launcher source-prerequisite audit + +Status: `LAUNCH_EXPORT_NAMES_PRESENT_ABI_AND_FW960_TITLE_CONTRACT_BLOCKED` + +This is an offline read-only audit of the pinned public SDK and official shsrv +v0.7 source. It creates no target source, launcher artifact, transport or +device action. + +The SDK stubs contain export names for foreground-user lookup, running-BigApp +lookup and application launch. They do not contain C declarations, parameter +semantics or the launch-context structure layout. All declarations used by +the candidate—including `app_launch_ctx_t`—are local to the same shsrv v0.7 +source lineage. A symbol name is not an ABI, so this is insufficient evidence +for new Chimera target code. + +The v0.7 source hard-codes `PPSA01659` and labels it VideoPlayer/WebApp. That +proves upstream intent only. It does not prove that the title exists, launches +or supplies VideoOut ownership on firmware 9.60. No currently reviewed, +nonmutating observer can establish that premise. + +A bounded public search on 2026-07-30 targeted the exact function and +structure names. It found a recent +[`sonicloader`](https://git.earthonion.com/soniciso/sonicloader/commits/tag/1.0.48/sonic-loader.elf) +candidate, but that project's +own commit description says the context changes were ported from a supplied +reference snippet. The same project includes persistent/autolaunch scope. It +is therefore derivative evidence, not an independent ABI source, and is not a +permitted Chimera dependency. The search found no qualifying independent +header or firmware-9.60 title contract; this is a bounded result, not a claim +of global absence. + +AF–AU already model bounded ELF admission, mapping rollback, copy/credential +restoration, worker supervision, result records and deadline-driven FD +ownership. Those host models do not supply the missing ABI or firmware +runtime evidence. DC therefore cannot yet bind a launcher hash. + +Target implementation remains blocked until independent public source binds +the three function signatures and launch-context layout, firmware-9.60 title +presence can be established fail-closed, and one target composition proves +absolute deadlines, restoration and the live result channel. Guessing the ABI, +copying v0.7 declarations or invoking hbldr is explicitly excluded. diff --git a/docs/retroarch/phase-1.0de-independent-abi-provenance-search.md b/docs/retroarch/phase-1.0de-independent-abi-provenance-search.md new file mode 100644 index 0000000..947d8b5 --- /dev/null +++ b/docs/retroarch/phase-1.0de-independent-abi-provenance-search.md @@ -0,0 +1,27 @@ +# Phase 1.0DE: independent ABI provenance search + +Status: `NO_INDEPENDENT_ABI_OR_FW960_TITLE_SOURCE_FOUND_IN_BOUNDED_SEARCH` + +Phase 1.0DE broadened the exact-name search beyond the local SDK and shsrv +trees. It used public source hosts only and cloned one candidate read-only +under the ignored `work/` tree. No PS5 connection or device operation occurred. + +The strongest candidate was +[`sonicloader`](https://git.earthonion.com/soniciso/sonicloader). Its initial +import already contains the launch context and function declarations. The +normalized 615-character declaration block is exactly equal to official +shsrv v0.19, and later history describes the `structsize` correction as ported +from a reference snippet. This is lineage reuse, not independent ABI +corroboration. The project also bundles payloads and persistent/autolaunch +features outside Chimera's safety boundary. + +Search results mentioning PS5 Upload Suite did not provide a source-bound, +independent context layout. PS4 SystemService material is not PS5 ABI evidence. +No independent `PPSA01659` firmware-9.60 presence or VideoOut-ownership source +was found in the bounded scope. This does not claim global absence. + +The source gate therefore remains closed. The next permissible step is an +offline design for a fail-closed, read-only title-presence observer. That design +must not reuse shsrv's general shell, enumerate arbitrary paths, launch a title, +terminate an app or write to the device; it does not itself authorize a live +observation. diff --git a/docs/retroarch/phase-1.0df-inactive-title-presence-observer.md b/docs/retroarch/phase-1.0df-inactive-title-presence-observer.md new file mode 100644 index 0000000..fb3f0ac --- /dev/null +++ b/docs/retroarch/phase-1.0df-inactive-title-presence-observer.md @@ -0,0 +1,25 @@ +# Phase 1.0DF: inactive title-presence observer + +Status: `HOST_OBSERVER_CONTRACT_COMPLETE_LIVE_REQUEST_UNFORMABLE` + +Phase 1.0DF implements only a pure host contract and synthetic result model. +It has no socket, target, CLI, clock, file output, target source or artifact. +No PS5 request or other device action occurred. + +The model recognizes two future method classes. `SOURCE_BOUND_QUERY` requires +an independently proven read-only query ABI. `EXACT_PATH_METADATA` requires a +literal absolute path plus separate provenance for that exact mapping. It does +not derive a path from `PPSA01659`, enumerate a directory, use a shell or guess +fallback locations. + +Both methods bind firmware 9.60, title `PPSA01659`, exact request bytes, one +read-only request and at most 4,096 result bytes. Only a complete, explicitly +typed PRESENT or ABSENT response may produce that classification. An error, +EOF, incomplete response or ambiguity always produces UNKNOWN; an error is +never interpreted as title absence. + +The current source set provides neither an independent query ABI nor an exact +title path. A request cannot therefore be formed and the tracked activation is +empty. The synthetic tests demonstrate policy behavior only, not firmware or +device evidence. A live client, shsrv command, title launch, app termination, +device write, retry and reconnect remain forbidden. diff --git a/docs/retroarch/phase-1.0dg-title-presence-primitive-audit.md b/docs/retroarch/phase-1.0dg-title-presence-primitive-audit.md new file mode 100644 index 0000000..30c8924 --- /dev/null +++ b/docs/retroarch/phase-1.0dg-title-presence-primitive-audit.md @@ -0,0 +1,29 @@ +# Phase 1.0DG: title-presence primitive audit + +Status: `OFFLINE_SNAPSHOT_QUERY_PRIMITIVE_SOURCE_BOUND_LIVE_ACQUISITION_BLOCKED` + +This phase audited public and locally pinned source only. No PS5 connection, +request, transfer or execution occurred. + +The pinned official Y2JB source identifies +`/system_data/priv/mms/appinfo.db` and queries `tbl_appinfo` with a bound +`titleId` predicate. This supports one narrow offline primitive: open an +already acquired, byte-exact database snapshot read-only and evaluate +`SELECT 1 FROM tbl_appinfo WHERE titleId = ? LIMIT 1`. The exact minimal query +is an inference from the published schema usage; it is not quoted from Y2JB. + +This does not establish `PPSA01659`. Y2JB's editor names `PPSA01650`, +`PPSA01651` and `PPSA01652`, not the shsrv v0.7 VideoPlayer candidate. It also +retrieves and later replaces the database over FTP and its editor deliberately +backs up and modifies the local copy. Those mutation steps are excluded. + +No accepted path currently acquires a consistent snapshot: the existing +Chimera-audited stack has no binary-safe PS5-to-host readback, live database +access is unproven, and even a read may have unbounded low-level effects such +as access-time changes. Therefore the primitive is usable only if a snapshot +is independently supplied later. Errors, malformed schemas and zero rows may +not be promoted to live firmware evidence. + +The next offline phase may specify a hash-bound snapshot-query contract. It +must contain no acquisition client, network adapter, target address, fallback +path, database write, backup creation or device authorization. diff --git a/docs/retroarch/phase-1.0dh-hash-bound-snapshot-query.md b/docs/retroarch/phase-1.0dh-hash-bound-snapshot-query.md new file mode 100644 index 0000000..cf989db --- /dev/null +++ b/docs/retroarch/phase-1.0dh-hash-bound-snapshot-query.md @@ -0,0 +1,21 @@ +# Phase 1.0DH: hash-bound snapshot query + +Status: `OFFLINE_QUERY_IMPLEMENTED_NO_SNAPSHOT_NO_LIVE_ACQUISITION` + +Phase 1.0DH implements the DG primitive for local snapshots only. It accepts an +exact size and SHA-256 binding, rejects files larger than 64 MiB or without the +SQLite 3 header, then opens the database with `mode=ro&immutable=1` and enables +`PRAGMA query_only`. It never creates a result file. + +The only query is parameterized for fixed title `PPSA01659`. Exactly one row is +`PRESENT`, zero rows is `ABSENT`, and duplicate rows, schema mismatch or SQLite +errors are `UNKNOWN`. Identity and format mismatches are rejected before SQL. + +Synthetic tests create disposable host databases and verify present, absent, +ambiguous and rejected cases, including absence of journal or WAL sidecars. +These tests are not PS5 evidence. No real snapshot is tracked or available. + +The module contains no network adapter, socket, CLI, target source or artifact. +It cannot retrieve the database and authorizes no console operation. The next +offline step is to audit whether a consistent, bounded, binary-safe acquisition +can be formed at all and, if so, define the minimum exact authorization needed. diff --git a/docs/retroarch/phase-1.0di-live-acquisition-boundary.md b/docs/retroarch/phase-1.0di-live-acquisition-boundary.md new file mode 100644 index 0000000..6529a77 --- /dev/null +++ b/docs/retroarch/phase-1.0di-live-acquisition-boundary.md @@ -0,0 +1,25 @@ +# Phase 1.0DI: live-acquisition boundary + +Status: `OFFLINE_WORK_COMPLETE_EXACT_CONSOLE_PERMISSION_REQUIRED` + +The DH query is ready, but no byte-exact `appinfo.db` snapshot exists. The +controlled elfldr and Payload Manager expose no binary-safe readback. Official +shsrv v0.7 has a raw `cat` candidate; audited v0.9-v0.19 routes shell output +through `libtelnet` NVT text framing, which cannot be treated as byte-exact +database transport. The deployed family and exact binary identity are unknown. + +A shell `grep` is not a substitute: finding the title text does not prove that +it is a `tbl_appinfo.titleId` row, while no output cannot prove absence. There +is no further offline input that can select a safe acquisition route. + +The minimum next observation is the already modelled T2 family fingerprint: +one connection to the console's shsrv port 2323, one exact ASCII batch +`help\n`, at most 65,536 received bytes and a ten-second hard deadline. The +collector retains only a sanitized source-family classification, never raw +serial, telemetry or transcript. It performs no file read, transfer, payload +execution, title launch, app termination or device write, and permits no retry +or reconnect. + +The tracked window is inactive and target-free. A live attempt requires a new +exact permission containing a numeric target, run ID and bounded time window. +Approval for earlier elfldr runs does not carry to this shsrv observation. diff --git a/docs/retroarch/phase-1.0dj-di-t2-result.md b/docs/retroarch/phase-1.0dj-di-t2-result.md new file mode 100644 index 0000000..9e46a3a --- /dev/null +++ b/docs/retroarch/phase-1.0dj-di-t2-result.md @@ -0,0 +1,21 @@ +# Phase 1.0DJ: DI-T2 result + +Status: `AUTHORIZED_ATTEMPT_CONSUMED_CONNECT_FAILED` + +The exact `CHIMERA_DI_T2_001` authorization was consumed once. The TCP +handshake to the authorized numeric target on port 2323 failed before a +connection was established. The numeric target was not retained. +No command bytes were sent and no response bytes were received. There was no +retry or reconnect, and no raw transcript, serial or telemetry was retained. + +This result does not establish target reachability, the state of shsrv, its +source family or its exact identity. It does not identify whether the listener +was absent, not yet started, filtered or otherwise unavailable. No file was +read, and no transfer, payload execution or device write occurred. + +The live runner was temporary and removed immediately after the attempt. All +authorization is closed again. Offline analysis cannot distinguish the root +cause. Before another attempt, the operator must independently confirm on the +console/exploit UI that shsrv is running and listening on port 2323; a new +exact one-shot permission is then required. The consumed permission cannot be +reused. diff --git a/docs/retroarch/phase-1.0dk-port-status-result.md b/docs/retroarch/phase-1.0dk-port-status-result.md new file mode 100644 index 0000000..570761c --- /dev/null +++ b/docs/retroarch/phase-1.0dk-port-status-result.md @@ -0,0 +1,18 @@ +# Phase 1.0DK: port-status result + +Status: `AUTHORIZED_STATUS_CHECK_CONSUMED_ELFLDR_OPEN_SHSRV_REFUSED` + +The exact DK authorization was consumed. One TCP handshake was attempted for +each permitted port with a three-second timeout. Port 2323 returned `REFUSED`; +port 9021 accepted the handshake and was immediately closed. No application +bytes were sent or received, and no retry or reconnect occurred. + +This proves only that an elfldr-compatible port was listening and shsrv was not +listening at that instant. It does not identify the port-9021 implementation, +prove that shsrv is absent from storage or explain why its listener is absent. +No transfer, execution or device write was performed. + +The next safe work is offline: bind an official shsrv release artifact to its +source, enumerate its startup and runtime effects, and determine whether a +separately authorized one-shot elfldr launch could remain inside Chimera's +safety boundary. DK grants no such launch authority. diff --git a/docs/retroarch/phase-1.0dl-official-shsrv-artifact-audit.md b/docs/retroarch/phase-1.0dl-official-shsrv-artifact-audit.md new file mode 100644 index 0000000..5d98c30 --- /dev/null +++ b/docs/retroarch/phase-1.0dl-official-shsrv-artifact-audit.md @@ -0,0 +1,23 @@ +# Phase 1.0DL: official shsrv artifact audit + +Status: `OFFICIAL_ARTIFACT_BOUND_EXECUTION_BLOCKED_UNBOUNDED_AND_RWE` + +The official v0.19 release artifact was downloaded only to ignored `work/` +storage and bound to source commit `6f320637d56d344a0e7797753099e33238bbf146`. +It is 981,872 bytes with SHA-256 +`797827b50a93dbf327abdca45063d76ea24b17e561d927f397a0b73cadb2e2bd`. +It was inspected as bytes and was neither executed on the host nor sent to the +console. + +The ELF has an `RWE` load segment. Source inspection also proves an unbounded +accept loop, unbounded process lifetime, per-connection threads, `/dev/console` +opened read/write and a startup notification. It has no automatic termination +deadline. Those properties violate Chimera's bounded-effect requirements even +though startup does not require a persistent filesystem write. + +The official binary is therefore transfer- and execution-ineligible. Exact +provenance does not make its runtime safe. The safer route is a new offline +design for a single-purpose raw-elfldr payload: fixed read-only database path, +strict byte ceiling, inherited result descriptor only, no listener, one result +stream and terminal return. That design itself grants no build or device +authority. diff --git a/docs/retroarch/phase-1.0dm-bounded-snapshot-observer.md b/docs/retroarch/phase-1.0dm-bounded-snapshot-observer.md new file mode 100644 index 0000000..f565e81 --- /dev/null +++ b/docs/retroarch/phase-1.0dm-bounded-snapshot-observer.md @@ -0,0 +1,27 @@ +# Phase 1.0DM: bounded snapshot observer + +Status: `OFFLINE_ARTIFACT_BUILT_AUDITED_INACTIVE` + +DM replaces the rejected daemon route with one single-purpose raw-elfldr +process. It opens only `/system_data/priv/mms/appinfo.db` using `O_RDONLY`, +`O_CLOEXEC` and `O_NOFOLLOW`, rejects non-regular, empty or larger-than-64-MiB +objects, and transmits only over inherited stdout. It creates no socket, +listener, connection or filesystem output on the target. + +The stream is `CHS10DM1` begin frame, exact declared file bytes and a terminal +frame. Metadata is checked before and after. A process-wide 20-second alarm +interrupts reads or sends; short progress is continued, while errors terminate +without reconnect or a second file open. The host parser requires exact length, +matching terminal metadata and an SQLite 3 header, then computes SHA-256. + +The stock SDK linker script marks text RWE. The build tool accepts only the +pinned v0.41 script hash and mechanically derives a build-local variant whose +text segment is RE. The resulting 109,896-byte ELF has load flags RE/RW/RW and +SHA-256 `147b5bede0f0b5b7d2be903bc72ff0d0541a2cdc28eae7d86b6bf95e1978ebdf`; +two consecutive builds produced identical ELF bytes. Linker maps differ in +non-runtime build metadata and are not identity evidence. + +Normal SDK CRT effects still apply, and runtime/cleanup are unproven. The ELF +is therefore inactive and transfer-/execution-ineligible. The one-shot host +runner and consumed-receipt gate exist but its tracked activation is inert; a +separate exact approval remains mandatory before any socket is opened. diff --git a/docs/retroarch/phase-1.0dn-inactive-snapshot-receiver.md b/docs/retroarch/phase-1.0dn-inactive-snapshot-receiver.md new file mode 100644 index 0000000..671fc61 --- /dev/null +++ b/docs/retroarch/phase-1.0dn-inactive-snapshot-receiver.md @@ -0,0 +1,15 @@ +# Phase 1.0DN: inactive snapshot receiver + +Status: `OFFLINE_STREAM_RECEIVER_COMPLETE_LIVE_ADAPTER_ABSENT` + +DN adds a network-free streaming receiver for `CHS10DM1`. It creates one new +host output exclusively, hashes payload bytes incrementally, bounds them to +64 MiB and requires exact terminal metadata plus the SQLite 3 header. It +flushes and `fsync`s before returning success. Truncation, extra bytes, +metadata mismatch and an existing destination fail closed; partial output is +never classified as a snapshot. + +The receiver has no socket or target and the tracked activation is empty. The +next phase may compose it with a manifest- and separate-approval-gated live +adapter, including receipt-before-connect and one-shot enforcement. DN grants +no console, file-read, transfer, execution or result-reception authority. diff --git a/docs/retroarch/phase-1.0do-inactive-snapshot-runner.md b/docs/retroarch/phase-1.0do-inactive-snapshot-runner.md new file mode 100644 index 0000000..1c725dc --- /dev/null +++ b/docs/retroarch/phase-1.0do-inactive-snapshot-runner.md @@ -0,0 +1,15 @@ +# Phase 1.0DO: inactive one-shot snapshot runner + +Status: `OFFLINE_RUNNER_COMPLETE_INACTIVE` + +The runner pins the 109,896-byte DM artifact and its SHA-256, accepts only a +numeric IPv4 target on port 9021, and requires byte-identical activation and +separate approval records. It creates and flushes an exclusive consumed +receipt before constructing a socket. It then permits exactly one connection, +one complete ELF send, one write-half-close, and at most 67,108,992 received +bytes within a 30-second total result deadline. The snapshot itself is created +exclusively and sealed only after an exact terminal frame and SQLite header. + +The tracked manifest and approval template are deliberately inactive. They do +not contain a target, run ID, paths, time window, or authority. Host tests do +not constitute PS5 evidence. No console action is authorized by this phase. diff --git a/docs/retroarch/phase-1.0do-snapshot-result.md b/docs/retroarch/phase-1.0do-snapshot-result.md new file mode 100644 index 0000000..4609cca --- /dev/null +++ b/docs/retroarch/phase-1.0do-snapshot-result.md @@ -0,0 +1,19 @@ +# Phase 1.0DO snapshot result + +Status: `SUCCESS_AUTHORIZATION_CONSUMED` + +Run `CHIMERA_1_0DO_001` used the exact 109,896-byte observer once. The host +made one connection, sent the ELF once, closed its write half once and received +one complete framed result. No retry or reconnect occurred. + +The resulting ignored snapshot is 1,306,624 bytes with SHA-256 +`5a66279266c27228431f7d8acb9c70a803fba8912bd6285cf36a1da2f2210fcf`. +SQLite reports integrity `ok`, three tables, 5,803 appinfo rows and 69 distinct +title IDs. Metadata records exist for `FAKE00000` (Homebrew Launcher), +`PLDM00001` (Payload Manager), and `ITEM00001` (Itemzflow Game Manager). + +This is exact evidence of installed appinfo metadata only. It does not prove +which title was active, the bytes of the installed launcher, a deployed shsrv +or hbldr identity, VideoOut ownership, visible output, or complete cleanup. +The numeric target and database contents are not retained in Git. The approval +is consumed and grants no retry or subsequent device action. diff --git a/docs/retroarch/phase-1.0dp-listener-status-result.md b/docs/retroarch/phase-1.0dp-listener-status-result.md new file mode 100644 index 0000000..977df1b --- /dev/null +++ b/docs/retroarch/phase-1.0dp-listener-status-result.md @@ -0,0 +1,15 @@ +# Phase 1.0DP listener-status result + +Status: `REFUSED_AUTHORIZATION_CONSUMED` + +After Homebrew Launcher displayed its local-page error, run +`CHIMERA_1_0DP_001` made one authorized TCP status attempt to port 2323. The +connection was refused. The host sent and received no application bytes and +performed no retry or reconnect. + +This proves only that port 2323 accepted no connection during that attempt. It +does not identify the installed launcher bytes, establish why its local page +failed, or prove that a listener could not exist at another time. The next +work is offline preparation of a bounded read-only inventory observer for the +appinfo-proven `/user/app/FAKE00000` path. No further device action is +authorized. diff --git a/docs/retroarch/phase-1.0dq-bounded-fake00000-inventory.md b/docs/retroarch/phase-1.0dq-bounded-fake00000-inventory.md new file mode 100644 index 0000000..4ca898e --- /dev/null +++ b/docs/retroarch/phase-1.0dq-bounded-fake00000-inventory.md @@ -0,0 +1,22 @@ +# Phase 1.0DQ: bounded FAKE00000 inventory observer + +Status: `OFFLINE_ARTIFACT_BUILT_AUDITED_INACTIVE` + +DQ enumerates only the immediate children of the appinfo-proven directory +`/user/app/FAKE00000`. It emits at most 256 fixed 320-byte records containing +name, directory type, mode, size, inode and modification time. `lstat` avoids +following symlinks. The observer neither recurses nor reads file contents and +has a ten-second process alarm. + +The exact ignored ELF is 110,032 bytes with SHA-256 +`914fce06a490ad048fdd0a85ae117858e8904b47c72054bf12fbfebc213a6db8`. +Its W^X load flags and reproducibility are audited offline. Normal SDK CRT +temporary effects remain applicable. Directory enumeration may also update +access-time metadata depending on the target filesystem, so any future exact +permission must acknowledge that possible low-level effect. + +The DR host runner is inactive and pins the artifact, one connection, one +transfer, one execution, a 20-second host deadline and at most 82,048 result +bytes. It creates an exclusive consumed receipt before socket construction and +never permits retry, reconnect, installation, autoload, persistent write or +file-content read. No console action is authorized by this phase. diff --git a/docs/retroarch/phase-1.0dr-inventory-result.md b/docs/retroarch/phase-1.0dr-inventory-result.md new file mode 100644 index 0000000..8ea4e22 --- /dev/null +++ b/docs/retroarch/phase-1.0dr-inventory-result.md @@ -0,0 +1,15 @@ +# Phase 1.0DR inventory result + +Status: `SUCCESS_AUTHORIZATION_CONSUMED` + +The single authorized run returned six direct metadata records for +`/user/app/FAKE00000`: `app.pbm` (323 bytes), `app.pbm.backup` (323), +`app.json` (257), `app.xml` (298), `app.crc` (1,108), and `app.pkg` +(18,153,472). No loose `eboot.bin` was present. No file contents were read, +and no retry or reconnect occurred. + +This proves a packaged launcher layout at the appinfo path, not package +contents, the executable identity, active launch state, or why the local page +failed. The authorization is consumed. A later observer may read only the +four small configuration/metadata files under separate exact permission while +explicitly excluding `app.pkg` and `app.pbm.backup`. diff --git a/docs/retroarch/phase-1.0ds-bounded-fake00000-metadata.md b/docs/retroarch/phase-1.0ds-bounded-fake00000-metadata.md new file mode 100644 index 0000000..a64911f --- /dev/null +++ b/docs/retroarch/phase-1.0ds-bounded-fake00000-metadata.md @@ -0,0 +1,14 @@ +# Phase 1.0DS/DT: bounded FAKE00000 metadata observer + +Status: `OFFLINE_ARTIFACT_BUILT_AUDITED_INACTIVE` + +DS reads exactly `app.pbm`, `app.json`, `app.xml`, and `app.crc` from the +proven `/user/app/FAKE00000` directory. Each no-follow regular file is capped +at 4,096 bytes and checked for metadata stability. `app.pkg` and +`app.pbm.backup` are excluded by construction. The exact 109,928-byte ELF has +SHA-256 `077307b98e44f566fa1db82b08cd5e71bd56bd9792826fc7254965fa768c0dc7`. + +DT permits at most 16,768 result bytes and creates an exclusive consumed +receipt before its one socket. Its tracked records are inactive and target- +free. Normal SDK CRT effects and possible read-atime effects remain disclosed; +no device action is authorized. diff --git a/docs/retroarch/phase-1.0dt-metadata-result.md b/docs/retroarch/phase-1.0dt-metadata-result.md new file mode 100644 index 0000000..a18cf46 --- /dev/null +++ b/docs/retroarch/phase-1.0dt-metadata-result.md @@ -0,0 +1,15 @@ +# Phase 1.0DT metadata result + +Status: `SUCCESS_AUTHORIZATION_CONSUMED` + +The one authorized run returned the four exact allowlisted metadata files. The +ignored `app.json` identifies a one-piece fake package whose original size is +18,153,472 bytes and whose source URL is the exact path +`/mnt/usb0/IV9999-FAKE00000_00-HOMEBREWLOADER01.pkg`. The PBM contains the same +package digest. `app.pkg`, the backup and every other path remained unread. + +This strongly indicates that the Homebrew Launcher entry depends on a USB +package source or its mapping. It does not yet prove whether that USB path +exists, whether the local `app.pkg` is sparse/mapped, or whether the failure is +later in launcher startup. The DT authorization is consumed. The minimal next +observation is `lstat` only on the exact local and USB package paths. diff --git a/docs/retroarch/phase-1.0du-exact-package-stat.md b/docs/retroarch/phase-1.0du-exact-package-stat.md new file mode 100644 index 0000000..8e6e22b --- /dev/null +++ b/docs/retroarch/phase-1.0du-exact-package-stat.md @@ -0,0 +1,7 @@ +# Phase 1.0DU/DV exact package stat + +The exact 109,688-byte ELF (SHA-256 +`25b972ac202050ab5a9c1c89651b27e841cdf788d19c93bf75f051c9c8acd403`) +performs `lstat` only on the local `app.pkg` and the exact USB source named by +the consumed DT metadata. It emits two fixed 72-byte records and reads no file +content. The runner is inactive and target-free; no device action is authorized. diff --git a/docs/retroarch/phase-1.0dv-package-stat-result.md b/docs/retroarch/phase-1.0dv-package-stat-result.md new file mode 100644 index 0000000..7ef7a40 --- /dev/null +++ b/docs/retroarch/phase-1.0dv-package-stat-result.md @@ -0,0 +1,14 @@ +# Phase 1.0DV package-stat result + +Status: `USB_SOURCE_MISSING_AUTHORIZATION_CONSUMED` + +The single authorized run proved that `/user/app/FAKE00000/app.pkg` exists and +is exactly 18,153,472 bytes. The exact USB source path recorded by `app.json` +returned `ENOENT` (errno 2). No package bytes were read and no retry or +reconnect occurred. + +This establishes the immediate launcher failure: the fake-package metadata +depends on a USB0 source file that is absent. DNS is not the primary blocker. +Before using the local package as a replacement, a separately authorized +read-only readback must prove its complete SHA-256 matches the package digest +already recorded in `app.json`. The DV authorization is consumed. diff --git a/docs/retroarch/phase-1.0dw-exact-package-readback.md b/docs/retroarch/phase-1.0dw-exact-package-readback.md new file mode 100644 index 0000000..952f80b --- /dev/null +++ b/docs/retroarch/phase-1.0dw-exact-package-readback.md @@ -0,0 +1,9 @@ +# Phase 1.0DW/DX exact package readback + +The exact 109,904-byte observer has SHA-256 +`b41763f9261b838be2f83b0ee1c7b9bb5e07ced856e5638e3357de37e4405394`. +It reads only the stable, no-follow, exactly 18,153,472-byte local `app.pkg`. +The inactive DX runner accepts success only when the received bytes hash to +`dbcdd4dbc6303fc7a94aa0e8bb3e2c7de1d8b5770c6a30cf1ce50bc6e373aa7e`. +It creates an exclusive consumed receipt and output. No device action is +authorized. diff --git a/docs/retroarch/phase-1.0dx-official-package-correlation.md b/docs/retroarch/phase-1.0dx-official-package-correlation.md new file mode 100644 index 0000000..b19508c --- /dev/null +++ b/docs/retroarch/phase-1.0dx-official-package-correlation.md @@ -0,0 +1,20 @@ +# Phase 1.0DX official package correlation + +Status: `EXPECTED_PACKAGE_NOT_FOUND_IN_OFFICIAL_HISTORY` + +The public GPL-3.0-or-later `ps5-payload-dev/websrv` repository was bound at +master commit `b525c5921ff9da4e9e2a7386fb3e52a5940b9271`. Its current launcher +package is 18,168,015 bytes with SHA-256 +`c737b79be8c47b89b2e4e38ca404cdad6d30c0ff659aaf25c7e9ea1368341434`. + +The complete Git path history contains one earlier package blob, commit +`c8d94bdbc65cc3086261a008e2aaa48f4bda296c`, of 18,954,495 bytes with SHA-256 +`fec3080fd59aed6c1d5b0b887c7e97090029853756ff5cbc21ed7a06cba7fa50`. +The later blob was introduced by commit +`86b52cf42b104e935f0026987d254d3f963a622e` and is byte-identical to current +master. Neither matches the installed metadata size or digest. + +Downloaded correlation copies remain ignored. No community mirror was +promoted, and no package was transferred or executed. The exact original +package required by this installation is not recoverable from accepted +official history currently available. diff --git a/docs/retroarch/phase-1.0dx-package-readback-result.md b/docs/retroarch/phase-1.0dx-package-readback-result.md new file mode 100644 index 0000000..45248e3 --- /dev/null +++ b/docs/retroarch/phase-1.0dx-package-readback-result.md @@ -0,0 +1,16 @@ +# Phase 1.0DX package-readback result + +Status: `COMPLETE_BYTES_DIGEST_MISMATCH_AUTHORIZATION_CONSUMED` + +The single authorized run received exactly 18,153,472 bytes from the local +`app.pkg`. Their SHA-256 is +`604e5b56033bd48dd72b68b6844d72c4d9a730e7ed8ce2577fe4b0d6538a667b`, +which differs from the metadata-bound expected SHA-256 +`dbcdd4dbc6303fc7a94aa0e8bb3e2c7de1d8b5770c6a30cf1ce50bc6e373aa7e`. +No retry or reconnect occurred. + +The complete host copy remains ignored and was renamed with +`MISMATCH_DO_NOT_USE`. It must not be copied to USB or treated as recovery +material. The fake-package installation requires an independently obtained +original package matching the expected digest. The DX authorization is +consumed and grants no further device action. diff --git a/docs/retroarch/phase-1.0dy-upstream-websrv-audit.md b/docs/retroarch/phase-1.0dy-upstream-websrv-audit.md new file mode 100644 index 0000000..1f9bd33 --- /dev/null +++ b/docs/retroarch/phase-1.0dy-upstream-websrv-audit.md @@ -0,0 +1,17 @@ +# Phase 1.0DY upstream websrv audit + +Status: `UPSTREAM_WEBSRV_EXECUTION_BLOCKED_BROAD_MUTATING_SURFACE` + +The current official release is v0.33, source commit +`baabe27e5449baeb059b850d0393c31fdee219b7`. Its source does create the +documented port-8080 listener, but it also exposes generic filesystem and POST +routes and links package installation, fake-app creation, ptrace, +kernel/credential mutation, RWX changes and launch machinery without the +bounded Chimera lifecycle contract. + +Starting this upstream artifact would materially exceed a request to restore +the launcher page and violates the persistent-write and unknown-effect hard +stops. It is therefore not built, downloaded as an executable, transferred or +authorized. A future offline phase must remove generic endpoints, bind one +exact RetroArch artifact, prohibit persistent setup and bound every wait and +cleanup path before a new hardware request can be considered. diff --git a/docs/retroarch/phase-1.0dy-websrv-listener-result.md b/docs/retroarch/phase-1.0dy-websrv-listener-result.md new file mode 100644 index 0000000..8272def --- /dev/null +++ b/docs/retroarch/phase-1.0dy-websrv-listener-result.md @@ -0,0 +1,13 @@ +# Phase 1.0DY websrv-listener result + +Status: `REFUSED_AUTHORIZATION_CONSUMED` + +The single authorized TCP status attempt to port 8080 was refused. No +application data was sent or received and there was no retry or reconnect. +This explains the launcher's local connection error: the separately required +websrv listener was not running at that moment. + +It does not authorize starting upstream websrv. The public server exposes +broad filesystem, network and mutating endpoints, so its exact source, +artifact and startup effects require an offline audit before any later device +request. The DY authorization is consumed. diff --git a/docs/retroarch/phase-1.0dz-direct-launcher-decision.md b/docs/retroarch/phase-1.0dz-direct-launcher-decision.md new file mode 100644 index 0000000..6872e87 --- /dev/null +++ b/docs/retroarch/phase-1.0dz-direct-launcher-decision.md @@ -0,0 +1,50 @@ +# Phase 1.0DZ: direct-launcher decision + +Status: `TITLE_PRESENT_DIRECT_ROUTE_SELECTED_TARGET_BUILD_BLOCKED` + +This offline consolidation incorporates the consumed Phase 1.0DO through DY +observations and the official websrv v0.33 source audit. It performs no PS5 +operation and creates no target source or artifact. + +The firmware-9.60 snapshot proves an installed appinfo record for +`FAKE00000`. The operator also launched the Homebrew Launcher far enough to +observe its local-page error. These facts close only the title-presence and +operator-observed browser-reachability premise. They do not prove launch ABI, +VideoOut ownership, executable identity, safe process replacement or cleanup. +The refused port-8080 attempt proves only that no listener accepted that one +connection. + +The official websrv is not a suitable dependency. Its generic filesystem and +POST routes, package installation, persistent fake-app creation, remount, +ptrace, credential/kernel writes, RWX transitions and unbounded waits exceed +the Chimera contract. A loopback HTTP server is also unnecessary: the desired +end state can be represented more narrowly as one launcher containing one +exact, hash-bound RetroArch ELF and no network service. + +## Minimal future contract + +- exact firmware `9.60`, exact existing title `FAKE00000` and one exact + embedded RetroArch artifact; +- no HTTP listener, generic endpoint, device-side payload file or fallback; +- no fake-app creation, remount, installation, autoload or persistent write; +- fail closed if a BigApp is already active; never terminate one implicitly; +- one launch and one replacement transaction with absolute deadlines, + restoration, a bounded result channel and no retry or reconnect; +- consumed receipt before a separately authorized console connection. + +This is an architecture contract, not implementation permission. Target source +and a build remain blocked because public SDK stubs provide export names but +not independently corroborated PS5 function declarations, parameter semantics +or `app_launch_ctx_t` layout. The available declarations share the shsrv +lineage. The reviewed implementation lineage also does not provide a complete, +bounded and source-proven failure restoration/cleanup composition suitable for +the contract above. Copying those declarations or merely replacing unbounded +loops with guessed timeouts would not close either gap. + +## Next step + +Obtain independent public, firmware-applicable evidence for the three launch +function signatures and launch-context layout, then perform a separate offline +composition proof for every ptrace, credential, mapping, process and cleanup +edge. Only after both gates pass may a hash-bound target artifact be built and +audited; console permission would then be requested separately. diff --git a/docs/retroarch/phase-1.0e-device-observations.md b/docs/retroarch/phase-1.0e-device-observations.md new file mode 100644 index 0000000..9aca228 --- /dev/null +++ b/docs/retroarch/phase-1.0e-device-observations.md @@ -0,0 +1,27 @@ +# Phase 1.0C/D/E device observations + +Evidence class: **operator-observed, artifact-bound hardware evidence**. + +Three separately authorized one-shot runs have occurred. Every authorization +is consumed and none authorizes another connection, transfer or execution. + +| Run | Exact artifact | Host action | Operator observation | Proven boundary | +|---|---|---|---|---| +| A | `chimera_ps5_crt_entry_canary.elf`, 106,344 bytes, `c84c0fb43392ce17abc1e7e6be38b522f1b32a681fd516d684c3dbbf7b34b743` | one connection and one complete send; no retry | `CHIMERA C1 — CRT MAIN REACHED` visibly appeared | elfldr reached SDK `_start`, CRT completed, `main` and the notification ABI executed on firmware 9.60 | +| B | `retroarch_ps5_early_diag.elf`, 1,844,664 bytes, `092bcabc0f8717a648030c592aa0dc900f878952fb14ee453851a29f298ea7ee` | one connection and one complete send; no retry | one notification appeared too briefly to identify its D-code; console remained responsive | some notification-producing payload code ran, but the exact D-stage is **UNCLASSIFIED** | +| C | `retroarch_ps5_result_diag.elf`, 1,844,880 bytes, `1049c78099a60b472a3fb0e2999e3393b6ad76337a28532a7e53872e7772dedf` | one connection, complete send, write-half-close and bounded receive; no retry/reconnect | three binary frames validated, then remote EOF | D00 `main`, D01 platform-init begin and D02 platform-init result `0`; inherited stdout worked; incomplete before D03 | + +The ignored local host traces bind each send to its artifact and show no retry. +They contain the session address and are deliberately not committed. RUN C's +trace is 1,795 bytes with SHA-256 +`4ff27a0eac48283cdc4c7ff964226def2689e808e3adea6594d0e77674a676f0`. +It records one connect, one `sendall`, one `shutdown(SHUT_WR)`, five receives, +3,413 received bytes, 3,221 discarded non-frame stdout bytes, remote EOF, zero +parser errors, zero retries and zero reconnects. + +The three valid frames had sequences 1-3 and CRC-32 values `93f81433`, +`f2b975e4` and `69173d14`. All reported notification result `0`; D02 carried +`raw0=0`. In source, D03 is emitted at entry to `sdl2_gfx_init()`. Its absence +means SDL-init begin, SDL/VideoOut, first flip, rendering, terminal status, +cleanup and safe exit remain **UNPROVEN**. The unretained stdout bytes cannot +be used to infer why execution ended before D03. diff --git a/docs/retroarch/phase-1.0e-inherited-result-channel.md b/docs/retroarch/phase-1.0e-inherited-result-channel.md new file mode 100644 index 0000000..c2f3cc2 --- /dev/null +++ b/docs/retroarch/phase-1.0e-inherited-result-channel.md @@ -0,0 +1,104 @@ +# Phase 1.0E inherited result channel + +Status: **ONE-SHOT TEST CONSUMED; INCOMPLETE BEFORE D03; NO DEVICE ACTION AUTHORIZED**. + +## Source-bound transport + +Hardened elfldr commit `197623058f509eddde18868dafcb92fdcac66464` +provides a duplex channel only on its legacy raw-ELF route: + +- `socksrv.c:335-339` invokes `elfldr_read` for raw ELF input; +- `elfldr.c:837-923` derives and reads the exact ELF extent, so socket EOF is + not the input framing contract; +- `socksrv.c:139-141` passes the accepted descriptor to `elfldr_spawn` for the + legacy route but passes `-1` for the controlled route; +- `elfldr.c:499-520` duplicates that descriptor to payload stdin, stdout and + stderr; and +- `socksrv.c:421-423` closes the parent copy only after spawn returns. + +The Phase-1.0E profile therefore writes results to inherited stdout. It adds no +target-side `socket`, `connect`, `bind`, `listen`, `accept` or `recv`, no target +address or port, and no loader change. This finding does **not** apply to the +controlled route, whose `stdio=-1` contract remains intentionally outputless. + +## Target frame and write behavior + +After each first D00-D12 notification attempt, the target performs exactly one +nonblocking, signal-suppressed operation: + +```c +send(STDOUT_FILENO, frame, 64, MSG_DONTWAIT | MSG_NOSIGNAL) +``` + +There is no send loop or retry. A short write or error is retained only in +process memory and cannot block the pre-existing notification/diagnostic path. +D11 and D12 are terminal frames. Each 64-byte big-endian frame contains magic +`CHD10E01`, version, size, strict sequence, stage, kind, flags, raw results, +notification result/masks, twenty zero bytes and CRC-32 over bytes 0-59. + +## Host behavior + +The host tool validates exact ELF size and SHA-256 before socket creation. It +then permits one connect, one `sendall`, one `shutdown(SHUT_WR)`, bounded +receive of at most 65,536 bytes, and one close. It never retries, reconnects, +resumes or overwrites an existing trace. The half-close ends only host-to-PS5 +input; it leaves payload stdout readable on the same descriptor. + +The parser rejects invalid magic/version/size, CRC, reserved bytes, stage, +kind, flags or sequence. EOF, timeout or the byte ceiling without a valid +terminal frame is incomplete evidence. A valid frame proves code output from +the transferred payload process on that connection; it does not prove safe +exit, loader cleanup or an unreported graphics boundary. + +## Static artifact result + +`retroarch_ps5_result_diag.elf` is 1,844,880 bytes with SHA-256 +`1049c78099a60b472a3fb0e2999e3393b6ad76337a28532a7e53872e7772dedf`. +Two clean builds from chimera-retroarch commit +`b9fc037304a14199f35f8229edac26fa5c840509` produced byte-identical ELF and +map files. Compared with RUN B, the complete undefined-symbol set changes only +by adding `send`. + +The ELF retains the ordinary SDK v0.41 CRT and Phase-1.0D SDL/VideoOut path. +Disassembly proves `_start` calls `__patch_init`; the statically linked CRT +contains its credential/capability and syscall-range helpers. Those bounded, +temporary runtime effects are not hidden or reclassified by this channel. +No RWX load segment, TLS or non-empty init/fini array exists. + +## Consumed RUN C result + +The exact one-shot permission was consumed on 2026-07-22. The host verified +the authorized 1,844,880-byte artifact before connecting, used one connection, +one `sendall` and one write-half-close, then received 3,413 bytes before remote +EOF. There was no retry, reconnect, parser error or timeout. + +Three 64-byte frames validated: D00, D01 and D02 in strict sequence. D02's +`raw0=0` proves the bounded in-memory PS5 platform/frontend initialization +marks succeeded. The result channel itself is therefore proven on firmware +9.60 for those stages. No D03 or terminal frame arrived. Because D03 is the +first operation in `sdl2_gfx_init()`, this result does not prove entry into the +SDL video driver, SDL initialization, VideoOut, a flip, rendering, cleanup or +safe exit. Remote EOF is an observed transport boundary, not proof of normal +process return. + +The ignored local trace is bound by size 1,795 and SHA-256 +`4ff27a0eac48283cdc4c7ff964226def2689e808e3adea6594d0e77674a676f0`. +It is not committed because it contains the session address. All authorization +fields are false after this consumed attempt. + +## Offline D02-to-D03 follow-up + +Source inspection bounds the unobserved interval but does not identify a root +cause. After D02, RetroArch still performs synchronous content-load setup, +argument/config processing, driver selection, static-core/content init and the +entry into `drivers_init()`. D03 is the first instrumented operation in +`sdl2_gfx_init()`. Any earlier failure or external termination can therefore +produce the observed boundary. + +Chimera-retroarch commit `f1391c3e6717ff4e2b869007fb9627c9edde52e4` +updates only the host receiver, its tests and documentation. Future traces can +retain the full bounded receive stream and non-frame stdout losslessly as +base64 with byte counts and SHA-256 values. The pre-encoding limit remains +65,536 bytes and trace creation remains exclusive. The client explicitly +rejects the consumed RUN-C state. No target code, target artifact, device +action or authorization was added. diff --git a/docs/retroarch/phase-1.0e-next-device-test.md b/docs/retroarch/phase-1.0e-next-device-test.md new file mode 100644 index 0000000..0f21eaf --- /dev/null +++ b/docs/retroarch/phase-1.0e-next-device-test.md @@ -0,0 +1,38 @@ +# Phase 1.0E consumed one-shot result test + +Current state: + +```text +ps5_connection_authorized=false +device_transfer_authorized=false +device_execution_authorized=false +result_receive_authorized=false +installation_authorized=false +autoload_authorized=false +device_write_authorized=false +automatic_retry=false +``` + +The consumed permission named exactly `retroarch_ps5_result_diag.elf`, size +1,844,880 bytes and SHA-256 +`1049c78099a60b472a3fb0e2999e3393b6ad76337a28532a7e53872e7772dedf`. +It authorized exactly one connection, one in-memory raw-ELF transfer, one +execution, result reception on that same inherited connection, one new host +trace, and one bounded observation deadline. Those actions have occurred. + +It kept installation, autoload, persistent staging, Payload Manager +upload, live replacement, device cleanup, retry, reconnect, resume, a second +transfer/execution, reboot and every other artifact forbidden. The trace path +must not already exist. Failure, EOF or timeout ends the attempt; it never +causes a retry. + +The machine classification is: + +- `REMOTE_PAYLOAD_OUTPUT_PROVEN_INCOMPLETE_BEFORE_D03`; +- last proven stage D02 with platform result `0`; +- remote EOF without D03 or a terminal D11/D12 frame; and +- zero retry and zero reconnect. + +The first connection attempt consumed this permission. The artifact is now +`execution_eligible=false` and `transfer_eligible=false`. A later test would +require a new exact artifact-specific approval; none exists. diff --git a/docs/retroarch/phase-1.0ea-expanded-launch-evidence.md b/docs/retroarch/phase-1.0ea-expanded-launch-evidence.md new file mode 100644 index 0000000..6e63f98 --- /dev/null +++ b/docs/retroarch/phase-1.0ea-expanded-launch-evidence.md @@ -0,0 +1,59 @@ +# Phase 1.0EA: expanded launch evidence and cleanup audit + +Status: `NEW_FW960_LNC_CANDIDATE_FOUND_ABI_AND_CLEANUP_GATES_STILL_BLOCKED` + +This phase is a bounded public-source and offline source-history audit. It +creates no target source or artifact and performs no PS5 operation. + +## Newly reviewed candidate + +The public `phantomptr/ps5upload` repository was bound at commit +`978add1864eb54351eaddbd242634282ef3cd120`. Its current ShellUI RPC source is +Git blob `b30d17bd0068affd5b4d42550953bc6bd44203f7`; its ptrace implementation is +blob `6b68f26995f746df5b2b0b9fd1495432304e8f35`. + +Commit `e3875ffba7cb08e017dcdb537d045af3fddc133d` first introduced the reviewed +route and states that game launch through `sceLncUtilLaunchApp` worked on +firmware 9.60. The implementation constructs a packed 24-byte local parameter +with size, user ID, options, crash-report and check fields, then remotely calls +the export from `SceShellUI`. This is useful independent implementation and +operator-report evidence for a different launch API. It is not independent ABI +provenance: the introducing commit supplies no public specification, header, +binary-derived layout proof or second implementation lineage for that +parameter layout. Exact-name searches for the layout fields produced no other +qualifying public source. + +The route also does not satisfy the Chimera end state. It starts a registered +title; it does not bind or replace that title's process with the exact embedded +RetroArch ELF. The previously reviewed shsrv replacement lineage would still +be required for that second operation, so finding the LNC launch candidate does +not remove the replacement ABI or composition gates. + +## Cleanup findings + +The candidate cannot be imported as a bounded lifecycle implementation: + +- attach, remote-call and single-step paths use blocking `waitpid` without an + absolute deadline; +- launch cleanup discards `pt_munmap` and `pt_detach` results; +- the tracked-attached flag is cleared even when detach fails; +- a dispatched call with unreadable completion is promoted to soft success; +- register restoration and authid restoration include best-effort failures; +- an authid restore failure is logged and execution continues with elevated + process state until a later operation might repair it; +- its retry/fallback semantics exceed the Chimera one-shot contract. + +These are source facts about the candidate, not claims about an observed PS5 +failure. The existing Chimera host ownership models remain useful, but no +source-bound target composition currently replaces these failure behaviors +with absolute deadlines, verified restoration and terminal ownership. + +## Decision and next step + +The direct embedded route selected by DZ remains the minimum architecture, but +target source and build remain blocked. The next gate requires either an +independent public specification or two demonstrably independent, +firmware-applicable implementations that agree on the complete launch and +replacement ABI, followed by a target-source composition whose every blocking +wait, mapping, register, credential, attach and detach edge has a deadline and +verified cleanup result. Console permission is not yet actionable. diff --git a/docs/retroarch/phase-1.0eb-public-launch-matrix.md b/docs/retroarch/phase-1.0eb-public-launch-matrix.md new file mode 100644 index 0000000..031b2c4 --- /dev/null +++ b/docs/retroarch/phase-1.0eb-public-launch-matrix.md @@ -0,0 +1,65 @@ +# Phase 1.0EB: public launch and replacement source matrix + +Status: `PUBLIC_SOURCE_MATRIX_EXHAUSTED_EXTERNAL_ABI_EVIDENCE_REQUIRED` + +This offline phase compares every qualifying public candidate found in the +bounded searches through 2026-07-30. Clones remain under ignored `build/` +paths. No candidate program or binary was executed, no proprietary library was +opened, and no PS5 operation occurred. + +## Source matrix + +| Lineage | Launch contract | ELF replacement | Firmware 9.60 evidence | Result | +|---|---|---|---|---| +| official shsrv/websrv | local `sceSystemServiceLaunchApp` and `app_launch_ctx_t` declarations | BigApp replacement | deployed identity and safe behavior unproven | architecture reference only | +| sonicloader | declaration block derives from shsrv | copies same replacement lineage | operator claims only | not independent | +| phantomptr/ps5upload | `sceLncUtilLaunchApp`, packed 24-byte local parameter | none | source author reports launch success | independent implementation, no ABI provenance | +| manos555555/PS5-Upload-Suite | `sceLncUtilLaunchApp` with guessed 256-byte block, then NULL and SystemService fallbacks | none | no exact result contract | contradicts 24-byte candidate; probing code, not ABI evidence | +| astrelsky/libhijacker | daemon/process spawner, not BigApp launch | loads ELF in separate process | no applicable BigApp/VideoOut proof | different architecture with unbounded polling | +| etaHEN public tree | only `hbldr_launch` declaration found | implementation absent from visible source | not source-bound | incomplete and inadmissible as proof | + +The two independent LNC candidates do not agree on the parameter contract. +The 256-byte candidate explicitly describes its size as typical and tries +fallbacks, so successful launch could not identify which ABI path worked. The +24-byte candidate supplies a concrete layout but no independent provenance. +Neither performs the embedded RetroArch replacement. + +The only reviewed BigApp replacement implementations share the official +shsrv/websrv lineage. Sonicloader is derivative. The etaHEN tree contains an +`hbldr_launch` header but no matching implementation in the visible source +tree. Its tracked binary libraries are outside Chimera's public-source-only +boundary and were not inspected. Libhijacker is independently useful for a +separate daemon process but does not establish BigApp VideoOut ownership and +contains polling without the required deadline contract. + +## Exact external evidence request + +Further target work requires a new public, redistribution-compatible evidence +package containing all of the following: + +1. declarations and complete field layout for the chosen PS5 title-launch API, + with derivation or an independently reviewable specification; +2. the full source of the firmware-applicable BigApp process selection and + replacement path, including every structure and constant it consumes; +3. evidence that the selected title/context owns usable VideoOut on firmware + 9.60, without inferring this from successful title launch alone; +4. explicit semantics for every wait, stop, return and error state; +5. source-permitted replacement of every blocking wait with an absolute + monotonic deadline and a terminal kill/reap policy; +6. checked rollback for registers, credentials, mappings, descriptors, + breakpoints, attachment state and the target process; +7. license and provenance sufficient to cite or reuse the declarations without + proprietary or leaked SDK material. + +A binary dump, symbol list, operator statement, fallback experiment or new +device authorization cannot satisfy this request. Unknown or conflicting +evidence remains `STOP`. + +## Decision and next step + +All work that can be completed safely from the available public evidence is +done. No ADR can accept an ABI while the independent candidates conflict, and +no target source or artifact may be created. The next step is external evidence +acquisition using the seven-item request above. Once supplied, the first local +action is a provenance/lineage audit; only a passing audit can open a new ADR +and the bounded target-composition phase. diff --git a/docs/retroarch/phase-1.0f-startup-interval.md b/docs/retroarch/phase-1.0f-startup-interval.md new file mode 100644 index 0000000..ead14c4 --- /dev/null +++ b/docs/retroarch/phase-1.0f-startup-interval.md @@ -0,0 +1,93 @@ +# Phase 1.0F startup interval diagnostic + +Status: **OFFLINE ARTIFACT PREPARED; NO DEVICE ACTION AUTHORIZED**. + +Phase-1.0E RUN C proved D00-D02 and returned D02 raw result `0`, then the +inherited stream reached EOF before D03. Phase 1.0F adds fifteen stream-only +checkpoints inside that source interval. It creates no new observation route, +does not explain RUN C retrospectively and is not hardware evidence. + +## Bounded design + +The `interval-diag` profile retains D00-D12 and uses separate frame magic +`CHD10F01`. I00-I14 occupy indices 13-27. Each reached checkpoint performs at +most one nonblocking, signal-suppressed 64-byte `send` on the raw-elfldr socket +already inherited as stdout. It never retries a short write or failure. + +The interval functions do not call the notification ABI, create/connect a +socket, listen, receive, open a device file, persist state, install, autoload, +launch another payload or contain an address/port. The existing D-stage +notifications remain unchanged. The controlled elfldr route still has +`stdio=-1` and cannot carry this result stream. + +| Stage | Source location | Boundary | +|---|---|---| +| I00 | `retroarch.c:6038` | before CLI content-load dispatch | +| I01 | `tasks/task_content.c:1413` | content-load entry | +| I02 | `tasks/task_content.c:1437` | frontend environment callback returned | +| I03 | `retroarch.c:7817` | `retroarch_main_init()` entry | +| I04 | `retroarch.c:7839` | argument/config parsing returned | +| I05 | `retroarch.c:7981` | before driver lookup | +| I06 | `retroarch.c:7987` | audio driver found | +| I07 | `retroarch.c:7993` | video driver found | +| I08 | `retroarch.c:7999` | input driver found | +| I09 | `retroarch.c:8005` | camera driver found | +| I10 | `retroarch.c:8034` | core initialization begins | +| I11 | `retroarch.c:8048` | core result, raw0 `0` or `-1` | +| I12 | `retroarch.c:8116` | before `drivers_init()` | +| I13 | `gfx/video_driver.c:3385` | internal video initialization entry | +| I14 | `gfx/drivers/sdl2_gfx.c:329` | SDL2 video entry immediately before D03 | + +I-frame notification masks are read-only snapshots of earlier D-stage state. +Their notification-result field is the sentinel `INT32_MIN`, not a PS5 call +result. + +## Artifact identity + +Source repository: private `chimera-retroarch`, branch +`codex/ps5-d02-d03-interval-diagnostic`, commit +`0eaf68d6de4dc9757d85cc7ad5c714b1d151c8f8`. + +| Local ignored output | Bytes | SHA-256 | +|---|---:|---| +| `build/phase10f/interval-a/retroarch_ps5_interval_diag.elf` | 1,845,152 | `e8bfc01c61bfb14b5814280a6e5442f1a5ad05ace5439d1c09e7e5ee00cd0055` | +| `build/phase10f/interval-a/retroarch_ps5_interval_diag.map` | 637,603 | `638642b750b8d5b108cf6c73215a3f1759bcb6da0b29ee0a0ade5c47e8b7b2d5` | + +Two independent `make clean` builds produced byte-identical ELF and map files. +The outputs remain ignored; Git contains hashes only. + +## Static audit + +- ET_DYN, x86-64, entry `0xff110`, four program headers; +- three PT_LOAD segments with R-E, R-- and RW- permissions; no RWX segment; +- zero-byte preinit/init/fini arrays and no TLS section; +- `.bss` section 697,632 bytes; load zero-fill 697,636 bytes; +- 1,055 relocations: 142 `R_X86_64_GLOB_DAT`, 913 `R_X86_64_RELATIVE`; +- seven DT_NEEDED modules and 142 undefined symbols, exactly matching the + Phase-1.0E result artifact; +- one `send` relocation; no `socket`, `connect`, `bind`, `listen`, `accept`, + `recv` or GNM import; +- all fifteen I-stage callsites bind to the two interval wrappers; disassembly + shows those wrappers call only notification-state access and the existing + frame emitter; +- linker map binds all objects, the pinned SDL2 archive and SDK startup objects. + +Static strings include dormant generic RetroArch features and the SDK CRT +contains `__patch_init`, kernel copy helpers and temporary credential/capability +logic. Claims therefore rely on source selection, linker map, symbols and +disassembly—not string absence. Existing startup effects remain exactly as +classified in Phase 1.0E, so the artifact is not side-effect-free. The linked +write firewall blocks reachable write/create/truncate/rename/delete operations. + +## Test result and gate + +ASan/UBSan C tests, malformed/range/duplicate checks, 8 Phase-1.0D Python +cases, 14 Phase-1.0E/F parser cases and the structural validator pass. No PS5 +connection, request, transfer, receive or execution occurred. + +All authorization fields remain false. The artifact is +`execution_eligible=false`, `transfer_eligible=false` and +`installation_eligible=false`. The inactive template in +`docs/approvals/phase-1.0f-device-test-template.md` grants nothing. A later +one-shot test requires a new exact permission explicitly covering result +reception. diff --git a/docs/retroarch/phase-1.0g-device-result.md b/docs/retroarch/phase-1.0g-device-result.md new file mode 100644 index 0000000..c743098 --- /dev/null +++ b/docs/retroarch/phase-1.0g-device-result.md @@ -0,0 +1,37 @@ +# Phase 1.0G one-shot device result + +The separately authorized exact Phase-1.0G attempt was consumed on 2026-07-22. +One connection sent the 1,845,152-byte interval artifact and received 3,669 +bytes before remote EOF. There was no retry, reconnect, installation, autoload, +device filesystem write, cleanup action or reboot. + +Seven valid `CHD10F01` frames establish this exact sequence: + +```text +D00 -> D01 -> D02(raw0=0) -> I00 -> I01 -> I02 -> I03 -> remote EOF +``` + +No parser error, I04, D03 or terminal frame occurred. The bounded result is +`REMOTE_PAYLOAD_OUTPUT_PROVEN_INCOMPLETE_AFTER_I03_BEFORE_I04`. + +The host trace also preserved 3,221 ordinary stdout bytes. RetroArch printed: + +```text +No arguments supplied and no menu built-in, displaying help... +``` + +followed by its help page. This is an exact source-bound explanation. The +`interval-diag` profile has `HAVE_MENU := 0`; its loader supplies `argc == 1`. +`retroarch_parse_input_and_config()` therefore takes the guarded help path, +prints at `retroarch.c:7120` and calls `exit(0)` at line 7122. I03 is immediately before the parser +call, while I04 is after it. + +This run did not fail inside SDL or VideoOut: it deliberately exited before +either could be reached. SDL initialization, VideoOut, rendering, terminal +cleanup and safe exit remain unproven. A future offline change must address the +no-argument/no-menu contract before preparing a different artifact. This +result grants no such artifact or device action. + +Evidence hashes and exact transport counters are recorded in +`manifests/retroarch/phase-1.0g-device-result.json`. The raw trace remains an +ignored local evidence file and is not packaged or committed. diff --git a/docs/retroarch/phase-1.0g-one-shot-runner.md b/docs/retroarch/phase-1.0g-one-shot-runner.md new file mode 100644 index 0000000..f4d6e0c --- /dev/null +++ b/docs/retroarch/phase-1.0g-one-shot-runner.md @@ -0,0 +1,54 @@ +# Phase 1.0G offline one-shot runner + +Phase 1.0G prepares only the host-side policy boundary for a possible later +single run of the unchanged Phase-1.0F interval diagnostic. It did not connect +to a PS5, transfer or execute an artifact, or receive device output. The +tracked manifest and approval template are deliberately inactive. + +## Bound identity + +- runner repository: `chimera-retroarch` +- runner commit: `55e873df12f4dd099c082d4a26b225644b8e0567` +- target source commit: `0eaf68d6de4dc9757d85cc7ad5c714b1d151c8f8` +- artifact: `retroarch_ps5_interval_diag.elf` +- size: `1845152` +- SHA-256: `e8bfc01c61bfb14b5814280a6e5442f1a5ad05ace5439d1c09e7e5ee00cd0055` +- protocol: `CHD10F01`, version 1, fixed 64-byte frames + +The target source and artifact are unchanged from Phase 1.0F. No ELF, map, +device address or active approval is tracked here. + +## Fail-closed order + +For any future separately authorized attempt, the runner must perform these +steps in order: + +1. reject an existing trace; +2. read and hash the active manifest, local approval and actual ELF; +3. require the exact artifact, firmware, F protocol, target and limits; +4. require all four positive permissions: connection, transfer, execution and + result reception; +5. reject installation, autoload, device write, retry, reconnect, resume and + automatic reboot; +6. derive a filesystem-safe unique run identifier; +7. exclusively create and `fsync` a consumed-attempt receipt before opening a + socket; +8. only then allow one connect, one complete send, one write-half-close and one + bounded receive; +9. exclusively create the local result trace without overwrite. + +The durable pre-connect receipt intentionally consumes the attempt if the host +crashes after authorization but before or during transport. Reusing the same +run identifier is rejected. There is no automatic recovery, retry or reconnect. + +## Current state + +`protocol_activation_authorized=false`, the run identifier and tracked target +are null, artifact eligibility is false and every authorization is false. The +runner cannot be activated by a command-line protocol switch. A later device +action would need a new exact permission and an untracked, reviewed local +approval record; this document grants none. + +Fake-socket tests prove only host policy and parser behavior. They are not PS5, +firmware, SDL, VideoOut, rendering, exit or cleanup evidence. Existing SDK CRT +effects and the unproven D02-to-D03 runtime interval remain unchanged. diff --git a/docs/retroarch/phase-1.0h-device-result.md b/docs/retroarch/phase-1.0h-device-result.md new file mode 100644 index 0000000..a897cce --- /dev/null +++ b/docs/retroarch/phase-1.0h-device-result.md @@ -0,0 +1,50 @@ +# Phase 1.0H one-shot device result + +Status: **AUTHORIZATION CONSUMED; FIRST FLIP SUBMIT FAILED**. + +One exact authorized attempt ran on firmware 9.60. The host used one +connection, one 1,845,152-byte transfer, one execution and bounded result +reception. It performed no retry or reconnect. Remote EOF arrived after 2.27 +seconds. + +## Result + +Twenty-five valid `CHD10H01` frames and 2,353 bytes of ordinary stdout prove: + +1. I04 was reached, so the Phase-1.0H `-v` correction fixed the deterministic + no-argument/no-menu exit. +2. Core initialization and driver selection continued through I12. +3. I13, I14 and D03 reached internal video, SDL2 video and SDL initialization. +4. D05 returned positive VideoOut handle `1309671680`. +5. D06 returned `0` for VideoOut buffer registration. +6. Source order proves the diagnostic pattern was copied into mapped video + memory before D07. +7. D07 returned `-1` from the first `sceVideoOutSubmitFlip` attempt. +8. D04 returned `-1` from SDL initialization. +9. Stdout reports `Cannot open video driver` and a fatal error in + `video_driver_init_internal()`, followed by core unload messages. + +D12 appeared earlier with shutdown reason `6` and error `118`: the write +firewall rejected a requested filesystem write during argument/config parsing. +The exact operation was not included in the frame. The shutdown request did +not immediately stop subsequent initialization. + +No D08, D09 or D11 was received. A successful/visible flip, input/audio +initialization, runloop entry, complete VideoOut cleanup and safe exit remain +unproven. The console's visible display result has not been operator-attested. + +## Evidence + +- artifact SHA-256: + `822f2cf1f4d33a514d2bdd88fde40ad580dda5d85f537362ef6dff2eafcb56b6`; +- ignored local trace SHA-256: + `858a205afcf682a498d8bc11947b67d2446098883a61f6871e31df4d950d0a61`; +- attempt receipt SHA-256: + `aa598838c2db9a03799ce69ed977ad1dbb4f9d0b92ad017c0e1f91c680f1e04a`; +- raw stream SHA-256: + `6578869b15575c57a19dc0c0317f6b516a85bf6e246abaa85df76e8ae9d5cf67`; +- ordinary stdout SHA-256: + `109e465582ae55354cb1f7b52c1b17d6741264735b41c59d6ecde565bf3cdc55`. + +The permission and run ID are consumed. All current authorization and artifact +eligibility fields are false. No follow-up device action is authorized. diff --git a/docs/retroarch/phase-1.0h-startup-args.md b/docs/retroarch/phase-1.0h-startup-args.md new file mode 100644 index 0000000..cdd4eab --- /dev/null +++ b/docs/retroarch/phase-1.0h-startup-args.md @@ -0,0 +1,66 @@ +# Phase 1.0H startup-argument correction + +Status: **OFFLINE ARTIFACT PREPARED; NO DEVICE ACTION AUTHORIZED**. + +Phase-1.0G device evidence reached D00-D02 and I00-I03, printed RetroArch's +no-argument/no-menu help, and exited before I04. The source-bound cause is the +`!HAVE_MENU && argc == 1` guard in `retroarch_parse_input_and_config()`; SDL, +VideoOut and rendering were not reached. + +## Minimal correction + +The new private `chimera-retroarch` profile `startup-args-diag` changes only +the contentless frontend wrapper contract. Under +`CHIMERA_PS5_STARTUP_ARGS_DIAG`, `frontend_ps5_environment_get()` retains +`TOUCHED | NO_CONTENT` and adds the existing `RARCH_MAIN_WRAP_FLAG_VERBOSE`. +The generic wrapper maps that flag to `-v`, so its modeled arguments are +`["retroarch", "-v"]`. It adds no content, core, configuration or filesystem +path and leaves `HAVE_MENU=0`. + +The profile otherwise retains the Phase-1.0F static core, write firewall, +60-second/3,600-frame bounds, D00-D12 and I00-I14 checkpoints, inherited +stdout stream and no-retry behavior. Its distinct frame magic is `CHD10H01`. +Runner commit `f2dd710f2dff8c840a4c747cbbb3654c24cf23a7` recognizes H only when an +exact active H manifest and separate local approval agree. There is no free +protocol switch, tracked target, tracked run ID or current authorization. + +## Reproducible artifact + +Source commit: `c710f85816b5e456dd3a85a46f85eb28883522b3` on +`codex/ps5-noarg-startup-fix`. + +| Ignored local output | Bytes | SHA-256 | +|---|---:|---| +| `retroarch_ps5_startup_args_diag.elf` | 1,845,152 | `822f2cf1f4d33a514d2bdd88fde40ad580dda5d85f537362ef6dff2eafcb56b6` | +| `retroarch_ps5_startup_args_diag.map` | 637,603 | `638642b750b8d5b108cf6c73215a3f1759bcb6da0b29ee0a0ade5c47e8b7b2d5` | + +Two independent clean WSL builds produced byte-identical outputs. The files +remain ignored and are not packaged or committed. + +## Static audit + +- ET_DYN x86-64, entry `0xff110`, three R-E/R--/RW- load segments, no RWX; +- no TLS, zero-byte init/fini arrays, 697,632-byte BSS; +- 1,055 relocations (142 type 6, 913 type 8); +- seven DT_NEEDED modules and 142 undefined imports, byte-for-byte identical + import names to Phase 1.0F; +- one inherited `send`; no socket creation/connect/listen/receive or GNM + submission/flip import; +- both normalized disassemblies hash to + `d38ccaecb2f52b1529680dab68314b6b014ec446ac66cba645887fbe8f1d8c11`; +- disassembly at `frontend_ps5_environment_get` writes flag mask `0x07`, the + exact verbose/touched/no-content bit union; +- the linker map remains byte-identical to Phase 1.0F. + +Normal SDK CRT startup and its already classified temporary patch effects are +unchanged. Dormant generic RetroArch file/network features remain linked, so +this is not a side-effect-free artifact. Source selection, callsites, imports, +link map, disassembly and the write firewall form the claim—not string absence. + +## Gate + +No PS5 connection, request, transfer, reception or execution occurred. The +artifact is `execution_eligible=false`, `transfer_eligible=false` and +`installation_eligible=false`; all authorizations remain false. Phase-1.0G +authority was consumed and does not carry forward. I04, SDL, VideoOut, +rendering, terminal state and cleanup remain unproven on firmware 9.60. diff --git a/docs/retroarch/phase-1.0i-flip-and-write-analysis.md b/docs/retroarch/phase-1.0i-flip-and-write-analysis.md new file mode 100644 index 0000000..2815f43 --- /dev/null +++ b/docs/retroarch/phase-1.0i-flip-and-write-analysis.md @@ -0,0 +1,139 @@ +# Phase 1.0I offline flip and write analysis + +Status: **OFFLINE ANALYSIS COMPLETE; DEVICE RETEST BLOCKED**. + +This is a static postmortem of the consumed Phase-1.0H run. It binds the +captured frames to the exact RetroArch source, SDL overlay, linker map and +artifact disassembly. No PS5 connection, request, transfer, execution or new +target build was performed. + +## Conclusions first + +- The first VideoOut submit failed at the exact call + `sceVideoOutSubmitFlip(handle, 0, 1, 0)`; the wait was not reached. +- The diagnostic helper uses buffer/frame zero while the same patch makes the + normal update path start at frame one. This is a strong code-level + inconsistency, not a proven firmware root cause. +- The original submit `errno` was not captured. D07 output occurs before + `errno` is read and can itself change it. +- E118 occurred between I03 and I04. The linked binary can classify the + rejected operation only as `OPEN` or `STREAM`; D12 does not carry the exact + stored operation. +- The E118 shutdown request did not gate later initialization: I04 through + D07 still ran. + +The next safe action is another offline design/build phase that improves +observability and shutdown gating. It is not another device run. + +## Evidence binding + +| Object | Identity | +|---|---| +| RetroArch source | `c710f85816b5e456dd3a85a46f85eb28883522b3` | +| Phase-1.0H result commit | `5db76beddd56c0f82ecbcccfbe8ed12ea3bb4bda` | +| Artifact | 1,845,152 bytes, SHA-256 `822f2cf1f4d33a514d2bdd88fde40ad580dda5d85f537362ef6dff2eafcb56b6` | +| Linker map | 637,603 bytes, SHA-256 `638642b750b8d5b108cf6c73215a3f1759bc6da0b29ee0a0ade5c47e8b7b2d5` | +| SDL diagnostic patch | SHA-256 `c287cf0207192b06c2658c2ae6982a3c2fe3ee9179908de5911d074786dc889e` | +| Materialized SDL video source | SHA-256 `9a1c171711d2b5a1167b66a3c0e2ac926d7fd101b1f08b0a9240a3aa9c62211e` | +| Device trace | SHA-256 `858a205afcf682a498d8bc11947b67d2446098883a61f6871e31df4d950d0a61` | +| Public SDL source | commit `0baf4ac49382b537ba449901b5b6d0d189bb1fbb` | +| Public SDK v0.41 | commit `d2e2e585740362976a39fdd5ccf390f199a7bc37` | + +The ignored artifact, map and trace remain local evidence; they are not added +to Git. + +## E118 write-firewall interval + +D12 has shutdown reason `6` (`WRITE_FIREWALL`) and first error `118` +(`E118_WRITE_BLOCKED`). Its sequence position proves the request occurred +after I03 and before I04. The in-memory status also stores +`first_blocked_write`, but D12 transmits only reason and error. + +The exact linker map retains these blocking functions: + +| Function | Address | Block category | +|---|---:|---| +| `__wrap_open` | `0x47580` | `OPEN` | +| `__wrap_fopen` | `0x475b0` | `OPEN` | +| `__wrap_fwrite` | `0x47600` | `STREAM` | + +Configured wrappers for `openat`, `write`, create, truncate, directory and +rename/delete operations were garbage-collected from this artifact. Static +reachability within I03-to-I04 contains logging/configuration paths to +`__wrap_fopen`; it does not prove which runtime branch fired. The initial +`retro_main_log_file_init(NULL, false)` is specifically excluded because both +source and disassembly return before `fopen`. Remaining indirect/runtime +behavior prevents selecting `OPEN` over `STREAM` without inventing evidence. + +Consequently the exact operation, path, mode/flags and intended file remain +`UNPROVEN`. This is an observability defect, not evidence that a device write +succeeded: the wrapper returned failure and set `EROFS`. + +## Flip call and failure boundary + +The SDL source fills its CPU surface, tiles it into `vbuf[0]`, then submits. +The artifact disassembly independently fixes the arguments: + +```text +0xfe6ac esi = 0 buffer index +0xfe6ae edx = 1 flip mode +0xfe6b3 ecx = 0 64-bit flip argument +0xfe6bc call sceVideoOutSubmitFlip +``` + +D05 proves a positive handle for this run and D06 proves buffer registration +returned zero. D07 proves submit returned `-1`. The failure branch bypasses +`sceKernelWaitEqueue`; therefore no event wait, successful flip or visible +presentation is claimed. + +The helper hard-codes `vbuf[0]` and `(0, 1, 0)`, while the patched normal +`PS5_UpdateWindowFramebuffer()` initializes `frame_id = 1` and derives the +buffer index from it. That mismatch is source-proven. However, the pinned SDL +upstream itself initializes the normal path at zero, so firmware semantics do +not establish that changing to one is correct. + +SDK v0.41 exports the symbol but provides no public VideoOut prototype or +structure contract. The declaration and opaque layouts used here originate in +the public SDL fork. Buffer-index validity, flip argument monotonicity, buffer +attribute semantics and exact firmware-9.60 error codes remain unproven. + +The helper calls the D07 diagnostic sender before `strerror(errno)`. Since the +sender performs an inherited-socket `send`, it can overwrite `errno`; the +original submit error must be copied immediately after the submit in any +future diagnostic. No reliable error text was present in captured stdout. + +## Review matrix + +| Question | Result | +|---|---| +| H startup-argument correction | PROVEN_ON_FIRMWARE_9_60 | +| E118 interval | PROVEN | +| E118 operation domain | PROVEN: `OPEN` OR `STREAM` | +| Exact E118 operation/path | UNPROVEN | +| Pattern write before submit | PROVEN_FROM_SOURCE_AND_STAGE | +| Submit argument tuple | PROVEN_FROM_SOURCE_AND_DISASSEMBLY | +| Submit return | PROVEN: `-1` FOR CONSUMED RUN | +| Original submit `errno` | UNPROVEN | +| Frame-zero mismatch | STRONG_SOURCE_CANDIDATE | +| Frame-zero mismatch is root cause | UNPROVEN | +| VideoOut argument/ABI semantics | UNPROVEN | +| Visible presentation | UNPROVEN | +| Complete cleanup/safe exit | UNPROVEN | + +## Gate for a future offline artifact phase + +A later phase may design and build, offline only, a newly hashed diagnostic +that: + +1. captures submit return and `errno` before any reporting call; +2. emits the exact blocked write operation and count; +3. stops initialization before I04 once the firewall requests shutdown; +4. removes the divergent first-frame constants through one reviewed source of + truth; +5. preserves one submit, no submit retry, and waits only after submit success; +6. adds no target socket creation, address, filesystem write, install or + autoload path. + +Choosing frame zero or one requires an evidence-backed decision. A new build +would remain ineligible until a separate review. No current PS5 connection, +transfer, execution, result reception or retry is authorized. diff --git a/docs/retroarch/phase-1.0j-write-firewall-diagnostic.md b/docs/retroarch/phase-1.0j-write-firewall-diagnostic.md new file mode 100644 index 0000000..7743635 --- /dev/null +++ b/docs/retroarch/phase-1.0j-write-firewall-diagnostic.md @@ -0,0 +1,107 @@ +# Phase 1.0J offline write-firewall diagnostic + +Status: **OFFLINE ARTIFACT AUDITED; DEVICE ACTION BLOCKED**. + +Phase 1.0J implements the bounded offline follow-up required by Phase 1.0I. +It does not connect to a PS5, transfer an ELF, receive a device result or run +target code. The built ELF and map stay in ignored local build storage and are +not committed. + +## Exact source and artifact binding + +| Object | Identity | +|---|---| +| RetroArch source | `2e385265ab893dbba5898178dde5e108d3dbfa19` | +| Chimera GFX baseline | `2bee49e5e8f1ee6faa0589d2d74de5ba0d809293` | +| Public PS5 SDL source | `0baf4ac49382b537ba449901b5b6d0d189bb1fbb` | +| Public PS5 Payload SDK v0.41 | `d2e2e585740362976a39fdd5ccf390f199a7bc37` | +| SDL diagnostic patch | SHA-256 `aa1d3ebc6043d6051938cee1dbffecb80729b3b6595eef964915e93f1570c193` | +| Materialized SDL video source | SHA-256 `9949a280fed40241746788a8c001280455bc629e0566a88f82f16a8634d43025` | +| J ELF | 1,845,208 bytes; SHA-256 `6ff0f7ea391da5f15ea43512a871078133e896a6900ae9f8f3fa75711abb8009` | +| Linker map | 637,728 bytes; SHA-256 `19f1cf851ad8f99d31d5de3a14591f81faa83589c18e93cd60be96ebef5d7719` | +| Normalized disassembly | SHA-256 `45dc4e0233b3770add430aaa6bb23a60210b50744effb76b515fca4b9e6e310f` | + +Two clean `PS5_PROFILE=write-diag` builds produced byte-identical ELFs and +maps. The raw audit disassembly hashes differ because `objdump` includes each +input pathname; replacing only the `write-diag-a`/`write-diag-b` path component +produces the identical normalized hash above. + +## Bounded diagnostic behavior + +The protocol uses distinct magic `CHD10J01`. Existing stage numbers and wire +values are preserved; D13 is appended and carries exactly: + +```text +value_a = first_blocked_write +value_b = write_block_count +``` + +The first write-firewall rejection emits one inherited-stdout, nonblocking, +signal-suppressed D13 frame immediately before terminal D12. No target socket +is created and the existing live runner has no J selector. Immediately after +argument/config parsing, `retroarch_main_init()` observes +`shutdown_requested` and returns false before I04 and all SDL/VideoOut driver +lookup. Thus the first unresolved E118 operation can be identified without +continuing into graphics initialization. + +The SDL helper separately copies `errno` immediately after its single +`sceVideoOutSubmitFlip()` call and before notification or stream output. Under +J, D07 would carry `(submit_result, saved_errno)`. That branch is deliberately +unreachable after an earlier firewall shutdown; it is retained for a later, +separately reviewed phase only. + +ADR-0012 fixes one `CHIMERA_PS5_FIRST_FRAME_INDEX` value of zero for the early +copy, early submit and normal initial counter. This removes an internal source +inconsistency. It is not firmware proof or a root-cause claim. + +## Static and transitive audit + +The ELF is a PIE with entry point `0xff210`, 20 sections and four program +headers. Three LOAD segments are `R E`, `R` and `RW`; no segment is RWX. +`.init_array` and `.fini_array` both have size zero, and there is no TLS +section. `.bss` is `0xaa520` bytes. The dynamic closure has 142 undefined +symbols and 1,055 relocations: 142 `R_X86_64_GLOB_DAT` and 913 +`R_X86_64_RELATIVE`. + +The exact DT_NEEDED set is: + +- `libSceAudioOut.sprx` +- `libSceLibcInternal.sprx` +- `libScePad.sprx` +- `libSceSystemService.sprx` +- `libSceUserService.sprx` +- `libSceVideoOut.sprx` +- `libkernel_web.sprx` + +The import set contains `send` for the already-audited inherited stdout +descriptor. It contains no `socket`, `connect`, `bind`, `listen`, `accept` or +`recv`, and no GNM symbol. The linker map binds the firewall entry at +`0x466d0`, its surviving wrappers at `0x475a0`, `0x47610` and `0x476a0`, +`retroarch_main_init` at `0x3dc0`, and the dormant early VideoOut helper at +`0xfe550`. + +String scanning still finds dormant generic RetroArch text mentioning +networking, installation, credentials, autoload and writes. Absence of a +string is not used as safety proof. Safety rests on the source profile, +write-wrapper closure, linker map and disassembly; the scan findings remain +recorded as dormant linked data rather than declared reachable behavior. + +## Review matrix + +| Property | Result | +|---|---| +| Exact E118 operation reporting | PROVEN_OFFLINE_FROM_SOURCE_AND_TESTS | +| Stop before I04 after E118 | PROVEN_OFFLINE_FROM_SOURCE_AND_DISASSEMBLY | +| Submit errno capture ordering | PROVEN_OFFLINE_FROM_SOURCE | +| Unified first-frame value | PROVEN_OFFLINE_FROM_SOURCE | +| Byte-reproducible ELF and map | PROVEN_OFFLINE | +| Firmware-9.60 runtime behavior | UNPROVEN | +| Exact blocked operation for the consumed H run | UNPROVEN | +| Submit errno on firmware 9.60 | UNPROVEN | +| Visible presentation | UNPROVEN | +| Complete target cleanup and safe exit | UNPROVEN | + +The normal SDK CRT and its documented runtime effects remain inherited. The +early fail-closed return occurs before driver initialization, but the complete +loader/process cleanup path is still not firmware-proven. Every artifact and +authorization gate remains false. No device action is authorized. diff --git a/docs/retroarch/phase-1.0k-write-diag-one-shot-runner.md b/docs/retroarch/phase-1.0k-write-diag-one-shot-runner.md new file mode 100644 index 0000000..16a8a25 --- /dev/null +++ b/docs/retroarch/phase-1.0k-write-diag-one-shot-runner.md @@ -0,0 +1,76 @@ +# Phase 1.0K write-diagnostic one-shot runner + +Status: **OFFLINE RUNNER PREPARED; NO DEVICE AUTHORIZATION**. + +Phase 1.0K adds host-only parsing and authorization support for the unchanged, +ignored Phase-1.0J artifact. It creates no target artifact and performs no PS5 +connection, transfer, execution or result reception. + +## Exact binding + +| Object | Identity | +|---|---| +| Chimera GFX Phase J | `05d316e4d48660ab24d116153f094ef7e2f69522` | +| RetroArch J artifact source | `2e385265ab893dbba5898178dde5e108d3dbfa19` | +| RetroArch K host runner | `ee965a0be3cd3e0032330680e7614c766688410a` | +| Runner source SHA-256 | `4ee58f08ff51cff3624cbc072c0e915e8c415eb8cd98185fa0d6a20c02b7c330` | +| J artifact | 1,845,208 bytes; SHA-256 `6ff0f7ea391da5f15ea43512a871078133e896a6900ae9f8f3fa75711abb8009` | +| J linker map | 637,728 bytes; SHA-256 `19f1cf851ad8f99d31d5de3a14591f81faa83589c18e93cd60be96ebef5d7719` | + +The runner change touches host code and tests only. The artifact source, +artifact bytes, SDL overlay and target protocol emitter remain unchanged. + +## Wire contract + +J extends historical diagnostics without renumbering them. The parser therefore +uses this exact 30-entry table: + +```text +0..12 D00..D12 +13..27 I00..I14 +28 C1 +29 D13 +``` + +Magic is `CHD10J01`, version is 1 and every frame is 64 bytes in big-endian +wire order with CRC-32 over the first 60 bytes. A wrong table, including an +apparently natural but incorrect `D13, C1` suffix, is rejected before socket +creation. + +## Fail-closed activation contract + +The CLI has no protocol selector. J is selected only by an exact active +`PHASE_1_0K_WRITE_DIAG_ONE_SHOT_RUNNER` manifest. Before transport the runner +requires: + +- exact artifact name, profile, size and SHA-256; +- `execution_eligible=true` and `transfer_eligible=true` in a separately + reviewed active manifest; +- explicit one-shot connection, transfer, execution and reception authority; +- a separate untracked approval for exact firmware 9.60, target, port, timeout + and unique run ID; +- false installation, autoload, device-write, retry, reconnect, resume and + automatic-reboot fields; +- exact agreement on magic, frame size and the complete wire table. + +The runner rehashes the artifact and durably creates an exclusive consumed-run +receipt before any socket. It permits one connection, one `sendall`, one +write-half-close and bounded reception. Trace creation is exclusive, capture +is capped at 65,536 bytes, and there is no retry, reconnect, resume or +overwrite. + +The tracked manifest and approval template are deliberately inactive: no +target, port or run ID is present; every authorization and artifact eligibility +field is false. These files cannot activate transport. + +## Offline evidence and limits + +Fake-socket tests cover fragmented J frames, C1/D13 index preservation, +terminal D12, exact manifest selection, incorrect stage-table rejection, +inactive-manifest rejection before transport, consumed-run protection and the +inherited timeout/capture limits. + +This phase provides no firmware evidence. E118's historical operation, J +runtime behavior, submit errno, visible presentation and complete cleanup all +remain unproven. A future device action would require a new, exact, +artifact-specific permission and a separately reviewed active record. diff --git a/docs/retroarch/phase-1.0l-write-firewall-result-analysis.md b/docs/retroarch/phase-1.0l-write-firewall-result-analysis.md new file mode 100644 index 0000000..9fb7f5a --- /dev/null +++ b/docs/retroarch/phase-1.0l-write-firewall-result-analysis.md @@ -0,0 +1,64 @@ +# Phase 1.0L write-firewall result analysis + +Status: **ONE-SHOT AUTHORIZATION CONSUMED; FIRST WRITE IS MKDIR**. + +The exact ignored Phase-1.0J artifact ran once on firmware 9.60 through the +manifest-gated Phase-1.0K runner. The host received a valid terminal trace +with no parser error, retry or reconnect: + +```text +D00 D01 D02(0) I00 I01 I02 I03 D13(6,1) D12(6,118) +``` + +`D13` is source-defined as `(first_blocked_write, write_block_count)`. Enum +value 6 is `CHIMERA_SMOKE_WRITE_MKDIR`; the count was one. `D12` records +shutdown reason 6 (`WRITE_FIREWALL`) and first error 118 +(`E118_WRITE_BLOCKED`). The J guard observed shutdown and returned before I04, +so this run did not enter SDL, VideoOut or rendering. + +## Bounded cause + +The exact source, map, disassembly and relocation evidence agree on this +bounded call chain: + +```text +I03 + -> retroarch_parse_input_and_config() + -> config_load() + -> config_set_defaults() + -> built-in playlist default-directory check + -> path_mkdir() + -> path_mkdir_cb + -> mkdir write-firewall wrapper + -> chimera_ps5_smoke_block_write(MKDIR) +``` + +The source site is `configuration.c:3357` at artifact source commit +`2e385265ab893dbba5898178dde5e108d3dbfa19`. The ELF map binds +`config_set_defaults=0x22ba0`, `path_mkdir=0x188f0` and +`chimera_ps5_smoke_block_write=0x466d0`. Disassembly contains the conditional +`path_mkdir` call at `0x26856`. The `path_mkdir_cb` relocation at `0x17c280` +resolves to the wrapper at `0x15ae0`, which passes operation value 6. + +The runtime pathname was not included in D13 and is therefore **UNOBSERVED**. +This evidence proves the operation category and compiled source route, not an +on-device path identity or successful filesystem mutation. The firewall +returned failure; no project filesystem write is evidenced. +Normal SDK CRT effects remain inherited under ADR-0010, so the run is not +classified as globally side-effect-free. + +## Next offline boundary + +A later artifact phase may make one scoped source correction: when +`CHIMERA_PS5_NO_FILESYSTEM_WRITES` is defined, skip the built-in playlist +directory creation in `config_set_defaults` while preserving all in-memory +path derivation and every write wrapper. It must then rebuild twice, re-audit +the complete artifact and remain ineligible. It must not weaken the firewall, +permit a real mkdir, infer a device path, or inherit this consumed authority. + +The historical H flip result remains separate. This K run intentionally never +reached the dormant J D07 errno instrumentation, so flip errno, successful +presentation and cleanup remain unproven. + +All current authorization and eligibility values are false. There is no +connection, transfer, execution, result-receive, retry or reconnect authority. diff --git a/docs/retroarch/phase-1.0m-write-free-defaults.md b/docs/retroarch/phase-1.0m-write-free-defaults.md new file mode 100644 index 0000000..13de8b9 --- /dev/null +++ b/docs/retroarch/phase-1.0m-write-free-defaults.md @@ -0,0 +1,63 @@ +# Phase 1.0M write-free defaults correction + +Status: **OFFLINE ARTIFACT AUDITED; DEVICE ACTION BLOCKED**. + +The consumed Phase-1.0K trace identified one blocked `MKDIR` before I04, and +Phase 1.0L bound it to the built-in playlist directory creation in +`config_set_defaults()`. Phase 1.0M makes only the corresponding offline source +correction in the separate `chimera-retroarch` fork. + +For `CHIMERA_PS5_NO_FILESYSTEM_WRITES`, RetroArch now skips the directory +existence probe and `path_mkdir()` at that source site. It still calculates +`new_path` and all subsequent in-memory playlist paths. The write-diagnostic +profile has `HAVE_CONFIGFILE=0`, so the separate legacy migration block is not +compiled. All 17 `--wrap` write-firewall options remain in the link contract. + +## Static callgraph delta + +The exact old Phase-1.0J function contained: + +```text +0x26845 -> path_is_directory +0x26856 -> path_mkdir +``` + +In the exact Phase-1.0M ELF, `config_set_defaults` retains six calls to +`fill_pathname_join_special` for this playlist block and contains no call to +`path_is_directory` or `path_mkdir`. The generic `path_mkdir` symbol remains in +the binary, but is no longer reachable from this defaults function. Link-time +garbage collection retains the actually referenced `__wrap_open`, +`__wrap_fopen` and `__wrap_fwrite`; each still enters the common fail-closed +write blocker. + +## Reproducible artifact + +Two clean builds from RetroArch source commit +`12cf1d783c41eb303987e49a5a920805a59ef7a4` produced identical outputs: + +| Evidence | Size | SHA-256 | +|---|---:|---| +| `retroarch_ps5_write_diag.elf` | 1,845,208 | `c99a0856309a357ad2667d89b4924e4063ad214cae09c8a419457b0732f583cd` | +| linker map | 637,728 | `6768ffc7267b9b362c3b953571e5dfeed1e70004a3e19f41ef500c3f26272719` | +| normalized disassembly | n/a | `141a620484784e5e01ec0cb2a51f77b67712bc484aae6de0f139ba1e28922c87` | + +The ELF remains ET_DYN, entry `0xff220`, with `R-X`, `R--` and `RW-` LOAD +segments and no RWX LOAD segment. Init/fini arrays are empty, TLS is absent and +BSS is 697,632 bytes. It has 142 undefined dynamic symbols, seven DT_NEEDED +modules and 1,055 relocations (142 GLOB_DAT, 913 RELATIVE). `send` is the only +network primitive; socket creation, connect, listener, receive and GNM imports +are absent. + +The artifact, map and audit JSON remain ignored local evidence and are not +committed. The SDK v0.41 CRT effects remain inherited under ADR-0010. Static +acceptance by the hardened-loader model is not runtime proof. + +All current authorization fields are false. This new artifact is +`execution_eligible=false`, `transfer_eligible=false` and +`installation_eligible=false`. No PS5 connection, request, transfer, result +reception, execution, device write, retry or reconnect occurred in Phase 1.0M. + +The Chimera GFX whole-tree secrets scan passed. The sibling RetroArch +changed-file scan found zero matches; its whole-tree scan separately flags +pre-existing upstream BearSSL/mbedTLS sample-key fixtures, so no whole-tree +RetroArch PASS is claimed. diff --git a/docs/retroarch/phase-1.0n-inactive-one-shot-runner.md b/docs/retroarch/phase-1.0n-inactive-one-shot-runner.md new file mode 100644 index 0000000..d5bfa31 --- /dev/null +++ b/docs/retroarch/phase-1.0n-inactive-one-shot-runner.md @@ -0,0 +1,40 @@ +# Phase 1.0N inactive one-shot runner + +Status: **OFFLINE RUNNER PREPARED; NO DEVICE AUTHORIZATION**. + +Phase 1.0N binds the existing host-only one-shot runner to the exact ignored +Phase-1.0M artifact. It creates no target code or artifact and performs no PS5 +connection, transfer, execution or result reception. + +## Exact binding + +| Object | Identity | +|---|---| +| Chimera GFX Phase M | `ab364b8e213e136fff746b948dce89ab028dbf7d` | +| RetroArch M artifact source | `12cf1d783c41eb303987e49a5a920805a59ef7a4` | +| RetroArch N host runner | `606909706f91d7213751c245081333f56c2cce89` | +| Runner source | 21,179 bytes; SHA-256 `1d46510369349c1e75ef3b5f983a2ef6fa5896fdd4398788bf58708821d0385d` | +| M artifact | 1,845,208 bytes; SHA-256 `c99a0856309a357ad2667d89b4924e4063ad214cae09c8a419457b0732f583cd` | +| M linker map | 637,728 bytes; SHA-256 `6768ffc7267b9b362c3b953571e5dfeed1e70004a3e19f41ef500c3f26272719` | + +The M target bytes and emitter are unchanged. Their wire contract remains +`CHD10J01`, version 1, 64-byte big-endian frames, with D00-D12 at indices +0-12, I00-I14 at 13-27, C1 at 28 and D13 at 29. The host assigns the distinct +protocol identity `PHASE_1_0N_WRITE_FREE_DEFAULTS`, so a future N trace cannot +be mislabeled as the consumed K run. + +## Inactive fail-closed state + +The tracked manifest and approval template keep every authorization and +eligibility field false. They contain no target, port or run ID. The CLI has +no protocol selector: N can be selected only by an exact active N manifest and +an independent local approval using scope `EXACT_ONE_SHOT_PHASE_1_0N`. +Phase-1.0K authority is rejected. + +Before any future socket, the runner would rehash the artifact and exclusively +create and flush a consumed-attempt receipt. Its bound limits remain one +connection, one transfer, one execution, one bounded result reception, no +retry, reconnect, resume, overwrite, install, autoload or device write. + +Host and fake-socket tests are not firmware evidence. I04, SDL, VideoOut, flip +behavior, visible output and complete cleanup remain unproven for the M hash. diff --git a/docs/retroarch/phase-1.0o-write-free-device-result.md b/docs/retroarch/phase-1.0o-write-free-device-result.md new file mode 100644 index 0000000..691e96d --- /dev/null +++ b/docs/retroarch/phase-1.0o-write-free-device-result.md @@ -0,0 +1,53 @@ +# Phase 1.0O write-free one-shot device result + +The exact Phase-1.0M artifact was transferred and executed once on firmware +9.60 through the separately authorized one-shot N runner. The attempt is +consumed. There was no installation, staging, autoload, device filesystem +write, retry, reconnect or reboot. + +## Result + +The source correction worked: valid frames reached I04 and all later startup +intervals through I14. No D13 was emitted, so this run observed no write +firewall activation. RetroArch entered the SDL video driver, opened VideoOut +with positive handle `0x4e100100`, and recorded buffer registration result +`0`. + +The early diagnostic copied its pattern and called +`sceVideoOutSubmitFlip(handle, 0, 1, 0)`. D07 recorded submit result `-1` and +the immediately saved `errno` value `0`; the event wait was therefore not +called. The SDL overlay then followed its generic `framebuffer_fail` path, +emitting D12 `(RUNTIME_FAILURE=5, E104_FRAMEBUFFER=104)`, and SDL initialization +returned `-1` in D04. E104 does not prove an allocation failure here: source +order binds it to the generic label used after the preceding early-flip +failure. + +This reproduces the Phase-1.0H flip boundary after removing the Phase-1.0K +`MKDIR` blocker. It strengthens `sceVideoOutSubmitFlip()` as the current +source-bound failure site. It does not explain why that public driver call +returns `-1`, and `errno=0` supplies no additional error identity. + +## Transport evidence + +- artifact: 1,845,208 bytes; SHA-256 + `c99a0856309a357ad2667d89b4924e4063ad214cae09c8a419457b0732f583cd`; +- one connection, one `sendall`, one write-half-close; +- 3,953 bytes received in 14 reads, followed by remote EOF; +- 25 CRC-valid frames, no parser errors and no capture truncation; +- trace: 18,031 bytes; SHA-256 + `3d0b8811ae11f5cac2c2d331e252e1789a10588cab0e6045828a6b6af0fe1eb6`; +- zero retry and zero reconnect. + +The runner returned its strict incomplete status because D12 carried the +terminal flag at sequence 24 while D04 followed at sequence 25 in the same +received block. Consequently the last parsed frame was nonterminal. This does +not invalidate the individual CRC-checked frames or lossless stdout, but it +does mean a successful terminal/cleanup contract was not observed. + +## Remaining boundary + +Visible presentation, successful flip submission, wait-event behavior, +complete cleanup and safe process exit remain unproven. All current +authorizations and artifact eligibility fields are false. The local active +manifest, approval, receipt and raw trace remain ignored evidence and are not +committed. diff --git a/docs/retroarch/phase-1.0p-terminal-ordering-analysis.md b/docs/retroarch/phase-1.0p-terminal-ordering-analysis.md new file mode 100644 index 0000000..f629541 --- /dev/null +++ b/docs/retroarch/phase-1.0p-terminal-ordering-analysis.md @@ -0,0 +1,24 @@ +# Phase 1.0P terminal ordering analysis + +The Phase-1.0O order `D07, D12, D04` is deterministic in the bound source; it +is not evidence of network reordering. + +1. The early diagnostic detects the nonzero submit result and enters SDL's + `framebuffer_fail` path. +2. `chimera_ps5_smoke_video_error(E104)` requests failure shutdown. +3. The shutdown request synchronously emits D12, which the current stream + helper marks terminal. +4. `PS5_VideoInit` then returns failure to `sdl2_gfx.c`. +5. The caller emits D04 with the SDL initialization result `-1`. + +D12 and D04 arrived in the same host receive block. The runner intentionally +requires the last parsed frame to be terminal, so D04 correctly prevented a +successful terminal classification. The individual CRC-valid frames remain +runtime evidence; successful cleanup and safe exit do not. + +Accepting any earlier terminal frame would incorrectly promote a shutdown +request into lifecycle completion. A future protocol must instead use a new +magic and a distinct final stage emitted only after D04 and bounded cleanup or +return evidence. D12 may remain failure/shutdown-request evidence, but must not +serve as the lifecycle terminal on this error path. This is a design +requirement only: Phase 1.0P changes neither target code nor the host parser. diff --git a/docs/retroarch/phase-1.0p-videoout-submit-analysis.md b/docs/retroarch/phase-1.0p-videoout-submit-analysis.md new file mode 100644 index 0000000..e2e285b --- /dev/null +++ b/docs/retroarch/phase-1.0p-videoout-submit-analysis.md @@ -0,0 +1,74 @@ +# Phase 1.0P VideoOut submit analysis + +Phase 1.0P is an offline postmortem of the consumed Phase-1.0O run. It adds no +target source or artifact and performs no device action. The exact runtime +evidence remains the M artifact (1,845,208 bytes, SHA-256 +`c99a0856309a357ad2667d89b4924e4063ad214cae09c8a419457b0732f583cd`) +and the ignored O trace (18,031 bytes, SHA-256 +`3d0b8811ae11f5cac2c2d331e252e1789a10588cab0e6045828a6b6af0fe1eb6`). + +## Proven call boundary + +The exact linked SDL overlay source has SHA-256 +`9949a280fed40241746788a8c001280455bc629e0566a88f82f16a8634d43025`. +Its linker map has SHA-256 +`6768ffc7267b9b362c3b953571e5dfeed1e70004a3e19f41ef500c3f26272719` +and locates `PS5_VideoInit` at `0xfdc20` and +`PS5_PresentEarlyDiagnosticFrame` at `0xfe560`. + +Disassembly of the exact M ELF proves that the indirect call at `0xfe7bc` +loads the VideoOut handle into `edi`, zero into `esi`, one into `edx`, and +zero into `ecx`. The corresponding GOT slot is `0x1779b0`. The code tests +`eax` and, on failure, reads `errno` before emitting D07. The O trace therefore +binds the following facts across source, map, disassembly and runtime: + +- `sceVideoOutOpen` returned positive handle `0x4e100100`; +- `sceVideoOutRegisterBuffers2(handle, 0, 0, buffers, 2, attr, 0, NULL)` + returned `0`; +- the diagnostic copied pixels into registered buffer zero; +- `sceVideoOutSubmitFlip(handle, 0, 1, 0)` returned `-1`; +- the immediately saved `errno` was `0`; +- no flip-event wait followed the failed submit. + +The unmodified public SDL source at commit +`0baf4ac49382b537ba449901b5b6d0d189bb1fbb` also starts `frame_id` at zero, +uses `idx = frame_id % 2`, and calls the same tuple on its first normal frame. +The earlier diagnostic-versus-normal first-index mismatch has therefore been +eliminated and cannot explain the O result. + +## Evidence limits + +SDK v0.41 commit `d2e2e585740362976a39fdd5ccf390f199a7bc37` exports symbol stubs, +but supplies no public VideoOut prototypes, structures, constants or return +semantics. The SDL fork is the only local public source for the prototype, +the 32-byte buffer descriptor, the opaque 80-byte attribute object and the +format constant `0x8000000022000000`. Buffer registration returning zero is +useful runtime evidence, but it does not independently validate those opaque +semantics. + +| Question | Classification | Reason | +|---|---|---| +| exact failure site | PROVEN | source/map/disassembly plus D07 | +| raw submit return | PROVEN | D07 is `-1` | +| useful errno identity | ABSENT | saved value is `0` | +| frame-zero difference from normal path | REJECTED_CURRENT_CAUSE | both paths now use zero | +| buffer-zero validity | UNPROVEN | consistent with source, not independently specified | +| flip-mode value `1` | UNPROVEN | no local public semantic definition | +| frame-ID zero validity | UNPROVEN | used by SDL, not independently specified | +| buffer attribute/layout semantics | UNPROVEN | opaque fork-local declaration | +| flip-master or active-app ownership | UNPROVEN | export names are not callable ABI evidence | +| visible presentation or cleanup | UNPROVEN | no successful submit or terminal proof | + +Calling `sceVideoOutSetFlipMaster`, a status query, or changing a submit +argument based only on an export name would be an ABI guess. Phase 1.0P does +not authorize or recommend such a change. Its decision is +`VIDEOOUT_SUBMIT_FAILURE_SITE_PROVEN_ROOT_CAUSE_UNRESOLVED`. + +## Safe continuation gate + +Before another target artifact is designed, acceptable public evidence must +define the relevant VideoOut signature, arguments, structure layouts, return +codes and required ownership/state. If that evidence cannot be obtained, the +hardware path remains fail-closed. A later artifact also needs a corrected +terminal protocol, separately audited and separately authorized before any +device action. diff --git a/docs/retroarch/phase-1.0q-public-videoout-evidence.md b/docs/retroarch/phase-1.0q-public-videoout-evidence.md new file mode 100644 index 0000000..0a2c9e1 --- /dev/null +++ b/docs/retroarch/phase-1.0q-public-videoout-evidence.md @@ -0,0 +1,109 @@ +# Phase 1.0Q public VideoOut evidence + +Phase 1.0Q is bounded public-source research following the Phase-1.0P submit +postmortem. It performed no PS5 connection, transfer, execution, target build +or target-source change. Searches and source acquisition were limited to official +GitHub repositories and immutable source archives. No downloaded code was +executed. + +## PS5 evidence lineage + +The exact SDK v0.41 tag remains +[`d2e2e585740362976a39fdd5ccf390f199a7bc37`](https://github.com/ps5-payload-dev/sdk/tree/d2e2e585740362976a39fdd5ccf390f199a7bc37). +Its `libSceVideoOut.c` is a 25,199-byte export-stub file with SHA-256 +`da6cff9b3255e9ccb0440790f73e0265696ca47724cf8fe5950b579702940016`. +It establishes symbol names, but contains no VideoOut declarations, data +layouts, constants or return semantics. The official SDK master inspected at +`a0d2bc60bdcc0a5ee9e790fa3b02fe5051a152d0` still has no public VideoOut +header. v0.41 was the latest published SDK release during this audit. + +The PS5 declarations originate in one source lineage: the +[`ps5-payload-dev/SDL` PS5 backend](https://github.com/ps5-payload-dev/SDL/tree/0baf4ac49382b537ba449901b5b6d0d189bb1fbb/src/video/ps5). +The initial backend commit was +[`2682a5e31e8aee888538b0fb7253d8cadf2797a1`](https://github.com/ps5-payload-dev/SDL/commit/2682a5e31e8aee888538b0fb7253d8cadf2797a1). +It introduced the four-argument submit declaration, `mode=1`, first +`frame_id=0`, `RegisterBuffers2`, `SetBufferAttribute2`, the 32-byte buffer +record and the 80-byte attribute record together. The two unknown records were +already named with `junk0`; the commit cites no independent ABI source. + +Commit +[`fdfa470a0fa33215c677193982dfe3651ac1321a`](https://github.com/ps5-payload-dev/SDL/commit/fdfa470a0fa33215c677193982dfe3651ac1321a) +only moved those declarations into `SDL_ps5video.h`. Commit +[`14ac2ec2ab5889af47218a957e3456e613b13d3d`](https://github.com/ps5-payload-dev/SDL/commit/14ac2ec2ab5889af47218a957e3456e613b13d3d) +changed `uint` to `uint32_t` and improved resource handling; it did not add a +second provenance source. Later history preserves the same submit tuple. + +The pinned header and source hashes are respectively +`e60766e0b43c2a7fceba2ada0c030e7f7b05e63885590092f2f8f5b9da34ed75` +and `44124546da132ea6e12b1f06a1808d61690c73460a65c3161e8453e8108684b6`. + +## Bounded official-project scan + +Immutable source archives for the graphics-relevant repositories in the +`ps5-payload-dev` organization were downloaded to a separate temporary host +directory, hashed, extracted and searched statically. None contains a direct +VideoOut declaration or call. PacBrew's SDL2 recipe points back to the same SDL +repository and uses a moving Git source with `sha256sums=('SKIP')`; it is not +independent ABI evidence. + +| Repository | Commit | Archive SHA-256 | Direct VideoOut evidence | +|---|---|---|---| +| libcross2d | `829b167262d3567a048b6e69416f8e3be399c098` | `eb952a041396ba01224a890d14d41662b017488f487ddd4c4018ffddd4a59ae2` | none; SDL consumer | +| pemu | `4136088e13e825f33c9d0cc43ff9a8c8f749dcb0` | `9ed14a1ea432638992b614f9946bcbc9770f70b6f18ded5778fd6809a927eaeb` | none; SDL consumer | +| FBNeo | `ca4222ca2cd52215a673c565a5eae3589b352ba2` | `824d6e5b6b61555f5ecc598204052dcb4b1cf16d28be29b9594a44866e726ef3` | none | +| LakeSnes | `a2db690123649c7ffbc68a663af31efb3a41bf3f` | `253a08957a68461a8535a4727ee1242c21b2dc40c8eb8c99bdb85af799ac3ca6` | none; links SDL2 | +| yquake2 | `9e233b6a601c393be4ff1dcb1c003750346b8bbb` | `01ef41608b2be0ace119019901a6ea22f304542972bace714d1177cb7f567978` | none; SDL consumer | +| pacbrew-repo | `c2abcfcb60f569128abd0e8e70ad03a67bee5ea7` | `17be50219ce41772cfc50ea9274b0a62a2259162556b6eeecfaed4a691822872` | references the same SDL fork | + +The separate public [`PS5Dev/PS5SDK`](https://github.com/PS5Dev/PS5SDK/tree/a2e03a2a0231a3a3397fa6cd087a01ca6d04f273) +tree at `a2e03a2a0231a3a3397fa6cd087a01ca6d04f273` has no VideoOut source or +header. It cannot corroborate the current PS5 declarations. + +## PS4 analogue boundary + +OpenOrbis PS4 source at +[`0a1aaf9dd4a92695538bdeb09fb056d06dd11725`](https://github.com/OpenOrbis/OpenOrbis-PS4-Toolchain/tree/0a1aaf9dd4a92695538bdeb09fb056d06dd11725) +declares the historical four-argument `sceVideoOutSubmitFlip` and documents +flip mode `1` as VSYNC. This is useful lineage evidence, but it is PS4 Orbis +evidence. It does not define PS5's `RegisterBuffers2`, `SetBufferAttribute2`, +descriptor layouts, ownership rules or firmware-9.60 return behavior. It is +classified `PS4_ANALOG_ONLY` and cannot authorize a PS5 parameter change. + +The six source archives total 28,169,547 bytes. They remain outside the +repository in the dedicated temporary research directory because the local +command policy rejected cleanup. Nothing from that directory is tracked or +used by tests. + +## Evidence matrix + +| Contract | PS5 evidence | Independent PS5 corroboration | Decision | +|---|---|---|---| +| submit has four arguments | SDL lineage | none | PARTIAL_SINGLE_LINEAGE | +| argument positions | SDL lineage | none | PARTIAL_SINGLE_LINEAGE | +| mode `1` means VSYNC | SDL use; PS4 analogue | none | UNPROVEN_ON_PS5 | +| first frame/index zero is valid | SDL use | none | UNPROVEN_ON_PS5 | +| 32-byte `PS5_VideoBuf` layout | SDL `junk0` record | none | UNPROVEN | +| 80-byte `PS5_VideoAttr` layout | SDL `junk0` record | none | UNPROVEN | +| `0x8000000022000000` semantics | SDL literal | none | UNPROVEN | +| `RegisterBuffers2` return semantics | O observed zero | no public definition | PARTIAL_RUNTIME_ONLY | +| submit error/return semantics | O observed `-1`, errno `0` | none | UNPROVEN | +| flip-master/active-app requirement | SDK export name only | none | UNPROVEN | +| status-query signatures/layouts | no PS5 declarations | none | UNPROVEN | + +## Decision + +The bounded public inventory does not supply an independent, acceptable PS5 +VideoOut ABI and runtime-state contract. The exact submit failure remains +source-bound, but its root cause is unresolved. Changing the index, mode, +frame ID, buffer attributes or ownership state would still be an experiment +based on guessed semantics. Calling `sceVideoOutSetFlipMaster` or a status +query would additionally guess a signature and output layout. + +Classification: +`PUBLIC_VIDEOOUT_EVIDENCE_INSUFFICIENT_PARAMETER_CHANGE_BLOCKED`. + +No Phase-1.0R target artifact or device run may begin from this evidence. A +safe continuation must either obtain a new acceptable public PS5 contract or +audit, offline, whether the known SDL users run under a different launch/app +context than the raw elfldr payload. Host/software-only integration can +continue independently. diff --git a/docs/retroarch/phase-1.0r-launch-context-comparison.md b/docs/retroarch/phase-1.0r-launch-context-comparison.md new file mode 100644 index 0000000..c55ab39 --- /dev/null +++ b/docs/retroarch/phase-1.0r-launch-context-comparison.md @@ -0,0 +1,126 @@ +# Phase 1.0R: PS5 launch-context comparison + +Status: `NO_SOURCE_PROVEN_LAUNCH_CONTEXT_FIX_TARGET_CHANGE_BLOCKED` + +Date: 2026-07-22 + +This is an offline source, map, and provenance audit. It did not build target +code, connect to the PS5, transfer an artifact, receive a device result, or +execute anything on the PS5. Phase-1.0O authority remains consumed and every +authorization remains false. + +## Question and evidence boundary + +Phase 1.0O proved that the exact M artifact opened VideoOut, registered its +buffers, and received `-1` with saved errno `0` from the first +`sceVideoOutSubmitFlip(handle, 0, 1, 0)`. Phase 1.0R asks whether the raw +elfldr path differs, in a source-proven way relevant to that call, from the +launch context used by existing PS5 SDL ports. + +The comparison is bounded to these exact clean source identities: + +| Component | Commit | Relevant evidence | +|---|---|---| +| Chimera RetroArch | artifact source `12cf1d783c41eb303987e49a5a920805a59ef7a4`; inspected HEAD `606909706f91d7213751c245081333f56c2cce89` | `Makefile.ps5`; exact M map and artifact identities | +| PS5 Payload SDK | `d2e2e585740362976a39fdd5ccf390f199a7bc37` | `crt/crt.c`, `toolchain/prospero.mk` | +| PS5 SDL | `0baf4ac49382b537ba449901b5b6d0d189bb1fbb` | `src/main/ps5/SDL_ps5_main.c`, `src/video/ps5/SDL_ps5video.c` | +| hardened elfldr | `197623058f509eddde18868dafcb92fdcac66464` | `elfldr.c`, `socksrv.c` | +| controlled Payload Manager | `e23d94ff91233aa770e2342800c1467875bdef44` | `src/ps5_launcher.c`, `src/main.c` | +| PacBrew recipes | `c2abcfcb60f569128abd0e8e70ad03a67bee5ea7` | SDL2 and port recipes | +| LakeSnes reference | `a2db690123649c7ffbc68a663af31efb3a41bf3f` | `Makefile.prospero`, `main.c`, `homebrew.js`, `README.prospero` | + +The official port archives retained from Phase 1.0Q are source references, +not deployed-artifact or on-device launch evidence. + +## Exact compared paths + +### Direct raw ELF and Payload Manager + +The hardened elfldr listener sends every accepted raw ELF through +`payload_spawn()` in `socksrv.c`, then through `elfldr_spawn()` in `elfldr.c`. +That routine creates a process with `rfork_thread`, executes the system +`SceSpZeroConf` image, replaces its executable mapping under ptrace, and +detaches. The legacy route duplicates the accepted descriptor to standard +input/output/error; the controlled route changes standard-I/O and watchdog +policy. Neither route registers an application, title ID, flip owner, or +display context. + +Payload Manager's `ps5_launch_elf()` reads the selected ELF and sends it to +loopback port 9021. It does not create a distinct payload process. Therefore +both a direct host send and a Payload Manager launch reach the same +`payload_spawn()`/`elfldr_spawn()` process constructor. Payload Manager does +contain separate LNC-based disc-player management, but that path is not the +ELF process constructor and is not evidence of payload display ownership. + +### Exact RetroArch artifact + +`Makefile.ps5` links `libSDL2.a`, but not `libSDL2main.a`. The exact M linker +map proves `_start` comes from SDK `crt1.o` and includes +`SDL_ps5video.c.o`. It contains no `SDL_ps5_main.c`, no `libSDL2main`, and no +`sceSystemServiceLoadExec`; it does import +`sceSystemServiceHideSplashScreen` through SDL video initialization. + +SDK `_start(payload_args_t *)` initializes its runtime, invokes the ordinary +`main(argc, argv, environ)`, finalizes, and terminates according to the SDK +payload contract. The inspected CRT contains no application/title +registration and no LNC or VideoOut ownership setup. + +### SDL2main ports + +At the pinned SDL commit, PS5 SDL2main is only this lifecycle wrapper: + +1. `sceSystemServiceHideSplashScreen()`; +2. `SDL_main(argc, argv)`; +3. after `SDL_main` returns, `sceSystemServiceLoadExec("exit", 0)`. + +It contains no app registration, title-ID selection, LNC call, process +creation, VideoOut call, or flip-owner acquisition. The `LoadExec` operation +is post-return and cannot affect the first VideoOut submit. + +The exact SDL PS5 `PS5_VideoInit()` independently calls +`sceSystemServiceHideSplashScreen()` immediately before +`sceVideoOutOpen(0xff, 0, 0, NULL)`. Consequently the tested RetroArch path +already performs the only SDL2main pre-entry action relevant by name before +opening VideoOut. Linking SDL2main would merely perform that call earlier and +again; no public source proves that this changes display ownership. + +LakeSnes does link `-lSDL2main -lSceSystemService` and defines `SDL_main`. +Its `homebrew.js` returns an executable path and arguments, while its README +describes `hbldr` through `ps5-payload-shsrv`. These are launcher references, +not source for application registration. The exact `hbldr`/`shsrv` process +constructor and its active-app state were not present in this bounded source +set, so the launch-context relationship is `PARTIAL_UNBOUND`. + +## Comparison matrix + +| Property before first submit | Direct host to elfldr | Payload Manager to elfldr | SDL2main-linked port | Evidence result | +|---|---|---|---|---| +| process constructor | hardened `elfldr_spawn` | same hardened `elfldr_spawn` | launcher-dependent | first two proven same; port launcher unbound | +| SDK CRT | `crt1.o` | `crt1.o` | ordinarily `crt1.o` through `prospero.mk` | same SDK model in inspected builds | +| splash hide | SDL `VideoInit` | SDL `VideoInit` | wrapper plus SDL `VideoInit` | only proven pre-submit difference is earlier duplicate call | +| SDL application entry | ordinary RetroArch `main` | ordinary RetroArch `main` | wrapper calls `SDL_main` | symbol/lifecycle difference only | +| post-return exit | SDK termination | SDK termination | `LoadExec("exit")` before wrapper returns | post-submit lifecycle only | +| title/app registration | absent in inspected path | absent in ELF launch path | absent in SDL2main | no source-proven registration | +| flip/display ownership | absent in inspected path | absent in ELF launch path | absent in SDL2main | unproven everywhere | +| autoload or persistent setup | none in launch route | none required by ELF launch route | launcher-dependent | no evidence of a required fix | + +## LNC log classification + +Phase 1.0O ordinary stdout repeatedly included +`getAppStatus: LNC_ISOK::0x80940004`. It is +`OBSERVED_NONUNIQUE_CORRELATION`: the trace does not identify its caller, +multiple system-service/video activities can interleave, and public source +does not define that value as the cause of the flip failure. It is not a +root-cause verdict and does not justify adding LNC calls. + +## Decision + +`SDL2main` is not a source-proven launch-context correction. PacBrew is a +build/package repository, not a launcher. The official port metadata points +to launchers whose exact process/app/display setup remains unbound. The +possibility that active-app or launcher state matters therefore remains +plausible but unproven. + +The root cause is unresolved. Do not link SDL2main as an experiment, add LNC +or SystemService calls, change submit parameters, call another VideoOut +export, build a target, or perform a device action under Phase 1.0R. diff --git a/docs/retroarch/phase-1.0r-sdl2main-and-packaging-analysis.md b/docs/retroarch/phase-1.0r-sdl2main-and-packaging-analysis.md new file mode 100644 index 0000000..d3c46e7 --- /dev/null +++ b/docs/retroarch/phase-1.0r-sdl2main-and-packaging-analysis.md @@ -0,0 +1,81 @@ +# Phase 1.0R: SDL2main and packaging analysis + +Status: `SOURCE_BOUND_NEGATIVE_RESULT` + +## SDL2main + +Pinned source: `ps5-payload-dev/SDL` commit +`0baf4ac49382b537ba449901b5b6d0d189bb1fbb`, file +`src/main/ps5/SDL_ps5_main.c`, 438 bytes, SHA-256 +`55953065bb81ec6f00224d54c19f667336936e55a6805e08b60f6dda126d6648`. + +| Source location | Operation | Timing | Launch-context conclusion | +|---|---|---|---| +| lines 14-16 | normal `main`, splash hide | before `SDL_main` | duplicate of an action also performed by PS5 `VideoInit`; no registration | +| line 17 | call `SDL_main(argc, argv)` | application entry | C entry-wrapper convention only | +| line 18 | `sceSystemServiceLoadExec("exit", 0)` | after application return | cannot affect first VideoOut submit | + +No application ID, title ID, LNC function, process creation, VideoOut export, +module load, or persistent state appears in this file. + +Pinned PS5 video source: `src/video/ps5/SDL_ps5video.c`, 12,162 bytes, +SHA-256 +`44124546da132ea6e12b1f06a1808d61690c73460a65c3161e8453e8108684b6`. +`PS5_VideoInit()` hides the splash at line 225 and opens VideoOut at line 226. +It creates the event queue, adds the flip event, sets the flip rate, sets the +opaque buffer attributes, and registers buffers. The update path submits +`(handle, idx, 1, frame_id)` at line 145. No source-proven app registration or +flip-owner acquisition precedes it. + +## Exact RetroArch linkage + +The artifact source is RetroArch commit +`12cf1d783c41eb303987e49a5a920805a59ef7a4`. `Makefile.ps5` is 16,122 bytes, +SHA-256 +`6d248aa2b77d5ccea3a8d1de02e939946b7e7764e5b012ef513c1eed4dcc6e27`. +It links static `libSDL2.a` plus `SceSystemService`; it does not link +`libSDL2main.a`. + +The exact M evidence identities remain: + +| Evidence | Size | SHA-256 | +|---|---:|---| +| `retroarch_ps5_write_diag.elf` | 1,845,208 | `c99a0856309a357ad2667d89b4924e4063ad214cae09c8a419457b0732f583cd` | +| linker map | 637,728 | `6768ffc7267b9b362c3b953571e5dfeed1e70004a3e19f41ef500c3f26272719` | + +The map binds `_start` to SDK `crt1.o` and binds the PS5 video functions to +`libSDL2.a(SDL_ps5video.c.o)`. Searches for `SDL_ps5_main`, `libSDL2main`, and +`sceSystemServiceLoadExec` are empty. Thus the tested artifact omitted the +wrapper but not SDL's pre-VideoOut splash-hide call. + +## PacBrew and official port packaging + +PacBrew commit `c2abcfcb60f569128abd0e8e70ad03a67bee5ea7` builds and installs +libraries and ordinary executables. Its SDL2 recipe fetches the same SDL fork +without a pinned source checksum and installs the resulting SDK files. +PacBrew recipes do not create PS5 application IDs or define the process that +runs an installed executable. PacBrew is therefore `PACKAGE_METADATA_ONLY`, +not a launch-context implementation. + +The bounded official source set gives these results: + +| Project | Relevant observation | Classification | +|---|---|---| +| LakeSnes | links SDL2main; `homebrew.js` returns path/args; README uses `hbldr` via shsrv | `SDL2MAIN_USER_LAUNCHER_PARTIAL_UNBOUND` | +| pemu | PS5 packages use `homebrew.js`; its explicit `LoadExec("exit")` is guarded for PS4 only | `SDL_USER_LAUNCHER_PARTIAL_UNBOUND` | +| FBNeo, Mednafen, DevilutionX, EDuke32 | PacBrew builds/install ordinary SDL executables | `PACKAGE_METADATA_ONLY` | +| yquake2 | SDL consumer; no exact PS5 launched artifact/process contract | `SDL_CONSUMER_LAUNCH_CONTEXT_UNPROVEN` | + +A `homebrew.js` object that returns `path` and `args` is not application +registration. A README command naming `hbldr` is not source proof of how +`hbldr` creates a process or which active-app/display state exists. No +official-port success claim is used as firmware-9.60 hardware evidence. + +## Fail-closed outcome + +The exact pre-submit effect contributed by SDL2main is +`EARLIER_HIDE_SPLASH_ONLY`. The exact post-return effect is +`LOAD_EXEC_EXIT`. Neither is a source-proven correction for the observed +submit failure. The launcher and active-app hypotheses remain unproven until +the exact `hbldr`/`shsrv` implementation and launch provenance are bound in a +separate offline phase. diff --git a/docs/retroarch/phase-1.0s-bigapp-launch-contract.md b/docs/retroarch/phase-1.0s-bigapp-launch-contract.md new file mode 100644 index 0000000..60f7587 --- /dev/null +++ b/docs/retroarch/phase-1.0s-bigapp-launch-contract.md @@ -0,0 +1,104 @@ +# Phase 1.0S: hbldr BigApp launch and safety contract + +Status: `EXISTING_ROUTE_UNSAFE_FOR_CURRENT_DEVICE_GATE` + +## v0.19 callgraph + +```text +host telnet client + -> shsrv TCP listener on port 2323 + -> accepted connection + -> elfldr_spawn(embedded sh.elf) + -> shell parses command + -> builtin hbldr + -> elfldr_spawn(embedded hbldr.elf) + -> read requested ELF from device filesystem + -> ensure /system_ex/app/FAKE00000 exists + -> optionally remount /system_ex + -> optionally mkdir/write param.json and eboot.bin + -> initialize foreground-user context + -> kill currently running BigApp, if present + -> attach to SceSysCore and follow fork + -> sceSystemServiceLaunchApp("FAKE00000", ...) + -> follow child exec + -> set child rootdir/jaildir through kernel helpers + -> replace BigApp process image with requested ELF + -> detach; target continues +``` + +The v0.7 path has the same essential distinction from raw elfldr but directly +launches VideoPlayer title `PPSA01659`. It does not contain the later +`FAKE00000` creation/remount path. It still kills a running BigApp, reads a +device-resident target ELF, changes target-process root/jail state, and injects +the ELF into a SystemService-launched BigApp. + +## Raw elfldr versus hbldr + +| Property | Hardened raw elfldr | Official hbldr | +|---|---|---| +| input | host-streamed raw ELF | path to ELF already on device | +| process basis | `SceSpZeroConf` child | SystemService-launched BigApp | +| foreground user | not established by inspected route | queried and passed to launch context | +| app/title launch | none | v0.7 `PPSA01659`; v0.19 `FAKE00000` | +| running BigApp | left untouched by constructor | may be killed before launch | +| persistent writes | none in raw launch route | possible v0.19 fake-app creation | +| kernel/ptrace process mutation | yes | yes, plus BigApp substitution | +| display permission | unproven | unproven; architecture is materially different | + +The difference is source-proven. Its effect on VideoOut is not. + +## Side-effect matrix + +| Effect | v0.7 | v0.19 | Classification | +|---|---|---|---| +| read target ELF from device | yes | yes | requires staging; current hard blocker | +| terminate active BigApp | yes | yes | `HIGH_SESSION_DISRUPTION` | +| launch SystemService BigApp | VideoPlayer | fake game | `HIGH_FUNCTIONAL` | +| attach/follow SysCore child | event tracking | ptrace follow-fork/exec | `HIGH_RUNTIME_MUTATION` | +| kernel root/jail changes | yes | yes | outside current safety scope | +| executable mapping replacement | yes | yes | expected launcher mutation, not persistent file proof | +| remount `/system_ex` | no | possible | `BRICK_RELEVANT_HARD_BLOCKER` | +| create fake-app directories/files | no | possible | `PERSISTENT_SYSTEM_MUTATION_HARD_BLOCKER` | +| autoload change | not found | not found | source-negative only | +| bounded deadline | no | no | `SESSION_HANG_RISK` | +| automatic retry | not found | not found | source-negative only | +| rollback/power-loss protocol | absent | absent | v0.19 `POWER_LOSS_UNSAFE_OR_UNPROVEN` | + +## Persistent-write audit for v0.19 + +`fakeapp_create_if_missing()` checks fixed paths, creates directories, writes a +fixed `param.json`, and copies the existing PSNow eboot into the fake app. If +the initial attempt fails, `remount_system_ex()` performs an `MNT_UPDATE` and +the creation is attempted again. + +The writes have no temporary sibling, atomic rename, `fsync`, byte-exact +post-write verification, backup, rollback, or power-loss recovery. `open()` +uses `O_CREAT|O_WRONLY`, without `O_EXCL` or an atomic replacement protocol. +This is incompatible with Chimera GFX's current anti-brick gate regardless of +whether a particular target already has the fake app. + +## Robustness findings + +- `readfile()` has no maximum input size and allocates the whole ELF; +- process waits, child-await, and `pthread_join` have no hard deadline; +- the port-2323 listener binds all IPv4 interfaces and the inspected source + shows no authentication or transport protection; +- `splitstring()` initially allocates `bufsize * sizeof(char)` but stores + pointers, a source-visible memory-corruption risk; +- `which()` uses unbounded `strcpy`/`sprintf` into `PATH_MAX` storage; +- failure branches do not establish a complete restoration contract for every + traced process and modified breakpoint; +- target authenticity, size, and SHA-256 are not verified before launch. + +These are `SESSION_CRASH_OR_INTEGRITY_RISK` unless a persistent write is +involved. A session crash is not automatically a brick, but it does not satisfy +the bounded one-shot Chimera test contract. + +## Decision + +The official hbldr route cannot be reused, imitated, or treated as an approved +launcher in the current phase. It requires device staging and contains +operations explicitly outside the active authorization and safety boundary. +No `hbldr` command, port-2323 connection, shsrv deployment, target build, +transfer, installation, app termination, system remount, file creation, or +execution is authorized. diff --git a/docs/retroarch/phase-1.0s-hbldr-shsrv-provenance.md b/docs/retroarch/phase-1.0s-hbldr-shsrv-provenance.md new file mode 100644 index 0000000..5b9a32d --- /dev/null +++ b/docs/retroarch/phase-1.0s-hbldr-shsrv-provenance.md @@ -0,0 +1,107 @@ +# Phase 1.0S: official shsrv/hbldr provenance + +Status: +`BIGAPP_CONTEXT_SOURCE_PROVEN_DEPLOYED_IDENTITY_UNPROVEN_DEVICE_PATH_BLOCKED` + +Date: 2026-07-22 + +This phase acquired and inspected only the official public +`ps5-payload-dev/shsrv` Git source. No downloaded code was built or executed. +No PS5 address was used, no connection or request was made, and no target +source, target artifact, transfer package, installation package, or device +client was created. + +## Provenance boundary + +The official repository is `https://github.com/ps5-payload-dev/shsrv`. During +the audit its `master`, latest release tag `v0.19`, and commit all resolved to +`6f320637d56d344a0e7797753099e33238bbf146`; the tree object is +`c26ce02b6c3ca4202993e039b3db7c28c353dee4`. The clean, detached historical +`v0.7` worktree resolves to commit +`74287f5db6b20320efd7892d7b29cf438fe7cb98` and tree +`7184968c702afe038551bf3228cc25f455388bb6`. + +These source identities prove official implementations, not which shsrv +binary—if any—is installed or running on the target PS5. No locally supplied +shsrv/hbldr binary, package receipt, deployment log, or byte-exact device +identity was found. The deployed identity remains `UNPROVEN`. + +## Why v0.7 and v0.19 are both relevant + +The Phase-1.0Q LakeSnes source at commit +`a2db690123649c7ffbc68a663af31efb3a41bf3f` states that shsrv v0.7 or later is +required and launches the emulator with `hbldr`. Therefore v0.7 is the oldest +source-bound implementation relevant to that port documentation. Version +v0.19 is the latest official release inspected during Phase 1.0S. + +The `hbldr` family began at commit +`4a70b50eecab853408f0a93a579b90428369fa09` as an experimental loader that +injects an ELF into a web application. The significant lineage is: + +| Point | Commit/tag | Source-proven behavior | +|---|---|---| +| introduction | `4a70b50eecab853408f0a93a579b90428369fa09` | inject an ELF into a webapp process | +| LakeSnes minimum | `v0.7`, `74287f5...` | launch VideoPlayer BigApp `PPSA01659`, replace its process image | +| fake-game transition | `18e9a62aea801b5a1be4a692dba76bf5381e9fcb` | attach homebrew to a created fake game | +| first release containing transition | `v0.8` | fake-app/system-ex path present | +| current official release | `v0.19`, `6f32063...` | launch `FAKE00000`, replace its process image | + +An official tag or source lineage is not deployed-use proof. The current +device may use no shsrv, a historical build, a current build, or a modified +binary. + +## Exact source identities + +### v0.19 + +| File | Size | SHA-256 | +|---|---:|---| +| `README.md` | 3,546 | `4855fa2adbe1fc0c7aa0174aa3275742d9b9aba3d818ae09cf2722a89b06cd34` | +| `shsrv.c` | 5,293 | `6ec71b4eb6c2bc1159f21568c1c9834f8aecac8d8881113bf09cf8981be19ee3` | +| `sh.c` | 13,278 | `3c4b7f76efdd157436ed4b353ee1b550bf3ff9df17c147b4762b767982fc8253` | +| `elfldr.c` | 17,911 | `4aa31f5a942681f8d88281b9713f087ced8963de842438cacad1088933f8e785` | +| `bundles/hbldr/main.c` | 1,520 | `6fa519888f79fe0458a983a517073c1eccc61239edfbe40d607f69b6b38a8af4` | +| `bundles/hbldr/hbldr.c` | 13,438 | `0096f86a00fdedcbc7509db47eb0f54dbc3e487d37e699f3de79048bac001be9` | + +### v0.7 + +| File | Size | SHA-256 | +|---|---:|---| +| `README.md` | 3,248 | `3d16e46416dd07940fcde1190a6ab558348bc80263da615331dda0f3eb6183ed` | +| `shsrv.c` | 4,716 | `66197d08308180fe923aa8aedc91bd5025f08938156ae3092ea2c69d0ad0be57` | +| `sh.c` | 8,449 | `bf97bc6dd3f49345ad8da9a29b28a5d6bcde5e53a6f32c60a538d6e187c7e05a` | +| `elfldr.c` | 17,920 | `1ff6cfa1300a95e8be48e5f7adc1413e3384ba04ea266c167fcd441ef20197d1` | +| `bundles/hbldr/main.c` | 7,134 | `2081ece2f7d0a7e9b392660696c2a44802f64680365607e742b37dcf3223a55a` | + +## PacBrew relationship + +PacBrew commit `c2abcfcb60f569128abd0e8e70ad03a67bee5ea7` contains an shsrv recipe that +clones the official repository, uses `pkgver=git`, and declares +`sha256sums=('SKIP')`. It installs `shsrv.elf`, but does not pin the fetched +shsrv commit or prove which package was installed on the target. It is +`SOURCE_REPOSITORY_ASSOCIATION`, not byte-exact deployment provenance. + +## Proven and unproven conclusions + +`SOURCE_PROVEN`: + +- the official hbldr family creates or launches a BigApp and replaces that + process with the requested ELF; +- v0.7 and v0.19 differ materially from raw elfldr's SceSpZeroConf process; +- both variants read the target ELF from a device path; +- both may terminate the currently running BigApp and perform kernel/ptrace + process mutations; +- v0.19 may remount and persistently populate `/system_ex/app/FAKE00000`. + +`UNPROVEN`: + +- which shsrv/hbldr binary is deployed; +- whether `FAKE00000` already exists on the target; +- whether the launcher works on the specific firmware-9.60 runtime; +- whether BigApp substitution provides the missing VideoOut permission; +- whether it would make the exact RetroArch first submit succeed; +- crash, cleanup, reboot, and visibility behavior on the target. + +The source difference is a +`STRONG_SOURCE_CANDIDATE_NOT_PROVEN_ROOT_CAUSE`. It does not authorize using +hbldr, staging an ELF, connecting to shsrv, or creating a replacement launcher. diff --git a/docs/retroarch/phase-1.0t-inactive-shsrv-identity-gate.md b/docs/retroarch/phase-1.0t-inactive-shsrv-identity-gate.md new file mode 100644 index 0000000..42e1248 --- /dev/null +++ b/docs/retroarch/phase-1.0t-inactive-shsrv-identity-gate.md @@ -0,0 +1,130 @@ +# Phase 1.0T: inactive shsrv identity-collection gate + +Status: `INACTIVE_METADATA_GATE_DESIGNED_EXACT_IDENTITY_UNAVAILABLE` + +Date: 2026-07-22 + +This phase designs host-only controls. It does not contain a network client, +target address, command sender, target code, artifact, active approval, or +device authorization. No connection or PS5 request was performed. + +## Feasibility result + +The existing shsrv protocol cannot attest the exact deployed binary: + +- the greeting exposes a compile date/time but no source commit or binary + SHA-256; +- `help` exposes a command-set fingerprint, which can identify a source family + but not a byte-exact build; +- `stat` exposes size and filesystem metadata only; +- `sum` opens the target read-only and computes a 16-bit rotating checksum, + not a cryptographic digest; +- no existing command returns a binary-safe full-file stream or SHA-256; +- a PacBrew path or official release identity is not a live-path identity. + +The strongest permitted future result is `WEAK_FILE_CORRELATION_ONLY`. It must +never be labelled `EXACT_DEPLOYED_IDENTITY`, `RUNTIME_VERIFIED`, or recovery +proof. + +## Mandatory connection side effects + +Even before the operator sends a command, a connection to port 2323 causes +shsrv to: + +1. accept a socket and spawn an embedded `sh.elf` through its ELF loader; +2. create a new session, pipes, a thread, telnet state, heap allocations and + process-local environment; +3. query model, serial number, firmware, SoC/CPU temperatures and CPU + frequency; +4. send those values plus the compile timestamp in the greeting. + +The serial is sensitive and unrelated to launcher identity. A future approved +collector must discard it before persistence and must never print it to Codex, +logs, manifests, filenames, screenshots, or commits. Temperature/frequency +values must also be discarded because they add no provenance value. The +offline parser in `tools/phase10t_shsrv_transcript.py` enforces this for an +already-supplied transcript, but it is not a network client. + +## Command-effect matrix + +| Command or event | Source behavior | Identity value | Future gate | +|---|---|---|---| +| connect/greeting | spawns shell; exposes serial and telemetry | compile metadata | separate explicit acceptance required | +| `help` | current source allocates/sorts command entries; v0.7 uses a static map | source-family fingerprint | first and only initial command candidate | +| `stat ABS_PATH` | metadata query; current implementation runs in a forked helper | size/times only | exact pre-attested path only | +| `sum ABS_PATH` | `O_RDONLY`, full read, 16-bit rotate checksum; forked helper | weak correlation only | exact pre-attested path only | +| `ps`/`procstat` | exposes broad process/app information | unnecessary | excluded | +| `env`/`sysctl` | may expose unrelated or sensitive state | unnecessary | excluded | +| `ls`/`find` | directory discovery/path expansion | path guessing | excluded | +| `cat`/`hexdump` | unframed content over telnet | not binary-safe | excluded | +| `hbldr`/`exec`/`launch` | launches or replaces a process | none for identity | permanently forbidden here | +| every write/mount/signal command | mutation | none | permanently forbidden here | + +For current source, `stat` and `sum` use `fork=true`: the command helper uses +`rfork_thread`, allocates a 4 MiB stack, changes its budget and duplicates +descriptors. In v0.7 these commands execute through an embedded core ELF. A +read-only filesystem operation therefore still has process and scheduler side +effects. `sum` may additionally affect atime, caches and I/O accounting. + +## Offline transcript model + +The parser accepts text only through standard input and optionally retains +metadata for literal `--expected-path` values. It: + +- retains no serial, model, temperature, frequency, raw transcript or unknown + path; +- fingerprints sorted command names; +- recognizes the exact official v0.7 and v0.19 source-family fingerprints; +- labels the 16-bit checksum `BSD_ROTATE_16` and non-cryptographic; +- always emits `exact_identity=false`; +- imports no socket, HTTP or URL client and writes no file. + +Official source fingerprints are: + +| Source | Commands shown by `help` | SHA-256 of sorted names | +|---|---:|---| +| v0.7 | 44 | `40313637116b532f3c7f9bebe2c23c0018fe7d4093840cf463a22ba0314ca021` | +| v0.19 | 50 | `f41168292e205590bda1d243cdf727044e0af280a89fb0c070f4c5d6c92f2fd7` | + +A fingerprint match is still only a source-family candidate because builds +can change while preserving the command set. + +## Future operational windows—not authorized + +### Window T1: manual host facts + +Without touching the PS5, the operator may later identify the original local +shsrv binary, source URL, download date, filename, size and host SHA-256. This +creates a local candidate only. No binary was found during Phase 1.0T. + +### Window T2: greeting and `help` + +Requires new exact authorization, an already-running listener attestation, one +connection, no scan, no reconnect and explicit acceptance of shell spawning +and automatic serial/telemetry reads. The collector must redact before any +persistence. Only `help` may be sent. Failure or mismatch ends the window. + +### Window T3: one exact path + +Requires separate authorization and a path supplied from independent evidence. +One fresh connection may issue `stat` and optionally `sum` for that path only. +No wildcard, relative path, shell expansion, pipe, redirection, semicolon, +newline injection, directory listing or path guessing is allowed. + +### Window T4: fake-app metadata + +Requires separate authorization. Only `stat` of the fixed source-bound +`FAKE00000` paths may be considered. It must not call hbldr, create missing +paths, remount, repair, delete or inspect file contents. + +## Hard stops + +- listener presence is not independently attested; +- address, exact path, command list or consent is absent; +- raw serial could reach persistent output; +- protocol framing or prompt is not recognized; +- any response is partial, malformed or exceeds its bound; +- any automatic retry, reconnect, resume or fallback is configured; +- any command outside the separately approved literal list is requested. + +Phase 1.0T does not authorize any of these future windows. diff --git a/docs/retroarch/phase-1.0u-local-shsrv-artifact-inventory.md b/docs/retroarch/phase-1.0u-local-shsrv-artifact-inventory.md new file mode 100644 index 0000000..bfff2d7 --- /dev/null +++ b/docs/retroarch/phase-1.0u-local-shsrv-artifact-inventory.md @@ -0,0 +1,78 @@ +# Phase 1.0U: bounded local shsrv artifact inventory + +Status: `BOUNDED_LOCAL_INVENTORY_COMPLETE_NO_DEPLOYED_CANDIDATE_FOUND` + +Date: 2026-07-22 + +This phase is host-only. It performed no internet request, PS5 connection, +port operation, target build, payload execution, transfer or file mutation. +It did not execute any discovered script or binary. + +## Question and result + +Phase 1.0T established that the deployed shsrv binary cannot be identified +exactly through its existing shell metadata. Phase 1.0U therefore searched for +the original host-side `shsrv.elf`, package, receipt or deployment record that +could instead be hashed directly. + +No deployed candidate was found in the bounded scope. This means only +`NO_CANDIDATE_IN_SCANNED_SCOPE`; it does not prove that no copy exists +elsewhere on the host or device. + +## Bounded scope + +The inventory was restricted to: + +- the Chimera GFX repository, including existing work and outputs; +- the known Chimera elfldr, Payload Manager and RetroArch sibling trees; +- already supplied Codex attachment storage; +- candidate filenames in the previously evidenced user download directory; +- entry names, but not extraction or execution, for ZIP files in those roots. + +The scan matched case-insensitive `shsrv`/`hbldr` names and known target/host +artifact extensions. It did not scan the whole computer, browser databases, +unrelated archives, network shares or the PS5. Non-ZIP archives with unrelated +names were not inspected. + +## Findings + +| Object | Size | SHA-256 | Classification | +|---|---:|---|---| +| `work/upstream/shsrv` | n/a | commit `6f320637d56d344a0e7797753099e33238bbf146` | official source reference only | +| `work/upstream/shsrv-v0.7` | n/a | commit `74287f5db6b20320efd7892d7b29cf438fe7cb98` | historical source reference only | +| `work/upstream/shsrv/host/prospero-shsrv-shell` | 1,026 | `34283c1d27a8b723ba529dfd5ed79ff9a1f967ab2ad2b97ed2b8889351f0973a` | official host wrapper, not target binary | +| `work/upstream/pacbrew-repo/shsrv/PKGBUILD` | 1,051 | `3f134e9986fb8352136150216006d68fb92052ec55d5698d36825a97c37c3b30` | unpinned package recipe only | + +The official host wrapper selects `rlwrap telnet` or `telnet`. It was read and +hashed but never executed. It contains no target shsrv bytes and cannot prove +what is installed. The PacBrew recipe would build `shsrv-ps5.elf` and package +it as `shsrv.elf`, but uses an unpinned Git source with `sha256sums=('SKIP')`. +It is not a package receipt or installed-file identity. + +The existing Y2JB and Chimera ZIP entry-name inventory yielded no shsrv/hbldr +entry. Existing elfldr, Payload Manager, Chimera GFX and RetroArch ELFs are +distinct artifacts and were not relabelled as shsrv candidates. + +## Evidence boundary + +None of the following was found: + +- a local `shsrv.elf` or `shsrv-ps5.elf` deployment candidate; +- a package archive containing a shsrv/hbldr-named target; +- a package-manager receipt with installed hash and path; +- a host transfer log binding exact bytes to the device; +- an operator-provided original binary; +- an independently verified current device path or hash. + +An official source checkout, host helper or package recipe is not deployed +identity. A future operator-supplied file must first be treated as +`LOCAL_CANDIDATE_ONLY`, hashed without execution, and provenance-reviewed. + +## Decision + +Exact deployed shsrv identity remains `UNPROVEN`. No Phase-1 launch-context +experiment is enabled. The next permitted engineering activity is an offline, +inactive design for a one-shot sanitizing collector. That design must still +begin with all addresses, commands and authorizations absent and may not open a +socket. Live collection would require separate exact authorization and +acceptance of shsrv's unavoidable shell, serial and telemetry effects. diff --git a/docs/retroarch/phase-1.0v-inactive-one-shot-shsrv-collector.md b/docs/retroarch/phase-1.0v-inactive-one-shot-shsrv-collector.md new file mode 100644 index 0000000..e8324a4 --- /dev/null +++ b/docs/retroarch/phase-1.0v-inactive-one-shot-shsrv-collector.md @@ -0,0 +1,105 @@ +# Phase 1.0V: inactive one-shot shsrv collector model + +Status: `INACTIVE_OFFLINE_COLLECTOR_MODEL_COMPLETE_NO_LIVE_CLIENT` + +Date: 2026-07-22 + +Phase 1.0V implements and tests only an offline input model. It has no socket, +address, port, DNS, connection, command sender, timeout loop, retry or device +transport. No PS5 action was performed and no live action is authorized. + +## Purpose + +Phase 1.0T proved that a shsrv greeting automatically exposes serial and +telemetry and that existing metadata cannot establish an exact binary +identity. Phase 1.0U found no original host target to hash. This phase models +the minimum safe receive-and-sanitize boundary before deciding whether a later +separately approved live collector is justified. + +The model accepts only synthetic or already-supplied bytes through stdin or a +host unit test. It cannot initiate collection. + +## State and bounds + +The state sequence is: + +`READY -> RECEIVING -> SEALED` + +Failure produces `INVALID`; an operator abort produces `ABORTED`. Input after +sealing/abort and a second finalize are rejected. Limits are: + +- 65,536 total input bytes; +- 65,536 post-Telnet bytes; +- 256 chunks; +- strict UTF-8 after Telnet filtering; +- one finalize and one sanitized result only. + +There is no automatic retry, reconnect, resume or fallback. + +## Telnet handling + +The incremental filter removes `WILL`, `WONT`, `DO`, `DONT` option exchanges +and bounded subnegotiation sequences. It supports control sequences split +across chunks. An incomplete control sequence at EOF fails closed. The model +does not send the negotiation replies a live Telnet client may require; that +is deliberately outside this phase. + +## Sanitization + +After filtering, Phase 1.0T parsing: + +- discards serial, model, temperature and CPU-frequency values; +- retains compile metadata and firmware only; +- retains file metadata only for an exact pre-approved literal path; +- labels `sum` as a non-cryptographic 16-bit checksum; +- never returns raw transcript data; +- always returns `exact_identity=false`. + +The internal bytearray is overwritten and cleared before return or invalidation. +Python and the operating system may have made copies outside that bytearray, +so `physical_memory_erasure_proven=false`. The guarantee is limited to no raw +transcript in the model's persistent output. + +## CLI boundary + +The only supported CLI input mode is: + +```text +--offline-transcript [--expected-path LITERAL_PATH] +``` + +There is no target, host, address, port, command, connect or send option. The +CLI reads stdin in 4 KiB chunks and prints sanitized JSON. It is an offline +analysis tool, not a deviceclient. + +## Future live requirements + +A later live implementation would require a separate phase and review of: + +- an exact inactive manifest and untracked approval; +- one pre-attested listener and literal address; +- one connection and one command window; +- an absolute monotonic deadline; +- no raw stdout/log/file persistence; +- explicit acceptance of automatic serial/telemetry reads; +- Telnet reply behavior and prompt framing; +- exclusive sanitized output creation; +- no retry, reconnect, resume, path discovery or fallback; +- a consumed-attempt receipt before opening a socket. + +None of those live capabilities or approvals exists in Phase 1.0V. + +## Phase 1.0W self-review remediation + +Before using this model as an architecture dependency, self-review corrected +the doubled-`IAC` subnegotiation state, stopped empty chunks consuming the +chunk budget, added absolute normalized path allowlisting, gated firmware +metadata to exact `9.60`, validated compile date/time formats and normalized +numeric parser failures to a generic fail-closed result. The model was rehashed +after these changes. Physical memory erasure remains unproven. + +## Decision + +The offline sanitization model is ready for human review. Exact deployed shsrv +identity remains unproven. No launch-context experiment, target build, +connection, command, transfer or execution is enabled. diff --git a/docs/retroarch/phase-1.0w-self-review-and-inactive-client-architecture.md b/docs/retroarch/phase-1.0w-self-review-and-inactive-client-architecture.md new file mode 100644 index 0000000..673c4a0 --- /dev/null +++ b/docs/retroarch/phase-1.0w-self-review-and-inactive-client-architecture.md @@ -0,0 +1,95 @@ +# Phase 1.0W: self-review and inactive shsrv client architecture + +Status: `INACTIVE_CLIENT_ARCHITECTURE_COMPLETE_NETWORK_IMPLEMENTATION_BLOCKED` + +Date: 2026-07-22 + +This phase reviewed and remediated the Phase-1.0V offline collector, then +implemented only an offline dual-record policy and fake transport. It created +no socket, DNS lookup, live CLI, device address, command sender, network +transport, target code or target artifact. No device action occurred. + +## Self-review result + +| Area | Finding | Result | +|---|---|---| +| Telnet subnegotiation | doubled `IAC` retained the wrong continuation state | fixed and regression-tested | +| chunk accounting | empty chunks consumed the 256-chunk budget | fixed; empty chunks are ignored | +| expected paths | arbitrary exact strings could be retained | fixed; absolute normalized character-allowlisted paths only | +| firmware metadata | arbitrary `S/W:` text could reach sanitized output | fixed; exact `9.60` or absent only | +| compile metadata | date/time shape was not constrained after parsing | fixed with strict format validation | +| numeric metadata | extreme decimal input could raise a raw parser failure | fixed to a generic fail-closed collector error | +| application buffer | bytearray is overwritten and cleared | logical discard only | +| physical memory | Python/runtime copies cannot be proven erased | `UNPROVEN`, unchanged | +| live protocol | negotiation replies, prompt and disconnect cleanup | `UNPROVEN`, no implementation | + +The remediated collector is 7,429 bytes with SHA-256 +`f8a306dafee5d135919bec5afda789dd741e57f39803b7683fb8747c186db25c`. + +## Inactive architecture + +`tools/phase10w_shsrv_client_policy.py` validates two synthetic records: + +1. a manifest activation; +2. a separate operator approval. + +The following fields must match byte-for-value: collector hash, run ID, +target, source-bound port, window, literal path, command tokens, deadline and +expiry. Unknown fields fail closed, both records bind the policy hash, and the +approval must attest that the listener is already running. The policy returns +an immutable `SessionPlan` data object with no +connect/send/receive methods. + +The tracked records are inactive and contain no values. The active path exists +only as a unit-tested policy function; there is no CLI or transport that can +use the returned plan. + +## Allowlisted future windows + +| Window | Exact command tokens | Exact path | Identity value | +|---|---|---|---| +| `T2_GREETING_AND_HELP` | `help` | forbidden | source-family fingerprint only | +| `T3_ONE_EXACT_PATH` | `stat`, then `sum` | required, safe absolute normalized path | weak file correlation only | + +Command tokens are policy data, not rendered shell lines. No formatter or +sender is implemented, so the architecture cannot issue either sequence. + +## Fixed future limits + +- source-bound port: 2323; +- one connection and no second open; +- maximum runtime deadline: 10 seconds; +- approval expiry: after explicit host time and within 15 minutes; +- retry, reconnect, resume and fallback: forbidden; +- transfer, execution, installation, autoload and device write: forbidden; +- explicit acceptance required for spawned shell, serial query, telemetry + query, sanitized-only output and unproven physical memory erasure. + +The fake transport records `OPEN`, allowlisted command tokens, `RECEIVE` and +`CLOSE` in memory. It has no network import or retry API. + +## Missing before any live implementation + +- a consumed-attempt receipt durably created before socket open; +- exclusive sanitized output creation without overwrite; +- monotonic deadline enforcement and host clock acquisition; +- a network transport with exactly one connect and no name-discovery fallback; +- Telnet reply bytes and prompt/completion framing proven against source; +- lossless bounded receive feeding the remediated collector; +- deterministic half-close/close behavior; +- explicit redaction review for every error and exception path; +- a new inactive, hash-bound manifest and approval for that implementation. + +These are design blockers, not authority requests. + +## Classification + +- remediated offline collector: `SAFE_FOR_OFFLINE_MODEL_USE`; +- policy and fake transport: `SAFE_FOR_OFFLINE_ARCHITECTURE_TESTS`; +- exact deployed shsrv identity: `UNPROVEN`; +- live client implementation: `BLOCKED_NOT_PRESENT`; +- connection or device request: `NOT_AUTHORIZED`. + +The next permitted step is a separate offline-only phase for an inactive +transport implementation with fake sockets. It must still perform no real +connection and cannot reuse this phase as live authorization. diff --git a/docs/retroarch/phase-1.0x-inactive-injected-transport.md b/docs/retroarch/phase-1.0x-inactive-injected-transport.md new file mode 100644 index 0000000..47a0033 --- /dev/null +++ b/docs/retroarch/phase-1.0x-inactive-injected-transport.md @@ -0,0 +1,95 @@ +# Phase 1.0X: inactive injected transport and local evidence + +Status: `INACTIVE_INJECTED_TRANSPORT_COMPLETE_LIVE_PROTOCOL_BLOCKED` + +Date: 2026-07-22 + +Phase 1.0X implements only host-side orchestration around an injected adapter. +It has no socket or DNS import, live CLI, target address, port, command-byte +formatter, Telnet reply generator or prompt detector. All tests use synthetic +bytes and a fake monotonic clock. No device action occurred. + +## Source audit before implementation + +The clean official shsrv reference tree at commit +`6f320637d56d344a0e7797753099e33238bbf146` proves the following source +behavior in `sh.c`: + +- `sh_thread` calls `chdir("/")` and `setenv("PWD", "/", 0)`; +- `sh_prompt` reads `PWD`, falls back to `getcwd`, writes `%s$ ` and flushes; +- the greeting automatically reads and emits serial, firmware, temperature + and CPU-frequency data; +- `sh_thread` emits a prompt before input and again after each command path. + +This does not prove the exact deployed binary or live byte stream. In +particular, `setenv` does not overwrite a pre-existing `PWD`, Telnet negotiation +may be fragmented, and the exact relationship between command echo, output and +the next prompt is not bound to deployed firmware-9.60 bytes. Phase 1.0X +therefore passes abstract boundary labels and command tokens to a fake adapter; +it never guesses wire bytes. + +## Implemented offline controls + +`tools/phase10x_inactive_transport.py` provides: + +1. an injected adapter protocol with `open_once`, boundary receive, + allowlisted command-token send and `close_once` methods; +2. an injected monotonic clock and a single absolute deadline; +3. a consumed-attempt receipt created with `O_CREAT|O_EXCL`, flushed with + `fsync`, closed and reopened before adapter open; +4. one pass through the Phase-1.0W immutable session plan and remediated + Phase-1.0V collector; +5. exclusive sanitized JSON output, also flushed, closed and reopened; +6. normalized adapter and collector failures that retain neither raw error + messages nor exception causes; +7. deterministic close after every adapter-open attempt, including an open + operation that raises. + +The receipt intentionally excludes the target address. Sanitized output binds +the receipt SHA-256 and never contains the raw transcript. The evidence store +has no overwrite, delete or cleanup operation. A partial local file left by a +host crash is invalid evidence and permanently consumes that run ID. + +## Proven and unproven properties + +| Property | Classification | Basis | +|---|---|---| +| receipt precedes adapter open | `SAFE_OFFLINE` | code order plus fake-adapter assertion | +| leaf-file overwrite resistance | `SAFE_OFFLINE` | `O_EXCL` plus repeated-run tests | +| file content flush/reopen/hash | `SAFE_OFFLINE` | `fsync`, close, reopen and byte comparison | +| raw transcript/target exclusion | `SAFE_OFFLINE` | output schema and tests | +| one-shot/no retry | `SAFE_OFFLINE` | no retry API or loop; consumed run ID | +| deterministic injected-adapter close | `SAFE_OFFLINE` | fault-injection tests | +| directory-entry durability | `UNPROVEN` | containing directory is not fsynced | +| hostile/symlinked evidence root | `UNPROVEN` | root must be a trusted caller-owned host path | +| deadline during a blocking adapter call | `UNPROVEN` | only pre/post checks and remaining budget are passed | +| exact live prompt and Telnet replies | `UNPROVEN` | no deployed byte-bound protocol evidence | +| live socket cleanup | `UNPROVEN` | no live socket implementation exists | +| firmware-9.60 behavior | `UNPROVEN` | host-only synthetic tests | + +The missing directory fsync is not hidden by file fsync. A future platform +implementation must define a safe cross-platform directory-durability contract +or retain this limitation. Likewise, a future transport must enforce OS-level +timeouts so an adapter call cannot outlive the absolute deadline. + +## Fault model + +The 18 host tests cover receipt ordering, one-shot command order, sanitization, +target exclusion, duplicate run and output rejection, deadline expiry, open/ +receive/send/close faults, boundary limits, malformed transcript rejection, +reopen/hash validation and absence of cleanup, CLI and socket imports. These +tests are architecture evidence only and make no hardware or network claim. + +## Decision + +- inactive injected orchestration: `SAFE_FOR_OFFLINE_FAKE_ADAPTER_TESTS`; +- local exclusive file-content evidence: `SAFE_OFFLINE_WITH_DIRECTORY_DURABILITY_UNPROVEN`; +- exact deployed shsrv identity: `UNPROVEN`; +- live protocol framing: `BLOCKED_UNPROVEN`; +- live network adapter: `BLOCKED_NOT_PRESENT`; +- connection, request or result reception: `NOT_AUTHORIZED`. + +The next permitted step is an offline, source-bound audit of exact prompt, +Telnet negotiation and command-completion framing. It may extend fake protocol +models, but it may not add a socket, address, live CLI, connection or device +request. diff --git a/docs/retroarch/phase-1.0y-offline-shsrv-framing-audit.md b/docs/retroarch/phase-1.0y-offline-shsrv-framing-audit.md new file mode 100644 index 0000000..a7a456b --- /dev/null +++ b/docs/retroarch/phase-1.0y-offline-shsrv-framing-audit.md @@ -0,0 +1,143 @@ +# Phase 1.0Y: offline shsrv framing audit + +Status: `SOURCE_FAMILY_FRAMING_PARTIAL_PROMPT_COMPLETION_UNPROVEN` + +Date: 2026-07-22 + +This phase audits only clean, already-local official shsrv Git history and +implements a pure host model. It creates no socket, address, live CLI, command +formatter or target artifact. No device action occurred. + +## Bound source history + +| Reference | Commit | Framing family | +|---|---|---| +| v0.7 | `74287f5db6b20320efd7892d7b29cf438fe7cb98` | legacy raw | +| v0.8 | `8f76139ee69df4b8cb7c3aee401f05bccb2c2a31` | legacy raw | +| v0.9 | `2f2bc5501d40064c18c06f06f7b1f4cab756389b` | libtelnet/NVT introduced | +| v0.19/current | `6f320637d56d344a0e7797753099e33238bbf146` | libtelnet/NVT | + +Commit `2f2bc55` added `libtelnet.c`, `libtelnet.h` and the Telnet state machine +to `sh.c`. The current reference is also official tag v0.19. Both local trees +are clean. Every local official tag from v0.9 through v0.19 was checked for the +same empty option table, NVT flag, send path and `libtelnet.c` presence. These +facts identify official source families; they do not identify the deployed +binary. + +## Byte-flow graph + +```text +legacy v0.7-v0.8: +peer bytes -> socket-backed stdin -> sh_readline +stdout/stderr -> socket bytes unchanged + +v0.9-v0.19: +peer bytes -> read(4096) -> telnet_recv/NVT -> pipe -> sh_readline +stdout/stderr -> pipe -> read(4096) -> telnet_send_text/NVT -> peer +``` + +The current server configures an empty Telnet option table and does not call +`telnet_negotiate`. It therefore sends no proactive negotiation bytes. From +the initial RFC1143 state, received `IAC WILL x` produces `IAC DONT x`, and +received `IAC DO x` produces `IAC WONT x`; initial `WONT` and `DONT` produce no +reply. A client that sends only plain command bytes triggers none of these +replies. + +## Exact source-family transformations + +| Property | legacy raw | libtelnet/NVT | +|---|---|---| +| incoming plain bytes | unchanged | unchanged except NVT EOL | +| incoming CR LF | remains CR LF; CR is later an argument delimiter | becomes LF | +| incoming CR NUL | unchanged | becomes CR | +| incoming IAC IAC | remains two bytes | becomes one IAC application byte | +| outgoing LF | LF | CR LF | +| outgoing CR | CR | CR NUL | +| outgoing IAC | one IAC | doubled IAC | +| proactive negotiation | none | none | +| server-side command echo | none in source | none in source | + +The official host wrapper invokes an external `telnet` program, optionally +under `rlwrap`. Local echo, negotiation defaults, chunking and display behavior +therefore belong to that external client and are not defined by shsrv. The +wrapper has no bounded evidence, sanitization or timeout contract and is not a +Chimera client candidate. + +## Prompt and completion + +Both source families use the same control flow: + +1. `sh_greet` writes the automatic greeting; +2. `sh_prompt` writes `PWD + "$ "` without a newline and flushes; +3. `sh_readline` waits for LF; +4. `help` completes synchronously, while forked `stat`/`sum` are waited for; +5. all streams are flushed and the loop emits the next prompt. + +This proves source ordering but not an exact live boundary: + +- `setenv("PWD", "/", 0)` does not overwrite an inherited `PWD`; +- the prompt has no length or prefix field; +- arbitrary inherited `PWD` can itself contain the two-byte `$ ` shape; +- an external Telnet client's local echo is outside the server contract; +- stdout, stderr and negotiation writes can be chunked/interleaved; +- current `sh_on_telnet_data` and `sh_on_telnet_send` each use one `write` and + only log a short write; they do not finish the remaining bytes; +- the exact deployed source family and binary remain unproven. + +The allowlisted Phase-1.0W path alphabet excludes whitespace, `$`, pipe and +shell separators. The audited v0.19 static strings for `help`, `stat` and `sum` +contain no `$ ` except `sh_prompt`. This makes a terminal `$ ` a useful +source-shaped candidate under that exact source, but not a deployed completion +attestation. The offline model therefore never returns +`exact_completion_proven=true`. + +## Command framing evidence + +`sh_readline` consumes one byte at a time and ends a command at LF. Both source +families therefore accept plain LF termination. In the legacy family, an +external Telnet client's preceding CR is removed later by the argument-token +delimiter. Multiple already-buffered newline-terminated commands would be +processed sequentially by the source loop. + +No Chimera command-byte formatter is added here. A future passive client +contract can avoid Telnet negotiation entirely and use a bounded, prevalidated +LF-only batch. That claim remains design-only until separately implemented and +tested. + +## Robustness and side effects + +| Finding | Classification | +|---|---| +| automatic model/serial/firmware/temperature/frequency greeting | `HIGH_FUNCTIONAL_ACCEPTANCE_REQUIRED` | +| current single-write handling for pipe and socket | `PROTOCOL_INTEGRITY_RISK` | +| no server-side session deadline | `SESSION_HANG_RISK` | +| IAC IP/SUSP/ABORT may signal a running child | `HIGH_FUNCTIONAL_EXCLUDED_INPUT` | +| legacy Telnet controls pass into the shell byte stream | `PROTOCOL_INTEGRITY_RISK` | +| disconnect exits the shell process; outer server can accept again | `SOURCE_DESIGN_ONLY_RUNTIME_UNPROVEN` | +| no live filesystem write or autoload in framing model | `SAFE_OFFLINE` | + +IAC command generation is excluded from every future Chimera plan. The model +records synthetic IAC commands so tests cannot silently treat them as text. + +## Offline model + +`tools/phase10y_shsrv_framing_model.py` models both source families with fixed +65,536-byte and 256-chunk limits. It covers NVT EOL, IAC escaping, initial +unsupported-option replies, subnegotiation removal and prompt-candidate +classification. It has no network import, CLI, target, command sender or file +output. Its 22 tests use synthetic bytes only. + +## Decision + +- official framing families: `PROVEN_FROM_LOCAL_OFFICIAL_SOURCE`; +- deployed family and binary: `UNPROVEN`; +- source prompt order: `PROVEN_FROM_SOURCE`; +- terminal `$ ` recognition: `SOURCE_SHAPE_CANDIDATE_ONLY`; +- exact live command completion: `UNPROVEN`; +- passive no-negotiation approach: `OFFLINE_DESIGN_CANDIDATE`; +- live network adapter or collection: `NOT_AUTHORIZED`. + +The next permitted step is an offline-only passive, source-family-tolerant +batch contract. It must retain deadline sealing and partial-result rejection, +must never emit Telnet commands, and still may not add a real socket or device +address. diff --git a/docs/retroarch/phase-1.0z-offline-passive-batch-contract.md b/docs/retroarch/phase-1.0z-offline-passive-batch-contract.md new file mode 100644 index 0000000..6532db1 --- /dev/null +++ b/docs/retroarch/phase-1.0z-offline-passive-batch-contract.md @@ -0,0 +1,75 @@ +# Phase 1.0Z: offline passive batch contract + +Status: `PASSIVE_LF_BATCH_CONTRACT_COMPLETE_LIVE_TRANSPORT_BLOCKED` + +Date: 2026-07-22 + +Phase 1.0Z defines and tests a target-free host model only. It creates no +socket, address, sender CLI, target code or artifact. No PS5 action occurred. + +## Contract + +An already validated Phase-1.0W `SessionPlan` can produce exactly one immutable +outbound batch: + +| Window | Exact byte shape | Bound | +|---|---|---| +| `T2_GREETING_AND_HELP` | `help` followed by LF | 5 bytes | +| `T3_ONE_EXACT_PATH` | `stat PATH` LF, then `sum PATH` LF | 1,035 bytes | + +The path is the existing normalized absolute-path alphabet, ASCII-encoded and +at most 512 bytes. NUL, CR, IAC (`0xff`), whitespace inside the path and shell +metacharacters cannot enter the batch. The contract retains no target address. +It permits one outbound batch, no retry, no reconnect and no resume. + +Plain LF is accepted by both audited official source families. The legacy +v0.7-v0.8 server passes it through; v0.9-v0.19 `libtelnet` also passes plain LF +to `sh_readline`. The model does not select a deployed family. + +## Passive receive and completion + +The accumulator accepts only bytes already supplied by a synthetic caller. It +does not own a transport or clock. Any incoming IAC is rejected because this +passive contract emits no negotiation and both audited families send none +proactively. That fail-closed rule avoids silently adding Telnet replies or +signal semantics. + +Prompt text and remote EOF are never completion events. Only the explicit +`seal_at_hard_deadline(true)` model event can request sealing. This is a +synthetic boundary, not a working timer or proof that a future blocking receive +can be preempted. + +Sealing is successful only when: + +- T2 contains a complete help listing matching the known v0.7 or v0.19 source + family fingerprint; +- T3 contains `stat` metadata including size and a non-cryptographic BSD + rotate-16 `sum` for the one exact allowed path. + +Partial output, unknown help, missing stat, missing sum, invalid UTF-8, +unexpected firmware metadata, IAC and a missing deadline event all fail closed. +The sanitized result never proves an exact binary or device behavior. + +## Effects and limitations + +The offline model has no side effects. A future real T2/T3 session would still +need explicit acceptance of the automatic model/serial/firmware/temperature/ +frequency greeting, shell process and connection state. T3 reads one file; +that may cause low-level effects such as atime, cache, accounting and scheduler +activity. Those effects are not claimed absent. + +Unresolved blockers remain: + +- exact deployed shsrv identity and family; +- a real bounded adapter and preemptible hard deadline; +- single-write/short-write behavior in current shsrv; +- live chunking, disconnect, cleanup and restart behavior on firmware 9.60; +- physical erasure of sensitive bytes from host memory; +- directory durability for exclusive evidence inherited from Phase 1.0X. + +## Decision + +The offline passive batch contract is complete. Live transport remains blocked +and unauthorized. The next permitted step is an offline fake-adapter +integration that injects the exact batch and synthetic deadline without adding +a socket, address, device client or device action. diff --git a/docs/reviews/phase0-final-2026-07-17.md b/docs/reviews/phase0-final-2026-07-17.md new file mode 100644 index 0000000..3d64292 --- /dev/null +++ b/docs/reviews/phase0-final-2026-07-17.md @@ -0,0 +1,51 @@ +# Independent Phase-0 final review pass + +- Review date: 2026-07-17 +- Reviewed implementation: `72292f2d5788ad643e9d1816ce28ab754ef739b5` +- Review type: separate post-implementation source, package, and artifact pass +- Result: **Phase 0 accepted offline; Phase 1 hardware execution blocked** + +This was an independent review pass in the workflow sense: acceptance criteria +were frozen, the full diff and generated artifacts were inspected after the +implementation pass, and findings were required to receive tests before the +review could close. It is not represented as an external human audit. + +## Areas reviewed + +- public C ABI sizing/versioning and opaque ownership; +- context/surface/texture cleanup, limits, overflow checks, and mock state; +- separate PS5 refusal backend and symbol-resolution boundary; +- firmware gates, acknowledgements, and pre-initialization control flow; +- RetroArch/SDL2 capability claims and installed CMake targets; +- manifest schema, SBOM, dependency pins, and license notices; +- source-level prohibited-call audit and ELF import inventory; +- CI/Docker/build graphs for upload, run, boot, or credential behavior; +- host, sanitizer, install-consumer, secret, and cross-build results. + +## Findings resolved + +| ID | Severity | Finding | Resolution | +|---|---|---|---| +| R-001 | high | Two empty firmware identifiers compared equal and passed the gate. | Empty embedded or requested identifiers now fail; regression tests cover empty, `NONE`, mismatch, null, and exact match. | +| R-002 | medium | Adapter headers were installed without their scaffold libraries in the exported CMake package. | Both static scaffold targets are installed/exported; an installed adapter consumer compiles and exits 0. | +| R-003 | low | Unified-diff context prefixes triggered false trailing-whitespace reports. | `.patch` whitespace checking is disabled through `.gitattributes`; ordinary sources remain checked. | + +No unresolved Phase-0 code, documentation, licensing, or packaging finding +remains. + +## Accepted limitations and blockers + +- No PS5 firmware is identified, allowlisted, or observed. +- GNM signatures, layouts, ownership, synchronization, and shader contracts + remain unknown; no GNM function is called. +- Module-loader internal side effects remain unknown. +- The pinned SDL VideoOut backend waits in `sceKernelWaitEqueue` without a + proven bounded timeout in this path. +- Safe cleanup after forced process termination is unproven. +- The local Docker engine is unavailable; the equivalent direct commands pass, + but no container-build success is claimed. +- No ELF was transferred to or executed on a PS5. + +The first meaningful next step therefore requires new authority and exact +firmware information: build a firmware-specific, hash-bound, non-rendering +capability probe. Even that build is not permission to transfer or execute it. diff --git a/docs/runtime/controlled-runtime-policy.md b/docs/runtime/controlled-runtime-policy.md new file mode 100644 index 0000000..5fd0fb3 --- /dev/null +++ b/docs/runtime/controlled-runtime-policy.md @@ -0,0 +1,87 @@ +# Controlled PS5 runtime profile + +`manifests/controlled-runtime-profile.schema.json` defines the mandatory +machine boundary for hardened-runtime deployment preparation. Missing metadata +is a denial, not an invitation to infer a value. + +## Required invariants + +- exact firmware `9.60`, supported by Jens's explicit confirmation; +- exact hardened Payload Manager, hardened elfldr, SDK, and artifact source + commits, sizes, and SHA-256 values; +- byte-identical double clean builds for every ELF; +- lifecycle artifact filename, byte size, SHA-256, and clean source commit + equal in artifact manifest, runtime profile, audit, and local bytes; +- persistent writes limited to removable controlled application artifacts; +- filesystem writes limited to the controlled artifact directory; +- payload network access `none`; +- maximum payload runtime 2000 ms; +- automatic retry disabled; +- no effect classified `PERSISTENT_WRITE` or `UNBOUNDED_OR_UNKNOWN`; +- no hard blocker and decision + `READY_FOR_HARDENED_RUNTIME_DEPLOYMENT`; +- explicit `execution_eligible=true`; +- profile name exactly `controlled-ps5-runtime`; +- `expected_volatile_effects` exactly equal to the IDs classified + `EXPECTED_VOLATILE_RUNTIME_EFFECT`; +- explicit top-level `execution_authorized=false`; +- deployment fields remain `installed=false`, + `ready_for_installation=true`, and `rollback_prepared=true`. + +The static pass means the exact bytes are eligible for a later, separate +installation approval. It does not authorize a PS5 connection, transfer, +installation, or execution. + +`rollback_prepared=true` means only that the offline review and rollback +packages exist. It does not claim that byte-exact stock backups are already +present on the console. Phase 0.8 keeps installation hard-blocked until those +current on-device rollback preconditions are proven through an admissible +collector. + +## Current exact profile + +`manifests/runtime/controlled-ps5-runtime-profile.json` binds: + +- lifecycle probe + `bfb4a5cc768e162fe4c2fddf41c3978e152722918a39085277fd172cb95a7182`, + 112680 bytes, source + `fe08300339a13f899fb78ea404ada381a5cba87c`; +- hardened elfldr + `63e810982471eb40cae3a20aa9df9a0a02892f420e429874fae4e99aa400b561`, + 397000 bytes, source + `197623058f509eddde18868dafcb92fdcac66464`; +- controlled Payload Manager + `8fecf8241a46246eddbd21e8bb4d875f5d76f1f4f4c6a11384df1f131aa5e5b1`, + 99560 bytes, source + `e23d94ff91233aa770e2342800c1467875bdef44`; +- SDK v0.41 commit + `d2e2e585740362976a39fdd5ccf390f199a7bc37`. + +The profile records `installed=false`, `execution_authorized=false`, +`transferred=false`, and `executed=false`. Firmware was not queried from the +device because this phase was offline; the evidence string records Jens's +exact 9.60 confirmation. + +## Static gate + +`tools/check_artifact_execution_policy.py` verifies the profile, artifact +manifest, local lifecycle bytes, firmware argument, and permanent denylist. It +returns `PASS_STATIC_DEPLOYMENT_ELIGIBILITY_GATE` only for the exact reviewed +set and still emits `execution_authorized=false`. + +The gate rejects a blocked decision, altered bytes, wrong firmware, an +uninstalled-identity claim, a widened budget, a hard effect, missing hardened +runtime identity, any authorization/transfer/execution claim, or a permanent +denylist match. + +## Historical boundary + +Phase 0.5's freestanding startup and Phase 0.6's unmodified public loader chain +remain blocked historical records. Phase 0.7 does not weaken or overwrite +those findings; it builds new hardened binaries that close the Phase-0.6 +implementation blockers. + +Phase 0.8 is a separate operational preflight gate. Its current decision is +`READ_ONLY_PREFLIGHT_BLOCKED`; it neither revokes the exact Phase-0.7 static +build evidence nor promotes it into connection, installation, transfer, or +execution authority. diff --git a/docs/runtime/kernelwrite-proof-matrix.md b/docs/runtime/kernelwrite-proof-matrix.md new file mode 100644 index 0000000..29db781 --- /dev/null +++ b/docs/runtime/kernelwrite-proof-matrix.md @@ -0,0 +1,93 @@ +# Kernelwrite proof matrix + +Statuses mean: `SAFE` is proven only for the named scope, `UNSAFE` has a proven +policy violation, and `UNPROVEN` lacks necessary primary evidence. Absence of +an import is never used as the sole proof because SDK code is statically +combined in `crt1.o`. + +| Component or behavior | Source | Linked in stock `crt1.o` | Reachable from stock `_start` | Status | Basis | +|---|---|---:|---:|---|---| +| `payload_args_t` order/offsets | public header + disassembly | yes | yes | `SAFE` | six x86-64 fields; `payloadout` at `0x28` | +| stock SDK startup | `crt/crt.c` | yes | yes | `UNSAFE` | invokes patch and rtld paths | +| `__patch_init` | `crt/patch.c` | yes | yes | `UNSAFE` | relocation and callgraph edge from `_start` | +| `kernel_copyin/out` | `crt/kernel.c` | yes | yes | `UNSAFE` | reachable read/write transport | +| `kernel_set_ucred_caps` | `crt/kernel.c` | yes | yes | `UNSAFE` | reaches `kernel_copyin` | +| `kernel_set_ucred_attrs` | `crt/kernel.c` | yes | yes | `UNSAFE` | reaches `kernel_copyin` | +| syscall address lower/upper bounds | `crt/patch.c` | yes | yes | `UNSAFE` | writes offsets `0xf0` and `0xf8` | +| JIT capability change | `crt/patch.c` | yes | yes | `UNSAFE` | `caps[15] |= 0x40` before credential write | +| ptrace attribute change | `crt/patch.c` | yes | yes | `UNSAFE` | `attrs[3] |= 0x80` before attribute write | +| runtime symbol discovery | `syscall.c`, `crt.c`, rtld | yes | yes | `UNSAFE` for minimal profile | multiple reachable dynamic resolutions | +| `__dlopen`/`__dlsym` facade | `rtld_dlfcn.c` | yes | no static startup edge | `UNSAFE` linked capability | absence from imports is not absence from bytes | +| Sce module load/start | `rtld_sprx.c` | yes | yes | `UNSAFE` | `__rtld_sprx_init` reaches function-pointer call | +| Sce module stop/unload | `rtld_sprx.c` | yes | cleanup-dependent | `UNPROVEN` cleanup | no guarantee after crash or partial init | +| SO mapping/`mprotect` | `rtld_so.c` | yes | path-dependent | `UNSAFE` linked capability | forbidden for minimal profile | +| socket-overlap helper | `kernel.c` | yes | no static startup edge | `UNSAFE` linked capability | still present in monolithic startup object | +| new network initialization | driver adds `libSceNet` | dependency present in stock links | module-init internals unknown | `UNPROVEN` | no caller/module initialization contract | +| GNM calls | no Phase-0.5 candidate source | no candidate | no candidate | `SAFE` source scope only | no custom source or artifact created | +| VideoOut calls | no Phase-0.5 candidate source | no candidate | no candidate | `SAFE` source scope only | no custom source or artifact created | +| SDL calls | no Phase-0.5 candidate source | no candidate | no candidate | `SAFE` source scope only | no custom source or artifact created | +| direct MMIO/register writes | audited project source | no candidate | no candidate | `SAFE` source scope only | none added | +| `-nostartfiles -nodefaultlibs` suppression | compiler `-###` | not linked in trace | n/a | `SAFE` driver fact | no CRT or default library added | +| loader stack/return contract | exact caller absent | n/a | n/a | `UNPROVEN` | callee cannot prove caller | +| pre-entry loader changes | exact caller absent | n/a | before entry | `UNPROVEN` | incoming kernel access exists; origin unknown | + +The full source hashes, stock-object hashes, relocation-table hash, +disassembly hash, all reachable edges, and linked-but-unreachable prohibited +functions are in `manifests/runtime/phase-0.5-startup-audit.json`. + +## Phase 0.6 controlled-runtime classification + +The table above preserves the stricter Phase-0.5 "kernelwrite-free" question. +Phase 0.6 uses the user's corrected policy: a documented temporary runtime +write is not automatically unsafe. Only `PERSISTENT_WRITE` and +`UNBOUNDED_OR_UNKNOWN` are hard blockers. + +| Component/effect | Classification | Review status | Basis | +|---|---|---|---| +| first-stage QA flag enable | `EXPECTED_VOLATILE_RUNTIME_EFFECT` | `SAFE` only as exact-source classification | loader bootstrap, before payload | +| child creation with `rfork_thread`/`execve` | `PAYLOAD_PROCESS_LOCAL` | `SAFE` source fact | separate SceSpZeroConf child | +| ptrace service authid, normal path | `RESTORED_BY_LOADER` | `SAFE` success path | authid backed up and restored | +| ptrace authid restore failure | `UNBOUNDED_OR_UNKNOWN` | `UNSAFE` for controlled profile | returns without a second restore | +| `pt_call`/`pt_syscall` completion | `UNBOUNDED_OR_UNKNOWN` | `UNSAFE` | no step or time bound | +| breakpoint byte | `RESTORED_BY_LOADER` | `SAFE` source fact | original byte copied back | +| breakpoint page RWX permission | `PAYLOAD_PROCESS_LOCAL` | `UNPROVEN` cleanup | permission not restored; relies on child exit | +| child root/jail/caps/authid | `RESTORED_BY_LOADER` on success | `UNPROVEN` partial | UID is not restored | +| payload mapping, argument page, sockets, pipe | `UNBOUNDED_OR_UNKNOWN` | `UNSAFE` for controlled profile | no success-path cleanup before detach | +| SDK `__patch_init` | `PAYLOAD_PROCESS_LOCAL` | `SAFE` classification, not execution proof | affects payload child; not an automatic blocker | +| SDK terminate branch | `UNBOUNDED_OR_UNKNOWN` | `UNSAFE` for controlled profile | return/exit/trap branch unresolved | +| detached payload runtime | `UNBOUNDED_OR_UNKNOWN` | `UNSAFE` | no two-second watchdog | +| child reaping | `UNBOUNDED_OR_UNKNOWN` | `UNSAFE` | SIGCHLD ignored; semantics undocumented | +| Manager load hash binding | `UNBOUNDED_OR_UNKNOWN` | `UNSAFE` | path streamed without SHA-256 check | +| Manager upload path | `PERSISTENT_WRITE` | `UNSAFE` | writes and commits under `/data/pldmgr/payloads` | + +No source or binary evidence showed a GNM, VideoOut, SDL, direct MMIO, flash, +firmware, savegame, database, or system-configuration write in the proposed +payload path. Because no payload was built, that is a source-scope absence, +not an execution claim. + +## Phase 0.7 hardened-runtime classification + +The machine-readable counterpart is +`manifests/runtime/phase-0.7-kernelwrite-proof-matrix.json`. + +| Component/effect | Phase-0.7 status | Classification | Evidence boundary | +|---|---|---|---| +| freestanding kernelwrite-free startup | `UNPROVEN` | historical blocked target | exact safe return contract still absent | +| stock SDK `__patch_init` | `UNSAFE` for a kernelwrite-free claim | `PAYLOAD_PROCESS_LOCAL` accepted effect | source, linker map, disassembly, reachable callgraph | +| stock SDK rtld module loading | `UNPROVEN` runtime branch; bounded externally | `PAYLOAD_PROCESS_LOCAL` accepted effect | linked module start/stop symbols, SDK source, child watchdog | +| ptrace step completion | `SAFE` offline | `BOUNDED_WATCHDOG` | source, unit/fuzz tests, disassembly | +| five-field credential restoration | `SAFE` offline | `RESTORED_BY_LOADER` | source, failure injection tests, callgraph | +| restoration failure | `SAFE` fail-closed logic | `FAIL_CLOSED_TERMINATION` | child killed/reaped; service disables ptrace | +| breakpoint byte/page | `SAFE` offline | `RESTORED_BY_LOADER` | source and disassembly | +| payload child lifetime | `SAFE` offline | `BOUNDED_WATCHDOG` | 2000 ms, SIGTERM, SIGKILL, reap | +| mapping/descriptor cleanup | `SAFE` offline | `OS_RECLAIMED_ON_EXIT` plus tracked cleanup | source, tests, callgraph | +| manager hash-to-stream binding | `SAFE` offline | `HASH_BOUND_SAME_FD` | source, TOCTOU and negative tests | +| receiver digest/denylist enforcement | `SAFE` offline | fail-closed | source, tests, disassembly | +| controlled artifact-directory write | `SAFE` for reviewed deployment budget | `ALLOWED_APPLICATION_WRITE` | exclusive temp, fsync, verify, atomic rename | +| GNM/VideoOut/SDL/MMIO/firmware mutation | `SAFE` absence claim | prohibited | source scan, imports, map, disassembly | +| firmware-9.60 runtime behavior | `UNPROVEN` | no execution evidence | no PS5 action occurred | +| installation, transfer, execution | `UNPROVEN` and unauthorized | action state false | manifests and offline audit | + +The overall decision is +`READY_FOR_HARDENED_RUNTIME_DEPLOYMENT`, not `PROVEN KERNELWRITE-FREE`. +The known normal-CRT process-local write prevents the latter claim. diff --git a/docs/runtime/loader-contract.md b/docs/runtime/loader-contract.md new file mode 100644 index 0000000..3a4350a --- /dev/null +++ b/docs/runtime/loader-contract.md @@ -0,0 +1,220 @@ +# PS5 payload loader contract audit + +Audit date: 2026-07-17. Scope: pinned public PS5 Payload SDK v0.41 at +`d2e2e585740362976a39fdd5ccf390f199a7bc37`, inspected offline. No PS5 was +contacted and no ELF was built, transferred, or executed. + +## Available primary evidence + +The SDK README names three external loader families: `ps5-payload-dev/elfldr`, +`cryonumb/elfloader` through `ps5-jar-loader`, and +`shahrilnet/remote_lua_loader`. None of those caller repositories is present in +the pinned local evidence set. A `git grep` of every other local primary-source +checkout found no `payload_args_t` or `payloadout` caller implementation. The +only available contract is therefore the SDK callee side: + +- `include/ps5/payload.h` defines the six-field `payload_args_t`; +- `crt/crt.c` defines `int _start(payload_args_t *args)` and labels it as the + entry invoked by the ELF loader; +- `crt1.o` disassembly shows the first argument consumed from `RDI` and + `payloadout` read at offset `0x28`; +- the SDK linker script defines image, BSS, constructor, destructor, and + dynamic-section boundaries. + +These facts prove the callee layout. They do not prove how a particular loader +constructs the argument, stack, return address, or post-return state. + +## Argument layout + +For the audited x86-64 target the public fields are six eight-byte values in +this order: + +| Offset | Field | Callee use | +|---:|---|---| +| `0x00` | `sys_dynlib_dlsym` | SDK syscall/symbol bootstrap | +| `0x08` | `rwpipe` | existing kernel read/write transport descriptors | +| `0x10` | `rwpair` | existing kernel read/write transport descriptors | +| `0x18` | `kpipe_addr` | existing kernel pipe address | +| `0x20` | `kdata_base_addr` | existing kernel data-base address | +| `0x28` | `payloadout` | result location written by the SDK CRT | + +The 48-byte layout is `SAFE` as a source and disassembly fact. The lifetime, +writability, ownership, nullability, and post-return consumption of every +pointer are `UNPROVEN` because the caller is absent. + +## Entry ABI and stack + +The compiled SDK `_start` has a normal x86-64 function prologue, consumes the +argument from `RDI`, makes further calls without dynamically realigning `RSP`, +and ends one path with `ret`. This establishes what the callee expects, not what +the loader guarantees. In particular, the following remain `UNPROVEN`: + +- the exact stack alignment at entry; +- whether a valid return address is present; +- which registers or floating-point state the loader expects preserved; +- whether the loader calls the entry as an ordinary function or transfers + control by another mechanism; +- whether the memory behind `payload_args_t` remains valid until return. + +## BSS and image initialization + +The SDK linker script provides `__bss_start` and `__bss_end`. The stock `_start` +explicitly zeros that range byte-by-byte before accessing its own BSS state. +The loop itself is freestanding and has no import, so a comparable loop is +technically possible. It is not evidence that the loader maps every segment +with the permissions, alignment, relocation state, or zero-fill behavior a new +artifact would require. Those caller/mapper properties are `UNPROVEN`. + +## Return, exit, crash, and cleanup + +The stock CRT does not define one universal return rule. Its +`payload_terminate()` first tries to detect a hijacked process with +`kernel_dynlib_dlsym(-1, 0x2001, "sceKernelDlsym")`. In that branch it returns +from `_start`. Otherwise it resolves and invokes `exit`; if that fails it +executes a trap. This heuristic itself depends on the syscall/kernel/rtld stack +that Phase 0.5 forbids. + +Consequently, a custom `_start` that simply executes `ret` cannot be claimed +safe from the SDK callee alone. No local primary evidence proves: + +- who consumes the integer return value; +- whether return resumes a hijacked process or exits a dedicated process; +- which mappings, descriptors, credentials, or signal state are restored; +- what happens after an invalid argument, fault, trap, or partial BSS clear; +- whether any cleanup exists after a crash. + +All five are `UNPROVEN` and are build blockers. + +## Loader state versus artifact-caused state + +The argument fields show that kernel read/write descriptors and kernel +addresses already exist when the SDK entry is called. That is a fact about the +incoming state, not proof of how the loader created it. The exact loader may +have changed credentials, syscall bounds, mappings, modules, or other process +state before entry; the available callee source cannot distinguish those +possibilities. + +The stock SDK artifact then independently causes additional writes through +`__patch_init`; those writes are attributable to `crt1.o` and are `UNSAFE`. +No Phase-0.5 candidate artifact exists, so it caused no new state changes. + +## Contract decision + +The public callee contract is insufficient for a safe-return artifact. +Decision: **BLOCKED**. The minimal missing evidence is the exact loader used for +firmware 9.60 at a pinned public commit, including its entry call site, +argument ownership, return path, pre-entry changes, cleanup, and crash path. + +Machine evidence: `manifests/runtime/phase-0.5-startup-audit.json`. + +## Phase 0.6 exact-loader addendum + +The Phase-0.5 conclusion above is preserved as the evidence available at that +time. Phase 0.6 identified the installed loader exactly through the read-only +Payload Manager inventory and a byte-for-byte public release match: + +- Payload Manager v0.3.1, commit + `cfbc70f30f419b09bf2b52283f7409e2d3117ee1`, SHA-256 + `518740adbacccb9094fadb07dd424c53ee290f38306449ccc9d6957fdf813c0b`; +- `ps5-payload-dev/elfldr` v0.23, commit + `699e8bcff03e91e8d6ca6eba281af25c5a58d8c2`, SHA-256 + `092d16ee0ede0c494947efd38d1a17bbd7cc4b022d3858ea898833c188c703e8`. + +The proposed `itsPLK/ps5-elfldr` v0.23.2 candidate is not installed: its +release hash is +`2c28f847c859d3126b0f089ee23b72b84a733548da222ceafd91ae2f3df8f933`. +The exact exploit/autoloader that originally starts the first-stage loader +remains unidentified. + +### Proven entry setup + +The exact v0.23 source creates a new process using +`rfork_thread(RFPROC | RFCFDG | RFMEM)`. The child enables tracing and executes +`/system/vsh/app/NPXS40112/eboot.bin` (`SceSpZeroConf`). The parent stops at an +INT3 placed 58 bytes after the mapped eboot entry, restores that byte, maps the +payload, and then sets: + +| Register/state | Exact loader operation | Status | +|---|---|---| +| `RSP` | writes observed `RIP` at `RSP - 8`, then subtracts 8 | `SAFE` source fact | +| `RIP` | payload ELF entry | `SAFE` source fact | +| `RDI` | loader-created `payload_args_t` page | `SAFE` source fact | +| breakpoint byte | original byte restored before injection | `RESTORED_BY_LOADER` | +| breakpoint page permission | changed to RWX and not restored | `PAYLOAD_PROCESS_LOCAL` | + +This is a synthetic function-call frame. The public source does not establish +the meaning of the observed trap `RIP`, the exact original instruction +boundary, or the validity of resuming there after payload return. Therefore +the continuation is still `UNPROVEN`. + +### Argument ownership and lifetime + +`elfldr_payload_args()` maps one page into the child, creates two IPv6 sockets +and a pipe, overlaps the sockets through the SDK kernel helper, and populates +the six public fields. On successful detach the loader does not unmap that +page or close those child descriptors. They are process-local, but cleanup +depends on the child terminating. That termination and reaping path is not +bounded or proven. + +### Return, exit, crash, and hang + +The stock SDK v0.41 `_start` may return, resolve and call `exit`, or trap, +depending on `payload_terminate()`. Which branch applies to this injected +SceSpZeroConf child is not proven. Separately, elfldr: + +- has no payload wall-clock deadline after `PT_DETACH`; +- has unbounded single-step loops in both `pt_call()` and `pt_syscall()`; +- ignores `SIGCHLD` in the service and documents no post-detach reaping + contract; +- kills the child on some preparation errors, but does not supervise normal + runtime, return, crash, or hang. + +These are `UNBOUNDED_OR_UNKNOWN`, not objections to the mere presence of +temporary kernel/process writes. They block the requested controlled +two-second lifecycle. + +### Existing loader state versus artifact-caused state + +The first-stage loader enables QA flag bits and temporarily changes +credentials before the candidate payload exists. Those are existing-loader +effects. For each ptrace operation the service temporarily changes its authid +and normally restores it. The child receives process-local credentials, +mappings, sockets, pipes, executable permissions, and SDK `__patch_init` +changes. The artifact would cause SDK startup only after entry; it would not +cause the loader bootstrap. + +The separation does not make the chain ready: UID restoration is absent, +authid restoration has an unrecovered failure path, and child lifetime is +unbounded. Full evidence is in +`manifests/runtime/phase-0.6-loader-runtime-audit.json`. + +## Phase 0.7 hardened loader contract + +Phase 0.7 does not depend on safely returning from `main`. The lifecycle +probe calls `_exit` after exactly one notification, so the unresolved SDK +`payload_terminate` return/exit/trap branch is outside the reachable +application path. + +The hardened elfldr preserves the public `payload_args_t *` entry convention +and adds a controlled versioned transport header with exact artifact ID, +firmware 9.60, size, SHA-256, 2000 ms timeout, and no-retry fields. Both sender +and receiver validate the metadata; the receiver hashes the actual received +bytes and rejects the permanent denylist. + +The loader's process mutations are exact and bounded: + +- the initial QA flag change is an existing volatile bootstrap effect; +- ptrace service auth-ID and all child UID/jail/root/caps/auth fields are + backed up, restored, and checked; +- breakpoint byte and page protection are restored; +- single-step loops have independent time and step limits; +- every incomplete launch path terminates and reaps the child; +- the detached success path is replaced with a two-second supervised wait, + bounded termination escalation, and reap; +- an auth restoration failure permanently disables further ptrace work and + stops the service. + +This is sufficient for the offline +`READY_FOR_HARDENED_RUNTIME_DEPLOYMENT` decision. Loader behavior on firmware +9.60 remains `UNPROVEN` until separately authorized installation and execution +produce on-device evidence. diff --git a/docs/runtime/minimal-crt-feasibility.md b/docs/runtime/minimal-crt-feasibility.md new file mode 100644 index 0000000..4e9af62 --- /dev/null +++ b/docs/runtime/minimal-crt-feasibility.md @@ -0,0 +1,131 @@ +# Minimal kernelwrite-free CRT feasibility + +## Decision: BLOCKED + +A compiler-driver trace proves that SDK v0.41 can be asked to omit its stock +CRT and default libraries. A freestanding BSS clear and ordinary x86-64 return +instruction are mechanically expressible. Those facts do not prove that the +actual PS5 loader safely calls or accepts return from such an entrypoint. + +Because safe return was not proven, no custom `_start` source was added and no +PS5 ELF was built. This is the required fail-closed outcome, not an incomplete +artifact audit. + +## Necessary versus convenience or patching + +| Element | Minimal need | Classification | Evidence | +|---|---:|---|---| +| loader-recognized entry symbol | yes | `UNPROVEN` contract | SDK provides callee only | +| correct incoming stack/register ABI | yes | `UNPROVEN` caller | stock disassembly shows expectation only | +| mapped load segments and relocations | yes | `UNPROVEN` loader | linker script is not loader source | +| deterministic BSS state | yes when BSS exists | `SAFE` implementation primitive | linker symbols plus byte-zero loop | +| valid completion/return path | yes | `UNPROVEN` blocker | exact caller absent | +| `payloadout` write | unknown | `UNPROVEN` ownership | field exists; caller semantics absent | +| `__crt_syscall_init` | no for a no-call entry | convenience | stock SDK runtime only | +| `__kernel_init` | no | kernel transport convenience | consumes pre-existing kernel access | +| `__klog_init` | no | logging convenience | forbidden for minimal profile | +| `__patch_init` | no | prohibited patch | proven kernel writes | +| `__rtld_init` | no | dynamic-loader convenience | module loading and allocation | +| libc/stdio | no | convenience | omitted by `-nodefaultlibs` trace | +| constructors/destructors/TLS | no | convenience for this audit object | no artifact built | + +## Exact blockers + +The smallest evidence set needed before reconsidering a build is: + +1. identification of the exact loader used with firmware 9.60 and a pinned, + public source commit; +2. the caller instructions establishing `RDI`, `RSP` alignment, the return + address, and any preserved state; +3. allocation and ownership of `payload_args_t`, including `payloadout` + lifetime and writability; +4. the complete caller path after `_start` returns, including restoration and + cleanup; +5. crash/fault behavior before and during entry; +6. all process, credential, syscall, module, and memory changes performed by + the loader before control reaches the artifact. + +An SDK header, a function signature, or a successful link is not a substitute +for these caller facts. + +## Verification disposition + +| Requested check | Result | +|---|---| +| SDK source, Makefile, compiler and linker audit | passed offline | +| all stock startup objects/archives enumerated | passed offline | +| stock CRT disassembly and reachable callgraph | passed; stock CRT is `UNSAFE` | +| freestanding compiler-driver suppression | passed as no-output `-###` trace | +| custom source compile with warnings as errors | not performed; build blocked | +| custom linker map and full object/archive trace | not performed; build blocked | +| custom undefined symbols and `DT_NEEDED` | not performed; build blocked | +| custom relocations, arrays and TLS | not performed; build blocked | +| custom prohibited-code scan | not performed; no custom bytes exist | +| reproducible double clean cross-build and SHA-256 | not performed; no artifact exists | +| host tests and sanitizer tests | required by `TEST_PLAN.md` and run separately | +| negative build configuration | implemented; enabling the target must fail | + +The non-build decision record is +`manifests/runtime/minimal-startup-artifact-decision.json`; it has +`execution_eligible=false` and deliberately has null filename, size, and hash. + +## Safe alternatives + +1. Continue `libchimera-gfx` through its deterministic mock and a separately + designed software backend. This advances API lifecycle, format conversion, + scaling, adapter contracts, and cleanup without PS5 startup or GPU risk. +2. Maintain a separate Linux-on-PS5 backend as a long-term track. Linux + `amdgpu`/RADV work can inform a Linux userspace backend, but it does not + establish a native PS5 userland ABI and must remain architecturally separate. + +Neither alternative authorizes transfer or execution on the PS5. + +## Phase 0.6 disposition + +Phase 0.6 found the exact installed elfldr caller, so +`EXACT_LOADER_CALLER_SOURCE_ABSENT` is no longer the current blocker. The +Phase-0.5 non-build decision remains historically correct because that source +was not then available. + +The corrected Phase-0.6 safety model does not reject normal SDK startup merely +because `__patch_init` performs documented temporary process/kernel runtime +changes. A lifecycle probe would therefore use the normal pinned SDK v0.41 +startup rather than reopening the custom `-nostartfiles -nodefaultlibs` target. + +No lifecycle source or ELF was created because the surrounding exact chain +still cannot enforce the required profile: + +1. elfldr's ptrace single-step completion loops have no finite bound; +2. the detached child has no two-second loader watchdog; +3. SDK return/exit/trap selection for the injected child is unproven; +4. UID and some failure-path credential state are not restored; +5. child exit/reaping and mapping/descriptor cleanup are unproven; +6. Payload Manager launch is path-bound rather than SHA-256-bound; +7. its supported upload path performs persistent filesystem writes; +8. the exploit/autoloader identity and independent firmware attestation remain + unproven. + +Decision: **BLOCKED_VERSION_OR_UNBOUNDED_EFFECT**. Building a normal-CRT +lifecycle probe would create bytes that cannot satisfy the mandatory runtime +profile, so Phase D was correctly skipped. + +## Phase 0.7 disposition + +The Phase-0.5 freestanding target remains blocked and was not bypassed. +Phase 0.7 instead resolves the lifecycle termination uncertainty with an +ordinary SDK v0.41 payload whose `main` calls `_exit` directly. This preserves +the public loader entry ABI and avoids relying on an unproven return to the +loader. + +The resulting lifecycle ELF has exactly `_exit` and +`sceKernelSendNotificationRequest` as undefined imports and exactly +`libSceLibcInternal.sprx` and `libkernel_web.sprx` as `DT_NEEDED` modules. +There are no GNM, VideoOut, SDL, network, module-loader, thread, retry, or +autoload imports. + +This does not prove a kernelwrite-free startup. The normal CRT is statically +linked and its reachable startup includes `__patch_init`. Phase 0.7 accepts +that exact process-local effect under ADR-0010/ADR-0011 and hardens the +surrounding loader lifecycle. The freestanding question remains `BLOCKED`; +the normal-CRT lifecycle is +`READY_FOR_HARDENED_RUNTIME_DEPLOYMENT` offline only. diff --git a/docs/runtime/payload-manager-policy.md b/docs/runtime/payload-manager-policy.md new file mode 100644 index 0000000..494b91d --- /dev/null +++ b/docs/runtime/payload-manager-policy.md @@ -0,0 +1,72 @@ +# Chimera Payload Manager artifact policy contract + +The controlled Phase-0.7 Payload Manager is maintained in the separate private +`chimera-ps5-payload-manager` repository. This repository supplies the +hash-bound artifact and runtime profile that the manager must enforce. + +## Same-file-descriptor launch boundary + +The controlled manager: + +1. opens the selected artifact once with read-only, no-follow, close-on-exec + flags; +2. requires a regular file and exact compiled byte size; +3. hashes that same file descriptor; +4. rejects the permanent denylist and every metadata mismatch; +5. rewinds and streams that same descriptor; +6. checks descriptor identity and path identity after streaming; +7. sends an exact versioned header over loopback only; +8. uses a compiled exact artifact ID, SHA-256, size, firmware 9.60, 2000 ms + timeout, and `retry=false`. + +There is no path reopen between hash and stream. A symlink, truncation, +replacement, digest mismatch, denylist match, wrong route, wrong firmware, +non-loopback destination, or post-stream identity change fails closed. + +## Controlled upload boundary + +The upload route accepts only the controlled directory, creates an exclusive +no-follow temporary file, writes and synchronizes it, verifies exact content, +then atomically renames it. Upload never autoloads or executes the artifact. +The removable application write is explicit in the Phase-0.7 budget; it is not +a firmware write. + +## Receiver enforcement + +The hardened elfldr independently hashes the received bytes, validates the +exact artifact ID/size/firmware/digest/timeout/no-retry header, and enforces the +permanent denylist. Sender metadata alone is therefore insufficient. + +The legacy route is classified `UNVERIFIED_LEGACY` and is not a controlled +Chimera launch path. + +## Repository static gate + +Before preparing a later action, the exact lifecycle bytes must also pass: + +```text +python3 tools/check_artifact_execution_policy.py \ + --manifest \ + --denylist manifests/artifact-denylist.json \ + --artifact \ + --runtime-profile manifests/runtime/controlled-ps5-runtime-profile.json \ + --firmware 9.60 +``` + +Only `PASS_STATIC_DEPLOYMENT_ELIGIBILITY_GATE` is accepted. The result always +contains `execution_authorized=false`; it is preparation evidence, not +permission. + +## Permanent denial + +SHA-256 `4be1c17b4964f2b68c39b5145bc4af4619c32512d60269ecf5c39728b390fa63` +must always return `ARTIFACT_PERMANENTLY_DENYLISTED`, regardless of metadata, +runtime profile, or later generic approval. The entry is also copied into both +hardened consumers. + +## Current action state + +No hardened binary is installed. No lifecycle artifact has been transferred +or executed. A separate exact-hash installation approval is required first; +after successful installation verification, lifecycle transfer and one-time +execution require another artifact-specific approval. diff --git a/docs/runtime/phase-0.5-review.md b/docs/runtime/phase-0.5-review.md new file mode 100644 index 0000000..096e024 --- /dev/null +++ b/docs/runtime/phase-0.5-review.md @@ -0,0 +1,48 @@ +# Phase 0.5 independent offline review + +Date: 2026-07-17. Decision: **BLOCKED**. + +## Review result + +| Area | Status | Finding | +|---|---|---| +| SDK source pin and release | `SAFE` | local source is exact v0.41 commit | +| stock CRT object provenance | `SAFE` | all 12 sources and six empty archives enumerated and hashed | +| stock CRT execution | `UNSAFE` | reachable kernel credential and syscall-bound writes | +| stock rtld initialization | `UNSAFE` | reachable module load/start path and runtime allocation | +| freestanding flag suppression | `SAFE` | no-output driver trace removes CRT and default libraries | +| BSS byte-clear primitive | `SAFE` | implementable without imports using linker bounds | +| exact firmware-9.60 loader identity | `UNPROVEN` | not present in local pinned evidence | +| incoming stack/register contract | `UNPROVEN` | caller source absent | +| `payloadout` ownership/lifetime | `UNPROVEN` | only callee declaration exists | +| safe return and post-return cleanup | `UNPROVEN` | caller continuation absent | +| crash cleanup | `UNPROVEN` | loader fault path absent | +| pre-entry process modifications | `UNPROVEN` | kernel-access state is supplied but creation is absent | +| custom startup artifact | `SAFE` nonexistence | build gate stopped before source/link | +| permanent legacy artifact block | `SAFE` policy control | exact SHA-256 is denylisted and ineligible | +| Payload Manager integration surface | `SAFE` repository control | fail-closed JSON policy gate rejects false eligibility and denylisted hashes | +| external manager implementation | `UNPROVEN` outside repository | no Chimera Payload Manager source is in this project | + +## Side-effect conclusion + +The stock SDK startup is conclusively not kernelwrite-free. A hypothetical +freestanding entry could omit that startup, but safe loader return and cleanup +are not proven. It would be unsafe to turn a compiler/linker possibility into +an executable artifact without the caller evidence. + +No PS5 connection, transfer, execution, VideoOut open, GNM call, framebuffer +write, network initialization, module load, credential change, syscall-bound +change, or new PS5 ELF occurred during this review. + +## Artifact policy + +The legacy capability-probe SHA-256 +`4be1c17b4964f2b68c39b5145bc4af4619c32512d60269ecf5c39728b390fa63` +is permanently `BLOCKED`. `tools/check_artifact_execution_policy.py` returns a +nonzero decision for either `execution_eligible=false` or an exact denylist +match. A zero status means only that this static eligibility layer passed; its +JSON explicitly sets `execution_authorized=false`. + +There is no approval package for transfer or one-time execution because the +task explicitly forbids asking for execution permission and the feasibility +decision is blocked. diff --git a/docs/runtime/phase-0.6-loader-audit.md b/docs/runtime/phase-0.6-loader-audit.md new file mode 100644 index 0000000..4354058 --- /dev/null +++ b/docs/runtime/phase-0.6-loader-audit.md @@ -0,0 +1,122 @@ +# Phase 0.6 exact loader audit + +Historical record: superseded for current deployment preparation by the new +Phase-0.7 hardened binaries. This blocked decision remains authoritative for +the unmodified public-release chain. + +Audit date: 2026-07-17. Decision: +**BLOCKED_VERSION_OR_UNBOUNDED_EFFECT**. + +No ELF was built, transferred, or executed. The PS5 was not contacted through +raw port 9021. Read-only Payload Manager status endpoints were used only to +identify the installed public components. A strict existing read-only status +client on port 744 could not connect, so firmware 9.60 remains user-attested, +not device-attested. + +## Identity evidence + +| Component | Observed identity | Public source/release match | Result | +|---|---|---|---| +| firmware | exact `9.60`, stated by Jens | no independent device response | `UNPROVEN` attestation | +| Payload Manager | `/version` = `0.3.1`; inventory SHA-256 `518740ad...3c0b` | v0.3.1, commit `cfbc70f30...` | exact | +| elfldr | inventory v0.23; SHA-256 `092d16ee...03e8` | `ps5-payload-dev/elfldr` v0.23, commit `699e8bcff0...` | exact | +| itsPLK elfldr candidate | SHA-256 `2c28f847...8f933` | differs from installed hash | excluded | +| SDK | v0.41 | commit `d2e2e58574...` | exact | +| exploit/autoloader | local backup candidate only | no installed identity proof | `UNPROVEN` | + +The exact source and release pins are in `manifests/upstreams.lock.json`. The +machine audit normalizes source text to LF, hashes every reviewed file, hashes +the complete release-binary disassembly and readelf report, and asserts the +critical source tokens before generating its result. + +## Exact release binary + +The installed elfldr release asset is a stripped ELF64 PIE with entry `0x4700`. +Its complete import surface is: + +```text +DT_NEEDED: + libSceLibcInternal.sprx + libSceNet.sprx + libkernel_web.sprx + +Undefined dynamic symbols: + __error + close + execve + free + getpid + kevent + kill + kqueue + malloc + memcpy + memset + open + printf + puts + realloc + recv + rfork_thread + sceKernelSendNotificationRequest + strcmp + strerror + strlen + sysctl + vsnprintf + waitpid +``` + +It has 164 dynamic relocations, 140 of them `R_X86_64_RELATIVE`; zero-byte +preinit/init/fini arrays; and no TLS segment or TLS sections. The first load +segment is RWE. This import list does not prove absence of statically linked +kernel helpers, so the source and disassembly audits remain primary. + +## Loader lifecycle + +The loader uses a dedicated SceSpZeroConf child. It establishes a synthetic +call frame by placing the observed trap RIP at `RSP-8`, setting `RIP` to the +payload entry and `RDI` to a loader-created `payload_args_t`. It restores the +INT3 byte, jail/root/caps/authid on the child success path, and its own authid +after each ptrace syscall on the normal path. + +It does not prove or enforce: + +- the continuation reached when the payload returns; +- a finite step count in `pt_call()` or `pt_syscall()`; +- a two-second payload runtime; +- restoration after an authid-restore failure; +- child UID restoration; +- child mapping/socket/pipe cleanup after detach; +- child termination and reaping after normal return, crash, or hang. + +The installed service's existing QA flag and bootstrap credential effects are +separate from new artifact effects. They are still documented because the +full lifecycle depends on them. + +## Payload Manager lifecycle + +The exact `/loadpayload:` handler resolves a filename/path and streams that +file to elfldr on loopback port 9021. It does not calculate or compare the file +SHA-256 at launch. The `/manage:upload` path creates +`/data/pldmgr/payloads/.tmp` and commits the upload into storage. +Consequently the current manager cannot simultaneously provide exact-hash +launch binding and the required zero-filesystem-write transfer budget. + +No manager route was invoked to upload, load, kill, configure, retry, or +contact port 9021 during this audit. + +## Minimum evidence needed to reopen Phase D + +1. Exact exploit/autoloader identity and public-source lifecycle evidence. +2. Read-only, exact device attestation of firmware 9.60. +3. A bounded loader path whose ptrace and payload phases enforce a maximum of + 2000 ms without automatic retry. +4. Proven payload return/exit/crash continuation and child cleanup/reaping. +5. Proven restoration or bounded termination on every credential-change + failure path, including UID. +6. A Payload Manager path that verifies the exact SHA-256 immediately before + launch and performs no filesystem write. + +No workaround is authorized. The two safe development tracks remain the +mock/software backend and a separately scoped Linux-on-PS5 backend. diff --git a/docs/runtime/phase-0.6-review.md b/docs/runtime/phase-0.6-review.md new file mode 100644 index 0000000..40882f0 --- /dev/null +++ b/docs/runtime/phase-0.6-review.md @@ -0,0 +1,58 @@ +# Phase 0.6 independent review + +Historical record: superseded for current deployment preparation by the new +Phase-0.7 hardened binaries. This blocked decision remains authoritative for +the unmodified public-release chain. + +Date: 2026-07-17. Final decision: +**BLOCKED_VERSION_OR_UNBOUNDED_EFFECT**. + +`SAFE` below means proven only for the stated offline/source scope. `UNSAFE` +means incompatible with the mandatory controlled profile. `UNPROVEN` means +required primary evidence is absent. + +| Component | Status | Finding | +|---|---|---| +| private project isolation | `SAFE` | only `chimera-gfx` changed | +| firmware claim | `UNPROVEN` | exact 9.60 supplied by Jens; no device attestation | +| Payload Manager identity | `SAFE` | v0.3.1 asset hash matches commit/release | +| elfldr identity | `SAFE` | installed v0.23 asset hash matches exact public release | +| proposed itsPLK elfldr | `SAFE` exclusion | release hash differs; not audited as installed | +| exploit/autoloader identity | `UNPROVEN` | local backup is not installed-chain proof | +| separate payload process | `SAFE` source fact | rfork/exec creates SceSpZeroConf child | +| `RSP`/`RIP`/`RDI` setup | `SAFE` source fact | exact assignments identified | +| return continuation | `UNPROVEN` | trap RIP semantics and SDK terminate branch unresolved | +| ptrace completion | `UNSAFE` | single-step loops have no finite bound | +| maximum runtime | `UNSAFE` | detached child has no two-second watchdog | +| retry behavior | `SAFE` project policy | no project retry or execution path exists | +| normal authid restore | `SAFE` success path | loader backs up and restores | +| authid restore failure | `UNSAFE` | no recovery after failed restore | +| child credential restore | `UNPROVEN` partial | jail/root/caps/authid restored; UID not restored | +| breakpoint byte | `SAFE` | restored before payload detach | +| breakpoint permissions | `UNPROVEN` cleanup | page remains RWX until unproven child exit | +| payload args/resources | `UNPROVEN` cleanup | mappings, sockets and pipe rely on child exit | +| `__patch_init` | `SAFE` classification | known payload-process-local effect; not an automatic blocker | +| child exit/reaping | `UNPROVEN` | post-detach semantics not documented | +| manager launch hash | `UNSAFE` | path-bound stream; no launch-time SHA-256 verification | +| manager upload | `UNSAFE` | persistent `/data/pldmgr/payloads` write | +| GNM/VideoOut/SDL/MMIO | `SAFE` nonexistence | no lifecycle source or artifact was created | +| lifecycle artifact | `SAFE` nonexistence | Phase D stopped before source/build | +| legacy blocked artifact | `SAFE` policy | permanent denylist unchanged | +| transfer/execution | `SAFE` nonoccurrence | neither happened; no approval requested | + +## Artifact disposition + +There is no artifact path, artifact size, artifact SHA-256, or source commit +because no lifecycle probe was built. The machine profile contains null for +each of those fields and `execution_eligible=false`. No ELF is committed. + +The permanent blocked SHA-256 remains: + +```text +4be1c17b4964f2b68c39b5145bc4af4619c32512d60269ecf5c39728b390fa63 +``` + +This review is not an approval package. Reopening Phase D requires resolving +every minimum-evidence item in `phase-0.6-loader-audit.md`, followed by a new +offline audit and a new decision. It does not require or request execution +permission now. diff --git a/docs/runtime/phase-0.7-hardening.md b/docs/runtime/phase-0.7-hardening.md new file mode 100644 index 0000000..90c15c5 --- /dev/null +++ b/docs/runtime/phase-0.7-hardening.md @@ -0,0 +1,79 @@ +# Phase 0.7 hardened runtime — offline review + +Decision: **READY_FOR_HARDENED_RUNTIME_DEPLOYMENT**. + +This is an offline deployment-readiness decision. No PS5 connection, +installation, transfer, or execution occurred. + +## Exact identities + +| Component | Base | Hardened source | ELF SHA-256 | Size | +|---|---|---|---|---:| +| elfldr | `699e8bcff03e91e8d6ca6eba281af25c5a58d8c2` | `197623058f509eddde18868dafcb92fdcac66464` | `63e810982471eb40cae3a20aa9df9a0a02892f420e429874fae4e99aa400b561` | 397000 | +| controlled Payload Manager | `cfbc70f30f419b09bf2b52283f7409e2d3117ee1` | `e23d94ff91233aa770e2342800c1467875bdef44` | `8fecf8241a46246eddbd21e8bb4d875f5d76f1f4f4c6a11384df1f131aa5e5b1` | 99560 | +| lifecycle probe | Chimera GFX | `fe08300339a13f899fb78ea404ada381a5cba87c` | `bfb4a5cc768e162fe4c2fddf41c3978e152722918a39085277fd172cb95a7182` | 112680 | + +All three pairs of clean builds are byte-identical. Complete disassembly, +symbol tables, relocations, normalized linker maps, and call-edge inventories +are in the ignored offline package under `outputs/phase07/audit/`; their +hashes and the complete call-edge lists are committed in +`manifests/runtime/phase-0.7-offline-audit.json`. + +## Lifecycle binary boundary + +The only undefined imports are: + +- `_exit` +- `sceKernelSendNotificationRequest` + +The only `DT_NEEDED` modules are: + +- `libSceLibcInternal.sprx` +- `libkernel_web.sprx` + +There are no GNM, VideoOut, SDL, SceNet, socket, download, thread, retry, or +autoload imports. The normal SDK startup object is statically present and +reaches its documented `__patch_init` and rtld initialization before `main`; +absence from imports is not used to hide that fact. The linker map also +contains `sceKernelLoadStartModule`/`sceKernelStopUnloadModule` through the +stock SDK rtld; its conditional module behavior is payload-process-local and +supervised by the external watchdog, but remains unobserved on firmware 9.60. +After one notification, `main` calls `_exit`, so the uncertain SDK +`main -> payload_terminate` return branch is not taken. + +## Hardening closure + +| Phase-0.6 finding | Phase-0.7 implementation | Review | +|---|---|---| +| unbounded ptrace loop | monotonic one-second deadline plus 65,536 steps | `SAFE` offline logic | +| incomplete child credential restore | UID, jaildir, rootdir, caps, auth-ID all restored and checked | `SAFE` offline logic | +| restore failure continues | fail-closed kill and deadline-bounded reap; no detach | `SAFE` offline logic | +| scattered cleanup | one tracked cleanup state machine for breakpoint, mappings, fds, child | `SAFE` offline logic | +| breakpoint page stays RWX | original byte restored and page reset RX | `SAFE` source fact | +| detached runtime unbounded | 2000 ms wait, SIGTERM grace, SIGKILL, reap | `SAFE` offline logic | +| ignored SIGCHLD/zombie risk | default SIGCHLD plus explicit reap | `SAFE` offline logic | +| path-based Manager load | one no-follow FD, fstat, hash, rewind, stream, post-check | `SAFE` offline logic | +| upload TOCTOU | exclusive temp, fsync, verify, atomic rename, inode check | `SAFE` offline logic | +| arbitrary controlled artifact | manager binary compiled for exact hash, size, and ID | `SAFE` offline configuration | +| sender-only hash | elfldr rehashes received bytes and checks denylist | `SAFE` offline logic | + +`SAFE` here means source, test, map, and disassembly evidence agree. It is not +a hardware execution claim. + +## Remaining risks + +- No on-device observation exists yet; loader/firmware behavior remains + unverified at runtime. +- QA flags are an existing expected volatile loader bootstrap effect. +- Normal SDK startup performs known process-local credential/capability and + syscall-bound writes before `main`. +- The controlled manager listens for the later operator workflow, but accepts + only the compiled exact artifact identity; installation security and + network exposure still require a separate review. +- Abrupt platform failure can bypass userland reporting, though OS process + teardown should reclaim child mappings and descriptors. +- Rollback has only been prepared offline; its on-device paths and installed + hashes must be captured before any replacement. + +These are deployment/execution residual risks, not unresolved compiler, +linker, or ABI blockers for preparing the hardened runtime. diff --git a/docs/runtime/phase-0.8-read-only-preflight.md b/docs/runtime/phase-0.8-read-only-preflight.md new file mode 100644 index 0000000..3c88deb --- /dev/null +++ b/docs/runtime/phase-0.8-read-only-preflight.md @@ -0,0 +1,137 @@ +# Phase 0.8 read-only on-device preflight + +Audit date: 2026-07-18. Decision: +**READ_ONLY_PREFLIGHT_BLOCKED**. + +This is an offline admissibility audit. No on-device preflight session was +started, no connection to the PS5 was made, and no collector was run against +the PS5. No file, configuration, service, process, artifact, autoload setting, +or retry state on the console was touched. Local documentation and manifest +changes are outside that console-action statement. + +The attached preflight form requires both an exact permission record and a +collector whose reads demonstrably cannot change atime, audit logs, caches, +metadata, process state, or on-device logs. Neither condition can currently be +filled truthfully. The form's own fail-closed rule therefore requires +`STOP-RO`. + +## Authorization record + +| Field | Recorded value | +|---|---| +| explicit read-only preflight permission | absent; no exact text/reference was supplied | +| connection authorized | `false` | +| installation authorized | `false` | +| lifecycle authorized | `false` | +| execution authorized | `false` | +| automatic retry | `false` | + +The request to read and act on a blank checklist is not recorded as an exact, +artifact-independent authorization sentence. This finding alone prevents an +on-device session. It does not prevent this offline source audit. + +## Collector review + +### Stock Payload Manager HTTP + +The exact public v0.3.1 source is commit +`cfbc70f30f419b09bf2b52283f7409e2d3117ee1`. + +This candidate is **UNSAFE for the Phase-0.8 read-only contract**: + +- `src/http_server.c:131-152` calls `log_server_set_active()` for every + non-`OPTIONS` request before route dispatch; +- `src/log_server.c:20-23` implements that call by writing + `server_active_flag = 1`; +- `src/http_server.c:847-871` dispatches `/autoload_status` to + `pldmgr_autoload_get_status()` and opens + `/data/pldmgr/autoload.txt`; +- `src/autoload.c:44-48` writes `autoload_triggered = 1`; the worker at + `src/autoload.c:78-93` uses that flag to leave its frontend-wait state and + start the countdown; +- `src/http_server.c:911-930` also opens the autoload file for `/get_config`; +- non-noisy routes call `pldmgr_log()` at `src/http_server.c:479-481`; + `src/log_server.c:25-55` prints to stdout and mutates the in-process ring, + log version, mutex/condition state, and waiting clients. + +Even `/version`, which is filtered from the explicit request log, first calls +`log_server_set_active()`. Therefore an apparently informational endpoint is +not sufficient evidence of a no-side-effect collector. `/autoload_status` +must not be used at all under this contract. + +Pinned source-file SHA-256 values: + +| File | SHA-256 | +|---|---| +| `src/http_server.c` | `35cf5d8f0dd44cf64ceab5e4b0ecc09413c82d7e9946ba9de2ca4b1898631fdd` | +| `src/autoload.c` | `7051cab3ee1a3e0b9f6498000565eb9e160b9c63efa1771f250e98ec3aa4ae67` | +| `src/log_server.c` | `659095f43df1bbe8eb24acb165f027edc277af1e60aabb26ba9e3920b233d6f1` | + +### Direct filesystem or process collector + +No exact collector, version, source, transport, or operation set was supplied. +No evidence proves `noatime` behavior, suppression of audit records, absence +of caches, absence of network/service counters, or absence of on-device +logging. Classifying these effects as “probably harmless” would contradict the +required `Onbekend -> STOP-RO` rule. + +Historical Phase-0.6 endpoint observations are not reused as current +Phase-0.8 evidence. They lack current pre/post identity and were gathered +under a different, less strict observation boundary. + +## Gate state + +| Preflight section | Review | Reason | +|---|---|---| +| permission | `STOP-RO` | exact permission text/reference absent | +| collector | `STOP-RO` | side-effect freedom unproven | +| stock HTTP collector | `STOP-RO` | proven process-state mutations | +| two-source firmware 9.60 | `STOP-GATE` | one user attestation, no two current device sources | +| live elfldr path/object/hash | `STOP-GATE` | no current admissible observation | +| live Payload Manager path/object/hash | `STOP-GATE` | no current admissible observation | +| processes/services/listeners | `STOP-GATE` | current topology and ownership absent | +| autoload/startup/retry | `STOP-GATE` | authoritative sources not collected | +| separate backup destination | `STOP-GATE` | device, space, and reserve unproven | +| stock elfldr rollback backup | `STOP-GATE` | exact separate backup unproven | +| stock Payload Manager rollback backup | `HARD STOP-GATE` | exact separate on-device backup unproven | + +The expected stock identities remain reference values only: + +- elfldr: 397000 bytes, + `092d16ee0ede0c494947efd38d1a17bbd7cc4b022d3858ea898833c188c703e8`; +- Payload Manager: 2050320 bytes, + `518740adbacccb9094fadb07dd424c53ee290f38306449ccc9d6957fdf813c0b`. + +They are not claims about the console's current state. + +## Minimum evidence to reopen + +1. Exact permission text and a stable task reference authorizing only this + read-only preflight, while leaving installation, lifecycle execution, + transfer, artifact execution, and automatic retry unauthorized. +2. A pinned public collector and transport with a complete source and + environment proof excluding file, atime, metadata, audit-log, cache, + process-state, service-state, network-state, and on-device-log mutation. +3. Two authoritative current firmware sources that agree exactly on `9.60`. +4. A side-effect-free collection plan for current object identities, full + metadata, hashes, process/service ownership, ports 8084/8085/9021, and all + autoload/startup/retry sources, including stable pre/post observations. +5. Proof that separate, already-present, byte-exact rollback objects exist. + The stock Payload Manager backup is a hard gate. + +The stock Payload Manager HTTP interface cannot satisfy item 2 without a +different, separately reviewed implementation. No workaround is attempted. + +## Final state + +- dataset complete: `false`; +- open STOP-RO: `true`; +- open STOP-GATE: `true`; +- Payload Manager backup exact/current: `UNPROVEN`; +- installation approval: **NOT AUTHORIZED**; +- lifecycle approval: **NOT AUTHORIZED**; +- execution authorized: `false`; +- automatic retry: `false`. + +The complete machine-readable record is +`manifests/runtime/phase-0.8-read-only-preflight.json`. diff --git a/docs/runtime/phase-0.8-remediation.md b/docs/runtime/phase-0.8-remediation.md new file mode 100644 index 0000000..078d048 --- /dev/null +++ b/docs/runtime/phase-0.8-remediation.md @@ -0,0 +1,289 @@ +# Phase 0.8R offline remediation and evidence contract + +Decision: **READ_ONLY_PREFLIGHT_BLOCKED**. + +## Scope + +Phase 0.8R is documentation, evidence-contract design, and host-only +regression protection. It does not contact a PS5, observe hardware, implement +a collector, build a target artifact, transfer a file, install a component, or +execute code on a target. + +It grants no authority. `installation_authorized=false`, +`lifecycle_authorized=false`, `execution_authorized=false`, +`transfer_authorized=false`, and `automatic_retry=false`. + +The absolute read-only rule used by historical Phase 0.8 is unchanged. +Possible future bounded observation would be a separate phase with a new name, +effect budget, exact permission, and review. This document does not approve +such a phase. + +## Immutable historical identity + +The Phase-0.8 record is anchored to repository commit +`2c944d6d65a08f7e1c02f518721cde061b999329`. + +| Immutable file | Role | SHA-256 | +|---|---|---| +| `docs/runtime/phase-0.8-read-only-preflight.md` | human-readable historical record | `3fbe086175a6048176075f447ec1482074928e3b5282db97ea2169395fe1d508` | +| `manifests/runtime/phase-0.8-read-only-preflight.json` | machine-readable historical record | `47d7f452f8799979fe99b3e6d56859f03544112725bf7e5b349eba5ed81b3322` | +| `tests/test_phase08_preflight.py` | original fail-closed regression | `8a4ad7c70de28ffe3148fd3fd1f68c36a872c53c691c9068e1ff163970863c48` | + +Historical reporting dated 2026-07-18 recorded 17/17 host tests passing, +manifest and safety checks passing, and a secret scan passing across 558 text +files. Those results are historical repository evidence. They are not current +hardware observations or proof of firmware behavior. + +The immutable result remains `READ_ONLY_PREFLIGHT_BLOCKED`. Firmware 9.60 +runtime behavior remains `UNPROVEN`. The stock elfldr and Payload Manager +sizes and hashes remain reference values only; their current on-device +identity is not established. + +## Schema boundary + +The existing artifact schema describes built target artifacts, while the +controlled-runtime schema describes the separate Phase-0.7 hardened runtime. +Neither can represent an offline remediation dossier without implying +inapplicable artifact or deployment semantics. No shared schema is widened. +The remediation manifest therefore names the dedicated +`chimera-gfx-phase-0.8-remediation-v1` contract and is validated semantically +by `tools/validate_phase08_remediation.py`. + +## Side-effect classification + +- **A — persistent mutation:** file create/write/delete/rename, configuration + or autoload change, metadata change, or persistent platform-state change. +- **B — process- or service-wide mutation:** functional global flags and + counters, `server_active_flag`, `autoload_triggered`, process/service state, + signals, or retry state. +- **C — volatile technical effect:** socket acceptance, temporary buffers, + allocations, scheduler activity, kernel counters, caches, audit or logging + paths, and temporary process-local state. +- **D — observation:** firmware fields, file metadata and hashes, process and + listener lists, configuration, rollback identities, and storage conditions. + +No persistent write found is not equivalent to side-effect-free. Category C +is not claimed absent. Missing observations are unknown, never evidence of +safe absence. + +## Pinned Payload Manager source + +The reviewed local source is public Payload Manager v0.3.1 commit +`cfbc70f30f419b09bf2b52283f7409e2d3117ee1`. Relevant source identities are +recorded in `manifests/runtime/phase-0.8-remediation.json`. + +The three confirmed root findings are: + +1. `http_on_request()` returns early for `OPTIONS`, then calls + `log_server_set_active()` for every other request at + `work/upstream/pldmgr-v0.3.1/src/http_server.c:131-152`. + `log_server_set_active()` writes the process-global + `server_active_flag = 1` at + `work/upstream/pldmgr-v0.3.1/src/log_server.c:20-23`. +2. `/autoload_status` calls `pldmgr_autoload_get_status()` at + `work/upstream/pldmgr-v0.3.1/src/http_server.c:847-852`. + That function writes `autoload_triggered = 1` at + `work/upstream/pldmgr-v0.3.1/src/autoload.c:44-48`. The worker reads that + flag when deciding whether to leave its frontend wait and begin the + countdown at `src/autoload.c:78-93`. +3. The same handler opens `/data/pldmgr/autoload.txt` and calls + `config_read()` at `src/http_server.c:854-871`. `config_read()` opens + `/data/pldmgr/pldmgr_config.txt` at `src/config.c:12-49`. + +### Request side-effect matrix + +All handlers below are rooted in `http_on_request()`. “Filesystem/config” +means project source performs such a read; it does not assert that no other +read or technical effect occurs. + +| Method | Endpoint | Handler path | `server_active_flag` | `autoload_triggered` | Filesystem/config | Other proven or unproven effects | Strict read-only | +|---|---|---|---:|---:|---:|---|---:| +| `OPTIONS` | `*` | `http_on_request` | no | no | no | response/transport allocations; network, cache, audit, scheduler and kernel effects unproven | **no** | +| `GET` | `/version` | `http_on_request` | yes | no | no | B: global flag; C: transport effects | **no** | +| `GET` | `/log` | `http_on_request -> log_build_json` | yes | no | no | B: global flag; C: allocation and mutex; D: ring observation | **no** | +| `GET` | `/autoload_status` | `http_on_request -> pldmgr_autoload_get_status -> config_read` | yes | yes | yes | B: functional autoload change; C: read/transport effects; D: config observation | **no** | +| `GET` | `/get_config` | `http_on_request -> config_read` | yes | no | yes | B: global flag; C: atime/audit/cache/metadata unproven; D: config observation | **no** | +| `GET` | `/list_payloads` | `http_on_request -> payload_mgr_list_json` | yes | no | yes | directory and sidecar scans; possible USB enumeration; technical effects unproven | **no** | +| `GET` | `/processes_list` | `http_on_request -> process_list_json` | yes | no | no | `sysctl` snapshot and allocation; kernel/audit/scheduler effects unproven | **no** | +| `GET` | `/sources_list` | `http_on_request -> sources_list_json` | yes | no | yes | not excluded by `is_noisy_route`, so `pldmgr_log()` also writes stdout and the log ring | **no** | +| `GET` | `/getip` | `http_on_request -> pldmgr_get_local_ip` | yes | no | no | network-interface query; kernel/cache/audit effects unproven | **no** | + +Supporting locations: + +- route constants: `include/pldmgr.h:7-44`; +- noisy-route list and generic request entry: + `src/http_server.c:108-152`; +- non-noisy request logging: `src/http_server.c:479-481`; +- payload and process lists: `src/http_server.c:601-616`; +- sources list: `src/http_server.c:706-713`; +- version, log, IP, autoload and config handlers: + `src/http_server.c:826-954`; +- ring and stdout mutation: `src/log_server.c:12-55`; +- payload scan: `src/payload_mgr.c:114-153,196-238`; +- sources file read: `src/sources.c:28-84,112-134`; +- process snapshot: `src/process_mgr.c:43-88`. + +`OPTIONS` is not promoted merely because it returns before the two proven +flag writes. It provides none of the required device evidence, and network, +allocation, logging below the reviewed layer, cache, audit, scheduling, and +kernel-counter effects are still unproven. + +## Root-cause analysis + +The current HTTP interface is a frontend-control interface, not an evidence +collector. A request is treated as evidence that the frontend is active, so +the server deliberately changes functional process state. The autoload route +goes further and changes a flag consumed by the autoload worker. This makes +the interface incompatible with the historical absolute read-only contract +even where the response body appears informational. + +A filesystem open using mode `"r"` prevents application-level content writes; +it does not prove absence of atime, audit records, page-cache activity, +metadata/cache updates, scheduling, kernel counters, or platform-specific +effects. Those effects require an exact collector and environment contract. + +Unavailable data cannot be interpreted as a negative observation. No current +firmware query, file identity, process/service inventory, listener inventory, +autoload snapshot, or rollback identity exists in this dossier. + +A missing Payload Manager backup cannot be created in an absolute read-only +phase. Creating it necessarily writes a new persistent object and may change +filesystem allocation and metadata. Backup creation therefore belongs to a +separate mutating rollback-preparation phase with separate authorization. + +## Evidence contract for a possible future method + +No collector is selected. Every timeout below is deliberately unset until the +collector implementation, transport, and cleanup contract are reviewed. An +unset timeout, timeout expiry, unknown result, identity drift, or partial +dataset yields `STOP`. + +| Observation | Needed fact | Possible source class | Confidence | Known mutations | Unknown effects | Required identity | Timeout | Reviewer return | +|---|---|---|---|---|---|---|---|---| +| firmware | two authoritative current values agree exactly | two independently reviewed authoritative sources | `UNPROVEN` | none established | UI/transport/audit/cache/platform query | tool/source/operation identity | unset → `STOP` | raw values, times, identities, agreement | +| live paths | literal paths, mounts, types, no symlink | reviewed no-follow metadata operation | `UNPROVEN` | none established | atime/audit/cache/namespace | tool, source, no-follow and mount semantics | unset → `STOP` | raw paths, mounts, types, symlink decision | +| object identities | stable pre/post object/device IDs and full metadata | same reviewed object collector | `UNPROVEN` | none established | cache/audit/race/concurrency | lookup, race, symlink policy | unset → `STOP` | complete pre/post records and errors | +| file sizes | exact current live byte sizes | same bound object handles | `UNPROVEN` | none established | cache/audit/concurrent modification | same-object and race policy | unset → `STOP` | sizes, references, bindings | +| SHA-256 | digest of exact current bytes | no-follow same-object streaming hash | `UNPROVEN` | none established | atime/audit/page cache/scheduling | implementation, version, commit, binary hash, open policy | unset → `STOP` | bytes, times, digest, identity, errors | +| processes/services | all relevant owners, paths, parents, starts and startup modes | reviewed process/service snapshot | `UNPROVEN` | none established | counters/audit/scheduling/locks/cache | tool/API/source/snapshot semantics | unset → `STOP` | raw snapshot and unmapped entries | +| listeners | owners and binding for 8084/8085/9021 | reviewed listener snapshot | `UNPROVEN` | none established | stack counters/audit/cache/locks/races | tool/API/source/owner mapping | unset → `STOP` | protocol/address/port/owner/exposure | +| autoload/startup/retry | complete authoritative state; artifacts absent; retry off | collector distinct from stock `/autoload_status` | `UNPROVEN` | stock route mutates `autoload_triggered` | atime/audit/cache/metadata/service/race | tool/source plus complete source list and no-functional-mutation proof | unset → `STOP` | safe raw representations, identities, hashes, decisions | +| rollback files | separate existing exact backups and restore mappings | same admissible object/hash collector | `UNPROVEN` | none established | atime/audit/cache/metadata/mount/race | hash, object and separation semantics | unset → `STOP` | paths, IDs, hashes, separation and mapping | +| storage condition | separate target, 2447320 content bytes plus reserve | reviewed capacity/mount operation | `UNPROVEN` | none established | mount/audit/cache/accounting/concurrency | tool/API and capacity semantics | unset → `STOP` | IDs, available bytes, reserve and separation | + +Any code-bearing collector would additionally require its exact name, version, +origin, source commit, byte size, SHA-256, complete operation set, imports, +transitive effects, output channel, cleanup, and fail-closed timeout behavior. + +## Architecture options — design only + +| Option | Can prove | Cannot prove | Transfer/execution needed | Persistent effects | Volatile effects | Required permission | Remaining blockers | +|---|---|---|---|---|---|---|---| +| A. Current Payload Manager endpoints | limited in-memory version/config/list responses | strict side-effect freedom, complete files/listeners/rollback identity | no new target transfer; requires a network request to an existing process | reads may affect metadata; no content write proven for selected GETs | proven global flags; autoload flag on status; sockets, allocation, logs, caches and audit | new bounded-observation permission would still be required | current interface violates the absolute read-only contract | +| B. Future one-shot observation method | potentially a purpose-built complete bounded dataset | nothing until source, ABI, transport and cleanup are reviewed | likely transfer and execution, both currently unspecified and unauthorized | must be forbidden by contract but absence is unproven | startup, loader, transport, allocation, scheduling, cache, audit and logs | new exact artifact- and method-bound permission | no design, binary, hash, ABI, timeout or cleanup proof exists | +| C. Manual/external firmware observation | one user-visible firmware value, possibly an independent second value | live files, processes, listeners, autoload and rollback state | no code transfer or execution if strictly manual | UI/platform persistence is unreviewed | UI, audit, cache and human transcription | new exact observation permission if used as formal evidence | insufficient for the complete preflight | +| D. Split observation from rollback preparation | observation can be reviewed before any backup write; later backup phase can prove rollback objects | observation alone cannot create missing backups | observation method unknown; backup preparation necessarily performs writes | backup creation, allocation and metadata are expected persistent effects | tool, storage, cache, scheduling and audit effects | separate observation permission, then separate mutating rollback permission | exact live identity and admissible backup procedure remain absent | + +None of these options is implemented by Phase 0.8R. + +## Hard gates + +The active blockers are: + +1. `exact_permission_quote_absent` — `STOP-RO`; +2. `collector_identity_absent` — `STOP-RO`; +3. `collector_side_effect_contract_absent` — `STOP-RO`; +4. `two_current_firmware_sources_absent` — `STOP-GATE`; +5. `live_object_identities_absent` — `STOP-GATE`; +6. `listeners_absent` — `STOP-GATE`; +7. `autoload_status_absent` — `STOP-GATE`; +8. `rollback_backups_absent` — `STOP-GATE`; +9. `payload_manager_backup_not_byte_exact_on_device` — `HARD_STOP-GATE`; +10. `unknown_result_is_stop` — `STOP`; +11. `timeout_is_stop` — `STOP`; +12. `deviation_is_stop` — `STOP`; +13. `automatic_retry_forbidden` — `STOP`. + +No general `READY`, `COMPLETE`, or deployment status may override any one of +these gates. The Payload Manager backup gate can be closed only by +artifact-specific, current, on-device evidence gathered under a separately +approved method, not by changing this manifest. + +## Phase separation + +Authority never flows forward. Each step requires its own completed review and +new permission where it involves a target: + +1. this offline remediation; +2. a possible later bounded-observation design; +3. separate permission for any observation transfer or execution; +4. review of the returned observations; +5. separate mutating rollback preparation if a backup must be created; +6. separate installation approval for only hardened elfldr and controlled + Payload Manager; +7. installation verification and complete rollback proof; +8. separate lifecycle approval; +9. non-graphical capability probes; +10. much later, a graphics backend and RetroArch. + +Installation permission does not grant lifecycle permission. Observation +permission does not grant rollback preparation. No earlier wording may be +reused for a later artifact, method, or phase. + +## Relationship to RetroArch + +RetroArch is a long-term goal, not an active Phase-0.8R workstream. The +dependency chain is: + +```text +offline evidence integrity + -> admissible bounded observation + -> verified rollback and installation boundary + -> separately approved lifecycle + -> non-graphical capability evidence + -> later graphics/runtime contracts + -> later adapters + -> eventual RetroArch port +``` + +Phase 0.8R implements none of GNM, VideoOut, SDL, hardware acceleration, +input, audio, RetroArch frontend behavior, cores, or content launching. + +## Machine-readable consistency record + +The validator compares this block structurally with the remediation manifest. + + +```json +{ + "status": "READ_ONLY_PREFLIGHT_BLOCKED", + "authorization": { + "authorized": false, + "installation_authorized": false, + "lifecycle_authorized": false, + "execution_authorized": false, + "transfer_authorized": false, + "automatic_retry": false + }, + "blockers": [ + "exact_permission_quote_absent", + "collector_identity_absent", + "collector_side_effect_contract_absent", + "two_current_firmware_sources_absent", + "live_object_identities_absent", + "listeners_absent", + "autoload_status_absent", + "rollback_backups_absent", + "payload_manager_backup_not_byte_exact_on_device", + "unknown_result_is_stop", + "timeout_is_stop", + "deviation_is_stop", + "automatic_retry_forbidden" + ], + "firmware_runtime_behavior": "UNPROVEN", + "stock_identity_classification": "reference_only", + "payload_manager_backup_classification": "hard_blocker", + "retroarch_active_phase": false +} +``` + diff --git a/docs/runtime/phase-0.9-anti-brick-threat-model.md b/docs/runtime/phase-0.9-anti-brick-threat-model.md new file mode 100644 index 0000000..68b29f4 --- /dev/null +++ b/docs/runtime/phase-0.9-anti-brick-threat-model.md @@ -0,0 +1,188 @@ +# Phase 0.9A offline anti-brick threat model + +Status: **DESIGN_ONLY**. Historical operational status: +**READ_ONLY_PREFLIGHT_BLOCKED**. + +This is an offline design review. It does not describe a proven PS5 +filesystem, installation method, recovery method, path, syscall or switch +primitive. No PS5 was contacted; no file was transferred, installed or +executed; no target artifact was created. Host simulation is not hardware +evidence. + +## Evidence boundary + +The review uses only existing repository evidence: + +- immutable Phase-0.8 record and Phase-0.8R remediation contract; +- the permanent artifact denylist; +- Phase-0.7 offline identities for the hardened elfldr and controlled Payload + Manager candidates; +- stock elfldr and Payload Manager sizes and hashes as `reference_only`; +- the offline rollback and installation review documents; +- clean local source identities for hardened elfldr, controlled Payload + Manager and the unchanged lifecycle-probe source. + +Current on-device paths, mounts, object IDs, hashes, firmware behavior, +processes, services, listeners, autoload state, backups and recovery paths +remain unobserved. The byte-exact on-device Payload Manager backup remains a +hard STOP gate. + +Severity means: + +- `CATASTROPHIC`: can remove the only working launch/recovery path, destroy + both live and rollback identities, run unverified code automatically, or + make recovery depend on a failed component; +- `HIGH`: can select or persist wrong/partial bytes, invalidate the evidence + binding, or prevent controlled recovery; +- `MEDIUM`: disrupts control or observation but does not alone prove loss of + both live and recovery objects; +- `LOW`: occurs before live mutation and should leave the old object intact, + but still needs platform evidence. + +## Control profiles + +### A — wrong target + +Possible damage is replacement of an unrelated or critical object. Before any +future mutation, one no-follow stable object must bind literal path, mount ID, +object ID, type, size and SHA-256, and the identity must be repeated +immediately before mutation. AB-001, AB-003, AB-004 and AB-007 prevent the +write. Recovery would require a separate verified backup and independent +executor; neither is proven. Namespace and object-identity semantics remain +`UNPROVEN`, so Phase 0.9A blocks. + +| ID | Scenario | Severity | +|---|---|---| +| TM-A-01 | wrong path | CATASTROPHIC | +| TM-A-02 | wrong mountpoint | CATASTROPHIC | +| TM-A-03 | symlink, junction or other redirect | CATASTROPHIC | +| TM-A-04 | path resolves to another object after preflight | CATASTROPHIC | +| TM-A-05 | case or path normalization changes identity | HIGH | +| TM-A-06 | target is replaced between check and write | CATASTROPHIC | + +### B — wrong pre-image + +Possible damage is treating modified or firmware-incompatible bytes as stock. +The future evidence must bind firmware, manifest, stable object, size and +digest in one current observation. AB-001 through AB-004 reject every partial +match. Detection must happen before mutation; rollback is not a substitute. +Current device identities and firmware runtime behavior remain `UNPROVEN`. + +| ID | Scenario | Severity | +|---|---|---| +| TM-B-01 | live hash differs from stock reference | HIGH | +| TM-B-02 | live size differs | HIGH | +| TM-B-03 | object ID changes | CATASTROPHIC | +| TM-B-04 | firmware differs | HIGH | +| TM-B-05 | manifest belongs to another build | HIGH | +| TM-B-06 | modified installation is treated as stock | CATASTROPHIC | + +### C — backup failure + +Possible damage is loss of all trusted rollback bytes. A backup must be +created before staging, on a separate identity, then closed, reopened, +re-identified and rehashed. Capacity, mapping and an independent recovery +executor must be proven. AB-005, AB-006, AB-016 and AB-017 block mutation +until this holds. The Payload Manager backup is currently a +`CATASTROPHIC` hard blocker. + +| ID | Scenario | Severity | +|---|---|---| +| TM-C-01 | backup missing | CATASTROPHIC | +| TM-C-02 | backup is the same object as live | CATASTROPHIC | +| TM-C-03 | backup is on the same vulnerable path | CATASTROPHIC | +| TM-C-04 | backup is partial | CATASTROPHIC | +| TM-C-05 | backup hash differs | CATASTROPHIC | +| TM-C-06 | backup cannot be reopened | HIGH | +| TM-C-07 | backup-to-target mapping is ambiguous | CATASTROPHIC | +| TM-C-08 | recovery tool depends on the replaced component | CATASTROPHIC | +| TM-C-09 | storage or metadata capacity is insufficient | HIGH | +| TM-C-10 | backup is created only after live mutation | CATASTROPHIC | + +### D — write and power loss + +Possible damage is a missing, partial or ambiguous live identity. A safe design +needs isolated staging, durable files and directory metadata, and one +old-or-new atomic switch. AB-007 through AB-013 reject in-place overwrite and +unproven switch semantics. No such PS5 primitive is proven. + +| ID | Scenario | Severity | +|---|---|---| +| TM-D-01 | power loss before staging | LOW | +| TM-D-02 | power loss during staging | HIGH | +| TM-D-03 | power loss after staging before verification | HIGH | +| TM-D-04 | power loss during live switch | CATASTROPHIC | +| TM-D-05 | power loss immediately after live switch | CATASTROPHIC | +| TM-D-06 | power loss before directory metadata is durable | CATASTROPHIC | +| TM-D-07 | partial write | CATASTROPHIC | +| TM-D-08 | short write | HIGH | +| TM-D-09 | write error | HIGH | +| TM-D-10 | flush or fsync error | CATASTROPHIC | +| TM-D-11 | file bytes correct but directory not durable | CATASTROPHIC | +| TM-D-12 | new file has wrong ownership, mode or metadata | HIGH | + +### E — process and lifecycle + +Possible damage is automatic launch of unverified bytes, stale mappings or a +crash loop. A future observation must establish all relevant owners, +instances, mappings, listeners, supervisors and authoritative autoload/retry +state. AB-010, AB-013, AB-014 and AB-020 require quiescence and a later +one-shot approval. Unknown topology blocks before switch. + +| ID | Scenario | Severity | +|---|---|---| +| TM-E-01 | target executable is active | HIGH | +| TM-E-02 | multiple instances exist | HIGH | +| TM-E-03 | service restarts itself | CATASTROPHIC | +| TM-E-04 | listener conflict | MEDIUM | +| TM-E-05 | process holds old file or mappings | HIGH | +| TM-E-06 | watchdog or supervisor automatically restarts | CATASTROPHIC | +| TM-E-07 | autoload starts the candidate before verification | CATASTROPHIC | +| TM-E-08 | automatic retry creates a crash loop | CATASTROPHIC | + +### F — rollback failure + +Possible damage is restoring wrong bytes to a wrong path, automatic execution +or loss of the recovery executor. Rollback must bind the preverified backup, +exact target mapping and independent executor. AB-006 and AB-015 through +AB-017 require a separate approval and post-rollback identity proof. A failed +rollback verification is `CATASTROPHIC/BLOCKED`, never a retry. + +| ID | Scenario | Severity | +|---|---|---| +| TM-F-01 | wrong backup selected | CATASTROPHIC | +| TM-F-02 | wrong rollback target path | CATASTROPHIC | +| TM-F-03 | rollback interrupted | CATASTROPHIC | +| TM-F-04 | restored hash is wrong | CATASTROPHIC | +| TM-F-05 | rollback automatically starts a service | CATASTROPHIC | +| TM-F-06 | rollback works only if Payload Manager works | CATASTROPHIC | +| TM-F-07 | rollback works only if elfldr works | CATASTROPHIC | + +### G — operator error + +Possible damage is a role swap, reused authority or combined loss of both +runtime components. Component manifests and approvals must be bound to one +device, firmware, action, hash, size and expiration. AB-018 through AB-020 +prohibit a combined transaction, exclude the lifecycle probe and stop phase +carryover. Component order is not guessed. + +| ID | Scenario | Severity | +|---|---|---| +| TM-G-01 | hardened elfldr and Payload Manager are swapped | CATASTROPHIC | +| TM-G-02 | lifecycle probe is treated as installation candidate | CATASTROPHIC | +| TM-G-03 | wrong firmware or build is selected | HIGH | +| TM-G-04 | two installation phases are combined | CATASTROPHIC | +| TM-G-05 | approval for one component is reused for another | HIGH | +| TM-G-06 | stale manifest is used | HIGH | +| TM-G-07 | general READY overrides a specific blocker | CATASTROPHIC | + +The item-specific reasons and machine bindings are in +`manifests/runtime/phase-0.9-anti-brick-design.json`. + +## Decision + +Every threat is detectable only under evidence contracts that are not yet +satisfied on the PS5. The design is fail-closed and suitable for host review, +but not for installation engineering on a device. + +**BLOCKER: NO PROVEN POWER-LOSS-SAFE SWITCH.** diff --git a/docs/runtime/phase-0.9-installation-transaction-design.md b/docs/runtime/phase-0.9-installation-transaction-design.md new file mode 100644 index 0000000..3d2cee1 --- /dev/null +++ b/docs/runtime/phase-0.9-installation-transaction-design.md @@ -0,0 +1,201 @@ +# Phase 0.9A installation transaction design + +Status: **DESIGN_ONLY — NOT INSTALLATION READY**. + +This document defines a future evidence and state contract. It contains no +PS5 path, install command, device write implementation, lifecycle transition, +autoload, retry, graphics code or RetroArch transition. All approval values +remain false. + +## Anti-brick invariants + +| ID | Mandatory invariant | +|---|---| +| AB-001 | Never write without exact stable path, mount ID, object ID, type, size and pre-image SHA-256. | +| AB-002 | A stock hash remains `reference_only` until observed on one stable on-device object. | +| AB-003 | Hash match without object identity is insufficient. | +| AB-004 | Object identity without hash match is insufficient. | +| AB-005 | Before staging, a separate backup survives close, reopen, identity, size and hash verification. | +| AB-006 | Backup and recovery do not depend on the candidate or replaced component. | +| AB-007 | Never overwrite a live executable in place. | +| AB-008 | Stage under a separate inactive identity. | +| AB-009 | Reopen and hash the candidate before activation; bytes equal the approved artifact. | +| AB-010 | No autoload, retry, service restart or boot starts the candidate. | +| AB-011 | Switch only with a separately proven transactional or atomic primitive. | +| AB-012 | Unproven atomicity or durability blocks installation. | +| AB-013 | Verify the new live identity; execution is a later approval. | +| AB-014 | At most one manual execution and zero automatic retries. | +| AB-015 | Failed post-switch verification enters `ROLLBACK_REQUIRED`, never execution. | +| AB-016 | Rollback uses only the verified backup and exact target mapping. | +| AB-017 | After rollback, re-establish size, hash, object identity and metadata. | +| AB-018 | elfldr and Payload Manager are never replaced in one uninterrupted transaction. | +| AB-019 | The lifecycle probe is not an installation candidate. | +| AB-020 | No later phase begins without separate human permission. | + +## State machine + +The complete ordered state set is: + +```text +OFFLINE_ONLY +-> OBSERVATION_NOT_AUTHORIZED +-> OBSERVATION_AUTHORIZED +-> DEVICE_IDENTITY_OBSERVED +-> LIVE_OBJECTS_VERIFIED +-> BACKUP_NOT_PRESENT +-> BACKUP_CREATION_NOT_AUTHORIZED +-> BACKUP_CREATION_AUTHORIZED +-> BACKUP_CREATED +-> BACKUP_REOPENED_AND_VERIFIED +-> RECOVERY_PATH_VERIFIED +-> CANDIDATE_NOT_AUTHORIZED +-> CANDIDATE_APPROVED_OFFLINE +-> STAGING_NOT_AUTHORIZED +-> STAGING_AUTHORIZED +-> CANDIDATE_STAGED +-> CANDIDATE_REOPENED_AND_VERIFIED +-> TARGET_NOT_QUIESCENT +-> TARGET_QUIESCENT +-> SWITCH_NOT_AUTHORIZED +-> SWITCH_AUTHORIZED +-> SWITCH_IN_PROGRESS +-> POST_SWITCH_VERIFY +-> MANUAL_EXECUTION_NOT_AUTHORIZED +-> MANUAL_EXECUTION_AUTHORIZED +-> ONE_SHOT_EXECUTION +-> ACCEPTED +``` + +The only recovery branch is: + +```text +POST_SWITCH_VERIFY +-> ROLLBACK_REQUIRED +-> ROLLBACK_AUTHORIZED +-> ROLLBACK_IN_PROGRESS +-> ROLLBACK_VERIFIED +``` + +Any error, mismatch, timeout, unknown or object change goes to `BLOCKED`. +No state automatically crosses an authorization state. There is no automatic +retry, autoload, lifecycle, graphics or RetroArch transition. A general READY +value cannot bypass a specific gate. + +The current real state is `OFFLINE_ONLY`; the repository templates do not move +it. + +## Approval separation + +Observation, backup creation, staging, switch, one-shot execution and rollback +each require a different active-task authorization. An authorization must bind +one component, device, exact firmware, live pre-image, candidate hash and +size, action count, runtime, expiration and stop conditions. Reuse for another +hash, action or component fails. + +Phase 0.9A grants none of these authorizations. The simulator uses explicitly +labelled synthetic authorization objects only to test state-machine logic. +Those objects always report `installation_authorized=false` and +`execution_authorized=false`. + +## Component-separated models + +### Hardened elfldr + +- stock reference: 397000 bytes, + `092d16ee0ede0c494947efd38d1a17bbd7cc4b022d3858ea898833c188c703e8`; +- candidate reference: 397000 bytes, + `63e810982471eb40cae3a20aa9df9a0a02892f420e429874fae4e99aa400b561`; +- live path, mount, object ID and current hash: `UNPROVEN`; +- separate on-device backup: `UNPROVEN`; +- process, service, listener, autoload and quiescence: `UNPROVEN`; +- target mapping, recovery independence and switch primitive: `UNPROVEN`; +- approval, staging, switch, execution, acceptance and rollback: not + authorized or performed. + +### Controlled Payload Manager + +- stock reference: 2050320 bytes, + `518740adbacccb9094fadb07dd424c53ee290f38306449ccc9d6957fdf813c0b`; +- candidate reference: 99560 bytes, + `8fecf8241a46246eddbd21e8bb4d875f5d76f1f4f4c6a11384df1f131aa5e5b1`; +- live path, mount, object ID and current hash: `UNPROVEN`; +- byte-exact, separate, reopened on-device backup: **HARD_BLOCKER**; +- process, service, listener, autoload and quiescence: `UNPROVEN`; +- target mapping, recovery independence and switch primitive: `UNPROVEN`; +- approval, staging, switch, execution, acceptance and rollback: not + authorized or performed. + +There is no “install all” operation and no shared approval. Component B may be +considered only after component A is separately accepted or fully rolled back, +but Phase 0.9A does not infer which component is A. Order requires proven +dependency and recovery evidence. The lifecycle probe belongs to neither +model. + +## Switch contract + +Design preference, without assuming platform support: + +1. side-by-side or dual-slot activation without deleting stock; +2. proven atomic exchange; +3. proven transactional snapshot; +4. another method only after separate evidence. + +An in-place copy-overwrite is always forbidden. The sequence `rename live -> +backup; rename candidate -> live` is forbidden while a power-loss interval can +leave the live name absent. + +Before a future switch, evidence must establish: + +- filesystem type, mount identity and read/write state; +- atomic rename or exchange behavior and replacement semantics; +- file-flush and directory-flush durability; +- interruption and power-loss behavior; +- no-follow behavior and stable open-object identity; +- ownership, mode and metadata preservation; +- capacity, metadata reserve and maximum file size; +- behavior while the target executable is open. + +None is proven for the PS5 in this phase. + +**BLOCKER: NO PROVEN POWER-LOSS-SAFE SWITCH.** + +## Power-loss boundary table + +All results are `UNPROVEN` for PS5. The host simulator can prove only its own +virtual atomic primitive. + +| # | Boundary | Possible state | Live visibility | Backup | Auto-start | Recovery | Result | +|---:|---|---|---|---|---:|---|---| +| 1 | before backup write | old live only | old | absent | no | no mutation yet | UNPROVEN | +| 2 | during backup write | old plus partial backup | old | invalid partial | no | old must remain | UNPROVEN | +| 3 | after backup write before flush | volatile backup | old | not durable | no | old must remain | UNPROVEN | +| 4 | after flush before reopen/hash | unverified backup | old | not reverified | no | old must remain | UNPROVEN | +| 5 | during candidate staging | partial candidate | old | verified if contract holds | no | independent | UNPROVEN | +| 6 | after staging before verification | unverified candidate | old | verified if contract holds | no | independent | UNPROVEN | +| 7 | immediately before switch | three verified logical objects | old | verified | no | independent | UNPROVEN | +| 8 | during switch | must be complete old or complete new | unknown | must remain verified | no | independent | UNPROVEN | +| 9 | immediately after switch | namespace/durability uncertain | unknown | must remain verified | no | independent | UNPROVEN | +| 10 | after switch before live hash | nominal new live unverified | unverified | verified | no | independent | UNPROVEN | +| 11 | after live hash before execution | nominal verified new | new | verified | no | independent | UNPROVEN | +| 12 | during first manual execution | new plus backup | new | verified | no automatic start | must work without new component | UNPROVEN | +| 13 | during rollback | must be complete new or complete old | unknown | must remain verified | no | independent | UNPROVEN | +| 14 | after rollback before verification | nominal restored old | unverified old | verified | no | independent | UNPROVEN | + +Because at least one boundary is `UNPROVEN`, the model is not +installation-suitable. Here all fourteen are `UNPROVEN`. + +## Host simulator + +`tools/simulate_phase09_transaction.py` models logical objects, identities, +hashes, mounts, approvals, transitions, failures and all fourteen power-loss +boundaries in memory. It has no device path, network operation, cross-compiler +call, target artifact open, production installer or output-file option. + +The default scenario stops at +`NO_PROVEN_POWER_LOSS_SAFE_SWITCH`. A special in-memory primitive can be +treated as atomic for fault-testing, but every report marks it +`host_simulation_only=true` and `hardware_evidence=false`. Even that synthetic +happy path stops at `MANUAL_EXECUTION_NOT_AUTHORIZED`. + +The machine-readable contract is +`manifests/runtime/phase-0.9-anti-brick-design.json`. diff --git a/docs/runtime/phase-0.9-recovery-and-rollback-contract.md b/docs/runtime/phase-0.9-recovery-and-rollback-contract.md new file mode 100644 index 0000000..72d46d9 --- /dev/null +++ b/docs/runtime/phase-0.9-recovery-and-rollback-contract.md @@ -0,0 +1,106 @@ +# Phase 0.9A recovery and rollback contract + +Status: **DESIGN_ONLY — RECOVERY UNPROVEN**. + +This contract states what future evidence must prove. It does not authorize or +implement backup creation, transfer, installation, switch, rollback or +execution. + +## Valid backup contract + +A future backup is valid only when all conditions hold: + +1. it is created before staging or any live mutation; +2. the source object and exact restoration target are recorded; +3. the source stays the same stable object throughout the read; +4. every byte is read and the exact size is recorded; +5. SHA-256 equals the current observed pre-image; +6. the backup is a distinct object, not an alias of live; +7. it is closed, reopened without redirection, re-identified and rehashed; +8. its storage medium and mount are recorded; +9. content and metadata capacity plus reserve are proven; +10. autoload, cleanup and packaging cannot delete it; +11. it is never replaced by candidate bytes; +12. mapping is literal and not selected by a glob, basename or guess; +13. rollback does not guess the live target; +14. recovery can use it without the component being replaced. + +A second byte-exact off-device copy with its own manifest and SHA-256 is +strongly recommended. Creating or transferring that copy is a later, +separately authorized phase and is not permitted here. + +For the controlled Payload Manager, an absent, differing, unstable, +non-reopenable or recovery-dependent byte-exact on-device backup remains a +**HARD STOP-GATE**. + +## Recovery independence + +Before any component transaction, evidence must answer all rows without +inference: + +| Question | Hardened elfldr | Controlled Payload Manager | +|---|---|---| +| How is recovery started? | UNPROVEN | UNPROVEN | +| Which exact component executes recovery? | UNPROVEN | UNPROVEN | +| Is that executor outside the component being replaced? | UNPROVEN | UNPROVEN | +| Does recovery work when elfldr does not start? | UNPROVEN | UNPROVEN | +| Does recovery work when Payload Manager does not start? | UNPROVEN | UNPROVEN | +| Does recovery work without autoload? | UNPROVEN | UNPROVEN | +| Does recovery work without the replaced listener? | UNPROVEN | UNPROVEN | +| Is a second independent recovery path proven? | UNPROVEN | UNPROVEN | + +A generic console recovery mode is not counted. No public evidence in this +repository proves that it restores custom project files. + +If rollback depends on the component being replaced, the result is: + +**CATASTROPHIC BLOCKER.** + +## Rollback transition + +Rollback is not an automatic exception path. It requires: + +```text +POST_SWITCH_VERIFY failure +-> ROLLBACK_REQUIRED +-> separate human ROLLBACK_AUTHORIZED +-> ROLLBACK_IN_PROGRESS +-> ROLLBACK_VERIFIED +``` + +The rollback action may use only the already reopened and verified backup and +the fixed target mapping. It may not autoload, start or retry either component. +After the switch, path, mount, object ID, type, size, SHA-256, ownership, mode +and relevant metadata are re-established before any later decision. + +An interrupted rollback is acceptable only if a proven atomic primitive leaves +exactly one complete live identity and preserves the verified backup. That +primitive is not proven on PS5. A wrong post-rollback hash is +`CATASTROPHIC/BLOCKED`; there is no retry. + +## Component isolation + +The hardened elfldr and controlled Payload Manager have separate live +pre-images, backups, candidates, target mappings, recovery dependencies, +process/service/listener states, autoload evidence, approvals, staging, +switches, post-switch checks, executions, acceptances and rollbacks. + +There is no shared approval and no combined transaction. A second component +cannot begin until the first is separately accepted or fully rolled back. +Which component comes first is `UNPROVEN`; order must follow demonstrated +dependencies rather than convenience. The lifecycle probe is outside both +installation and rollback models. + +## Current blockers + +- historical `READ_ONLY_PREFLIGHT_BLOCKED`; +- current live identities and firmware behavior are unproven; +- exact Payload Manager backup is absent as proven evidence; +- filesystem atomicity, exchange, file durability and directory durability are + unproven; +- process, supervisor, listener, autoload and retry topology is unproven; +- no independent recovery executor or second recovery path is proven; +- no component order is proven; +- every device authorization remains false. + +No blocker is lifted by this design, manifest, host simulator or test result. diff --git a/docs/runtime/phase-0.9b-bounded-observer-design.md b/docs/runtime/phase-0.9b-bounded-observer-design.md new file mode 100644 index 0000000..79b6ef8 --- /dev/null +++ b/docs/runtime/phase-0.9b-bounded-observer-design.md @@ -0,0 +1,133 @@ +# Phase 0.9B bounded observer design + +Status: **BLOCKED — OFFLINE AUDIT ONLY**. + +No observer source, PS5 target, ELF, installation package, lifecycle package or +execution package was created. No PS5 connection, scan, transfer or execution +occurred. This document describes the rejected design boundary and the exact +evidence needed before implementation may start. + +## Canonical boundary + +- Historical Phase 0.8 remains `READ_ONLY_PREFLIGHT_BLOCKED`. +- Phase 0.9A remains `DESIGN_ONLY`. +- Firmware 9.60 runtime behavior remains `UNPROVEN`. +- Stock sizes and hashes remain `reference_only`. +- The byte-exact on-device Payload Manager backup remains a `HARD_BLOCKER`. +- Every authorization field remains `false`; automatic retry remains `false`. +- SHA-256 + `4be1c17b4964f2b68c39b5145bc4af4619c32512d60269ecf5c39728b390fa63` + remains permanently denied and execution-ineligible. + +## Source identities + +| Source | Commit | Tree | +|---|---|---| +| hardened elfldr | `197623058f509eddde18868dafcb92fdcac66464` | clean | +| controlled Payload Manager | `e23d94ff91233aa770e2342800c1467875bdef44` | clean | +| public elfldr v0.23 base | `699e8bcff03e91e8d6ca6eba281af25c5a58d8c2` | clean | +| public Payload Manager v0.3.1 base | `cfbc70f30f419b09bf2b52283f7409e2d3117ee1` | clean | +| PS5 Payload SDK v0.41 | `d2e2e585740362976a39fdd5ccf390f199a7bc37` | clean | +| lifecycle-probe source | `fe08300339a13f899fb78ea404ada381a5cba87c` | byte identity checked | + +The public bases establish provenance and the pre-hardening behavior. The +hardened source commits establish the only controlled route considered here. +No external source was contacted during this audit. + +## Mandatory gates + +| Gate | Required | Evidence | Result | +|---|---|---|---| +| kernelwrite-free startup | complete reachable startup path excludes kernel/process patch writes | SDK `crt/crt.c:48-80,193-220`, `crt/patch.c:25-101`, `crt/Makefile:19-26` | **FAIL** for normal CRT | +| custom entry ABI | entry register, BSS, stack, return, exit and cleanup are all proven | elfldr `elfldr.c:273-415`; prior Phase-0.5 audit | **UNPROVEN** | +| non-persistent output | payload bytes return over the hash-bound route without a device file, listener or global-state mutation | elfldr `socksrv.c:129-145,361-365`, `elfldr.c:499-520`; manager `verified_launcher.c:119-185`, `ps5_launcher.c:18-70` | **FAIL** | +| bounded exit | successful and failing observer paths terminate without relying on an unproven ABI | SDK `crt/crt.c:150-220`; hardened loader watchdog | **UNPROVEN** | + +Both implementation gates fail independently. The build gate therefore stops +before observer source or target creation. + +## Capability matrix + +“Source present” does not mean firmware-9.60 behavior is proven. “Implement” +is `no` for every row because startup/exit and output fail first. + +| Needed fact | Available local API | Source | Evidence | Implement | +|---|---|---|---|---| +| firmware source 1 | `kernel_get_fw_version()` reads `sdk_ps5_ver` | SDK `crt/kernel.c:148-171` | source present; runtime unproven | no | +| firmware source 2 | none established | — | unproven | no | +| file metadata | `open(O_RDONLY\|O_NOFOLLOW\|O_CLOEXEC)`, `fstat`, `lstat`, `close` | manager `verified_launcher.c:76-115,174-184` | source/host evidence only | no | +| object identity | pre/post `st_dev`, `st_ino`, `st_size` | manager `verified_launcher.c:26-29,87-101` | source/host evidence only | no | +| SHA-256 | bounded descriptor read plus local SHA-256 | manager `verified_launcher.c:31-55`, `src/sha256.c` | source/host evidence only | no | +| mount information | `getmntinfo()` / `getfsstat()` sample | SDK `samples/mntinfo/main.c`, `libc/getmntinfo.c` | sample present; runtime unproven | no | +| processes/services | `sysctl(KERN_PROC)` sample; no complete service map | SDK `samples/ps/main.c` | partial and runtime unproven | no | +| listeners | none established | — | unproven | no | +| autoload configuration | stock file readers and mutating HTTP handlers | manager `config.c`, `autoload.c`, `http_server.c` | unsafe or unproven | no | +| output channel | controlled route suppresses payload stdio and manager has no receive operation | elfldr `socksrv.c`, manager `ps5_launcher.c` | proven absent on controlled route | no | +| monotonic deadline | `clock_gettime(CLOCK_MONOTONIC)` in loader watchdog | elfldr `ps5_controlled.c:26-59` | source/host evidence only | no | +| process exit | normal CRT terminate path or custom return | SDK `crt/crt.c:150-220`, elfldr `elfldr.c:380-415` | observer ABI unproven | no | + +Unknown capabilities remain unknown. There is no FreeBSD, PS4 or other +firmware fallback. + +## Rejected runtime designs + +### Normal SDK CRT + +The installed compiler wrapper adds `crt1.o` unless `-nostartfiles` is used. +That object transitively contains `patch.o`. `_start()` calls +`payload_init()`, which calls `__patch_init()` before `main`. +`__patch_init()` changes credential capabilities/attributes and syscall +permission bounds. This violates the Phase-0.9B kernelwrite-free requirement. + +### Freestanding custom `_start` + +The loader places its `payload_args_t *` in `RDI`, stores the old instruction +pointer at `RSP-8`, and enters the ELF. Those operations show how control is +transferred; they do not prove that a bare `ret`, a direct process-exit import, +or another custom termination path performs every required cleanup safely. +The prior minimal-startup gate remains blocked. It was not bypassed. + +### Controlled stdout/stderr + +The controlled route passes `-1` as `stdio`. The elfldr duplicates the request +socket into standard descriptors only when `stdio > 0`. The manager transport +exposes connect, send and close callbacks but no receive callback. The fixed +`Controlled payload exited` response carries no observer data. The legacy +stdio route is not the hash-bound controlled route and is inadmissible. + +### Notifications, klog and manager HTTP + +A system notification is not a complete deterministic machine-readable return +channel. Klog is not proven to be a bounded host-return channel. The Payload +Manager HTTP service is a persistent listener, and ordinary routes change +process-global state; `/autoload_status` also changes autoload state. None is +admissible. + +## Intended observer shape if gates are later proven + +This is a non-implemented contract: + +1. enter once through a source-bound, kernelwrite-free ABI; +2. emit a fixed offline-bound artifact identity; +3. initialize bounded in-memory results; +4. execute each explicitly authorized read at most once; +5. emit raw value or explicit error; +6. emit a final status; +7. terminate through a proven exit/cleanup path. + +It would have no event loop, server, thread, callback, retry, sleep, process +signal, subprocess, module load, file mutation, lifecycle action, graphics, +SDL, RetroArch, GNM or VideoOut behavior. + +## Decision + +**BLOCKED — OBSERVER STARTUP OR EXIT ABI UNPROVEN; NO PROVEN +NON-PERSISTENT OUTPUT CHANNEL.** + +The minimum missing evidence is: + +1. a commit-bound, kernelwrite-free entry and termination contract that covers + BSS, stack, return/exit, crash paths and loader cleanup; and +2. a commit-bound controlled response route that carries bounded observer + bytes without a device file, new listener, persistent state or Payload + Manager global-state mutation. diff --git a/docs/runtime/phase-0.9b-observer-limitations.md b/docs/runtime/phase-0.9b-observer-limitations.md new file mode 100644 index 0000000..4ac6fb1 --- /dev/null +++ b/docs/runtime/phase-0.9b-observer-limitations.md @@ -0,0 +1,108 @@ +# Phase 0.9B observer limitations + +Status: **BLOCKED**. + +## What was established + +- Exact local commits and relevant source-file identities were recorded. +- Normal SDK v0.41 startup is **UNSAFE** for this phase because + `__patch_init()` is reachable before `main` and performs prohibited writes. +- A `-nostartfiles` link can omit that CRT, but safe observer entry, + return/exit, crash cleanup and resource cleanup remain **UNPROVEN**. +- The hardened controlled route is hash-bound but passes `stdio=-1`. +- The controlled manager transport sends and closes; it has no receive + callback for observer data. +- The fixed loader exit response cannot carry the requested observation set. +- Stock Payload Manager HTTP routes and log state are not an admissible + replacement output collector. + +## What was not built + +- no observer source; +- no target declaration; +- no PS5 object or ELF; +- no linker map; +- no disassembly or reachable callgraph; +- no artifact import, relocation, string or open-flag report; +- no double clean target build; +- no artifact manifest; +- no install, lifecycle or execution package; and +- no transfer or execution instructions. + +These are intentional consequences of the pre-build hard gates, not omitted +positive evidence. + +## Unsupported or unproven observations + +All target observations remain unimplemented. In particular: + +- only one possible firmware source exists in the reviewed SDK source; +- no independent second firmware source is established; +- firmware 9.60 runtime behavior of the first source is untested; +- mount layouts and semantics are not runtime-proven; +- a process sample exists, but complete service mapping does not; +- no exact listener snapshot and owner-mapping API was established; +- live and backup paths are absent; +- live and backup objects, sizes, identities and hashes are absent; +- authoritative autoload/startup/retry source completeness is absent; +- stable no-follow behavior on the target filesystem is unproven; +- atime, audit, cache, counter and scheduler effects remain possible; and +- output and exit remain blocked. + +## Remaining anti-brick blockers + +1. two current firmware sources; +2. literal live paths; +3. live mount and object identities; +4. live sizes and SHA-256; +5. mount and filesystem properties; +6. process and service inventory; +7. listener inventory and ownership; +8. complete autoload/startup/retry state; +9. existing separate rollback objects; +10. byte-exact on-device Payload Manager backup (`HARD_BLOCKER`); +11. independent recovery; +12. power-loss-safe switch semantics; +13. component order; +14. observer startup/exit ABI; and +15. non-persistent output. + +No offline test may promote a stock hash, clear a device blocker, prove +firmware behavior, or authorize a later action. + +## Risk of a later one-shot run + +Even if a future artifact were approved, unresolved risks would include: + +- the existing loader’s ptrace, credential, mapping, breakpoint and + socket-overlap operations before payload entry; +- loader cleanup failure or a child that does not terminate; +- crash or return behavior different from the source-reviewed expectation; +- kernel, audit, cache, atime, scheduler and counter effects from reads; +- stale or conflicting path plans; +- concurrent object replacement during observation; +- incomplete process/service/listener snapshots; +- output truncation or loss; +- a false inference from an unsupported result; and +- operator confusion between observation evidence and installation approval. + +One observation could not prove atomic switch semantics, write durability, +power-loss behavior, recovery independence under actual failure, component +order, future runtime stability, graphics safety, RetroArch compatibility or +the absence of every platform side effect. + +## Minimum evidence to resume engineering + +Resume target engineering only after both items are available in local, +commit-bound public source and accepted by a new review: + +1. a kernelwrite-free startup and termination ABI covering BSS, stack, + return/exit, crash behavior and loader cleanup; and +2. a one-shot response channel that returns bounded observer bytes over the + controlled route without a device file, new listener, persistent state, + global Payload Manager mutation or post-exit activity. + +Until then the exact decision is: + +**BLOCKED — OBSERVER STARTUP OR EXIT ABI UNPROVEN; NO PROVEN +NON-PERSISTENT OUTPUT CHANNEL.** diff --git a/docs/runtime/phase-0.9b-observer-result-contract.md b/docs/runtime/phase-0.9b-observer-result-contract.md new file mode 100644 index 0000000..7376fff --- /dev/null +++ b/docs/runtime/phase-0.9b-observer-result-contract.md @@ -0,0 +1,114 @@ +# Phase 0.9B observer result contract + +Status: **HOST CONTRACT ONLY — NO TARGET IMPLEMENTATION**. + +This contract defines how a future observer result would be represented after +startup, exit and output gates are separately proven. It does not establish +those capabilities and does not authorize transfer or execution. + +## Framing + +The preferred representation is bounded JSON Lines because every entry can be +validated independently and a final entry can mark truncation or failure. +Whether JSON Lines is feasible without unacceptable target dependencies +remains unproven. + +Every future entry must contain: + +```json +{ + "schema_version": 1, + "observer_build_id": "offline-bound identifier", + "observation_id": "unique bounded identifier", + "category": "firmware", + "source": "exact source identity", + "raw_value": null, + "raw_error": "UNSUPPORTED_OR_UNPROVEN", + "normalized_value": null, + "confidence": "UNPROVEN", + "persistent_mutation_performed": false, + "retry_performed": false, + "monotonic_offset_ms": null, + "truncation": false, + "fail_closed": true +} +``` + +Allowed confidence values are `OBSERVED`, `PARTIAL`, `UNSUPPORTED`, +`UNPROVEN`, `CONFLICT`, and `ERROR`. + +`raw_value` and `raw_error` are mutually exclusive. A normalized value may be +present only when it cannot hide a raw discrepancy. The raw values from two +firmware sources are always returned separately. + +## Fixed limits + +No target constants are selected while the build is blocked. A future +observation plan must bind all of these to finite positive values: + +- maximum complete output bytes; +- maximum entry bytes; +- maximum path bytes; +- maximum observations; +- maximum objects; +- maximum processes; +- maximum services; +- maximum listeners; +- maximum errors; and +- maximum monotonic runtime. + +An exceeded limit produces one final bounded error when the output channel +still works, sets `truncation=true` and `fail_closed=true`, performs no retry, +and exits through the proven termination path. + +## Per-category rules + +| Category | Success evidence | Required failure form | +|---|---|---| +| observer identity | offline manifest identity repeated unchanged | `UNPROVEN` if runtime self-binding is not available | +| firmware | two separately sourced raw values and exact comparison | `UNPROVEN` for a missing source; `CONFLICT` for mismatch | +| mount | only fields with exact ABI evidence | `UNSUPPORTED` for unproven layouts or semantics | +| object metadata | literal authorized path, no-follow open, stable pre/post same-object metadata | explicit symlink, path, object-ID or size error | +| SHA-256 | exact planned size, complete bounded read, stable post-read object | short read, read error, drift or hash mismatch | +| rollback object | same rules plus a distinct available identity from live | backup missing or same-object conflict | +| processes/services | bounded read-only snapshot with exact structures | `UNSUPPORTED` when service mapping is incomplete | +| listeners | bounded read-only snapshot with exact owner mapping | `UNSUPPORTED` when API or ownership is unproven | +| autoload | minimal fields from complete authoritative sources | missing source, parse error or incomplete-source `UNPROVEN` | + +No file content unrelated to the minimal autoload/retry decision may be +returned. Secrets and full configuration contents are excluded. + +## Filesystem contract + +A future target may read an object only when its compiled and audited path is: + +```text +authorized literal path + -> open read-only + no-follow + close-on-exec + -> fstat same descriptor + -> validate regular object and planned size + -> one bounded read stream + -> fstat same descriptor + -> compare identity and size + -> no-follow path metadata comparison + -> close +``` + +If no-follow is not source- and artifact-proven, the result is +`PATH_SYMLINK_SAFETY_UNPROVEN` and the object is not hashed. No path is +hardcoded by Phase 0.9B. The default observation plan contains no paths. + +Reads may affect atime, caches, audit records, counters or scheduling. Those +volatile or unknown effects must be reported; this contract does not call them +absent and does not authorize persistent writes. + +## Terminal behavior + +Every future path, including output failure, deadline, unsupported query, +parse error, read error, limit exhaustion and conflict, must reach the proven +termination path. No retry, sleep-and-retry, callback, background thread, +listener, process signal, service action or subprocess may occur. + +The host-only model in `tests/phase09b_observer_model.py` exercises these +contract decisions without filesystem or network I/O. It is a guardrail, not +PS5 code or hardware evidence. diff --git a/docs/runtime/phase-0.9b-observer-static-audit.md b/docs/runtime/phase-0.9b-observer-static-audit.md new file mode 100644 index 0000000..1b40cc5 --- /dev/null +++ b/docs/runtime/phase-0.9b-observer-static-audit.md @@ -0,0 +1,152 @@ +# Phase 0.9B observer static audit + +Status: **NOT PERFORMED — BLOCKED BEFORE SOURCE AND BUILD**. + +This is a truthful negative audit record. No observer artifact exists, so no +ELF property can be reported as passing. Absence of imports or strings is not +used as a substitute for a reachable-code proof. + +## Build decision + +The source audit stopped the target build because: + +- normal SDK startup reaches `__patch_init()` and performs prohibited writes; +- a freestanding observer return/exit and cleanup contract remains unproven; +- the hash-bound controlled route suppresses payload stdout/stderr; and +- the controlled manager sends the ELF and closes without receiving observer + output. + +No observer source path or CMake target was added. The existing +`CHIMERA_GFX_BUILD_PS5_MINIMAL_STARTUP` configure-time blocker remains intact. + +## Required artifact audit status + +| Audit | Status | Reason | +|---|---|---| +| SHA-256 and size | `NOT_APPLICABLE_NO_ARTIFACT` | no binary | +| source commit binding | `NOT_APPLICABLE_NO_ARTIFACT` | no target source | +| toolchain identity | `PARTIAL_OFFLINE_REFERENCE_ONLY` | SDK v0.41 pinned; no observer link | +| double clean build | `NOT_PERFORMED_BLOCKED_BEFORE_BUILD` | hard gates failed | +| ELF header | `NOT_APPLICABLE_NO_ARTIFACT` | no ELF | +| program headers | `NOT_APPLICABLE_NO_ARTIFACT` | no ELF | +| section headers | `NOT_APPLICABLE_NO_ARTIFACT` | no ELF | +| dynamic dependencies | `NOT_APPLICABLE_NO_ARTIFACT` | no ELF | +| imported/undefined symbols | `NOT_APPLICABLE_NO_ARTIFACT` | no ELF | +| exported symbols | `NOT_APPLICABLE_NO_ARTIFACT` | no ELF | +| relocations | `NOT_APPLICABLE_NO_ARTIFACT` | no ELF | +| linker map | `NOT_CREATED` | no link | +| reachable callgraph | `NOT_CREATED` | no code | +| disassembly | `NOT_CREATED` | no code | +| strings | `NOT_APPLICABLE_NO_ARTIFACT` | no ELF | +| denied capabilities | `NOT_APPLICABLE_NO_ARTIFACT` | no ELF | +| filesystem open flags | `NOT_APPLICABLE_NO_ARTIFACT` | no ELF | +| network functions | `NOT_APPLICABLE_NO_ARTIFACT` | no ELF | +| threads/loops | `NOT_APPLICABLE_NO_ARTIFACT` | no ELF | +| kernelwrites | `NOT_APPLICABLE_NO_ARTIFACT` | no ELF | + +The validator fails if a Phase-0.9B ELF, map, observer target, observer source +directory, installation package or lifecycle package appears while the +manifest remains blocked. + +## Transitive startup audit + +### Normal SDK link + +The SDK compiler wrapper at commit +`d2e2e585740362976a39fdd5ccf390f199a7bc37` selects +`target/lib/crt1.o` unless `-nostartfiles` is present. The CRT Makefile links: + +`crt.o`, `syscall.o`, `klog.o`, `nid.o`, `kernel.o`, `rtld.o`, `rtld_so.o`, +`rtld_sprx.o`, `rtld_payload.o`, `rtld_dlfcn.o`, `mdbg.o`, and `patch.o`. + +The installed `crt1.o` is 112256 bytes with SHA-256 +`0e4c1e5e38f22026580e856238a53e75d9e112f99a34bfe5495a3a0ffac15238`. +It was inspected as source-bound evidence and was not linked into a new +artifact. + +Reachable sequence from normal `_start`: + +```text +_start + -> clear BSS + -> payload_init + -> __crt_syscall_init + -> __kernel_init + -> __klog_init + -> write __isthreaded + -> __patch_init + -> patch_kernel_ucred + -> kernel_set_ucred_caps + -> kernel_set_ucred_attrs + -> patch_syscall_permissions + -> kernel_copyin syscall low bound + -> kernel_copyin syscall high bound + -> __rtld_init + -> payload_run + -> constructors + -> main + -> destructors + -> payload_terminate +``` + +Result: **UNSAFE** for the Phase-0.9B kernelwrite-free contract. + +### Custom entry + +`-nostartfiles -nodefaultlibs` can remove `crt1.o` and default libraries. +However, compile/link capability alone does not establish safe behavior. +elfldr `elfldr_prepare_exec()` shows `RDI=args`, `RIP=entry`, and the old RIP +stored on the adjusted stack. No source establishes the complete observer +post-return cleanup, process-exit, crash, or resource-lifetime contract for a +new bare entry. Result: **UNPROVEN**. + +## Output audit + +Controlled elfldr call path: + +```text +controlled manager + -> connect_loopback + -> send_all(control header) + -> send_all(ELF bytes) + -> close_socket + +elfldr controlled route + -> payload_read_controlled + -> payload_spawn + -> elfldr_spawn(stdio = -1) + -> no stdio duplication + -> watchdog/reap + -> fixed "Controlled payload exited" loader response +``` + +There is no manager receive callback and no payload-provided response body. +Result: **NO PROVEN NON-PERSISTENT OUTPUT CHANNEL**. + +## Existing loader effects versus observer effects + +The hardened loader performs ptrace, credential, mapping, socket-overlap, +breakpoint, protection and cleanup operations before and around entry. Those +are existing loader effects, not code introduced by an observer artifact. +They are still relevant to the risk of a later execution and do not make the +observer’s own startup kernelwrite-free. + +Because no observer was built: + +- observer imports: none to report; +- observer filesystem reads: none implemented; +- observer network functions: none implemented; +- observer threads or loops: none implemented; +- observer prohibited capabilities: none implemented; and +- absence of an artifact is not hardware-safety proof. + +## Reproducibility + +Build 1 hash: `null`. + +Build 2 hash: `null`. + +Final artifact hash/size: `null` / `null`. + +Result: `NOT_PERFORMED_BLOCKED_BEFORE_BUILD`, not +`REPRODUCIBILITY_PASSED`. diff --git a/docs/runtime/phase-0.9c-capability-closure.md b/docs/runtime/phase-0.9c-capability-closure.md new file mode 100644 index 0000000..b51d89f --- /dev/null +++ b/docs/runtime/phase-0.9c-capability-closure.md @@ -0,0 +1,103 @@ +# Phase 0.9C capability closure + +Status: **NO TARGET CAPABILITY CLOSED**. + +“Host evidence” below means a deterministic model or source validator. It is +not PS5 evidence. `implementation allowed` and `execution allowed` are false +for every capability because the startup/exit, output, firmware-source, and +side-effect gates fail before target implementation. + +## Closure matrix + +| Capability | Source evidence | Host evidence | Target evidence | Implement allowed | Execute allowed | Remaining blocker | +|---|---|---|---|---:|---:|---| +| runtime self-identity | future protocol binds observer version and artifact SHA-256 | framing validates both | absent | false | false | no target artifact or runtime self-binding | +| firmware source 1 | SDK `kernel_get_fw_version`, `crt/kernel.c:148-170` | protocol binds raw value | absent | false | false | depends on prohibited runtime and is not independent system identity | +| firmware source 2 | export-stub name only, no accepted signature/semantics | missing/conflict tests fail closed | absent | false | false | independent accepted source absent | +| mount query | SDK `samples/mntinfo` and libc wrapper | unsupported can be represented | absent | false | false | ABI, completeness, runtime behavior and effects unproven | +| metadata | controlled manager no-follow pattern | result framing can represent error | absent | false | false | target metadata/no-follow semantics and effects unproven | +| object ID | manager compares `st_dev/st_ino` | completeness rules modeled | absent | false | false | stable target identity and race contract unproven | +| size | manager compares exact `st_size` | checked result length modeled | absent | false | false | current object and stable-read evidence absent | +| SHA-256 | bounded same-FD manager implementation | checksum corruption is rejected | absent | false | false | target read effects, current paths and stable object absent | +| processes | SDK `samples/ps` partial snapshot | unsupported result modeled | absent | false | false | complete ABI, bounds and semantics absent | +| services | no complete source/API mapping | unsupported result modeled | absent | false | false | service inventory and ownership semantics absent | +| listeners | no accepted snapshot/owner API | unsupported result modeled | absent | false | false | listener API, ownership and side effects absent | +| autoload/startup/retry | stock manager readers/HTTP are incomplete or side-effecting | missing source remains blocked | absent | false | false | complete authoritative sources and safe collector absent | +| rollback objects | Phase 0.8R/0.9A evidence contract only | no false promotion in validators | absent | false | false | current exact backups absent; manager backup is hard blocker | +| monotonic time | hardened loader uses `CLOCK_MONOTONIC` | deadline expiry is rejected | absent in observer ABI | false | false | loader exposes no clock callback; freestanding callable ABI absent | +| startup | SDK and hardened loader callgraphs | startup classifications validated | absent | false | false | normal CRT writes; freestanding dependency closure incomplete | +| output | current route proven send-only; D1 host contract defined | 4096-byte framing and negative tests | absent | false | false | no caller-owned target buffer/copy-out/manager delivery | +| normal exit | loader has wait/reap; status discarded | no safe exit terminal modeled | absent | false | false | return/exit semantics and teardown unproven | +| error exit | CRT error/longjmp/trap and loader reap reviewed | observer error cannot equal empty success | absent | false | false | partial unwind, status and cleanup unproven | +| timeout | loader SIGTERM/SIGKILL watchdog reviewed | timeout record is rejected | absent | false | false | kill/timeout is not an admissible safe exit | +| cleanup | pre-detach cleanup state exists; post-detach relies on process lifetime | failed/incomplete cleanup rejected | absent | false | false | mappings, FDs, buffers, loader and manager cleanup not jointly proven | +| recovery independence | Phase 0.9A contract | blocker preserved | absent | false | false | recovery executor and exact rollback objects unproven | + +## Firmware-source closure + +Current source 1 reads a process-parameter field associated with +`libSceLibcInternal`; its own comment says it is chosen because some payloads +modify the kernel-reported value. That makes it useful source evidence, but +not an independent second current device identity. + +The SDK stubs contain +`sceKernelGetProsperoSystemSwVersion`, but a stub exports only a name/NID +surface. The repository has no reviewed public prototype, result layout, +return semantics, side-effect contract, or firmware-9.60 observation. It is +not callable evidence and is not source 2. + +A future source 2 must: + +1. be official/reproducible public platform evidence or a separately reviewed + locally obtained runtime value; +2. have exact function/data ABI and side-effect evidence; +3. identify the current system, not the observer's compiled SDK requirement; +4. produce an exact raw value under the same deadline; +5. be independent of source 1's data origin; +6. bind source identity, raw result, nonce, request ID, observer artifact hash + and deadline into the result record; and +7. fail closed if absent, ambiguous, stale, or conflicting. + +The host protocol returns +`BLOCKED_FIRMWARE_SOURCE_2_ABSENT` for absence and +`BLOCKED_FIRMWARE_CONFLICT` for disagreement. It never normalizes a conflict +away or prefers one source. + +## Cross-gate closure + +The following dependencies form one conjunctive gate: + +```text +kernelwrite-free usable startup + + proven stack/relocation/BSS/TLS closure + + callable bounded observation primitives + + two bound firmware sources + + bounded transient output + + normal/error/deadline exit without kill + + complete cleanup and unambiguous status + + bounded accepted observation effects + = target implementation may be reconsidered +``` + +Every term is required. A passing host protocol cannot compensate for a +missing exit ABI. A proven read algorithm cannot compensate for an absent +output channel. Two firmware strings cannot compensate for an unreviewed ABI +or stale execution binding. + +## Decision + +Closed for host design: + +- startup graph classifications are explicit; +- the freestanding missing dependencies are explicit; +- D1 framing is fixed at 4096 bytes and fails closed; +- stale, duplicate, incomplete, corrupt, timed-out and conflicting records + are rejected; and +- every capability has an explicit blocker. + +Not closed for target implementation: + +- all 21 capabilities in the matrix. + +The capability result contributes to +`BLOCKED_MULTIPLE_FOUNDATIONAL_CONTRACTS`. diff --git a/docs/runtime/phase-0.9c-output-channel-feasibility.md b/docs/runtime/phase-0.9c-output-channel-feasibility.md new file mode 100644 index 0000000..f755327 --- /dev/null +++ b/docs/runtime/phase-0.9c-output-channel-feasibility.md @@ -0,0 +1,268 @@ +# 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. diff --git a/docs/runtime/phase-0.9c-side-effect-model.md b/docs/runtime/phase-0.9c-side-effect-model.md new file mode 100644 index 0000000..b0b71f3 --- /dev/null +++ b/docs/runtime/phase-0.9c-side-effect-model.md @@ -0,0 +1,108 @@ +# Phase 0.9C observation side-effect model + +Status: **OBSERVATION SIDE EFFECTS UNBOUNDED**. + +This is an offline classification. No observation ran on a PS5. A source-level +`read`, `O_RDONLY`, metadata query, syscall, or manager request is never +promoted to side-effect-free merely because project code does not write file +content. + +## Distinct effect dimensions + +| Dimension | Meaning in this review | Required proof for absence | +|---|---|---| +| content | bytes of the observed object do not change | exact operation and all callees contain no content mutation | +| metadata | timestamps, flags, ownership, allocation or namespace state do not change | target filesystem and syscall semantics for the exact mount | +| atime | access time is not updated immediately or lazily | exact mount policy plus operation semantics | +| audit | platform audit/security/logging does not record the access | complete platform policy and service path | +| cache | page, name, attribute, service or network caches do not change | complete kernel/service implementation or an accepted bounded effect | +| counters | syscall, network, service, kernel or security counters do not change | complete implementation and counter inventory | +| service state | flags, wakeups, queues, worker state or functional service state do not change | exact service source and all invoked dependencies | +| security monitoring | access does not trigger security/accounting observation | exact policy and monitor implementation | +| open bookkeeping | descriptor tables, vnode references, locks, access records and reference counts remain unchanged | exact open/query semantics and close cleanup | +| process accounting | CPU time, scheduler data, resource usage and process records remain unchanged | platform accounting contract | +| object lifetime | an open/reference cannot delay deletion, replacement, unmount or cleanup | exact reference and namespace semantics | +| races | the observed identity cannot change between validation and read | stable-handle and concurrent-mutation contract | + +Absence of a persistent content write proves none of the other rows. + +## Classification vocabulary + +Every proposed observation is labelled with one or more of: + +- `SEMANTICALLY_READONLY`; +- `METADATA_EFFECT_POSSIBLE`; +- `ATIME_EFFECT_POSSIBLE`; +- `AUDIT_EFFECT_POSSIBLE`; +- `CACHE_EFFECT_POSSIBLE`; +- `COUNTER_EFFECT_POSSIBLE`; +- `SERVICE_STATE_EFFECT_POSSIBLE`; +- `SECURITY_MONITORING_EFFECT_POSSIBLE`; +- `OPEN_BOOKKEEPING_EFFECT_POSSIBLE`; +- `PROCESS_ACCOUNTING_EFFECT_POSSIBLE`; +- `OBJECT_LIFETIME_EFFECT_POSSIBLE`; +- `OBJECT_RACE_POSSIBLE`; +- `NOT_PROVABLY_SIDE_EFFECT_FREE`. + +`SEMANTICALLY_READONLY` means only that the requested information flow does +not intentionally alter the observed value. Every row below remains +`NOT_PROVABLY_SIDE_EFFECT_FREE`. + +## Observation matrix + +| Observation | Application intent | Possible effects | Source boundary | Result | +|---|---|---|---|---| +| runtime self-identity | compare in-memory fixed identity | cache, audit, process accounting, loader mapping already exists | no target implementation | `NOT_PROVABLY_SIDE_EFFECT_FREE` | +| firmware source 1 | read `sdk_ps5_ver` process parameter | syscall/dynamic-member lookup, cache, audit, counters, process accounting | SDK `crt/kernel.c:148-170`; requires prohibited normal runtime dependencies | `NOT_PROVABLY_SIDE_EFFECT_FREE` | +| firmware source 2 | independently query current system firmware | all query ABI and effects unknown | only an export name exists; no accepted ABI | `UNPROVEN` | +| mount query | enumerate mount state | cache, audit, counters, locks, reference lifetime, snapshot races | SDK sample only; runtime semantics unproven | `NOT_PROVABLY_SIDE_EFFECT_FREE` | +| file metadata | no-follow stable-object metadata | metadata/access bookkeeping, cache, audit, reference lifetime, races | manager host/source pattern only | `NOT_PROVABLY_SIDE_EFFECT_FREE` | +| object ID and size | compare pre/post identity | same as metadata plus concurrent replacement race | manager `verified_launcher.c:26-29,83-115,174-176` | `NOT_PROVABLY_SIDE_EFFECT_FREE` | +| file SHA-256 | bounded descriptor read | atime, metadata, page cache, audit, counters, open bookkeeping, lifetime, races | manager `verified_launcher.c:31-55`; PS5 semantics unproven | `NOT_PROVABLY_SIDE_EFFECT_FREE` | +| process snapshot | enumerate process records | kernel allocation/cache/counters, scheduler/accounting, snapshot races | SDK process sample is partial | `NOT_PROVABLY_SIDE_EFFECT_FREE` | +| service snapshot | map services and supervisors | service queries, queues/counters/cache/audit and races | no complete API or mapping | `UNPROVEN` | +| listener snapshot | enumerate sockets and owners | network counters/cache/locks, service state, owner mapping races | no accepted API | `UNPROVEN` | +| autoload/startup/retry | read all authoritative sources | atime/metadata/cache/audit/open state; stock HTTP also changes global/autoload state | Phase 0.8R source audit | `NOT_PROVABLY_SIDE_EFFECT_FREE`; stock route is side-effecting | +| rollback objects | metadata and hash existing backups | same filesystem effects plus object lifetime and target/backup race | no current paths or objects | `UNPROVEN` | +| monotonic time | enforce deadline | clock/syscall counters, vDSO/runtime dependencies, accounting | only loader-owned clock use is source-proven | `NOT_PROVABLY_SIDE_EFFECT_FREE` for a future observer | +| result publication | write caller-owned transient buffer | caller memory mutation, cache, accounting, synchronization and lifetime | host-only concept; no target owner | `UNPROVEN` | + +## Stock Payload Manager remains inadmissible + +Phase 0.8R already proves that non-`OPTIONS` requests set +`server_active_flag`, `/autoload_status` sets `autoload_triggered`, and several +routes read configuration or mutate logging state. `OPTIONS` does not provide +the needed data and still has unproven network, allocation, audit, cache, +scheduler and counter effects. + +Phase 0.9C does not reinterpret those endpoints as a collector. A persistent +listener or HTTP response is also forbidden as observer output. + +## Filesystem-specific conclusion + +The controlled manager demonstrates a useful source pattern: + +```text +open(O_RDONLY | O_NOFOLLOW | O_CLOEXEC) +-> fstat +-> bounded read +-> fstat +-> lstat path +-> compare identity/size +-> close +``` + +That pattern can detect several races in the reviewed source. It does not +prove no atime update, no audit record, no page-cache fill, no vnode/reference +bookkeeping, no process accounting, or no platform-specific service effect. +It also cannot make a hash atomic with respect to every target namespace race +without proven filesystem and stable-object semantics. + +## Decision + +No planned observation is proven side-effect-free on firmware 9.60. A later +phase would need an explicit bounded-effect budget rather than an absolute +zero-effect claim, plus exact public ABI/source evidence and separately +authorized runtime observation. Phase 0.9C grants neither. + +Current blocker: `BLOCKED_OBSERVATION_SIDE_EFFECTS_UNBOUNDED`. diff --git a/docs/runtime/phase-0.9c-startup-exit-feasibility.md b/docs/runtime/phase-0.9c-startup-exit-feasibility.md new file mode 100644 index 0000000..b18c237 --- /dev/null +++ b/docs/runtime/phase-0.9c-startup-exit-feasibility.md @@ -0,0 +1,289 @@ +# Phase 0.9C startup and exit feasibility + +Status: **BLOCKED**. This is a source-bound offline analysis. It does not add +an observer, target source, PS5 object, ELF, linker map, installation package, +lifecycle package, transfer path, or execution authority. + +## Evidence boundary + +The exact reviewed sources are: + +| Component | Commit | Relevant file identity | +|---|---|---| +| hardened elfldr | `197623058f509eddde18868dafcb92fdcac66464` | `elfldr.c` SHA-256 `9949f8e4037984d10f1f5aa498e4665593d1fac8a33614d7f2141349839bb803` | +| hardened elfldr controlled route | same | `socksrv.c` SHA-256 `d642ced3e9b4a296dd15e355050ebe956f53a6dfdaa6ac10109cd067a3bba3d7` | +| hardened cleanup model | same | `controlled_runtime.c` SHA-256 `10145f3bbb3b54e3d715b1667e45c6f12d3c7f52a04342ab104ec8dd49e384c9` | +| hardened PS5 cleanup adapter | same | `ps5_controlled.c` SHA-256 `68717ef1cc31c483743c5af325c59e73a90f66e93b880b989e349a4ddc748772` | +| controlled Payload Manager | `e23d94ff91233aa770e2342800c1467875bdef44` | `verified_launcher.c` SHA-256 `066100ca4917c7acc560e2e85666ca136cd7ccfd9094417377048f41106dd56e` | +| controlled manager adapter | same | `ps5_launcher.c` SHA-256 `29c1a5fd01784a59e88b3698940f120cb03020071bc2b7d74a1da1a51524ef59` | +| PS5 Payload SDK v0.41 | `d2e2e585740362976a39fdd5ccf390f199a7bc37` | `crt/crt.c` SHA-256 `3875f4739ec40b33f1f4967a1acbb585a527d2c281a360153e1f69c8b945932a` | +| SDK patch implementation | same | `crt/patch.c` SHA-256 `4f76a677bba54f4641e1cf2755768c29afb7464a35b2f21e85db2aa2785eceac` | +| SDK CRT object set | same | `crt/Makefile` SHA-256 `6a62f777f32ab05cbe7bff81c00cfd6639ba8bf440aba611a40ff0b62732568b` | +| SDK compiler wrapper | same | `host/bin/prospero-clang` SHA-256 `0cf49ae43d6110a7606c0ee4d702fc4b5d5e1c3ae9a722945a48b80294e295ba` | +| SDK linker script | same | `host/elf_x86_64.x` SHA-256 `169b80d01da601ef96bbc584986608dec1d9c01397eae81eccd4e1a66b0a6c6a` | + +The public bases remain elfldr +`699e8bcff03e91e8d6ca6eba281af25c5a58d8c2` and Payload Manager +`cfbc70f30f419b09bf2b52283f7409e2d3117ee1`. The lifecycle source remains +bound to commit `fe08300339a13f899fb78ea404ada381a5cba87c`; its unchanged +`samples/lifecycle_probe/main.c` is +`1ae7df1fe921ccab2a252f77975d3d441ef7725e34535b024580c0d4a242d766`. +None of these historical target sources was modified. + +Classifications in this document mean: + +- `PROVEN_SAFE`: the cited source proves the narrow property stated; +- `PROVEN_SIDE_EFFECTING`: the cited source proves a state change or external + operation, whether or not an earlier phase accepted it; +- `UNPROVEN`: evidence required for the claimed contract is absent; +- `NOT_APPLICABLE`: the path deliberately excludes the facility. + +`PROVEN_SAFE` never means hardware-tested. + +## Loader-to-entry call graph + +The controlled route has this source-proven graph: + +```text +controlled manager +|- open_verified +| |- open(O_RDONLY | O_NOFOLLOW | O_CLOEXEC) +| |- fstat -> bounded read/SHA-256 -> fstat +| `- lseek(fd, 0) +|- connect_loopback +|- send_all(control header) +|- send_all(exact ELF bytes) +`- close_socket + +hardened elfldr on_connection +|- recv/validate controlled header +|- recv exact payload bytes -> independent SHA-256 +|- payload_spawn(route=CONTROLLED) +| `- elfldr_spawn(stdio=-1) +| |- rfork_thread -> child execve(SceSpZeroConf) +| |- wait for exec/stop under finite preparation waits +| |- pt_syscall(process-needed-and-relocate) +| |- change child heap-size parameter +| |- make entry page RWX +| |- install INT3 -> continue -> stop +| |- restore byte and RX protection +| |- map payload PT_LOAD image +| |- apply R_X86_64_RELATIVE relocations +| |- map payload_args_t page +| |- create two IPv6 sockets, overlap them, and create a pipe +| |- back up child credentials +| |- raise child credentials +| |- write saved RIP to [RSP-8] +| |- set RSP=RSP-8, RIP=ELF entry, RDI=payload_args_t* +| |- restore all five credential fields +| `- ptrace detach +|- watchdog wait/reap +| `- on timeout: SIGTERM -> grace -> SIGKILL -> reap +`- write fixed loader-generated exit text +``` + +Sources: hardened elfldr `socksrv.c:129-157,162-215,289-375`; +`elfldr.c:80-265,273-415,480-545,643-769`; +`controlled_runtime.c:60-92,95-143,146-220,223-250`; and +`ps5_controlled.c:26-59,132-220`. The manager side is +`verified_launcher.c:31-184`, `include/verified_launcher.h:38-42`, and +`ps5_launcher.c:18-69`. + +### Entry contract classification + +| Property | Classification | Evidence and consequence | +|---|---|---| +| entry symbol comes from `e_entry` | `PROVEN_SAFE` | `elfldr.c:143-265` returns `base + e_entry`. | +| first argument in `RDI` | `PROVEN_SAFE` | `elfldr.c:401-409` sets `r_rdi` to the loader-created argument page. | +| synthetic return address | `PROVEN_SIDE_EFFECTING` | `elfldr.c:401-407` writes the observed RIP at `RSP-8` and moves `RSP`; the child stack is changed. | +| x86-64 stack alignment at entry | `UNPROVEN` | the loader subtracts eight but never validates the trapped `RSP` alignment. | +| meaning and safety of saved RIP | `UNPROVEN` | it is the RIP observed at the breakpoint; no reviewed contract proves the post-`ret` continuation. | +| preserved general/SIMD register set | `UNPROVEN` | source changes `RSP`, `RIP`, and `RDI` but defines no caller/callee preservation contract for a custom entry. | +| argument layout | `PROVEN_SAFE` | SDK `include/ps5/payload.h:27-36` and elfldr `elfldr.c:349-367` agree on six x86-64 fields. | +| argument ownership | `PROVEN_SIDE_EFFECTING` | elfldr maps and populates the page in the child at `elfldr.c:273-372`. | +| argument lifetime after return or exit | `UNPROVEN` | success clears the loader cleanup bitmap at `elfldr.c:538-545`; the mapping then relies on child lifetime/OS teardown. | +| image mapping | `PROVEN_SIDE_EFFECTING` | elfldr allocates child mappings, copies bytes, changes protection, and calls `msync` at `elfldr.c:143-265`. | +| relocation support | `PROVEN_SAFE` only for `R_X86_64_RELATIVE` | `elfldr.c:208-222`; no complete freestanding artifact exists whose relocations can be checked. | +| BSS zero-fill by loader | `UNPROVEN` and source-negative | `base_mirror` comes from `malloc`; `data_load` copies only `p_filesz`, then the entire mirror is copied. No zero-fill is visible at `elfldr.c:96-109,183-224`. | +| TLS setup | `UNPROVEN` | elfldr has no reviewed TLS setup in this path; no future observer layout exists. | +| constructors/destructors | `NOT_APPLICABLE` to a proposed freestanding entry | they are a stock CRT facility and are forbidden for the proposed path. | + +## Normal SDK v0.41 startup + +The exact normal graph is: + +```text +_start(payload_args_t *) +|- clear [__bss_start, __bss_end) +|- store payload_args +|- payload_init +| |- __crt_syscall_init +| |- __kernel_init +| |- __klog_init +| |- resolve __isthreaded and write 1 +| |- __patch_init +| | |- get process/ucred state +| | |- write credential capability bytes +| | |- write credential attribute bytes +| | |- write syscall lower bound 0 +| | `- write syscall upper bound -1 +| `- __rtld_init +|- setjmp +|- payload_run +| |- resolve argc/argv/environ/progname +| |- create/open payload rtld object +| |- load DT_NEEDED dependencies and relocate +| |- run init array +| |- main +| |- run fini array +| `- close/destroy rtld object +`- payload_terminate + |- return 0 for the runtime's detected hijacked-process case + |- resolve and call exit(payloadout) + `- trap if exit resolution/call returns +``` + +Sources: SDK `crt/crt.c:30-44,51-80,88-146,154-180,197-220`; +`crt/patch.c:27-100`; `crt/rtld_payload.c:51-60,203-260`; and +`crt/Makefile:19-26,50-54`. + +| Step | Classification | Reason | +|---|---|---| +| BSS clear | `PROVEN_SIDE_EFFECTING` | writes every byte in the payload BSS; deterministic and process-local, but still a mutation. | +| global argument/setjmp state | `PROVEN_SIDE_EFFECTING` | writes CRT BSS state. | +| syscall/kernel/klog bootstrap | `PROVEN_SIDE_EFFECTING` | consumes loader-provided kernel transport and initializes global function/state. | +| `__isthreaded = 1` | `PROVEN_SIDE_EFFECTING` | writes resolved libc state at `crt.c:65-69`. | +| `__patch_init` | `PROVEN_SIDE_EFFECTING` and prohibited | exact capability, attribute, and syscall-bound kernel writes occur before `main`. | +| rtld initialization | `PROVEN_SIDE_EFFECTING` | resolves functions, allocates bookkeeping, and can load/start Sce modules. | +| relocation and `DT_NEEDED` processing | `PROVEN_SIDE_EFFECTING` | writes relocations and opens dependencies. | +| constructors | `PROVEN_SIDE_EFFECTING` | arbitrary linked constructor effects are permitted by this path. | +| heap use | `PROVEN_SIDE_EFFECTING` | stock rtld resolves and uses allocation functions. | +| `main` | `UNPROVEN` for an observer | no Phase-0.9C target implementation exists. | +| destructors and rtld close | `PROVEN_SAFE` only as attempted normal-path calls | no proof covers partial initialization, longjmp, trap, crash, or module-internal cleanup. | +| `payload_terminate` branch selection | `UNPROVEN` | the actual branch and its platform semantics are not bound to an observer execution. | + +The normal SDK startup is therefore **not kernelwrite-free** and is unsuitable +for Phase 0.9C. The fact that Phase 0.7 accepted its bounded, process-local +effects for a different lifecycle profile does not satisfy this phase's +stricter contract. + +## Error and abnormal paths + +### Initialization error + +`_start` writes the error to `payloadout` and calls `payload_terminate` +(`crt.c:207-210`). A failure after credential capabilities were written but +before all patch writes finish has no CRT restoration path. A failure during +rtld initialization has no demonstrated full unwind in `_start`. + +Classification: `PROVEN_SIDE_EFFECTING` for partial startup and `UNPROVEN` for +complete restoration and termination. + +### Runtime error + +`payload_run` attempts close/destroy on some open/init/fini failures +(`crt.c:116-146`). These are source-proven calls, not proof of module, heap, +TLS, file-descriptor, or process cleanup on firmware 9.60. + +Classification: `PROVEN_SAFE` for the presence of the attempted calls; +`UNPROVEN` for complete cleanup. + +### `payload_exit` + +`payload_exit` writes `payloadout` and longjmps to `_start` +(`crt.c:175-180`). The longjmp leaves `payload_run`; `_start` proceeds directly +to `payload_terminate`, so the normal fini/close/destroy sequence is not on +that control path. + +Classification: `PROVEN_SIDE_EFFECTING`; full cleanup is `UNPROVEN`. + +### Trap, crash, unexpected return, and timeout + +- `payload_terminate` deliberately traps if its resolved `exit` path returns + or cannot be used (`crt.c:162-170`). +- The controlled watchdog treats any reaped child as completion but discards + the `waitpid` status (`ps5_controlled.c:36-59`). +- If the child remains alive, the watchdog uses `SIGTERM`, then `SIGKILL`, and + reaps it (`controlled_runtime.c:223-250`). +- The connection handler emits the same fixed success text after + `payload_spawn` returns a nonnegative value (`socksrv.c:361-365`). + +A trap/crash can therefore be indistinguishable from a normal exit to the +manager, and timeout termination is not a safe exit under the Phase-0.9C +rules. Classification: `UNPROVEN` for crash cleanup and status meaning; +`PROVEN_SIDE_EFFECTING` and **inadmissible** for signal termination. + +## Freestanding feasibility without target implementation + +`prospero-clang` omits `crt1.o` for `-nostartfiles` and omits libc, +`libkernel_web`, `libSceLibcInternal`, and `libSceNet` for `-nodefaultlibs` +(`host/bin/prospero-clang:25-46,69-84`). This proves driver suppression only. +No custom entry source or target was assembled or linked. + +| Required facility | Theoretical availability | Classification | +|---|---|---| +| entry address | ELF `e_entry` is transferred to `RIP` | `PROVEN_SAFE` narrow loader fact | +| valid stack memory | an existing child stack is used | `UNPROVEN` size, alignment, guard, and post-return contract | +| small automatic buffers | mechanically possible if the stack contract holds | `UNPROVEN` | +| read-only constants | PT_LOAD bytes can be mapped and protected | `PROVEN_SAFE` only for loader mapping; relocation closure is artifact-dependent | +| BSS | loader does not prove zero-fill; a custom clear is mechanically possible | `UNPROVEN` until a reviewed entry and map exist | +| TLS | no freestanding TLS contract | `UNPROVEN` | +| constructors/destructors | excluded | `NOT_APPLICABLE` | +| heap | excluded | `NOT_APPLICABLE` | +| libc/stdio | excluded by design | `NOT_APPLICABLE` | +| raw syscalls | no callable observer syscall ABI is supplied by `payload_args_t` | `UNPROVEN` | +| system calls from injected text | SDK patch widens permitted syscall address bounds | `UNPROVEN` without the prohibited patch | +| monotonic time | hardened loader has a clock, but exposes no clock callback to the payload | `UNPROVEN` in the entry | +| filesystem `open/read/fstat/close` | requires unresolved imports, a proven raw-syscall ABI, or another supplied callback | `UNPROVEN` | +| process/service/listener queries | no freestanding callable ABI is supplied | `UNPROVEN` | +| normal `ret` | mechanically reaches the saved RIP | `UNPROVEN` continuation and cleanup | +| direct process exit | would require a proven import/raw syscall and exact teardown contract | `UNPROVEN` | +| exit status | current waiter discards it | `UNPROVEN` delivery | +| output | current `payloadout` is inside the child mapping and never read by the controlled caller | `UNPROVEN` | + +The important closure result is negative: removing the CRT also removes the +only reviewed syscall, kernel, rtld, import, time, and libc initialization +machinery. The loader supplies a dynamic-symbol function pointer and kernel +transport fields, but using them would reintroduce dynamic resolution or +kernel-access dependencies that this phase excludes. No useful observation +primitive is proven available to a zero-import freestanding entry. + +## Return, exit, and cleanup contract + +| Resource/state | Normal `ret` | process exit | error/crash | timeout | +|---|---|---|---|---| +| saved stack continuation | resumes the observed RIP; meaning unproven | not used | may be bypassed | bypassed | +| ELF and argument mappings | remain in child after detach | OS reclamation expected but not source-proven for this exact contract | unproven | child is killed/reaped, which is not safe exit | +| loader-created pipe/sockets | remain in child after detach | same unproven teardown dependency | unproven | same inadmissible kill path | +| observer local buffers | stack lifetime follows unproven continuation/process semantics | teardown semantics unproven | unproven | forcibly discarded | +| file descriptors opened by a future observer | no implementation or cleanup contract | no implementation or exact teardown proof | unproven | forcibly discarded | +| threads | a future observer forbids them; existing child/runtime state remains | no target observer exists | unproven | signals affect the whole child | +| credentials | loader restores five fields before detach | source-proven attempted and checked restore | pre-detach cleanup is tracked; post-detach crash remains dependent on process teardown | watchdog does not prove observer cleanup | +| breakpoint/page | restored before payload entry | already restored | preparation failures use cleanup | already restored | +| loader bookkeeping | returns from watchdog on reap | `waitpid` status is discarded | status is ambiguous | returns timeout/failure | +| manager bookkeeping | send-only function closes its socket before a result | no result is received | indistinguishable | no result is received | +| exit status | no defined status | locally available to `waitpid` but discarded | discarded | timeout result is loader-local | +| output availability | none | child mapping gone; no copy-out | none | none | + +The current normal return path is especially unsuitable: `ret` resumes the +SceSpZeroConf continuation rather than proving process termination. The +controlled watchdog waits for process exit; if that continuation remains +alive, the only bounded endpoint is signal termination. The user explicitly +forbids treating kill, crash, timeout, or disconnect as a safe exit. + +## Decision + +No reviewed path proves all of: + +1. kernelwrite-free entry with usable observation dependencies; +2. correct stack and register ABI; +3. deterministic BSS/relocation/TLS closure; +4. normal return or process exit with exact status semantics; +5. cleanup of mappings, descriptors, stack, loader and manager state; +6. a safe error path; +7. a safe deadline path that does not rely on kill; and +8. result availability after cleanup. + +Startup is therefore `BLOCKED_STARTUP_ABI_UNPROVEN`, exit and cleanup are +`BLOCKED_EXIT_CLEANUP_UNPROVEN`, and the combined Phase-0.9C decision cannot be +positive. diff --git a/docs/runtime/phase-0.9c-static-audit.md b/docs/runtime/phase-0.9c-static-audit.md new file mode 100644 index 0000000..9a7f116 --- /dev/null +++ b/docs/runtime/phase-0.9c-static-audit.md @@ -0,0 +1,147 @@ +# Phase 0.9C static audit + +Date: 2026-07-18. Final classification: +**BLOCKED_MULTIPLE_FOUNDATIONAL_CONTRACTS**. + +This audit stops before target implementation. No observer source, assembly, +object, ELF, map, target build, lifecycle package, installation package, +autoload package, transfer template, device address, device path, or +authorization exists. + +## Exact source basis + +| Source | Commit | Tree | +|---|---|---| +| hardened elfldr | `197623058f509eddde18868dafcb92fdcac66464` | clean | +| controlled Payload Manager | `e23d94ff91233aa770e2342800c1467875bdef44` | clean | +| public elfldr v0.23 | `699e8bcff03e91e8d6ca6eba281af25c5a58d8c2` | clean | +| public Payload Manager v0.3.1 | `cfbc70f30f419b09bf2b52283f7409e2d3117ee1` | clean | +| PS5 Payload SDK v0.41 | `d2e2e585740362976a39fdd5ccf390f199a7bc37` | clean | +| lifecycle source identity | `fe08300339a13f899fb78ea404ada381a5cba87c` | unchanged file hash | + +Every reviewed file hash is recorded in +`manifests/runtime/phase-0.9c-feasibility.json`. The validator rehashes the +files and verifies all five external Git trees without network access. + +## Immutable evidence + +| Evidence | SHA-256 | +|---|---| +| Phase-0.8 document | `3fbe086175a6048176075f447ec1482074928e3b5282db97ea2169395fe1d508` | +| Phase-0.8 manifest | `47d7f452f8799979fe99b3e6d56859f03544112725bf7e5b349eba5ed81b3322` | +| Phase-0.8 original test | `8a4ad7c70de28ffe3148fd3fd1f68c36a872c53c691c9068e1ff163970863c48` | +| Phase-0.9A manifest | `39fd7c70cae998d9d74e7caf1ff3c19f9f76f7de2e5a50bc09baede4ba53e9e9` | +| Phase-0.9B manifest | `104c4a667ad17f9827fc7276852c6faeef96effb8e3561a5ebc19a62c7d51634` | +| Phase-0.9B schema | `efcea3b0001ef5b2da65c372ceb93ee2fec09c9331b2e4cbb6008212504c0918` | + +Phase 0.9B remains a truthful historical blocked audit. It was not rewritten +as if Phase 0.9C evidence existed earlier. + +## Startup and transitive code result + +### Normal SDK path + +`crt1.o` transitively includes `crt`, `syscall`, `klog`, `nid`, `kernel`, +`rtld`, `rtld_so`, `rtld_sprx`, `rtld_payload`, `rtld_dlfcn`, `mdbg`, and +`patch`. `_start` reaches `__patch_init` before `main`; credential +capabilities/attributes and syscall bounds are written. It also initializes +rtld, permits DT_NEEDED processing, relocations, constructors, heap +bookkeeping and module loading. + +Result: `PROVEN_SIDE_EFFECTING`, unsuitable for the kernelwrite-free contract. + +### Freestanding theoretical path + +The compiler can suppress CRT/default libraries, and the loader can transfer +`e_entry` and `RDI`. The reviewed source does not close: + +- stack alignment and saved-RIP continuation; +- BSS zero-fill, complete relocations, TLS, or register preservation; +- a callable read/syscall ABI without prohibited patch/runtime setup; +- monotonic time in the observer; +- normal return or process exit; +- error/deadline exit without crash/kill; +- mapping, descriptor, buffer, loader and manager cleanup; or +- result copy-out. + +Result: `BLOCKED_STARTUP_ABI_UNPROVEN` and +`BLOCKED_EXIT_CLEANUP_UNPROVEN`. + +## Output result + +| Architecture | Audit result | +|---|---| +| D1 caller-owned buffer | coherent 4096-byte host contract; no current loader buffer, return finalizer, copy-out, or manager delivery | +| D2 existing request/response | rejected; manager is connect/send/close only | +| D3 loader-owned status | unproven; wait status is discarded and no record propagates | +| D4 process status | rejected; ambiguous, discarded, and too small | + +The D1 host model validates magic/version, nonce, request ID, two firmware +fields, observer version, fixed/actual sizes, status, requested/observed/ +unsupported bitmaps, truncation, artifact hash, body/final checksums, deadline, +completion, cleanup, zero padding and one-time consumption. No target code +implements it. + +Result: `BLOCKED_NO_BOUNDED_OUTPUT_CHANNEL`. + +## Firmware and side-effect result + +SDK `kernel_get_fw_version()` is source 1 but reads a process-parameter SDK +field and is runtime-unobserved. A system-version export name in the SDK stub +has no accepted prototype or semantics and is not source 2. Result: +`BLOCKED_FIRMWARE_SOURCE_INCOMPLETE`. + +No planned observation is proven free of metadata, atime, audit, cache, +counter, service/security, open-bookkeeping, process-accounting, +object-lifetime, or race effects. Result: +`BLOCKED_OBSERVATION_SIDE_EFFECTS_UNBOUNDED`. + +## Target artifact audit disposition + +| Audit item | Result | +|---|---| +| target source/assembly | `NOT_CREATED` | +| target compile/link | `NOT_PERFORMED` | +| ELF headers/program headers/sections | `NOT_APPLICABLE_NO_ARTIFACT` | +| imports/undefined symbols/DT_NEEDED | `NOT_APPLICABLE_NO_ARTIFACT` | +| relocations/init/fini/TLS | `NOT_APPLICABLE_NO_ARTIFACT` | +| linker map | `NOT_CREATED` | +| target reachable callgraph | `NOT_CREATED` | +| target disassembly | `NOT_CREATED` | +| double target build/hash | `NOT_PERFORMED` | +| lifecycle/install/autoload package | `NOT_CREATED` | +| execution eligibility | `false` | +| execution authority | `false` | + +These are nonexistence results, not evidence that hypothetical bytes would be +safe. + +## Offline verification + +The final verification run is recorded as: + +| Check | Result | +|---|---| +| Phase-0.9A host tests | `PASS` — 41 guardrails, 33 fault types for each of 2 components, and 14 power-loss boundaries | +| Phase-0.9A hardened-elfldr virtual fault suite | `PASS` — 33 fault results and 14 power-loss results; host simulation only, no hardware evidence, connection, or execution | +| Phase-0.9A controlled-manager virtual fault suite | `PASS` — 33 fault results and 14 power-loss results; host simulation only, no hardware evidence, connection, or execution | +| Phase-0.9B validator and host tests | `PASS` — blocked-audit validator and artifact guardrails | +| Phase-0.9C protocol cases | `PASS` — 21/21 | +| Phase-0.9C feasibility cases | `PASS` — 12/12 | +| full host configure/build/CTest | `PASS` — 22/22 | +| manifests/schema/safety/denylist/secrets/immutable/checksums | `PASS` — all dedicated validators and regression guards | +| `git diff --check` | `PASS` — no output | + +The committed version of this document must contain no pending result. + +## Decision + +The only permitted positive classification required every foundational +contract to be proven. Five independent blocker families remain. The exact +decision is: + +`BLOCKED_MULTIPLE_FOUNDATIONAL_CONTRACTS` + +No automatic Phase 0.9D transition is permitted. Reconsideration requires new +public, commit-bound evidence; it does not authorize an experimental target +run to discover the missing contracts. diff --git a/docs/runtime/phase-0.9d-bootstrap-recovery-chain.md b/docs/runtime/phase-0.9d-bootstrap-recovery-chain.md new file mode 100644 index 0000000..b87c13d --- /dev/null +++ b/docs/runtime/phase-0.9d-bootstrap-recovery-chain.md @@ -0,0 +1,107 @@ +# Phase 0.9D bootstrap and recovery chain + +Date: 2026-07-18 +Status: `DESIGN_ONLY` + +This is an offline source audit. It does not establish current device state, +firmware behavior, persistence, a live path, or recovery success. +No PS5 was contacted. + +## Source-bound chain + +```mermaid +flowchart LR + A["External jailbreak / exploit host
source absent from audited trees"] + B["Rudimentary exploit ELF loader
port 9020 per elfldr README"] + C["elfldr bootstrap
bdj.elf -> SceRedisServer"] + D["elfldr socket service
elfldr.elf, port 9021"] + E["Payload Manager ELF
manual or external autoloader"] + F["Payload Manager HTTP service"] + + A --> B --> C --> D --> E --> F +``` + +The public and hardened elfldr `README.md:3-19` describe host-to-console +delivery to a rudimentary exploit loader on port 9020. The exploit and its host +implementation are not present in any audited tree, so its repeatability and +recovery behavior are unproven. Hardened elfldr `main.c:28-89` enters through +`bdj.elf`, temporarily attaches to `SceRedisServer`, and +`bootstrap.c:38-81` starts the socket server. `socksrv.c:477-507` names that +service `elfldr.elf`, removes an already running process with that name, and +listens on port 9021. + +The Payload Manager README describes either manual loading or placement by an +external autoloader. Its `deploy.sh` only sends an ELF to elfldr; it does not +install, read back, or recover a device file. The external autoloader, +jailbreak host, browser exploit, and any USB recovery implementation are absent +from the audited sources. + +## Dependency and recovery graph + +| Component | Commit | Persistence | Start mechanism | Boot critical | Failure impact | Recovery executor | Self-dependent | Depends on elfldr | Depends on Manager | After reboot | Evidence / classification | +|---|---|---|---|---|---|---|---:|---:|---:|---|---| +| Jailbreak/exploit host | not present | `UNPROVEN` | user/browser/host, details absent | `UNPROVEN` | bootstrap unavailable if absent | itself/external | unknown | no | no | `UNPROVEN` | README description only; `UNPROVEN` | +| Rudimentary port-9020 loader | not present | session behavior `UNPROVEN` | exploit chain | `UNPROVEN` | elfldr cannot be bootstrapped through the described route | external exploit chain | unknown | no | no | `UNPROVEN` | README description only; `UNPROVEN` | +| Hardened elfldr bootstrap | `197623058f509eddde18868dafcb92fdcac66464` | source shows process injection, not filesystem installation | sent to port 9020 | no source proof | no port-9021 service | port-9020 chain, if still available | no | no | no | `UNPROVEN` | source plus missing upstream executor; `PARTIAL` | +| elfldr socket service | same | process-local in source | bootstrap creates `elfldr.elf` | no source proof | no normal Payload Manager launch transport | bootstrap | no | no | no | `UNPROVEN` | source; `PARTIAL` | +| Public/full Payload Manager | public base `cfbc70f30f419b09bf2b52283f7409e2d3117ee1` | external autoloader or manual, exact current deployment unknown | elfldr or external autoloader | `UNPROVEN` | management UI/listener unavailable | elfldr/manual route | no | yes | no | `UNPROVEN` | README/source; `PARTIAL` | +| Controlled minimal Manager source | `e23d94ff91233aa770e2342800c1467875bdef44` | explicitly not installed by the Phase-0.7 record | would require a separately authorized transfer and launch | no | no impact on current device because installation is unproven | none in this phase | no | yes | no | not installed | source/offline record; `UNPROVEN` as device runtime | + +## Bootstrap questions + +1. **Original exploit start:** only described as a BD-J or WebKit-style host + route to a rudimentary loader; implementation and current operator sequence + are absent. `UNPROVEN`. +2. **Direct browser/exploit delivery:** the elfldr README proves only that an + elfldr ELF is sent host-to-console. Exact browser assets are absent. +3. **elfldr storage:** source proves a process service, not a persistent + filesystem installation. Current storage is `UNPROVEN`. +4. **Payload Manager storage:** README permits manual or external-autoloader + use; actual current storage is `UNPROVEN`. +5. **Automatic start:** controlled by an external autoloader and/or Manager + configuration not present as current device evidence. +6. **Manual-only files:** any ELF sent to elfldr can be manually launched in + principle; current availability is not proven. +7. **Gone after reboot:** process state and volatile flags are source-local, + but device restart behavior of the complete chain is `UNPROVEN`. +8. **Persistent after reboot:** no current artifact or configuration is proven + persistent. +9. **Exploit after broken elfldr:** conceptually independent in the README, + but source and current operability of the original exploit are absent: + `PARTIAL`. +10. **Exploit after broken Manager:** same `PARTIAL` result. +11. **Independent recovery payload:** the README permits sending another ELF + through port 9020, but neither the executor nor a recovery payload is + audited: `PARTIAL`, not recovery proof. +12. **Write-back through original chain:** no source-bound file-write contract + is present: `UNPROVEN`. +13. **Ignore a wrong non-autoload file:** plausible, but live path and + configuration are unobserved: `UNPROVEN`. +14. **Boot-loop potential:** depends on unknown persistent autoload state: + `UNPROVEN`. +15. **Automatic-start configuration:** external autoloader configuration is + absent. Manager uses `/data/pldmgr/autoload.txt` and + `/data/pldmgr/pldmgr_config.txt`, but this does not prove the current + bootstrap configuration. +16. **Keep automatic start disabled:** design policy requires it; actual + current state has no non-mutating source-bound attestation. +17. **Recovery after reboot:** external exploit restart is only described: + `PARTIAL`. +18. **Recovery after crash/hang:** elfldr source restarts its accept loop and + uses a controlled watchdog, but recovery of a broken persistent file is + absent. +19. **USB/browser/host independent path:** upload and launch paths exist; + no audited PS5-to-host readback or independent restore path exists. +20. **Unproven answers:** current persistence, live paths, installed versions, + autoload state, exploit repeatability, boot-loop behavior, write-back, + power-loss recovery, and post-reboot recovery all remain hardware facts. + +## Recovery classification + +- elfldr: `PARTIAL`. The described port-9020 bootstrap does not depend on the + port-9021 elfldr service, but its implementation and current operability are + missing. +- Payload Manager: `PARTIAL` with a `CROSS_DEPENDENT` restore shape. Manual + launch through elfldr is described, but durable restoration and verification + are absent. +- Byte-exact file recovery for either component: `UNPROVEN`. diff --git a/docs/runtime/phase-0.9d-existing-stack-endpoint-matrix.md b/docs/runtime/phase-0.9d-existing-stack-endpoint-matrix.md new file mode 100644 index 0000000..71ef9ad --- /dev/null +++ b/docs/runtime/phase-0.9d-existing-stack-endpoint-matrix.md @@ -0,0 +1,123 @@ +# Phase 0.9D existing-stack endpoint matrix + +Date: 2026-07-18 +Status: `DESIGN_ONLY` + +## Scope and common behavior + +The full-profile matrix is bound to controlled Payload Manager commit +`e23d94ff91233aa770e2342800c1467875bdef44`, +`include/pldmgr.h:8-40` and `src/http_server.c:188-1181`. Its public-base +equivalent is commit `cfbc70f30f419b09bf2b52283f7409e2d3117ee1`; +the public base lacks the two `chimera` routes. The separately compiled minimal +controlled profile is in `src/controlled_manager.c:181-334`. + +Unless a row says otherwise: + +- authentication is absent and full-profile responses add permissive CORS; +- every non-`OPTIONS` full-profile request writes the process-local + `server_active_flag` (`http_server.c:207-208`); +- non-noisy requests also append stdout/ring log state + (`http_server.c:638-641`); +- handler responses are bounded in-memory text/JSON/static assets, not an + arbitrary-file response; +- there is no automatic server-side retry, but client/UI retry is not a safe + property of the route; +- exact timeouts and maximum request/response sizes are `UNPROVEN` unless + explicitly bounded in the handler; +- no row is a readback candidate. + +`ANY` means the handler branch itself does not enforce a verb. This is not a +recommendation to call it. + +## Full-profile routes + +| Method | Exact endpoint | Handler lines | Parameters / response | File and functional effects | Classification | +|---|---|---:|---|---|---| +| `OPTIONS` | any | 193-205 | empty CORS preflight | no active flag; sockets/counters only | observation false; not file readback | +| `ANY` | `/`, `/index.html`, `/cache.appcache`, `/favicon.svg`, `/icon.png` | 709-733 | compiled static bytes | no file open; active flag | `LOW_VOLATILE`, observation partial | +| `POST` | `/chimera:upload-controlled` | 212-252, 481-537 | query filename plus identity headers; text | exclusive write flags, then verified `O_RDONLY|O_NOFOLLOW|O_CLOEXEC` reopen/hash, fsync, rename, metadata write | forbidden: create/write/rename | +| `ANY` | `/manage:upload` | 254-286, 540-585 | query filename, request body; text | `fopen("wb")`, write, rename/import, metadata | forbidden: create/truncate/write/rename | +| `POST` | `/set_config` | 288-295, 363-398 | form body; text | reads current config, then config/autoload files can use `fopen("w")` | forbidden: configuration write | +| `POST` | `/repository_push` | 297-304, 401-443 | JSON body; text | temp/cache write, remove on error, rename, config timestamp | forbidden: write/remove/rename | +| `POST` | `/sources_set` | 306-313, 446-478 | JSON body; text | sources file replacement | forbidden: configuration write | +| `POST` | `/repository_install_push` | 315-356, 588-636 | filename/repo URL plus body; text | `fopen("wb")`, write, install rename/metadata | forbidden: create/truncate/write/rename | +| `ANY` | `/usb_move_check` | 648-674 | query `path`; JSON includes USB size | directory/stat reads only; path resolve | not binary response; observation partial | +| `ANY` | `/usb_move_perform` | 676-706 | path, overwrite, keep; JSON | reads USB, writes internal copy, optionally removes source | forbidden: write/remove | +| `ANY` | `/manage:check` | 735-759 | filename; existence JSON | `stat` directory and file | no bytes/size/hash; observation partial | +| `ANY` | `/list_payloads` | 760-767 | JSON list | directory traversal, `stat`, metadata-sidecar reads | no file bytes/size/hash attestation; observation partial | +| `ANY` | `/processes_list` | 768-775 | process JSON | process enumeration | functional observation only; not readback | +| `ANY` | `/process_kill` | 776-796 | pid query; text | sends process signal | forbidden: process action | +| `ANY` | `/repository_payloads` | 797-810 | JSON cache/list | cache/config reads; multi-source path can refresh/write | forbidden: possible network/cache write | +| `ANY` | `/repository_refresh` | 811-824 | JSON | network download, cache/temp/config writes and renames | forbidden: network/write/rename | +| `ANY` | `/repository_install` | 825-865 | filename/source/detail; JSON | network or cache reads, temp/final write and rename | forbidden: install/write/rename | +| `ANY` | `/sources_list` | 866-873 | JSON | reads sources configuration | configuration disclosure; no binary file framing | +| `ANY` | `/sources_add` | 874-900 | URL query; JSON | sources configuration write | forbidden: configuration write | +| `ANY` | `/sources_remove` | 901-921 | index query; JSON | sources configuration write | forbidden: configuration write | +| `ANY` | `/chimera:load-controlled` | 922-951 | filename plus identity headers; text | `O_RDONLY|O_NOFOLLOW|O_CLOEXEC`, stat/hash/read, sends to elfldr and launches | forbidden: payload launch | +| `ANY` | `/loadpayload:` | 952-983 | path suffix; text | `open(O_RDONLY)`, stat/read, sends to elfldr and launches | forbidden: payload launch | +| `ANY` | `/manage:delete` | 984-1008 | filename query; text | unlinks payload/metadata, may update autoload | forbidden: delete/configuration | +| `ANY` | `/shutdown` | 1009-1015 | text | changes server run state | forbidden: service action | +| `ANY` | `/log` | 1016-1024 | log JSON | reads ring; active flag and connection state | observation partial | +| `ANY` | `/version` | 1025-1028 | text version | no file open; active flag | observation partial | +| `ANY` | `/getip` | 1029-1036 | text address | interface/socket query; active flag | observation partial; device/network metadata | +| `ANY` | `/autoload_status` | 1037-1091 | JSON | reads config/autoload; **sets `autoload_triggered=1`** | forbidden in Windows 1 and 2 | +| `ANY` | `/autoload_clear` | 1092-1097 | text | resets process-local autoload counters/trigger state; no file write in this call | forbidden: functional autoload mutation | +| `ANY` | `/abort` | 1098-1103 | text | changes autoload abort state | forbidden: functional mutation | +| unreachable | `/autoload_status` | 1104-1111 | short JSON | duplicate branch shadowed by 1037 | never independently callable | +| `ANY` | `/get_config` | 1112-1155 | JSON | `fopen("r")` config/autoload | no arbitrary file bytes; observation partial | +| `ANY` | `/events` | 1156-1165 | server-sent log stream | connection allocation and log reads | indefinite stream; not file readback | + +## Minimal controlled-profile routes + +This source defines a different listener/profile, not extra routes proven to be +installed beside the full profile. + +| Method | Exact endpoint | Handler lines | Parameters / response | Effects | Classification | +|---|---|---:|---|---|---| +| `POST` | `/chimera/upload` | 181-303, 322-324 | required identity headers and filename; text status | exclusive temp create/write, then verified read/hash, fsync/rename | forbidden: device write | +| `POST` | `/chimera/launch` | 305-309, 324-326 | filename and identity headers; text status | verified local read/hash, sends and launches | forbidden: payload launch | +| `DELETE` | `/chimera/artifact` | 311-327 | filename; text status | unlink | forbidden: delete | + +## Requested capability inventory + +| Capability | Existing route | Result | +|---|---|---| +| Directory listing | `/list_payloads` | yes, filtered payload listing | +| File stat | `/manage:check`, `/usb_move_check`, listing internals | partial/fixed-purpose; no general stat identity | +| File read | internal only | no arbitrary bytes returned | +| File download | none | absent | +| File hash | internal controlled-launch verification only | no hash response route | +| File size | `/usb_move_check` for allowed USB source | no general live component size route | +| Upload | both upload routes and minimal `/chimera/upload` | present and forbidden | +| Rename | internal upload/install commits | present and forbidden; no standalone route | +| Delete | `/manage:delete`, minimal `/chimera/artifact` | present and forbidden | +| Process listing | `/processes_list` | present | +| Service listing | none | absent | +| Listener listing | none | absent | +| Firmware | none | absent | +| Configuration | `/get_config` | present, but request has functional active-flag effect | +| Autoload | status/clear/config routes | present and excluded | +| Payload launch | both load routes and minimal `/chimera/launch` | present and forbidden | + +## File-open evidence + +Filesystem reads occur in listing, configuration, repository/source handling, +USB checking, self-update discovery, and launch verification. The only +launch-side exact flags are controlled +`O_RDONLY|O_NOFOLLOW|O_CLOEXEC` (`verified_launcher.c:83-99`) and legacy +`O_RDONLY` (`ps5_launcher.c:77-84`). Upload paths use the write/create flags +shown above. None of these internal reads is wired to an HTTP binary-body +response. + +Consequently: + +- `readback_candidate=false` for every existing route; +- binary framing, remote EOF, returned byte count, partial-transfer detection, + and end-to-end integrity are absent; +- a local file read for launch is not evidence of host readback; +- a general HTTP response is not evidence of file streaming. + +The machine-readable matrix in +`manifests/runtime/phase-0.9d-existing-stack-readback.json` records the +per-route booleans and hard-stop reasons used by the validator. diff --git a/docs/runtime/phase-0.9d-independent-recovery-analysis.md b/docs/runtime/phase-0.9d-independent-recovery-analysis.md new file mode 100644 index 0000000..872e321 --- /dev/null +++ b/docs/runtime/phase-0.9d-independent-recovery-analysis.md @@ -0,0 +1,71 @@ +# Phase 0.9D independent recovery analysis + +Date: 2026-07-18 +Status: `UNPROVEN` + +## elfldr + +| Question | Source-bound answer | +|---|---| +| Write mechanism | none in audited source | +| Executor | original external exploit/rudimentary loader could launch an ELF, but its source is absent | +| Works if elfldr is broken | bootstrap independence is described, not demonstrated: `PARTIAL` | +| Works if Manager is broken | bootstrap path does not name Manager: `PARTIAL` | +| Works after reboot | `UNPROVEN` | +| Works without autoload | manual host send is described: `PARTIAL` | +| Works without defective listener | port 9020 is described as distinct from elfldr port 9021: `PARTIAL` | +| Exact restore target selectable | no live target mapping or write contract | +| Write then verify | absent | +| Old file remains intact | no restore transaction exists | +| Power-loss behavior | `UNPROVEN`; no atomic restoration contract | +| Second route | none source-bound | + +Top-level required classification: `PARTIAL`. Detailed restore classifications: +`UNPROVEN` and `ABSENT`; no `INDEPENDENT_RECOVERY_CANDIDATE` is established. + +## Payload Manager + +| Question | Source-bound answer | +|---|---| +| Write mechanism | full Manager upload/install routes write files, but using a Manager to restore that same Manager is self-dependent and is not authorized | +| Executor | elfldr can launch a Manager ELF sent from a host, according to README/deploy source | +| Works if elfldr is broken | no; described manual Manager route depends on elfldr: `CROSS_DEPENDENT` | +| Works if Manager is broken | a separately supplied candidate could be launched through elfldr, but persistent restoration is absent: `PARTIAL` | +| Works after reboot | original exploit and elfldr repeatability are `UNPROVEN` | +| Works without autoload | manual send is described: `PARTIAL` | +| Works without normal Manager listener | host-to-elfldr launch is structurally separate: `PARTIAL` | +| Exact restore target selectable | no current live mapping | +| Write then verify | absent | +| Old file remains intact | upload/install paths are not an audited self-recovery transaction | +| Power-loss behavior | existing writes/renames are not proven safe for the current live Manager object | +| Second route | absent | + +Top-level required classification: `PARTIAL`. Detailed classifications: +`CROSS_DEPENDENT`, `SELF_DEPENDENT`, and `POWER_LOSS_UNSAFE` for the available +shapes. None is recovery proof. + +## Failure matrix + +| Failed component | Still source-plausible | Not proven / unavailable | +|---|---|---| +| elfldr | original port-9020 exploit bootstrap, if the absent external chain remains usable | current exploit availability, persistent rewrite, exact target, verification, power-loss recovery | +| Payload Manager | elfldr remains capable of accepting and launching a separately supplied ELF in source | Manager live path, durable replacement, independent verification, recovery when elfldr also fails | + +The current byte-exact Payload Manager backup remains +`HARD_BLOCKER_FOR_INSTALLATION`. Lack of independent recovery remains +`HARD_BLOCKER_FOR_EVERY_DEVICE_WRITE`. + +## Side-by-side feasibility + +elfldr `socksrv.c:477-507` terminates an already running `elfldr.elf` service +before creating another one. Payload Manager `main.c:81-103` terminates another +`pldmgr.elf` instance during startup. Both therefore have process/listener +overlap and global-state risks. + +The controlled Manager source uses a separate profile/port but is not installed +or runtime-proven. Any candidate would require transfer and execution, a +source-bound non-live path, collision review, a timeout/cleanup contract, and +independent recovery. None exists here. + +Classification: `BLOCKED` / `RECOVERY_UNPROVEN`. This is not staging, +installation, or execution approval. diff --git a/docs/runtime/phase-0.9d-off-device-backup-contract.md b/docs/runtime/phase-0.9d-off-device-backup-contract.md new file mode 100644 index 0000000..4fc46e7 --- /dev/null +++ b/docs/runtime/phase-0.9d-off-device-backup-contract.md @@ -0,0 +1,81 @@ +# Phase 0.9D off-device backup contract + +Date: 2026-07-18 +Status: `DESIGN_ONLY`; no readback route currently satisfies this contract. + +## Invariants + +A future host workflow shall process one component per separately authorized +session. It shall contain no upload, create-on-device, rename, unlink, delete, +chmod, chown, launch, process/service action, mount action, configuration +change, `/autoload_status`, automatic resume, or automatic retry. + +The host must: + +1. record an approved literal source path without inference; +2. record non-secret device identity, session identity, route, raw + request/response metadata, and exact client source commit; +3. create a new local temporary output exclusively and refuse overwrite; +4. receive in binary mode while counting every byte; +5. reject timeout, protocol error, unexpected EOF, short transfer, surplus + bytes, missing expected length, or any partial output as + `TRANSFER_INCOMPLETE` then `INVALID`; +6. close the file, reopen it, record its exact size, and compute SHA-256; +7. start a new connection and session for a second new exclusive local + temporary file, with no reused partial bytes; +8. close, reopen, size, and hash the second file; +9. compare exact sizes, SHA-256 values, and every byte; +10. make no automatic third attempt when the copies differ. + +If a transport cannot provide a trustworthy expected byte count, the host may +save an exploratory copy only as `SOURCE_MAPPING_PARTIAL`; it cannot classify +the transfer as byte-exact source attestation. The current stack has the +stronger blocker that it does not return arbitrary file bytes at all. + +## State machine + +```mermaid +stateDiagram-v2 + [*] --> TRANSFER_NOT_STARTED + TRANSFER_NOT_STARTED --> TRANSFER_INCOMPLETE: first byte accepted + TRANSFER_INCOMPLETE --> INVALID: timeout / short read / framing error + TRANSFER_INCOMPLETE --> HOST_COPY_RECEIVED: exact complete response + HOST_COPY_RECEIVED --> HOST_COPY_REOPENED: close + reopen + HOST_COPY_REOPENED --> HOST_COPY_HASHED: size + SHA-256 + HOST_COPY_HASHED --> SECOND_COPY_CREATED: new session/output + SECOND_COPY_CREATED --> SECOND_COPY_REOPENED: exact receive + close/reopen + SECOND_COPY_REOPENED --> SECOND_COPY_HASHED: size + SHA-256 + SECOND_COPY_HASHED --> INVALID: size/hash/byte mismatch + SECOND_COPY_HASHED --> COPIES_MATCH: size/hash/all bytes equal + COPIES_MATCH --> SOURCE_MAPPING_PARTIAL: path/object identity incomplete + COPIES_MATCH --> SOURCE_MAPPING_VERIFIED: independent source mapping proven +``` + +Required status vocabulary: + +- `TRANSFER_NOT_STARTED` +- `TRANSFER_INCOMPLETE` +- `HOST_COPY_RECEIVED` +- `HOST_COPY_REOPENED` +- `HOST_COPY_HASHED` +- `SECOND_COPY_CREATED` +- `SECOND_COPY_REOPENED` +- `SECOND_COPY_HASHED` +- `COPIES_MATCH` +- `SOURCE_MAPPING_PARTIAL` +- `SOURCE_MAPPING_VERIFIED` +- `INVALID` + +## Classification boundaries + +`OFF_DEVICE_BACKUP_VALID` is allowed only after the two closed-and-reopened +local copies have identical sizes, SHA-256 values, and bytes. It says nothing +about the current live source path unless source mapping was independently +proven. + +`RECOVERY_PROVEN` is forbidden in this phase. A matching host backup is not +proof that any actor can safely restore it, survive power loss, retain the old +live file, or recover without the component being replaced. + +The contract is intentionally descriptive. Phase 0.9D creates no client, +script, device command, backup, or transfer package. diff --git a/docs/runtime/phase-0.9d-operational-windows.md b/docs/runtime/phase-0.9d-operational-windows.md new file mode 100644 index 0000000..718a087 --- /dev/null +++ b/docs/runtime/phase-0.9d-operational-windows.md @@ -0,0 +1,91 @@ +# Phase 0.9D operational windows + +Date: 2026-07-18 +Status: future review design only + +No window is authorized by this document. Because the existing stack has no +file readback route, Window 2 is currently blocked and the later windows cannot +open. + +## Window 1 — manual fact collection + +Proposed content: + +- firmware text exactly as displayed in the PS5 interface; +- visible exploit, loader, and Manager status/version strings; +- non-secret device/session identifier; +- operator photograph or literal transcript; +- autoload state only if it is visible without a request or mutation. + +Prohibitions: network request to Manager unless separately justified, file +transfer, `/autoload_status`, payload launch, configuration change, USB +operation, retry. + +Current outcome: `PARTIAL`, then `BLOCKED_WINDOW1` until a concrete +non-mutating operator procedure is reviewed. It can become +`READY_FOR_WINDOW2_REVIEW` only through a new exact authorization. + +## Window 2 — single off-device readback + +Intended constraints: + +- one existing component and one literal proven live source path; +- PS5-to-host only; +- no write/create/truncate/rename/unlink/delete/upload/launch/autoload/config, + no automatic retry or resume; +- new exclusive local output, binary framing, exact byte count, short-read and + timeout rejection; +- any accepted `server_active_flag`, logging, socket, buffer, counter, cache, + atime, scheduling, audit, accounting, or network-counter effect must be named + by the future permission. + +Current outcome: `BLOCKED_NO_READBACK_PATH`. There is no first component to +recommend. No route satisfies binary response, exact length, EOF, short-read, +and partial-output rejection. + +## Window 3 — independent repeat readback + +Would require a new connection/session and output, no reused bytes, no +resume/retry, full size/hash/byte comparison, and no automatic third attempt. +A mismatch means `SOURCE_UNSTABLE_OR_READBACK_INVALID`. + +Current outcome: `BLOCKED` because Window 2 is blocked. + +## Window 4 — second component + +Would require separate permission, session, and files after successful Windows +2 and 3. A combined backup-all operation is forbidden. + +Current outcome: `BLOCKED`. + +## Window 5 — recovery contract review + +Offline only. A matching pair of host copies may establish +`OFF_DEVICE_BACKUP_VALID`, never `RECOVERY_PROVEN`. Exact write executor, +non-self-dependence, target selection, write-then-verify, old-file retention, +and power-loss behavior must be proven. + +Current outcome: `UNPROVEN`. + +## Window 6 — non-live staging + +Outside Phase 0.9D and blocked until both readbacks match, live target mapping +is proven, independent recovery is sufficient, a non-live staging path is +proven, and a separate permission exists. + +## Window 7 — side-by-side execution + +Outside Phase 0.9D. No autoload, retry, live replacement, or combined lifecycle +probe is permitted. Current classification: `BLOCKED`. + +## Hard stops + +- no existing PS5-to-host file transport; +- no runtime-observed live component path; +- current installed identity and persistence are unproven; +- exploit/port-9020 executor source and repeatability are absent; +- independent byte-exact restore is absent; +- power-loss-safe restore is unproven; +- current Payload Manager backup is still the installation blocker; +- every device write is blocked by missing independent recovery; +- all authorization fields remain false. diff --git a/docs/runtime/phase-0.9d-readback-feasibility.md b/docs/runtime/phase-0.9d-readback-feasibility.md new file mode 100644 index 0000000..7d3a689 --- /dev/null +++ b/docs/runtime/phase-0.9d-readback-feasibility.md @@ -0,0 +1,115 @@ +# Phase 0.9D existing-stack readback feasibility + +Date: 2026-07-18 +Decision: `BLOCKED_NO_READBACK_PATH` + +## Route search, in required order + +| Order | Candidate | Source result | Binary framing | Short-read / EOF / size | Decision | +|---:|---|---|---|---|---| +| 1 | Payload Manager download | no endpoint exists | absent | absent | `ABSENT` | +| 2 | Payload Manager file read | internal reads are not returned to HTTP client | absent | absent | `ABSENT` | +| 3 | elfldr response or host pipe | controlled route returns fixed status text and disables payload stdio; legacy stdio requires a payload process | no file protocol | no file completeness contract | rejected | +| 4 | exploit host | README sends host-to-PS5; implementation absent | no reverse framing | unproven | rejected | +| 5 | browser/USB host | browser uploads toward PS5; USB move writes device storage | reverse direction or mutating | no host-copy contract | rejected | +| 6 | repository tools | deploy scripts send through port 9021 only | no reverse framing | absent | rejected | +| 7 | no route | all audited paths exhausted | — | — | selected | + +Hardened elfldr `socksrv.c:288-375` sends only rejection, error, or +`Controlled payload exited` status. The payload data is discarded after +loading and controlled stdio is `-1`. Controlled Manager +`verified_launcher.c:31-184` verifies and sends a descriptor to elfldr and then +closes; `ps5_launcher.c:18-69` exposes connect/send/close only. Neither side +contains a receive path for component bytes. + +## Flag semantics + +### `server_active_flag` + +- Declaration and initial value: static volatile integer initialized to zero, + `src/log_server.c:20-23`. +- Sole writer: `log_server_set_active()` assigns one. +- Sole reader: `pldmgr_server_is_active()`, used by + `src/autoload.c:101` only to suppress fallback notification behavior while a + browser/server request has occurred. +- Reset: none in the process. +- Lifetime: process-local; it cannot persist as this variable after process + exit or reboot. Exact manager process lifetime on hardware is unobserved. +- No source path connects it to filesystem writes, autoload enablement, + payload launch, request authorization, shutdown, or boot configuration. +- Disconnect does not reset it; a crash/process exit destroys the process + state. + +Classification: `LOW_VOLATILE`, not `ANTI_BRICK_CRITICAL`. Because there is no +reset semantic within the running process, any otherwise observational route +remains `PARTIAL` and a future permission must explicitly accept this effect, +log-ring changes, connection state, buffers, sockets, and counters. + +### `autoload_triggered` + +- Declaration/initial value: static volatile integer zero, + `src/autoload.c:31`. +- Writers: `pldmgr_autoload_get_status()` sets one (`:45`); + `pldmgr_autoload_reset()` sets zero (`:235`). +- Readers: autoload worker wait logic at `:82-86`. +- `/autoload_status` calls the mutating getter before building its response + (`src/http_server.c:1037-1041`). +- Reset is explicit clear/resume logic, not disconnect cleanup. +- It is process-local, but can change worker timing and therefore launch + behavior while that process exists. + +Classification: `HIGH_FUNCTIONAL`. `/autoload_status` is excluded from +Windows 1 and 2. The later duplicate route at `http_server.c:1104-1111` is +unreachable because the first identical comparison already matches. + +## Path inventory + +| Literal path | Source | Meaning | Live status | Evidence | +|---|---|---|---|---| +| `/data/pldmgr` | controlled Manager `include/pldmgr.h:8` | base device directory | configured default only | source literal | +| `/data/pldmgr/payloads` | same `:12` | payload storage | configured default only | source literal | +| `/data/pldmgr/autoload.txt` | same `:9` | Manager autoload list | configured default only | source literal | +| `/data/pldmgr/pldmgr_config.txt` | same `:10` | Manager config | configured default only | source literal | +| `/data/pldmgr/repository_cache.json` | same `:11` | repository cache | configured default only | source literal | +| `/data/pldmgr/sources.json` | same `:14` | repository sources | configured default only | source literal | +| `/mnt/usb0/pldmgr` … `/mnt/usb7/pldmgr` | `payload_mgr.c:201-218` | USB scan roots | possible source directories | source literal/pattern | +| `/data/pldmgr/payloads/chimera-controlled` | controlled Manager source/profile | controlled staging directory | explicitly not installed | offline candidate only | +| `/data/pldmgr/payloads/elfldr/elfldr_v0.23.elf` | historical Phase-0.6 manifest/tool input | stock identity reference | not runtime-observed | `reference_only` | +| `/data/pldmgr/payloads/pldmgr/pldmgr_v0.3.1.elf` | historical Phase-0.6 manifest/tool input | stock identity reference | not runtime-observed | `reference_only` | +| `/data/payload.elf` | elfldr README example | generic manual launch example | not a current mapping | documentation example | +| `/user/temp/payload_.self` | elfldr self-loader source | temporary converted image | runtime-generated, not component live path | source template | +| `/system/vsh/app/NPXS40112/eboot.bin` | elfldr bootstrap source | injection victim executable | not an elfldr installation path | source literal | + +No audited input binds an actual live elfldr or Manager object to a current +path. The package/reference paths and configured defaults therefore produce +`PATH_CONFLICT`; no future readback window may guess. + +## Readback decision matrix + +- `NEW_OBSERVER_FEASIBILITY`: `BLOCKED` +- `EXISTING_STACK_MANUAL_FACT_COLLECTION`: `PARTIAL` +- `EXISTING_STACK_SINGLE_READBACK`: `BLOCKED_NO_READBACK_PATH` +- `EXISTING_STACK_REPEAT_READBACK`: `BLOCKED` +- `ELFLDR_INDEPENDENT_RECOVERY`: `PARTIAL` +- `PAYLOAD_MANAGER_INDEPENDENT_RECOVERY`: `PARTIAL` +- `SIDE_BY_SIDE_FEASIBILITY`: `BLOCKED` +- `DEVICE_WRITE`: `NOT_AUTHORIZED` +- `TARGET_EXECUTION`: `NOT_AUTHORIZED` +- `INSTALLATION`: `NOT_AUTHORIZED` + +There is no possible first readback component: the component-risk order cannot +override an absent transport. A missing metadata query would merely reduce a +copy to `SOURCE_MAPPING_PARTIAL`; here the earlier condition—getting complete +binary bytes—already fails. + +## Explicitly acceptable only after later consent + +A future read-only request could reasonably entail sockets, temporary buffers, +heap allocation, connection/request counters, scheduler activity, cache or +atime effects, audit/process/network accounting, stdout logging, ring logging, +and a process-local `server_active_flag=1`. These effects are not claimed +absent. They are acceptable only if the chosen route has no device-file write, +launch, autoload, service, or persistent configuration effect and the +artifact-specific permission names them. + +No such permission is requested in Phase 0.9D. diff --git a/docs/runtime/phase-0.9e-bootstrap-provenance.md b/docs/runtime/phase-0.9e-bootstrap-provenance.md new file mode 100644 index 0000000..930cee8 --- /dev/null +++ b/docs/runtime/phase-0.9e-bootstrap-provenance.md @@ -0,0 +1,109 @@ +# Phase 0.9E bootstrap provenance + +Date: 2026-07-18 + +Status: `BOOTSTRAP_IMPLEMENTATION_MISSING` + +This is an offline inventory. No internet operation, package installation, +target build, device request, transfer, execution, or PS5 connection occurred. +Y2JB was used only as a filename/search hint. + +## Search boundary + +The search was limited to: + +- this `chimera-gfx` tree and its existing `work/upstream` trees; +- the existing `chimera-elfldr` and `chimera-ps5-payload-manager` siblings; +- exact-name checks in the user Downloads directory and supplied Codex + attachments; +- existing repository manifests, scripts, audit records, and ignored local + Phase-0.6/0.7 evidence. + +No other sibling project exists in the dated project directory. No BD-J SDK, +WebKit exploit tree, Y2JB source tree, 9020-loader tree, or external-autoloader +tree is present under the allowed source roots. + +## Candidate archive + +`~/Downloads/Y2JB-Autoloader-403-1240.zip` exactly matches the candidate +recorded by Phase 0.6: + +- size: `504159435`; +- SHA-256: + `805e3f87f0c371223619ffc7d3a7b3c0d41a1fae8a8b1171d9e2f162659e8291`; +- ZIP integrity: valid central directory and CRC; +- entries: four directories plus + `PS5/EXPORT/BACKUP/202606102126_00/archive.dat`; +- no ZIP comment and no source/version manifest. + +The inner object has: + +- size: `504365056`; +- SHA-256: + `6439834e8856d45b6d6fe699b74c35ca6985a199ea8ecf3e398c018d37be2d55`; +- leading bytes: `SIECAF`; +- measured byte entropy: `7.999987` bits/byte; +- zero literal matches for `Y2JB`, `y2jb`, `9020`, `elfldr`, `autoload`, + `exact artifact missing"] + B["Y2JB/browser exploit host
opaque candidate only"] + C["Rudimentary port-9020 listener
implementation missing"] + D["Host sender
README concept only"] + E["hardened elfldr-ps5.elf
local reference build"] + F["embedded bootstrap
source design"] + G["elfldr service :9021
source design"] + H["controlled Payload Manager
offline reference build"] + + A --> B --> C + D --> C + C --> E --> F --> G --> H +``` + +| Node | Environment / side | State | Start/input/output | Dependencies | Device file / autoload | Recovery role | Evidence | +|---|---|---|---|---|---|---|---| +| operator trigger | host/browser | unknown | unknown | exact package | unknown | begins jailbreak session | `UNKNOWN` | +| Y2JB/exploit host | browser/host | unknown | opaque backup candidate | trigger/package | unknown | could recreate 9020 state | `POSSIBLE` | +| rudimentary loader | PS5 process/kernel context unknown | unknown | presumed listener on 9020 | exploit | unknown | candidate rescue executor | implementation missing | +| conceptual sender | host | temporary | host connects and streams an ELF per README | working listener | no sender-side device path | sends elfldr | architecture only | +| hardened elfldr bootstrap | PS5 | temporary entry, then background service design | final ELF embeds bootstrap and service | 9020 execution | source itself does not install, but performs documented process/kernel changes | creates port 9021 | exact local source, not deployed-use proof | +| elfldr service | PS5 process | background design | listens TCP 9021 | hardened bootstrap | no autoload source path | later payload executor | exact source, hardware unproven | +| controlled Manager | PS5 process | not installed in evidence | separate launch via elfldr | elfldr 9021 | controlled storage if used; no autoload | management layer | offline reference only | + +No node before hardened elfldr has a source-bound executable identity. + +## Required questions + +1. **Who listens on 9020?** Unknown. The elfldr README calls it a + “rudimentary ELF loader”; no implementation is present. +2. **Who starts it?** Presumably the exploit chain, but exact trigger and + process are unknown. +3. **Before elfldr?** Architecturally yes; exact implementation proof is + absent. +4. **Available again after reboot?** Plausible for a repeatable jailbreak, not + proven for the used package. +5. **Requires working elfldr on filesystem?** Architecture says no; exact + loader proof is absent, so `independent_from_elfldr=unproven`. +6. **Requires Payload Manager?** Architecture says no; exact proof is absent, + so `independent_from_payload_manager=unproven`. +7. **Can it receive a payload directly?** The README assumes an elfldr ELF can + be sent; limits and exact acceptance are unknown. +8. **Accepted format?** The sender streams ELF bytes. Server validation, + container rules, and truncation behavior are unknown. +9. **Automatic execution?** Required by the described bootstrap, but no server + source proves it. +10. **Writes a device file first?** Unknown. +11. **Can it execute without live replacement?** Plausible, not proven. +12. **Restart after a payload crash?** Unknown. +13. **Does reboot remove temporary state?** Plausible, not proven. +14. **Depends on autoload?** No architectural requirement is documented, but + actual configuration is absent. +15. **Works with both later components absent?** Conceptual chain says yes; + exact implementation is missing. +16. **Rescue without filesystem write?** Unknown. +17. **Independent result channel?** No exact result/output contract exists. +18. **Hardware-unproven aspects:** all process identities, boot persistence, + port behavior, parser behavior, mapping/entry, file writes, cleanup, + crash/hang response, and reboot recovery. + +## Independence decision + +An architectural diagram alone permits at most +`PARTIAL_RESCUE_EXECUTOR`. Here, the actual executable implementation itself +is unavailable, so the more precise required classification is: + +`BOOTSTRAP_IMPLEMENTATION_MISSING` + +This does not mean the chain is dependent; it means independence cannot be +audited. `phase09f_rescue_payload_design_allowed=false`. diff --git a/docs/runtime/phase-0.9e-loader-9020-protocol.md b/docs/runtime/phase-0.9e-loader-9020-protocol.md new file mode 100644 index 0000000..f7418ad --- /dev/null +++ b/docs/runtime/phase-0.9e-loader-9020-protocol.md @@ -0,0 +1,63 @@ +# Phase 0.9E port-9020 protocol + +Date: 2026-07-18 + +Protocol status: `UNPROVEN_IMPLEMENTATION_MISSING` + +## Available evidence + +Hardened elfldr `README.md:11-19` describes a host connecting to port 9020 and +piping `elfldr-ps5.elf` bytes through a generic TCP stream tool. Its Makefile +defines `PS5_PORT ?= 9020`, but contains no deploy target or protocol client. +The README names external BD-J and WebKit projects; neither project nor the +used Y2JB implementation exists in the allowed local source trees. + +This proves only the expected sender shape. It does not prove the listener +implementation or actual deployed package. + +| Field | Offline result | +|---|---| +| server role | PS5-side rudimentary loader, description only | +| client role | host-side generic stream sender, no exact client artifact | +| transport | TCP is strongly indicated by the documented stream tool | +| port | 9020 in README/Makefile | +| bind address | unknown | +| direction | host connects to PS5 | +| handshake / magic / version | unknown | +| endianness / headers / length fields | unknown | +| maximum payload size | unknown | +| chunking | TCP fragmentation behavior only; application contract unknown | +| EOF | sender closes after input; listener EOF semantics unknown | +| acknowledgement / error frame | unknown | +| checksum / hash | none documented; listener behavior unknown | +| timeout / retry / reconnect | unknown | +| upload direction | host to PS5 | +| response direction | unknown | +| accepted object | ELF assumed by documentation; validation unknown | +| parser / bounds / overflow | implementation missing | +| partial recv/send detection | implementation missing | +| allocation / mapping / permissions | implementation missing | +| entrypoint selection | implementation missing | +| filesystem staging | implementation missing | +| cleanup / crash behavior | implementation missing | + +The exact source-backed port-9021 protocol is deliberately not substituted for +port 9020. Port 9021 belongs to `socksrv.c`; it has different parser and +lifecycle code. + +## Host model decision + +No parser/emulator was created. A model derived from the README would invent +server framing, maximum length, EOF, mapping, and error semantics. The +machine-readable result is +`manifests/runtime/phase-0.9e-loader-protocol.json`. + +Minimum input needed to build a host-only model: + +- exact 9020 listener source or a fully audited exact binary; +- exact sender/client implementation; +- maximum length and allocation rules; +- complete recv/EOF/short-read state machine; +- accepted format and entrypoint validation; +- response/error/close behavior; +- timeout and retry rules. diff --git a/docs/runtime/phase-0.9e-output-architecture-options.md b/docs/runtime/phase-0.9e-output-architecture-options.md new file mode 100644 index 0000000..c42a052 --- /dev/null +++ b/docs/runtime/phase-0.9e-output-architecture-options.md @@ -0,0 +1,30 @@ +# Phase 0.9E output architecture options + +Date: 2026-07-18 + +No output architecture is selected. The actual bootstrap implementation is +missing, so `output_channel_for_future_rescue=UNKNOWN`. + +| Option | Exists now | Changes required | Device file | Cleanup/crash model | Risk / feasibility | +|---|---|---|---|---|---| +| A. same 9020 connection, duplex | unknown | exact server and host protocol may need change; payload result propagation required | not inherently | unknown socket ownership, EOF and abort | potentially low persistent risk, feasibility unknown | +| B. temporary PS5-to-approved-host outbound connection | absent | host receiver and target payload required | no | bounded connect/send/close and deadline required | temporary socket is not automatically brick-relevant; network policy and cleanup unproven | +| C. temporary one-connection listener | absent | target listener and host client required | no | bind/listen/accept deadline and guaranteed close required | listener lifetime/session collision risk; not automatically a brick risk | +| D. caller-owned host buffer | absent | exact loader ABI, buffer ownership, copy-out and post-exit delivery required | no | caller finalization and crash-safe ownership required | Phase-0.9C already shows current elfldr has no such output path | +| E. no usable output | consistent with current evidence | none | no | no recovery result possible | only defensible current operational assumption | + +Options A through D are designs, not claims. A generic TCP connection can be +duplex at transport level, but that does not prove that the 9020 listener keeps +the socket, passes it to a payload, accepts response bytes, or returns them to +the host. + +No option may: + +- create or replace a live device file; +- enable autoload; +- retry automatically; +- combine recovery of both components; +- persist a listener or configuration across sessions. + +The missing exact listener source prevents selection and prevents a +source-derived host-only emulator. diff --git a/docs/runtime/phase-0.9e-r-official-hostsender-audit.md b/docs/runtime/phase-0.9e-r-official-hostsender-audit.md new file mode 100644 index 0000000..b51d4a7 --- /dev/null +++ b/docs/runtime/phase-0.9e-r-official-hostsender-audit.md @@ -0,0 +1,56 @@ +# Phase 0.9E-R official Y2JB host sender audit + +Date: 2026-07-18 + +Official sender: +`Gezine/Y2JB@0dbbf4e7e0203af7e5d101a3256c634edf4e3ba2:payload_sender.py` + +Identity: + +- size: 1064 bytes; +- SHA-256: + `8c87920c41dbdbd66b9f36ca9509f0d6bef9170f351dd97ff831cfb98e642ec6`; +- Git blob: `4ad80085bd8f2cf9ca3520d8ff1353b9c764fb75`; +- same blob in all five official tags; +- originating commit: + `edf0d631dd6e4dbd5a3db54ebdae47934d8bd994`. + +The file was inspected statically and was never run against any network +target. + +## Contract + +| Lines | Behavior | +|---|---| +| 2 | `send_payload(path, host, port=50000)` | +| 3–4 | opens the selected local file `rb` and reads it completely | +| 6 | creates IPv4 TCP socket | +| 7 | blocking connect to caller-supplied host/port | +| 8 | `sendall(data)`; CPython manages short sends or raises | +| 9 | closes after successful `sendall` | +| 10 | reports local byte count, not peer acknowledgement | +| 14–22 | accepts `host file` or `host port file`; explicit port uses `int()` | +| 23–29 | prints usage/examples, including non-binding 9020 example | + +Findings: + +- binary mode: yes; +- default port: 50000; +- explicit port selection: yes; +- maximum input size: absent; +- timeout: absent; +- automatic retry/resume: absent; +- response read: absent; +- acknowledgement, remote byte count, checksum, or hash: absent; +- exception handling: absent; file/socket errors propagate; +- success exit: implicit zero; +- usage error exit: also implicit zero; +- local mutation: none; selected file is read-only; +- duplex classification: false. + +The server added same-connection log writes in Y2JB 1.4, but this official +sender closes after `sendall` and never reads. Transport-level duplex +capability is not an end-to-end duplex sender contract. + +No claim is made that this official sender is the one actually used by the +operator. That requires the separate operator attestation. diff --git a/docs/runtime/phase-0.9e-r-official-release-correlation.md b/docs/runtime/phase-0.9e-r-official-release-correlation.md new file mode 100644 index 0000000..3f16524 --- /dev/null +++ b/docs/runtime/phase-0.9e-r-official-release-correlation.md @@ -0,0 +1,125 @@ +# Phase 0.9E-R official Y2JB release correlation + +Date: 2026-07-18 + +Result: `OFFICIAL_RELEASE_NO_MATCH` + +Overall classification: `LOCAL_BACKUP_NOT_CORRELATED` + +This was a bounded host-side investigation. Public network access was limited +to official `Gezine/Y2JB` GitHub repository, API, tag, release, asset metadata, +and source-archive URLs. No PS5 address, request, connection, transfer, +payload, target build, restore, or execution was used. + +## Local evidence captured before network access + +| Field | Value | +|---|---| +| path | `/Y2JB-Autoloader-403-1240.zip` | +| size | `504159435` | +| SHA-256 | `805e3f87f0c371223619ffc7d3a7b3c0d41a1fae8a8b1171d9e2f162659e8291` | +| creation UTC | `2026-06-23T14:10:39.0466596Z` | +| modification UTC | `2026-06-23T14:11:10.6992328Z` | +| ZIP comment | empty | +| first 64 bytes | `504b030414000000000041abca5c000000000000000000000000040000005053352f504b030414000000000041abca5c0000000000000000000000000b000000` | +| last 64 bytes | `76652e6461740a0020000000000001001800c065678d17f9dc0100000000000000000000000000000000504b050600000000050005000a020000abda0c1e0000` | +| Zone.Identifier | 327 bytes; SHA-256 `70928a3d66cbb9f584596414c4fe535b498dd5ed7c5f55bbeae4efeb146ea841` | +| local download indication | `ReferrerUrl` and `HostUrl` are MediaFire; exact volatile signed URL is retained only in the original ADS | +| browser records | exact filename absent from five bounded Chrome/Edge/Brave/Firefox history candidates | + +The MediaFire marker is local provenance evidence, not evidence about the +bytes hosted there and not an official-source match. + +### ZIP entries + +The four directory entries use method 0, CRC `00000000`, flag bits 0, and +zero sizes. Central-directory metadata was captured without normalization: + +| Name | Method | Compressed | Uncompressed | CRC32 | DOS timestamp | Header offset | External attr | +|---|---:|---:|---:|---|---|---:|---:| +| `PS5/` | 0 | 0 | 0 | `00000000` | `2026-06-10 21:26:02` | 0 | 16 | +| `PS5/EXPORT/` | 0 | 0 | 0 | `00000000` | `2026-06-10 21:26:02` | 34 | 16 | +| `PS5/EXPORT/BACKUP/` | 0 | 0 | 0 | `00000000` | `2026-06-10 21:26:54` | 75 | 16 | +| `PS5/EXPORT/BACKUP/202606102126_00/` | 0 | 0 | 0 | `00000000` | `2026-06-10 21:27:24` | 123 | 16 | +| `PS5/EXPORT/BACKUP/202606102126_00/archive.dat` | 8 | 504158629 | 504365056 | `522808c8` | `2026-06-10 21:27:28` | 187 | 32 | + +All five entries use general-purpose flag bits 0. The archive entry reports +create system 0, create version 63, and extraction version 20. + +The inner first 64 bytes are +`5349454341460000010000000000000001000000000000000300000000000000c6ff138694ca1d2701779aae67de1525e5158700e6158700e715870000000000`. +Only the `SIECAF` magic and bounded header were inspected. No new parsing, +decryption, or reverse engineering was attempted. Its previously established +SHA-256 remains +`6439834e8856d45b6d6fe699b74c35ca6985a199ea8ecf3e398c018d37be2d55`. + +## Official inventory + +Metadata came from: + +- `https://api.github.com/repos/Gezine/Y2JB`; +- `https://api.github.com/repos/Gezine/Y2JB/releases?per_page=100`; +- `https://api.github.com/repos/Gezine/Y2JB/tags?per_page=100`. + +The official API returned three releases and five tags. + +| Version | Official state | Tag commit | Release publication | Source tree | +|---|---|---|---|---| +| 1.2 | changelog text only; no current tag or release | unknown; not guessed | none | no exact release tree | +| 1.2.1 | tag `Y2JB-1.2.1`; no current release | `6210f98c23552e6eb3c4c8981f72a200bb6121b2` | none | `928ceb979881a063457270dde4961ea4a06bc1fa` | +| 1.3 | release `Y2JB 1.3 (deprecated)`, tag `1.3` | `82e6bbf1935d5f41c3bd5bd9e6598cf90bb2e022` | `2026-05-25T14:29:41Z` | `df29ffe4d8131b5f8b0da1874e34b95a41f0660d` | +| 1.4 | tag `1.4`; no current release | `02eda2e772df629ecfe5a46459769bd8c994ef3e` | none | `ec06403b45f7b68e01eb5053f81277dee4910dd2` | +| 1.5 | release `Y2JB 1.5 (deprecated)`, tag `1.5` | `5fa556e6281229057dace3254b099f95261df386` | `2026-05-22T15:06:30Z` | `db364529c347f34b8ddb186f57bb61dab91113db` | +| 1.6 | release `Y2JB 1.6`, tag `1.6` | `0dbbf4e7e0203af7e5d101a3256c634edf4e3ba2` | `2026-06-09T14:54:35Z` | `c4344f43af7c268337437e6419548dba6f6bc211` | + +The 1.6 release body contains the historical 1.2 through 1.5 changelogs. That +does not create missing release objects or identify an exact 1.2 commit. + +## Metadata-first asset comparison + +GitHub supplied SHA-256 digests for every current official asset. Each digest +and size excludes the local outer ZIP, so no large release asset was +downloaded. + +| Tag | Asset ID | Official asset | Official size | Size delta vs local | Official SHA-256 | Result | +|---|---:|---|---:|---:|---|---| +| 1.6 | 442805805 | `Y2JB_backup_1.6.12.20.zip` | 498991521 | -5167914 | `9c097ed9000792507bcf307eff0802f40ee64dc047ab6de3d38acccc62d39dcc` | `NO_MATCH` | +| 1.6 | 442358421 | `Y2JB_backup_1.6.4.03.zip` | 504395044 | +235609 | `b01b4f442327f9eca90ffc4506dfa58249e4ac70cb9d7488c856c9e8dfaf37b4` | `NO_MATCH` | +| 1.6 | 442358388 | `Y2JB_download0_1.6.zip` | 315229037 | -188930398 | `d032954033c17acda47c41179a22fa09016ddd40a274f4e9afbb051e5d08d3bd` | `NO_MATCH` | +| 1.5 | 429350761 | `Y2JB_backup_1.5.zip` | 503863926 | -295509 | `91bc9fb38e62d2b34d0009dbfd6a46dffb379277eef3915dd0a1956f65462a45` | `NO_MATCH` | +| 1.5 | 429351154 | `Y2JB_download0_1.5.zip` | 336514975 | -167644460 | `f7e5debb2e4c6b3fe7224e549c4aaf9b273a178543cd0bfe84e37bc0521d590a` | `NO_MATCH` | +| 1.3 | 429415428 | `Y2JB_backup_1.3.7z` | 488117068 | -16042367 | `e63a6b8fe134ad93b8a6a3b332b98810598aff7dfb6703a226523143c3844dfb` | `NO_MATCH` | +| 1.3 | 429415862 | `Y2JB_download0_1.3.zip` | 336789664 | -167369771 | `c64ef5bc508e4d81395928a8a54b2832f2b37c9cd1603ec9e7e3bd6f5ad6b526` | `NO_MATCH` | + +Asset creation/update times recorded by GitHub were: + +- 1.6 `12.20`: `2026-06-09T14:52:35Z` / `14:53:10Z`; +- 1.6 `4.03`: `2026-06-09T03:33:57Z` / `03:34:40Z`; +- 1.6 `download0`: `2026-06-09T03:33:55Z` / `03:34:30Z`; +- 1.5 backup: `2026-05-25T12:46:49Z` / `12:47:34Z`; +- 1.5 `download0`: `2026-05-25T12:47:34Z` / `12:47:56Z`; +- 1.3 backup: `2026-05-25T14:27:41Z` / `14:28:29Z`; +- 1.3 `download0`: `2026-05-25T14:28:29Z` / `14:29:18Z`. + +The 1.6 release labels its `4.03` backup for firmware 4.03 through 12.40 and +its `12.20`-named backup for 12.60 and later. Releases 1.3 and 1.5 are marked +deprecated. Their release bodies, and the 1.6 historical changelog, were +retained as descriptive metadata only; they do not establish local identity. + +No asset has the local name, size, or SHA-256. An exact byte match is therefore +mathematically impossible for the inventoried assets without downloading +them. Downloading a digest-excluded 315–504 MB object would add no evidence and +would violate the metadata-first rule. + +## Decision + +- correlation: `OFFICIAL_RELEASE_NO_MATCH`; +- identified local release: none; +- local outer-to-release association: none; +- large backup assets downloaded: zero; +- local backup classification: `LOCAL_BACKUP_NOT_CORRELATED`; +- Phase 0.9F offline design allowed: `false`. + +This does not prove that the local package contains no official-derived +content. It proves that its outer bytes are not any current official +`Gezine/Y2JB` release asset. diff --git a/docs/runtime/phase-0.9e-r-port9020-source-audit.md b/docs/runtime/phase-0.9e-r-port9020-source-audit.md new file mode 100644 index 0000000..b4aaf79 --- /dev/null +++ b/docs/runtime/phase-0.9e-r-port9020-source-audit.md @@ -0,0 +1,148 @@ +# Phase 0.9E-R official Y2JB port audit + +Date: 2026-07-18 + +Port-9020 classification: `PORT_9020_REFERENCE_ONLY` + +## Exact 9020 search + +Every official tag (`Y2JB-1.2.1`, `1.3`, `1.4`, `1.5`, `1.6`) has exactly two +9020 text references: + +- `README.md`: a `payload_sender.py ... 9020 payload.bin` example; +- `payload_sender.py:29`: the same usage example. + +No tag contains a socket bind, listen, connect target, parser, mapper, ELF +loader, or dispatch function fixed to port 9020. Therefore the example cannot +be promoted to an implementation. + +## Actual Remote JS Loader + +At release 1.6 commit +`0dbbf4e7e0203af7e5d101a3256c634edf4e3ba2`, the implementation is +`download0/cache/splash_screen/aHR0cHM6Ly93d3cueW91dHViZS5jb20vdHY=/remotejsloader.js`. + +| Lines | Function / block | Contract | +|---|---|---| +| 2–8 | allocation | fixed `MAXSIZE = 500 * 1024`; one 512000-byte receive buffer | +| 10–39 | `create_socket` | TCP/IPv4, `SO_REUSEADDR`, `INADDR_ANY`, port 0, backlog 3 | +| 41–47 | `get_port` | reads assigned ephemeral port | +| 49–88 | `setup_socket_until_port_50000` | creates/closes up to 60000 sockets seeking assigned port 50000; falls back to last port | +| 90–105 | `recreate_socket` | creates a new ephemeral listener after accept failure | +| 107–119 | initial setup | reports actual IP and port | +| 122–209 | accept loop | infinite accept/read/decode/eval loop | +| 145–164 | receive loop | reads until EOF or 512000 bytes; partial reads accumulate | +| 168–188 | validation | rejects empty/error; 1.6 rejects ELF magic only | +| 190–203 | dispatch | UTF-8 decode, `eval`, same-connection log writes, close in `finally` | + +Protocol properties: + +- server: PS5-side JavaScript listener; +- expected port: normally 50000, but explicitly dynamic; +- framing: TCP connection close/EOF; +- maximum accepted prefix: 512000 bytes; +- content: JavaScript; 1.6 rejects only `7f 45 4c 46`; +- authentication: none; +- declared length, version, checksum, or hash: none; +- timeout: none; +- full-limit rejection: absent; reaching 512000 bytes can dispatch the prefix + without proving EOF; +- native mapping/entrypoint: absent; received bytes are text-decoded and + evaluated; +- filesystem write: none in this receive function; +- retry behavior: internal listener allocation/recreation loops exist; +- crash/hang risk: untrusted JavaScript evaluation, unbounded accept lifetime, + blocking reads, and no deadline. + +The 1.2.1/1.3 loader uses Git blob +`21663be4a0fab6b6a829824946f54706fb1be839`; 1.4/1.5 use +`decb42ba0bbd2bdc8d1568d33b6d772f9fe24d8e`; 1.6 uses +`dd4957530e7a88c889877a7dcb3433642a71e019`. Release 1.6 added ELF-magic +rejection. Release 1.4 added streaming logs over the accepted connection. + +This is a concrete official implementation, but it is not a port-9020 +implementation. + +## Lapse and port 9021 + +The official chain is: + +`Remote JS Loader (~50000) → lapse.js → load_aioshellcode → embedded kexp + elfldr bytes → elfldr listener 9021` + +Relevant source: + +- `payloads/lapse.js:1707-1717` requires `load_aioshellcode` and rejects + firmware above 10.01; +- `payloads/lapse.js:1725-1734` creates a per-run `lapse.fail` marker; +- `payloads/lapse.js:1639` calls `load_aioshellcode` after exploit setup; +- `aioshellcode.js:12-24` locates embedded files in sandbox slots; +- `aioshellcode.js:26-45` maps the kexp binary with JIT shared executable + memory; +- `aioshellcode.js:47-76` starts and joins its thread; +- `aioshellcode.js:78-90` reads the embedded elfldr into process memory; +- `aioshellcode.js:107-117` passes pipe state, `allproc`, elfldr address, and + size to the embedded binary; +- `misc.js:262-302` treats a successful loopback connection to 9021 as a + jailbroken/elfldr-running signal; +- `README.md:127-137` tells the host to send an ELF to 9021 after Lapse. + +The port-9021 listener itself is contained in an ELF binary. Y2JB contains no +generator or listener source for the 1.6 ELF. Release 1.5 embeds the already +pinned public elfldr v0.23 asset byte-for-byte; release 1.6 embeds a distinct +binary. No 1.6 elfldr source commit is invented. + +## Safety classification + +The Remote JS Loader is independent of an already running elfldr and Payload +Manager at source-architecture level, but deployed use is unbound because the +local backup is not an official outer asset. It also uses ~50000, not 9020. + +Consequently: + +- `PORT_9020_IMPLEMENTATION_FOUND`: false; +- `PORT_9020_IMPLEMENTATION_PARTIAL`: false; +- `PORT_9020_REFERENCE_ONLY`: true; +- Phase 0.9F offline design allowed: false. + +## Security and robustness findings + +| Finding | Classification | Evidence and consequence | +|---|---|---| +| Receiver dispatches UTF-8 through `eval` | `SESSION_CRASH_RISK` | arbitrary JavaScript can hang or terminate the current exploit session | +| No receiver deadline; infinite accept loop | `SESSION_CRASH_RISK` | blocked reads and repeated listener lifetime have no bounded cleanup | +| Buffer limit can dispatch without proven EOF | `PROTOCOL_INTEGRITY_RISK` | a 512000-byte prefix can be treated as complete | +| No length, authentication, checksum, or hash | `PROTOCOL_INTEGRITY_RISK` | source does not bind received bytes to an intended request | +| Sender has no timeout, acknowledgement, response read, or hash | `PROTOCOL_INTEGRITY_RISK` | local `sendall` completion is not peer receipt or execution proof | +| Lapse creates `common_temp/lapse.fail` | `UNKNOWN` | source proves a filesystem side effect, but deployed persistence and reboot semantics remain unobserved | +| Lapse/kexp intentionally changes kernel state | `BRICK_RELEVANT` | it is outside every authorization in this phase and cannot be treated as read-only | +| Embedded 1.6 elfldr has no source/generator | `UNKNOWN` | listener bounds, parser, mapping, cleanup, and crash behavior cannot be source-audited here | +| Sender usage errors return implicit success | `HOST_ONLY` | automation could misread a local invocation error; no device action occurred | + +A session crash is not automatically a brick: the reviewed Remote JS receive +function does not itself replace a live file or enable autoload. However, the +local backup is unbound and the subsequent Lapse path contains explicit kernel +and filesystem effects, so no device-risk conclusion or recovery guarantee is +promoted from that source architecture. + +## Firmware selection and reboot boundary + +At release 1.6: + +- `main.js:681-729` identifies supported YouTube app layouts and labels + versions `01.000.003`, `01.000.030`, and `01.009.202` with minimum firmware + 4.03, 12.20, and 13.20 respectively; +- `misc.js:92-108` obtains `kern.sdk_version` and formats the firmware value; +- `main.js:967-971` assigns and reports that value; +- `payloads/lapse.js:1707-1717` requires `load_aioshellcode` and rejects + versions above 10.01. + +Thus 9.60 passes the release-source comparison in Lapse. It does not prove +that release 1.6 is deployed, that its masks match the device, or that the +runtime chain succeeds. + +Listener sockets, JavaScript state, mappings, threads, and loaded code are +process- or kernel-resident in source architecture. The Y2JB cache and +`lapse.fail` path are filesystem state. Source alone does not prove which +state a reboot removes, whether the original trigger remains usable, or +whether a crash permits clean restart. The classification stays +`REBOOT_RECOVERY_UNPROVEN`. diff --git a/docs/runtime/phase-0.9e-r-provenance-gaps.md b/docs/runtime/phase-0.9e-r-provenance-gaps.md new file mode 100644 index 0000000..f668055 --- /dev/null +++ b/docs/runtime/phase-0.9e-r-provenance-gaps.md @@ -0,0 +1,63 @@ +# Phase 0.9E-R remaining provenance gaps + +Date: 2026-07-18 + +Overall decision: `LOCAL_BACKUP_NOT_CORRELATED` + +## Closed questions + +- The local outer ZIP retains its Phase-0.9E size and SHA-256. +- The official current inventory consists of releases 1.3, 1.5, and 1.6 and + tags `Y2JB-1.2.1`, 1.3, 1.4, 1.5, and 1.6. +- All seven current official release assets have official SHA-256 digests, + names, and sizes different from the local outer ZIP. +- Official tag commits, trees, source archives, and host sender are bound. +- Official Y2JB does not implement a port-9020 listener in the inspected tags. +- Its Remote JS Loader is a dynamic port listener normally seeking 50000. +- Port 9021 belongs to an embedded elfldr path reached after Lapse/kexp. + +## Remaining gaps + +1. The source, author, version, and construction procedure for + `Y2JB-Autoloader-403-1240.zip` are unknown. +2. The MediaFire download marker does not identify an official release asset. +3. The local opaque `SIECAF` content cannot be matched to official source. +4. No evidence binds the local outer ZIP to what was restored on the PS5. +5. No evidence shows whether `download0.dat` or app data was later replaced. +6. No evidence identifies the operator's actual host sender. +7. No evidence explains the previously assumed port-9020 listener. +8. The source/generator identity of the 1.6 embedded elfldr and kexp blobs is + not present in Y2JB. +9. Runtime behavior on firmware 9.60 remains unobserved. +10. Independent recovery and the current Payload Manager backup remain + unresolved installation blockers. +11. The exact requested official source-archive URLs are recorded, but the + transient GitHub redirect URLs were not retained. + +The official sender is one-way. The JavaScript server can write log bytes on +its accepted socket, but `payload_sender.py` never receives them. Therefore no +existing end-to-end duplex result channel is established. Reusing that +connection would require a different host contract; an outbound connection or +temporary listener would require new target design. No output architecture is +selected while the exact-used bootstrap remains unbound. + +## Phase 0.9F gate + +`phase09f_offline_design_allowed=false`. + +The mandatory official outer-asset byte match is absent and the inspected +official source has only a 9020 reference, not a found or partial 9020 +implementation. Device observation cannot be used to repair these facts in +this phase. + +Minimum safe next evidence: + +- a verifiable publisher/provenance record for the exact MediaFire-marked + outer ZIP, or a different operator-provided exact outer ZIP that matches an + official asset; +- a completed non-runtime operator attestation; +- exact identification of any external autoloader or 9020 implementation and + sender used in the real workflow. + +Supplying evidence grants no connection, transfer, installation, execution, +autoload, device-write, or retry authority. diff --git a/docs/runtime/phase-0.9e-r-release-source-binding.md b/docs/runtime/phase-0.9e-r-release-source-binding.md new file mode 100644 index 0000000..d4d0ef6 --- /dev/null +++ b/docs/runtime/phase-0.9e-r-release-source-binding.md @@ -0,0 +1,93 @@ +# Phase 0.9E-R Y2JB release-to-source binding + +Date: 2026-07-18 + +## Official source acquisition + +The official repository was acquired from +`https://github.com/Gezine/Y2JB.git` into ignored host-only path +`work/upstream/Y2JB-official`. It is detached at tag `1.6`: + +- commit: `0dbbf4e7e0203af7e5d101a3256c634edf4e3ba2`; +- tree: `c4344f43af7c268337437e6419548dba6f6bc211`; +- status: clean; +- submodules: none; +- Git LFS pointers: none; +- tracked files at 1.6: 19; +- license: MIT. + +No project file from that tree was executed and no dependency was installed. +The upstream tree remains ignored and is not vendored. + +## Source archives + +Each official GitHub tag source archive was downloaded outside tracked paths, +closed, hashed locally, and compared byte-for-byte by pathname with the exact +Git tree. All regular files match their tag tree. + +The requested URLs were the official GitHub API form +`https://api.github.com/repos/Gezine/Y2JB/zipball/` for each exact tag in +the table. GitHub's redirect target was not retained as provenance and is +listed as a gap rather than reconstructed. + +| Tag | Commit | Tree | Archive bytes | Locally computed SHA-256 | Archive ↔ tree | +|---|---|---|---:|---|---| +| `Y2JB-1.2.1` | `6210f98c23552e6eb3c4c8981f72a200bb6121b2` | `928ceb979881a063457270dde4961ea4a06bc1fa` | 142038 | `dd7b05234aec35696b6d00ea4420f7bc1e09d57a4fe64363fdaa6728a37b7201` | 20/20 files exact | +| `1.3` | `82e6bbf1935d5f41c3bd5bd9e6598cf90bb2e022` | `df29ffe4d8131b5f8b0da1874e34b95a41f0660d` | 185243 | `661993fbeec566bdfc0c5d9b89eeddc4a1f21f4a8c0b7392fddea4f73ce636d6` | 22/22 files exact | +| `1.4` | `02eda2e772df629ecfe5a46459769bd8c994ef3e` | `ec06403b45f7b68e01eb5053f81277dee4910dd2` | 151619 | `53d7fb1e665d0534d446cfe980841a0b8eed7eda35424c2f9c4abad32d5aaec5` | 19/19 files exact | +| `1.5` | `5fa556e6281229057dace3254b099f95261df386` | `db364529c347f34b8ddb186f57bb61dab91113db` | 149934 | `5d964faaf1b941c2fc8a006b10ae909b15d8f2c27d120e08b37196f1e464ad6d` | 19/19 files exact | +| `1.6` | `0dbbf4e7e0203af7e5d101a3256c634edf4e3ba2` | `c4344f43af7c268337437e6419548dba6f6bc211` | 150964 | `8385c439458c6954fd1198760ab0ef8245543235c83db90d041591362027cae4` | 19/19 files exact | + +This establishes `REPRODUCIBLE_CONTENT_BINDING` between each downloaded source +archive and its official Git tree. It establishes no relationship between the +local MediaFire-marked backup and any tag. + +## Static 1.6 inventory + +The 1.6 tree contains one HTML file, ten JavaScript files, three Python host +tools, one embedded kexp binary, and one embedded ELF. It contains no WASM, +service worker, web manifest, submodule, LFS pointer, loader generator, or +build script for the embedded blobs. + +Important 1.6 file identities: + +| Path | Bytes | SHA-256 | +|---|---:|---| +| `README.md` | 7500 | `16bfdaa624b8b04f4a6a4a7d512ca8473ad2db73e80df39a974267403e34f751` | +| `payload_sender.py` | 1064 | `8c87920c41dbdbd66b9f36ca9509f0d6bef9170f351dd97ff831cfb98e642ec6` | +| `log_server.py` | 929 | `463114fd46479a7286706de13beb3f52221f36f3cbe37dc5a27bdb6104787a98` | +| `appinfo_editor.py` | 2008 | `c1bcb453660f597cbc9026dba76519a4929fc3e967c183deec8a1ca56912e2e8` | +| `splash.html` | 596 | `0060fce48b104d57025399814e29e9db83923068e7fce899c6235ff7da2e72ef` | +| `main.js` | 44909 | `4f2a87a4550e453b13ada660136d9e7019230bf8fdf7016e855911bd38f0e3d5` | +| `global.js` | 8760 | `1b39c3fe7576f80f6ff3cd32fcb5eef2cbb972b574897f80a28370fb1afceb1d` | +| `kernel.js` | 9113 | `631aac816ed674dce6460cea49fa62961b06ed9446b14f6340416bb98d0326de` | +| `misc.js` | 16308 | `05df5102239af19a9f5a28fb6994dc213db8ab54c7bd7120243b92409c8f8407` | +| `remotejsloader.js` | 7132 | `30cc6d1535549b2a49b47a9e0c85a3444cf84177be54398691694b7c6505f38e` | +| `aioshellcode.js` | 3870 | `feb50f1a7596dfde61611ae347fc7d2ec2ca34e75d05fa5d91340df2d762d310` | +| `kexp_2026_05_25.bin` | 18912 | `618f4b121cd26f2b259ade4fc3a69b3bdb631667758ae63fb9d316b4362015eb` | +| `elfldr-ps5-1340.elf` | 397000 | `30478bcadb6439e1247451c4ac706b6e1385044dd0f12d6486b6d7057929453b` | + +The embedded 1.6 ELF is an x86-64 PIE with entry `0x4700` and `DT_NEEDED` +entries for `libkernel_web.sprx`, `libSceLibcInternal.sprx`, and +`libSceNet.sprx`. It has no source or generator in Y2JB; its commit identity is +therefore the containing Y2JB blob only, not a claimed elfldr source commit. + +Across tags the embedded elfldr identities are: + +| Tag | Embedded file | SHA-256 | Binding | +|---|---|---|---| +| 1.2.1 / 1.3 | `elfldr.elf` | `00ce93eb217b3720f40f18f47221ed25702847c570593e468823abffbcadd562` | binary only | +| 1.4 | `elfldr_1320_v5.elf` | `5839049ca949462c9e8cb40be35afd783ac07d50a290857a5558ceeb81fb90fe` | binary only | +| 1.5 | `elfldr-ps5-0.23.elf` | `092d16ee0ede0c494947efd38d1a17bbd7cc4b022d3858ea898833c188c703e8` | byte-equal to the already pinned public v0.23 asset | +| 1.6 | `elfldr-ps5-1340.elf` | `30478bcadb6439e1247451c4ac706b6e1385044dd0f12d6486b6d7057929453b` | binary only | + +## Binding decision + +- official tag source archive → Git tree: + `REPRODUCIBLE_CONTENT_BINDING`; +- official release → same-tag source: `OFFICIAL_RELEASE_ASSOCIATION`; +- local outer ZIP → official release: no association; +- local inner `SIECAF` → source: `UNBOUND`; +- local backup overall: `LOCAL_BACKUP_NOT_CORRELATED`. + +No official source association was promoted into a local inner-content match. diff --git a/docs/runtime/phase-0.9e-r2-community-backup-correlation.md b/docs/runtime/phase-0.9e-r2-community-backup-correlation.md new file mode 100644 index 0000000..f7001c4 --- /dev/null +++ b/docs/runtime/phase-0.9e-r2-community-backup-correlation.md @@ -0,0 +1,70 @@ +# Phase 0.9E-R2 community backup correlation + +Date: 2026-07-19 + +Only the three named official GitHub repositories and their release metadata +were inspected. No mirror or general scene download was used. Metadata, +source trees, and release assets were read statically; downloaded archives +were not executed. + +## Candidate matrix + +| Family | Relevant release identity | Firmware statement | Public system backup | Components/autoload | Decision | +|---|---|---|---|---|---| +| `itsPLK/ps5-y2jb-autoloader` | `v0.8-a0146a2`, commit `a0146a2a9229fa5df5b5127257bf2e11ffc6d2de` | 4.03–12.70 | no; README explicitly says the project does not distribute preconfigured system backups | Lapse blob `190996c…`, P2JB blob `510bf4b…`, elfldr submodule `148b71c…`, Payload Manager submodule `5d33ce1…`; exploit and localhost elfldr auto-load, later payloads use `autoload.txt` | `THIRD_PARTY_BUILD_FROM_PUBLIC_SOURCE_POSSIBLE`, but unbound | +| `owendswang/Y2JB-P2JB-bdj_unpatch` | `v1.4-autoloader`, commit `9990e917cdb24c249ce24f4ff18e6e869e99cf51` | tag README says 9.00–12.40 disc; downloaded backup name says 7.61 | yes | Gezine Y2JB base declared; Lapse/P2JB blobs and embedded elfldr are identifiable; no Payload Manager identity at this tag | downloaded candidate inner and structure do not match | +| `lz-anonz/Y2X` | `v1.3`, commit `2ae881259be647738713b83a6a91ffe693ccf9af` | 4.03–12.70 | no current large release asset despite README backup wording | P2JB/Lapse, v1/v2 embedded elfldr, Payload Manager 0.3.1 and FTP; v2 auto-loads Manager/FTP and warns against kstuff in autoload | excluded by asset inventory; no large download | + +## itsPLK + +The relevant tag was published 2026-06-10 09:05:51Z, before the local inner +ZIP timestamp. Its official assets are only: + +- `download0.dat`, 336789504 bytes, + SHA-256 `268c82f7a8f581c804949a86c259c982953ff479c13a3dc9f9da02c354b32d6e`; +- `y2jb_update.zip`, 1152857 bytes, + SHA-256 `301599b05e7eb7224025ef9ed6ebc896d86d2c14bba5c32ced5a88bdf2c3517e`. + +Its README's firmware range, “Autoloader” naming, timing, and credit to Rush +for creating a backup make the family plausible. The same README explicitly +says itsPLK does not distribute such backups. No public hash, backup asset, +MediaFire link, or maker attestation binds this source to the local bytes. +Accordingly it is not an official itsPLK release and is not promoted to +`THIRD_PARTY_REPACK_PROVENANCE_BOUND`. + +## owendswang + +Release `v1.4-autoloader` published 2026-06-10 09:01:19Z includes: + +- `download0_Y2JB+autoloader-20260609.zip`, 333151616 bytes; +- `PS5-12.20-backup-Y2JB+autoloader-20260609-en.zip`, 498991549 bytes; +- `PS5-7.61-backup-Y2JB+autoloader-20260609-en.zip`, 504425435 bytes. + +The 7.61 backup was the sole downloaded community candidate because its +family, date, and size were concrete matches. Its local download exactly +matched GitHub size and SHA-256 +`8552d7c3c7bb3f078d967f56dfe1b488b37e3ed13e10c5fe97ae222482d56897`. +Its inner is 504627200 bytes with SHA-256 +`d4f9b1ba4e32af175a3431a7734085d5420370ebb79f9a32852eb824a90e190e`. +The local inner is 262144 bytes smaller, has a different full hash, differs +byte-for-byte, and has version/count 3/62 rather than 6/58. Normalized +segment layout and section hashes also differ. + +Classification: no `COMMUNITY_INNER_BYTE_MATCH`. + +## lz-anonz + +Release `v1.3` published 2026-06-10 21:58:27Z has only: + +- `USB-ps5_autoloader.rar`, 904337 bytes; +- `v1.3-download0-Y2X-P2JB+Lapse.rar`, 521801 bytes. + +The source contains autoload and embedded components, but the current release +does not expose a system-backup object to compare. README language is not a +byte identity. No large asset was downloaded. + +## Download bound + +One of the maximum three allowed community backups was downloaded. It used one +attempt, no resume, and no retry. The other two families had no public large +candidate justified by their own release metadata. diff --git a/docs/runtime/phase-0.9e-r2-final-provenance-decision.md b/docs/runtime/phase-0.9e-r2-final-provenance-decision.md new file mode 100644 index 0000000..561deb7 --- /dev/null +++ b/docs/runtime/phase-0.9e-r2-final-provenance-decision.md @@ -0,0 +1,67 @@ +# Phase 0.9E-R2 final provenance decision + +Date: 2026-07-19 + +Primary classification: `LOCAL_BACKUP_UNCORRELATED` + +## Decision chain + +1. The complete local ADS establishes `MEDIAFIRE_URL_EXACT` for file key + `jq3fcutuwbb1mrb` and the exact local filename. +2. The exact MediaFire page is still present and establishes + `MEDIAFIRE_OBJECT_METADATA_BOUND`, but exposes no server digest, exact byte + count, public uploader, maker, tutorial, or source. +3. The mandatory official Gezine/Y2JB 1.6 4.03 inner differs in size, + SHA-256, complete bytes, normalized layout, and section hashes. +4. The one justified community backup from owendswang also differs on every + inner and structural measure. +5. itsPLK is a plausible public source family, but explicitly does not publish + system backups and supplies no binding to this MediaFire object. +6. Y2X has no current large system-backup asset at its relevant release. + +Therefore: + +- `OFFICIAL_INNER_BYTE_MATCH`: false; +- `COMMUNITY_INNER_BYTE_MATCH`: false; +- `THIRD_PARTY_REPACK_PROVENANCE_BOUND`: false; +- `SIECAF_STRUCTURAL_RELATION_ONLY`: false; +- `LOCAL_BACKUP_UNCORRELATED`: true. + +`LOCAL_BACKUP_NOT_CORRELATED` from Phase 0.9E-R remains historically correct +for the outer-asset analysis. R2 refines the current fail-closed result after +inner, structure, origin, and community correlation; it does not silently +replace the prior record. + +## Separate identities + +| Question | Result | +|---|---| +| Is the outer ZIP official? | no | +| Is the MediaFire object metadata-bound? | yes | +| Is its maker/source bound? | no | +| Is the inner official/community byte-bound? | no | +| Is even a normalized structural relation established? | no | +| Was this backup restored to the PS5? | `UNPROVEN` | +| Are current PS5 bytes known? | `UNPROVEN` | +| Is firmware-9.60 runtime behavior known? | `UNPROVEN` | + +## Phase 0.9F + +Phase 0.9F offline design may not be reconsidered. Neither of the entry +conditions is met: + +- the inner is not byte-exactly bound to an auditable source family; +- the MediaFire backup is not bound to an auditable maker/release with a + complete actual bootstrap implementation. + +The actual deployed bootstrap, embedded listener closure, autoloader component +manifest, independent recovery, and current device contents remain unproven. +The byte-exact Payload Manager backup remains +`HARD_BLOCKER_FOR_INSTALLATION`. + +## Authorization + +All device, target-build, transfer, execution, installation, lifecycle, +autoload, device-write, rescue-design, and retry fields remain false. No PS5 +action, target code, target artifact, payload, device client, restore, or +execution occurred. diff --git a/docs/runtime/phase-0.9e-r2-inner-archive-correlation.md b/docs/runtime/phase-0.9e-r2-inner-archive-correlation.md new file mode 100644 index 0000000..8dc3f6a --- /dev/null +++ b/docs/runtime/phase-0.9e-r2-inner-archive-correlation.md @@ -0,0 +1,68 @@ +# Phase 0.9E-R2 inner archive correlation + +Date: 2026-07-19 + +Result: `INNER_ARCHIVE_HASH_MISMATCH` + +Phase 0.9E-R excluded an outer byte match. R2 separately tests the plausible +repack case by comparing complete inner `archive.dat` bytes. + +## Local identity + +| Object | Size | SHA-256 | +|---|---:|---| +| local outer ZIP | 504159435 | `805e3f87f0c371223619ffc7d3a7b3c0d41a1fae8a8b1171d9e2f162659e8291` | +| local `PS5/EXPORT/BACKUP/202606102126_00/archive.dat` | 504365056 | `6439834e8856d45b6d6fe699b74c35ca6985a199ea8ecf3e398c018d37be2d55` | + +## Mandatory official candidate + +The exact official Gezine/Y2JB 1.6 asset +`Y2JB_backup_1.6.4.03.zip` was downloaded once from its GitHub release URL. +Resume and retry were disabled. The closed and reopened file was 504395044 +bytes and its locally calculated SHA-256 was +`b01b4f442327f9eca90ffc4506dfa58249e4ac70cb9d7488c856c9e8dfaf37b4`, +equal to GitHub's official digest. + +It was not executed or restored. Static ZIP inventory: + +| Entry | Method | Compressed | Uncompressed | CRC32 | Timestamp | Entry SHA-256 | +|---|---:|---:|---:|---|---|---| +| `PS5/` | 0 | 0 | 0 | `00000000` | `2026-06-09 04:15:20` | empty SHA-256 | +| `PS5/EXPORT/` | 0 | 0 | 0 | `00000000` | `2026-06-09 04:15:20` | empty SHA-256 | +| `PS5/EXPORT/BACKUP/` | 0 | 0 | 0 | `00000000` | `2026-06-09 04:15:20` | empty SHA-256 | +| `PS5/EXPORT/BACKUP/202606090510_00/` | 0 | 0 | 0 | `00000000` | `2026-06-09 04:15:20` | empty SHA-256 | +| `PS5/EXPORT/BACKUP/202606090510_00/archive.dat` | 8 | 504394122 | 504496128 | `7db3b89a` | `2026-06-09 05:15:58` | `3e1f0867d5d8362cbf722d51d0e68c26243d6b33d78460d978580b510c854212` | + +The four directory entries use Unix create-system 3, create/extract version +20, external attributes 1107230720, and empty comments. The inner entry uses +create system/version 3/20, extract version 20, flag bits 8, external +attributes 2176188416, header offset 315, and an empty comment. + +## Full inner comparison + +| Property | Local inner | Official inner | +|---|---:|---:| +| size | 504365056 | 504496128 | +| SHA-256 | `6439834e...37be2d55` | `3e1f0867...0c854212` | +| complete bytes equal | no | no | + +The size differs by 131072 bytes. Complete streaming comparison and both +hashes disagree. Classification is `INNER_ARCHIVE_HASH_MISMATCH`, so the +local outer cannot be classified `REPACKAGED_OFFICIAL_BACKUP`. + +## Community comparison performed + +The one source-motivated community download was +owendswang/Y2JB-P2JB-bdj_unpatch release `v1.4-autoloader`, +`PS5-7.61-backup-Y2JB+autoloader-20260609-en.zip`. GitHub and local size were +504425435 bytes; both SHA-256 values were +`8552d7c3c7bb3f078d967f56dfe1b488b37e3ed13e10c5fe97ae222482d56897`. + +Its inner `PS5/EXPORT/BACKUP/202606100638_00/archive.dat` is 504627200 bytes, +CRC32 `cf138df3`, and SHA-256 +`d4f9b1ba4e32af175a3431a7734085d5420370ebb79f9a32852eb824a90e190e`. +It is neither the same size, hash, nor bytes as the local inner: +`INNER_ARCHIVE_HASH_MISMATCH`. + +Outer mismatch did not substitute for inner analysis. The negative inner +results now independently exclude both downloaded candidates. diff --git a/docs/runtime/phase-0.9e-r2-local-download-provenance.md b/docs/runtime/phase-0.9e-r2-local-download-provenance.md new file mode 100644 index 0000000..c9e135c --- /dev/null +++ b/docs/runtime/phase-0.9e-r2-local-download-provenance.md @@ -0,0 +1,78 @@ +# Phase 0.9E-R2 local download provenance + +Date: 2026-07-19 + +Result: `MEDIAFIRE_URL_EXACT` + +This evidence is host-only. No PS5 address, request, connection, transfer, +restore, target build, or execution was used. + +## Local object + +| Field | Value | +|---|---| +| name | `Y2JB-Autoloader-403-1240.zip` | +| size | `504159435` | +| SHA-256 | `805e3f87f0c371223619ffc7d3a7b3c0d41a1fae8a8b1171d9e2f162659e8291` | +| creation UTC | `2026-06-23T14:10:39.0466596Z` | +| modification UTC | `2026-06-23T14:11:10.6992328Z` | + +The file was read without changing it. Its `Zone.Identifier` alternate data +stream is 327 bytes and has SHA-256 +`70928a3d66cbb9f584596414c4fe535b498dd5ed7c5f55bbeae4efeb146ea841`. +It contains exactly the `ZoneTransfer` section and these fields: + +| Field | Safe evidence | +|---|---| +| `ZoneId` | `3` | +| `ReferrerUrl` | host `www.mediafire.com`, path `/`, no query; full-value SHA-256 `9a0ff5a3e34286b2e53192003fca8b6c7b00ad6df3c681765da04ccaa54c0919` | +| `HostUrl` | host `download2434.mediafire.com`, file key `jq3fcutuwbb1mrb`, filename `Y2JB-Autoloader-403-1240.zip`; full-value SHA-256 `a39f05923d0a5368ea5138c1974ac109bb09f8ab795c6edb8e470d8dfe4d784c` | +| `LastWriterPackageFamilyName` | absent | +| `AppZoneId` | absent | +| other fields | none | + +The transient signed path component is deliberately redacted. It was neither +committed nor reproduced here. The complete value remains only in the +unchanged local ADS. + +## Browser metadata + +The bounded search covered existing Chrome Default, Chrome System Profile, +and Edge Default Chromium History databases. The query was limited to the +exact filename, size, filesystem-date window, MediaFire host, and file key. +Brave, standalone Chromium, Opera, and Opera GX profiles were absent. + +Three read-only database copies were made because browser databases can be +live-locked. Chrome Default, Chrome System Profile, and Edge Default each +returned `NO_MATCH`; no download rows were exported and the originals were +not changed. The temporary copies were removed with the rest of the research +directory. + +Browser classification: `NO_BROWSER_HISTORY_MATCH`. + +## Exact MediaFire page + +Only +`https://www.mediafire.com/file/jq3fcutuwbb1mrb/Y2JB-Autoloader-403-1240.zip/file` +was requested. Static HTML was 333321 bytes with SHA-256 +`655e4c3c2e5c423019b0195ca863ee8906f7ca02601b773b39688a590bf7fcb7`. +It displayed: + +- filename `Y2JB-Autoloader-403-1240.zip`; +- size `480.8MB`; +- uploaded value `2026-06-11 15:32:16`; +- upload country `United Kingdom`; +- the same MediaFire file key. + +No public account/uploader, user description, tutorial, maker, server digest, +or exact server byte count was exposed. The download object was not +downloaded again. The local ADS plus page provide +`MEDIAFIRE_OBJECT_METADATA_BOUND`, not `MEDIAFIRE_OBJECT_BYTE_BOUND` and not +maker/source provenance. + +## Boundary + +The download-origin classification is `MEDIAFIRE_URL_EXACT`. That describes +where the local bytes were downloaded from. It does not prove who created +them, which public source generated them, whether they were restored, or what +is currently on the PS5. diff --git a/docs/runtime/phase-0.9e-r2-siecaf-structural-analysis.md b/docs/runtime/phase-0.9e-r2-siecaf-structural-analysis.md new file mode 100644 index 0000000..b9a4964 --- /dev/null +++ b/docs/runtime/phase-0.9e-r2-siecaf-structural-analysis.md @@ -0,0 +1,70 @@ +# Phase 0.9E-R2 SIECAF structural analysis + +Date: 2026-07-19 + +Result: all pairwise comparisons are `SIECAF_LAYOUT_DIFFERENT`. + +## Evidence and scope + +`tools/inspect_siecaf_header.py` is a host-only read-only parser. Its fixed +little-endian layouts are bound to: + +- PS5 DevWiki `Archive.dat` structure documentation; +- `c0w-ar/ps5-bar-tool` commit + `36d014672bc87577a6e0d750c2cccadc3fae0854`, tree + `d3e973b60432fdc32bd16e3b7424e82dcc38a23f`; +- public `include/bar_file.h` blob + `fdbc368353a7797464873ada306f0297257eb95e`. + +The public header names the 64-bit value after the magic `unknown`, and the +later signed 32-bit field `version`. Versions 3 and 6 were observed and are +parsed using that same public fixed-width layout. Other versions fail closed. + +The parser reads only the 0x58-byte header, 0x40-byte metadata records, and +0x30-byte hash records. It never decrypts, guesses keys, extracts content, +executes `ps5-bar-tool`, writes output files, or opens a network connection. +The 16-byte header field named `key` by the public source and the complete raw +header are not emitted; only their SHA-256 fingerprints are recorded. This +preserves exact structural comparison without committing cryptographic header +material. + +It checks exact magic, table arithmetic with uint64 overflow guards, table and +declared-data bounds, 64 KiB alignment, coherent aligned/unaligned sizes, +metadata `(section_id, part_number)` identity, hash-table index coverage, +duplicates, overlaps, gaps, and trailing data. Repeated section IDs with +different part numbers are reported but are valid multipart records. + +## Header comparison + +| Field | Local | Official Y2JB 1.6 4.03 | owendswang v1.4 autoloader 7.61 | +|---|---:|---:|---:| +| inner size | 504365056 | 504496128 | 504627200 | +| version | 3 | 3 | 6 | +| segment count | 62 | 64 | 58 | +| data offset | 65536 | 65536 | 65536 | +| declared data size | 504299520 | 504430592 | 504561664 | +| header SHA-256 | `4454809c9831770901491368f08b648129aef7ee3148aff17df8716ff3a5cd76` | `1af2bd937e5b6a2386331893f7ea1295661858cb91c90ee728141a4fec2dbf9b` | `db2a9677f051b372e208261d767ac45f653374426a5594ce3306568138ebd3ae` | + +All three parse as `SIECAF_VALID_STRUCTURE`. None has gaps, overlaps, trailing +data, duplicate metadata composite keys, or duplicate hash-table IDs. + +## Normalized fingerprints + +| Object | Segment table SHA-256 | Hash blocks SHA-256 | Layout SHA-256 | Structural SHA-256 | +|---|---|---|---|---| +| local | `ee35a92fe5e46c307310715fc5bd0edb9ac46c2046b7d77e9c1c437446e399ea` | `6e052f186dc8e683d501e376d79dcec1ba0d3268c257b6071e96eae738a7be24` | `c6dac8313ef99accba68b4a36871f0d8ae99676df0b721d4447ecb05a12a6539` | `7bb1b70187d1f48370b9bae218429af79105585bd043307824c0d84982e6922f` | +| official | `d31def8e731d7bba0eaf23177f7c7245ef17f5d37ade53c9ca18744154bc8e25` | `c3236f5d85a4d7741be5a1fda5ce96d2bb7f005b0e48fed043af5ea48fb045d2` | `de6cc47686e5e301b725267b9b777c80a07a788e51f214cfb6e9d7f6e2e11110` | `9c453e1fa99ef1f1e6fe1d992f9b0a2ede4dbd4b9a2ad8551c08c8c61ed9be2a` | +| community | `f0aed4ae115866010902671a511983c3e6e21d1a7ab187bae7f36566979beb89` | `434b209865af8c5815b1277073db59348fddf65086908b2fd00dee5324459bcb` | `be7fdf87361c17fa52d3b4b263f4ee0cce69e924f8dab0ab66c554b4d9557549` | `4884c54e280969371ae1d0b84533be5a7c50a61dff6931cf595fdb5862655218` | + +Pairwise outcomes: + +- local versus official: `SIECAF_LAYOUT_DIFFERENT`; +- local versus community: `SIECAF_LAYOUT_DIFFERENT`; +- official versus community: `SIECAF_LAYOUT_DIFFERENT`. + +`manifests/runtime/phase-0.9e-r2-siecaf-fingerprints.json` contains every +non-secret normalized input field, metadata record, section hash, redacted +header-field hashes, parser warning/error array, and comparison. Structural +hashes are non-authorizing fingerprints: +even `SIECAF_STRUCTURAL_EXACT` would not prove decrypted-content identity or +deployment. Here, not even normalized layout equality exists. diff --git a/docs/runtime/phase-0.9e-reboot-and-crash-model.md b/docs/runtime/phase-0.9e-reboot-and-crash-model.md new file mode 100644 index 0000000..4061358 --- /dev/null +++ b/docs/runtime/phase-0.9e-reboot-and-crash-model.md @@ -0,0 +1,57 @@ +# Phase 0.9E reboot and crash model + +Date: 2026-07-18 + +Classification: `REBOOT_RECOVERY_UNPROVEN` + +## State model + +| State | Evidence | Expected lifetime | Reboot statement | +|---|---|---|---| +| exact exploit/browser state | implementation absent | unknown | `UNPROVEN` | +| exact port-9020 listener state | implementation absent | unknown | `UNPROVEN` | +| hardened elfldr entry process | `main.c` source | temporary by design | hardware behavior unproven | +| embedded bootstrap in `SceRedisServer` | `main.c`/`bootstrap.c` | temporary injection stage | likely process/kernel state, not a proven recovery mechanism | +| `elfldr.elf` port-9021 service | `socksrv.c` | process-local background service by design | README says rest-mode resume, not reboot persistence | +| controlled Manager | source/offline artifact | process-local when launched | explicitly not installed in current controlled-runtime evidence | +| Manager configuration/autoload files | source paths only | filesystem-persistent if actually present | current device values and use unobserved | +| Y2JB-named `SIECAF` backup | host file | host-persistent | not proof of device installation | + +The hardened elfldr README states that the port-9021 service survives payload +crashes and resumes after rest mode. It does not say that the service survives +a full reboot. Source creates process state and does not install a boot item, +but the complete device bootstrap/autoload environment is not available. + +## Crash cases + +- A payload crash under hardened port 9021 is isolated by design from the + service process. That is source architecture, not a tested firmware-9.60 + guarantee. +- A crash in the unknown port-9020 listener may terminate a process, exploit + state, or more. No cleanup, watchdog, retry, or restart path is available for + audit. +- A malformed or truncated 9020 transfer has unknown allocation, mapping, + execution, and close behavior. +- A hang has no proven host abort, deadline, or recovery response. +- No source proves that a bad payload cannot create persistent state; a future + rescue contract must explicitly prohibit such writes. + +## Anti-brick boundary + +A session crash is not automatically brick-relevant if live files, autoload, +configuration, mounts, and boot state remain unchanged and a reboot or fresh +jailbreak session restores the executor. Those premises are not yet proven for +the actual chain. + +Consequently: + +- `restartable_after_reboot=unproven`; +- no guarantee is made that reboot always restores access; +- no automatic retry is permitted; +- no future device phase may rely on reboot recovery until the exact package + and its state transitions are audited. + +Minimum evidence to promote to `REBOOT_RECOVERY_SUPPORTED_BY_DESIGN` is an +exact source-bound trigger/listener implementation proving that all bootstrap +state is session-local or safely reproducible, plus an exact operator procedure +that does not rely on a live component or autoload. diff --git a/docs/runtime/startup-callgraph.md b/docs/runtime/startup-callgraph.md new file mode 100644 index 0000000..b1cae81 --- /dev/null +++ b/docs/runtime/startup-callgraph.md @@ -0,0 +1,198 @@ +# SDK v0.41 startup call graph + +This is an offline source, relocation, and disassembly audit of the installed +SDK v0.41 `target/lib/crt1.o`. It describes the unsafe stock startup, not a new +Chimera GFX artifact. The complete machine-readable reachable graph contains +31 functions and 182 call or tail-call edges in +`manifests/runtime/phase-0.5-startup-audit.json`. + +## Transitive startup objects + +`crt/Makefile` compiles and partially links exactly these objects into one +relocatable `crt1.o` with `prospero-lld -r`: + +| Object | Public source | Role | +|---|---|---| +| `crt.o` | `crt/crt.c` | entry, BSS, init/run/terminate | +| `syscall.o` | `crt/syscall.c` | syscall bootstrap | +| `klog.o` | `crt/klog.c` | kernel log helpers | +| `nid.o` | `crt/nid.c` | symbol-name encoding | +| `kernel.o` | `crt/kernel.c` | kernel transport and process helpers | +| `rtld.o` | `crt/rtld.c` | runtime-loader core | +| `rtld_so.o` | `crt/rtld_so.c` | shared-object mapping and relocation | +| `rtld_sprx.o` | `crt/rtld_sprx.c` | Sce module load/start/stop/unload | +| `rtld_payload.o` | `crt/rtld_payload.c` | payload relocation and arrays | +| `rtld_dlfcn.o` | `crt/rtld_dlfcn.c` | `dlopen`/`dlsym` facade | +| `mdbg.o` | `crt/mdbg.c` | memory-debug transport helpers | +| `patch.o` | `crt/patch.c` | credentials and syscall-bound patches | + +The files named `crti.o`, `crtn.o`, `crtbegin.o`, `crtend.o`, `crtbeginS.o`, +and `crtendS.o` are empty `ar` archives at the installed release. The audit +enumerates them rather than assuming their filename implies contents. + +## Reachable startup paths + +The important conservative paths from source, relocations, and disassembly are: + +```text +_start +|- zero [__bss_start, __bss_end) +|- __crt_syscall_init +|- __kernel_init +| `- kernel_copyout (reachable in compiled object) +|- __klog_init +|- kernel_dynlib_dlsym -> set __isthreaded = 1 +|- __patch_init UNSAFE +| |- kernel_get_ucred_caps -> kernel_copyout +| |- kernel_get_ucred_attrs -> kernel_copyout +| |- kernel_set_ucred_caps -> kernel_copyin KERNEL WRITE +| |- kernel_set_ucred_attrs -> kernel_copyin KERNEL WRITE +| `- kernel_copyin at process offsets 0xf0/0xf8 KERNEL WRITE +|- __rtld_init UNSAFE +| |- __rtld_sprx_init -> sceKernelLoadStartModule +| |- __rtld_so_init +| |- __rtld_payload_init +| `- __rtld_dlfcn_init +|- __rtld_payload_new/open/init +| `- constructors +|- main +|- __rtld_lib_fini/close/destroy +| `- destructors +`- payload_terminate + |- ret for the CRT's detected hijacked-process case + |- resolved exit for another case + `- trap when exit cannot be resolved +``` + +Indirect calls are retained as `INDIRECT:` in the JSON graph rather +than guessed. A relocation to the `sceKernelLoadStartModule` function-pointer +slot is retained explicitly. This makes the graph conservative without +inventing a firmware ABI. + +## Linked but not startup-reachable + +The monolithic `crt1.o` also contains `__dlopen`, `__dlsym`, +`kernel_mprotect`, `kernel_overlap_sockets`, and +`kernel_set_vmem_protection`, although the disassembly graph did not find them +reachable from the stock `_start`. Their presence demonstrates why an import +inventory alone would be insufficient: prohibited-capability code can be +statically linked without appearing as an undefined symbol. + +## Driver and linker behavior + +The `prospero-clang` wrapper normally adds `crt1.o`, `libc`, `libkernel_web`, +`libSceLibcInternal`, and `libSceNet`. A no-output `-###` trace with both +`-nostartfiles` and `-nodefaultlibs` adds none of them. The linker wrapper still +selects the public `elf_x86_64.x` script, PIE mode, an ELF x86-64 emulation, +0x4000 maximum page size, emulated TLS support, and GNU hashing unless the +caller overrides applicable options. + +The linker script retains preinit/init/fini arrays and a dynamic segment. It +also declares the text load segment with flags `0x7`; that is a source fact, +not a claim about final loader-enforced permissions. + +## Why there is no custom linker map + +The loader return contract failed before the build gate. In accordance with +the task rule, no custom `_start` source or PS5 ELF was created. Therefore a +custom linker map, imports, `DT_NEEDED`, relocations, arrays, TLS inventory, +double-build hash, and artifact disassembly are all explicitly recorded as +`NOT_PERFORMED_BLOCKED_BEFORE_BUILD`, not silently treated as passing. + +## Phase 0.6 exact runtime chain + +This addendum is the reviewed source call graph for the installed chain. No new +probe artifact exists, so there is no probe disassembly call graph to report. + +```text +Payload Manager /loadpayload: +`- ps5_launch_elf(path) + |- open(path, O_RDONLY) + |- connect(127.0.0.1:9021) + `- send(ELF bytes) + +elfldr serve_elfldr +`- elfldr_spawn + |- rfork_thread + | `- child: elfldr_rfork_entry + | |- sys_budget_set(0) + | |- open(/dev/deci_{stdin,stdout,stderr}) + | |- ptrace(PT_TRACE_ME) + | `- execve(SceSpZeroConf) + |- parent: pt_syscall(599) + | `- pt_syscall -> unbounded pt_step loop + |- elfldr_set_heap_size(-1) + |- kernel_mprotect(eboot entry page, RWX) + |- install INT3 -> continue -> wait -> restore byte + `- elfldr_exec + |- backup jaildir/rootdir/caps/authid + |- elfldr_raise_privileges + | `- set rootdir/jaildir/UID/caps + |- elfldr_prepare_exec + | |- elfldr_load + | | |- mmap/copy/RELATIVE relocations + | | `- per-segment mprotect + msync + | |- elfldr_payload_args + | | |- mmap page + | | |- two IPv6 sockets + overlap + | | `- pipe + six payload_args_t fields + | `- [RSP-8]=old RIP; RIP=entry; RDI=args + |- restore jaildir/rootdir/caps/authid (not UID) + `- ptrace(PT_DETACH) + +SDK v0.41 payload _start +|- clear BSS +|- initialize syscall/kernel/klog +|- __patch_init +|- initialize rtld and constructors +|- main +|- destructors and rtld cleanup +`- payload_terminate + `- return OR exit OR trap (exact child branch unproven) +``` + +The exact elfldr release binary is stripped, so the source graph is bound to +the release by source commit and release-asset SHA-256. Static binary evidence +records entry `0x4700`, 164 relocations, 24 undefined dynamic symbols, three +`DT_NEEDED` modules, zero-byte init/fini arrays, no TLS, and hashes of the +complete readelf report and disassembly in +`manifests/runtime/phase-0.6-loader-runtime-audit.json`. + +## Phase 0.7 hardened callgraph addendum + +Phase 0.7 uses new hardened elfldr and controlled Payload Manager binaries; +the graph above remains the historical stock Phase-0.6 chain. + +The lifecycle source-level action is deliberately narrow: + +```text +SDK _start +|- BSS and stock SDK v0.41 runtime initialization +|- __patch_init +|- main +| |- sceKernelSendNotificationRequest +| `- _exit +`- payload_terminate [not reached from main] +``` + +The hardened loader retains its required first-stage and ptrace operations, +but each single-step operation now has both a one-second monotonic deadline +and a 65,536-step ceiling. Cleanup tracks the child, breakpoint, mappings, and +descriptors in one state. The success and failure paths restore all five +credential fields and verify every restoration. The runtime supervisor waits +at most 2000 ms, then uses a bounded SIGTERM/SIGKILL/reap sequence. + +The controlled manager opens, hashes, rewinds, and streams one no-follow file +descriptor. The hardened receiver independently rehashes the received bytes +and checks the exact versioned header and permanent denylist. + +The committed machine audit contains every extracted call edge, not a sample: + +- lifecycle: 499 edges; +- hardened elfldr: 1032 edges; +- controlled manager: 478 edges. + +Full disassembly, normalized linker maps, symbol/relocation/section reports, +and text callgraphs are in the ignored `outputs/phase07/audit/` package. Their +hashes and the complete edge arrays are committed in +`manifests/runtime/phase-0.7-offline-audit.json`. diff --git a/include/chimera/gfx/adapters/retroarch.h b/include/chimera/gfx/adapters/retroarch.h new file mode 100644 index 0000000..8bae107 --- /dev/null +++ b/include/chimera/gfx/adapters/retroarch.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +#ifndef CHIMERA_GFX_ADAPTERS_RETROARCH_H +#define CHIMERA_GFX_ADAPTERS_RETROARCH_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct chimera_gfx_retroarch_adapter_info { + size_t struct_size; + uint32_t api_version; + uint32_t accepts_software_frames; + uint32_t supports_hardware_contexts; + uint32_t supports_rgui; + uint32_t has_sdl_software_fallback; +} chimera_gfx_retroarch_adapter_info; + +#define CHIMERA_GFX_RETROARCH_ADAPTER_INFO_INIT \ + {sizeof(chimera_gfx_retroarch_adapter_info), 0u, 0u, 0u, 0u, 0u} + +chimera_gfx_status chimera_gfx_retroarch_query_scaffold( + chimera_gfx_retroarch_adapter_info *out_info); + +chimera_gfx_status +chimera_gfx_retroarch_bind_scaffold(chimera_gfx_context *context, + uint32_t request_hardware_context); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/chimera/gfx/adapters/sdl2.h b/include/chimera/gfx/adapters/sdl2.h new file mode 100644 index 0000000..ac7596e --- /dev/null +++ b/include/chimera/gfx/adapters/sdl2.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +#ifndef CHIMERA_GFX_ADAPTERS_SDL2_H +#define CHIMERA_GFX_ADAPTERS_SDL2_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct chimera_gfx_sdl2_adapter_info { + size_t struct_size; + uint32_t api_version; + uint32_t accelerated_renderer_available; + uint32_t reuses_ps5_window_backend; + uint32_t reuses_ps5_input_audio; + uint32_t software_fallback_required; +} chimera_gfx_sdl2_adapter_info; + +#define CHIMERA_GFX_SDL2_ADAPTER_INFO_INIT \ + {sizeof(chimera_gfx_sdl2_adapter_info), 0u, 0u, 0u, 0u, 0u} + +chimera_gfx_status +chimera_gfx_sdl2_query_scaffold(chimera_gfx_sdl2_adapter_info *out_info); + +chimera_gfx_status +chimera_gfx_sdl2_create_renderer_scaffold(chimera_gfx_context *context, + uint32_t request_acceleration); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/chimera/gfx/chimera_gfx.h b/include/chimera/gfx/chimera_gfx.h new file mode 100644 index 0000000..172f1e2 --- /dev/null +++ b/include/chimera/gfx/chimera_gfx.h @@ -0,0 +1,201 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +#ifndef CHIMERA_GFX_CHIMERA_GFX_H +#define CHIMERA_GFX_CHIMERA_GFX_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define CHIMERA_GFX_API_VERSION 2u + +typedef struct chimera_gfx_context chimera_gfx_context; +typedef struct chimera_gfx_surface chimera_gfx_surface; +typedef struct chimera_gfx_texture chimera_gfx_texture; + +typedef enum chimera_gfx_status { + CHIMERA_GFX_STATUS_OK = 0, + CHIMERA_GFX_STATUS_INVALID_ARGUMENT = 1, + CHIMERA_GFX_STATUS_VERSION_MISMATCH = 2, + CHIMERA_GFX_STATUS_UNSUPPORTED = 3, + CHIMERA_GFX_STATUS_BACKEND_UNAVAILABLE = 4, + CHIMERA_GFX_STATUS_OUT_OF_MEMORY = 5, + CHIMERA_GFX_STATUS_SAFETY_POLICY = 6, + CHIMERA_GFX_STATUS_INTERNAL_ERROR = 7, + CHIMERA_GFX_STATUS_INVALID_STATE = 8, + CHIMERA_GFX_STATUS_RESOURCE_BUSY = 9, + CHIMERA_GFX_STATUS_LIMIT_EXCEEDED = 10 +} chimera_gfx_status; + +typedef enum chimera_gfx_backend { + CHIMERA_GFX_BACKEND_MOCK = 1, + CHIMERA_GFX_BACKEND_PS5 = 2 +} chimera_gfx_backend; + +typedef enum chimera_gfx_config_flag { + /* Reserved for a future hardware phase. Phase 0 always rejects it. */ + CHIMERA_GFX_CONFIG_ALLOW_HARDWARE_RENDERING = 1u << 0 +} chimera_gfx_config_flag; + +/* Backward-compatible spelling retained for the initial API revision. */ +#define CHIMERA_GFX_CONFIG_ALLOW_RENDERING \ + CHIMERA_GFX_CONFIG_ALLOW_HARDWARE_RENDERING + +typedef enum chimera_gfx_capability_flag { + CHIMERA_GFX_CAPABILITY_BACKEND_AVAILABLE = 1u << 0, + CHIMERA_GFX_CAPABILITY_NON_RENDERING = 1u << 1, + CHIMERA_GFX_CAPABILITY_HOST_TEST_ONLY = 1u << 2, + CHIMERA_GFX_CAPABILITY_SURFACE_LIFECYCLE = 1u << 3, + CHIMERA_GFX_CAPABILITY_TEXTURE_LIFECYCLE = 1u << 4, + CHIMERA_GFX_CAPABILITY_TEXTURE_UPLOAD = 1u << 5, + CHIMERA_GFX_CAPABILITY_PRESENT_MODEL = 1u << 6 +} chimera_gfx_capability_flag; + +typedef enum chimera_gfx_pixel_format { + CHIMERA_GFX_PIXEL_FORMAT_RGBA8_UNORM = 1 +} chimera_gfx_pixel_format; + +typedef struct chimera_gfx_config { + size_t struct_size; + uint32_t api_version; + uint32_t backend; + uint32_t flags; +} chimera_gfx_config; + +#define CHIMERA_GFX_CONFIG_INIT \ + {sizeof(chimera_gfx_config), CHIMERA_GFX_API_VERSION, \ + CHIMERA_GFX_BACKEND_MOCK, 0u} + +typedef struct chimera_gfx_capabilities { + size_t struct_size; + uint32_t api_version; + uint32_t backend; + uint32_t flags; + uint32_t max_surface_width; + uint32_t max_surface_height; + uint32_t max_texture_width; + uint32_t max_texture_height; + uint32_t max_live_surfaces; + uint32_t max_live_textures; +} chimera_gfx_capabilities; + +#define CHIMERA_GFX_CAPABILITIES_INIT \ + {sizeof(chimera_gfx_capabilities), 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u} + +typedef struct chimera_gfx_surface_desc { + size_t struct_size; + uint32_t api_version; + uint32_t width; + uint32_t height; + uint32_t format; + uint32_t flags; +} chimera_gfx_surface_desc; + +#define CHIMERA_GFX_SURFACE_DESC_INIT(width_value, height_value) \ + {sizeof(chimera_gfx_surface_desc), \ + CHIMERA_GFX_API_VERSION, \ + (width_value), \ + (height_value), \ + CHIMERA_GFX_PIXEL_FORMAT_RGBA8_UNORM, \ + 0u} + +typedef struct chimera_gfx_texture_desc { + size_t struct_size; + uint32_t api_version; + uint32_t width; + uint32_t height; + uint32_t format; + uint32_t flags; +} chimera_gfx_texture_desc; + +#define CHIMERA_GFX_TEXTURE_DESC_INIT(width_value, height_value) \ + {sizeof(chimera_gfx_texture_desc), \ + CHIMERA_GFX_API_VERSION, \ + (width_value), \ + (height_value), \ + CHIMERA_GFX_PIXEL_FORMAT_RGBA8_UNORM, \ + 0u} + +typedef struct chimera_gfx_texture_upload_info { + size_t struct_size; + uint32_t api_version; + const void *pixels; + size_t row_pitch; + size_t data_size; +} chimera_gfx_texture_upload_info; + +#define CHIMERA_GFX_TEXTURE_UPLOAD_INIT(pixel_data, pitch_value, size_value) \ + {sizeof(chimera_gfx_texture_upload_info), CHIMERA_GFX_API_VERSION, \ + (pixel_data), (pitch_value), (size_value)} + +typedef struct chimera_gfx_present_info { + size_t struct_size; + uint32_t api_version; + chimera_gfx_surface *surface; + chimera_gfx_texture *texture; + uint32_t flags; +} chimera_gfx_present_info; + +#define CHIMERA_GFX_PRESENT_INFO_INIT(surface_value, texture_value) \ + {sizeof(chimera_gfx_present_info), CHIMERA_GFX_API_VERSION, \ + (surface_value), (texture_value), 0u} + +typedef struct chimera_gfx_surface_stats { + size_t struct_size; + uint32_t api_version; + uint64_t present_count; + uint64_t last_present_serial; + uint64_t last_texture_hash; +} chimera_gfx_surface_stats; + +#define CHIMERA_GFX_SURFACE_STATS_INIT \ + {sizeof(chimera_gfx_surface_stats), 0u, 0u, 0u, 0u} + +chimera_gfx_status chimera_gfx_create(const chimera_gfx_config *config, + chimera_gfx_context **out_context); + +chimera_gfx_status +chimera_gfx_get_capabilities(const chimera_gfx_context *context, + chimera_gfx_capabilities *out_capabilities); + +chimera_gfx_status +chimera_gfx_surface_create(chimera_gfx_context *context, + const chimera_gfx_surface_desc *desc, + chimera_gfx_surface **out_surface); + +chimera_gfx_status +chimera_gfx_surface_get_stats(const chimera_gfx_surface *surface, + chimera_gfx_surface_stats *out_stats); + +chimera_gfx_status chimera_gfx_surface_destroy(chimera_gfx_surface *surface); + +chimera_gfx_status +chimera_gfx_texture_create(chimera_gfx_context *context, + const chimera_gfx_texture_desc *desc, + chimera_gfx_texture **out_texture); + +chimera_gfx_status +chimera_gfx_texture_upload(chimera_gfx_texture *texture, + const chimera_gfx_texture_upload_info *upload); + +chimera_gfx_status chimera_gfx_texture_destroy(chimera_gfx_texture *texture); + +chimera_gfx_status +chimera_gfx_present(chimera_gfx_context *context, + const chimera_gfx_present_info *present_info); + +/* Returns RESOURCE_BUSY until all child handles have been destroyed. */ +chimera_gfx_status chimera_gfx_context_destroy(chimera_gfx_context *context); + +/* Version-1 compatibility wrapper. New code uses context_destroy(). */ +void chimera_gfx_destroy(chimera_gfx_context *context); + +const char *chimera_gfx_status_string(chimera_gfx_status status); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/manifests/artifact-denylist.json b/manifests/artifact-denylist.json new file mode 100644 index 0000000..3e4a910 --- /dev/null +++ b/manifests/artifact-denylist.json @@ -0,0 +1,21 @@ +{ + "$schema": "./artifact-denylist.schema.json", + "entries": [ + { + "added_on": "2026-07-17", + "artifact_id": "chimera-gfx-capability-probe-fw-9.60", + "execution_eligible": false, + "permanent": true, + "reason_codes": [ + "SDK_CRT_KERNEL_CREDENTIAL_WRITE", + "SDK_CRT_SYSCALL_BOUND_WRITE", + "USER_PERMANENT_BLOCK" + ], + "sha256": "4be1c17b4964f2b68c39b5145bc4af4619c32512d60269ecf5c39728b390fa63", + "status": "BLOCKED" + } + ], + "fail_closed": true, + "hash_algorithm": "sha256", + "schema_version": 1 +} diff --git a/manifests/artifact-denylist.schema.json b/manifests/artifact-denylist.schema.json new file mode 100644 index 0000000..54949ee --- /dev/null +++ b/manifests/artifact-denylist.schema.json @@ -0,0 +1,46 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://chimera-gfx.invalid/schemas/artifact-denylist-v1.json", + "title": "Chimera GFX permanent artifact denylist", + "type": "object", + "additionalProperties": false, + "required": ["schema_version", "hash_algorithm", "fail_closed", "entries"], + "properties": { + "$schema": {"type": "string", "minLength": 1}, + "schema_version": {"const": 1}, + "hash_algorithm": {"const": "sha256"}, + "fail_closed": {"const": true}, + "entries": { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "sha256", + "artifact_id", + "status", + "permanent", + "execution_eligible", + "added_on", + "reason_codes" + ], + "properties": { + "sha256": {"type": "string", "pattern": "^[0-9a-f]{64}$"}, + "artifact_id": {"type": "string", "minLength": 1}, + "status": {"const": "BLOCKED"}, + "permanent": {"const": true}, + "execution_eligible": {"const": false}, + "added_on": {"type": "string", "format": "date"}, + "reason_codes": { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": {"type": "string", "pattern": "^[A-Z0-9_]+$"} + } + } + } + } + } +} diff --git a/manifests/artifact-manifest.schema.json b/manifests/artifact-manifest.schema.json new file mode 100644 index 0000000..c32062f --- /dev/null +++ b/manifests/artifact-manifest.schema.json @@ -0,0 +1,87 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://chimera-gfx.invalid/schemas/artifact-manifest-v1.json", + "title": "Chimera GFX artifact manifest", + "type": "object", + "additionalProperties": false, + "required": [ + "schema_version", + "artifact", + "source", + "toolchain", + "firmware_gate", + "execution", + "safety" + ], + "properties": { + "schema_version": {"const": 1}, + "artifact": { + "type": "object", + "additionalProperties": false, + "required": ["id", "version", "filename", "size", "sha256", "target"], + "properties": { + "id": {"type": "string", "minLength": 1}, + "version": {"type": "string", "minLength": 1}, + "filename": {"type": "string", "pattern": "^[^/\\\\]+$"}, + "size": {"type": "integer", "minimum": 1}, + "sha256": {"type": "string", "pattern": "^[0-9a-f]{64}$"}, + "target": {"type": "string", "minLength": 1} + } + }, + "source": { + "type": "object", + "additionalProperties": false, + "required": ["repository", "commit", "dirty"], + "properties": { + "repository": {"type": "string", "minLength": 1}, + "commit": {"type": "string", "pattern": "^[0-9a-f]{40}$"}, + "dirty": {"const": false} + } + }, + "toolchain": { + "type": "object", + "additionalProperties": false, + "required": ["ps5_payload_sdk_release", "ps5_payload_sdk_commit"], + "properties": { + "ps5_payload_sdk_release": {"type": "string", "minLength": 1}, + "ps5_payload_sdk_commit": {"type": "string", "pattern": "^[0-9a-f]{40}$"}, + "sdl_commit": {"type": "string", "pattern": "^[0-9a-f]{40}$"} + } + }, + "firmware_gate": { + "type": "object", + "additionalProperties": false, + "required": ["embedded_identifier", "allowlisted"], + "properties": { + "embedded_identifier": {"type": "string", "minLength": 1}, + "allowlisted": {"type": "boolean"} + } + }, + "execution": { + "type": "object", + "additionalProperties": false, + "required": [ + "execution_eligible", + "authorized", + "transferred", + "executed" + ], + "properties": { + "execution_eligible": {"type": "boolean"}, + "authorized": {"const": false}, + "transferred": {"const": false}, + "executed": {"const": false} + } + }, + "safety": { + "type": "object", + "additionalProperties": false, + "required": ["profile", "direct_gnm_imports", "notes"], + "properties": { + "profile": {"type": "string", "minLength": 1}, + "direct_gnm_imports": {"const": 0}, + "notes": {"type": "array", "items": {"type": "string"}} + } + } + } +} diff --git a/manifests/artifacts/chimera-elfldr-phase07-fw-9.60.json b/manifests/artifacts/chimera-elfldr-phase07-fw-9.60.json new file mode 100644 index 0000000..0f3dfdd --- /dev/null +++ b/manifests/artifacts/chimera-elfldr-phase07-fw-9.60.json @@ -0,0 +1,38 @@ +{ + "artifact": { + "filename": "chimera-elfldr-phase07.elf", + "id": "chimera-elfldr-phase07-fw960-v1", + "sha256": "63e810982471eb40cae3a20aa9df9a0a02892f420e429874fae4e99aa400b561", + "size": 397000, + "target": "ps5-hardened-elfldr", + "version": "0.23-chimera-phase07" + }, + "execution": { + "authorized": false, + "executed": false, + "execution_eligible": true, + "transferred": false + }, + "firmware_gate": { + "allowlisted": true, + "embedded_identifier": "9.60" + }, + "safety": { + "direct_gnm_imports": 0, + "notes": [ + "Offline-only artifact; not transferred, installed, or executed", + "Bounded ptrace, complete checked restoration, centralized cleanup, receiver-side SHA-256 and permanent denylist" + ], + "profile": "hardened-controlled-elfldr" + }, + "schema_version": 1, + "source": { + "commit": "197623058f509eddde18868dafcb92fdcac66464", + "dirty": false, + "repository": "private-gitea/chimera-elfldr" + }, + "toolchain": { + "ps5_payload_sdk_commit": "d2e2e585740362976a39fdd5ccf390f199a7bc37", + "ps5_payload_sdk_release": "v0.41" + } +} diff --git a/manifests/artifacts/chimera-gfx-capability-probe-0.1.0-fw-9.60.json b/manifests/artifacts/chimera-gfx-capability-probe-0.1.0-fw-9.60.json new file mode 100644 index 0000000..51b7776 --- /dev/null +++ b/manifests/artifacts/chimera-gfx-capability-probe-0.1.0-fw-9.60.json @@ -0,0 +1,39 @@ +{ + "artifact": { + "filename": "chimera-gfx-capability-probe-0.1.0-fw-9.60-offline-audit-only.elf", + "id": "chimera-gfx-capability-probe-fw-9.60", + "sha256": "4be1c17b4964f2b68c39b5145bc4af4619c32512d60269ecf5c39728b390fa63", + "size": 110424, + "target": "ps5-x86_64", + "version": "0.1.0" + }, + "execution": { + "authorized": false, + "execution_eligible": false, + "executed": false, + "transferred": false + }, + "firmware_gate": { + "allowlisted": true, + "embedded_identifier": "9.60" + }, + "safety": { + "direct_gnm_imports": 0, + "notes": [ + "Transfer and execution are not authorized.", + "Project code calls no resolved GNM symbol and requests no rendering or GPU mutation.", + "SDK v0.41 CRT performs kernel credential and syscall-permission writes before main; execution is blocked by project policy." + ], + "profile": "symbol-discovery-only-offline-audit" + }, + "schema_version": 1, + "source": { + "commit": "ba8f6a40cf37dff628254caa9b11d83a73957cf8", + "dirty": false, + "repository": "https://gitea.itworx.tech/Jens/chimera-gfx.git" + }, + "toolchain": { + "ps5_payload_sdk_commit": "d2e2e585740362976a39fdd5ccf390f199a7bc37", + "ps5_payload_sdk_release": "v0.41" + } +} diff --git a/manifests/artifacts/chimera-gfx-capability-probe-0.1.0-none.json b/manifests/artifacts/chimera-gfx-capability-probe-0.1.0-none.json new file mode 100644 index 0000000..bd4f645 --- /dev/null +++ b/manifests/artifacts/chimera-gfx-capability-probe-0.1.0-none.json @@ -0,0 +1,40 @@ +{ + "artifact": { + "filename": "chimera-gfx-capability-probe.elf", + "id": "chimera-gfx-capability-probe", + "sha256": "f0f74978ac62490ff2482386e9c4efa3ad6d6cc46e10d54c41cb8669b4449f36", + "size": 110424, + "target": "ps5-x86_64", + "version": "0.1.0" + }, + "execution": { + "authorized": false, + "execution_eligible": false, + "executed": false, + "transferred": false + }, + "firmware_gate": { + "allowlisted": false, + "embedded_identifier": "NONE" + }, + "safety": { + "direct_gnm_imports": 0, + "notes": [ + "Default NONE gate refuses before the project-requested GNM module open; SDK CRT initialization still precedes main.", + "Static audit found zero direct Sce and GNM imports.", + "SDK v0.41 CRT performs kernel credential and syscall-permission writes before main; this artifact is not execution-eligible under project policy.", + "Artifact was not transferred or executed." + ], + "profile": "symbol-discovery-only" + }, + "schema_version": 1, + "source": { + "commit": "72292f2d5788ad643e9d1816ce28ab754ef739b5", + "dirty": false, + "repository": "https://gitea.itworx.tech/Jens/chimera-gfx.git" + }, + "toolchain": { + "ps5_payload_sdk_commit": "d2e2e585740362976a39fdd5ccf390f199a7bc37", + "ps5_payload_sdk_release": "v0.41" + } +} diff --git a/manifests/artifacts/chimera-gfx-lifecycle-probe-phase07-fw-9.60.json b/manifests/artifacts/chimera-gfx-lifecycle-probe-phase07-fw-9.60.json new file mode 100644 index 0000000..37bf3bc --- /dev/null +++ b/manifests/artifacts/chimera-gfx-lifecycle-probe-phase07-fw-9.60.json @@ -0,0 +1,38 @@ +{ + "artifact": { + "filename": "chimera-gfx-lifecycle-probe.elf", + "id": "chimera-gfx-lifecycle-phase07-fw960-v1", + "sha256": "bfb4a5cc768e162fe4c2fddf41c3978e152722918a39085277fd172cb95a7182", + "size": 112680, + "target": "ps5-lifecycle-probe", + "version": "0.1.0-phase07" + }, + "execution": { + "authorized": false, + "executed": false, + "execution_eligible": true, + "transferred": false + }, + "firmware_gate": { + "allowlisted": true, + "embedded_identifier": "9.60" + }, + "safety": { + "direct_gnm_imports": 0, + "notes": [ + "Offline-only artifact; not transferred, installed, or executed", + "Explicit _exit after one notification; no GNM, VideoOut, SDL, payload network, retry, or autoload" + ], + "profile": "controlled-lifecycle-notify-exit" + }, + "schema_version": 1, + "source": { + "commit": "fe08300339a13f899fb78ea404ada381a5cba87c", + "dirty": false, + "repository": "private-gitea/chimera-gfx" + }, + "toolchain": { + "ps5_payload_sdk_commit": "d2e2e585740362976a39fdd5ccf390f199a7bc37", + "ps5_payload_sdk_release": "v0.41" + } +} diff --git a/manifests/artifacts/chimera-gfx-phase1-videoout-clear-0.1.0-none.json b/manifests/artifacts/chimera-gfx-phase1-videoout-clear-0.1.0-none.json new file mode 100644 index 0000000..211c84f --- /dev/null +++ b/manifests/artifacts/chimera-gfx-phase1-videoout-clear-0.1.0-none.json @@ -0,0 +1,42 @@ +{ + "artifact": { + "filename": "chimera-gfx-phase1-videoout-clear.elf", + "id": "chimera-gfx-phase1-videoout-clear", + "sha256": "2e826ac4ea2bffb626d82e370f98909bb508417d92b1ac3796e9f5a902e975c9", + "size": 1566368, + "target": "ps5-x86_64", + "version": "0.1.0" + }, + "execution": { + "authorized": false, + "execution_eligible": false, + "executed": false, + "transferred": false + }, + "firmware_gate": { + "allowlisted": false, + "embedded_identifier": "NONE" + }, + "safety": { + "direct_gnm_imports": 0, + "notes": [ + "Default NONE gate refuses before SDL initialization; SDK CRT initialization still precedes main.", + "SDK v0.41 CRT performs kernel credential and syscall-permission writes before main; this artifact is not execution-eligible under project policy.", + "Static audit found exactly 15 reviewed Sce imports and zero direct GNM, keyboard, IME, or UserService imports.", + "Unbounded sceKernelWaitEqueue behavior blocks hardware authorization.", + "Artifact was not transferred or executed." + ], + "profile": "offline-videoout-review-only" + }, + "schema_version": 1, + "source": { + "commit": "72292f2d5788ad643e9d1816ce28ab754ef739b5", + "dirty": false, + "repository": "https://gitea.itworx.tech/Jens/chimera-gfx.git" + }, + "toolchain": { + "ps5_payload_sdk_commit": "d2e2e585740362976a39fdd5ccf390f199a7bc37", + "ps5_payload_sdk_release": "v0.41", + "sdl_commit": "0baf4ac49382b537ba449901b5b6d0d189bb1fbb" + } +} diff --git a/manifests/artifacts/chimera-payload-manager-phase07-fw-9.60.json b/manifests/artifacts/chimera-payload-manager-phase07-fw-9.60.json new file mode 100644 index 0000000..999ea0a --- /dev/null +++ b/manifests/artifacts/chimera-payload-manager-phase07-fw-9.60.json @@ -0,0 +1,38 @@ +{ + "artifact": { + "filename": "chimera-payload-manager-phase07.elf", + "id": "chimera-payload-manager-phase07-fw960-v1", + "sha256": "8fecf8241a46246eddbd21e8bb4d875f5d76f1f4f4c6a11384df1f131aa5e5b1", + "size": 99560, + "target": "ps5-controlled-payload-manager", + "version": "0.3.1-chimera-controlled-phase07" + }, + "execution": { + "authorized": false, + "executed": false, + "execution_eligible": true, + "transferred": false + }, + "firmware_gate": { + "allowlisted": true, + "embedded_identifier": "9.60" + }, + "safety": { + "direct_gnm_imports": 0, + "notes": [ + "Offline-only artifact; not transferred, installed, or executed", + "Exact same-FD hash-to-stream launch, controlled atomic upload, loopback-only versioned transport, no autoload" + ], + "profile": "controlled-hash-bound-payload-manager" + }, + "schema_version": 1, + "source": { + "commit": "e23d94ff91233aa770e2342800c1467875bdef44", + "dirty": false, + "repository": "private-gitea/chimera-ps5-payload-manager" + }, + "toolchain": { + "ps5_payload_sdk_commit": "d2e2e585740362976a39fdd5ccf390f199a7bc37", + "ps5_payload_sdk_release": "v0.41" + } +} diff --git a/manifests/controlled-runtime-profile.schema.json b/manifests/controlled-runtime-profile.schema.json new file mode 100644 index 0000000..0d5b991 --- /dev/null +++ b/manifests/controlled-runtime-profile.schema.json @@ -0,0 +1,237 @@ +{ + "$id": "https://chimera-gfx.invalid/schemas/controlled-runtime-profile-v1.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "additionalProperties": false, + "required": [ + "schema_version", + "profile", + "decision", + "firmware", + "payload_manager", + "loader", + "sdk", + "budgets", + "effects", + "execution_authorized", + "expected_volatile_effects", + "hard_blockers", + "artifact", + "execution", + "deployment" + ], + "properties": { + "schema_version": {"const": 1}, + "profile": {"const": "controlled-ps5-runtime"}, + "decision": { + "enum": [ + "READY_FOR_HARDENED_RUNTIME_DEPLOYMENT", + "BLOCKED_VERSION_OR_UNBOUNDED_EFFECT" + ] + }, + "execution_authorized": {"const": false}, + "artifact": { + "type": "object", + "additionalProperties": false, + "required": ["id", "built", "filename", "size", "sha256", "source_commit"], + "properties": { + "id": {"type": "string", "minLength": 1}, + "built": {"type": "boolean"}, + "filename": {"type": ["string", "null"]}, + "size": {"type": ["integer", "null"], "minimum": 1}, + "sha256": { + "oneOf": [ + {"type": "string", "pattern": "^[0-9a-f]{64}$"}, + {"type": "null"} + ] + }, + "source_commit": { + "oneOf": [ + {"type": "string", "pattern": "^[0-9a-f]{40}$"}, + {"type": "null"} + ] + } + } + }, + "budgets": { + "type": "object", + "additionalProperties": false, + "required": [ + "persistent_write_budget", + "filesystem_write_budget", + "payload_network_access", + "maximum_runtime_ms", + "automatic_retry" + ], + "properties": { + "persistent_write_budget": {"const": "controlled_artifact_removable"}, + "filesystem_write_budget": {"const": "controlled_artifact_directory_only"}, + "payload_network_access": {"const": "none"}, + "maximum_runtime_ms": {"type": "integer", "minimum": 1, "maximum": 2000}, + "automatic_retry": {"const": false} + } + }, + "deployment": { + "type": "object", + "additionalProperties": false, + "required": ["installed", "ready_for_installation", "rollback_prepared"], + "properties": { + "installed": {"const": false}, + "ready_for_installation": {"type": "boolean"}, + "rollback_prepared": {"type": "boolean"} + } + }, + "effects": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["id", "classification"], + "properties": { + "id": {"type": "string", "minLength": 1}, + "classification": { + "enum": [ + "EXPECTED_VOLATILE_RUNTIME_EFFECT", + "RESTORED_BY_LOADER", + "PAYLOAD_PROCESS_LOCAL", + "FAIL_CLOSED_TERMINATION", + "BOUNDED_WATCHDOG", + "OS_RECLAIMED_ON_EXIT", + "EXPLICIT_PROCESS_EXIT", + "HASH_BOUND_SAME_FD", + "ALLOWED_APPLICATION_WRITE", + "PERSISTENT_WRITE", + "UNBOUNDED_OR_UNKNOWN" + ] + } + } + } + }, + "execution": { + "type": "object", + "additionalProperties": false, + "required": ["execution_eligible", "authorized", "transferred", "executed"], + "properties": { + "execution_eligible": {"type": "boolean"}, + "authorized": {"const": false}, + "transferred": {"const": false}, + "executed": {"const": false} + } + }, + "expected_volatile_effects": { + "type": "array", + "uniqueItems": true, + "items": {"type": "string", "minLength": 1} + }, + "firmware": { + "type": "object", + "additionalProperties": false, + "required": ["exact", "device_attested", "evidence"], + "properties": { + "exact": {"const": "9.60"}, + "device_attested": {"type": "boolean"}, + "evidence": {"type": "string", "minLength": 1} + } + }, + "hard_blockers": { + "type": "array", + "items": {"type": "string", "minLength": 1} + }, + "loader": { + "type": "object", + "additionalProperties": false, + "required": [ + "release", + "base_commit", + "hardened_commit", + "sha256", + "size", + "installed", + "reproducible" + ], + "properties": { + "release": {"const": "v0.23-chimera-phase07"}, + "base_commit": {"const": "699e8bcff03e91e8d6ca6eba281af25c5a58d8c2"}, + "hardened_commit": {"const": "197623058f509eddde18868dafcb92fdcac66464"}, + "sha256": {"const": "63e810982471eb40cae3a20aa9df9a0a02892f420e429874fae4e99aa400b561"}, + "size": {"const": 397000}, + "installed": {"const": false}, + "reproducible": {"const": true} + } + }, + "payload_manager": { + "type": "object", + "additionalProperties": false, + "required": [ + "release", + "base_commit", + "hardened_commit", + "sha256", + "size", + "installed", + "reproducible" + ], + "properties": { + "release": {"const": "v0.3.1-chimera-controlled-phase07"}, + "base_commit": {"const": "cfbc70f30f419b09bf2b52283f7409e2d3117ee1"}, + "hardened_commit": {"const": "e23d94ff91233aa770e2342800c1467875bdef44"}, + "sha256": {"const": "8fecf8241a46246eddbd21e8bb4d875f5d76f1f4f4c6a11384df1f131aa5e5b1"}, + "size": {"const": 99560}, + "installed": {"const": false}, + "reproducible": {"const": true} + } + }, + "sdk": { + "type": "object", + "additionalProperties": false, + "required": ["release", "commit"], + "properties": { + "release": {"const": "v0.41"}, + "commit": {"const": "d2e2e585740362976a39fdd5ccf390f199a7bc37"} + } + } + }, + "allOf": [ + { + "if": { + "properties": { + "decision": {"const": "READY_FOR_HARDENED_RUNTIME_DEPLOYMENT"} + }, + "required": ["decision"] + }, + "then": { + "properties": { + "artifact": { + "properties": { + "built": {"const": true}, + "filename": {"type": "string", "pattern": "^[^/\\\\]+$"}, + "size": {"type": "integer", "minimum": 1}, + "sha256": {"type": "string", "pattern": "^[0-9a-f]{64}$"}, + "source_commit": {"type": "string", "pattern": "^[0-9a-f]{40}$"} + } + }, + "deployment": { + "properties": { + "ready_for_installation": {"const": true}, + "rollback_prepared": {"const": true} + } + }, + "execution": { + "properties": {"execution_eligible": {"const": true}} + }, + "hard_blockers": {"maxItems": 0} + } + }, + "else": { + "properties": { + "artifact": { + "properties": {"built": {"const": false}} + }, + "execution": { + "properties": {"execution_eligible": {"const": false}} + } + } + } + } + ] +} diff --git a/manifests/ps5_gnm_symbols.json b/manifests/ps5_gnm_symbols.json new file mode 100644 index 0000000..f5264f8 --- /dev/null +++ b/manifests/ps5_gnm_symbols.json @@ -0,0 +1,44 @@ +{ + "schema_version": 1, + "purpose": "read_only_symbol_discovery", + "module_candidate": "libSceGnmDriver.sprx", + "module_name_status": "inference_from_public_stub_filename_and_sdk_dlopen_convention", + "firmware_allowlist": ["9.60"], + "evidence": { + "repository": "https://github.com/ps5-payload-dev/sdk.git", + "commit": "d2e2e585740362976a39fdd5ccf390f199a7bc37", + "path": "sce_stubs/libSceGnmDriver.c" + }, + "global_policy": { + "call_resolved_symbols": "never", + "log_symbol_addresses": false, + "submit": false, + "draw": false, + "dispatch": false, + "flip": false, + "mutate_gpu_memory": false + }, + "symbols": [ + {"name": "sceGnmAreSubmitsAllowed", "category": "status", "abi_status": "name_only_unverified", "call_policy": "never"}, + {"name": "sceGnmComputeWaitOnAddress", "category": "synchronization", "abi_status": "name_only_unverified", "call_policy": "never"}, + {"name": "sceGnmComputeWaitSemaphore", "category": "synchronization", "abi_status": "name_only_unverified", "call_policy": "never"}, + {"name": "sceGnmDispatchDirect", "category": "dispatch", "abi_status": "name_only_unverified", "call_policy": "never"}, + {"name": "sceGnmDispatchIndirect", "category": "dispatch", "abi_status": "name_only_unverified", "call_policy": "never"}, + {"name": "sceGnmDrawIndex", "category": "draw", "abi_status": "name_only_unverified", "call_policy": "never"}, + {"name": "sceGnmDrawIndexAuto", "category": "draw", "abi_status": "name_only_unverified", "call_policy": "never"}, + {"name": "sceGnmDrawIndirect", "category": "draw", "abi_status": "name_only_unverified", "call_policy": "never"}, + {"name": "sceGnmInsertWaitFlipDone", "category": "synchronization", "abi_status": "name_only_unverified", "call_policy": "never"}, + {"name": "sceGnmMapComputeQueue", "category": "queue", "abi_status": "name_only_unverified", "call_policy": "never"}, + {"name": "sceGnmRegisterOwner", "category": "resource", "abi_status": "name_only_unverified", "call_policy": "never"}, + {"name": "sceGnmRegisterResource", "category": "resource", "abi_status": "name_only_unverified", "call_policy": "never"}, + {"name": "sceGnmRequestFlipAndSubmitDone", "category": "synchronization", "abi_status": "name_only_unverified", "call_policy": "never"}, + {"name": "sceGnmSetEmbeddedPsShader", "category": "embedded_shader", "abi_status": "name_only_unverified", "call_policy": "never"}, + {"name": "sceGnmSetEmbeddedVsShader", "category": "embedded_shader", "abi_status": "name_only_unverified", "call_policy": "never"}, + {"name": "sceGnmSubmitAndFlipCommandBuffers", "category": "submission", "abi_status": "name_only_unverified", "call_policy": "never"}, + {"name": "sceGnmSubmitCommandBuffers", "category": "submission", "abi_status": "name_only_unverified", "call_policy": "never"}, + {"name": "sceGnmSubmitDone", "category": "synchronization", "abi_status": "name_only_unverified", "call_policy": "never"}, + {"name": "sceGnmUnmapComputeQueue", "category": "queue", "abi_status": "name_only_unverified", "call_policy": "never"}, + {"name": "sceGnmUnregisterOwnerAndResources", "category": "resource", "abi_status": "name_only_unverified", "call_policy": "never"}, + {"name": "sceGnmUnregisterResource", "category": "resource", "abi_status": "name_only_unverified", "call_policy": "never"} + ] +} diff --git a/manifests/retroarch/phase-1.0a-artifacts.json b/manifests/retroarch/phase-1.0a-artifacts.json new file mode 100644 index 0000000..4fc73ab --- /dev/null +++ b/manifests/retroarch/phase-1.0a-artifacts.json @@ -0,0 +1,661 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0A_RETROARCH_PS5_PORT_BOOTSTRAP", + "status": "RETROARCH_PS5_SOFTWARE_PORT_BUILT", + "source_commit": "ca1b45680577befc743e1c92fa40687e1b1745e7", + "artifact_repository": "chimera-retroarch", + "artifacts_tracked_in_git": false, + "shared_audit": { + "startup_source": "PS5 Payload SDK v0.41 crt/crt.c and crt/patch.c at d2e2e585740362976a39fdd5ccf390f199a7bc37", + "startup_callgraph": [ + "_start", + "BSS clear", + "inlined payload_init", + "__crt_syscall_init", + "__kernel_init", + "__klog_init", + "kernel_dynlib_dlsym(__isthreaded)", + "__patch_init", + "patch_kernel_ucred", + "patch_syscall_permissions", + "__rtld_init", + "payload_run", + "main", + "rarch_main", + "retroarch_main_init", + "runloop_iterate", + "retro_run" + ], + "shutdown_callgraph": [ + "RETRO_ENVIRONMENT_SHUTDOWN", + "RetroArch shutdown state", + "retro_deinit", + "frontend_ps5_deinit", + "frontend_ps5_shutdown", + "SDK fini/close/destroy", + "payload_terminate" + ], + "sdk_crt_static_risk_symbols": [ + "__patch_init", + "kernel_copyin", + "kernel_copyout", + "kernel_get_ucred_caps", + "kernel_set_ucred_caps", + "kernel_get_ucred_attrs", + "kernel_set_ucred_attrs" + ], + "sdk_crt_effect_classification": "KNOWN_PROCESS_AND_KERNEL_RUNTIME_WRITES_NOT_EXECUTED", + "application_network_imports": [], + "application_dynamic_loader_imports": [], + "personal_host_paths_found": false, + "credentials_found": false, + "device_or_private_ip_addresses_found": false, + "generic_string_findings": [ + "udp://127.0.0.1:", + "RTMP template", + "overlay autoload labels", + "downloader localization", + "SDK RTLD module-name tables" + ], + "hardware_tested": false, + "device_action_performed": false + }, + "artifacts": [ + { + "name": "retroarch_ps5_headless.elf", + "profile": "ps5-headless-smokecore", + "local_relative_path": "build/phase10a/final/retroarch_ps5_headless.elf", + "linker_map_local_relative_path": "build/phase10a/final/retroarch_ps5_headless.map", + "size": 722392, + "sha256": "fd595a826f64d18598be0b55e539bb524b33bd469b98f62c958ee50acb544628", + "linker_map_size": 277193, + "linker_map_sha256": "fb935f7a768c91408a87290f03bad8ed59f5061de75232d5660957eff8ec017f", + "execution_eligible": false, + "target_execution_performed": false, + "hardware_tested": false, + "reproducibility": "BYTE_IDENTICAL_TWO_CLEAN_BUILDS", + "file_type": "ELF 64-bit LSB PIE, x86-64, SYSV, dynamically linked, not stripped", + "elf_header": { + "class": "ELF64", + "data": "little-endian", + "os_abi": "UNIX System V", + "type": "DYN", + "machine": "AMD x86-64", + "entrypoint": "0x47d10", + "program_header_count": 4, + "section_header_count": 20 + }, + "program_headers": [ + { + "type": "LOAD", + "offset": "0x004000", + "virtual_address": "0x000000", + "file_size": "0x04e570", + "memory_size": "0x04e570", + "flags": "RWE", + "alignment": "0x4000" + }, + { + "type": "LOAD", + "offset": "0x054000", + "virtual_address": "0x050000", + "file_size": "0x048140", + "memory_size": "0x048140", + "flags": "RW", + "alignment": "0x4000" + }, + { + "type": "LOAD", + "offset": "0x0a0000", + "virtual_address": "0x09c000", + "file_size": "0x000ccc", + "memory_size": "0x0a8560", + "flags": "RW", + "alignment": "0x4000" + }, + { + "type": "DYNAMIC", + "offset": "0x09c000", + "virtual_address": "0x098000", + "file_size": "0x000140", + "memory_size": "0x000140", + "flags": "RW", + "alignment": "0x4000" + } + ], + "section_names": [ + ".text", + ".eh_frame_hdr", + ".eh_frame", + ".dynsym", + ".gnu.hash", + ".dynstr", + ".rela.dyn", + ".data.rel.ro", + ".got", + ".rodata", + ".init_array", + ".fini_array", + ".dynamic", + ".data", + ".bss", + ".comment", + ".symtab", + ".shstrtab", + ".strtab" + ], + "interpreter": null, + "dt_needed": [ + "libkernel_web.sprx", + "libSceLibcInternal.sprx" + ], + "build_id": null, + "gnu_stack_program_header": "ABSENT", + "elf_tls_segment": false, + "elf_tls_sections": [], + "init_array_size": 0, + "fini_array_size": 0, + "relocations": { + "section": ".rela.dyn", + "entries": 610 + }, + "dynamic_symbol_count": 79, + "full_symbol_count": 1387, + "defined_global_symbol_count": 719, + "required_symbols": [ + "frontend_ctx_ps5", + "rarch_main", + "retro_deinit", + "retro_init", + "retro_run", + "retroarch_main_init", + "runloop_iterate" + ], + "key_symbol_addresses": { + "rarch_main": "0x3ce0", + "main": "0x3f90", + "retroarch_main_init": "0x4060", + "runloop_iterate": "0xb340", + "retro_init": "0x47600", + "retro_deinit": "0x476a0", + "retro_run": "0x47810", + "_start": "0x47d10", + "frontend_ctx_ps5": "0x9cba0", + "__patch_init": "0x4e460" + }, + "undefined_symbol_count": 73, + "undefined_symbols": [ + "__error", + "__isthreaded", + "__stderrp", + "__stdoutp", + "abort", + "atoi", + "calloc", + "clock_gettime", + "close", + "closedir", + "exit", + "fclose", + "fflush", + "fileno", + "fopen", + "fprintf", + "fputc", + "fputs", + "fread", + "free", + "fseek", + "fseeko", + "ftello", + "ftruncate", + "fwrite", + "getenv", + "isgraph", + "localtime", + "longjmp", + "lseek", + "malloc", + "memchr", + "memcmp", + "memcpy", + "memmove", + "memset", + "mkdir", + "nanosleep", + "open", + "opendir", + "powf", + "puts", + "qsort", + "read", + "readdir", + "realloc", + "remove", + "rename", + "setjmp", + "setvbuf", + "sin", + "snprintf", + "sscanf", + "stat", + "strchr", + "strcmp", + "strdup", + "strerror", + "strftime", + "strlen", + "strncmp", + "strrchr", + "strstr", + "strtod", + "strtok_r", + "strtol", + "strtoul", + "strtoull", + "time", + "tolower", + "vfprintf", + "vsnprintf", + "write" + ], + "filesystem_write_capable_imports": [ + "fopen", + "ftruncate", + "fwrite", + "mkdir", + "open", + "remove", + "rename", + "write" + ], + "network_imports": [], + "process_or_service_control_imports": [], + "rwx_segment_count": 1, + "embedded_personal_paths": false, + "embedded_ip_addresses": [ + "127.0.0.1" + ], + "embedded_device_ip_addresses": false, + "embedded_credentials": false, + "autoload_route_found": false, + "install_route_found": false, + "updater_route_compiled": false, + "forbidden_markers_found": [] + }, + { + "name": "retroarch_ps5_software.elf", + "profile": "ps5-software-rgui-smokecore", + "local_relative_path": "build/phase10a/final/retroarch_ps5_software.elf", + "linker_map_local_relative_path": "build/phase10a/final/retroarch_ps5_software.map", + "size": 3318432, + "sha256": "7beb09592404b5c1fb4161c632171d2901f3715db26e59458998690e8c49f3fc", + "linker_map_size": 1608730, + "linker_map_sha256": "1573a9951fa53bfa1bf304e0038e6f049f859fbfbe92f6a895196dc283c27597", + "execution_eligible": false, + "target_execution_performed": false, + "hardware_tested": false, + "reproducibility": "BYTE_IDENTICAL_TWO_CLEAN_BUILDS", + "file_type": "ELF 64-bit LSB PIE, x86-64, SYSV, dynamically linked, not stripped", + "elf_header": { + "class": "ELF64", + "data": "little-endian", + "os_abi": "UNIX System V", + "type": "DYN", + "machine": "AMD x86-64", + "entrypoint": "0x1c29c0", + "program_header_count": 4, + "section_header_count": 20 + }, + "program_headers": [ + { + "type": "LOAD", + "offset": "0x004000", + "virtual_address": "0x000000", + "file_size": "0x1c93e0", + "memory_size": "0x1c93e0", + "flags": "RWE", + "alignment": "0x4000" + }, + { + "type": "LOAD", + "offset": "0x1d0000", + "virtual_address": "0x1cc000", + "file_size": "0x0d0190", + "memory_size": "0x0d0190", + "flags": "RW", + "alignment": "0x4000" + }, + { + "type": "LOAD", + "offset": "0x2a4000", + "virtual_address": "0x2a0000", + "file_size": "0x029584", + "memory_size": "0x0e0008", + "flags": "RW", + "alignment": "0x4000" + }, + { + "type": "DYNAMIC", + "offset": "0x2a0000", + "virtual_address": "0x29c000", + "file_size": "0x000190", + "memory_size": "0x000190", + "flags": "RW", + "alignment": "0x4000" + } + ], + "section_names": [ + ".text", + ".eh_frame_hdr", + ".eh_frame", + ".dynsym", + ".gnu.hash", + ".dynstr", + ".rela.dyn", + ".data.rel.ro", + ".got", + ".rodata", + ".init_array", + ".fini_array", + ".dynamic", + ".data", + ".bss", + ".comment", + ".symtab", + ".shstrtab", + ".strtab" + ], + "interpreter": null, + "dt_needed": [ + "libSceVideoOut.sprx", + "libScePad.sprx", + "libSceUserService.sprx", + "libSceSystemService.sprx", + "libSceAudioOut.sprx", + "libkernel_web.sprx", + "libSceLibcInternal.sprx" + ], + "build_id": null, + "gnu_stack_program_header": "ABSENT", + "elf_tls_segment": false, + "elf_tls_sections": [], + "init_array_size": 0, + "fini_array_size": 0, + "relocations": { + "section": ".rela.dyn", + "entries": 3373 + }, + "dynamic_symbol_count": 220, + "full_symbol_count": 7256, + "defined_global_symbol_count": 2403, + "required_symbols": [ + "frontend_ctx_ps5", + "rarch_main", + "retro_deinit", + "retro_init", + "retro_run", + "retroarch_main_init", + "runloop_iterate" + ], + "key_symbol_addresses": { + "rarch_main": "0x4cc0", + "main": "0x4ff0", + "retroarch_main_init": "0x50c0", + "runloop_iterate": "0xd0c0", + "retro_init": "0xe23d0", + "retro_deinit": "0xe2470", + "retro_run": "0xe25e0", + "_start": "0x1c29c0", + "frontend_ctx_ps5": "0x2a53c0", + "__patch_init": "0x1c92d0" + }, + "undefined_symbol_count": 210, + "undefined_symbols": [ + "_Exit", + "__error", + "__isthreaded", + "__stderrp", + "__stdinp", + "__stdoutp", + "abort", + "acos", + "acosf", + "asin", + "asinf", + "atan", + "atan2", + "atan2f", + "atanf", + "atof", + "atoi", + "bsearch", + "calloc", + "clock_gettime", + "close", + "closedir", + "cos", + "cosf", + "exit", + "exp", + "expf", + "fclose", + "ferror", + "fflush", + "fgets", + "fileno", + "fmod", + "fmodf", + "fopen", + "fprintf", + "fputc", + "fputs", + "fread", + "free", + "fseek", + "fseeko", + "fstat", + "ftello", + "ftruncate", + "fwrite", + "getenv", + "gettimeofday", + "isalnum", + "isalpha", + "isblank", + "iscntrl", + "isgraph", + "islower", + "isprint", + "ispunct", + "isspace", + "isupper", + "isxdigit", + "localtime", + "log", + "log10", + "log10f", + "logf", + "longjmp", + "lround", + "lroundf", + "lseek", + "malloc", + "memchr", + "memcmp", + "memcpy", + "memmove", + "memset", + "mkdir", + "mktime", + "nanosleep", + "open", + "opendir", + "pow", + "powf", + "pthread_attr_init", + "pthread_attr_setdetachstate", + "pthread_attr_setstacksize", + "pthread_cond_broadcast", + "pthread_cond_destroy", + "pthread_cond_init", + "pthread_cond_signal", + "pthread_cond_timedwait", + "pthread_cond_wait", + "pthread_create", + "pthread_detach", + "pthread_getschedparam", + "pthread_getspecific", + "pthread_join", + "pthread_key_create", + "pthread_key_delete", + "pthread_mutex_destroy", + "pthread_mutex_init", + "pthread_mutex_lock", + "pthread_mutex_trylock", + "pthread_mutex_unlock", + "pthread_mutexattr_init", + "pthread_mutexattr_settype", + "pthread_self", + "pthread_set_name_np", + "pthread_setcanceltype", + "pthread_setschedparam", + "pthread_setspecific", + "pthread_sigmask", + "puts", + "qsort", + "rand", + "read", + "readdir", + "realloc", + "remove", + "rename", + "scalbn", + "scalbnf", + "sceAudioOutClose", + "sceAudioOutInit", + "sceAudioOutOpen", + "sceAudioOutOutput", + "sceKernelAllocateMainDirectMemory", + "sceKernelCreateEqueue", + "sceKernelDeleteEqueue", + "sceKernelMapDirectMemory", + "sceKernelReleaseDirectMemory", + "sceKernelWaitEqueue", + "scePadClose", + "scePadInit", + "scePadOpen", + "scePadReadState", + "scePadSetLightBar", + "scePadSetVibration", + "scePadSetVibrationMode", + "sceSystemServiceHideSplashScreen", + "sceUserServiceGetLoginUserIdList", + "sceUserServiceGetUserName", + "sceUserServiceInitialize", + "sceVideoOutAddFlipEvent", + "sceVideoOutClose", + "sceVideoOutDeleteFlipEvent", + "sceVideoOutOpen", + "sceVideoOutRegisterBuffers2", + "sceVideoOutSetBufferAttribute2", + "sceVideoOutSetFlipRate", + "sceVideoOutSubmitFlip", + "sched_get_priority_max", + "sched_get_priority_min", + "sem_destroy", + "sem_getvalue", + "sem_init", + "sem_post", + "sem_timedwait", + "sem_trywait", + "sem_wait", + "setenv", + "setjmp", + "setlocale", + "setvbuf", + "sigaction", + "sigaddset", + "sigemptyset", + "signal", + "sin", + "sincos", + "sinf", + "snprintf", + "sprintf", + "srand", + "sscanf", + "stat", + "strchr", + "strcmp", + "strcpy", + "strdup", + "strerror", + "strftime", + "strlcat", + "strlcpy", + "strlen", + "strncasecmp", + "strncmp", + "strpbrk", + "strrchr", + "strstr", + "strtod", + "strtok_r", + "strtol", + "strtoll", + "strtoul", + "strtoull", + "sysconf", + "sysctl", + "sysctlbyname", + "tan", + "tanf", + "time", + "tolower", + "toupper", + "vfprintf", + "vsnprintf", + "vsscanf", + "wcscmp", + "wcslen", + "wcsncmp", + "wcsstr", + "write" + ], + "filesystem_write_capable_imports": [ + "fopen", + "ftruncate", + "fwrite", + "mkdir", + "open", + "remove", + "rename", + "setenv", + "write" + ], + "network_imports": [], + "process_or_service_control_imports": [ + "sceSystemServiceHideSplashScreen" + ], + "rwx_segment_count": 1, + "embedded_personal_paths": false, + "embedded_ip_addresses": [ + "0.1.2.3", + "4.5.6.7", + "127.0.0.1" + ], + "embedded_ip_context": { + "0.1.2.3": "non-address adjacent string-table byte pattern", + "4.5.6.7": "non-address adjacent string-table byte pattern", + "127.0.0.1": "generic disabled RetroArch UDP log template" + }, + "embedded_device_ip_addresses": false, + "embedded_credentials": false, + "autoload_route_found": false, + "install_route_found": false, + "updater_route_compiled": false, + "forbidden_markers_found": [] + } + ] +} diff --git a/manifests/retroarch/phase-1.0a-build.json b/manifests/retroarch/phase-1.0a-build.json new file mode 100644 index 0000000..9c6a209 --- /dev/null +++ b/manifests/retroarch/phase-1.0a-build.json @@ -0,0 +1,223 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0A_RETROARCH_PS5_PORT_BOOTSTRAP", + "status": "RETROARCH_PS5_SOFTWARE_PORT_BUILT", + "date": "2026-07-19", + "baseline_commit": "4c086f84ab2e7c53e6750b66b3b3e1f591ff6a06", + "branch": "codex/chimera-gfx-phase10a-retroarch-port-bootstrap", + "offline_source_acquisition_authorized": true, + "offline_source_modification_authorized": true, + "offline_target_build_authorized": true, + "offline_artifact_analysis_authorized": true, + "private_gitea_push_authorized": true, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "installation_authorized": false, + "lifecycle_authorized": false, + "autoload_authorized": false, + "device_write_authorized": false, + "automatic_retry": false, + "ps5_connected": false, + "device_request_performed": false, + "files_transferred": false, + "device_write_performed": false, + "target_execution_performed": false, + "install_package_created": false, + "execution_package_created": false, + "chimera_retroarch": { + "repository_kind": "SEPARATE_PRIVATE_FORK", + "upstream_base_tag": "v1.22.2", + "upstream_base_commit": "69a4f0ea1e8aaf442ae4858f2e7f2b31a1776576", + "branch": "codex/ps5-port-bootstrap", + "commit": "ca1b45680577befc743e1c92fa40687e1b1745e7", + "tree": "f0dc2ae84a975be210f9b24df6f688df21dc1c68", + "commits": [ + { + "commit": "1ba009b062841ac5ef9bad01620ab491524c1b22", + "subject": "build(ps5): bootstrap RetroArch target" + }, + { + "commit": "268485e87c459ca3defaaf03d16a8b71b818be69", + "subject": "feat(ps5): add platform and static smokecore integration" + }, + { + "commit": "ca1b45680577befc743e1c92fa40687e1b1745e7", + "subject": "feat(ps5): add software frontend drivers" + } + ], + "working_tree_clean": true, + "upstream_push_disabled": true, + "private_origin_configured": true + }, + "build_environment": { + "host": "WSL2", + "kernel": "Linux 6.6.114.1-microsoft-standard-WSL2 x86_64", + "source_date_epoch": 1763597828, + "timezone": "UTC", + "locale": "C", + "cross_compiler": "Ubuntu clang 18.1.8", + "cross_target": "x86_64-sie-ps5", + "make": "GNU Make 4.4.1", + "cmake": "4.2.3", + "ninja": "1.13.2", + "host_compiler": "GCC 15.2.0", + "python": "3.14.4" + }, + "profiles": { + "ps5-headless-smokecore": { + "toolchain": "PS5 Payload SDK v0.41 prospero-clang", + "enabled_features": [ + "real_retroarch_frontend", + "real_retroarch_runloop", + "frontend_ctx_ps5", + "static_chimera_smokecore", + "null_video", + "null_audio", + "null_input" + ], + "disabled_features": [ + "networking", + "online_updater", + "dynamic_cores", + "autoload", + "installation", + "gnm", + "opengl", + "vulkan", + "menu", + "shaders", + "screenshots", + "recording", + "achievements", + "database" + ], + "filesystem_writes_on_default_path": false, + "automatic_retry": false, + "output": "retroarch_ps5_headless.elf" + }, + "ps5-software-rgui-smokecore": { + "toolchain": "PS5 Payload SDK v0.41 prospero-clang", + "enabled_features": [ + "real_retroarch_frontend", + "real_retroarch_runloop", + "frontend_ctx_ps5", + "static_chimera_smokecore", + "rgui", + "sdl2_software_video", + "sdl2_ps5_pad", + "sdl2_ps5_audio" + ], + "disabled_features": [ + "networking", + "online_updater", + "dynamic_cores", + "autoload", + "installation", + "gnm", + "opengl", + "vulkan", + "shaders", + "hardware_libretro_context", + "screenshots", + "recording", + "achievements", + "database", + "keyboard_ime", + "haptics" + ], + "filesystem_writes_on_default_path": false, + "automatic_retry": false, + "output": "retroarch_ps5_software.elf" + }, + "host-smokecore-integration": { + "toolchain": "GCC 15.2.0 with ASan and UBSan", + "enabled_features": [ + "libretro_api_harness", + "deterministic_600_frames", + "digital_input", + "analog_input", + "clean_shutdown", + "leak_detection" + ], + "disabled_features": [ + "networking", + "online_updater", + "dynamic_cores", + "autoload", + "installation", + "gnm", + "opengl", + "vulkan", + "device_io" + ], + "filesystem_writes_on_default_path": false, + "automatic_retry": false, + "output": "temporary_host_test_removed_after_pass" + } + }, + "target_builds": [ + { + "profile": "ps5-headless-smokecore", + "clean_builds": 2, + "command_sequence": [ + "make -f Makefile.ps5 PS5_PROFILE=headless clean", + "make -f Makefile.ps5 PS5_PROFILE=headless -j4 all" + ], + "result": "PASS", + "reproducibility": "BYTE_IDENTICAL_TWO_CLEAN_BUILDS", + "elf_sha256": "fd595a826f64d18598be0b55e539bb524b33bd469b98f62c958ee50acb544628", + "map_sha256": "fb935f7a768c91408a87290f03bad8ed59f5061de75232d5660957eff8ec017f" + }, + { + "profile": "ps5-software-rgui-smokecore", + "clean_builds": 2, + "command_sequence": [ + "make -f Makefile.ps5 PS5_PROFILE=software clean", + "make -f Makefile.ps5 PS5_PROFILE=software -j4 all" + ], + "result": "PASS", + "reproducibility": "BYTE_IDENTICAL_TWO_CLEAN_BUILDS", + "elf_sha256": "7beb09592404b5c1fb4161c632171d2901f3715db26e59458998690e8c49f3fc", + "map_sha256": "1573a9951fa53bfa1bf304e0038e6f049f859fbfbe92f6a895196dc283c27597" + } + ], + "sdl2": { + "classification": "PS5_SDL_PARTIAL_CANDIDATE", + "commit": "0baf4ac49382b537ba449901b5b6d0d189bb1fbb", + "static_archive_size": 3120572, + "static_archive_sha256": "353065505f54e71fa8f7fff41e090dce52f39fe2aaafab85c4653648fc5f1b56", + "overlay_size": 5195, + "overlay_sha256": "b547260d8af40ce2360575ab7831c009036c1f35f8b525cad6fd376dc5ca9d6b", + "runtime_tested": false + }, + "host_tests": { + "command": "make -f Makefile.ps5 test-ps5-port HOST_CC=cc", + "frames": 600, + "video_fnv1a64": "43f920496eb5f435", + "audio_fnv1a64": "a48f47dc08c56625", + "asan_ubsan": "PASS", + "leak_detection": "PASS", + "input_mapping": "PASS", + "clean_shutdown": "PASS", + "port_structural_guardrails": "PASS", + "hardware_evidence": false + }, + "validation": { + "chimera_gfx_ctest": "29/29 PASS", + "phase10a_guardrails": "25/25 PASS", + "artifact_identity_check": "PASS", + "phase0_safety_audit": "PASS", + "chimera_gfx_secret_scan": "658 text files PASS", + "chimera_retroarch_delta_secret_scan": "21 text files PASS", + "whole_retroarch_upstream_scan": "PRE_EXISTING_BEARSSL_TEST_KEYS_AND_MBEDTLS_SOURCE_MARKERS_ONLY", + "git_diff_check": "PASS" + }, + "first_device_test": { + "status": "NOT_AUTHORIZED", + "candidate": "retroarch_ps5_software.elf", + "candidate_sha256": "7beb09592404b5c1fb4161c632171d2901f3715db26e59458998690e8c49f3fc", + "firmware": "9.60", + "automatic_retry": false + } +} diff --git a/manifests/retroarch/phase-1.0aa-offline-fake-adapter.json b/manifests/retroarch/phase-1.0aa-offline-fake-adapter.json new file mode 100644 index 0000000..8fc5530 --- /dev/null +++ b/manifests/retroarch/phase-1.0aa-offline-fake-adapter.json @@ -0,0 +1,115 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0AA_OFFLINE_FAKE_ADAPTER_INTEGRATION", + "status": "OFFLINE_FAKE_BATCH_INTEGRATION_COMPLETE_LIVE_ADAPTER_BLOCKED", + "date": "2026-07-22", + "start_commit": "57ff9a1c5937575b00df05f2cd9897118eab1f2a", + "activation": { + "active": false, + "integration_sha256": null, + "run_id": null, + "target_address": null, + "target_port": null, + "window": null, + "deadline_seconds": null + }, + "source_bindings": { + "phase10x_transport_size": 8040, + "phase10x_transport_sha256": "568d7578482ecf2fcd9e29085b2eb9d8705fc699611508acdf22afd30f2ddd23", + "phase10z_contract_size": 10487, + "phase10z_contract_sha256": "0728c2be7f368e0a7f4b68efe86f6e0c5c2f50704a41d0e1992b0bfec19dde06", + "phase10aa_integration_size": 11200, + "phase10aa_integration_sha256": "8e1cac255f85d2cd14baf8fbc27d631c9b607fc7d19fc57c65462089f0574055", + "phase10aa_integration_tests_size": 10667, + "phase10aa_integration_tests_sha256": "18a5470a651fcbe4b23f2a68cba499dd19d3da15a229623c054be0b598025699" + }, + "fake_boundary": { + "exact_builtin_adapter_required": true, + "adapter_subclasses_allowed": false, + "exact_builtin_clock_required": true, + "clock_subclasses_allowed": false, + "exact_fake_evidence_store_required": true, + "live_adapter_protocol_present": false, + "network_import_present": false, + "real_clock_present": false, + "target_present": false, + "cli_present": false, + "maximum_fake_events": 257, + "event_kinds": ["DATA", "HARD_DEADLINE", "REMOTE_EOF", "BLOCKED"] + }, + "ordering_contract": { + "receipt_before_fake_open": true, + "fake_open_count": 1, + "complete_batch_send_count": 1, + "fake_close_count": 1, + "completion_event": "SYNTHETIC_HARD_DEADLINE_ONLY", + "early_deadline": "INVALID", + "remote_eof": "INVALID", + "blocked_receive": "INVALID", + "missing_deadline": "INVALID", + "data_at_or_after_deadline": "INVALID", + "partial_result": "INVALID", + "incoming_iac": "INVALID", + "retry_allowed": false, + "reconnect_allowed": false, + "resume_allowed": false + }, + "evidence_contract": { + "exclusive_create": true, + "consumed_receipt_retained_on_failure": true, + "failure_output_created": false, + "sanitized_output_receipt_bound": true, + "batch_sha256_recorded": true, + "batch_size_recorded": true, + "target_retained": false, + "raw_transcript_persisted": false, + "logical_event_buffer_discarded": true, + "physical_memory_erasure_proven": false, + "directory_entry_durability_proven": false, + "device_behavior_proven": false, + "exact_identity_proven": false + }, + "authorizations": { + "target_build_authorized": false, + "ps5_connection_authorized": false, + "device_request_authorized": false, + "result_receive_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "installation_authorized": false, + "autoload_authorized": false, + "device_write_authorized": false, + "automatic_retry": false, + "reconnect_authorized": false, + "resume_authorized": false + }, + "decision": { + "offline_fake_batch_integration_complete": true, + "live_adapter_created": false, + "live_adapter_allowed": false, + "live_collection_allowed": false, + "device_action_allowed": false, + "phase10ab_offline_live_adapter_feasibility_review_allowed": true, + "next_step": "OFFLINE_LIVE_ADAPTER_TIMEOUT_AND_CLEANUP_FEASIBILITY_REVIEW" + }, + "performed_actions": { + "network_client_created": false, + "network_socket_opened": false, + "ps5_address_used": false, + "ps5_connected": false, + "device_request_performed": false, + "result_received_from_device": false, + "target_source_changed": false, + "target_artifact_created": false, + "target_execution_performed": false + }, + "tests": { + "chimera_gfx_ctest": "89_OF_89_PASS", + "phase10aa_guardrails": 20, + "phase10aa_integration_tests": 25, + "safety_audit": "PASS", + "secret_scan": "PASS", + "network_required_by_tests": false, + "hardware_claim_from_host_test": false + } +} diff --git a/manifests/retroarch/phase-1.0ab-live-adapter-feasibility.json b/manifests/retroarch/phase-1.0ab-live-adapter-feasibility.json new file mode 100644 index 0000000..de34f3f --- /dev/null +++ b/manifests/retroarch/phase-1.0ab-live-adapter-feasibility.json @@ -0,0 +1,120 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0AB_OFFLINE_LIVE_ADAPTER_FEASIBILITY", + "status": "PARTIAL_FEASIBILITY_LIVE_IMPLEMENTATION_BLOCKED", + "date": "2026-07-22", + "start_commit": "06d2fe831959b71562401722ae36821faa197636", + "activation": { + "active": false, + "trace_model_sha256": null, + "target_address": null, + "target_port": null, + "run_id": null + }, + "local_runtime": { + "python_version": "3.13.2", + "compiler": "MSC_V_1942_64_BIT_AMD64", + "platform": "WINDOWS", + "default_selector": "SelectSelector", + "monotonic_implementation": "QueryPerformanceCounter()", + "monotonic": true, + "monotonic_adjustable": false, + "reported_resolution_seconds": 1e-7, + "socket_py_size": 38741, + "socket_py_sha256": "523695ac3383799547b421b4fe18004de1e80181e97181b6d7a10533b47f4c49", + "selectors_py_size": 20060, + "selectors_py_sha256": "b3d6cebd4a3a03b4a614f12f171622ce4e4ba3295b9e8b89e2bde051003106eb", + "socket_pyd_size": 84984, + "socket_pyd_sha256": "8daefaff53e6956f5aea5279a7c71f17d8c63e2b0d54031c3b9e82fcb0fb84db", + "select_pyd_size": 32248, + "select_pyd_sha256": "baee284995b22d495fd12fa8378077e470978db1522c61bfb9af37fb827f33d1" + }, + "source_bindings": { + "phase10aa_integration_sha256": "8e1cac255f85d2cd14baf8fbc27d631c9b607fc7d19fc57c65462089f0574055", + "phase10ab_trace_model_size": 8738, + "phase10ab_trace_model_sha256": "7d1aa32d49b91b1e5cf3a085dda033767bdf17ab34389ff044f7403f86287959", + "phase10ab_trace_tests_size": 7558, + "phase10ab_trace_tests_sha256": "39597e991b15bcfa9aa28cb2f68c87ed048c38482c6dbf81a28c56ccfceb0a48" + }, + "feasibility": { + "receipt_before_socket": "FEASIBLE_FROM_EXISTING_HOST_MODEL", + "numeric_address_only": "DESIGN_REQUIRED", + "nonblocking_before_connect": "FEASIBLE_FROM_LOCAL_RUNTIME", + "pending_connect": "PARTIAL", + "complete_send_loop": "FEASIBLE_FROM_LOCAL_RUNTIME", + "bounded_receive_memory": "FEASIBLE_FROM_EXISTING_MODEL", + "hard_wall_clock_deadline": "PARTIAL", + "prompt_independent_completion": "FEASIBLE_FROM_Z", + "remote_eof": "FEASIBLE_FAIL_CLOSED", + "local_descriptor_cleanup": "FEASIBLE_BY_DESIGN", + "remote_shell_cleanup": "UNPROVEN", + "retry_reconnect_resume": "EXCLUDED" + }, + "trace_model": { + "synthetic_input_only": true, + "maximum_events": 512, + "maximum_batch_bytes": 1035, + "maximum_receive_bytes": 65536, + "maximum_deadline_seconds": 10, + "network_import_present": false, + "selector_import_present": false, + "real_clock_present": false, + "address_present": false, + "cli_present": false, + "file_output_present": false, + "exact_identity_proven": false, + "device_behavior_proven": false + }, + "hard_stops": { + "live_adapter_implementation": true, + "socket_creation": true, + "dns": true, + "target_retention": true, + "connection": true, + "request": true, + "retry": true, + "device_action": true + }, + "authorizations": { + "target_build_authorized": false, + "ps5_connection_authorized": false, + "device_request_authorized": false, + "result_receive_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "installation_authorized": false, + "autoload_authorized": false, + "device_write_authorized": false, + "automatic_retry": false, + "reconnect_authorized": false, + "resume_authorized": false + }, + "decision": { + "overall": "PARTIAL_FEASIBILITY_LIVE_IMPLEMENTATION_BLOCKED", + "live_adapter_created": false, + "live_adapter_allowed": false, + "device_action_allowed": false, + "phase10ac_offline_dormant_syscall_facade_allowed": true, + "next_step": "OFFLINE_DORMANT_TARGET_FREE_ADAPTER_WITH_FAKE_SYSCALLS" + }, + "performed_actions": { + "network_client_created": false, + "network_socket_opened": false, + "ps5_address_used": false, + "ps5_connected": false, + "device_request_performed": false, + "result_received_from_device": false, + "target_source_changed": false, + "target_artifact_created": false, + "target_execution_performed": false + }, + "tests": { + "chimera_gfx_ctest": "92_OF_92_PASS", + "phase10ab_guardrails": 20, + "phase10ab_trace_tests": 25, + "safety_audit": "PASS", + "secret_scan": "PASS", + "network_required_by_tests": false, + "hardware_claim_from_host_test": false + } +} diff --git a/manifests/retroarch/phase-1.0ac-dormant-adapter.json b/manifests/retroarch/phase-1.0ac-dormant-adapter.json new file mode 100644 index 0000000..ff167e1 --- /dev/null +++ b/manifests/retroarch/phase-1.0ac-dormant-adapter.json @@ -0,0 +1,110 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0AC_OFFLINE_DORMANT_ADAPTER", + "status": "OFFLINE_DORMANT_FAKE_SYSCALL_ADAPTER_COMPLETE_LIVE_ADAPTER_BLOCKED", + "date": "2026-07-22", + "start_commit": "3bc8ac09615dda3c4ee3ad02f19440ca9f4f8f96", + "activation": { + "active": false, + "adapter_sha256": null, + "target_address": null, + "target_port": null, + "run_id": null + }, + "source_bindings": { + "phase10ab_trace_model_sha256": "7d1aa32d49b91b1e5cf3a085dda033767bdf17ab34389ff044f7403f86287959", + "phase10ac_adapter_size": 13282, + "phase10ac_adapter_sha256": "6f28926b59fd9afa6de1ff36d4fa9b013d7e027c9adc0bffd9445d7c89acf939", + "phase10ac_tests_size": 11681, + "phase10ac_tests_sha256": "9c8c611dbab5e43df71d523169d9a1bf7579ed1918943df7531bf75ef4b9abfc" + }, + "adapter": { + "exact_builtin_fake_facade_required": true, + "exact_builtin_fake_clock_required": true, + "precommitted_receipt_required": true, + "maximum_fake_steps": 1024, + "maximum_receive_bytes": 65536, + "maximum_batch_bytes": 1035, + "maximum_deadline_seconds": 10, + "network_import_present": false, + "selector_import_present": false, + "dns_present": false, + "real_clock_present": false, + "address_present": false, + "cli_present": false, + "file_output_present": false, + "live_adapter_protocol_present": false, + "target_retained": false, + "device_behavior_proven": false + }, + "lifecycle": { + "create_count": 1, + "nonblocking_before_connect": true, + "pending_connect_requires_write_ready": true, + "pending_connect_requires_zero_so_error": true, + "partial_write_loop": true, + "zero_write_rejected": true, + "remote_eof_rejected": true, + "deadline_wins_readiness_race": true, + "deadline_only_completion": true, + "local_close_on_success": true, + "local_close_on_failure": true, + "remote_cleanup_proven": false, + "automatic_retry": false, + "reconnect": false, + "resume": false + }, + "hard_stops": { + "live_adapter_implementation": true, + "socket_creation": true, + "dns": true, + "target_retention": true, + "connection": true, + "device_request": true, + "retry": true, + "device_action": true + }, + "authorizations": { + "target_build_authorized": false, + "ps5_connection_authorized": false, + "device_request_authorized": false, + "result_receive_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "installation_authorized": false, + "autoload_authorized": false, + "device_write_authorized": false, + "automatic_retry": false, + "reconnect_authorized": false, + "resume_authorized": false + }, + "performed_actions": { + "network_client_created": false, + "network_socket_opened": false, + "ps5_address_used": false, + "ps5_connected": false, + "device_request_performed": false, + "result_received_from_device": false, + "target_source_changed": false, + "target_artifact_created": false, + "target_execution_performed": false + }, + "decision": { + "overall": "OFFLINE_DORMANT_FAKE_SYSCALL_ADAPTER_COMPLETE_LIVE_ADAPTER_BLOCKED", + "dormant_fake_adapter_created": true, + "live_adapter_created": false, + "live_adapter_allowed": false, + "device_action_allowed": false, + "phase10ad_offline_inactive_activation_design_allowed": true, + "next_step": "OFFLINE_NUMERIC_TARGET_AND_INACTIVE_ACTIVATION_CONTRACT" + }, + "tests": { + "chimera_gfx_ctest": "95_OF_95_PASS", + "phase10ac_guardrails": 20, + "phase10ac_adapter_tests": 32, + "safety_audit": "PASS", + "secret_scan": "PASS", + "network_required_by_tests": false, + "hardware_claim_from_host_test": false + } +} diff --git a/manifests/retroarch/phase-1.0ad-inactive-activation.json b/manifests/retroarch/phase-1.0ad-inactive-activation.json new file mode 100644 index 0000000..4ff1b38 --- /dev/null +++ b/manifests/retroarch/phase-1.0ad-inactive-activation.json @@ -0,0 +1,60 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0AD_INACTIVE_NUMERIC_TARGET_CONTRACT", + "status": "INACTIVE_NUMERIC_TARGET_CONTRACT_COMPLETE_NO_LIVE_CAPABILITY", + "date": "2026-07-29", + "source_bindings": { + "contract_size": 5624, + "contract_sha256": "b28c80a34bc30ae2d4db8df1360c8d5ce3515de8b91d2bba409a0a404da4b79a", + "tests_size": 3808, + "tests_sha256": "5800f8bc393921b31fcf0df3bd853b5936ec2ad5a3fef100505ab04af9729d15", + "documentation_size": 1154, + "documentation_sha256": "6cc0b2dc8140e6860929e543df820d4d9905c8720a366b70cd1a008f7548d93e" + }, + "activation": { + "active": false, + "target_address": null, + "target_port": null, + "run_id": null, + "not_before": null, + "expires_at": null, + "launcher_sha256": null, + "payload_sha256": null, + "approval_sha256": null, + "one_shot": true, + "automatic_retry": false, + "reconnect": false, + "resume": false + }, + "contract": { + "numeric_private_ipv4_only": true, + "source_bound_port": 2323, + "maximum_window_seconds": 300, + "exact_launcher_hash_required": true, + "exact_payload_hash_required": true, + "separate_approval_hash_required": true, + "dns_present": false, + "socket_present": false, + "clock_present": false, + "cli_present": false, + "file_output_present": false, + "live_adapter_present": false + }, + "authorizations": { + "ps5_connection_authorized": false, + "device_request_authorized": false, + "result_receive_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "installation_authorized": false, + "device_write_authorized": false, + "app_termination_authorized": false, + "system_remount_authorized": false + }, + "decision": { + "overall": "INACTIVE_NUMERIC_TARGET_CONTRACT_COMPLETE_NO_LIVE_CAPABILITY", + "device_action_allowed": false, + "bigapp_launcher_implementation_allowed": false, + "next_step": "SEPARATE_REVIEW_OF_MINIMAL_BIGAPP_LAUNCHER_ARCHITECTURE" + } +} diff --git a/manifests/retroarch/phase-1.0ae-launcher-architecture.json b/manifests/retroarch/phase-1.0ae-launcher-architecture.json new file mode 100644 index 0000000..afa3d64 --- /dev/null +++ b/manifests/retroarch/phase-1.0ae-launcher-architecture.json @@ -0,0 +1,72 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0AE_MINIMAL_BIGAPP_LAUNCHER_ARCHITECTURE", + "status": "V07_NONPERSISTENT_LINEAGE_SELECTED_TARGET_IMPLEMENTATION_BLOCKED", + "date": "2026-07-29", + "official_sources": { + "v019_commit": "6f320637d56d344a0e7797753099e33238bbf146", + "v019_tree": "c26ce02b6c3ca4202993e039b3db7c28c353dee4", + "v019_hbldr_sha256": "0096f86a00fdedcbc7509db47eb0f54dbc3e487d37e699f3de79048bac001be9", + "v019_elfldr_sha256": "4aa31f5a942681f8d88281b9713f087ced8963de842438cacad1088933f8e785", + "v019_pt_sha256": "1fee8e3007d8ade9c9f72eb237fe1a125426f9e127544d7122124125378ceff3", + "v07_commit": "74287f5db6b20320efd7892d7b29cf438fe7cb98", + "v07_tree": "7184968c702afe038551bf3228cc25f455388bb6", + "v07_hbldr_sha256": "2081ece2f7d0a7e9b392660696c2a44802f64680365607e742b37dcf3223a55a", + "v07_elfldr_sha256": "1ff6cfa1300a95e8be48e5f7adc1413e3384ba04ea266c167fcd441ef20197d1", + "v07_pt_sha256": "7588867fc9a9bd82e751aac9c58131b641dc2d22d743bfafc69076b000a5a3e3" + }, + "lineage_decision": { + "selected_reference": "OFFICIAL_SHSRV_V0_7", + "fixed_existing_title": "PPSA01659", + "v019_rejected_for_persistent_creation": true, + "v019_rejected_for_system_ex_remount": true, + "v07_fakeapp_creation_present": false, + "v07_system_ex_remount_present": false, + "firmware_9_60_title_availability_proven": false, + "videoout_ownership_proven": false, + "root_cause_proven": false + }, + "mandatory_policy": { + "require_no_running_bigapp": true, + "kill_existing_bigapp": false, + "fixed_payload_hash": true, + "fixed_argument_vector": true, + "bounded_waits": true, + "unique_child_correlation": true, + "complete_parent_detach": true, + "complete_child_cleanup": true, + "temporary_mutation_restoration": true, + "automatic_retry": false, + "fallback_title": false, + "persistent_write": false, + "system_remount": false, + "installation": false, + "autoload": false + }, + "hard_blockers": [ + "BOUNDED_WAIT_PRIMITIVES_UNDESIGNED", + "UNIQUE_CHILD_CORRELATION_UNPROVEN", + "COMPLETE_FAILURE_CLEANUP_UNPROVEN", + "TEMPORARY_MUTATION_RESTORATION_UNPROVEN", + "BOUNDED_ELF_VALIDATION_UNDESIGNED", + "PPSA01659_FIRMWARE_9_60_ROLE_UNPROVEN", + "SYSTEMSERVICE_DECLARATIONS_SINGLE_LINEAGE", + "KERNEL_PTRACE_EFFECT_POLICY_UNREVIEWED" + ], + "authorizations": { + "target_source_authorized": false, + "target_build_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "device_write_authorized": false, + "app_termination_authorized": false, + "system_remount_authorized": false + }, + "decision": { + "target_implementation_allowed": false, + "device_action_allowed": false, + "host_lifecycle_model_allowed": true, + "next_step": "OFFLINE_INJECTED_BIGAPP_LIFECYCLE_MODEL" + } +} diff --git a/manifests/retroarch/phase-1.0af-bigapp-lifecycle-model.json b/manifests/retroarch/phase-1.0af-bigapp-lifecycle-model.json new file mode 100644 index 0000000..5cc859e --- /dev/null +++ b/manifests/retroarch/phase-1.0af-bigapp-lifecycle-model.json @@ -0,0 +1,57 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0AF_OFFLINE_BIGAPP_LIFECYCLE_MODEL", + "status": "HOST_LIFECYCLE_MODEL_COMPLETE_TARGET_IMPLEMENTATION_BLOCKED", + "date": "2026-07-29", + "source_bindings": { + "phase10ae_manifest_sha256": "eeebff316a84b184bb802485e0ed5ad42016f3236ba44c04e718c9fb08ca680d", + "model_size": 8096, + "model_sha256": "00c8bed501715d737ad1328f3d4c98fe16f69c061016a939ecb3774d9c1f28f3", + "tests_size": 4749, + "tests_sha256": "d112a5202fd3520a407256ebb64e1b06e37f76a52d7e6cfe95d2410ae4c834a1" + }, + "model": { + "fixed_title": "PPSA01659", + "maximum_main_ticks": 64, + "maximum_fake_events": 64, + "maximum_cleanup_events": 3, + "require_no_existing_bigapp": true, + "unique_positive_child_id": true, + "parent_detach_on_every_acquired_path": true, + "restore_before_child_termination": true, + "terminate_only_observed_new_child": true, + "existing_bigapp_killed": false, + "target_code_present": false, + "socket_present": false, + "process_api_present": false, + "syscall_present": false, + "real_clock_present": false, + "filesystem_output_present": false + }, + "remaining_blockers": [ + "REAL_BOUNDED_WAIT_IMPLEMENTATION_ABSENT", + "REAL_UNIQUE_CHILD_CORRELATION_UNPROVEN", + "TARGET_CLEANUP_BEHAVIOR_UNPROVEN", + "BOUNDED_ELF_VALIDATOR_ABSENT", + "PPSA01659_FIRMWARE_9_60_ROLE_UNPROVEN", + "SYSTEMSERVICE_DECLARATIONS_SINGLE_LINEAGE", + "KERNEL_PTRACE_EFFECT_POLICY_UNREVIEWED" + ], + "authorizations": { + "target_source_authorized": false, + "target_build_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "device_write_authorized": false, + "app_termination_authorized": false, + "system_remount_authorized": false + }, + "decision": { + "host_model_complete": true, + "target_implementation_allowed": false, + "device_action_allowed": false, + "bounded_elf_host_validator_allowed": true, + "next_step": "OFFLINE_BOUNDED_ELF_CONTRACT_AND_VALIDATOR" + } +} diff --git a/manifests/retroarch/phase-1.0ag-bounded-elf-contract.json b/manifests/retroarch/phase-1.0ag-bounded-elf-contract.json new file mode 100644 index 0000000..686e7bb --- /dev/null +++ b/manifests/retroarch/phase-1.0ag-bounded-elf-contract.json @@ -0,0 +1,64 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0AG_OFFLINE_BOUNDED_ELF_CONTRACT", + "status": "BOUNDED_ELF_ADMISSION_IMPLEMENTED_HISTORICAL_ARTIFACT_BYTES_MISSING", + "date": "2026-07-29", + "source_bindings": { + "phase10af_manifest_sha256": "c0e74c0e828168faede5f3db3e9964b6191d6cd9d9834dc3926e0807feee273c", + "validator_size": 6078, + "validator_sha256": "748c93eb5c9275c992e4b471747ff0dbbcf71cd5b795f44d47f2c82e19691270", + "tests_size": 4305, + "tests_sha256": "774afc1151044f4362bcfcd92fad97286d23477046fb4b542a4cf739723b0c36" + }, + "admission": { + "exact_sha256_before_parse": true, + "maximum_file_bytes": 2097152, + "elf_class": 64, + "endianness": "LITTLE", + "elf_type": "ET_DYN", + "machine": "EM_X86_64", + "maximum_program_headers": 32, + "maximum_section_headers": 256, + "maximum_load_segments": 8, + "maximum_total_load_memory": 67108864, + "maximum_load_span": 134217728, + "maximum_alignment": 2097152, + "writable_executable_load_allowed": false, + "interpreter_allowed": false, + "overlapping_load_ranges_allowed": false, + "entry_must_be_in_executable_load": true, + "file_interface_present": false, + "execution_interface_present": false + }, + "historical_reference": { + "phase10m_size": 1845208, + "phase10m_sha256": "c99a0856309a357ad2667d89b4924e4063ad214cae09c8a419457b0732f583cd", + "bytes_present_in_scanned_workspace": false, + "validated_by_phase10ag": false, + "reason": "HISTORICAL_ARTIFACT_BYTES_NOT_LOCALLY_AVAILABLE" + }, + "remaining_blockers": [ + "EXACT_FUTURE_CANARY_BYTES_NOT_BUILT", + "DYNAMIC_RELOCATION_CONTRACT_UNDESIGNED", + "TARGET_MAPPING_IMPLEMENTATION_ABSENT", + "PPSA01659_FIRMWARE_9_60_ROLE_UNPROVEN", + "SYSTEMSERVICE_DECLARATIONS_SINGLE_LINEAGE", + "KERNEL_PTRACE_EFFECT_POLICY_UNREVIEWED" + ], + "authorizations": { + "target_source_authorized": false, + "target_build_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "device_write_authorized": false + }, + "decision": { + "bounded_elf_contract_complete": true, + "historical_artifact_admitted": false, + "target_implementation_allowed": false, + "device_action_allowed": false, + "relocation_contract_design_allowed": true, + "next_step": "OFFLINE_DYNAMIC_RELOCATION_CONTRACT" + } +} diff --git a/manifests/retroarch/phase-1.0ah-dynamic-contract.json b/manifests/retroarch/phase-1.0ah-dynamic-contract.json new file mode 100644 index 0000000..46bf4f4 --- /dev/null +++ b/manifests/retroarch/phase-1.0ah-dynamic-contract.json @@ -0,0 +1,90 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0AH_OFFLINE_DYNAMIC_AND_RELOCATION_CONTRACT", + "status": "LOADER_CRT_RELOCATION_SPLIT_BOUNDED_TARGET_MAPPING_BLOCKED", + "date": "2026-07-29", + "source_bindings": { + "phase10ag_manifest_sha256": "4178adf244650d7359cf400492a8d8404b60296a5aee5fc88df4a32adf61bdf1", + "contract_size": 7710, + "contract_sha256": "51ffa5d8acc475f1edae48f104d27c48ba2725459b217326694f1361552aee3c", + "tests_size": 5044, + "tests_sha256": "d78eb2441e091bb98586aae36a4e0b1aca24ea8a62feb27103c1e1461b4aa415", + "official_shsrv_v07_commit": "74287f5db6b20320efd7892d7b29cf438fe7cb98", + "official_shsrv_v07_elfldr_sha256": "1ff6cfa1300a95e8be48e5f7adc1413e3384ba04ea266c167fcd441ef20197d1", + "official_sdk_v041_commit": "d2e2e585740362976a39fdd5ccf390f199a7bc37", + "official_sdk_v041_rtld_payload_sha256": "5598ca0870c2c7a120507755897d76e5aad0d3a6846cee4482230650ed181f82" + }, + "contract": { + "maximum_dynamic_entries": 256, + "maximum_needed_modules": 16, + "maximum_needed_name_bytes": 128, + "maximum_relocation_sections": 4, + "maximum_relocations": 4096, + "loader_applied_relocation_types": ["R_X86_64_RELATIVE"], + "crt_applied_relocation_types": ["R_X86_64_GLOB_DAT"], + "unknown_relocation_types_allowed": false, + "relocation_target_requires_aligned_rw_load": true, + "relative_requires_zero_symbol_and_mapped_addend": true, + "glob_dat_requires_nonzero_symbol_and_zero_addend": true, + "exact_needed_order_required": true, + "file_interface_present": false, + "mapping_interface_present": false, + "module_loading_interface_present": false + }, + "module_allowlist": [ + "libSceAudioOut.sprx", + "libSceLibcInternal.sprx", + "libScePad.sprx", + "libSceSystemService.sprx", + "libSceUserService.sprx", + "libSceVideoOut.sprx", + "libkernel_web.sprx" + ], + "historical_phase10m_reference": { + "exact_bytes_present": false, + "validated_by_phase10ah": false, + "relative_relocations": 913, + "glob_dat_relocations": 142, + "total_relocations": 1055, + "needed_modules": [ + "libSceAudioOut.sprx", + "libSceLibcInternal.sprx", + "libScePad.sprx", + "libSceSystemService.sprx", + "libSceUserService.sprx", + "libSceVideoOut.sprx", + "libkernel_web.sprx" + ] + }, + "runtime_classification": { + "relative_application": "FUTURE_LAUNCHER_BOUNDED_WRITE_MODEL_ONLY", + "glob_dat_resolution": "SDK_CRT_SIDE_EFFECTING_RUNTIME", + "dt_needed_loading": "SDK_CRT_SIDE_EFFECTING_RUNTIME", + "firmware_9_60_runtime_proven": false, + "videoout_behavior_proven": false + }, + "remaining_blockers": [ + "EXACT_FUTURE_CANARY_BYTES_NOT_BUILT", + "TARGET_MAPPING_AND_PROTECTION_RESTORATION_UNDESIGNED", + "SDK_CRT_MODULE_LOADING_SIDE_EFFECTS_RUNTIME_UNPROVEN", + "PPSA01659_FIRMWARE_9_60_ROLE_UNPROVEN", + "SYSTEMSERVICE_DECLARATIONS_SINGLE_LINEAGE", + "KERNEL_PTRACE_EFFECT_POLICY_UNREVIEWED" + ], + "authorizations": { + "target_source_authorized": false, + "target_build_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "device_write_authorized": false + }, + "decision": { + "dynamic_contract_complete": true, + "target_mapping_allowed": false, + "target_implementation_allowed": false, + "device_action_allowed": false, + "mapping_state_model_allowed": true, + "next_step": "OFFLINE_MAPPING_PROTECTION_AND_RESTORATION_MODEL" + } +} diff --git a/manifests/retroarch/phase-1.0ai-mapping-model.json b/manifests/retroarch/phase-1.0ai-mapping-model.json new file mode 100644 index 0000000..8e2a598 --- /dev/null +++ b/manifests/retroarch/phase-1.0ai-mapping-model.json @@ -0,0 +1,65 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0AI_OFFLINE_MAPPING_TRANSACTION_MODEL", + "status": "HOST_MAPPING_ROLLBACK_MODEL_COMPLETE_TARGET_MAPPING_BLOCKED", + "date": "2026-07-29", + "source_bindings": { + "phase10ah_manifest_sha256": "ecb38325bcfdda905a5909f82ca690a174806d0108a45e716b4f8f5a58da168d", + "model_size": 9722, + "model_sha256": "26c01c09086f04650dc8834205439e21a9fbe5f92ec78d15a769dbb09487fac0", + "tests_size": 6179, + "tests_sha256": "d5454bcb6f249fccf99d6883a4248d00c6daa5dcac7ff640e9e72418378e6529" + }, + "bounds": { + "page_size": 16384, + "maximum_segments": 8, + "maximum_relative_relocations": 4096, + "maximum_fake_events": 64, + "maximum_main_ticks": 128, + "maximum_region_size": 134217728 + }, + "transaction": { + "reserve_one_new_child_region": true, + "create_one_host_mirror": true, + "copy_exact_file_bytes_per_segment": true, + "zero_exact_bss_per_segment": true, + "apply_relative_before_child_copy": true, + "single_mirror_to_child_copy": true, + "final_non_wx_protection_per_segment": true, + "sync_before_commit": true, + "release_mirror_before_commit": true, + "deadline_preempts_operation_atomically": true, + "failure_releases_mirror": true, + "failure_unmaps_entire_new_child_region": true, + "partial_mapping_retained_on_failure": false, + "target_memory_interface_present": false, + "host_memory_mapping_present": false, + "process_interface_present": false + }, + "remaining_blockers": [ + "REAL_CHILD_RESERVE_UNMAP_PRIMITIVES_UNBOUND", + "REAL_MIRROR_ALIAS_AND_COPY_PRIMITIVES_UNBOUND", + "REAL_FINAL_PROTECTION_RESTORATION_UNPROVEN", + "REAL_CACHE_SYNC_CONTRACT_UNPROVEN", + "SDK_CRT_MODULE_LOADING_RUNTIME_UNPROVEN", + "PPSA01659_FIRMWARE_9_60_ROLE_UNPROVEN", + "SYSTEMSERVICE_DECLARATIONS_SINGLE_LINEAGE", + "KERNEL_PTRACE_EFFECT_POLICY_UNREVIEWED" + ], + "authorizations": { + "target_source_authorized": false, + "target_build_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "device_write_authorized": false + }, + "decision": { + "host_mapping_model_complete": true, + "target_mapping_allowed": false, + "target_implementation_allowed": false, + "device_action_allowed": false, + "primitive_source_audit_allowed": true, + "next_step": "OFFLINE_MAPPING_PRIMITIVE_SOURCE_AND_RESTORATION_AUDIT" + } +} diff --git a/manifests/retroarch/phase-1.0aj-primitive-audit.json b/manifests/retroarch/phase-1.0aj-primitive-audit.json new file mode 100644 index 0000000..f9512cd --- /dev/null +++ b/manifests/retroarch/phase-1.0aj-primitive-audit.json @@ -0,0 +1,60 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0AJ_OFFLINE_PRIMITIVE_SOURCE_AUDIT", + "status": "HYBRID_PRIMITIVE_REUSE_CANDIDATE_GAPS_REMAIN_TARGET_CODE_BLOCKED", + "date": "2026-07-29", + "source_bindings": { + "phase10ai_manifest_sha256": "c0d19a8a4df45f2311861c1cd1e7ab30d262c97ddc5d317e6229a3ec27d415f5", + "shsrv_v07_commit": "74287f5db6b20320efd7892d7b29cf438fe7cb98", + "shsrv_v07_elfldr_sha256": "1ff6cfa1300a95e8be48e5f7adc1413e3384ba04ea266c167fcd441ef20197d1", + "shsrv_v07_pt_sha256": "7588867fc9a9bd82e751aac9c58131b641dc2d22d743bfafc69076b000a5a3e3", + "hardened_elfldr_commit": "197623058f509eddde18868dafcb92fdcac66464", + "controlled_runtime_c_sha256": "2463efc4978373a19b9f9be6e45facc974dd1b533aed3c2160649f762d8caffa", + "controlled_runtime_h_sha256": "5f80b4502d60b61e90d13fbfd6b5627b85dc58500e27b2656bb112aa01bc32f8", + "hardened_pt_sha256": "397bd178b11c729a20e3df97be9bbb1ae93408e3a12656d224567da7c9610df7", + "ps5_controlled_sha256": "7824305bcd6520c70265dd2138f1de3aae659d32239a01fcb049590302bab0cf" + }, + "primitive_matrix": { + "bigapp_creation": "SHSRV_V07_SOURCE_CANDIDATE_ONLY", + "bounded_remote_call_and_syscall": "HARDENED_ELFLDR_REUSABLE_CANDIDATE", + "credential_backup_and_restore": "HARDENED_ELFLDR_REUSABLE_CANDIDATE", + "mapping_and_fd_cleanup_tracking": "HARDENED_ELFLDR_PARTIAL_REUSABLE_CANDIDATE", + "child_kill_and_bounded_reap": "HARDENED_ELFLDR_REUSABLE_CANDIDATE", + "mirror_and_jit_alias_transaction": "SHSRV_V07_UNHARDENED_NOT_REUSABLE", + "complete_bigapp_composition": "ABSENT", + "firmware_960_runtime_proof": "ABSENT" + }, + "findings": [ + "SHSRV_V07_REMOTE_CALLS_HAVE_NO_STEP_OR_DEADLINE_BOUND", + "SHSRV_V07_IGNORES_ALIAS_UNMAP_FD_CLOSE_HOST_UNMAP_AND_ROLLBACK_RESULTS", + "HARDENED_ELFLDR_BOUNDS_PTRACE_CALLS_AND_RESTORES_REGISTERS", + "HARDENED_ELFLDR_ATTEMPTS_ALL_CREDENTIAL_RESTORES_AND_FAILS_CLOSED", + "HARDENED_ELFLDR_CLEANUP_REPORTS_MAPPING_FD_BREAKPOINT_AND_CHILD_FAILURES", + "HARDENED_ELFLDR_CLEANUP_CLEARS_OWNERSHIP_BITS_AFTER_FAILED_CLEANUP", + "HARDENED_ELFLDR_MAPPING_CLEANUP_TRACKS_ONLY_ARGS_AND_WHOLE_ELF_REGION", + "BIGAPP_CREATION_AND_HARDENED_REPLACEMENT_HAVE_NOT_BEEN_COMPOSED" + ], + "remaining_blockers": [ + "BIGAPP_IDENTITY_AND_UNIQUE_CORRELATION_NOT_BOUND_TO_HARDENED_RUNTIME", + "JIT_FD_ALIAS_AND_HOST_MIRROR_ACQUISITION_NOT_IN_CENTRAL_CLEANUP_STATE", + "CLEANUP_FAILURE_OWNERSHIP_AND_TERMINATION_POLICY_NEEDS_COMPOSITION_MODEL", + "MDBG_COPY_PARTIAL_PROGRESS_AND_CREDENTIAL_RESTORE_CONTRACT_UNBOUND", + "MSYNC_AND_FINAL_PROTECTION_FAILURE_ROLLBACK_NOT_SOURCE_PROVEN_AS_ONE_TRANSACTION", + "TARGET_BUILD_AND_FIRMWARE_960_RUNTIME_UNPROVEN" + ], + "authorizations": { + "target_source_authorized": false, + "target_build_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "device_write_authorized": false + }, + "decision": { + "copy_shsrv_loader_code_allowed": false, + "target_implementation_allowed": false, + "device_action_allowed": false, + "host_composition_model_allowed": true, + "next_step": "OFFLINE_HYBRID_PRIMITIVE_COMPOSITION_AND_CLEANUP_MODEL" + } +} diff --git a/manifests/retroarch/phase-1.0ak-hybrid-composition.json b/manifests/retroarch/phase-1.0ak-hybrid-composition.json new file mode 100644 index 0000000..ae9cfe3 --- /dev/null +++ b/manifests/retroarch/phase-1.0ak-hybrid-composition.json @@ -0,0 +1,57 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0AK_OFFLINE_HYBRID_COMPOSITION_MODEL", + "status": "HOST_HYBRID_RESOURCE_OWNERSHIP_MODEL_COMPLETE_TARGET_CODE_BLOCKED", + "date": "2026-07-29", + "source_bindings": { + "phase10aj_manifest_sha256": "2883c11d3e798ca6f838bb5f8978f2b95a1598630d15a7d9b0d3b35ed3338144", + "model_size": 8093, + "model_sha256": "35115ccdc201f7c6040eb0d0638d4ece7f36ddc1386ed8c4352f7c73426d3751", + "tests_size": 5953, + "tests_sha256": "8f76fb3f9d91ed96e2a19c4b3386915f306187a1f0ee2e842d24351c282ef026" + }, + "bounds": { + "maximum_executable_segments": 8, + "maximum_fake_events": 256, + "maximum_main_ticks": 256 + }, + "ownership": { + "child": "EXPLICIT", + "whole_image_region": "EXPLICIT", + "host_mirror": "EXPLICIT", + "jit_master_descriptors": "PER_EXECUTABLE_SEGMENT", + "jit_alias_descriptors": "PER_EXECUTABLE_SEGMENT", + "host_alias_mappings": "PER_EXECUTABLE_SEGMENT", + "remote_alias_mappings": "PER_EXECUTABLE_SEGMENT", + "reverse_cleanup": true, + "cleanup_failure_requires_child_termination": true, + "failed_child_termination_is_hard_error": true, + "partial_success_allowed": false, + "real_process_interface_present": false, + "real_mapping_interface_present": false, + "real_clock_present": false + }, + "remaining_blockers": [ + "MDBG_COPY_EXACT_PARTIAL_PROGRESS_CONTRACT_UNPROVEN", + "BIGAPP_UNIQUE_IDENTITY_BINDING_NOT_IMPLEMENTED", + "HARDENED_PRIMITIVE_API_COMPOSITION_NOT_IMPLEMENTED", + "CONTAINING_SERVICE_SURVIVAL_AFTER_CHILD_CLEANUP_FAILURE_UNPROVEN", + "PS5_CROSS_TARGET_BUILD_NOT_AUTHORIZED", + "FIRMWARE_960_RUNTIME_BEHAVIOR_UNPROVEN" + ], + "authorizations": { + "target_source_authorized": false, + "target_build_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "device_write_authorized": false + }, + "decision": { + "host_composition_model_complete": true, + "target_implementation_allowed": false, + "device_action_allowed": false, + "copy_contract_audit_allowed": true, + "next_step": "OFFLINE_MDBG_COPY_PARTIAL_PROGRESS_AND_RESTORE_AUDIT" + } +} diff --git a/manifests/retroarch/phase-1.0al-mdbg-copy-audit.json b/manifests/retroarch/phase-1.0al-mdbg-copy-audit.json new file mode 100644 index 0000000..d31d85d --- /dev/null +++ b/manifests/retroarch/phase-1.0al-mdbg-copy-audit.json @@ -0,0 +1,53 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0AL_OFFLINE_MDBG_COPY_AND_RESTORE_AUDIT", + "status": "SDK_MDBG_COPY_NOT_FAIL_CLOSED_DIRECT_REUSE_BLOCKED", + "date": "2026-07-29", + "source_bindings": { + "phase10ak_manifest_sha256": "3322d42eb18718f1de7c85d043e68f37a9c5b5632f0d9565fd3f7b6d2ffd3d00", + "sdk_commit": "d2e2e585740362976a39fdd5ccf390f199a7bc37", + "mdbg_c_size": 3824, + "mdbg_c_sha256": "98add54968b138ca7fcf200e08587e0722e270960a637241bca841e75dfedc3c", + "public_header_size": 1657, + "public_header_sha256": "0c6695b6865c13112b8187f605f3a5c8a8e4cc32240cef76c753c8e1546df18d" + }, + "findings": { + "authid_changed_before_caps": true, + "caps_set_failure_restores_authid": false, + "restore_attempts_all_fields_after_one_failure": false, + "partial_progress_exposed_to_caller": false, + "zero_progress_nonzero_status_is_hard_failure": false, + "result_length_bounded_by_remaining": false, + "address_and_length_arithmetic_checked": false, + "iteration_ceiling_present": false, + "monotonic_deadline_present": false, + "target_may_be_partially_mutated_on_error": true, + "return_zero_proves_complete_copy": false + }, + "required_replacement_contract": [ + "BACKUP_AUTHID_AND_CAPS_BEFORE_ANY_CHANGE", + "RESTORE_EVERY_CHANGED_FIELD_ON_EVERY_EXIT", + "REPORT_RESTORE_FAILURE_BITS_SEPARATELY", + "REQUIRE_ZERO_STATUS_AND_EXACT_TOTAL_LENGTH", + "REJECT_ZERO_OR_OVERSIZED_PROGRESS", + "CHECK_POINTER_AND_LENGTH_ARITHMETIC", + "BOUND_ITERATIONS_AND_MONOTONIC_DEADLINE", + "REPORT_EXACT_PARTIAL_BYTE_COUNT", + "KILL_AND_REAP_CHILD_AFTER_PARTIAL_COPY_OR_RESTORE_FAILURE" + ], + "authorizations": { + "target_source_authorized": false, + "target_build_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "device_write_authorized": false + }, + "decision": { + "direct_sdk_mdbg_copy_reuse_allowed": false, + "target_implementation_allowed": false, + "device_action_allowed": false, + "host_copy_restore_model_allowed": true, + "next_step": "OFFLINE_BOUNDED_COPY_AND_CREDENTIAL_RESTORE_MODEL" + } +} diff --git a/manifests/retroarch/phase-1.0am-bounded-copy-model.json b/manifests/retroarch/phase-1.0am-bounded-copy-model.json new file mode 100644 index 0000000..01964ac --- /dev/null +++ b/manifests/retroarch/phase-1.0am-bounded-copy-model.json @@ -0,0 +1,60 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0AM_OFFLINE_BOUNDED_COPY_RESTORE_MODEL", + "status": "HOST_EXACT_PROGRESS_AND_RESTORE_MODEL_COMPLETE_TARGET_CODE_BLOCKED", + "date": "2026-07-29", + "source_bindings": { + "phase10al_manifest_sha256": "4a143c0fb2aae98ee0c7452326f8a2fd72afc24d79867282b82ab77cb58c188a", + "model_size": 7140, + "model_sha256": "025976fe8736caea6ee33ae107f97411ff2f5981b16f7005098eb9314b1ddeb3", + "tests_size": 5081, + "tests_sha256": "6f9f97134efb83e50881fae813cb0119d4da2f17fda979ffe841209e8e5c8895" + }, + "bounds": { + "maximum_copy_size": 134217728, + "maximum_chunks": 4096, + "maximum_fake_events": 4112, + "maximum_ticks": 8192, + "address_width_bits": 64 + }, + "contract": { + "backup_both_fields_before_change": true, + "restore_caps_and_authid_on_every_changed_exit": true, + "independent_restore_failure_bits": true, + "exact_progress_reported": true, + "zero_progress_rejected": true, + "oversized_progress_rejected": true, + "complete_status_requires_exact_length": true, + "range_arithmetic_checked": true, + "partial_copy_kills_and_reaps_child": true, + "restore_failure_kills_child_and_terminates_service": true, + "terminal_cleanup_failure_is_hard_error": true, + "credential_interface_present": false, + "process_interface_present": false, + "memory_interface_present": false, + "clock_interface_present": false + }, + "remaining_blockers": [ + "TARGET_SIDE_BOUNDED_COPY_IMPLEMENTATION_ABSENT", + "SERVICE_TERMINATION_AND_RESTART_OWNERSHIP_UNPROVEN", + "MDBG_RESULT_STATUS_SEMANTICS_NOT_INDEPENDENTLY_DOCUMENTED", + "BIGAPP_AND_HARDENED_LOADER_COMPOSITION_NOT_BUILT", + "PS5_CROSS_TARGET_BUILD_NOT_AUTHORIZED", + "FIRMWARE_960_RUNTIME_BEHAVIOR_UNPROVEN" + ], + "authorizations": { + "target_source_authorized": false, + "target_build_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "device_write_authorized": false + }, + "decision": { + "host_copy_restore_model_complete": true, + "target_implementation_allowed": false, + "device_action_allowed": false, + "service_lifecycle_source_audit_allowed": true, + "next_step": "OFFLINE_SERVICE_TERMINATION_RESTART_OWNERSHIP_AUDIT" + } +} diff --git a/manifests/retroarch/phase-1.0an-service-lifecycle-audit.json b/manifests/retroarch/phase-1.0an-service-lifecycle-audit.json new file mode 100644 index 0000000..82f43ab --- /dev/null +++ b/manifests/retroarch/phase-1.0an-service-lifecycle-audit.json @@ -0,0 +1,59 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0AN_OFFLINE_SERVICE_LIFECYCLE_AND_COPY_AUDIT", + "status": "FAIL_STOP_PRESENT_RESTART_AND_BOUNDED_COPY_UNPROVEN_TARGET_CODE_BLOCKED", + "date": "2026-07-29", + "source_bindings": { + "phase10am_manifest_sha256": "c02d59b2d3ced8fb1e50700cd85b60f69f99be9c8ff9559ac4e18512e4992a96", + "hardened_elfldr_commit": "197623058f509eddde18868dafcb92fdcac66464", + "socksrv_size": 11719, + "socksrv_sha256": "65dd20743e4ffe45c9f41a4a374ddf6d2e8084b4c4da6c1b7292087b55628a49", + "pt_size": 10819, + "pt_sha256": "397bd178b11c729a20e3df97be9bbb1ae93408e3a12656d224567da7c9610df7", + "main_size": 2496, + "main_sha256": "d85d10696cc1fd381a60c471b414761a8a15560a15a138250b3ab2df94b58d29", + "elfldr_size": 22104, + "elfldr_sha256": "4cffb63c5d5f5f266fe62dfd940be3d202958373f6012ee5bbbb4489431b8c64" + }, + "service_lifecycle": { + "ptrace_auth_restore_failure_latched": true, + "further_ptrace_calls_rejected": true, + "request_handler_process_exits_125": true, + "exit_occurs_after_request_cleanup_attempt": true, + "service_restart_owner_present": false, + "restart_deadline_present": false, + "restart_identity_verification_present": false, + "startup_credential_failure_exit_cleanup_proven": false + }, + "copy_path": { + "pt_copyin_uses_single_ptrace_io": true, + "ptrace_io_descriptor_progress_checked": false, + "exact_total_progress_proven": false, + "hard_deadline_or_preemption_present": false, + "safe_replacement_for_mdbg_copy_proven": false + }, + "remaining_blockers": [ + "SERVICE_RESTART_OWNERSHIP_AND_IDENTITY_ABSENT", + "BLOCKING_PTRACE_IO_CANNOT_ENFORCE_HARD_DEADLINE", + "PTRACE_IO_EXACT_PROGRESS_SEMANTICS_UNPROVEN", + "STARTUP_CREDENTIAL_FAILURE_PROCESS_CLEANUP_UNPROVEN", + "TARGET_IMPLEMENTATION_AND_CROSS_BUILD_NOT_AUTHORIZED", + "FIRMWARE_960_RUNTIME_BEHAVIOR_UNPROVEN" + ], + "authorizations": { + "target_source_authorized": false, + "target_build_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "device_write_authorized": false + }, + "decision": { + "hardened_pt_copyin_reuse_allowed": false, + "automatic_service_restart_allowed": false, + "target_implementation_allowed": false, + "device_action_allowed": false, + "offline_preemption_architecture_allowed": true, + "next_step": "OFFLINE_COPY_PREEMPTION_AND_SERVICE_OWNER_ARCHITECTURE" + } +} diff --git a/manifests/retroarch/phase-1.0ao-worker-supervisor-model.json b/manifests/retroarch/phase-1.0ao-worker-supervisor-model.json new file mode 100644 index 0000000..72cc39a --- /dev/null +++ b/manifests/retroarch/phase-1.0ao-worker-supervisor-model.json @@ -0,0 +1,59 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0AO_OFFLINE_WORKER_SUPERVISOR_MODEL", + "status": "HOST_PREEMPTION_OWNERSHIP_MODEL_COMPLETE_TARGET_FEASIBILITY_UNPROVEN", + "date": "2026-07-29", + "source_bindings": { + "phase10an_manifest_sha256": "6754ce238b4e696c76e8fceee0fe6d3347b09568b3f883ec4a1ac52a9f7e83fc", + "model_size": 6824, + "model_sha256": "9093142ba53790cc6faee38d1c53d2b5221946a60f37c72cc3e24299e56a1b0c", + "tests_size": 4964, + "tests_sha256": "fe90f44d4f8da27f5fc0c8110551647d039257a2e3f3d9d10d56603c1275bfa9" + }, + "bounds": { + "maximum_copy_size": 134217728, + "maximum_fake_events": 16, + "maximum_ticks": 256, + "attempts": 1 + }, + "architecture": { + "persistent_supervisor_changes_credentials": false, + "one_worker_per_attempt": true, + "worker_identity_must_match": true, + "exact_copy_length_required": true, + "zero_restore_failure_bits_required": true, + "deadline_terminates_and_reaps_worker": true, + "ambiguous_or_partial_attempt_terminates_child": true, + "terminal_cleanup_failure_is_hard_error": true, + "automatic_restart": false, + "retry": false, + "real_process_interface_present": false, + "real_signal_interface_present": false, + "real_clock_present": false, + "real_ipc_present": false + }, + "remaining_blockers": [ + "PS5_WORKER_CREATION_AND_UNIQUE_IDENTITY_PRIMITIVES_UNPROVEN", + "BLOCKED_SYSCALL_WORKER_TERMINATION_PREEMPTION_UNPROVEN", + "WORKER_RESULT_CHANNEL_ABI_UNDEFINED", + "CHILD_STATE_AFTER_WORKER_TERMINATION_UNPROVEN", + "TARGET_SOURCE_AND_CROSS_BUILD_NOT_AUTHORIZED", + "FIRMWARE_960_RUNTIME_BEHAVIOR_UNPROVEN" + ], + "authorizations": { + "target_source_authorized": false, + "target_build_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "device_write_authorized": false + }, + "decision": { + "host_preemption_model_complete": true, + "target_architecture_feasible": false, + "target_implementation_allowed": false, + "device_action_allowed": false, + "worker_feasibility_source_audit_allowed": true, + "next_step": "OFFLINE_WORKER_CREATION_PREEMPTION_AND_RESULT_CHANNEL_FEASIBILITY_AUDIT" + } +} diff --git a/manifests/retroarch/phase-1.0ap-worker-feasibility-audit.json b/manifests/retroarch/phase-1.0ap-worker-feasibility-audit.json new file mode 100644 index 0000000..1a597f3 --- /dev/null +++ b/manifests/retroarch/phase-1.0ap-worker-feasibility-audit.json @@ -0,0 +1,72 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0AP_OFFLINE_CURRENT_UPSTREAM_WORKER_FEASIBILITY_AUDIT", + "status": "WORKER_CREATION_SOURCE_CANDIDATE_PREEMPTION_AND_RESULT_CHANNEL_BLOCKED", + "date": "2026-07-29", + "source_bindings": { + "phase10ao_manifest_sha256": "46dd4144a4392cbc08a1f1b38087c8858cdff0e3c3935fc16a51e85eff1a812c", + "sdk_commit": "a0d2bc60bdcc0a5ee9e790fa3b02fe5051a152d0", + "shsrv_commit": "6f320637d56d344a0e7797753099e33238bbf146", + "sdk_unistd_blob": "a30be948adfc3c554b1ec9e25362544c35a5fbd5", + "sdk_unistd_size": 19016, + "sdk_syscall_blob": "729550d5184c667d3da837d270ca48d47d57b407", + "sdk_syscall_size": 1551, + "shsrv_builtin_blob": "59a244fba80c1d51d1cb4204ea1a3b26d0be3ada", + "shsrv_builtin_size": 3980, + "shsrv_elfldr_blob": "2a398ce68b836cc13c100ddc716b44aaac4c7080", + "shsrv_elfldr_size": 17141, + "shsrv_shell_blob": "40d7107dbc1c3616f830a27a2b51c17cba1cec83", + "shsrv_shell_size": 12635, + "shsrv_pt_blob": "c7f64ec4f4f6576797b2aafb6de6234588eb7c51", + "shsrv_pt_size": 9376 + }, + "worker_creation": { + "rfork_thread_declared_by_public_sdk": true, + "rfork_thread_used_by_official_shsrv": true, + "returned_pid_available": true, + "pid_birth_identity_or_generation_token_present": false, + "bounded_worker_start_handshake_present": false, + "firmware_960_behavior_proven": false, + "classification": "STRONG_SOURCE_CANDIDATE_NOT_RUNTIME_PROOF" + }, + "preemption": { + "kill_sigkill_used_in_official_source": true, + "waitpid_used_in_official_source": true, + "waitpid_calls_are_bounded": false, + "blocked_mdbg_or_ptrace_io_kill_completion_proven": false, + "child_state_after_worker_kill_proven": false + }, + "result_channel": { + "generic_shell_pipe_usage_present": true, + "fixed_size_worker_result_record_present": false, + "exclusive_worker_identity_binding_present": false, + "exact_progress_and_restore_bits_present": false, + "bounded_receive_and_deadline_present": false + }, + "remaining_blockers": [ + "PID_REUSE_SAFE_WORKER_IDENTITY_ABSENT", + "BOUNDED_START_HANDSHAKE_ABSENT", + "BLOCKED_CALL_TERMINATION_AND_REAP_UNPROVEN", + "FIXED_RESULT_RECORD_AND_CHANNEL_ABSENT", + "CHILD_STATE_AFTER_WORKER_KILL_UNPROVEN", + "TARGET_SOURCE_AND_CROSS_BUILD_NOT_AUTHORIZED", + "FIRMWARE_960_RUNTIME_BEHAVIOR_UNPROVEN" + ], + "authorizations": { + "target_source_authorized": false, + "target_build_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "device_write_authorized": false + }, + "decision": { + "worker_creation_source_candidate": true, + "target_worker_architecture_feasible": false, + "copy_current_shsrv_code_allowed": false, + "target_implementation_allowed": false, + "device_action_allowed": false, + "offline_result_record_model_allowed": true, + "next_step": "OFFLINE_FIXED_WORKER_RESULT_RECORD_AND_PID_GENERATION_CONTRACT" + } +} diff --git a/manifests/retroarch/phase-1.0aq-worker-result-record.json b/manifests/retroarch/phase-1.0aq-worker-result-record.json new file mode 100644 index 0000000..084ac9a --- /dev/null +++ b/manifests/retroarch/phase-1.0aq-worker-result-record.json @@ -0,0 +1,75 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0AQ_OFFLINE_WORKER_RESULT_RECORD", + "status": "FIXED_RESULT_AND_GENERATION_IDENTITY_COMPLETE_TRANSPORT_BLOCKED", + "date": "2026-07-29", + "source_bindings": { + "phase10ap_manifest_sha256": "f2e7d5e28df5b294d055378ec239120d13fb8ba0f895a1f7ef981a739a19d423", + "model_size": 5362, + "model_sha256": "e05a754165e78eb42ac46f5783efdb73da015a547c87628384f8eb24f95ff28b", + "tests_size": 4369, + "tests_sha256": "774c1eaee561b4058d8069d263f07d30a2e575dd8a063edabb73cc0065193ded" + }, + "record": { + "magic_ascii": "CHW10AQ1", + "version": 1, + "size": 128, + "hashed_prefix_size": 96, + "digest": "SHA-256", + "byte_order": "LITTLE_ENDIAN", + "reserved_bytes_must_be_zero": true, + "flags_must_be_zero": true, + "single_complete_record_only": true + }, + "identity": { + "attempt_id_bytes": 16, + "worker_nonce_bytes": 16, + "worker_pid_required": true, + "child_pid_required": true, + "monotonic_generation_required": true, + "all_identity_fields_precommitted": true, + "pid_alone_is_identity": false + }, + "result": { + "requested_and_copied_bytes_present": true, + "restore_failure_bits_present": true, + "success_requires_exact_copy": true, + "success_requires_zero_restore_bits": true, + "failure_preserves_partial_progress": true, + "every_single_byte_mutation_tested": true, + "digest_is_authentication": false + }, + "capabilities": { + "pipe_present": false, + "socket_present": false, + "process_present": false, + "clock_present": false, + "target_present": false + }, + "remaining_blockers": [ + "WORKER_NONCE_AND_GENERATION_CREATION_NOT_SOURCE_BOUND", + "EXCLUSIVE_SINGLE_WRITER_CHANNEL_ABSENT", + "PARTIAL_READ_AND_EOF_DEADLINE_SEMANTICS_UNMODELED", + "PIPE_ATOMICITY_FOR_128_BYTES_UNPROVEN_ON_PS5", + "BLOCKED_WORKER_TERMINATION_AND_REAP_UNPROVEN", + "TARGET_SOURCE_AND_CROSS_BUILD_NOT_AUTHORIZED", + "FIRMWARE_960_RUNTIME_BEHAVIOR_UNPROVEN" + ], + "authorizations": { + "target_source_authorized": false, + "target_build_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "device_write_authorized": false + }, + "decision": { + "fixed_result_record_complete": true, + "generation_safe_identity_contract_complete": true, + "transport_implementation_allowed": false, + "target_implementation_allowed": false, + "device_action_allowed": false, + "offline_channel_model_allowed": true, + "next_step": "OFFLINE_EXCLUSIVE_RESULT_CHANNEL_PARTIAL_READ_AND_DEADLINE_MODEL" + } +} diff --git a/manifests/retroarch/phase-1.0ar-result-channel-model.json b/manifests/retroarch/phase-1.0ar-result-channel-model.json new file mode 100644 index 0000000..171fc56 --- /dev/null +++ b/manifests/retroarch/phase-1.0ar-result-channel-model.json @@ -0,0 +1,62 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0AR_OFFLINE_EXCLUSIVE_RESULT_CHANNEL_MODEL", + "status": "HOST_PARTIAL_READ_AND_DEADLINE_MODEL_COMPLETE_LIVE_CHANNEL_BLOCKED", + "date": "2026-07-29", + "source_bindings": { + "phase10aq_manifest_sha256": "04d5e26c3dcb54ddd029e477e329d1824fae3ab851752cac35c3b450975e7cfc", + "model_size": 3999, + "model_sha256": "416a17addde1ecf114f2f9924287c3358c9a10c84bed251c008d2aee6e4dd1c8", + "tests_size": 4202, + "tests_sha256": "a8dfc17daf3d05dade537aacd4cd60482958a30a527c7bfc60ae9cba5355e1b4" + }, + "bounds": { + "record_size": 128, + "maximum_chunks": 128, + "maximum_ticks": 256, + "writers": 1, + "records": 1 + }, + "channel": { + "writer_pid_generation_and_nonce_precommitted": true, + "exclusive_writer_required": true, + "arbitrary_partial_reads_supported": true, + "byte_at_a_time_supported": true, + "record_overflow_rejected": true, + "deadline_preempts_crossing_read": true, + "eof_is_success": false, + "silent_incomplete_is_success": false, + "record_completion_is_success_boundary": true, + "trailing_event_after_record_rejected": true, + "failure_requires_worker_child_containment": true, + "live_pipe_present": false, + "live_fd_present": false, + "real_clock_present": false, + "process_interface_present": false + }, + "remaining_blockers": [ + "PS5_PIPE_OR_SOCKETPAIR_CREATION_OWNERSHIP_UNPROVEN", + "EXCLUSIVE_FD_INHERITANCE_AND_CLOSE_ORDER_UNPROVEN", + "NONBLOCKING_READ_AND_HARD_DEADLINE_PRIMITIVES_UNBOUND", + "PIPE_BUF_ATOMICITY_NOT_REQUIRED_BY_MODEL_BUT_LIVE_FRAMING_UNPROVEN", + "BLOCKED_WORKER_TERMINATION_AND_REAP_UNPROVEN", + "TARGET_SOURCE_AND_CROSS_BUILD_NOT_AUTHORIZED", + "FIRMWARE_960_RUNTIME_BEHAVIOR_UNPROVEN" + ], + "authorizations": { + "target_source_authorized": false, + "target_build_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "device_write_authorized": false + }, + "decision": { + "host_channel_model_complete": true, + "live_channel_implementation_allowed": false, + "target_implementation_allowed": false, + "device_action_allowed": false, + "channel_primitive_source_audit_allowed": true, + "next_step": "OFFLINE_RESULT_CHANNEL_PRIMITIVE_AND_FD_OWNERSHIP_AUDIT" + } +} diff --git a/manifests/retroarch/phase-1.0as-channel-primitive-audit.json b/manifests/retroarch/phase-1.0as-channel-primitive-audit.json new file mode 100644 index 0000000..e188b29 --- /dev/null +++ b/manifests/retroarch/phase-1.0as-channel-primitive-audit.json @@ -0,0 +1,74 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0AS_OFFLINE_CHANNEL_PRIMITIVE_AND_FD_OWNERSHIP_AUDIT", + "status": "PIPE_POLL_CANDIDATES_PRESENT_FD_INHERITANCE_AND_HARD_DEADLINE_BLOCKED", + "date": "2026-07-29", + "source_bindings": { + "phase10ar_manifest_sha256": "86f101a48aadd1586594f3b4cbbd6d84a01b8cf51c664dd8512c8bab50e00f22", + "sdk_commit": "a0d2bc60bdcc0a5ee9e790fa3b02fe5051a152d0", + "shsrv_commit": "6f320637d56d344a0e7797753099e33238bbf146", + "sdk_sys_unistd_blob": "d4d4d340c3d90def316b2f061f81280767191c2b", + "sdk_sys_unistd_size": 7543, + "sdk_unistd_blob": "a30be948adfc3c554b1ec9e25362544c35a5fbd5", + "sdk_unistd_size": 19016, + "sdk_poll_blob": "de178cf5dd60f573d82c3ec4f954459b25d25271", + "sdk_poll_size": 4145, + "sdk_time_blob": "9c47c3f2b3d8d820d3b00a3a7ef8ba1c0bda029b", + "sdk_time_size": 14389, + "shsrv_shell_blob": "40d7107dbc1c3616f830a27a2b51c17cba1cec83", + "shsrv_shell_size": 12635, + "shsrv_builtin_blob": "59a244fba80c1d51d1cb4204ea1a3b26d0be3ada", + "shsrv_builtin_size": 3980 + }, + "channel_primitives": { + "pipe_declared": true, + "pipe_used_by_official_shsrv": true, + "poll_declared": true, + "poll_used_by_official_shsrv": true, + "short_poll_timeout_used": true, + "clock_monotonic_declared": true, + "pipe_poll_monotonic_deadline_composition_present": false, + "nonblocking_result_read_callsite_present": false + }, + "fd_ownership": { + "rffdg_documented_as_copy_fd_table": true, + "rfcfdg_documented_as_close_all_fds": true, + "official_shsrv_worker_uses_rfcfdg": true, + "result_fd_inherited_by_worker": false, + "exclusive_parent_child_end_close_order_present": false, + "close_on_exec_result_channel_present": false + }, + "deadline_and_cleanup": { + "absolute_monotonic_deadline_present": false, + "eintr_budget_recalculation_present": false, + "bounded_worker_reap_present": false, + "all_channel_fds_closed_on_every_failure": false + }, + "remaining_blockers": [ + "CURRENT_WORKER_FLAGS_CLOSE_PRECREATED_RESULT_FDS", + "RFFDG_WORKER_COMPOSITION_NOT_SOURCE_PROVEN", + "EXCLUSIVE_PIPE_END_CLOSE_ORDER_ABSENT", + "ABSOLUTE_MONOTONIC_DEADLINE_COMPOSITION_ABSENT", + "NONBLOCKING_RESULT_READ_CALLSITE_ABSENT", + "BOUNDED_WORKER_REAP_ABSENT", + "TARGET_SOURCE_AND_CROSS_BUILD_NOT_AUTHORIZED", + "FIRMWARE_960_RUNTIME_BEHAVIOR_UNPROVEN" + ], + "authorizations": { + "target_source_authorized": false, + "target_build_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "device_write_authorized": false + }, + "decision": { + "pipe_poll_source_candidates": true, + "live_channel_architecture_feasible": false, + "copy_current_shsrv_code_allowed": false, + "target_implementation_allowed": false, + "device_action_allowed": false, + "offline_fd_ownership_model_allowed": true, + "next_step": "OFFLINE_FD_INHERITANCE_CLOSE_ORDER_AND_ABSOLUTE_DEADLINE_MODEL" + } +} diff --git a/manifests/retroarch/phase-1.0at-fd-deadline-model.json b/manifests/retroarch/phase-1.0at-fd-deadline-model.json new file mode 100644 index 0000000..d38dc36 --- /dev/null +++ b/manifests/retroarch/phase-1.0at-fd-deadline-model.json @@ -0,0 +1,68 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0AT_OFFLINE_RFFDG_FD_DEADLINE_MODEL", + "status": "OFFLINE_OWNERSHIP_MODEL_COMPLETE_LIVE_COMPOSITION_BLOCKED", + "date": "2026-07-29", + "source_bindings": { + "phase10as_manifest_sha256": "750764e8ea177afb0fc7c21d7c4f1441bd90bb539917c2506ecb61f8aa39fc2a", + "model_sha256": "341f90e559c3611b9921c21a906baa01c82a49597d886c53fddd6fa3e0903e2d", + "failure_tests_sha256": "ca365b93b955d24b5ce3653cb20cef1fccddc91eca8d6382a56220accecdeafe" + }, + "model_boundary": { + "fake_facade_only": true, + "os_imports_present": false, + "process_api_present": false, + "clock_api_present": false, + "network_api_present": false, + "filesystem_output_present": false, + "target_address_present": false, + "target_artifact_present": false, + "max_ticks": 256, + "max_read_events": 256, + "record_size": 128 + }, + "ownership_contract": { + "pipe_created_before_worker": true, + "rffdg_copy_mode_modeled": true, + "parent_write_end_closed_before_read": true, + "child_read_end_closed_before_result": true, + "parent_read_is_nonblocking": true, + "child_write_closed_before_reap": true, + "all_acquired_ends_closed_on_failure": true, + "started_worker_terminated_and_reaped_on_failure": true + }, + "deadline_contract": { + "one_absolute_budget_across_setup_and_reads": true, + "eintr_consumes_budget": true, + "would_block_consumes_budget": true, + "deadline_preempts_late_event": true, + "eof_before_exact_record_fails": true, + "overflow_fails": true, + "trailing_read_event_fails": true, + "cleanup_is_modeled_as_terminal_and_must_succeed": true + }, + "remaining_blockers": [ + "MODEL_USES_FAKE_OPERATIONS_ONLY", + "RFFDG_WORKER_COMPOSITION_NOT_SOURCE_PROVEN", + "REAL_NONBLOCKING_FD_CONFIGURATION_UNIMPLEMENTED", + "HARD_WALL_CLOCK_PREEMPTION_UNPROVEN", + "BOUNDED_REAL_WORKER_REAP_UNPROVEN", + "FIRMWARE_960_RUNTIME_BEHAVIOR_UNPROVEN", + "TARGET_SOURCE_AND_CROSS_BUILD_NOT_AUTHORIZED" + ], + "authorizations": { + "target_source_authorized": false, + "target_build_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "device_write_authorized": false + }, + "decision": { + "offline_ownership_contract_complete": true, + "live_channel_architecture_feasible": false, + "target_implementation_allowed": false, + "device_action_allowed": false, + "next_step": "OFFLINE_LIVE_CHANNEL_SOURCE_FEASIBILITY_REASSESSMENT" + } +} diff --git a/manifests/retroarch/phase-1.0au-live-channel-feasibility.json b/manifests/retroarch/phase-1.0au-live-channel-feasibility.json new file mode 100644 index 0000000..a109975 --- /dev/null +++ b/manifests/retroarch/phase-1.0au-live-channel-feasibility.json @@ -0,0 +1,84 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0AU_OFFLINE_LIVE_RESULT_CHANNEL_FEASIBILITY_REASSESSMENT", + "status": "PRIMITIVE_SIGNATURES_COMPLETE_COMPOSITION_AND_RUNTIME_BLOCKED", + "date": "2026-07-29", + "source_bindings": { + "phase10at_manifest_sha256": "17584b4ee70ecca138b8fbddf03526b6070daa6ccb4aa366231457b5272df9da", + "sdk_commit": "a0d2bc60bdcc0a5ee9e790fa3b02fe5051a152d0", + "shsrv_commit": "6f320637d56d344a0e7797753099e33238bbf146", + "sdk_sys_unistd_blob": "d4d4d340c3d90def316b2f061f81280767191c2b", + "sdk_sys_unistd_size": 7543, + "sdk_unistd_blob": "a30be948adfc3c554b1ec9e25362544c35a5fbd5", + "sdk_unistd_size": 19016, + "sdk_sys_fcntl_blob": "ac16b8e4dbd4b097a697f39b779d46d68ae5992a", + "sdk_sys_fcntl_size": 11295, + "sdk_time_blob": "a19d550e5264b4a082b0ef0eab5cb98be210b627", + "sdk_time_size": 6699, + "sdk_wait_blob": "0d41bc6e40518cb88c8df4ac92bac581c12e1661", + "sdk_wait_size": 6272, + "sdk_signal_blob": "0b98653e66cd54641f42b2e51ddc5c936abcae3c", + "sdk_signal_size": 4545, + "shsrv_builtin_blob": "59a244fba80c1d51d1cb4204ea1a3b26d0be3ada", + "shsrv_builtin_size": 3980, + "shsrv_shell_blob": "40d7107dbc1c3616f830a27a2b51c17cba1cec83", + "shsrv_shell_size": 12635, + "shsrv_service_blob": "7f77aef966a995b6fd4f1fec3f7c681115884177", + "shsrv_service_size": 5051 + }, + "public_source_contracts": { + "pipe_declared": true, + "rffdg_copy_fd_table_declared": true, + "fcntl_getfl_setfl_and_nonblock_declared": true, + "poll_declared": true, + "clock_monotonic_declared": true, + "kill_declared": true, + "waitpid_and_wnohang_declared": true, + "source_level_design_inputs_complete": true + }, + "official_composition_audit": { + "worker_uses_rffdg": false, + "worker_uses_rfcfdg_close_all": true, + "exclusive_result_pipe_ownership_present": false, + "nonblocking_result_read_present": false, + "absolute_monotonic_deadline_present": false, + "bounded_kill_and_reap_present": false, + "service_has_unbounded_restart_loop": true, + "direct_shsrv_reuse_allowed": false + }, + "evidence_limits": { + "headers_prove_firmware_960_runtime": false, + "source_callsites_prove_hard_preemption": false, + "phase10at_fake_model_proves_live_cleanup": false, + "launch_context_fix_proven": false, + "videoout_permission_proven": false, + "visible_flip_proven": false + }, + "remaining_blockers": [ + "NO_OFFICIAL_SAFE_RESULT_CHANNEL_COMPOSITION", + "CURRENT_SHSRV_WORKER_CLOSES_PRECREATED_FDS", + "CURRENT_SHSRV_SERVICE_RESTARTS_AUTOMATICALLY", + "HARD_WALL_CLOCK_PREEMPTION_UNPROVEN", + "BOUNDED_REAL_WORKER_REAP_UNPROVEN", + "DEPLOYED_SHSRV_IDENTITY_UNPROVEN", + "FIRMWARE_960_RUNTIME_BEHAVIOR_UNPROVEN", + "TARGET_SOURCE_AND_CROSS_BUILD_NOT_AUTHORIZED" + ], + "authorizations": { + "target_source_authorized": false, + "target_build_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "device_write_authorized": false + }, + "decision": { + "offline_source_feasibility_reassessment_complete": true, + "live_result_channel_implementation_allowed": false, + "copy_shsrv_code_allowed": false, + "target_implementation_allowed": false, + "device_action_allowed": false, + "offline_canary_contract_design_allowed": true, + "next_step": "OFFLINE_LAUNCH_CONTEXT_CANARY_CONTRACT_WITH_DISTINCT_TERMINAL" + } +} diff --git a/manifests/retroarch/phase-1.0av-launch-context-canary-contract.json b/manifests/retroarch/phase-1.0av-launch-context-canary-contract.json new file mode 100644 index 0000000..dabd5d5 --- /dev/null +++ b/manifests/retroarch/phase-1.0av-launch-context-canary-contract.json @@ -0,0 +1,87 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0AV_TARGET_FREE_LAUNCH_CONTEXT_CANARY", + "status": "OFFLINE_CAUSAL_PAIR_CONTRACT_COMPLETE_NO_ARTIFACT_OR_AUTHORITY", + "date": "2026-07-29", + "source_bindings": { + "phase10au_manifest_sha256": "10687d050b1354efd7edbbd0e6739255278dc5edc85e3312ea85497f9052ceef", + "contract_sha256": "a03cd687e9e5d9d61cce768ebc662babe11bebe1972eaf18f6ead4dd616db5d4", + "tests_sha256": "095aec531c123a634e8267c7f97c64ea95b8ff603a2d7c6d4e996e2bbb51508d" + }, + "pair_contract": { + "firmware": "9.60", + "protocol_magic": "CHD10AV1", + "same_payload_sha256_required": true, + "raw_elfldr_arm_required": true, + "bigapp_candidate_arm_required": true, + "distinct_launcher_sha256_required": true, + "distinct_run_id_required": true, + "distinct_approval_sha256_required": true, + "one_shot_each": true, + "automatic_retry": false, + "reconnect": false, + "resume": false + }, + "result_contract": { + "exact_identity_match_required": true, + "submit_before_d04_required": true, + "d04_required_for_each_arm": true, + "distinct_terminal_after_d04_required": true, + "cleanup_complete_required": true, + "retry_count_must_be_zero": true, + "persistent_write_count_must_be_zero": true, + "visible_output_claim_rejected": true, + "incomplete_arm_is_not_comparable": true + }, + "interpretation_limits": { + "submit_return_difference_is_candidate_only": true, + "submit_zero_means_visible_flip": false, + "launch_context_root_cause_proven": false, + "videoout_abi_semantics_proven": false, + "firmware_960_behavior_proven": false + }, + "tracked_state": { + "payload_sha256": null, + "raw_launcher_sha256": null, + "bigapp_launcher_sha256": null, + "raw_run_id": null, + "bigapp_run_id": null, + "raw_approval_sha256": null, + "bigapp_approval_sha256": null, + "target_address": null, + "target_port": null, + "target_source_present": false, + "target_artifact_present": false, + "execution_eligible": false, + "transfer_eligible": false, + "installation_eligible": false + }, + "remaining_blockers": [ + "NO_CANARY_TARGET_SOURCE_OR_ARTIFACT", + "DISTINCT_POST_D04_TERMINAL_NOT_IMPLEMENTED", + "BIGAPP_LAUNCHER_IDENTITY_UNBOUND", + "BIGAPP_APP_TERMINATION_EFFECT_REQUIRES_SEPARATE_APPROVAL", + "RESULT_CHANNEL_LIVE_COMPOSITION_BLOCKED", + "VIDEOOUT_ABI_SEMANTICS_UNPROVEN", + "FIRMWARE_960_RUNTIME_BEHAVIOR_UNPROVEN" + ], + "authorizations": { + "target_source_authorized": false, + "target_build_authorized": false, + "graphics_operation_authorized": false, + "app_termination_authorized": false, + "result_reception_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "device_write_authorized": false + }, + "decision": { + "offline_pair_contract_complete": true, + "causal_hardware_comparison_ready": false, + "target_implementation_allowed": false, + "device_action_allowed": false, + "offline_artifact_source_delta_audit_allowed": true, + "next_step": "OFFLINE_CANARY_ARTIFACT_SOURCE_DELTA_AND_EFFECT_AUDIT" + } +} diff --git a/manifests/retroarch/phase-1.0aw-canary-source-delta-audit.json b/manifests/retroarch/phase-1.0aw-canary-source-delta-audit.json new file mode 100644 index 0000000..d5092e9 --- /dev/null +++ b/manifests/retroarch/phase-1.0aw-canary-source-delta-audit.json @@ -0,0 +1,112 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0AW_OFFLINE_CANARY_SOURCE_DELTA_AND_EFFECT_AUDIT", + "status": "SOURCE_DELTA_BOUNDED_RESULT_PATH_CANDIDATE_EFFECTS_BLOCK_ARTIFACT", + "date": "2026-07-29", + "source_bindings": { + "phase10av_manifest_sha256": "364e1ca4c052f69165ed2c38d3a20b6729fe8cb192199f3afc9ebe706a14b391", + "retroarch_commit": "12cf1d783c41eb303987e49a5a920805a59ef7a4", + "retroarch_c_blob": "f159522957c75f1e5fc5b4628b9e3a1ddd641f9e", + "retroarch_c_size": 303528, + "sdl2_gfx_blob": "3a34ea43bf71bf2dd371a7d0354f3e3d7fbbc8fb", + "sdl2_gfx_size": 19901, + "platform_smoke_c_blob": "b34cbad6b215ded2ed2ddcf4e1a38cc811044610", + "platform_smoke_c_size": 27314, + "platform_smoke_h_blob": "2954004d44d7e41fb3ff6ef25fb237397927ef3c", + "platform_smoke_h_size": 10395, + "diag_c_blob": "ef99ad1c50657450ddbd89bac13fa2eb62898d21", + "diag_c_size": 6120, + "diag_h_blob": "d2f7e18ce5adee613000f30b499bb15b874843a2", + "diag_h_size": 2110, + "stream_c_blob": "7807cb747569667db15215c424c01d8d1f8ee5ff", + "stream_c_size": 4282, + "stream_h_blob": "e63badcb615bf3cdf6e996f00ff75566bf94e2fd", + "stream_h_size": 1491, + "makefile_blob": "2d5588f9fb30009f0e82137b42718520b4e48b70", + "makefile_size": 15967, + "sdl_hardening_patch_blob": "5626feeae20a037d52d18e484428ec8717ba70d5", + "sdl_hardening_patch_size": 21663, + "shsrv_v07_commit": "74287f5db6b20320efd7892d7b29cf438fe7cb98", + "v07_hbldr_blob": "0f5414f78a925e05fe0051fce6c98d4588b35988", + "v07_hbldr_size": 6788, + "v07_elfldr_blob": "b4019a527e6a40b9a4d9595628a8769dcf472e43", + "v07_elfldr_size": 17164, + "v07_shell_blob": "9454a571a481e42247bfa817163db8f399d7ac59", + "v07_shell_size": 8020, + "v07_service_blob": "2277e398dc447f0860bc7fb7d38a364dd150a1d7", + "v07_service_size": 4498 + }, + "historical_payload": { + "phase10m_sha256": "c99a0856309a357ad2667d89b4924e4063ad214cae09c8a419457b0732f583cd", + "bytes_present": false, + "protocol_magic": "CHD10J01", + "distinct_post_d04_terminal_present": false, + "reusable_as_av_canary": false + }, + "required_payload_source_delta": { + "new_profile_identity_required": true, + "new_protocol_magic": "CHD10AV1", + "new_stage": "D14_POST_D04_CLEANUP_TERMINAL", + "d12_must_not_be_lifecycle_terminal": true, + "submit_before_d04_order_preserved": true, + "d14_emitted_only_after_rarch_main_returns": true, + "d14_emitted_only_after_s15_complete": true, + "initialized_mask_must_be_zero": true, + "cleanup_order_errors_must_be_zero": true, + "new_cleanup_failure_counter_required": true, + "cleanup_failure_counter_must_be_zero": true, + "one_nonblocking_inherited_stdout_send_for_d14": true, + "launcher_identity_compile_define_forbidden": true, + "same_elf_bytes_for_both_arms_required": true + }, + "bigapp_result_path": { + "v07_shell_framing": "RAW", + "hbldr_passes_stdout_to_elfldr_exec": true, + "elfldr_duplicates_stdout_into_bigapp": true, + "binary_frame_source_candidate": true, + "bounded_receive_and_completion_present": false, + "deployed_v07_identity_proven": false, + "live_result_path_proven": false + }, + "launcher_effects": { + "reads_device_resident_elf": true, + "kills_running_bigapp": true, + "launches_ppsa01659": true, + "unbounded_child_discovery_wait": true, + "kernel_and_ptrace_mutation": true, + "root_and_jail_mutation": true, + "bounded_cleanup_on_every_failure": false, + "direct_v07_reuse_allowed": false + }, + "remaining_blockers": [ + "NEW_CANARY_SOURCE_NOT_IMPLEMENTED", + "NEW_CANARY_ARTIFACT_NOT_BUILT", + "BIGAPP_LAUNCHER_IDENTITY_UNBOUND", + "V07_CHILD_DISCOVERY_WAIT_UNBOUNDED", + "BIGAPP_TERMINATION_NOT_AUTHORIZED", + "DEVICE_RESIDENT_ELF_STAGING_NOT_AUTHORIZED", + "LIVE_BINARY_RESULT_PATH_UNPROVEN", + "FIRMWARE_960_RUNTIME_BEHAVIOR_UNPROVEN" + ], + "authorizations": { + "target_source_authorized": false, + "target_build_authorized": false, + "graphics_operation_authorized": false, + "app_termination_authorized": false, + "result_reception_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "device_write_authorized": false + }, + "decision": { + "source_delta_audit_complete": true, + "same_payload_pair_source_feasible": true, + "bigapp_result_path_source_candidate": true, + "causal_hardware_comparison_ready": false, + "target_artifact_build_allowed": false, + "device_action_allowed": false, + "host_tested_source_design_allowed": true, + "next_step": "OFFLINE_HOST_TESTED_CANARY_SOURCE_DESIGN_NO_TARGET_BUILD" + } +} diff --git a/manifests/retroarch/phase-1.0ax-canary-protocol-model.json b/manifests/retroarch/phase-1.0ax-canary-protocol-model.json new file mode 100644 index 0000000..4892cb8 --- /dev/null +++ b/manifests/retroarch/phase-1.0ax-canary-protocol-model.json @@ -0,0 +1,89 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0AX_HOST_ONLY_CANARY_PROTOCOL_AND_CLEANUP_MODEL", + "status": "HOST_REFERENCE_MODEL_COMPLETE_TARGET_SOURCE_AND_ARTIFACT_BLOCKED", + "date": "2026-07-29", + "source_bindings": { + "phase10aw_manifest_sha256": "425bf66d12e1b9c0e9a26790ea58372ad5736ab6886028c57c4a1be241927a68", + "model_sha256": "faa43bc3b767208bfa2e4cd7bf804384bf497bb5651f6d0aa945b1b300649a02", + "tests_sha256": "9d7adb3f2a35b7af1f671aa967f4f247ed82f562ce2c9139d5c36e174ecb5306" + }, + "frame_contract": { + "magic": "CHD10AV1", + "version": 1, + "frame_size": 64, + "byte_order": "BIG_ENDIAN", + "crc32_over_first_bytes": 60, + "reserved_bytes_zero": true, + "strict_sequence": true, + "d07_stage_value": 7, + "d04_stage_value": 4, + "d12_stage_value": 12, + "d14_stage_value": 30, + "only_d14_terminal": true, + "d12_terminal_forbidden": true, + "d07_and_d04_exact_raw0": true, + "d12_maximum_count": 1 + }, + "cleanup_terminal_contract": { + "rarch_main_returned_required": true, + "d04_emitted_required": true, + "s15_complete_required": true, + "initialized_mask_zero_required": true, + "cleanup_order_errors_zero_required": true, + "cleanup_failure_count_zero_required": true, + "d14_fields": { + "raw0": "initialized_mask", + "raw1": "cleaned_mask", + "result": "rarch_main_result", + "aux0": "cleanup_order_errors", + "aux1": "cleanup_failure_count" + } + }, + "trace_contract": { + "required_order": ["D07", "D04", "D14"], + "terminal_must_be_last": true, + "post_terminal_data_allowed": false, + "duplicate_required_stage_allowed": false, + "submit_zero_means_visible_flip": false, + "host_trace_means_firmware_behavior": false + }, + "model_boundary": { + "python_bytes_only": true, + "target_source_present": false, + "target_profile_present": false, + "target_artifact_present": false, + "socket_present": false, + "process_present": false, + "filesystem_output_present": false, + "address_or_port_present": false + }, + "remaining_blockers": [ + "RETROARCH_TARGET_SOURCE_NOT_IMPLEMENTED", + "CLEANUP_FAILURE_COUNTER_NOT_IN_TARGET_STATUS", + "CHD10AV1_AND_D14_NOT_IN_TARGET_STREAM", + "NO_CANARY_TARGET_PROFILE_OR_ARTIFACT", + "BIGAPP_LAUNCHER_IDENTITY_UNBOUND", + "LIVE_RESULT_PATH_UNPROVEN", + "FIRMWARE_960_RUNTIME_BEHAVIOR_UNPROVEN" + ], + "authorizations": { + "target_source_authorized": false, + "target_build_authorized": false, + "graphics_operation_authorized": false, + "app_termination_authorized": false, + "result_reception_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "device_write_authorized": false + }, + "decision": { + "host_reference_model_complete": true, + "target_source_matches_model": false, + "target_artifact_build_allowed": false, + "device_action_allowed": false, + "offline_target_source_reassessment_allowed": true, + "next_step": "OFFLINE_RETROARCH_TARGET_SOURCE_REASSESSMENT_NO_BUILD" + } +} diff --git a/manifests/retroarch/phase-1.0ay-target-source-base.json b/manifests/retroarch/phase-1.0ay-target-source-base.json new file mode 100644 index 0000000..82ee365 --- /dev/null +++ b/manifests/retroarch/phase-1.0ay-target-source-base.json @@ -0,0 +1,62 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0AY_OFFLINE_RETROARCH_TARGET_SOURCE_BASE_REASSESSMENT", + "status": "EXACT_PHASE10N_BASE_SELECTED_HOST_SOURCE_PATCH_ONLY", + "date": "2026-07-29", + "source_bindings": { + "phase10ax_manifest_sha256": "ed9208ea371c36a6025334dc58b5261b4e754cf97bc6b6962bce26b4fe445ddc", + "selected_retroarch_commit": "606909706f91d7213751c245081333f56c2cce89", + "selected_remote_ref": "origin/codex/ps5-phase10n-inactive-one-shot-runner", + "phase10m_source_commit": "12cf1d783c41eb303987e49a5a920805a59ef7a4", + "retroarch_c_blob": "f159522957c75f1e5fc5b4628b9e3a1ddd641f9e", + "sdl2_gfx_blob": "3a34ea43bf71bf2dd371a7d0354f3e3d7fbbc8fb", + "platform_smoke_c_blob": "b34cbad6b215ded2ed2ddcf4e1a38cc811044610", + "platform_smoke_h_blob": "2954004d44d7e41fb3ff6ef25fb237397927ef3c", + "diag_c_blob": "ef99ad1c50657450ddbd89bac13fa2eb62898d21", + "diag_h_blob": "d2f7e18ce5adee613000f30b499bb15b874843a2", + "stream_c_blob": "7807cb747569667db15215c424c01d8d1f8ee5ff", + "stream_h_blob": "e63badcb615bf3cdf6e996f00ff75566bf94e2fd", + "makefile_blob": "32fd801349fc2096e3b68f6fea520c1ea4e65996" + }, + "base_selection": { + "selected_commit_descends_from_phase10m_source": true, + "selected_commit_contains_phase10m_artifact_audit": true, + "selected_commit_contains_inactive_phase10n_runner": true, + "target_source_blobs_unchanged_from_phase10m": true, + "makefile_changed_only_for_inactive_runner_validation": true, + "current_primary_checkout_requires_switch": false, + "separate_worktree_required": true, + "new_branch_prefix": "codex/", + "existing_runner_must_remain_inactive": true + }, + "permitted_patch_scope": { + "host_tests_only": true, + "cleanup_failure_counter": true, + "chd10av1_frame_selection": true, + "d14_stage_and_cleanup_predicate": true, + "launcher_compile_define": false, + "target_profile": false, + "cross_build": false, + "artifact": false, + "live_runner_activation": false + }, + "authorizations": { + "target_source_authorized": false, + "target_build_authorized": false, + "graphics_operation_authorized": false, + "app_termination_authorized": false, + "result_reception_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "device_write_authorized": false + }, + "decision": { + "source_base_exact": true, + "host_tested_source_patch_allowed": true, + "target_profile_allowed": false, + "target_artifact_build_allowed": false, + "device_action_allowed": false, + "next_step": "SEPARATE_WORKTREE_HOST_TESTED_AV_SOURCE_PATCH" + } +} diff --git a/manifests/retroarch/phase-1.0az-host-av-source.json b/manifests/retroarch/phase-1.0az-host-av-source.json new file mode 100644 index 0000000..9587c93 --- /dev/null +++ b/manifests/retroarch/phase-1.0az-host-av-source.json @@ -0,0 +1,68 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0AZ_HOST_TESTED_AV_SOURCE_STRUCTURE", + "status": "REMOTE_BOUND_HOST_TESTED_SOURCE_TARGET_REASSESSMENT_ONLY", + "date": "2026-07-29", + "source_bindings": { + "phase10ay_manifest_sha256": "c9d161f1cd7053f32480d9c9f8b31902107d5af78518cbfaeb5d08676fc63a2e", + "retroarch_base_commit": "606909706f91d7213751c245081333f56c2cce89", + "retroarch_local_commit": "a3887edd90780426b3762d42b8e3b96f8ca6813d", + "retroarch_branch": "codex/ps5-phase10az-host-av-source", + "remote_push_verified": true, + "verified_remote_ref": "origin/codex/ps5-phase10az-host-av-source", + "makefile_blob": "c5c090e9c4f2b972214ee5c76b4560dc79e276e0", + "platform_smoke_c_blob": "ba435ebdef0bb28468e2ed0b99f9af309f89d866", + "platform_smoke_h_blob": "08f8f807e2bc73a81f5ffbb4f6c44ae1db9c3945", + "diag_c_blob": "dc483ab1038fc322b668059a8ad8a5e9bef39dae", + "diag_h_blob": "71dd8adde81109bf0b5d5e556f9d1d5aec100de1", + "stream_c_blob": "b0be265de1a32ee53ad10563119ef36e5c4f7413", + "host_test_blob": "92a61f3dd0a0a129939742707841a93d79264172", + "source_doc_blob": "547ce03367b1ce86af66283041aaa6e3a1c0ed5f" + }, + "protocol": { + "magic": "CHD10AV1", + "d14_numeric_stage": 30, + "d12_terminal": false, + "d14_only_terminal": true, + "d14_one_shot": true, + "requires_successfully_written_d04": true, + "requires_rarch_main_return": true, + "requires_s15": true, + "requires_initialized_mask_zero": true, + "requires_cleanup_order_errors_zero": true, + "requires_cleanup_failures_zero": true + }, + "verification": { + "asan_ubsan_av_host_test": "PASS", + "all_c_host_tests": "PASS", + "windows_legacy_validators": "PASS", + "wsl_combined_suite": "ENVIRONMENT_BLOCKED_WINDOWS_WORKTREE_GITDIR", + "changed_file_secret_scan": "PASS", + "diff_check": "PASS" + }, + "scope": { + "retroarch_c_modified": false, + "target_profile_added": false, + "cross_build_performed": false, + "target_artifact_created": false, + "runner_selector_added": false, + "network_or_device_code_added": false + }, + "authorizations": { + "target_build_authorized": false, + "graphics_operation_authorized": false, + "result_reception_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "device_write_authorized": false + }, + "decision": { + "host_source_structure_complete": true, + "remote_source_binding_complete": true, + "target_profile_reassessment_allowed": true, + "target_artifact_build_allowed": false, + "device_action_allowed": false, + "next_step": "OFFLINE_TARGET_PROFILE_AND_ARTIFACT_BUILD_REASSESSMENT" + } +} diff --git a/manifests/retroarch/phase-1.0b-artifact.json b/manifests/retroarch/phase-1.0b-artifact.json new file mode 100644 index 0000000..b8c0d7b --- /dev/null +++ b/manifests/retroarch/phase-1.0b-artifact.json @@ -0,0 +1,443 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0B_FIRST_DEVICE_SMOKE_CANDIDATE_HARDENING", + "status": "RETROARCH_PS5_DEVICE_SMOKE_CANDIDATE_BUILT_WITH_DECLARED_RISKS", + "artifact": { + "name": "retroarch_ps5_software_smoke.elf", + "profile": "ps5-software-rgui-smoke-bounded", + "local_relative_path": "retroarch_ps5_software_smoke.elf", + "linker_map_relative_path": "retroarch_ps5_software_smoke.map", + "size": 1826216, + "sha256": "16c04afe26c490b5071388dc9a32524d09d50d181b1baffbbc54bd96f0c2c688", + "linker_map_size": 620047, + "linker_map_sha256": "21dfdaa44261f8d546fa9ea71102162dc880a8ca8037526019a04fece5b21313", + "clean_build_sha256": [ + "16c04afe26c490b5071388dc9a32524d09d50d181b1baffbbc54bd96f0c2c688", + "16c04afe26c490b5071388dc9a32524d09d50d181b1baffbbc54bd96f0c2c688" + ], + "source_commit": "392d14871f3b121a346d1e2e727e528552bdd700", + "file_type": "ELF64 little-endian x86-64 PIE, dynamically linked, not stripped", + "entrypoint": "0xfdd70", + "program_headers": [ + { + "type": "LOAD", + "flags": "RE", + "offset": 16384, + "virtual_address": 0, + "file_size": 1066462, + "memory_size": 1066462, + "alignment": 16384 + }, + { + "type": "LOAD", + "flags": "R", + "offset": 1097728, + "virtual_address": 1081344, + "file_size": 392408, + "memory_size": 392408, + "alignment": 16384 + }, + { + "type": "LOAD", + "flags": "RW", + "offset": 1490944, + "virtual_address": 1474560, + "file_size": 206120, + "memory_size": 903360, + "alignment": 16384 + }, + { + "type": "DYNAMIC", + "flags": "RW", + "offset": 1540096, + "virtual_address": 1523712, + "file_size": 400, + "memory_size": 400, + "alignment": 16384 + } + ], + "rwx_segment_count": 0, + "wx_status": "NO_WRITABLE_EXECUTABLE_LOAD_SEGMENT", + "relro": "NOT_CLAIMED", + "interpreter": null, + "build_id": null, + "gnu_stack_program_header": "ABSENT", + "elf_tls_segment": false, + "elf_tls_sections": [], + "init_array_size": 0, + "fini_array_size": 0, + "relocation_audit": { + "total": 1053, + "relative": 913, + "by_type": { + "8": 913, + "6": 140 + }, + "outside_rw_load": 0, + "relative_outside_rw_load": 0 + }, + "relative_relocations_outside_rw_load": 0, + "dt_needed": [ + "libSceVideoOut.sprx", + "libScePad.sprx", + "libSceUserService.sprx", + "libSceSystemService.sprx", + "libSceAudioOut.sprx", + "libkernel_web.sprx", + "libSceLibcInternal.sprx" + ], + "imports": [ + "_Exit", + "__error", + "__isthreaded", + "__stderrp", + "__stdoutp", + "abort", + "atan2", + "atof", + "atoi", + "calloc", + "clock_gettime", + "close", + "closedir", + "cos", + "cosf", + "exit", + "fclose", + "ferror", + "fflush", + "fileno", + "fopen", + "fprintf", + "fputc", + "fputs", + "fread", + "free", + "fseek", + "fseeko", + "fstat", + "ftello", + "fwrite", + "getenv", + "gettimeofday", + "isgraph", + "localtime", + "longjmp", + "lseek", + "malloc", + "memcmp", + "memcpy", + "memmove", + "memset", + "nanosleep", + "open", + "opendir", + "powf", + "pthread_attr_init", + "pthread_attr_setdetachstate", + "pthread_attr_setstacksize", + "pthread_create", + "pthread_getschedparam", + "pthread_getspecific", + "pthread_join", + "pthread_key_create", + "pthread_key_delete", + "pthread_mutex_destroy", + "pthread_mutex_init", + "pthread_mutex_lock", + "pthread_mutex_unlock", + "pthread_mutexattr_init", + "pthread_mutexattr_settype", + "pthread_self", + "pthread_set_name_np", + "pthread_setcanceltype", + "pthread_setschedparam", + "pthread_setspecific", + "pthread_sigmask", + "puts", + "qsort", + "read", + "readdir", + "realloc", + "sceAudioOutClose", + "sceAudioOutInit", + "sceAudioOutOpen", + "sceAudioOutOutput", + "sceKernelAllocateMainDirectMemory", + "sceKernelCreateEqueue", + "sceKernelDeleteEqueue", + "sceKernelMapDirectMemory", + "sceKernelReleaseDirectMemory", + "sceKernelWaitEqueue", + "scePadClose", + "scePadInit", + "scePadOpen", + "scePadReadState", + "sceSystemServiceHideSplashScreen", + "sceUserServiceGetLoginUserIdList", + "sceUserServiceGetUserName", + "sceUserServiceInitialize", + "sceVideoOutAddFlipEvent", + "sceVideoOutClose", + "sceVideoOutDeleteFlipEvent", + "sceVideoOutOpen", + "sceVideoOutRegisterBuffers2", + "sceVideoOutSetBufferAttribute2", + "sceVideoOutSetFlipRate", + "sceVideoOutSubmitFlip", + "sched_get_priority_max", + "sched_get_priority_min", + "sem_destroy", + "sem_init", + "sem_post", + "sem_timedwait", + "sem_trywait", + "sem_wait", + "setjmp", + "setvbuf", + "sigaction", + "sigaddset", + "sigemptyset", + "signal", + "sin", + "sinf", + "snprintf", + "sprintf", + "sscanf", + "stat", + "strchr", + "strcmp", + "strdup", + "strerror", + "strftime", + "strlcat", + "strlcpy", + "strlen", + "strncasecmp", + "strncmp", + "strrchr", + "strstr", + "strtod", + "strtok_r", + "strtol", + "strtoul", + "strtoull", + "time", + "tolower", + "vfprintf", + "vsnprintf", + "vsscanf" + ], + "undefined_symbols": [ + "_Exit", + "__error", + "__isthreaded", + "__stderrp", + "__stdoutp", + "abort", + "atan2", + "atof", + "atoi", + "calloc", + "clock_gettime", + "close", + "closedir", + "cos", + "cosf", + "exit", + "fclose", + "ferror", + "fflush", + "fileno", + "fopen", + "fprintf", + "fputc", + "fputs", + "fread", + "free", + "fseek", + "fseeko", + "fstat", + "ftello", + "fwrite", + "getenv", + "gettimeofday", + "isgraph", + "localtime", + "longjmp", + "lseek", + "malloc", + "memcmp", + "memcpy", + "memmove", + "memset", + "nanosleep", + "open", + "opendir", + "powf", + "pthread_attr_init", + "pthread_attr_setdetachstate", + "pthread_attr_setstacksize", + "pthread_create", + "pthread_getschedparam", + "pthread_getspecific", + "pthread_join", + "pthread_key_create", + "pthread_key_delete", + "pthread_mutex_destroy", + "pthread_mutex_init", + "pthread_mutex_lock", + "pthread_mutex_unlock", + "pthread_mutexattr_init", + "pthread_mutexattr_settype", + "pthread_self", + "pthread_set_name_np", + "pthread_setcanceltype", + "pthread_setschedparam", + "pthread_setspecific", + "pthread_sigmask", + "puts", + "qsort", + "read", + "readdir", + "realloc", + "sceAudioOutClose", + "sceAudioOutInit", + "sceAudioOutOpen", + "sceAudioOutOutput", + "sceKernelAllocateMainDirectMemory", + "sceKernelCreateEqueue", + "sceKernelDeleteEqueue", + "sceKernelMapDirectMemory", + "sceKernelReleaseDirectMemory", + "sceKernelWaitEqueue", + "scePadClose", + "scePadInit", + "scePadOpen", + "scePadReadState", + "sceSystemServiceHideSplashScreen", + "sceUserServiceGetLoginUserIdList", + "sceUserServiceGetUserName", + "sceUserServiceInitialize", + "sceVideoOutAddFlipEvent", + "sceVideoOutClose", + "sceVideoOutDeleteFlipEvent", + "sceVideoOutOpen", + "sceVideoOutRegisterBuffers2", + "sceVideoOutSetBufferAttribute2", + "sceVideoOutSetFlipRate", + "sceVideoOutSubmitFlip", + "sched_get_priority_max", + "sched_get_priority_min", + "sem_destroy", + "sem_init", + "sem_post", + "sem_timedwait", + "sem_trywait", + "sem_wait", + "setjmp", + "setvbuf", + "sigaction", + "sigaddset", + "sigemptyset", + "signal", + "sin", + "sinf", + "snprintf", + "sprintf", + "sscanf", + "stat", + "strchr", + "strcmp", + "strdup", + "strerror", + "strftime", + "strlcat", + "strlcpy", + "strlen", + "strncasecmp", + "strncmp", + "strrchr", + "strstr", + "strtod", + "strtok_r", + "strtol", + "strtoul", + "strtoull", + "time", + "tolower", + "vfprintf", + "vsnprintf", + "vsscanf" + ], + "defined_symbols": [ + "_start", + "main", + "rarch_main", + "retroarch_main_init", + "runloop_iterate", + "retro_init", + "retro_deinit", + "retro_run", + "frontend_ctx_ps5", + "chimera_ps5_smoke_tick", + "chimera_ps5_smoke_block_write", + "__wrap_open", + "__wrap_fopen", + "__wrap_fwrite" + ], + "key_symbol_addresses": { + "main": "0x3cc0", + "rarch_main": "0x3a10", + "retroarch_main_init": "0x3db0", + "runloop_iterate": "0xaf50", + "retro_init": "0x44fd0", + "retro_deinit": "0x450b0", + "retro_run": "0x451c0", + "chimera_ps5_smoke_tick": "0x46230", + "chimera_ps5_smoke_block_write": "0x46330", + "__wrap_open": "0x46f40", + "__wrap_fopen": "0x46fe0", + "__wrap_fwrite": "0x470a0", + "_start": "0xfdd70", + "frontend_ctx_ps5": "0x178e80" + }, + "selected_exit_method": "PROCESS__EXIT_AFTER_TEARDOWN", + "smoke_owned_exit_callsite_count": 1, + "sdl_exit_process_callsite_count": 0, + "sdk_init_failure_exit_path": true, + "runtime_resolved_sdk_module_loader": true, + "runtime_resolved_sdk_module": "/system/common/lib/libSceSysmodule.sprx", + "static_core": "chimera_smokecore", + "video_backend": "SDL2 PS5 software framebuffer / VideoOut", + "input_backend": "SDL2 PS5 Pad", + "audio_backend": "SDL2 PS5 AudioOut 48 kHz stereo", + "string_audit": { + "personal_host_paths_found": false, + "credentials_found": false, + "device_or_private_ip_addresses_found": false, + "disabled_feature_localization_strings_remain": true, + "generic_ipv4_like_strings": [ + "udp://127.0.0.1:", + ".0.1.2.3.", + ".4.5.6.7." + ], + "network_imports_found": false, + "retroarch_network_callgraph_found": false, + "sdk_runtime_module_name_strings_found": true + }, + "persistent_writes_allowed": false, + "content_required": false, + "config_required": false, + "networking": false, + "autoload": false, + "automatic_retry": false, + "installation_eligible": false, + "device_write_eligible": false, + "transfer_eligible": false, + "execution_eligible": false, + "execution_authorized": false, + "hardware_tested": false, + "device_action_performed": false + } +} diff --git a/manifests/retroarch/phase-1.0b-build.json b/manifests/retroarch/phase-1.0b-build.json new file mode 100644 index 0000000..2b54602 --- /dev/null +++ b/manifests/retroarch/phase-1.0b-build.json @@ -0,0 +1,178 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0B_FIRST_DEVICE_SMOKE_CANDIDATE_HARDENING", + "status": "RETROARCH_PS5_DEVICE_SMOKE_CANDIDATE_BUILT_WITH_DECLARED_RISKS", + "date": "2026-07-19", + "baseline_commit": "8e1fe8b0c44dace6e824696ff14b6193c1c28c6c", + "branch": "codex/chimera-gfx-phase10b-device-smoke-hardening", + "offline_source_modification_authorized": true, + "offline_target_build_authorized": true, + "offline_artifact_analysis_authorized": true, + "host_test_authorized": true, + "private_gitea_push_authorized": true, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "installation_authorized": false, + "lifecycle_authorized": false, + "autoload_authorized": false, + "device_write_authorized": false, + "automatic_retry": false, + "ps5_connected": false, + "device_request_performed": false, + "files_transferred": false, + "device_write_performed": false, + "target_execution_performed": false, + "transfer_package_created": false, + "execution_package_created": false, + "installation_package_created": false, + "target_artifact_created": true, + "sources": { + "retroarch_tag": "v1.22.2", + "retroarch_release_commit": "69a4f0ea1e8aaf442ae4858f2e7f2b31a1776576", + "retroarch_release_tree": "33babf9eb7699b5d571a3063ea21c3e488c159fe", + "retroarch_fork_base_commit": "ca1b45680577befc743e1c92fa40687e1b1745e7", + "ps5_payload_sdk_commit": "d2e2e585740362976a39fdd5ccf390f199a7bc37", + "pacbrew_commit": "c2abcfcb60f569128abd0e8e70ad03a67bee5ea7", + "sdl_commit": "0baf4ac49382b537ba449901b5b6d0d189bb1fbb", + "sdl_version": "2.30.12" + }, + "chimera_retroarch": { + "repository_kind": "SEPARATE_PRIVATE_FORK", + "branch": "codex/ps5-device-smoke-hardening", + "source_commit": "392d14871f3b121a346d1e2e727e528552bdd700", + "source_tree": "cc604d0ad5db28e41f9acb1638c37fcd6b1e907f", + "working_tree_clean": true, + "upstream_push_disabled": true + }, + "build_environment": { + "host": "WSL2", + "kernel": "Linux 6.6.114.1-microsoft-standard-WSL2 x86_64", + "source_date_epoch": 1763597828, + "timezone": "UTC", + "locale": "C", + "cross_compiler": "Ubuntu clang 18.1.8", + "cross_target": "x86_64-sie-ps5", + "make": "GNU Make 4.4.1", + "cmake": "4.2.3", + "ninja": "1.13.2", + "host_compiler": "GCC 15.2.0", + "python": "3.14.4" + }, + "profile": { + "logical_name": "ps5-software-rgui-smoke-bounded", + "make_selector": "PS5_PROFILE=software-smoke", + "output": "retroarch_ps5_software_smoke.elf", + "enabled_features": [ + "real_retroarch_frontend", + "real_retroarch_runloop", + "static_chimera_smokecore", + "sdl2_software_video", + "sdl2_ps5_videoout", + "sdl2_ps5_pad", + "sdl2_ps5_audioout", + "bounded_status_overlay", + "write_firewall", + "reverse_order_teardown", + "process_exit_after_teardown" + ], + "disabled_features": [ + "achievements", + "autoconfig_save", + "autoload", + "compressed_content", + "config_save", + "content_browser_data", + "databases", + "dynamic_cores", + "gnm", + "haptics", + "history", + "installation", + "keyboard_ime", + "lightbar", + "log_file", + "networking", + "online_updater", + "payload_launch", + "playlists", + "recording", + "remap_save", + "retroarch_dynamic_core_loading", + "rumble", + "savestates", + "screenshots", + "shaders", + "sram", + "temporary_files" + ], + "compile_defines": [ + "CHIMERA_PS5_SMOKE_MODE=1", + "CHIMERA_PS5_SMOKE_TARGET=1", + "CHIMERA_SMOKE_MAX_RUNTIME_MS=60000", + "CHIMERA_SMOKE_MAX_FRAMES=3600", + "CHIMERA_PS5_EXIT_PROCESS_EXIT=1", + "CHIMERA_PS5_STATIC_CORE", + "CHIMERA_PS5_NO_FILESYSTEM_WRITES", + "HAVE_CONFIGFILE=0", + "HAVE_MENU=0", + "HAVE_RGUI=0", + "HAVE_NETWORKING=0", + "HAVE_DYNAMIC=0", + "SDL_DYNAMIC_API=0", + "SDL_ASSERT_LEVEL=0" + ], + "persistent_writes_allowed": false, + "content_required": false, + "config_required": false, + "networking": false, + "autoload": false, + "automatic_retry": false + }, + "target_build": { + "clean_builds": 2, + "result": "PASS", + "reproducibility": "BYTE_IDENTICAL_TWO_CLEAN_BUILDS", + "elf_sha256": [ + "16c04afe26c490b5071388dc9a32524d09d50d181b1baffbbc54bd96f0c2c688", + "16c04afe26c490b5071388dc9a32524d09d50d181b1baffbbc54bd96f0c2c688" + ], + "map_sha256": [ + "21dfdaa44261f8d546fa9ea71102162dc880a8ca8037526019a04fece5b21313", + "21dfdaa44261f8d546fa9ea71102162dc880a8ca8037526019a04fece5b21313" + ] + }, + "sdl2_smoke_overlay": { + "static_archive_size": 2408922, + "static_archive_sha256": "fc814681c54e02092591165b657c33b12c4f34a3919c80384139aa70b752553d", + "patch_size": 21663, + "patch_sha256": "e5e7413266e4d4491b6c2000fbd26dacc1e25e65c337c4e0b0f21bd4c3f85a4e", + "dynamic_api": false, + "runtime_assertions": false, + "runtime_tested": false + }, + "host_tests": { + "command": "make -f Makefile.ps5 HOST_CC=cc test-ps5-port", + "frames": 600, + "video_fnv1a64": "43f920496eb5f435", + "audio_fnv1a64": "a48f47dc08c56625", + "fault_injection": "PASS", + "exit_selection_negative": "PASS", + "asan": "PASS", + "ubsan": "PASS", + "leak_detection": "PASS", + "structural_guardrails": "PASS", + "hardware_evidence": false + }, + "validation": { + "chimera_gfx_host_build": "PASS", + "chimera_gfx_ctest": "30/30 PASS", + "phase10b_manifest_validation": "PASS", + "phase10b_guardrails": "26/26 PASS", + "phase0_safety_audit": "12 C source files PASS", + "chimera_gfx_secret_scan": "671 text files PASS", + "chimera_retroarch_delta_secret_scan": "PASS", + "git_diff_check": "PASS", + "hardware_evidence": false + } +} diff --git a/manifests/retroarch/phase-1.0b-runtime-contract.json b/manifests/retroarch/phase-1.0b-runtime-contract.json new file mode 100644 index 0000000..5917c6c --- /dev/null +++ b/manifests/retroarch/phase-1.0b-runtime-contract.json @@ -0,0 +1,159 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0B_FIRST_DEVICE_SMOKE_CANDIDATE_HARDENING", + "status": "SOURCE_BOUND_OFFLINE_CONTRACT", + "runtime_limit_ms": 60000, + "frame_limit": 3600, + "shutdown_hold_ms": 2000, + "shutdown_request_max": 1, + "exit_reachable_call_count": 1, + "selected_exit_method": "PROCESS__EXIT_AFTER_TEARDOWN", + "automatic_retry": false, + "autoload": false, + "persistent_writes_allowed": false, + "audio_submit_bounded": false, + "hardware_evidence": false, + "phases": [ + "S00_ENTRY", + "S01_PLATFORM_INIT", + "S02_SDL_INIT", + "S03_VIDEO_INIT", + "S04_INPUT_INIT", + "S05_AUDIO_INIT", + "S06_CORE_INIT", + "S07_RUNNING", + "S08_SHUTDOWN_REQUESTED", + "S09_CORE_DEINIT", + "S10_AUDIO_DEINIT", + "S11_INPUT_DEINIT", + "S12_VIDEO_DEINIT", + "S13_SDL_DEINIT", + "S14_EXITING", + "S15_COMPLETE" + ], + "errors": [ + "E000_NONE", + "E101_PLATFORM_INIT", + "E102_SDL_INIT", + "E103_VIDEO_OPEN", + "E104_FRAMEBUFFER_ALLOC", + "E105_FRAMEBUFFER_BOUNDS", + "E106_FLIP_SUBMIT", + "E107_FLIP_WAIT_TIMEOUT", + "E108_INPUT_OPEN", + "E109_INPUT_READ", + "E110_INPUT_DISCONNECTED", + "E111_AUDIO_OPEN", + "E112_AUDIO_SUBMIT", + "E113_AUDIO_SUBMIT_TIMEOUT", + "E114_CORE_INIT", + "E115_CORE_RUN", + "E116_RUNTIME_DEADLINE", + "E117_FRAME_DEADLINE", + "E118_WRITE_BLOCKED", + "E119_DUPLICATE_INIT", + "E120_CLEANUP_ORDER", + "E121_DUPLICATE_CLEANUP", + "E122_CLEANUP_FAILED", + "E123_EXIT_UNAVAILABLE" + ], + "initialized_subsystems": [ + "platform", + "sdl", + "video", + "input", + "audio", + "retroarch_frontend", + "smokecore", + "threads", + "buffers", + "direct_memory", + "device_handles" + ], + "cleanup_contract": { + "initialized_only": true, + "exactly_once": true, + "reverse_order": true, + "failures_are_sticky": true, + "retry": false + }, + "write_routes": [ + { + "name": "retroarch_vfs", + "write": false, + "create": false, + "append": false, + "truncate": false, + "rename": false, + "unlink": false, + "mkdir": false, + "retry": false + }, + { + "name": "link_time_libc_wrappers", + "write": false, + "create": false, + "append": false, + "truncate": false, + "rename": false, + "unlink": false, + "mkdir": false, + "retry": false + }, + { + "name": "config_content_and_save_paths", + "write": false, + "create": false, + "append": false, + "truncate": false, + "rename": false, + "unlink": false, + "mkdir": false, + "retry": false + } + ], + "input_contract": { + "controller_count": 1, + "shutdown_mapping": "PS5_OPTIONS_TO_SDL_BACK_TO_RETROARCH_SELECT", + "hold_ms": 2000, + "deadzone_raw": 4915, + "deadzone_percent": 15, + "rumble": false, + "lightbar": false + }, + "video_contract": { + "source_width": 320, + "source_height": 240, + "pixel_format": "XRGB8888", + "double_buffered": true, + "copy_bounds_checked": true, + "flip_wait_timeout_us": 100000, + "open_timeout_evidenced": false + }, + "audio_contract": { + "sample_rate_hz": 48000, + "channels": 2, + "submit_calls_counted": true, + "submitted_frames_counted": true, + "submit_duration_measured": true, + "cleanup_duration_measured": true, + "native_underrun_counter": "UNSUPPORTED", + "native_overrun_counter": "UNSUPPORTED", + "blocking_submit_timeout_evidenced": false + }, + "declared_unbounded_or_unproven_calls": [ + "sceVideoOutOpen", + "scePadOpen", + "sceAudioOutOpen", + "sceAudioOutOutput", + "pthread_join", + "sceKernelMapDirectMemory_cleanup" + ], + "startup_effects": { + "sdk_crt_temporary_process_kernel_writes": true, + "sdk_runtime_module_resolution": true, + "sdk_conditional_sysmodule_load": true, + "retroarch_dynamic_core_loading": false, + "firmware_9_60_runtime_behavior": "UNPROVEN" + } +} diff --git a/manifests/retroarch/phase-1.0ba-target-profile-callsite-audit.json b/manifests/retroarch/phase-1.0ba-target-profile-callsite-audit.json new file mode 100644 index 0000000..26dcfc8 --- /dev/null +++ b/manifests/retroarch/phase-1.0ba-target-profile-callsite-audit.json @@ -0,0 +1,49 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0BA_OFFLINE_AV_TARGET_PROFILE_CALLSITE_AUDIT", + "status": "EXACT_CALLSITE_AND_PROFILE_DELTA_SOURCE_PATCH_ALLOWED_NO_BUILD", + "date": "2026-07-29", + "source_bindings": { + "phase10az_manifest_sha256": "0dee988ed67b7f8ea81bc80f0b2fe22818196868510794c68560b5a910624a79", + "retroarch_commit": "a3887edd90780426b3762d42b8e3b96f8ca6813d", + "retroarch_c_blob": "f159522957c75f1e5fc5b4628b9e3a1ddd641f9e", + "platform_ps5_c_blob": "3da820bd33e75b4186d29fdbcde4034b83193bde", + "makefile_blob": "c5c090e9c4f2b972214ee5c76b4560dc79e276e0" + }, + "callsite_contract": { + "rarch_main_return_required": true, + "s15_before_d14": true, + "d14_before_process_exit": true, + "early_return_initialized_mask_rejected": true, + "terminal_emit_result_changes_exit_code": false + }, + "profile_delta": { + "profile_name": "launch-canary", + "inherits_write_diag_behavior": true, + "adds_only_av_target_define": true, + "same_elf_for_both_launch_arms": true, + "launcher_define_forbidden": true, + "new_socket_or_address_forbidden": true, + "runner_activation_forbidden": true + }, + "authorizations": { + "target_source_patch_authorized": true, + "target_profile_addition_authorized": true, + "target_build_authorized": false, + "graphics_operation_authorized": false, + "result_reception_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "device_write_authorized": false + }, + "decision": { + "exact_callsite_bound": true, + "minimal_profile_delta_bound": true, + "source_patch_allowed": true, + "cross_build_allowed": false, + "target_artifact_allowed": false, + "device_action_allowed": false, + "next_step": "IMPLEMENT_HOST_AND_STATIC_TESTED_LAUNCH_CANARY_PROFILE_NO_CROSS_BUILD" + } +} diff --git a/manifests/retroarch/phase-1.0bb-source-only-launch-canary-profile.json b/manifests/retroarch/phase-1.0bb-source-only-launch-canary-profile.json new file mode 100644 index 0000000..627c46a --- /dev/null +++ b/manifests/retroarch/phase-1.0bb-source-only-launch-canary-profile.json @@ -0,0 +1,52 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0BB_SOURCE_ONLY_LAUNCH_CANARY_PROFILE", + "status": "REMOTE_BOUND_SOURCE_PROFILE_COMPLETE_BUILD_PREREQUISITE_AUDIT_ONLY", + "date": "2026-07-29", + "source_bindings": { + "phase10ba_manifest_sha256": "5a028550751b20555855de3f0cc538f91e7d574533a40b48b76442ee5e42cb22", + "retroarch_parent_commit": "a3887edd90780426b3762d42b8e3b96f8ca6813d", + "retroarch_commit": "b0f5178e5acb59172fb352098d2ba32e5c42831b", + "remote_ref": "origin/codex/ps5-phase10az-host-av-source", + "makefile_blob": "1f28ad0f80202f25e2a176348077d917f74297f4", + "retroarch_c_blob": "f8dbc7105e544884bdfe2d9afb4c18075cb37f9b", + "validator_blob": "152b35fd6750639ceeac4bde26402908589d8cde", + "tests_blob": "1dac05556b9d4aac9e60bb9b655c94dc47a24c90", + "source_doc_blob": "351d0420efa76efb7ea7f3a55da88b00afbeea76" + }, + "verification": { + "host_c_sanitizer_matrix": "PASS", + "legacy_parser_runner_tests": "PASS_36", + "historical_source_validators": "PASS", + "phase10bb_mutation_tests": "PASS_3", + "ps5_structural_guardrails": "PASS", + "launch_canary_artifact_absent": true, + "remote_commit_verified": true + }, + "source_properties": { + "post_s15_pre_exit_call": true, + "distinct_launch_canary_profile": true, + "inherits_write_diag": true, + "launcher_independent_payload": true, + "new_network_or_device_path": false, + "runner_activation": false + }, + "authorizations": { + "build_prerequisite_audit_authorized": true, + "target_build_authorized": false, + "graphics_operation_authorized": false, + "result_reception_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "device_write_authorized": false + }, + "decision": { + "source_profile_complete": true, + "cross_build_prerequisites_proven": false, + "cross_build_allowed": false, + "artifact_allowed": false, + "device_action_allowed": false, + "next_step": "OFFLINE_EXACT_CROSS_BUILD_PREREQUISITE_AUDIT" + } +} diff --git a/manifests/retroarch/phase-1.0bc-cross-build-prerequisite-audit.json b/manifests/retroarch/phase-1.0bc-cross-build-prerequisite-audit.json new file mode 100644 index 0000000..f50ccf4 --- /dev/null +++ b/manifests/retroarch/phase-1.0bc-cross-build-prerequisite-audit.json @@ -0,0 +1,48 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0BC_OFFLINE_CROSS_BUILD_PREREQUISITE_AUDIT", + "status": "PREREQUISITES_PARTIAL_EXACT_SDL_MATERIALIZATION_REQUIRED", + "date": "2026-07-29", + "source_bindings": { + "phase10bb_manifest_sha256": "201b08ac542b94d4c7c47e3f45fdaceb2f54d7da11b5f5c8007a8ec162c3daf3", + "retroarch_commit": "b0f5178e5acb59172fb352098d2ba32e5c42831b", + "sdk_release_commit": "d2e2e585740362976a39fdd5ccf390f199a7bc37", + "sdk_archive_sha256": "ebfb0acb5260511951a80e17db41650c62d20a8caf8659a230b928dc85005984", + "sdk_prospero_mk_sha256": "4e0bf7096ba7d623e990b417aea0743f9f03b55c4df3ef270f954d4697c791a4", + "sdk_crt1_sha256": "0e4c1e5e38f22026580e856238a53e75d9e112f99a34bfe5495a3a0ffac15238", + "sdl_commit": "0baf4ac49382b537ba449901b5b6d0d189bb1fbb", + "sdl_patch_sha256": "aa1d3ebc6043d6051938cee1dbffecb80729b3b6595eef964915e93f1570c193", + "linker_script_sha256": "5a01242573bc98340b1de51924c53e1ec1372c23eee62856063431d04d4e5b9f" + }, + "observed_prerequisites": { + "sdk_present": true, + "sdk_lock_bound": true, + "clean_sdl_source_present": true, + "sdl_patch_present": true, + "linker_script_present": true, + "prospero_clang_version": "Ubuntu clang version 18.1.8 (20ubuntu8)", + "wsl_cmake_present": false, + "wsl_ninja_present": false, + "exact_sdl_archive_present": false, + "launch_canary_elf_present": false, + "launch_canary_map_present": false + }, + "authorizations": { + "materializer_source_authorized": true, + "sdl_build_authorized": false, + "retroarch_target_build_authorized": false, + "graphics_operation_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "device_write_authorized": false + }, + "decision": { + "prerequisites_complete": false, + "unknown_archive_reuse_allowed": false, + "materializer_source_allowed": true, + "cross_build_allowed": false, + "device_action_allowed": false, + "next_step": "IMPLEMENT_AND_HOST_TEST_EXACT_SDL_MATERIALIZER_NO_BUILD" + } +} diff --git a/manifests/retroarch/phase-1.0bd-dormant-sdl-materializer-policy.json b/manifests/retroarch/phase-1.0bd-dormant-sdl-materializer-policy.json new file mode 100644 index 0000000..92bafb5 --- /dev/null +++ b/manifests/retroarch/phase-1.0bd-dormant-sdl-materializer-policy.json @@ -0,0 +1,47 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0BD_DORMANT_EXACT_SDL_MATERIALIZER_POLICY", + "status": "REMOTE_BOUND_POLICY_COMPLETE_ADAPTER_SOURCE_ONLY", + "date": "2026-07-29", + "source_bindings": { + "phase10bc_manifest_sha256": "8b7f2793fb1cce79cf701efee01af307b5c7ccce98e6d0506913929b319207b3", + "retroarch_parent_commit": "b0f5178e5acb59172fb352098d2ba32e5c42831b", + "retroarch_commit": "c003c503ff0b747bd161c75ad7329a62d3880da0", + "remote_ref": "origin/codex/ps5-phase10az-host-av-source", + "makefile_blob": "d13ed4842ef221bf6ffe530c89a2e0ba17225924", + "policy_blob": "54d22319361d61e1e8865d66d6b8b4fe065b97ed", + "validator_blob": "083c6639d58f6727a5ae401aef52c973b5cbe6d4", + "tests_blob": "7ee92b2d1615a2ee3db81548c0c120d22ecb56f7", + "source_doc_blob": "332d80ae29b6c2729984edd1eada29d25c33764a" + }, + "policy_properties": { + "exact_identity_validation": true, + "clean_absent_output_required": true, + "immutable_plan": true, + "exact_three_file_patch_scope": true, + "process_adapter_present": false, + "filesystem_adapter_present": false, + "network_present": false, + "cli_present": false, + "retroarch_build_included": false, + "device_action_included": false + }, + "authorizations": { + "injected_adapter_source_authorized": true, + "live_adapter_authorized": false, + "sdl_build_authorized": false, + "retroarch_target_build_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "device_write_authorized": false + }, + "decision": { + "policy_complete": true, + "adapter_complete": false, + "materialization_allowed": false, + "cross_build_allowed": false, + "device_action_allowed": false, + "next_step": "IMPLEMENT_INJECTED_FAKE_TESTED_HOST_ADAPTER_NO_LIVE_BUILD" + } +} diff --git a/manifests/retroarch/phase-1.0be-fake-only-sdl-materializer.json b/manifests/retroarch/phase-1.0be-fake-only-sdl-materializer.json new file mode 100644 index 0000000..341dda1 --- /dev/null +++ b/manifests/retroarch/phase-1.0be-fake-only-sdl-materializer.json @@ -0,0 +1,46 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0BE_FAKE_ONLY_SDL_MATERIALIZER_ADAPTER", + "status": "REMOTE_BOUND_FAKE_ADAPTER_COMPLETE_LIVE_ADAPTER_REASSESSMENT_ONLY", + "date": "2026-07-29", + "source_bindings": { + "phase10bd_manifest_sha256": "2f4ca8b0e50da928bba52ea6a7b49333ba6682bea336bfb7d289e87179f81be0", + "retroarch_parent_commit": "c003c503ff0b747bd161c75ad7329a62d3880da0", + "retroarch_commit": "1c874e725d3c4a51ffbc7b21e18b7548ffcf1b97", + "remote_ref": "origin/codex/ps5-phase10az-host-av-source", + "makefile_blob": "6c0bbf581dc9a1f9591ef0816cdf50cc3b44bfa9", + "adapter_blob": "533199d5e1da7a9cec522bc272b2f2bb544e28c6", + "validator_blob": "0542a469cd9ef5ac23e7e9e485f3f120bb8feb06", + "tests_blob": "e22831e3db742e5d4ac235b16c14816b5d0333f9", + "source_doc_blob": "27a6e3dcdf4f595b6036f4b5df654cd23fd3621c" + }, + "properties": { + "exact_fake_type_only": true, + "ordered_operations": 9, + "all_operation_failures_tested": true, + "changed_file_drift_rejected": true, + "process_capability": false, + "filesystem_capability": false, + "network_capability": false, + "retroarch_build_capability": false, + "device_capability": false + }, + "authorizations": { + "live_adapter_source_reassessment_authorized": true, + "live_adapter_authorized": false, + "sdl_build_authorized": false, + "retroarch_target_build_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "device_write_authorized": false + }, + "decision": { + "fake_adapter_complete": true, + "live_adapter_complete": false, + "materialization_allowed": false, + "cross_build_allowed": false, + "device_action_allowed": false, + "next_step": "AUDIT_LIVE_ADAPTER_SOURCE_AND_TOOL_INSTALLATION_BOUNDARY" + } +} diff --git a/manifests/retroarch/phase-1.0bf-live-sdl-adapter-boundary-audit.json b/manifests/retroarch/phase-1.0bf-live-sdl-adapter-boundary-audit.json new file mode 100644 index 0000000..810523e --- /dev/null +++ b/manifests/retroarch/phase-1.0bf-live-sdl-adapter-boundary-audit.json @@ -0,0 +1,54 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0BF_OFFLINE_LIVE_SDL_ADAPTER_BOUNDARY_AUDIT", + "status": "LIVE_ADAPTER_CONTRACT_BOUND_SOURCE_ONLY_NO_INSTALL_OR_BUILD", + "date": "2026-07-29", + "source_bindings": { + "phase10be_manifest_sha256": "c5e502a4b2d1f3fc873df4d0be3c08a8444d381c7bb734e152deea9aa0d14874", + "retroarch_commit": "1c874e725d3c4a51ffbc7b21e18b7548ffcf1b97" + }, + "tool_observation": { + "repository": "http://archive.ubuntu.com/ubuntu", + "suite": "resolute", + "cmake_candidate": "4.2.3-2ubuntu2", + "ninja_build_candidate": "1.13.2-1", + "cmake_installed": false, + "ninja_installed": false, + "package_install_authorized": false + }, + "adapter_contract": { + "shell_forbidden": true, + "fixed_argv_required": true, + "canonical_build_root_containment": true, + "fresh_outputs_required": true, + "bounded_identity_output": true, + "hard_process_timeout": true, + "patch_check_before_apply": true, + "exact_three_changed_files": true, + "one_attempt": true, + "automatic_retry": false, + "automatic_cleanup": false, + "network_capability": false, + "retroarch_build_capability": false, + "device_capability": false + }, + "authorizations": { + "dormant_live_adapter_source_authorized": true, + "real_adapter_invocation_authorized": false, + "package_install_authorized": false, + "sdl_build_authorized": false, + "retroarch_target_build_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false + }, + "decision": { + "adapter_contract_complete": true, + "dormant_source_allowed": true, + "tool_install_allowed": false, + "materialization_allowed": false, + "cross_build_allowed": false, + "device_action_allowed": false, + "next_step": "IMPLEMENT_DORMANT_LIVE_ADAPTER_WITH_FAKE_PROCESS_AND_FILESYSTEM" + } +} diff --git a/manifests/retroarch/phase-1.0bg-dormant-sdl-request-compiler.json b/manifests/retroarch/phase-1.0bg-dormant-sdl-request-compiler.json new file mode 100644 index 0000000..034c009 --- /dev/null +++ b/manifests/retroarch/phase-1.0bg-dormant-sdl-request-compiler.json @@ -0,0 +1,48 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0BG_DORMANT_SDL_MATERIALIZER_REQUEST_COMPILER", + "status": "REMOTE_BOUND_REQUEST_GRAPH_COMPLETE_EXECUTOR_SOURCE_ONLY", + "date": "2026-07-29", + "source_bindings": { + "phase10bf_manifest_sha256": "cb02fabb01770738a1322715648da4adabbbf064af759c92a4f90eb141e1ca03", + "retroarch_parent_commit": "1c874e725d3c4a51ffbc7b21e18b7548ffcf1b97", + "retroarch_commit": "de23ac2e1debe8c538b2eb1ff973849ad6892946", + "remote_ref": "origin/codex/ps5-phase10az-host-av-source", + "makefile_blob": "49ddfd60f01cdedf2d92b00f4a4a63a90fa10722", + "request_compiler_blob": "bf50f2873d14c7ad25425cfc57713d009d0e8982", + "validator_blob": "e3f1141f95439febb54bc639d43518641b4f758e", + "tests_blob": "1dba1701917595ff0cc40f6dfd11ead284753941", + "source_doc_blob": "932b9525423a8661cfd75fd5c96c90bd91cfb0c8" + }, + "request_graph": { + "operation_count": 8, + "fixed_argv": true, + "shell_present": false, + "canonical_output_containment": true, + "maximum_timeout_seconds": 600, + "maximum_output_bytes": 65536, + "package_manager_present": false, + "retroarch_build_present": false, + "network_present": false, + "device_action_present": false, + "executor_present": false + }, + "authorizations": { + "bounded_executor_source_authorized": true, + "real_executor_invocation_authorized": false, + "package_install_authorized": false, + "sdl_build_authorized": false, + "retroarch_target_build_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false + }, + "decision": { + "request_graph_complete": true, + "executor_complete": false, + "materialization_allowed": false, + "cross_build_allowed": false, + "device_action_allowed": false, + "next_step": "IMPLEMENT_BOUNDED_EXECUTOR_WITH_FAKE_PROCESS_AND_FILESYSTEM_FACADES" + } +} diff --git a/manifests/retroarch/phase-1.0bh-bounded-sdl-executor.json b/manifests/retroarch/phase-1.0bh-bounded-sdl-executor.json new file mode 100644 index 0000000..707d71f --- /dev/null +++ b/manifests/retroarch/phase-1.0bh-bounded-sdl-executor.json @@ -0,0 +1,48 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0BH_BOUNDED_ONE_SHOT_SDL_EXECUTOR", + "status": "REMOTE_BOUND_FAKE_FACADE_EXECUTOR_COMPLETE_REAL_FACADE_REASSESSMENT_ONLY", + "date": "2026-07-29", + "source_bindings": { + "phase10bg_manifest_sha256": "14373371cbf032ce139507c8ab0d4204e5da382dfb4a20be4dfd14d95af07efc", + "retroarch_parent_commit": "de23ac2e1debe8c538b2eb1ff973849ad6892946", + "retroarch_commit": "aa5002e0794834f737f7a3a55571a29465d877df", + "remote_ref": "origin/codex/ps5-phase10az-host-av-source", + "makefile_blob": "e0635414236b71323141a5db495d950f1f6b9c34", + "executor_blob": "68f8d11b1ee4dc9c20ff048424c4d57495100907", + "validator_blob": "9ab3c38997868117a3cd940e60328c6edc28b119", + "tests_blob": "b33af50928d0ac28cf392622ac128b172116e6a5", + "source_doc_blob": "b353a2db36f21251b3483b246e82efc8224c3d24" + }, + "executor": { + "attempt_consumed_before_preflight": true, + "exact_request_order": true, + "exit_output_utf8_semantics_bounded": true, + "success_reuse_forbidden": true, + "failure_reuse_forbidden": true, + "exact_fake_facades_only": true, + "real_process_present": false, + "real_filesystem_present": false, + "cli_present": false + }, + "authorizations": { + "real_facade_reassessment_authorized": true, + "real_facade_source_authorized": false, + "package_install_authorized": false, + "real_executor_invocation_authorized": false, + "sdl_build_authorized": false, + "retroarch_target_build_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false + }, + "decision": { + "bounded_executor_complete": true, + "real_facade_complete": false, + "tool_install_allowed": false, + "materialization_allowed": false, + "cross_build_allowed": false, + "device_action_allowed": false, + "next_step": "AUDIT_REAL_SUBPROCESS_FILESYSTEM_FACADE_AND_OFFICIAL_TOOL_INSTALLATION" + } +} diff --git a/manifests/retroarch/phase-1.0bi-real-facade-and-tool-install-audit.json b/manifests/retroarch/phase-1.0bi-real-facade-and-tool-install-audit.json new file mode 100644 index 0000000..44587ba --- /dev/null +++ b/manifests/retroarch/phase-1.0bi-real-facade-and-tool-install-audit.json @@ -0,0 +1,55 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0BI_REAL_FACADE_AND_EXACT_TOOL_INSTALL_AUDIT", + "status": "REAL_FACADE_SOURCE_AND_EXACT_TOOL_INSTALL_ALLOWED_NO_BUILD", + "date": "2026-07-29", + "source_bindings": { + "phase10bh_manifest_sha256": "d64f17854e1ae7c2f6f183a5f4ea33f9bce9340c89c0638bfad171688bf7554b", + "retroarch_commit": "aa5002e0794834f737f7a3a55571a29465d877df" + }, + "runtime": { + "platform": "WSL_LINUX_X86_64", + "python": "3.14.4", + "sudo_noninteractive": true + }, + "packages": { + "origin": "Ubuntu", + "repository": "http://archive.ubuntu.com/ubuntu", + "suite": "resolute", + "cmake": {"version": "4.2.3-2ubuntu2", "deb_sha256": "87c496f1e37b64bc0b4ad9c5fe0e75c09655f5399ed39695b876afba55becc61"}, + "ninja_build": {"version": "1.13.2-1", "deb_sha256": "533239a464183f041f7c979f899fd8419e31ac82366540e57902a255de78f156"} + }, + "facade_contract": { + "shell_false": true, + "new_process_session": true, + "nonblocking_merged_output": true, + "selector_driven": true, + "monotonic_deadline": true, + "per_request_output_cap": true, + "process_group_kill_on_timeout_or_overflow": true, + "read_only_filesystem_checks": true, + "cli_present": false, + "retry_present": false, + "cleanup_present": false, + "network_present": false, + "device_present": false + }, + "authorizations": { + "real_facade_source_authorized": true, + "exact_package_install_authorized": true, + "real_facade_invocation_authorized": false, + "sdl_build_authorized": false, + "retroarch_target_build_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false + }, + "decision": { + "facade_contract_complete": true, + "tool_install_allowed": true, + "materialization_allowed": false, + "cross_build_allowed": false, + "device_action_allowed": false, + "next_step": "INSTALL_EXACT_HOST_TOOLS_AND_IMPLEMENT_DORMANT_REAL_FACADE" + } +} diff --git a/manifests/retroarch/phase-1.0bj-exact-host-tool-install-result.json b/manifests/retroarch/phase-1.0bj-exact-host-tool-install-result.json new file mode 100644 index 0000000..8556509 --- /dev/null +++ b/manifests/retroarch/phase-1.0bj-exact-host-tool-install-result.json @@ -0,0 +1,42 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0BJ_EXACT_HOST_TOOL_INSTALL_RESULT", + "status": "EXACT_HOST_TOOLS_INSTALLED_REAL_FACADE_DORMANT_NO_BUILD", + "date": "2026-07-29", + "source_bindings": { + "phase10bi_manifest_sha256": "0f53877e27aec5a432b7d5d650f4f4203d7419d4d0a220c27de9913095452267", + "retroarch_commit": "c7511c4f2187794b6dbf56fb2c68b57137424f3b", + "real_facade_sha256": "0819b5e556de13f5a09a7069d5a18ae07a27d3e7cb39ecb2d33f7e939f5bfa32", + "static_validator_sha256": "4a4038a14612e28daa5d22c7d9601295c764ecddee8b0e9b66af2e1225d6a2ec", + "source_test_sha256": "edf3f94750b5244235fd65d808f07cef7919dfbbafd968a3f9dabce16dcf5575" + }, + "installation": { + "attempts": 1, + "completed": true, + "platform": "WSL_LINUX_X86_64", + "cmake_version": "4.2.3-2ubuntu2", + "cmake_data_version": "4.2.3-2ubuntu2", + "ninja_build_version": "1.13.2-1", + "cmake_executable_sha256": "6e1dccda39845415d68eabb934c598998949c99ec4668625d571aee1827b05c7", + "ninja_executable_sha256": "91e9548850cda2799facfdaa7abe33f9e978832f85e98212255708a7bbe437f2" + }, + "authorizations": { + "real_facade_source_authorized": true, + "real_facade_invocation_authorized": false, + "materializer_invocation_authorized": false, + "sdl_build_authorized": false, + "retroarch_target_build_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false + }, + "decision": { + "exact_host_tools_present": true, + "dormant_real_facade_source_present": true, + "real_facade_invoked": false, + "materialization_allowed": false, + "cross_build_allowed": false, + "device_action_allowed": false, + "next_step": "IMPLEMENT_AND_STATICALLY_TEST_DORMANT_REAL_FACADE_SOURCE" + } +} diff --git a/manifests/retroarch/phase-1.0bk-bounded-real-facade-host-fixture-gate.json b/manifests/retroarch/phase-1.0bk-bounded-real-facade-host-fixture-gate.json new file mode 100644 index 0000000..7c2e7c7 --- /dev/null +++ b/manifests/retroarch/phase-1.0bk-bounded-real-facade-host-fixture-gate.json @@ -0,0 +1,62 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0BK_BOUNDED_REAL_FACADE_HOST_FIXTURE_GATE", + "status": "EXACT_HOST_FIXTURES_AUTHORIZED_NO_MATERIALIZER", + "date": "2026-07-29", + "source_bindings": { + "phase10bj_manifest_sha256": "aa4d6ee9df8e2fe67ce1d30d3bb62d5c7593f43770a86e0ea8c1f0f8122176fa", + "retroarch_commit": "c7511c4f2187794b6dbf56fb2c68b57137424f3b", + "real_facade_sha256": "0819b5e556de13f5a09a7069d5a18ae07a27d3e7cb39ecb2d33f7e939f5bfa32", + "python_path": "/usr/bin/python3.14", + "python_version": "3.14.4", + "python_sha256": "fa9796cd3a30878e11a2f40372f773d3fcd913fff35e5bee8dd9a036e22e93ab" + }, + "fixture_contract": { + "suite_attempts": 1, + "per_fixture_attempts": 1, + "success": { + "argv": ["/usr/bin/python3.14", "-c", "import os; os.write(1,b'OK\\n')"], + "timeout_seconds": 2, + "output_limit": 64, + "expected_returncode": 0, + "expected_output_hex": "4f4b0a" + }, + "nonzero": { + "argv": ["/usr/bin/python3.14", "-c", "import os,sys; os.write(1,b'E7\\n'); sys.exit(7)"], + "timeout_seconds": 2, + "output_limit": 64, + "expected_returncode": 7, + "expected_output_hex": "45370a" + }, + "overflow": { + "argv": ["/usr/bin/python3.14", "-c", "import os; os.write(1,b'X'*65)"], + "timeout_seconds": 2, + "output_limit": 64, + "expected_error": "output limit exceeded: fixture_overflow" + }, + "timeout": { + "argv": ["/usr/bin/python3.14", "-c", "import time; time.sleep(5)"], + "timeout_seconds": 1, + "output_limit": 64, + "expected_error": "process timeout: fixture_timeout" + } + }, + "authorizations": { + "exact_host_fixture_suite_authorized": true, + "arbitrary_process_authorized": false, + "materializer_invocation_authorized": false, + "sdl_build_authorized": false, + "retroarch_target_build_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false + }, + "decision": { + "fixture_source_allowed": true, + "fixture_execution_allowed_once": true, + "materialization_allowed": false, + "cross_build_allowed": false, + "device_action_allowed": false, + "next_step": "RUN_ONCE_AND_RECORD_EXACT_HOST_FIXTURE_RESULTS" + } +} diff --git a/manifests/retroarch/phase-1.0bl-fixture-import-failure-and-new-gate.json b/manifests/retroarch/phase-1.0bl-fixture-import-failure-and-new-gate.json new file mode 100644 index 0000000..59b1d51 --- /dev/null +++ b/manifests/retroarch/phase-1.0bl-fixture-import-failure-and-new-gate.json @@ -0,0 +1,51 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0BL_FIXTURE_IMPORT_FAILURE_AND_NEW_GATE", + "status": "BK_CONSUMED_BEFORE_FIXTURES_CORRECTED_RUNNER_AUTHORIZED_ONCE", + "date": "2026-07-29", + "source_bindings": { + "phase10bk_manifest_sha256": "fb2e32e17e9d9984cb6054ca2d057f162a208364de77720c39834c45227fead0", + "retroarch_commit": "f96af6d87ac808809fc486bb1b73a3cf37050986", + "corrected_runner_sha256": "71d6f7327dd43e215faff171bcb886865ddb9228c07911c869f3b90b86d9329c", + "corrected_validator_sha256": "ce0d34dfbfc6c44e3264d2a54cb246e386c88b3c2cbac0dfb55d4d814f15f888", + "python_path": "/usr/bin/python3.14", + "python_sha256": "fa9796cd3a30878e11a2f40372f773d3fcd913fff35e5bee8dd9a036e22e93ab" + }, + "consumed_bk_attempt": { + "suite_attempts": 1, + "exit_code": 1, + "failure": "ModuleNotFoundError: No module named 'tools'", + "failed_runner_sha256": "db17710eefe8960e655c1afb3e1374deabe6d2164965e3aedd933ac4e8ff6ecc", + "main_entered": false, + "facade_constructed": false, + "fixture_processes_started": 0, + "materializer_invoked": false, + "sdl_build_invoked": false, + "retroarch_build_invoked": false, + "device_action_invoked": false + }, + "new_bl_gate": { + "suite_attempts_authorized": 1, + "fixtures": ["fixture_success", "fixture_nonzero", "fixture_overflow", "fixture_timeout"], + "contract_identical_to_phase10bk": true, + "automatic_retry": false + }, + "authorizations": { + "exact_corrected_host_fixture_suite_authorized": true, + "arbitrary_process_authorized": false, + "materializer_invocation_authorized": false, + "sdl_build_authorized": false, + "retroarch_target_build_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false + }, + "decision": { + "bk_consumed": true, + "bl_fixture_execution_allowed_once": true, + "materialization_allowed": false, + "cross_build_allowed": false, + "device_action_allowed": false, + "next_step": "RUN_CORRECTED_FIXTURE_SUITE_ONCE_AND_RECORD_RESULT" + } +} diff --git a/manifests/retroarch/phase-1.0bm-real-facade-fixture-result.json b/manifests/retroarch/phase-1.0bm-real-facade-fixture-result.json new file mode 100644 index 0000000..332ad08 --- /dev/null +++ b/manifests/retroarch/phase-1.0bm-real-facade-fixture-result.json @@ -0,0 +1,47 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0BM_REAL_FACADE_FIXTURE_RESULT", + "status": "REAL_FACADE_BOUNDARIES_PROVEN_ON_HOST_DORMANT_COMPOSITION_ALLOWED", + "date": "2026-07-29", + "source_bindings": { + "phase10bl_manifest_sha256": "f4b2b98bcba59ec161f52297ef4998ff4db782957806883e55a479ffc3d2a437", + "retroarch_commit": "f96af6d87ac808809fc486bb1b73a3cf37050986", + "runner_sha256": "71d6f7327dd43e215faff171bcb886865ddb9228c07911c869f3b90b86d9329c", + "real_facade_sha256": "0819b5e556de13f5a09a7069d5a18ae07a27d3e7cb39ecb2d33f7e939f5bfa32" + }, + "result": { + "suite_attempts": 1, + "fixture_attempts": 4, + "exit_code": 0, + "success_returncode": 0, + "success_output_hex": "4f4b0a", + "nonzero_returncode": 7, + "nonzero_output_hex": "45370a", + "overflow_error": "output limit exceeded: fixture_overflow", + "timeout_error": "process timeout: fixture_timeout", + "materializer_invoked": false, + "sdl_build_invoked": false, + "retroarch_build_invoked": false, + "device_action_invoked": false + }, + "authorizations": { + "dormant_real_composition_source_authorized": true, + "real_composition_invocation_authorized": false, + "materializer_invocation_authorized": false, + "sdl_build_authorized": false, + "retroarch_target_build_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false + }, + "decision": { + "real_facade_success_path_proven_on_host": true, + "real_facade_nonzero_path_proven_on_host": true, + "real_facade_output_cap_proven_on_host": true, + "real_facade_timeout_kill_proven_on_host": true, + "materialization_allowed": false, + "cross_build_allowed": false, + "device_action_allowed": false, + "next_step": "IMPLEMENT_STATICALLY_TEST_DORMANT_EXACT_REAL_COMPOSITION" + } +} diff --git a/manifests/retroarch/phase-1.0bn-materializer-preflight-patch-chain-audit.json b/manifests/retroarch/phase-1.0bn-materializer-preflight-patch-chain-audit.json new file mode 100644 index 0000000..8bdda7a --- /dev/null +++ b/manifests/retroarch/phase-1.0bn-materializer-preflight-patch-chain-audit.json @@ -0,0 +1,52 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0BN_MATERIALIZER_PREFLIGHT_PATCH_CHAIN_AUDIT", + "status": "MATERIALIZER_BLOCKED_PATCH_CHAIN_INCOMPLETE_OFFLINE_REBASE_ALLOWED", + "date": "2026-07-29", + "source_bindings": { + "phase10bm_manifest_sha256": "ace70d11be0a30caf4e93a4a7b92d1f9627645284c9a9b6c92bbfd86f9e86664", + "retroarch_commit": "efa88c5b10f77684cdcae9bcc69236ac81fc6e36", + "composition_sha256": "bce9fa0f1a320614013cabe9f3253a41b2b9c76f8b40a5d487fe9e029f96ff48", + "policy_sha256": "0f3757dca922b84c2910b156f2558429dd598a60bf219bf86ee46640c4ef1c08", + "request_graph_sha256": "50545657ab2ef027b14980a3be2c9c5377b05ddc48d024e031e3073481f39dfc", + "sdl_commit": "0baf4ac49382b537ba449901b5b6d0d189bb1fbb", + "smoke_patch_sha256": "e5e7413266e4d4491b6c2000fbd26dacc1e25e65c337c4e0b0f21bd4c3f85a4e", + "early_diag_patch_sha256": "aa1d3ebc6043d6051938cee1dbffecb80729b3b6595eef964915e93f1570c193" + }, + "preflight": { + "sdl_commit_exact": true, + "sdl_source_clean": true, + "stage_absent": true, + "build_absent": true, + "archive_absent": true, + "cmake_sha256": "6e1dccda39845415d68eabb934c598998949c99ec4668625d571aee1827b05c7", + "ninja_sha256": "91e9548850cda2799facfdaa7abe33f9e978832f85e98212255708a7bbe437f2", + "patch_version": "GNU patch 2.8", + "patch_sha256": "44ecd416d1958be9c2b60488fa893e76aae8d0f81d2fedb9e83eb661da0ee18c", + "sdk_prospero_mk_exact": true, + "sdk_crt1_exact": true, + "smoke_patch_applies_to_clean_source": true, + "early_diag_patch_applies_directly_to_clean_source": false, + "current_request_graph_has_complete_patch_chain": false, + "current_changed_file_set_complete": false, + "current_cmake_contract_complete": false + }, + "authorizations": { + "temporary_offline_patch_rebase_authorized": true, + "corrected_contract_source_authorized": true, + "cmake_invocation_authorized": false, + "materializer_invocation_authorized": false, + "sdl_build_authorized": false, + "retroarch_target_build_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false + }, + "decision": { + "preflight_passed": false, + "materialization_allowed": false, + "cross_build_allowed": false, + "device_action_allowed": false, + "next_step": "REBASE_EXISTING_OVERLAYS_AND_REMEDIATE_CONTRACTS_OFFLINE" + } +} diff --git a/manifests/retroarch/phase-1.0bo-patch-chain-remediation-result.json b/manifests/retroarch/phase-1.0bo-patch-chain-remediation-result.json new file mode 100644 index 0000000..89d9a65 --- /dev/null +++ b/manifests/retroarch/phase-1.0bo-patch-chain-remediation-result.json @@ -0,0 +1,59 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0BO_PATCH_CHAIN_REMEDIATION_RESULT", + "status": "COMPLETE_PATCH_CHAIN_BOUND_RUNNER_SOURCE_ALLOWED_NO_BUILD", + "date": "2026-07-29", + "source_bindings": { + "phase10bn_manifest_sha256": "de3606c7f4c5f029c160d4508e82b38067302a72aff02cfdb7cde3741672ed49", + "retroarch_commit": "80c3f1c1d06477e8acf723142eb67badb56e43fc", + "policy_sha256": "7560dcc82d4745a3cdd9ed8baea3724fbb013f62321d89b35ead1198e49b35b7", + "request_graph_sha256": "700c6460bee83d06b9084e4c7a4eb12fa7d84aedda39ebfaaec500003f13ff3c", + "composition_sha256": "40b1f47bc5c89688be3e65bc8179e228cb9e702a71867f53f3bce19f925721e0" + }, + "rebase_evidence": { + "windows_checkout_crlf_rejected": true, + "first_wsl_checkout_incomplete_rejected": true, + "complete_wsl_checkout_line_endings": "LF", + "smoke_patch_dry_run_passed": true, + "smoke_patch_applied": true, + "early_diag_patch_dry_run_passed_after_smoke": true, + "early_diag_patch_applied_after_smoke": true, + "final_diff_check_passed": true, + "exact_changed_files": [ + "src/audio/ps5/SDL_ps5audio.c", + "src/dynapi/SDL_dynapi.h", + "src/joystick/ps5/SDL_ps5joystick.c", + "src/video/ps5/SDL_ps5video.c" + ], + "insertions": 430, + "deletions": 93 + }, + "corrected_contract": { + "request_count": 10, + "both_patch_dry_runs_present": true, + "both_patch_applications_present": true, + "complete_cmake_flags_present": true, + "clean_first_present": true, + "policy_derived_output_paths": true, + "retroarch_build_included": false, + "device_action_included": false + }, + "authorizations": { + "one_shot_runner_source_authorized": true, + "materializer_invocation_authorized": false, + "cmake_invocation_authorized": false, + "sdl_build_authorized": false, + "retroarch_target_build_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false + }, + "decision": { + "patch_chain_complete": true, + "corrected_contract_complete": true, + "materialization_allowed": false, + "cross_build_allowed": false, + "device_action_allowed": false, + "next_step": "IMPLEMENT_AND_STATICALLY_AUDIT_EXACT_ONE_SHOT_RUNNER" + } +} diff --git a/manifests/retroarch/phase-1.0bp-one-shot-sdl-materialization-gate.json b/manifests/retroarch/phase-1.0bp-one-shot-sdl-materialization-gate.json new file mode 100644 index 0000000..d583c1f --- /dev/null +++ b/manifests/retroarch/phase-1.0bp-one-shot-sdl-materialization-gate.json @@ -0,0 +1,56 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0BP_ONE_SHOT_SDL_MATERIALIZATION_GATE", + "status": "EXACT_SDL_ONLY_MATERIALIZATION_AUTHORIZED_ONCE", + "date": "2026-07-29", + "source_bindings": { + "phase10bo_manifest_sha256": "7f65395e0a91ccaff90319d018d09d15eed71c2de0383e491ef991e1ff5b5ce6", + "retroarch_commit": "d142e49be923582c27a833ad2124635e54c87adc", + "runner_sha256": "4d89f5ab7c0d104cac6f451c94e410f229151e8a7b0f53d0e7b746e44e79d805", + "validator_sha256": "2304537fc1b92982f004d1284ff2ee2985b93b61f95c8ac1725dcbe44d7d09fc", + "source_test_sha256": "29ee112c63335f3599e75fe5fd4db5b9364f4336f2548454de3a6a0080dd0b31", + "python_path": "/usr/bin/python3.14", + "python_sha256": "fa9796cd3a30878e11a2f40372f773d3fcd913fff35e5bee8dd9a036e22e93ab" + }, + "preflight": { + "sdl_source_clean": true, + "stage_absent": true, + "build_absent": true, + "archive_absent": true, + "request_count": 10, + "exact_output_paths": [ + "/mnt/c/Projects/chimera-retroarch-phase10az/build/phase10bd/sdl-source", + "/mnt/c/Projects/chimera-retroarch-phase10az/build/phase10bd/sdl-build", + "/mnt/c/Projects/chimera-retroarch-phase10az/build/phase10bd/sdl-build/libSDL2.a" + ] + }, + "invocation": { + "working_directory": "/mnt/c/Projects/chimera-retroarch-phase10az", + "environment": {"PYTHONDONTWRITEBYTECODE": "1"}, + "argv": [ + "/usr/bin/python3.14", + "-c", + "from tools.phase10bo_sdl_materializer_one_shot_runner import run_once; print(','.join(run_once()))" + ], + "attempts_authorized": 1, + "automatic_retry": false, + "cleanup_authorized": false + }, + "authorizations": { + "exact_materializer_invocation_authorized": true, + "cmake_invocation_authorized": true, + "sdl_build_authorized": true, + "retroarch_target_build_authorized": false, + "target_artifact_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_installation_authorized": false, + "device_execution_authorized": false + }, + "decision": { + "materialization_allowed_once": true, + "retroarch_cross_build_allowed": false, + "device_action_allowed": false, + "next_step": "INVOKE_ONCE_THEN_AUDIT_RETAINED_SDL_OUTPUTS" + } +} diff --git a/manifests/retroarch/phase-1.0bq-inline-invocation-failure.json b/manifests/retroarch/phase-1.0bq-inline-invocation-failure.json new file mode 100644 index 0000000..b6f3db1 --- /dev/null +++ b/manifests/retroarch/phase-1.0bq-inline-invocation-failure.json @@ -0,0 +1,43 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0BQ_INLINE_INVOCATION_FAILURE", + "status": "BP_CONSUMED_BEFORE_PYTHON_ENTRYPOINT_SOURCE_ALLOWED", + "date": "2026-07-29", + "source_bindings": { + "phase10bp_manifest_sha256": "833be486e1c90fe917d5872904823bb0c673814fd18d54dca57f5f08a7ea812f", + "retroarch_commit": "d142e49be923582c27a833ad2124635e54c87adc", + "runner_sha256": "4d89f5ab7c0d104cac6f451c94e410f229151e8a7b0f53d0e7b746e44e79d805" + }, + "consumed_attempt": { + "attempts": 1, + "exit_code": 1, + "failure_class": "HOST_SHELL_PARSE_ERROR", + "python_started": false, + "runner_imported": false, + "runner_consumed": false, + "materializer_requests_started": 0, + "stage_absent_after": true, + "build_absent_after": true, + "archive_absent_after": true, + "retroarch_build_invoked": false, + "device_action_invoked": false + }, + "authorizations": { + "exact_entrypoint_source_authorized": true, + "entrypoint_invocation_authorized": false, + "materializer_invocation_authorized": false, + "cmake_invocation_authorized": false, + "sdl_build_authorized": false, + "retroarch_target_build_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false + }, + "decision": { + "bp_consumed": true, + "outputs_unchanged": true, + "materialization_allowed": false, + "device_action_allowed": false, + "next_step": "IMPLEMENT_AND_STATICALLY_AUDIT_EXACT_SCRIPT_ENTRYPOINT" + } +} diff --git a/manifests/retroarch/phase-1.0br-script-entrypoint-one-shot-gate.json b/manifests/retroarch/phase-1.0br-script-entrypoint-one-shot-gate.json new file mode 100644 index 0000000..378eb2b --- /dev/null +++ b/manifests/retroarch/phase-1.0br-script-entrypoint-one-shot-gate.json @@ -0,0 +1,51 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0BR_SCRIPT_ENTRYPOINT_ONE_SHOT_GATE", + "status": "EXACT_SCRIPT_ENTRYPOINT_AUTHORIZED_ONCE_SDL_ONLY", + "date": "2026-07-29", + "source_bindings": { + "phase10bq_manifest_sha256": "fe80f9a526c0ac928d50b64de71673ab2f93a34aba223bfd9ae79a08183c5ab1", + "retroarch_commit": "8c9d3a51cc9bcaba124e2b30cf593b6e4b55edb0", + "entrypoint_sha256": "01460c2c4c3a37251b463238df5a1c746d51f069dd90f2061d486b81cbf53887", + "validator_sha256": "85f428fdbd42b1a62b1e09d9a40a32dc01ee1c7340361ce374807bfde02be1f8", + "source_test_sha256": "07715981afd91503a80cc708f105daddfd54c56db6ed948f8d35e6dd7a3e8742", + "runner_sha256": "4d89f5ab7c0d104cac6f451c94e410f229151e8a7b0f53d0e7b746e44e79d805" + }, + "preflight": { + "entrypoint_argument_free": true, + "run_once_call_count": 1, + "stage_absent": true, + "build_absent": true, + "archive_absent": true + }, + "invocation": { + "working_directory": "/mnt/c/Projects/chimera-retroarch-phase10az", + "environment": {"PYTHONDONTWRITEBYTECODE": "1"}, + "argv": [ + "/usr/bin/python3.14", + "tools/phase10bq_sdl_materializer_entrypoint.py" + ], + "attempts_authorized": 1, + "automatic_retry": false, + "cleanup_authorized": false + }, + "authorizations": { + "exact_script_entrypoint_invocation_authorized": true, + "materializer_invocation_authorized": true, + "cmake_invocation_authorized": true, + "sdl_build_authorized": true, + "retroarch_target_build_authorized": false, + "target_artifact_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_installation_authorized": false, + "device_execution_authorized": false + }, + "decision": { + "new_attempt_is_automatic_retry": false, + "materialization_allowed_once": true, + "retroarch_cross_build_allowed": false, + "device_action_allowed": false, + "next_step": "INVOKE_EXACT_SCRIPT_ONCE_THEN_AUDIT_RETAINED_OUTPUTS" + } +} diff --git a/manifests/retroarch/phase-1.0bs-read-only-status-timeout.json b/manifests/retroarch/phase-1.0bs-read-only-status-timeout.json new file mode 100644 index 0000000..072da68 --- /dev/null +++ b/manifests/retroarch/phase-1.0bs-read-only-status-timeout.json @@ -0,0 +1,51 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0BS_READ_ONLY_STATUS_TIMEOUT", + "status": "BR_CONSUMED_AT_STATUS_TIMEOUT_TIMEOUT_SOURCE_CORRECTION_ALLOWED", + "date": "2026-07-29", + "source_bindings": { + "phase10br_manifest_sha256": "0bf54f5566abd876d1041879f430e355431bdfdf3b4144b9427531305d16fa00", + "retroarch_commit": "8c9d3a51cc9bcaba124e2b30cf593b6e4b55edb0", + "request_graph_sha256": "700c6460bee83d06b9084e4c7a4eb12fa7d84aedda39ebfaaec500003f13ff3c" + }, + "consumed_attempt": { + "attempts": 1, + "exit_code": 1, + "completed_operations": ["verify_sdl_commit"], + "failed_operation": "verify_sdl_clean", + "failure": "process timeout: verify_sdl_clean", + "old_timeout_seconds": 10, + "process_group_terminated": true, + "materializer_processes_started": 2, + "stage_created": false, + "cmake_invoked": false, + "sdl_build_invoked": false, + "retroarch_build_invoked": false, + "device_action_invoked": false, + "stage_absent_after": true, + "build_absent_after": true, + "archive_absent_after": true + }, + "correction_contract": { + "operation": "verify_sdl_clean", + "argv_unchanged": true, + "output_limit_unchanged": true, + "new_timeout_seconds": 120 + }, + "authorizations": { + "status_timeout_source_correction_authorized": true, + "materializer_invocation_authorized": false, + "cmake_invocation_authorized": false, + "sdl_build_authorized": false, + "retroarch_target_build_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false + }, + "decision": { + "br_consumed": true, + "materialization_allowed": false, + "device_action_allowed": false, + "next_step": "CHANGE_ONLY_STATUS_TIMEOUT_TO_120_AND_REAUDIT" + } +} diff --git a/manifests/retroarch/phase-1.0bt-corrected-timeout-one-shot-gate.json b/manifests/retroarch/phase-1.0bt-corrected-timeout-one-shot-gate.json new file mode 100644 index 0000000..f6af04c --- /dev/null +++ b/manifests/retroarch/phase-1.0bt-corrected-timeout-one-shot-gate.json @@ -0,0 +1,52 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0BT_CORRECTED_TIMEOUT_ONE_SHOT_GATE", + "status": "EXACT_120_SECOND_STATUS_MATERIALIZATION_AUTHORIZED_ONCE", + "date": "2026-07-29", + "source_bindings": { + "phase10bs_manifest_sha256": "fa923e5f33967606c65c585eb1a1273fd7e64faf1650776e33717f73f1861987", + "retroarch_commit": "5888b873dc8daa5fe8c931b978ca3e7976b32c0a", + "request_graph_sha256": "6ac24e4f625f2ce6c6331ed490aaff55cbc61706dafbb38111a8ffb3cb9ddcab", + "entrypoint_sha256": "01460c2c4c3a37251b463238df5a1c746d51f069dd90f2061d486b81cbf53887", + "runner_sha256": "4d89f5ab7c0d104cac6f451c94e410f229151e8a7b0f53d0e7b746e44e79d805" + }, + "correction": { + "operation": "verify_sdl_clean", + "timeout_seconds": 120, + "argv_unchanged": true, + "output_limit": 4096, + "all_other_requests_unchanged": true + }, + "preflight": { + "stage_absent": true, + "build_absent": true, + "archive_absent": true + }, + "invocation": { + "working_directory": "/mnt/c/Projects/chimera-retroarch-phase10az", + "environment": {"PYTHONDONTWRITEBYTECODE": "1"}, + "argv": ["/usr/bin/python3.14", "tools/phase10bq_sdl_materializer_entrypoint.py"], + "attempts_authorized": 1, + "automatic_retry": false, + "cleanup_authorized": false + }, + "authorizations": { + "exact_script_entrypoint_invocation_authorized": true, + "materializer_invocation_authorized": true, + "cmake_invocation_authorized": true, + "sdl_build_authorized": true, + "retroarch_target_build_authorized": false, + "target_artifact_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_installation_authorized": false, + "device_execution_authorized": false + }, + "decision": { + "new_attempt_is_automatic_retry": false, + "materialization_allowed_once": true, + "retroarch_cross_build_allowed": false, + "device_action_allowed": false, + "next_step": "INVOKE_ONCE_AND_AUDIT_RETAINED_OUTPUTS" + } +} diff --git a/manifests/retroarch/phase-1.0bu-wsl-git-status-timeout-and-windows-git-fixture-gate.json b/manifests/retroarch/phase-1.0bu-wsl-git-status-timeout-and-windows-git-fixture-gate.json new file mode 100644 index 0000000..11b07a0 --- /dev/null +++ b/manifests/retroarch/phase-1.0bu-wsl-git-status-timeout-and-windows-git-fixture-gate.json @@ -0,0 +1,61 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0BU_WSL_GIT_STATUS_TIMEOUT_AND_WINDOWS_GIT_FIXTURE_GATE", + "status": "BT_CONSUMED_WSL_STATUS_UNSUITABLE_WINDOWS_GIT_READ_ONLY_FIXTURE_ALLOWED", + "date": "2026-07-29", + "source_bindings": { + "phase10bt_manifest_sha256": "313e95ccc61cdb6005e39a7cb4c4ab82cc9e4e791889e90e8bea5f1a826fed84", + "retroarch_commit": "5888b873dc8daa5fe8c931b978ca3e7976b32c0a" + }, + "consumed_attempt": { + "attempts": 1, + "exit_code": 1, + "completed_operations": ["verify_sdl_commit"], + "failed_operation": "verify_sdl_clean", + "failure": "process timeout: verify_sdl_clean", + "timeout_seconds": 120, + "process_group_terminated": true, + "stage_absent_after": true, + "build_absent_after": true, + "archive_absent_after": true, + "cmake_invoked": false, + "sdl_build_invoked": false, + "retroarch_build_invoked": false, + "device_action_invoked": false + }, + "windows_git": { + "windows_path": "C:/Program Files/Git/cmd/git.exe", + "wsl_path": "/mnt/c/Program Files/Git/cmd/git.exe", + "version": "git version 2.54.0.windows.1", + "sha256": "81ef35ae005ca9318018d18e3327578ce939fb99feaad6b2d7c8ab15f3de8db5" + }, + "fixture": { + "attempts_authorized": 1, + "working_directory": "/mnt/c/Projects/chimera-retroarch-phase10az", + "commands": [ + ["/mnt/c/Program Files/Git/cmd/git.exe", "-C", "C:/Projects/chimera-gfx/work/upstream/SDL", "rev-parse", "HEAD"], + ["/mnt/c/Program Files/Git/cmd/git.exe", "-C", "C:/Projects/chimera-gfx/work/upstream/SDL", "status", "--porcelain"] + ], + "per_command_timeout_seconds": 30, + "per_command_output_limit": 4096, + "automatic_retry": false + }, + "authorizations": { + "exact_windows_git_read_only_fixture_authorized": true, + "request_graph_source_change_authorized": false, + "materializer_invocation_authorized": false, + "cmake_invocation_authorized": false, + "sdl_build_authorized": false, + "retroarch_target_build_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false + }, + "decision": { + "wsl_git_status_suitable": false, + "fixture_allowed_once": true, + "materialization_allowed": false, + "device_action_allowed": false, + "next_step": "RUN_EXACT_READ_ONLY_WINDOWS_GIT_FIXTURE_ONCE" + } +} diff --git a/manifests/retroarch/phase-1.0bv-windows-git-fixture-result-and-request-correction-gate.json b/manifests/retroarch/phase-1.0bv-windows-git-fixture-result-and-request-correction-gate.json new file mode 100644 index 0000000..9ac2c03 --- /dev/null +++ b/manifests/retroarch/phase-1.0bv-windows-git-fixture-result-and-request-correction-gate.json @@ -0,0 +1,52 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0BV_WINDOWS_GIT_FIXTURE_RESULT_AND_REQUEST_CORRECTION_GATE", + "status": "WINDOWS_GIT_READ_ONLY_FIXTURE_PASSED_REQUEST_SOURCE_CORRECTION_ALLOWED", + "date": "2026-07-30", + "source_bindings": { + "phase10bu_manifest_sha256": "4085201165f16771b793a5e9866d90992589080da841b44847d7761b68e78b46", + "retroarch_commit": "5888b873dc8daa5fe8c931b978ca3e7976b32c0a" + }, + "fixture_result": { + "attempts": 1, + "working_directory": "/mnt/c/Projects/chimera-retroarch-phase10az", + "windows_git_sha256": "81ef35ae005ca9318018d18e3327578ce939fb99feaad6b2d7c8ab15f3de8db5", + "rev_parse_exit_code": 0, + "observed_commit": "0baf4ac49382b537ba449901b5b6d0d189bb1fbb", + "status_exit_code": 0, + "porcelain_status": "", + "worktree_created": false, + "patch_invoked": false, + "cmake_invoked": false, + "sdl_build_invoked": false, + "retroarch_build_invoked": false, + "device_action_invoked": false + }, + "authorized_source_correction": { + "read_only_git_executable": "/mnt/c/Program Files/Git/cmd/git.exe", + "read_only_git_source_path": "C:/Projects/chimera-gfx/work/upstream/SDL", + "read_only_timeout_seconds": 30, + "read_only_output_limit": 4096, + "hash_bind_windows_git_in_runner": true, + "detached_stage_add_quiet": true, + "detached_stage_timeout_seconds": 900, + "automatic_retry": false + }, + "authorizations": { + "request_graph_source_change_authorized": true, + "materializer_invocation_authorized": false, + "cmake_invocation_authorized": false, + "sdl_build_authorized": false, + "retroarch_target_build_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false + }, + "decision": { + "windows_git_read_only_suitable": true, + "source_correction_allowed": true, + "materialization_allowed": false, + "device_action_allowed": false, + "next_step": "IMPLEMENT_AND_HOST_TEST_EXACT_REQUEST_SOURCE_CORRECTION" + } +} diff --git a/manifests/retroarch/phase-1.0bw-corrected-sdl-materializer-one-shot-gate.json b/manifests/retroarch/phase-1.0bw-corrected-sdl-materializer-one-shot-gate.json new file mode 100644 index 0000000..370d47f --- /dev/null +++ b/manifests/retroarch/phase-1.0bw-corrected-sdl-materializer-one-shot-gate.json @@ -0,0 +1,54 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0BW_CORRECTED_SDL_MATERIALIZER_ONE_SHOT_GATE", + "status": "CORRECTED_REQUEST_GRAPH_BOUND_ONE_OFFLINE_SDL_MATERIALIZATION_ALLOWED", + "date": "2026-07-30", + "source_bindings": { + "phase10bv_manifest_sha256": "fb326bcb93ea777991918bf06956613136639f8fffe8b68b697e0d75c7d9d852", + "retroarch_commit": "3974ee13ddac828109b3f558067378be3515f10b", + "request_compiler_sha256": "f57d4f0d6940ecc4d7b83c0f273d7440d8d21fb328c26c03f3ef453d886161e4", + "real_composition_sha256": "f7a6271352dcfaa24faa7b7dec8482360d0d57620eec1d940539fe35725787a3", + "one_shot_runner_sha256": "2f1f9f9c16369321b24c08495f3a7ae26f9990cb1e7ee5eabd5f0f537fb0e880", + "entrypoint_sha256": "01460c2c4c3a37251b463238df5a1c746d51f069dd90f2061d486b81cbf53887" + }, + "preflight": { + "retroarch_worktree_clean": true, + "stage_absent": true, + "build_absent": true, + "archive_absent": true, + "phase_b_host_tests_passed": 31, + "relevant_static_validators_passed": 10 + }, + "one_shot": { + "attempts_authorized": 1, + "working_directory": "/mnt/c/Projects/chimera-retroarch-phase10az", + "argv": [ + "/usr/bin/python3.14", + "tools/phase10bq_sdl_materializer_entrypoint.py" + ], + "environment": { + "PYTHONDONTWRITEBYTECODE": "1" + }, + "automatic_retry": false, + "cleanup_on_failure_authorized": false, + "output_path_reuse_authorized": false + }, + "authorizations": { + "materializer_invocation_authorized": true, + "detached_sdl_stage_authorized": true, + "offline_patch_application_authorized": true, + "cmake_invocation_authorized": true, + "sdl_build_authorized": true, + "retroarch_target_build_authorized": false, + "network_access_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false + }, + "decision": { + "offline_sdl_materialization_allowed_once": true, + "retroarch_target_build_allowed": false, + "device_action_allowed": false, + "next_step": "INVOKE_EXACT_SCRIPT_ENTRYPOINT_ONCE_AND_RECORD_RESULT" + } +} diff --git a/manifests/retroarch/phase-1.0bx-changed-files-timeout-and-read-only-fixture-gate.json b/manifests/retroarch/phase-1.0bx-changed-files-timeout-and-read-only-fixture-gate.json new file mode 100644 index 0000000..7079eb7 --- /dev/null +++ b/manifests/retroarch/phase-1.0bx-changed-files-timeout-and-read-only-fixture-gate.json @@ -0,0 +1,74 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0BX_CHANGED_FILES_TIMEOUT_AND_READ_ONLY_FIXTURE_GATE", + "status": "BW_CONSUMED_CHANGED_FILES_TIMEOUT_WINDOWS_GIT_STAGE_FIXTURE_ALLOWED", + "date": "2026-07-30", + "source_bindings": { + "phase10bw_manifest_sha256": "82f7f20d6cf829ce3a7621ade6ca5124e12c35a79898d4cb1eb9df93d1643f4d", + "retroarch_commit": "3974ee13ddac828109b3f558067378be3515f10b" + }, + "consumed_attempt": { + "attempts": 1, + "exit_code": 1, + "completed_operations": [ + "verify_sdl_commit", + "verify_sdl_clean", + "create_detached_stage", + "smoke_patch_check", + "smoke_patch_apply", + "early_diag_patch_check", + "early_diag_patch_apply" + ], + "failed_operation": "verify_changed_files", + "failure": "process timeout: verify_changed_files", + "timeout_seconds": 10, + "process_group_terminated": true, + "stage_exists_after": true, + "build_absent_after": true, + "archive_absent_after": true, + "cmake_invoked": false, + "sdl_build_invoked": false, + "retroarch_build_invoked": false, + "device_action_invoked": false + }, + "fixture": { + "attempts_authorized": 1, + "working_directory": "/mnt/c/Projects/chimera-retroarch-phase10az", + "argv": [ + "/mnt/c/Program Files/Git/cmd/git.exe", + "-C", + "C:/Projects/chimera-retroarch-phase10az/build/phase10bd/sdl-source", + "diff", + "--name-only" + ], + "timeout_seconds": 30, + "output_limit": 4096, + "expected_changed_files": [ + "src/audio/ps5/SDL_ps5audio.c", + "src/dynapi/SDL_dynapi.h", + "src/joystick/ps5/SDL_ps5joystick.c", + "src/video/ps5/SDL_ps5video.c" + ], + "automatic_retry": false + }, + "authorizations": { + "exact_windows_git_read_only_fixture_authorized": true, + "request_graph_source_change_authorized": false, + "stage_cleanup_authorized": false, + "output_path_reuse_authorized": false, + "materializer_invocation_authorized": false, + "cmake_invocation_authorized": false, + "sdl_build_authorized": false, + "retroarch_target_build_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false + }, + "decision": { + "fixture_allowed_once": true, + "cleanup_allowed": false, + "materialization_allowed": false, + "device_action_allowed": false, + "next_step": "RUN_EXACT_READ_ONLY_STAGE_DIFF_FIXTURE_ONCE" + } +} diff --git a/manifests/retroarch/phase-1.0by-windows-git-stage-fixture-failure-and-file-hash-gate.json b/manifests/retroarch/phase-1.0by-windows-git-stage-fixture-failure-and-file-hash-gate.json new file mode 100644 index 0000000..4262d28 --- /dev/null +++ b/manifests/retroarch/phase-1.0by-windows-git-stage-fixture-failure-and-file-hash-gate.json @@ -0,0 +1,54 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0BY_WINDOWS_GIT_STAGE_FIXTURE_FAILURE_AND_FILE_HASH_GATE", + "status": "BX_CONSUMED_CROSS_PLATFORM_WORKTREE_UNREADABLE_EXACT_FILE_HASH_FIXTURE_ALLOWED", + "date": "2026-07-30", + "source_bindings": { + "phase10bx_manifest_sha256": "ac40bda2d1893d09a86f47d09475c43d608360f2d04fc1192a382c24f9bb9adc", + "retroarch_commit": "3974ee13ddac828109b3f558067378be3515f10b" + }, + "consumed_fixture": { + "attempts": 1, + "exit_code": 1, + "failure": "fatal: not a git repository: (NULL)", + "stage_mutated": false, + "cleanup_invoked": false, + "cmake_invoked": false, + "sdl_build_invoked": false, + "device_action_invoked": false + }, + "file_hash_fixture": { + "attempts_authorized": 1, + "stage_root": "C:/Projects/chimera-retroarch-phase10az/build/phase10bd/sdl-source", + "read_git_indirection": true, + "sha256_paths": [ + "src/audio/ps5/SDL_ps5audio.c", + "src/dynapi/SDL_dynapi.h", + "src/joystick/ps5/SDL_ps5joystick.c", + "src/video/ps5/SDL_ps5video.c" + ], + "directory_enumeration_authorized": false, + "git_invocation_authorized": false, + "automatic_retry": false + }, + "authorizations": { + "exact_file_hash_fixture_authorized": true, + "request_graph_source_change_authorized": false, + "stage_cleanup_authorized": false, + "output_path_reuse_authorized": false, + "materializer_invocation_authorized": false, + "cmake_invocation_authorized": false, + "sdl_build_authorized": false, + "retroarch_target_build_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false + }, + "decision": { + "file_hash_fixture_allowed_once": true, + "cleanup_allowed": false, + "materialization_allowed": false, + "device_action_allowed": false, + "next_step": "READ_EXACT_GIT_INDIRECTION_AND_HASH_FOUR_PATCH_TARGETS_ONCE" + } +} diff --git a/manifests/retroarch/phase-1.0bz-exact-sdl-stage-cleanup-gate.json b/manifests/retroarch/phase-1.0bz-exact-sdl-stage-cleanup-gate.json new file mode 100644 index 0000000..164679a --- /dev/null +++ b/manifests/retroarch/phase-1.0bz-exact-sdl-stage-cleanup-gate.json @@ -0,0 +1,59 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0BZ_EXACT_SDL_STAGE_CLEANUP_GATE", + "status": "PATCH_TARGET_HASHES_BOUND_EXACT_FAILED_STAGE_CLEANUP_ALLOWED_ONCE", + "date": "2026-07-30", + "source_bindings": { + "phase10by_manifest_sha256": "d44773c113c716d6fc158aa34673c6d676b13d50742def7e453c90f175647486", + "retroarch_commit": "3974ee13ddac828109b3f558067378be3515f10b" + }, + "fixture_result": { + "attempts": 1, + "git_indirection": "gitdir: /mnt/c/Projects/chimera-gfx/work/upstream/SDL/.git/worktrees/sdl-source", + "patch_target_sha256": { + "src/audio/ps5/SDL_ps5audio.c": "dceb7f29a237bbd01c8e5b912ca60a22bacd6acde0124f5fbc3f82a4a6d816b2", + "src/dynapi/SDL_dynapi.h": "ce69459df53f01b7bfa32fd4aa9ae0732f1ff825f7af095522f76b8eacea401c", + "src/joystick/ps5/SDL_ps5joystick.c": "f9bea71ff2d24578eadbbef6460d927966514a4b530551c1cbc17900a838b08d", + "src/video/ps5/SDL_ps5video.c": "b59a665ec1eedeb05f630aa8e48149c71b63771a3604a13a6698410fcfd3af79" + }, + "stage_mutated": false, + "cleanup_invoked": false, + "cmake_invoked": false, + "sdl_build_invoked": false + }, + "cleanup": { + "attempts_authorized": 1, + "working_directory": "/mnt/c/Projects/chimera-retroarch-phase10az", + "argv": [ + "/usr/bin/git", + "-C", + "/mnt/c/Projects/chimera-gfx/work/upstream/SDL", + "worktree", + "remove", + "--force", + "/mnt/c/Projects/chimera-retroarch-phase10az/build/phase10bd/sdl-source" + ], + "timeout_seconds": 900, + "automatic_retry": false + }, + "authorizations": { + "exact_failed_stage_cleanup_authorized": true, + "other_worktree_cleanup_authorized": false, + "request_graph_source_change_authorized": false, + "output_path_reuse_authorized": false, + "materializer_invocation_authorized": false, + "cmake_invocation_authorized": false, + "sdl_build_authorized": false, + "retroarch_target_build_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false + }, + "decision": { + "exact_cleanup_allowed_once": true, + "source_change_allowed": false, + "materialization_allowed": false, + "device_action_allowed": false, + "next_step": "REMOVE_ONLY_EXACT_FAILED_STAGE_ONCE_AND_VERIFY_ABSENCE" + } +} diff --git a/manifests/retroarch/phase-1.0c-device-smoke-result.json b/manifests/retroarch/phase-1.0c-device-smoke-result.json new file mode 100644 index 0000000..8a10e12 --- /dev/null +++ b/manifests/retroarch/phase-1.0c-device-smoke-result.json @@ -0,0 +1,183 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0C_ONE_SHOT_PS5_DEVICE_SMOKE_TEST", + "status": "DEVICE_SMOKE_PARTIAL", + "date": "2026-07-19", + "local_window": "2026-07-19 16:19-16:31 CEST", + "classification_detail": [ + "NO_VISIBLE_OUTPUT", + "REMOTE_EXECUTION_AND_ENTRY_UNCONFIRMED", + "CONSOLE_RESPONSIVE" + ], + "repositories": { + "chimera_gfx": { + "baseline_branch": "codex/chimera-gfx-phase10b-device-smoke-hardening", + "baseline_commit": "4daf57e1f446d18f0a1e104ac880feb0a2460611", + "result_branch": "codex/chimera-gfx-phase10c-device-smoke-result" + }, + "chimera_retroarch": { + "branch": "codex/ps5-device-smoke-hardening", + "commit": "392d14871f3b121a346d1e2e727e528552bdd700", + "modified_in_phase10c": false + }, + "ps5_payload_sdk_commit": "d2e2e585740362976a39fdd5ccf390f199a7bc37", + "pacbrew_commit": "c2abcfcb60f569128abd0e8e70ad03a67bee5ea7", + "sdl_commit": "0baf4ac49382b537ba449901b5b6d0d189bb1fbb", + "hardened_elfldr_source_commit": "197623058f509eddde18868dafcb92fdcac66464" + }, + "artifact": { + "name": "retroarch_ps5_software_smoke.elf", + "tracked_in_git": false, + "size": 1826216, + "sha256": "16c04afe26c490b5071388dc9a32524d09d50d181b1baffbbc54bd96f0c2c688", + "linker_map_sha256": "21dfdaa44261f8d546fa9ea71102162dc880a8ca8037526019a04fece5b21313", + "build_manifest": "manifests/retroarch/phase-1.0b-build.json", + "build_manifest_sha256": "8ee4305bc2d7e5d75d3f7a4b0b335de39c68932ca49c199310cda9f2279dd69f", + "stable_reopen_hash_before_first_attempt": true, + "stable_reopen_hash_before_second_attempt": true + }, + "offline_preflight": { + "result": "PASS", + "clean_rebuild": "PASS", + "artifact_identity": "PASS", + "linker_map_identity": "PASS", + "chimera_gfx_ctest": "30/30 PASS", + "phase10b_guardrails": "26/26 PASS", + "retroarch_host_smoke": "PASS", + "fault_injection": "PASS", + "asan_ubsan_leak": "PASS", + "elf_import_relocation_segment_audit": "PASS", + "rwx_segments": 0, + "write_firewall": "PRESENT", + "bounded_smoke_mode": true, + "hardware_evidence": false + }, + "authorization": { + "operator_confirmed_all_12_preflight_statements": true, + "initial_artifact_specific_authorization": true, + "second_manual_override_authorization": true, + "second_action_was_automatic_retry": false, + "further_connection_authorized": false, + "further_transfer_authorized": false, + "further_execution_authorized": false, + "installation_authorized": false, + "autoload_authorized": false, + "persistent_staging_authorized": false, + "device_file_write_authorized": false, + "payload_manager_upload_authorized": false, + "automatic_retry": false, + "automatic_reboot_authorized": false + }, + "client": { + "name": "Gezine/Y2JB payload_sender.py", + "source_commit": "0dbbf4e7e0203af7e5d101a3256c634edf4e3ba2", + "source_sha256": "8c87920c41dbdbd66b9f36ca9509f0d6bef9170f351dd97ff831cfb98e642ec6", + "source_controlled": true, + "binary_mode": true, + "transport": "IPv4_TCP", + "port": 9021, + "destination_recorded_in_git": false, + "send_primitive": "CPYTHON_SENDALL", + "short_send_handling": true, + "remote_acknowledgement": false, + "response_read": false, + "retry": false, + "resume": false + }, + "transport_contract": { + "classification": "DIRECT_MEMORY_RAW_ELF_HOST_SEND_CONFIRMED_REMOTE_RECEIPT_UNACKNOWLEDGED", + "direction": "HOST_TO_PS5", + "loader_input": "RAW_ELF", + "filesystem_staging": false, + "installation": false, + "autoload": false, + "live_file_replacement": false, + "rename": false, + "delete": false, + "cleanup_write": false, + "running_listener_identity": "NOT_BYTE_EXACTLY_ATTESTED_ON_DEVICE" + }, + "attempts": [ + { + "attempt": 1, + "authorization": "INITIAL_EXACT_ARTIFACT_AUTHORIZATION", + "artifact_size_reverified": 1826216, + "artifact_sha256_reverified": "16c04afe26c490b5071388dc9a32524d09d50d181b1baffbbc54bd96f0c2c688", + "client_exit_code": 0, + "client_reported_bytes": 1826216, + "connections": 1, + "transfers": 1, + "execution_requests": 1, + "remote_execution_observed": false, + "observation_ms": 75235, + "operator_observation": "DISPLAY_WAS_OFF", + "console_responsive_after": true, + "reboot_needed": false + }, + { + "attempt": 2, + "authorization": "SEPARATE_MANUAL_OVERRIDE_BY_OPERATOR", + "artifact_size_reverified": 1826216, + "artifact_sha256_reverified": "16c04afe26c490b5071388dc9a32524d09d50d181b1baffbbc54bd96f0c2c688", + "client_exit_code": 0, + "client_reported_bytes": 1826216, + "connections": 1, + "transfers": 1, + "execution_requests": 1, + "remote_execution_observed": false, + "observation_ms": 75405, + "operator_observation": "NO_VISIBLE_CHANGE", + "console_responsive_after": true, + "reboot_needed": false + } + ], + "totals": { + "connections": 2, + "transfers": 2, + "bytes_reported_sent": 3652432, + "execution_requests": 2, + "executions_independently_observed": 0, + "automatic_retries": 0, + "further_attempts": 0 + }, + "device_observation": { + "anonymized_device_id": "PS5-FW960-SESSION-A", + "firmware_source": "OPERATOR_CONFIRMED_PS5_UI", + "firmware_raw": "9.60", + "first_visible_phase": "UNOBSERVED", + "last_visible_phase": "UNOBSERVED", + "first_error_code": "UNOBSERVED", + "write_firewall_status": "UNOBSERVED", + "video": "NO_VISIBLE_CHANGE_OBSERVED", + "frame_progression": "UNOBSERVED", + "flip_progression": "UNOBSERVED", + "input": "UNOBSERVED", + "audio": "UNOBSERVED", + "manual_shutdown": "UNOBSERVED", + "automatic_deadline": "UNOBSERVED", + "process_exit": "UNOBSERVED", + "console_responsive": true, + "host_session": "NOT_INDEPENDENTLY_TESTED", + "reboot_needed": false, + "unexpected_effects": "NONE_REPORTED_NOT_INDEPENDENTLY_INSPECTED", + "photo_or_video_reference": null + }, + "persistent_actions": { + "installation_performed": false, + "autoload_modified": false, + "persistent_staging_performed": false, + "payload_manager_request_performed": false, + "live_file_replaced": false, + "file_deleted_or_renamed": false, + "cleanup_write_performed": false, + "reboot_command_performed": false + }, + "remaining_runtime_questions": [ + "deployed port-9021 listener identity", + "remote receipt and complete ELF parse", + "child creation and entry", + "failure before first visible VideoOut frame", + "runtime deadline and teardown" + ], + "next_phase": "PHASE_1_0D_OFFLINE_LOADER_TO_ENTRY_AND_EARLIEST_VISIBLE_ERROR_DIAGNOSIS" +} diff --git a/manifests/retroarch/phase-1.0ca-cleanup-result-and-hash-verifier-correction-gate.json b/manifests/retroarch/phase-1.0ca-cleanup-result-and-hash-verifier-correction-gate.json new file mode 100644 index 0000000..890af13 --- /dev/null +++ b/manifests/retroarch/phase-1.0ca-cleanup-result-and-hash-verifier-correction-gate.json @@ -0,0 +1,55 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0CA_CLEANUP_RESULT_AND_HASH_VERIFIER_CORRECTION_GATE", + "status": "EXACT_STAGE_CLEANUP_PASSED_HASH_VERIFIER_SOURCE_CORRECTION_ALLOWED", + "date": "2026-07-30", + "source_bindings": { + "phase10bz_manifest_sha256": "06ddaafc41354cc03ecbf5ad8efab9f9ebaee7534ea9e152344d71b6bfacd686", + "retroarch_commit": "3974ee13ddac828109b3f558067378be3515f10b" + }, + "cleanup_result": { + "attempts": 1, + "exit_code": 0, + "stage_absent_after": true, + "worktree_metadata_absent_after": true, + "source_commit_after": "0baf4ac49382b537ba449901b5b6d0d189bb1fbb", + "source_clean_after": true, + "other_worktrees_removed": false, + "build_invoked": false, + "device_action_invoked": false + }, + "authorized_source_correction": { + "operation": "verify_patch_target_hashes", + "executable": "/usr/bin/sha256sum", + "executable_version": "sha256sum (uutils coreutils) 0.8.0", + "executable_sha256": "48893b0fb21436b54619db80486e83ef39dfccaf1aefe83dfa00c02d6146e8c0", + "timeout_seconds": 30, + "output_limit": 4096, + "expected_sha256": { + "src/audio/ps5/SDL_ps5audio.c": "dceb7f29a237bbd01c8e5b912ca60a22bacd6acde0124f5fbc3f82a4a6d816b2", + "src/dynapi/SDL_dynapi.h": "ce69459df53f01b7bfa32fd4aa9ae0732f1ff825f7af095522f76b8eacea401c", + "src/joystick/ps5/SDL_ps5joystick.c": "f9bea71ff2d24578eadbbef6460d927966514a4b530551c1cbc17900a838b08d", + "src/video/ps5/SDL_ps5video.c": "b59a665ec1eedeb05f630aa8e48149c71b63771a3604a13a6698410fcfd3af79" + }, + "hash_bind_executable_in_runner": true, + "automatic_retry": false + }, + "authorizations": { + "request_graph_source_change_authorized": true, + "output_path_reuse_authorized": false, + "materializer_invocation_authorized": false, + "cmake_invocation_authorized": false, + "sdl_build_authorized": false, + "retroarch_target_build_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false + }, + "decision": { + "cleanup_complete": true, + "source_change_allowed": true, + "materialization_allowed": false, + "device_action_allowed": false, + "next_step": "IMPLEMENT_AND_HOST_TEST_EXACT_PATCH_TARGET_HASH_VERIFIER" + } +} diff --git a/manifests/retroarch/phase-1.0cb-hash-verified-sdl-materializer-one-shot-gate.json b/manifests/retroarch/phase-1.0cb-hash-verified-sdl-materializer-one-shot-gate.json new file mode 100644 index 0000000..85d0051 --- /dev/null +++ b/manifests/retroarch/phase-1.0cb-hash-verified-sdl-materializer-one-shot-gate.json @@ -0,0 +1,55 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0CB_HASH_VERIFIED_SDL_MATERIALIZER_ONE_SHOT_GATE", + "status": "HASH_VERIFIER_BOUND_ONE_OFFLINE_SDL_MATERIALIZATION_ALLOWED", + "date": "2026-07-30", + "source_bindings": { + "phase10ca_manifest_sha256": "960b248cd57c1b82c1c81f02376ce37a49e0ca7b871ab63ca33326302608d8e3", + "retroarch_commit": "a4b1fb0ca125b23c6a22069f2937f69b055b6cab", + "request_compiler_sha256": "7bc42f847143abcd2a3056d33bc3cb6ef436d9f7040ecc8aff4789c535bcf6af", + "executor_sha256": "d1ec31513d0b4dfc82b75584ce087caa4c36f587f425f6bd89eb62fab3fde24b", + "real_composition_sha256": "3eabc0eaee65fc49de1b5b8859e040b5181d03c94007f90dea546b89bac9ccce", + "one_shot_runner_sha256": "5f1df086c5a6936223c95d05067f9c569199cf1e33f7409e799ab45baf3d12d1", + "entrypoint_sha256": "01460c2c4c3a37251b463238df5a1c746d51f069dd90f2061d486b81cbf53887" + }, + "preflight": { + "retroarch_worktree_clean": true, + "stage_absent": true, + "build_absent": true, + "archive_absent": true, + "phase_b_host_tests_passed": 31, + "relevant_static_validators_passed": 10 + }, + "one_shot": { + "attempts_authorized": 1, + "working_directory": "/mnt/c/Projects/chimera-retroarch-phase10az", + "argv": [ + "/usr/bin/python3.14", + "tools/phase10bq_sdl_materializer_entrypoint.py" + ], + "environment": { + "PYTHONDONTWRITEBYTECODE": "1" + }, + "automatic_retry": false, + "cleanup_on_failure_authorized": false + }, + "authorizations": { + "output_path_reuse_authorized": true, + "materializer_invocation_authorized": true, + "detached_sdl_stage_authorized": true, + "offline_patch_application_authorized": true, + "cmake_invocation_authorized": true, + "sdl_build_authorized": true, + "retroarch_target_build_authorized": false, + "network_access_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false + }, + "decision": { + "offline_sdl_materialization_allowed_once": true, + "retroarch_target_build_allowed": false, + "device_action_allowed": false, + "next_step": "INVOKE_HASH_VERIFIED_SCRIPT_ENTRYPOINT_ONCE_AND_RECORD_RESULT" + } +} diff --git a/manifests/retroarch/phase-1.0cc-cmake-timeout-and-exact-output-cleanup-gate.json b/manifests/retroarch/phase-1.0cc-cmake-timeout-and-exact-output-cleanup-gate.json new file mode 100644 index 0000000..a46f986 --- /dev/null +++ b/manifests/retroarch/phase-1.0cc-cmake-timeout-and-exact-output-cleanup-gate.json @@ -0,0 +1,67 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0CC_CMAKE_TIMEOUT_AND_EXACT_OUTPUT_CLEANUP_GATE", + "status": "CB_CONSUMED_CONFIGURE_TIMEOUT_EXACT_DERIVED_OUTPUT_CLEANUP_ALLOWED", + "date": "2026-07-30", + "source_bindings": { + "phase10cb_manifest_sha256": "e4894b904e01eb2c3b191d1696679f5d02e5c7026be3c37eaf218ff0296c13bb", + "retroarch_commit": "a4b1fb0ca125b23c6a22069f2937f69b055b6cab" + }, + "consumed_attempt": { + "attempts": 1, + "exit_code": 1, + "completed_operations": [ + "verify_sdl_commit", + "verify_sdl_clean", + "create_detached_stage", + "smoke_patch_check", + "smoke_patch_apply", + "early_diag_patch_check", + "early_diag_patch_apply", + "verify_patch_target_hashes" + ], + "failed_operation": "cmake_configure_sdl_only", + "failure": "process timeout: cmake_configure_sdl_only", + "timeout_seconds": 120, + "process_group_terminated": true, + "stage_exists_after": true, + "build_exists_after": true, + "cmake_cache_absent_after": true, + "archive_absent_after": true, + "configure_log_length": 453432, + "configure_log_sha256": "10eafca229edfad3dce34262f4ad8cae1ad0c7010a36778ac87c4051ca678088", + "c_compiler_identification_succeeded": true, + "cxx_compiler_identification_succeeded": true, + "sdl_build_invoked": false, + "retroarch_build_invoked": false, + "device_action_invoked": false + }, + "cleanup": { + "attempts_authorized": 1, + "stage": "/mnt/c/Projects/chimera-retroarch-phase10az/build/phase10bd/sdl-source", + "build": "C:/Projects/chimera-retroarch-phase10az/build/phase10bd/sdl-build", + "stage_git_worktree_remove_force": true, + "build_recursive_remove_after_absolute_containment_check": true, + "other_worktree_cleanup_authorized": false, + "automatic_retry": false + }, + "authorizations": { + "exact_derived_output_cleanup_authorized": true, + "request_graph_source_change_authorized": false, + "output_path_reuse_authorized": false, + "materializer_invocation_authorized": false, + "cmake_invocation_authorized": false, + "sdl_build_authorized": false, + "retroarch_target_build_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false + }, + "decision": { + "exact_cleanup_allowed_once": true, + "source_change_allowed": false, + "materialization_allowed": false, + "device_action_allowed": false, + "next_step": "REMOVE_EXACT_STAGE_AND_PARTIAL_BUILD_ONCE_THEN_VERIFY_ABSENCE" + } +} diff --git a/manifests/retroarch/phase-1.0cd-cleanup-result-and-configure-timeout-correction-gate.json b/manifests/retroarch/phase-1.0cd-cleanup-result-and-configure-timeout-correction-gate.json new file mode 100644 index 0000000..1b786fa --- /dev/null +++ b/manifests/retroarch/phase-1.0cd-cleanup-result-and-configure-timeout-correction-gate.json @@ -0,0 +1,46 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0CD_CLEANUP_RESULT_AND_CONFIGURE_TIMEOUT_CORRECTION_GATE", + "status": "EXACT_OUTPUT_CLEANUP_PASSED_CONFIGURE_TIMEOUT_CORRECTION_ALLOWED", + "date": "2026-07-30", + "source_bindings": { + "phase10cc_manifest_sha256": "52262a2f86664b1493b8a5c5d738a217a090b39b1f05907106483c18017fa6f4", + "retroarch_commit": "a4b1fb0ca125b23c6a22069f2937f69b055b6cab" + }, + "cleanup_result": { + "attempts": 1, + "exit_code": 0, + "stage_absent_after": true, + "build_absent_after": true, + "worktree_metadata_absent_after": true, + "source_clean_after": true, + "other_worktrees_removed": false + }, + "authorized_source_correction": { + "operation": "cmake_configure_sdl_only", + "old_timeout_seconds": 120, + "new_timeout_seconds": 600, + "argv_change_authorized": false, + "output_limit_change_authorized": false, + "build_timeout_change_authorized": false, + "automatic_retry": false + }, + "authorizations": { + "request_graph_source_change_authorized": true, + "output_path_reuse_authorized": false, + "materializer_invocation_authorized": false, + "cmake_invocation_authorized": false, + "sdl_build_authorized": false, + "retroarch_target_build_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false + }, + "decision": { + "cleanup_complete": true, + "source_change_allowed": true, + "materialization_allowed": false, + "device_action_allowed": false, + "next_step": "IMPLEMENT_AND_HOST_TEST_ONLY_CONFIGURE_TIMEOUT_CORRECTION" + } +} diff --git a/manifests/retroarch/phase-1.0ce-extended-configure-sdl-materializer-one-shot-gate.json b/manifests/retroarch/phase-1.0ce-extended-configure-sdl-materializer-one-shot-gate.json new file mode 100644 index 0000000..95a65ff --- /dev/null +++ b/manifests/retroarch/phase-1.0ce-extended-configure-sdl-materializer-one-shot-gate.json @@ -0,0 +1,48 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0CE_EXTENDED_CONFIGURE_SDL_MATERIALIZER_ONE_SHOT_GATE", + "status": "EXTENDED_CONFIGURE_REQUEST_BOUND_ONE_OFFLINE_MATERIALIZATION_ALLOWED", + "date": "2026-07-30", + "source_bindings": { + "phase10cd_manifest_sha256": "cd491f229682cbdcaa0866f84109bb3a1a831c0634679d02926b4d9e4a9a737b", + "retroarch_commit": "e936a1fc36058a5df40cef1a805e6ae571fc2f4a", + "request_compiler_sha256": "4968b8bf17ea2f8195373d062af7bd0cfedd6414877197966e56f0b82cafc076", + "one_shot_runner_sha256": "5f1df086c5a6936223c95d05067f9c569199cf1e33f7409e799ab45baf3d12d1", + "entrypoint_sha256": "01460c2c4c3a37251b463238df5a1c746d51f069dd90f2061d486b81cbf53887" + }, + "preflight": { + "retroarch_worktree_clean": true, + "stage_absent": true, + "build_absent": true, + "archive_absent": true, + "phase_b_host_tests_passed": 31, + "relevant_static_validators_passed": 10 + }, + "one_shot": { + "attempts_authorized": 1, + "working_directory": "/mnt/c/Projects/chimera-retroarch-phase10az", + "argv": ["/usr/bin/python3.14", "tools/phase10bq_sdl_materializer_entrypoint.py"], + "environment": {"PYTHONDONTWRITEBYTECODE": "1"}, + "configure_timeout_seconds": 600, + "build_timeout_seconds": 600, + "automatic_retry": false, + "cleanup_on_failure_authorized": false + }, + "authorizations": { + "output_path_reuse_authorized": true, + "materializer_invocation_authorized": true, + "cmake_invocation_authorized": true, + "sdl_build_authorized": true, + "retroarch_target_build_authorized": false, + "network_access_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false + }, + "decision": { + "offline_sdl_materialization_allowed_once": true, + "retroarch_target_build_allowed": false, + "device_action_allowed": false, + "next_step": "INVOKE_EXTENDED_CONFIGURE_ENTRYPOINT_ONCE_AND_RECORD_RESULT" + } +} diff --git a/manifests/retroarch/phase-1.0cf-repeated-configure-timeout-and-cleanup-gate.json b/manifests/retroarch/phase-1.0cf-repeated-configure-timeout-and-cleanup-gate.json new file mode 100644 index 0000000..4f290e6 --- /dev/null +++ b/manifests/retroarch/phase-1.0cf-repeated-configure-timeout-and-cleanup-gate.json @@ -0,0 +1,52 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0CF_REPEATED_CONFIGURE_TIMEOUT_AND_CLEANUP_GATE", + "status": "CE_CONSUMED_MOUNTED_BUILDROOT_UNSUITABLE_EXACT_CLEANUP_ALLOWED", + "date": "2026-07-30", + "source_bindings": { + "phase10ce_manifest_sha256": "9ef6f65bd4641f6b5d1e2e5facd7ea5642d51b7da7b208be99f9df30ed0e0187", + "retroarch_commit": "e936a1fc36058a5df40cef1a805e6ae571fc2f4a" + }, + "consumed_attempt": { + "attempts": 1, + "failed_operation": "cmake_configure_sdl_only", + "failure": "process timeout: cmake_configure_sdl_only", + "timeout_seconds": 600, + "process_group_terminated": true, + "all_preconfigure_operations_completed": true, + "stage_exists_after": true, + "build_exists_after": true, + "cmake_cache_absent_after": true, + "archive_absent_after": true, + "configure_log_length": 595802, + "configure_log_sha256": "fccd17edf07a98209068fcccc8bdf72f60a766803b39e1f54495ba3b35ed2546", + "successful_try_compile_observed": true, + "sdl_build_invoked": false, + "device_action_invoked": false + }, + "cleanup": { + "attempts_authorized": 1, + "stage": "/mnt/c/Projects/chimera-retroarch-phase10az/build/phase10bd/sdl-source", + "build": "C:/Projects/chimera-retroarch-phase10az/build/phase10bd/sdl-build", + "stage_git_worktree_remove_force": true, + "build_recursive_remove_after_absolute_containment_check": true, + "automatic_retry": false + }, + "authorizations": { + "exact_derived_output_cleanup_authorized": true, + "native_wsl_source_design_authorized": false, + "materializer_invocation_authorized": false, + "cmake_invocation_authorized": false, + "sdl_build_authorized": false, + "retroarch_target_build_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false + }, + "decision": { + "mounted_buildroot_suitable": false, + "exact_cleanup_allowed_once": true, + "materialization_allowed": false, + "next_step": "CLEAN_EXACT_OUTPUTS_THEN_DESIGN_NATIVE_WSL_MATERIALIZER_OFFLINE" + } +} diff --git a/manifests/retroarch/phase-1.0cg-native-wsl-sdl-materializer-design-gate.json b/manifests/retroarch/phase-1.0cg-native-wsl-sdl-materializer-design-gate.json new file mode 100644 index 0000000..9750a37 --- /dev/null +++ b/manifests/retroarch/phase-1.0cg-native-wsl-sdl-materializer-design-gate.json @@ -0,0 +1,53 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0CG_NATIVE_WSL_SDL_MATERIALIZER_DESIGN_GATE", + "status": "MOUNTED_OUTPUTS_CLEAN_NATIVE_WSL_SOURCE_DESIGN_ALLOWED", + "date": "2026-07-30", + "source_bindings": { + "phase10cf_manifest_sha256": "1ba00ed257d680ab229fa39ca11b6e7b4070c3ce52aab347e039607ede51ebf9", + "retroarch_commit": "e936a1fc36058a5df40cef1a805e6ae571fc2f4a" + }, + "cleanup_result": { + "stage_absent": true, + "build_absent": true, + "worktree_metadata_absent": true + }, + "native_root_observation": { + "path": "/tmp/chimera-gfx-phase10ch", + "parent_filesystem": "tmpfs", + "available_bytes": 8226119680, + "root_absent": true, + "observation_read_only": true + }, + "design_contract": { + "native_root": "/tmp/chimera-gfx-phase10ch", + "native_stage": "/tmp/chimera-gfx-phase10ch/build/phase10bd/sdl-source", + "native_build": "/tmp/chimera-gfx-phase10ch/build/phase10bd/sdl-build", + "native_archive": "/tmp/chimera-gfx-phase10ch/build/phase10bd/sdl-build/libSDL2.a", + "mounted_source_retained": true, + "mounted_sdk_retained": true, + "mounted_patches_retained": true, + "archive_export_included": false, + "retroarch_build_included": false, + "device_action_included": false + }, + "authorizations": { + "native_wsl_source_design_authorized": true, + "fake_host_tests_authorized": true, + "native_root_creation_authorized": false, + "materializer_invocation_authorized": false, + "cmake_invocation_authorized": false, + "sdl_build_authorized": false, + "archive_export_authorized": false, + "retroarch_target_build_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false + }, + "decision": { + "source_design_allowed": true, + "native_materialization_allowed": false, + "device_action_allowed": false, + "next_step": "IMPLEMENT_AND_FAKE_TEST_EXACT_NATIVE_WSL_OUTPUT_ROOT" + } +} diff --git a/manifests/retroarch/phase-1.0ch-native-wsl-sdl-materializer-one-shot-gate.json b/manifests/retroarch/phase-1.0ch-native-wsl-sdl-materializer-one-shot-gate.json new file mode 100644 index 0000000..b68de38 --- /dev/null +++ b/manifests/retroarch/phase-1.0ch-native-wsl-sdl-materializer-one-shot-gate.json @@ -0,0 +1,49 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0CH_NATIVE_WSL_SDL_MATERIALIZER_ONE_SHOT_GATE", + "status": "NATIVE_WSL_REQUEST_GRAPH_BOUND_ONE_MATERIALIZATION_ALLOWED", + "date": "2026-07-30", + "source_bindings": { + "phase10cg_manifest_sha256": "1be804144320648ba42bc2fecb8e80dced8c3422dca40f350b5077989fdea9cf", + "retroarch_commit": "ab419bb85d4ce54aa0d9f59671498957615d1811", + "request_compiler_sha256": "13971598c32a7b54609101d53538ad1a0a72d1ee5a9dbc0747b7caa9ac4a5871", + "real_facade_sha256": "c0c70da7ed48ef3c657bf5406b6ed17b52b2091ab36fb7c766fac2c771477452", + "real_composition_sha256": "8d673f9b41e11f6b669d8587f13980cc621977ede7a5f6d11fd65b9f2d330416", + "one_shot_runner_sha256": "e9c8a6efef264387a4a0590dcf43430766ebcd3ca12ef13189ffd98537369d48", + "entrypoint_sha256": "01460c2c4c3a37251b463238df5a1c746d51f069dd90f2061d486b81cbf53887" + }, + "preflight": { + "native_root": "/tmp/chimera-gfx-phase10ch", + "native_root_absent": true, + "retroarch_worktree_clean": true, + "host_tests_passed": 31, + "static_validators_passed": 10, + "wsl_import_check_passed": true + }, + "one_shot": { + "attempts_authorized": 1, + "working_directory": "/mnt/c/Projects/chimera-retroarch-phase10az", + "argv": ["/usr/bin/python3.14", "tools/phase10bq_sdl_materializer_entrypoint.py"], + "environment": {"PYTHONDONTWRITEBYTECODE": "1"}, + "automatic_retry": false, + "cleanup_on_failure_authorized": false + }, + "authorizations": { + "native_root_creation_authorized": true, + "materializer_invocation_authorized": true, + "cmake_invocation_authorized": true, + "sdl_build_authorized": true, + "archive_export_authorized": false, + "retroarch_target_build_authorized": false, + "network_access_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false + }, + "decision": { + "native_materialization_allowed_once": true, + "archive_export_allowed": false, + "device_action_allowed": false, + "next_step": "INVOKE_NATIVE_WSL_ENTRYPOINT_ONCE_AND_AUDIT_RESULT" + } +} diff --git a/manifests/retroarch/phase-1.0ci-native-sdk-bottleneck-and-cleanup-gate.json b/manifests/retroarch/phase-1.0ci-native-sdk-bottleneck-and-cleanup-gate.json new file mode 100644 index 0000000..7d8a34a --- /dev/null +++ b/manifests/retroarch/phase-1.0ci-native-sdk-bottleneck-and-cleanup-gate.json @@ -0,0 +1,53 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0CI_NATIVE_SDK_BOTTLENECK_AND_CLEANUP_GATE", + "status": "CH_CONSUMED_MOUNTED_SDK_PROBE_BOTTLENECK_EXACT_NATIVE_CLEANUP_ALLOWED", + "date": "2026-07-30", + "source_bindings": { + "phase10ch_manifest_sha256": "9b03963689440e30d73f847abaa55d137fca8366e5f07ec2ef57831ba8d11e3a", + "retroarch_commit": "ab419bb85d4ce54aa0d9f59671498957615d1811" + }, + "consumed_attempt": { + "attempts": 1, + "failed_operation": "cmake_configure_sdl_only", + "timeout_seconds": 600, + "all_preconfigure_operations_completed": true, + "native_stage_exists": true, + "native_build_exists": true, + "cmake_cache_absent": true, + "archive_absent": true, + "configure_log_length": 940713, + "configure_log_sha256": "4cb1b862c5b70d41df2af1569eda9369db17e93bce583fb3259a99221535b50f", + "successful_symbol_try_compile_observed": true, + "sdl_build_invoked": false, + "device_action_invoked": false + }, + "sdk_observation": { + "mounted_path": "/mnt/c/Projects/chimera-gfx/work/toolchains/ps5-payload-sdk-v0.41", + "total_bytes": 29406271, + "observation_read_only": true + }, + "cleanup": { + "attempts_authorized": 1, + "exact_root": "/tmp/chimera-gfx-phase10ch", + "git_worktree_remove_first": true, + "recursive_root_remove_after_containment_check": true, + "automatic_retry": false + }, + "authorizations": { + "exact_native_root_cleanup_authorized": true, + "native_sdk_stage_authorized": false, + "materializer_invocation_authorized": false, + "cmake_invocation_authorized": false, + "sdl_build_authorized": false, + "retroarch_target_build_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false + }, + "decision": { + "timeout_increase_allowed": false, + "exact_cleanup_allowed_once": true, + "next_step": "CLEAN_EXACT_NATIVE_ROOT_THEN_DESIGN_HASH_BOUND_NATIVE_SDK_STAGE" + } +} diff --git a/manifests/retroarch/phase-1.0cj-native-sdk-stage-design-gate.json b/manifests/retroarch/phase-1.0cj-native-sdk-stage-design-gate.json new file mode 100644 index 0000000..21a0486 --- /dev/null +++ b/manifests/retroarch/phase-1.0cj-native-sdk-stage-design-gate.json @@ -0,0 +1,52 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0CJ_NATIVE_SDK_STAGE_DESIGN_GATE", + "status": "NATIVE_ROOT_CLEAN_FULL_SDK_TREE_BOUND_STAGE_DESIGN_ALLOWED", + "date": "2026-07-30", + "source_bindings": { + "phase10ci_manifest_sha256": "3b469a4363ded25ac8a0055e504e87c2b667a5c035be145e112b01e2ecf5742c", + "retroarch_commit": "ab419bb85d4ce54aa0d9f59671498957615d1811" + }, + "cleanup_result": { + "native_root_absent": true, + "worktree_metadata_absent": true + }, + "sdk_tree": { + "source": "/mnt/c/Projects/chimera-gfx/work/toolchains/ps5-payload-sdk-v0.41", + "regular_files": 2923, + "symbolic_links": 0, + "total_bytes": 29406271, + "normalized_tar_sha256": "7e946dbd7631ed08b030b7c5c8e9d4cf52d0bd8b8d6522db4b4dd0348dbb508f" + }, + "tool_bindings": { + "/usr/bin/cp": "ddffb913956d7f8cbdd7722b8c331cea51e9a13c59e92de8c194ae30e5eb0b1e", + "/usr/bin/tar": "5a97e6f6670ffd3496a26db2acb25efc390480c9f417e67fb939f9b9f7968ed7" + }, + "design_contract": { + "native_sdk": "/tmp/chimera-gfx-phase10ch/sdk", + "copy_exclusive": true, + "copy_preserves_metadata": true, + "verify_complete_tree_before_configure": true, + "configure_uses_native_sdk_only": true, + "archive_export_included": false, + "device_action_included": false + }, + "authorizations": { + "native_sdk_stage_source_design_authorized": true, + "fake_host_tests_authorized": true, + "native_sdk_copy_authorized": false, + "materializer_invocation_authorized": false, + "cmake_invocation_authorized": false, + "sdl_build_authorized": false, + "archive_export_authorized": false, + "retroarch_target_build_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false + }, + "decision": { + "source_design_allowed": true, + "native_sdk_stage_allowed": false, + "next_step": "IMPLEMENT_AND_FAKE_TEST_EXCLUSIVE_NATIVE_SDK_STAGE" + } +} diff --git a/manifests/retroarch/phase-1.0ck-native-sdk-sdl-materializer-one-shot-gate.json b/manifests/retroarch/phase-1.0ck-native-sdk-sdl-materializer-one-shot-gate.json new file mode 100644 index 0000000..0a5d499 --- /dev/null +++ b/manifests/retroarch/phase-1.0ck-native-sdk-sdl-materializer-one-shot-gate.json @@ -0,0 +1,49 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0CK_NATIVE_SDK_SDL_MATERIALIZER_ONE_SHOT_GATE", + "status": "FULL_SDK_TREE_STAGE_BOUND_ONE_NATIVE_MATERIALIZATION_ALLOWED", + "date": "2026-07-30", + "source_bindings": { + "phase10cj_manifest_sha256": "c330690cb3df7c5858a21fd450a3106df281838e81b8abfb6dca234bd1942c9b", + "retroarch_commit": "57f00244a374b771cddf259a70c144a1dff96457", + "request_compiler_sha256": "561a103b6e9a9f86f8e158d474fc8880639a41de99d7dd49a989ec851bc6c341", + "executor_sha256": "dfa5ea3683c532ea397a8de16b6b380b87436bf25322866f62108aa46bec33ec", + "composition_sha256": "4f545686f11ee5d69755b67cb893abc17446ca5402219a273533fb8173ca1de2", + "runner_sha256": "64c432add264500969dd8c882e011a065ebf05693bb7fab7629aa3a3e63d7c74", + "entrypoint_sha256": "01460c2c4c3a37251b463238df5a1c746d51f069dd90f2061d486b81cbf53887" + }, + "preflight": { + "native_root": "/tmp/chimera-gfx-phase10ch", + "native_root_absent": true, + "retroarch_worktree_clean": true, + "host_tests_passed": 31, + "static_validators_passed": 10, + "wsl_contract_check_passed": true + }, + "one_shot": { + "attempts_authorized": 1, + "working_directory": "/mnt/c/Projects/chimera-retroarch-phase10az", + "argv": ["/usr/bin/python3.14", "tools/phase10bq_sdl_materializer_entrypoint.py"], + "environment": {"PYTHONDONTWRITEBYTECODE": "1"}, + "automatic_retry": false, + "cleanup_on_failure_authorized": false + }, + "authorizations": { + "native_root_creation_authorized": true, + "native_sdk_stage_authorized": true, + "materializer_invocation_authorized": true, + "cmake_invocation_authorized": true, + "sdl_build_authorized": true, + "archive_export_authorized": false, + "retroarch_target_build_authorized": false, + "network_access_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false + }, + "decision": { + "native_materialization_allowed_once": true, + "archive_export_allowed": false, + "next_step": "INVOKE_NATIVE_SDK_ENTRYPOINT_ONCE_AND_AUDIT_RESULT" + } +} diff --git a/manifests/retroarch/phase-1.0cl-native-sdk-configure-progress-and-cleanup-gate.json b/manifests/retroarch/phase-1.0cl-native-sdk-configure-progress-and-cleanup-gate.json new file mode 100644 index 0000000..6d394e4 --- /dev/null +++ b/manifests/retroarch/phase-1.0cl-native-sdk-configure-progress-and-cleanup-gate.json @@ -0,0 +1,49 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0CL_NATIVE_SDK_CONFIGURE_PROGRESS_AND_CLEANUP_GATE", + "status": "CK_CONSUMED_96_SUCCESSFUL_CHECKS_EXACT_NATIVE_CLEANUP_ALLOWED", + "date": "2026-07-30", + "source_bindings": { + "phase10ck_manifest_sha256": "667807cb1ebb2f18779d41978abe02ba1dc60185e0f7554d37df8b5584de8b1e", + "retroarch_commit": "57f00244a374b771cddf259a70c144a1dff96457" + }, + "consumed_attempt": { + "failed_operation": "cmake_configure_sdl_only", + "timeout_seconds": 600, + "all_preconfigure_operations_completed": true, + "completed_cmake_checks": 96, + "last_reached_check": "Looking for strcasestr", + "configure_log_length": 783668, + "configure_log_sha256": "6754f6c242bd4602b31626292eacd04daecb8232bc1d8cb5ef6dd4f48bef186e", + "native_compiler_used": true, + "native_sysroot_used": true, + "cmake_cache_absent": true, + "archive_absent": true, + "sdl_build_invoked": false, + "device_action_invoked": false + }, + "cleanup": { + "attempts_authorized": 1, + "exact_root": "/tmp/chimera-gfx-phase10ch", + "git_worktree_remove_first": true, + "recursive_root_remove_after_containment_check": true, + "automatic_retry": false + }, + "authorizations": { + "exact_native_root_cleanup_authorized": true, + "configure_timeout_change_authorized": false, + "materializer_invocation_authorized": false, + "cmake_invocation_authorized": false, + "sdl_build_authorized": false, + "archive_export_authorized": false, + "retroarch_target_build_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false + }, + "decision": { + "exact_cleanup_allowed_once": true, + "materialization_allowed": false, + "next_step": "CLEAN_EXACT_NATIVE_ROOT_THEN_GATE_MEASURED_1800_SECOND_CONFIGURE_BOUND" + } +} diff --git a/manifests/retroarch/phase-1.0cm-measured-configure-timeout-correction-gate.json b/manifests/retroarch/phase-1.0cm-measured-configure-timeout-correction-gate.json new file mode 100644 index 0000000..a6121b0 --- /dev/null +++ b/manifests/retroarch/phase-1.0cm-measured-configure-timeout-correction-gate.json @@ -0,0 +1,46 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0CM_MEASURED_CONFIGURE_TIMEOUT_CORRECTION_GATE", + "status": "NATIVE_ROOT_CLEAN_MEASURED_1800_SECOND_SOURCE_CORRECTION_ALLOWED", + "date": "2026-07-30", + "source_bindings": { + "phase10cl_manifest_sha256": "0a94ffbb3554ed55d63d5e138d03479d9bbdaf14948051bf4bd7db7f07b6136f", + "retroarch_commit": "57f00244a374b771cddf259a70c144a1dff96457" + }, + "cleanup_result": { + "native_root_absent": true, + "worktree_metadata_absent": true + }, + "measurement": { + "completed_checks": 96, + "elapsed_seconds": 600, + "selected_bound_seconds": 1800, + "timeout_increase_factor": 3 + }, + "authorized_source_correction": { + "operation": "cmake_configure_sdl_only", + "old_timeout_seconds": 600, + "new_timeout_seconds": 1800, + "argv_change_authorized": false, + "output_limit_change_authorized": false, + "checkout_timeout_change_authorized": false, + "build_timeout_change_authorized": false, + "automatic_retry": false + }, + "authorizations": { + "request_graph_source_change_authorized": true, + "materializer_invocation_authorized": false, + "cmake_invocation_authorized": false, + "sdl_build_authorized": false, + "archive_export_authorized": false, + "retroarch_target_build_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false + }, + "decision": { + "source_change_allowed": true, + "materialization_allowed": false, + "next_step": "IMPLEMENT_AND_TEST_ONLY_MEASURED_CONFIGURE_TIMEOUT_CHANGE" + } +} diff --git a/manifests/retroarch/phase-1.0cn-measured-native-sdl-materializer-one-shot-gate.json b/manifests/retroarch/phase-1.0cn-measured-native-sdl-materializer-one-shot-gate.json new file mode 100644 index 0000000..c609c03 --- /dev/null +++ b/manifests/retroarch/phase-1.0cn-measured-native-sdl-materializer-one-shot-gate.json @@ -0,0 +1,46 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0CN_MEASURED_NATIVE_SDL_MATERIALIZER_ONE_SHOT_GATE", + "status": "MEASURED_1800_SECOND_NATIVE_REQUEST_BOUND_ONE_BUILD_ALLOWED", + "date": "2026-07-30", + "source_bindings": { + "phase10cm_manifest_sha256": "861fc229b4066089610ee7e1982512a775f391772bae24fd35603743cc6ba996", + "retroarch_commit": "140dcc917dcbb6a49082ac03a388b79d73252239", + "request_compiler_sha256": "29346df0342a10020e5b0980a1d417ccdb703ffa1c97a2893b6c5ba4c40d1aac", + "runner_sha256": "64c432add264500969dd8c882e011a065ebf05693bb7fab7629aa3a3e63d7c74", + "entrypoint_sha256": "01460c2c4c3a37251b463238df5a1c746d51f069dd90f2061d486b81cbf53887" + }, + "preflight": { + "native_root_absent": true, + "retroarch_worktree_clean": true, + "host_tests_passed": 31, + "static_validators_passed": 10 + }, + "one_shot": { + "attempts_authorized": 1, + "working_directory": "/mnt/c/Projects/chimera-retroarch-phase10az", + "argv": ["/usr/bin/python3.14", "tools/phase10bq_sdl_materializer_entrypoint.py"], + "environment": {"PYTHONDONTWRITEBYTECODE": "1"}, + "configure_timeout_seconds": 1800, + "build_timeout_seconds": 600, + "automatic_retry": false, + "cleanup_on_failure_authorized": false + }, + "authorizations": { + "native_sdk_stage_authorized": true, + "materializer_invocation_authorized": true, + "cmake_invocation_authorized": true, + "sdl_build_authorized": true, + "archive_export_authorized": false, + "retroarch_target_build_authorized": false, + "network_access_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false + }, + "decision": { + "native_materialization_allowed_once": true, + "archive_export_allowed": false, + "next_step": "INVOKE_MEASURED_NATIVE_ENTRYPOINT_ONCE_AND_AUDIT_RESULT" + } +} diff --git a/manifests/retroarch/phase-1.0co-configure-success-build-output-limit-cleanup-gate.json b/manifests/retroarch/phase-1.0co-configure-success-build-output-limit-cleanup-gate.json new file mode 100644 index 0000000..2613219 --- /dev/null +++ b/manifests/retroarch/phase-1.0co-configure-success-build-output-limit-cleanup-gate.json @@ -0,0 +1,45 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0CO_CONFIGURE_SUCCESS_BUILD_OUTPUT_LIMIT_CLEANUP_GATE", + "status": "CN_CONFIGURE_PASSED_BUILD_OUTPUT_LIMIT_STOP_EXACT_CLEANUP_ALLOWED", + "date": "2026-07-30", + "source_bindings": { + "phase10cn_manifest_sha256": "b6535723c1f129366c71324a9d38e76940977816e1410d388a478bdbc175bac3", + "retroarch_commit": "140dcc917dcbb6a49082ac03a388b79d73252239" + }, + "consumed_attempt": { + "completed_through": "cmake_configure_sdl_only", + "failed_operation": "cmake_build_sdl2_static", + "failure": "output limit exceeded: cmake_build_sdl2_static", + "output_limit": 65536, + "process_group_terminated": true, + "cmake_cache_length": 33343, + "cmake_cache_sha256": "7669746db710fa4d9502e550b31b65c7db7539769aa512cde9aa33212c99230c", + "object_files_after": 46, + "archive_absent": true, + "device_action_invoked": false + }, + "cleanup": { + "attempts_authorized": 1, + "exact_root": "/tmp/chimera-gfx-phase10ch", + "git_worktree_remove_first": true, + "recursive_root_remove_after_containment_check": true, + "automatic_retry": false + }, + "authorizations": { + "exact_native_root_cleanup_authorized": true, + "build_output_limit_change_authorized": false, + "materializer_invocation_authorized": false, + "sdl_build_authorized": false, + "archive_export_authorized": false, + "retroarch_target_build_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false + }, + "decision": { + "configure_proven": true, + "exact_cleanup_allowed_once": true, + "next_step": "CLEAN_EXACT_ROOT_THEN_GATE_BUILD_OUTPUT_LIMIT_CORRECTION" + } +} diff --git a/manifests/retroarch/phase-1.0cp-build-output-limit-correction-gate.json b/manifests/retroarch/phase-1.0cp-build-output-limit-correction-gate.json new file mode 100644 index 0000000..746fd54 --- /dev/null +++ b/manifests/retroarch/phase-1.0cp-build-output-limit-correction-gate.json @@ -0,0 +1,30 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0CP_BUILD_OUTPUT_LIMIT_CORRECTION_GATE", + "status": "NATIVE_ROOT_CLEAN_BUILD_OUTPUT_LIMIT_SOURCE_CORRECTION_ALLOWED", + "date": "2026-07-30", + "source_bindings": { + "phase10co_manifest_sha256": "3dba9f8b230e034f16c11ba22b4aeffe2f8dabafc4233a4218da8b577b5479e3", + "retroarch_commit": "140dcc917dcbb6a49082ac03a388b79d73252239" + }, + "cleanup_result": {"native_root_absent": true, "worktree_metadata_absent": true}, + "authorized_source_correction": { + "operation": "cmake_build_sdl2_static", + "old_output_limit": 65536, + "new_output_limit": 1048576, + "argv_change_authorized": false, + "timeout_change_authorized": false, + "automatic_retry": false + }, + "authorizations": { + "request_graph_source_change_authorized": true, + "materializer_invocation_authorized": false, + "sdl_build_authorized": false, + "archive_export_authorized": false, + "retroarch_target_build_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false + }, + "decision": {"source_change_allowed": true, "materialization_allowed": false, "next_step": "IMPLEMENT_AND_TEST_ONLY_BUILD_OUTPUT_LIMIT_CHANGE"} +} diff --git a/manifests/retroarch/phase-1.0cq-full-output-native-sdl-one-shot-gate.json b/manifests/retroarch/phase-1.0cq-full-output-native-sdl-one-shot-gate.json new file mode 100644 index 0000000..c2cab3d --- /dev/null +++ b/manifests/retroarch/phase-1.0cq-full-output-native-sdl-one-shot-gate.json @@ -0,0 +1,11 @@ +{ + "schema_version":1, + "phase":"PHASE_1_0CQ_FULL_OUTPUT_NATIVE_SDL_ONE_SHOT_GATE", + "status":"CONFIGURE_PROVEN_FULL_BUILD_OUTPUT_BOUND_ONE_BUILD_ALLOWED", + "date":"2026-07-30", + "source_bindings":{"phase10cp_manifest_sha256":"3f3af1e73ecb060d2869fdaebb18f1055a61183037542e3dc202bcadf501094f","retroarch_commit":"ee705d2ac4291e857a4376eee23d1af0986d677a","request_compiler_sha256":"a50cc61cd5185e0961211ee84d039847423a75a959688c5f31f841ce50f9a0ee"}, + "preflight":{"native_root_absent":true,"retroarch_worktree_clean":true,"host_tests_passed":31,"static_validators_passed":10}, + "one_shot":{"attempts_authorized":1,"working_directory":"/mnt/c/Projects/chimera-retroarch-phase10az","argv":["/usr/bin/python3.14","tools/phase10bq_sdl_materializer_entrypoint.py"],"configure_timeout_seconds":1800,"build_timeout_seconds":600,"build_output_limit":1048576,"automatic_retry":false,"cleanup_on_failure_authorized":false}, + "authorizations":{"native_sdk_stage_authorized":true,"materializer_invocation_authorized":true,"cmake_invocation_authorized":true,"sdl_build_authorized":true,"archive_export_authorized":false,"retroarch_target_build_authorized":false,"network_access_authorized":false,"ps5_connection_authorized":false,"device_transfer_authorized":false,"device_execution_authorized":false}, + "decision":{"native_materialization_allowed_once":true,"archive_export_allowed":false,"next_step":"INVOKE_FULL_OUTPUT_NATIVE_BUILD_ONCE_AND_AUDIT_ARCHIVE"} +} diff --git a/manifests/retroarch/phase-1.0cr-audited-sdl-archive-export-gate.json b/manifests/retroarch/phase-1.0cr-audited-sdl-archive-export-gate.json new file mode 100644 index 0000000..99eecec --- /dev/null +++ b/manifests/retroarch/phase-1.0cr-audited-sdl-archive-export-gate.json @@ -0,0 +1,12 @@ +{ + "schema_version":1, + "phase":"PHASE_1_0CR_AUDITED_SDL_ARCHIVE_EXPORT_GATE", + "status":"NATIVE_SDL_ARCHIVE_AUDITED_ONE_EXCLUSIVE_EXPORT_ALLOWED", + "date":"2026-07-30", + "source_bindings":{"phase10cq_manifest_sha256":"8d120585502993c93d5475ce31b1f1b48461183037542e3dc202bcadf501094f","retroarch_commit":"a28e171018b9d1789eb640d91400d76a20f74864","exporter_sha256":"9bcd218603683c291a45fb8be7d3b98df39508af797700556c2b760d23631f29","exporter_tests_sha256":"8404908a15d96513f145e515f03edbe22f7db8992f0d7905546db3dada635a5f"}, + "completed_build":{"operation_count":15,"all_operations_completed":true,"device_action_invoked":false,"retroarch_build_invoked":false}, + "archive":{"source":"/tmp/chimera-gfx-phase10ch/build/phase10bd/sdl-build/libSDL2.a","destination":"/mnt/c/Projects/chimera-retroarch-phase10az/build/phase10bd/sdl-build/libSDL2.a","size":2412290,"sha256":"4ced0a042c0b8a70424e42a14cfe9b2c4d30a4cd73b661dc1fcf8ad967a7f7e1","format":"current ar archive","member_count":143,"ps5_member_count":5,"expected_undefined_chimera_hooks":26,"destination_absent_at_gate":true}, + "one_shot":{"attempts_authorized":1,"argv":["/usr/bin/python3.14","tools/phase10cr_export_sdl_archive.py"],"working_directory":"/mnt/c/Projects/chimera-retroarch-phase10az","exclusive_leaf_create":true,"file_fsync":true,"reopen_rehash":true,"source_cleanup_authorized":false,"automatic_retry":false}, + "authorizations":{"archive_export_authorized":true,"retroarch_target_build_authorized":false,"network_access_authorized":false,"ps5_connection_authorized":false,"device_transfer_authorized":false,"device_execution_authorized":false}, + "decision":{"archive_export_allowed_once":true,"retroarch_target_build_allowed":false,"next_step":"EXPORT_EXACT_ARCHIVE_ONCE_AND_VERIFY_DESTINATION"} +} diff --git a/manifests/retroarch/phase-1.0cs-export-parent-correction-gate.json b/manifests/retroarch/phase-1.0cs-export-parent-correction-gate.json new file mode 100644 index 0000000..23fdc32 --- /dev/null +++ b/manifests/retroarch/phase-1.0cs-export-parent-correction-gate.json @@ -0,0 +1,12 @@ +{ + "schema_version":1, + "phase":"PHASE_1_0CS_EXPORT_PARENT_CORRECTION_GATE", + "status":"CR_REJECTED_PRE_MUTATION_EXACT_PARENT_AND_ONE_EXPORT_ALLOWED", + "date":"2026-07-30", + "source_bindings":{"phase10cr_manifest_sha256":"b2206270e5004a9588b9da3b5210dd8df076bee2beefee349075f491e468bb98","retroarch_commit":"92fedc571d200457fd3553cff7cd2be017f16aa1","exporter_sha256":"b74ac356eedca1669f5d021eeeb14c408b68586cd7b11925f73cf59d0d6169b1","exporter_tests_sha256":"0d2914cce3f05f1a26f21c361c96cca83f7a34129e949b341927580892d4c51f"}, + "consumed_attempt":{"phase":"PHASE_1_0CR_AUDITED_SDL_ARCHIVE_EXPORT_GATE","classification":"REJECTED_BEFORE_DESTINATION_MUTATION","reason":"DESTINATION_PARENT_ABSENT","destination_leaf_created":false,"retry_performed":false}, + "correction":{"existing_grandparent":"/mnt/c/Projects/chimera-retroarch-phase10az/build/phase10bd","exact_parent_to_create":"/mnt/c/Projects/chimera-retroarch-phase10az/build/phase10bd/sdl-build","destination":"/mnt/c/Projects/chimera-retroarch-phase10az/build/phase10bd/sdl-build/libSDL2.a","parent_creation_bounded":true}, + "one_shot":{"attempts_authorized":1,"argv":["/usr/bin/python3.14","tools/phase10cr_export_sdl_archive.py"],"working_directory":"/mnt/c/Projects/chimera-retroarch-phase10az","exclusive_leaf_create":true,"file_fsync":true,"reopen_rehash":true,"automatic_retry":false}, + "authorizations":{"exact_parent_creation_authorized":true,"archive_export_authorized":true,"source_cleanup_authorized":false,"retroarch_target_build_authorized":false,"network_access_authorized":false,"ps5_connection_authorized":false,"device_transfer_authorized":false,"device_execution_authorized":false}, + "decision":{"corrected_export_allowed_once":true,"next_step":"CREATE_EXACT_PARENT_EXPORT_ONCE_AND_VERIFY"} +} diff --git a/manifests/retroarch/phase-1.0ct-sdl-archive-export-result.json b/manifests/retroarch/phase-1.0ct-sdl-archive-export-result.json new file mode 100644 index 0000000..1e34048 --- /dev/null +++ b/manifests/retroarch/phase-1.0ct-sdl-archive-export-result.json @@ -0,0 +1,11 @@ +{ + "schema_version":1, + "phase":"PHASE_1_0CT_SDL_ARCHIVE_EXPORT_RESULT", + "status":"EXACT_ARCHIVE_EXPORTED_TARGET_BUILD_BLOCKED_MISSING_HEADERS", + "date":"2026-07-30", + "source_bindings":{"phase10cs_manifest_sha256":"260937d3b5626305f256925535c0d4aaa10db4dc32bbf45fcba014eddac9b545","retroarch_commit":"92fedc571d200457fd3553cff7cd2be017f16aa1"}, + "result":{"attempts_consumed":1,"destination":"C:/Projects/chimera-retroarch-phase10az/build/phase10bd/sdl-build/libSDL2.a","size":2412290,"sha256":"4ced0a042c0b8a70424e42a14cfe9b2c4d30a4cd73b661dc1fcf8ad967a7f7e1","exclusive_export_succeeded":true,"source_retained":true}, + "makefile_audit":{"required_archive":"$(PS5_SDL_BUILD)/libSDL2.a","required_include_roots":["$(PS5_SDL_BUILD)/include/SDL2","$(PS5_SDL_BUILD)/include-config-release/SDL2"],"exported_include_roots_present":false,"target_build_blocker":"REVIEWED_SDL_HEADER_TREES_NOT_EXPORTED"}, + "authorizations":{"sdl_header_export_authorized":false,"retroarch_target_build_authorized":false,"network_access_authorized":false,"ps5_connection_authorized":false,"device_transfer_authorized":false,"device_execution_authorized":false}, + "decision":{"target_build_allowed":false,"next_step":"AUDIT_AND_BIND_EXACT_SDL_HEADER_TREES_BEFORE_EXPORT"} +} diff --git a/manifests/retroarch/phase-1.0cu-exact-sdl-header-export-gate.json b/manifests/retroarch/phase-1.0cu-exact-sdl-header-export-gate.json new file mode 100644 index 0000000..d1aae88 --- /dev/null +++ b/manifests/retroarch/phase-1.0cu-exact-sdl-header-export-gate.json @@ -0,0 +1,12 @@ +{ + "schema_version":1, + "phase":"PHASE_1_0CU_EXACT_SDL_HEADER_EXPORT_GATE", + "status":"HEADER_TREES_BOUND_ONE_HOST_EXPORT_ALLOWED", + "date":"2026-07-30", + "source_bindings":{"phase10ct_manifest_sha256":"9e979b08288b77a6f8dc5a32ede41e59eda1e465768f3ec75c16207522bbd4fc","retroarch_commit":"e8419862f1a0a5e7fc780452f539c10ebc1a6438","exporter_sha256":"c4089730114e05353f4f2c0daefc2bc745c4d8c14c458afc136b063d97cb8057","tests_sha256":"f7a202716da420fd0db13071e6b6da555e84c619d62dc9f3fe6755e921123db3"}, + "trees":{"include/SDL2":{"regular_files":78,"total_bytes":2400049,"identity_sha256":"19bb3567da676831afd1b4288245f82d7fda940b2e60b903df7f5c7900188631"},"include-config-release/SDL2":{"regular_files":1,"total_bytes":16566,"identity_sha256":"6c942d65cf297979df0112445652e51b36584890885fba48d02b5af06224605c"}}, + "identity_algorithm":"SHA256_SORTED_UTF8_RELATIVE_PATH_NUL_DECIMAL_SIZE_NUL_FILE_SHA256_LF", + "one_shot":{"attempts_authorized":1,"argv":["/usr/bin/python3.14","tools/phase10cu_sdl_header_export.py"],"working_directory":"/mnt/c/Projects/chimera-retroarch-phase10az","exclusive_stage":true,"exclusive_files":true,"file_fsync":true,"reopen_rehash":true,"automatic_retry":false,"cleanup_on_failure_authorized":false}, + "authorizations":{"sdl_header_export_authorized":true,"retroarch_target_build_authorized":false,"network_access_authorized":false,"ps5_connection_authorized":false,"device_transfer_authorized":false,"device_execution_authorized":false}, + "decision":{"header_export_allowed_once":true,"target_build_allowed":false,"next_step":"EXPORT_EXACT_HEADER_TREES_ONCE_AND_AUDIT_COMPLETE_BUILD_INPUT"} +} diff --git a/manifests/retroarch/phase-1.0cv-launch-canary-offline-build-gate.json b/manifests/retroarch/phase-1.0cv-launch-canary-offline-build-gate.json new file mode 100644 index 0000000..42c2966 --- /dev/null +++ b/manifests/retroarch/phase-1.0cv-launch-canary-offline-build-gate.json @@ -0,0 +1,12 @@ +{ + "schema_version":1, + "phase":"PHASE_1_0CV_LAUNCH_CANARY_OFFLINE_BUILD_GATE", + "status":"COMPLETE_SDL_INPUT_BOUND_ONE_OFFLINE_TARGET_BUILD_ALLOWED", + "date":"2026-07-30", + "source_bindings":{"phase10cu_manifest_sha256":"a1a864ad0a330f7e119464b6149d6f4831c7f6f5d5972f2ee3ff20ac9a93af65","retroarch_commit":"e8419862f1a0a5e7fc780452f539c10ebc1a6438","makefile_sha256":"19bca33655260b679917427ba7c66353d05353a8dd0b2baa8fc6da31dc216eab","linker_script_sha256":"5a01242573bc98340b1de51924c53e1ec1372c23eee62856063431d04d4e5b9f","native_sdk_normalized_tar_sha256":"7e946dbd7631ed08b030b7c5c8e9d4cf52d0bd8b8d6522db4b4dd0348dbb508f"}, + "sdl_inputs":{"archive_sha256":"4ced0a042c0b8a70424e42a14cfe9b2c4d30a4cd73b661dc1fcf8ad967a7f7e1","public_headers_identity_sha256":"19bb3567da676831afd1b4288245f82d7fda940b2e60b903df7f5c7900188631","config_headers_identity_sha256":"6c942d65cf297979df0112445652e51b36584890885fba48d02b5af06224605c"}, + "preflight":{"header_export_succeeded":true,"source_worktree_clean":true,"stale_object_or_dependency_files":0,"target_elf_absent":true,"target_map_absent":true,"make_dry_run_commands":121,"make_dry_run_output_bytes":162345}, + "one_shot":{"attempts_authorized":1,"working_directory":"/mnt/c/Projects/chimera-retroarch-phase10az","argv":["/usr/bin/make","-f","Makefile.ps5","PS5_PROFILE=launch-canary","PS5_PAYLOAD_SDK=/tmp/chimera-gfx-phase10ch/sdk","PS5_SDL_BUILD=build/phase10bd/sdl-build","all"],"timeout_seconds":1800,"output_limit_bytes":2097152,"automatic_retry":false,"clean_authorized":false}, + "authorizations":{"retroarch_target_build_authorized":true,"artifact_execution_eligible":false,"artifact_transfer_eligible":false,"artifact_installation_eligible":false,"network_access_authorized":false,"ps5_connection_authorized":false,"device_transfer_authorized":false,"device_execution_authorized":false}, + "decision":{"offline_build_allowed_once":true,"next_step":"BUILD_LAUNCH_CANARY_ONCE_THEN_AUDIT_ELF_MAP_AND_CALLGRAPH"} +} diff --git a/manifests/retroarch/phase-1.0cw-launch-canary-artifact-audit.json b/manifests/retroarch/phase-1.0cw-launch-canary-artifact-audit.json new file mode 100644 index 0000000..6a70ae2 --- /dev/null +++ b/manifests/retroarch/phase-1.0cw-launch-canary-artifact-audit.json @@ -0,0 +1,12 @@ +{ + "schema_version":1, + "phase":"PHASE_1_0CW_LAUNCH_CANARY_ARTIFACT_AUDIT", + "status":"OFFLINE_ARTIFACT_BUILT_STATIC_AUDIT_PASS_REPRODUCIBILITY_UNPROVEN", + "date":"2026-07-30", + "source_bindings":{"phase10cv_manifest_sha256":"3ff666f2157511d76b525ee271f931bce2d2d35c72d4178dd87096089305b809","retroarch_commit":"e8419862f1a0a5e7fc780452f539c10ebc1a6438"}, + "artifact":{"path":"C:/Projects/chimera-retroarch-phase10az/retroarch_ps5_launch_canary.elf","size":1845240,"sha256":"8dadce9d9faaef21ea129a3d216c768eea9a3ca9bf8ecb8d852e376b58a9bf95","map_size":630127,"map_sha256":"1cabe4d61b2eb2c1844a58883db6b8644c980759690242bef17f92e9c260b3cd","format":"ELF64_X86_64_DYN_PIE","entry":"0xff2f0","stripped":false}, + "static_audit":{"program_headers":4,"load_segments":3,"writable_executable_segment":false,"needed":["libSceVideoOut.sprx","libScePad.sprx","libSceUserService.sprx","libSceSystemService.sprx","libSceAudioOut.sprx","libkernel_web.sprx","libSceLibcInternal.sprx"],"init_array_size":0,"fini_array_size":0,"diagnostic_magic":"CHD10AV1","d14_emitter_present":true,"diagnostic_stream_present":true,"live_write_wrappers":["open","fopen","fwrite"],"socket_constructor_imports":[],"receive_imports":[],"inherited_descriptor_send_import":true}, + "limitations":{"second_independent_build_performed":false,"byte_reproducibility_proven":false,"firmware_behavior_proven":false,"visible_output_proven":false,"cleanup_proven":false}, + "authorizations":{"rebuild_authorized":false,"artifact_execution_eligible":false,"artifact_transfer_eligible":false,"artifact_installation_eligible":false,"network_access_authorized":false,"ps5_connection_authorized":false,"device_transfer_authorized":false,"device_execution_authorized":false}, + "decision":{"artifact_static_audit_passed":true,"hardware_gate_allowed":false,"next_step":"DESIGN_INDEPENDENT_NATIVE_REPRODUCIBILITY_BUILD_WITHOUT_REUSING_OBJECTS"} +} diff --git a/manifests/retroarch/phase-1.0cx-isolated-reproducibility-build-gate.json b/manifests/retroarch/phase-1.0cx-isolated-reproducibility-build-gate.json new file mode 100644 index 0000000..a0bed55 --- /dev/null +++ b/manifests/retroarch/phase-1.0cx-isolated-reproducibility-build-gate.json @@ -0,0 +1,12 @@ +{ + "schema_version":1, + "phase":"PHASE_1_0CX_ISOLATED_REPRODUCIBILITY_BUILD_GATE", + "status":"FIRST_ARTIFACT_AUDITED_ONE_ISOLATED_REBUILD_ALLOWED", + "date":"2026-07-30", + "source_bindings":{"phase10cw_manifest_sha256":"de31fe618c8fda49cb7bb0a1d056a44c0f0745d1249fdfdfe9cdff6d7facd849","builder_commit":"feba9cdf2d73109ed2df025044fafa9f42fb7806","target_source_commit":"e8419862f1a0a5e7fc780452f539c10ebc1a6438","builder_sha256":"41fdb13b4baf6b6a7757ad6e50a87b7c522487387ee9e0018e9aa1618f68db11","builder_tests_sha256":"4b8b796628593ba0dac003405fc12c27f822b53bbb86a81c4331037cf7f76445","make_sha256":"27c9f6d806aee15882b01c2c61848f7aa75caa14bc7b6f608ba422f9e46a7d49","tar_sha256":"5a97e6f6670ffd3496a26db2acb25efc390480c9f417e67fb939f9b9f7968ed7"}, + "expected":{"elf_size":1845240,"elf_sha256":"8dadce9d9faaef21ea129a3d216c768eea9a3ca9bf8ecb8d852e376b58a9bf95","map_size":630127,"map_sha256":"1cabe4d61b2eb2c1844a58883db6b8644c980759690242bef17f92e9c260b3cd"}, + "isolation":{"tracked_source_archive":"C:/Projects/chimera-retroarch-phase10az/build/phase10cx/source.tar","native_root":"/tmp/chimera-gfx-phase10cx","native_repo":"/tmp/chimera-gfx-phase10cx/repo","mounted_stage_absent":true,"native_root_absent":true,"current_objects_reused":false,"source_commit_exact":true}, + "one_shot":{"attempts_authorized":1,"working_directory":"/mnt/c/Projects/chimera-retroarch-phase10az","argv":["/usr/bin/python3.14","tools/phase10cx_repro_build.py"],"timeout_seconds":1800,"output_limit_bytes":2097152,"automatic_retry":false,"cleanup_authorized":false}, + "authorizations":{"tracked_source_archive_authorized":true,"isolated_native_stage_authorized":true,"independent_target_build_authorized":true,"artifact_execution_eligible":false,"artifact_transfer_eligible":false,"artifact_installation_eligible":false,"network_access_authorized":false,"ps5_connection_authorized":false,"device_transfer_authorized":false,"device_execution_authorized":false}, + "decision":{"isolated_rebuild_allowed_once":true,"hardware_gate_allowed":false,"next_step":"RUN_ONCE_AND_REQUIRE_BYTE_EXACT_ELF_AND_MAP"} +} diff --git a/manifests/retroarch/phase-1.0cy-launch-canary-reproducibility-result.json b/manifests/retroarch/phase-1.0cy-launch-canary-reproducibility-result.json new file mode 100644 index 0000000..6e8c2ae --- /dev/null +++ b/manifests/retroarch/phase-1.0cy-launch-canary-reproducibility-result.json @@ -0,0 +1,12 @@ +{ + "schema_version":1, + "phase":"PHASE_1_0CY_LAUNCH_CANARY_REPRODUCIBILITY_RESULT", + "status":"BYTE_EXACT_ELF_AND_MAP_REPRODUCIBILITY_PROVEN_OFFLINE", + "date":"2026-07-30", + "source_bindings":{"phase10cx_manifest_sha256":"e063ea356b8b2b114c26f69680ab677817b0696e23898dbcd6fb8e99dd20d399","target_source_commit":"e8419862f1a0a5e7fc780452f539c10ebc1a6438"}, + "independent_build":{"source_archive_sha256":"03a213399f5030d53b46cdea17c8214f38ccb729060ca662789f92f9970f647c","build_output_bytes":157989,"native_root":"/tmp/chimera-gfx-phase10cx","current_objects_reused":false,"network_action_invoked":false,"device_action_invoked":false}, + "comparison":{"first_elf_sha256":"8dadce9d9faaef21ea129a3d216c768eea9a3ca9bf8ecb8d852e376b58a9bf95","second_elf_sha256":"8dadce9d9faaef21ea129a3d216c768eea9a3ca9bf8ecb8d852e376b58a9bf95","elf_byte_exact":true,"first_map_sha256":"1cabe4d61b2eb2c1844a58883db6b8644c980759690242bef17f92e9c260b3cd","second_map_sha256":"1cabe4d61b2eb2c1844a58883db6b8644c980759690242bef17f92e9c260b3cd","map_byte_exact":true}, + "limitations":{"firmware_960_behavior_proven":false,"launch_context_proven":false,"visible_output_proven":false,"cleanup_proven":false,"host_av_runner_activated":false}, + "authorizations":{"artifact_execution_eligible":false,"artifact_transfer_eligible":false,"artifact_installation_eligible":false,"network_access_authorized":false,"ps5_connection_authorized":false,"device_transfer_authorized":false,"device_execution_authorized":false}, + "decision":{"offline_reproducibility_proven":true,"hardware_action_allowed":false,"next_step":"ADD_INACTIVE_MANIFEST_ONLY_CHD10AV1_ONE_SHOT_RUNNER_SUPPORT"} +} diff --git a/manifests/retroarch/phase-1.0cz-launch-canary-one-shot-active.json b/manifests/retroarch/phase-1.0cz-launch-canary-one-shot-active.json new file mode 100644 index 0000000..8c253b7 --- /dev/null +++ b/manifests/retroarch/phase-1.0cz-launch-canary-one-shot-active.json @@ -0,0 +1,14 @@ +{ + "schema_version":1, + "phase":"PHASE_1_0CZ_LAUNCH_CANARY_ONE_SHOT_RUNNER", + "status":"CONSUMED_ONE_SHOT_DEVICE_TEST_AUTHORIZATION", + "date":"2026-07-30", + "approval":{"authorized_by":"Jens","approval_reference":"active-task-user-statement-2026-07-30","scope":"ONE_CZ_RAW_ELFLDR_BASELINE_ATTEMPT"}, + "source_bindings":{"phase10cy_manifest_sha256":"850d0a2860ebb53ff69bcfd3cb0ac268809d470bb208d16db18bf76a63fc3c36","retroarch_runner_commit":"fd676c6c46a95342e2bb0ee6b7141330b17c72ad","runner_sha256":"56bef4dfab27fa5d7aee38394292ade9bcbf256d9c08b49e37531ff45d5029f5","runner_tests_sha256":"f67c6f8f75783dd4417fa0eb17150077203a07bee0d97da1d8e264746cc01ca2"}, + "artifact":{"name":"retroarch_ps5_launch_canary.elf","profile":"launch-canary","size":1845240,"sha256":"8dadce9d9faaef21ea129a3d216c768eea9a3ca9bf8ecb8d852e376b58a9bf95","execution_eligible":false,"transfer_eligible":false,"installation_eligible":false}, + "route":{"arm":"RAW_ELFLDR_BASELINE","loader":"HASH_BOUND_HARDENED_ELFLDR","firmware":"9.60","target":null,"port":null,"bigapp_context_expected":false,"shsrv_or_hbldr_used":false}, + "result_protocol":{"magic":"CHD10AV1","version":1,"frame_size":64,"interval_stages":["I00","I01","I02","I03","I04","I05","I06","I07","I08","I09","I10","I11","I12","I13","I14"],"wire_stages":["D00","D01","D02","D03","D04","D05","D06","D07","D08","D09","D10","D11","D12","I00","I01","I02","I03","I04","I05","I06","I07","I08","I09","I10","I11","I12","I13","I14","C1","D13","D14"],"sole_terminal":"D14","required_order":["D07","D04","D14"],"d07_d04_kind":"RAW","d07_d04_flags":1,"d12_max_count":1,"d14_raw0_required":0,"d14_aux0_required":0,"d14_aux1_required":0,"post_terminal_bytes_allowed":false}, + "runner":{"protocol_selection":"MANIFEST_ONLY","protocol_activation_authorized":false,"connection_count":1,"run_id":"cz-20260730-raw-baseline-001","retry":false,"reconnect":false,"resume":false,"consumed_receipt_before_connect":true,"exclusive_trace":true,"capture_limit_bytes":65536,"timeout_seconds":75.0}, + "authorizations":{"ps5_connection_authorized":false,"device_transfer_authorized":false,"device_execution_authorized":false,"result_receive_authorized":false,"installation_authorized":false,"autoload_authorized":false,"device_write_authorized":false,"automatic_retry":false}, + "decision":{"hardware_action_allowed":false,"single_use":true,"next_step":"RECORD_AND_ANALYZE_CONSUMED_RAW_BASELINE_OFFLINE_NO_RETRY"} +} diff --git a/manifests/retroarch/phase-1.0cz-launch-canary-one-shot-runner.json b/manifests/retroarch/phase-1.0cz-launch-canary-one-shot-runner.json new file mode 100644 index 0000000..88a9ab4 --- /dev/null +++ b/manifests/retroarch/phase-1.0cz-launch-canary-one-shot-runner.json @@ -0,0 +1,14 @@ +{ + "schema_version":1, + "phase":"PHASE_1_0CZ_LAUNCH_CANARY_ONE_SHOT_RUNNER", + "status":"INACTIVE_ARTIFACT_SPECIFIC_HARDWARE_GATE", + "date":"2026-07-30", + "source_bindings":{"phase10cy_manifest_sha256":"850d0a2860ebb53ff69bcfd3cb0ac268809d470bb208d16db18bf76a63fc3c36","retroarch_runner_commit":"fd676c6c46a95342e2bb0ee6b7141330b17c72ad","runner_sha256":"56bef4dfab27fa5d7aee38394292ade9bcbf256d9c08b49e37531ff45d5029f5","runner_tests_sha256":"f67c6f8f75783dd4417fa0eb17150077203a07bee0d97da1d8e264746cc01ca2"}, + "artifact":{"name":"retroarch_ps5_launch_canary.elf","profile":"launch-canary","size":1845240,"sha256":"8dadce9d9faaef21ea129a3d216c768eea9a3ca9bf8ecb8d852e376b58a9bf95","execution_eligible":false,"transfer_eligible":false,"installation_eligible":false}, + "route":{"arm":"RAW_ELFLDR_BASELINE","loader":"HASH_BOUND_HARDENED_ELFLDR","firmware":"9.60","target":null,"port":null,"bigapp_context_expected":false,"shsrv_or_hbldr_used":false}, + "result_protocol":{"magic":"CHD10AV1","version":1,"frame_size":64,"interval_stages":["I00","I01","I02","I03","I04","I05","I06","I07","I08","I09","I10","I11","I12","I13","I14"],"wire_stages":["D00","D01","D02","D03","D04","D05","D06","D07","D08","D09","D10","D11","D12","I00","I01","I02","I03","I04","I05","I06","I07","I08","I09","I10","I11","I12","I13","I14","C1","D13","D14"],"sole_terminal":"D14","required_order":["D07","D04","D14"],"d07_d04_kind":"RAW","d07_d04_flags":1,"d12_max_count":1,"d14_raw0_required":0,"d14_aux0_required":0,"d14_aux1_required":0,"post_terminal_bytes_allowed":false}, + "runner":{"protocol_selection":"MANIFEST_ONLY","protocol_activation_authorized":false,"connection_count":1,"run_id":null,"retry":false,"reconnect":false,"resume":false,"consumed_receipt_before_connect":true,"exclusive_trace":true,"capture_limit_bytes":65536}, + "authorizations":{"ps5_connection_authorized":false,"device_transfer_authorized":false,"device_execution_authorized":false,"result_receive_authorized":false,"installation_authorized":false,"autoload_authorized":false,"device_write_authorized":false,"automatic_retry":false}, + "activation_requirements":{"new_active_task_approval_by_jens":true,"exact_target_required":true,"exact_port_required":true,"unique_run_id_required":true,"untracked_matching_approval_required":true,"artifact_rehash_before_connect":true}, + "decision":{"hardware_action_allowed":false,"next_step":"OBTAIN_EXPLICIT_ARTIFACT_SPECIFIC_RAW_BASELINE_APPROVAL_OR_CONTINUE_BIGAPP_LAUNCHER_WORK_OFFLINE"} +} diff --git a/manifests/retroarch/phase-1.0cz-one-shot-approval-template.json b/manifests/retroarch/phase-1.0cz-one-shot-approval-template.json new file mode 100644 index 0000000..924907f --- /dev/null +++ b/manifests/retroarch/phase-1.0cz-one-shot-approval-template.json @@ -0,0 +1,31 @@ +{ + "schema_version":1, + "phase":"PHASE_1_0CZ_LAUNCH_CANARY_ONE_SHOT_RUNNER", + "authorized":false, + "consumed":false, + "authorization_scope":"EXACT_ONE_SHOT_PHASE_1_0CZ", + "authorized_by":null, + "approval_reference":null, + "protocol_magic":"CHD10AV1", + "protocol_version":1, + "protocol_frame_size":64, + "run_id":null, + "target":null, + "port":null, + "firmware":"9.60", + "artifact_name":"retroarch_ps5_launch_canary.elf", + "artifact_size":1845240, + "artifact_sha256":"8dadce9d9faaef21ea129a3d216c768eea9a3ca9bf8ecb8d852e376b58a9bf95", + "timeout_seconds":null, + "connection_count":0, + "transfer_count":0, + "execution_count":0, + "result_receive_count":0, + "installation":false, + "autoload":false, + "device_write":false, + "retry":false, + "reconnect":false, + "resume":false, + "reboot":false +} diff --git a/manifests/retroarch/phase-1.0d-canary-artifact.json b/manifests/retroarch/phase-1.0d-canary-artifact.json new file mode 100644 index 0000000..28e7932 --- /dev/null +++ b/manifests/retroarch/phase-1.0d-canary-artifact.json @@ -0,0 +1,128 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0D_LOADER_TO_ENTRY_DIAGNOSIS", + "status": "RETROARCH_PS5_ENTRY_DIAGNOSTIC_LADDER_BUILT_OFFLINE", + "scope": "HOST_ONLY_BUILD_AND_STATIC_AUDIT", + "date": "2026-07-19", + "repository": "chimera-retroarch", + "branch": "codex/ps5-loader-entry-diagnosis", + "source_commit": "69b65858ffaee826d70f5c0df61013cd1b0e2048", + "baseline_commit": "392d14871f3b121a346d1e2e727e528552bdd700", + "source_bindings": { + "ps5_payload_sdk": "d2e2e585740362976a39fdd5ccf390f199a7bc37", + "hardened_elfldr": "197623058f509eddde18868dafcb92fdcac66464", + "lifecycle_notification_source": "fe08300339a13f899fb78ea404ada381a5cba87c", + "lifecycle_notification_blob": "654e6659c9e6f2569ddccf1c9e46ea1c62c066f3" + }, + "authorizations": { + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "installation_authorized": false, + "autoload_authorized": false, + "device_write_authorized": false, + "automatic_retry": false + }, + "actions": { + "ps5_connected": false, + "device_request_performed": false, + "files_transferred": false, + "device_write_performed": false, + "target_execution_performed": false, + "transfer_package_created": false, + "execution_package_created": false, + "installation_package_created": false + }, + "artifact": { + "name": "chimera_ps5_crt_entry_canary.elf", + "profile": "crt-canary", + "local_relative_path": "build/phase10d/canary-a/chimera_ps5_crt_entry_canary.elf", + "linker_map_relative_path": "build/phase10d/canary-a/chimera_ps5_crt_entry_canary.map", + "size": 106344, + "sha256": "c84c0fb43392ce17abc1e7e6be38b522f1b32a681fd516d684c3dbbf7b34b743", + "clean_build_sha256": [ + "c84c0fb43392ce17abc1e7e6be38b522f1b32a681fd516d684c3dbbf7b34b743", + "c84c0fb43392ce17abc1e7e6be38b522f1b32a681fd516d684c3dbbf7b34b743" + ], + "linker_map_size": 38668, + "linker_map_sha256": "96132ef015d1743606a86906c810b9e9b2832889a6d49863653b03dddb75cf07", + "clean_map_sha256": [ + "96132ef015d1743606a86906c810b9e9b2832889a6d49863653b03dddb75cf07", + "96132ef015d1743606a86906c810b9e9b2832889a6d49863653b03dddb75cf07" + ], + "execution_eligible": false, + "transfer_eligible": false, + "installation_eligible": false, + "device_action_performed": false + }, + "elf": { + "type": "ET_DYN", + "machine": "EM_X86_64", + "entrypoint": "0x290", + "program_header_count": 4, + "load_segment_count": 3, + "rwx_load_segment_count": 0, + "program_headers": [ + {"type": "LOAD", "flags": "RE", "offset": 16384, "vaddr": 0, "filesz": 27376, "memsz": 27376, "align": 16384}, + {"type": "LOAD", "flags": "R", "offset": 49152, "vaddr": 32768, "filesz": 4824, "memsz": 4824, "align": 16384}, + {"type": "LOAD", "flags": "RW", "offset": 65536, "vaddr": 49152, "filesz": 32788, "memsz": 35312, "align": 16384} + ], + "bss_size": 2512, + "init_array_size": 0, + "fini_array_size": 0, + "tls": false, + "relocations": {"total": 144, "by_type": {"6": 4, "8": 140}}, + "dt_needed": ["libSceLibcInternal.sprx", "libkernel_web.sprx"], + "undefined_symbols": [ + "_Exit", + "memset", + "nanosleep", + "sceKernelSendNotificationRequest" + ] + }, + "import_classification": { + "CRT_PRE_MAIN": ["memset"], + "CANARY_MAIN": ["nanosleep", "sceKernelSendNotificationRequest"], + "SHUTDOWN": ["_Exit"], + "RETROARCH_PRE_VIDEO": [], + "VIDEO": [], + "INPUT": [], + "AUDIO": [], + "UNREACHABLE_IN_PROFILE": [] + }, + "notification_abi": { + "request_reserved_bytes": 45, + "request_message_bytes": 3075, + "call": "sceKernelSendNotificationRequest(0, &request, sizeof(request), 0)", + "message": "CHIMERA C1 — CRT MAIN REACHED", + "maximum_attempts": 1, + "retry": false + }, + "behavior": { + "sleep_seconds": 2, + "sleep_attempts": 1, + "interrupted_sleep_retry": false, + "exit": "_Exit(0)", + "retroarch": false, + "sdl": false, + "videoout": false, + "pad": false, + "audioout": false, + "filesystem": false, + "networking": false, + "threads": false, + "autoload": false, + "installation": false + }, + "callsites": { + "main": "0x0", + "notification_call": "0x24b", + "_Exit_call": "0x23", + "_start": "0x290", + "disassembly_sha256": "8282716f4c0159acb66650db3028c5e0519bac12a0dbc396f3c589c92cb04bcd" + }, + "loader_model": { + "classification": "ACCEPTED_BY_STATIC_MODEL", + "hardware_evidence": false + } +} diff --git a/manifests/retroarch/phase-1.0d-early-diag-artifact.json b/manifests/retroarch/phase-1.0d-early-diag-artifact.json new file mode 100644 index 0000000..dbbc7dd --- /dev/null +++ b/manifests/retroarch/phase-1.0d-early-diag-artifact.json @@ -0,0 +1,202 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0D_LOADER_TO_ENTRY_DIAGNOSIS", + "status": "RETROARCH_PS5_ENTRY_DIAGNOSTIC_LADDER_BUILT_OFFLINE", + "scope": "HOST_ONLY_BUILD_AND_STATIC_AUDIT", + "date": "2026-07-19", + "repository": "chimera-retroarch", + "branch": "codex/ps5-loader-entry-diagnosis", + "source_commit": "69b65858ffaee826d70f5c0df61013cd1b0e2048", + "baseline_commit": "392d14871f3b121a346d1e2e727e528552bdd700", + "source_bindings": { + "retroarch_release": "69a4f0ea1e8aaf442ae4858f2e7f2b31a1776576", + "ps5_payload_sdk": "d2e2e585740362976a39fdd5ccf390f199a7bc37", + "pacbrew": "c2abcfcb60f569128abd0e8e70ad03a67bee5ea7", + "sdl": "0baf4ac49382b537ba449901b5b6d0d189bb1fbb", + "hardened_elfldr": "197623058f509eddde18868dafcb92fdcac66464", + "lifecycle_notification_source": "fe08300339a13f899fb78ea404ada381a5cba87c" + }, + "authorizations": { + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "installation_authorized": false, + "autoload_authorized": false, + "device_write_authorized": false, + "automatic_retry": false + }, + "actions": { + "ps5_connected": false, + "device_request_performed": false, + "files_transferred": false, + "device_write_performed": false, + "target_execution_performed": false, + "transfer_package_created": false, + "execution_package_created": false, + "installation_package_created": false + }, + "artifact": { + "name": "retroarch_ps5_early_diag.elf", + "profile": "early-diag", + "compile_define": "CHIMERA_PS5_EARLY_DIAG=1", + "local_relative_path": "build/phase10d/early-a/retroarch_ps5_early_diag.elf", + "linker_map_relative_path": "build/phase10d/early-a/retroarch_ps5_early_diag.map", + "size": 1844664, + "sha256": "092bcabc0f8717a648030c592aa0dc900f878952fb14ee453851a29f298ea7ee", + "clean_build_sha256": [ + "092bcabc0f8717a648030c592aa0dc900f878952fb14ee453851a29f298ea7ee", + "092bcabc0f8717a648030c592aa0dc900f878952fb14ee453851a29f298ea7ee" + ], + "linker_map_size": 635853, + "linker_map_sha256": "0d4d4614826fa4cbbf11e7374a2d2ecb574a00b55ad9deac17272f9504b254a0", + "clean_map_sha256": [ + "0d4d4614826fa4cbbf11e7374a2d2ecb574a00b55ad9deac17272f9504b254a0", + "0d4d4614826fa4cbbf11e7374a2d2ecb574a00b55ad9deac17272f9504b254a0" + ], + "execution_eligible": false, + "transfer_eligible": false, + "installation_eligible": false, + "device_action_performed": false + }, + "elf": { + "type": "ET_DYN", + "machine": "EM_X86_64", + "entrypoint": "0xfec40", + "program_header_count": 4, + "load_segment_count": 3, + "rwx_load_segment_count": 0, + "program_headers": [ + {"type": "LOAD", "flags": "RE", "offset": 16384, "vaddr": 0, "filesz": 1070254, "memsz": 1070254, "align": 16384}, + {"type": "LOAD", "flags": "R", "offset": 1097728, "vaddr": 1081344, "filesz": 394136, "memsz": 394136, "align": 16384}, + {"type": "LOAD", "flags": "RW", "offset": 1507328, "vaddr": 1490944, "filesz": 206124, "memsz": 903744, "align": 16384} + ], + "bss_size": 697616, + "init_array_size": 0, + "fini_array_size": 0, + "tls": false, + "relocations": {"total": 1054, "by_type": {"6": 141, "8": 913}}, + "dt_needed": [ + "libSceAudioOut.sprx", + "libSceLibcInternal.sprx", + "libScePad.sprx", + "libSceSystemService.sprx", + "libSceUserService.sprx", + "libSceVideoOut.sprx", + "libkernel_web.sprx" + ], + "undefined_symbols": [ + "_Exit", "__error", "__isthreaded", "__stderrp", "__stdoutp", "abort", + "atan2", "atof", "atoi", "calloc", "clock_gettime", "close", "closedir", + "cos", "cosf", "exit", "fclose", "ferror", "fflush", "fileno", "fopen", + "fprintf", "fputc", "fputs", "fread", "free", "fseek", "fseeko", "fstat", + "ftello", "fwrite", "getenv", "gettimeofday", "isgraph", "localtime", + "longjmp", "lseek", "malloc", "memcmp", "memcpy", "memmove", "memset", + "nanosleep", "open", "opendir", "powf", "pthread_attr_init", + "pthread_attr_setdetachstate", "pthread_attr_setstacksize", + "pthread_create", "pthread_getschedparam", "pthread_getspecific", + "pthread_join", "pthread_key_create", "pthread_key_delete", + "pthread_mutex_destroy", "pthread_mutex_init", "pthread_mutex_lock", + "pthread_mutex_unlock", "pthread_mutexattr_init", + "pthread_mutexattr_settype", "pthread_self", "pthread_set_name_np", + "pthread_setcanceltype", "pthread_setschedparam", "pthread_setspecific", + "pthread_sigmask", "puts", "qsort", "read", "readdir", "realloc", + "sceAudioOutClose", "sceAudioOutInit", "sceAudioOutOpen", + "sceAudioOutOutput", "sceKernelAllocateMainDirectMemory", + "sceKernelCreateEqueue", "sceKernelDeleteEqueue", + "sceKernelMapDirectMemory", "sceKernelReleaseDirectMemory", + "sceKernelSendNotificationRequest", "sceKernelWaitEqueue", "scePadClose", + "scePadInit", "scePadOpen", "scePadReadState", + "sceSystemServiceHideSplashScreen", "sceUserServiceGetLoginUserIdList", + "sceUserServiceGetUserName", "sceUserServiceInitialize", + "sceVideoOutAddFlipEvent", "sceVideoOutClose", + "sceVideoOutDeleteFlipEvent", "sceVideoOutOpen", + "sceVideoOutRegisterBuffers2", "sceVideoOutSetBufferAttribute2", + "sceVideoOutSetFlipRate", "sceVideoOutSubmitFlip", + "sched_get_priority_max", "sched_get_priority_min", "sem_destroy", + "sem_init", "sem_post", "sem_timedwait", "sem_trywait", "sem_wait", + "setjmp", "setvbuf", "sigaction", "sigaddset", "sigemptyset", "signal", + "sin", "sinf", "snprintf", "sprintf", "sscanf", "stat", "strchr", + "strcmp", "strdup", "strerror", "strftime", "strlcat", "strlcpy", + "strlen", "strncasecmp", "strncmp", "strrchr", "strstr", "strtod", + "strtok_r", "strtol", "strtoul", "strtoull", "time", "tolower", + "vfprintf", "vsnprintf", "vsscanf" + ] + }, + "diagnostic": { + "stages": ["D00", "D01", "D02", "D03", "D04", "D05", "D06", "D07", "D08", "D09", "D10", "D11", "D12"], + "notification_maximum_attempts_per_stage": 1, + "notification_retry": false, + "notification_failure_blocks_primary_path": false, + "message_capacity": 192, + "dynamic_format_allocation": false, + "user_names_reported": false, + "raw_user_ids_only": true, + "buffer_attribute_result": "NO_RETURN_VALUE_SENTINEL" + }, + "first_frame": { + "background": "MAGENTA", + "fixed_rectangle": "WHITE", + "embedded_pattern": "BLACK", + "resource_file": false, + "frame_id": 0, + "submit_attempts": 1, + "wait_timeout_us": 100000, + "retry": false, + "second_buffer_initialization": false + }, + "profile_policy": { + "real_retroarch_frontend": true, + "static_chimera_smokecore": true, + "write_firewall": true, + "filesystem_writes_allowed": false, + "networking": false, + "dynamic_cores": false, + "gnm": false, + "autoload": false, + "installation": false, + "payload_launch": false, + "automatic_retry": false, + "runtime_limit_ms": 60000, + "frame_limit": 3600, + "exit": "_Exit" + }, + "fault_injection": { + "asan": "PASS", + "ubsan": "PASS", + "leak_detection": "PASS", + "notification_failure": "PASS", + "notification_truncation": "PASS", + "notification_duplicate": "PASS", + "user_service_failure": "PASS", + "no_login_user": "PASS", + "videoout_open_failure": "PASS", + "buffer_attribute_failure": "NOT_REPRESENTABLE_VOID_SOURCE_ABI", + "direct_memory_allocation_failure": "PASS", + "map_failure": "PASS", + "buffer_registration_failure": "PASS", + "equeue_failure": "PASS", + "flip_event_failure": "PASS", + "first_flip_failure": "PASS", + "pad_failure": "PASS", + "audioout_failure": "PASS", + "core_failure": "PASS", + "deadline": "PASS", + "cleanup_every_boundary": "PASS", + "exit_selected_once": "PASS", + "hardware_evidence": false + }, + "callsites": { + "D00_source": "retroarch.c:6201", + "D00_disassembly": "0x3ccd", + "notification_call": "0x4776b", + "diagnostic_frame_helper": "0xfdf90", + "first_magenta_flip_call": "0xfe1ec", + "_Exit_call": "0x3cef", + "_start": "0xfec40", + "disassembly_sha256": "754f9defd84241ffc1716225002cf1cc1f266874da88259142592b13d59f14fb" + }, + "loader_model": { + "classification": "ACCEPTED_BY_STATIC_MODEL", + "hardware_evidence": false + } +} diff --git a/manifests/retroarch/phase-1.0d-loader-model-results.json b/manifests/retroarch/phase-1.0d-loader-model-results.json new file mode 100644 index 0000000..296d480 --- /dev/null +++ b/manifests/retroarch/phase-1.0d-loader-model-results.json @@ -0,0 +1,125 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0D_LOADER_TO_ENTRY_DIAGNOSIS", + "status": "RETROARCH_PS5_ENTRY_DIAGNOSTIC_LADDER_BUILT_OFFLINE", + "scope": "HOST_ONLY_STATIC_SOURCE_MODEL", + "model": { + "name": "HARDENED_ELFLDR_STATIC_SOURCE_MODEL", + "source_commit": "197623058f509eddde18868dafcb92fdcac66464", + "source_file": "elfldr.c", + "source_file_sha256": "9949f8e4037984d10f1f5aa498e4665593d1fac8a33614d7f2141349839bb803", + "invented_rules": false, + "hardware_evidence": false + }, + "authorizations": { + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "installation_authorized": false, + "autoload_authorized": false, + "device_write_authorized": false, + "automatic_retry": false + }, + "actions": { + "ps5_connected": false, + "device_request_performed": false, + "files_transferred": false, + "device_write_performed": false, + "target_execution_performed": false + }, + "real_artifacts": [ + { + "name": "retroarch_ps5_software_smoke.elf", + "size": 1826216, + "sha256": "16c04afe26c490b5071388dc9a32524d09d50d181b1baffbbc54bd96f0c2c688", + "entrypoint": "0xfdd70", + "program_headers": 4, + "load_segments": 3, + "relocations": 1053, + "imports": 140, + "dt_needed": 7, + "classification": "ACCEPTED_BY_STATIC_MODEL", + "first_reason": "no deterministic rejection in modeled hardened source path" + }, + { + "name": "chimera_ps5_crt_entry_canary.elf", + "size": 106344, + "sha256": "c84c0fb43392ce17abc1e7e6be38b522f1b32a681fd516d684c3dbbf7b34b743", + "entrypoint": "0x290", + "program_headers": 4, + "load_segments": 3, + "relocations": 144, + "imports": 4, + "dt_needed": 2, + "classification": "ACCEPTED_BY_STATIC_MODEL", + "first_reason": "no deterministic rejection in modeled hardened source path" + }, + { + "name": "retroarch_ps5_early_diag.elf", + "size": 1844664, + "sha256": "092bcabc0f8717a648030c592aa0dc900f878952fb14ee453851a29f298ea7ee", + "entrypoint": "0xfec40", + "program_headers": 4, + "load_segments": 3, + "relocations": 1054, + "imports": 141, + "dt_needed": 7, + "classification": "ACCEPTED_BY_STATIC_MODEL", + "first_reason": "no deterministic rejection in modeled hardened source path" + }, + { + "name": "chimera-gfx-lifecycle-probe.elf", + "size": 112680, + "sha256": "bfb4a5cc768e162fe4c2fddf41c3978e152722918a39085277fd172cb95a7182", + "entrypoint": "0x90", + "program_headers": 4, + "load_segments": 3, + "relocations": 142, + "imports": 2, + "dt_needed": 2, + "classification": "ACCEPTED_BY_STATIC_MODEL", + "first_reason": "no deterministic rejection in modeled hardened source path" + } + ], + "negative_host_cases": [ + {"mutation": "bad_magic", "classification": "REJECTED_BY_STATIC_MODEL", "first_reason_contains": "magic"}, + {"mutation": "unsupported_type", "classification": "REJECTED_BY_STATIC_MODEL", "first_reason_contains": "type not supported"}, + {"mutation": "file_range_out_of_bounds", "classification": "REJECTED_BY_STATIC_MODEL", "first_reason_contains": "file range"}, + {"mutation": "entry_outside_load", "classification": "MODEL_INCOMPLETE", "first_reason_contains": "entrypoint"}, + {"mutation": "wrong_machine", "classification": "ACCEPTED_BY_STATIC_MODEL", "source_gap_contains": "EM_X86_64"} + ], + "source_validation_gaps": [ + "ELF class is not checked", + "ELF data encoding is not checked", + "machine is not checked", + "header entry sizes are ignored", + "overlapping PT_LOAD segments are not rejected", + "entrypoint mapping is not checked before transfer", + "dynamic symbol resolution is delegated to SDK startup" + ], + "runtime_unproven": [ + "network receipt", + "complete remote buffer receipt", + "runtime allocation and mapping", + "payload argument resource construction", + "dynamic import resolution", + "entry transfer", + "firmware 9.60 behavior", + "notification visibility", + "cleanup and return" + ], + "sender_trace_contract": { + "official_source_commit": "0dbbf4e7e0203af7e5d101a3256c634edf4e3ba2", + "connections": 1, + "sendall_calls": 1, + "hash_before_connect": true, + "dns_for_literal_ip": false, + "shutdown_called": false, + "response_read": false, + "retry": false, + "reconnect": false, + "probe": false, + "can_prove": ["HOST_CONNECTED", "HOST_SEND_COMPLETED", "REMOTE_RESET_OBSERVED_IF_RAISED"], + "cannot_prove": ["REMOTE_ELF_RECEIPT", "REMOTE_EXECUTION"] + } +} diff --git a/manifests/retroarch/phase-1.0da-raw-elfldr-baseline-result.json b/manifests/retroarch/phase-1.0da-raw-elfldr-baseline-result.json new file mode 100644 index 0000000..313783a --- /dev/null +++ b/manifests/retroarch/phase-1.0da-raw-elfldr-baseline-result.json @@ -0,0 +1,16 @@ +{ + "schema_version":1, + "phase":"PHASE_1_0DA_RAW_ELFLDR_BASELINE_RESULT", + "status":"CONSUMED_INCOMPLETE_DEVICE_RESULT", + "date":"2026-07-30", + "run_id":"cz-20260730-raw-baseline-001", + "artifact":{"name":"retroarch_ps5_launch_canary.elf","size":1845240,"sha256":"8dadce9d9faaef21ea129a3d216c768eea9a3ca9bf8ecb8d852e376b58a9bf95","execution_eligible":false,"transfer_eligible":false,"installation_eligible":false}, + "authorization_bindings":{"active_manifest_sha256":"0cf6106e8072d19d9000a54d2a974fa0f8bffc19b8cb6be4bd15b530456b045c","local_approval_sha256":"a1fc456864a4a6f4dc7fdeefc96a7a69450c2a7b3c6bc642670f78666cc178e3","attempt_receipt_sha256":"bc68bd80825a630a6a403f29c0fc848cad3215b6d979864a48c78462510db238"}, + "transport":{"connect_count":1,"sendall_count":1,"shutdown_write_count":1,"reconnect_count":0,"retry_count":0,"received_byte_count":3761,"remote_eof":true,"duration_seconds":2.0673740999991423,"raw_stream_sha256":"2747f07f09cb56685508d460133b1e2d163b33e16016bd2b7f68e399ff5f34e4","ordinary_stdout_sha256":"87ee3b86ed3cb488ab0a2f8d03c9be3a0669fab59847a6e76ac47b55db499762"}, + "proven_frames":{"count":23,"ordered_stages":["D00","D01","D02","I00","I01","I02","I03","I04","I05","I06","I07","I08","I09","I10","D10","I11","I12","I13","I14","D03","D05","D12","D04"],"D02":{"raw0":0},"D10":{"raw0":1},"D05":{"raw0":-2144796663},"D12":{"raw0":5,"raw1":103},"D04":{"raw0":-1}}, + "missing_required_stages":["D07","D14"], + "stdout_evidence":{"video_size":"960x720","video_driver_open_failed":true,"fatal_site":"video_driver_init_internal()","core_unload_messages_observed":true}, + "classification":{"result":"INCOMPLETE_REMOTE_EOF_BEFORE_D14","raw_elfldr_launch_context_videoout_open_failed":true,"visible_output_proven":false,"successful_cleanup_proven":false,"root_cause_proven":false}, + "current_authorizations":{"ps5_connection_authorized":false,"device_transfer_authorized":false,"device_execution_authorized":false,"result_receive_authorized":false,"installation_authorized":false,"autoload_authorized":false,"device_write_authorized":false,"automatic_retry":false}, + "decision":{"retry_allowed":false,"reconnect_allowed":false,"next_step":"OFFLINE_COMPARE_RAW_CONTEXT_FAILURE_WITH_PUBLIC_BIGAPP_LAUNCH_CONTEXT_EVIDENCE"} +} diff --git a/manifests/retroarch/phase-1.0db-post-cz-differential-analysis.json b/manifests/retroarch/phase-1.0db-post-cz-differential-analysis.json new file mode 100644 index 0000000..20e2772 --- /dev/null +++ b/manifests/retroarch/phase-1.0db-post-cz-differential-analysis.json @@ -0,0 +1,75 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0DB_POST_CZ_DIFFERENTIAL_ANALYSIS", + "status": "RAW_CONTEXT_FAILURE_CONFIRMED_CAUSAL_BIGAPP_COMPARISON_NOT_READY", + "date": "2026-07-30", + "source_bindings": { + "phase10da_result_sha256": "f745c8762d1b88743751df1af625b3a08e452dff8d2a16091abd9eed82647e50", + "phase10o_result_sha256": "a92650732bc2fd4509970eb8c731e06ba1b91eff5c1bb7f9e2b844ac58ca8537", + "phase10aw_audit_sha256": "425bf66d12e1b9c0e9a26790ea58372ad5736ab6886028c57c4a1be241927a68", + "phase10cy_result_sha256": "850d0a2860ebb53ff69bcfd3cb0ac268809d470bb208d16db18bf76a63fc3c36" + }, + "raw_runs": { + "phase10o": { + "artifact_sha256": "c99a0856309a357ad2667d89b4924e4063ad214cae09c8a419457b0732f583cd", + "videoout_open_raw": 1309671680, + "buffer_registration_raw": 0, + "flip_submit_raw": -1, + "sdl_init_raw": -1, + "terminal_cleanup_proven": false + }, + "phase10cz": { + "artifact_sha256": "8dadce9d9faaef21ea129a3d216c768eea9a3ca9bf8ecb8d852e376b58a9bf95", + "videoout_open_raw_signed": -2144796663, + "videoout_open_raw_hex": "0x80290009", + "buffer_registration_reached": false, + "flip_submit_reached": false, + "sdl_init_raw": -1, + "terminal_cleanup_proven": false + } + }, + "evidence_classification": { + "raw_elfldr_can_reach_retroarch_video_initialization": true, + "raw_elfldr_reliably_supplies_usable_videoout_context": false, + "phase10cz_visible_output_proven": false, + "phase10cz_complete_cleanup_proven": false, + "d05_error_semantics_publicly_bound": false, + "o_to_cz_difference_is_causal_evidence": false, + "artifact_bytes_identical_between_runs": false, + "runtime_state_identical_between_runs": false, + "bigapp_context_tested": false, + "bigapp_context_root_cause_proven": false + }, + "bigapp_gate": { + "official_v07_source_is_architecturally_distinct": true, + "same_canary_bytes_required_for_future_comparison": true, + "deployed_shsrv_identity_proven": false, + "bounded_bigapp_launcher_artifact_exists": false, + "bounded_live_result_channel_exists": false, + "device_resident_staging_required_by_existing_hbldr": true, + "active_bigapp_termination_required_by_existing_hbldr": true, + "existing_hbldr_direct_use_allowed": false, + "causal_hardware_comparison_ready": false + }, + "authorizations": { + "ps5_connection_authorized": false, + "shsrv_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "result_receive_authorized": false, + "app_termination_authorized": false, + "installation_authorized": false, + "autoload_authorized": false, + "device_write_authorized": false, + "automatic_retry": false, + "reconnect_authorized": false + }, + "decision": { + "raw_baseline_complete": true, + "raw_retry_useful_or_allowed": false, + "bigapp_path_remains_primary_candidate": true, + "existing_shsrv_hbldr_use_allowed": false, + "device_action_allowed": false, + "next_step": "OFFLINE_POST_CZ_BIGAPP_GATE_GAP_CLOSURE" + } +} diff --git a/manifests/retroarch/phase-1.0dc-inactive-bigapp-comparison-gate.json b/manifests/retroarch/phase-1.0dc-inactive-bigapp-comparison-gate.json new file mode 100644 index 0000000..77b8a75 --- /dev/null +++ b/manifests/retroarch/phase-1.0dc-inactive-bigapp-comparison-gate.json @@ -0,0 +1,87 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0DC_INACTIVE_BIGAPP_COMPARISON_GATE", + "status": "INACTIVE_DUAL_ARTIFACT_BIGAPP_GATE_COMPLETE_TARGET_IMPLEMENTATION_BLOCKED", + "date": "2026-07-30", + "source_bindings": { + "phase10db_manifest_sha256": "dc5ce0c80ae771c35f16ea51b3ef4e2369133dc150b3f39bc39a81805d6fd6c7", + "phase10ae_manifest_sha256": "eeebff316a84b184bb802485e0ed5ad42016f3236ba44c04e718c9fb08ca680d", + "phase10au_manifest_sha256": "10687d050b1354efd7edbbd0e6739255278dc5edc85e3312ea85497f9052ceef", + "contract_size": 6096, + "contract_sha256": "b8e965f343a0c3df5cf7026b68f547a81dad08d4b2e0934e4f9721de8b99010f", + "tests_size": 2630, + "tests_sha256": "ecef8f126e2245d707a776b4fd02aad13553e6e79515287e1002d2855a8db2d2" + }, + "activation": { + "active": false, + "firmware": null, + "title_id": null, + "run_id": null, + "not_before": null, + "expires_at": null, + "launcher_name": null, + "launcher_size": null, + "launcher_sha256": null, + "payload_name": null, + "payload_size": null, + "payload_sha256": null, + "approval_sha256": null, + "result_magic": null, + "result_terminal": null + }, + "candidate_contract": { + "firmware": "9.60", + "fixed_existing_title": "PPSA01659", + "exact_payload_name": "retroarch_ps5_launch_canary.elf", + "exact_payload_size": 1845240, + "exact_payload_sha256": "8dadce9d9faaef21ea129a3d216c768eea9a3ca9bf8ecb8d852e376b58a9bf95", + "result_magic": "CHD10AV1", + "sole_cleanup_terminal": "D14", + "maximum_activation_window_seconds": 300, + "maximum_launcher_size": 1048576, + "no_running_bigapp_attestation_required": true, + "kernel_ptrace_effect_acceptance_required": true, + "bounded_parent_detach_proof_required": true, + "bounded_child_cleanup_proof_required": true, + "bounded_result_channel_proof_required": true + }, + "forbidden_effects": { + "terminate_existing_bigapp": true, + "persistent_write": true, + "system_remount": true, + "installation": true, + "autoload": true, + "automatic_retry": true, + "reconnect": true, + "fallback_title": true + }, + "implementation_boundary": { + "network_import_present": false, + "socket_present": false, + "cli_present": false, + "file_output_present": false, + "target_source_present": false, + "launcher_artifact_present": false, + "live_result_channel_present": false + }, + "authorizations": { + "target_source_authorized": false, + "target_build_authorized": false, + "ps5_connection_authorized": false, + "shsrv_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "result_receive_authorized": false, + "kernel_ptrace_effects_authorized": false, + "app_termination_authorized": false, + "device_write_authorized": false, + "automatic_retry": false + }, + "decision": { + "host_gate_contract_complete": true, + "candidate_can_currently_pass": false, + "target_implementation_allowed": false, + "device_action_allowed": false, + "next_step": "OFFLINE_MINIMAL_LAUNCHER_SOURCE_PREREQUISITE_CLOSURE" + } +} diff --git a/manifests/retroarch/phase-1.0dd-minimal-launcher-source-prerequisite-audit.json b/manifests/retroarch/phase-1.0dd-minimal-launcher-source-prerequisite-audit.json new file mode 100644 index 0000000..0a8285b --- /dev/null +++ b/manifests/retroarch/phase-1.0dd-minimal-launcher-source-prerequisite-audit.json @@ -0,0 +1,97 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0DD_MINIMAL_LAUNCHER_SOURCE_PREREQUISITE_AUDIT", + "status": "LAUNCH_EXPORT_NAMES_PRESENT_ABI_AND_FW960_TITLE_CONTRACT_BLOCKED", + "date": "2026-07-30", + "source_bindings": { + "phase10dc_manifest_sha256": "729bc5c19d46966049a5ee1863df4efa3aa736dc90cf23c120fff8159c2e76f4", + "sdk_commit": "d2e2e585740362976a39fdd5ccf390f199a7bc37", + "shsrv_v07_commit": "74287f5db6b20320efd7892d7b29cf438fe7cb98", + "v07_hbldr_main_size": 7134, + "v07_hbldr_main_sha256": "2081ece2f7d0a7e9b392660696c2a44802f64680365607e742b37dcf3223a55a", + "sdk_systemservice_stub_size": 85838, + "sdk_systemservice_stub_sha256": "3bede484f4e62b8e6a924631df33ebac46c28884b986ff1481ea9ea0e0e5b0a1", + "sdk_userservice_stub_size": 78215, + "sdk_userservice_stub_sha256": "6aa2ea06095cf3f7baf9514740475bdd7b8f4ec042e4c6ba0efca47bf36add96" + }, + "export_inventory": { + "sceSystemServiceLaunchApp_name_in_sdk_stub": true, + "sceSystemServiceGetAppIdOfRunningBigApp_name_in_sdk_stub": true, + "sceUserServiceGetForegroundUser_name_in_sdk_stub": true, + "public_sdk_header_declaration_for_launch_app": false, + "public_sdk_header_declaration_for_running_bigapp": false, + "public_sdk_header_declaration_for_foreground_user": false, + "sdk_stub_proves_function_signature": false, + "sdk_stub_proves_structure_layout": false, + "sdk_stub_proves_firmware_960_behavior": false + }, + "single_lineage_declarations": { + "source": "OFFICIAL_SHSRV_V0_7_BUNDLES_HBLDR_MAIN_C", + "app_launch_ctx_declared_locally": true, + "launch_app_prototype_declared_locally": true, + "running_bigapp_prototype_declared_locally": true, + "foreground_user_prototype_declared_locally": true, + "independent_public_abi_corroboration": false, + "acceptable_as_architecture_reference": true, + "acceptable_as_new_target_abi_contract": false + }, + "title_contract": { + "fixed_candidate": "PPSA01659", + "v07_source_labels_as_videoplayer_webapp": true, + "firmware_960_presence_proven": false, + "firmware_960_launch_result_proven": false, + "videoout_ownership_proven": false, + "read_only_presence_observer_available": false, + "fallback_title_allowed": false + }, + "bounded_public_search": { + "date": "2026-07-30", + "queries_targeted_exact_function_and_structure_names": true, + "independent_primary_header_found": false, + "independent_firmware_960_title_contract_found": false, + "sonicloader_candidate_found": true, + "sonicloader_candidate_url": "https://git.earthonion.com/soniciso/sonicloader/commits/tag/1.0.48/sonic-loader.elf", + "sonicloader_candidate_explicitly_ports_reference_snippet": true, + "sonicloader_candidate_contains_persistent_or_autolaunch_scope": true, + "sonicloader_accepted_as_independent_corroboration": false, + "global_absence_claimed": false + }, + "closed_by_existing_offline_phases": { + "bounded_elf_admission_model": true, + "mapping_rollback_model": true, + "bounded_copy_restore_model": true, + "worker_supervisor_model": true, + "fixed_result_record_model": true, + "fd_deadline_model": true, + "unchanged_launch_canary_artifact": true, + "inactive_dual_artifact_gate": true, + "firmware_runtime_proof_from_models": false + }, + "minimum_new_evidence": [ + "INDEPENDENT_PUBLIC_SOURCE_BOUND_LAUNCH_APP_SIGNATURE_AND_CONTEXT_LAYOUT", + "INDEPENDENT_PUBLIC_SOURCE_BOUND_RUNNING_BIGAPP_QUERY_SIGNATURE", + "INDEPENDENT_PUBLIC_SOURCE_BOUND_FOREGROUND_USER_SIGNATURE", + "FIRMWARE_960_PPSA01659_PRESENCE_OR_FAIL_CLOSED_NONMUTATING_OBSERVATION_CONTRACT", + "TARGET_COMPOSITION_PROOF_WITH_ABSOLUTE_DEADLINES_AND_COMPLETE_RESTORE", + "LIVE_RESULT_CHANNEL_COMPOSITION_PROOF" + ], + "authorizations": { + "target_source_authorized": false, + "target_build_authorized": false, + "ps5_connection_authorized": false, + "shsrv_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "result_receive_authorized": false, + "kernel_ptrace_effects_authorized": false, + "app_termination_authorized": false, + "device_write_authorized": false + }, + "decision": { + "source_prerequisite_audit_complete": true, + "minimal_launcher_target_source_allowed": false, + "minimal_launcher_artifact_possible_now": false, + "device_action_allowed": false, + "next_step": "OBTAIN_INDEPENDENT_PUBLIC_ABI_AND_FW960_TITLE_EVIDENCE_OR_KEEP_TARGET_BLOCKED" + } +} diff --git a/manifests/retroarch/phase-1.0de-independent-abi-provenance-search.json b/manifests/retroarch/phase-1.0de-independent-abi-provenance-search.json new file mode 100644 index 0000000..c22d6ae --- /dev/null +++ b/manifests/retroarch/phase-1.0de-independent-abi-provenance-search.json @@ -0,0 +1,70 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0DE_INDEPENDENT_ABI_PROVENANCE_SEARCH", + "status": "NO_INDEPENDENT_ABI_OR_FW960_TITLE_SOURCE_FOUND_IN_BOUNDED_SEARCH", + "date": "2026-07-30", + "source_bindings": { + "phase10dd_manifest_sha256": "aa0657e1b51f457a65f134e533799f5bee58560fcc7f8761f6539811f75ef6ee", + "official_shsrv_v019_commit": "6f320637d56d344a0e7797753099e33238bbf146", + "official_shsrv_hbldr_blob": "53485304ac837e0a76c80581523107d03bba7a57", + "sonicloader_remote_head": "a3bdced572bca75948f66e6f91d592855ef7a78f", + "sonicloader_initial_commit": "d4792b14ab8a70067403ec0e3c8d0a95dc7dfdbe", + "sonicloader_initial_hbldr_blob": "7dd0c3510f93c553c437d51e2cfe49dabfd07430" + }, + "bounded_search": { + "exact_terms": [ + "sceSystemServiceLaunchApp", + "app_launch_ctx_t", + "sceSystemServiceGetAppIdOfRunningBigApp", + "sceUserServiceGetForegroundUser", + "PPSA01659" + ], + "official_ps5_payload_sdk_checked": true, + "official_shsrv_lineage_checked": true, + "public_code_search_checked": true, + "sonicloader_repository_cloned_read_only_under_ignored_work": true, + "device_or_ps5_network_used": false, + "global_absence_claimed": false + }, + "sonicloader_lineage": { + "repository": "https://git.earthonion.com/soniciso/sonicloader.git", + "declarations_present_in_initial_import": true, + "declaration_blame_initial_commit": true, + "initial_normalized_declaration_block_equals_official_shsrv_v019": true, + "normalized_block_length": 615, + "later_structsize_change_described_as_ported_reference_snippet": true, + "contains_embedded_payloads_and_persistent_autolaunch_scope": true, + "independent_abi_corroboration": false, + "acceptable_runtime_dependency": false + }, + "other_candidates": { + "ps5_upload_suite_release_claims_launch_calls": true, + "primary_source_and_independent_context_layout_bound": false, + "ps4_systemservice_material_is_ps5_abi_evidence": false, + "qualifying_independent_candidate_count": 0 + }, + "title_evidence": { + "ppsa01659_reference_outside_shsrv_lineage_found": false, + "firmware_960_presence_proven": false, + "launch_behavior_proven": false, + "videoout_ownership_proven": false + }, + "authorizations": { + "target_source_authorized": false, + "target_build_authorized": false, + "ps5_connection_authorized": false, + "shsrv_connection_authorized": false, + "device_request_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "device_write_authorized": false + }, + "decision": { + "independent_abi_gate_closed": false, + "firmware_title_gate_closed": false, + "minimal_launcher_source_allowed": false, + "device_action_allowed": false, + "offline_read_only_title_observer_gate_design_allowed": true, + "next_step": "OFFLINE_FAIL_CLOSED_TITLE_PRESENCE_OBSERVER_GATE_DESIGN" + } +} diff --git a/manifests/retroarch/phase-1.0df-inactive-title-presence-observer.json b/manifests/retroarch/phase-1.0df-inactive-title-presence-observer.json new file mode 100644 index 0000000..a0b2fad --- /dev/null +++ b/manifests/retroarch/phase-1.0df-inactive-title-presence-observer.json @@ -0,0 +1,82 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0DF_INACTIVE_TITLE_PRESENCE_OBSERVER", + "status": "HOST_OBSERVER_CONTRACT_COMPLETE_LIVE_REQUEST_UNFORMABLE", + "date": "2026-07-30", + "source_bindings": { + "phase10de_manifest_sha256": "0fd33ad39747cab0b7ff22488363a36b774dc0e516df89c604216824ad9eef75", + "contract_size": 5298, + "contract_sha256": "2679f03617241abf37ca547141a2a51e3f89b75fec957a1da4f13b904d931f2d", + "tests_size": 2496, + "tests_sha256": "3760fa60e66857b24bf507f196060ec9e4d8b96ebce3843ab02fc185c0f8c0b8" + }, + "activation": { + "active": false, + "firmware": null, + "title_id": null, + "method": null, + "query_contract_sha256": null, + "exact_literal_path": null, + "path_provenance_sha256": null, + "request_bytes_sha256": null, + "maximum_result_bytes": null + }, + "observer_contract": { + "fixed_firmware": "9.60", + "fixed_title_id": "PPSA01659", + "allowlisted_methods": ["SOURCE_BOUND_QUERY", "EXACT_PATH_METADATA"], + "maximum_result_bytes": 4096, + "one_request_required": true, + "read_only_required": true, + "complete_explicit_present_required_for_present": true, + "complete_explicit_absent_required_for_absent": true, + "error_is_absence": false, + "incomplete_is_absence": false, + "ambiguous_is_absence": false, + "shell_forbidden": true, + "directory_enumeration_forbidden": true, + "title_launch_forbidden": true, + "app_termination_forbidden": true, + "device_write_forbidden": true, + "retry_forbidden": true, + "reconnect_forbidden": true + }, + "current_evidence": { + "source_bound_query_abi_available": false, + "exact_literal_title_path_available": false, + "exact_path_provenance_available": false, + "request_bytes_formable": false, + "live_result_transport_available": false, + "candidate_plan_formable": false, + "host_synthetic_model_is_device_evidence": false + }, + "implementation_boundary": { + "network_import_present": false, + "socket_present": false, + "cli_present": false, + "clock_present": false, + "file_output_present": false, + "target_source_present": false, + "target_artifact_present": false, + "live_adapter_present": false + }, + "authorizations": { + "target_source_authorized": false, + "target_build_authorized": false, + "ps5_connection_authorized": false, + "shsrv_connection_authorized": false, + "device_request_authorized": false, + "result_receive_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "device_write_authorized": false, + "automatic_retry": false + }, + "decision": { + "host_observer_contract_complete": true, + "live_observer_implementation_allowed": false, + "live_observation_allowed": false, + "device_action_allowed": false, + "next_step": "OFFLINE_SOURCE_AUDIT_FOR_ONE_NONMUTATING_TITLE_PRESENCE_PRIMITIVE" + } +} diff --git a/manifests/retroarch/phase-1.0dg-title-presence-primitive-audit.json b/manifests/retroarch/phase-1.0dg-title-presence-primitive-audit.json new file mode 100644 index 0000000..d5e6042 --- /dev/null +++ b/manifests/retroarch/phase-1.0dg-title-presence-primitive-audit.json @@ -0,0 +1,76 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0DG_TITLE_PRESENCE_PRIMITIVE_AUDIT", + "status": "OFFLINE_SNAPSHOT_QUERY_PRIMITIVE_SOURCE_BOUND_LIVE_ACQUISITION_BLOCKED", + "date": "2026-07-30", + "source_bindings": { + "phase10df_manifest_sha256": "b0cce485969e20ba607311afc9b9d566d135a1cf0ab1ac240ef95c69f9bcba5f", + "y2jb_repository": "https://github.com/Gezine/Y2JB.git", + "y2jb_commit": "0dbbf4e7e0203af7e5d101a3256c634edf4e3ba2", + "readme_size": 7500, + "readme_sha256": "16bfdaa624b8b04f4a6a4a7d512ca8473ad2db73e80df39a974267403e34f751", + "editor_size": 2008, + "editor_sha256": "c1bcb453660f597cbc9026dba76519a4929fc3e967c183deec8a1ca56912e2e8" + }, + "audited_primitive": { + "method": "OFFLINE_SQLITE_SNAPSHOT_SELECT", + "source_bound_device_path": "/system_data/priv/mms/appinfo.db", + "source_bound_table": "tbl_appinfo", + "source_bound_title_column": "titleId", + "parameterized_predicate_supported_by_source": true, + "candidate_query": "SELECT 1 FROM tbl_appinfo WHERE titleId = ? LIMIT 1", + "query_is_present_in_upstream_verbatim": false, + "query_is_minimal_inference_from_source_schema": true, + "requires_already_acquired_byte_exact_snapshot": true, + "opens_snapshot_read_only": true, + "modifies_snapshot": false, + "modifies_device": false + }, + "evidence_limits": { + "y2jb_titles_are_ppsa01650_through_ppsa01652_not_ppsa01659": true, + "ppsa01659_presence_proven": false, + "firmware_960_snapshot_observed": false, + "live_database_path_access_proven": false, + "safe_snapshot_acquisition_path_available": false, + "existing_stack_binary_safe_readback_available": false, + "database_consistency_during_live_copy_proven": false, + "sqlite_read_has_zero_low_level_effects_proven": false, + "offline_snapshot_result_is_live_device_evidence": false, + "global_absence_claimed": false + }, + "forbidden_scope": { + "database_write": true, + "database_backup_creation": true, + "ftp_retrieval": true, + "shsrv_shell": true, + "directory_enumeration": true, + "title_launch": true, + "app_termination": true, + "device_connection": true, + "transfer": true, + "execution": true, + "retry": true, + "reconnect": true + }, + "authorizations": { + "snapshot_acquisition_authorized": false, + "target_source_authorized": false, + "target_build_authorized": false, + "ps5_connection_authorized": false, + "shsrv_connection_authorized": false, + "device_request_authorized": false, + "result_receive_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "device_write_authorized": false, + "automatic_retry": false + }, + "decision": { + "offline_primitive_source_bound": true, + "df_exact_path_metadata_candidate_supported": true, + "live_request_formable": false, + "live_observation_allowed": false, + "device_action_allowed": false, + "next_step": "OFFLINE_HASH_BOUND_SNAPSHOT_QUERY_CONTRACT" + } +} diff --git a/manifests/retroarch/phase-1.0dh-hash-bound-snapshot-query.json b/manifests/retroarch/phase-1.0dh-hash-bound-snapshot-query.json new file mode 100644 index 0000000..f2b52ad --- /dev/null +++ b/manifests/retroarch/phase-1.0dh-hash-bound-snapshot-query.json @@ -0,0 +1,57 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0DH_HASH_BOUND_SNAPSHOT_QUERY", + "status": "OFFLINE_QUERY_IMPLEMENTED_NO_SNAPSHOT_NO_LIVE_ACQUISITION", + "date": "2026-07-30", + "activation": { + "snapshot_path": null, + "snapshot_size": null, + "snapshot_sha256": null, + "result": null + }, + "contract": { + "fixed_title_id": "PPSA01659", + "maximum_snapshot_bytes": 67108864, + "sqlite_uri_mode": "ro", + "sqlite_immutable": true, + "query_only_pragma": true, + "parameterized_query": "SELECT 1 FROM tbl_appinfo WHERE titleId = ? LIMIT 2", + "duplicate_rows_are_unknown": true, + "schema_error_is_unknown": true, + "hash_mismatch_is_rejected": true, + "writes_output_file": false, + "acquires_snapshot": false + }, + "implementation": { + "source": "tools/phase10dh_snapshot_query.py", + "tests": "tests/test_phase10dh_snapshot_query.py", + "network_import_present": false, + "socket_present": false, + "cli_present": false, + "target_source_present": false, + "target_artifact_present": false + }, + "remaining_gates": { + "byte_exact_snapshot_supplied": false, + "snapshot_from_target_firmware_960_proven": false, + "safe_consistent_acquisition_path_proven": false, + "binary_safe_readback_path_available": false, + "ppsa01659_presence_proven": false + }, + "authorizations": { + "snapshot_acquisition_authorized": false, + "ps5_connection_authorized": false, + "device_request_authorized": false, + "result_receive_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "device_write_authorized": false, + "automatic_retry": false + }, + "decision": { + "offline_query_complete": true, + "live_acquisition_implementation_allowed": false, + "device_action_allowed": false, + "next_step": "OFFLINE_LIVE_ACQUISITION_FEASIBILITY_AND_MINIMUM_AUTHORIZATION_AUDIT" + } +} diff --git a/manifests/retroarch/phase-1.0di-live-acquisition-boundary.json b/manifests/retroarch/phase-1.0di-live-acquisition-boundary.json new file mode 100644 index 0000000..639a517 --- /dev/null +++ b/manifests/retroarch/phase-1.0di-live-acquisition-boundary.json @@ -0,0 +1,80 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0DI_LIVE_ACQUISITION_BOUNDARY", + "status": "OFFLINE_WORK_COMPLETE_EXACT_CONSOLE_PERMISSION_REQUIRED", + "date": "2026-07-30", + "acquisition_audit": { + "controlled_elfldr_binary_readback": false, + "payload_manager_binary_readback": false, + "shsrv_v07_cat_raw_stdout_candidate": true, + "shsrv_v09_through_v019_cat_nvt_text_framed": true, + "nvt_cat_is_binary_safe": false, + "deployed_shsrv_family_known": false, + "deployed_shsrv_exact_identity_known": false, + "direct_snapshot_acquisition_formable": false, + "grep_string_match_proves_tbl_appinfo_row": false, + "offline_alternative_remaining": false + }, + "minimum_observation_window": { + "name": "DI_T2_SHSRV_FAMILY_FINGERPRINT", + "target": null, + "port": 2323, + "connections": 1, + "outbound_batches": 1, + "exact_command_ascii": "help\n", + "exact_command_sha256": "41ae84877dd3ce346a7d97fc7f493c8e9dcacfa67ce6974ace56baecee9a8411", + "maximum_received_bytes": 65536, + "hard_deadline_seconds": 10, + "result": "SOURCE_FAMILY_FINGERPRINT_ONLY", + "raw_serial_retained": false, + "raw_telemetry_retained": false, + "raw_transcript_retained": false, + "device_file_read": false, + "device_write": false, + "payload_transfer": false, + "payload_execution": false, + "title_launch": false, + "app_termination": false, + "retry": false, + "reconnect": false, + "active": false, + "run_id": null, + "not_before": null, + "not_after": null + }, + "required_explicit_permission_fields": [ + "phase", + "window_name", + "numeric_target", + "port", + "run_id", + "not_before", + "not_after", + "one_connection", + "one_exact_help_batch", + "bounded_receive", + "result_reception", + "no_retry", + "no_reconnect", + "no_device_write", + "no_transfer", + "no_execution" + ], + "authorizations": { + "ps5_connection_authorized": false, + "shsrv_connection_authorized": false, + "device_request_authorized": false, + "result_receive_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "device_write_authorized": false, + "automatic_retry": false + }, + "decision": { + "all_safe_offline_prerequisites_complete": true, + "connection_is_next_required_action": true, + "tracked_activation_may_be_enabled": false, + "device_action_allowed": false, + "next_step": "REQUEST_EXACT_DI_T2_CONSOLE_CONNECTION_PERMISSION" + } +} diff --git a/manifests/retroarch/phase-1.0dj-di-t2-result.json b/manifests/retroarch/phase-1.0dj-di-t2-result.json new file mode 100644 index 0000000..28f7602 --- /dev/null +++ b/manifests/retroarch/phase-1.0dj-di-t2-result.json @@ -0,0 +1,53 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0DJ_DI_T2_RESULT", + "status": "AUTHORIZED_ATTEMPT_CONSUMED_CONNECT_FAILED", + "date": "2026-07-30", + "run": { + "run_id": "CHIMERA_DI_T2_001", + "target": null, + "authorized_numeric_target_was_used_but_not_retained": true, + "port": 2323, + "command_sha256": "41ae84877dd3ce346a7d97fc7f493c8e9dcacfa67ce6974ace56baecee9a8411", + "connection_attempts": 1, + "connection_established": false, + "command_bytes_sent": 0, + "received_bytes": 0, + "retry_performed": false, + "reconnect_performed": false, + "raw_transcript_retained": false, + "serial_retained": false, + "telemetry_retained": false, + "device_file_read": false, + "device_write": false, + "payload_transfer": false, + "payload_execution": false + }, + "evidence": { + "shsrv_source_family_observed": false, + "shsrv_exact_identity_observed": false, + "port_2323_listener_observed": false, + "target_reachability_inferred": false, + "failure_identifies_root_cause": false, + "snapshot_acquisition_route_selected": false + }, + "authorization_state": { + "di_t2_authorization_consumed": true, + "ps5_connection_authorized": false, + "shsrv_connection_authorized": false, + "device_request_authorized": false, + "result_receive_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "device_write_authorized": false, + "automatic_retry": false + }, + "decision": { + "retry_allowed": false, + "offline_root_cause_determinable": false, + "operator_must_confirm_shsrv_listener_ready": true, + "new_exact_permission_required_after_readiness_change": true, + "device_action_allowed": false, + "next_step": "OPERATOR_CONFIRM_SHSRV_PORT_2323_LISTENER_READY_THEN_REQUEST_NEW_EXACT_DI_T2_PERMISSION" + } +} diff --git a/manifests/retroarch/phase-1.0dk-port-status-result.json b/manifests/retroarch/phase-1.0dk-port-status-result.json new file mode 100644 index 0000000..eb54ee4 --- /dev/null +++ b/manifests/retroarch/phase-1.0dk-port-status-result.json @@ -0,0 +1,44 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0DK_PORT_STATUS_RESULT", + "status": "AUTHORIZED_STATUS_CHECK_CONSUMED_ELFLDR_OPEN_SHSRV_REFUSED", + "date": "2026-07-30", + "target": null, + "authorized_numeric_target_was_used_but_not_retained": true, + "observations": [ + {"port": 2323, "classification": "REFUSED", "attempts": 1, "timeout_seconds": 3}, + {"port": 9021, "classification": "OPEN", "attempts": 1, "timeout_seconds": 3} + ], + "effects": { + "application_bytes_sent": 0, + "application_bytes_received": 0, + "transfer": false, + "execution": false, + "device_write": false, + "retry": false, + "reconnect": false + }, + "interpretation": { + "shsrv_listener_observed": false, + "elfldr_listener_observed": true, + "elfldr_exact_identity_proven": false, + "port_9021_application_protocol_exercised": false, + "shsrv_installation_state_proven": false, + "root_cause_of_absent_listener_proven": false + }, + "authorization_state": { + "dk_authorization_consumed": true, + "ps5_connection_authorized": false, + "device_request_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "device_write_authorized": false, + "automatic_retry": false + }, + "decision": { + "shsrv_fingerprint_attempt_formable_now": false, + "offline_shsrv_artifact_provenance_audit_allowed": true, + "device_action_allowed": false, + "next_step": "OFFLINE_OFFICIAL_SHSRV_ARTIFACT_PROVENANCE_AND_EFFECT_AUDIT" + } +} diff --git a/manifests/retroarch/phase-1.0dl-official-shsrv-artifact-audit.json b/manifests/retroarch/phase-1.0dl-official-shsrv-artifact-audit.json new file mode 100644 index 0000000..a6f7142 --- /dev/null +++ b/manifests/retroarch/phase-1.0dl-official-shsrv-artifact-audit.json @@ -0,0 +1,58 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0DL_OFFICIAL_SHSRV_ARTIFACT_AUDIT", + "status": "OFFICIAL_ARTIFACT_BOUND_EXECUTION_BLOCKED_UNBOUNDED_AND_RWE", + "date": "2026-07-30", + "artifact": { + "release": "v0.19", + "source_commit": "6f320637d56d344a0e7797753099e33238bbf146", + "source_tree": "c26ce02b6c3ca4202993e039b3db7c28c353dee4", + "download_url": "https://github.com/ps5-payload-dev/shsrv/releases/download/v0.19/shsrv-ps5.elf", + "filename": "shsrv-ps5.elf", + "size": 981872, + "sha256": "797827b50a93dbf327abdca45063d76ea24b17e561d927f397a0b73cadb2e2bd", + "stored_only_under_ignored_work": true, + "executed_on_host": false, + "sent_to_device": false + }, + "static_elf": { + "needed": ["libkernel_sys.sprx", "libSceLibcInternal.sprx", "libSceNet.sprx"], + "load_segment_flags": ["RWE", "RW", "RW"], + "writable_executable_segment_present": true + }, + "source_effects": { + "default_listener_port": 2323, + "opens_dev_console_read_write": true, + "creates_listener_socket": true, + "binds_and_listens": true, + "accept_loop_is_unbounded": true, + "per_connection_threads": true, + "startup_notification": true, + "normal_sdk_crt_effects_inherited": true, + "automatic_self_termination_deadline": false, + "persistent_filesystem_write_required_for_startup": false + }, + "hard_blockers": [ + "WRITABLE_EXECUTABLE_LOAD_SEGMENT", + "UNBOUNDED_ACCEPT_LOOP", + "UNBOUNDED_PROCESS_LIFETIME", + "CONNECTION_THREAD_LIFETIME_UNBOUNDED", + "COMPLETE_TARGET_CLEANUP_UNPROVEN" + ], + "authorizations": { + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "device_write_authorized": false, + "automatic_retry": false + }, + "decision": { + "official_artifact_provenance_bound": true, + "official_artifact_execution_eligible": false, + "official_artifact_transfer_eligible": false, + "shsrv_route_selected": false, + "direct_bounded_raw_elfldr_snapshot_reader_design_allowed": true, + "device_action_allowed": false, + "next_step": "OFFLINE_BOUNDED_RAW_ELFLDR_SNAPSHOT_READER_DESIGN" + } +} diff --git a/manifests/retroarch/phase-1.0dm-bounded-snapshot-observer.json b/manifests/retroarch/phase-1.0dm-bounded-snapshot-observer.json new file mode 100644 index 0000000..662c594 --- /dev/null +++ b/manifests/retroarch/phase-1.0dm-bounded-snapshot-observer.json @@ -0,0 +1,63 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0DM_BOUNDED_SNAPSHOT_OBSERVER", + "status": "OFFLINE_ARTIFACT_BUILT_AUDITED_INACTIVE", + "date": "2026-07-30", + "artifact": { + "name": "chimera_title_snapshot_observer.elf", + "size": 109896, + "sha256": "147b5bede0f0b5b7d2be903bc72ff0d0541a2cdc28eae7d86b6bf95e1978ebdf", + "two_consecutive_build_hashes_equal": true, + "ignored_build_output": true, + "execution_eligible": false, + "transfer_eligible": false, + "installation_eligible": false + }, + "source": { + "path": "../chimera-retroarch/pkg/ps5/chimera_ps5_title_snapshot_observer.c", + "size": 3733, + "sha256": "ddc422d2e5afa779607d56bf6ab14395da76e8df070fe73ce6d0bca8d56559d1", + "sdk": "ps5-payload-sdk-v0.41", + "sdk_linker_script_sha256": "3bacf56a21602a1298752023a4c5b0c305954ddf6cdbce1464476fc9da9d9093", + "derived_text_segment_flags": "RE" + }, + "static_elf": { + "load_segment_flags": ["RE", "RW", "RW"], + "writable_executable_segment_present": false, + "needed": ["libkernel_web.sprx", "libSceLibcInternal.sprx", "libSceNet.sprx"] + }, + "target_contract": { + "firmware": "9.60", + "fixed_path": "/system_data/priv/mms/appinfo.db", + "open_flags": ["O_RDONLY", "O_CLOEXEC", "O_NOFOLLOW"], + "maximum_snapshot_bytes": 67108864, + "total_alarm_seconds": 20, + "frame_magic": "CHS10DM1", + "frame_size": 64, + "single_inherited_descriptor": 1, + "socket_creation": false, + "connect": false, + "listener": false, + "filesystem_write": false, + "retry": false, + "metadata_before_after": true, + "terminal_frame_after_exact_payload": true, + "normal_sdk_crt_runtime_effects": true + }, + "remaining_gates": { + "host_stream_receiver_implemented": false, + "exclusive_consumed_receipt_implemented": false, + "artifact_specific_approval_present": false, + "target_file_read_authorized": false, + "transfer_authorized": false, + "execution_authorized": false, + "result_reception_authorized": false, + "firmware_runtime_behavior_proven": false, + "complete_cleanup_proven": false + }, + "decision": { + "offline_artifact_complete": true, + "device_action_allowed": false, + "next_step": "INACTIVE_ONE_SHOT_STREAM_RECEIVER_AND_ACTIVATION_GATE" + } +} diff --git a/manifests/retroarch/phase-1.0dn-inactive-snapshot-receiver.json b/manifests/retroarch/phase-1.0dn-inactive-snapshot-receiver.json new file mode 100644 index 0000000..2dda6ec --- /dev/null +++ b/manifests/retroarch/phase-1.0dn-inactive-snapshot-receiver.json @@ -0,0 +1,11 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0DN_INACTIVE_SNAPSHOT_RECEIVER", + "status": "OFFLINE_STREAM_RECEIVER_COMPLETE_LIVE_ADAPTER_ABSENT", + "date": "2026-07-30", + "artifact": {"name":"chimera_title_snapshot_observer.elf","size":109896,"sha256":"147b5bede0f0b5b7d2be903bc72ff0d0541a2cdc28eae7d86b6bf95e1978ebdf","execution_eligible":false,"transfer_eligible":false}, + "receiver": {"network_import_present":false,"exclusive_output":true,"incremental_sha256":true,"maximum_payload_bytes":67108864,"exact_terminal_required":true,"sqlite_header_required":true,"fsync_before_success":true,"partial_is_success":false,"overwrite_allowed":false}, + "activation": {"active":false,"target":null,"port":null,"run_id":null,"not_before":null,"not_after":null,"output_path":null}, + "authorizations": {"ps5_connection_authorized":false,"target_file_read_authorized":false,"device_transfer_authorized":false,"device_execution_authorized":false,"result_receive_authorized":false,"device_write_authorized":false,"automatic_retry":false}, + "decision": {"offline_receiver_complete":true,"live_adapter_allowed":false,"device_action_allowed":false,"next_step":"INACTIVE_EXACT_ARTIFACT_ONE_SHOT_LIVE_RUNNER"} +} diff --git a/manifests/retroarch/phase-1.0do-inactive-snapshot-runner.json b/manifests/retroarch/phase-1.0do-inactive-snapshot-runner.json new file mode 100644 index 0000000..1c260d8 --- /dev/null +++ b/manifests/retroarch/phase-1.0do-inactive-snapshot-runner.json @@ -0,0 +1 @@ +{"active":false,"run_id":null,"target":null,"port":9021,"artifact_size":109896,"artifact_sha256":"147b5bede0f0b5b7d2be903bc72ff0d0541a2cdc28eae7d86b6bf95e1978ebdf","snapshot_path":null,"receipt_path":null,"not_before":null,"not_after":null,"one_connection":false,"one_transfer":false,"one_execution":false,"result_receive":false,"target_file_read":false,"device_write":false,"installation":false,"autoload":false,"retry":false,"reconnect":false} diff --git a/manifests/retroarch/phase-1.0do-snapshot-result.json b/manifests/retroarch/phase-1.0do-snapshot-result.json new file mode 100644 index 0000000..211cb62 --- /dev/null +++ b/manifests/retroarch/phase-1.0do-snapshot-result.json @@ -0,0 +1,15 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0DO_BOUNDED_SNAPSHOT_RESULT", + "status": "SUCCESS_AUTHORIZATION_CONSUMED", + "date": "2026-07-30", + "run_id": "CHIMERA_1_0DO_001", + "authorized_numeric_target_retained": false, + "route": {"port": 9021, "connections": 1, "transfers": 1, "executions": 1, "write_half_closes": 1, "retries": 0, "reconnects": 0}, + "artifact": {"name": "chimera_title_snapshot_observer.elf", "size": 109896, "sha256": "147b5bede0f0b5b7d2be903bc72ff0d0541a2cdc28eae7d86b6bf95e1978ebdf"}, + "snapshot": {"retained_in_git": false, "size": 1306624, "sha256": "5a66279266c27228431f7d8acb9c70a803fba8912bd6285cf36a1da2f2210fcf", "sqlite_integrity": "ok", "table_count": 3, "appinfo_rows": 5803, "distinct_title_ids": 69}, + "bounded_observations": {"homebrew_launcher_appinfo_present": true, "homebrew_launcher_title_id": "FAKE00000", "payload_manager_appinfo_present": true, "payload_manager_title_id": "PLDM00001", "itemzflow_appinfo_present": true, "itemzflow_title_id": "ITEM00001"}, + "not_proven": ["active_launch_state", "deployed_executable_identity", "hbldr_or_shsrv_identity", "VideoOut_ownership", "visible_output", "complete_target_cleanup"], + "authorization": {"consumed": true, "retry_authorized": false, "reconnect_authorized": false, "future_device_action_authorized": false}, + "next_step": "OFFLINE_APPINFO_CORRELATION_AND_NEW_SEPARATE_GATE" +} diff --git a/manifests/retroarch/phase-1.0dp-listener-status-result.json b/manifests/retroarch/phase-1.0dp-listener-status-result.json new file mode 100644 index 0000000..d8c5c79 --- /dev/null +++ b/manifests/retroarch/phase-1.0dp-listener-status-result.json @@ -0,0 +1,19 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0DP_LISTENER_STATUS", + "status": "REFUSED_AUTHORIZATION_CONSUMED", + "date": "2026-07-30", + "run_id": "CHIMERA_1_0DP_001", + "target_retained": false, + "port": 2323, + "connect_attempts": 1, + "timeout_seconds": 3, + "classification": "REFUSED", + "application_bytes_sent": 0, + "application_bytes_received": 0, + "retry_count": 0, + "reconnect_count": 0, + "authorization_consumed": true, + "not_proven": ["launcher_binary_identity", "launcher_failure_cause", "listener_absent_at_other_times"], + "next_step": "OFFLINE_BOUNDED_FAKE00000_INVENTORY_OBSERVER" +} diff --git a/manifests/retroarch/phase-1.0dq-inventory-observer.json b/manifests/retroarch/phase-1.0dq-inventory-observer.json new file mode 100644 index 0000000..7a5a79d --- /dev/null +++ b/manifests/retroarch/phase-1.0dq-inventory-observer.json @@ -0,0 +1 @@ +{"schema_version":1,"phase":"PHASE_1_0DQ_BOUNDED_FAKE00000_INVENTORY","status":"OFFLINE_ARTIFACT_BUILT_AUDITED_INACTIVE","artifact":{"name":"chimera_fake00000_inventory_observer.elf","size":110032,"sha256":"914fce06a490ad048fdd0a85ae117858e8904b47c72054bf12fbfebc213a6db8","execution_eligible":false,"transfer_eligible":false,"installation_eligible":false},"source":{"path":"../chimera-retroarch/pkg/ps5/chimera_ps5_fake00000_inventory_observer.c","size":5457,"sha256":"7889796a708a022d146d00f4625f45caaf9f06ac7cc2391bc1596d3c24dd16ab"},"contract":{"path":"/user/app/FAKE00000","recursive":false,"maximum_entries":256,"entry_bytes":320,"maximum_wire_bytes":82048,"deadline_seconds":10,"file_contents_read":false,"symlinks_followed":false,"filesystem_write":false,"possible_directory_atime_effect":true,"retry":false},"authorization":{"connection":false,"transfer":false,"execution":false,"result_receive":false,"directory_inventory":false,"automatic_retry":false}} diff --git a/manifests/retroarch/phase-1.0dr-inactive-inventory-runner.json b/manifests/retroarch/phase-1.0dr-inactive-inventory-runner.json new file mode 100644 index 0000000..e833cfa --- /dev/null +++ b/manifests/retroarch/phase-1.0dr-inactive-inventory-runner.json @@ -0,0 +1 @@ +{"active":false,"run_id":null,"target":null,"port":9021,"artifact_size":110032,"artifact_sha256":"914fce06a490ad048fdd0a85ae117858e8904b47c72054bf12fbfebc213a6db8","output_path":null,"receipt_path":null,"not_before":null,"not_after":null,"one_connection":false,"one_transfer":false,"one_execution":false,"result_receive":false,"directory_inventory":false,"possible_atime_effect_acknowledged":false,"device_file_content_read":false,"persistent_device_write":false,"installation":false,"autoload":false,"retry":false,"reconnect":false} diff --git a/manifests/retroarch/phase-1.0dr-inventory-result.json b/manifests/retroarch/phase-1.0dr-inventory-result.json new file mode 100644 index 0000000..3eecb1e --- /dev/null +++ b/manifests/retroarch/phase-1.0dr-inventory-result.json @@ -0,0 +1,14 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0DR_FAKE00000_INVENTORY_RESULT", + "status": "SUCCESS_AUTHORIZATION_CONSUMED", + "date": "2026-07-30", + "run_id": "CHIMERA_1_0DR_001", + "target_retained": false, + "artifact": {"size": 110032, "sha256": "914fce06a490ad048fdd0a85ae117858e8904b47c72054bf12fbfebc213a6db8"}, + "result": {"entry_count": 6, "local_json_size": 708, "local_json_sha256": "5f31ca53e357ce299fa76f4d064c3b502e43966bf8854b908bf0d6d9e7658f81", "retained_in_git": false}, + "entries": [{"name":"app.pbm","size":323},{"name":"app.pbm.backup","size":323},{"name":"app.json","size":257},{"name":"app.xml","size":298},{"name":"app.crc","size":1108},{"name":"app.pkg","size":18153472}], + "observations": {"loose_eboot_present": false, "package_present": true, "file_contents_read": false}, + "authorization": {"consumed": true, "retry": false, "reconnect": false, "future_device_action": false}, + "next_step": "OFFLINE_BOUNDED_SMALL_METADATA_OBSERVER_EXCLUDING_APP_PKG" +} diff --git a/manifests/retroarch/phase-1.0ds-metadata-observer.json b/manifests/retroarch/phase-1.0ds-metadata-observer.json new file mode 100644 index 0000000..aeafe7d --- /dev/null +++ b/manifests/retroarch/phase-1.0ds-metadata-observer.json @@ -0,0 +1 @@ +{"schema_version":1,"phase":"PHASE_1_0DS_BOUNDED_FAKE00000_METADATA","status":"OFFLINE_ARTIFACT_BUILT_AUDITED_INACTIVE","artifact":{"name":"chimera_fake00000_metadata_observer.elf","size":109928,"sha256":"077307b98e44f566fa1db82b08cd5e71bd56bd9792826fc7254965fa768c0dc7","execution_eligible":false,"transfer_eligible":false},"source":{"size":3680,"sha256":"9997c38e557cb2b7d893c7c22a5cf02c0e7f043f5d84a2584a3ab0f98a626833"},"allowlist":["app.pbm","app.json","app.xml","app.crc"],"excluded":["app.pkg","app.pbm.backup"],"maximum_each":4096,"maximum_wire":16768,"deadline_seconds":10,"possible_atime_effect":true,"authorization":{"connection":false,"transfer":false,"execution":false,"receive":false}} diff --git a/manifests/retroarch/phase-1.0dt-inactive-metadata-runner.json b/manifests/retroarch/phase-1.0dt-inactive-metadata-runner.json new file mode 100644 index 0000000..8e7c799 --- /dev/null +++ b/manifests/retroarch/phase-1.0dt-inactive-metadata-runner.json @@ -0,0 +1 @@ +{"active":false,"run_id":null,"target":null,"port":9021,"artifact_size":109928,"artifact_sha256":"077307b98e44f566fa1db82b08cd5e71bd56bd9792826fc7254965fa768c0dc7","output_path":null,"receipt_path":null,"not_before":null,"not_after":null,"one_connection":false,"one_transfer":false,"one_execution":false,"result_receive":false,"four_exact_metadata_reads":false,"possible_atime_effect_acknowledged":false,"app_pkg_read":false,"backup_read":false,"persistent_device_write":false,"installation":false,"autoload":false,"retry":false,"reconnect":false} diff --git a/manifests/retroarch/phase-1.0dt-metadata-result.json b/manifests/retroarch/phase-1.0dt-metadata-result.json new file mode 100644 index 0000000..756a985 --- /dev/null +++ b/manifests/retroarch/phase-1.0dt-metadata-result.json @@ -0,0 +1,15 @@ +{ + "schema_version":1, + "phase":"PHASE_1_0DT_FAKE00000_METADATA_RESULT", + "status":"SUCCESS_AUTHORIZATION_CONSUMED", + "date":"2026-07-30", + "run_id":"CHIMERA_1_0DT_001", + "target_retained":false, + "artifact":{"size":109928,"sha256":"077307b98e44f566fa1db82b08cd5e71bd56bd9792826fc7254965fa768c0dc7"}, + "result":{"local_json_size":3173,"local_json_sha256":"88ab1326d106f342ad5ea70f8b40a7ae1ffa0afa3288c80b0e06795a9af6c85a","retained_in_git":false}, + "files":{"app.pbm":{"size":323,"sha256":"3bf2b1014fefe0dcc4aadf3d4a3c077a5527c9f922576c3d470aaa01524bfa12"},"app.json":{"size":257,"sha256":"9211256ecfe7b7a5430de99f93edac989dd8df122ecde933f4b2aed5b5d6b2fb"},"app.xml":{"size":298,"sha256":"147b9d45474ad58da485ab82377c7cd93cd2cf3f407ed3c90e5d5a3297c68e27"},"app.crc":{"size":1108,"sha256":"60c5247c8e93532345aeb747326b5b5b37760fcf2b1f58406ad1cb87304e9aa3"}}, + "bounded_findings":{"fake":true,"number_of_split_files":1,"original_file_size":18153472,"source_path":"/mnt/usb0/IV9999-FAKE00000_00-HOMEBREWLOADER01.pkg","pbm_digest_matches_json_digest":true}, + "excluded":{"app_pkg_read":true,"backup_read":true}, + "authorization":{"consumed":true,"retry":false,"reconnect":false,"future_device_action":false}, + "next_step":"OFFLINE_EXACT_LOCAL_AND_USB_PACKAGE_STAT_OBSERVER" +} diff --git a/manifests/retroarch/phase-1.0du-package-stat.json b/manifests/retroarch/phase-1.0du-package-stat.json new file mode 100644 index 0000000..290c92f --- /dev/null +++ b/manifests/retroarch/phase-1.0du-package-stat.json @@ -0,0 +1 @@ +{"phase":"PHASE_1_0DU_EXACT_PACKAGE_STAT","status":"OFFLINE_ARTIFACT_BUILT_AUDITED_INACTIVE","artifact":{"size":109688,"sha256":"25b972ac202050ab5a9c1c89651b27e841cdf788d19c93bf75f051c9c8acd403","execution_eligible":false,"transfer_eligible":false},"source":{"size":1420,"sha256":"6f609be548cc5c9009c8f228ee5f96eaa8d6f06a02d6f0733449d06e8298cbf2"},"paths":["/user/app/FAKE00000/app.pkg","/mnt/usb0/IV9999-FAKE00000_00-HOMEBREWLOADER01.pkg"],"contract":{"lstat_only":true,"content_read":false,"wire_bytes":144,"deadline_seconds":5},"authorization":{"all":false}} diff --git a/manifests/retroarch/phase-1.0dv-inactive-package-stat-runner.json b/manifests/retroarch/phase-1.0dv-inactive-package-stat-runner.json new file mode 100644 index 0000000..6ef1cfd --- /dev/null +++ b/manifests/retroarch/phase-1.0dv-inactive-package-stat-runner.json @@ -0,0 +1 @@ +{"active":false,"run_id":null,"target":null,"port":9021,"artifact_size":109688,"artifact_sha256":"25b972ac202050ab5a9c1c89651b27e841cdf788d19c93bf75f051c9c8acd403","output_path":null,"receipt_path":null,"not_before":null,"not_after":null,"one_connection":false,"one_transfer":false,"one_execution":false,"result_receive":false,"two_exact_lstat":false,"file_content_read":false,"device_write":false,"retry":false,"reconnect":false} diff --git a/manifests/retroarch/phase-1.0dv-package-stat-result.json b/manifests/retroarch/phase-1.0dv-package-stat-result.json new file mode 100644 index 0000000..5eca125 --- /dev/null +++ b/manifests/retroarch/phase-1.0dv-package-stat-result.json @@ -0,0 +1,15 @@ +{ + "schema_version":1, + "phase":"PHASE_1_0DV_EXACT_PACKAGE_STAT_RESULT", + "status":"USB_SOURCE_MISSING_AUTHORIZATION_CONSUMED", + "date":"2026-07-30", + "run_id":"CHIMERA_1_0DV_001", + "target_retained":false, + "artifact":{"size":109688,"sha256":"25b972ac202050ab5a9c1c89651b27e841cdf788d19c93bf75f051c9c8acd403"}, + "result":{"local_json_size":399,"local_json_sha256":"4b28421b453af9dbbfbe44d35f47e40fcf7a1df6fcf810e7897fc227e9b574eb","retained_in_git":false}, + "local_package":{"path":"/user/app/FAKE00000/app.pkg","exists":true,"size":18153472,"mode":33272}, + "usb_source":{"path":"/mnt/usb0/IV9999-FAKE00000_00-HOMEBREWLOADER01.pkg","exists":false,"errno":2,"classification":"ENOENT"}, + "content_read":false, + "authorization":{"consumed":true,"retry":false,"reconnect":false,"future_device_action":false}, + "next_step":"OFFLINE_EXACT_LOCAL_PACKAGE_READBACK_ARTIFACT" +} diff --git a/manifests/retroarch/phase-1.0dw-package-readback.json b/manifests/retroarch/phase-1.0dw-package-readback.json new file mode 100644 index 0000000..1c6a4bc --- /dev/null +++ b/manifests/retroarch/phase-1.0dw-package-readback.json @@ -0,0 +1 @@ +{"phase":"PHASE_1_0DW_EXACT_PACKAGE_READBACK","status":"OFFLINE_ARTIFACT_BUILT_AUDITED_INACTIVE","artifact":{"size":109904,"sha256":"b41763f9261b838be2f83b0ee1c7b9bb5e07ced856e5638e3357de37e4405394","execution_eligible":false,"transfer_eligible":false},"source":{"size":2866,"sha256":"fed9f1e8754120c6f1815f5a1212c145a150839a7daff4ace7ef62b9aa235a1c"},"path":"/user/app/FAKE00000/app.pkg","exact_size":18153472,"expected_sha256":"dbcdd4dbc6303fc7a94aa0e8bb3e2c7de1d8b5770c6a30cf1ce50bc6e373aa7e","deadline_seconds":30,"authorization":{"all":false}} diff --git a/manifests/retroarch/phase-1.0dx-inactive-package-readback-runner.json b/manifests/retroarch/phase-1.0dx-inactive-package-readback-runner.json new file mode 100644 index 0000000..7421a63 --- /dev/null +++ b/manifests/retroarch/phase-1.0dx-inactive-package-readback-runner.json @@ -0,0 +1 @@ +{"active":false,"run_id":null,"target":null,"port":9021,"artifact_size":109904,"artifact_sha256":"b41763f9261b838be2f83b0ee1c7b9bb5e07ced856e5638e3357de37e4405394","output_path":null,"receipt_path":null,"not_before":null,"not_after":null,"one_connection":false,"one_transfer":false,"one_execution":false,"result_receive":false,"exact_package_read":false,"possible_atime_effect_acknowledged":false,"device_write":false,"usb_read":false,"backup_read":false,"installation":false,"autoload":false,"retry":false,"reconnect":false} diff --git a/manifests/retroarch/phase-1.0dx-package-readback-result.json b/manifests/retroarch/phase-1.0dx-package-readback-result.json new file mode 100644 index 0000000..8085333 --- /dev/null +++ b/manifests/retroarch/phase-1.0dx-package-readback-result.json @@ -0,0 +1,13 @@ +{ + "schema_version":1, + "phase":"PHASE_1_0DX_EXACT_PACKAGE_READBACK_RESULT", + "status":"COMPLETE_BYTES_DIGEST_MISMATCH_AUTHORIZATION_CONSUMED", + "date":"2026-07-30", + "run_id":"CHIMERA_1_0DX_001", + "target_retained":false, + "artifact":{"size":109904,"sha256":"b41763f9261b838be2f83b0ee1c7b9bb5e07ced856e5638e3357de37e4405394"}, + "received":{"size":18153472,"sha256":"604e5b56033bd48dd72b68b6844d72c4d9a730e7ed8ce2577fe4b0d6538a667b","expected_sha256":"dbcdd4dbc6303fc7a94aa0e8bb3e2c7de1d8b5770c6a30cf1ce50bc6e373aa7e","digest_match":false,"retained_in_git":false,"local_classification":"MISMATCH_DO_NOT_USE"}, + "route":{"connections":1,"transfers":1,"executions":1,"retries":0,"reconnects":0}, + "authorization":{"consumed":true,"future_device_action":false}, + "decision":{"copy_received_bytes_to_usb":false,"use_as_recovery_source":false,"next_step":"OBTAIN_INDEPENDENT_EXACT_ORIGINAL_PACKAGE"} +} diff --git a/manifests/retroarch/phase-1.0dy-upstream-websrv-audit.json b/manifests/retroarch/phase-1.0dy-upstream-websrv-audit.json new file mode 100644 index 0000000..6ce669f --- /dev/null +++ b/manifests/retroarch/phase-1.0dy-upstream-websrv-audit.json @@ -0,0 +1,10 @@ +{ + "schema_version":1, + "phase":"PHASE_1_0DY_UPSTREAM_WEBSRV_AUDIT", + "status":"UPSTREAM_WEBSRV_EXECUTION_BLOCKED_BROAD_MUTATING_SURFACE", + "date":"2026-07-30", + "upstream":{"repository":"ps5-payload-dev/websrv","tag":"v0.33","commit":"baabe27e5449baeb059b850d0393c31fdee219b7","license":"GPL-3.0-or-later"}, + "source_observations":{"port_8080_listener":true,"filesystem_routes":true,"post_routes":true,"package_installation":true,"persistent_fake_app_creation":true,"ptrace":true,"kernel_credential_writes":true,"rwx_changes":true,"bounded_launch_waits":false}, + "authorization":{"build":false,"transfer":false,"execution":false,"device_write":false,"automatic_retry":false}, + "decision":{"upstream_artifact_acceptable":false,"next_step":"OFFLINE_MINIMAL_BOUNDED_HASH_SCOPED_WEBSRV_HBLDR_DESIGN"} +} diff --git a/manifests/retroarch/phase-1.0dy-websrv-listener-result.json b/manifests/retroarch/phase-1.0dy-websrv-listener-result.json new file mode 100644 index 0000000..0fe0b4f --- /dev/null +++ b/manifests/retroarch/phase-1.0dy-websrv-listener-result.json @@ -0,0 +1,17 @@ +{ + "schema_version":1, + "phase":"PHASE_1_0DY_WEBSRV_LISTENER_STATUS", + "status":"REFUSED_AUTHORIZATION_CONSUMED", + "date":"2026-07-30", + "run_id":"CHIMERA_1_0DY_001", + "target_retained":false, + "port":8080, + "connect_attempts":1, + "classification":"REFUSED", + "application_bytes_sent":0, + "application_bytes_received":0, + "retries":0, + "reconnects":0, + "authorization_consumed":true, + "next_step":"OFFLINE_EXACT_UPSTREAM_WEBSRV_EFFECT_AUDIT" +} diff --git a/manifests/retroarch/phase-1.0dz-direct-launcher-decision.json b/manifests/retroarch/phase-1.0dz-direct-launcher-decision.json new file mode 100644 index 0000000..e7e995c --- /dev/null +++ b/manifests/retroarch/phase-1.0dz-direct-launcher-decision.json @@ -0,0 +1,56 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0DZ_DIRECT_LAUNCHER_DECISION", + "status": "TITLE_PRESENT_DIRECT_ROUTE_SELECTED_TARGET_BUILD_BLOCKED", + "date": "2026-07-30", + "consumed_evidence": { + "firmware": "9.60", + "fake00000_appinfo_present": true, + "operator_observed_launcher_browser_reached": true, + "port_8080_single_attempt": "REFUSED", + "port_8080_refusal_proves_general_listener_absence": false, + "videoout_ownership_proven": false, + "safe_process_replacement_proven": false, + "safe_cleanup_proven": false + }, + "route_decision": { + "loopback_web_server_required": false, + "official_websrv_acceptable": false, + "direct_embedded_hash_bound_retroarch_route_selected": true, + "generic_endpoints_allowed": false, + "persistent_fake_app_setup_allowed": false, + "implicit_bigapp_termination_allowed": false, + "retry_allowed": false, + "unbounded_wait_allowed": false + }, + "remaining_blockers": { + "independent_public_ps5_launch_function_signatures": false, + "independent_public_app_launch_ctx_layout": false, + "bounded_failure_restoration_composition": false, + "firmware_960_runtime_behavior_proven": false + }, + "artifact": { + "target_source_created": false, + "target_artifact_created": false, + "artifact_hash_bound": false, + "execution_eligible": false, + "transfer_eligible": false, + "installation_eligible": false + }, + "authorization": { + "connection": false, + "transfer": false, + "execution": false, + "result_receive": false, + "device_write": false, + "automatic_retry": false + }, + "decision": { + "title_presence_gate_closed": true, + "browser_reachability_is_launch_abi_evidence": false, + "target_source_allowed": false, + "target_build_allowed": false, + "device_action_allowed": false, + "next_step": "INDEPENDENT_PUBLIC_LAUNCH_ABI_AND_BOUNDED_RESTORATION_COMPOSITION_EVIDENCE" + } +} diff --git a/manifests/retroarch/phase-1.0e-result-channel.json b/manifests/retroarch/phase-1.0e-result-channel.json new file mode 100644 index 0000000..024954c --- /dev/null +++ b/manifests/retroarch/phase-1.0e-result-channel.json @@ -0,0 +1,327 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0E_INHERITED_RESULT_CHANNEL", + "status": "ONE_SHOT_DEVICE_TEST_COMPLETED_INCOMPLETE_BEFORE_D03", + "scope": "CONSUMED_ONE_SHOT_DEVICE_EVIDENCE_AND_OFFLINE_REVIEW", + "date": "2026-07-22", + "repository": "chimera-retroarch", + "branch": "codex/ps5-inherited-result-channel", + "source_commit": "b9fc037304a14199f35f8229edac26fa5c840509", + "host_client_commit": "aed1a6e014d56ed25456b8b095955c7d41f7025d", + "baseline_commit": "69b65858ffaee826d70f5c0df61013cd1b0e2048", + "source_bindings": { + "retroarch_release": "69a4f0ea1e8aaf442ae4858f2e7f2b31a1776576", + "ps5_payload_sdk": "d2e2e585740362976a39fdd5ccf390f199a7bc37", + "sdl": "0baf4ac49382b537ba449901b5b6d0d189bb1fbb", + "hardened_elfldr": "197623058f509eddde18868dafcb92fdcac66464", + "lifecycle_notification_source": "fe08300339a13f899fb78ea404ada381a5cba87c" + }, + "authorizations": { + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "result_receive_authorized": false, + "installation_authorized": false, + "autoload_authorized": false, + "device_write_authorized": false, + "automatic_retry": false + }, + "phase_actions": { + "ps5_connected": true, + "device_request_performed": true, + "files_transferred": true, + "device_write_performed": false, + "target_execution_performed": true, + "result_received_from_device": true, + "installation_performed": false, + "autoload_performed": false + }, + "prior_device_observations": { + "evidence_class": "OPERATOR_OBSERVED_ARTIFACT_BOUND", + "run_a": { + "artifact_sha256": "c84c0fb43392ce17abc1e7e6be38b522f1b32a681fd516d684c3dbbf7b34b743", + "visible_result": "CHIMERA C1 — CRT MAIN REACHED", + "classification": "CRT_MAIN_AND_NOTIFICATION_PROVEN_ON_FW_9_60", + "authorization_consumed": true + }, + "run_b": { + "artifact_sha256": "092bcabc0f8717a648030c592aa0dc900f878952fb14ee453851a29f298ea7ee", + "visible_result": "ONE_NOTIFICATION_STAGE_UNREADABLE", + "classification": "PAYLOAD_NOTIFICATION_CODE_EXECUTED_STAGE_UNCLASSIFIED", + "console_remained_responsive": true, + "authorization_consumed": true + } + }, + "artifact": { + "name": "retroarch_ps5_result_diag.elf", + "profile": "result-diag", + "local_relative_path": "build/phase10e/result-a/retroarch_ps5_result_diag.elf", + "linker_map_relative_path": "build/phase10e/result-a/retroarch_ps5_result_diag.map", + "size": 1844880, + "sha256": "1049c78099a60b472a3fb0e2999e3393b6ad76337a28532a7e53872e7772dedf", + "clean_build_sha256": [ + "1049c78099a60b472a3fb0e2999e3393b6ad76337a28532a7e53872e7772dedf", + "1049c78099a60b472a3fb0e2999e3393b6ad76337a28532a7e53872e7772dedf" + ], + "linker_map_size": 636588, + "linker_map_sha256": "ae9739f6f578953bc8dc562bb55967ba587912d161b2d6787438450addec3b44", + "clean_map_sha256": [ + "ae9739f6f578953bc8dc562bb55967ba587912d161b2d6787438450addec3b44", + "ae9739f6f578953bc8dc562bb55967ba587912d161b2d6787438450addec3b44" + ], + "execution_eligible": false, + "transfer_eligible": false, + "installation_eligible": false, + "device_action_performed": true + }, + "active_one_shot": { + "authorized_by": "Jens", + "authorization_date": "2026-07-22", + "approval_record": "docs/approvals/phase-1.0e-one-shot-result-test.md", + "firmware": "9.60", + "artifact_name": "retroarch_ps5_result_diag.elf", + "artifact_size": 1844880, + "artifact_sha256": "1049c78099a60b472a3fb0e2999e3393b6ad76337a28532a7e53872e7772dedf", + "connection_count": 1, + "transfer_count": 1, + "execution_count": 1, + "result_receive_count": 1, + "timeout_seconds": 75, + "automatic_retry": false, + "reconnect": false, + "installation": false, + "autoload": false, + "device_write": false, + "consumed": true + }, + "device_run": { + "run_id": "RUN_C", + "authorization_consumed": true, + "firmware": "9.60", + "trace_relative_path": "build/phase10e/run-c-device-trace.json", + "trace_tracked": false, + "trace_size": 1795, + "trace_sha256": "4ff27a0eac48283cdc4c7ff964226def2689e808e3adea6594d0e77674a676f0", + "connection_count": 1, + "sendall_count": 1, + "shutdown_write_count": 1, + "recv_call_count": 5, + "received_byte_count": 3413, + "discarded_stdout_bytes": 3221, + "retry_count": 0, + "reconnect_count": 0, + "close_called": true, + "remote_eof_observed": true, + "timeout_observed": false, + "parser_errors": [], + "validated_frames": [ + { + "stage": "D00", + "sequence": 1, + "kind": "LITERAL", + "crc32": "93f81433", + "notification_attempted_mask": 1, + "notification_delivered_mask": 1, + "notification_result": 0, + "terminal": false + }, + { + "stage": "D01", + "sequence": 2, + "kind": "LITERAL", + "crc32": "f2b975e4", + "notification_attempted_mask": 3, + "notification_delivered_mask": 3, + "notification_result": 0, + "terminal": false + }, + { + "stage": "D02", + "sequence": 3, + "kind": "RAW", + "crc32": "69173d14", + "raw0": 0, + "notification_attempted_mask": 7, + "notification_delivered_mask": 7, + "notification_result": 0, + "terminal": false + } + ], + "terminal_stage": null, + "last_proven_stage": "D02", + "classification": "REMOTE_PAYLOAD_OUTPUT_PROVEN_INCOMPLETE_BEFORE_D03", + "proves": [ + "TRANSFERRED_ARTIFACT_EXECUTED", + "SDK_CRT_MAIN_REACHED", + "PLATFORM_INIT_BEGIN_REACHED", + "PLATFORM_INIT_COMPLETED_WITH_RAW_ZERO", + "INHERITED_STDOUT_RESULT_CHANNEL_WORKED" + ], + "does_not_prove": [ + "D03_SDL_INIT_BEGIN", + "SDL_INIT_RESULT", + "VIDEOOUT_OPEN", + "FIRST_FLIP", + "RENDERING", + "TERMINAL_STAGE", + "SAFE_EXIT_OR_LOADER_CLEANUP" + ] + }, + "offline_followup": { + "repository": "chimera-retroarch", + "branch": "codex/ps5-inherited-result-channel", + "commit": "f1391c3e6717ff4e2b869007fb9627c9edde52e4", + "classification": "D02_TO_D03_SOURCE_INTERVAL_BOUNDED_CAUSE_UNPROVEN", + "bounded_raw_stream_retention": true, + "bounded_ordinary_stdout_retention": true, + "encoding": "base64", + "sha256_recorded": true, + "maximum_pre_encoding_bytes": 65536, + "exclusive_trace_creation": true, + "consumed_manifest_rejected": true, + "retry": false, + "reconnect": false, + "device_action_performed": false, + "target_code_changed": false, + "target_artifact_created": false, + "authorization_changed": false + }, + "elf": { + "type": "ET_DYN", + "machine": "EM_X86_64", + "entrypoint": "0xfef60", + "program_header_count": 4, + "load_segment_count": 3, + "rwx_load_segment_count": 0, + "program_headers": [ + {"type": "LOAD", "flags": "RE", "offset": 16384, "vaddr": 0, "filesz": 1071054, "memsz": 1071054, "align": 16384}, + {"type": "LOAD", "flags": "R", "offset": 1097728, "vaddr": 1081344, "filesz": 394264, "memsz": 394264, "align": 16384}, + {"type": "LOAD", "flags": "RW", "offset": 1507328, "vaddr": 1490944, "filesz": 206124, "memsz": 903760, "align": 16384} + ], + "bss_size": 697636, + "init_array_size": 0, + "fini_array_size": 0, + "tls": false, + "relocations": {"total": 1055, "by_type": {"6": 142, "8": 913}}, + "dt_needed": [ + "libSceAudioOut.sprx", "libSceLibcInternal.sprx", "libScePad.sprx", + "libSceSystemService.sprx", "libSceUserService.sprx", + "libSceVideoOut.sprx", "libkernel_web.sprx" + ], + "undefined_symbols": [ + "_Exit", "__error", "__isthreaded", "__stderrp", "__stdoutp", "abort", + "atan2", "atof", "atoi", "calloc", "clock_gettime", "close", "closedir", + "cos", "cosf", "exit", "fclose", "ferror", "fflush", "fileno", "fopen", + "fprintf", "fputc", "fputs", "fread", "free", "fseek", "fseeko", "fstat", + "ftello", "fwrite", "getenv", "gettimeofday", "isgraph", "localtime", + "longjmp", "lseek", "malloc", "memcmp", "memcpy", "memmove", "memset", + "nanosleep", "open", "opendir", "powf", "pthread_attr_init", + "pthread_attr_setdetachstate", "pthread_attr_setstacksize", "pthread_create", + "pthread_getschedparam", "pthread_getspecific", "pthread_join", + "pthread_key_create", "pthread_key_delete", "pthread_mutex_destroy", + "pthread_mutex_init", "pthread_mutex_lock", "pthread_mutex_unlock", + "pthread_mutexattr_init", "pthread_mutexattr_settype", "pthread_self", + "pthread_set_name_np", "pthread_setcanceltype", "pthread_setschedparam", + "pthread_setspecific", "pthread_sigmask", "puts", "qsort", "read", "readdir", + "realloc", "sceAudioOutClose", "sceAudioOutInit", "sceAudioOutOpen", + "sceAudioOutOutput", "sceKernelAllocateMainDirectMemory", + "sceKernelCreateEqueue", "sceKernelDeleteEqueue", "sceKernelMapDirectMemory", + "sceKernelReleaseDirectMemory", "sceKernelSendNotificationRequest", + "sceKernelWaitEqueue", "scePadClose", "scePadInit", "scePadOpen", + "scePadReadState", "sceSystemServiceHideSplashScreen", + "sceUserServiceGetLoginUserIdList", "sceUserServiceGetUserName", + "sceUserServiceInitialize", "sceVideoOutAddFlipEvent", "sceVideoOutClose", + "sceVideoOutDeleteFlipEvent", "sceVideoOutOpen", "sceVideoOutRegisterBuffers2", + "sceVideoOutSetBufferAttribute2", "sceVideoOutSetFlipRate", + "sceVideoOutSubmitFlip", "sched_get_priority_max", "sched_get_priority_min", + "sem_destroy", "sem_init", "sem_post", "sem_timedwait", "sem_trywait", + "sem_wait", "send", "setjmp", "setvbuf", "sigaction", "sigaddset", + "sigemptyset", "signal", "sin", "sinf", "snprintf", "sprintf", "sscanf", + "stat", "strchr", "strcmp", "strdup", "strerror", "strftime", "strlcat", + "strlcpy", "strlen", "strncasecmp", "strncmp", "strrchr", "strstr", + "strtod", "strtok_r", "strtol", "strtoul", "strtoull", "time", "tolower", + "vfprintf", "vsnprintf", "vsscanf" + ], + "undefined_symbol_delta_from_phase10d": ["send"] + }, + "loader_transport": { + "classification": "SOURCE_BOUND_LEGACY_RAW_ELF_INHERITED_STDIO_DUPLEX", + "raw_elf_exact_length_read": true, + "payload_stdout_inherits_connection": true, + "controlled_route_supported": false, + "new_loader_change": false, + "new_target_socket": false, + "new_target_connection": false, + "target_address_embedded": false, + "source_locations": [ + "socksrv.c:139-141", "socksrv.c:335-339", "socksrv.c:421-423", + "elfldr.c:499-520", "elfldr.c:837-923" + ] + }, + "result_protocol": { + "magic": "CHD10E01", + "version": 1, + "frame_size": 64, + "byte_order": "BIG_ENDIAN", + "crc32_range": "0..59", + "reserved_zero_bytes": 20, + "stages": ["D00", "D01", "D02", "D03", "D04", "D05", "D06", "D07", "D08", "D09", "D10", "D11", "D12"], + "terminal_stages": ["D11", "D12"], + "target_write_call": "send(STDOUT_FILENO, frame, 64, MSG_DONTWAIT | MSG_NOSIGNAL)", + "target_write_attempts_per_stage": 1, + "target_write_retry": false, + "short_write_retry": false, + "target_import_added": "send", + "target_forbidden_imports_absent": ["socket", "connect", "bind", "listen", "accept", "recv"] + }, + "host_contract": { + "hash_before_socket_creation": true, + "exact_size_before_socket_creation": true, + "literal_ip_required": true, + "connection_count": 1, + "sendall_count": 1, + "shutdown_write_count": 1, + "receive_limit_bytes": 65536, + "timeout_required": true, + "timeout_maximum_seconds": 120, + "retry": false, + "reconnect": false, + "resume": false, + "trace_overwrite": false, + "partial_result_is_success": false + }, + "startup_effects": { + "normal_sdk_crt": true, + "patch_init_reachable_from_start": true, + "kernel_copy_helpers_statically_linked": true, + "credential_capability_changes": "TEMPORARY_SOURCE_BOUND_EXISTING_CRT_EFFECT", + "syscall_range_change": "TEMPORARY_SOURCE_BOUND_EXISTING_CRT_EFFECT", + "new_effect_from_phase10e": "ONE_NONBLOCKING_SEND_ATTEMPT_PER_REACHED_STAGE", + "side_effect_free": false + }, + "callsites": { + "main": "0x3cc0", + "diagnostic_dispatch_stream_call": "0x477f5", + "stream_emit": "0x47ce0", + "send_call": "0x47eb8", + "_start": "0xfef60", + "_start_patch_init_call": "0xfefee", + "disassembly_sha256": "c49bada12eae987dd885e745417626226cd60eea0ff3002aa3656d4bb076e808" + }, + "tests": { + "target_stream_host_asan_ubsan": "PASS", + "phase10e_python_cases": 13, + "phase10e_python": "PASS", + "full_retroarch_ps5_host_suite": "PASS", + "structural_validator": "PASS", + "double_clean_build": "PASS_BYTE_IDENTICAL", + "hardware_evidence_from_phase10e": true + }, + "hard_stops": [ + "NO_SECOND_CONNECTION", + "NO_SECOND_TRANSFER_OR_EXECUTION", + "NO_INSTALLATION_OR_AUTOLOAD", + "NO_RETRY_OR_RECONNECT", + "STOP_AFTER_TIMEOUT_EOF_ERROR_OR_TERMINAL_FRAME" + ] +} diff --git a/manifests/retroarch/phase-1.0ea-expanded-launch-evidence.json b/manifests/retroarch/phase-1.0ea-expanded-launch-evidence.json new file mode 100644 index 0000000..a1bb6a6 --- /dev/null +++ b/manifests/retroarch/phase-1.0ea-expanded-launch-evidence.json @@ -0,0 +1,63 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0EA_EXPANDED_LAUNCH_EVIDENCE", + "status": "NEW_FW960_LNC_CANDIDATE_FOUND_ABI_AND_CLEANUP_GATES_STILL_BLOCKED", + "date": "2026-07-30", + "bounded_search": { + "exact_system_service_terms_searched": true, + "exact_lnc_parameter_terms_searched": true, + "public_source_hosts_only": true, + "global_absence_claimed": false, + "device_or_ps5_network_used": false + }, + "candidate": { + "repository": "https://github.com/phantomptr/ps5upload.git", + "head_commit": "978add1864eb54351eaddbd242634282ef3cd120", + "introduction_commit": "e3875ffba7cb08e017dcdb537d045af3fddc133d", + "shellui_rpc_blob": "b30d17bd0068affd5b4d42550953bc6bd44203f7", + "ptrace_remote_blob": "6b68f26995f746df5b2b0b9fd1495432304e8f35", + "claims_fw960_launch_worked": true, + "uses_sceLncUtilLaunchApp": true, + "uses_24_byte_packed_parameter": true, + "independent_public_parameter_provenance_supplied": false, + "replaces_title_process_with_embedded_elf": false, + "acceptable_chimera_runtime_dependency": false + }, + "cleanup_audit": { + "absolute_deadline_on_all_waitpid_calls": false, + "all_munmap_results_checked": false, + "all_detach_results_checked": false, + "attached_state_retained_on_detach_failure": false, + "dispatched_unknown_completion_is_hard_failure": false, + "authid_restoration_failure_is_terminal": false, + "one_shot_no_retry_contract": false, + "bounded_verified_cleanup_composition_proven": false + }, + "gate_state": { + "fake00000_presence_proven": true, + "operator_browser_reachability_observed": true, + "independent_system_service_abi_proven": false, + "independent_lnc_parameter_abi_proven": false, + "embedded_elf_replacement_abi_proven": false, + "firmware_960_videoout_ownership_proven": false, + "target_source_allowed": false, + "target_build_allowed": false, + "device_action_allowed": false + }, + "artifact": { + "target_source_created": false, + "target_artifact_created": false, + "execution_eligible": false, + "transfer_eligible": false, + "installation_eligible": false + }, + "authorization": { + "connection": false, + "transfer": false, + "execution": false, + "result_receive": false, + "device_write": false, + "automatic_retry": false + }, + "next_step": "INDEPENDENT_COMPLETE_LAUNCH_REPLACEMENT_ABI_THEN_BOUNDED_TARGET_COMPOSITION" +} diff --git a/manifests/retroarch/phase-1.0eb-public-launch-matrix.json b/manifests/retroarch/phase-1.0eb-public-launch-matrix.json new file mode 100644 index 0000000..f210296 --- /dev/null +++ b/manifests/retroarch/phase-1.0eb-public-launch-matrix.json @@ -0,0 +1,93 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0EB_PUBLIC_LAUNCH_MATRIX", + "status": "PUBLIC_SOURCE_MATRIX_EXHAUSTED_EXTERNAL_ABI_EVIDENCE_REQUIRED", + "date": "2026-07-30", + "scope": { + "public_source_only": true, + "candidate_binaries_executed": false, + "proprietary_libraries_opened": false, + "ps5_or_device_network_used": false, + "global_internet_absence_claimed": false + }, + "candidates": { + "official_shsrv_websrv": { + "lineage": "ps5-payload-dev/shsrv-websrv", + "independent_launch_abi": false, + "bigapp_replacement_source_present": true, + "bounded_cleanup": false, + "acceptable": false + }, + "sonicloader": { + "lineage": "DERIVED_FROM_SHSRV_DECLARATIONS", + "independent_launch_abi": false, + "acceptable": false + }, + "ps5upload": { + "commit": "978add1864eb54351eaddbd242634282ef3cd120", + "parameter_bytes": 24, + "independent_abi_provenance": false, + "embedded_elf_replacement": false, + "bounded_cleanup": false, + "acceptable": false + }, + "ps5_upload_suite": { + "commit": "326f2ae23cf289e5b5e653a1baf186737cf75a90", + "main_blob": "b8be499363f09d7a98c579e1a82e775d7874cbf9", + "parameter_bytes": 256, + "parameter_described_as_typical": true, + "multiple_fallback_calls": true, + "independent_abi_provenance": false, + "embedded_elf_replacement": false, + "acceptable": false + }, + "libhijacker": { + "commit": "498577e29456cf35d817fbe1457fdb8789025f99", + "spawner_blob": "cf9cdc006a4638571e2c8d322f23768aa34a0426", + "elf_loader_blob": "9bf4cb1f85d3ee782b312c0f64766d5ecfff894c", + "bigapp_videoout_context": false, + "bounded_cleanup": false, + "acceptable": false + }, + "etahen": { + "commit": "dafa13b562ddb137a4b4a97b9aaa287c0c57cc9c", + "hbldr_header_blob": "d2b96078aba256e65081b551d3390b866594877a", + "hbldr_declaration_present": true, + "matching_visible_implementation_present": false, + "tracked_binary_libraries_inspected": false, + "acceptable": false + } + }, + "correlation": { + "independent_lnc_candidates_agree_on_parameter_size": false, + "independent_bigapp_replacement_lineages": 1, + "complete_launch_replacement_abi_proven": false, + "firmware_960_videoout_ownership_proven": false, + "bounded_verified_cleanup_proven": false + }, + "required_external_evidence": [ + "PUBLIC_LAUNCH_DECLARATIONS_AND_LAYOUT_PROVENANCE", + "COMPLETE_FW_APPLICABLE_BIGAPP_REPLACEMENT_SOURCE", + "FW960_VIDEOOUT_OWNERSHIP_EVIDENCE", + "WAIT_STOP_RETURN_ERROR_SEMANTICS", + "ABSOLUTE_DEADLINE_AND_TERMINAL_REAP_POLICY", + "CHECKED_FULL_ROLLBACK", + "REDISTRIBUTION_COMPATIBLE_LICENSE_AND_PROVENANCE" + ], + "decision": { + "new_adr_allowed": false, + "target_source_allowed": false, + "target_build_allowed": false, + "device_authorization_actionable": false, + "device_action_allowed": false, + "next_step": "EXTERNAL_EVIDENCE_ACQUISITION_THEN_PROVENANCE_AUDIT" + }, + "authorization": { + "connection": false, + "transfer": false, + "execution": false, + "result_receive": false, + "device_write": false, + "automatic_retry": false + } +} diff --git a/manifests/retroarch/phase-1.0f-startup-interval.json b/manifests/retroarch/phase-1.0f-startup-interval.json new file mode 100644 index 0000000..bed1cf0 --- /dev/null +++ b/manifests/retroarch/phase-1.0f-startup-interval.json @@ -0,0 +1,247 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0F_STARTUP_INTERVAL_DIAGNOSTIC", + "status": "OFFLINE_ARTIFACT_PREPARED_NO_DEVICE_AUTHORIZATION", + "scope": "OFFLINE_BUILD_AND_STATIC_AUDIT_ONLY", + "date": "2026-07-22", + "repository": "chimera-retroarch", + "branch": "codex/ps5-d02-d03-interval-diagnostic", + "source_commit": "0eaf68d6de4dc9757d85cc7ad5c714b1d151c8f8", + "baseline_commit": "f1391c3e6717ff4e2b869007fb9627c9edde52e4", + "source_bindings": { + "retroarch_release": "69a4f0ea1e8aaf442ae4858f2e7f2b31a1776576", + "ps5_payload_sdk": "d2e2e585740362976a39fdd5ccf390f199a7bc37", + "sdl": "0baf4ac49382b537ba449901b5b6d0d189bb1fbb", + "hardened_elfldr": "197623058f509eddde18868dafcb92fdcac66464", + "lifecycle_notification_source": "fe08300339a13f899fb78ea404ada381a5cba87c" + }, + "authorizations": { + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "result_receive_authorized": false, + "installation_authorized": false, + "autoload_authorized": false, + "device_write_authorized": false, + "automatic_retry": false + }, + "phase_actions": { + "ps5_connected": false, + "device_request_performed": false, + "files_transferred": false, + "device_write_performed": false, + "target_execution_performed": false, + "result_received_from_device": false, + "installation_performed": false, + "autoload_performed": false + }, + "prior_evidence": { + "run": "RUN_C", + "artifact_sha256": "1049c78099a60b472a3fb0e2999e3393b6ad76337a28532a7e53872e7772dedf", + "classification": "REMOTE_PAYLOAD_OUTPUT_PROVEN_INCOMPLETE_BEFORE_D03", + "last_proven_stage": "D02", + "d02_raw0": 0, + "authorization_consumed": true, + "authority_inherited": false + }, + "artifact": { + "name": "retroarch_ps5_interval_diag.elf", + "profile": "interval-diag", + "local_relative_path": "build/phase10f/interval-a/retroarch_ps5_interval_diag.elf", + "linker_map_relative_path": "build/phase10f/interval-a/retroarch_ps5_interval_diag.map", + "size": 1845152, + "sha256": "e8bfc01c61bfb14b5814280a6e5442f1a5ad05ace5439d1c09e7e5ee00cd0055", + "clean_build_sha256": [ + "e8bfc01c61bfb14b5814280a6e5442f1a5ad05ace5439d1c09e7e5ee00cd0055", + "e8bfc01c61bfb14b5814280a6e5442f1a5ad05ace5439d1c09e7e5ee00cd0055" + ], + "linker_map_size": 637603, + "linker_map_sha256": "638642b750b8d5b108cf6c73215a3f1759bcb6da0b29ee0a0ade5c47e8b7b2d5", + "clean_map_sha256": [ + "638642b750b8d5b108cf6c73215a3f1759bcb6da0b29ee0a0ade5c47e8b7b2d5", + "638642b750b8d5b108cf6c73215a3f1759bcb6da0b29ee0a0ade5c47e8b7b2d5" + ], + "execution_eligible": false, + "transfer_eligible": false, + "installation_eligible": false, + "device_action_performed": false, + "tracked": false + }, + "result_protocol": { + "magic": "CHD10F01", + "version": 1, + "frame_size": 64, + "byte_order": "BIG_ENDIAN", + "crc32_range": "0..59", + "reserved_zero_bytes": 20, + "d_stages": ["D00", "D01", "D02", "D03", "D04", "D05", "D06", "D07", "D08", "D09", "D10", "D11", "D12"], + "interval_stages": ["I00", "I01", "I02", "I03", "I04", "I05", "I06", "I07", "I08", "I09", "I10", "I11", "I12", "I13", "I14"], + "terminal_stages": ["D11", "D12"], + "interval_notification_calls": 0, + "interval_notification_result_sentinel": -2147483648, + "target_write_call": "send(STDOUT_FILENO, frame, 64, MSG_DONTWAIT | MSG_NOSIGNAL)", + "target_write_attempts_per_reached_stage": 1, + "target_write_retry": false, + "short_write_retry": false, + "new_target_imports_from_phase10e": [], + "forbidden_target_imports_absent": ["socket", "connect", "bind", "listen", "accept", "recv"] + }, + "interval_callsites": [ + {"stage": "I00", "source": "retroarch.c:6038", "function": "rarch_main", "address": "0x3c48"}, + {"stage": "I01", "source": "tasks/task_content.c:1413", "function": "content_load", "address": "0x10370"}, + {"stage": "I02", "source": "tasks/task_content.c:1437", "function": "content_load", "address": "0x103c1"}, + {"stage": "I03", "source": "retroarch.c:7817", "function": "retroarch_main_init", "address": "0x3e11"}, + {"stage": "I04", "source": "retroarch.c:7839", "function": "retroarch_main_init", "address": "0x48d3"}, + {"stage": "I05", "source": "retroarch.c:7981", "function": "retroarch_main_init", "address": "0x4a45"}, + {"stage": "I06", "source": "retroarch.c:7987", "function": "retroarch_main_init", "address": "0x4a75"}, + {"stage": "I07", "source": "retroarch.c:7993", "function": "retroarch_main_init", "address": "0x4a9a"}, + {"stage": "I08", "source": "retroarch.c:7999", "function": "retroarch_main_init", "address": "0x4abf"}, + {"stage": "I09", "source": "retroarch.c:8005", "function": "retroarch_main_init", "address": "0x4ae1"}, + {"stage": "I10", "source": "retroarch.c:8034", "function": "retroarch_main_init", "address": "0x4baa"}, + {"stage": "I11", "source": "retroarch.c:8048", "function": "retroarch_main_init", "address": "0x4bf9", "raw0": "0_OR_NEGATIVE_ONE"}, + {"stage": "I12", "source": "retroarch.c:8116", "function": "retroarch_main_init", "address": "0x4c78"}, + {"stage": "I13", "source": "gfx/video_driver.c:3385", "function": "video_driver_init_internal", "address": "0x2061f"}, + {"stage": "I14", "source": "gfx/drivers/sdl2_gfx.c:329", "function": "sdl2_gfx_init", "address": "0x3f696"} + ], + "elf": { + "type": "ET_DYN", + "machine": "EM_X86_64", + "entrypoint": "0xff110", + "program_header_count": 4, + "load_segment_count": 3, + "rwx_load_segment_count": 0, + "program_headers": [ + {"type": "LOAD", "flags": "RE", "offset": 16384, "vaddr": 0, "filesz": 1071486, "memsz": 1071486, "align": 16384}, + {"type": "LOAD", "flags": "R", "offset": 1097728, "vaddr": 1081344, "filesz": 394360, "memsz": 394360, "align": 16384}, + {"type": "LOAD", "flags": "RW", "offset": 1507328, "vaddr": 1490944, "filesz": 206124, "memsz": 903760, "align": 16384} + ], + "bss_section_size": 697632, + "load_zero_fill_size": 697636, + "preinit_array_size": 0, + "init_array_size": 0, + "fini_array_size": 0, + "tls": false, + "relocations": {"total": 1055, "by_type": {"6": 142, "8": 913}}, + "dt_needed": [ + "libSceAudioOut.sprx", "libSceLibcInternal.sprx", "libScePad.sprx", + "libSceSystemService.sprx", "libSceUserService.sprx", + "libSceVideoOut.sprx", "libkernel_web.sprx" + ], + "undefined_symbol_count": 142, + "undefined_symbols_match_phase10e": true, + "undefined_symbols": [ + "_Exit", "__error", "__isthreaded", "__stderrp", "__stdoutp", "abort", + "atan2", "atof", "atoi", "calloc", "clock_gettime", "close", "closedir", + "cos", "cosf", "exit", "fclose", "ferror", "fflush", "fileno", "fopen", + "fprintf", "fputc", "fputs", "fread", "free", "fseek", "fseeko", "fstat", + "ftello", "fwrite", "getenv", "gettimeofday", "isgraph", "localtime", + "longjmp", "lseek", "malloc", "memcmp", "memcpy", "memmove", "memset", + "nanosleep", "open", "opendir", "powf", "pthread_attr_init", + "pthread_attr_setdetachstate", "pthread_attr_setstacksize", "pthread_create", + "pthread_getschedparam", "pthread_getspecific", "pthread_join", + "pthread_key_create", "pthread_key_delete", "pthread_mutex_destroy", + "pthread_mutex_init", "pthread_mutex_lock", "pthread_mutex_unlock", + "pthread_mutexattr_init", "pthread_mutexattr_settype", "pthread_self", + "pthread_set_name_np", "pthread_setcanceltype", "pthread_setschedparam", + "pthread_setspecific", "pthread_sigmask", "puts", "qsort", "read", "readdir", + "realloc", "sceAudioOutClose", "sceAudioOutInit", "sceAudioOutOpen", + "sceAudioOutOutput", "sceKernelAllocateMainDirectMemory", + "sceKernelCreateEqueue", "sceKernelDeleteEqueue", "sceKernelMapDirectMemory", + "sceKernelReleaseDirectMemory", "sceKernelSendNotificationRequest", + "sceKernelWaitEqueue", "scePadClose", "scePadInit", "scePadOpen", + "scePadReadState", "sceSystemServiceHideSplashScreen", + "sceUserServiceGetLoginUserIdList", "sceUserServiceGetUserName", + "sceUserServiceInitialize", "sceVideoOutAddFlipEvent", "sceVideoOutClose", + "sceVideoOutDeleteFlipEvent", "sceVideoOutOpen", "sceVideoOutRegisterBuffers2", + "sceVideoOutSetBufferAttribute2", "sceVideoOutSetFlipRate", + "sceVideoOutSubmitFlip", "sched_get_priority_max", "sched_get_priority_min", + "sem_destroy", "sem_init", "sem_post", "sem_timedwait", "sem_trywait", + "sem_wait", "send", "setjmp", "setvbuf", "sigaction", "sigaddset", + "sigemptyset", "signal", "sin", "sinf", "snprintf", "sprintf", "sscanf", + "stat", "strchr", "strcmp", "strdup", "strerror", "strftime", "strlcat", + "strlcpy", "strlen", "strncasecmp", "strncmp", "strrchr", "strstr", + "strtod", "strtok_r", "strtol", "strtoul", "strtoull", "time", "tolower", + "vfprintf", "vsnprintf", "vsscanf" + ], + "forbidden_imports_absent": [ + "socket", "connect", "bind", "listen", "accept", "recv", "recvfrom", + "sendto", "dlopen", "dlsym", "sceGnmSubmitCommandBuffers", + "sceGnmSubmitAndFlipCommandBuffers" + ] + }, + "static_reachability": { + "main": "0x3cc0", + "interval_checkpoint": "0x47ff0", + "interval_checkpoint_result": "0x48060", + "stream_emit": "0x47d90", + "send_relocation": "0x177888", + "_start": "0xff110", + "_start_patch_init_call": "0xff19e", + "patch_init": "0x105860", + "kernel_copyin": "0x101e00", + "kernel_copyout": "0x101fe0", + "kernel_set_ucred_caps": "0x1027e0", + "kernel_set_ucred_attrs": "0x1028c0", + "audit_a_disassembly_sha256": "821cba84114c75d8a8561436410a3081212724187f086ea5d18be814f6b6054a", + "normalized_disassembly_sha256": [ + "32725415b86a8b3aafbd8b7fa2d089633a3cf90935b111e3c175bdd28dde242a", + "32725415b86a8b3aafbd8b7fa2d089633a3cf90935b111e3c175bdd28dde242a" + ] + }, + "loader_transport": { + "classification": "SOURCE_BOUND_LEGACY_RAW_ELF_INHERITED_STDIO_DUPLEX", + "raw_elf_exact_length_read": true, + "payload_stdout_inherits_connection": true, + "controlled_route_supported": false, + "new_loader_change": false, + "new_target_socket": false, + "new_target_connection": false, + "target_address_embedded": false + }, + "startup_effects": { + "normal_sdk_crt": true, + "patch_init_reachable_from_start": true, + "kernel_copy_helpers_statically_linked": true, + "credential_capability_changes": "TEMPORARY_SOURCE_BOUND_EXISTING_CRT_EFFECT", + "syscall_range_change": "TEMPORARY_SOURCE_BOUND_EXISTING_CRT_EFFECT", + "new_effect_from_phase10f": "ONE_NONBLOCKING_SEND_ATTEMPT_PER_REACHED_INTERVAL_STAGE", + "interval_notification_effect": "NONE", + "side_effect_free": false + }, + "host_parser": { + "offline_interval_parser_available": true, + "live_cli_defaults_to_consumed_phase10e_protocol": true, + "live_interval_activation_available": false, + "manifest_must_be_execution_eligible": true, + "authorization_must_be_active_and_exact": true, + "retry": false, + "reconnect": false, + "resume": false, + "trace_overwrite": false + }, + "tests": { + "warnings_as_errors_cross_build": "PASS", + "target_interval_host_asan_ubsan": "PASS", + "negative_stage_host_asan_ubsan": "PASS", + "phase10d_python_cases": 8, + "phase10d_python": "PASS", + "phase10e_f_python_cases": 14, + "phase10e_f_python": "PASS", + "phase10f_policy_cases": 24, + "phase10f_policy": "PASS", + "full_retroarch_ps5_host_suite": "PASS", + "structural_validator": "PASS", + "double_clean_build": "PASS_BYTE_IDENTICAL", + "dual_static_audit_semantics": "PASS_IDENTICAL", + "hardware_evidence_from_phase10f": false + }, + "hard_stops": [ + "NO_PS5_CONNECTION_OR_REQUEST", + "NO_TRANSFER_OR_EXECUTION", + "NO_RESULT_RECEPTION", + "NO_INSTALLATION_OR_AUTOLOAD", + "NO_DEVICE_WRITE", + "NO_RETRY_OR_RECONNECT", + "NEW_EXACT_ARTIFACT_PERMISSION_REQUIRED" + ] +} diff --git a/manifests/retroarch/phase-1.0g-device-result.json b/manifests/retroarch/phase-1.0g-device-result.json new file mode 100644 index 0000000..151689d --- /dev/null +++ b/manifests/retroarch/phase-1.0g-device-result.json @@ -0,0 +1,106 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0G_INTERVAL_ONE_SHOT_DEVICE_RESULT", + "status": "ONE_SHOT_AUTHORIZATION_CONSUMED_RESULT_INCOMPLETE", + "date": "2026-07-22", + "firmware": "9.60", + "source_commit": "0eaf68d6de4dc9757d85cc7ad5c714b1d151c8f8", + "runner_commit": "55e873df12f4dd099c082d4a26b225644b8e0567", + "artifact": { + "name": "retroarch_ps5_interval_diag.elf", + "size": 1845152, + "sha256": "e8bfc01c61bfb14b5814280a6e5442f1a5ad05ace5439d1c09e7e5ee00cd0055", + "execution_eligible": false, + "transfer_eligible": false, + "installation_eligible": false, + "tracked": false + }, + "authorization": { + "run_id": "phase10g-20260722-960-once", + "active_manifest_sha256": "fd359d204d0067637e3eb84b06f0e83aa1dac27c62f54ec27eb7564cf43f8c4e", + "local_approval_sha256": "386a143bdb1801dde5fd72712aebf5280af22b81366828cca6f6d523c781519c", + "attempt_receipt_sha256": "aeb402cb45c16adea8d79d856633a76280895828b8d32822d9bc183fc7251442", + "consumed": true, + "authority_inherited_by_future_action": false + }, + "current_authorizations": { + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "result_receive_authorized": false, + "installation_authorized": false, + "autoload_authorized": false, + "device_write_authorized": false, + "automatic_retry": false + }, + "performed_actions": { + "ps5_connected": true, + "device_request_performed": true, + "files_transferred": true, + "target_execution_performed": true, + "result_received_from_device": true, + "device_write_performed": false, + "installation_performed": false, + "autoload_performed": false, + "reboot_performed": false + }, + "transport": { + "connect_count": 1, + "sendall_count": 1, + "bytes_sent": 1845152, + "shutdown_write_count": 1, + "recv_call_count": 5, + "received_byte_count": 3669, + "raw_stream_stored_bytes": 3669, + "raw_stream_sha256": "71fb93a166c651fabfddee20a59e1d04e5f693e323a369c2328e0f7f7ba0b1d4", + "raw_stream_truncated": false, + "retry_count": 0, + "reconnect_count": 0, + "close_called": true, + "remote_eof_observed": true, + "timeout_observed": false, + "host_duration_seconds": 1.221382600007928 + }, + "trace": { + "tracked": false, + "size": 13189, + "sha256": "eb73611d98e602b89b3cdb3a0e94ec97b9410c5845b0449a07bf2aa6fdec7249" + }, + "protocol_result": { + "magic": "CHD10F01", + "frame_size": 64, + "frame_count": 7, + "stages": ["D00", "D01", "D02", "I00", "I01", "I02", "I03"], + "d02_raw0": 0, + "parser_errors": [], + "terminal_stage": null, + "last_proven_stage": "I03", + "first_unproven_stage": "I04", + "classification": "REMOTE_PAYLOAD_OUTPUT_PROVEN_INCOMPLETE_AFTER_I03_BEFORE_I04" + }, + "ordinary_stdout": { + "byte_count": 3221, + "sha256": "0b56b42dabf2661233bbb57a63a520d791c13debd5da6839feeb30b534ad315f", + "preserved_losslessly_in_local_trace": true, + "key_message": "No arguments supplied and no menu built-in, displaying help...", + "remaining_output": "RETROARCH_HELP_TEXT" + }, + "source_binding": { + "profile_have_menu": false, + "i03_location": "retroarch.c:7817 before retroarch_parse_input_and_config", + "guard_location": "retroarch.c:7117-7123 when !HAVE_MENU and argc == 1", + "message_location": "retroarch.c:7120", + "exit_location": "retroarch.c:7122 exit(0)", + "i04_location": "retroarch.c:7839 after retroarch_parse_input_and_config", + "cause": "DETERMINISTIC_NO_ARGUMENT_NO_MENU_EXIT", + "sdl_videoout_reached": false, + "rendering_reached": false, + "terminal_cleanup_proven": false + }, + "hard_stops": [ + "AUTHORIZATION_CONSUMED", + "NO_RETRY_OR_RECONNECT", + "NO_FURTHER_DEVICE_ACTION", + "NEW_ARTIFACT_AND_NEW_EXACT_PERMISSION_REQUIRED" + ] +} diff --git a/manifests/retroarch/phase-1.0g-one-shot-approval-template.json b/manifests/retroarch/phase-1.0g-one-shot-approval-template.json new file mode 100644 index 0000000..b93150a --- /dev/null +++ b/manifests/retroarch/phase-1.0g-one-shot-approval-template.json @@ -0,0 +1,31 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0G_INTERVAL_ONE_SHOT_RUNNER", + "authorized": false, + "consumed": false, + "authorization_scope": "EXACT_ONE_SHOT_PHASE_1_0G", + "authorized_by": null, + "approval_reference": null, + "protocol_magic": "CHD10F01", + "protocol_version": 1, + "protocol_frame_size": 64, + "run_id": null, + "target": null, + "port": null, + "firmware": "9.60", + "artifact_name": "retroarch_ps5_interval_diag.elf", + "artifact_size": 1845152, + "artifact_sha256": "e8bfc01c61bfb14b5814280a6e5442f1a5ad05ace5439d1c09e7e5ee00cd0055", + "timeout_seconds": 75, + "maximum_connections": 1, + "maximum_transfers": 1, + "maximum_executions": 1, + "maximum_result_receives": 1, + "installation_authorized": false, + "autoload_authorized": false, + "device_write_authorized": false, + "automatic_retry": false, + "reconnect_authorized": false, + "resume_authorized": false, + "automatic_reboot_authorized": false +} diff --git a/manifests/retroarch/phase-1.0g-one-shot-runner.json b/manifests/retroarch/phase-1.0g-one-shot-runner.json new file mode 100644 index 0000000..929c230 --- /dev/null +++ b/manifests/retroarch/phase-1.0g-one-shot-runner.json @@ -0,0 +1,111 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0G_INTERVAL_ONE_SHOT_RUNNER", + "status": "OFFLINE_RUNNER_PREPARED_NO_DEVICE_AUTHORIZATION", + "scope": "HOST_ONLY_RUNNER_AND_POLICY_PREPARATION", + "date": "2026-07-22", + "repository": "chimera-retroarch", + "branch": "codex/ps5-interval-one-shot-runner", + "runner_commit": "55e873df12f4dd099c082d4a26b225644b8e0567", + "target_source_commit": "0eaf68d6de4dc9757d85cc7ad5c714b1d151c8f8", + "prior_phase": "PHASE_1_0F_STARTUP_INTERVAL_DIAGNOSTIC", + "authorizations": { + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "result_receive_authorized": false, + "installation_authorized": false, + "autoload_authorized": false, + "device_write_authorized": false, + "automatic_retry": false + }, + "phase_actions": { + "ps5_connected": false, + "device_request_performed": false, + "files_transferred": false, + "device_write_performed": false, + "target_execution_performed": false, + "result_received_from_device": false, + "installation_performed": false, + "autoload_performed": false + }, + "artifact": { + "name": "retroarch_ps5_interval_diag.elf", + "profile": "interval-diag", + "local_relative_path": "build/phase10f/interval-a/retroarch_ps5_interval_diag.elf", + "size": 1845152, + "sha256": "e8bfc01c61bfb14b5814280a6e5442f1a5ad05ace5439d1c09e7e5ee00cd0055", + "execution_eligible": false, + "transfer_eligible": false, + "installation_eligible": false, + "device_action_performed": false, + "tracked": false, + "changed_from_phase10f": false + }, + "result_protocol": { + "magic": "CHD10F01", + "version": 1, + "frame_size": 64, + "byte_order": "BIG_ENDIAN", + "interval_stages": ["I00", "I01", "I02", "I03", "I04", "I05", "I06", "I07", "I08", "I09", "I10", "I11", "I12", "I13", "I14"], + "d_stages": ["D00", "D01", "D02", "D03", "D04", "D05", "D06", "D07", "D08", "D09", "D10", "D11", "D12"] + }, + "runner": { + "implementation_available": true, + "protocol_selection": "MANIFEST_ONLY", + "protocol_activation_authorized": false, + "run_id": null, + "tracked_target": null, + "tracked_port": null, + "active_manifest_required": true, + "local_approval_required": true, + "actual_artifact_rehashed_before_receipt": true, + "actual_artifact_rehashed_before_socket": true, + "attempt_receipt_required": true, + "attempt_receipt_exclusive_create": true, + "attempt_receipt_durable_fsync": true, + "attempt_receipt_written_before_connect": true, + "maximum_connections": 1, + "maximum_transfers": 1, + "maximum_executions": 1, + "maximum_result_receives": 1, + "receive_limit_bytes": 65536, + "retry": false, + "reconnect": false, + "resume": false, + "trace_exclusive_create": true, + "trace_overwrite": false + }, + "source_files": { + "tools/ps5_diag_duplex.py": "df72af4e738ab5969b78c42cf71d376c2a95b43e26b3d445cd7be9d88cadbf19", + "tests/test_ps5_phase10e_tools.py": "1d21072d9bae6d67e38e3908737326bc5b9eceefd90a659ae3d601833f543944", + "pkg/ps5/validate_port.py": "c65373df687328243d20184ddb65e589b1f0d2e6460058062054a9dbc8e3c185" + }, + "activation_requirements": { + "new_exact_artifact_specific_permission": true, + "exact_target_supplied_only_in_untracked_local_approval": true, + "unique_run_id_required": true, + "authorization_reference_required": true, + "all_four_device_actions_must_be_explicitly_authorized": true, + "forbidden_actions_must_remain_false": true, + "current_requirements_satisfied": false + }, + "tests": { + "retroarch_runner_python_cases": 21, + "retroarch_runner_python": "PASS", + "retroarch_ps5_host_suite": "PASS", + "fake_socket_only": true, + "hardware_evidence_from_phase10g": false + }, + "hard_stops": [ + "NO_CURRENT_DEVICE_AUTHORIZATION", + "NO_PS5_CONNECTION_OR_REQUEST", + "NO_TRANSFER_OR_EXECUTION", + "NO_RESULT_RECEPTION", + "NO_INSTALLATION_OR_AUTOLOAD", + "NO_DEVICE_WRITE", + "NO_RETRY_RECONNECT_OR_RESUME", + "NO_TARGET_OR_PORT_IN_TRACKED_ACTIVATION_RECORD", + "NEW_EXACT_PERMISSION_REQUIRED" + ] +} diff --git a/manifests/retroarch/phase-1.0h-device-result.json b/manifests/retroarch/phase-1.0h-device-result.json new file mode 100644 index 0000000..61bc706 --- /dev/null +++ b/manifests/retroarch/phase-1.0h-device-result.json @@ -0,0 +1,128 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0H_STARTUP_ARGUMENT_ONE_SHOT_DEVICE_RESULT", + "status": "ONE_SHOT_AUTHORIZATION_CONSUMED_FLIP_SUBMIT_FAILED", + "date": "2026-07-22", + "firmware": "9.60", + "source_commit": "c710f85816b5e456dd3a85a46f85eb28883522b3", + "runner_commit": "f2dd710f2dff8c840a4c747cbbb3654c24cf23a7", + "artifact": { + "name": "retroarch_ps5_startup_args_diag.elf", + "size": 1845152, + "sha256": "822f2cf1f4d33a514d2bdd88fde40ad580dda5d85f537362ef6dff2eafcb56b6", + "execution_eligible": false, + "transfer_eligible": false, + "installation_eligible": false, + "tracked": false + }, + "authorization": { + "run_id": "phase10h-20260722-960-once", + "active_manifest_sha256": "b76f6b0264451c78be374e34e0c97a1f344b60f43682f04d49bca4aadf66db44", + "local_approval_sha256": "73b438faee14a068174284b658b25f6223cd048cb0dfa0b9f5a78d49ae4dc4ab", + "attempt_receipt_sha256": "aa598838c2db9a03799ce69ed977ad1dbb4f9d0b92ad017c0e1f91c680f1e04a", + "consumed": true, + "authority_inherited_by_future_action": false + }, + "current_authorizations": { + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "result_receive_authorized": false, + "installation_authorized": false, + "autoload_authorized": false, + "device_write_authorized": false, + "automatic_retry": false + }, + "performed_actions": { + "ps5_connected": true, + "device_request_performed": true, + "files_transferred": true, + "target_execution_performed": true, + "result_received_from_device": true, + "persistent_staging_performed": false, + "installation_performed": false, + "autoload_performed": false, + "retry_performed": false, + "reconnect_performed": false, + "display_memory_mutated_by_diagnostic_pattern": true, + "videoout_flip_submit_attempted": true, + "videoout_flip_submit_succeeded": false + }, + "transport": { + "connect_count": 1, + "sendall_count": 1, + "bytes_sent": 1845152, + "shutdown_write_count": 1, + "recv_call_count": 15, + "received_byte_count": 3953, + "raw_stream_stored_bytes": 3953, + "raw_stream_sha256": "6578869b15575c57a19dc0c0317f6b516a85bf6e246abaa85df76e8ae9d5cf67", + "raw_stream_truncated": false, + "retry_count": 0, + "reconnect_count": 0, + "close_called": true, + "remote_eof_observed": true, + "timeout_observed": false, + "host_duration_seconds": 2.270605700003216 + }, + "trace": { + "tracked": false, + "size": 18066, + "sha256": "858a205afcf682a498d8bc11947b67d2446098883a61f6871e31df4d950d0a61", + "parser_errors": [] + }, + "protocol_result": { + "magic": "CHD10H01", + "frame_size": 64, + "frame_count": 25, + "stages": [ + "D00", "D01", "D02", "I00", "I01", "I02", "I03", "D12", + "I04", "I05", "I06", "I07", "I08", "I09", "I10", "D10", + "I11", "I12", "I13", "I14", "D03", "D05", "D06", "D07", "D04" + ], + "raw_results": { + "D02": 0, + "D12_shutdown_reason": 6, + "D12_first_error": 118, + "D10": 1, + "I11": 0, + "D05": 1309671680, + "D06": 0, + "D07": -1, + "D04": -1 + }, + "last_frame": "D04", + "terminal_frame_present": "D12_EARLY_SHUTDOWN_REQUEST", + "runloop_terminal_frame_present": false, + "classification": "STARTUP_ARGUMENT_FIX_PROVEN_VIDEOOUT_BUFFERS_READY_FLIP_SUBMIT_FAILED" + }, + "ordinary_stdout": { + "byte_count": 2353, + "sha256": "109e465582ae55354cb1f7b52c1b17d6741264735b41c59d6ecde565bf3cdc55", + "preserved_losslessly_in_local_trace": true, + "key_error": "Cannot open video driver", + "fatal_location": "video_driver_init_internal()", + "core_unload_messages_present": true + }, + "source_binding": { + "startup_argument_fix_reached_i04": true, + "write_firewall_triggered": true, + "write_firewall_error": "E118_WRITE_BLOCKED", + "exact_blocked_write_operation": "UNOBSERVED", + "sdl_video_entry_reached": true, + "videoout_open_returned_positive_handle": true, + "videoout_buffer_registration_raw": 0, + "diagnostic_pattern_copied_before_flip": true, + "first_flip_submit_raw": -1, + "sdl_init_raw": -1, + "visible_presentation": "UNPROVEN", + "complete_cleanup": "UNPROVEN", + "safe_exit": "UNPROVEN" + }, + "hard_stops": [ + "AUTHORIZATION_CONSUMED", + "NO_RETRY_OR_RECONNECT", + "NO_FURTHER_DEVICE_ACTION", + "NEW_ARTIFACT_AND_NEW_EXACT_PERMISSION_REQUIRED" + ] +} diff --git a/manifests/retroarch/phase-1.0h-startup-args.json b/manifests/retroarch/phase-1.0h-startup-args.json new file mode 100644 index 0000000..a94b87d --- /dev/null +++ b/manifests/retroarch/phase-1.0h-startup-args.json @@ -0,0 +1,163 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0H_STARTUP_ARGUMENT_CORRECTION", + "status": "OFFLINE_ARTIFACT_PREPARED_NO_DEVICE_AUTHORIZATION", + "scope": "OFFLINE_BUILD_AND_STATIC_AUDIT_ONLY", + "date": "2026-07-22", + "firmware": "9.60", + "repository": "chimera-retroarch", + "branch": "codex/ps5-noarg-startup-fix", + "source_commit": "c710f85816b5e456dd3a85a46f85eb28883522b3", + "runner_commit": "f2dd710f2dff8c840a4c747cbbb3654c24cf23a7", + "baseline_commit": "51c10650ae5cefb0da8ae489ac57a2ac8a0e6c0f", + "source_bindings": { + "retroarch_release": "69a4f0ea1e8aaf442ae4858f2e7f2b31a1776576", + "ps5_payload_sdk": "d2e2e585740362976a39fdd5ccf390f199a7bc37", + "sdl": "0baf4ac49382b537ba449901b5b6d0d189bb1fbb", + "hardened_elfldr": "197623058f509eddde18868dafcb92fdcac66464" + }, + "authorizations": { + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "result_receive_authorized": false, + "installation_authorized": false, + "autoload_authorized": false, + "device_write_authorized": false, + "automatic_retry": false + }, + "phase_actions": { + "ps5_connected": false, + "device_request_performed": false, + "files_transferred": false, + "device_write_performed": false, + "target_execution_performed": false, + "result_received_from_device": false, + "installation_performed": false, + "autoload_performed": false + }, + "prior_evidence": { + "phase": "PHASE_1_0G_INTERVAL_ONE_SHOT_DEVICE_RESULT", + "artifact_sha256": "e8bfc01c61bfb14b5814280a6e5442f1a5ad05ace5439d1c09e7e5ee00cd0055", + "last_proven_stage": "I03", + "first_unproven_stage": "I04", + "cause": "DETERMINISTIC_NO_ARGUMENT_NO_MENU_EXIT", + "authorization_consumed": true, + "authority_inherited": false + }, + "correction": { + "profile": "startup-args-diag", + "profile_define": "CHIMERA_PS5_STARTUP_ARGS_DIAG=1", + "have_menu": false, + "content_path": null, + "config_path": null, + "libretro_path": null, + "existing_flag_added": "RARCH_MAIN_WRAP_FLAG_VERBOSE", + "preserved_flags": ["RARCH_MAIN_WRAP_FLAG_TOUCHED", "RARCH_MAIN_WRAP_FLAG_NO_CONTENT"], + "disassembled_flag_mask": "0x07", + "generic_argument_added": "-v", + "modeled_argv": ["retroarch", "-v"], + "modeled_argc": 2, + "avoids_guard": "!HAVE_MENU && argc == 1", + "expected_next_checkpoint": "I04", + "runtime_outcome": "UNPROVEN" + }, + "artifact": { + "name": "retroarch_ps5_startup_args_diag.elf", + "profile": "startup-args-diag", + "local_relative_path": "build/phase10h/startup-args-a/retroarch_ps5_startup_args_diag.elf", + "linker_map_relative_path": "build/phase10h/startup-args-a/retroarch_ps5_startup_args_diag.map", + "size": 1845152, + "sha256": "822f2cf1f4d33a514d2bdd88fde40ad580dda5d85f537362ef6dff2eafcb56b6", + "clean_build_sha256": [ + "822f2cf1f4d33a514d2bdd88fde40ad580dda5d85f537362ef6dff2eafcb56b6", + "822f2cf1f4d33a514d2bdd88fde40ad580dda5d85f537362ef6dff2eafcb56b6" + ], + "linker_map_size": 637603, + "linker_map_sha256": "638642b750b8d5b108cf6c73215a3f1759bcb6da0b29ee0a0ade5c47e8b7b2d5", + "clean_map_sha256": [ + "638642b750b8d5b108cf6c73215a3f1759bcb6da0b29ee0a0ade5c47e8b7b2d5", + "638642b750b8d5b108cf6c73215a3f1759bcb6da0b29ee0a0ade5c47e8b7b2d5" + ], + "changed_from_phase10g": true, + "execution_eligible": false, + "transfer_eligible": false, + "installation_eligible": false, + "device_action_performed": false, + "tracked": false + }, + "result_protocol": { + "magic": "CHD10H01", + "version": 1, + "frame_size": 64, + "d_stages": ["D00", "D01", "D02", "D03", "D04", "D05", "D06", "D07", "D08", "D09", "D10", "D11", "D12"], + "interval_stages": ["I00", "I01", "I02", "I03", "I04", "I05", "I06", "I07", "I08", "I09", "I10", "I11", "I12", "I13", "I14"], + "target_write": "ONE_NONBLOCKING_SIGNAL_SUPPRESSED_INHERITED_STDOUT_SEND_PER_REACHED_STAGE", + "target_write_retry": false, + "short_write_retry": false, + "live_activation_available": true, + "live_activation_contract": "EXACT_ACTIVE_MANIFEST_AND_SEPARATE_LOCAL_APPROVAL_ONLY", + "protocol_activation_authorized": false, + "tracked_target": null, + "tracked_run_id": null, + "offline_parser_available": true + }, + "elf": { + "type": "ET_DYN", + "machine": "EM_X86_64", + "entrypoint": "0xff110", + "program_header_count": 4, + "load_segment_count": 3, + "rwx_load_segment_count": 0, + "bss_section_size": 697632, + "preinit_array_size": 0, + "init_array_size": 0, + "fini_array_size": 0, + "tls": false, + "relocations": {"total": 1055, "by_type": {"6": 142, "8": 913}}, + "dt_needed": [ + "libSceAudioOut.sprx", "libSceLibcInternal.sprx", "libScePad.sprx", + "libSceSystemService.sprx", "libSceUserService.sprx", + "libSceVideoOut.sprx", "libkernel_web.sprx" + ], + "undefined_symbol_count": 142, + "undefined_symbol_list_sha256": "719d1585e9e04b50f6db1764ff70e154b1b941e76acf9287c9d72811db0a7a55", + "undefined_symbols_match_phase10f": true, + "inherited_send_present": true, + "forbidden_imports_absent": [ + "socket", "connect", "bind", "listen", "accept", "recv", "recvfrom", + "sendto", "dlopen", "dlsym", "sceGnmSubmitCommandBuffers", + "sceGnmSubmitAndFlipCommandBuffers" + ], + "normalized_disassembly_sha256": [ + "d38ccaecb2f52b1529680dab68314b6b014ec446ac66cba645887fbe8f1d8c11", + "d38ccaecb2f52b1529680dab68314b6b014ec446ac66cba645887fbe8f1d8c11" + ] + }, + "startup_effects": { + "normal_sdk_crt": true, + "patch_init_reachable_from_start": true, + "temporary_existing_crt_effects_unchanged": true, + "new_effect": "VERBOSE_ARGUMENT_AND_DISTINCT_DIAGNOSTIC_MAGIC", + "side_effect_free": false + }, + "tests": { + "warnings_as_errors_cross_build": "PASS", + "target_host_asan_ubsan": "PASS", + "retroarch_python_cases": 22, + "retroarch_python": "PASS", + "structural_validator": "PASS", + "double_clean_build": "PASS_BYTE_IDENTICAL", + "dual_static_audit_semantics": "PASS_IDENTICAL", + "hardware_evidence_from_phase10h": false + }, + "hard_stops": [ + "NO_PS5_CONNECTION_OR_REQUEST", + "NO_TRANSFER_OR_EXECUTION", + "NO_RESULT_RECEPTION", + "NO_INSTALLATION_OR_AUTOLOAD", + "NO_DEVICE_WRITE", + "NO_RETRY_OR_RECONNECT", + "NEW_EXACT_ARTIFACT_PERMISSION_REQUIRED" + ] +} diff --git a/manifests/retroarch/phase-1.0i-flip-and-write-analysis.json b/manifests/retroarch/phase-1.0i-flip-and-write-analysis.json new file mode 100644 index 0000000..961cec8 --- /dev/null +++ b/manifests/retroarch/phase-1.0i-flip-and-write-analysis.json @@ -0,0 +1,149 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0I_OFFLINE_FLIP_AND_WRITE_ANALYSIS", + "status": "OFFLINE_ANALYSIS_COMPLETE_DEVICE_RETEST_BLOCKED", + "date": "2026-07-22", + "firmware_evidence": "9.60_CONSUMED_PHASE_1_0H_RUN_ONLY", + "source_commits": { + "retroarch_artifact_source": "c710f85816b5e456dd3a85a46f85eb28883522b3", + "retroarch_phase10h_result": "5db76beddd56c0f82ecbcccfbe8ed12ea3bb4bda", + "chimera_gfx_phase10h_result": "c0eb104eb516d82815f6611a32152a6f187842c6", + "public_sdl": "0baf4ac49382b537ba449901b5b6d0d189bb1fbb", + "public_ps5_payload_sdk_v041": "d2e2e585740362976a39fdd5ccf390f199a7bc37" + }, + "bound_evidence": { + "artifact": { + "name": "retroarch_ps5_startup_args_diag.elf", + "size": 1845152, + "sha256": "822f2cf1f4d33a514d2bdd88fde40ad580dda5d85f537362ef6dff2eafcb56b6", + "tracked": false, + "execution_eligible": false, + "transfer_eligible": false, + "installation_eligible": false + }, + "linker_map": { + "size": 637603, + "sha256": "638642b750b8d5b108cf6c73215a3f1759bc6da0b29ee0a0ade5c47e8b7b2d5", + "tracked": false + }, + "sdl_diagnostic_patch_sha256": "c287cf0207192b06c2658c2ae6982a3c2fe3ee9179908de5911d074786dc889e", + "materialized_sdl_video_source_sha256": "9a1c171711d2b5a1167b66a3c0e2ac926d7fd101b1f08b0a9240a3aa9c62211e", + "device_trace_sha256": "858a205afcf682a498d8bc11947b67d2446098883a61f6871e31df4d950d0a61" + }, + "phase_actions": { + "ps5_connected": false, + "device_request_performed": false, + "files_transferred": false, + "target_execution_performed": false, + "result_received_from_device": false, + "target_build_performed": false, + "target_artifact_created": false, + "device_write_performed": false, + "installation_performed": false, + "autoload_performed": false, + "retry_performed": false, + "reconnect_performed": false + }, + "current_authorizations": { + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "result_receive_authorized": false, + "target_build_authorized": false, + "installation_authorized": false, + "autoload_authorized": false, + "device_write_authorized": false, + "automatic_retry": false + }, + "write_firewall": { + "observed_after": "I03", + "observed_before": "I04", + "shutdown_reason": 6, + "first_error": 118, + "d12_transmitted_fields": ["shutdown_reason", "first_error"], + "status_field_not_transmitted": "first_blocked_write", + "linked_blocking_wrappers": [ + {"symbol": "__wrap_open", "address": "0x47580", "operation": "OPEN"}, + {"symbol": "__wrap_fopen", "address": "0x475b0", "operation": "OPEN"}, + {"symbol": "__wrap_fwrite", "address": "0x47600", "operation": "STREAM"} + ], + "possible_operations": ["OPEN", "STREAM"], + "exact_operation": "UNPROVEN", + "exact_path": "UNPROVEN", + "exact_mode_or_flags": "UNPROVEN", + "write_succeeded": false, + "wrapper_failure_errno": "EROFS", + "shutdown_stopped_later_initialization": false + }, + "first_flip": { + "pattern_destination": "vbuf[0]", + "pattern_copied_before_submit": true, + "submit_symbol": "sceVideoOutSubmitFlip", + "arguments": { + "handle": "D05_POSITIVE_RUNTIME_VALUE", + "buffer_index": 0, + "flip_mode": 1, + "flip_argument": 0 + }, + "disassembly": { + "buffer_index_zero": "0xfe6ac", + "flip_mode_one": "0xfe6ae", + "flip_argument_zero": "0xfe6b3", + "submit_call": "0xfe6bc" + }, + "buffer_registration_raw": 0, + "submit_raw": -1, + "event_wait_attempted": false, + "original_errno": "UNPROVEN", + "errno_captured_before_diagnostic_send": false, + "visible_presentation": "UNPROVEN" + }, + "source_inconsistency": { + "diagnostic_buffer_index": 0, + "diagnostic_flip_argument": 0, + "patched_normal_initial_frame_id": 1, + "patched_normal_initial_buffer_index": 1, + "public_sdl_initial_frame_id": 0, + "classification": "STRONG_SOURCE_CANDIDATE_NOT_PROVEN_ROOT_CAUSE" + }, + "abi_evidence": { + "sdk_export_stub_present": true, + "public_sdk_prototype_present": false, + "prototype_source": "PUBLIC_SDL_FORK", + "buffer_layout_source": "PUBLIC_SDL_FORK", + "exact_firmware_semantics": "UNPROVEN" + }, + "decision": { + "offline_analysis_complete": true, + "root_cause_identified": false, + "next_device_test_ready": false, + "next_offline_artifact_design_allowed": true, + "new_artifact_created_in_phase10i": false, + "new_device_client_created_in_phase10i": false + }, + "future_offline_requirements": [ + "CAPTURE_SUBMIT_ERRNO_BEFORE_ANY_REPORTING_CALL", + "REPORT_EXACT_FIRST_BLOCKED_WRITE_OPERATION", + "STOP_BEFORE_I04_AFTER_WRITE_FIREWALL_SHUTDOWN", + "USE_ONE_REVIEWED_FIRST_FRAME_SOURCE_OF_TRUTH", + "ONE_SUBMIT_NO_RETRY", + "WAIT_ONLY_AFTER_SUCCESSFUL_SUBMIT", + "NEW_HASH_AND_SEPARATE_REVIEW_BEFORE_ANY_DEVICE_ACTION" + ], + "tests": { + "chimera_gfx_ctest": "46_OF_46_PASS", + "retroarch_ps5_host_suite": "PASS_WITH_ASAN_UBSAN", + "phase10i_gfx_guardrails": 17, + "phase10i_retroarch_guardrails": 6, + "safety_audit": "PASS", + "secret_scan": "PASS", + "hardware_evidence_from_phase10i": false + }, + "hard_stops": [ + "PHASE_1_0H_AUTHORIZATION_CONSUMED", + "NO_CURRENT_DEVICE_ACTION_AUTHORIZATION", + "NO_RETRY_OR_RECONNECT", + "NO_ROOT_CAUSE_CLAIM", + "NO_FRAME_ID_CHANGE_WITHOUT_NEW_ADR" + ] +} diff --git a/manifests/retroarch/phase-1.0j-write-firewall-diagnostic.json b/manifests/retroarch/phase-1.0j-write-firewall-diagnostic.json new file mode 100644 index 0000000..e885e34 --- /dev/null +++ b/manifests/retroarch/phase-1.0j-write-firewall-diagnostic.json @@ -0,0 +1,165 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0J_WRITE_FIREWALL_DIAGNOSTIC", + "status": "OFFLINE_ARTIFACT_AUDITED_DEVICE_ACTION_BLOCKED", + "date": "2026-07-22", + "firmware_target": "9.60_BUILD_CONTEXT_ONLY_RUNTIME_UNPROVEN", + "source_commits": { + "retroarch": "2e385265ab893dbba5898178dde5e108d3dbfa19", + "chimera_gfx_baseline": "2bee49e5e8f1ee6faa0589d2d74de5ba0d809293", + "public_sdl": "0baf4ac49382b537ba449901b5b6d0d189bb1fbb", + "public_ps5_payload_sdk_v041": "d2e2e585740362976a39fdd5ccf390f199a7bc37" + }, + "artifact": { + "name": "retroarch_ps5_write_diag.elf", + "local_path": "../chimera-retroarch/build/phase10j/write-diag-a/retroarch_ps5_write_diag.elf", + "size": 1845208, + "sha256": "6ff0f7ea391da5f15ea43512a871078133e896a6900ae9f8f3fa75711abb8009", + "tracked": false, + "execution_eligible": false, + "transfer_eligible": false, + "installation_eligible": false + }, + "linker_map": { + "local_path": "../chimera-retroarch/build/phase10j/write-diag-a/retroarch_ps5_write_diag.map", + "size": 637728, + "sha256": "19f1cf851ad8f99d31d5de3a14591f81faa83589c18e93cd60be96ebef5d7719", + "tracked": false + }, + "source_material": { + "sdl_diagnostic_patch_sha256": "aa1d3ebc6043d6051938cee1dbffecb80729b3b6595eef964915e93f1570c193", + "materialized_sdl_video_source_sha256": "9949a280fed40241746788a8c001280455bc629e0566a88f82f16a8634d43025", + "sdl_diskaudio_enabled": false, + "upstream_base_clean_before_materialization": true, + "materialized_tree_contains_only_reviewed_patch_stack": true + }, + "protocol": { + "magic": "CHD10J01", + "new_stage": "D13", + "historical_stage_values_preserved": true, + "d13_fields": ["first_blocked_write", "write_block_count"], + "d13_immediately_precedes_terminal_d12": true, + "d13_stream_only": true, + "target_socket_created": false, + "live_runner_supports_j": false + }, + "write_firewall": { + "first_rejection_requests_shutdown": true, + "stop_check_location": "IMMEDIATELY_AFTER_RETROARCH_PARSE_INPUT_AND_CONFIG", + "stop_before": "I04", + "stop_before_driver_lookup": true, + "write_succeeds": false, + "wrapper_errno": "EROFS", + "surviving_wrappers": [ + {"symbol": "__wrap_open", "address": "0x475a0", "operation": "OPEN"}, + {"symbol": "__wrap_fopen", "address": "0x47610", "operation": "OPEN"}, + {"symbol": "__wrap_fwrite", "address": "0x476a0", "operation": "STREAM"} + ] + }, + "flip_diagnostic": { + "submit_count_maximum_per_reached_helper": 1, + "submit_errno_saved_immediately": true, + "reporting_before_errno_save": false, + "d07_fields_under_j": ["submit_result", "saved_errno"], + "wait_only_after_submit_success": true, + "first_frame_source": "CHIMERA_PS5_FIRST_FRAME_INDEX", + "first_frame_value": 0, + "adr": "docs/adr/0012-phase10j-first-frame-identity.md", + "firmware_semantics": "UNPROVEN", + "root_cause_claimed": false + }, + "artifact_audit": { + "elf_type": "DYN_PIE", + "entry_point": "0xff210", + "program_headers": 4, + "sections": 20, + "load_segment_permissions": ["R_E", "R", "RW"], + "rwx_load_segments": 0, + "init_array_size": 0, + "fini_array_size": 0, + "tls_sections": 0, + "bss_size": 697632, + "undefined_dynamic_symbols": 142, + "relocations": { + "total": 1055, + "R_X86_64_GLOB_DAT": 142, + "R_X86_64_RELATIVE": 913 + }, + "dt_needed": [ + "libSceAudioOut.sprx", + "libSceLibcInternal.sprx", + "libScePad.sprx", + "libSceSystemService.sprx", + "libSceUserService.sprx", + "libSceVideoOut.sprx", + "libkernel_web.sprx" + ], + "inherited_send_import_count": 1, + "socket_create_import": false, + "connect_import": false, + "listener_import": false, + "receive_import": false, + "gnm_imports": [], + "normalized_disassembly_sha256": "45dc4e0233b3770add430aaa6bb23a60210b50744effb76b515fca4b9e6e310f", + "dormant_string_findings_retained": true, + "string_absence_used_as_reachability_proof": false + }, + "reproducibility": { + "clean_builds": 2, + "elf_byte_identical": true, + "map_byte_identical": true, + "normalized_disassembly_identical": true, + "warnings_as_errors_for_retroarch_target": true, + "upstream_sdl_nonfatal_warnings": true + }, + "phase_actions": { + "ps5_connected": false, + "device_request_performed": false, + "files_transferred": false, + "target_execution_performed": false, + "result_received_from_device": false, + "target_build_performed": true, + "target_artifact_created": true, + "device_write_performed": false, + "installation_performed": false, + "autoload_performed": false, + "retry_performed": false, + "reconnect_performed": false + }, + "current_authorizations": { + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "result_receive_authorized": false, + "target_build_authorized": false, + "installation_authorized": false, + "autoload_authorized": false, + "device_write_authorized": false, + "automatic_retry": false + }, + "tests": { + "chimera_gfx_ctest": "49_OF_49_PASS", + "retroarch_ps5_host_suite": "PASS_WITH_ASAN_UBSAN", + "phase10j_gfx_guardrails": 20, + "phase10j_retroarch_guardrails": 4, + "safety_audit": "PASS", + "secret_scan": "PASS", + "hardware_evidence_from_phase10j": false + }, + "remaining_risks": [ + "NORMAL_SDK_CRT_RUNTIME_EFFECTS_INHERITED", + "FIRMWARE_9_60_J_RUNTIME_UNPROVEN", + "COMPLETE_TARGET_CLEANUP_UNPROVEN", + "BLOCKED_OPERATION_FOR_CONSUMED_H_RUN_UNPROVEN", + "VIDEOOUT_SUBMIT_ERRNO_UNPROVEN", + "VISIBLE_PRESENTATION_UNPROVEN", + "UPSTREAM_SDL_BUILDS_WITH_NONFATAL_WARNINGS" + ], + "hard_stops": [ + "NO_CURRENT_DEVICE_ACTION_AUTHORIZATION", + "NO_CONNECTION_TRANSFER_RECEPTION_OR_EXECUTION", + "NO_RETRY_OR_RECONNECT", + "NO_INSTALLATION_AUTOLOAD_OR_DEVICE_WRITE", + "ARTIFACT_AND_MAP_MUST_NOT_BE_COMMITTED" + ] +} diff --git a/manifests/retroarch/phase-1.0k-one-shot-approval-template.json b/manifests/retroarch/phase-1.0k-one-shot-approval-template.json new file mode 100644 index 0000000..ef5aa00 --- /dev/null +++ b/manifests/retroarch/phase-1.0k-one-shot-approval-template.json @@ -0,0 +1,31 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0K_WRITE_DIAG_ONE_SHOT_RUNNER", + "authorized": false, + "consumed": false, + "authorization_scope": "EXACT_ONE_SHOT_PHASE_1_0K", + "authorized_by": null, + "approval_reference": null, + "protocol_magic": "CHD10J01", + "protocol_version": 1, + "protocol_frame_size": 64, + "run_id": null, + "target": null, + "port": null, + "firmware": "9.60", + "artifact_name": "retroarch_ps5_write_diag.elf", + "artifact_size": 1845208, + "artifact_sha256": "6ff0f7ea391da5f15ea43512a871078133e896a6900ae9f8f3fa75711abb8009", + "timeout_seconds": 75, + "connection_count": 0, + "transfer_count": 0, + "execution_count": 0, + "result_receive_count": 0, + "installation": false, + "autoload": false, + "device_write": false, + "retry": false, + "reconnect": false, + "resume": false, + "reboot": false +} diff --git a/manifests/retroarch/phase-1.0k-write-diag-one-shot-runner.json b/manifests/retroarch/phase-1.0k-write-diag-one-shot-runner.json new file mode 100644 index 0000000..f14a896 --- /dev/null +++ b/manifests/retroarch/phase-1.0k-write-diag-one-shot-runner.json @@ -0,0 +1,130 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0K_WRITE_DIAG_ONE_SHOT_RUNNER", + "status": "OFFLINE_RUNNER_PREPARED_NO_DEVICE_AUTHORIZATION", + "scope": "HOST_ONLY_RUNNER_AND_POLICY_PREPARATION", + "date": "2026-07-22", + "source_commits": { + "chimera_gfx_phase10j": "05d316e4d48660ab24d116153f094ef7e2f69522", + "retroarch_artifact_source": "2e385265ab893dbba5898178dde5e108d3dbfa19", + "retroarch_runner": "ee965a0be3cd3e0032330680e7614c766688410a" + }, + "authorizations": { + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "result_receive_authorized": false, + "installation_authorized": false, + "autoload_authorized": false, + "device_write_authorized": false, + "automatic_retry": false + }, + "phase_actions": { + "ps5_connected": false, + "device_request_performed": false, + "files_transferred": false, + "target_execution_performed": false, + "result_received_from_device": false, + "target_build_performed": false, + "target_artifact_created": false, + "device_write_performed": false, + "installation_performed": false, + "autoload_performed": false, + "retry_performed": false, + "reconnect_performed": false + }, + "artifact": { + "name": "retroarch_ps5_write_diag.elf", + "profile": "write-diag", + "local_relative_path": "build/phase10j/write-diag-a/retroarch_ps5_write_diag.elf", + "size": 1845208, + "sha256": "6ff0f7ea391da5f15ea43512a871078133e896a6900ae9f8f3fa75711abb8009", + "source_unchanged_from_phase10j": true, + "execution_eligible": false, + "transfer_eligible": false, + "installation_eligible": false, + "tracked": false + }, + "result_protocol": { + "magic": "CHD10J01", + "version": 1, + "frame_size": 64, + "byte_order": "BIG_ENDIAN", + "wire_stages": [ + "D00", "D01", "D02", "D03", "D04", "D05", "D06", "D07", + "D08", "D09", "D10", "D11", "D12", + "I00", "I01", "I02", "I03", "I04", "I05", "I06", "I07", + "I08", "I09", "I10", "I11", "I12", "I13", "I14", "C1", "D13" + ], + "c1_wire_index": 28, + "d13_wire_index": 29, + "terminal_stage": "D12", + "protocol_activation_authorized": false, + "tracked_target": null, + "tracked_port": null, + "tracked_run_id": null + }, + "runner": { + "implementation_available": true, + "source_path": "tools/ps5_diag_duplex.py", + "source_sha256": "4ee58f08ff51cff3624cbc072c0e915e8c415eb8cd98185fa0d6a20c02b7c330", + "protocol_selection": "MANIFEST_ONLY", + "free_protocol_selector": false, + "active_manifest_required": true, + "separate_untracked_approval_required": true, + "actual_artifact_rehashed_before_socket": true, + "attempt_receipt_required": true, + "attempt_receipt_exclusive_create": true, + "attempt_receipt_durable_fsync": true, + "attempt_receipt_written_before_connect": true, + "maximum_connections": 1, + "maximum_transfers": 1, + "maximum_executions": 1, + "maximum_result_receives": 1, + "receive_limit_bytes": 65536, + "retry": false, + "reconnect": false, + "resume": false, + "trace_exclusive_create": true, + "trace_overwrite": false + }, + "approval_template": { + "path": "manifests/retroarch/phase-1.0k-one-shot-approval-template.json", + "authorized": false, + "consumed": false, + "target": null, + "port": null, + "run_id": null + }, + "activation_requirements": { + "new_exact_artifact_specific_permission": true, + "separately_reviewed_active_manifest": true, + "exact_target_only_in_untracked_local_approval": true, + "unique_run_id_required": true, + "authorization_reference_required": true, + "all_four_device_actions_must_be_explicitly_authorized": true, + "forbidden_actions_must_remain_false": true, + "current_requirements_satisfied": false + }, + "tests": { + "retroarch_ps5_host_suite": "PASS_WITH_ASAN_UBSAN", + "retroarch_duplex_python_cases": 26, + "retroarch_phase10k_guardrails": 4, + "chimera_gfx_ctest": "51_OF_51_PASS", + "chimera_gfx_phase10k_guardrails": 20, + "safety_audit": "PASS", + "secret_scan": "PASS", + "fake_socket_only": true, + "hardware_evidence_from_phase10k": false + }, + "hard_stops": [ + "NO_CURRENT_DEVICE_AUTHORIZATION", + "NO_PS5_CONNECTION_OR_REQUEST", + "NO_TRANSFER_EXECUTION_OR_RESULT_RECEPTION", + "NO_INSTALLATION_AUTOLOAD_OR_DEVICE_WRITE", + "NO_RETRY_RECONNECT_RESUME_OR_REBOOT", + "NO_TARGET_PORT_OR_RUN_ID_IN_TRACKED_RECORD", + "ARTIFACT_REMAINS_INELIGIBLE", + "NEW_EXACT_PERMISSION_REQUIRED" + ] +} diff --git a/manifests/retroarch/phase-1.0l-write-firewall-result-analysis.json b/manifests/retroarch/phase-1.0l-write-firewall-result-analysis.json new file mode 100644 index 0000000..9009912 --- /dev/null +++ b/manifests/retroarch/phase-1.0l-write-firewall-result-analysis.json @@ -0,0 +1,137 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0L_WRITE_FIREWALL_DEVICE_RESULT_ANALYSIS", + "status": "ONE_SHOT_AUTHORIZATION_CONSUMED_FIRST_WRITE_MKDIR", + "date": "2026-07-22", + "firmware": "9.60", + "source_commits": { + "chimera_gfx_runner": "e2aeb3335646eceda6b12fe723da3a7095be8d3f", + "retroarch_artifact": "2e385265ab893dbba5898178dde5e108d3dbfa19", + "retroarch_runner": "ee965a0be3cd3e0032330680e7614c766688410a", + "retroarch_result_record": "3da7e589f99676555025f2af238eaca1285ce363" + }, + "artifact": { + "name": "retroarch_ps5_write_diag.elf", + "size": 1845208, + "sha256": "6ff0f7ea391da5f15ea43512a871078133e896a6900ae9f8f3fa75711abb8009", + "execution_eligible": false, + "transfer_eligible": false, + "installation_eligible": false, + "tracked": false + }, + "authorization": { + "run_id": "phase10k-20260722-write-diag-01", + "active_manifest_sha256": "a909d1fe36ea54c72c96799c19222f8865596012a1c70bc10546103751a9213d", + "local_approval_sha256": "5d98bdca7ef8820ec70d050301a94ce0fd8cc65be67b949328a7c81f5a597131", + "attempt_receipt_sha256": "64358f967b0f6e8a1007a7bbb82f90e463f5b065391e5048cdd08e8a6ef68be0", + "consumed": true, + "authority_inherited_by_future_action": false + }, + "current_authorizations": { + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "result_receive_authorized": false, + "installation_authorized": false, + "autoload_authorized": false, + "device_write_authorized": false, + "automatic_retry": false + }, + "performed_actions": { + "ps5_connected": true, + "device_request_performed": true, + "files_transferred": true, + "target_execution_performed": true, + "result_received_from_device": true, + "persistent_staging_performed": false, + "installation_performed": false, + "autoload_performed": false, + "device_filesystem_write_performed": false, + "normal_sdk_crt_effects": "INHERITED_PER_ADR_0010", + "retry_performed": false, + "reconnect_performed": false, + "sdl_entered": false, + "videoout_entered": false, + "rendering_entered": false + }, + "transport": { + "connect_count": 1, + "sendall_count": 1, + "bytes_sent": 1845208, + "shutdown_write_count": 1, + "recv_call_count": 2, + "received_byte_count": 741, + "raw_stream_stored_bytes": 741, + "raw_stream_sha256": "9571d98d8b4db1bae051f94cd4a31a79c98a3c8549e053675686079a08bf0efc", + "raw_stream_truncated": false, + "retry_count": 0, + "reconnect_count": 0, + "close_called": true, + "valid_terminal_frame_received": true, + "host_duration_seconds": 1.1703325000125915 + }, + "trace": { + "tracked": false, + "size": 5841, + "sha256": "d06613712df01a51ed4af83b96b844668456589783b6202d669d27b4fd6b915a", + "parser_errors": [] + }, + "protocol_result": { + "magic": "CHD10J01", + "frame_size": 64, + "frame_count": 9, + "stages": [ + "D00", "D01", "D02", "I00", "I01", "I02", "I03", "D13", "D12" + ], + "raw_results": { + "D02": 0, + "D13_first_blocked_write": 6, + "D13_write_block_count": 1, + "D12_shutdown_reason": 6, + "D12_first_error": 118 + }, + "terminal_stage": "D12", + "classification": "PRE_I04_FAIL_CLOSED_FIRST_WRITE_MKDIR" + }, + "ordinary_stdout": { + "byte_count": 165, + "sha256": "2e4144d6a3dd8c695fe318bc20e0fec9efaa4878994fc46437a4805f4ab7a81b", + "preserved_losslessly_in_local_trace": true + }, + "source_binding": { + "first_blocked_write_operation": "MKDIR", + "write_block_count": 1, + "shutdown_reason": "WRITE_FIREWALL", + "first_error": "E118_WRITE_BLOCKED", + "runtime_path": "UNOBSERVED", + "source_route": "CONFIG_SET_DEFAULTS_BUILTIN_PLAYLIST_DIRECTORY", + "source_line": "configuration.c:3357", + "config_set_defaults_address": "0x22ba0", + "path_mkdir_call_address": "0x26856", + "path_mkdir_address": "0x188f0", + "path_mkdir_cb_relocation": "0x17c280 -> 0x15ae0", + "write_firewall_address": "0x466d0", + "reached_i04": false, + "sdl_video": "NOT_REACHED", + "videoout": "NOT_REACHED", + "rendering": "NOT_REACHED", + "complete_cleanup": "UNPROVEN", + "safe_exit": "UNPROVEN" + }, + "next_offline_candidate": { + "classification": "SCOPED_SOURCE_CORRECTION_READY_FOR_OFFLINE_REVIEW", + "change": "SKIP_BUILTIN_PLAYLIST_MKDIR_UNDER_NO_FILESYSTEM_WRITES", + "preserve_path_derivation": true, + "preserve_global_write_firewall": true, + "target_build_authorized": false, + "device_action_authorized": false + }, + "hard_stops": [ + "AUTHORIZATION_CONSUMED", + "NO_RETRY_OR_RECONNECT", + "NO_FURTHER_DEVICE_ACTION", + "NO_RUNTIME_PATH_CLAIM", + "NO_FLIP_ERRNO_RESULT_FROM_THIS_RUN", + "NEW_ARTIFACT_AND_NEW_EXACT_PERMISSION_REQUIRED" + ] +} diff --git a/manifests/retroarch/phase-1.0m-write-free-defaults.json b/manifests/retroarch/phase-1.0m-write-free-defaults.json new file mode 100644 index 0000000..41124fc --- /dev/null +++ b/manifests/retroarch/phase-1.0m-write-free-defaults.json @@ -0,0 +1,158 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0M_WRITE_FREE_DEFAULTS", + "status": "OFFLINE_ARTIFACT_AUDITED_DEVICE_ACTION_BLOCKED", + "date": "2026-07-22", + "firmware_target": "9.60_BUILD_CONTEXT_ONLY_RUNTIME_UNPROVEN", + "source_commits": { + "retroarch": "12cf1d783c41eb303987e49a5a920805a59ef7a4", + "chimera_gfx_baseline": "2618307eb2b7d2f35800fc42413ae8458f091e1c", + "public_sdl": "0baf4ac49382b537ba449901b5b6d0d189bb1fbb", + "public_ps5_payload_sdk_v041": "d2e2e585740362976a39fdd5ccf390f199a7bc37" + }, + "artifact": { + "name": "retroarch_ps5_write_diag.elf", + "local_path": "../chimera-retroarch/build/phase10m/write-diag-a/retroarch_ps5_write_diag.elf", + "size": 1845208, + "sha256": "c99a0856309a357ad2667d89b4924e4063ad214cae09c8a419457b0732f583cd", + "tracked": false, + "execution_eligible": false, + "transfer_eligible": false, + "installation_eligible": false + }, + "linker_map": { + "local_path": "../chimera-retroarch/build/phase10m/write-diag-a/retroarch_ps5_write_diag.map", + "size": 637728, + "sha256": "6768ffc7267b9b362c3b953571e5dfeed1e70004a3e19f41ef500c3f26272719", + "tracked": false + }, + "source_correction": { + "source_site": "configuration.c:3356-3359", + "guard": "!defined(CHIMERA_PS5_NO_FILESYSTEM_WRITES)", + "skipped_under_write_free": ["path_is_directory", "path_mkdir"], + "path_derivation_preserved": true, + "legacy_migration_compiled": false, + "have_configfile": false, + "config_set_defaults_address": "0x22ba0", + "config_set_defaults_path_mkdir_calls": 0, + "generic_path_mkdir_symbol_retained": true, + "runtime_effect": "UNPROVEN" + }, + "write_firewall": { + "linker_wrap_option_count": 17, + "all_linker_wrap_options_preserved": true, + "first_rejection_requests_shutdown": true, + "stop_before_i04_on_rejection": true, + "wrapper_errno": "EROFS", + "surviving_wrappers": [ + {"symbol": "__wrap_open", "address": "0x475b0", "operation": "OPEN"}, + {"symbol": "__wrap_fopen", "address": "0x47620", "operation": "OPEN"}, + {"symbol": "__wrap_fwrite", "address": "0x476b0", "operation": "STREAM"} + ] + }, + "artifact_audit": { + "elf_type": "DYN_PIE", + "entry_point": "0xff220", + "program_headers": 4, + "sections": 20, + "load_segment_permissions": ["R_E", "R", "RW"], + "rwx_load_segments": 0, + "init_array_size": 0, + "fini_array_size": 0, + "tls_sections": 0, + "bss_size": 697632, + "undefined_dynamic_symbols": 142, + "relocations": { + "total": 1055, + "R_X86_64_GLOB_DAT": 142, + "R_X86_64_RELATIVE": 913 + }, + "dt_needed": [ + "libSceAudioOut.sprx", + "libSceLibcInternal.sprx", + "libScePad.sprx", + "libSceSystemService.sprx", + "libSceUserService.sprx", + "libSceVideoOut.sprx", + "libkernel_web.sprx" + ], + "inherited_send_import_count": 1, + "socket_create_import": false, + "connect_import": false, + "listener_import": false, + "receive_import": false, + "gnm_imports": [], + "normalized_disassembly_sha256": "141a620484784e5e01ec0cb2a51f77b67712bc484aae6de0f139ba1e28922c87", + "hardened_loader_static_model": "ACCEPTED_BY_STATIC_MODEL", + "hardware_evidence": false + }, + "reproducibility": { + "clean_builds": 2, + "elf_byte_identical": true, + "map_byte_identical": true, + "normalized_disassembly_identical": true, + "warnings_as_errors_for_retroarch_target": true + }, + "inherited_runtime_contract": { + "protocol_magic": "CHD10J01", + "normal_sdk_crt_effects": "INHERITED_PER_ADR_0010", + "target_socket_created": false, + "live_runner_authorizes_artifact": false, + "i04": "UNPROVEN", + "sdl": "UNPROVEN", + "videoout": "UNPROVEN", + "visible_flip": "UNPROVEN", + "complete_cleanup": "UNPROVEN" + }, + "phase_actions": { + "ps5_connected": false, + "device_request_performed": false, + "files_transferred": false, + "target_execution_performed": false, + "result_received_from_device": false, + "target_build_performed": true, + "target_artifact_created": true, + "device_write_performed": false, + "installation_performed": false, + "autoload_performed": false, + "retry_performed": false, + "reconnect_performed": false + }, + "current_authorizations": { + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "result_receive_authorized": false, + "target_build_authorized": false, + "installation_authorized": false, + "autoload_authorized": false, + "device_write_authorized": false, + "automatic_retry": false + }, + "tests": { + "chimera_gfx_ctest": "54_OF_54_PASS", + "retroarch_ps5_host_suite": "PASS_WITH_ASAN_UBSAN", + "phase10m_gfx_guardrails": 20, + "phase10m_retroarch_guardrails": 6, + "safety_audit": "PASS", + "chimera_gfx_secret_scan": "WHOLE_TREE_PASS", + "retroarch_secret_scan": "PHASE_CHANGED_FILES_PASS", + "retroarch_whole_tree_scan": "PREEXISTING_UPSTREAM_TEST_KEY_FIXTURES_FLAGGED", + "hardware_evidence_from_phase10m": false + }, + "remaining_risks": [ + "NORMAL_SDK_CRT_RUNTIME_EFFECTS_INHERITED", + "FIRMWARE_9_60_M_RUNTIME_UNPROVEN", + "NEXT_WRITE_OR_INITIALIZATION_RESULT_UNPROVEN", + "I04_SDL_VIDEOOUT_FLIP_AND_VISIBILITY_UNPROVEN", + "COMPLETE_TARGET_CLEANUP_UNPROVEN" + ], + "hard_stops": [ + "NO_CURRENT_DEVICE_ACTION_AUTHORIZATION", + "NO_CONNECTION_TRANSFER_RECEPTION_OR_EXECUTION", + "NO_RETRY_OR_RECONNECT", + "NO_INSTALLATION_AUTOLOAD_OR_DEVICE_WRITE", + "CONSUMED_K_AUTHORITY_DOES_NOT_CARRY", + "ARTIFACT_MAP_AND_AUDIT_JSON_MUST_NOT_BE_COMMITTED" + ] +} diff --git a/manifests/retroarch/phase-1.0n-one-shot-approval-template.json b/manifests/retroarch/phase-1.0n-one-shot-approval-template.json new file mode 100644 index 0000000..29f65f2 --- /dev/null +++ b/manifests/retroarch/phase-1.0n-one-shot-approval-template.json @@ -0,0 +1,31 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0N_WRITE_FREE_DEFAULTS_ONE_SHOT_RUNNER", + "authorized": false, + "consumed": false, + "authorization_scope": "EXACT_ONE_SHOT_PHASE_1_0N", + "authorized_by": null, + "approval_reference": null, + "protocol_magic": "CHD10J01", + "protocol_version": 1, + "protocol_frame_size": 64, + "run_id": null, + "target": null, + "port": null, + "firmware": "9.60", + "artifact_name": "retroarch_ps5_write_diag.elf", + "artifact_size": 1845208, + "artifact_sha256": "c99a0856309a357ad2667d89b4924e4063ad214cae09c8a419457b0732f583cd", + "timeout_seconds": 75, + "connection_count": 0, + "transfer_count": 0, + "execution_count": 0, + "result_receive_count": 0, + "installation": false, + "autoload": false, + "device_write": false, + "retry": false, + "reconnect": false, + "resume": false, + "reboot": false +} diff --git a/manifests/retroarch/phase-1.0n-write-free-one-shot-runner.json b/manifests/retroarch/phase-1.0n-write-free-one-shot-runner.json new file mode 100644 index 0000000..c0babf6 --- /dev/null +++ b/manifests/retroarch/phase-1.0n-write-free-one-shot-runner.json @@ -0,0 +1,134 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0N_WRITE_FREE_DEFAULTS_ONE_SHOT_RUNNER", + "status": "OFFLINE_RUNNER_PREPARED_NO_DEVICE_AUTHORIZATION", + "scope": "HOST_ONLY_RUNNER_AND_POLICY_PREPARATION", + "date": "2026-07-22", + "source_commits": { + "chimera_gfx_phase10m": "ab364b8e213e136fff746b948dce89ab028dbf7d", + "retroarch_artifact_source": "12cf1d783c41eb303987e49a5a920805a59ef7a4", + "retroarch_runner": "606909706f91d7213751c245081333f56c2cce89" + }, + "authorizations": { + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "result_receive_authorized": false, + "installation_authorized": false, + "autoload_authorized": false, + "device_write_authorized": false, + "automatic_retry": false + }, + "phase_actions": { + "ps5_connected": false, + "device_request_performed": false, + "files_transferred": false, + "target_execution_performed": false, + "result_received_from_device": false, + "target_build_performed": false, + "target_artifact_created": false, + "device_write_performed": false, + "installation_performed": false, + "autoload_performed": false, + "retry_performed": false, + "reconnect_performed": false + }, + "artifact": { + "name": "retroarch_ps5_write_diag.elf", + "profile": "write-diag", + "local_relative_path": "build/phase10m/write-diag-a/retroarch_ps5_write_diag.elf", + "size": 1845208, + "sha256": "c99a0856309a357ad2667d89b4924e4063ad214cae09c8a419457b0732f583cd", + "source_unchanged_from_phase10m": true, + "execution_eligible": false, + "transfer_eligible": false, + "installation_eligible": false, + "tracked": false + }, + "result_protocol": { + "name": "PHASE_1_0N_WRITE_FREE_DEFAULTS", + "magic": "CHD10J01", + "version": 1, + "frame_size": 64, + "byte_order": "BIG_ENDIAN", + "wire_stages": [ + "D00", "D01", "D02", "D03", "D04", "D05", "D06", "D07", + "D08", "D09", "D10", "D11", "D12", + "I00", "I01", "I02", "I03", "I04", "I05", "I06", "I07", + "I08", "I09", "I10", "I11", "I12", "I13", "I14", "C1", "D13" + ], + "c1_wire_index": 28, + "d13_wire_index": 29, + "terminal_stage": "D12", + "protocol_activation_authorized": false, + "tracked_target": null, + "tracked_port": null, + "tracked_run_id": null + }, + "runner": { + "implementation_available": true, + "source_path": "tools/ps5_diag_duplex.py", + "source_size": 21179, + "source_sha256": "1d46510369349c1e75ef3b5f983a2ef6fa5896fdd4398788bf58708821d0385d", + "protocol_selection": "MANIFEST_ONLY", + "free_protocol_selector": false, + "active_manifest_required": true, + "separate_untracked_approval_required": true, + "actual_artifact_rehashed_before_socket": true, + "attempt_receipt_required": true, + "attempt_receipt_exclusive_create": true, + "attempt_receipt_durable_fsync": true, + "attempt_receipt_written_before_connect": true, + "maximum_connections": 1, + "maximum_transfers": 1, + "maximum_executions": 1, + "maximum_result_receives": 1, + "receive_limit_bytes": 65536, + "retry": false, + "reconnect": false, + "resume": false, + "trace_exclusive_create": true, + "trace_overwrite": false + }, + "approval_template": { + "path": "manifests/retroarch/phase-1.0n-one-shot-approval-template.json", + "authorized": false, + "consumed": false, + "target": null, + "port": null, + "run_id": null + }, + "activation_requirements": { + "new_exact_artifact_specific_permission": true, + "separately_reviewed_active_manifest": true, + "exact_target_only_in_untracked_local_approval": true, + "unique_run_id_required": true, + "authorization_reference_required": true, + "all_four_device_actions_must_be_explicitly_authorized": true, + "forbidden_actions_must_remain_false": true, + "phase10k_authority_reusable": false, + "current_requirements_satisfied": false + }, + "tests": { + "retroarch_ps5_host_suite": "PASS_WITH_ASAN_UBSAN", + "retroarch_duplex_python_cases": 28, + "retroarch_phase10n_guardrails": 5, + "chimera_gfx_ctest": "56_OF_56_PASS", + "chimera_gfx_phase10n_guardrails": 20, + "safety_audit": "PASS", + "secret_scan": "PASS", + "fake_socket_only": true, + "hardware_evidence_from_phase10n": false + }, + "hard_stops": [ + "NO_CURRENT_DEVICE_AUTHORIZATION", + "NO_PS5_CONNECTION_OR_REQUEST", + "NO_TRANSFER_EXECUTION_OR_RESULT_RECEPTION", + "NO_INSTALLATION_AUTOLOAD_OR_DEVICE_WRITE", + "NO_RETRY_RECONNECT_RESUME_OR_REBOOT", + "NO_TARGET_PORT_OR_RUN_ID_IN_TRACKED_RECORD", + "ARTIFACT_REMAINS_INELIGIBLE", + "PHASE_1_0K_AUTHORITY_CONSUMED_AND_NOT_REUSABLE", + "NEW_EXACT_PERMISSION_REQUIRED" + ] +} diff --git a/manifests/retroarch/phase-1.0o-write-free-device-result.json b/manifests/retroarch/phase-1.0o-write-free-device-result.json new file mode 100644 index 0000000..d9cf8c5 --- /dev/null +++ b/manifests/retroarch/phase-1.0o-write-free-device-result.json @@ -0,0 +1,144 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0O_WRITE_FREE_DEFAULTS_ONE_SHOT_DEVICE_RESULT", + "status": "ONE_SHOT_AUTHORIZATION_CONSUMED_I04_REACHED_FLIP_SUBMIT_FAILED", + "date": "2026-07-22", + "firmware": "9.60", + "source_commit": "12cf1d783c41eb303987e49a5a920805a59ef7a4", + "runner_commit": "606909706f91d7213751c245081333f56c2cce89", + "chimera_gfx_gate_commit": "dddf16da5448d816e913c2f3a982d112851d983d", + "artifact": { + "name": "retroarch_ps5_write_diag.elf", + "size": 1845208, + "sha256": "c99a0856309a357ad2667d89b4924e4063ad214cae09c8a419457b0732f583cd", + "execution_eligible": false, + "transfer_eligible": false, + "installation_eligible": false, + "tracked": false + }, + "authorization": { + "run_id": "phase10o-20260722-write-free-01", + "active_manifest_sha256": "64bb022b2c7de5627c42800406c5db444611a89e98fbfb99ca301e7a90fbfbef", + "local_approval_sha256": "0dc47462e4d5be47def77195aca74c4e320d4c21b45c4decbc319f6d16dbf064", + "attempt_receipt_sha256": "92ba19c3306f99ec73d269f62d7b63c433b83f9a358f6f43faf1c41875613fbb", + "consumed": true, + "authority_inherited_by_future_action": false + }, + "current_authorizations": { + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "result_receive_authorized": false, + "installation_authorized": false, + "autoload_authorized": false, + "device_write_authorized": false, + "automatic_retry": false + }, + "performed_actions": { + "ps5_connected": true, + "device_request_performed": true, + "files_transferred": true, + "target_execution_performed": true, + "result_received_from_device": true, + "persistent_staging_performed": false, + "installation_performed": false, + "autoload_performed": false, + "retry_performed": false, + "reconnect_performed": false, + "display_memory_mutated_by_diagnostic_pattern": true, + "videoout_flip_submit_attempted": true, + "videoout_flip_submit_succeeded": false + }, + "transport": { + "connect_count": 1, + "sendall_count": 1, + "bytes_sent": 1845208, + "shutdown_write_count": 1, + "recv_call_count": 14, + "received_byte_count": 3953, + "raw_stream_stored_bytes": 3953, + "raw_stream_sha256": "61941124ea06b22f8e27df795705329bd5b39e76b86770872dbbf4a611d02c1e", + "raw_stream_truncated": false, + "remote_eof_observed": true, + "retry_count": 0, + "reconnect_count": 0, + "close_called": true, + "host_duration_seconds": 2.0589744000171777 + }, + "trace": { + "tracked": false, + "size": 18031, + "sha256": "3d0b8811ae11f5cac2c2d331e252e1789a10588cab0e6045828a6b6af0fe1eb6", + "parser_errors": [], + "runner_terminal_classification": "INCOMPLETE_LAST_FRAME_AFTER_TERMINAL", + "individual_frames_usable_as_device_evidence": true + }, + "protocol_result": { + "name": "PHASE_1_0N_WRITE_FREE_DEFAULTS", + "magic": "CHD10J01", + "frame_size": 64, + "frame_count": 25, + "stages": [ + "D00", "D01", "D02", "I00", "I01", "I02", "I03", "I04", + "I05", "I06", "I07", "I08", "I09", "I10", "D10", "I11", + "I12", "I13", "I14", "D03", "D05", "D06", "D07", "D12", "D04" + ], + "raw_results": { + "D02_platform_init": 0, + "D10_core_init": 1, + "I11_core_init": 0, + "D05_videoout_handle": 1309671680, + "D06_buffer_registration": 0, + "D07_flip_submit": -1, + "D07_saved_errno": 0, + "D12_shutdown_reason": 5, + "D12_first_error": 104, + "D04_sdl_init": -1 + }, + "terminal_flag_frame": "D12_SEQUENCE_24", + "frame_after_terminal": "D04_SEQUENCE_25", + "valid_terminal_frame_received_classification": false, + "remote_eof_after_frames": true, + "classification": "WRITE_FREE_DEFAULTS_PASSED_I04_VIDEOOUT_BUFFERS_REGISTERED_EARLY_FLIP_SUBMIT_FAILED" + }, + "ordinary_stdout": { + "byte_count": 2353, + "sha256": "3365f10a8ea05b6e6040defedf66f03309fc6dc22aefe32ce10e968c266bd8f4", + "preserved_losslessly_in_local_trace": true, + "key_error": "Cannot open video driver", + "fatal_location": "video_driver_init_internal()", + "core_unload_messages_present": true + }, + "source_binding": { + "phase10m_write_free_correction_passed_i04": true, + "write_firewall_triggered": false, + "d13_present": false, + "sdl_video_entry_reached": true, + "videoout_open_returned_positive_handle": true, + "videoout_buffer_registration_raw": 0, + "diagnostic_pattern_copied_before_submit": true, + "first_flip_call": "sceVideoOutSubmitFlip(handle,0,1,0)", + "first_flip_submit_raw": -1, + "first_flip_saved_errno": 0, + "first_flip_wait_called": false, + "e104_is_generic_framebuffer_fail_label_after_early_flip_failure": true, + "sdl_init_raw": -1, + "visible_presentation": "UNPROVEN", + "complete_cleanup": "UNPROVEN", + "safe_exit": "UNPROVEN" + }, + "tests": { + "chimera_gfx_ctest": "58_OF_58_PASS", + "phase10o_guardrails": 20, + "safety_audit": "PASS", + "secret_scan": "PASS", + "device_result_not_generated_by_host_test": true + }, + "hard_stops": [ + "AUTHORIZATION_CONSUMED", + "NO_RETRY_OR_RECONNECT", + "NO_FURTHER_DEVICE_ACTION", + "NO_INSTALLATION_AUTOLOAD_OR_DEVICE_WRITE", + "NEW_ARTIFACT_AND_NEW_EXACT_PERMISSION_REQUIRED" + ] +} diff --git a/manifests/retroarch/phase-1.0p-videoout-submit-analysis.json b/manifests/retroarch/phase-1.0p-videoout-submit-analysis.json new file mode 100644 index 0000000..ae993e6 --- /dev/null +++ b/manifests/retroarch/phase-1.0p-videoout-submit-analysis.json @@ -0,0 +1,127 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0P_VIDEOOUT_SUBMIT_OFFLINE_ANALYSIS", + "status": "VIDEOOUT_SUBMIT_FAILURE_SITE_PROVEN_ROOT_CAUSE_UNRESOLVED", + "date": "2026-07-22", + "firmware": "9.60", + "source_commits": { + "chimera_gfx_phase10o": "af283ef93fae907e6c9c376df24d053ada3f7318", + "chimera_retroarch_artifact": "12cf1d783c41eb303987e49a5a920805a59ef7a4", + "chimera_retroarch_runner": "606909706f91d7213751c245081333f56c2cce89", + "ps5_payload_sdk_v0_41": "d2e2e585740362976a39fdd5ccf390f199a7bc37", + "public_ps5_sdl": "0baf4ac49382b537ba449901b5b6d0d189bb1fbb" + }, + "bound_inputs": { + "artifact_name": "retroarch_ps5_write_diag.elf", + "artifact_size": 1845208, + "artifact_sha256": "c99a0856309a357ad2667d89b4924e4063ad214cae09c8a419457b0732f583cd", + "trace_size": 18031, + "trace_sha256": "3d0b8811ae11f5cac2c2d331e252e1789a10588cab0e6045828a6b6af0fe1eb6", + "linked_sdl_source_size": 19642, + "linked_sdl_source_sha256": "9949a280fed40241746788a8c001280455bc629e0566a88f82f16a8634d43025", + "linker_map_size": 637728, + "linker_map_sha256": "6768ffc7267b9b362c3b953571e5dfeed1e70004a3e19f41ef500c3f26272719" + }, + "offline_actions": { + "source_inspected": true, + "linker_map_inspected": true, + "disassembly_inspected": true, + "dynamic_symbols_and_relocations_inspected": true, + "target_source_changed": false, + "target_artifact_created": false, + "ps5_connected": false, + "device_transfer_performed": false, + "target_execution_performed": false, + "result_received_from_device": false + }, + "current_authorizations": { + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "result_receive_authorized": false, + "installation_authorized": false, + "autoload_authorized": false, + "device_write_authorized": false, + "automatic_retry": false + }, + "exact_call": { + "symbol": "sceVideoOutSubmitFlip", + "tuple": "(handle,0,1,0)", + "function": "PS5_PresentEarlyDiagnosticFrame", + "function_address": "0xfe560", + "call_address": "0xfe7bc", + "got_address": "0x1779b0", + "registers": {"edi": "handle", "esi": 0, "edx": 1, "ecx": 0}, + "runtime_handle": "0x4e100100", + "runtime_return": -1, + "saved_errno": 0, + "wait_called_after_failure": false + }, + "registration": { + "function": "PS5_VideoInit", + "function_address": "0xfdc20", + "tuple": "sceVideoOutRegisterBuffers2(handle,0,0,buffers,2,attr,0,NULL)", + "runtime_return": 0, + "buffer_index_used": 0, + "opaque_layout_semantics": "UNPROVEN" + }, + "abi_evidence": { + "sdk_exports_symbols_only": true, + "sdk_public_videoout_headers_present": false, + "sdl_fork_is_only_local_public_prototype_source": true, + "submit_signature_independently_corroborated": false, + "flip_mode_semantics": "UNPROVEN", + "frame_id_semantics": "UNPROVEN", + "buffer_zero_validity": "UNPROVEN", + "buffer_attribute_semantics": "UNPROVEN", + "flip_master_or_active_app_requirement": "UNPROVEN", + "return_code_semantics": "UNPROVEN", + "errno_is_useful_for_observed_failure": false + }, + "candidate_matrix": { + "diagnostic_normal_first_index_mismatch": "REJECTED_CURRENT_CAUSE", + "wrong_submit_tuple": "UNPROVEN", + "wrong_opaque_buffer_contract": "UNPROVEN", + "missing_flip_master_or_app_state": "UNPROVEN", + "buffer_registration_failure": "REJECTED_BY_OBSERVED_RETURN_ZERO", + "errno_specific_failure_identity": "ABSENT", + "visible_flip": "UNPROVEN", + "complete_cleanup": "UNPROVEN", + "safe_exit": "UNPROVEN" + }, + "terminal_ordering": { + "observed": ["D07", "D12", "D04"], + "source_deterministic": true, + "network_reordering_required": false, + "d12_meaning": "FAILURE_SHUTDOWN_REQUEST", + "d12_proves_cleanup_complete": false, + "current_trace_valid_terminal_classification": false, + "host_parser_relaxation_allowed": false, + "future_protocol_requires_new_magic": true, + "future_terminal_after_d04_and_cleanup_evidence": true + }, + "decision": { + "classification": "VIDEOOUT_SUBMIT_FAILURE_SITE_PROVEN_ROOT_CAUSE_UNRESOLVED", + "parameter_experiment_allowed": false, + "new_videoout_export_call_allowed": false, + "host_parser_promotion_allowed": false, + "phase10q_target_build_allowed": false, + "phase10q_device_action_allowed": false, + "required_next_evidence": "ACCEPTABLE_PUBLIC_VIDEOOUT_ABI_AND_RUNTIME_STATE_CONTRACT" + }, + "tests": { + "chimera_gfx_ctest": "60_OF_60_PASS", + "phase10p_guardrails": 20, + "safety_audit": "PASS", + "secret_scan": "PASS", + "hardware_claim_from_host_test": false + }, + "hard_stops": [ + "NO_PARAMETER_GUESS", + "NO_NEW_VIDEOOUT_CALL_FROM_SYMBOL_NAME", + "NO_TARGET_BUILD", + "NO_DEVICE_CONNECTION_TRANSFER_EXECUTION_OR_RESULT_RECEIVE", + "NO_RETRY_OR_RECONNECT", + "NEW_EXACT_ARTIFACT_AND_PERMISSION_REQUIRED_FOR_ANY_FUTURE_RUN" + ] +} diff --git a/manifests/retroarch/phase-1.0q-public-videoout-evidence.json b/manifests/retroarch/phase-1.0q-public-videoout-evidence.json new file mode 100644 index 0000000..2387daa --- /dev/null +++ b/manifests/retroarch/phase-1.0q-public-videoout-evidence.json @@ -0,0 +1,129 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0Q_PUBLIC_VIDEOOUT_EVIDENCE", + "status": "PUBLIC_VIDEOOUT_EVIDENCE_INSUFFICIENT_PARAMETER_CHANGE_BLOCKED", + "date": "2026-07-22", + "scope": "BOUNDED_PUBLIC_SOURCE_RESEARCH", + "start_commit": "df470c691f375b73768fd0d4a51dc2d35523e71d", + "network_scope": { + "github_metadata": true, + "official_source_archives": true, + "arbitrary_mirrors": false, + "downloaded_code_executed": false, + "ps5_address_used": false, + "ps5_connected": false + }, + "authorizations": { + "target_build_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "result_receive_authorized": false, + "installation_authorized": false, + "autoload_authorized": false, + "device_write_authorized": false, + "automatic_retry": false + }, + "sdk": { + "repository": "https://github.com/ps5-payload-dev/sdk", + "release": "v0.41", + "release_commit": "d2e2e585740362976a39fdd5ccf390f199a7bc37", + "latest_release_during_audit": true, + "master_commit_inspected": "a0d2bc60bdcc0a5ee9e790fa3b02fe5051a152d0", + "videoout_stub_size": 25199, + "videoout_stub_sha256": "da6cff9b3255e9ccb0440790f73e0265696ca47724cf8fe5950b579702940016", + "exports_only": true, + "public_videoout_header": false, + "signatures_or_layouts": false, + "return_semantics": false + }, + "sdl_lineage": { + "repository": "https://github.com/ps5-payload-dev/SDL", + "pinned_commit": "0baf4ac49382b537ba449901b5b6d0d189bb1fbb", + "initial_ps5_video_commit": "2682a5e31e8aee888538b0fb7253d8cadf2797a1", + "initial_source_size": 7873, + "initial_source_sha256": "1b9f917cac4e00ba1eccb22093e3ca079238f91c9a44ceefa9e23570ca9abab3", + "declaration_move_commit": "fdfa470a0fa33215c677193982dfe3651ac1321a", + "uint32_cleanup_commit": "14ac2ec2ab5889af47218a957e3456e613b13d3d", + "pinned_header_size": 2878, + "pinned_header_sha256": "e60766e0b43c2a7fceba2ada0c030e7f7b05e63885590092f2f8f5b9da34ed75", + "pinned_source_size": 12162, + "pinned_source_sha256": "44124546da132ea6e12b1f06a1808d61690c73460a65c3161e8453e8108684b6", + "independent_abi_source_cited": false, + "opaque_buffer_member_name": "junk0", + "opaque_attribute_member_name": "junk0", + "single_lineage_only": true + }, + "official_project_archives": [ + {"repository": "ps5-payload-dev/libcross2d", "commit": "829b167262d3567a048b6e69416f8e3be399c098", "size": 1838927, "sha256": "eb952a041396ba01224a890d14d41662b017488f487ddd4c4018ffddd4a59ae2", "direct_videoout_hits": 0, "relationship": "SDL_CONSUMER"}, + {"repository": "ps5-payload-dev/pemu", "commit": "4136088e13e825f33c9d0cc43ff9a8c8f749dcb0", "size": 7540449, "sha256": "9ed14a1ea432638992b614f9946bcbc9770f70b6f18ded5778fd6809a927eaeb", "direct_videoout_hits": 0, "relationship": "SDL_CONSUMER"}, + {"repository": "ps5-payload-dev/FBNeo", "commit": "ca4222ca2cd52215a673c565a5eae3589b352ba2", "size": 15318689, "sha256": "824d6e5b6b61555f5ecc598204052dcb4b1cf16d28be29b9594a44866e726ef3", "direct_videoout_hits": 0, "relationship": "NO_DIRECT_IMPLEMENTATION"}, + {"repository": "ps5-payload-dev/LakeSnes", "commit": "a2db690123649c7ffbc68a663af31efb3a41bf3f", "size": 413067, "sha256": "253a08957a68461a8535a4727ee1242c21b2dc40c8eb8c99bdb85af799ac3ca6", "direct_videoout_hits": 0, "relationship": "SDL_CONSUMER"}, + {"repository": "ps5-payload-dev/yquake2", "commit": "9e233b6a601c393be4ff1dcb1c003750346b8bbb", "size": 2914919, "sha256": "01ef41608b2be0ace119019901a6ea22f304542972bace714d1177cb7f567978", "direct_videoout_hits": 0, "relationship": "SDL_CONSUMER"}, + {"repository": "ps5-payload-dev/pacbrew-repo", "commit": "c2abcfcb60f569128abd0e8e70ad03a67bee5ea7", "size": 143496, "sha256": "17be50219ce41772cfc50ea9274b0a62a2259162556b6eeecfaed4a691822872", "direct_videoout_hits": 0, "relationship": "REFERENCES_SAME_SDL_FORK"} + ], + "temporary_research_storage": { + "archive_count": 6, + "downloaded_bytes": 28169547, + "retained_in_git": false, + "cleaned_after_static_scan": false, + "cleanup_status": "RETAINED_TEMP_LOCAL_DELETE_POLICY_BLOCKED" + }, + "additional_ps5_source": { + "repository": "https://github.com/PS5Dev/PS5SDK", + "commit": "a2e03a2a0231a3a3397fa6cd087a01ca6d04f273", + "videoout_implementation_found": false, + "classification": "NO_CORROBORATION" + }, + "ps4_analogue": { + "repository": "https://github.com/OpenOrbis/OpenOrbis-PS4-Toolchain", + "commit": "0a1aaf9dd4a92695538bdeb09fb056d06dd11725", + "header_sha256": "910142e308e7ad9a6e1bc952e5ba1f24e8b7348f86180f62f4fa27c1e0ff016f", + "documentation_sha256": "f1dd2b91726aa0e0ca5828556deb23f27b2422b4174bd423786df429bccf20db", + "four_argument_submit": true, + "mode_one_documented_as_vsync": true, + "classification": "PS4_ANALOG_ONLY", + "accepted_as_ps5_abi": false + }, + "evidence_matrix": { + "submit_signature": "PARTIAL_SINGLE_LINEAGE", + "argument_positions": "PARTIAL_SINGLE_LINEAGE", + "mode_one_semantics": "UNPROVEN_ON_PS5", + "frame_zero_validity": "UNPROVEN_ON_PS5", + "video_buffer_layout": "UNPROVEN", + "video_attribute_layout": "UNPROVEN", + "format_constant_semantics": "UNPROVEN", + "register_return_semantics": "PARTIAL_RUNTIME_ONLY", + "submit_error_semantics": "UNPROVEN", + "flip_master_or_active_app_requirement": "UNPROVEN", + "status_query_signatures_and_layouts": "UNPROVEN" + }, + "decision": { + "independent_ps5_abi_corroboration_found": false, + "root_cause_resolved": false, + "parameter_change_allowed": false, + "new_videoout_call_allowed": false, + "phase10r_target_artifact_allowed": false, + "phase10r_device_action_allowed": false, + "safe_next_steps": [ + "OFFLINE_LAUNCH_CONTEXT_COMPARISON", + "HOST_OR_SOFTWARE_ONLY_INTEGRATION" + ] + }, + "performed_actions": { + "target_source_changed": false, + "target_artifact_created": false, + "ps5_connected": false, + "device_transfer_performed": false, + "target_execution_performed": false, + "result_received_from_device": false + }, + "tests": { + "chimera_gfx_ctest": "62_OF_62_PASS", + "phase10q_guardrails": 20, + "safety_audit": "PASS", + "secret_scan": "PASS", + "network_required_by_tests": false, + "hardware_claim_from_host_test": false + } +} diff --git a/manifests/retroarch/phase-1.0r-launch-context-analysis.json b/manifests/retroarch/phase-1.0r-launch-context-analysis.json new file mode 100644 index 0000000..3fd3941 --- /dev/null +++ b/manifests/retroarch/phase-1.0r-launch-context-analysis.json @@ -0,0 +1,112 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0R_LAUNCH_CONTEXT_ANALYSIS", + "status": "NO_SOURCE_PROVEN_LAUNCH_CONTEXT_FIX_TARGET_CHANGE_BLOCKED", + "date": "2026-07-22", + "scope": "OFFLINE_SOURCE_MAP_AND_PROVENANCE_AUDIT", + "start_commit": "dbd2b2658bb12018a691c4a0edfb06c2a103848f", + "authorizations": { + "target_build_authorized": false, + "ps5_connection_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "result_receive_authorized": false, + "installation_authorized": false, + "autoload_authorized": false, + "device_write_authorized": false, + "automatic_retry": false + }, + "source_identities": { + "retroarch_artifact_source_commit": "12cf1d783c41eb303987e49a5a920805a59ef7a4", + "retroarch_inspected_head": "606909706f91d7213751c245081333f56c2cce89", + "sdk_commit": "d2e2e585740362976a39fdd5ccf390f199a7bc37", + "sdl_commit": "0baf4ac49382b537ba449901b5b6d0d189bb1fbb", + "hardened_elfldr_commit": "197623058f509eddde18868dafcb92fdcac66464", + "controlled_payload_manager_commit": "e23d94ff91233aa770e2342800c1467875bdef44", + "pacbrew_commit": "c2abcfcb60f569128abd0e8e70ad03a67bee5ea7", + "lakesnes_reference_commit": "a2db690123649c7ffbc68a663af31efb3a41bf3f" + }, + "exact_files": { + "sdl2main": {"size": 438, "sha256": "55953065bb81ec6f00224d54c19f667336936e55a6805e08b60f6dda126d6648"}, + "sdl_ps5_video": {"size": 12162, "sha256": "44124546da132ea6e12b1f06a1808d61690c73460a65c3161e8453e8108684b6"}, + "sdk_crt": {"size": 4683, "sha256": "3875f4739ec40b33f1f4967a1acbb585a527d2c281a360153e1f69c8b945932a"}, + "elfldr": {"size": 21758, "sha256": "9949f8e4037984d10f1f5aa498e4665593d1fac8a33614d7f2141349839bb803"}, + "elfldr_socket_server": {"size": 11556, "sha256": "d642ced3e9b4a296dd15e355050ebe956f53a6dfdaa6ac10109cd067a3bba3d7"}, + "payload_manager_launcher": {"size": 6326, "sha256": "29c1a5fd01784a59e88b3698940f120cb03020071bc2b7d74a1da1a51524ef59"}, + "retroarch_makefile": {"size": 16122, "sha256": "6d248aa2b77d5ccea3a8d1de02e939946b7e7764e5b012ef513c1eed4dcc6e27"}, + "artifact": {"size": 1845208, "sha256": "c99a0856309a357ad2667d89b4924e4063ad214cae09c8a419457b0732f583cd"}, + "linker_map": {"size": 637728, "sha256": "6768ffc7267b9b362c3b953571e5dfeed1e70004a3e19f41ef500c3f26272719"} + }, + "sdl2main": { + "linked_by_exact_retroarch_artifact": false, + "calls_hide_splash_before_sdl_main": true, + "calls_load_exec_exit_after_sdl_main_returns": true, + "adds_application_registration": false, + "adds_title_identity": false, + "adds_lnc_setup": false, + "adds_videoout_ownership_setup": false, + "pre_submit_difference": "EARLIER_HIDE_SPLASH_ONLY", + "post_return_action": "LOAD_EXEC_EXIT" + }, + "exact_retroarch_path": { + "sdk_crt1_linked": true, + "sdl_video_backend_linked": true, + "sdl2main_linked": false, + "system_service_hide_splash_imported": true, + "system_service_load_exec_imported": false, + "sdl_video_hides_splash_before_videoout_open": true + }, + "launch_routes": { + "direct_raw_elf": {"constructor": "HARDENED_ELFLDR_SPAWN", "app_registration_found": false}, + "payload_manager_raw_elf": {"constructor": "HARDENED_ELFLDR_SPAWN", "transport": "LOOPBACK_9021", "app_registration_found": false}, + "direct_and_manager_same_elfldr_spawn_path": true, + "controlled_route_creates_distinct_app_context": false, + "port_launcher_contract": "PARTIAL_UNBOUND", + "active_app_state_runtime": "UNPROVEN" + }, + "packaging_and_ports": { + "pacbrew_is_launcher": false, + "pacbrew_classification": "PACKAGE_METADATA_ONLY", + "homebrew_js_is_app_registration": false, + "lakesnes_links_sdl2main": true, + "lakesnes_launcher_implementation_bound": false, + "official_port_runtime_success_used_as_hardware_evidence": false + }, + "runtime_observation": { + "lnc_log_classification": "OBSERVED_NONUNIQUE_CORRELATION", + "lnc_exact_caller_observed": false, + "lnc_log_accepted_as_root_cause": false, + "first_submit_failure_remains": true, + "visible_output_proven": false, + "cleanup_proven": false + }, + "decision": { + "root_cause_resolved": false, + "sdl2main_change_allowed": false, + "lnc_or_system_service_change_allowed": false, + "submit_parameter_change_allowed": false, + "new_videoout_call_allowed": false, + "target_build_allowed": false, + "device_action_allowed": false, + "safe_next_steps": [ + "OFFLINE_EXACT_HBLDR_SHSRV_PROVENANCE_AUDIT", + "HOST_OR_SOFTWARE_ONLY_INTEGRATION" + ] + }, + "performed_actions": { + "target_source_changed": false, + "target_artifact_created": false, + "ps5_connected": false, + "device_transfer_performed": false, + "target_execution_performed": false, + "result_received_from_device": false + }, + "tests": { + "chimera_gfx_ctest": "64_OF_64_PASS", + "phase10r_guardrails": 20, + "safety_audit": "PASS", + "secret_scan": "PASS", + "network_required_by_tests": false, + "hardware_claim_from_host_test": false + } +} diff --git a/manifests/retroarch/phase-1.0s-launcher-provenance.json b/manifests/retroarch/phase-1.0s-launcher-provenance.json new file mode 100644 index 0000000..7ec07d0 --- /dev/null +++ b/manifests/retroarch/phase-1.0s-launcher-provenance.json @@ -0,0 +1,162 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0S_LAUNCHER_PROVENANCE", + "status": "BIGAPP_CONTEXT_SOURCE_PROVEN_DEPLOYED_IDENTITY_UNPROVEN_DEVICE_PATH_BLOCKED", + "date": "2026-07-22", + "scope": "OFFICIAL_SOURCE_ONLY_STATIC_AUDIT", + "start_commit": "189a4afdf4b6bb6d76b8a5fa7b5ce79cd4f82243", + "authorizations": { + "target_build_authorized": false, + "ps5_connection_authorized": false, + "device_request_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "result_receive_authorized": false, + "installation_authorized": false, + "autoload_authorized": false, + "device_write_authorized": false, + "app_termination_authorized": false, + "system_remount_authorized": false, + "automatic_retry": false + }, + "source_acquisition": { + "official_repository": "https://github.com/ps5-payload-dev/shsrv.git", + "official_github_only": true, + "arbitrary_mirrors_used": false, + "dependencies_installed": false, + "downloaded_code_built": false, + "downloaded_code_executed": false, + "target_artifact_downloaded": false, + "ps5_address_used": false, + "ps5_connected": false + }, + "official_current": { + "tag": "v0.19", + "commit": "6f320637d56d344a0e7797753099e33238bbf146", + "tree": "c26ce02b6c3ca4202993e039b3db7c28c353dee4", + "latest_release_during_audit": true, + "worktree_clean": true, + "files": { + "README.md": {"size": 3546, "sha256": "4855fa2adbe1fc0c7aa0174aa3275742d9b9aba3d818ae09cf2722a89b06cd34"}, + "shsrv.c": {"size": 5293, "sha256": "6ec71b4eb6c2bc1159f21568c1c9834f8aecac8d8881113bf09cf8981be19ee3"}, + "sh.c": {"size": 13278, "sha256": "3c4b7f76efdd157436ed4b353ee1b550bf3ff9df17c147b4762b767982fc8253"}, + "elfldr.c": {"size": 17911, "sha256": "4aa31f5a942681f8d88281b9713f087ced8963de842438cacad1088933f8e785"}, + "bundles/hbldr/main.c": {"size": 1520, "sha256": "6fa519888f79fe0458a983a517073c1eccc61239edfbe40d607f69b6b38a8af4"}, + "bundles/hbldr/hbldr.c": {"size": 13438, "sha256": "0096f86a00fdedcbc7509db47eb0f54dbc3e487d37e699f3de79048bac001be9"} + } + }, + "historical_reference": { + "reason": "LAKESNES_REQUIRES_SHSRV_V0_7_OR_LATER", + "tag": "v0.7", + "commit": "74287f5db6b20320efd7892d7b29cf438fe7cb98", + "tree": "7184968c702afe038551bf3228cc25f455388bb6", + "worktree_clean": true, + "bigapp_title": "PPSA01659", + "bigapp_kind": "VIDEOPLAYER_WEBAPP", + "fakeapp_persistent_creation_present": false, + "files": { + "README.md": {"size": 3248, "sha256": "3d16e46416dd07940fcde1190a6ab558348bc80263da615331dda0f3eb6183ed"}, + "shsrv.c": {"size": 4716, "sha256": "66197d08308180fe923aa8aedc91bd5025f08938156ae3092ea2c69d0ad0be57"}, + "sh.c": {"size": 8449, "sha256": "bf97bc6dd3f49345ad8da9a29b28a5d6bcde5e53a6f32c60a538d6e187c7e05a"}, + "elfldr.c": {"size": 17920, "sha256": "1ff6cfa1300a95e8be48e5f7adc1413e3384ba04ea266c167fcd441ef20197d1"}, + "bundles/hbldr/main.c": {"size": 7134, "sha256": "2081ece2f7d0a7e9b392660696c2a44802f64680365607e742b37dcf3223a55a"} + } + }, + "lineage": { + "hbldr_introduction_commit": "4a70b50eecab853408f0a93a579b90428369fa09", + "fake_game_transition_commit": "18e9a62aea801b5a1be4a692dba76bf5381e9fcb", + "first_fake_game_release": "v0.8", + "pacbrew_commit": "c2abcfcb60f569128abd0e8e70ad03a67bee5ea7", + "pacbrew_source_checksum_policy": "SKIP", + "pacbrew_proves_deployed_identity": false + }, + "deployed_identity": { + "local_shsrv_binary_found": false, + "device_version_observed": false, + "device_hash_observed": false, + "package_receipt_found": false, + "classification": "UNPROVEN" + }, + "launch_callgraph": [ + "HOST_TELNET_TO_PORT_2323", + "SHSRV_ACCEPT", + "ELFLDR_SPAWN_EMBEDDED_SHELL", + "SHELL_BUILTIN_HBLDR", + "ELFLDR_SPAWN_EMBEDDED_HBLDR", + "READ_TARGET_ELF_FROM_DEVICE_PATH", + "PREPARE_OR_SELECT_BIGAPP", + "KILL_RUNNING_BIGAPP_IF_PRESENT", + "SYSTEM_SERVICE_LAUNCH_BIGAPP", + "FOLLOW_FORK_AND_EXEC", + "SET_TARGET_ROOT_AND_JAIL", + "REPLACE_BIGAPP_PROCESS_WITH_ELF", + "DETACH_TARGET" + ], + "launch_context": { + "raw_elfldr_process_basis": "SCE_SP_ZERO_CONF", + "hbldr_process_basis": "SYSTEM_SERVICE_BIGAPP", + "different_from_raw_elfldr": true, + "bigapp_launch_source_proven": true, + "foreground_user_context_source_proven": true, + "process_image_replacement_source_proven": true, + "videoout_permission_proven": false, + "firmware_9_60_runtime_proven": false, + "root_cause_classification": "STRONG_SOURCE_CANDIDATE_NOT_PROVEN_ROOT_CAUSE" + }, + "effects": { + "target_elf_must_exist_on_device": true, + "direct_host_to_memory_target_input": false, + "running_bigapp_may_be_killed": true, + "kernel_or_ptrace_runtime_writes": true, + "v019_system_ex_remount_possible": true, + "v019_persistent_fakeapp_creation_possible": true, + "autoload_change_found": false, + "hard_deadline_present": false, + "automatic_retry_present": false, + "atomic_persistent_write_protocol_present": false, + "rollback_protocol_present": false, + "power_loss_safe": false + }, + "robustness_findings": [ + "UNBOUNDED_WHOLE_ELF_ALLOCATION", + "UNBOUNDED_PROCESS_WAITS", + "UNAUTHENTICATED_ALL_INTERFACE_LISTENER", + "SPLITSTRING_POINTER_ALLOCATION_UNDERSIZED", + "UNBOUNDED_PATH_COPY_AND_FORMAT", + "NO_TARGET_HASH_OR_SIZE_POLICY", + "INCOMPLETE_GLOBAL_CLEANUP_PROOF" + ], + "decision": { + "root_cause_resolved": false, + "existing_hbldr_route_safe_for_device_test": false, + "existing_hbldr_route_reuse_allowed": false, + "launcher_code_copy_allowed": false, + "target_source_change_allowed": false, + "target_build_allowed": false, + "device_fact_collection_allowed": false, + "device_action_allowed": false, + "safe_next_steps": [ + "OFFLINE_DEPLOYED_SHSRV_IDENTITY_COLLECTION_DESIGN", + "HOST_OR_SOFTWARE_ONLY_INTEGRATION" + ] + }, + "performed_actions": { + "target_source_changed": false, + "target_artifact_created": false, + "ps5_connected": false, + "device_request_performed": false, + "device_transfer_performed": false, + "target_execution_performed": false, + "device_file_created": false, + "device_app_terminated": false, + "device_remounted": false + }, + "tests": { + "chimera_gfx_ctest": "66_OF_66_PASS", + "phase10s_guardrails": 20, + "safety_audit": "PASS", + "secret_scan": "PASS", + "network_required_by_tests": false, + "hardware_claim_from_host_test": false + } +} diff --git a/manifests/retroarch/phase-1.0t-shsrv-identity-gate.json b/manifests/retroarch/phase-1.0t-shsrv-identity-gate.json new file mode 100644 index 0000000..3d2a7ad --- /dev/null +++ b/manifests/retroarch/phase-1.0t-shsrv-identity-gate.json @@ -0,0 +1,132 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0T_INACTIVE_SHSRV_IDENTITY_GATE", + "status": "INACTIVE_METADATA_GATE_DESIGNED_EXACT_IDENTITY_UNAVAILABLE", + "date": "2026-07-22", + "scope": "OFFLINE_DESIGN_AND_SYNTHETIC_TRANSCRIPT_TESTS", + "start_commit": "4c1944ef1412ed8a0bb18ae534244cde92db00ba", + "authorizations": { + "target_build_authorized": false, + "ps5_connection_authorized": false, + "device_request_authorized": false, + "result_receive_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "installation_authorized": false, + "autoload_authorized": false, + "device_write_authorized": false, + "app_termination_authorized": false, + "system_remount_authorized": false, + "automatic_retry": false, + "reconnect_authorized": false + }, + "activation": { + "active": false, + "target_address": null, + "listener_already_running_attested": false, + "window": null, + "exact_literal_path": null, + "commands": [], + "expires_at": null + }, + "source_identities": { + "official_current_tag": "v0.19", + "official_current_commit": "6f320637d56d344a0e7797753099e33238bbf146", + "historical_tag": "v0.7", + "historical_commit": "74287f5db6b20320efd7892d7b29cf438fe7cb98" + }, + "mandatory_connection_effects": { + "accepted_socket_spawns_shell": true, + "new_session_created": true, + "pipes_created": true, + "thread_created": true, + "heap_allocations": true, + "process_environment_changed": true, + "model_queried": true, + "serial_queried_and_transmitted": true, + "firmware_queried": true, + "temperature_queried_and_transmitted": true, + "cpu_frequency_queried_and_transmitted": true, + "filesystem_write_found": false, + "autoload_change_found": false, + "target_payload_launched": false + }, + "identity_capabilities": { + "greeting_compile_date_time": "METADATA_ONLY", + "help_command_fingerprint": "SOURCE_FAMILY_FINGERPRINT_ONLY", + "stat": "FILE_METADATA_ONLY", + "sum": "BSD_ROTATE_16_WEAK_CHECKSUM_ONLY", + "sha256_command_available": false, + "binary_safe_file_read_available": false, + "exact_deployed_identity_possible": false, + "strongest_possible_classification": "WEAK_FILE_CORRELATION_ONLY" + }, + "command_fingerprints": { + "v0_7": {"command_count": 44, "sha256": "40313637116b532f3c7f9bebe2c23c0018fe7d4093840cf463a22ba0314ca021", "proves_exact_binary": false}, + "v0_19": {"command_count": 50, "sha256": "f41168292e205590bda1d243cdf727044e0af280a89fb0c070f4c5d6c92f2fd7", "proves_exact_binary": false} + }, + "command_policy": { + "initial_candidate": ["help"], + "exact_path_candidates": ["stat", "sum"], + "fakeapp_metadata_candidate": ["stat"], + "excluded_read_commands": ["cat", "env", "file", "find", "grep", "hexdump", "ls", "procstat", "ps", "sysctl"], + "forbidden_mutating_or_launch_commands": ["authid", "browse", "cd", "chgrp", "chmod", "chown", "chroot", "cp", "exec", "export", "hbdbg", "hbldr", "http2_get", "kill", "launch", "ln", "mkdir", "mknod", "mount", "mv", "notify", "pkg_install", "reptyr", "rm", "rmdir", "sfocreate", "suspend", "sync", "touch", "umount"], + "wildcards_allowed": false, + "relative_paths_allowed": false, + "pipes_allowed": false, + "redirection_allowed": false, + "multiple_commands_per_line_allowed": false, + "path_guessing_allowed": false + }, + "sanitization": { + "raw_transcript_persistence_allowed": false, + "serial_value_retained": false, + "model_value_retained": false, + "temperature_values_retained": false, + "cpu_frequency_value_retained": false, + "unknown_paths_retained": false, + "compile_metadata_retained": true, + "firmware_retained": true, + "approved_path_metadata_retained": true + }, + "future_windows": { + "T1_MANUAL_HOST_FACTS": "DESIGNED_NOT_ACTIVE", + "T2_GREETING_AND_HELP": "DESIGNED_NOT_ACTIVE", + "T3_ONE_EXACT_PATH": "DESIGNED_NOT_ACTIVE", + "T4_FAKEAPP_METADATA": "DESIGNED_NOT_ACTIVE" + }, + "decision": { + "exact_deployed_identity_obtainable_from_existing_shsrv": false, + "metadata_collection_design_complete": true, + "network_client_created": false, + "live_collection_allowed": false, + "target_change_allowed": false, + "device_action_allowed": false, + "safe_next_steps": [ + "OFFLINE_INACTIVE_ONE_SHOT_SANITIZING_CLIENT_DESIGN", + "MANUAL_HOST_ARTIFACT_INVENTORY", + "HOST_OR_SOFTWARE_ONLY_INTEGRATION" + ] + }, + "performed_actions": { + "network_client_created": false, + "network_socket_opened": false, + "ps5_address_used": false, + "ps5_connected": false, + "device_request_performed": false, + "result_received_from_device": false, + "target_source_changed": false, + "target_artifact_created": false, + "device_file_created": false, + "target_execution_performed": false + }, + "tests": { + "chimera_gfx_ctest": "69_OF_69_PASS", + "phase10t_guardrails": 20, + "phase10t_transcript_tests": 12, + "safety_audit": "PASS", + "secret_scan": "PASS", + "network_required_by_tests": false, + "hardware_claim_from_host_test": false + } +} diff --git a/manifests/retroarch/phase-1.0u-local-shsrv-inventory.json b/manifests/retroarch/phase-1.0u-local-shsrv-inventory.json new file mode 100644 index 0000000..febed3b --- /dev/null +++ b/manifests/retroarch/phase-1.0u-local-shsrv-inventory.json @@ -0,0 +1,119 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0U_BOUNDED_LOCAL_SHSRV_INVENTORY", + "status": "BOUNDED_LOCAL_INVENTORY_COMPLETE_NO_DEPLOYED_CANDIDATE_FOUND", + "date": "2026-07-22", + "start_commit": "f5b0ff720dabf3ab745ad05aea4ea8edc9666ac7", + "scope": { + "roots": [ + "CHIMERA_GFX_REPOSITORY", + "KNOWN_CHIMERA_SIBLING_REPOSITORIES", + "SUPPLIED_CODEX_ATTACHMENTS", + "EVIDENCED_USER_DOWNLOAD_DIRECTORY" + ], + "full_computer_scan_performed": false, + "browser_database_scan_performed": false, + "network_share_scan_performed": false, + "internet_access_performed": false, + "ps5_access_performed": false + }, + "methods": { + "case_insensitive_filename_search": true, + "zip_entry_name_inventory": true, + "zip_extraction_performed": false, + "non_zip_unrelated_archives_inspected": false, + "downloaded_or_local_code_executed": false, + "host_sender_executed": false, + "target_artifact_executed": false, + "files_modified_by_inventory": false + }, + "reference_objects": [ + { + "logical_name": "OFFICIAL_SHSRV_CURRENT_SOURCE", + "relative_path": "work/upstream/shsrv", + "commit": "6f320637d56d344a0e7797753099e33238bbf146", + "tree": "c26ce02b6c3ca4202993e039b3db7c28c353dee4", + "classification": "REFERENCE_SOURCE_ONLY", + "deployed_identity": false + }, + { + "logical_name": "OFFICIAL_SHSRV_V07_SOURCE", + "relative_path": "work/upstream/shsrv-v0.7", + "commit": "74287f5db6b20320efd7892d7b29cf438fe7cb98", + "tree": "7184968c702afe038551bf3228cc25f455388bb6", + "classification": "HISTORICAL_REFERENCE_SOURCE_ONLY", + "deployed_identity": false + }, + { + "logical_name": "OFFICIAL_HOST_TELNET_WRAPPER", + "relative_path": "work/upstream/shsrv/host/prospero-shsrv-shell", + "size": 1026, + "sha256": "34283c1d27a8b723ba529dfd5ed79ff9a1f967ab2ad2b97ed2b8889351f0973a", + "classification": "HOST_WRAPPER_NOT_TARGET_BINARY", + "executed": false, + "deployed_identity": false + }, + { + "logical_name": "PACBREW_SHSRV_RECIPE", + "relative_path": "work/upstream/pacbrew-repo/shsrv/PKGBUILD", + "size": 1051, + "sha256": "3f134e9986fb8352136150216006d68fb92052ec55d5698d36825a97c37c3b30", + "classification": "UNPINNED_RECIPE_NOT_PACKAGE_RECEIPT", + "executed": false, + "deployed_identity": false + } + ], + "results": { + "local_shsrv_target_candidate_found": false, + "local_shsrv_package_found": false, + "local_shsrv_package_receipt_found": false, + "local_shsrv_transfer_log_found": false, + "zip_entry_name_match_found": false, + "operator_supplied_original_binary_found": false, + "exact_device_path_found": false, + "exact_device_hash_found": false, + "global_absence_proven": false, + "classification": "NO_CANDIDATE_IN_SCANNED_SCOPE" + }, + "authorizations": { + "target_build_authorized": false, + "ps5_connection_authorized": false, + "device_request_authorized": false, + "result_receive_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "installation_authorized": false, + "autoload_authorized": false, + "device_write_authorized": false, + "automatic_retry": false, + "reconnect_authorized": false + }, + "decision": { + "exact_deployed_shsrv_identity": "UNPROVEN", + "local_direct_hash_path_available": false, + "phase1_launch_context_experiment_allowed": false, + "phase10v_inactive_collector_design_allowed": true, + "live_collection_allowed": false, + "device_action_allowed": false, + "next_step": "OFFLINE_INACTIVE_ONE_SHOT_SANITIZING_COLLECTOR_DESIGN" + }, + "performed_actions": { + "network_client_created": false, + "network_socket_opened": false, + "ps5_address_used": false, + "ps5_connected": false, + "device_request_performed": false, + "result_received_from_device": false, + "target_source_changed": false, + "target_artifact_created": false, + "target_execution_performed": false + }, + "tests": { + "chimera_gfx_ctest": "71_OF_71_PASS", + "phase10u_guardrails": 16, + "safety_audit": "PASS", + "secret_scan": "PASS", + "network_required_by_tests": false, + "hardware_claim_from_host_test": false + } +} diff --git a/manifests/retroarch/phase-1.0v-inactive-shsrv-collector.json b/manifests/retroarch/phase-1.0v-inactive-shsrv-collector.json new file mode 100644 index 0000000..4288481 --- /dev/null +++ b/manifests/retroarch/phase-1.0v-inactive-shsrv-collector.json @@ -0,0 +1,117 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0V_INACTIVE_ONE_SHOT_SHSRV_COLLECTOR", + "status": "INACTIVE_OFFLINE_COLLECTOR_MODEL_COMPLETE_NO_LIVE_CLIENT", + "date": "2026-07-22", + "start_commit": "50e042a96cd75925aa39a579c6242f3613caca03", + "activation": { + "active": false, + "target_address": null, + "target_port": null, + "window": null, + "exact_literal_path": null, + "commands": [], + "deadline_seconds": null, + "run_id": null, + "expires_at": null + }, + "model": { + "relative_path": "tools/phase10v_shsrv_collector_model.py", + "size": 7429, + "sha256": "f8a306dafee5d135919bec5afda789dd741e57f39803b7683fb8747c186db25c", + "phase10t_parser_sha256": "4701a057a98b4874e49e1bcf11db9a9a3a105e48f2c25e42796bff10f238f7c2", + "offline_only": true, + "stdin_only": true, + "network_transport_present": false, + "socket_import_present": false, + "address_argument_present": false, + "port_argument_present": false, + "command_argument_present": false, + "command_send_present": false, + "filesystem_output_present": false + }, + "bounds": { + "max_raw_bytes": 65536, + "max_sanitized_bytes": 65536, + "max_chunks": 256, + "stdin_chunk_bytes": 4096, + "strict_utf8": true, + "one_shot": true, + "automatic_retry": false, + "reconnect": false, + "resume": false, + "fallback": false + }, + "telnet_model": { + "incremental": true, + "fragmented_negotiation_supported": true, + "subnegotiation_removed": true, + "incomplete_control_rejected": true, + "negotiation_replies_sent": false, + "live_prompt_contract_proven": false + }, + "sanitization": { + "raw_transcript_output": false, + "serial_value_output": false, + "model_value_output": false, + "temperature_value_output": false, + "cpu_frequency_value_output": false, + "unknown_path_output": false, + "approved_literal_path_metadata_output": true, + "exact_identity_output": false, + "logical_buffer_discard": true, + "physical_memory_erasure_proven": false + }, + "review_remediation": { + "telnet_doubled_iac_state_fixed": true, + "empty_chunks_ignored": true, + "expected_paths_absolute_normalized_and_character_allowlisted": true, + "firmware_metadata_gated_to_exact_9_60": true, + "compile_metadata_format_validated": true, + "parser_numeric_failure_normalized": true, + "physical_memory_erasure_proven": false + }, + "authorizations": { + "target_build_authorized": false, + "ps5_connection_authorized": false, + "device_request_authorized": false, + "result_receive_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "installation_authorized": false, + "autoload_authorized": false, + "device_write_authorized": false, + "automatic_retry": false, + "reconnect_authorized": false + }, + "decision": { + "offline_collector_model_complete": true, + "exact_deployed_shsrv_identity": "UNPROVEN", + "live_network_client_created": false, + "live_client_implementation_allowed": false, + "live_collection_allowed": false, + "launch_context_experiment_allowed": false, + "device_action_allowed": false, + "next_step": "HUMAN_REVIEW_OF_OFFLINE_COLLECTOR_MODEL" + }, + "performed_actions": { + "network_client_created": false, + "network_socket_opened": false, + "ps5_address_used": false, + "ps5_connected": false, + "device_request_performed": false, + "result_received_from_device": false, + "target_source_changed": false, + "target_artifact_created": false, + "target_execution_performed": false + }, + "tests": { + "chimera_gfx_ctest": "77_OF_77_PASS", + "phase10v_guardrails": 18, + "phase10v_model_tests": 21, + "safety_audit": "PASS", + "secret_scan": "PASS", + "network_required_by_tests": false, + "hardware_claim_from_host_test": false + } +} diff --git a/manifests/retroarch/phase-1.0w-inactive-client-architecture.json b/manifests/retroarch/phase-1.0w-inactive-client-architecture.json new file mode 100644 index 0000000..344e078 --- /dev/null +++ b/manifests/retroarch/phase-1.0w-inactive-client-architecture.json @@ -0,0 +1,124 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0W_INACTIVE_SHSRV_CLIENT_ARCHITECTURE", + "status": "INACTIVE_CLIENT_ARCHITECTURE_COMPLETE_NETWORK_IMPLEMENTATION_BLOCKED", + "date": "2026-07-22", + "start_commit": "665d02995fdb72080a20cd53c92265b1e24afb1c", + "activation": { + "active": false, + "policy_sha256": null, + "collector_sha256": null, + "run_id": null, + "target_address": null, + "target_port": null, + "window": null, + "exact_literal_path": null, + "commands": [], + "deadline_seconds": null, + "expires_at": null + }, + "source_bindings": { + "phase10t_parser_sha256": "4701a057a98b4874e49e1bcf11db9a9a3a105e48f2c25e42796bff10f238f7c2", + "remediated_phase10v_collector_size": 7429, + "remediated_phase10v_collector_sha256": "f8a306dafee5d135919bec5afda789dd741e57f39803b7683fb8747c186db25c", + "phase10w_policy_size": 6997, + "phase10w_policy_sha256": "747d23c88f2722e8e8846599c3ac1dae3826eb3fca881caaad36b251f30f3592", + "fake_transport_size": 1870, + "fake_transport_sha256": "e2f074e844102177f45fb0f97254108ee641db00931bb037574dee82111f316f" + }, + "architecture": { + "policy_returns_data_only": true, + "policy_cli_present": false, + "network_transport_present": false, + "socket_import_present": false, + "dns_lookup_present": false, + "connect_present": false, + "command_formatter_present": false, + "command_sender_present": false, + "receive_present": false, + "filesystem_output_present": false, + "fake_transport_only": true + }, + "future_contract": { + "source_bound_port": 2323, + "maximum_connections": 1, + "maximum_deadline_seconds": 10, + "maximum_approval_lifetime_minutes": 15, + "allowed_windows": { + "T2_GREETING_AND_HELP": ["help"], + "T3_ONE_EXACT_PATH": ["stat", "sum"] + }, + "dual_record_exact_match_required": true, + "exact_record_shape_required": true, + "policy_hash_dual_match_required": true, + "operator_attestation_required": true, + "listener_attestation_required": true, + "spawned_shell_effects_acceptance_required": true, + "serial_query_acceptance_required": true, + "telemetry_query_acceptance_required": true, + "unproven_memory_erasure_acceptance_required": true, + "automatic_retry": false, + "reconnect": false, + "resume": false, + "fallback": false + }, + "missing_live_components": { + "consumed_attempt_receipt": true, + "exclusive_sanitized_output": true, + "monotonic_deadline": true, + "host_clock_acquisition": true, + "network_transport": true, + "telnet_reply_contract": true, + "prompt_completion_contract": true, + "bounded_receive_integration": true, + "deterministic_close": true, + "error_path_redaction_review": true + }, + "authorizations": { + "target_build_authorized": false, + "ps5_connection_authorized": false, + "device_request_authorized": false, + "result_receive_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "installation_authorized": false, + "autoload_authorized": false, + "device_write_authorized": false, + "automatic_retry": false, + "reconnect_authorized": false + }, + "decision": { + "self_review_complete": true, + "review_findings_remediated": true, + "offline_client_architecture_complete": true, + "exact_deployed_shsrv_identity": "UNPROVEN", + "live_network_client_created": false, + "live_client_implementation_allowed": false, + "phase10x_offline_inactive_transport_implementation_allowed": true, + "live_collection_allowed": false, + "launch_context_experiment_allowed": false, + "device_action_allowed": false, + "next_step": "OFFLINE_INACTIVE_FAKE_SOCKET_TRANSPORT_IMPLEMENTATION" + }, + "performed_actions": { + "network_client_created": false, + "network_socket_opened": false, + "ps5_address_used": false, + "ps5_connected": false, + "device_request_performed": false, + "result_received_from_device": false, + "target_source_changed": false, + "target_artifact_created": false, + "target_execution_performed": false + }, + "tests": { + "chimera_gfx_ctest": "77_OF_77_PASS", + "phase10w_guardrails": 18, + "phase10w_policy_tests": 25, + "remediated_phase10v_model_tests": 21, + "safety_audit": "PASS", + "secret_scan": "PASS", + "network_required_by_tests": false, + "hardware_claim_from_host_test": false + } +} diff --git a/manifests/retroarch/phase-1.0x-inactive-transport.json b/manifests/retroarch/phase-1.0x-inactive-transport.json new file mode 100644 index 0000000..a4a4920 --- /dev/null +++ b/manifests/retroarch/phase-1.0x-inactive-transport.json @@ -0,0 +1,145 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0X_INACTIVE_INJECTED_TRANSPORT", + "status": "INACTIVE_INJECTED_TRANSPORT_COMPLETE_LIVE_PROTOCOL_BLOCKED", + "date": "2026-07-22", + "start_commit": "45228225e3a0e8811b4a5bcaa79e6f125db3a7f8", + "activation": { + "active": false, + "policy_sha256": null, + "collector_sha256": null, + "transport_sha256": null, + "run_id": null, + "target_address": null, + "target_port": null, + "window": null, + "exact_literal_path": null, + "commands": [], + "deadline_seconds": null, + "expires_at": null + }, + "source_bindings": { + "shsrv_reference_commit": "6f320637d56d344a0e7797753099e33238bbf146", + "phase10t_parser_sha256": "4701a057a98b4874e49e1bcf11db9a9a3a105e48f2c25e42796bff10f238f7c2", + "phase10v_collector_size": 7429, + "phase10v_collector_sha256": "f8a306dafee5d135919bec5afda789dd741e57f39803b7683fb8747c186db25c", + "phase10w_policy_size": 6997, + "phase10w_policy_sha256": "747d23c88f2722e8e8846599c3ac1dae3826eb3fca881caaad36b251f30f3592", + "phase10x_transport_size": 8040, + "phase10x_transport_sha256": "568d7578482ecf2fcd9e29085b2eb9d8705fc699611508acdf22afd30f2ddd23", + "phase10x_transport_tests_size": 13087, + "phase10x_transport_tests_sha256": "19709ed6ab456be428d262b3f0afb4f6f577b34e4db80e66a73ed63d9bf2cd43" + }, + "source_protocol_evidence": { + "prompt_function": "sh_prompt", + "session_function": "sh_thread", + "prompt_uses_dynamic_pwd": true, + "pwd_set_without_overwrite": true, + "prompt_flushes_stdout": true, + "prompt_before_input": true, + "prompt_after_command_path": true, + "automatic_greeting_sensitive_queries": true, + "exact_deployed_bytes_proven": false, + "exact_prompt_framing_proven": false, + "exact_telnet_reply_contract_proven": false + }, + "architecture": { + "injected_adapter_only": true, + "injected_monotonic_clock_only": true, + "policy_cli_present": false, + "live_cli_present": false, + "network_transport_present": false, + "socket_import_present": false, + "dns_lookup_present": false, + "connect_present": false, + "target_address_present": false, + "command_byte_formatter_present": false, + "telnet_reply_generator_present": false, + "live_prompt_detector_present": false, + "fake_boundary_labels_only": true + }, + "local_evidence": { + "receipt_before_adapter_open": true, + "exclusive_leaf_create": true, + "file_fsync": true, + "close_and_reopen": true, + "reopened_bytes_verified": true, + "sanitized_output_receipt_hash_binding": true, + "overwrite_supported": false, + "delete_or_cleanup_supported": false, + "raw_transcript_persisted": false, + "target_address_persisted": false, + "partial_file_cleanup": false, + "partial_file_is_valid_evidence": false, + "directory_fsync": false, + "directory_entry_durability": "UNPROVEN", + "trusted_caller_owned_root_required": true + }, + "deadline_and_cleanup": { + "one_absolute_monotonic_deadline": true, + "pre_and_post_boundary_checks": true, + "remaining_budget_passed_to_adapter": true, + "adapter_close_after_open_attempt": true, + "retry_loop_present": false, + "second_open_present": false, + "blocking_adapter_call_preemption": false, + "real_socket_timeout_present": false, + "live_cleanup_proven": false + }, + "missing_live_components": { + "exact_deployed_shsrv_identity": true, + "exact_prompt_completion_framing": true, + "exact_telnet_reply_contract": true, + "command_byte_formatter": true, + "network_adapter": true, + "os_level_blocking_call_timeout": true, + "live_socket_cleanup_evidence": true, + "directory_entry_durability_contract": true + }, + "authorizations": { + "target_build_authorized": false, + "ps5_connection_authorized": false, + "device_request_authorized": false, + "result_receive_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "installation_authorized": false, + "autoload_authorized": false, + "device_write_authorized": false, + "automatic_retry": false, + "reconnect_authorized": false + }, + "decision": { + "offline_injected_transport_complete": true, + "offline_local_evidence_complete_with_limitations": true, + "exact_deployed_shsrv_identity": "UNPROVEN", + "live_protocol_framing": "BLOCKED_UNPROVEN", + "live_network_client_created": false, + "live_client_implementation_allowed": false, + "live_collection_allowed": false, + "device_action_allowed": false, + "next_step": "OFFLINE_EXACT_PROMPT_AND_TELNET_FRAMING_AUDIT" + }, + "performed_actions": { + "network_client_created": false, + "network_socket_opened": false, + "ps5_address_used": false, + "ps5_connected": false, + "device_request_performed": false, + "result_received_from_device": false, + "target_source_changed": false, + "target_artifact_created": false, + "target_execution_performed": false + }, + "tests": { + "chimera_gfx_ctest": "80_OF_80_PASS", + "phase10x_guardrails": 18, + "phase10x_transport_tests": 18, + "phase10w_policy_tests": 25, + "phase10v_model_tests": 21, + "safety_audit": "PASS", + "secret_scan": "PASS", + "network_required_by_tests": false, + "hardware_claim_from_host_test": false + } +} diff --git a/manifests/retroarch/phase-1.0y-shsrv-framing.json b/manifests/retroarch/phase-1.0y-shsrv-framing.json new file mode 100644 index 0000000..4fbf1a3 --- /dev/null +++ b/manifests/retroarch/phase-1.0y-shsrv-framing.json @@ -0,0 +1,145 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0Y_OFFLINE_SHSRV_FRAMING_AUDIT", + "status": "SOURCE_FAMILY_FRAMING_PARTIAL_PROMPT_COMPLETION_UNPROVEN", + "date": "2026-07-22", + "start_commit": "65c274f5ee723680ca6a576b74aa8b3c280978cd", + "activation": { + "active": false, + "framing_model_sha256": null, + "source_family": null, + "run_id": null, + "target_address": null, + "target_port": null, + "commands": [] + }, + "source_bindings": { + "shsrv_v07_commit": "74287f5db6b20320efd7892d7b29cf438fe7cb98", + "shsrv_v08_commit": "8f76139ee69df4b8cb7c3aee401f05bccb2c2a31", + "shsrv_v09_commit": "2f2bc5501d40064c18c06f06f7b1f4cab756389b", + "shsrv_v019_current_commit": "6f320637d56d344a0e7797753099e33238bbf146", + "current_sh_c_size": 13278, + "current_sh_c_sha256": "3c4b7f76efdd157436ed4b353ee1b550bf3ff9df17c147b4762b767982fc8253", + "current_shsrv_c_size": 5293, + "current_shsrv_c_sha256": "6ec71b4eb6c2bc1159f21568c1c9834f8aecac8d8881113bf09cf8981be19ee3", + "current_libtelnet_c_size": 45904, + "current_libtelnet_c_sha256": "64d0b8dc96b128aed30945393d112c2deba000fd00741a9b24b24ae5c596edeb", + "current_libtelnet_h_size": 22302, + "current_libtelnet_h_sha256": "8d3977ff3993480d18bee8ff91fc14e654b7fb0fdb6581cea91cdfb4120ecca8", + "legacy_v07_sh_c_size": 8449, + "legacy_v07_sh_c_sha256": "bf97bc6dd3f49345ad8da9a29b28a5d6bcde5e53a6f32c60a538d6e187c7e05a", + "phase10x_transport_sha256": "568d7578482ecf2fcd9e29085b2eb9d8705fc699611508acdf22afd30f2ddd23", + "phase10y_model_size": 6678, + "phase10y_model_sha256": "5081898ec86be52900670be2f9949a20b9abb7781a6b04d5337178a8340775d4", + "phase10y_model_tests_size": 6485, + "phase10y_model_tests_sha256": "802742450d65b237c0865e5820a8523131391988ca9eadd206766fb51693ca95" + }, + "source_families": { + "LEGACY_RAW_V07_V08": { + "versions": ["v0.7", "v0.8"], + "incoming_telnet_parser": false, + "outgoing_nvt_translation": false, + "telnet_controls_pass_to_shell": true, + "server_side_echo": false, + "proactive_negotiation": false + }, + "LIBTELNET_NVT_V09_V019": { + "versions": [ + "v0.9", "v0.10", "v0.10.1", "v0.11", "v0.12", "v0.13", + "v0.13.1", "v0.14", "v0.15", "v0.16", "v0.16.1", + "v0.16.2", "v0.17", "v0.18", "v0.18.1", "v0.18.2", "v0.19" + ], + "incoming_telnet_parser": true, + "outgoing_nvt_translation": true, + "empty_option_table": true, + "will_reply": "IAC_DONT", + "do_reply": "IAC_WONT", + "initial_wont_dont_reply": "NONE", + "server_side_echo": false, + "proactive_negotiation": false + } + }, + "prompt_and_completion": { + "prompt_source_shape": "PWD_PLUS_DOLLAR_SPACE", + "prompt_has_newline": false, + "prompt_explicitly_flushed": true, + "prompt_precedes_each_readline": true, + "prompt_follows_synchronous_help": true, + "prompt_follows_waited_stat_sum": true, + "pwd_forced_overwrite": false, + "wire_chunk_boundaries_defined": false, + "short_write_completion_loop": false, + "external_client_local_echo_defined": false, + "terminal_shape_candidate_available": true, + "exact_live_completion_proven": false + }, + "offline_model": { + "two_source_families": true, + "maximum_bytes": 65536, + "maximum_chunks": 256, + "network_import_present": false, + "cli_present": false, + "command_formatter_present": false, + "target_present": false, + "file_output_present": false, + "synthetic_negotiation_replies_only": true, + "prompt_candidate_never_exact": true, + "device_behavior_proven": false + }, + "risks": { + "automatic_sensitive_greeting": "HIGH_FUNCTIONAL_ACCEPTANCE_REQUIRED", + "single_write_short_write_handling": "PROTOCOL_INTEGRITY_RISK", + "server_deadline_absent": "SESSION_HANG_RISK", + "iac_signal_commands": "HIGH_FUNCTIONAL_EXCLUDED_INPUT", + "legacy_telnet_controls_to_shell": "PROTOCOL_INTEGRITY_RISK", + "deployed_source_family": "UNPROVEN", + "firmware_9_60_runtime": "UNPROVEN" + }, + "authorizations": { + "target_build_authorized": false, + "ps5_connection_authorized": false, + "device_request_authorized": false, + "result_receive_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "installation_authorized": false, + "autoload_authorized": false, + "device_write_authorized": false, + "automatic_retry": false, + "reconnect_authorized": false + }, + "decision": { + "official_source_families_identified": true, + "offline_framing_model_complete": true, + "exact_deployed_shsrv_identity": "UNPROVEN", + "exact_live_prompt_completion": "UNPROVEN", + "passive_no_negotiation_batch": "OFFLINE_DESIGN_CANDIDATE", + "live_network_client_created": false, + "live_client_implementation_allowed": false, + "phase10z_offline_passive_batch_contract_allowed": true, + "live_collection_allowed": false, + "device_action_allowed": false, + "next_step": "OFFLINE_PASSIVE_SOURCE_FAMILY_TOLERANT_BATCH_CONTRACT" + }, + "performed_actions": { + "network_client_created": false, + "network_socket_opened": false, + "ps5_address_used": false, + "ps5_connected": false, + "device_request_performed": false, + "result_received_from_device": false, + "target_source_changed": false, + "target_artifact_created": false, + "target_execution_performed": false + }, + "tests": { + "chimera_gfx_ctest": "83_OF_83_PASS", + "phase10y_guardrails": 18, + "phase10y_framing_model_tests": 22, + "phase10x_transport_tests": 18, + "safety_audit": "PASS", + "secret_scan": "PASS", + "network_required_by_tests": false, + "hardware_claim_from_host_test": false + } +} diff --git a/manifests/retroarch/phase-1.0z-passive-batch.json b/manifests/retroarch/phase-1.0z-passive-batch.json new file mode 100644 index 0000000..f3c9aee --- /dev/null +++ b/manifests/retroarch/phase-1.0z-passive-batch.json @@ -0,0 +1,129 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0Z_OFFLINE_PASSIVE_BATCH_CONTRACT", + "status": "PASSIVE_LF_BATCH_CONTRACT_COMPLETE_LIVE_TRANSPORT_BLOCKED", + "date": "2026-07-22", + "start_commit": "0433b73958f466b50c3b3f301f29e6af4ad9d319", + "activation": { + "active": false, + "batch_contract_sha256": null, + "run_id": null, + "target_address": null, + "target_port": null, + "window": null, + "exact_literal_path": null, + "deadline_seconds": null, + "commands": [] + }, + "source_bindings": { + "phase10v_collector_size": 7429, + "phase10v_collector_sha256": "f8a306dafee5d135919bec5afda789dd741e57f39803b7683fb8747c186db25c", + "phase10w_policy_size": 6997, + "phase10w_policy_sha256": "747d23c88f2722e8e8846599c3ac1dae3826eb3fca881caaad36b251f30f3592", + "phase10x_transport_size": 8040, + "phase10x_transport_sha256": "568d7578482ecf2fcd9e29085b2eb9d8705fc699611508acdf22afd30f2ddd23", + "phase10y_framing_size": 6678, + "phase10y_framing_sha256": "5081898ec86be52900670be2f9949a20b9abb7781a6b04d5337178a8340775d4", + "phase10z_contract_size": 10487, + "phase10z_contract_sha256": "0728c2be7f368e0a7f4b68efe86f6e0c5c2f50704a41d0e1992b0bfec19dde06", + "phase10z_contract_tests_size": 9792, + "phase10z_contract_tests_sha256": "7706cc212a0fc683eb32acaef26ecaa64cbe5784ea6aeeed7e29ade11612490c" + }, + "batch_contract": { + "outbound_batches": 1, + "ascii_only": true, + "lf_only": true, + "nul_allowed": false, + "cr_allowed": false, + "iac_allowed": false, + "telnet_negotiation_emitted": false, + "server_echo_required": false, + "retry_allowed": false, + "reconnect_allowed": false, + "resume_allowed": false, + "target_retained": false, + "maximum_literal_path_bytes": 512, + "maximum_batch_bytes": 1035, + "windows": { + "T2_GREETING_AND_HELP": { + "commands": ["help"], + "exact_payload_hex": "68656c700a", + "exact_payload_bytes": 5, + "completion_requirement": "KNOWN_COMPLETE_HELP_FINGERPRINT" + }, + "T3_ONE_EXACT_PATH": { + "commands": ["stat", "sum"], + "payload_shape": "stat PATH LF sum PATH LF", + "completion_requirement": "STAT_SIZE_AND_WEAK_SUM_FOR_EXACT_PATH" + } + } + }, + "receive_contract": { + "input_source": "ALREADY_SUPPLIED_SYNTHETIC_BYTES_ONLY", + "incoming_iac": "FAIL_CLOSED", + "prompt_completion_used": false, + "remote_eof_completion_used": false, + "completion_event": "SYNTHETIC_HARD_DEADLINE_ONLY", + "partial_result": "INVALID", + "unknown_help_fingerprint": "INVALID", + "source_family_selected": false, + "network_transport_present": false, + "clock_present": false, + "cli_present": false, + "file_output_present": false, + "device_behavior_proven": false, + "exact_identity_proven": false + }, + "side_effects": { + "offline_model": "NONE", + "future_automatic_sensitive_greeting": "EXPLICIT_ACCEPTANCE_REQUIRED", + "future_shell_and_connection_state": "EXPLICIT_ACCEPTANCE_REQUIRED", + "future_stat_sum_file_read": "READ_ONLY_SOURCE_OPERATION", + "future_atime_cache_accounting_scheduler": "POSSIBLE_LOW_LEVEL_EFFECTS", + "effects_claimed_absent_on_device": false + }, + "authorizations": { + "target_build_authorized": false, + "ps5_connection_authorized": false, + "device_request_authorized": false, + "result_receive_authorized": false, + "device_transfer_authorized": false, + "device_execution_authorized": false, + "installation_authorized": false, + "autoload_authorized": false, + "device_write_authorized": false, + "automatic_retry": false, + "reconnect_authorized": false, + "resume_authorized": false + }, + "decision": { + "offline_passive_batch_contract_complete": true, + "exact_deployed_shsrv_identity": "UNPROVEN", + "live_hard_deadline_preemption": "UNPROVEN", + "live_transport_created": false, + "live_collection_allowed": false, + "device_action_allowed": false, + "phase10aa_offline_fake_adapter_integration_allowed": true, + "next_step": "OFFLINE_FAKE_ADAPTER_BATCH_AND_DEADLINE_INTEGRATION" + }, + "performed_actions": { + "network_client_created": false, + "network_socket_opened": false, + "ps5_address_used": false, + "ps5_connected": false, + "device_request_performed": false, + "result_received_from_device": false, + "target_source_changed": false, + "target_artifact_created": false, + "target_execution_performed": false + }, + "tests": { + "chimera_gfx_ctest": "86_OF_86_PASS", + "phase10z_guardrails": 18, + "phase10z_contract_tests": 25, + "safety_audit": "PASS", + "secret_scan": "PASS", + "network_required_by_tests": false, + "hardware_claim_from_host_test": false + } +} diff --git a/manifests/retroarch/upstreams.json b/manifests/retroarch/upstreams.json new file mode 100644 index 0000000..ae8c527 --- /dev/null +++ b/manifests/retroarch/upstreams.json @@ -0,0 +1,72 @@ +{ + "schema_version": 1, + "phase": "PHASE_1_0A_RETROARCH_PS5_PORT_BOOTSTRAP", + "acquisition_date": "2026-07-19", + "sources": { + "retroarch": { + "repository": "https://github.com/libretro/RetroArch.git", + "tag": "v1.22.2", + "commit": "69a4f0ea1e8aaf442ae4858f2e7f2b31a1776576", + "tree": "33babf9eb7699b5d571a3063ea21c3e488c159fe", + "commit_date": "2025-11-20T00:17:08Z", + "source_archive_url": "https://github.com/libretro/RetroArch/archive/refs/tags/v1.22.2.tar.gz", + "source_archive_size": 71629881, + "source_archive_sha256": "245ef18c8fa8fbd9fbb5eb25cf43e17c6aace2f95c1ed99873cbd794012bb232", + "license": "GPL-3.0-or-later", + "official_source": true, + "working_tree_clean_at_acquisition": true + }, + "ps5_payload_sdk": { + "repository": "https://github.com/ps5-payload-dev/sdk.git", + "tag": "v0.41", + "commit": "d2e2e585740362976a39fdd5ccf390f199a7bc37", + "tree": "93ffa60ae28d3cd1572cdaf922ba35388b7d8f31", + "working_tree_clean_at_final_build": true, + "role": "cross compiler, public headers, stubs, CRT and linker scripts" + }, + "pacbrew": { + "repository": "https://github.com/ps5-payload-dev/pacbrew-repo.git", + "commit": "c2abcfcb60f569128abd0e8e70ad03a67bee5ea7", + "tree": "7d509ae7cb78fa04513302c96851db5fc41150d2", + "working_tree_clean_at_review": true, + "role": "PS5 package and dependency reference" + }, + "ps5_sdl": { + "repository": "https://github.com/ps5-payload-dev/SDL.git", + "commit": "0baf4ac49382b537ba449901b5b6d0d189bb1fbb", + "tree": "ca59958bb0c96a89578583409cdbc70706101960", + "reported_version": "SDL-2.30.12-g0baf4ac4", + "license": "Zlib", + "working_tree_clean_before_staging": true, + "role": "static PS5 software video, Pad and AudioOut backend" + }, + "elfldr_public_reference": { + "repository": "https://github.com/ps5-payload-dev/elfldr.git", + "commit": "699e8bcff03e91e8d6ca6eba281af25c5a58d8c2", + "linked_into_target": false, + "identity_status": "REFERENCE_ONLY" + }, + "elfldr_hardened_runtime_reference": { + "commit": "197623058f509eddde18868dafcb92fdcac66464", + "linked_into_target": false, + "identity_status": "CONTROLLED_REFERENCE_ONLY" + }, + "payload_manager_public_reference": { + "commit": "cfbc70f30f419b09bf2b52283f7409e2d3117ee1", + "linked_into_target": false, + "identity_status": "REFERENCE_ONLY" + }, + "payload_manager_controlled_runtime_reference": { + "commit": "e23d94ff91233aa770e2342800c1467875bdef44", + "linked_into_target": false, + "identity_status": "CONTROLLED_REFERENCE_ONLY" + }, + "libretro_samples": { + "acquired": false, + "used": false, + "reason": "The purpose-built GPL-compatible smoke core required no additional upstream." + } + }, + "unapproved_sources_used": false, + "proprietary_or_leaked_material_used": false +} diff --git a/manifests/runtime/controlled-ps5-runtime-profile.json b/manifests/runtime/controlled-ps5-runtime-profile.json new file mode 100644 index 0000000..a3808ec --- /dev/null +++ b/manifests/runtime/controlled-ps5-runtime-profile.json @@ -0,0 +1,133 @@ +{ + "artifact": { + "built": true, + "filename": "chimera-gfx-lifecycle-probe.elf", + "id": "chimera-gfx-lifecycle-phase07-fw960-v1", + "sha256": "bfb4a5cc768e162fe4c2fddf41c3978e152722918a39085277fd172cb95a7182", + "size": 112680, + "source_commit": "fe08300339a13f899fb78ea404ada381a5cba87c" + }, + "budgets": { + "automatic_retry": false, + "filesystem_write_budget": "controlled_artifact_directory_only", + "maximum_runtime_ms": 2000, + "payload_network_access": "none", + "persistent_write_budget": "controlled_artifact_removable" + }, + "decision": "READY_FOR_HARDENED_RUNTIME_DEPLOYMENT", + "deployment": { + "installed": false, + "ready_for_installation": true, + "rollback_prepared": true + }, + "effects": [ + { + "classification": "EXPECTED_VOLATILE_RUNTIME_EFFECT", + "id": "elfldr_first_stage_qaflags_enable" + }, + { + "classification": "RESTORED_BY_LOADER", + "id": "elfldr_first_stage_privilege_restore" + }, + { + "classification": "PAYLOAD_PROCESS_LOCAL", + "id": "payload_process_creation" + }, + { + "classification": "RESTORED_BY_LOADER", + "id": "ptrace_authid_restore_success_path" + }, + { + "classification": "FAIL_CLOSED_TERMINATION", + "id": "ptrace_authid_restore_failure_path" + }, + { + "classification": "BOUNDED_WATCHDOG", + "id": "ptrace_single_step_completion" + }, + { + "classification": "RESTORED_BY_LOADER", + "id": "breakpoint_byte" + }, + { + "classification": "RESTORED_BY_LOADER", + "id": "breakpoint_page_permissions" + }, + { + "classification": "RESTORED_BY_LOADER", + "id": "payload_credentials" + }, + { + "classification": "OS_RECLAIMED_ON_EXIT", + "id": "payload_mapping_args_sockets_pipes" + }, + { + "classification": "PAYLOAD_PROCESS_LOCAL", + "id": "sdk_patch_init" + }, + { + "classification": "PAYLOAD_PROCESS_LOCAL", + "id": "sdk_rtld_module_loading" + }, + { + "classification": "EXPLICIT_PROCESS_EXIT", + "id": "sdk_termination_branch" + }, + { + "classification": "BOUNDED_WATCHDOG", + "id": "payload_runtime_limit" + }, + { + "classification": "BOUNDED_WATCHDOG", + "id": "child_reaping" + }, + { + "classification": "HASH_BOUND_SAME_FD", + "id": "payload_manager_launch_hash_binding" + }, + { + "classification": "ALLOWED_APPLICATION_WRITE", + "id": "payload_manager_upload" + } + ], + "execution_authorized": false, + "expected_volatile_effects": [ + "elfldr_first_stage_qaflags_enable" + ], + "execution": { + "authorized": false, + "executed": false, + "execution_eligible": true, + "transferred": false + }, + "firmware": { + "device_attested": false, + "evidence": "jens_explicitly_confirmed_exact_9.60", + "exact": "9.60" + }, + "hard_blockers": [], + "loader": { + "base_commit": "699e8bcff03e91e8d6ca6eba281af25c5a58d8c2", + "hardened_commit": "197623058f509eddde18868dafcb92fdcac66464", + "installed": false, + "release": "v0.23-chimera-phase07", + "reproducible": true, + "sha256": "63e810982471eb40cae3a20aa9df9a0a02892f420e429874fae4e99aa400b561", + "size": 397000 + }, + "payload_manager": { + "base_commit": "cfbc70f30f419b09bf2b52283f7409e2d3117ee1", + "hardened_commit": "e23d94ff91233aa770e2342800c1467875bdef44", + "installed": false, + "release": "v0.3.1-chimera-controlled-phase07", + "reproducible": true, + "sha256": "8fecf8241a46246eddbd21e8bb4d875f5d76f1f4f4c6a11384df1f131aa5e5b1", + "size": 99560 + }, + "profile": "controlled-ps5-runtime", + "schema_version": 1, + "sdk": { + "commit": "d2e2e585740362976a39fdd5ccf390f199a7bc37", + "release": "v0.41" + } +} diff --git a/manifests/runtime/kernelwrite-proof-matrix.json b/manifests/runtime/kernelwrite-proof-matrix.json new file mode 100644 index 0000000..75326c2 --- /dev/null +++ b/manifests/runtime/kernelwrite-proof-matrix.json @@ -0,0 +1,77 @@ +{ + "artifact_built": false, + "decision": "BLOCKED", + "entries": [ + { + "component": "payload_args_t field layout", + "evidence": ["include/ps5/payload.h", "crt1.o disassembly offset 0x28"], + "status": "SAFE" + }, + { + "component": "default crt1.o startup", + "evidence": ["crt/crt.c", "crt/Makefile", "crt1.o reachable callgraph"], + "status": "UNSAFE" + }, + { + "component": "__patch_init", + "evidence": ["crt/patch.c", "crt1.o relocation and disassembly"], + "status": "UNSAFE" + }, + { + "component": "kernel credential capability and attribute writes", + "evidence": ["kernel_set_ucred_caps", "kernel_set_ucred_attrs", "kernel_copyin"], + "status": "UNSAFE" + }, + { + "component": "syscall address-bound writes", + "evidence": ["crt/patch.c offsets 0xf0 and 0xf8", "kernel_copyin"], + "status": "UNSAFE" + }, + { + "component": "ptrace and JIT capability changes", + "evidence": ["crt/patch.c caps[15] and attrs[3] mutations"], + "status": "UNSAFE" + }, + { + "component": "SPRX module loading during SDK rtld initialization", + "evidence": ["__rtld_init", "__rtld_sprx_init", "sceKernelLoadStartModule"], + "status": "UNSAFE" + }, + { + "component": "new network initialization", + "evidence": ["kernel_overlap_sockets linked but not startup-reachable", "default libSceNet dependency"], + "status": "UNPROVEN" + }, + { + "component": "GNM, VideoOut, and SDL calls in Phase-0.5 project source", + "evidence": ["no Phase-0.5 candidate source or artifact was created"], + "status": "SAFE" + }, + { + "component": "-nostartfiles -nodefaultlibs driver suppression", + "evidence": ["prospero-clang -### trace"], + "status": "SAFE" + }, + { + "component": "loader caller stack and return ABI", + "evidence": ["exact pinned loader caller source absent"], + "status": "UNPROVEN" + }, + { + "component": "payloadout ownership, lifetime, and writability", + "evidence": ["callee header only"], + "status": "UNPROVEN" + }, + { + "component": "post-return and crash cleanup", + "evidence": ["exact pinned loader caller source absent"], + "status": "UNPROVEN" + }, + { + "component": "loader changes before entry", + "evidence": ["kernel-access fields are supplied but their creation path is absent"], + "status": "UNPROVEN" + } + ], + "schema_version": 1 +} diff --git a/manifests/runtime/minimal-startup-artifact-decision.json b/manifests/runtime/minimal-startup-artifact-decision.json new file mode 100644 index 0000000..3225c60 --- /dev/null +++ b/manifests/runtime/minimal-startup-artifact-decision.json @@ -0,0 +1,41 @@ +{ + "artifact": { + "built": false, + "filename": null, + "id": "chimera-gfx-minimal-startup-fw-9.60", + "sha256": null, + "size": null, + "target": "ps5-x86_64" + }, + "audit_scope": "offline-only", + "decision": "BLOCKED", + "execution": { + "authorized": false, + "executed": false, + "execution_eligible": false, + "transferred": false + }, + "firmware_gate": { + "embedded_identifier": null, + "requested_identifier": "9.60" + }, + "reason_codes": [ + "CALLER_STACK_ABI_UNPROVEN", + "CRASH_CLEANUP_UNPROVEN", + "EXACT_LOADER_CALLER_SOURCE_ABSENT", + "PAYLOADOUT_OWNERSHIP_UNPROVEN", + "POST_RETURN_CLEANUP_UNPROVEN", + "RETURN_CONTRACT_UNPROVEN" + ], + "schema_version": 1, + "verification": { + "disassembly": "NOT_PERFORMED_BLOCKED_BEFORE_BUILD", + "double_clean_build": "NOT_PERFORMED_BLOCKED_BEFORE_BUILD", + "dt_needed": "NOT_PERFORMED_BLOCKED_BEFORE_BUILD", + "init_fini_arrays": "NOT_PERFORMED_BLOCKED_BEFORE_BUILD", + "linker_map": "NOT_PERFORMED_BLOCKED_BEFORE_BUILD", + "relocations": "NOT_PERFORMED_BLOCKED_BEFORE_BUILD", + "tls": "NOT_PERFORMED_BLOCKED_BEFORE_BUILD", + "undefined_symbols": "NOT_PERFORMED_BLOCKED_BEFORE_BUILD" + } +} diff --git a/manifests/runtime/phase-0.5-startup-audit.json b/manifests/runtime/phase-0.5-startup-audit.json new file mode 100644 index 0000000..57c0800 --- /dev/null +++ b/manifests/runtime/phase-0.5-startup-audit.json @@ -0,0 +1,1401 @@ +{ + "artifact": { + "built": false, + "execution_eligible": false, + "reason": "safe loader return and cleanup are not proven", + "sha256": null + }, + "audit_date": "2026-07-17", + "crt1_static_evidence": { + "disassembly_sha256": "7cb0035c8bb1ff534a8c406c92e18ecedf10882723a0e4a891f9675c903cb142", + "elf_header_sha256": "8bd149e3bef18d6c4a37d6201c65036d2be0e054b0fed1c63f38f07375fc5c39", + "linked_but_not_startup_reachable": [ + "__dlopen", + "__dlsym", + "kernel_mprotect", + "kernel_overlap_sockets", + "kernel_set_vmem_protection" + ], + "linked_prohibited_functions": [ + "__dlopen", + "__dlsym", + "__patch_init", + "kernel_copyin", + "kernel_copyout", + "kernel_mprotect", + "kernel_overlap_sockets", + "kernel_set_ucred_attrs", + "kernel_set_ucred_caps", + "kernel_set_vmem_protection" + ], + "reachable_callgraph": [ + { + "address": "17", + "caller": "__crt_syscall", + "kind": "call", + "target": ".bss.ptr_syscall" + }, + { + "address": "21", + "caller": "__crt_syscall_init", + "kind": "call", + "target": "INDIRECT:*(%rbx)" + }, + { + "address": "37", + "caller": "__crt_syscall_init", + "kind": "call", + "target": "INDIRECT:*(%rbx)" + }, + { + "address": "5e", + "caller": "__crt_syscall_init", + "kind": "call", + "target": "INDIRECT:*%rax" + }, + { + "address": "77", + "caller": "__crt_syscall_init", + "kind": "call", + "target": "INDIRECT:*(%rbx)" + }, + { + "address": "aa", + "caller": "__kernel_init", + "kind": "call", + "target": "__crt_syscall" + }, + { + "address": "af1", + "caller": "__kernel_init", + "kind": "call", + "target": "kernel_get_proc" + }, + { + "address": "b0b", + "caller": "__kernel_init", + "kind": "call", + "target": "kernel_copyout" + }, + { + "address": "b31", + "caller": "__kernel_init", + "kind": "call", + "target": "kernel_copyout" + }, + { + "address": "b5a", + "caller": "__kernel_init", + "kind": "call", + "target": "nid_encode" + }, + { + "address": "b6d", + "caller": "__kernel_init", + "kind": "call", + "target": "kernel_dynlib_resolve" + }, + { + "address": "b88", + "caller": "__kernel_init", + "kind": "call", + "target": "nid_encode" + }, + { + "address": "b9b", + "caller": "__kernel_init", + "kind": "call", + "target": "kernel_dynlib_resolve" + }, + { + "address": "1c", + "caller": "__klog_init", + "kind": "call", + "target": "kernel_dynlib_dlsym" + }, + { + "address": "43", + "caller": "__klog_init", + "kind": "call", + "target": "kernel_dynlib_dlsym" + }, + { + "address": "66", + "caller": "__klog_init", + "kind": "call", + "target": "kernel_dynlib_dlsym" + }, + { + "address": "89", + "caller": "__klog_init", + "kind": "call", + "target": "kernel_dynlib_dlsym" + }, + { + "address": "ac", + "caller": "__klog_init", + "kind": "call", + "target": "kernel_dynlib_dlsym" + }, + { + "address": "100", + "caller": "__patch_init", + "kind": "call", + "target": "kernel_copyin" + }, + { + "address": "12", + "caller": "__patch_init", + "kind": "call", + "target": "__crt_syscall" + }, + { + "address": "22", + "caller": "__patch_init", + "kind": "call", + "target": "kernel_get_ucred_caps" + }, + { + "address": "38", + "caller": "__patch_init", + "kind": "call", + "target": "kernel_get_ucred_attrs" + }, + { + "address": "59", + "caller": "__patch_init", + "kind": "call", + "target": "kernel_set_ucred_caps" + }, + { + "address": "79", + "caller": "__patch_init", + "kind": "call", + "target": "kernel_set_ucred_attrs" + }, + { + "address": "8e", + "caller": "__patch_init", + "kind": "call", + "target": "__crt_syscall" + }, + { + "address": "96", + "caller": "__patch_init", + "kind": "call", + "target": "kernel_get_proc" + }, + { + "address": "b8", + "caller": "__patch_init", + "kind": "call", + "target": "kernel_copyout" + }, + { + "address": "dc", + "caller": "__patch_init", + "kind": "call", + "target": "kernel_copyin" + }, + { + "address": "121", + "caller": "__rtld_dlfcn_init", + "kind": "call", + "target": "kernel_dynlib_dlsym" + }, + { + "address": "144", + "caller": "__rtld_dlfcn_init", + "kind": "call", + "target": "kernel_dynlib_dlsym" + }, + { + "address": "1c", + "caller": "__rtld_dlfcn_init", + "kind": "call", + "target": "kernel_dynlib_dlsym" + }, + { + "address": "43", + "caller": "__rtld_dlfcn_init", + "kind": "call", + "target": "kernel_dynlib_dlsym" + }, + { + "address": "6a", + "caller": "__rtld_dlfcn_init", + "kind": "call", + "target": "kernel_dynlib_dlsym" + }, + { + "address": "91", + "caller": "__rtld_dlfcn_init", + "kind": "call", + "target": "kernel_dynlib_dlsym" + }, + { + "address": "b4", + "caller": "__rtld_dlfcn_init", + "kind": "call", + "target": "kernel_dynlib_dlsym" + }, + { + "address": "db", + "caller": "__rtld_dlfcn_init", + "kind": "call", + "target": "kernel_dynlib_dlsym" + }, + { + "address": "fe", + "caller": "__rtld_dlfcn_init", + "kind": "call", + "target": "kernel_dynlib_dlsym" + }, + { + "address": "106", + "caller": "__rtld_init", + "kind": "call", + "target": "kernel_dynlib_dlsym" + }, + { + "address": "129", + "caller": "__rtld_init", + "kind": "call", + "target": "kernel_dynlib_dlsym" + }, + { + "address": "14c", + "caller": "__rtld_init", + "kind": "call", + "target": "kernel_dynlib_dlsym" + }, + { + "address": "15e", + "caller": "__rtld_init", + "kind": "call", + "target": "__rtld_sprx_init" + }, + { + "address": "16a", + "caller": "__rtld_init", + "kind": "call", + "target": "__rtld_so_init" + }, + { + "address": "176", + "caller": "__rtld_init", + "kind": "call", + "target": "__rtld_payload_init" + }, + { + "address": "191", + "caller": "__rtld_init", + "kind": "tail_call", + "target": "__rtld_dlfcn_init" + }, + { + "address": "1c", + "caller": "__rtld_init", + "kind": "call", + "target": "kernel_dynlib_dlsym" + }, + { + "address": "43", + "caller": "__rtld_init", + "kind": "call", + "target": "kernel_dynlib_dlsym" + }, + { + "address": "6a", + "caller": "__rtld_init", + "kind": "call", + "target": "kernel_dynlib_dlsym" + }, + { + "address": "91", + "caller": "__rtld_init", + "kind": "call", + "target": "kernel_dynlib_dlsym" + }, + { + "address": "b8", + "caller": "__rtld_init", + "kind": "call", + "target": "kernel_dynlib_dlsym" + }, + { + "address": "df", + "caller": "__rtld_init", + "kind": "call", + "target": "kernel_dynlib_dlsym" + }, + { + "address": "43", + "caller": "__rtld_lib_close", + "kind": "call", + "target": "__rtld_lib_close" + }, + { + "address": "69", + "caller": "__rtld_lib_close", + "kind": "call", + "target": "INDIRECT:*0x28(%rbx)" + }, + { + "address": "c2", + "caller": "__rtld_lib_close", + "kind": "call", + "target": ".bss.free" + }, + { + "address": "5", + "caller": "__rtld_lib_destroy", + "kind": "tail_call", + "target": "INDIRECT:*0x30(%rdi)" + }, + { + "address": "33", + "caller": "__rtld_lib_fini", + "kind": "call", + "target": "__rtld_lib_fini" + }, + { + "address": "e", + "caller": "__rtld_lib_fini", + "kind": "call", + "target": "INDIRECT:*0x20(%rdi)" + }, + { + "address": "3d", + "caller": "__rtld_lib_init", + "kind": "call", + "target": "__rtld_lib_init" + }, + { + "address": "86", + "caller": "__rtld_lib_init", + "kind": "tail_call", + "target": "INDIRECT:*%rax" + }, + { + "address": "21", + "caller": "__rtld_lib_open", + "kind": "tail_call", + "target": "INDIRECT:*(%rdi)" + }, + { + "address": "1c", + "caller": "__rtld_payload_init", + "kind": "call", + "target": "kernel_dynlib_dlsym" + }, + { + "address": "43", + "caller": "__rtld_payload_init", + "kind": "call", + "target": "kernel_dynlib_dlsym" + }, + { + "address": "66", + "caller": "__rtld_payload_init", + "kind": "call", + "target": "kernel_dynlib_dlsym" + }, + { + "address": "89", + "caller": "__rtld_payload_init", + "kind": "call", + "target": "kernel_dynlib_dlsym" + }, + { + "address": "ac", + "caller": "__rtld_payload_init", + "kind": "call", + "target": "kernel_dynlib_dlsym" + }, + { + "address": "1b", + "caller": "__rtld_payload_new", + "kind": "call", + "target": ".bss.calloc" + }, + { + "address": "32", + "caller": "__rtld_payload_new", + "kind": "call", + "target": "INDIRECT:*%r13" + }, + { + "address": "b7", + "caller": "__rtld_payload_new", + "kind": "call", + "target": ".bss.strcpy" + }, + { + "address": "cf", + "caller": "__rtld_payload_new", + "kind": "call", + "target": "INDIRECT:*%r13" + }, + { + "address": "1c", + "caller": "__rtld_so_init", + "kind": "call", + "target": "kernel_dynlib_dlsym" + }, + { + "address": "43", + "caller": "__rtld_so_init", + "kind": "call", + "target": "kernel_dynlib_dlsym" + }, + { + "address": "6a", + "caller": "__rtld_so_init", + "kind": "call", + "target": "kernel_dynlib_dlsym" + }, + { + "address": "8d", + "caller": "__rtld_so_init", + "kind": "call", + "target": "kernel_dynlib_dlsym" + }, + { + "address": "b0", + "caller": "__rtld_so_init", + "kind": "call", + "target": "kernel_dynlib_dlsym" + }, + { + "address": "d3", + "caller": "__rtld_so_init", + "kind": "call", + "target": "kernel_dynlib_dlsym" + }, + { + "address": "109", + "caller": "__rtld_sprx_init", + "kind": "call", + "target": "kernel_dynlib_dlsym" + }, + { + "address": "130", + "caller": "__rtld_sprx_init", + "kind": "call", + "target": "kernel_dynlib_dlsym" + }, + { + "address": "153", + "caller": "__rtld_sprx_init", + "kind": "call", + "target": "kernel_dynlib_dlsym" + }, + { + "address": "175", + "caller": "__rtld_sprx_init", + "kind": "call", + "target": "kernel_dynlib_handle" + }, + { + "address": "192", + "caller": "__rtld_sprx_init", + "kind": "call", + "target": ".bss.sceKernelLoadStartModule" + }, + { + "address": "1ae", + "caller": "__rtld_sprx_init", + "kind": "call", + "target": "kernel_dynlib_dlsym" + }, + { + "address": "27", + "caller": "__rtld_sprx_init", + "kind": "call", + "target": "INDIRECT:*%r15" + }, + { + "address": "4b", + "caller": "__rtld_sprx_init", + "kind": "call", + "target": "INDIRECT:*%r15" + }, + { + "address": "6d", + "caller": "__rtld_sprx_init", + "kind": "call", + "target": "kernel_dynlib_dlsym" + }, + { + "address": "94", + "caller": "__rtld_sprx_init", + "kind": "call", + "target": "kernel_dynlib_dlsym" + }, + { + "address": "bb", + "caller": "__rtld_sprx_init", + "kind": "call", + "target": "kernel_dynlib_dlsym" + }, + { + "address": "e2", + "caller": "__rtld_sprx_init", + "kind": "call", + "target": "kernel_dynlib_dlsym" + }, + { + "address": "122", + "caller": "_start", + "kind": "call", + "target": "kernel_dynlib_dlsym" + }, + { + "address": "141", + "caller": "_start", + "kind": "call", + "target": "kernel_dynlib_dlsym" + }, + { + "address": "160", + "caller": "_start", + "kind": "call", + "target": "kernel_dynlib_dlsym" + }, + { + "address": "17f", + "caller": "_start", + "kind": "call", + "target": "kernel_dynlib_dlsym" + }, + { + "address": "18d", + "caller": "_start", + "kind": "call", + "target": "INDIRECT:*%rbx" + }, + { + "address": "192", + "caller": "_start", + "kind": "call", + "target": "INDIRECT:*%r15" + }, + { + "address": "1b0", + "caller": "_start", + "kind": "call", + "target": "kernel_dynlib_dlsym" + }, + { + "address": "1cf", + "caller": "_start", + "kind": "call", + "target": "kernel_dynlib_dlsym" + }, + { + "address": "1ef", + "caller": "_start", + "kind": "call", + "target": "kernel_dynlib_dlsym" + }, + { + "address": "20b", + "caller": "_start", + "kind": "call", + "target": "kernel_dynlib_dlsym" + }, + { + "address": "225", + "caller": "_start", + "kind": "call", + "target": "__rtld_payload_new" + }, + { + "address": "236", + "caller": "_start", + "kind": "call", + "target": "__rtld_dlfcn_setroot" + }, + { + "address": "23f", + "caller": "_start", + "kind": "call", + "target": "__rtld_lib_open" + }, + { + "address": "25a", + "caller": "_start", + "kind": "call", + "target": "__rtld_lib_init" + }, + { + "address": "26a", + "caller": "_start", + "kind": "call", + "target": "__rtld_lib_close" + }, + { + "address": "273", + "caller": "_start", + "kind": "call", + "target": "__rtld_lib_destroy" + }, + { + "address": "29d", + "caller": "_start", + "kind": "call", + "target": "kernel_dynlib_dlsym" + }, + { + "address": "2c2", + "caller": "_start", + "kind": "call", + "target": "main" + }, + { + "address": "2d8", + "caller": "_start", + "kind": "call", + "target": "__rtld_lib_fini" + }, + { + "address": "2e3", + "caller": "_start", + "kind": "call", + "target": "__rtld_lib_close" + }, + { + "address": "2ef", + "caller": "_start", + "kind": "call", + "target": "__rtld_lib_destroy" + }, + { + "address": "31e", + "caller": "_start", + "kind": "call", + "target": "kernel_dynlib_dlsym" + }, + { + "address": "336", + "caller": "_start", + "kind": "call", + "target": "INDIRECT:*%rax" + }, + { + "address": "45", + "caller": "_start", + "kind": "call", + "target": "__crt_syscall_init" + }, + { + "address": "52", + "caller": "_start", + "kind": "call", + "target": "__kernel_init" + }, + { + "address": "5c", + "caller": "_start", + "kind": "call", + "target": "__klog_init" + }, + { + "address": "7d", + "caller": "_start", + "kind": "call", + "target": "kernel_dynlib_dlsym" + }, + { + "address": "8e", + "caller": "_start", + "kind": "call", + "target": "__patch_init" + }, + { + "address": "ad", + "caller": "_start", + "kind": "call", + "target": "__rtld_init" + }, + { + "address": "c5", + "caller": "_start", + "kind": "call", + "target": "klog_puts" + }, + { + "address": "13b", + "caller": "kernel_copyin", + "kind": "call", + "target": "INDIRECT:*%rax" + }, + { + "address": "16f", + "caller": "kernel_copyin", + "kind": "call", + "target": "__crt_syscall" + }, + { + "address": "19f", + "caller": "kernel_copyin", + "kind": "call", + "target": "__crt_syscall" + }, + { + "address": "1c1", + "caller": "kernel_copyin", + "kind": "call", + "target": "__crt_syscall" + }, + { + "address": "58", + "caller": "kernel_copyin", + "kind": "call", + "target": "INDIRECT:*%rax" + }, + { + "address": "74", + "caller": "kernel_copyin", + "kind": "call", + "target": "INDIRECT:*%rax" + }, + { + "address": "b5", + "caller": "kernel_copyin", + "kind": "call", + "target": "__crt_syscall" + }, + { + "address": "f7", + "caller": "kernel_copyin", + "kind": "call", + "target": "__crt_syscall" + }, + { + "address": "13e", + "caller": "kernel_copyout", + "kind": "call", + "target": "INDIRECT:*%rax" + }, + { + "address": "172", + "caller": "kernel_copyout", + "kind": "call", + "target": "__crt_syscall" + }, + { + "address": "1a2", + "caller": "kernel_copyout", + "kind": "call", + "target": "__crt_syscall" + }, + { + "address": "1c4", + "caller": "kernel_copyout", + "kind": "call", + "target": "__crt_syscall" + }, + { + "address": "5b", + "caller": "kernel_copyout", + "kind": "call", + "target": "INDIRECT:*%rax" + }, + { + "address": "77", + "caller": "kernel_copyout", + "kind": "call", + "target": "INDIRECT:*%rax" + }, + { + "address": "b8", + "caller": "kernel_copyout", + "kind": "call", + "target": "__crt_syscall" + }, + { + "address": "fa", + "caller": "kernel_copyout", + "kind": "call", + "target": "__crt_syscall" + }, + { + "address": "1c", + "caller": "kernel_dynlib_dlsym", + "kind": "call", + "target": "nid_encode" + }, + { + "address": "2a", + "caller": "kernel_dynlib_dlsym", + "kind": "call", + "target": "kernel_dynlib_resolve" + }, + { + "address": "111", + "caller": "kernel_dynlib_handle", + "kind": "call", + "target": "kernel_copyout" + }, + { + "address": "1a", + "caller": "kernel_dynlib_handle", + "kind": "call", + "target": "kernel_get_proc" + }, + { + "address": "1ab", + "caller": "kernel_dynlib_handle", + "kind": "call", + "target": "INDIRECT:*%rax" + }, + { + "address": "3f", + "caller": "kernel_dynlib_handle", + "kind": "call", + "target": "kernel_copyout" + }, + { + "address": "68", + "caller": "kernel_dynlib_handle", + "kind": "call", + "target": "kernel_copyout" + }, + { + "address": "af", + "caller": "kernel_dynlib_handle", + "kind": "call", + "target": "kernel_copyout" + }, + { + "address": "d6", + "caller": "kernel_dynlib_handle", + "kind": "call", + "target": "kernel_copyout" + }, + { + "address": "f3", + "caller": "kernel_dynlib_handle", + "kind": "call", + "target": "kernel_copyout" + }, + { + "address": "14", + "caller": "kernel_dynlib_obj", + "kind": "call", + "target": "kernel_get_proc" + }, + { + "address": "39", + "caller": "kernel_dynlib_obj", + "kind": "call", + "target": "kernel_copyout" + }, + { + "address": "5c", + "caller": "kernel_dynlib_obj", + "kind": "call", + "target": "kernel_copyout" + }, + { + "address": "7a", + "caller": "kernel_dynlib_obj", + "kind": "call", + "target": "kernel_copyout" + }, + { + "address": "94", + "caller": "kernel_dynlib_obj", + "kind": "call", + "target": "kernel_copyout" + }, + { + "address": "ac", + "caller": "kernel_dynlib_obj", + "kind": "call", + "target": "INDIRECT:*%rax" + }, + { + "address": "109", + "caller": "kernel_dynlib_resolve", + "kind": "call", + "target": "__crt_syscall" + }, + { + "address": "1e", + "caller": "kernel_dynlib_resolve", + "kind": "call", + "target": "kernel_dynlib_obj" + }, + { + "address": "52", + "caller": "kernel_dynlib_resolve", + "kind": "call", + "target": "kernel_copyout" + }, + { + "address": "91", + "caller": "kernel_dynlib_resolve", + "kind": "call", + "target": "__crt_syscall" + }, + { + "address": "b4", + "caller": "kernel_dynlib_resolve", + "kind": "call", + "target": "kernel_copyout" + }, + { + "address": "d4", + "caller": "kernel_dynlib_resolve", + "kind": "call", + "target": "kernel_copyout" + }, + { + "address": "109", + "caller": "kernel_get_proc", + "kind": "call", + "target": "kernel_copyout" + }, + { + "address": "16a", + "caller": "kernel_get_proc", + "kind": "call", + "target": "INDIRECT:*%rax" + }, + { + "address": "3f", + "caller": "kernel_get_proc", + "kind": "call", + "target": "__crt_syscall" + }, + { + "address": "af", + "caller": "kernel_get_proc", + "kind": "call", + "target": "kernel_copyout" + }, + { + "address": "ef", + "caller": "kernel_get_proc", + "kind": "call", + "target": "kernel_copyout" + }, + { + "address": "11", + "caller": "kernel_get_ucred_attrs", + "kind": "call", + "target": "kernel_get_proc" + }, + { + "address": "2b", + "caller": "kernel_get_ucred_attrs", + "kind": "call", + "target": "kernel_copyout" + }, + { + "address": "57", + "caller": "kernel_get_ucred_attrs", + "kind": "call", + "target": "kernel_copyout" + }, + { + "address": "11", + "caller": "kernel_get_ucred_caps", + "kind": "call", + "target": "kernel_get_proc" + }, + { + "address": "2b", + "caller": "kernel_get_ucred_caps", + "kind": "call", + "target": "kernel_copyout" + }, + { + "address": "57", + "caller": "kernel_get_ucred_caps", + "kind": "call", + "target": "kernel_copyout" + }, + { + "address": "11", + "caller": "kernel_set_ucred_attrs", + "kind": "call", + "target": "kernel_get_proc" + }, + { + "address": "2b", + "caller": "kernel_set_ucred_attrs", + "kind": "call", + "target": "kernel_copyout" + }, + { + "address": "57", + "caller": "kernel_set_ucred_attrs", + "kind": "call", + "target": "kernel_copyin" + }, + { + "address": "11", + "caller": "kernel_set_ucred_caps", + "kind": "call", + "target": "kernel_get_proc" + }, + { + "address": "2b", + "caller": "kernel_set_ucred_caps", + "kind": "call", + "target": "kernel_copyout" + }, + { + "address": "57", + "caller": "kernel_set_ucred_caps", + "kind": "call", + "target": "kernel_copyin" + }, + { + "address": "2a", + "caller": "klog_puts", + "kind": "call", + "target": "INDIRECT:*%r12" + }, + { + "address": "47", + "caller": "klog_puts", + "kind": "call", + "target": "INDIRECT:*%r12" + }, + { + "address": "65", + "caller": "klog_puts", + "kind": "call", + "target": ".bss.snprintf" + }, + { + "address": "8a", + "caller": "klog_puts", + "kind": "call", + "target": "INDIRECT:*%r15" + }, + { + "address": "9e", + "caller": "klog_puts", + "kind": "call", + "target": "__crt_syscall" + }, + { + "address": "17b", + "caller": "nid_encode", + "kind": "call", + "target": ".text.SHA1Transform" + }, + { + "address": "24d", + "caller": "nid_encode", + "kind": "call", + "target": ".text.SHA1Transform" + }, + { + "address": "2e0", + "caller": "nid_encode", + "kind": "call", + "target": ".text.SHA1Transform" + }, + { + "address": "447", + "caller": "nid_encode", + "kind": "call", + "target": ".text.SHA1Transform" + }, + { + "address": "99", + "caller": "nid_encode", + "kind": "call", + "target": ".text.SHA1Transform" + }, + { + "address": "ca", + "caller": "nid_encode", + "kind": "call", + "target": ".text.SHA1Transform" + } + ], + "reachable_functions": [ + "__crt_syscall", + "__crt_syscall_init", + "__kernel_init", + "__klog_init", + "__patch_init", + "__rtld_dlfcn_init", + "__rtld_dlfcn_setroot", + "__rtld_init", + "__rtld_lib_close", + "__rtld_lib_destroy", + "__rtld_lib_fini", + "__rtld_lib_init", + "__rtld_lib_open", + "__rtld_payload_init", + "__rtld_payload_new", + "__rtld_so_init", + "__rtld_sprx_init", + "_start", + "kernel_copyin", + "kernel_copyout", + "kernel_dynlib_dlsym", + "kernel_dynlib_handle", + "kernel_dynlib_obj", + "kernel_dynlib_resolve", + "kernel_get_proc", + "kernel_get_ucred_attrs", + "kernel_get_ucred_caps", + "kernel_set_ucred_attrs", + "kernel_set_ucred_caps", + "klog_puts", + "nid_encode" + ], + "reachable_prohibited_functions": [ + "__patch_init", + "kernel_copyin", + "kernel_copyout", + "kernel_set_ucred_attrs", + "kernel_set_ucred_caps" + ], + "relocation_count": 1033, + "relocation_table_sha256": "8c4db854706e3ddd1f5b068f891a242c3917d13f39b1ea388242bdd65ca5fbce", + "section_table_sha256": "65934dd9fcee1ea5333669ec2dd2204c6d0e7197e5f96a677d08c234f3b64c48", + "source_token_inventory": { + "GNM": false, + "SDL": false, + "VideoOut": false, + "module_loading": true, + "network_socket_helper": true + }, + "tls_sections": [] + }, + "custom_artifact_verification": { + "artifact_sha256": null, + "disassembly": "NOT_PERFORMED_BLOCKED_BEFORE_BUILD", + "double_clean_build": "NOT_PERFORMED_BLOCKED_BEFORE_BUILD", + "dt_needed": "NOT_PERFORMED_BLOCKED_BEFORE_BUILD", + "init_fini_arrays": "NOT_PERFORMED_BLOCKED_BEFORE_BUILD", + "linker_map": "NOT_PERFORMED_BLOCKED_BEFORE_BUILD", + "relocations": "NOT_PERFORMED_BLOCKED_BEFORE_BUILD", + "tls": "NOT_PERFORMED_BLOCKED_BEFORE_BUILD", + "undefined_symbols": "NOT_PERFORMED_BLOCKED_BEFORE_BUILD" + }, + "decision": "BLOCKED", + "loader_evidence": { + "callee_first_argument_register_rdi": "SAFE_DISASSEMBLY_FACT", + "caller_source_present": false, + "caller_stack_alignment": "UNPROVEN", + "crash_cleanup": "UNPROVEN", + "entry_argument_layout": "SAFE_SOURCE_FACT", + "exact_loader_used_for_firmware_9_60_identified": false, + "local_non_sdk_contract_hits": { + "RetroArch": [], + "SDL": [], + "pacbrew-repo": [], + "ps5-linux-loader": [] + }, + "payloadout_lifetime_and_writability": "UNPROVEN", + "post_return_cleanup": "UNPROVEN", + "pre_entry_loader_process_changes": "UNPROVEN", + "return_address_and_return_consumption": "UNPROVEN", + "sdk_readme_loader_candidates": [ + "ps5-payload-dev/elfldr", + "cryonumb/elfloader via ps5-jar-loader", + "shahrilnet/remote_lua_loader" + ] + }, + "minimal_missing_evidence": [ + "the exact loader and pinned source commit used on firmware 9.60", + "the caller instruction sequence that establishes RDI, RSP alignment, and return address", + "payload_args_t allocation, payloadout lifetime, writability, and ownership", + "the caller path after _start returns, including restoration and cleanup", + "crash/fault behavior before and during _start", + "all process, credential, syscall, module, and memory changes made before entry" + ], + "review": [ + { + "basis": "public SDK header plus _start payloadout access at offset 0x28", + "component": "payload_args_t field order and 48-byte x86_64 layout", + "status": "SAFE" + }, + { + "basis": "source, relocation, and reachable disassembly prove kernel-write paths", + "component": "SDK v0.41 default crt1.o", + "status": "UNSAFE" + }, + { + "basis": "compiler -### trace only; no output ELF was produced", + "component": "-nostartfiles -nodefaultlibs removes SDK CRT/default libs", + "status": "SAFE" + }, + { + "basis": "linker symbols and byte-zero loop can be freestanding", + "component": "custom BSS zeroing implementation", + "status": "SAFE" + }, + { + "basis": "callee disassembly does not prove caller behavior", + "component": "loader entry stack and ABI preconditions", + "status": "UNPROVEN" + }, + { + "basis": "no exact pinned public loader caller source is locally available", + "component": "return, exit, crash, and cleanup behavior", + "status": "UNPROVEN" + }, + { + "basis": "payload args expose pre-existing kernel access but not how it was established", + "component": "loader changes before _start", + "status": "UNPROVEN" + } + ], + "safe_alternatives": [ + "continue libchimera-gfx through mock and software backends", + "develop a separately scoped Linux-on-PS5 backend as a long-term track" + ], + "schema_version": 1, + "scope": "phase-0.5-kernelwrite-free-startup-offline-only", + "sdk": { + "commit": "d2e2e585740362976a39fdd5ccf390f199a7bc37", + "release": "v0.41", + "source_hashes": { + "Makefile.inc": "57a78c9d6d9cefa8e4da873e0c1be119ac8b255ad8d6823b42124310e15bf4f0", + "crt/Makefile": "6a62f777f32ab05cbe7bff81c00cfd6639ba8bf440aba611a40ff0b62732568b", + "crt/crt.c": "3875f4739ec40b33f1f4967a1acbb585a527d2c281a360153e1f69c8b945932a", + "crt/elf.h": "fce18521dd93e6ca9dd9138cac56121918ec6d9d213fa7365b199248b145a83a", + "crt/kernel.c": "ac1c375aae8d3cb1be5fb8bad2f4e6492b6ec1f1450977b534202f46b5b70321", + "crt/kernel.h": "735b516fa580b48e7ee45d69f4ceef1485fc5a1b91d11611a742626cb3fa8c15", + "crt/klog.c": "a74f262dcae14c0cfc7f8a4d9b7ee094a1148d13369286e3adaa698af64c31e5", + "crt/klog.h": "0efd3ed80f35f1d142a885153fed296fc4905a385d1f96c95a4b811896246213", + "crt/mdbg.c": "98add54968b138ca7fcf200e08587e0722e270960a637241bca841e75dfedc3c", + "crt/mdbg.h": "04d6c9f4ab28814164d93945de8a729cfad785e4fb01f95765d95e60e159a81f", + "crt/nid.c": "33c9318b89d223836108a27478691d49f107081170a42ff0392f6a029d486c8f", + "crt/nid.h": "5997918a961b3b86011c2ef54e5988901fe98c51c6266ab3dc7de7b023d6d157", + "crt/patch.c": "4f76a677bba54f4641e1cf2755768c29afb7464a35b2f21e85db2aa2785eceac", + "crt/patch.h": "724eea4309d3948379a0f329f5275b37afe6c699ca4bee0a93f6b3e88852a7dc", + "crt/payload.h": "9284a4448e1c6602297b7b46723580c6f62faeed0899dbd82c3da00ec7ddccbd", + "crt/rtld.c": "f46d071528d13c9ca3e4ea27f6884e1a30456fd30dcd4218f7a8e7493b3244ff", + "crt/rtld.h": "77cae43e1f0da581ce72bdbb9cb84ad9d06ef34635d4017b82d133e60c33ecf8", + "crt/rtld_dlfcn.c": "bdb3a17aa88d1b4e4e303709fb39b72d03729a6de029b1791a1dc08a2b94637c", + "crt/rtld_dlfcn.h": "a6706a1bbf79d3566a80daa4fe66614b4ab91107ee20140eacaca47f215f5271", + "crt/rtld_payload.c": "5598ca0870c2c7a120507755897d76e5aad0d3a6846cee4482230650ed181f82", + "crt/rtld_payload.h": "44cc522bb5979ead9b8f3cb576b8695ad8cfcdf45e244307667d722e3c326efd", + "crt/rtld_so.c": "26f59cb8970001e4508a07abf4bf3507b6d58638f210c59839ac7344d3832399", + "crt/rtld_so.h": "8c248e5993784449cab246dcf33637abcc20c7144c883583e71b95515b324621", + "crt/rtld_sprx.c": "d9bbff93518e681cb468a7dc3aa0bb185a8071bf9bc04483081e4f8effd9e7ca", + "crt/rtld_sprx.h": "ca4919450941be9f47a3797112255e4fbf8c4c578436b2ac2563c981d63a2209", + "crt/syscall.c": "3482a5be32ed7f268d6db98f3b31c56156b16c634e1c63a12686e9bbc5e1b186", + "crt/syscall.h": "66e8c3481fd5bf4828602e74d3f8acdd9ee778c2cd95793b19830b77bd963bab", + "host/Makefile": "9b1d99324a8d4c283b9536d9a040f5b96528a5dd7cd1d8121decfe499053fb6a", + "host/bin/prospero-clang": "0cf49ae43d6110a7606c0ee4d702fc4b5d5e1c3ae9a722945a48b80294e295ba", + "host/bin/prospero-lld": "f48918af3717f142ea8cba89741a0eb161c43ad2db1c535b92fcae0ae899a445", + "host/elf_x86_64.x": "169b80d01da601ef96bbc584986608dec1d9c01397eae81eccd4e1a66b0a6c6a", + "host/toolchain/prospero.cmake": "78e376ca7d6128f97b763636a9a0283bfb5eaaadc158bb5b0a6f80969a694f74", + "host/toolchain/prospero.mk": "214f4a39f5c14dd21a0360ac8d838be5a6d9a7ffe92962274ca84e6c0eb06f62", + "host/toolchain/prospero.sh": "e503a8c8c78a0fb5b3c2f74963bbb8dc52303b9c7d57ca9fdc2c1ab3fb0354d9", + "include/ps5/payload.h": "32f8808c1caddeb2bef367ce99b278b79ff70b883854262f0b287c3cc920593e" + }, + "target": "x86_64-sie-ps5" + }, + "startup_linkage": { + "crt1_composition": [ + { + "object": "crt.o", + "source": "crt/crt.c" + }, + { + "object": "syscall.o", + "source": "crt/syscall.c" + }, + { + "object": "klog.o", + "source": "crt/klog.c" + }, + { + "object": "nid.o", + "source": "crt/nid.c" + }, + { + "object": "kernel.o", + "source": "crt/kernel.c" + }, + { + "object": "rtld.o", + "source": "crt/rtld.c" + }, + { + "object": "rtld_so.o", + "source": "crt/rtld_so.c" + }, + { + "object": "rtld_sprx.o", + "source": "crt/rtld_sprx.c" + }, + { + "object": "rtld_payload.o", + "source": "crt/rtld_payload.c" + }, + { + "object": "rtld_dlfcn.o", + "source": "crt/rtld_dlfcn.c" + }, + { + "object": "mdbg.o", + "source": "crt/mdbg.c" + }, + { + "object": "patch.o", + "source": "crt/patch.c" + } + ], + "crt1_sha256": "0e4c1e5e38f22026580e856238a53e75d9e112f99a34bfe5495a3a0ffac15238", + "crt1_undefined_symbols": [ + "_DYNAMIC", + "__bss_end", + "__bss_start", + "__fini_array_end", + "__fini_array_start", + "__image_end", + "__image_start", + "__init_array_end", + "__init_array_start", + "main" + ], + "default_driver_additions": [ + "target/lib/crt1.o", + "libc", + "libkernel_web", + "libSceLibcInternal", + "libSceNet" + ], + "default_driver_trace_sha256": "cd0d3b31ae97e78d6e7752ac096b1d56a516706705dfcd83c1aedc2e18e48fc2", + "empty_startup_archives": { + "crtbegin.o": [], + "crtbeginS.o": [], + "crtend.o": [], + "crtendS.o": [], + "crti.o": [], + "crtn.o": [] + }, + "freestanding_driver_additions": [], + "freestanding_driver_trace_sha256": "71023b3810af87380e9fc17ce76e9093b29636355c6265814d61e1aa75231929", + "freestanding_flags": [ + "-nostartfiles", + "-nodefaultlibs" + ], + "installed_linker_script_sha256": "3bacf56a21602a1298752023a4c5b0c305954ddf6cdbce1464476fc9da9d9093", + "installed_main_script_sha256": "3bacf56a21602a1298752023a4c5b0c305954ddf6cdbce1464476fc9da9d9093", + "installed_prx_script_sha256": "3bacf56a21602a1298752023a4c5b0c305954ddf6cdbce1464476fc9da9d9093" + }, + "tool_versions": { + "objdump": "Ubuntu LLVM version 18.1.8", + "prospero_clang": "Ubuntu clang version 18.1.8 (20ubuntu8)", + "prospero_lld": "Ubuntu LLD 18.1.8 (compatible with GNU linkers)", + "readelf": "GNU readelf (GNU Binutils for Ubuntu) 2.46" + } +} diff --git a/manifests/runtime/phase-0.6-loader-runtime-audit.json b/manifests/runtime/phase-0.6-loader-runtime-audit.json new file mode 100644 index 0000000..265a30e --- /dev/null +++ b/manifests/runtime/phase-0.6-loader-runtime-audit.json @@ -0,0 +1,541 @@ +{ + "artifact": { + "built": false, + "execution_eligible": false, + "filename": null, + "sha256": null, + "size": null + }, + "binary_evidence": { + "disassembly_sha256": "a4af871eb9bf7df57e3f323972616f48dbffaadfea7afb3976404304cc80ca31", + "dt_needed": [ + "libSceLibcInternal.sprx", + "libSceNet.sprx", + "libkernel_web.sprx" + ], + "entry_point": "0x4700", + "init_fini_array_sizes": { + "fini_array": 0, + "init_array": 0, + "preinit_array": 0 + }, + "load_segments": [ + { + "file_size": 51455, + "memory_size": 51455, + "permissions": "RWE" + }, + { + "file_size": 33104, + "memory_size": 33104, + "permissions": "RW" + }, + { + "file_size": 264688, + "memory_size": 267000, + "permissions": "RW" + } + ], + "readelf_report_sha256": "e0526deb23e40a3a8ad6f613dfbc3d5764ceb1cb770ce9e6fa1010c4e83ba9f9", + "relocations": { + "relative": 140, + "total": 164 + }, + "tls_present": false, + "undefined_dynamic_symbols": [ + "__error", + "close", + "execve", + "free", + "getpid", + "kevent", + "kill", + "kqueue", + "malloc", + "memcpy", + "memset", + "open", + "printf", + "puts", + "realloc", + "recv", + "rfork_thread", + "sceKernelSendNotificationRequest", + "strcmp", + "strerror", + "strlen", + "sysctl", + "vsnprintf", + "waitpid" + ] + }, + "callgraph": { + "edges": [ + [ + "Payload Manager /loadpayload:", + "ps5_launch_elf" + ], + [ + "ps5_launch_elf", + "connect 127.0.0.1:9021" + ], + [ + "ps5_launch_elf", + "send ELF bytes" + ], + [ + "serve_elfldr", + "elfldr_spawn" + ], + [ + "elfldr_spawn", + "rfork_thread" + ], + [ + "rfork_thread child", + "elfldr_rfork_entry" + ], + [ + "elfldr_rfork_entry", + "ptrace PT_TRACE_ME" + ], + [ + "elfldr_rfork_entry", + "execve SceSpZeroConf" + ], + [ + "elfldr_spawn parent", + "pt_syscall 599" + ], + [ + "elfldr_spawn parent", + "install then restore INT3 byte" + ], + [ + "elfldr_spawn parent", + "elfldr_exec" + ], + [ + "elfldr_exec", + "elfldr_raise_privileges" + ], + [ + "elfldr_exec", + "elfldr_prepare_exec" + ], + [ + "elfldr_prepare_exec", + "elfldr_load" + ], + [ + "elfldr_prepare_exec", + "elfldr_payload_args" + ], + [ + "elfldr_prepare_exec", + "push observed RIP at RSP-8" + ], + [ + "elfldr_prepare_exec", + "set RIP=payload entry" + ], + [ + "elfldr_prepare_exec", + "set RDI=payload_args" + ], + [ + "elfldr_exec", + "restore subset of credentials" + ], + [ + "elfldr_exec", + "ptrace PT_DETACH" + ], + [ + "payload _start", + "SDK __patch_init" + ], + [ + "payload _start", + "payload main" + ], + [ + "payload _start", + "payload_terminate" + ], + [ + "payload_terminate", + "return or exit or trap" + ] + ], + "entry": "Payload Manager /loadpayload:", + "extraction": "reviewed source edges with required-token assertions" + }, + "decision": "BLOCKED_VERSION_OR_UNBOUNDED_EFFECT", + "effects": [ + { + "blocker": false, + "classification": "EXPECTED_VOLATILE_RUNTIME_EFFECT", + "evidence": [ + "elfldr/main.c:48-59" + ], + "id": "elfldr_first_stage_qaflags_enable", + "scope": "existing_loader_bootstrap" + }, + { + "blocker": true, + "classification": "UNBOUNDED_OR_UNKNOWN", + "evidence": [ + "elfldr/main.c:61-105 restores jail/root/caps/authid", + "UID is changed by elfldr_raise_privileges but is not backed up or restored" + ], + "id": "elfldr_first_stage_privilege_restore", + "scope": "existing_exploit_host_process" + }, + { + "blocker": false, + "classification": "PAYLOAD_PROCESS_LOCAL", + "evidence": [ + "elfldr/elfldr.c:570-710" + ], + "id": "payload_process_creation", + "scope": "new_SceSpZeroConf_child" + }, + { + "blocker": false, + "classification": "RESTORED_BY_LOADER", + "evidence": [ + "elfldr/pt.c:35-54" + ], + "id": "ptrace_authid_restore_success_path", + "scope": "elfldr_service_process" + }, + { + "blocker": true, + "classification": "UNBOUNDED_OR_UNKNOWN", + "evidence": [ + "elfldr/pt.c:50-51 returns after failed restoration", + "no second restoration or process shutdown is present" + ], + "id": "ptrace_authid_restore_failure_path", + "scope": "elfldr_service_process" + }, + { + "blocker": true, + "classification": "UNBOUNDED_OR_UNKNOWN", + "evidence": [ + "elfldr/pt.c:238-246", + "elfldr/pt.c:291-299" + ], + "id": "ptrace_single_step_completion", + "scope": "loader_control_path" + }, + { + "blocker": false, + "classification": "RESTORED_BY_LOADER", + "evidence": [ + "elfldr/elfldr.c:675-700" + ], + "id": "breakpoint_byte", + "scope": "payload_child" + }, + { + "blocker": false, + "classification": "PAYLOAD_PROCESS_LOCAL", + "evidence": [ + "elfldr/elfldr.c:669 changes page to RWX", + "no source edge restores the original protection" + ], + "id": "breakpoint_page_permissions", + "scope": "payload_child" + }, + { + "blocker": true, + "classification": "UNBOUNDED_OR_UNKNOWN", + "evidence": [ + "elfldr/elfldr.c:447-513 restores jail/root/caps/authid", + "UID is set to zero and is not restored" + ], + "id": "payload_credentials", + "scope": "payload_child" + }, + { + "blocker": true, + "classification": "UNBOUNDED_OR_UNKNOWN", + "evidence": [ + "elfldr/elfldr.c:143-343", + "successful detach has no explicit unmap/close cleanup", + "cleanup depends on an unproven child termination path" + ], + "id": "payload_mapping_args_sockets_pipes", + "scope": "payload_child" + }, + { + "blocker": false, + "classification": "PAYLOAD_PROCESS_LOCAL", + "evidence": [ + "sdk/crt/crt.c", + "sdk/crt/patch.c" + ], + "id": "sdk_patch_init", + "scope": "payload_child" + }, + { + "blocker": true, + "classification": "UNBOUNDED_OR_UNKNOWN", + "evidence": [ + "sdk/crt/crt.c payload_terminate may return, call exit, or trap", + "the exact branch for the injected SceSpZeroConf child is not proven" + ], + "id": "sdk_termination_branch", + "scope": "payload_child" + }, + { + "blocker": true, + "classification": "UNBOUNDED_OR_UNKNOWN", + "evidence": [ + "elfldr/elfldr.c:703-710 detaches and returns the PID", + "no 2000 ms watchdog, wait, kill, or retry budget is present" + ], + "id": "payload_runtime_limit", + "scope": "detached_payload_child" + }, + { + "blocker": true, + "classification": "UNBOUNDED_OR_UNKNOWN", + "evidence": [ + "elfldr/socksrv.c:400 ignores SIGCHLD", + "post-detach exit/resource cleanup semantics are not documented" + ], + "id": "child_reaping", + "scope": "elfldr_service_process" + }, + { + "blocker": true, + "classification": "UNBOUNDED_OR_UNKNOWN", + "evidence": [ + "pldmgr/http_server.c resolves a path then calls ps5_launch_elf", + "pldmgr/ps5_launcher.c streams bytes without calculating or checking SHA-256" + ], + "id": "payload_manager_launch_hash_binding", + "scope": "payload_manager_to_elfldr" + }, + { + "blocker": true, + "classification": "PERSISTENT_WRITE", + "evidence": [ + "pldmgr/http_server.c writes /data/pldmgr/payloads/.tmp", + "the upload is committed into payload storage" + ], + "id": "payload_manager_upload", + "scope": "payload_manager_storage" + } + ], + "firmware": { + "device_attested": false, + "evidence": "user_provided_only", + "exact": "9.60" + }, + "hard_blockers": [ + "child_reaping", + "elfldr_first_stage_privilege_restore", + "exact_exploit_and_autoloader_identity_unproven", + "firmware_9_60_not_independently_device_attested", + "payload_credentials", + "payload_manager_launch_hash_binding", + "payload_manager_upload", + "payload_mapping_args_sockets_pipes", + "payload_runtime_limit", + "ptrace_authid_restore_failure_path", + "ptrace_single_step_completion", + "return_continuation_after_payload_start_unproven", + "sdk_termination_branch" + ], + "identity": { + "elfldr": { + "commit": "699e8bcff03e91e8d6ca6eba281af25c5a58d8c2", + "dirty": false, + "installed_asset_hash_match": true, + "observed_inventory_path": "/data/pldmgr/payloads/elfldr/elfldr_v0.23.elf", + "observed_inventory_sha256": "092d16ee0ede0c494947efd38d1a17bbd7cc4b022d3858ea898833c188c703e8", + "observed_inventory_version": "v0.23", + "release": "v0.23", + "release_asset_sha256": "092d16ee0ede0c494947efd38d1a17bbd7cc4b022d3858ea898833c188c703e8", + "release_asset_size": 397000, + "repository": "https://github.com/ps5-payload-dev/elfldr.git" + }, + "exact_exploit_autoloader": { + "identified": false, + "local_candidate": { + "filename": "Y2JB-Autoloader-403-1240.zip", + "sha256": "805e3f87f0c371223619ffc7d3a7b3c0d41a1fae8a8b1171d9e2f162659e8291", + "size": 504159435, + "status": "local_backup_candidate_not_installed_identity_proof" + }, + "status": "UNPROVEN" + }, + "payload_manager": { + "commit": "cfbc70f30f419b09bf2b52283f7409e2d3117ee1", + "dirty": false, + "installed_asset_hash_match": true, + "observed_inventory_path": "/data/pldmgr/payloads/pldmgr/pldmgr_v0.3.1.elf", + "observed_inventory_sha256": "518740adbacccb9094fadb07dd424c53ee290f38306449ccc9d6957fdf813c0b", + "observed_inventory_version": "v0.3.1", + "observed_version_endpoint": "0.3.1", + "release": "v0.3.1", + "release_asset_sha256": "518740adbacccb9094fadb07dd424c53ee290f38306449ccc9d6957fdf813c0b", + "repository": "https://github.com/itsPLK/ps5-payload-manager.git" + }, + "sdk": { + "commit": "d2e2e585740362976a39fdd5ccf390f199a7bc37", + "dirty": false, + "release": "v0.41", + "repository": "https://github.com/ps5-payload-dev/sdk.git" + } + }, + "no_console_actions": { + "elf_executed": false, + "elf_transferred": false, + "gnm": false, + "raw_port_9021_contacted": false, + "rendering": false, + "videoout": false + }, + "observation_scope": { + "date": "2026-07-17", + "payload_manager_routes": [ + "/autoload_status", + "/get_config", + "/list_payloads", + "/log", + "/sources_list", + "/version" + ], + "payload_manager_routes_read_only": true, + "strict_status_port_744_result": "ECONNREFUSED" + }, + "phase": "0.6", + "schema_version": 1, + "source_evidence": { + "elfldr": [ + { + "normalization": "lf", + "path": "work/upstream/elfldr-v0.23/main.c", + "sha256": "51176f0c864af2eb269dff37a7148ec31f72cfa593e7edf85df04685d9639fef", + "size": 2792 + }, + { + "normalization": "lf", + "path": "work/upstream/elfldr-v0.23/elfldr.c", + "sha256": "2c6ec9f00b346e23bc9fff6b1077072dce7309b59536f420e8578aac9a95fc03", + "size": 18071 + }, + { + "normalization": "lf", + "path": "work/upstream/elfldr-v0.23/elfldr.h", + "sha256": "48c6840304bf914003d1c9f54efbde7b6ee6317692ddce47443d0c0abf73fe08", + "size": 1260 + }, + { + "normalization": "lf", + "path": "work/upstream/elfldr-v0.23/pt.c", + "sha256": "8c87c76850587e671b8ad49f1f0e98489a7b1f4bf789da84bcfbb21918d6b046", + "size": 7489 + }, + { + "normalization": "lf", + "path": "work/upstream/elfldr-v0.23/pt.h", + "sha256": "09a51aef9723f5314b29175875410e55274fa5b4e6708ef7d76599712ae387e4", + "size": 2391 + }, + { + "normalization": "lf", + "path": "work/upstream/elfldr-v0.23/socksrv.c", + "sha256": "6cc6887969754befa7424da981ea2cc778940c0d45e7bcc7abd99ec1ec39522e", + "size": 8385 + }, + { + "normalization": "lf", + "path": "work/upstream/elfldr-v0.23/Makefile", + "sha256": "468ac60791fb09885face7d383d0cf43b0fd7afde1c20fb0984d0bb4999fc6fc", + "size": 1592 + } + ], + "payload_manager": [ + { + "normalization": "lf", + "path": "work/upstream/pldmgr-v0.3.1/src/ps5_launcher.c", + "sha256": "082836bfd7a9726f62ea0dad8b7be99a31ab30130bf4f1165bf4bf52acd8673c", + "size": 4794 + }, + { + "normalization": "lf", + "path": "work/upstream/pldmgr-v0.3.1/src/http_server.c", + "sha256": "362175cb576bcd1e07209987c42fee2cfe515bdd31d1c8588d531570c2ca5a0c", + "size": 46713 + }, + { + "normalization": "lf", + "path": "work/upstream/pldmgr-v0.3.1/src/autoload.c", + "sha256": "117545fcc3b45e71c97cca2675d772c72fea25523f0ffb66e8b4cf989da32d85", + "size": 9197 + }, + { + "normalization": "lf", + "path": "work/upstream/pldmgr-v0.3.1/src/main.c", + "sha256": "cd1af1f58f6275828c8a392371ef84a1e9e999a5e76345d0fe8bf3f92f7be51a", + "size": 7030 + }, + { + "normalization": "lf", + "path": "work/upstream/pldmgr-v0.3.1/src/payload_mgr.c", + "sha256": "65303c3696626fa20eb2d0e7f4da4c51a43d5bc4bc82904f416e7c3b74cf8fb7", + "size": 18326 + }, + { + "normalization": "lf", + "path": "work/upstream/pldmgr-v0.3.1/include/pldmgr.h", + "sha256": "b1881b943e38a1b8ecdc53e254b0187a011335e5117a36d0ab00e889cb8cb6f5", + "size": 2450 + }, + { + "normalization": "lf", + "path": "work/upstream/pldmgr-v0.3.1/Makefile", + "sha256": "6d02de74a46c104da3cd373d51911ebea1137190fe61bc6529403cbac5108004", + "size": 3675 + } + ], + "sdk": [ + { + "normalization": "lf", + "path": "work/upstream/sdk/crt/crt.c", + "sha256": "2695ae6cf23f867849590977e47edfd7743caa8f34255e6de9aa9eae9a4e4ea6", + "size": 4462 + }, + { + "normalization": "lf", + "path": "work/upstream/sdk/crt/patch.c", + "sha256": "a9cf1814874f3c2c4b337a23270a80ff159b2e6247f9781018bbe1def1845deb", + "size": 2184 + }, + { + "normalization": "lf", + "path": "work/upstream/sdk/crt/kernel.c", + "sha256": "10d2868647b684d7a7f20e029b6a8e792725fa484196a6e00e21c4507469fd12", + "size": 37992 + }, + { + "normalization": "lf", + "path": "work/upstream/sdk/include/ps5/payload.h", + "sha256": "752279ee971eee0dc1eda7286a0008caaf7cd34a99c7501d054d40866c0b8fdf", + "size": 1304 + }, + { + "normalization": "lf", + "path": "work/upstream/sdk/crt/Makefile", + "sha256": "4b4d8856ee05b751888bdc70e7ff146692ced7755644c140ff4a02a3cd154512", + "size": 1596 + } + ] + } +} diff --git a/manifests/runtime/phase-0.7-kernelwrite-proof-matrix.json b/manifests/runtime/phase-0.7-kernelwrite-proof-matrix.json new file mode 100644 index 0000000..e938791 --- /dev/null +++ b/manifests/runtime/phase-0.7-kernelwrite-proof-matrix.json @@ -0,0 +1,174 @@ +{ + "artifacts": { + "lifecycle": { + "sha256": "bfb4a5cc768e162fe4c2fddf41c3978e152722918a39085277fd172cb95a7182", + "size": 112680, + "source_commit": "fe08300339a13f899fb78ea404ada381a5cba87c" + }, + "loader": { + "sha256": "63e810982471eb40cae3a20aa9df9a0a02892f420e429874fae4e99aa400b561", + "size": 397000, + "source_commit": "197623058f509eddde18868dafcb92fdcac66464" + }, + "payload_manager": { + "sha256": "8fecf8241a46246eddbd21e8bb4d875f5d76f1f4f4c6a11384df1f131aa5e5b1", + "size": 99560, + "source_commit": "e23d94ff91233aa770e2342800c1467875bdef44" + } + }, + "decision": "READY_FOR_HARDENED_RUNTIME_DEPLOYMENT", + "firmware": "9.60", + "kernelwrite_free_claim": false, + "no_ps5_actions": { + "connected": false, + "executed": false, + "installed": false, + "transferred": false + }, + "reviews": [ + { + "basis": [ + "historical loader contract audit" + ], + "classification": "HISTORICAL_BLOCKED_TARGET", + "component": "freestanding_startup", + "status": "UNPROVEN" + }, + { + "basis": [ + "SDK source", + "lifecycle linker map", + "lifecycle disassembly", + "reachable callgraph" + ], + "classification": "PAYLOAD_PROCESS_LOCAL", + "component": "sdk_patch_init", + "status": "UNSAFE" + }, + { + "basis": [ + "SDK source", + "lifecycle linker map", + "static sensitive-symbol inventory", + "external child watchdog" + ], + "classification": "PAYLOAD_PROCESS_LOCAL", + "component": "sdk_rtld_module_loading", + "status": "UNPROVEN" + }, + { + "basis": [ + "loader source", + "deadline and step-budget tests", + "loader disassembly" + ], + "classification": "BOUNDED_WATCHDOG", + "component": "ptrace_step_completion", + "status": "SAFE" + }, + { + "basis": [ + "loader source", + "credential restore failure-injection tests", + "loader callgraph" + ], + "classification": "RESTORED_BY_LOADER", + "component": "five_field_credential_restore", + "status": "SAFE" + }, + { + "basis": [ + "loader source", + "cleanup state tests", + "loader callgraph" + ], + "classification": "FAIL_CLOSED_TERMINATION", + "component": "credential_restore_failure", + "status": "SAFE" + }, + { + "basis": [ + "loader source", + "loader disassembly" + ], + "classification": "RESTORED_BY_LOADER", + "component": "breakpoint_byte_and_page", + "status": "SAFE" + }, + { + "basis": [ + "loader source", + "watchdog tests", + "loader callgraph" + ], + "classification": "BOUNDED_WATCHDOG", + "component": "payload_child_lifetime_and_reap", + "status": "SAFE" + }, + { + "basis": [ + "manager source", + "symlink truncation mismatch TOCTOU and fuzz tests", + "manager disassembly" + ], + "classification": "HASH_BOUND_SAME_FD", + "component": "manager_hash_to_stream", + "status": "SAFE" + }, + { + "basis": [ + "loader source", + "denylist and digest negative tests", + "loader disassembly" + ], + "classification": "FAIL_CLOSED_TERMINATION", + "component": "receiver_hash_and_denylist", + "status": "SAFE" + }, + { + "basis": [ + "manager source", + "upload negative tests", + "manager callgraph" + ], + "classification": "ALLOWED_APPLICATION_WRITE", + "component": "controlled_artifact_directory_upload", + "status": "SAFE" + }, + { + "basis": [ + "source scan", + "imports", + "DT_NEEDED", + "linker maps", + "disassembly" + ], + "classification": "PROHIBITED_OPERATION_ABSENT", + "component": "gnm_videoout_sdl_mmio_firmware_mutation", + "status": "SAFE" + }, + { + "basis": [ + "no console action occurred" + ], + "classification": "NO_RUNTIME_EVIDENCE", + "component": "firmware_9_60_runtime_behavior", + "status": "UNPROVEN" + }, + { + "basis": [ + "artifact manifests", + "controlled runtime profile", + "offline audit" + ], + "classification": "UNAUTHORIZED_ACTION", + "component": "installation_transfer_execution", + "status": "UNPROVEN" + } + ], + "schema_version": 1, + "sdk": { + "commit": "d2e2e585740362976a39fdd5ccf390f199a7bc37", + "release": "v0.41" + } +} diff --git a/manifests/runtime/phase-0.7-offline-audit.json b/manifests/runtime/phase-0.7-offline-audit.json new file mode 100644 index 0000000..4594455 --- /dev/null +++ b/manifests/runtime/phase-0.7-offline-audit.json @@ -0,0 +1,12457 @@ +{ + "artifacts": { + "lifecycle": { + "byte_identical_clean_builds": true, + "call_edges": [ + { + "address": "0x26", + "from": "main", + "indirect": true, + "target": "*%rax" + }, + { + "address": "0x3d", + "from": "main", + "indirect": true, + "target": "*%rax" + }, + { + "address": "0xd5", + "from": "_start", + "indirect": false, + "target": "__crt_syscall_init" + }, + { + "address": "0xe2", + "from": "_start", + "indirect": false, + "target": "__kernel_init" + }, + { + "address": "0xec", + "from": "_start", + "indirect": false, + "target": "__klog_init" + }, + { + "address": "0x10d", + "from": "_start", + "indirect": false, + "target": "kernel_dynlib_dlsym" + }, + { + "address": "0x11e", + "from": "_start", + "indirect": false, + "target": "__patch_init" + }, + { + "address": "0x13d", + "from": "_start", + "indirect": false, + "target": "__rtld_init" + }, + { + "address": "0x155", + "from": "_start", + "indirect": false, + "target": "klog_puts" + }, + { + "address": "0x1b2", + "from": "_start", + "indirect": false, + "target": "kernel_dynlib_dlsym" + }, + { + "address": "0x1d1", + "from": "_start", + "indirect": false, + "target": "kernel_dynlib_dlsym" + }, + { + "address": "0x1f0", + "from": "_start", + "indirect": false, + "target": "kernel_dynlib_dlsym" + }, + { + "address": "0x20f", + "from": "_start", + "indirect": false, + "target": "kernel_dynlib_dlsym" + }, + { + "address": "0x21d", + "from": "_start", + "indirect": true, + "target": "*%rbx" + }, + { + "address": "0x222", + "from": "_start", + "indirect": true, + "target": "*%r15" + }, + { + "address": "0x240", + "from": "_start", + "indirect": false, + "target": "kernel_dynlib_dlsym" + }, + { + "address": "0x25f", + "from": "_start", + "indirect": false, + "target": "kernel_dynlib_dlsym" + }, + { + "address": "0x27f", + "from": "_start", + "indirect": false, + "target": "kernel_dynlib_dlsym" + }, + { + "address": "0x29b", + "from": "_start", + "indirect": false, + "target": "kernel_dynlib_dlsym" + }, + { + "address": "0x2b5", + "from": "_start", + "indirect": false, + "target": "__rtld_payload_new" + }, + { + "address": "0x2c6", + "from": "_start", + "indirect": false, + "target": "__rtld_dlfcn_setroot" + }, + { + "address": "0x2cf", + "from": "_start", + "indirect": false, + "target": "__rtld_lib_open" + }, + { + "address": "0x2ea", + "from": "_start", + "indirect": false, + "target": "__rtld_lib_init" + }, + { + "address": "0x2fa", + "from": "_start", + "indirect": false, + "target": "__rtld_lib_close" + }, + { + "address": "0x303", + "from": "_start", + "indirect": false, + "target": "__rtld_lib_destroy" + }, + { + "address": "0x32d", + "from": "_start", + "indirect": false, + "target": "kernel_dynlib_dlsym" + }, + { + "address": "0x352", + "from": "_start", + "indirect": false, + "target": "main" + }, + { + "address": "0x368", + "from": "_start", + "indirect": false, + "target": "__rtld_lib_fini" + }, + { + "address": "0x373", + "from": "_start", + "indirect": false, + "target": "__rtld_lib_close" + }, + { + "address": "0x37f", + "from": "_start", + "indirect": false, + "target": "__rtld_lib_destroy" + }, + { + "address": "0x3ae", + "from": "_start", + "indirect": false, + "target": "kernel_dynlib_dlsym" + }, + { + "address": "0x3c6", + "from": "_start", + "indirect": true, + "target": "*%rax" + }, + { + "address": "0x3e7", + "from": "__crt_syscall", + "indirect": true, + "target": "ptr_syscall" + }, + { + "address": "0x411", + "from": "__crt_syscall_init", + "indirect": true, + "target": "*(%rbx)" + }, + { + "address": "0x427", + "from": "__crt_syscall_init", + "indirect": true, + "target": "*(%rbx)" + }, + { + "address": "0x44e", + "from": "__crt_syscall_init", + "indirect": true, + "target": "*%rax" + }, + { + "address": "0x467", + "from": "__crt_syscall_init", + "indirect": true, + "target": "*(%rbx)" + }, + { + "address": "0x5bc", + "from": "klog_printf", + "indirect": true, + "target": "vsnprintf" + }, + { + "address": "0x5d7", + "from": "klog_printf", + "indirect": true, + "target": "*%r15" + }, + { + "address": "0x5f3", + "from": "klog_printf", + "indirect": true, + "target": "*%r15" + }, + { + "address": "0x610", + "from": "klog_printf", + "indirect": true, + "target": "snprintf" + }, + { + "address": "0x639", + "from": "klog_printf", + "indirect": true, + "target": "*%r14" + }, + { + "address": "0x64d", + "from": "klog_printf", + "indirect": false, + "target": "__crt_syscall" + }, + { + "address": "0x69a", + "from": "klog_puts", + "indirect": true, + "target": "*%r12" + }, + { + "address": "0x6b7", + "from": "klog_puts", + "indirect": true, + "target": "*%r12" + }, + { + "address": "0x6d5", + "from": "klog_puts", + "indirect": true, + "target": "snprintf" + }, + { + "address": "0x6fa", + "from": "klog_puts", + "indirect": true, + "target": "*%r15" + }, + { + "address": "0x70e", + "from": "klog_puts", + "indirect": false, + "target": "__crt_syscall" + }, + { + "address": "0x75a", + "from": "klog_perror", + "indirect": true, + "target": "*%r12" + }, + { + "address": "0x777", + "from": "klog_perror", + "indirect": true, + "target": "*%r12" + }, + { + "address": "0x795", + "from": "klog_perror", + "indirect": true, + "target": "snprintf" + }, + { + "address": "0x7a2", + "from": "klog_perror", + "indirect": true, + "target": "__error" + }, + { + "address": "0x7aa", + "from": "klog_perror", + "indirect": true, + "target": "*%r14" + }, + { + "address": "0x7cf", + "from": "klog_perror", + "indirect": true, + "target": "*%r15" + }, + { + "address": "0x7e3", + "from": "klog_perror", + "indirect": false, + "target": "__crt_syscall" + }, + { + "address": "0x81c", + "from": "__klog_init", + "indirect": false, + "target": "kernel_dynlib_dlsym" + }, + { + "address": "0x843", + "from": "__klog_init", + "indirect": false, + "target": "kernel_dynlib_dlsym" + }, + { + "address": "0x866", + "from": "__klog_init", + "indirect": false, + "target": "kernel_dynlib_dlsym" + }, + { + "address": "0x889", + "from": "__klog_init", + "indirect": false, + "target": "kernel_dynlib_dlsym" + }, + { + "address": "0x8ac", + "from": "__klog_init", + "indirect": false, + "target": "kernel_dynlib_dlsym" + }, + { + "address": "0x969", + "from": "nid_encode", + "indirect": false, + "target": "SHA1Transform" + }, + { + "address": "0x99a", + "from": "nid_encode", + "indirect": false, + "target": "SHA1Transform" + }, + { + "address": "0xa4b", + "from": "nid_encode", + "indirect": false, + "target": "SHA1Transform" + }, + { + "address": "0xb1d", + "from": "nid_encode", + "indirect": false, + "target": "SHA1Transform" + }, + { + "address": "0xbb0", + "from": "nid_encode", + "indirect": false, + "target": "SHA1Transform" + }, + { + "address": "0xd17", + "from": "nid_encode", + "indirect": false, + "target": "SHA1Transform" + }, + { + "address": "0x2175", + "from": "kernel_get_fw_version", + "indirect": false, + "target": "__crt_syscall" + }, + { + "address": "0x224a", + "from": "__kernel_init", + "indirect": false, + "target": "__crt_syscall" + }, + { + "address": "0x2c91", + "from": "__kernel_init", + "indirect": false, + "target": "kernel_get_proc" + }, + { + "address": "0x2cab", + "from": "__kernel_init", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x2cd1", + "from": "__kernel_init", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x2cfa", + "from": "__kernel_init", + "indirect": false, + "target": "nid_encode" + }, + { + "address": "0x2d0d", + "from": "__kernel_init", + "indirect": false, + "target": "kernel_dynlib_resolve" + }, + { + "address": "0x2d28", + "from": "__kernel_init", + "indirect": false, + "target": "nid_encode" + }, + { + "address": "0x2d3b", + "from": "__kernel_init", + "indirect": false, + "target": "kernel_dynlib_resolve" + }, + { + "address": "0x2da8", + "from": "kernel_get_ucred_prison", + "indirect": false, + "target": "kernel_get_proc" + }, + { + "address": "0x2dc2", + "from": "kernel_get_ucred_prison", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x2dea", + "from": "kernel_get_ucred_prison", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x2e2c", + "from": "kernel_dynlib_dlsym", + "indirect": false, + "target": "nid_encode" + }, + { + "address": "0x2e3a", + "from": "kernel_dynlib_dlsym", + "indirect": false, + "target": "kernel_dynlib_resolve" + }, + { + "address": "0x2ea8", + "from": "kernel_copyin", + "indirect": true, + "target": "*%rax" + }, + { + "address": "0x2ec4", + "from": "kernel_copyin", + "indirect": true, + "target": "*%rax" + }, + { + "address": "0x2f05", + "from": "kernel_copyin", + "indirect": false, + "target": "__crt_syscall" + }, + { + "address": "0x2f47", + "from": "kernel_copyin", + "indirect": false, + "target": "__crt_syscall" + }, + { + "address": "0x2f8b", + "from": "kernel_copyin", + "indirect": true, + "target": "*%rax" + }, + { + "address": "0x2fbf", + "from": "kernel_copyin", + "indirect": false, + "target": "__crt_syscall" + }, + { + "address": "0x2fef", + "from": "kernel_copyin", + "indirect": false, + "target": "__crt_syscall" + }, + { + "address": "0x3011", + "from": "kernel_copyin", + "indirect": false, + "target": "__crt_syscall" + }, + { + "address": "0x308b", + "from": "kernel_copyout", + "indirect": true, + "target": "*%rax" + }, + { + "address": "0x30a7", + "from": "kernel_copyout", + "indirect": true, + "target": "*%rax" + }, + { + "address": "0x30e8", + "from": "kernel_copyout", + "indirect": false, + "target": "__crt_syscall" + }, + { + "address": "0x312a", + "from": "kernel_copyout", + "indirect": false, + "target": "__crt_syscall" + }, + { + "address": "0x316e", + "from": "kernel_copyout", + "indirect": true, + "target": "*%rax" + }, + { + "address": "0x31a2", + "from": "kernel_copyout", + "indirect": false, + "target": "__crt_syscall" + }, + { + "address": "0x31d2", + "from": "kernel_copyout", + "indirect": false, + "target": "__crt_syscall" + }, + { + "address": "0x31f4", + "from": "kernel_copyout", + "indirect": false, + "target": "__crt_syscall" + }, + { + "address": "0x322b", + "from": "kernel_setlong", + "indirect": false, + "target": "kernel_copyin" + }, + { + "address": "0x325a", + "from": "kernel_setint", + "indirect": false, + "target": "kernel_copyin" + }, + { + "address": "0x328b", + "from": "kernel_setshort", + "indirect": false, + "target": "kernel_copyin" + }, + { + "address": "0x32bb", + "from": "kernel_setchar", + "indirect": false, + "target": "kernel_copyin" + }, + { + "address": "0x32e9", + "from": "kernel_getlong", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x3318", + "from": "kernel_getint", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x3347", + "from": "kernel_getshort", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x3375", + "from": "kernel_getchar", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x33d6", + "from": "kernel_set_qaflags", + "indirect": false, + "target": "__crt_syscall" + }, + { + "address": "0x33f3", + "from": "kernel_set_qaflags", + "indirect": false, + "target": "kernel_copyin" + }, + { + "address": "0x3422", + "from": "kernel_set_qaflags", + "indirect": true, + "target": "*%rcx" + }, + { + "address": "0x3463", + "from": "kernel_get_root_vnode", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x34bf", + "from": "kernel_get_proc", + "indirect": false, + "target": "__crt_syscall" + }, + { + "address": "0x352f", + "from": "kernel_get_proc", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x356f", + "from": "kernel_get_proc", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x3589", + "from": "kernel_get_proc", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x35ea", + "from": "kernel_get_proc", + "indirect": true, + "target": "*%rax" + }, + { + "address": "0x360e", + "from": "kernel_get_proc_thread", + "indirect": false, + "target": "kernel_get_proc" + }, + { + "address": "0x3630", + "from": "kernel_get_proc_thread", + "indirect": false, + "target": "__crt_syscall" + }, + { + "address": "0x3651", + "from": "kernel_get_proc_thread", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x3687", + "from": "kernel_get_proc_thread", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x36ab", + "from": "kernel_get_proc_thread", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x36c5", + "from": "kernel_get_proc_thread", + "indirect": true, + "target": "*%rax" + }, + { + "address": "0x36f4", + "from": "kernel_dynlib_obj", + "indirect": false, + "target": "kernel_get_proc" + }, + { + "address": "0x3719", + "from": "kernel_dynlib_obj", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x373c", + "from": "kernel_dynlib_obj", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x375a", + "from": "kernel_dynlib_obj", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x3774", + "from": "kernel_dynlib_obj", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x378c", + "from": "kernel_dynlib_obj", + "indirect": true, + "target": "*%rax" + }, + { + "address": "0x37c7", + "from": "kernel_dynlib_find_handle", + "indirect": false, + "target": "kernel_get_proc" + }, + { + "address": "0x37ec", + "from": "kernel_dynlib_find_handle", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x3806", + "from": "kernel_dynlib_find_handle", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x383c", + "from": "kernel_dynlib_find_handle", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x385e", + "from": "kernel_dynlib_find_handle", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x3877", + "from": "kernel_dynlib_find_handle", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x38a3", + "from": "kernel_dynlib_find_handle", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x38c5", + "from": "kernel_dynlib_find_handle", + "indirect": true, + "target": "*%rax" + }, + { + "address": "0x38fa", + "from": "kernel_dynlib_handle", + "indirect": false, + "target": "kernel_get_proc" + }, + { + "address": "0x391f", + "from": "kernel_dynlib_handle", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x3948", + "from": "kernel_dynlib_handle", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x398f", + "from": "kernel_dynlib_handle", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x39b6", + "from": "kernel_dynlib_handle", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x39d3", + "from": "kernel_dynlib_handle", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x39f1", + "from": "kernel_dynlib_handle", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x3a8b", + "from": "kernel_dynlib_handle", + "indirect": true, + "target": "*%rax" + }, + { + "address": "0x3ad2", + "from": "kernel_dynlib_fini_addr", + "indirect": false, + "target": "kernel_dynlib_obj" + }, + { + "address": "0x3b1b", + "from": "kernel_dynlib_path", + "indirect": false, + "target": "kernel_dynlib_obj" + }, + { + "address": "0x3b37", + "from": "kernel_dynlib_path", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x3b72", + "from": "kernel_dynlib_init_addr", + "indirect": false, + "target": "kernel_dynlib_obj" + }, + { + "address": "0x3ba2", + "from": "kernel_dynlib_entry_addr", + "indirect": false, + "target": "kernel_dynlib_obj" + }, + { + "address": "0x3bd2", + "from": "kernel_dynlib_mapbase_addr", + "indirect": false, + "target": "kernel_dynlib_obj" + }, + { + "address": "0x3c0e", + "from": "kernel_dynlib_resolve", + "indirect": false, + "target": "kernel_dynlib_obj" + }, + { + "address": "0x3c42", + "from": "kernel_dynlib_resolve", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x3c81", + "from": "kernel_dynlib_resolve", + "indirect": false, + "target": "__crt_syscall" + }, + { + "address": "0x3ca4", + "from": "kernel_dynlib_resolve", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x3cc4", + "from": "kernel_dynlib_resolve", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x3cf9", + "from": "kernel_dynlib_resolve", + "indirect": false, + "target": "__crt_syscall" + }, + { + "address": "0x3d80", + "from": "kernel_get_proc_ucred", + "indirect": false, + "target": "kernel_get_proc" + }, + { + "address": "0x3d9a", + "from": "kernel_get_proc_ucred", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x3dd8", + "from": "kernel_get_ucred_authid", + "indirect": false, + "target": "kernel_get_proc" + }, + { + "address": "0x3df2", + "from": "kernel_get_ucred_authid", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x3e1a", + "from": "kernel_get_ucred_authid", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x3e54", + "from": "kernel_set_ucred_prison", + "indirect": false, + "target": "kernel_get_proc" + }, + { + "address": "0x3e6e", + "from": "kernel_set_ucred_prison", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x3e9b", + "from": "kernel_set_ucred_prison", + "indirect": false, + "target": "kernel_copyin" + }, + { + "address": "0x3ec4", + "from": "kernel_set_ucred_authid", + "indirect": false, + "target": "kernel_get_proc" + }, + { + "address": "0x3ede", + "from": "kernel_set_ucred_authid", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x3f0b", + "from": "kernel_set_ucred_authid", + "indirect": false, + "target": "kernel_copyin" + }, + { + "address": "0x3f31", + "from": "kernel_get_ucred_caps", + "indirect": false, + "target": "kernel_get_proc" + }, + { + "address": "0x3f4b", + "from": "kernel_get_ucred_caps", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x3f77", + "from": "kernel_get_ucred_caps", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x3fa1", + "from": "kernel_set_ucred_caps", + "indirect": false, + "target": "kernel_get_proc" + }, + { + "address": "0x3fbb", + "from": "kernel_set_ucred_caps", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x3fe7", + "from": "kernel_set_ucred_caps", + "indirect": false, + "target": "kernel_copyin" + }, + { + "address": "0x4011", + "from": "kernel_get_ucred_attrs", + "indirect": false, + "target": "kernel_get_proc" + }, + { + "address": "0x402b", + "from": "kernel_get_ucred_attrs", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x4057", + "from": "kernel_get_ucred_attrs", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x4081", + "from": "kernel_set_ucred_attrs", + "indirect": false, + "target": "kernel_get_proc" + }, + { + "address": "0x409b", + "from": "kernel_set_ucred_attrs", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x40c7", + "from": "kernel_set_ucred_attrs", + "indirect": false, + "target": "kernel_copyin" + }, + { + "address": "0x40f7", + "from": "kernel_get_ucred_uid", + "indirect": false, + "target": "kernel_get_proc" + }, + { + "address": "0x4111", + "from": "kernel_get_ucred_uid", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x413e", + "from": "kernel_get_ucred_uid", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x4163", + "from": "kernel_set_ucred_uid", + "indirect": false, + "target": "kernel_get_proc" + }, + { + "address": "0x417d", + "from": "kernel_set_ucred_uid", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x41aa", + "from": "kernel_set_ucred_uid", + "indirect": false, + "target": "kernel_copyin" + }, + { + "address": "0x41d7", + "from": "kernel_get_ucred_ruid", + "indirect": false, + "target": "kernel_get_proc" + }, + { + "address": "0x41f1", + "from": "kernel_get_ucred_ruid", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x421e", + "from": "kernel_get_ucred_ruid", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x4243", + "from": "kernel_set_ucred_ruid", + "indirect": false, + "target": "kernel_get_proc" + }, + { + "address": "0x425d", + "from": "kernel_set_ucred_ruid", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x428a", + "from": "kernel_set_ucred_ruid", + "indirect": false, + "target": "kernel_copyin" + }, + { + "address": "0x42b7", + "from": "kernel_get_ucred_svuid", + "indirect": false, + "target": "kernel_get_proc" + }, + { + "address": "0x42d1", + "from": "kernel_get_ucred_svuid", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x42fe", + "from": "kernel_get_ucred_svuid", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x4323", + "from": "kernel_set_ucred_svuid", + "indirect": false, + "target": "kernel_get_proc" + }, + { + "address": "0x433d", + "from": "kernel_set_ucred_svuid", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x436a", + "from": "kernel_set_ucred_svuid", + "indirect": false, + "target": "kernel_copyin" + }, + { + "address": "0x4397", + "from": "kernel_get_ucred_rgid", + "indirect": false, + "target": "kernel_get_proc" + }, + { + "address": "0x43b1", + "from": "kernel_get_ucred_rgid", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x43de", + "from": "kernel_get_ucred_rgid", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x4403", + "from": "kernel_set_ucred_rgid", + "indirect": false, + "target": "kernel_get_proc" + }, + { + "address": "0x441d", + "from": "kernel_set_ucred_rgid", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x444a", + "from": "kernel_set_ucred_rgid", + "indirect": false, + "target": "kernel_copyin" + }, + { + "address": "0x4477", + "from": "kernel_get_ucred_svgid", + "indirect": false, + "target": "kernel_get_proc" + }, + { + "address": "0x4491", + "from": "kernel_get_ucred_svgid", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x44be", + "from": "kernel_get_ucred_svgid", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x44e3", + "from": "kernel_set_ucred_svgid", + "indirect": false, + "target": "kernel_get_proc" + }, + { + "address": "0x44fd", + "from": "kernel_set_ucred_svgid", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x452a", + "from": "kernel_set_ucred_svgid", + "indirect": false, + "target": "kernel_copyin" + }, + { + "address": "0x4550", + "from": "kernel_get_proc_filedesc", + "indirect": false, + "target": "kernel_get_proc" + }, + { + "address": "0x456a", + "from": "kernel_get_proc_filedesc", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x45a2", + "from": "kernel_get_vmem_entry", + "indirect": false, + "target": "kernel_get_proc" + }, + { + "address": "0x45be", + "from": "kernel_get_vmem_entry", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x45de", + "from": "kernel_get_vmem_entry", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x461c", + "from": "kernel_get_vmem_entry", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x4635", + "from": "kernel_get_vmem_entry", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x466c", + "from": "kernel_get_vmem_entry", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x4692", + "from": "kernel_get_vmem_entry", + "indirect": true, + "target": "*%rax" + }, + { + "address": "0x46b7", + "from": "kernel_get_vmem_protection", + "indirect": false, + "target": "kernel_get_vmem_entry" + }, + { + "address": "0x46f4", + "from": "kernel_get_vmem_protection", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x471e", + "from": "kernel_get_vmem_protection", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x4754", + "from": "kernel_get_vmem_protection", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x47b5", + "from": "kernel_get_vmem_protection", + "indirect": true, + "target": "*%rax" + }, + { + "address": "0x47f2", + "from": "kernel_set_vmem_protection", + "indirect": false, + "target": "kernel_get_vmem_entry" + }, + { + "address": "0x4839", + "from": "kernel_set_vmem_protection", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x486b", + "from": "kernel_set_vmem_protection", + "indirect": false, + "target": "kernel_copyin" + }, + { + "address": "0x4885", + "from": "kernel_set_vmem_protection", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x48ab", + "from": "kernel_set_vmem_protection", + "indirect": true, + "target": "*%rax" + }, + { + "address": "0x48eb", + "from": "kernel_get_proc_file", + "indirect": false, + "target": "kernel_get_proc" + }, + { + "address": "0x4905", + "from": "kernel_get_proc_file", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x491b", + "from": "kernel_get_proc_file", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x493b", + "from": "kernel_get_proc_file", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x4951", + "from": "kernel_get_proc_file", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x4987", + "from": "kernel_overlap_sockets", + "indirect": false, + "target": "kernel_get_proc_file" + }, + { + "address": "0x49a0", + "from": "kernel_overlap_sockets", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x49ca", + "from": "kernel_overlap_sockets", + "indirect": false, + "target": "kernel_copyin" + }, + { + "address": "0x49df", + "from": "kernel_overlap_sockets", + "indirect": false, + "target": "kernel_get_proc_file" + }, + { + "address": "0x49f9", + "from": "kernel_overlap_sockets", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x4a14", + "from": "kernel_overlap_sockets", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x4a39", + "from": "kernel_overlap_sockets", + "indirect": false, + "target": "kernel_get_proc_file" + }, + { + "address": "0x4a56", + "from": "kernel_overlap_sockets", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x4a72", + "from": "kernel_overlap_sockets", + "indirect": false, + "target": "kernel_copyin" + }, + { + "address": "0x4a85", + "from": "kernel_overlap_sockets", + "indirect": false, + "target": "kernel_get_proc_file" + }, + { + "address": "0x4a9f", + "from": "kernel_overlap_sockets", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x4aba", + "from": "kernel_overlap_sockets", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x4ae9", + "from": "kernel_overlap_sockets", + "indirect": false, + "target": "kernel_copyin" + }, + { + "address": "0x4b15", + "from": "kernel_overlap_sockets", + "indirect": false, + "target": "kernel_copyin" + }, + { + "address": "0x4b48", + "from": "kernel_get_proc_rootdir", + "indirect": false, + "target": "kernel_get_proc" + }, + { + "address": "0x4b62", + "from": "kernel_get_proc_rootdir", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x4b8a", + "from": "kernel_get_proc_rootdir", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x4bc8", + "from": "kernel_get_proc_jaildir", + "indirect": false, + "target": "kernel_get_proc" + }, + { + "address": "0x4be2", + "from": "kernel_get_proc_jaildir", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x4c0a", + "from": "kernel_get_proc_jaildir", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x4c45", + "from": "kernel_set_proc_rootdir", + "indirect": false, + "target": "kernel_get_proc" + }, + { + "address": "0x4c61", + "from": "kernel_set_proc_rootdir", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x4c89", + "from": "kernel_set_proc_rootdir", + "indirect": false, + "target": "kernel_copyin" + }, + { + "address": "0x4cb5", + "from": "kernel_set_proc_jaildir", + "indirect": false, + "target": "kernel_get_proc" + }, + { + "address": "0x4cd1", + "from": "kernel_set_proc_jaildir", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x4cf9", + "from": "kernel_set_proc_jaildir", + "indirect": false, + "target": "kernel_copyin" + }, + { + "address": "0x4d6d", + "from": "__rtld_find_file", + "indirect": false, + "target": "__crt_syscall" + }, + { + "address": "0x4d85", + "from": "__rtld_find_file", + "indirect": true, + "target": "strcpy" + }, + { + "address": "0x4da2", + "from": "__rtld_find_file", + "indirect": true, + "target": "sprintf" + }, + { + "address": "0x4db9", + "from": "__rtld_find_file", + "indirect": false, + "target": "__crt_syscall" + }, + { + "address": "0x4dda", + "from": "__rtld_find_file", + "indirect": true, + "target": "sprintf" + }, + { + "address": "0x4df1", + "from": "__rtld_find_file", + "indirect": false, + "target": "__crt_syscall" + }, + { + "address": "0x4e12", + "from": "__rtld_find_file", + "indirect": true, + "target": "sprintf" + }, + { + "address": "0x4e29", + "from": "__rtld_find_file", + "indirect": false, + "target": "__crt_syscall" + }, + { + "address": "0x4e4a", + "from": "__rtld_find_file", + "indirect": true, + "target": "sprintf" + }, + { + "address": "0x4e61", + "from": "__rtld_find_file", + "indirect": false, + "target": "__crt_syscall" + }, + { + "address": "0x4e87", + "from": "__rtld_find_file", + "indirect": false, + "target": "__crt_syscall" + }, + { + "address": "0x4e9d", + "from": "__rtld_find_file", + "indirect": true, + "target": "getenv" + }, + { + "address": "0x4ecf", + "from": "__rtld_find_file", + "indirect": true, + "target": "strcat" + }, + { + "address": "0x4ee9", + "from": "__rtld_find_file", + "indirect": true, + "target": "*%r13" + }, + { + "address": "0x4f2b", + "from": "__rtld_find_file", + "indirect": true, + "target": "sprintf" + }, + { + "address": "0x4f42", + "from": "__rtld_find_file", + "indirect": false, + "target": "__crt_syscall" + }, + { + "address": "0x4f67", + "from": "__rtld_find_file", + "indirect": false, + "target": "__crt_syscall" + }, + { + "address": "0x4f88", + "from": "__rtld_find_file", + "indirect": true, + "target": "sprintf" + }, + { + "address": "0x4f9f", + "from": "__rtld_find_file", + "indirect": false, + "target": "__crt_syscall" + }, + { + "address": "0x4fd2", + "from": "__rtld_find_file", + "indirect": true, + "target": "sprintf" + }, + { + "address": "0x4fe9", + "from": "__rtld_find_file", + "indirect": false, + "target": "__crt_syscall" + }, + { + "address": "0x5011", + "from": "__rtld_find_file", + "indirect": true, + "target": "sprintf" + }, + { + "address": "0x5028", + "from": "__rtld_find_file", + "indirect": false, + "target": "__crt_syscall" + }, + { + "address": "0x504c", + "from": "__rtld_find_file", + "indirect": true, + "target": "sprintf" + }, + { + "address": "0x5063", + "from": "__rtld_find_file", + "indirect": false, + "target": "__crt_syscall" + }, + { + "address": "0x5087", + "from": "__rtld_find_file", + "indirect": true, + "target": "sprintf" + }, + { + "address": "0x509e", + "from": "__rtld_find_file", + "indirect": false, + "target": "__crt_syscall" + }, + { + "address": "0x510e", + "from": "__rtld_lib_new", + "indirect": false, + "target": "__rtld_find_file" + }, + { + "address": "0x5121", + "from": "__rtld_lib_new", + "indirect": true, + "target": "strcpy" + }, + { + "address": "0x5131", + "from": "__rtld_lib_new", + "indirect": false, + "target": "__rtld_lib_soname2lib" + }, + { + "address": "0x514c", + "from": "__rtld_lib_new", + "indirect": true, + "target": "calloc" + }, + { + "address": "0x51e1", + "from": "__rtld_lib_new", + "indirect": true, + "target": "strcpy" + }, + { + "address": "0x51f3", + "from": "__rtld_lib_new", + "indirect": true, + "target": "strlen" + }, + { + "address": "0x51ff", + "from": "__rtld_lib_new", + "indirect": true, + "target": "strlen" + }, + { + "address": "0x521d", + "from": "__rtld_lib_new", + "indirect": true, + "target": "strncmp" + }, + { + "address": "0x522d", + "from": "__rtld_lib_new", + "indirect": false, + "target": "__rtld_so_new" + }, + { + "address": "0x5247", + "from": "__rtld_lib_new", + "indirect": false, + "target": "__rtld_sprx_new" + }, + { + "address": "0x5269", + "from": "__rtld_lib_soname2lib", + "indirect": true, + "target": "strcmp" + }, + { + "address": "0x5296", + "from": "__rtld_lib_soname2lib", + "indirect": false, + "target": "__rtld_lib_soname2lib" + }, + { + "address": "0x53ca", + "from": "__rtld_lib_append_dep", + "indirect": true, + "target": "calloc" + }, + { + "address": "0x5471", + "from": "__rtld_lib_remove_dep", + "indirect": true, + "target": "free" + }, + { + "address": "0x54cd", + "from": "__rtld_lib_init", + "indirect": false, + "target": "__rtld_lib_init" + }, + { + "address": "0x554c", + "from": "__rtld_lib_sym2lib", + "indirect": true, + "target": "*0x10(%rdi)" + }, + { + "address": "0x5576", + "from": "__rtld_lib_sym2lib", + "indirect": false, + "target": "__rtld_lib_sym2lib" + }, + { + "address": "0x55d6", + "from": "__rtld_lib_addr2lib", + "indirect": false, + "target": "__rtld_lib_addr2lib" + }, + { + "address": "0x563e", + "from": "__rtld_lib_fini", + "indirect": true, + "target": "*0x20(%rdi)" + }, + { + "address": "0x5663", + "from": "__rtld_lib_fini", + "indirect": false, + "target": "__rtld_lib_fini" + }, + { + "address": "0x56c3", + "from": "__rtld_lib_close", + "indirect": false, + "target": "__rtld_lib_close" + }, + { + "address": "0x56e9", + "from": "__rtld_lib_close", + "indirect": true, + "target": "*0x28(%rbx)" + }, + { + "address": "0x5742", + "from": "__rtld_lib_close", + "indirect": true, + "target": "free" + }, + { + "address": "0x578c", + "from": "__rtld_init", + "indirect": false, + "target": "kernel_dynlib_dlsym" + }, + { + "address": "0x57b3", + "from": "__rtld_init", + "indirect": false, + "target": "kernel_dynlib_dlsym" + }, + { + "address": "0x57da", + "from": "__rtld_init", + "indirect": false, + "target": "kernel_dynlib_dlsym" + }, + { + "address": "0x5801", + "from": "__rtld_init", + "indirect": false, + "target": "kernel_dynlib_dlsym" + }, + { + "address": "0x5828", + "from": "__rtld_init", + "indirect": false, + "target": "kernel_dynlib_dlsym" + }, + { + "address": "0x584f", + "from": "__rtld_init", + "indirect": false, + "target": "kernel_dynlib_dlsym" + }, + { + "address": "0x5876", + "from": "__rtld_init", + "indirect": false, + "target": "kernel_dynlib_dlsym" + }, + { + "address": "0x5899", + "from": "__rtld_init", + "indirect": false, + "target": "kernel_dynlib_dlsym" + }, + { + "address": "0x58bc", + "from": "__rtld_init", + "indirect": false, + "target": "kernel_dynlib_dlsym" + }, + { + "address": "0x58ce", + "from": "__rtld_init", + "indirect": false, + "target": "__rtld_sprx_init" + }, + { + "address": "0x58da", + "from": "__rtld_init", + "indirect": false, + "target": "__rtld_so_init" + }, + { + "address": "0x58e6", + "from": "__rtld_init", + "indirect": false, + "target": "__rtld_payload_init" + }, + { + "address": "0x592a", + "from": "__rtld_so_new", + "indirect": true, + "target": "calloc" + }, + { + "address": "0x5998", + "from": "__rtld_so_new", + "indirect": true, + "target": "strcpy" + }, + { + "address": "0x59d5", + "from": "so_open", + "indirect": false, + "target": "__rtld_find_file" + }, + { + "address": "0x59f7", + "from": "so_open", + "indirect": false, + "target": "__crt_syscall" + }, + { + "address": "0x5a1a", + "from": "so_open", + "indirect": false, + "target": "__crt_syscall" + }, + { + "address": "0x5a36", + "from": "so_open", + "indirect": false, + "target": "__crt_syscall" + }, + { + "address": "0x5a44", + "from": "so_open", + "indirect": true, + "target": "malloc" + }, + { + "address": "0x5a66", + "from": "so_open", + "indirect": false, + "target": "__crt_syscall" + }, + { + "address": "0x5a7f", + "from": "so_open", + "indirect": false, + "target": "__crt_syscall" + }, + { + "address": "0x5a91", + "from": "so_open", + "indirect": false, + "target": "klog_perror" + }, + { + "address": "0x5a9a", + "from": "so_open", + "indirect": true, + "target": "free" + }, + { + "address": "0x5aa9", + "from": "so_open", + "indirect": false, + "target": "klog_perror" + }, + { + "address": "0x5abc", + "from": "so_open", + "indirect": false, + "target": "__crt_syscall" + }, + { + "address": "0x5acb", + "from": "so_open", + "indirect": false, + "target": "klog_perror" + }, + { + "address": "0x5bb7", + "from": "so_open", + "indirect": false, + "target": "__crt_syscall" + }, + { + "address": "0x5c51", + "from": "so_open", + "indirect": true, + "target": "memcpy" + }, + { + "address": "0x5e01", + "from": "so_open", + "indirect": false, + "target": "klog_printf" + }, + { + "address": "0x5e0a", + "from": "so_open", + "indirect": false, + "target": "__rtld_lib_destroy" + }, + { + "address": "0x5e41", + "from": "so_open", + "indirect": false, + "target": "__rtld_lib_new" + }, + { + "address": "0x5e57", + "from": "so_open", + "indirect": false, + "target": "__rtld_lib_open" + }, + { + "address": "0x5e68", + "from": "so_open", + "indirect": false, + "target": "__rtld_lib_append_dep" + }, + { + "address": "0x5f66", + "from": "so_open", + "indirect": false, + "target": "r_glob_dat" + }, + { + "address": "0x5fdc", + "from": "so_open", + "indirect": false, + "target": "__rtld_lib_sym2lib" + }, + { + "address": "0x5fed", + "from": "so_open", + "indirect": false, + "target": "__rtld_lib_sym2addr" + }, + { + "address": "0x6003", + "from": "so_open", + "indirect": false, + "target": "__rtld_lib_sym2lib" + }, + { + "address": "0x6014", + "from": "so_open", + "indirect": false, + "target": "__rtld_lib_sym2addr" + }, + { + "address": "0x6039", + "from": "so_open", + "indirect": true, + "target": "memcpy" + }, + { + "address": "0x606f", + "from": "so_open", + "indirect": true, + "target": "memcpy" + }, + { + "address": "0x609b", + "from": "so_open", + "indirect": false, + "target": "klog_printf" + }, + { + "address": "0x60fd", + "from": "so_open", + "indirect": false, + "target": "klog_printf" + }, + { + "address": "0x611b", + "from": "so_open", + "indirect": false, + "target": "klog_perror" + }, + { + "address": "0x6124", + "from": "so_open", + "indirect": true, + "target": "free" + }, + { + "address": "0x6148", + "from": "so_open", + "indirect": false, + "target": "klog_printf" + }, + { + "address": "0x6223", + "from": "so_open", + "indirect": false, + "target": "__crt_syscall" + }, + { + "address": "0x6230", + "from": "so_open", + "indirect": false, + "target": "klog_perror" + }, + { + "address": "0x626d", + "from": "so_open", + "indirect": false, + "target": "kernel_mprotect" + }, + { + "address": "0x628d", + "from": "so_open", + "indirect": true, + "target": "strcpy" + }, + { + "address": "0x62bc", + "from": "so_open", + "indirect": true, + "target": "*%r13" + }, + { + "address": "0x62f6", + "from": "so_open", + "indirect": false, + "target": "r_glob_dat" + }, + { + "address": "0x6361", + "from": "so_init", + "indirect": true, + "target": "*(%rax,%r13,8)" + }, + { + "address": "0x6430", + "from": "so_sym2addr", + "indirect": true, + "target": "strcmp" + }, + { + "address": "0x6527", + "from": "so_fini", + "indirect": true, + "target": "*(%rax,%r14,8)" + }, + { + "address": "0x6565", + "from": "so_close", + "indirect": true, + "target": "free" + }, + { + "address": "0x6585", + "from": "so_close", + "indirect": false, + "target": "__crt_syscall" + }, + { + "address": "0x65e5", + "from": "so_destroy", + "indirect": true, + "target": "free" + }, + { + "address": "0x6605", + "from": "so_destroy", + "indirect": false, + "target": "__crt_syscall" + }, + { + "address": "0x666c", + "from": "__rtld_so_init", + "indirect": false, + "target": "kernel_dynlib_dlsym" + }, + { + "address": "0x6693", + "from": "__rtld_so_init", + "indirect": false, + "target": "kernel_dynlib_dlsym" + }, + { + "address": "0x66ba", + "from": "__rtld_so_init", + "indirect": false, + "target": "kernel_dynlib_dlsym" + }, + { + "address": "0x66dd", + "from": "__rtld_so_init", + "indirect": false, + "target": "kernel_dynlib_dlsym" + }, + { + "address": "0x6700", + "from": "__rtld_so_init", + "indirect": false, + "target": "kernel_dynlib_dlsym" + }, + { + "address": "0x6723", + "from": "__rtld_so_init", + "indirect": false, + "target": "kernel_dynlib_dlsym" + }, + { + "address": "0x67a8", + "from": "r_glob_dat", + "indirect": false, + "target": "__rtld_lib_sym2lib" + }, + { + "address": "0x67b9", + "from": "r_glob_dat", + "indirect": false, + "target": "__rtld_lib_sym2addr" + }, + { + "address": "0x67ce", + "from": "r_glob_dat", + "indirect": false, + "target": "__rtld_lib_sym2lib" + }, + { + "address": "0x67df", + "from": "r_glob_dat", + "indirect": false, + "target": "__rtld_lib_sym2addr" + }, + { + "address": "0x67fa", + "from": "r_glob_dat", + "indirect": true, + "target": "memcpy" + }, + { + "address": "0x6827", + "from": "r_glob_dat", + "indirect": false, + "target": "klog_printf" + }, + { + "address": "0x686a", + "from": "__rtld_sprx_new", + "indirect": true, + "target": "calloc" + }, + { + "address": "0x68d8", + "from": "__rtld_sprx_new", + "indirect": true, + "target": "strcpy" + }, + { + "address": "0x691c", + "from": "sprx_open", + "indirect": true, + "target": "strcmp" + }, + { + "address": "0x6934", + "from": "sprx_open", + "indirect": true, + "target": "strcmp" + }, + { + "address": "0x694c", + "from": "sprx_open", + "indirect": true, + "target": "strcmp" + }, + { + "address": "0x696c", + "from": "sprx_open", + "indirect": false, + "target": "kernel_dynlib_handle" + }, + { + "address": "0x6984", + "from": "sprx_open", + "indirect": false, + "target": "__rtld_find_file" + }, + { + "address": "0x69d5", + "from": "sprx_open", + "indirect": true, + "target": "strcmp" + }, + { + "address": "0x69e3", + "from": "sprx_open", + "indirect": true, + "target": "sceSysmoduleLoadModuleInternal" + }, + { + "address": "0x69f8", + "from": "sprx_open", + "indirect": false, + "target": "klog_printf" + }, + { + "address": "0x6a1b", + "from": "sprx_open", + "indirect": true, + "target": "sceKernelLoadStartModule" + }, + { + "address": "0x6a41", + "from": "sprx_open", + "indirect": false, + "target": "kernel_dynlib_dlsym" + }, + { + "address": "0x6a77", + "from": "sprx_open", + "indirect": false, + "target": "kernel_dynlib_obj" + }, + { + "address": "0x6a94", + "from": "sprx_open", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x6ab5", + "from": "sprx_open", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x6aca", + "from": "sprx_open", + "indirect": true, + "target": "malloc" + }, + { + "address": "0x6af1", + "from": "sprx_open", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x6b02", + "from": "sprx_open", + "indirect": true, + "target": "malloc" + }, + { + "address": "0x6b29", + "from": "sprx_open", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x6b7f", + "from": "sprx_open", + "indirect": false, + "target": "klog_puts" + }, + { + "address": "0x6ba1", + "from": "sprx_open", + "indirect": false, + "target": "klog_perror" + }, + { + "address": "0x6bfc", + "from": "sprx_sym2addr", + "indirect": false, + "target": "nid_encode" + }, + { + "address": "0x6c7f", + "from": "sprx_sym2addr", + "indirect": true, + "target": "strncmp" + }, + { + "address": "0x6ca3", + "from": "sprx_sym2addr", + "indirect": true, + "target": "strncmp" + }, + { + "address": "0x6da4", + "from": "sprx_close", + "indirect": true, + "target": "sceKernelStopUnloadModule" + }, + { + "address": "0x6dba", + "from": "sprx_close", + "indirect": true, + "target": "free" + }, + { + "address": "0x6dcc", + "from": "sprx_close", + "indirect": true, + "target": "free" + }, + { + "address": "0x6e34", + "from": "sprx_destroy", + "indirect": true, + "target": "sceKernelStopUnloadModule" + }, + { + "address": "0x6e54", + "from": "sprx_destroy", + "indirect": true, + "target": "free" + }, + { + "address": "0x6e66", + "from": "sprx_destroy", + "indirect": true, + "target": "free" + }, + { + "address": "0x6ed7", + "from": "__rtld_sprx_init", + "indirect": true, + "target": "*%r15" + }, + { + "address": "0x6efb", + "from": "__rtld_sprx_init", + "indirect": true, + "target": "*%r15" + }, + { + "address": "0x6f1d", + "from": "__rtld_sprx_init", + "indirect": false, + "target": "kernel_dynlib_dlsym" + }, + { + "address": "0x6f44", + "from": "__rtld_sprx_init", + "indirect": false, + "target": "kernel_dynlib_dlsym" + }, + { + "address": "0x6f6b", + "from": "__rtld_sprx_init", + "indirect": false, + "target": "kernel_dynlib_dlsym" + }, + { + "address": "0x6f92", + "from": "__rtld_sprx_init", + "indirect": false, + "target": "kernel_dynlib_dlsym" + }, + { + "address": "0x6fb9", + "from": "__rtld_sprx_init", + "indirect": false, + "target": "kernel_dynlib_dlsym" + }, + { + "address": "0x6fe0", + "from": "__rtld_sprx_init", + "indirect": false, + "target": "kernel_dynlib_dlsym" + }, + { + "address": "0x7003", + "from": "__rtld_sprx_init", + "indirect": false, + "target": "kernel_dynlib_dlsym" + }, + { + "address": "0x7025", + "from": "__rtld_sprx_init", + "indirect": false, + "target": "kernel_dynlib_handle" + }, + { + "address": "0x7042", + "from": "__rtld_sprx_init", + "indirect": true, + "target": "sceKernelLoadStartModule" + }, + { + "address": "0x705e", + "from": "__rtld_sprx_init", + "indirect": false, + "target": "kernel_dynlib_dlsym" + }, + { + "address": "0x709b", + "from": "__rtld_payload_new", + "indirect": true, + "target": "calloc" + }, + { + "address": "0x70b2", + "from": "__rtld_payload_new", + "indirect": true, + "target": "*%r13" + }, + { + "address": "0x7137", + "from": "__rtld_payload_new", + "indirect": true, + "target": "strcpy" + }, + { + "address": "0x714f", + "from": "__rtld_payload_new", + "indirect": true, + "target": "*%r13" + }, + { + "address": "0x72ff", + "from": "payload_open", + "indirect": false, + "target": "__rtld_lib_new" + }, + { + "address": "0x7310", + "from": "payload_open", + "indirect": false, + "target": "__rtld_lib_open" + }, + { + "address": "0x7320", + "from": "payload_open", + "indirect": false, + "target": "__rtld_lib_append_dep" + }, + { + "address": "0x7334", + "from": "payload_open", + "indirect": false, + "target": "klog_printf" + }, + { + "address": "0x7348", + "from": "payload_open", + "indirect": false, + "target": "klog_printf" + }, + { + "address": "0x7351", + "from": "payload_open", + "indirect": false, + "target": "__rtld_lib_destroy" + }, + { + "address": "0x73ec", + "from": "payload_open", + "indirect": false, + "target": "klog_printf" + }, + { + "address": "0x745d", + "from": "payload_open", + "indirect": false, + "target": "__rtld_lib_sym2lib" + }, + { + "address": "0x746e", + "from": "payload_open", + "indirect": false, + "target": "__rtld_lib_sym2addr" + }, + { + "address": "0x7489", + "from": "payload_open", + "indirect": true, + "target": "memcpy" + }, + { + "address": "0x7552", + "from": "payload_open", + "indirect": false, + "target": "__rtld_lib_sym2lib" + }, + { + "address": "0x7567", + "from": "payload_open", + "indirect": false, + "target": "__rtld_lib_sym2addr" + }, + { + "address": "0x75be", + "from": "payload_open", + "indirect": false, + "target": "__rtld_lib_sym2lib" + }, + { + "address": "0x75d3", + "from": "payload_open", + "indirect": false, + "target": "__rtld_lib_sym2addr" + }, + { + "address": "0x761e", + "from": "payload_open", + "indirect": true, + "target": "memcpy" + }, + { + "address": "0x7641", + "from": "payload_open", + "indirect": false, + "target": "klog_printf" + }, + { + "address": "0x766b", + "from": "payload_open", + "indirect": false, + "target": "klog_printf" + }, + { + "address": "0x76d0", + "from": "payload_init", + "indirect": true, + "target": "*(%rax,%r13,8)" + }, + { + "address": "0x7789", + "from": "payload_sym2addr", + "indirect": true, + "target": "strcmp" + }, + { + "address": "0x7880", + "from": "payload_fini", + "indirect": true, + "target": "*(%r15,%rbx,8)" + }, + { + "address": "0x78dc", + "from": "__rtld_payload_init", + "indirect": false, + "target": "kernel_dynlib_dlsym" + }, + { + "address": "0x7903", + "from": "__rtld_payload_init", + "indirect": false, + "target": "kernel_dynlib_dlsym" + }, + { + "address": "0x7926", + "from": "__rtld_payload_init", + "indirect": false, + "target": "kernel_dynlib_dlsym" + }, + { + "address": "0x7949", + "from": "__rtld_payload_init", + "indirect": false, + "target": "kernel_dynlib_dlsym" + }, + { + "address": "0x796c", + "from": "__rtld_payload_init", + "indirect": false, + "target": "kernel_dynlib_dlsym" + }, + { + "address": "0x79c2", + "from": "__dlopen", + "indirect": false, + "target": "__rtld_lib_new" + }, + { + "address": "0x79d3", + "from": "__dlopen", + "indirect": false, + "target": "__rtld_lib_open" + }, + { + "address": "0x79e6", + "from": "__dlopen", + "indirect": false, + "target": "__rtld_lib_destroy" + }, + { + "address": "0x7a2a", + "from": "__dlopen", + "indirect": false, + "target": "__rtld_lib_append_dep" + }, + { + "address": "0x7a30", + "from": "__dlopen", + "indirect": true, + "target": "getargc" + }, + { + "address": "0x7a39", + "from": "__dlopen", + "indirect": true, + "target": "getargv" + }, + { + "address": "0x7a4f", + "from": "__dlopen", + "indirect": false, + "target": "__rtld_lib_init" + }, + { + "address": "0x7a8a", + "from": "__dladdr", + "indirect": false, + "target": "__rtld_lib_addr2lib" + }, + { + "address": "0x7ab0", + "from": "__dladdr", + "indirect": false, + "target": "__rtld_lib_addr2sym" + }, + { + "address": "0x7ac0", + "from": "__dladdr", + "indirect": false, + "target": "__rtld_lib_sym2addr" + }, + { + "address": "0x7b1d", + "from": "__dlsym", + "indirect": false, + "target": "__rtld_lib_sym2lib" + }, + { + "address": "0x7b34", + "from": "__dlsym", + "indirect": false, + "target": "__rtld_lib_sym2addr" + }, + { + "address": "0x7b67", + "from": "__dlclose", + "indirect": false, + "target": "__rtld_lib_fini" + }, + { + "address": "0x7b70", + "from": "__dlclose", + "indirect": false, + "target": "__rtld_lib_close" + }, + { + "address": "0x7b7f", + "from": "__dlclose", + "indirect": false, + "target": "__rtld_lib_destroy" + }, + { + "address": "0x7bfc", + "from": "__rtld_dlfcn_init", + "indirect": false, + "target": "kernel_dynlib_dlsym" + }, + { + "address": "0x7c23", + "from": "__rtld_dlfcn_init", + "indirect": false, + "target": "kernel_dynlib_dlsym" + }, + { + "address": "0x7c4a", + "from": "__rtld_dlfcn_init", + "indirect": false, + "target": "kernel_dynlib_dlsym" + }, + { + "address": "0x7c71", + "from": "__rtld_dlfcn_init", + "indirect": false, + "target": "kernel_dynlib_dlsym" + }, + { + "address": "0x7c94", + "from": "__rtld_dlfcn_init", + "indirect": false, + "target": "kernel_dynlib_dlsym" + }, + { + "address": "0x7cbb", + "from": "__rtld_dlfcn_init", + "indirect": false, + "target": "kernel_dynlib_dlsym" + }, + { + "address": "0x7cde", + "from": "__rtld_dlfcn_init", + "indirect": false, + "target": "kernel_dynlib_dlsym" + }, + { + "address": "0x7d01", + "from": "__rtld_dlfcn_init", + "indirect": false, + "target": "kernel_dynlib_dlsym" + }, + { + "address": "0x7d24", + "from": "__rtld_dlfcn_init", + "indirect": false, + "target": "kernel_dynlib_dlsym" + }, + { + "address": "0x7d70", + "from": "mdbg_copyout", + "indirect": false, + "target": "mdbg_memop" + }, + { + "address": "0x7d9e", + "from": "mdbg_memop", + "indirect": false, + "target": "__crt_syscall" + }, + { + "address": "0x7dc5", + "from": "mdbg_memop", + "indirect": false, + "target": "kernel_get_ucred_caps" + }, + { + "address": "0x7de9", + "from": "mdbg_memop", + "indirect": false, + "target": "kernel_get_ucred_authid" + }, + { + "address": "0x7e04", + "from": "mdbg_memop", + "indirect": false, + "target": "kernel_set_ucred_authid" + }, + { + "address": "0x7e17", + "from": "mdbg_memop", + "indirect": false, + "target": "kernel_set_ucred_caps" + }, + { + "address": "0x7e50", + "from": "mdbg_memop", + "indirect": true, + "target": "*%r13" + }, + { + "address": "0x7e8c", + "from": "mdbg_memop", + "indirect": false, + "target": "kernel_set_ucred_authid" + }, + { + "address": "0x7ea0", + "from": "mdbg_memop", + "indirect": false, + "target": "kernel_set_ucred_caps" + }, + { + "address": "0x7ee0", + "from": "mdbg_copyin", + "indirect": false, + "target": "mdbg_memop" + }, + { + "address": "0x7f1b", + "from": "mdbg_setchar", + "indirect": false, + "target": "mdbg_memop" + }, + { + "address": "0x7f5c", + "from": "mdbg_setshort", + "indirect": false, + "target": "mdbg_memop" + }, + { + "address": "0x7f9b", + "from": "mdbg_setint", + "indirect": false, + "target": "mdbg_memop" + }, + { + "address": "0x7fdc", + "from": "mdbg_setlong", + "indirect": false, + "target": "mdbg_memop" + }, + { + "address": "0x8020", + "from": "mdbg_getlong", + "indirect": false, + "target": "mdbg_memop" + }, + { + "address": "0x805f", + "from": "mdbg_getint", + "indirect": false, + "target": "mdbg_memop" + }, + { + "address": "0x809e", + "from": "mdbg_getshort", + "indirect": false, + "target": "mdbg_memop" + }, + { + "address": "0x80dc", + "from": "mdbg_getchar", + "indirect": false, + "target": "mdbg_memop" + }, + { + "address": "0x8102", + "from": "__patch_init", + "indirect": false, + "target": "__crt_syscall" + }, + { + "address": "0x8112", + "from": "__patch_init", + "indirect": false, + "target": "kernel_get_ucred_caps" + }, + { + "address": "0x8128", + "from": "__patch_init", + "indirect": false, + "target": "kernel_get_ucred_attrs" + }, + { + "address": "0x8149", + "from": "__patch_init", + "indirect": false, + "target": "kernel_set_ucred_caps" + }, + { + "address": "0x8169", + "from": "__patch_init", + "indirect": false, + "target": "kernel_set_ucred_attrs" + }, + { + "address": "0x817e", + "from": "__patch_init", + "indirect": false, + "target": "__crt_syscall" + }, + { + "address": "0x8186", + "from": "__patch_init", + "indirect": false, + "target": "kernel_get_proc" + }, + { + "address": "0x81a8", + "from": "__patch_init", + "indirect": false, + "target": "kernel_copyout" + }, + { + "address": "0x81cc", + "from": "__patch_init", + "indirect": false, + "target": "kernel_copyin" + }, + { + "address": "0x81f0", + "from": "__patch_init", + "indirect": false, + "target": "kernel_copyin" + } + ], + "dt_needed": [ + "libSceLibcInternal.sprx", + "libkernel_web.sprx" + ], + "filename": "chimera-gfx-lifecycle-probe-phase07-a.elf", + "imports": [ + "_exit", + "sceKernelSendNotificationRequest" + ], + "report_sha256": { + "callgraph": "7079dcbba22e05deeaa8388c1668764e9017f59ff18c4c1b9bb6e43fb1a98e14", + "disassembly": "811dd298d51b6637d77ab294b38e6e0dff570dcc4155b8fcc6121cf3d85414ec", + "dynamic": "261ba65fb412cb9335b063a829ae3e88aa6b049cd9001fe0c4fe8c5f68f309ba", + "headers_sections": "99683583e7ae87a76b9bcbf80fe49818b9a52aa71896d4de1713a2c15e729896", + "linker_map_normalized": "cf1f169914d93f97a481cd272d0390ff8216b044948ed3b574f67e97fd740218", + "relocations": "1d4a72fba84fb35f09804be9b816f94c4d3888d380ef17a2b937c5f159e91674", + "symbols": "80efe150f60e57dcb2844b1ca9db7a1ffb5af2e6d8b104cf8e6eacc06b26df94", + "undefined": "1792d93a34d5763741612e85e27e166be85626111480b4f7b68af09cfe4078e9" + }, + "sensitive_static_inventory": { + "categories": { + "dynamic_loading": { + "directly_reachable_from_entrypoint": [ + "kernel_dynlib_dlsym", + "kernel_dynlib_handle", + "kernel_dynlib_obj", + "kernel_dynlib_resolve" + ], + "linked": [ + "__dlopen", + "__dlsym", + "kernel_dynlib_dlsym", + "kernel_dynlib_entry_addr", + "kernel_dynlib_find_handle", + "kernel_dynlib_fini_addr", + "kernel_dynlib_handle", + "kernel_dynlib_init_addr", + "kernel_dynlib_mapbase_addr", + "kernel_dynlib_obj", + "kernel_dynlib_path", + "kernel_dynlib_resolve", + "sceKernelLoadStartModule", + "sceKernelStopUnloadModule" + ], + "linked_in_disassembly": [ + "__dlopen", + "__dlsym", + "kernel_dynlib_dlsym", + "kernel_dynlib_entry_addr", + "kernel_dynlib_find_handle", + "kernel_dynlib_fini_addr", + "kernel_dynlib_handle", + "kernel_dynlib_init_addr", + "kernel_dynlib_mapbase_addr", + "kernel_dynlib_obj", + "kernel_dynlib_path", + "kernel_dynlib_resolve" + ], + "linked_in_linker_map": [ + "__dlopen", + "__dlsym", + "kernel_dynlib_dlsym", + "kernel_dynlib_entry_addr", + "kernel_dynlib_find_handle", + "kernel_dynlib_fini_addr", + "kernel_dynlib_handle", + "kernel_dynlib_init_addr", + "kernel_dynlib_mapbase_addr", + "kernel_dynlib_obj", + "kernel_dynlib_path", + "kernel_dynlib_resolve", + "sceKernelLoadStartModule", + "sceKernelStopUnloadModule" + ] + }, + "graphics_or_display": { + "directly_reachable_from_entrypoint": [], + "linked": [], + "linked_in_disassembly": [], + "linked_in_linker_map": [] + }, + "kernel_runtime_write": { + "directly_reachable_from_entrypoint": [ + "__patch_init", + "kernel_copyin", + "kernel_copyout", + "kernel_set_ucred_attrs", + "kernel_set_ucred_caps" + ], + "linked": [ + "__patch_init", + "kernel_copyin", + "kernel_copyout", + "kernel_overlap_sockets", + "kernel_set_ucred_attrs", + "kernel_set_ucred_authid", + "kernel_set_ucred_caps", + "kernel_set_ucred_prison", + "kernel_set_ucred_rgid", + "kernel_set_ucred_ruid", + "kernel_set_ucred_svgid", + "kernel_set_ucred_svuid", + "kernel_set_ucred_uid" + ], + "linked_in_disassembly": [ + "__patch_init", + "kernel_copyin", + "kernel_copyout", + "kernel_overlap_sockets", + "kernel_set_ucred_attrs", + "kernel_set_ucred_authid", + "kernel_set_ucred_caps", + "kernel_set_ucred_prison", + "kernel_set_ucred_rgid", + "kernel_set_ucred_ruid", + "kernel_set_ucred_svgid", + "kernel_set_ucred_svuid", + "kernel_set_ucred_uid" + ], + "linked_in_linker_map": [ + "__patch_init", + "kernel_copyin", + "kernel_copyout", + "kernel_overlap_sockets", + "kernel_set_ucred_attrs", + "kernel_set_ucred_authid", + "kernel_set_ucred_caps", + "kernel_set_ucred_prison", + "kernel_set_ucred_rgid", + "kernel_set_ucred_ruid", + "kernel_set_ucred_svgid", + "kernel_set_ucred_svuid", + "kernel_set_ucred_uid" + ] + }, + "network": { + "directly_reachable_from_entrypoint": [], + "linked": [], + "linked_in_disassembly": [], + "linked_in_linker_map": [] + }, + "ptrace_or_jit": { + "directly_reachable_from_entrypoint": [], + "linked": [], + "linked_in_disassembly": [], + "linked_in_linker_map": [] + } + }, + "direct_call_reachability_available": true, + "direct_call_reachability_only": true, + "entrypoint": "_start", + "indirect_call_edges_retained_but_not_resolved": 116 + }, + "sha256": "bfb4a5cc768e162fe4c2fddf41c3978e152722918a39085277fd172cb95a7182", + "size": 112680 + }, + "loader": { + "byte_identical_clean_builds": true, + "call_edges": [ + { + "address": "0x11", + "from": ".text", + "indirect": true, + "target": "*0x146b9(%rip) # 0x146d0" + }, + { + "address": "0x25", + "from": ".text", + "indirect": false, + "target": "dup+0x1f20" + }, + { + "address": "0x2e", + "from": ".text", + "indirect": true, + "target": "*0x146a4(%rip) # 0x146d8" + }, + { + "address": "0x37", + "from": ".text", + "indirect": false, + "target": "dup+0x35f0" + }, + { + "address": "0x4d", + "from": ".text", + "indirect": false, + "target": ".text+0x210" + }, + { + "address": "0x67", + "from": ".text", + "indirect": false, + "target": "dup+0x6310" + }, + { + "address": "0x8b", + "from": ".text", + "indirect": false, + "target": "dup+0x26f0" + }, + { + "address": "0xa7", + "from": ".text", + "indirect": false, + "target": "dup+0x6330" + }, + { + "address": "0xc1", + "from": ".text", + "indirect": false, + "target": ".text+0x2350" + }, + { + "address": "0xd0", + "from": ".text", + "indirect": false, + "target": ".text+0x2a0" + }, + { + "address": "0xe4", + "from": ".text", + "indirect": true, + "target": "*0x145ee(%rip) # 0x146d8" + }, + { + "address": "0xed", + "from": ".text", + "indirect": false, + "target": "dup+0x35f0" + }, + { + "address": "0x108", + "from": ".text", + "indirect": true, + "target": "*0x145ca(%rip) # 0x146d8" + }, + { + "address": "0x111", + "from": ".text", + "indirect": false, + "target": "dup+0x35f0" + }, + { + "address": "0x12e", + "from": ".text", + "indirect": false, + "target": "dup+0xff0" + }, + { + "address": "0x138", + "from": ".text", + "indirect": true, + "target": "*0x145a2(%rip) # 0x146e0" + }, + { + "address": "0x140", + "from": ".text", + "indirect": true, + "target": "*0x145a2(%rip) # 0x146e8" + }, + { + "address": "0x16e", + "from": ".text", + "indirect": true, + "target": "*0x1457c(%rip) # 0x146f0" + }, + { + "address": "0x174", + "from": ".text", + "indirect": true, + "target": "*0x14566(%rip) # 0x146e0" + }, + { + "address": "0x17c", + "from": ".text", + "indirect": true, + "target": "*0x14566(%rip) # 0x146e8" + }, + { + "address": "0x195", + "from": ".text", + "indirect": false, + "target": "dup+0x3410" + }, + { + "address": "0x1b2", + "from": ".text", + "indirect": false, + "target": ".text+0x310" + }, + { + "address": "0x1c1", + "from": ".text", + "indirect": false, + "target": "dup+0x2720" + }, + { + "address": "0x1e2", + "from": ".text", + "indirect": true, + "target": "*0x14508(%rip) # 0x146f0" + }, + { + "address": "0x1ef", + "from": ".text", + "indirect": false, + "target": "dup+0x3410" + }, + { + "address": "0x1fd", + "from": ".text", + "indirect": false, + "target": "dup+0x2750" + }, + { + "address": "0x2aa", + "from": ".text", + "indirect": false, + "target": "dup+0x63c0" + }, + { + "address": "0x2c0", + "from": ".text", + "indirect": false, + "target": "dup+0x7bb0" + }, + { + "address": "0x2cf", + "from": ".text", + "indirect": false, + "target": "dup+0x7c20" + }, + { + "address": "0x2de", + "from": ".text", + "indirect": false, + "target": "dup+0x70d0" + }, + { + "address": "0x2f2", + "from": ".text", + "indirect": false, + "target": "dup+0x6f10" + }, + { + "address": "0x332", + "from": ".text", + "indirect": false, + "target": "dup+0x2460" + }, + { + "address": "0x344", + "from": ".text", + "indirect": false, + "target": ".text+0x360" + }, + { + "address": "0x387", + "from": ".text", + "indirect": false, + "target": "dup+0x26f0" + }, + { + "address": "0x39b", + "from": ".text", + "indirect": true, + "target": "*0x14337(%rip) # 0x146d8" + }, + { + "address": "0x3a4", + "from": ".text", + "indirect": false, + "target": "dup+0x35f0" + }, + { + "address": "0x3b3", + "from": ".text", + "indirect": false, + "target": "dup+0x63c0" + }, + { + "address": "0x3ca", + "from": ".text", + "indirect": false, + "target": "dup+0x7bb0" + }, + { + "address": "0x3d9", + "from": ".text", + "indirect": false, + "target": "dup+0x7c20" + }, + { + "address": "0x3e8", + "from": ".text", + "indirect": false, + "target": "dup+0x70d0" + }, + { + "address": "0x3fc", + "from": ".text", + "indirect": false, + "target": "dup+0x6f10" + }, + { + "address": "0x416", + "from": ".text", + "indirect": true, + "target": "*0x142bc(%rip) # 0x146d8" + }, + { + "address": "0x41f", + "from": ".text", + "indirect": false, + "target": "dup+0x35f0" + }, + { + "address": "0x448", + "from": ".text", + "indirect": true, + "target": "*0x14282(%rip) # 0x146d0" + }, + { + "address": "0x456", + "from": ".text", + "indirect": false, + "target": "dup+0x1dc0" + }, + { + "address": "0x470", + "from": ".text", + "indirect": false, + "target": "dup+0x1d00" + }, + { + "address": "0x482", + "from": ".text", + "indirect": false, + "target": "dup+0x1d00" + }, + { + "address": "0x491", + "from": ".text", + "indirect": false, + "target": "dup+0x1d00" + }, + { + "address": "0x4a3", + "from": ".text", + "indirect": false, + "target": "dup+0x1d90" + }, + { + "address": "0x4b8", + "from": ".text", + "indirect": false, + "target": "dup+0x1d90" + }, + { + "address": "0x4cd", + "from": ".text", + "indirect": false, + "target": "dup+0x1d90" + }, + { + "address": "0x4e9", + "from": ".text", + "indirect": false, + "target": "dup+0x1d00" + }, + { + "address": "0x50d", + "from": ".text", + "indirect": false, + "target": "dup+0x1230" + }, + { + "address": "0x52a", + "from": ".text", + "indirect": true, + "target": "*%r14" + }, + { + "address": "0x536", + "from": ".text", + "indirect": true, + "target": "*%r13" + }, + { + "address": "0x561", + "from": ".text", + "indirect": true, + "target": "*0x14189(%rip) # 0x146f0" + }, + { + "address": "0x567", + "from": ".text", + "indirect": true, + "target": "*%r14" + }, + { + "address": "0x56c", + "from": ".text", + "indirect": true, + "target": "*%r13" + }, + { + "address": "0x58a", + "from": ".text", + "indirect": false, + "target": "dup+0x3410" + }, + { + "address": "0x5a0", + "from": ".text", + "indirect": false, + "target": "dup+0x2720" + }, + { + "address": "0x5ba", + "from": ".text", + "indirect": true, + "target": "*0x14130(%rip) # 0x146f0" + }, + { + "address": "0x5c7", + "from": ".text", + "indirect": false, + "target": "dup+0x3410" + }, + { + "address": "0x5e0", + "from": ".text", + "indirect": false, + "target": "dup+0x28d0" + }, + { + "address": "0x602", + "from": ".text", + "indirect": false, + "target": "dup+0x1120" + }, + { + "address": "0x61b", + "from": ".text", + "indirect": true, + "target": "*%r12" + }, + { + "address": "0x627", + "from": ".text", + "indirect": true, + "target": "*%r13" + }, + { + "address": "0x652", + "from": ".text", + "indirect": true, + "target": "*0x14098(%rip) # 0x146f0" + }, + { + "address": "0x658", + "from": ".text", + "indirect": true, + "target": "*%r12" + }, + { + "address": "0x65d", + "from": ".text", + "indirect": true, + "target": "*%r13" + }, + { + "address": "0x673", + "from": ".text", + "indirect": false, + "target": "dup+0x3410" + }, + { + "address": "0x73a", + "from": ".text", + "indirect": true, + "target": "*0x13fb8(%rip) # 0x146f8" + }, + { + "address": "0x770", + "from": ".text", + "indirect": false, + "target": "dup+0x1be0" + }, + { + "address": "0x7f4", + "from": ".text", + "indirect": true, + "target": "*0x13f06(%rip) # 0x14700" + }, + { + "address": "0x806", + "from": ".text", + "indirect": true, + "target": "*0x13ecc(%rip) # 0x146d8" + }, + { + "address": "0x80f", + "from": ".text", + "indirect": false, + "target": "dup+0x35f0" + }, + { + "address": "0x8b8", + "from": ".text", + "indirect": false, + "target": "dup+0x1280" + }, + { + "address": "0x8d0", + "from": ".text", + "indirect": true, + "target": "*%r14" + }, + { + "address": "0x8dc", + "from": ".text", + "indirect": true, + "target": "*%r15" + }, + { + "address": "0x8fe", + "from": ".text", + "indirect": true, + "target": "*0x13dec(%rip) # 0x146f0" + }, + { + "address": "0x904", + "from": ".text", + "indirect": true, + "target": "*%r14" + }, + { + "address": "0x90d", + "from": ".text", + "indirect": true, + "target": "*%r15" + }, + { + "address": "0x92f", + "from": ".text", + "indirect": false, + "target": "dup+0x3410" + }, + { + "address": "0x947", + "from": ".text", + "indirect": true, + "target": "*%r14" + }, + { + "address": "0x953", + "from": ".text", + "indirect": true, + "target": "*%r13" + }, + { + "address": "0x97e", + "from": ".text", + "indirect": true, + "target": "*0x13d6c(%rip) # 0x146f0" + }, + { + "address": "0x984", + "from": ".text", + "indirect": true, + "target": "*%r14" + }, + { + "address": "0x989", + "from": ".text", + "indirect": true, + "target": "*%r13" + }, + { + "address": "0x9a5", + "from": ".text", + "indirect": false, + "target": "dup+0x3410" + }, + { + "address": "0x9b3", + "from": ".text", + "indirect": false, + "target": "dup+0x1e50" + }, + { + "address": "0x9c2", + "from": ".text", + "indirect": true, + "target": "*%r15" + }, + { + "address": "0x9ed", + "from": ".text", + "indirect": true, + "target": "*0x13cfd(%rip) # 0x146f0" + }, + { + "address": "0x9f6", + "from": ".text", + "indirect": false, + "target": "dup+0x1e50" + }, + { + "address": "0x9fe", + "from": ".text", + "indirect": true, + "target": "*%r15" + }, + { + "address": "0xa1a", + "from": ".text", + "indirect": false, + "target": "dup+0x3410" + }, + { + "address": "0xa24", + "from": ".text", + "indirect": true, + "target": "*0x13cde(%rip) # 0x14708" + }, + { + "address": "0xaa9", + "from": ".text", + "indirect": false, + "target": "dup+0x1c70" + }, + { + "address": "0xabe", + "from": ".text", + "indirect": false, + "target": "dup+0x1e50" + }, + { + "address": "0xac6", + "from": ".text", + "indirect": true, + "target": "*0x13c1c(%rip) # 0x146e8" + }, + { + "address": "0xaeb", + "from": ".text", + "indirect": true, + "target": "*0x13bff(%rip) # 0x146f0" + }, + { + "address": "0xaf4", + "from": ".text", + "indirect": false, + "target": "dup+0x1e50" + }, + { + "address": "0xafc", + "from": ".text", + "indirect": true, + "target": "*0x13be6(%rip) # 0x146e8" + }, + { + "address": "0xb25", + "from": ".text", + "indirect": false, + "target": "dup+0x7850" + }, + { + "address": "0xb36", + "from": ".text", + "indirect": true, + "target": "*%rax" + }, + { + "address": "0xb41", + "from": ".text", + "indirect": true, + "target": "*%r15" + }, + { + "address": "0xb63", + "from": ".text", + "indirect": true, + "target": "*0x13b87(%rip) # 0x146f0" + }, + { + "address": "0xb69", + "from": ".text", + "indirect": true, + "target": "*0x13b71(%rip) # 0x146e0" + }, + { + "address": "0xb71", + "from": ".text", + "indirect": true, + "target": "*%r15" + }, + { + "address": "0xb93", + "from": ".text", + "indirect": false, + "target": "dup+0x3410" + }, + { + "address": "0xbcd", + "from": ".text", + "indirect": false, + "target": "dup+0x1c10" + }, + { + "address": "0xbda", + "from": ".text", + "indirect": false, + "target": "dup+0x1e50" + }, + { + "address": "0xbe9", + "from": ".text", + "indirect": true, + "target": "*%r15" + }, + { + "address": "0xc0e", + "from": ".text", + "indirect": true, + "target": "*0x13adc(%rip) # 0x146f0" + }, + { + "address": "0xc17", + "from": ".text", + "indirect": false, + "target": "dup+0x1e50" + }, + { + "address": "0xc1f", + "from": ".text", + "indirect": true, + "target": "*%r15" + }, + { + "address": "0xc3d", + "from": ".text", + "indirect": false, + "target": "dup+0x3410" + }, + { + "address": "0xc4d", + "from": ".text", + "indirect": true, + "target": "*0x13ab5(%rip) # 0x14708" + }, + { + "address": "0xc62", + "from": ".text", + "indirect": false, + "target": "dup+0x1c40" + }, + { + "address": "0xcad", + "from": ".text", + "indirect": false, + "target": "dup+0x1be0" + }, + { + "address": "0xce9", + "from": ".text", + "indirect": false, + "target": "dup+0x1ca0" + }, + { + "address": "0xd0f", + "from": ".text", + "indirect": false, + "target": "dup+0x1210" + }, + { + "address": "0xd2a", + "from": ".text", + "indirect": false, + "target": "dup+0x1210" + }, + { + "address": "0xd42", + "from": ".text", + "indirect": false, + "target": "dup+0x1210" + }, + { + "address": "0xd58", + "from": ".text", + "indirect": false, + "target": "dup+0x1210" + }, + { + "address": "0xd6e", + "from": ".text", + "indirect": false, + "target": "dup+0x1210" + }, + { + "address": "0xd80", + "from": ".text", + "indirect": false, + "target": "dup+0x1210" + }, + { + "address": "0xd95", + "from": ".text", + "indirect": true, + "target": "*%r14" + }, + { + "address": "0xda1", + "from": ".text", + "indirect": true, + "target": "*%r12" + }, + { + "address": "0xdd2", + "from": ".text", + "indirect": true, + "target": "*0x13918(%rip) # 0x146f0" + }, + { + "address": "0xdd8", + "from": ".text", + "indirect": true, + "target": "*%r14" + }, + { + "address": "0xddd", + "from": ".text", + "indirect": true, + "target": "*%r12" + }, + { + "address": "0xdfd", + "from": ".text", + "indirect": true, + "target": "*0x138d5(%rip) # 0x146d8" + }, + { + "address": "0xe06", + "from": ".text", + "indirect": false, + "target": "dup+0x35f0" + }, + { + "address": "0xe1e", + "from": ".text", + "indirect": false, + "target": "dup+0x1e50" + }, + { + "address": "0xe2d", + "from": ".text", + "indirect": true, + "target": "*%r12" + }, + { + "address": "0xe5e", + "from": ".text", + "indirect": true, + "target": "*0x1388c(%rip) # 0x146f0" + }, + { + "address": "0xe67", + "from": ".text", + "indirect": false, + "target": "dup+0x1e50" + }, + { + "address": "0xe6f", + "from": ".text", + "indirect": true, + "target": "*%r12" + }, + { + "address": "0xe82", + "from": ".text", + "indirect": false, + "target": "dup+0x1e50" + }, + { + "address": "0xe91", + "from": ".text", + "indirect": true, + "target": "*%r12" + }, + { + "address": "0xec2", + "from": ".text", + "indirect": true, + "target": "*0x13828(%rip) # 0x146f0" + }, + { + "address": "0xecb", + "from": ".text", + "indirect": false, + "target": "dup+0x1e50" + }, + { + "address": "0xed3", + "from": ".text", + "indirect": true, + "target": "*%r12" + }, + { + "address": "0xee9", + "from": ".text", + "indirect": false, + "target": "dup+0x3410" + }, + { + "address": "0xf15", + "from": ".text", + "indirect": false, + "target": "dup+0x1350" + }, + { + "address": "0xf2a", + "from": ".text", + "indirect": true, + "target": "*%r14" + }, + { + "address": "0xf36", + "from": ".text", + "indirect": true, + "target": "*%r13" + }, + { + "address": "0xf61", + "from": ".text", + "indirect": true, + "target": "*0x13789(%rip) # 0x146f0" + }, + { + "address": "0xf67", + "from": ".text", + "indirect": true, + "target": "*%r14" + }, + { + "address": "0xf6c", + "from": ".text", + "indirect": true, + "target": "*%r13" + }, + { + "address": "0xf8d", + "from": ".text", + "indirect": true, + "target": "*0x13745(%rip) # 0x146d8" + }, + { + "address": "0xf96", + "from": ".text", + "indirect": false, + "target": "dup+0x35f0" + }, + { + "address": "0xfd3", + "from": ".text", + "indirect": false, + "target": "dup+0x1260" + }, + { + "address": "0xfe4", + "from": ".text", + "indirect": true, + "target": "*%r14" + }, + { + "address": "0xff0", + "from": ".text", + "indirect": true, + "target": "*%r13" + }, + { + "address": "0x101b", + "from": ".text", + "indirect": true, + "target": "*0x136cf(%rip) # 0x146f0" + }, + { + "address": "0x1021", + "from": ".text", + "indirect": true, + "target": "*%r14" + }, + { + "address": "0x1026", + "from": ".text", + "indirect": true, + "target": "*%r13" + }, + { + "address": "0x1040", + "from": ".text", + "indirect": false, + "target": "dup+0x3410" + }, + { + "address": "0x1079", + "from": ".text", + "indirect": false, + "target": "dup+0x1cd0" + }, + { + "address": "0x108a", + "from": ".text", + "indirect": false, + "target": "dup+0x1e50" + }, + { + "address": "0x1099", + "from": ".text", + "indirect": true, + "target": "*%r12" + }, + { + "address": "0x10ca", + "from": ".text", + "indirect": true, + "target": "*0x13620(%rip) # 0x146f0" + }, + { + "address": "0x10d3", + "from": ".text", + "indirect": false, + "target": "dup+0x1e50" + }, + { + "address": "0x10db", + "from": ".text", + "indirect": true, + "target": "*%r12" + }, + { + "address": "0x1100", + "from": ".text", + "indirect": false, + "target": "dup+0x1ca0" + }, + { + "address": "0x1122", + "from": ".text", + "indirect": false, + "target": "dup+0x1210" + }, + { + "address": "0x1134", + "from": ".text", + "indirect": false, + "target": "dup+0x1210" + }, + { + "address": "0x1147", + "from": ".text", + "indirect": false, + "target": "dup+0x1210" + }, + { + "address": "0x115a", + "from": ".text", + "indirect": false, + "target": "dup+0x1210" + }, + { + "address": "0x116d", + "from": ".text", + "indirect": false, + "target": "dup+0x1210" + }, + { + "address": "0x1182", + "from": ".text", + "indirect": true, + "target": "*%r14" + }, + { + "address": "0x118e", + "from": ".text", + "indirect": true, + "target": "*%r12" + }, + { + "address": "0x11bf", + "from": ".text", + "indirect": true, + "target": "*0x1352b(%rip) # 0x146f0" + }, + { + "address": "0x11c5", + "from": ".text", + "indirect": true, + "target": "*%r14" + }, + { + "address": "0x11ca", + "from": ".text", + "indirect": true, + "target": "*%r12" + }, + { + "address": "0x11e4", + "from": ".text", + "indirect": false, + "target": "dup+0x1e50" + }, + { + "address": "0x11f3", + "from": ".text", + "indirect": true, + "target": "*%r12" + }, + { + "address": "0x1224", + "from": ".text", + "indirect": true, + "target": "*0x134c6(%rip) # 0x146f0" + }, + { + "address": "0x122d", + "from": ".text", + "indirect": false, + "target": "dup+0x1e50" + }, + { + "address": "0x1235", + "from": ".text", + "indirect": true, + "target": "*%r12" + }, + { + "address": "0x1260", + "from": ".text", + "indirect": false, + "target": "dup+0x1cd0" + }, + { + "address": "0x1271", + "from": ".text", + "indirect": false, + "target": "dup+0x1e50" + }, + { + "address": "0x1280", + "from": ".text", + "indirect": true, + "target": "*%r12" + }, + { + "address": "0x12b1", + "from": ".text", + "indirect": true, + "target": "*0x13439(%rip) # 0x146f0" + }, + { + "address": "0x12ba", + "from": ".text", + "indirect": false, + "target": "dup+0x1e50" + }, + { + "address": "0x12c2", + "from": ".text", + "indirect": true, + "target": "*%r12" + }, + { + "address": "0x12dd", + "from": ".text", + "indirect": false, + "target": "dup+0x78f0" + }, + { + "address": "0x12f1", + "from": ".text", + "indirect": true, + "target": "*0x133e1(%rip) # 0x146d8" + }, + { + "address": "0x12fa", + "from": ".text", + "indirect": false, + "target": "dup+0x35f0" + }, + { + "address": "0x1311", + "from": ".text", + "indirect": false, + "target": "dup+0x1df0" + }, + { + "address": "0x1322", + "from": ".text", + "indirect": false, + "target": "dup+0x1e50" + }, + { + "address": "0x1331", + "from": ".text", + "indirect": true, + "target": "*%r12" + }, + { + "address": "0x1362", + "from": ".text", + "indirect": true, + "target": "*0x13388(%rip) # 0x146f0" + }, + { + "address": "0x136b", + "from": ".text", + "indirect": false, + "target": "dup+0x1e50" + }, + { + "address": "0x1373", + "from": ".text", + "indirect": true, + "target": "*%r12" + }, + { + "address": "0x1390", + "from": ".text", + "indirect": false, + "target": "dup+0x11f0" + }, + { + "address": "0x139f", + "from": ".text", + "indirect": false, + "target": "dup+0x11f0" + }, + { + "address": "0x13d6", + "from": ".text", + "indirect": false, + "target": "dup+0x7860" + }, + { + "address": "0x13e9", + "from": ".text", + "indirect": false, + "target": "dup+0xfb0" + }, + { + "address": "0x1412", + "from": ".text", + "indirect": false, + "target": "dup+0x1350" + }, + { + "address": "0x1435", + "from": ".text", + "indirect": false, + "target": "dup+0x1350" + }, + { + "address": "0x1458", + "from": ".text", + "indirect": false, + "target": "dup+0x1350" + }, + { + "address": "0x1474", + "from": ".text", + "indirect": false, + "target": "dup+0x1350" + }, + { + "address": "0x1497", + "from": ".text", + "indirect": false, + "target": "dup+0x1350" + }, + { + "address": "0x14bb", + "from": ".text", + "indirect": false, + "target": "dup+0x1350" + }, + { + "address": "0x14ce", + "from": ".text", + "indirect": false, + "target": "dup+0x1210" + }, + { + "address": "0x14e9", + "from": ".text", + "indirect": false, + "target": "dup+0x1210" + }, + { + "address": "0x14fc", + "from": ".text", + "indirect": false, + "target": "dup+0x1210" + }, + { + "address": "0x1516", + "from": ".text", + "indirect": false, + "target": "dup+0x1210" + }, + { + "address": "0x1529", + "from": ".text", + "indirect": false, + "target": "dup+0x1210" + }, + { + "address": "0x153e", + "from": ".text", + "indirect": true, + "target": "*%r14" + }, + { + "address": "0x154a", + "from": ".text", + "indirect": true, + "target": "*%r12" + }, + { + "address": "0x157b", + "from": ".text", + "indirect": true, + "target": "*0x1316f(%rip) # 0x146f0" + }, + { + "address": "0x1581", + "from": ".text", + "indirect": true, + "target": "*%r14" + }, + { + "address": "0x1586", + "from": ".text", + "indirect": true, + "target": "*%r12" + }, + { + "address": "0x15a0", + "from": ".text", + "indirect": true, + "target": "*%r14" + }, + { + "address": "0x15ac", + "from": ".text", + "indirect": true, + "target": "*%r12" + }, + { + "address": "0x15dd", + "from": ".text", + "indirect": true, + "target": "*0x1310d(%rip) # 0x146f0" + }, + { + "address": "0x15e3", + "from": ".text", + "indirect": true, + "target": "*%r14" + }, + { + "address": "0x15e8", + "from": ".text", + "indirect": true, + "target": "*%r12" + }, + { + "address": "0x16bc", + "from": ".text", + "indirect": true, + "target": "*0x1301e(%rip) # 0x146e0" + }, + { + "address": "0x16e9", + "from": ".text", + "indirect": true, + "target": "*0x13021(%rip) # 0x14710" + }, + { + "address": "0x16ff", + "from": ".text", + "indirect": true, + "target": "*0x12ff3(%rip) # 0x146f8" + }, + { + "address": "0x172a", + "from": ".text", + "indirect": true, + "target": "*0x12fe8(%rip) # 0x14718" + }, + { + "address": "0x1744", + "from": ".text", + "indirect": false, + "target": "dup+0x2460" + }, + { + "address": "0x1781", + "from": ".text", + "indirect": true, + "target": "*0x12f99(%rip) # 0x14720" + }, + { + "address": "0x1797", + "from": ".text", + "indirect": false, + "target": "dup+0xe00" + }, + { + "address": "0x17a5", + "from": ".text", + "indirect": true, + "target": "*0x12f35(%rip) # 0x146e0" + }, + { + "address": "0x17ad", + "from": ".text", + "indirect": true, + "target": "*0x12f35(%rip) # 0x146e8" + }, + { + "address": "0x17db", + "from": ".text", + "indirect": true, + "target": "*0x12f0f(%rip) # 0x146f0" + }, + { + "address": "0x17e1", + "from": ".text", + "indirect": true, + "target": "*0x12ef9(%rip) # 0x146e0" + }, + { + "address": "0x17e9", + "from": ".text", + "indirect": true, + "target": "*0x12ef9(%rip) # 0x146e8" + }, + { + "address": "0x1806", + "from": ".text", + "indirect": true, + "target": "*%r12" + }, + { + "address": "0x1812", + "from": ".text", + "indirect": true, + "target": "*%r13" + }, + { + "address": "0x183d", + "from": ".text", + "indirect": true, + "target": "*0x12ead(%rip) # 0x146f0" + }, + { + "address": "0x1843", + "from": ".text", + "indirect": true, + "target": "*%r12" + }, + { + "address": "0x1848", + "from": ".text", + "indirect": true, + "target": "*%r13" + }, + { + "address": "0x185e", + "from": ".text", + "indirect": false, + "target": "dup+0x3410" + }, + { + "address": "0x1870", + "from": ".text", + "indirect": true, + "target": "*%r13" + }, + { + "address": "0x1875", + "from": ".text", + "indirect": true, + "target": "*0x12e6d(%rip) # 0x146e8" + }, + { + "address": "0x18a3", + "from": ".text", + "indirect": true, + "target": "*0x12e47(%rip) # 0x146f0" + }, + { + "address": "0x18a9", + "from": ".text", + "indirect": true, + "target": "*%r13" + }, + { + "address": "0x18ae", + "from": ".text", + "indirect": true, + "target": "*0x12e34(%rip) # 0x146e8" + }, + { + "address": "0x18c7", + "from": ".text", + "indirect": false, + "target": "dup+0x3410" + }, + { + "address": "0x18cf", + "from": ".text", + "indirect": true, + "target": "*0x12e0b(%rip) # 0x146e0" + }, + { + "address": "0x18d7", + "from": ".text", + "indirect": true, + "target": "*0x12e0b(%rip) # 0x146e8" + }, + { + "address": "0x1905", + "from": ".text", + "indirect": true, + "target": "*0x12de5(%rip) # 0x146f0" + }, + { + "address": "0x190b", + "from": ".text", + "indirect": true, + "target": "*0x12dcf(%rip) # 0x146e0" + }, + { + "address": "0x1913", + "from": ".text", + "indirect": true, + "target": "*0x12dcf(%rip) # 0x146e8" + }, + { + "address": "0x192c", + "from": ".text", + "indirect": false, + "target": "dup+0x3410" + }, + { + "address": "0x1935", + "from": ".text", + "indirect": true, + "target": "*0x12dcd(%rip) # 0x14708" + }, + { + "address": "0x193e", + "from": ".text", + "indirect": true, + "target": "*0x12de4(%rip) # 0x14728" + }, + { + "address": "0x1949", + "from": ".text", + "indirect": true, + "target": "*0x12d91(%rip) # 0x146e0" + }, + { + "address": "0x1951", + "from": ".text", + "indirect": true, + "target": "*0x12d91(%rip) # 0x146e8" + }, + { + "address": "0x197f", + "from": ".text", + "indirect": true, + "target": "*0x12d6b(%rip) # 0x146f0" + }, + { + "address": "0x1985", + "from": ".text", + "indirect": true, + "target": "*0x12d55(%rip) # 0x146e0" + }, + { + "address": "0x198d", + "from": ".text", + "indirect": true, + "target": "*0x12d55(%rip) # 0x146e8" + }, + { + "address": "0x19a6", + "from": ".text", + "indirect": false, + "target": "dup+0x3410" + }, + { + "address": "0x19b4", + "from": ".text", + "indirect": true, + "target": "*0x12d4e(%rip) # 0x14708" + }, + { + "address": "0x19c2", + "from": ".text", + "indirect": true, + "target": "*0x12d60(%rip) # 0x14728" + }, + { + "address": "0x19d4", + "from": ".text", + "indirect": false, + "target": "dup+0x28d0" + }, + { + "address": "0x19e2", + "from": ".text", + "indirect": true, + "target": "*0x12d20(%rip) # 0x14708" + }, + { + "address": "0x19eb", + "from": ".text", + "indirect": true, + "target": "*0x12d37(%rip) # 0x14728" + }, + { + "address": "0x1a09", + "from": ".text", + "indirect": false, + "target": "dup+0x1880" + }, + { + "address": "0x1a1b", + "from": ".text", + "indirect": false, + "target": "dup+0x1e50" + }, + { + "address": "0x1a23", + "from": ".text", + "indirect": true, + "target": "*0x12cbf(%rip) # 0x146e8" + }, + { + "address": "0x1a53", + "from": ".text", + "indirect": true, + "target": "*0x12c97(%rip) # 0x146f0" + }, + { + "address": "0x1a5c", + "from": ".text", + "indirect": false, + "target": "dup+0x1e50" + }, + { + "address": "0x1a64", + "from": ".text", + "indirect": true, + "target": "*0x12c7e(%rip) # 0x146e8" + }, + { + "address": "0x1a7d", + "from": ".text", + "indirect": false, + "target": "dup+0x3410" + }, + { + "address": "0x1a8e", + "from": ".text", + "indirect": false, + "target": ".text+0x1e90" + }, + { + "address": "0x1aa1", + "from": ".text", + "indirect": true, + "target": "*0x12c31(%rip) # 0x146d8" + }, + { + "address": "0x1aaa", + "from": ".text", + "indirect": false, + "target": "dup+0x35f0" + }, + { + "address": "0x1ac2", + "from": ".text", + "indirect": false, + "target": "dup+0x6b10" + }, + { + "address": "0x1ae4", + "from": ".text", + "indirect": false, + "target": "dup+0x7850" + }, + { + "address": "0x1b0f", + "from": ".text", + "indirect": false, + "target": "dup+0x13a0" + }, + { + "address": "0x1b20", + "from": ".text", + "indirect": true, + "target": "*%r13" + }, + { + "address": "0x1b25", + "from": ".text", + "indirect": true, + "target": "*0x12bbd(%rip) # 0x146e8" + }, + { + "address": "0x1b55", + "from": ".text", + "indirect": true, + "target": "*0x12b95(%rip) # 0x146f0" + }, + { + "address": "0x1b5b", + "from": ".text", + "indirect": true, + "target": "*%r13" + }, + { + "address": "0x1b60", + "from": ".text", + "indirect": true, + "target": "*0x12b82(%rip) # 0x146e8" + }, + { + "address": "0x1b85", + "from": ".text", + "indirect": false, + "target": "dup+0x1280" + }, + { + "address": "0x1b96", + "from": ".text", + "indirect": true, + "target": "*%r13" + }, + { + "address": "0x1b9b", + "from": ".text", + "indirect": true, + "target": "*0x12b47(%rip) # 0x146e8" + }, + { + "address": "0x1bcb", + "from": ".text", + "indirect": true, + "target": "*0x12b1f(%rip) # 0x146f0" + }, + { + "address": "0x1bd1", + "from": ".text", + "indirect": true, + "target": "*%r13" + }, + { + "address": "0x1bd6", + "from": ".text", + "indirect": true, + "target": "*0x12b0c(%rip) # 0x146e8" + }, + { + "address": "0x1c06", + "from": ".text", + "indirect": false, + "target": "dup+0x11c0" + }, + { + "address": "0x1c17", + "from": ".text", + "indirect": true, + "target": "*%r13" + }, + { + "address": "0x1c1c", + "from": ".text", + "indirect": true, + "target": "*0x12ac6(%rip) # 0x146e8" + }, + { + "address": "0x1c4c", + "from": ".text", + "indirect": true, + "target": "*0x12a9e(%rip) # 0x146f0" + }, + { + "address": "0x1c52", + "from": ".text", + "indirect": true, + "target": "*%r13" + }, + { + "address": "0x1c57", + "from": ".text", + "indirect": true, + "target": "*0x12a8b(%rip) # 0x146e8" + }, + { + "address": "0x1c75", + "from": ".text", + "indirect": false, + "target": "dup+0xe00" + }, + { + "address": "0x1c86", + "from": ".text", + "indirect": true, + "target": "*%r13" + }, + { + "address": "0x1c8b", + "from": ".text", + "indirect": true, + "target": "*0x12a57(%rip) # 0x146e8" + }, + { + "address": "0x1cbb", + "from": ".text", + "indirect": true, + "target": "*0x12a2f(%rip) # 0x146f0" + }, + { + "address": "0x1cc1", + "from": ".text", + "indirect": true, + "target": "*%r13" + }, + { + "address": "0x1cc6", + "from": ".text", + "indirect": true, + "target": "*0x12a1c(%rip) # 0x146e8" + }, + { + "address": "0x1ce8", + "from": ".text", + "indirect": false, + "target": "dup+0x1280" + }, + { + "address": "0x1cf9", + "from": ".text", + "indirect": true, + "target": "*%r13" + }, + { + "address": "0x1cfe", + "from": ".text", + "indirect": true, + "target": "*0x129e4(%rip) # 0x146e8" + }, + { + "address": "0x1d2e", + "from": ".text", + "indirect": true, + "target": "*0x129bc(%rip) # 0x146f0" + }, + { + "address": "0x1d34", + "from": ".text", + "indirect": true, + "target": "*%r13" + }, + { + "address": "0x1d39", + "from": ".text", + "indirect": true, + "target": "*0x129a9(%rip) # 0x146e8" + }, + { + "address": "0x1d62", + "from": ".text", + "indirect": false, + "target": "dup+0x7850" + }, + { + "address": "0x1d86", + "from": ".text", + "indirect": false, + "target": ".text+0x2130" + }, + { + "address": "0x1da4", + "from": ".text", + "indirect": false, + "target": ".text+0x360" + }, + { + "address": "0x1dd3", + "from": ".text", + "indirect": false, + "target": ".text+0x2870" + }, + { + "address": "0x1de4", + "from": ".text", + "indirect": false, + "target": "dup+0x36b0" + }, + { + "address": "0x1dff", + "from": ".text", + "indirect": true, + "target": "*0x1292b(%rip) # 0x14730" + }, + { + "address": "0x1e21", + "from": ".text", + "indirect": true, + "target": "*0x12909(%rip) # 0x14730" + }, + { + "address": "0x1e39", + "from": ".text", + "indirect": true, + "target": "*0x128f1(%rip) # 0x14730" + }, + { + "address": "0x1e4e", + "from": ".text", + "indirect": false, + "target": ".text+0x28f0" + }, + { + "address": "0x1e75", + "from": ".text", + "indirect": true, + "target": "*0x128bd(%rip) # 0x14738" + }, + { + "address": "0x1ea3", + "from": ".text", + "indirect": false, + "target": "dup+0x1ec0" + }, + { + "address": "0x1ec0", + "from": ".text", + "indirect": false, + "target": "dup+0x13a0" + }, + { + "address": "0x1ed5", + "from": ".text", + "indirect": true, + "target": "*%r12" + }, + { + "address": "0x1ee1", + "from": ".text", + "indirect": true, + "target": "*%r13" + }, + { + "address": "0x1f0c", + "from": ".text", + "indirect": true, + "target": "*0x127de(%rip) # 0x146f0" + }, + { + "address": "0x1f12", + "from": ".text", + "indirect": true, + "target": "*%r12" + }, + { + "address": "0x1f17", + "from": ".text", + "indirect": true, + "target": "*%r13" + }, + { + "address": "0x1f2c", + "from": ".text", + "indirect": false, + "target": "dup+0x1e50" + }, + { + "address": "0x1f3b", + "from": ".text", + "indirect": true, + "target": "*%r13" + }, + { + "address": "0x1f66", + "from": ".text", + "indirect": true, + "target": "*0x12784(%rip) # 0x146f0" + }, + { + "address": "0x1f6e", + "from": ".text", + "indirect": false, + "target": "dup+0x1e50" + }, + { + "address": "0x1f76", + "from": ".text", + "indirect": true, + "target": "*%r13" + }, + { + "address": "0x1f9f", + "from": ".text", + "indirect": false, + "target": "dup+0x13a0" + }, + { + "address": "0x1fb0", + "from": ".text", + "indirect": true, + "target": "*%r12" + }, + { + "address": "0x1fbc", + "from": ".text", + "indirect": true, + "target": "*%r13" + }, + { + "address": "0x1fe7", + "from": ".text", + "indirect": true, + "target": "*0x12703(%rip) # 0x146f0" + }, + { + "address": "0x1fed", + "from": ".text", + "indirect": true, + "target": "*%r12" + }, + { + "address": "0x1ff2", + "from": ".text", + "indirect": true, + "target": "*%r13" + }, + { + "address": "0x2012", + "from": ".text", + "indirect": false, + "target": "dup+0x1350" + }, + { + "address": "0x2023", + "from": ".text", + "indirect": true, + "target": "*%r12" + }, + { + "address": "0x202f", + "from": ".text", + "indirect": true, + "target": "*%r13" + }, + { + "address": "0x205a", + "from": ".text", + "indirect": true, + "target": "*0x12690(%rip) # 0x146f0" + }, + { + "address": "0x2060", + "from": ".text", + "indirect": true, + "target": "*%r12" + }, + { + "address": "0x2065", + "from": ".text", + "indirect": true, + "target": "*%r13" + }, + { + "address": "0x2088", + "from": ".text", + "indirect": false, + "target": "dup+0x13a0" + }, + { + "address": "0x2099", + "from": ".text", + "indirect": true, + "target": "*%r12" + }, + { + "address": "0x20a5", + "from": ".text", + "indirect": true, + "target": "*%r13" + }, + { + "address": "0x20d0", + "from": ".text", + "indirect": true, + "target": "*0x1261a(%rip) # 0x146f0" + }, + { + "address": "0x20d6", + "from": ".text", + "indirect": true, + "target": "*%r12" + }, + { + "address": "0x20db", + "from": ".text", + "indirect": true, + "target": "*%r13" + }, + { + "address": "0x20f1", + "from": ".text", + "indirect": false, + "target": "dup+0x3410" + }, + { + "address": "0x2119", + "from": ".text", + "indirect": false, + "target": "dup+0x1350" + }, + { + "address": "0x2167", + "from": ".text", + "indirect": false, + "target": "dup+0x1be0" + }, + { + "address": "0x2181", + "from": ".text", + "indirect": true, + "target": "*0x125b9(%rip) # 0x14740" + }, + { + "address": "0x2193", + "from": ".text", + "indirect": false, + "target": "dup+0x1280" + }, + { + "address": "0x21bb", + "from": ".text", + "indirect": false, + "target": "dup+0x1880" + }, + { + "address": "0x21d6", + "from": ".text", + "indirect": false, + "target": "dup+0x1c10" + }, + { + "address": "0x21e0", + "from": ".text", + "indirect": true, + "target": "*0x124fa(%rip) # 0x146e0" + }, + { + "address": "0x21e8", + "from": ".text", + "indirect": true, + "target": "*0x124fa(%rip) # 0x146e8" + }, + { + "address": "0x2216", + "from": ".text", + "indirect": true, + "target": "*0x124d4(%rip) # 0x146f0" + }, + { + "address": "0x221c", + "from": ".text", + "indirect": true, + "target": "*0x124be(%rip) # 0x146e0" + }, + { + "address": "0x2224", + "from": ".text", + "indirect": true, + "target": "*0x124be(%rip) # 0x146e8" + }, + { + "address": "0x223d", + "from": ".text", + "indirect": false, + "target": "dup+0x3410" + }, + { + "address": "0x2253", + "from": ".text", + "indirect": false, + "target": "dup+0x1c40" + }, + { + "address": "0x2268", + "from": ".text", + "indirect": true, + "target": "*%r12" + }, + { + "address": "0x2274", + "from": ".text", + "indirect": true, + "target": "*%r13" + }, + { + "address": "0x229f", + "from": ".text", + "indirect": true, + "target": "*0x1244b(%rip) # 0x146f0" + }, + { + "address": "0x22a5", + "from": ".text", + "indirect": true, + "target": "*%r12" + }, + { + "address": "0x22aa", + "from": ".text", + "indirect": true, + "target": "*%r13" + }, + { + "address": "0x22c0", + "from": ".text", + "indirect": false, + "target": "dup+0x3410" + }, + { + "address": "0x22d0", + "from": ".text", + "indirect": false, + "target": "dup+0x1e50" + }, + { + "address": "0x22d8", + "from": ".text", + "indirect": true, + "target": "*0x1240a(%rip) # 0x146e8" + }, + { + "address": "0x2306", + "from": ".text", + "indirect": true, + "target": "*0x123e4(%rip) # 0x146f0" + }, + { + "address": "0x230e", + "from": ".text", + "indirect": false, + "target": "dup+0x1e50" + }, + { + "address": "0x2316", + "from": ".text", + "indirect": true, + "target": "*0x123cc(%rip) # 0x146e8" + }, + { + "address": "0x232f", + "from": ".text", + "indirect": false, + "target": "dup+0x3410" + }, + { + "address": "0x2371", + "from": ".text", + "indirect": true, + "target": "*0x12359(%rip) # 0x146d0" + }, + { + "address": "0x238f", + "from": ".text", + "indirect": true, + "target": "*0x123b3(%rip) # 0x14748" + }, + { + "address": "0x23a0", + "from": ".text", + "indirect": true, + "target": "*%r12" + }, + { + "address": "0x23ac", + "from": ".text", + "indirect": true, + "target": "*%r13" + }, + { + "address": "0x23d7", + "from": ".text", + "indirect": true, + "target": "*0x12313(%rip) # 0x146f0" + }, + { + "address": "0x23dd", + "from": ".text", + "indirect": true, + "target": "*%r12" + }, + { + "address": "0x23e2", + "from": ".text", + "indirect": true, + "target": "*%r13" + }, + { + "address": "0x23f8", + "from": ".text", + "indirect": false, + "target": "dup+0x3410" + }, + { + "address": "0x241a", + "from": ".text", + "indirect": true, + "target": "*0x122d8(%rip) # 0x146f8" + }, + { + "address": "0x2442", + "from": ".text", + "indirect": true, + "target": "*0x12300(%rip) # 0x14748" + }, + { + "address": "0x2450", + "from": ".text", + "indirect": true, + "target": "*0x1228a(%rip) # 0x146e0" + }, + { + "address": "0x245f", + "from": ".text", + "indirect": true, + "target": "*%r13" + }, + { + "address": "0x248a", + "from": ".text", + "indirect": true, + "target": "*0x12260(%rip) # 0x146f0" + }, + { + "address": "0x2490", + "from": ".text", + "indirect": true, + "target": "*0x1224a(%rip) # 0x146e0" + }, + { + "address": "0x2498", + "from": ".text", + "indirect": true, + "target": "*%r13" + }, + { + "address": "0x24ae", + "from": ".text", + "indirect": false, + "target": "dup+0x3410" + }, + { + "address": "0x24b7", + "from": ".text", + "indirect": true, + "target": "*0x1224b(%rip) # 0x14708" + }, + { + "address": "0x24c9", + "from": ".text", + "indirect": true, + "target": "*%r12" + }, + { + "address": "0x24d5", + "from": ".text", + "indirect": true, + "target": "*%r13" + }, + { + "address": "0x2500", + "from": ".text", + "indirect": true, + "target": "*0x121ea(%rip) # 0x146f0" + }, + { + "address": "0x2506", + "from": ".text", + "indirect": true, + "target": "*%r12" + }, + { + "address": "0x250b", + "from": ".text", + "indirect": true, + "target": "*%r13" + }, + { + "address": "0x2554", + "from": ".text", + "indirect": true, + "target": "*0x121f6(%rip) # 0x14750" + }, + { + "address": "0x257c", + "from": ".text", + "indirect": true, + "target": "*0x12186(%rip) # 0x14708" + }, + { + "address": "0x25d3", + "from": ".text", + "indirect": true, + "target": "*%r14" + }, + { + "address": "0x262d", + "from": ".text", + "indirect": true, + "target": "*0x120ad(%rip) # 0x146e0" + }, + { + "address": "0x2641", + "from": ".text", + "indirect": true, + "target": "*0x12099(%rip) # 0x146e0" + }, + { + "address": "0x2667", + "from": ".text", + "indirect": true, + "target": "*0x1208b(%rip) # 0x146f8" + }, + { + "address": "0x26c6", + "from": ".text", + "indirect": true, + "target": "*%r14" + }, + { + "address": "0x26ec", + "from": ".text", + "indirect": true, + "target": "*0x12016(%rip) # 0x14708" + }, + { + "address": "0x2777", + "from": ".text", + "indirect": true, + "target": "*0x11f8b(%rip) # 0x14708" + }, + { + "address": "0x277d", + "from": ".text", + "indirect": true, + "target": "*0x11f5d(%rip) # 0x146e0" + }, + { + "address": "0x27ce", + "from": ".text", + "indirect": true, + "target": "*0x11f8c(%rip) # 0x14760" + }, + { + "address": "0x27f5", + "from": ".text", + "indirect": true, + "target": "*%r14" + }, + { + "address": "0x3719", + "from": "dup", + "indirect": true, + "target": "*0x11049(%rip) # 0x14768" + }, + { + "address": "0x3757", + "from": "dup", + "indirect": false, + "target": "dup+0xec0" + }, + { + "address": "0x375e", + "from": "dup", + "indirect": true, + "target": "*0x10f7c(%rip) # 0x146e0" + }, + { + "address": "0x3791", + "from": "dup", + "indirect": true, + "target": "*0x10fd1(%rip) # 0x14768" + }, + { + "address": "0x380a", + "from": "dup", + "indirect": true, + "target": "*0x10f60(%rip) # 0x14770" + }, + { + "address": "0x381b", + "from": "dup", + "indirect": true, + "target": "*0x10ebf(%rip) # 0x146e0" + }, + { + "address": "0x384b", + "from": "dup", + "indirect": true, + "target": "*%r12" + }, + { + "address": "0x3876", + "from": "dup", + "indirect": true, + "target": "*0x10efc(%rip) # 0x14778" + }, + { + "address": "0x3886", + "from": "dup", + "indirect": true, + "target": "*0x10e54(%rip) # 0x146e0" + }, + { + "address": "0x38c5", + "from": "dup", + "indirect": false, + "target": "dup+0x6b70" + }, + { + "address": "0x3906", + "from": "dup", + "indirect": false, + "target": "dup+0x1060" + }, + { + "address": "0x3919", + "from": "dup", + "indirect": true, + "target": "*0x10e49(%rip) # 0x14768" + }, + { + "address": "0x393e", + "from": "dup", + "indirect": false, + "target": "dup+0xec0" + }, + { + "address": "0x397c", + "from": "dup", + "indirect": true, + "target": "*0x10d4e(%rip) # 0x146d0" + }, + { + "address": "0x398b", + "from": "dup", + "indirect": true, + "target": "*0x10d4f(%rip) # 0x146e0" + }, + { + "address": "0x39aa", + "from": "dup", + "indirect": false, + "target": "dup+0x6d40" + }, + { + "address": "0x39ca", + "from": "dup", + "indirect": false, + "target": "dup+0x6e30" + }, + { + "address": "0x39fa", + "from": "dup", + "indirect": false, + "target": ".text+0x2850" + }, + { + "address": "0x3a08", + "from": "dup", + "indirect": false, + "target": "dup+0x6e30" + }, + { + "address": "0x3a2f", + "from": "dup", + "indirect": false, + "target": "dup+0x1060" + }, + { + "address": "0x3a66", + "from": "dup", + "indirect": true, + "target": "*0x10cfc(%rip) # 0x14768" + }, + { + "address": "0x3a98", + "from": "dup", + "indirect": false, + "target": "dup+0x1060" + }, + { + "address": "0x3aad", + "from": "dup", + "indirect": false, + "target": "dup+0xec0" + }, + { + "address": "0x3ad2", + "from": "dup", + "indirect": false, + "target": "dup+0x1060" + }, + { + "address": "0x3baa", + "from": "dup", + "indirect": false, + "target": "dup+0x1060" + }, + { + "address": "0x3bf5", + "from": "dup", + "indirect": false, + "target": "dup+0x1060" + }, + { + "address": "0x3c36", + "from": "dup", + "indirect": false, + "target": "dup+0x1060" + }, + { + "address": "0x3c86", + "from": "dup", + "indirect": false, + "target": "dup+0x1060" + }, + { + "address": "0x3cca", + "from": "dup", + "indirect": false, + "target": "dup+0x1060" + }, + { + "address": "0x3d16", + "from": "dup", + "indirect": false, + "target": "dup+0x1060" + }, + { + "address": "0x3d68", + "from": "dup", + "indirect": false, + "target": "dup+0x1060" + }, + { + "address": "0x3dba", + "from": "dup", + "indirect": false, + "target": "dup+0x1060" + }, + { + "address": "0x3e62", + "from": "dup", + "indirect": false, + "target": "dup+0x1060" + }, + { + "address": "0x402a", + "from": "dup", + "indirect": false, + "target": "dup+0x1060" + }, + { + "address": "0x4040", + "from": "dup", + "indirect": true, + "target": "*0x10722(%rip) # 0x14768" + }, + { + "address": "0x40a7", + "from": "dup", + "indirect": false, + "target": "dup+0x2120" + }, + { + "address": "0x40c1", + "from": "dup", + "indirect": false, + "target": "dup+0x1060" + }, + { + "address": "0x410b", + "from": "dup", + "indirect": false, + "target": "dup+0x1060" + }, + { + "address": "0x4154", + "from": "dup", + "indirect": false, + "target": "dup+0x1060" + }, + { + "address": "0x4203", + "from": "dup", + "indirect": false, + "target": "dup+0x6b70" + }, + { + "address": "0x421c", + "from": "dup", + "indirect": false, + "target": "dup+0x6b70" + }, + { + "address": "0x4250", + "from": "dup", + "indirect": false, + "target": "dup+0x1060" + }, + { + "address": "0x441e", + "from": "dup", + "indirect": false, + "target": "dup+0x1060" + }, + { + "address": "0x4434", + "from": "dup", + "indirect": true, + "target": "*0x1032e(%rip) # 0x14768" + }, + { + "address": "0x449b", + "from": "dup", + "indirect": false, + "target": "dup+0x2120" + }, + { + "address": "0x44b4", + "from": "dup", + "indirect": false, + "target": "dup+0x1060" + }, + { + "address": "0x44ff", + "from": "dup", + "indirect": false, + "target": "dup+0x1880" + }, + { + "address": "0x452b", + "from": "dup", + "indirect": false, + "target": "dup+0x1880" + }, + { + "address": "0x455b", + "from": "dup", + "indirect": false, + "target": "dup+0x1880" + }, + { + "address": "0x458b", + "from": "dup", + "indirect": false, + "target": "dup+0x1880" + }, + { + "address": "0x45b9", + "from": "dup", + "indirect": false, + "target": "dup+0x1880" + }, + { + "address": "0x45ec", + "from": "dup", + "indirect": false, + "target": "dup+0x1880" + }, + { + "address": "0x4619", + "from": "dup", + "indirect": false, + "target": "dup+0x1880" + }, + { + "address": "0x464a", + "from": "dup", + "indirect": false, + "target": "dup+0x1880" + }, + { + "address": "0x467a", + "from": "dup", + "indirect": false, + "target": "dup+0x1880" + }, + { + "address": "0x46a9", + "from": "dup", + "indirect": false, + "target": "dup+0x1880" + }, + { + "address": "0x46d9", + "from": "dup", + "indirect": false, + "target": "dup+0x1880" + }, + { + "address": "0x4709", + "from": "dup", + "indirect": false, + "target": "dup+0x6b70" + }, + { + "address": "0x4723", + "from": "dup", + "indirect": false, + "target": "dup+0x6b70" + }, + { + "address": "0x4740", + "from": "dup", + "indirect": false, + "target": "dup+0x14d0" + }, + { + "address": "0x4764", + "from": "dup", + "indirect": false, + "target": "dup+0x6b70" + }, + { + "address": "0x477d", + "from": "dup", + "indirect": false, + "target": "dup+0x6b70" + }, + { + "address": "0x4798", + "from": "dup", + "indirect": false, + "target": "dup+0x14d0" + }, + { + "address": "0x47d4", + "from": "dup", + "indirect": false, + "target": "dup+0x6b70" + }, + { + "address": "0x47ed", + "from": "dup", + "indirect": false, + "target": "dup+0x6b70" + }, + { + "address": "0x4808", + "from": "dup", + "indirect": false, + "target": "dup+0x14d0" + }, + { + "address": "0x489a", + "from": "dup", + "indirect": true, + "target": "*0xfee0(%rip) # 0x14780" + }, + { + "address": "0x48d4", + "from": "dup", + "indirect": true, + "target": "*0xfeae(%rip) # 0x14788" + }, + { + "address": "0x48e6", + "from": "dup", + "indirect": true, + "target": "*0xfea4(%rip) # 0x14790" + }, + { + "address": "0x4993", + "from": "dup", + "indirect": true, + "target": "*0xfdff(%rip) # 0x14798" + }, + { + "address": "0x4ac0", + "from": "dup", + "indirect": true, + "target": "*(%r14)" + }, + { + "address": "0x4ad2", + "from": "dup", + "indirect": true, + "target": "*0x8(%r14)" + }, + { + "address": "0x4aec", + "from": "dup", + "indirect": true, + "target": "*0x10(%r14)" + }, + { + "address": "0x4bf8", + "from": "dup", + "indirect": true, + "target": "*%rcx" + }, + { + "address": "0x4c05", + "from": "dup", + "indirect": true, + "target": "*0x8(%r15)" + }, + { + "address": "0x4c14", + "from": "dup", + "indirect": true, + "target": "*0x10(%r15)" + }, + { + "address": "0x4c23", + "from": "dup", + "indirect": true, + "target": "*0x18(%r15)" + }, + { + "address": "0x4c45", + "from": "dup", + "indirect": true, + "target": "*0x20(%r15)" + }, + { + "address": "0x4cde", + "from": "dup", + "indirect": true, + "target": "*%rcx" + }, + { + "address": "0x4cf0", + "from": "dup", + "indirect": true, + "target": "*0x30(%r14)" + }, + { + "address": "0x4d06", + "from": "dup", + "indirect": true, + "target": "*0x38(%r14)" + }, + { + "address": "0x4d1b", + "from": "dup", + "indirect": true, + "target": "*0x40(%r14)" + }, + { + "address": "0x4d31", + "from": "dup", + "indirect": true, + "target": "*0x48(%r14)" + }, + { + "address": "0x4ddd", + "from": "dup", + "indirect": true, + "target": "*%rax" + }, + { + "address": "0x4e07", + "from": "dup", + "indirect": true, + "target": "*%rax" + }, + { + "address": "0x4e2f", + "from": "dup", + "indirect": true, + "target": "*%rax" + }, + { + "address": "0x4e57", + "from": "dup", + "indirect": true, + "target": "*%rax" + }, + { + "address": "0x4e7f", + "from": "dup", + "indirect": true, + "target": "*%rax" + }, + { + "address": "0x4eab", + "from": "dup", + "indirect": true, + "target": "*%rax" + }, + { + "address": "0x4ed7", + "from": "dup", + "indirect": true, + "target": "*%rax" + }, + { + "address": "0x4f02", + "from": "dup", + "indirect": true, + "target": "*%rax" + }, + { + "address": "0x4f83", + "from": "dup", + "indirect": true, + "target": "*%r9" + }, + { + "address": "0x4f95", + "from": "dup", + "indirect": true, + "target": "*0x8(%r12)" + }, + { + "address": "0x4fb4", + "from": "dup", + "indirect": true, + "target": "*(%r12)" + }, + { + "address": "0x4fc7", + "from": "dup", + "indirect": true, + "target": "*0x10(%r12)" + }, + { + "address": "0x4fd8", + "from": "dup", + "indirect": true, + "target": "*(%r12)" + }, + { + "address": "0x5009", + "from": "dup", + "indirect": false, + "target": "dup+0x2280" + }, + { + "address": "0x5039", + "from": "dup", + "indirect": false, + "target": "dup+0x2370" + }, + { + "address": "0x5061", + "from": "dup", + "indirect": true, + "target": "*0xf739(%rip) # 0x147a0" + }, + { + "address": "0x506b", + "from": "dup", + "indirect": true, + "target": "*0xf66f(%rip) # 0x146e0" + }, + { + "address": "0x50af", + "from": "dup", + "indirect": true, + "target": "*0xf6b3(%rip) # 0x14768" + }, + { + "address": "0x5127", + "from": "dup", + "indirect": true, + "target": "*%r14" + }, + { + "address": "0x5143", + "from": "dup", + "indirect": true, + "target": "*0xf627(%rip) # 0x14770" + }, + { + "address": "0x515b", + "from": "dup", + "indirect": true, + "target": "*0xf607(%rip) # 0x14768" + }, + { + "address": "0x518d", + "from": "dup", + "indirect": true, + "target": "*0xf54d(%rip) # 0x146e0" + }, + { + "address": "0x51ab", + "from": "dup", + "indirect": true, + "target": "*0xf52f(%rip) # 0x146e0" + }, + { + "address": "0x51f1", + "from": "dup", + "indirect": false, + "target": "dup+0x2490" + }, + { + "address": "0x5222", + "from": "dup", + "indirect": false, + "target": "dup+0x1280" + }, + { + "address": "0x523c", + "from": "dup", + "indirect": false, + "target": "dup+0x7850" + }, + { + "address": "0x5293", + "from": "dup", + "indirect": true, + "target": "*0xf507(%rip) # 0x147a0" + }, + { + "address": "0x529d", + "from": "dup", + "indirect": true, + "target": "*0xf43d(%rip) # 0x146e0" + }, + { + "address": "0x52ed", + "from": "dup", + "indirect": false, + "target": "dup+0x2630" + }, + { + "address": "0x52f9", + "from": "dup", + "indirect": true, + "target": "*0xf3e1(%rip) # 0x146e0" + }, + { + "address": "0x5328", + "from": "dup", + "indirect": false, + "target": "dup+0x2790" + }, + { + "address": "0x5331", + "from": "dup", + "indirect": true, + "target": "*0xf3a9(%rip) # 0x146e0" + }, + { + "address": "0x535b", + "from": "dup", + "indirect": true, + "target": "*0xf43f(%rip) # 0x147a0" + }, + { + "address": "0x5365", + "from": "dup", + "indirect": true, + "target": "*0xf375(%rip) # 0x146e0" + }, + { + "address": "0x538b", + "from": "dup", + "indirect": true, + "target": "*0xf40f(%rip) # 0x147a0" + }, + { + "address": "0x5395", + "from": "dup", + "indirect": true, + "target": "*0xf345(%rip) # 0x146e0" + }, + { + "address": "0x53bb", + "from": "dup", + "indirect": false, + "target": "dup+0x7060" + }, + { + "address": "0x53eb", + "from": "dup", + "indirect": false, + "target": "dup+0x7b30" + }, + { + "address": "0x540b", + "from": "dup", + "indirect": false, + "target": "dup+0x7ab0" + }, + { + "address": "0x544b", + "from": "dup", + "indirect": false, + "target": "dup+0x6d40" + }, + { + "address": "0x5529", + "from": "dup", + "indirect": false, + "target": "dup+0x2c50" + }, + { + "address": "0x5752", + "from": "dup", + "indirect": true, + "target": "*0xf028(%rip) # 0x14780" + }, + { + "address": "0x575e", + "from": "dup", + "indirect": false, + "target": "dup+0x2c50" + }, + { + "address": "0x577b", + "from": "dup", + "indirect": true, + "target": "*0xefff(%rip) # 0x14780" + }, + { + "address": "0x57c4", + "from": "dup", + "indirect": false, + "target": "dup+0x2c50" + }, + { + "address": "0x588c", + "from": "dup", + "indirect": false, + "target": "dup+0x2c50" + }, + { + "address": "0x58af", + "from": "dup", + "indirect": false, + "target": "dup+0x2e20" + }, + { + "address": "0x5955", + "from": "dup", + "indirect": false, + "target": "dup+0x3370" + }, + { + "address": "0x5962", + "from": "dup", + "indirect": false, + "target": "dup+0x5120" + }, + { + "address": "0x596c", + "from": "dup", + "indirect": false, + "target": "dup+0x3780" + }, + { + "address": "0x598d", + "from": "dup", + "indirect": false, + "target": "dup+0x5d90" + }, + { + "address": "0x599e", + "from": "dup", + "indirect": false, + "target": "dup+0xb070" + }, + { + "address": "0x59bd", + "from": "dup", + "indirect": false, + "target": "dup+0x86f0" + }, + { + "address": "0x59d5", + "from": "dup", + "indirect": false, + "target": "dup+0x35f0" + }, + { + "address": "0x5a32", + "from": "dup", + "indirect": false, + "target": "dup+0x5d90" + }, + { + "address": "0x5a51", + "from": "dup", + "indirect": false, + "target": "dup+0x5d90" + }, + { + "address": "0x5a70", + "from": "dup", + "indirect": false, + "target": "dup+0x5d90" + }, + { + "address": "0x5a8f", + "from": "dup", + "indirect": false, + "target": "dup+0x5d90" + }, + { + "address": "0x5a9d", + "from": "dup", + "indirect": true, + "target": "*%rbx" + }, + { + "address": "0x5aa2", + "from": "dup", + "indirect": true, + "target": "*%r15" + }, + { + "address": "0x5ac0", + "from": "dup", + "indirect": false, + "target": "dup+0x5d90" + }, + { + "address": "0x5adf", + "from": "dup", + "indirect": false, + "target": "dup+0x5d90" + }, + { + "address": "0x5aff", + "from": "dup", + "indirect": false, + "target": "dup+0x5d90" + }, + { + "address": "0x5b1b", + "from": "dup", + "indirect": false, + "target": "dup+0x5d90" + }, + { + "address": "0x5b35", + "from": "dup", + "indirect": false, + "target": "dup+0xa000" + }, + { + "address": "0x5b46", + "from": "dup", + "indirect": false, + "target": "dup+0xab50" + }, + { + "address": "0x5b4f", + "from": "dup", + "indirect": false, + "target": "dup+0x82f0" + }, + { + "address": "0x5b6a", + "from": "dup", + "indirect": false, + "target": "dup+0x8410" + }, + { + "address": "0x5b7a", + "from": "dup", + "indirect": false, + "target": "dup+0x8600" + }, + { + "address": "0x5b83", + "from": "dup", + "indirect": false, + "target": "dup+0x86e0" + }, + { + "address": "0x5bad", + "from": "dup", + "indirect": false, + "target": "dup+0x5d90" + }, + { + "address": "0x5bd2", + "from": "dup", + "indirect": false, + "target": ".text" + }, + { + "address": "0x5be8", + "from": "dup", + "indirect": false, + "target": "dup+0x85b0" + }, + { + "address": "0x5bf3", + "from": "dup", + "indirect": false, + "target": "dup+0x8600" + }, + { + "address": "0x5bff", + "from": "dup", + "indirect": false, + "target": "dup+0x86e0" + }, + { + "address": "0x5c2e", + "from": "dup", + "indirect": false, + "target": "dup+0x5d90" + }, + { + "address": "0x5c46", + "from": "dup", + "indirect": true, + "target": "*%rax" + }, + { + "address": "0x5c67", + "from": "dup", + "indirect": true, + "target": "*0x56e93(%rip) # 0x5cb00" + }, + { + "address": "0x5c91", + "from": "dup", + "indirect": true, + "target": "*(%rbx)" + }, + { + "address": "0x5ca7", + "from": "dup", + "indirect": true, + "target": "*(%rbx)" + }, + { + "address": "0x5cce", + "from": "dup", + "indirect": true, + "target": "*%rax" + }, + { + "address": "0x5ce7", + "from": "dup", + "indirect": true, + "target": "*(%rbx)" + }, + { + "address": "0x5e3c", + "from": "dup", + "indirect": true, + "target": "*0x56cc6(%rip) # 0x5cb08" + }, + { + "address": "0x5e57", + "from": "dup", + "indirect": true, + "target": "*%r15" + }, + { + "address": "0x5e73", + "from": "dup", + "indirect": true, + "target": "*%r15" + }, + { + "address": "0x5e90", + "from": "dup", + "indirect": true, + "target": "*0x56c7a(%rip) # 0x5cb10" + }, + { + "address": "0x5eb9", + "from": "dup", + "indirect": true, + "target": "*%r14" + }, + { + "address": "0x5ecd", + "from": "dup", + "indirect": false, + "target": "dup+0x3350" + }, + { + "address": "0x5f1a", + "from": "dup", + "indirect": true, + "target": "*%r12" + }, + { + "address": "0x5f37", + "from": "dup", + "indirect": true, + "target": "*%r12" + }, + { + "address": "0x5f55", + "from": "dup", + "indirect": true, + "target": "*0x56bb5(%rip) # 0x5cb10" + }, + { + "address": "0x5f7a", + "from": "dup", + "indirect": true, + "target": "*%r15" + }, + { + "address": "0x5f8e", + "from": "dup", + "indirect": false, + "target": "dup+0x3350" + }, + { + "address": "0x5fda", + "from": "dup", + "indirect": true, + "target": "*%r12" + }, + { + "address": "0x5ff7", + "from": "dup", + "indirect": true, + "target": "*%r12" + }, + { + "address": "0x6015", + "from": "dup", + "indirect": true, + "target": "*0x56af5(%rip) # 0x5cb10" + }, + { + "address": "0x6022", + "from": "dup", + "indirect": true, + "target": "*0x56af8(%rip) # 0x5cb20" + }, + { + "address": "0x602a", + "from": "dup", + "indirect": true, + "target": "*%r14" + }, + { + "address": "0x604f", + "from": "dup", + "indirect": true, + "target": "*%r15" + }, + { + "address": "0x6063", + "from": "dup", + "indirect": false, + "target": "dup+0x3350" + }, + { + "address": "0x609c", + "from": "dup", + "indirect": false, + "target": "dup+0x5d90" + }, + { + "address": "0x60c3", + "from": "dup", + "indirect": false, + "target": "dup+0x5d90" + }, + { + "address": "0x60e6", + "from": "dup", + "indirect": false, + "target": "dup+0x5d90" + }, + { + "address": "0x6109", + "from": "dup", + "indirect": false, + "target": "dup+0x5d90" + }, + { + "address": "0x612c", + "from": "dup", + "indirect": false, + "target": "dup+0x5d90" + }, + { + "address": "0x61e9", + "from": "dup", + "indirect": false, + "target": "dup+0x3cf0" + }, + { + "address": "0x621a", + "from": "dup", + "indirect": false, + "target": "dup+0x3cf0" + }, + { + "address": "0x62cb", + "from": "dup", + "indirect": false, + "target": "dup+0x3cf0" + }, + { + "address": "0x639d", + "from": "dup", + "indirect": false, + "target": "dup+0x3cf0" + }, + { + "address": "0x6430", + "from": "dup", + "indirect": false, + "target": "dup+0x3cf0" + }, + { + "address": "0x6597", + "from": "dup", + "indirect": false, + "target": "dup+0x3cf0" + }, + { + "address": "0x79f5", + "from": "dup", + "indirect": false, + "target": "dup+0x3350" + }, + { + "address": "0x7aca", + "from": "dup", + "indirect": false, + "target": "dup+0x3350" + }, + { + "address": "0x8511", + "from": "dup", + "indirect": false, + "target": "dup+0x6400" + }, + { + "address": "0x852b", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x8551", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x857a", + "from": "dup", + "indirect": false, + "target": "dup+0x3850" + }, + { + "address": "0x858d", + "from": "dup", + "indirect": false, + "target": "dup+0x6b70" + }, + { + "address": "0x85a8", + "from": "dup", + "indirect": false, + "target": "dup+0x3850" + }, + { + "address": "0x85bb", + "from": "dup", + "indirect": false, + "target": "dup+0x6b70" + }, + { + "address": "0x8628", + "from": "dup", + "indirect": false, + "target": "dup+0x6400" + }, + { + "address": "0x8642", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x866a", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x86ac", + "from": "dup", + "indirect": false, + "target": "dup+0x3850" + }, + { + "address": "0x86ba", + "from": "dup", + "indirect": false, + "target": "dup+0x6b70" + }, + { + "address": "0x8728", + "from": "dup", + "indirect": true, + "target": "*%rax" + }, + { + "address": "0x8744", + "from": "dup", + "indirect": true, + "target": "*%rax" + }, + { + "address": "0x8785", + "from": "dup", + "indirect": false, + "target": "dup+0x3350" + }, + { + "address": "0x87c7", + "from": "dup", + "indirect": false, + "target": "dup+0x3350" + }, + { + "address": "0x880b", + "from": "dup", + "indirect": true, + "target": "*%rax" + }, + { + "address": "0x883f", + "from": "dup", + "indirect": false, + "target": "dup+0x3350" + }, + { + "address": "0x886f", + "from": "dup", + "indirect": false, + "target": "dup+0x3350" + }, + { + "address": "0x8891", + "from": "dup", + "indirect": false, + "target": "dup+0x3350" + }, + { + "address": "0x890b", + "from": "dup", + "indirect": true, + "target": "*%rax" + }, + { + "address": "0x8927", + "from": "dup", + "indirect": true, + "target": "*%rax" + }, + { + "address": "0x8968", + "from": "dup", + "indirect": false, + "target": "dup+0x3350" + }, + { + "address": "0x89aa", + "from": "dup", + "indirect": false, + "target": "dup+0x3350" + }, + { + "address": "0x89ee", + "from": "dup", + "indirect": true, + "target": "*%rax" + }, + { + "address": "0x8a22", + "from": "dup", + "indirect": false, + "target": "dup+0x3350" + }, + { + "address": "0x8a52", + "from": "dup", + "indirect": false, + "target": "dup+0x3350" + }, + { + "address": "0x8a74", + "from": "dup", + "indirect": false, + "target": "dup+0x3350" + }, + { + "address": "0x8aab", + "from": "dup", + "indirect": false, + "target": "dup+0x5dd0" + }, + { + "address": "0x8ada", + "from": "dup", + "indirect": false, + "target": "dup+0x5dd0" + }, + { + "address": "0x8b0b", + "from": "dup", + "indirect": false, + "target": "dup+0x5dd0" + }, + { + "address": "0x8b3b", + "from": "dup", + "indirect": false, + "target": "dup+0x5dd0" + }, + { + "address": "0x8b69", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x8b98", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x8bc7", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x8bf5", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x8c56", + "from": "dup", + "indirect": false, + "target": "dup+0x3350" + }, + { + "address": "0x8c73", + "from": "dup", + "indirect": false, + "target": "dup+0x5dd0" + }, + { + "address": "0x8ca2", + "from": "dup", + "indirect": true, + "target": "*%rcx" + }, + { + "address": "0x8ce3", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x8d3f", + "from": "dup", + "indirect": false, + "target": "dup+0x3350" + }, + { + "address": "0x8daf", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x8def", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x8e09", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x8e6a", + "from": "dup", + "indirect": true, + "target": "*%rax" + }, + { + "address": "0x8e8e", + "from": "dup", + "indirect": false, + "target": "dup+0x6400" + }, + { + "address": "0x8eb0", + "from": "dup", + "indirect": false, + "target": "dup+0x3350" + }, + { + "address": "0x8ed1", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x8f07", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x8f2b", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x8f45", + "from": "dup", + "indirect": true, + "target": "*%rax" + }, + { + "address": "0x8f74", + "from": "dup", + "indirect": false, + "target": "dup+0x6400" + }, + { + "address": "0x8f99", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x8fbc", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x8fda", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x8ff4", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x900c", + "from": "dup", + "indirect": true, + "target": "*%rax" + }, + { + "address": "0x9047", + "from": "dup", + "indirect": false, + "target": "dup+0x6400" + }, + { + "address": "0x906c", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x9086", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x90bc", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x90de", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x90f7", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x9123", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x9145", + "from": "dup", + "indirect": true, + "target": "*%rax" + }, + { + "address": "0x917a", + "from": "dup", + "indirect": false, + "target": "dup+0x6400" + }, + { + "address": "0x919f", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x91c8", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x920f", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x9236", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x9253", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x9271", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x930b", + "from": "dup", + "indirect": true, + "target": "*%rax" + }, + { + "address": "0x9352", + "from": "dup", + "indirect": false, + "target": "dup+0x6660" + }, + { + "address": "0x939b", + "from": "dup", + "indirect": false, + "target": "dup+0x6660" + }, + { + "address": "0x93b7", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x93f2", + "from": "dup", + "indirect": false, + "target": "dup+0x6660" + }, + { + "address": "0x9422", + "from": "dup", + "indirect": false, + "target": "dup+0x6660" + }, + { + "address": "0x9452", + "from": "dup", + "indirect": false, + "target": "dup+0x6660" + }, + { + "address": "0x948e", + "from": "dup", + "indirect": false, + "target": "dup+0x6660" + }, + { + "address": "0x94c2", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x9501", + "from": "dup", + "indirect": false, + "target": "dup+0x3350" + }, + { + "address": "0x9524", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x9544", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x9579", + "from": "dup", + "indirect": false, + "target": "dup+0x3350" + }, + { + "address": "0x9600", + "from": "dup", + "indirect": false, + "target": "dup+0x6400" + }, + { + "address": "0x961a", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x9658", + "from": "dup", + "indirect": false, + "target": "dup+0x6400" + }, + { + "address": "0x9672", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x969a", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x96d4", + "from": "dup", + "indirect": false, + "target": "dup+0x6400" + }, + { + "address": "0x96ee", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x971b", + "from": "dup", + "indirect": false, + "target": "dup+0x5dd0" + }, + { + "address": "0x9744", + "from": "dup", + "indirect": false, + "target": "dup+0x6400" + }, + { + "address": "0x975e", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x978b", + "from": "dup", + "indirect": false, + "target": "dup+0x5dd0" + }, + { + "address": "0x97b1", + "from": "dup", + "indirect": false, + "target": "dup+0x6400" + }, + { + "address": "0x97cb", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x97f7", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x9821", + "from": "dup", + "indirect": false, + "target": "dup+0x6400" + }, + { + "address": "0x983b", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x9867", + "from": "dup", + "indirect": false, + "target": "dup+0x5dd0" + }, + { + "address": "0x9891", + "from": "dup", + "indirect": false, + "target": "dup+0x6400" + }, + { + "address": "0x98ab", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x98d7", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x9901", + "from": "dup", + "indirect": false, + "target": "dup+0x6400" + }, + { + "address": "0x991b", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x9947", + "from": "dup", + "indirect": false, + "target": "dup+0x5dd0" + }, + { + "address": "0x9977", + "from": "dup", + "indirect": false, + "target": "dup+0x6400" + }, + { + "address": "0x9991", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x99be", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x99e3", + "from": "dup", + "indirect": false, + "target": "dup+0x6400" + }, + { + "address": "0x99fd", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x9a2a", + "from": "dup", + "indirect": false, + "target": "dup+0x5dd0" + }, + { + "address": "0x9a57", + "from": "dup", + "indirect": false, + "target": "dup+0x6400" + }, + { + "address": "0x9a71", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x9a9e", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x9ac3", + "from": "dup", + "indirect": false, + "target": "dup+0x6400" + }, + { + "address": "0x9add", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x9b0a", + "from": "dup", + "indirect": false, + "target": "dup+0x5dd0" + }, + { + "address": "0x9b37", + "from": "dup", + "indirect": false, + "target": "dup+0x6400" + }, + { + "address": "0x9b51", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x9b7e", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x9ba3", + "from": "dup", + "indirect": false, + "target": "dup+0x6400" + }, + { + "address": "0x9bbd", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x9bea", + "from": "dup", + "indirect": false, + "target": "dup+0x5dd0" + }, + { + "address": "0x9c17", + "from": "dup", + "indirect": false, + "target": "dup+0x6400" + }, + { + "address": "0x9c31", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x9c5e", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x9c83", + "from": "dup", + "indirect": false, + "target": "dup+0x6400" + }, + { + "address": "0x9c9d", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x9cca", + "from": "dup", + "indirect": false, + "target": "dup+0x5dd0" + }, + { + "address": "0x9cf7", + "from": "dup", + "indirect": false, + "target": "dup+0x6400" + }, + { + "address": "0x9d11", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x9d3e", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x9d63", + "from": "dup", + "indirect": false, + "target": "dup+0x6400" + }, + { + "address": "0x9d7d", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x9daa", + "from": "dup", + "indirect": false, + "target": "dup+0x5dd0" + }, + { + "address": "0x9dd0", + "from": "dup", + "indirect": false, + "target": "dup+0x6400" + }, + { + "address": "0x9dea", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x9e22", + "from": "dup", + "indirect": false, + "target": "dup+0x6400" + }, + { + "address": "0x9e3e", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x9e5e", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x9e9c", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x9eb5", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x9eec", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x9f12", + "from": "dup", + "indirect": true, + "target": "*%rax" + }, + { + "address": "0x9f37", + "from": "dup", + "indirect": false, + "target": "dup+0x7510" + }, + { + "address": "0x9f74", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x9f9e", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0x9fd4", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0xa035", + "from": "dup", + "indirect": true, + "target": "*%rax" + }, + { + "address": "0xa072", + "from": "dup", + "indirect": false, + "target": "dup+0x7510" + }, + { + "address": "0xa0b9", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0xa0eb", + "from": "dup", + "indirect": false, + "target": "dup+0x5dd0" + }, + { + "address": "0xa105", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0xa12b", + "from": "dup", + "indirect": true, + "target": "*%rax" + }, + { + "address": "0xa16b", + "from": "dup", + "indirect": false, + "target": "dup+0x6400" + }, + { + "address": "0xa185", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0xa19b", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0xa1bb", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0xa1d1", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0xa207", + "from": "dup", + "indirect": false, + "target": "dup+0x7860" + }, + { + "address": "0xa220", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0xa24a", + "from": "dup", + "indirect": false, + "target": "dup+0x5dd0" + }, + { + "address": "0xa25f", + "from": "dup", + "indirect": false, + "target": "dup+0x7860" + }, + { + "address": "0xa279", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0xa294", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0xa2b9", + "from": "dup", + "indirect": false, + "target": "dup+0x7860" + }, + { + "address": "0xa2d6", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0xa2f2", + "from": "dup", + "indirect": false, + "target": "dup+0x5dd0" + }, + { + "address": "0xa305", + "from": "dup", + "indirect": false, + "target": "dup+0x7860" + }, + { + "address": "0xa31f", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0xa33a", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0xa369", + "from": "dup", + "indirect": false, + "target": "dup+0x5dd0" + }, + { + "address": "0xa395", + "from": "dup", + "indirect": false, + "target": "dup+0x5dd0" + }, + { + "address": "0xa3c8", + "from": "dup", + "indirect": false, + "target": "dup+0x6400" + }, + { + "address": "0xa3e2", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0xa40a", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0xa448", + "from": "dup", + "indirect": false, + "target": "dup+0x6400" + }, + { + "address": "0xa462", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0xa48a", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0xa4c5", + "from": "dup", + "indirect": false, + "target": "dup+0x6400" + }, + { + "address": "0xa4e1", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0xa509", + "from": "dup", + "indirect": false, + "target": "dup+0x5dd0" + }, + { + "address": "0xa535", + "from": "dup", + "indirect": false, + "target": "dup+0x6400" + }, + { + "address": "0xa551", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0xa579", + "from": "dup", + "indirect": false, + "target": "dup+0x5dd0" + }, + { + "address": "0xa5ed", + "from": "dup", + "indirect": false, + "target": "dup+0x3350" + }, + { + "address": "0xa605", + "from": "dup", + "indirect": true, + "target": "*0x52bc5(%rip) # 0x5d1d0" + }, + { + "address": "0xa622", + "from": "dup", + "indirect": true, + "target": "*0x52bc8(%rip) # 0x5d1f0" + }, + { + "address": "0xa639", + "from": "dup", + "indirect": false, + "target": "dup+0x3350" + }, + { + "address": "0xa65a", + "from": "dup", + "indirect": true, + "target": "*0x52b90(%rip) # 0x5d1f0" + }, + { + "address": "0xa671", + "from": "dup", + "indirect": false, + "target": "dup+0x3350" + }, + { + "address": "0xa692", + "from": "dup", + "indirect": true, + "target": "*0x52b58(%rip) # 0x5d1f0" + }, + { + "address": "0xa6a9", + "from": "dup", + "indirect": false, + "target": "dup+0x3350" + }, + { + "address": "0xa6ca", + "from": "dup", + "indirect": true, + "target": "*0x52b20(%rip) # 0x5d1f0" + }, + { + "address": "0xa6e1", + "from": "dup", + "indirect": false, + "target": "dup+0x3350" + }, + { + "address": "0xa707", + "from": "dup", + "indirect": false, + "target": "dup+0x3350" + }, + { + "address": "0xa71d", + "from": "dup", + "indirect": true, + "target": "*0x52ad5(%rip) # 0x5d1f8" + }, + { + "address": "0xa74f", + "from": "dup", + "indirect": true, + "target": "*0x52aab(%rip) # 0x5d200" + }, + { + "address": "0xa769", + "from": "dup", + "indirect": true, + "target": "*%r13" + }, + { + "address": "0xa7ab", + "from": "dup", + "indirect": true, + "target": "*0x52a3f(%rip) # 0x5d1f0" + }, + { + "address": "0xa7c2", + "from": "dup", + "indirect": false, + "target": "dup+0x3350" + }, + { + "address": "0xa7e7", + "from": "dup", + "indirect": false, + "target": "dup+0x3350" + }, + { + "address": "0xa808", + "from": "dup", + "indirect": true, + "target": "*0x529e2(%rip) # 0x5d1f0" + }, + { + "address": "0xa81f", + "from": "dup", + "indirect": false, + "target": "dup+0x3350" + }, + { + "address": "0xa852", + "from": "dup", + "indirect": true, + "target": "*0x52998(%rip) # 0x5d1f0" + }, + { + "address": "0xa869", + "from": "dup", + "indirect": false, + "target": "dup+0x3350" + }, + { + "address": "0xa891", + "from": "dup", + "indirect": true, + "target": "*0x52959(%rip) # 0x5d1f0" + }, + { + "address": "0xa8a8", + "from": "dup", + "indirect": false, + "target": "dup+0x3350" + }, + { + "address": "0xa8cc", + "from": "dup", + "indirect": true, + "target": "*0x5291e(%rip) # 0x5d1f0" + }, + { + "address": "0xa8e3", + "from": "dup", + "indirect": false, + "target": "dup+0x3350" + }, + { + "address": "0xa907", + "from": "dup", + "indirect": true, + "target": "*0x528e3(%rip) # 0x5d1f0" + }, + { + "address": "0xa91e", + "from": "dup", + "indirect": false, + "target": "dup+0x3350" + }, + { + "address": "0xa98e", + "from": "dup", + "indirect": false, + "target": "dup+0x7c90" + }, + { + "address": "0xa9a1", + "from": "dup", + "indirect": true, + "target": "*0x52829(%rip) # 0x5d1d0" + }, + { + "address": "0xa9b1", + "from": "dup", + "indirect": false, + "target": "dup+0x81d0" + }, + { + "address": "0xa9cc", + "from": "dup", + "indirect": true, + "target": "*0x52836(%rip) # 0x5d208" + }, + { + "address": "0xaa61", + "from": "dup", + "indirect": true, + "target": "*0x52769(%rip) # 0x5d1d0" + }, + { + "address": "0xaa73", + "from": "dup", + "indirect": true, + "target": "*0x5280f(%rip) # 0x5d288" + }, + { + "address": "0xaa7f", + "from": "dup", + "indirect": true, + "target": "*0x52803(%rip) # 0x5d288" + }, + { + "address": "0xaa9d", + "from": "dup", + "indirect": true, + "target": "*0x527d5(%rip) # 0x5d278" + }, + { + "address": "0xaaad", + "from": "dup", + "indirect": false, + "target": "dup+0x8890" + }, + { + "address": "0xaac7", + "from": "dup", + "indirect": false, + "target": "dup+0x97d0" + }, + { + "address": "0xaae9", + "from": "dup", + "indirect": true, + "target": "*0x52769(%rip) # 0x5d258" + }, + { + "address": "0xab16", + "from": "dup", + "indirect": false, + "target": "dup+0x81d0" + }, + { + "address": "0xac4a", + "from": "dup", + "indirect": true, + "target": "*0x525b8(%rip) # 0x5d208" + }, + { + "address": "0xacf1", + "from": "dup", + "indirect": true, + "target": "*0x52539(%rip) # 0x5d230" + }, + { + "address": "0xad4d", + "from": "dup", + "indirect": false, + "target": "dup+0x8410" + }, + { + "address": "0xadcc", + "from": "dup", + "indirect": true, + "target": "*0x10(%rdi)" + }, + { + "address": "0xadf6", + "from": "dup", + "indirect": false, + "target": "dup+0x84b0" + }, + { + "address": "0xae56", + "from": "dup", + "indirect": false, + "target": "dup+0x8510" + }, + { + "address": "0xaebe", + "from": "dup", + "indirect": true, + "target": "*0x20(%rdi)" + }, + { + "address": "0xaee3", + "from": "dup", + "indirect": false, + "target": "dup+0x85b0" + }, + { + "address": "0xaf43", + "from": "dup", + "indirect": false, + "target": "dup+0x8600" + }, + { + "address": "0xaf69", + "from": "dup", + "indirect": true, + "target": "*0x28(%rbx)" + }, + { + "address": "0xafc2", + "from": "dup", + "indirect": true, + "target": "*0x52268(%rip) # 0x5d230" + }, + { + "address": "0xb00c", + "from": "dup", + "indirect": false, + "target": "dup+0x5d90" + }, + { + "address": "0xb033", + "from": "dup", + "indirect": false, + "target": "dup+0x5d90" + }, + { + "address": "0xb05a", + "from": "dup", + "indirect": false, + "target": "dup+0x5d90" + }, + { + "address": "0xb081", + "from": "dup", + "indirect": false, + "target": "dup+0x5d90" + }, + { + "address": "0xb0a8", + "from": "dup", + "indirect": false, + "target": "dup+0x5d90" + }, + { + "address": "0xb0cf", + "from": "dup", + "indirect": false, + "target": "dup+0x5d90" + }, + { + "address": "0xb0f6", + "from": "dup", + "indirect": false, + "target": "dup+0x5d90" + }, + { + "address": "0xb119", + "from": "dup", + "indirect": false, + "target": "dup+0x5d90" + }, + { + "address": "0xb13c", + "from": "dup", + "indirect": false, + "target": "dup+0x5d90" + }, + { + "address": "0xb14e", + "from": "dup", + "indirect": false, + "target": "dup+0x9e30" + }, + { + "address": "0xb15a", + "from": "dup", + "indirect": false, + "target": "dup+0x95d0" + }, + { + "address": "0xb166", + "from": "dup", + "indirect": false, + "target": "dup+0xa840" + }, + { + "address": "0xb1aa", + "from": "dup", + "indirect": true, + "target": "*0x52060(%rip) # 0x5d210" + }, + { + "address": "0xb218", + "from": "dup", + "indirect": true, + "target": "*0x51fba(%rip) # 0x5d1d8" + }, + { + "address": "0xb255", + "from": "dup", + "indirect": false, + "target": "dup+0x7c90" + }, + { + "address": "0xb277", + "from": "dup", + "indirect": false, + "target": "dup+0x3350" + }, + { + "address": "0xb29a", + "from": "dup", + "indirect": false, + "target": "dup+0x3350" + }, + { + "address": "0xb2b6", + "from": "dup", + "indirect": false, + "target": "dup+0x3350" + }, + { + "address": "0xb2c4", + "from": "dup", + "indirect": true, + "target": "*0x51fc6(%rip) # 0x5d290" + }, + { + "address": "0xb2e6", + "from": "dup", + "indirect": false, + "target": "dup+0x3350" + }, + { + "address": "0xb2ff", + "from": "dup", + "indirect": false, + "target": "dup+0x3350" + }, + { + "address": "0xb311", + "from": "dup", + "indirect": false, + "target": "dup+0x36b0" + }, + { + "address": "0xb31a", + "from": "dup", + "indirect": true, + "target": "*0x51f18(%rip) # 0x5d238" + }, + { + "address": "0xb329", + "from": "dup", + "indirect": false, + "target": "dup+0x36b0" + }, + { + "address": "0xb33c", + "from": "dup", + "indirect": false, + "target": "dup+0x3350" + }, + { + "address": "0xb34b", + "from": "dup", + "indirect": false, + "target": "dup+0x36b0" + }, + { + "address": "0xb437", + "from": "dup", + "indirect": false, + "target": "dup+0x3350" + }, + { + "address": "0xb4d1", + "from": "dup", + "indirect": true, + "target": "*0x51dc9(%rip) # 0x5d2a0" + }, + { + "address": "0xb681", + "from": "dup", + "indirect": false, + "target": "dup+0x3410" + }, + { + "address": "0xb68a", + "from": "dup", + "indirect": false, + "target": "dup+0x86e0" + }, + { + "address": "0xb6c1", + "from": "dup", + "indirect": false, + "target": "dup+0x8050" + }, + { + "address": "0xb6d7", + "from": "dup", + "indirect": false, + "target": "dup+0x82f0" + }, + { + "address": "0xb6e8", + "from": "dup", + "indirect": false, + "target": "dup+0x8320" + }, + { + "address": "0xb7e6", + "from": "dup", + "indirect": false, + "target": "dup+0x96c0" + }, + { + "address": "0xb85c", + "from": "dup", + "indirect": false, + "target": "dup+0x84b0" + }, + { + "address": "0xb86d", + "from": "dup", + "indirect": false, + "target": "dup+0x8570" + }, + { + "address": "0xb883", + "from": "dup", + "indirect": false, + "target": "dup+0x84b0" + }, + { + "address": "0xb894", + "from": "dup", + "indirect": false, + "target": "dup+0x8570" + }, + { + "address": "0xb8b9", + "from": "dup", + "indirect": true, + "target": "*0x519e1(%rip) # 0x5d2a0" + }, + { + "address": "0xb8ef", + "from": "dup", + "indirect": true, + "target": "*0x519ab(%rip) # 0x5d2a0" + }, + { + "address": "0xb91b", + "from": "dup", + "indirect": false, + "target": "dup+0x3410" + }, + { + "address": "0xb97d", + "from": "dup", + "indirect": false, + "target": "dup+0x3410" + }, + { + "address": "0xb99b", + "from": "dup", + "indirect": false, + "target": "dup+0x36b0" + }, + { + "address": "0xb9a4", + "from": "dup", + "indirect": true, + "target": "*0x5188e(%rip) # 0x5d238" + }, + { + "address": "0xb9c8", + "from": "dup", + "indirect": false, + "target": "dup+0x3410" + }, + { + "address": "0xbaa3", + "from": "dup", + "indirect": false, + "target": "dup+0x3350" + }, + { + "address": "0xbab0", + "from": "dup", + "indirect": false, + "target": "dup+0x36b0" + }, + { + "address": "0xbaed", + "from": "dup", + "indirect": false, + "target": "dup+0x7850" + }, + { + "address": "0xbb0d", + "from": "dup", + "indirect": true, + "target": "*0x516c5(%rip) # 0x5d1d8" + }, + { + "address": "0xbb3c", + "from": "dup", + "indirect": true, + "target": "*%r13" + }, + { + "address": "0xbb76", + "from": "dup", + "indirect": false, + "target": "dup+0x96c0" + }, + { + "address": "0xbbe1", + "from": "dup", + "indirect": true, + "target": "*(%rax,%r13,8)" + }, + { + "address": "0xbcb0", + "from": "dup", + "indirect": true, + "target": "*0x515aa(%rip) # 0x5d260" + }, + { + "address": "0xbda7", + "from": "dup", + "indirect": true, + "target": "*(%rax,%r14,8)" + }, + { + "address": "0xbde5", + "from": "dup", + "indirect": true, + "target": "*0x5144d(%rip) # 0x5d238" + }, + { + "address": "0xbe05", + "from": "dup", + "indirect": false, + "target": "dup+0x3350" + }, + { + "address": "0xbe65", + "from": "dup", + "indirect": true, + "target": "*0x513cd(%rip) # 0x5d238" + }, + { + "address": "0xbe85", + "from": "dup", + "indirect": false, + "target": "dup+0x3350" + }, + { + "address": "0xbeec", + "from": "dup", + "indirect": false, + "target": "dup+0x5d90" + }, + { + "address": "0xbf13", + "from": "dup", + "indirect": false, + "target": "dup+0x5d90" + }, + { + "address": "0xbf3a", + "from": "dup", + "indirect": false, + "target": "dup+0x5d90" + }, + { + "address": "0xbf5d", + "from": "dup", + "indirect": false, + "target": "dup+0x5d90" + }, + { + "address": "0xbf80", + "from": "dup", + "indirect": false, + "target": "dup+0x5d90" + }, + { + "address": "0xbfa3", + "from": "dup", + "indirect": false, + "target": "dup+0x5d90" + }, + { + "address": "0xc028", + "from": "dup", + "indirect": false, + "target": "dup+0x84b0" + }, + { + "address": "0xc039", + "from": "dup", + "indirect": false, + "target": "dup+0x8570" + }, + { + "address": "0xc04e", + "from": "dup", + "indirect": false, + "target": "dup+0x84b0" + }, + { + "address": "0xc05f", + "from": "dup", + "indirect": false, + "target": "dup+0x8570" + }, + { + "address": "0xc07a", + "from": "dup", + "indirect": true, + "target": "*0x51220(%rip) # 0x5d2a0" + }, + { + "address": "0xc0a7", + "from": "dup", + "indirect": false, + "target": "dup+0x3410" + }, + { + "address": "0xc0ea", + "from": "dup", + "indirect": true, + "target": "*0x51128(%rip) # 0x5d218" + }, + { + "address": "0xc158", + "from": "dup", + "indirect": true, + "target": "*0x51082(%rip) # 0x5d1e0" + }, + { + "address": "0xc19c", + "from": "dup", + "indirect": true, + "target": "*0x510c6(%rip) # 0x5d268" + }, + { + "address": "0xc1b4", + "from": "dup", + "indirect": true, + "target": "*0x510ae(%rip) # 0x5d268" + }, + { + "address": "0xc1cc", + "from": "dup", + "indirect": true, + "target": "*0x51096(%rip) # 0x5d268" + }, + { + "address": "0xc1ec", + "from": "dup", + "indirect": false, + "target": "dup+0x6860" + }, + { + "address": "0xc204", + "from": "dup", + "indirect": false, + "target": "dup+0x7c90" + }, + { + "address": "0xc255", + "from": "dup", + "indirect": true, + "target": "*0x5100d(%rip) # 0x5d268" + }, + { + "address": "0xc263", + "from": "dup", + "indirect": true, + "target": "*0x51057(%rip) # 0x5d2c0" + }, + { + "address": "0xc278", + "from": "dup", + "indirect": false, + "target": "dup+0x3410" + }, + { + "address": "0xc29b", + "from": "dup", + "indirect": true, + "target": "*0x5100f(%rip) # 0x5d2b0" + }, + { + "address": "0xc2c1", + "from": "dup", + "indirect": false, + "target": "dup+0x5d90" + }, + { + "address": "0xc2f7", + "from": "dup", + "indirect": false, + "target": "dup+0x6660" + }, + { + "address": "0xc314", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0xc335", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0xc34a", + "from": "dup", + "indirect": true, + "target": "*0x50f48(%rip) # 0x5d298" + }, + { + "address": "0xc371", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0xc382", + "from": "dup", + "indirect": true, + "target": "*0x50f10(%rip) # 0x5d298" + }, + { + "address": "0xc3a9", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0xc3ff", + "from": "dup", + "indirect": false, + "target": "dup+0x35f0" + }, + { + "address": "0xc421", + "from": "dup", + "indirect": false, + "target": "dup+0x36b0" + }, + { + "address": "0xc47c", + "from": "dup", + "indirect": false, + "target": "dup+0x3850" + }, + { + "address": "0xc4ff", + "from": "dup", + "indirect": true, + "target": "*0x50d7b(%rip) # 0x5d280" + }, + { + "address": "0xc523", + "from": "dup", + "indirect": true, + "target": "*0x50d57(%rip) # 0x5d280" + }, + { + "address": "0xc624", + "from": "dup", + "indirect": true, + "target": "*0x50c8e(%rip) # 0x5d2b8" + }, + { + "address": "0xc63a", + "from": "dup", + "indirect": true, + "target": "*0x50c00(%rip) # 0x5d240" + }, + { + "address": "0xc64c", + "from": "dup", + "indirect": true, + "target": "*0x50bee(%rip) # 0x5d240" + }, + { + "address": "0xc6b4", + "from": "dup", + "indirect": true, + "target": "*0x50bfe(%rip) # 0x5d2b8" + }, + { + "address": "0xc6d4", + "from": "dup", + "indirect": true, + "target": "*0x50b66(%rip) # 0x5d240" + }, + { + "address": "0xc6e6", + "from": "dup", + "indirect": true, + "target": "*0x50b54(%rip) # 0x5d240" + }, + { + "address": "0xc757", + "from": "dup", + "indirect": true, + "target": "*%r15" + }, + { + "address": "0xc77b", + "from": "dup", + "indirect": true, + "target": "*%r15" + }, + { + "address": "0xc79d", + "from": "dup", + "indirect": false, + "target": "dup+0x5d90" + }, + { + "address": "0xc7c4", + "from": "dup", + "indirect": false, + "target": "dup+0x5d90" + }, + { + "address": "0xc7eb", + "from": "dup", + "indirect": false, + "target": "dup+0x5d90" + }, + { + "address": "0xc812", + "from": "dup", + "indirect": false, + "target": "dup+0x5d90" + }, + { + "address": "0xc839", + "from": "dup", + "indirect": false, + "target": "dup+0x5d90" + }, + { + "address": "0xc860", + "from": "dup", + "indirect": false, + "target": "dup+0x5d90" + }, + { + "address": "0xc883", + "from": "dup", + "indirect": false, + "target": "dup+0x5d90" + }, + { + "address": "0xc8a5", + "from": "dup", + "indirect": false, + "target": "dup+0x6860" + }, + { + "address": "0xc8c2", + "from": "dup", + "indirect": true, + "target": "*0x509e8(%rip) # 0x5d2b0" + }, + { + "address": "0xc8de", + "from": "dup", + "indirect": false, + "target": "dup+0x5d90" + }, + { + "address": "0xc91b", + "from": "dup", + "indirect": true, + "target": "*0x508ff(%rip) # 0x5d220" + }, + { + "address": "0xc932", + "from": "dup", + "indirect": true, + "target": "*%r13" + }, + { + "address": "0xc9b7", + "from": "dup", + "indirect": true, + "target": "*0x5082b(%rip) # 0x5d1e8" + }, + { + "address": "0xc9cf", + "from": "dup", + "indirect": true, + "target": "*%r13" + }, + { + "address": "0xcb7f", + "from": "dup", + "indirect": false, + "target": "dup+0x8050" + }, + { + "address": "0xcb90", + "from": "dup", + "indirect": false, + "target": "dup+0x82f0" + }, + { + "address": "0xcba0", + "from": "dup", + "indirect": false, + "target": "dup+0x8320" + }, + { + "address": "0xcbb4", + "from": "dup", + "indirect": false, + "target": "dup+0x3410" + }, + { + "address": "0xcbc8", + "from": "dup", + "indirect": false, + "target": "dup+0x3410" + }, + { + "address": "0xcbd1", + "from": "dup", + "indirect": false, + "target": "dup+0x86e0" + }, + { + "address": "0xcc6c", + "from": "dup", + "indirect": false, + "target": "dup+0x3410" + }, + { + "address": "0xccdd", + "from": "dup", + "indirect": false, + "target": "dup+0x84b0" + }, + { + "address": "0xccee", + "from": "dup", + "indirect": false, + "target": "dup+0x8570" + }, + { + "address": "0xcd09", + "from": "dup", + "indirect": true, + "target": "*0x50599(%rip) # 0x5d2a8" + }, + { + "address": "0xcdd2", + "from": "dup", + "indirect": false, + "target": "dup+0x84b0" + }, + { + "address": "0xcde7", + "from": "dup", + "indirect": false, + "target": "dup+0x8570" + }, + { + "address": "0xce3e", + "from": "dup", + "indirect": false, + "target": "dup+0x84b0" + }, + { + "address": "0xce53", + "from": "dup", + "indirect": false, + "target": "dup+0x8570" + }, + { + "address": "0xce9e", + "from": "dup", + "indirect": true, + "target": "*0x50404(%rip) # 0x5d2a8" + }, + { + "address": "0xcec1", + "from": "dup", + "indirect": false, + "target": "dup+0x3410" + }, + { + "address": "0xceeb", + "from": "dup", + "indirect": false, + "target": "dup+0x3410" + }, + { + "address": "0xcf50", + "from": "dup", + "indirect": true, + "target": "*(%rax,%r13,8)" + }, + { + "address": "0xd009", + "from": "dup", + "indirect": true, + "target": "*0x50261(%rip) # 0x5d270" + }, + { + "address": "0xd100", + "from": "dup", + "indirect": true, + "target": "*(%r15,%rbx,8)" + }, + { + "address": "0xd15c", + "from": "dup", + "indirect": false, + "target": "dup+0x5d90" + }, + { + "address": "0xd183", + "from": "dup", + "indirect": false, + "target": "dup+0x5d90" + }, + { + "address": "0xd1a6", + "from": "dup", + "indirect": false, + "target": "dup+0x5d90" + }, + { + "address": "0xd1c9", + "from": "dup", + "indirect": false, + "target": "dup+0x5d90" + }, + { + "address": "0xd1ec", + "from": "dup", + "indirect": false, + "target": "dup+0x5d90" + }, + { + "address": "0xd242", + "from": "dup", + "indirect": false, + "target": "dup+0x8050" + }, + { + "address": "0xd253", + "from": "dup", + "indirect": false, + "target": "dup+0x82f0" + }, + { + "address": "0xd266", + "from": "dup", + "indirect": false, + "target": "dup+0x86e0" + }, + { + "address": "0xd2aa", + "from": "dup", + "indirect": false, + "target": "dup+0x8320" + }, + { + "address": "0xd2b0", + "from": "dup", + "indirect": true, + "target": "*0x50022(%rip) # 0x5d2d8" + }, + { + "address": "0xd2b9", + "from": "dup", + "indirect": true, + "target": "*0x50021(%rip) # 0x5d2e0" + }, + { + "address": "0xd2cf", + "from": "dup", + "indirect": false, + "target": "dup+0x8410" + }, + { + "address": "0xd30a", + "from": "dup", + "indirect": false, + "target": "dup+0x8510" + }, + { + "address": "0xd330", + "from": "dup", + "indirect": false, + "target": "dup+0x8590" + }, + { + "address": "0xd340", + "from": "dup", + "indirect": false, + "target": "dup+0x8570" + }, + { + "address": "0xd39d", + "from": "dup", + "indirect": false, + "target": "dup+0x84b0" + }, + { + "address": "0xd3b4", + "from": "dup", + "indirect": false, + "target": "dup+0x8570" + }, + { + "address": "0xd3e7", + "from": "dup", + "indirect": false, + "target": "dup+0x85b0" + }, + { + "address": "0xd3f0", + "from": "dup", + "indirect": false, + "target": "dup+0x8600" + }, + { + "address": "0xd3ff", + "from": "dup", + "indirect": false, + "target": "dup+0x86e0" + }, + { + "address": "0xd47c", + "from": "dup", + "indirect": false, + "target": "dup+0x5d90" + }, + { + "address": "0xd4a3", + "from": "dup", + "indirect": false, + "target": "dup+0x5d90" + }, + { + "address": "0xd4ca", + "from": "dup", + "indirect": false, + "target": "dup+0x5d90" + }, + { + "address": "0xd4f1", + "from": "dup", + "indirect": false, + "target": "dup+0x5d90" + }, + { + "address": "0xd514", + "from": "dup", + "indirect": false, + "target": "dup+0x5d90" + }, + { + "address": "0xd53b", + "from": "dup", + "indirect": false, + "target": "dup+0x5d90" + }, + { + "address": "0xd55e", + "from": "dup", + "indirect": false, + "target": "dup+0x5d90" + }, + { + "address": "0xd581", + "from": "dup", + "indirect": false, + "target": "dup+0x5d90" + }, + { + "address": "0xd5a4", + "from": "dup", + "indirect": false, + "target": "dup+0x5d90" + }, + { + "address": "0xd5f0", + "from": "dup", + "indirect": false, + "target": "dup+0xad00" + }, + { + "address": "0xd61e", + "from": "dup", + "indirect": false, + "target": "dup+0x3350" + }, + { + "address": "0xd645", + "from": "dup", + "indirect": false, + "target": "dup+0x6ea0" + }, + { + "address": "0xd669", + "from": "dup", + "indirect": false, + "target": "dup+0x6d40" + }, + { + "address": "0xd684", + "from": "dup", + "indirect": false, + "target": "dup+0x6e30" + }, + { + "address": "0xd697", + "from": "dup", + "indirect": false, + "target": "dup+0x6f10" + }, + { + "address": "0xd6d0", + "from": "dup", + "indirect": true, + "target": "*%r13" + }, + { + "address": "0xd70c", + "from": "dup", + "indirect": false, + "target": "dup+0x6e30" + }, + { + "address": "0xd720", + "from": "dup", + "indirect": false, + "target": "dup+0x6f10" + }, + { + "address": "0xd760", + "from": "dup", + "indirect": false, + "target": "dup+0xad00" + }, + { + "address": "0xd79b", + "from": "dup", + "indirect": false, + "target": "dup+0xad00" + }, + { + "address": "0xd7dc", + "from": "dup", + "indirect": false, + "target": "dup+0xad00" + }, + { + "address": "0xd81b", + "from": "dup", + "indirect": false, + "target": "dup+0xad00" + }, + { + "address": "0xd85c", + "from": "dup", + "indirect": false, + "target": "dup+0xad00" + }, + { + "address": "0xd8a0", + "from": "dup", + "indirect": false, + "target": "dup+0xad00" + }, + { + "address": "0xd8df", + "from": "dup", + "indirect": false, + "target": "dup+0xad00" + }, + { + "address": "0xd91e", + "from": "dup", + "indirect": false, + "target": "dup+0xad00" + }, + { + "address": "0xd95c", + "from": "dup", + "indirect": false, + "target": "dup+0xad00" + }, + { + "address": "0xd982", + "from": "dup", + "indirect": false, + "target": "dup+0x3350" + }, + { + "address": "0xd992", + "from": "dup", + "indirect": false, + "target": "dup+0x6ea0" + }, + { + "address": "0xd9a8", + "from": "dup", + "indirect": false, + "target": "dup+0x6f80" + }, + { + "address": "0xd9c9", + "from": "dup", + "indirect": false, + "target": "dup+0x6f10" + }, + { + "address": "0xd9e9", + "from": "dup", + "indirect": false, + "target": "dup+0x6ff0" + }, + { + "address": "0xd9fe", + "from": "dup", + "indirect": false, + "target": "dup+0x3350" + }, + { + "address": "0xda06", + "from": "dup", + "indirect": false, + "target": "dup+0x6400" + }, + { + "address": "0xda28", + "from": "dup", + "indirect": false, + "target": "dup+0x5fb0" + }, + { + "address": "0xda4c", + "from": "dup", + "indirect": false, + "target": "dup+0x5dd0" + }, + { + "address": "0xda70", + "from": "dup", + "indirect": false, + "target": "dup+0x5dd0" + } + ], + "dt_needed": [ + "libSceLibcInternal.sprx", + "libSceNet.sprx", + "libkernel_web.sprx" + ], + "filename": "chimera-elfldr-phase07-a.elf", + "imports": [], + "report_sha256": { + "callgraph": "78e5b60788d9d1f37343c58da4edc42fe607a91d1a0681b07a8fd4943c287460", + "disassembly": "eff66d2d61b12afcf51362eca0bae75650a2f645f3ffd2a3cecd69b353eb4f8b", + "dynamic": "1ae00cf8437c25bdbff4025f8028aa52265552d46dfd3670c40c1caca5149e99", + "headers_sections": "8c106d69e0e474f465bb8a72cdb2cf32a364d29b11ef0de5dbc2478c2738e1b2", + "linker_map_normalized": "2ea5ff73299db6e61d5fd998c358ee910bc4e8fa1e6e7c73dbaa842bc813124f", + "relocations": "fa396389a5f4f28a09b92ca16c66d94101db03962f58ee1ffa1c545608eeb695", + "symbols": "3fef42ae464118767f8c87867b9c765293f8948a7e8ba5b77c7483a69c6992b9", + "undefined": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "sensitive_static_inventory": { + "categories": { + "dynamic_loading": { + "directly_reachable_from_entrypoint": [], + "linked": [ + "__dlopen", + "__dlsym", + "kernel_dynlib_dlsym", + "kernel_dynlib_entry_addr", + "kernel_dynlib_find_handle", + "kernel_dynlib_fini_addr", + "kernel_dynlib_handle", + "kernel_dynlib_init_addr", + "kernel_dynlib_mapbase_addr", + "kernel_dynlib_obj", + "kernel_dynlib_path", + "kernel_dynlib_resolve", + "sceKernelLoadStartModule", + "sceKernelStopUnloadModule" + ], + "linked_in_disassembly": [], + "linked_in_linker_map": [ + "__dlopen", + "__dlsym", + "kernel_dynlib_dlsym", + "kernel_dynlib_entry_addr", + "kernel_dynlib_find_handle", + "kernel_dynlib_fini_addr", + "kernel_dynlib_handle", + "kernel_dynlib_init_addr", + "kernel_dynlib_mapbase_addr", + "kernel_dynlib_obj", + "kernel_dynlib_path", + "kernel_dynlib_resolve", + "sceKernelLoadStartModule", + "sceKernelStopUnloadModule" + ] + }, + "graphics_or_display": { + "directly_reachable_from_entrypoint": [], + "linked": [], + "linked_in_disassembly": [], + "linked_in_linker_map": [] + }, + "kernel_runtime_write": { + "directly_reachable_from_entrypoint": [], + "linked": [ + "__patch_init", + "kernel_copyin", + "kernel_copyout", + "kernel_overlap_sockets", + "kernel_set_ucred_attrs", + "kernel_set_ucred_authid", + "kernel_set_ucred_caps", + "kernel_set_ucred_prison", + "kernel_set_ucred_rgid", + "kernel_set_ucred_ruid", + "kernel_set_ucred_svgid", + "kernel_set_ucred_svuid", + "kernel_set_ucred_uid" + ], + "linked_in_disassembly": [], + "linked_in_linker_map": [ + "__patch_init", + "kernel_copyin", + "kernel_copyout", + "kernel_overlap_sockets", + "kernel_set_ucred_attrs", + "kernel_set_ucred_authid", + "kernel_set_ucred_caps", + "kernel_set_ucred_prison", + "kernel_set_ucred_rgid", + "kernel_set_ucred_ruid", + "kernel_set_ucred_svgid", + "kernel_set_ucred_svuid", + "kernel_set_ucred_uid" + ] + }, + "network": { + "directly_reachable_from_entrypoint": [], + "linked": [ + "pt_socket" + ], + "linked_in_disassembly": [], + "linked_in_linker_map": [ + "pt_socket" + ] + }, + "ptrace_or_jit": { + "directly_reachable_from_entrypoint": [], + "linked": [ + "ptrace", + "sys_ptrace" + ], + "linked_in_disassembly": [], + "linked_in_linker_map": [ + "ptrace", + "sys_ptrace" + ] + } + }, + "direct_call_reachability_available": false, + "direct_call_reachability_only": true, + "entrypoint": "_start", + "indirect_call_edges_retained_but_not_resolved": 418 + }, + "sha256": "63e810982471eb40cae3a20aa9df9a0a02892f420e429874fae4e99aa400b561", + "size": 397000 + }, + "manager": { + "byte_identical_clean_builds": true, + "call_edges": [ + { + "address": "0x20", + "from": ".text", + "indirect": true, + "target": "*0xe23a(%rip) # 0xe260" + }, + { + "address": "0x48", + "from": ".text", + "indirect": true, + "target": "*0xe21a(%rip) # 0xe268" + }, + { + "address": "0x6f", + "from": ".text", + "indirect": true, + "target": "*0xe1fb(%rip) # 0xe270" + }, + { + "address": "0x80", + "from": ".text", + "indirect": true, + "target": "*0xe1f2(%rip) # 0xe278" + }, + { + "address": "0x8c", + "from": ".text", + "indirect": true, + "target": "*0xe1ee(%rip) # 0xe280" + }, + { + "address": "0xc0", + "from": ".text", + "indirect": true, + "target": "*0xe1ca(%rip) # 0xe290" + }, + { + "address": "0xea", + "from": ".text", + "indirect": true, + "target": "*0xe1a8(%rip) # 0xe298" + }, + { + "address": "0x115", + "from": ".text", + "indirect": true, + "target": "*%r15" + }, + { + "address": "0x137", + "from": ".text", + "indirect": true, + "target": "*0xe163(%rip) # 0xe2a0" + }, + { + "address": "0x163", + "from": ".text", + "indirect": true, + "target": "*0xe13f(%rip) # 0xe2a8" + }, + { + "address": "0x17d", + "from": ".text", + "indirect": true, + "target": "*0xe12d(%rip) # 0xe2b0" + }, + { + "address": "0x1a6", + "from": ".text", + "indirect": true, + "target": "*0xe10c(%rip) # 0xe2b8" + }, + { + "address": "0x1c5", + "from": ".text", + "indirect": true, + "target": "*0xe0e5(%rip) # 0xe2b0" + }, + { + "address": "0x1e6", + "from": ".text", + "indirect": true, + "target": "*0xe0d4(%rip) # 0xe2c0" + }, + { + "address": "0x1fe", + "from": ".text", + "indirect": false, + "target": ".text+0xa03" + }, + { + "address": "0x20c", + "from": ".text", + "indirect": false, + "target": ".text+0xa03" + }, + { + "address": "0x225", + "from": ".text", + "indirect": true, + "target": "*%r15" + }, + { + "address": "0x23a", + "from": ".text", + "indirect": true, + "target": "*%r15" + }, + { + "address": "0x24f", + "from": ".text", + "indirect": true, + "target": "*%r15" + }, + { + "address": "0x264", + "from": ".text", + "indirect": true, + "target": "*%r15" + }, + { + "address": "0x279", + "from": ".text", + "indirect": true, + "target": "*%r15" + }, + { + "address": "0x28e", + "from": ".text", + "indirect": true, + "target": "*%r15" + }, + { + "address": "0x2a3", + "from": ".text", + "indirect": true, + "target": "*%r15" + }, + { + "address": "0x2b8", + "from": ".text", + "indirect": true, + "target": "*%r15" + }, + { + "address": "0x2cd", + "from": ".text", + "indirect": true, + "target": "*%r15" + }, + { + "address": "0x2ff", + "from": ".text", + "indirect": false, + "target": ".text+0xa72" + }, + { + "address": "0x356", + "from": ".text", + "indirect": false, + "target": ".text+0xa72" + }, + { + "address": "0x3b9", + "from": ".text", + "indirect": true, + "target": "*0xdf09(%rip) # 0xe2c8" + }, + { + "address": "0x3cf", + "from": ".text", + "indirect": true, + "target": "*0xdedb(%rip) # 0xe2b0" + }, + { + "address": "0x401", + "from": ".text", + "indirect": false, + "target": ".text+0xa72" + }, + { + "address": "0x449", + "from": ".text", + "indirect": false, + "target": "strcasecmp" + }, + { + "address": "0x4ac", + "from": ".text", + "indirect": false, + "target": "strcasecmp" + }, + { + "address": "0x4d0", + "from": ".text", + "indirect": true, + "target": "*0xddfa(%rip) # 0xe2d0" + }, + { + "address": "0x4f9", + "from": ".text", + "indirect": true, + "target": "*0xddd9(%rip) # 0xe2d8" + }, + { + "address": "0x517", + "from": ".text", + "indirect": true, + "target": "*0xdd83(%rip) # 0xe2a0" + }, + { + "address": "0x52e", + "from": ".text", + "indirect": true, + "target": "*0xdd8c(%rip) # 0xe2c0" + }, + { + "address": "0x545", + "from": ".text", + "indirect": true, + "target": "*0xdd75(%rip) # 0xe2c0" + }, + { + "address": "0x55f", + "from": ".text", + "indirect": false, + "target": ".text+0x959" + }, + { + "address": "0x56a", + "from": ".text", + "indirect": true, + "target": "*0xdd10(%rip) # 0xe280" + }, + { + "address": "0x581", + "from": ".text", + "indirect": true, + "target": "*0xdd59(%rip) # 0xe2e0" + }, + { + "address": "0x599", + "from": ".text", + "indirect": false, + "target": "strcasecmp" + }, + { + "address": "0x5ad", + "from": ".text", + "indirect": false, + "target": "strcasecmp" + }, + { + "address": "0x5be", + "from": ".text", + "indirect": false, + "target": "strcasecmp+0x88e" + }, + { + "address": "0x5cf", + "from": ".text", + "indirect": false, + "target": ".text+0xae3" + }, + { + "address": "0x67f", + "from": ".text", + "indirect": true, + "target": "*0xdc43(%rip) # 0xe2c8" + }, + { + "address": "0x693", + "from": ".text", + "indirect": true, + "target": "*0xdc4f(%rip) # 0xe2e8" + }, + { + "address": "0x703", + "from": ".text", + "indirect": true, + "target": "*0xdbbf(%rip) # 0xe2c8" + }, + { + "address": "0x71e", + "from": ".text", + "indirect": false, + "target": "strcasecmp+0x95" + }, + { + "address": "0x762", + "from": ".text", + "indirect": true, + "target": "*0xdb88(%rip) # 0xe2f0" + }, + { + "address": "0x76c", + "from": ".text", + "indirect": true, + "target": "*0xdb5e(%rip) # 0xe2d0" + }, + { + "address": "0x787", + "from": ".text", + "indirect": true, + "target": "*0xdb63(%rip) # 0xe2f0" + }, + { + "address": "0x791", + "from": ".text", + "indirect": true, + "target": "*0xdb39(%rip) # 0xe2d0" + }, + { + "address": "0x7ac", + "from": ".text", + "indirect": true, + "target": "*0xdb3e(%rip) # 0xe2f0" + }, + { + "address": "0x7b6", + "from": ".text", + "indirect": true, + "target": "*0xdb14(%rip) # 0xe2d0" + }, + { + "address": "0x7e8", + "from": ".text", + "indirect": true, + "target": "*0xdada(%rip) # 0xe2c8" + }, + { + "address": "0x7f9", + "from": ".text", + "indirect": true, + "target": "*0xdaf9(%rip) # 0xe2f8" + }, + { + "address": "0x825", + "from": ".text", + "indirect": true, + "target": "*0xda9d(%rip) # 0xe2c8" + }, + { + "address": "0x849", + "from": ".text", + "indirect": true, + "target": "*0xdab1(%rip) # 0xe300" + }, + { + "address": "0x870", + "from": ".text", + "indirect": true, + "target": "*0xda92(%rip) # 0xe308" + }, + { + "address": "0x8a2", + "from": ".text", + "indirect": true, + "target": "*0xd9e0(%rip) # 0xe288" + }, + { + "address": "0x8b9", + "from": ".text", + "indirect": true, + "target": "*0xda49(%rip) # 0xe308" + }, + { + "address": "0x8cc", + "from": ".text", + "indirect": true, + "target": "*0xd9ae(%rip) # 0xe280" + }, + { + "address": "0x8d9", + "from": ".text", + "indirect": true, + "target": "*0xda31(%rip) # 0xe310" + }, + { + "address": "0x8e4", + "from": ".text", + "indirect": true, + "target": "*0xd996(%rip) # 0xe280" + }, + { + "address": "0x904", + "from": ".text", + "indirect": false, + "target": "strcasecmp+0x59d" + }, + { + "address": "0x915", + "from": ".text", + "indirect": true, + "target": "*0xd9cd(%rip) # 0xe2e8" + }, + { + "address": "0x940", + "from": ".text", + "indirect": false, + "target": ".text+0x959" + }, + { + "address": "0x98e", + "from": ".text", + "indirect": true, + "target": "*0xd984(%rip) # 0xe318" + }, + { + "address": "0x9b6", + "from": ".text", + "indirect": true, + "target": "*0xd90c(%rip) # 0xe2c8" + }, + { + "address": "0x9e8", + "from": ".text", + "indirect": true, + "target": "*%r12" + }, + { + "address": "0xa20", + "from": ".text", + "indirect": true, + "target": "*%r14" + }, + { + "address": "0xa34", + "from": ".text", + "indirect": true, + "target": "*0xd8de(%rip) # 0xe318" + }, + { + "address": "0xa51", + "from": ".text", + "indirect": true, + "target": "*%r15" + }, + { + "address": "0xa99", + "from": ".text", + "indirect": true, + "target": "*%r12" + }, + { + "address": "0xaad", + "from": ".text", + "indirect": true, + "target": "*0xd87d(%rip) # 0xe330" + }, + { + "address": "0xab6", + "from": ".text", + "indirect": true, + "target": "*%r12" + }, + { + "address": "0xb0a", + "from": ".text", + "indirect": true, + "target": "*0xd828(%rip) # 0xe338" + }, + { + "address": "0xb25", + "from": ".text", + "indirect": true, + "target": "*0xd80d(%rip) # 0xe338" + }, + { + "address": "0xb97", + "from": "strcasecmp", + "indirect": true, + "target": "*%r13" + }, + { + "address": "0xba3", + "from": "strcasecmp", + "indirect": true, + "target": "*%rcx" + }, + { + "address": "0xbb4", + "from": "strcasecmp", + "indirect": true, + "target": "*%r15" + }, + { + "address": "0xbbd", + "from": "strcasecmp", + "indirect": true, + "target": "*%r15" + }, + { + "address": "0xbfa", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x192" + }, + { + "address": "0xc1d", + "from": "strcasecmp", + "indirect": true, + "target": "*0xd63d(%rip) # 0xe260" + }, + { + "address": "0xc53", + "from": "strcasecmp", + "indirect": true, + "target": "*0xd6ef(%rip) # 0xe348" + }, + { + "address": "0xc60", + "from": "strcasecmp", + "indirect": true, + "target": "*0xd61a(%rip) # 0xe280" + }, + { + "address": "0xca6", + "from": "strcasecmp", + "indirect": true, + "target": "*%r13" + }, + { + "address": "0xd38", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3e9" + }, + { + "address": "0xdba", + "from": "strcasecmp", + "indirect": true, + "target": "*(%r14)" + }, + { + "address": "0xdd9", + "from": "strcasecmp", + "indirect": true, + "target": "*0x8(%r14)" + }, + { + "address": "0xe2f", + "from": "strcasecmp", + "indirect": true, + "target": "*0xd523(%rip) # 0xe358" + }, + { + "address": "0xe4e", + "from": "strcasecmp", + "indirect": true, + "target": "*0x8(%r14)" + }, + { + "address": "0xe80", + "from": "strcasecmp", + "indirect": true, + "target": "*0xd4d2(%rip) # 0xe358" + }, + { + "address": "0xe9b", + "from": "strcasecmp", + "indirect": true, + "target": "*0xd4bf(%rip) # 0xe360" + }, + { + "address": "0xee7", + "from": "strcasecmp", + "indirect": true, + "target": "*0x10(%r14)" + }, + { + "address": "0xefe", + "from": "strcasecmp", + "indirect": true, + "target": "*0xd37c(%rip) # 0xe280" + }, + { + "address": "0xf4c", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x88e" + }, + { + "address": "0xf68", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x795" + }, + { + "address": "0xf81", + "from": "strcasecmp", + "indirect": true, + "target": "*0xd379(%rip) # 0xe300" + }, + { + "address": "0xf92", + "from": "strcasecmp", + "indirect": true, + "target": "*0xd3b8(%rip) # 0xe350" + }, + { + "address": "0xfc2", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x6cc" + }, + { + "address": "0xfcd", + "from": "strcasecmp", + "indirect": true, + "target": "*0xd2ad(%rip) # 0xe280" + }, + { + "address": "0xff5", + "from": "strcasecmp", + "indirect": true, + "target": "*0xd285(%rip) # 0xe280" + }, + { + "address": "0x100b", + "from": "strcasecmp", + "indirect": true, + "target": "*0xd26f(%rip) # 0xe280" + }, + { + "address": "0x1021", + "from": "strcasecmp", + "indirect": true, + "target": "*0xd329(%rip) # 0xe350" + }, + { + "address": "0x107f", + "from": "strcasecmp", + "indirect": true, + "target": "*0xd1fb(%rip) # 0xe280" + }, + { + "address": "0x1091", + "from": "strcasecmp", + "indirect": true, + "target": "*0xd1e9(%rip) # 0xe280" + }, + { + "address": "0x10a7", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x853" + }, + { + "address": "0x10b3", + "from": "strcasecmp", + "indirect": true, + "target": "*0xd1c7(%rip) # 0xe280" + }, + { + "address": "0x10c9", + "from": "strcasecmp", + "indirect": true, + "target": "*0xd299(%rip) # 0xe368" + }, + { + "address": "0x1120", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3e9" + }, + { + "address": "0x1131", + "from": "strcasecmp", + "indirect": true, + "target": "*0xd1d9(%rip) # 0xe310" + }, + { + "address": "0x114f", + "from": "strcasecmp", + "indirect": true, + "target": "*0xd20b(%rip) # 0xe360" + }, + { + "address": "0x1190", + "from": "strcasecmp", + "indirect": true, + "target": "*0xd1da(%rip) # 0xe370" + }, + { + "address": "0x11aa", + "from": "strcasecmp", + "indirect": true, + "target": "*0xd1b0(%rip) # 0xe360" + }, + { + "address": "0x11e3", + "from": "strcasecmp", + "indirect": true, + "target": "*0xd0ff(%rip) # 0xe2e8" + }, + { + "address": "0x11f1", + "from": "strcasecmp", + "indirect": true, + "target": "*0xd089(%rip) # 0xe280" + }, + { + "address": "0x1230", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x9cb" + }, + { + "address": "0x1245", + "from": "strcasecmp", + "indirect": true, + "target": "*0xd10d(%rip) # 0xe358" + }, + { + "address": "0x1284", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x9eb" + }, + { + "address": "0x1295", + "from": "strcasecmp", + "indirect": true, + "target": "*%r13" + }, + { + "address": "0x12b9", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0xc6e" + }, + { + "address": "0x12fd", + "from": "strcasecmp", + "indirect": true, + "target": "*0xd015(%rip) # 0xe318" + }, + { + "address": "0x13e7", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x795" + }, + { + "address": "0x1419", + "from": "strcasecmp", + "indirect": true, + "target": "*0xcf19(%rip) # 0xe338" + }, + { + "address": "0x1498", + "from": "strcasecmp", + "indirect": true, + "target": "*0xce3a(%rip) # 0xe2d8" + }, + { + "address": "0x1569", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0xa51" + }, + { + "address": "0x17db", + "from": "strcasecmp", + "indirect": true, + "target": "*0xcab7(%rip) # 0xe298" + }, + { + "address": "0x17fc", + "from": "strcasecmp", + "indirect": true, + "target": "*0xca96(%rip) # 0xe298" + }, + { + "address": "0x1808", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0xa51" + }, + { + "address": "0x186d", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0xa51" + }, + { + "address": "0x1935", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x1110" + }, + { + "address": "0x1942", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x2e70" + }, + { + "address": "0x194c", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x1520" + }, + { + "address": "0x196d", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3a60" + }, + { + "address": "0x197e", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x7500" + }, + { + "address": "0x199d", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x5160" + }, + { + "address": "0x19b5", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x1390" + }, + { + "address": "0x1a12", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3a60" + }, + { + "address": "0x1a31", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3a60" + }, + { + "address": "0x1a50", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3a60" + }, + { + "address": "0x1a6f", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3a60" + }, + { + "address": "0x1a7d", + "from": "strcasecmp", + "indirect": true, + "target": "*%rbx" + }, + { + "address": "0x1a82", + "from": "strcasecmp", + "indirect": true, + "target": "*%r15" + }, + { + "address": "0x1aa0", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3a60" + }, + { + "address": "0x1abf", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3a60" + }, + { + "address": "0x1adf", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3a60" + }, + { + "address": "0x1afb", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3a60" + }, + { + "address": "0x1b15", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x6a70" + }, + { + "address": "0x1b26", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x7380" + }, + { + "address": "0x1b2f", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x4e50" + }, + { + "address": "0x1b4a", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x4f00" + }, + { + "address": "0x1b5a", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x5070" + }, + { + "address": "0x1b63", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x5150" + }, + { + "address": "0x1b8d", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3a60" + }, + { + "address": "0x1bb2", + "from": "strcasecmp", + "indirect": false, + "target": ".text" + }, + { + "address": "0x1bc8", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x5020" + }, + { + "address": "0x1bd3", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x5070" + }, + { + "address": "0x1bdf", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x5150" + }, + { + "address": "0x1c0e", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3a60" + }, + { + "address": "0x1c26", + "from": "strcasecmp", + "indirect": true, + "target": "*%rax" + }, + { + "address": "0x1c47", + "from": "strcasecmp", + "indirect": true, + "target": "*0x124e3(%rip) # 0x14130" + }, + { + "address": "0x1c71", + "from": "strcasecmp", + "indirect": true, + "target": "*(%rbx)" + }, + { + "address": "0x1c87", + "from": "strcasecmp", + "indirect": true, + "target": "*(%rbx)" + }, + { + "address": "0x1cae", + "from": "strcasecmp", + "indirect": true, + "target": "*%rax" + }, + { + "address": "0x1cc7", + "from": "strcasecmp", + "indirect": true, + "target": "*(%rbx)" + }, + { + "address": "0x1e1c", + "from": "strcasecmp", + "indirect": true, + "target": "*0x12316(%rip) # 0x14138" + }, + { + "address": "0x1e37", + "from": "strcasecmp", + "indirect": true, + "target": "*%r15" + }, + { + "address": "0x1e53", + "from": "strcasecmp", + "indirect": true, + "target": "*%r15" + }, + { + "address": "0x1e70", + "from": "strcasecmp", + "indirect": true, + "target": "*0x122ca(%rip) # 0x14140" + }, + { + "address": "0x1e99", + "from": "strcasecmp", + "indirect": true, + "target": "*%r14" + }, + { + "address": "0x1ead", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x10f0" + }, + { + "address": "0x1efa", + "from": "strcasecmp", + "indirect": true, + "target": "*%r12" + }, + { + "address": "0x1f17", + "from": "strcasecmp", + "indirect": true, + "target": "*%r12" + }, + { + "address": "0x1f35", + "from": "strcasecmp", + "indirect": true, + "target": "*0x12205(%rip) # 0x14140" + }, + { + "address": "0x1f5a", + "from": "strcasecmp", + "indirect": true, + "target": "*%r15" + }, + { + "address": "0x1f6e", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x10f0" + }, + { + "address": "0x1fba", + "from": "strcasecmp", + "indirect": true, + "target": "*%r12" + }, + { + "address": "0x1fd7", + "from": "strcasecmp", + "indirect": true, + "target": "*%r12" + }, + { + "address": "0x1ff5", + "from": "strcasecmp", + "indirect": true, + "target": "*0x12145(%rip) # 0x14140" + }, + { + "address": "0x2002", + "from": "strcasecmp", + "indirect": true, + "target": "*0x12148(%rip) # 0x14150" + }, + { + "address": "0x200a", + "from": "strcasecmp", + "indirect": true, + "target": "*%r14" + }, + { + "address": "0x202f", + "from": "strcasecmp", + "indirect": true, + "target": "*%r15" + }, + { + "address": "0x2043", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x10f0" + }, + { + "address": "0x207c", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3a60" + }, + { + "address": "0x20a3", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3a60" + }, + { + "address": "0x20c6", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3a60" + }, + { + "address": "0x20e9", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3a60" + }, + { + "address": "0x210c", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3a60" + }, + { + "address": "0x21c9", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x1a90" + }, + { + "address": "0x21fa", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x1a90" + }, + { + "address": "0x22ab", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x1a90" + }, + { + "address": "0x237d", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x1a90" + }, + { + "address": "0x2410", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x1a90" + }, + { + "address": "0x2577", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x1a90" + }, + { + "address": "0x3a5a", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x10f0" + }, + { + "address": "0x44a1", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3e60" + }, + { + "address": "0x44bb", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3c80" + }, + { + "address": "0x44e1", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3c80" + }, + { + "address": "0x450a", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x15f0" + }, + { + "address": "0x451d", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x4290" + }, + { + "address": "0x4538", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x15f0" + }, + { + "address": "0x454b", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x4290" + }, + { + "address": "0x45bc", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x15f0" + }, + { + "address": "0x45ca", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x4290" + }, + { + "address": "0x4638", + "from": "strcasecmp", + "indirect": true, + "target": "*%rax" + }, + { + "address": "0x4654", + "from": "strcasecmp", + "indirect": true, + "target": "*%rax" + }, + { + "address": "0x4695", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x10f0" + }, + { + "address": "0x46d7", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x10f0" + }, + { + "address": "0x471b", + "from": "strcasecmp", + "indirect": true, + "target": "*%rax" + }, + { + "address": "0x474f", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x10f0" + }, + { + "address": "0x477f", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x10f0" + }, + { + "address": "0x47a1", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x10f0" + }, + { + "address": "0x481b", + "from": "strcasecmp", + "indirect": true, + "target": "*%rax" + }, + { + "address": "0x4837", + "from": "strcasecmp", + "indirect": true, + "target": "*%rax" + }, + { + "address": "0x4878", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x10f0" + }, + { + "address": "0x48ba", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x10f0" + }, + { + "address": "0x48fe", + "from": "strcasecmp", + "indirect": true, + "target": "*%rax" + }, + { + "address": "0x4932", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x10f0" + }, + { + "address": "0x4962", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x10f0" + }, + { + "address": "0x4984", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x10f0" + }, + { + "address": "0x49df", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x10f0" + }, + { + "address": "0x4a4f", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3c80" + }, + { + "address": "0x4a8f", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3c80" + }, + { + "address": "0x4aa9", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3c80" + }, + { + "address": "0x4b0a", + "from": "strcasecmp", + "indirect": true, + "target": "*%rax" + }, + { + "address": "0x4b34", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3e60" + }, + { + "address": "0x4b59", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3c80" + }, + { + "address": "0x4b7c", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3c80" + }, + { + "address": "0x4b9a", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3c80" + }, + { + "address": "0x4bb4", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3c80" + }, + { + "address": "0x4bcc", + "from": "strcasecmp", + "indirect": true, + "target": "*%rax" + }, + { + "address": "0x4c0a", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3e60" + }, + { + "address": "0x4c2f", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3c80" + }, + { + "address": "0x4c58", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3c80" + }, + { + "address": "0x4c9f", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3c80" + }, + { + "address": "0x4cc6", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3c80" + }, + { + "address": "0x4ce3", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3c80" + }, + { + "address": "0x4d01", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3c80" + }, + { + "address": "0x4d9b", + "from": "strcasecmp", + "indirect": true, + "target": "*%rax" + }, + { + "address": "0x4dee", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3fe0" + }, + { + "address": "0x4e22", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3c80" + }, + { + "address": "0x4e61", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x10f0" + }, + { + "address": "0x4e84", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3c80" + }, + { + "address": "0x4ea4", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3c80" + }, + { + "address": "0x4ed9", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x10f0" + }, + { + "address": "0x4f61", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3e60" + }, + { + "address": "0x4f7b", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3c80" + }, + { + "address": "0x4fa7", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3c80" + }, + { + "address": "0x4fd1", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3e60" + }, + { + "address": "0x4feb", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3c80" + }, + { + "address": "0x5017", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3aa0" + }, + { + "address": "0x5041", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3e60" + }, + { + "address": "0x505b", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3c80" + }, + { + "address": "0x5087", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3c80" + }, + { + "address": "0x50b1", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3e60" + }, + { + "address": "0x50cb", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3c80" + }, + { + "address": "0x50f7", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3aa0" + }, + { + "address": "0x5122", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3e60" + }, + { + "address": "0x513e", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3c80" + }, + { + "address": "0x515e", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3c80" + }, + { + "address": "0x519c", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3c80" + }, + { + "address": "0x51b5", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3c80" + }, + { + "address": "0x51ec", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3c80" + }, + { + "address": "0x5212", + "from": "strcasecmp", + "indirect": true, + "target": "*%rax" + }, + { + "address": "0x5242", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x45d0" + }, + { + "address": "0x5289", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3c80" + }, + { + "address": "0x52bb", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3aa0" + }, + { + "address": "0x52d5", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3c80" + }, + { + "address": "0x52fb", + "from": "strcasecmp", + "indirect": true, + "target": "*%rax" + }, + { + "address": "0x538d", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x10f0" + }, + { + "address": "0x53a5", + "from": "strcasecmp", + "indirect": true, + "target": "*0xf455(%rip) # 0x14800" + }, + { + "address": "0x53c2", + "from": "strcasecmp", + "indirect": true, + "target": "*0xf458(%rip) # 0x14820" + }, + { + "address": "0x53d9", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x10f0" + }, + { + "address": "0x53fa", + "from": "strcasecmp", + "indirect": true, + "target": "*0xf420(%rip) # 0x14820" + }, + { + "address": "0x5411", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x10f0" + }, + { + "address": "0x5432", + "from": "strcasecmp", + "indirect": true, + "target": "*0xf3e8(%rip) # 0x14820" + }, + { + "address": "0x5449", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x10f0" + }, + { + "address": "0x546a", + "from": "strcasecmp", + "indirect": true, + "target": "*0xf3b0(%rip) # 0x14820" + }, + { + "address": "0x5481", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x10f0" + }, + { + "address": "0x54a7", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x10f0" + }, + { + "address": "0x54bd", + "from": "strcasecmp", + "indirect": true, + "target": "*0xf365(%rip) # 0x14828" + }, + { + "address": "0x54ef", + "from": "strcasecmp", + "indirect": true, + "target": "*0xf33b(%rip) # 0x14830" + }, + { + "address": "0x5509", + "from": "strcasecmp", + "indirect": true, + "target": "*%r13" + }, + { + "address": "0x554b", + "from": "strcasecmp", + "indirect": true, + "target": "*0xf2cf(%rip) # 0x14820" + }, + { + "address": "0x5562", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x10f0" + }, + { + "address": "0x5587", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x10f0" + }, + { + "address": "0x55a8", + "from": "strcasecmp", + "indirect": true, + "target": "*0xf272(%rip) # 0x14820" + }, + { + "address": "0x55bf", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x10f0" + }, + { + "address": "0x55f2", + "from": "strcasecmp", + "indirect": true, + "target": "*0xf228(%rip) # 0x14820" + }, + { + "address": "0x5609", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x10f0" + }, + { + "address": "0x5631", + "from": "strcasecmp", + "indirect": true, + "target": "*0xf1e9(%rip) # 0x14820" + }, + { + "address": "0x5648", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x10f0" + }, + { + "address": "0x566c", + "from": "strcasecmp", + "indirect": true, + "target": "*0xf1ae(%rip) # 0x14820" + }, + { + "address": "0x5683", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x10f0" + }, + { + "address": "0x56a7", + "from": "strcasecmp", + "indirect": true, + "target": "*0xf173(%rip) # 0x14820" + }, + { + "address": "0x56be", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x10f0" + }, + { + "address": "0x572e", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x47f0" + }, + { + "address": "0x5741", + "from": "strcasecmp", + "indirect": true, + "target": "*0xf0b9(%rip) # 0x14800" + }, + { + "address": "0x5751", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x4d30" + }, + { + "address": "0x576c", + "from": "strcasecmp", + "indirect": true, + "target": "*0xf0c6(%rip) # 0x14838" + }, + { + "address": "0x5801", + "from": "strcasecmp", + "indirect": true, + "target": "*0xeff9(%rip) # 0x14800" + }, + { + "address": "0x5813", + "from": "strcasecmp", + "indirect": true, + "target": "*0xf09f(%rip) # 0x148b8" + }, + { + "address": "0x581f", + "from": "strcasecmp", + "indirect": true, + "target": "*0xf093(%rip) # 0x148b8" + }, + { + "address": "0x583d", + "from": "strcasecmp", + "indirect": true, + "target": "*0xf065(%rip) # 0x148a8" + }, + { + "address": "0x584d", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x5300" + }, + { + "address": "0x5867", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x6240" + }, + { + "address": "0x5889", + "from": "strcasecmp", + "indirect": true, + "target": "*0xeff9(%rip) # 0x14888" + }, + { + "address": "0x58b6", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x4d30" + }, + { + "address": "0x59ea", + "from": "strcasecmp", + "indirect": true, + "target": "*0xee48(%rip) # 0x14838" + }, + { + "address": "0x5a7d", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x4f00" + }, + { + "address": "0x5afc", + "from": "strcasecmp", + "indirect": true, + "target": "*0x10(%rdi)" + }, + { + "address": "0x5b26", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x4fa0" + }, + { + "address": "0x5b6e", + "from": "strcasecmp", + "indirect": true, + "target": "*0x20(%rdi)" + }, + { + "address": "0x5b93", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x5020" + }, + { + "address": "0x5bf3", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x5070" + }, + { + "address": "0x5c19", + "from": "strcasecmp", + "indirect": true, + "target": "*0x28(%rbx)" + }, + { + "address": "0x5c72", + "from": "strcasecmp", + "indirect": true, + "target": "*0xebe8(%rip) # 0x14860" + }, + { + "address": "0x5cbc", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3a60" + }, + { + "address": "0x5ce3", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3a60" + }, + { + "address": "0x5d0a", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3a60" + }, + { + "address": "0x5d31", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3a60" + }, + { + "address": "0x5d58", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3a60" + }, + { + "address": "0x5d7f", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3a60" + }, + { + "address": "0x5da6", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3a60" + }, + { + "address": "0x5dc9", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3a60" + }, + { + "address": "0x5dec", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3a60" + }, + { + "address": "0x5dfe", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x68a0" + }, + { + "address": "0x5e0a", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x6040" + }, + { + "address": "0x5e16", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x72b0" + }, + { + "address": "0x5e5a", + "from": "strcasecmp", + "indirect": true, + "target": "*0xe9e0(%rip) # 0x14840" + }, + { + "address": "0x5ec8", + "from": "strcasecmp", + "indirect": true, + "target": "*0xe93a(%rip) # 0x14808" + }, + { + "address": "0x5f05", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x47f0" + }, + { + "address": "0x5f27", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x10f0" + }, + { + "address": "0x5f4a", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x10f0" + }, + { + "address": "0x5f66", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x10f0" + }, + { + "address": "0x5f74", + "from": "strcasecmp", + "indirect": true, + "target": "*0xe946(%rip) # 0x148c0" + }, + { + "address": "0x5f96", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x10f0" + }, + { + "address": "0x5faf", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x10f0" + }, + { + "address": "0x5fc1", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x1450" + }, + { + "address": "0x5fca", + "from": "strcasecmp", + "indirect": true, + "target": "*0xe898(%rip) # 0x14868" + }, + { + "address": "0x5fd9", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x1450" + }, + { + "address": "0x5fec", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x10f0" + }, + { + "address": "0x5ffb", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x1450" + }, + { + "address": "0x60e7", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x10f0" + }, + { + "address": "0x6181", + "from": "strcasecmp", + "indirect": true, + "target": "*0xe749(%rip) # 0x148d0" + }, + { + "address": "0x6331", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x11b0" + }, + { + "address": "0x633a", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x5150" + }, + { + "address": "0x6371", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x4bb0" + }, + { + "address": "0x6387", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x4e50" + }, + { + "address": "0x6398", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x4e80" + }, + { + "address": "0x6496", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x6130" + }, + { + "address": "0x650c", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x4fa0" + }, + { + "address": "0x651d", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x5000" + }, + { + "address": "0x6533", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x4fa0" + }, + { + "address": "0x6544", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x5000" + }, + { + "address": "0x6569", + "from": "strcasecmp", + "indirect": true, + "target": "*0xe361(%rip) # 0x148d0" + }, + { + "address": "0x659f", + "from": "strcasecmp", + "indirect": true, + "target": "*0xe32b(%rip) # 0x148d0" + }, + { + "address": "0x65cb", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x11b0" + }, + { + "address": "0x662d", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x11b0" + }, + { + "address": "0x664b", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x1450" + }, + { + "address": "0x6654", + "from": "strcasecmp", + "indirect": true, + "target": "*0xe20e(%rip) # 0x14868" + }, + { + "address": "0x6678", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x11b0" + }, + { + "address": "0x6753", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x10f0" + }, + { + "address": "0x6760", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x1450" + }, + { + "address": "0x679d", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x47e0" + }, + { + "address": "0x67bd", + "from": "strcasecmp", + "indirect": true, + "target": "*0xe045(%rip) # 0x14808" + }, + { + "address": "0x67ec", + "from": "strcasecmp", + "indirect": true, + "target": "*%r13" + }, + { + "address": "0x6826", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x6130" + }, + { + "address": "0x6891", + "from": "strcasecmp", + "indirect": true, + "target": "*(%rax,%r13,8)" + }, + { + "address": "0x6960", + "from": "strcasecmp", + "indirect": true, + "target": "*0xdf2a(%rip) # 0x14890" + }, + { + "address": "0x6a57", + "from": "strcasecmp", + "indirect": true, + "target": "*(%rax,%r14,8)" + }, + { + "address": "0x6a95", + "from": "strcasecmp", + "indirect": true, + "target": "*0xddcd(%rip) # 0x14868" + }, + { + "address": "0x6ab5", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x10f0" + }, + { + "address": "0x6b15", + "from": "strcasecmp", + "indirect": true, + "target": "*0xdd4d(%rip) # 0x14868" + }, + { + "address": "0x6b35", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x10f0" + }, + { + "address": "0x6b9c", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3a60" + }, + { + "address": "0x6bc3", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3a60" + }, + { + "address": "0x6bea", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3a60" + }, + { + "address": "0x6c0d", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3a60" + }, + { + "address": "0x6c30", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3a60" + }, + { + "address": "0x6c53", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3a60" + }, + { + "address": "0x6cd8", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x4fa0" + }, + { + "address": "0x6ce9", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x5000" + }, + { + "address": "0x6cfe", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x4fa0" + }, + { + "address": "0x6d0f", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x5000" + }, + { + "address": "0x6d2a", + "from": "strcasecmp", + "indirect": true, + "target": "*0xdba0(%rip) # 0x148d0" + }, + { + "address": "0x6d57", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x11b0" + }, + { + "address": "0x6d9a", + "from": "strcasecmp", + "indirect": true, + "target": "*0xdaa8(%rip) # 0x14848" + }, + { + "address": "0x6e08", + "from": "strcasecmp", + "indirect": true, + "target": "*0xda02(%rip) # 0x14810" + }, + { + "address": "0x6e4c", + "from": "strcasecmp", + "indirect": true, + "target": "*0xda46(%rip) # 0x14898" + }, + { + "address": "0x6e64", + "from": "strcasecmp", + "indirect": true, + "target": "*0xda2e(%rip) # 0x14898" + }, + { + "address": "0x6e7c", + "from": "strcasecmp", + "indirect": true, + "target": "*0xda16(%rip) # 0x14898" + }, + { + "address": "0x6e9c", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x40b0" + }, + { + "address": "0x6eb4", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x47f0" + }, + { + "address": "0x6f05", + "from": "strcasecmp", + "indirect": true, + "target": "*0xd98d(%rip) # 0x14898" + }, + { + "address": "0x6f13", + "from": "strcasecmp", + "indirect": true, + "target": "*0xd9d7(%rip) # 0x148f0" + }, + { + "address": "0x6f28", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x11b0" + }, + { + "address": "0x6f4b", + "from": "strcasecmp", + "indirect": true, + "target": "*0xd98f(%rip) # 0x148e0" + }, + { + "address": "0x6f71", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3a60" + }, + { + "address": "0x6fa7", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3fe0" + }, + { + "address": "0x6fc4", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3c80" + }, + { + "address": "0x6fe5", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3c80" + }, + { + "address": "0x6ffa", + "from": "strcasecmp", + "indirect": true, + "target": "*0xd8c8(%rip) # 0x148c8" + }, + { + "address": "0x7021", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3c80" + }, + { + "address": "0x7032", + "from": "strcasecmp", + "indirect": true, + "target": "*0xd890(%rip) # 0x148c8" + }, + { + "address": "0x7059", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3c80" + }, + { + "address": "0x70af", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x1390" + }, + { + "address": "0x70d1", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x1450" + }, + { + "address": "0x712c", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x15f0" + }, + { + "address": "0x71af", + "from": "strcasecmp", + "indirect": true, + "target": "*0xd6fb(%rip) # 0x148b0" + }, + { + "address": "0x71d3", + "from": "strcasecmp", + "indirect": true, + "target": "*0xd6d7(%rip) # 0x148b0" + }, + { + "address": "0x72d4", + "from": "strcasecmp", + "indirect": true, + "target": "*0xd60e(%rip) # 0x148e8" + }, + { + "address": "0x72ea", + "from": "strcasecmp", + "indirect": true, + "target": "*0xd580(%rip) # 0x14870" + }, + { + "address": "0x72fc", + "from": "strcasecmp", + "indirect": true, + "target": "*0xd56e(%rip) # 0x14870" + }, + { + "address": "0x7364", + "from": "strcasecmp", + "indirect": true, + "target": "*0xd57e(%rip) # 0x148e8" + }, + { + "address": "0x7384", + "from": "strcasecmp", + "indirect": true, + "target": "*0xd4e6(%rip) # 0x14870" + }, + { + "address": "0x7396", + "from": "strcasecmp", + "indirect": true, + "target": "*0xd4d4(%rip) # 0x14870" + }, + { + "address": "0x7407", + "from": "strcasecmp", + "indirect": true, + "target": "*%r15" + }, + { + "address": "0x742b", + "from": "strcasecmp", + "indirect": true, + "target": "*%r15" + }, + { + "address": "0x744d", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3a60" + }, + { + "address": "0x7474", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3a60" + }, + { + "address": "0x749b", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3a60" + }, + { + "address": "0x74c2", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3a60" + }, + { + "address": "0x74e9", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3a60" + }, + { + "address": "0x7510", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3a60" + }, + { + "address": "0x7533", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3a60" + }, + { + "address": "0x7555", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x40b0" + }, + { + "address": "0x7572", + "from": "strcasecmp", + "indirect": true, + "target": "*0xd368(%rip) # 0x148e0" + }, + { + "address": "0x758e", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3a60" + }, + { + "address": "0x75cb", + "from": "strcasecmp", + "indirect": true, + "target": "*0xd27f(%rip) # 0x14850" + }, + { + "address": "0x75e2", + "from": "strcasecmp", + "indirect": true, + "target": "*%r13" + }, + { + "address": "0x7667", + "from": "strcasecmp", + "indirect": true, + "target": "*0xd1ab(%rip) # 0x14818" + }, + { + "address": "0x767f", + "from": "strcasecmp", + "indirect": true, + "target": "*%r13" + }, + { + "address": "0x782f", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x4bb0" + }, + { + "address": "0x7840", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x4e50" + }, + { + "address": "0x7850", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x4e80" + }, + { + "address": "0x7864", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x11b0" + }, + { + "address": "0x7878", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x11b0" + }, + { + "address": "0x7881", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x5150" + }, + { + "address": "0x791c", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x11b0" + }, + { + "address": "0x798d", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x4fa0" + }, + { + "address": "0x799e", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x5000" + }, + { + "address": "0x79b9", + "from": "strcasecmp", + "indirect": true, + "target": "*0xcf19(%rip) # 0x148d8" + }, + { + "address": "0x7a82", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x4fa0" + }, + { + "address": "0x7a97", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x5000" + }, + { + "address": "0x7aee", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x4fa0" + }, + { + "address": "0x7b03", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x5000" + }, + { + "address": "0x7b4e", + "from": "strcasecmp", + "indirect": true, + "target": "*0xcd84(%rip) # 0x148d8" + }, + { + "address": "0x7b71", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x11b0" + }, + { + "address": "0x7b9b", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x11b0" + }, + { + "address": "0x7c00", + "from": "strcasecmp", + "indirect": true, + "target": "*(%rax,%r13,8)" + }, + { + "address": "0x7cb9", + "from": "strcasecmp", + "indirect": true, + "target": "*0xcbe1(%rip) # 0x148a0" + }, + { + "address": "0x7db0", + "from": "strcasecmp", + "indirect": true, + "target": "*(%r15,%rbx,8)" + }, + { + "address": "0x7e0c", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3a60" + }, + { + "address": "0x7e33", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3a60" + }, + { + "address": "0x7e56", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3a60" + }, + { + "address": "0x7e79", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3a60" + }, + { + "address": "0x7e9c", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3a60" + }, + { + "address": "0x7eec", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3a60" + }, + { + "address": "0x7f13", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3a60" + }, + { + "address": "0x7f3a", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3a60" + }, + { + "address": "0x7f61", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3a60" + }, + { + "address": "0x7f84", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3a60" + }, + { + "address": "0x7fab", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3a60" + }, + { + "address": "0x7fce", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3a60" + }, + { + "address": "0x7ff1", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3a60" + }, + { + "address": "0x8014", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3a60" + }, + { + "address": "0x8052", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x10f0" + }, + { + "address": "0x8062", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x4410" + }, + { + "address": "0x8078", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x44f0" + }, + { + "address": "0x8099", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x4480" + }, + { + "address": "0x80b9", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x4560" + }, + { + "address": "0x80ce", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x10f0" + }, + { + "address": "0x80d6", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3e60" + }, + { + "address": "0x80f8", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3c80" + }, + { + "address": "0x811c", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3aa0" + }, + { + "address": "0x8140", + "from": "strcasecmp", + "indirect": false, + "target": "strcasecmp+0x3aa0" + } + ], + "dt_needed": [ + "libSceLibcInternal.sprx", + "libSceNet.sprx", + "libkernel_web.sprx" + ], + "filename": "chimera-pldmgr-phase07-a.elf", + "imports": [], + "report_sha256": { + "callgraph": "edec2903b7f97d3173eb1f5dc68462a4931aac5f25ab09ab6ed89e6d3b4a0fc1", + "disassembly": "f93bac307884f4e3ac7645b47e22e55fb73304267f41bc6bd3b951bf81751d34", + "dynamic": "0f186dccc1ad1118ddf8d02533b700cd41bea55f21bf7184d66025e972c42499", + "headers_sections": "f0bdc54e0e1ef42dce727e483af5291b515e9a238bc7d97d5e2d470c28beb29e", + "linker_map_normalized": "9912ee358636a91433f77de01075ebd4eee22cf35b4f8715f4f944a930589906", + "relocations": "652715a1d36b2f91d82b9f44a28cc566cde7ffc1ecb7ddafee8f1d577b6207a0", + "symbols": "890dce24020ae3a27aa38ef719c482be23f2eb9cde7c8b8b36fbd8a659979d05", + "undefined": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "sensitive_static_inventory": { + "categories": { + "dynamic_loading": { + "directly_reachable_from_entrypoint": [], + "linked": [ + "kernel_dynlib_dlsym", + "kernel_dynlib_handle", + "kernel_dynlib_obj", + "kernel_dynlib_resolve", + "sceKernelLoadStartModule", + "sceKernelStopUnloadModule" + ], + "linked_in_disassembly": [], + "linked_in_linker_map": [ + "kernel_dynlib_dlsym", + "kernel_dynlib_handle", + "kernel_dynlib_obj", + "kernel_dynlib_resolve", + "sceKernelLoadStartModule", + "sceKernelStopUnloadModule" + ] + }, + "graphics_or_display": { + "directly_reachable_from_entrypoint": [], + "linked": [], + "linked_in_disassembly": [], + "linked_in_linker_map": [] + }, + "kernel_runtime_write": { + "directly_reachable_from_entrypoint": [], + "linked": [ + "__patch_init", + "kernel_copyin", + "kernel_copyout", + "kernel_set_ucred_attrs", + "kernel_set_ucred_caps" + ], + "linked_in_disassembly": [], + "linked_in_linker_map": [ + "__patch_init", + "kernel_copyin", + "kernel_copyout", + "kernel_set_ucred_attrs", + "kernel_set_ucred_caps" + ] + }, + "network": { + "directly_reachable_from_entrypoint": [], + "linked": [ + "verified_close_socket" + ], + "linked_in_disassembly": [], + "linked_in_linker_map": [ + "verified_close_socket" + ] + }, + "ptrace_or_jit": { + "directly_reachable_from_entrypoint": [], + "linked": [], + "linked_in_disassembly": [], + "linked_in_linker_map": [] + } + }, + "direct_call_reachability_available": false, + "direct_call_reachability_only": true, + "entrypoint": "_start", + "indirect_call_edges_retained_but_not_resolved": 208 + }, + "sha256": "8fecf8241a46246eddbd21e8bb4d875f5d76f1f4f4c6a11384df1f131aa5e5b1", + "size": 99560 + } + }, + "decision": "READY_FOR_HARDENED_RUNTIME_DEPLOYMENT", + "firmware": "9.60", + "forbidden_lifecycle_imports": [], + "offline_only": true, + "permanent_denylist_sha256": "4be1c17b4964f2b68c39b5145bc4af4619c32512d60269ecf5c39728b390fa63", + "ps5_actions": { + "connected": false, + "executed": false, + "installed": false, + "transferred": false + }, + "schema_version": 1, + "source_commits": { + "lifecycle": "fe08300339a13f899fb78ea404ada381a5cba87c", + "loader": "197623058f509eddde18868dafcb92fdcac66464", + "manager": "e23d94ff91233aa770e2342800c1467875bdef44" + } +} diff --git a/manifests/runtime/phase-0.8-read-only-preflight.json b/manifests/runtime/phase-0.8-read-only-preflight.json new file mode 100644 index 0000000..9f623b3 --- /dev/null +++ b/manifests/runtime/phase-0.8-read-only-preflight.json @@ -0,0 +1,232 @@ +{ + "schema_version": 1, + "phase": "0.8", + "preflight_id": "phase08-offline-admissibility-2026-07-18", + "scope": "offline_admissibility_audit_only", + "date": "2026-07-18", + "timezone": "Europe/Brussels", + "operator": "Codex", + "decision": "READ_ONLY_PREFLIGHT_BLOCKED", + "dataset_complete": false, + "open_stop_ro": true, + "open_stop_gate": true, + "on_device_session_started": false, + "authorization": { + "explicit_read_only_preflight_permission_recorded": false, + "permission_reference": null, + "permission_exact_text": null, + "connection_authorized": false, + "installation_authorized": false, + "lifecycle_authorized": false, + "execution_authorized": false, + "automatic_retry": false + }, + "ps5_actions": { + "connected": false, + "file_created": false, + "file_modified": false, + "file_deleted_or_renamed": false, + "configuration_changed": false, + "service_started_stopped_or_restarted": false, + "process_signaled": false, + "artifact_transferred": false, + "artifact_executed": false, + "autoload_activated": false, + "automatic_retry_performed": false + }, + "collector_assessment": { + "selected_collector": null, + "can_prove_no_atime_audit_cache_or_metadata_change": false, + "writes_output_or_logs_on_ps5": "unknown_no_collector_selected", + "candidates": [ + { + "id": "payload_manager_v0_3_1_http", + "version": "0.3.1", + "origin": "itsPLK/ps5-payload-manager", + "source_commit": "cfbc70f30f419b09bf2b52283f7409e2d3117ee1", + "usable": false, + "result": "STOP-RO", + "reasons": [ + "Every handled non-OPTIONS request calls log_server_set_active(), which writes server_active_flag in process memory.", + "The /autoload_status route calls pldmgr_autoload_get_status(), which writes autoload_triggered = 1 before returning status.", + "The /autoload_status and /get_config routes open /data/pldmgr/autoload.txt for reading; no no-atime mount or open guarantee is proven.", + "Non-noisy routes call pldmgr_log(), which writes stdout and the in-process log ring." + ] + }, + { + "id": "unspecified_filesystem_collector", + "version": null, + "origin": null, + "source_commit": null, + "usable": false, + "result": "STOP-RO", + "reasons": [ + "No exact collector, version, transport, operation set, or source was supplied.", + "No proof excludes atime, audit-log, cache, filesystem-metadata, process-state, or on-device log changes." + ] + } + ] + }, + "firmware": { + "expected_exact": "9.60", + "source_1": { + "kind": "user_attestation", + "value": "9.60", + "current_device_read": false + }, + "source_2": null, + "two_current_sources_agree": "UNPROVEN", + "result": "STOP-GATE" + }, + "current_live_state": { + "stock_elfldr": { + "expected_size": 397000, + "expected_sha256": "092d16ee0ede0c494947efd38d1a17bbd7cc4b022d3858ea898833c188c703e8", + "path": null, + "object_identity": null, + "metadata": null, + "pre_post_stable": "UNPROVEN", + "current_hash_match": "UNPROVEN", + "result": "STOP-GATE" + }, + "stock_payload_manager": { + "expected_size": 2050320, + "expected_sha256": "518740adbacccb9094fadb07dd424c53ee290f38306449ccc9d6957fdf813c0b", + "path": null, + "object_identity": null, + "metadata": null, + "pre_post_stable": "UNPROVEN", + "current_hash_match": "UNPROVEN", + "result": "STOP-GATE" + }, + "processes_and_services": { + "complete": false, + "unknown_or_unmapped_relevant_processes": "UNPROVEN", + "result": "STOP-GATE" + }, + "listeners": { + "required_ports": [ + 8084, + 8085, + 9021 + ], + "complete": false, + "unambiguous_owners": "UNPROVEN", + "no_conflict_or_unexpected_exposure": "UNPROVEN", + "result": "STOP-GATE" + }, + "autoload_startup_retry": { + "authoritative_sources_complete": false, + "lifecycle_probe_absent_from_autoload": "UNPROVEN", + "hardened_runtime_absent_from_autoload": "UNPROVEN", + "automatic_retry_inactive": "UNPROVEN", + "result": "STOP-GATE" + } + }, + "rollback_preconditions": { + "separate_destination": { + "minimum_content_bytes": 2447320, + "path": null, + "separate_device": "UNPROVEN", + "available_bytes": null, + "filesystem_reserve_assessed": false, + "result": "STOP-GATE" + }, + "stock_elfldr_backup": { + "already_present": "UNPROVEN", + "path": null, + "expected_size": 397000, + "expected_sha256": "092d16ee0ede0c494947efd38d1a17bbd7cc4b022d3858ea898833c188c703e8", + "exact_separate_object": "UNPROVEN", + "restore_mapping_unambiguous": "UNPROVEN", + "result": "STOP-GATE" + }, + "stock_payload_manager_backup": { + "already_present": "UNPROVEN", + "path": null, + "expected_size": 2050320, + "expected_sha256": "518740adbacccb9094fadb07dd424c53ee290f38306449ccc9d6957fdf813c0b", + "exact_separate_object": "UNPROVEN", + "restore_mapping_unambiguous": "UNPROVEN", + "result": "HARD_STOP-GATE" + } + }, + "blockers": [ + { + "id": "explicit_permission_record_absent", + "severity": "STOP-RO", + "minimum_missing_evidence": "Artifact-independent exact permission text and a stable task reference authorizing only this read-only preflight." + }, + { + "id": "collector_side_effect_freedom_unproven", + "severity": "STOP-RO", + "minimum_missing_evidence": "A pinned public collector and transport whose complete source and environment prove no file, metadata, audit-log, cache, process-state, network-service-state, or on-device log mutation." + }, + { + "id": "payload_manager_http_mutates_runtime_state", + "severity": "STOP-RO", + "minimum_missing_evidence": "A different collector path; the reviewed stock Payload Manager HTTP path cannot satisfy this Phase-0.8 contract." + }, + { + "id": "two_source_firmware_attestation_absent", + "severity": "STOP-GATE", + "minimum_missing_evidence": "Two authoritative current read-only firmware observations that agree exactly on 9.60." + }, + { + "id": "current_live_identity_and_topology_absent", + "severity": "STOP-GATE", + "minimum_missing_evidence": "Stable pre/post paths, object IDs, metadata, sizes, hashes, processes, services, and listener ownership from an admissible collector." + }, + { + "id": "autoload_startup_retry_state_absent", + "severity": "STOP-GATE", + "minimum_missing_evidence": "All authoritative current autoload, startup, and retry sources collected without side effects." + }, + { + "id": "stock_elfldr_backup_unproven", + "severity": "STOP-GATE", + "minimum_missing_evidence": "An already-present, separate, byte-exact stock elfldr backup and unambiguous restore mapping." + }, + { + "id": "stock_payload_manager_backup_unproven", + "severity": "HARD_STOP-GATE", + "minimum_missing_evidence": "An already-present, separate, byte-exact stock Payload Manager backup and unambiguous restore mapping." + } + ], + "source_evidence": [ + { + "component": "Payload Manager HTTP dispatch", + "path": "src/http_server.c", + "sha256": "35cf5d8f0dd44cf64ceab5e4b0ecc09413c82d7e9946ba9de2ca4b1898631fdd", + "source_commit": "cfbc70f30f419b09bf2b52283f7409e2d3117ee1", + "relevant_lines": [ + "131-152", + "479-481", + "835-930" + ] + }, + { + "component": "Payload Manager autoload state", + "path": "src/autoload.c", + "sha256": "7051cab3ee1a3e0b9f6498000565eb9e160b9c63efa1771f250e98ec3aa4ae67", + "source_commit": "cfbc70f30f419b09bf2b52283f7409e2d3117ee1", + "relevant_lines": [ + "33-49", + "51-93" + ] + }, + { + "component": "Payload Manager log state", + "path": "src/log_server.c", + "sha256": "659095f43df1bbe8eb24acb165f027edc277af1e60aabb26ba9e3920b233d6f1", + "source_commit": "cfbc70f30f419b09bf2b52283f7409e2d3117ee1", + "relevant_lines": [ + "12-55" + ] + } + ], + "historical_evidence_is_not_current_preflight_evidence": true, + "payload_manager_backup_exactly_present": "UNPROVEN", + "installation_approval_after_preflight": "NOT AUTHORIZED", + "lifecycle_approval_after_preflight": "NOT AUTHORIZED" +} diff --git a/manifests/runtime/phase-0.8-remediation.json b/manifests/runtime/phase-0.8-remediation.json new file mode 100644 index 0000000..dffe1fb --- /dev/null +++ b/manifests/runtime/phase-0.8-remediation.json @@ -0,0 +1,734 @@ +{ + "schema_version": 1, + "schema_contract": { + "id": "chimera-gfx-phase-0.8-remediation-v1", + "shared_schema_available": false, + "validator": "tools/validate_phase08_remediation.py", + "reason": "Existing artifact and controlled-runtime schemas describe built runtime artifacts and cannot represent an offline blocked evidence contract without misleading artifact semantics." + }, + "phase_id": "0.8R", + "phase": "offline_remediation", + "status": "READ_ONLY_PREFLIGHT_BLOCKED", + "date": "2026-07-18", + "historical_identity": { + "source_commit": "2c944d6d65a08f7e1c02f518721cde061b999329", + "status": "READ_ONLY_PREFLIGHT_BLOCKED", + "historical_validation_report": { + "reported_date": "2026-07-18", + "host_tests": "17/17 passed", + "manifest_and_safety_checks": "passed", + "secret_scan": "passed across 558 text files", + "classification": "historical_report_not_current_hardware_evidence" + } + }, + "immutable_evidence": [ + { + "path": "docs/runtime/phase-0.8-read-only-preflight.md", + "role": "historical_human_readable_preflight_record", + "sha256": "3fbe086175a6048176075f447ec1482074928e3b5282db97ea2169395fe1d508" + }, + { + "path": "manifests/runtime/phase-0.8-read-only-preflight.json", + "role": "historical_machine_readable_preflight_record", + "sha256": "47d7f452f8799979fe99b3e6d56859f03544112725bf7e5b349eba5ed81b3322" + }, + { + "path": "tests/test_phase08_preflight.py", + "role": "historical_fail_closed_regression_test", + "sha256": "8a4ad7c70de28ffe3148fd3fd1f68c36a872c53c691c9068e1ff163970863c48" + } + ], + "authorization": { + "authorized": false, + "installation_authorized": false, + "lifecycle_authorized": false, + "execution_authorized": false, + "transfer_authorized": false, + "automatic_retry": false + }, + "activity": { + "hardware_observed": false, + "ps5_connected": false, + "files_transferred": false, + "target_execution_performed": false, + "target_mutation_performed": false, + "target_artifact_created": false, + "collector_implemented": false, + "target_build_performed": false + }, + "firmware_runtime_behavior": "UNPROVEN", + "stock_identities": { + "classification": "reference_only", + "current_device_observed": false, + "elfldr": { + "size": 397000, + "sha256": "092d16ee0ede0c494947efd38d1a17bbd7cc4b022d3858ea898833c188c703e8", + "current_device_match": "UNPROVEN" + }, + "payload_manager": { + "size": 2050320, + "sha256": "518740adbacccb9094fadb07dd424c53ee290f38306449ccc9d6957fdf813c0b", + "current_device_match": "UNPROVEN" + } + }, + "payload_manager_backup": { + "classification": "hard_blocker", + "on_device_proven": false, + "byte_exact_proven": false, + "creation_allowed_in_strict_read_only_phase": false, + "result": "HARD_STOP-GATE" + }, + "claim_boundaries": { + "hardware_safety_proven": false, + "firmware_behavior_proven": false, + "absence_of_volatile_effects_proven": false, + "no_persistent_write_found_equals_side_effect_free": false, + "host_tests_are_hardware_evidence": false, + "missing_observation_means_safe_absence": false + }, + "side_effect_categories": [ + { + "id": "A", + "name": "PERSISTENT_MUTATION", + "definition": "File creation/write/delete/rename, configuration or autoload changes, metadata changes, or persistent platform-state changes." + }, + { + "id": "B", + "name": "PROCESS_OR_SERVICE_WIDE_MUTATION", + "definition": "Global flags, functional counters, autoload state, service state, process signaling, or retry state." + }, + { + "id": "C", + "name": "VOLATILE_TECHNICAL_EFFECT", + "definition": "Socket acceptance, temporary buffers, allocation, scheduler activity, kernel counters, caches, audit or logging paths, and temporary process-local state." + }, + { + "id": "D", + "name": "OBSERVATION", + "definition": "Firmware, file metadata, hashes, process/listener state, configuration contents, rollback identities, or storage conditions." + } + ], + "payload_manager_source": { + "origin": "itsPLK/ps5-payload-manager", + "release": "v0.3.1", + "commit": "cfbc70f30f419b09bf2b52283f7409e2d3117ee1", + "files": [ + { + "path": "include/pldmgr.h", + "sha256": "01c693a3248dce7a663dd4ed9c73ce5f3a4443b5f2bd210746d94993dee27b91" + }, + { + "path": "src/http_server.c", + "sha256": "35cf5d8f0dd44cf64ceab5e4b0ecc09413c82d7e9946ba9de2ca4b1898631fdd" + }, + { + "path": "src/autoload.c", + "sha256": "7051cab3ee1a3e0b9f6498000565eb9e160b9c63efa1771f250e98ec3aa4ae67" + }, + { + "path": "src/log_server.c", + "sha256": "659095f43df1bbe8eb24acb165f027edc277af1e60aabb26ba9e3920b233d6f1" + }, + { + "path": "src/config.c", + "sha256": "2fa05a2781f7e41555653ea0fa2923c21bca7bb24c0062c722e0ae13be757b58" + }, + { + "path": "src/payload_mgr.c", + "sha256": "c2a442c2cc26ce85b5c63db12102b22e7ca552761eb1bf0acfd650c24e05e761" + }, + { + "path": "src/sources.c", + "sha256": "a7a4a5cafccfba74902d6ed21ba001e6a9f62d4dff840ac38882d04827162c96" + }, + { + "path": "src/process_mgr.c", + "sha256": "5b1b1be70878ccc56d13295362b78da7faaf8e3b7c009472be8d65a9db9ad97c" + } + ] + }, + "side_effect_findings": [ + { + "id": "options_any_endpoint", + "http_method": "OPTIONS", + "endpoint": "*", + "handler_function": "http_on_request", + "writes_server_active_flag": false, + "writes_autoload_triggered": false, + "reads_filesystem_or_configuration": false, + "categories": [ + "C" + ], + "other_effects": [ + "network request acceptance and response handling", + "response allocation and transport effects", + "cache, audit, scheduler, and kernel-counter effects remain unproven" + ], + "source_references": [ + "src/http_server.c:131-147" + ], + "strict_read_only_preflight_suitable": false, + "reason": "OPTIONS does not produce the required evidence set and the transport's volatile effects are not proven absent." + }, + { + "id": "get_version", + "http_method": "GET", + "endpoint": "/version", + "handler_function": "http_on_request", + "writes_server_active_flag": true, + "writes_autoload_triggered": false, + "reads_filesystem_or_configuration": false, + "categories": [ + "B", + "C" + ], + "other_effects": [ + "writes process-global server_active_flag", + "network request and response effects remain" + ], + "source_references": [ + "src/http_server.c:131-152", + "src/http_server.c:835-838", + "src/log_server.c:20-23" + ], + "strict_read_only_preflight_suitable": false, + "reason": "The informational response still follows a process-wide state mutation." + }, + { + "id": "get_log", + "http_method": "GET", + "endpoint": "/log", + "handler_function": "http_on_request -> log_build_json", + "writes_server_active_flag": true, + "writes_autoload_triggered": false, + "reads_filesystem_or_configuration": false, + "categories": [ + "B", + "C", + "D" + ], + "other_effects": [ + "locks and reads the in-process log ring", + "allocates a response buffer" + ], + "source_references": [ + "src/http_server.c:131-152", + "src/http_server.c:826-834", + "src/log_server.c:20-23", + "src/log_server.c:111-127" + ], + "strict_read_only_preflight_suitable": false, + "reason": "It mutates process-global state and does not provide the required device evidence." + }, + { + "id": "get_autoload_status", + "http_method": "GET", + "endpoint": "/autoload_status", + "handler_function": "http_on_request -> pldmgr_autoload_get_status -> config_read", + "writes_server_active_flag": true, + "writes_autoload_triggered": true, + "reads_filesystem_or_configuration": true, + "categories": [ + "B", + "C", + "D" + ], + "other_effects": [ + "changes the functional autoload frontend-ready flag", + "can release the autoload worker from its frontend wait", + "reads /data/pldmgr/autoload.txt", + "reads /data/pldmgr/pldmgr_config.txt", + "atime, audit, cache, and metadata effects remain unproven" + ], + "source_references": [ + "include/pldmgr.h:22,41-42", + "src/http_server.c:131-152", + "src/http_server.c:847-871", + "src/autoload.c:44-48", + "src/autoload.c:78-93", + "src/config.c:12-49", + "src/log_server.c:20-23" + ], + "strict_read_only_preflight_suitable": false, + "reason": "The route is functionally mutating and performs configuration reads with unproven technical side effects." + }, + { + "id": "get_config", + "http_method": "GET", + "endpoint": "/get_config", + "handler_function": "http_on_request -> config_read", + "writes_server_active_flag": true, + "writes_autoload_triggered": false, + "reads_filesystem_or_configuration": true, + "categories": [ + "B", + "C", + "D" + ], + "other_effects": [ + "reads /data/pldmgr/pldmgr_config.txt", + "reads /data/pldmgr/autoload.txt", + "atime, audit, cache, and metadata effects remain unproven" + ], + "source_references": [ + "src/http_server.c:131-152", + "src/http_server.c:911-930", + "src/config.c:12-49", + "src/log_server.c:20-23" + ], + "strict_read_only_preflight_suitable": false, + "reason": "The route mutates process state and filesystem-read side effects are unproven." + }, + { + "id": "get_list_payloads", + "http_method": "GET", + "endpoint": "/list_payloads", + "handler_function": "http_on_request -> payload_mgr_list_json", + "writes_server_active_flag": true, + "writes_autoload_triggered": false, + "reads_filesystem_or_configuration": true, + "categories": [ + "B", + "C", + "D" + ], + "other_effects": [ + "reads configuration", + "scans internal and possible USB directories", + "reads sidecar metadata", + "filesystem and removable-media observation effects remain unproven" + ], + "source_references": [ + "src/http_server.c:131-152", + "src/http_server.c:601-608", + "src/payload_mgr.c:114-153", + "src/payload_mgr.c:196-238", + "src/log_server.c:20-23" + ], + "strict_read_only_preflight_suitable": false, + "reason": "It mutates process state and performs broad filesystem enumeration without a side-effect contract." + }, + { + "id": "get_processes_list", + "http_method": "GET", + "endpoint": "/processes_list", + "handler_function": "http_on_request -> process_list_json", + "writes_server_active_flag": true, + "writes_autoload_triggered": false, + "reads_filesystem_or_configuration": false, + "categories": [ + "B", + "C", + "D" + ], + "other_effects": [ + "performs sysctl process enumeration", + "allocates and frees a process snapshot", + "kernel counters, audit, scheduling, and service effects remain unproven" + ], + "source_references": [ + "src/http_server.c:131-152", + "src/http_server.c:609-616", + "src/process_mgr.c:43-88", + "src/log_server.c:20-23" + ], + "strict_read_only_preflight_suitable": false, + "reason": "The route mutates process state and its system-query effects are not contractually bounded." + }, + { + "id": "get_sources_list", + "http_method": "GET", + "endpoint": "/sources_list", + "handler_function": "http_on_request -> sources_list_json -> load_sources", + "writes_server_active_flag": true, + "writes_autoload_triggered": false, + "reads_filesystem_or_configuration": true, + "categories": [ + "B", + "C", + "D" + ], + "other_effects": [ + "reads /data/pldmgr/sources.json", + "is not on the noisy-route exclusion list and therefore also calls pldmgr_log", + "writes stdout and the process-local log ring" + ], + "source_references": [ + "src/http_server.c:111-126", + "src/http_server.c:131-152", + "src/http_server.c:479-481", + "src/http_server.c:706-713", + "src/sources.c:28-84", + "src/sources.c:112-134", + "src/log_server.c:20-55" + ], + "strict_read_only_preflight_suitable": false, + "reason": "It mutates active/log state and reads configuration without a side-effect guarantee." + }, + { + "id": "get_ip", + "http_method": "GET", + "endpoint": "/getip", + "handler_function": "http_on_request -> pldmgr_get_local_ip", + "writes_server_active_flag": true, + "writes_autoload_triggered": false, + "reads_filesystem_or_configuration": false, + "categories": [ + "B", + "C", + "D" + ], + "other_effects": [ + "queries network-interface state", + "network, kernel-counter, audit, cache, and scheduler effects remain unproven" + ], + "source_references": [ + "src/http_server.c:131-152", + "src/http_server.c:839-846", + "src/log_server.c:20-23" + ], + "strict_read_only_preflight_suitable": false, + "reason": "The route mutates process state and cannot establish the required preflight evidence set." + } + ], + "evidence_contract": [ + { + "id": "firmware", + "required_fact": "Two authoritative current observations agree on the exact firmware identifier and revision.", + "possible_evidence_source": "Two separately reviewed authoritative device or user-visible sources; exact methods are unset.", + "confidence": "UNPROVEN", + "known_mutations": [], + "unknown_effects": [ + "transport, UI, audit, cache, and platform-query effects" + ], + "required_collector_identity": [ + "name", + "version", + "origin", + "source commit", + "operation set", + "binary size and SHA-256 when code is involved" + ], + "timeout_ms": null, + "fail_closed_result": "STOP", + "reviewer_output": [ + "raw values", + "timestamps", + "source identities", + "exact agreement decision" + ] + }, + { + "id": "live_paths", + "required_fact": "Literal live paths, mount identities, object types, and no symlink traversal for both stock components.", + "possible_evidence_source": "A separately reviewed no-follow object metadata operation.", + "confidence": "UNPROVEN", + "known_mutations": [], + "unknown_effects": [ + "atime, audit, metadata cache, mount, and namespace effects" + ], + "required_collector_identity": [ + "tool and source identity", + "exact no-follow operation", + "filesystem and mount options" + ], + "timeout_ms": null, + "fail_closed_result": "STOP", + "reviewer_output": [ + "literal paths", + "mount IDs", + "object types", + "symlink decisions" + ] + }, + { + "id": "object_identities", + "required_fact": "Stable pre/post inode or object IDs, device IDs, owner, group, mode, mtime, and ctime.", + "possible_evidence_source": "A side-effect-reviewed metadata collector using the same objects before and after all reads.", + "confidence": "UNPROVEN", + "known_mutations": [], + "unknown_effects": [ + "metadata cache, audit, race, and concurrent process effects" + ], + "required_collector_identity": [ + "tool identity", + "object lookup semantics", + "race and symlink policy" + ], + "timeout_ms": null, + "fail_closed_result": "STOP", + "reviewer_output": [ + "complete pre/post metadata", + "stability decision", + "all raw errors" + ] + }, + { + "id": "file_sizes", + "required_fact": "Current live byte sizes equal the two reference sizes.", + "possible_evidence_source": "The same reviewed object handles used for identity collection.", + "confidence": "UNPROVEN", + "known_mutations": [], + "unknown_effects": [ + "metadata cache, audit, and concurrent modification effects" + ], + "required_collector_identity": [ + "tool identity", + "same-object binding", + "pre/post race controls" + ], + "timeout_ms": null, + "fail_closed_result": "STOP", + "reviewer_output": [ + "raw sizes", + "reference comparison", + "object bindings" + ] + }, + { + "id": "sha256", + "required_fact": "SHA-256 of the exact current live bytes equals each reference digest.", + "possible_evidence_source": "A source-reviewed, no-follow, same-object streaming hash operation.", + "confidence": "UNPROVEN", + "known_mutations": [], + "unknown_effects": [ + "atime, audit, page cache, scheduler, thermal, and concurrent modification effects" + ], + "required_collector_identity": [ + "hash implementation and version", + "source commit", + "binary size and SHA-256", + "object-open and race policy" + ], + "timeout_ms": null, + "fail_closed_result": "STOP", + "reviewer_output": [ + "bytes read", + "start/end time", + "digest", + "pre/post object identity", + "timeout and error state" + ] + }, + { + "id": "processes_services", + "required_fact": "Every relevant process and service maps to an executable object, parent, start time, status, and startup mechanism.", + "possible_evidence_source": "A separately reviewed process/service snapshot mechanism.", + "confidence": "UNPROVEN", + "known_mutations": [], + "unknown_effects": [ + "kernel counters, audit, scheduling, locking, cache, and service-query effects" + ], + "required_collector_identity": [ + "tool and API identity", + "source commit", + "snapshot semantics" + ], + "timeout_ms": null, + "fail_closed_result": "STOP", + "reviewer_output": [ + "complete raw snapshot", + "executable mappings", + "unknown/unmapped entries" + ] + }, + { + "id": "listeners", + "required_fact": "Ports 8084, 8085, and 9021 have unambiguous owners, addresses, and binding types with no unexpected exposure.", + "possible_evidence_source": "A separately reviewed listener snapshot mechanism.", + "confidence": "UNPROVEN", + "known_mutations": [], + "unknown_effects": [ + "network-stack counters, audit, cache, locking, and race effects" + ], + "required_collector_identity": [ + "tool and API identity", + "source commit", + "snapshot and ownership-mapping semantics" + ], + "timeout_ms": null, + "fail_closed_result": "STOP", + "reviewer_output": [ + "protocol/address/port rows", + "binding types", + "process and executable owners", + "exposure decision" + ] + }, + { + "id": "autoload", + "required_fact": "All authoritative autoload, startup, and retry sources prove that lifecycle and hardened artifacts are absent and automatic retry is disabled.", + "possible_evidence_source": "A collector distinct from the mutating stock /autoload_status route.", + "confidence": "UNPROVEN", + "known_mutations": [ + "stock /autoload_status writes autoload_triggered and is inadmissible" + ], + "unknown_effects": [ + "atime, audit, cache, metadata, service-state, and race effects" + ], + "required_collector_identity": [ + "tool and source identity", + "complete authoritative source list", + "no functional-state mutation proof" + ], + "timeout_ms": null, + "fail_closed_result": "STOP", + "reviewer_output": [ + "raw safe representations", + "source object identities and hashes", + "absence decisions", + "retry decision" + ] + }, + { + "id": "rollback_files", + "required_fact": "Separate already-present byte-exact stock backups exist with unambiguous restore mappings.", + "possible_evidence_source": "The same admissible object/hash collector used for live files, applied to separate backup objects.", + "confidence": "UNPROVEN", + "known_mutations": [], + "unknown_effects": [ + "atime, audit, page cache, metadata, mount, and race effects" + ], + "required_collector_identity": [ + "tool and source identity", + "same-object hash binding", + "separate-device/object proof" + ], + "timeout_ms": null, + "fail_closed_result": "STOP", + "reviewer_output": [ + "paths", + "object and mount IDs", + "sizes and digests", + "separation proof", + "restore mappings" + ] + }, + { + "id": "storage_precondition", + "required_fact": "A separate backup destination has at least 2447320 content bytes plus a separately assessed allocation reserve.", + "possible_evidence_source": "A separately reviewed filesystem capacity and mount identity operation.", + "confidence": "UNPROVEN", + "known_mutations": [], + "unknown_effects": [ + "mount, audit, cache, allocation accounting, and concurrent-use effects" + ], + "required_collector_identity": [ + "tool and API identity", + "mount/device identity semantics", + "capacity accounting semantics" + ], + "timeout_ms": null, + "fail_closed_result": "STOP", + "reviewer_output": [ + "mount and device IDs", + "available bytes", + "reserve calculation", + "separation decision" + ] + } + ], + "blockers": [ + { + "id": "exact_permission_quote_absent", + "severity": "STOP-RO" + }, + { + "id": "collector_identity_absent", + "severity": "STOP-RO" + }, + { + "id": "collector_side_effect_contract_absent", + "severity": "STOP-RO" + }, + { + "id": "two_current_firmware_sources_absent", + "severity": "STOP-GATE" + }, + { + "id": "live_object_identities_absent", + "severity": "STOP-GATE" + }, + { + "id": "listeners_absent", + "severity": "STOP-GATE" + }, + { + "id": "autoload_status_absent", + "severity": "STOP-GATE" + }, + { + "id": "rollback_backups_absent", + "severity": "STOP-GATE" + }, + { + "id": "payload_manager_backup_not_byte_exact_on_device", + "severity": "HARD_STOP-GATE" + }, + { + "id": "unknown_result_is_stop", + "severity": "STOP" + }, + { + "id": "timeout_is_stop", + "severity": "STOP" + }, + { + "id": "deviation_is_stop", + "severity": "STOP" + }, + { + "id": "automatic_retry_forbidden", + "severity": "STOP" + } + ], + "prohibited_actions": [ + "connect_to_ps5", + "probe_ip_port_or_device_interface", + "use_usb_or_removable_media", + "transfer_ps5_file", + "package_for_ps5_deployment", + "install_or_replace_target_component", + "execute_elf_or_payload", + "build_target_elf", + "start_cross_compiler", + "implement_or_build_collector", + "modify_payload_manager_production_code", + "modify_elfldr_production_code", + "modify_lifecycle_code", + "activate_or_modify_autoload", + "activate_retry", + "change_target_configuration", + "start_stop_or_signal_target_service_or_process", + "implement_gnm_videoout_sdl_audio_input_shaders_cores_or_retroarch", + "download_or_install_packages", + "contact_internet_gitea_or_other_remote", + "commit_or_push" + ], + "future_activity": { + "mode": "design_only", + "bounded_observation_implemented": false, + "collector_selected": false, + "transfer_method_selected": false, + "execution_method_selected": false, + "new_explicit_authorization_required": true + }, + "phase_separation": [ + "offline_remediation", + "possible_future_bounded_observation_design", + "separate_observation_transfer_execution_authorization", + "observation_result_review", + "separate_mutating_rollback_preparation", + "separate_hardened_runtime_installation_authorization", + "installation_verification_and_rollback_evidence", + "separate_lifecycle_authorization", + "non_graphical_capability_probes", + "much_later_graphics_backend_and_retroarch" + ], + "retroarch": { + "goal": "long_term_goal", + "active_phase": false, + "work_started": false, + "dependency_chain_only": true + } +} diff --git a/manifests/runtime/phase-0.9-anti-brick-design.json b/manifests/runtime/phase-0.9-anti-brick-design.json new file mode 100644 index 0000000..59b65af --- /dev/null +++ b/manifests/runtime/phase-0.9-anti-brick-design.json @@ -0,0 +1,491 @@ +{ + "schema_version": 1, + "schema_contract": { + "id": "chimera-gfx-phase-0.9a-anti-brick-design-v1", + "validator": "tests/test_phase09_transaction.py", + "host_simulator": "tools/simulate_phase09_transaction.py" + }, + "status": "DESIGN_ONLY", + "phase": "PHASE_0_9A_OFFLINE_ANTI_BRICK", + "historical_status": "READ_ONLY_PREFLIGHT_BLOCKED", + "hardware_observed": false, + "ps5_connected": false, + "device_write_performed": false, + "files_transferred": false, + "target_execution_performed": false, + "target_artifact_created": false, + "installation_authorized": false, + "lifecycle_authorized": false, + "execution_authorized": false, + "transfer_authorized": false, + "automatic_retry": false, + "firmware_runtime_behavior": "UNPROVEN", + "stock_identification": "reference_only", + "payload_manager_backup": "HARD_BLOCKER", + "lifecycle_probe_in_scope": false, + "graphics_in_scope": false, + "retroarch_in_scope": false, + "host_simulation_is_hardware_evidence": false, + "immutable_phase08_evidence": [ + { + "path": "docs/runtime/phase-0.8-read-only-preflight.md", + "sha256": "3fbe086175a6048176075f447ec1482074928e3b5282db97ea2169395fe1d508" + }, + { + "path": "manifests/runtime/phase-0.8-read-only-preflight.json", + "sha256": "47d7f452f8799979fe99b3e6d56859f03544112725bf7e5b349eba5ed81b3322" + }, + { + "path": "tests/test_phase08_preflight.py", + "sha256": "8a4ad7c70de28ffe3148fd3fd1f68c36a872c53c691c9068e1ff163970863c48" + } + ], + "permanent_denylist_binding": { + "sha256": "4be1c17b4964f2b68c39b5145bc4af4619c32512d60269ecf5c39728b390fa63", + "status": "BLOCKED", + "permanent": true, + "execution_eligible": false + }, + "source_evidence": { + "repository_head": "2c944d6d65a08f7e1c02f518721cde061b999329", + "hardened_elfldr_commit": "197623058f509eddde18868dafcb92fdcac66464", + "controlled_payload_manager_commit": "e23d94ff91233aa770e2342800c1467875bdef44", + "lifecycle_probe_source_commit": "fe08300339a13f899fb78ea404ada381a5cba87c", + "payload_manager_public_base_commit": "cfbc70f30f419b09bf2b52283f7409e2d3117ee1", + "elfldr_public_base_commit": "699e8bcff03e91e8d6ca6eba281af25c5a58d8c2" + }, + "threat_control_profiles": [ + { + "id": "A_WRONG_TARGET", + "possible_damage": "The wrong object is replaced, potentially removing the only working launcher or manager.", + "detection_before_mutation": "Require one stable no-follow object handle plus literal path token, mount ID, object ID, type, size and SHA-256; repeat identity immediately before mutation.", + "preventive_invariants": ["AB-001", "AB-003", "AB-004", "AB-007"], + "recovery": "Only a separately verified backup and an independent recovery executor could recover; neither is proven on PS5.", + "residual_risk": "Namespace races and platform object-identity semantics remain UNPROVEN.", + "phase_decision": "BLOCKED" + }, + { + "id": "B_WRONG_PREIMAGE", + "possible_damage": "A modified or firmware-incompatible live object is treated as stock and overwritten.", + "detection_before_mutation": "Bind firmware, build manifest, path, mount, object ID, size and digest to one current stable observation.", + "preventive_invariants": ["AB-001", "AB-002", "AB-003", "AB-004"], + "recovery": "Do not mutate on any mismatch; post-mutation recovery is not an acceptable substitute.", + "residual_risk": "Current on-device identities and firmware behavior remain UNPROVEN.", + "phase_decision": "BLOCKED" + }, + { + "id": "C_BACKUP_FAILURE", + "possible_damage": "Rollback bytes are absent, ambiguous, partial, corrupt or dependent on the failed component.", + "detection_before_mutation": "Create before staging, close, reopen, re-identify and rehash a separate object; verify capacity, mapping and an independent recovery executor.", + "preventive_invariants": ["AB-005", "AB-006", "AB-016", "AB-017"], + "recovery": "No live mutation is permitted until the backup and recovery path are proven.", + "residual_risk": "The byte-exact on-device Payload Manager backup is a HARD_BLOCKER.", + "phase_decision": "BLOCKED" + }, + { + "id": "D_WRITE_POWER_LOSS", + "possible_damage": "Power loss can expose a missing or partial live path, lose directory metadata or leave an ambiguous active identity.", + "detection_before_mutation": "Prove staging isolation, file and directory durability, and a single atomic switch primitive under interruption.", + "preventive_invariants": ["AB-007", "AB-008", "AB-009", "AB-011", "AB-012", "AB-013"], + "recovery": "Only a proven atomic old-or-new result plus an intact independent backup is acceptable.", + "residual_risk": "PS5 filesystem atomicity, exchange and durability are UNPROVEN.", + "phase_decision": "BLOCKED" + }, + { + "id": "E_PROCESS_LIFECYCLE", + "possible_damage": "An active mapping, supervisor, autoload or retry starts unverified bytes or creates a crash loop.", + "detection_before_mutation": "Prove process/service/listener ownership, quiescence, supervisor behavior and authoritative autoload/retry state.", + "preventive_invariants": ["AB-010", "AB-013", "AB-014", "AB-020"], + "recovery": "Stop before switch on any active or unknown state; never rely on automatic restart.", + "residual_risk": "Current process, listener and service topology is unobserved.", + "phase_decision": "BLOCKED" + }, + { + "id": "F_ROLLBACK_FAILURE", + "possible_damage": "Rollback restores wrong bytes, targets the wrong object, starts a service, or cannot run after the replaced component fails.", + "detection_before_mutation": "Bind rollback to the verified backup, target mapping, independent executor and post-rollback identity checks.", + "preventive_invariants": ["AB-006", "AB-015", "AB-016", "AB-017"], + "recovery": "A failed rollback verification is CATASTROPHIC/BLOCKED and never retried automatically.", + "residual_risk": "No independent on-device recovery path is proven.", + "phase_decision": "BLOCKED" + }, + { + "id": "G_OPERATOR_ERROR", + "possible_damage": "Artifacts, firmware, approvals or phases are confused and a general status bypasses a specific hard gate.", + "detection_before_mutation": "Use component-specific manifests and approvals bound to one device, firmware, hash, size, action and expiry.", + "preventive_invariants": ["AB-018", "AB-019", "AB-020"], + "recovery": "Fail closed before mutation; one component window must be accepted or rolled back before another begins.", + "residual_risk": "Component order is not inferred without proven dependency evidence.", + "phase_decision": "BLOCKED" + } + ], + "threat_model_items": [ + {"id": "TM-A-01", "profile": "A_WRONG_TARGET", "scenario": "wrong_path", "severity": "CATASTROPHIC", "reason": "May replace an unrelated critical object."}, + {"id": "TM-A-02", "profile": "A_WRONG_TARGET", "scenario": "wrong_mountpoint", "severity": "CATASTROPHIC", "reason": "Path text can name a different storage object."}, + {"id": "TM-A-03", "profile": "A_WRONG_TARGET", "scenario": "symlink_junction_or_redirect", "severity": "CATASTROPHIC", "reason": "A redirect defeats the reviewed target mapping."}, + {"id": "TM-A-04", "profile": "A_WRONG_TARGET", "scenario": "path_resolves_to_different_object_after_preflight", "severity": "CATASTROPHIC", "reason": "TOCTOU can switch the write target."}, + {"id": "TM-A-05", "profile": "A_WRONG_TARGET", "scenario": "case_or_path_normalization_changes_identity", "severity": "HIGH", "reason": "Canonicalization may collapse distinct names."}, + {"id": "TM-A-06", "profile": "A_WRONG_TARGET", "scenario": "target_replaced_between_check_and_write", "severity": "CATASTROPHIC", "reason": "The stable pre-image binding is lost."}, + {"id": "TM-B-01", "profile": "B_WRONG_PREIMAGE", "scenario": "live_hash_differs_from_stock_reference", "severity": "HIGH", "reason": "Existing modifications could be destroyed."}, + {"id": "TM-B-02", "profile": "B_WRONG_PREIMAGE", "scenario": "live_size_differs", "severity": "HIGH", "reason": "The observed object is not the approved pre-image."}, + {"id": "TM-B-03", "profile": "B_WRONG_PREIMAGE", "scenario": "object_id_changes", "severity": "CATASTROPHIC", "reason": "The write would target an unverified object."}, + {"id": "TM-B-04", "profile": "B_WRONG_PREIMAGE", "scenario": "firmware_differs", "severity": "HIGH", "reason": "Compatibility and recovery assumptions do not transfer."}, + {"id": "TM-B-05", "profile": "B_WRONG_PREIMAGE", "scenario": "manifest_belongs_to_another_build", "severity": "HIGH", "reason": "Candidate and recovery evidence become invalid."}, + {"id": "TM-B-06", "profile": "B_WRONG_PREIMAGE", "scenario": "modified_installation_treated_as_stock", "severity": "CATASTROPHIC", "reason": "Unknown required bytes could be irreversibly lost."}, + {"id": "TM-C-01", "profile": "C_BACKUP_FAILURE", "scenario": "backup_missing", "severity": "CATASTROPHIC", "reason": "No rollback bytes exist."}, + {"id": "TM-C-02", "profile": "C_BACKUP_FAILURE", "scenario": "backup_is_same_object_as_live", "severity": "CATASTROPHIC", "reason": "A switch or failure can destroy both identities."}, + {"id": "TM-C-03", "profile": "C_BACKUP_FAILURE", "scenario": "backup_on_same_vulnerable_path", "severity": "CATASTROPHIC", "reason": "One namespace failure can remove both copies."}, + {"id": "TM-C-04", "profile": "C_BACKUP_FAILURE", "scenario": "backup_partial", "severity": "CATASTROPHIC", "reason": "Partial bytes cannot restore the pre-image."}, + {"id": "TM-C-05", "profile": "C_BACKUP_FAILURE", "scenario": "backup_hash_mismatch", "severity": "CATASTROPHIC", "reason": "Rollback bytes are not the verified pre-image."}, + {"id": "TM-C-06", "profile": "C_BACKUP_FAILURE", "scenario": "backup_cannot_be_reopened", "severity": "HIGH", "reason": "Durability and later readability are unproven."}, + {"id": "TM-C-07", "profile": "C_BACKUP_FAILURE", "scenario": "backup_mapping_ambiguous", "severity": "CATASTROPHIC", "reason": "Rollback may restore the wrong target."}, + {"id": "TM-C-08", "profile": "C_BACKUP_FAILURE", "scenario": "recovery_tool_depends_on_replaced_component", "severity": "CATASTROPHIC", "reason": "The recovery executor may disappear with the failure."}, + {"id": "TM-C-09", "profile": "C_BACKUP_FAILURE", "scenario": "insufficient_storage", "severity": "HIGH", "reason": "Writes or metadata allocation can fail partway."}, + {"id": "TM-C-10", "profile": "C_BACKUP_FAILURE", "scenario": "backup_created_after_live_mutation", "severity": "CATASTROPHIC", "reason": "The only known-good pre-image may already be lost."}, + {"id": "TM-D-01", "profile": "D_WRITE_POWER_LOSS", "scenario": "power_loss_before_staging", "severity": "LOW", "reason": "No live mutation should have occurred, but platform evidence is still required."}, + {"id": "TM-D-02", "profile": "D_WRITE_POWER_LOSS", "scenario": "power_loss_during_staging", "severity": "HIGH", "reason": "A partial candidate must never become live."}, + {"id": "TM-D-03", "profile": "D_WRITE_POWER_LOSS", "scenario": "power_loss_after_staging_before_verification", "severity": "HIGH", "reason": "Unverified staged bytes may persist."}, + {"id": "TM-D-04", "profile": "D_WRITE_POWER_LOSS", "scenario": "power_loss_during_live_switch", "severity": "CATASTROPHIC", "reason": "The live identity can become missing or ambiguous."}, + {"id": "TM-D-05", "profile": "D_WRITE_POWER_LOSS", "scenario": "power_loss_immediately_after_live_switch", "severity": "CATASTROPHIC", "reason": "Directory metadata may not be durable."}, + {"id": "TM-D-06", "profile": "D_WRITE_POWER_LOSS", "scenario": "power_loss_before_directory_metadata_durable", "severity": "CATASTROPHIC", "reason": "The namespace may revert or lose the live entry."}, + {"id": "TM-D-07", "profile": "D_WRITE_POWER_LOSS", "scenario": "partial_write", "severity": "CATASTROPHIC", "reason": "A partial live executable is unusable."}, + {"id": "TM-D-08", "profile": "D_WRITE_POWER_LOSS", "scenario": "short_write", "severity": "HIGH", "reason": "Completion cannot be assumed from one write."}, + {"id": "TM-D-09", "profile": "D_WRITE_POWER_LOSS", "scenario": "write_error", "severity": "HIGH", "reason": "The candidate or backup may be incomplete."}, + {"id": "TM-D-10", "profile": "D_WRITE_POWER_LOSS", "scenario": "flush_or_fsync_error", "severity": "CATASTROPHIC", "reason": "Bytes may not survive interruption."}, + {"id": "TM-D-11", "profile": "D_WRITE_POWER_LOSS", "scenario": "file_correct_directory_not_durable", "severity": "CATASTROPHIC", "reason": "The live path may still disappear."}, + {"id": "TM-D-12", "profile": "D_WRITE_POWER_LOSS", "scenario": "new_file_wrong_metadata", "severity": "HIGH", "reason": "Ownership or mode may prevent startup or recovery."}, + {"id": "TM-E-01", "profile": "E_PROCESS_LIFECYCLE", "scenario": "target_file_active", "severity": "HIGH", "reason": "Old mappings and replacement behavior are unproven."}, + {"id": "TM-E-02", "profile": "E_PROCESS_LIFECYCLE", "scenario": "multiple_instances", "severity": "HIGH", "reason": "Quiescence and ownership become ambiguous."}, + {"id": "TM-E-03", "profile": "E_PROCESS_LIFECYCLE", "scenario": "service_self_restarts", "severity": "CATASTROPHIC", "reason": "Unverified bytes could start automatically."}, + {"id": "TM-E-04", "profile": "E_PROCESS_LIFECYCLE", "scenario": "listener_conflict", "severity": "MEDIUM", "reason": "Control and recovery channels may be unavailable."}, + {"id": "TM-E-05", "profile": "E_PROCESS_LIFECYCLE", "scenario": "process_holds_old_file_or_mapping", "severity": "HIGH", "reason": "Observed path identity may differ from running code."}, + {"id": "TM-E-06", "profile": "E_PROCESS_LIFECYCLE", "scenario": "watchdog_or_supervisor_auto_restart", "severity": "CATASTROPHIC", "reason": "It bypasses the execution approval gate."}, + {"id": "TM-E-07", "profile": "E_PROCESS_LIFECYCLE", "scenario": "autoload_starts_candidate_before_verification", "severity": "CATASTROPHIC", "reason": "Unverified code can run automatically."}, + {"id": "TM-E-08", "profile": "E_PROCESS_LIFECYCLE", "scenario": "automatic_retry_crash_loop", "severity": "CATASTROPHIC", "reason": "Repeated failure may prevent stable recovery."}, + {"id": "TM-F-01", "profile": "F_ROLLBACK_FAILURE", "scenario": "rollback_uses_wrong_backup", "severity": "CATASTROPHIC", "reason": "The restored object is not the pre-image."}, + {"id": "TM-F-02", "profile": "F_ROLLBACK_FAILURE", "scenario": "rollback_uses_wrong_target_path", "severity": "CATASTROPHIC", "reason": "Recovery can corrupt another object."}, + {"id": "TM-F-03", "profile": "F_ROLLBACK_FAILURE", "scenario": "rollback_interrupted", "severity": "CATASTROPHIC", "reason": "Neither old nor new live identity may remain usable."}, + {"id": "TM-F-04", "profile": "F_ROLLBACK_FAILURE", "scenario": "rollback_hash_wrong_after_restore", "severity": "CATASTROPHIC", "reason": "Recovery did not restore verified bytes."}, + {"id": "TM-F-05", "profile": "F_ROLLBACK_FAILURE", "scenario": "rollback_auto_starts_service", "severity": "CATASTROPHIC", "reason": "Execution occurs before restored identity review."}, + {"id": "TM-F-06", "profile": "F_ROLLBACK_FAILURE", "scenario": "rollback_requires_payload_manager", "severity": "CATASTROPHIC", "reason": "Payload Manager replacement can remove its own recovery path."}, + {"id": "TM-F-07", "profile": "F_ROLLBACK_FAILURE", "scenario": "rollback_requires_elfldr", "severity": "CATASTROPHIC", "reason": "elfldr replacement can remove its own recovery path."}, + {"id": "TM-G-01", "profile": "G_OPERATOR_ERROR", "scenario": "elfldr_and_manager_swapped", "severity": "CATASTROPHIC", "reason": "The wrong executable is installed into a critical role."}, + {"id": "TM-G-02", "profile": "G_OPERATOR_ERROR", "scenario": "lifecycle_probe_treated_as_install_candidate", "severity": "CATASTROPHIC", "reason": "The probe is not a runtime replacement component."}, + {"id": "TM-G-03", "profile": "G_OPERATOR_ERROR", "scenario": "wrong_firmware_or_build_selected", "severity": "HIGH", "reason": "Evidence and compatibility binding are invalid."}, + {"id": "TM-G-04", "profile": "G_OPERATOR_ERROR", "scenario": "two_installation_phases_combined", "severity": "CATASTROPHIC", "reason": "A single failure can remove both recovery dependencies."}, + {"id": "TM-G-05", "profile": "G_OPERATOR_ERROR", "scenario": "approval_reused_for_other_component", "severity": "HIGH", "reason": "Authority is not component or hash bound."}, + {"id": "TM-G-06", "profile": "G_OPERATOR_ERROR", "scenario": "stale_manifest_used", "severity": "HIGH", "reason": "The approved identities may no longer match."}, + {"id": "TM-G-07", "profile": "G_OPERATOR_ERROR", "scenario": "general_ready_overrides_specific_blocker", "severity": "CATASTROPHIC", "reason": "A hard stop can be silently bypassed."} + ], + "anti_brick_invariants": [ + {"id": "AB-001", "text": "No write without exact stable path, mount ID, object ID, type, size and pre-image SHA-256."}, + {"id": "AB-002", "text": "A stock digest remains reference_only until observed on one stable on-device object."}, + {"id": "AB-003", "text": "A digest match without object identity is insufficient."}, + {"id": "AB-004", "text": "Object identity without a digest match is insufficient."}, + {"id": "AB-005", "text": "The separate backup exists before staging and passes close, reopen, identity, size and digest verification."}, + {"id": "AB-006", "text": "Backup and recovery do not depend on the candidate or replaced component."}, + {"id": "AB-007", "text": "Never overwrite a live executable in place."}, + {"id": "AB-008", "text": "Stage under a separate inactive identity."}, + {"id": "AB-009", "text": "Reopen and hash the staged candidate before activation; bytes equal the offline-approved artifact."}, + {"id": "AB-010", "text": "No autoload, retry, service restart or boot starts the candidate."}, + {"id": "AB-011", "text": "Switch only with a separately proven transactional or atomic primitive."}, + {"id": "AB-012", "text": "Unproven atomicity or durability blocks installation."}, + {"id": "AB-013", "text": "Verify live identity after switch; execution is a later separate approval."}, + {"id": "AB-014", "text": "At most one manual execution and zero automatic retries."}, + {"id": "AB-015", "text": "Failed post-switch verification enters ROLLBACK_REQUIRED, never execution."}, + {"id": "AB-016", "text": "Rollback uses only the preverified backup and exact target mapping."}, + {"id": "AB-017", "text": "After rollback, re-establish size, SHA-256, object identity and relevant metadata."}, + {"id": "AB-018", "text": "Hardened elfldr and controlled Payload Manager are never replaced in one uninterrupted transaction."}, + {"id": "AB-019", "text": "The lifecycle probe is never an installation candidate."}, + {"id": "AB-020", "text": "No later phase begins without separate human permission."} + ], + "transaction_states": [ + "OFFLINE_ONLY", + "OBSERVATION_NOT_AUTHORIZED", + "OBSERVATION_AUTHORIZED", + "DEVICE_IDENTITY_OBSERVED", + "LIVE_OBJECTS_VERIFIED", + "BACKUP_NOT_PRESENT", + "BACKUP_CREATION_NOT_AUTHORIZED", + "BACKUP_CREATION_AUTHORIZED", + "BACKUP_CREATED", + "BACKUP_REOPENED_AND_VERIFIED", + "RECOVERY_PATH_VERIFIED", + "CANDIDATE_NOT_AUTHORIZED", + "CANDIDATE_APPROVED_OFFLINE", + "STAGING_NOT_AUTHORIZED", + "STAGING_AUTHORIZED", + "CANDIDATE_STAGED", + "CANDIDATE_REOPENED_AND_VERIFIED", + "TARGET_NOT_QUIESCENT", + "TARGET_QUIESCENT", + "SWITCH_NOT_AUTHORIZED", + "SWITCH_AUTHORIZED", + "SWITCH_IN_PROGRESS", + "POST_SWITCH_VERIFY", + "MANUAL_EXECUTION_NOT_AUTHORIZED", + "MANUAL_EXECUTION_AUTHORIZED", + "ONE_SHOT_EXECUTION", + "ACCEPTED", + "ROLLBACK_REQUIRED", + "ROLLBACK_AUTHORIZED", + "ROLLBACK_IN_PROGRESS", + "ROLLBACK_VERIFIED", + "BLOCKED" + ], + "allowed_transitions": [ + "OFFLINE_ONLY->OBSERVATION_NOT_AUTHORIZED", + "OBSERVATION_NOT_AUTHORIZED->OBSERVATION_AUTHORIZED", + "OBSERVATION_AUTHORIZED->DEVICE_IDENTITY_OBSERVED", + "DEVICE_IDENTITY_OBSERVED->LIVE_OBJECTS_VERIFIED", + "LIVE_OBJECTS_VERIFIED->BACKUP_NOT_PRESENT", + "BACKUP_NOT_PRESENT->BACKUP_CREATION_NOT_AUTHORIZED", + "BACKUP_CREATION_NOT_AUTHORIZED->BACKUP_CREATION_AUTHORIZED", + "BACKUP_CREATION_AUTHORIZED->BACKUP_CREATED", + "BACKUP_CREATED->BACKUP_REOPENED_AND_VERIFIED", + "BACKUP_REOPENED_AND_VERIFIED->RECOVERY_PATH_VERIFIED", + "RECOVERY_PATH_VERIFIED->CANDIDATE_NOT_AUTHORIZED", + "CANDIDATE_NOT_AUTHORIZED->CANDIDATE_APPROVED_OFFLINE", + "CANDIDATE_APPROVED_OFFLINE->STAGING_NOT_AUTHORIZED", + "STAGING_NOT_AUTHORIZED->STAGING_AUTHORIZED", + "STAGING_AUTHORIZED->CANDIDATE_STAGED", + "CANDIDATE_STAGED->CANDIDATE_REOPENED_AND_VERIFIED", + "CANDIDATE_REOPENED_AND_VERIFIED->TARGET_NOT_QUIESCENT", + "TARGET_NOT_QUIESCENT->TARGET_QUIESCENT", + "TARGET_QUIESCENT->SWITCH_NOT_AUTHORIZED", + "SWITCH_NOT_AUTHORIZED->SWITCH_AUTHORIZED", + "SWITCH_AUTHORIZED->SWITCH_IN_PROGRESS", + "SWITCH_IN_PROGRESS->POST_SWITCH_VERIFY", + "POST_SWITCH_VERIFY->MANUAL_EXECUTION_NOT_AUTHORIZED", + "MANUAL_EXECUTION_NOT_AUTHORIZED->MANUAL_EXECUTION_AUTHORIZED", + "MANUAL_EXECUTION_AUTHORIZED->ONE_SHOT_EXECUTION", + "ONE_SHOT_EXECUTION->ACCEPTED", + "POST_SWITCH_VERIFY->ROLLBACK_REQUIRED", + "ROLLBACK_REQUIRED->ROLLBACK_AUTHORIZED", + "ROLLBACK_AUTHORIZED->ROLLBACK_IN_PROGRESS", + "ROLLBACK_IN_PROGRESS->ROLLBACK_VERIFIED", + "ANY_ERROR_MISMATCH_TIMEOUT_OR_UNKNOWN->BLOCKED" + ], + "forbidden_transitions": [ + "automatic_transition_across_any_authorization_state", + "automatic_retry", + "autoload", + "in_place_overwrite", + "two_step_rename_with_missing_live_gap", + "post_switch_direct_to_execution", + "combined_component_installation", + "second_component_before_first_accepted_or_rolled_back", + "lifecycle_probe_to_installation_candidate", + "lifecycle_transition", + "graphics_transition", + "retroarch_transition", + "general_ready_overrides_specific_blocker" + ], + "approval_gates": [ + {"id": "observation", "authorized": false, "separate_human_approval_required": true}, + {"id": "backup_creation", "authorized": false, "separate_human_approval_required": true}, + {"id": "staging", "authorized": false, "separate_human_approval_required": true}, + {"id": "switch", "authorized": false, "separate_human_approval_required": true}, + {"id": "one_shot_execution", "authorized": false, "separate_human_approval_required": true}, + {"id": "rollback", "authorized": false, "separate_human_approval_required": true} + ], + "power_loss_boundaries": [ + {"id": "before_backup_write", "before": "verified old live; no backup write", "possible_objects": "old live only", "visible_live": "old live", "backup": "absent", "automatic_start": false, "independent_recovery": "not needed before mutation", "recoverable": "UNPROVEN", "result": "UNPROVEN"}, + {"id": "during_backup_write", "before": "old live intact; backup creation authorized", "possible_objects": "old live plus partial backup", "visible_live": "old live", "backup": "partial and invalid", "automatic_start": false, "independent_recovery": "old live must remain independent", "recoverable": "UNPROVEN", "result": "UNPROVEN"}, + {"id": "after_backup_write_before_flush", "before": "backup bytes written but not durable", "possible_objects": "old live plus volatile backup", "visible_live": "old live", "backup": "not durable", "automatic_start": false, "independent_recovery": "unproven", "recoverable": "UNPROVEN", "result": "UNPROVEN"}, + {"id": "after_flush_before_reopen_hash", "before": "backup flush requested", "possible_objects": "old live plus unverified backup", "visible_live": "old live", "backup": "not reopened or rehashed", "automatic_start": false, "independent_recovery": "unproven", "recoverable": "UNPROVEN", "result": "UNPROVEN"}, + {"id": "during_candidate_staging", "before": "verified backup; separate candidate write", "possible_objects": "old live, verified backup, partial candidate", "visible_live": "old live", "backup": "must remain intact", "automatic_start": false, "independent_recovery": "must not use candidate", "recoverable": "UNPROVEN", "result": "UNPROVEN"}, + {"id": "after_candidate_staging_before_verification", "before": "candidate present but unverified", "possible_objects": "old live, verified backup, unverified candidate", "visible_live": "old live", "backup": "must remain intact", "automatic_start": false, "independent_recovery": "old live remains primary", "recoverable": "UNPROVEN", "result": "UNPROVEN"}, + {"id": "immediately_before_live_switch", "before": "candidate and backup verified; target quiescent", "possible_objects": "old live, verified backup, verified candidate", "visible_live": "old live", "backup": "verified", "automatic_start": false, "independent_recovery": "required", "recoverable": "UNPROVEN", "result": "UNPROVEN"}, + {"id": "during_live_switch", "before": "atomic switch requested", "possible_objects": "must be exactly complete old or complete new; platform behavior unknown", "visible_live": "UNPROVEN", "backup": "must remain verified", "automatic_start": false, "independent_recovery": "required", "recoverable": "UNPROVEN", "result": "UNPROVEN"}, + {"id": "immediately_after_live_switch", "before": "namespace switch returned", "possible_objects": "new live plus backup if atomic; directory durability unknown", "visible_live": "UNPROVEN", "backup": "must remain verified", "automatic_start": false, "independent_recovery": "required", "recoverable": "UNPROVEN", "result": "UNPROVEN"}, + {"id": "after_switch_before_live_hash", "before": "candidate nominally live but not verified", "possible_objects": "new live or ambiguous namespace plus backup", "visible_live": "unverified", "backup": "must remain verified", "automatic_start": false, "independent_recovery": "required", "recoverable": "UNPROVEN", "result": "UNPROVEN"}, + {"id": "after_live_hash_before_execution", "before": "post-switch hash recorded; execution still unauthorized", "possible_objects": "verified new live plus backup if platform durable", "visible_live": "new live", "backup": "must remain verified", "automatic_start": false, "independent_recovery": "required", "recoverable": "UNPROVEN", "result": "UNPROVEN"}, + {"id": "during_first_manual_execution", "before": "separate one-shot execution gate", "possible_objects": "new live plus backup", "visible_live": "new live", "backup": "must remain verified", "automatic_start": false, "independent_recovery": "must work without executed component", "recoverable": "UNPROVEN", "result": "UNPROVEN"}, + {"id": "during_rollback", "before": "rollback separately authorized", "possible_objects": "must be complete new or complete old plus intact backup", "visible_live": "UNPROVEN", "backup": "must remain verified", "automatic_start": false, "independent_recovery": "required", "recoverable": "UNPROVEN", "result": "UNPROVEN"}, + {"id": "after_rollback_before_recovery_verification", "before": "rollback switch returned", "possible_objects": "restored old live if atomic and durable", "visible_live": "unverified restored object", "backup": "must remain verified", "automatic_start": false, "independent_recovery": "required", "recoverable": "UNPROVEN", "result": "UNPROVEN"} + ], + "backup_conditions": [ + "created_before_any_live_mutation", + "exact_source_object_and_restore_target_recorded", + "source_stable_during_read", + "all_bytes_read", + "exact_size", + "exact_sha256", + "separate_object", + "closed_reopened_reidentified_and_rehashed", + "storage_medium_and_mount_recorded", + "capacity_and_metadata_reserve_proven", + "not_removed_by_autoload_cleanup_or_packaging", + "not_replaced_by_candidate", + "mapping_not_derived_from_glob_or_basename", + "rollback_requires_no_target_path_guess", + "strongly_recommended_second_off_device_copy_requires_separate_future_transfer_approval" + ], + "recovery_conditions": [ + "recovery_start_mechanism_exactly_identified", + "recovery_executor_exactly_identified", + "recovery_executor_is_not_the_replaced_component", + "works_if_elfldr_does_not_start", + "works_if_payload_manager_does_not_start", + "works_without_autoload", + "works_without_replaced_component_network_listener", + "second_independent_recovery_path_is_proven", + "post_rollback_size_hash_object_identity_and_metadata_verified" + ], + "filesystem_evidence_requirements": [ + "filesystem_type", + "mount_identity", + "read_write_status", + "atomic_rename_or_exchange_support", + "replace_existing_file_semantics", + "file_flush_durability", + "directory_flush_durability", + "power_loss_behavior", + "symlink_no_follow_semantics", + "stable_open_object_identity", + "ownership_and_mode_preservation", + "available_space_and_metadata_capacity", + "maximum_file_size", + "behavior_while_target_process_has_file_open" + ], + "unproven_platform_properties": [ + "exact_current_firmware_from_two_device_sources", + "current_live_paths_mounts_objects_sizes_and_hashes", + "filesystem_type_and_mount_semantics", + "atomic_exchange_or_transactional_switch", + "file_and_directory_flush_durability", + "power_loss_namespace_behavior", + "no_follow_and_stable_object_identity", + "ownership_mode_and_metadata_preservation", + "capacity_and_maximum_file_size", + "open_executable_replacement_behavior", + "process_service_listener_and_supervisor_topology", + "autoload_and_retry_state", + "independent_recovery_executor", + "second_independent_recovery_path", + "byte_exact_reopenable_on_device_payload_manager_backup" + ], + "component_models": [ + { + "component": "hardened_elfldr", + "live_preimage": { + "classification": "reference_only", + "path": null, + "mount_id": null, + "object_id": null, + "size": 397000, + "sha256": "092d16ee0ede0c494947efd38d1a17bbd7cc4b022d3858ea898833c188c703e8", + "current_device_match": "UNPROVEN" + }, + "backup": {"status": "STOP_GATE", "on_device_present": "UNPROVEN", "reopened_and_verified": false}, + "candidate": { + "classification": "exact_offline_reviewed_reference", + "size": 397000, + "sha256": "63e810982471eb40cae3a20aa9df9a0a02892f420e429874fae4e99aa400b561", + "source_commit": "197623058f509eddde18868dafcb92fdcac66464" + }, + "target_mapping": {"live_path": null, "backup_path": null, "staging_path": null, "status": "UNPROVEN"}, + "recovery_dependency": "UNPROVEN", + "process_service_status": "UNPROVEN", + "listener_status": "UNPROVEN", + "autoload_status": "UNPROVEN", + "approval": false, + "staging": "NOT_AUTHORIZED", + "switch": {"authorized": false, "primitive": "UNPROVEN", "atomicity": "UNPROVEN", "durability": "UNPROVEN"}, + "post_switch_verification": "NOT_PERFORMED", + "manual_execution": "NOT_AUTHORIZED", + "accepted": false, + "rollback": "NOT_AUTHORIZED" + }, + { + "component": "controlled_payload_manager", + "live_preimage": { + "classification": "reference_only", + "path": null, + "mount_id": null, + "object_id": null, + "size": 2050320, + "sha256": "518740adbacccb9094fadb07dd424c53ee290f38306449ccc9d6957fdf813c0b", + "current_device_match": "UNPROVEN" + }, + "backup": {"status": "HARD_BLOCKER", "on_device_present": "UNPROVEN", "reopened_and_verified": false}, + "candidate": { + "classification": "exact_offline_reviewed_reference", + "size": 99560, + "sha256": "8fecf8241a46246eddbd21e8bb4d875f5d76f1f4f4c6a11384df1f131aa5e5b1", + "source_commit": "e23d94ff91233aa770e2342800c1467875bdef44" + }, + "target_mapping": {"live_path": null, "backup_path": null, "staging_path": null, "status": "UNPROVEN"}, + "recovery_dependency": "UNPROVEN", + "process_service_status": "UNPROVEN", + "listener_status": "UNPROVEN", + "autoload_status": "UNPROVEN", + "approval": false, + "staging": "NOT_AUTHORIZED", + "switch": {"authorized": false, "primitive": "UNPROVEN", "atomicity": "UNPROVEN", "durability": "UNPROVEN"}, + "post_switch_verification": "NOT_PERFORMED", + "manual_execution": "NOT_AUTHORIZED", + "accepted": false, + "rollback": "NOT_AUTHORIZED" + } + ], + "component_order": { + "selected": null, + "reason": "No order is inferred without proven dependency and independent recovery evidence.", + "combined_install_all": false, + "second_component_requires_first_accepted_or_fully_rolled_back": true + }, + "switch_preference": [ + "side_by_side_or_dual_slot_without_removing_stock", + "proven_atomic_exchange", + "proven_transactional_snapshot", + "other_method_only_after_separate_proof" + ], + "blockers": [ + {"id": "READ_ONLY_PREFLIGHT_BLOCKED", "severity": "HARD_STOP"}, + {"id": "PAYLOAD_MANAGER_BACKUP_NOT_BYTE_EXACT_ON_DEVICE", "severity": "HARD_STOP"}, + {"id": "NO_PROVEN_POWER_LOSS_SAFE_SWITCH", "severity": "HARD_STOP"}, + {"id": "FILESYSTEM_ATOMICITY_UNPROVEN", "severity": "STOP"}, + {"id": "FILE_DURABILITY_UNPROVEN", "severity": "STOP"}, + {"id": "DIRECTORY_DURABILITY_UNPROVEN", "severity": "STOP"}, + {"id": "RECOVERY_INDEPENDENCE_UNPROVEN", "severity": "HARD_STOP"}, + {"id": "CURRENT_LIVE_IDENTITIES_UNPROVEN", "severity": "STOP"}, + {"id": "CURRENT_PROCESS_AND_SERVICE_TOPOLOGY_UNPROVEN", "severity": "STOP"}, + {"id": "FIRMWARE_RUNTIME_BEHAVIOR_UNPROVEN", "severity": "STOP"}, + {"id": "COMPONENT_ORDER_UNPROVEN", "severity": "STOP"}, + {"id": "ALL_DEVICE_ACTION_AUTHORIZATIONS_FALSE", "severity": "STOP"}, + {"id": "ANY_UNSAFE_OR_UNPROVEN_POWER_LOSS_BOUNDARY", "severity": "HARD_STOP"} + ], + "proof_requirements_before_future_bounded_observation": [ + "new exact observation-only human authorization", + "exact collector identity and complete side-effect contract", + "collector bounded runtime and fail-closed cleanup", + "two current authoritative firmware observations", + "no installation transfer execution lifecycle autoload retry graphics or RetroArch authority", + "exact raw output and reviewer evidence contract", + "immutable Phase-0.8 and Phase-0.8R blockers preserved" + ], + "prohibited_actions": [ + "connect_to_ps5", + "use_usb_or_device_transport", + "transfer_file", + "execute_target_code", + "install_or_replace_component", + "create_on_device_backup", + "change_configuration", + "activate_autoload_or_retry", + "start_stop_or_signal_process_or_service", + "build_target_elf_self_sprx_pkg_or_payload", + "build_or_run_lifecycle_probe", + "generate_device_install_commands", + "modify_hardened_elfldr_production_code", + "modify_payload_manager_production_code", + "modify_lifecycle_probe_code", + "modify_target_toolchain", + "modify_graphics_sdl_or_retroarch_code", + "contact_network_gitea_or_upstream", + "install_packages", + "commit_or_push" + ] +} diff --git a/manifests/runtime/phase-0.9b-observation-plan.schema.json b/manifests/runtime/phase-0.9b-observation-plan.schema.json new file mode 100644 index 0000000..dc077fa --- /dev/null +++ b/manifests/runtime/phase-0.9b-observation-plan.schema.json @@ -0,0 +1,267 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://chimera.invalid/schema/phase-0.9b-observation-plan-v1.json", + "title": "Chimera GFX Phase-0.9B non-authorizing observation plan", + "description": "Offline review schema only. A conforming plan does not authorize transfer, execution, installation, lifecycle, autoload, backup creation, or retry.", + "type": "object", + "additionalProperties": false, + "required": [ + "schema_version", + "plan_id", + "authorized", + "transfer_authorized", + "execution_authorized", + "installation_authorized", + "lifecycle_authorized", + "autoload_authorized", + "backup_creation_authorized", + "automatic_retry", + "device_address", + "device_identity", + "observer", + "firmware_gate", + "allowed_observations", + "read_paths", + "output_channel", + "maximum_runtime_ms", + "maximum_execution_count", + "expires_at", + "stop_criteria" + ], + "properties": { + "schema_version": { + "const": 1 + }, + "plan_id": { + "type": ["string", "null"], + "minLength": 1, + "maxLength": 128 + }, + "authorized": { + "const": false + }, + "transfer_authorized": { + "const": false + }, + "execution_authorized": { + "const": false + }, + "installation_authorized": { + "const": false + }, + "lifecycle_authorized": { + "const": false + }, + "autoload_authorized": { + "const": false + }, + "backup_creation_authorized": { + "const": false + }, + "automatic_retry": { + "const": false + }, + "device_address": { + "const": null + }, + "device_identity": { + "type": ["string", "null"], + "minLength": 1, + "maxLength": 256 + }, + "observer": { + "type": "object", + "additionalProperties": false, + "required": [ + "sha256", + "size", + "source_commit", + "toolchain_identity" + ], + "properties": { + "sha256": { + "type": ["string", "null"], + "pattern": "^[0-9a-f]{64}$" + }, + "size": { + "type": ["integer", "null"], + "minimum": 1 + }, + "source_commit": { + "type": ["string", "null"], + "pattern": "^[0-9a-f]{40}$" + }, + "toolchain_identity": { + "type": ["string", "null"], + "minLength": 1, + "maxLength": 512 + } + } + }, + "firmware_gate": { + "type": ["string", "null"], + "pattern": "^9\\.60$" + }, + "allowed_observations": { + "type": "array", + "maxItems": 32, + "uniqueItems": true, + "items": { + "enum": [ + "observer_self_identity", + "firmware_source_1", + "firmware_source_2", + "mount_information", + "live_object_metadata", + "live_object_sha256", + "rollback_object_metadata", + "rollback_object_sha256", + "process_snapshot", + "service_snapshot", + "listener_snapshot", + "autoload_snapshot" + ] + } + }, + "read_paths": { + "type": "array", + "maxItems": 16, + "uniqueItems": true, + "items": { + "type": "string", + "minLength": 1, + "maxLength": 512 + } + }, + "output_channel": { + "type": ["object", "null"], + "additionalProperties": false, + "required": [ + "id", + "source_commit", + "source_reference", + "non_persistent_proof" + ], + "properties": { + "id": { + "type": "string", + "minLength": 1, + "maxLength": 128 + }, + "source_commit": { + "type": "string", + "pattern": "^[0-9a-f]{40}$" + }, + "source_reference": { + "type": "string", + "minLength": 1, + "maxLength": 512 + }, + "non_persistent_proof": { + "type": "string", + "minLength": 1, + "maxLength": 2048 + } + } + }, + "maximum_runtime_ms": { + "type": ["integer", "null"], + "minimum": 1, + "maximum": 2000 + }, + "maximum_execution_count": { + "const": 0 + }, + "expires_at": { + "type": ["string", "null"], + "format": "date-time" + }, + "stop_criteria": { + "type": "array", + "maxItems": 64, + "uniqueItems": true, + "items": { + "type": "string", + "minLength": 1, + "maxLength": 512 + } + } + }, + "allOf": [ + { + "if": { + "properties": { + "allowed_observations": { + "minItems": 1 + } + } + }, + "then": { + "properties": { + "observer": { + "properties": { + "sha256": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "source_commit": { + "type": "string" + }, + "toolchain_identity": { + "type": "string" + } + } + }, + "firmware_gate": { + "const": "9.60" + }, + "device_identity": { + "type": "string" + }, + "output_channel": { + "type": "object" + }, + "maximum_runtime_ms": { + "type": "integer" + }, + "expires_at": { + "type": "string" + }, + "stop_criteria": { + "minItems": 1 + } + } + } + } + ], + "x-chimera-default-plan": { + "schema_version": 1, + "plan_id": null, + "authorized": false, + "transfer_authorized": false, + "execution_authorized": false, + "installation_authorized": false, + "lifecycle_authorized": false, + "autoload_authorized": false, + "backup_creation_authorized": false, + "automatic_retry": false, + "device_address": null, + "device_identity": null, + "observer": { + "sha256": null, + "size": null, + "source_commit": null, + "toolchain_identity": null + }, + "firmware_gate": null, + "allowed_observations": [], + "read_paths": [], + "output_channel": null, + "maximum_runtime_ms": null, + "maximum_execution_count": 0, + "expires_at": null, + "stop_criteria": [] + } +} diff --git a/manifests/runtime/phase-0.9b-observer.json b/manifests/runtime/phase-0.9b-observer.json new file mode 100644 index 0000000..dc0fbaf --- /dev/null +++ b/manifests/runtime/phase-0.9b-observer.json @@ -0,0 +1,372 @@ +{ + "schema_version": 1, + "phase": "0.9B", + "date": "2026-07-18", + "scope": "offline_bounded_observer_feasibility_audit_only", + "status": "BLOCKED", + "decision": [ + "OBSERVER_STARTUP_OR_EXIT_ABI_UNPROVEN", + "NO_PROVEN_NON_PERSISTENT_OUTPUT_CHANNEL" + ], + "baseline_commit": "833874f4196825765e2dfaa28327daea73919d01", + "branch": "codex/chimera-gfx-phase09b-observer", + "authorization": { + "authorized": false, + "transfer_authorized": false, + "execution_authorized": false, + "installation_authorized": false, + "lifecycle_authorized": false, + "autoload_authorized": false, + "backup_creation_authorized": false, + "automatic_retry": false + }, + "canonical_state_preserved": { + "historical_phase08_status": "READ_ONLY_PREFLIGHT_BLOCKED", + "phase09a_status": "DESIGN_ONLY", + "firmware_runtime_behavior": "UNPROVEN", + "stock_hashes": "reference_only", + "payload_manager_backup": "HARD_BLOCKER", + "device_contact_performed": false, + "device_transfer_performed": false, + "device_execution_performed": false + }, + "permanent_denylist_binding": { + "sha256": "4be1c17b4964f2b68c39b5145bc4af4619c32512d60269ecf5c39728b390fa63", + "status": "BLOCKED", + "permanent": true, + "execution_eligible": false + }, + "source_commits": { + "hardened_elfldr": "197623058f509eddde18868dafcb92fdcac66464", + "controlled_payload_manager": "e23d94ff91233aa770e2342800c1467875bdef44", + "elfldr_public_base": "699e8bcff03e91e8d6ca6eba281af25c5a58d8c2", + "payload_manager_public_base": "cfbc70f30f419b09bf2b52283f7409e2d3117ee1", + "ps5_payload_sdk_v0_41": "d2e2e585740362976a39fdd5ccf390f199a7bc37", + "lifecycle_probe_source": "fe08300339a13f899fb78ea404ada381a5cba87c" + }, + "source_tree_status": { + "hardened_elfldr": "clean", + "controlled_payload_manager": "clean", + "elfldr_public_base": "clean", + "payload_manager_public_base": "clean", + "ps5_payload_sdk_v0_41": "clean" + }, + "source_evidence": [ + { + "component": "hardened_elfldr_controlled_route", + "path": "../chimera-elfldr/socksrv.c", + "sha256": "d642ced3e9b4a296dd15e355050ebe956f53a6dfdaa6ac10109cd067a3bba3d7", + "lines": ["129-145", "289-365"], + "finding": "The controlled route passes stdio=-1 to elfldr_spawn and sends only a fixed loader-generated exit message." + }, + { + "component": "hardened_elfldr_entry_and_stdio", + "path": "../chimera-elfldr/elfldr.c", + "sha256": "9949f8e4037984d10f1f5aa498e4665593d1fac8a33614d7f2141349839bb803", + "lines": ["273-415", "450-545", "643-759"], + "finding": "The loader places payload_args_t in RDI and a saved RIP on the stack; stdio duplication occurs only when stdio>0. The loader also performs process and kernel mutations before entry." + }, + { + "component": "hardened_elfldr_log", + "path": "../chimera-elfldr/log.h", + "sha256": "cf1a85e7b047fbd4164b48241896fd955d384efc7e475a54fb24a62f53d2029e", + "lines": ["1-49"], + "finding": "Logging targets stdout and klog; neither is a proven controlled observer response channel." + }, + { + "component": "controlled_payload_manager_transport", + "path": "../chimera-ps5-payload-manager/src/verified_launcher.c", + "sha256": "066100ca4917c7acc560e2e85666ca136cd7ccfd9094417377048f41106dd56e", + "lines": ["31-116", "119-185"], + "finding": "The verified transport opens and hashes read-only, sends a header and bytes, then closes without receiving a payload result." + }, + { + "component": "controlled_payload_manager_loopback_adapter", + "path": "../chimera-ps5-payload-manager/src/ps5_launcher.c", + "sha256": "29c1a5fd01784a59e88b3698940f120cb03020071bc2b7d74a1da1a51524ef59", + "lines": ["18-70"], + "finding": "The adapter provides connect, send_all and close_socket only; no receive callback exists." + }, + { + "component": "controlled_payload_manager_service", + "path": "../chimera-ps5-payload-manager/src/controlled_manager.c", + "sha256": "042b55b2cece32effed636529249fd18061a2fe3c5e70c7f755f2817cfb84b99", + "lines": ["208-235", "243-365"], + "finding": "The manager is a persistent listener and its upload/delete paths mutate files; it is not an admissible observer output collector." + }, + { + "component": "sdk_crt_startup", + "path": "work/upstream/sdk/crt/crt.c", + "sha256": "3875f4739ec40b33f1f4967a1acbb585a527d2c281a360153e1f69c8b945932a", + "lines": ["48-80", "150-220"], + "finding": "Normal _start invokes runtime initialization and __patch_init before main; its terminate path depends on runtime symbol resolution or returning to a hijacked process." + }, + { + "component": "sdk_crt_patch", + "path": "work/upstream/sdk/crt/patch.c", + "sha256": "4f76a677bba54f4641e1cf2755768c29afb7464a35b2f21e85db2aa2785eceac", + "lines": ["25-50", "58-101"], + "finding": "__patch_init writes syscall boundaries, credential capabilities and attributes." + }, + { + "component": "sdk_crt_object_set", + "path": "work/upstream/sdk/crt/Makefile", + "sha256": "6a62f777f32ab05cbe7bff81c00cfd6639ba8bf440aba611a40ff0b62732568b", + "lines": ["19-26", "50-54"], + "finding": "crt1.o transitively contains crt, syscall, klog, kernel, rtld, mdbg and patch objects." + }, + { + "component": "sdk_compiler_wrapper", + "path": "work/upstream/sdk/host/bin/prospero-clang", + "sha256": "0cf49ae43d6110a7606c0ee4d702fc4b5d5e1c3ae9a722945a48b80294e295ba", + "lines": ["25-47", "69-84"], + "finding": "The wrapper adds crt1.o unless -nostartfiles is present and removes default libraries for -nodefaultlibs." + }, + { + "component": "sdk_linker_script", + "path": "work/upstream/sdk/host/elf_x86_64.x", + "sha256": "169b80d01da601ef96bbc584986608dec1d9c01397eae81eccd4e1a66b0a6c6a", + "finding": "The script defines image, init/fini and BSS boundaries but does not prove loader cleanup after a custom entry returns." + }, + { + "component": "installed_sdk_crt1", + "path": "work/toolchains/ps5-payload-sdk-v0.41/target/lib/crt1.o", + "size": 112256, + "sha256": "0e4c1e5e38f22026580e856238a53e75d9e112f99a34bfe5495a3a0ffac15238", + "finding": "Installed startup object identity recorded; it was not linked into a Phase-0.9B artifact." + }, + { + "component": "lifecycle_probe_source", + "path": "samples/lifecycle_probe/main.c", + "sha256": "1ae7df1fe921ccab2a252f77975d3d441ef7725e34535b024580c0d4a242d766", + "finding": "The prior lifecycle probe uses the normal SDK runtime, a notification API and _exit; it is unchanged and is not an observer ABI proof." + } + ], + "capability_matrix": [ + { + "needed_fact": "firmware_source_1", + "local_api": "kernel_get_fw_version reads sdk_ps5_ver from libSceLibcInternal process parameters", + "source": "work/upstream/sdk/crt/kernel.c:148-171", + "evidence": "SOURCE_PRESENT_RUNTIME_UNPROVEN", + "implement": false + }, + { + "needed_fact": "firmware_source_2", + "local_api": null, + "source": null, + "evidence": "UNPROVEN", + "implement": false + }, + { + "needed_fact": "file_metadata", + "local_api": "open(O_RDONLY|O_NOFOLLOW|O_CLOEXEC), fstat, lstat, close", + "source": "../chimera-ps5-payload-manager/src/verified_launcher.c:76-115,174-184", + "evidence": "SOURCE_AND_HOST_TEST_EVIDENCE_ONLY", + "implement": false + }, + { + "needed_fact": "object_identity", + "local_api": "st_dev, st_ino and st_size pre/post comparison", + "source": "../chimera-ps5-payload-manager/src/verified_launcher.c:26-29,87-101,174-176", + "evidence": "SOURCE_AND_HOST_TEST_EVIDENCE_ONLY", + "implement": false + }, + { + "needed_fact": "sha256", + "local_api": "bounded SHA-256 over an already-open descriptor", + "source": "../chimera-ps5-payload-manager/src/verified_launcher.c:31-55 and src/sha256.c", + "evidence": "SOURCE_AND_HOST_TEST_EVIDENCE_ONLY", + "implement": false + }, + { + "needed_fact": "mount_information", + "local_api": "getmntinfo/getfsstat sample", + "source": "work/upstream/sdk/samples/mntinfo/main.c and libc/getmntinfo.c", + "evidence": "SDK_SAMPLE_PRESENT_FIRMWARE_9_60_RUNTIME_UNPROVEN", + "implement": false + }, + { + "needed_fact": "processes_services", + "local_api": "sysctl KERN_PROC snapshot sample; no complete service mapping", + "source": "work/upstream/sdk/samples/ps/main.c", + "evidence": "PARTIAL_SOURCE_PRESENT_RUNTIME_AND_SERVICE_SEMANTICS_UNPROVEN", + "implement": false + }, + { + "needed_fact": "listeners", + "local_api": null, + "source": null, + "evidence": "UNPROVEN", + "implement": false + }, + { + "needed_fact": "autoload_configuration", + "local_api": "stock config readers exist but paths are not current-device evidence and HTTP status routes mutate global state", + "source": "../chimera-ps5-payload-manager/src/config.c, src/autoload.c, src/http_server.c", + "evidence": "UNSAFE_OR_UNPROVEN", + "implement": false + }, + { + "needed_fact": "output_channel", + "local_api": null, + "source": "../chimera-elfldr/socksrv.c:129-145,361-365; ../chimera-ps5-payload-manager/src/ps5_launcher.c:18-70", + "evidence": "PROVEN_ABSENT_ON_CONTROLLED_ROUTE", + "implement": false + }, + { + "needed_fact": "monotonic_time_deadline", + "local_api": "clock_gettime(CLOCK_MONOTONIC) used by hardened loader watchdog", + "source": "../chimera-elfldr/ps5_controlled.c:26-59", + "evidence": "SOURCE_AND_HOST_TEST_EVIDENCE_ONLY", + "implement": false + }, + { + "needed_fact": "process_exit", + "local_api": "normal CRT payload_terminate/payload_exit or custom entry return", + "source": "work/upstream/sdk/crt/crt.c:150-220; ../chimera-elfldr/elfldr.c:380-415", + "evidence": "OBSERVER_STARTUP_OR_EXIT_ABI_UNPROVEN", + "implement": false + } + ], + "build_gate": { + "startup_and_exit_abi_proven": false, + "non_persistent_output_channel_proven": false, + "normal_sdk_crt_kernelwrite_free": false, + "custom_freestanding_cleanup_proven": false, + "observer_source_created": false, + "observer_target_declared": false, + "target_build_performed": false, + "double_clean_build_performed": false, + "reason": "BLOCKED_BEFORE_SOURCE_AND_BUILD" + }, + "implementation": { + "observer_only": true, + "observer_logic_implemented": false, + "observations_implemented": [], + "unsupported_or_unproven": [ + "observer_self_identity_at_runtime", + "firmware_source_1_at_runtime", + "firmware_source_2", + "mount_and_filesystem_information", + "live_object_metadata", + "live_sha256", + "rollback_object_metadata_and_sha256", + "processes_and_services", + "listeners", + "autoload_state", + "bounded_runtime_output", + "safe_process_exit" + ], + "host_contract_model_only": "tests/phase09b_observer_model.py" + }, + "artifact": { + "present": false, + "path": null, + "sha256": null, + "size": null, + "imports": [], + "undefined_symbols": [], + "dynamic_dependencies": [], + "network_functions": [], + "filesystem_reads": [], + "installation_eligible": false, + "lifecycle_eligible": false, + "autoload_eligible": false, + "execution_authorized": false, + "execution_eligible": false + }, + "static_artifact_audit": { + "status": "NOT_PERFORMED_BLOCKED_BEFORE_BUILD", + "elf_headers": "NOT_APPLICABLE_NO_ARTIFACT", + "program_headers": "NOT_APPLICABLE_NO_ARTIFACT", + "section_headers": "NOT_APPLICABLE_NO_ARTIFACT", + "dynamic_dependencies": "NOT_APPLICABLE_NO_ARTIFACT", + "imports_and_undefined_symbols": "NOT_APPLICABLE_NO_ARTIFACT", + "exports": "NOT_APPLICABLE_NO_ARTIFACT", + "relocations": "NOT_APPLICABLE_NO_ARTIFACT", + "linker_map": "NOT_CREATED", + "callgraph": "NOT_CREATED", + "disassembly": "NOT_CREATED", + "strings": "NOT_APPLICABLE_NO_ARTIFACT", + "denied_capabilities": "NOT_APPLICABLE_NO_ARTIFACT", + "filesystem_open_flags": "NOT_APPLICABLE_NO_ARTIFACT", + "network": "NOT_APPLICABLE_NO_ARTIFACT", + "threads_and_loops": "NOT_APPLICABLE_NO_ARTIFACT", + "kernelwrites": "NOT_APPLICABLE_NO_ARTIFACT" + }, + "reproducibility": { + "status": "NOT_PERFORMED_BLOCKED_BEFORE_BUILD", + "build_1_sha256": null, + "build_2_sha256": null, + "byte_identical": null, + "toolchain": { + "sdk_release": "v0.41", + "sdk_commit": "d2e2e585740362976a39fdd5ccf390f199a7bc37", + "installed_crt1_sha256": "0e4c1e5e38f22026580e856238a53e75d9e112f99a34bfe5495a3a0ffac15238", + "current_windows_wrapper_executable_available": false + } + }, + "default_observation_plan": { + "schema_version": 1, + "plan_id": null, + "authorized": false, + "transfer_authorized": false, + "execution_authorized": false, + "installation_authorized": false, + "lifecycle_authorized": false, + "autoload_authorized": false, + "backup_creation_authorized": false, + "automatic_retry": false, + "device_address": null, + "device_identity": null, + "observer": { + "sha256": null, + "size": null, + "source_commit": null, + "toolchain_identity": null + }, + "firmware_gate": null, + "allowed_observations": [], + "read_paths": [], + "output_channel": null, + "maximum_runtime_ms": null, + "maximum_execution_count": 0, + "expires_at": null, + "stop_criteria": [] + }, + "remaining_anti_brick_blockers": [ + "two_current_firmware_sources_absent", + "current_live_paths_absent", + "current_live_object_identities_absent", + "current_live_sizes_and_sha256_absent", + "mount_and_filesystem_properties_absent", + "processes_and_services_absent", + "listeners_absent", + "autoload_state_absent", + "rollback_backups_absent", + "payload_manager_backup_hard_blocker", + "recovery_independence_unproven", + "power_loss_safe_switch_unproven", + "component_order_unproven", + "observer_startup_or_exit_abi_unproven", + "no_proven_non_persistent_output_channel" + ], + "prohibited_actions": [ + "connect_to_ps5", + "scan_ps5_ports", + "use_usb", + "transfer_observer", + "execute_observer_or_elf", + "install_or_replace_component", + "create_backup", + "change_configuration", + "activate_autoload_or_retry", + "reboot", + "start_stop_restart_or_signal_process_or_service", + "modify_hardened_elfldr_runtime", + "modify_controlled_payload_manager_runtime", + "modify_lifecycle_probe", + "build_observer_until_both_hard_gates_are_proven", + "start_graphics_sdl_retroarch_or_gnm_work" + ] +} diff --git a/manifests/runtime/phase-0.9c-feasibility.json b/manifests/runtime/phase-0.9c-feasibility.json new file mode 100644 index 0000000..28828a7 --- /dev/null +++ b/manifests/runtime/phase-0.9c-feasibility.json @@ -0,0 +1,416 @@ +{ + "schema_version": 1, + "phase": "0.9C", + "date": "2026-07-18", + "scope": "offline_observer_execution_feasibility_closure_only", + "status": "BLOCKED", + "classification": "BLOCKED_MULTIPLE_FOUNDATIONAL_CONTRACTS", + "baseline_commit": "3ddc213ea67bb286256ae42e52c65e00488608ca", + "branch": "codex/chimera-gfx-phase09c-execution-feasibility", + "authorization": { + "authorized": false, + "transfer_authorized": false, + "execution_authorized": false, + "installation_authorized": false, + "lifecycle_authorized": false, + "autoload_authorized": false, + "backup_creation_authorized": false, + "observer_build_authorized": false, + "automatic_retry": false + }, + "canonical_state_preserved": { + "historical_phase08_status": "READ_ONLY_PREFLIGHT_BLOCKED", + "phase09a_status": "DESIGN_ONLY", + "phase09b_status": "BLOCKED", + "firmware_runtime_behavior": "UNPROVEN", + "stock_hashes": "reference_only", + "payload_manager_backup": "HARD_BLOCKER", + "device_contact_performed": false, + "device_transfer_performed": false, + "device_execution_performed": false, + "target_build_performed": false + }, + "permanent_denylist_binding": { + "sha256": "4be1c17b4964f2b68c39b5145bc4af4619c32512d60269ecf5c39728b390fa63", + "status": "BLOCKED", + "permanent": true, + "execution_eligible": false + }, + "source_commits": { + "hardened_elfldr": "197623058f509eddde18868dafcb92fdcac66464", + "controlled_payload_manager": "e23d94ff91233aa770e2342800c1467875bdef44", + "elfldr_public_base": "699e8bcff03e91e8d6ca6eba281af25c5a58d8c2", + "payload_manager_public_base": "cfbc70f30f419b09bf2b52283f7409e2d3117ee1", + "ps5_payload_sdk_v0_41": "d2e2e585740362976a39fdd5ccf390f199a7bc37", + "lifecycle_probe_source": "fe08300339a13f899fb78ea404ada381a5cba87c" + }, + "source_tree_status": { + "hardened_elfldr": "clean", + "controlled_payload_manager": "clean", + "elfldr_public_base": "clean", + "payload_manager_public_base": "clean", + "ps5_payload_sdk_v0_41": "clean" + }, + "source_evidence": [ + { + "component": "hardened_elfldr_entry_mapper", + "path": "../chimera-elfldr/elfldr.c", + "sha256": "9949f8e4037984d10f1f5aa498e4665593d1fac8a33614d7f2141349839bb803", + "lines": ["80-265", "273-415", "480-545", "643-769"], + "finding": "Maps the payload and child-owned argument resources, installs a synthetic return address, sets RIP/RDI, restores credentials, and detaches without a proven observer return contract." + }, + { + "component": "hardened_elfldr_controlled_route", + "path": "../chimera-elfldr/socksrv.c", + "sha256": "d642ced3e9b4a296dd15e355050ebe956f53a6dfdaa6ac10109cd067a3bba3d7", + "lines": ["129-215", "289-375"], + "finding": "Passes stdio=-1, invokes the watchdog, discards payload data, and writes only fixed loader text." + }, + { + "component": "hardened_cleanup_model", + "path": "../chimera-elfldr/controlled_runtime.c", + "sha256": "10145f3bbb3b54e3d715b1667e45c6f12d3c7f52a04342ab104ec8dd49e384c9", + "lines": ["60-92", "95-143", "146-250"], + "finding": "Bounds ptrace preparation and models pre-detach cleanup plus a SIGTERM/SIGKILL watchdog; signal termination is not a safe observer exit." + }, + { + "component": "hardened_ps5_cleanup_adapter", + "path": "../chimera-elfldr/ps5_controlled.c", + "sha256": "68717ef1cc31c483743c5af325c59e73a90f66e93b880b989e349a4ddc748772", + "lines": ["26-59", "132-220"], + "finding": "Uses waitpid but discards child status and exposes no payload result copy-out." + }, + { + "component": "controlled_manager_send_only_transport", + "path": "../chimera-ps5-payload-manager/src/verified_launcher.c", + "sha256": "066100ca4917c7acc560e2e85666ca136cd7ccfd9094417377048f41106dd56e", + "lines": ["31-184"], + "finding": "Hashes and sends one descriptor, then closes; there is no receive operation." + }, + { + "component": "controlled_manager_loopback_adapter", + "path": "../chimera-ps5-payload-manager/src/ps5_launcher.c", + "sha256": "29c1a5fd01784a59e88b3698940f120cb03020071bc2b7d74a1da1a51524ef59", + "lines": ["18-69"], + "finding": "Defines connect_loopback, send_all, and close_socket only." + }, + { + "component": "sdk_normal_crt", + "path": "work/upstream/sdk/crt/crt.c", + "sha256": "3875f4739ec40b33f1f4967a1acbb585a527d2c281a360153e1f69c8b945932a", + "lines": ["30-80", "88-180", "193-220"], + "finding": "Clears BSS, initializes syscall/kernel/klog/rtld, calls __patch_init before main, and chooses return/exit/trap at termination." + }, + { + "component": "sdk_patch", + "path": "work/upstream/sdk/crt/patch.c", + "sha256": "4f76a677bba54f4641e1cf2755768c29afb7464a35b2f21e85db2aa2785eceac", + "lines": ["27-100"], + "finding": "Writes credential capabilities/attributes and syscall address bounds." + }, + { + "component": "sdk_crt_object_set", + "path": "work/upstream/sdk/crt/Makefile", + "sha256": "6a62f777f32ab05cbe7bff81c00cfd6639ba8bf440aba611a40ff0b62732568b", + "lines": ["19-26", "50-54"], + "finding": "The normal crt1.o transitively includes patch, kernel, rtld, and related startup objects." + }, + { + "component": "sdk_compiler_wrapper", + "path": "work/upstream/sdk/host/bin/prospero-clang", + "sha256": "0cf49ae43d6110a7606c0ee4d702fc4b5d5e1c3ae9a722945a48b80294e295ba", + "lines": ["25-46", "69-84"], + "finding": "-nostartfiles and -nodefaultlibs can suppress CRT and default libraries but do not prove a usable startup ABI." + }, + { + "component": "sdk_linker_script", + "path": "work/upstream/sdk/host/elf_x86_64.x", + "sha256": "169b80d01da601ef96bbc584986608dec1d9c01397eae81eccd4e1a66b0a6c6a", + "lines": ["17-92"], + "finding": "Defines image, arrays, dynamic, data, and BSS boundaries; it does not prove loader initialization or cleanup." + }, + { + "component": "sdk_kernel_firmware_source_one", + "path": "work/upstream/sdk/crt/kernel.c", + "sha256": "ac1c375aae8d3cb1be5fb8bad2f4e6492b6ec1f1450977b534202f46b5b70321", + "lines": ["148-170"], + "finding": "Reads sdk_ps5_ver from the libSceLibcInternal process parameter; not an independent second current system source." + }, + { + "component": "sdk_firmware_export_name_only", + "path": "work/upstream/sdk/sce_stubs/libkernel_web.c", + "sha256": "dca70757a0680ede52502fe7db10060fe30506f7ed0adfc43ce00700145ab4ff", + "lines": ["2358-2360"], + "finding": "sceKernelGetProsperoSystemSwVersion is a name-only export stub without an accepted prototype or semantics and is not source two." + }, + { + "component": "lifecycle_probe_source", + "path": "samples/lifecycle_probe/main.c", + "sha256": "1ae7df1fe921ccab2a252f77975d3d441ef7725e34535b024580c0d4a242d766", + "finding": "Historical normal-CRT lifecycle source is unchanged and is not an observer startup or output proof." + } + ], + "immutable_evidence": { + "phase08": { + "commit": "2c944d6d65a08f7e1c02f518721cde061b999329", + "files": { + "docs/runtime/phase-0.8-read-only-preflight.md": "3fbe086175a6048176075f447ec1482074928e3b5282db97ea2169395fe1d508", + "manifests/runtime/phase-0.8-read-only-preflight.json": "47d7f452f8799979fe99b3e6d56859f03544112725bf7e5b349eba5ed81b3322", + "tests/test_phase08_preflight.py": "8a4ad7c70de28ffe3148fd3fd1f68c36a872c53c691c9068e1ff163970863c48" + } + }, + "phase09a_manifest_sha256": "39fd7c70cae998d9d74e7caf1ff3c19f9f76f7de2e5a50bc09baede4ba53e9e9", + "phase09b_manifest_sha256": "104c4a667ad17f9827fc7276852c6faeef96effb8e3561a5ebc19a62c7d51634", + "phase09b_schema_sha256": "efcea3b0001ef5b2da65c372ceb93ee2fec09c9331b2e4cbb6008212504c0918" + }, + "startup_exit": { + "normal_sdk_kernelwrite_free": false, + "normal_sdk_status": "PROVEN_SIDE_EFFECTING", + "freestanding_mechanically_linkable": true, + "freestanding_dependency_closure_proven": false, + "stack_alignment_proven": false, + "complete_relocation_and_bss_tls_contract_proven": false, + "callable_read_and_time_abi_proven": false, + "safe_return_proven": false, + "safe_process_exit_proven": false, + "error_exit_proven": false, + "timeout_safe_exit_proven": false, + "complete_cleanup_proven": false, + "blockers": [ + "normal_sdk_reaches_prohibited_patch_writes", + "freestanding_stack_alignment_unproven", + "freestanding_bss_relocation_tls_closure_unproven", + "freestanding_read_and_monotonic_time_abi_absent", + "return_resumes_unproven_process_continuation", + "process_exit_and_status_delivery_unproven", + "timeout_relies_on_inadmissible_signal_termination", + "post_detach_mapping_descriptor_and_manager_cleanup_unproven" + ] + }, + "output_architectures": [ + { + "id": "D1_CALLER_OWNED_BOUNDED_BUFFER", + "status": "CONCEPT_FEASIBLE_REQUIRES_LOADER_CHANGE_AND_EXIT_PROOF", + "current_implementation": false, + "reason": "Current payloadout is child-owned mapped memory and is never copied out; a future caller-owned buffer remains a host-only contract." + }, + { + "id": "D2_EXISTING_REQUEST_RESPONSE", + "status": "REJECTED_SEND_ONLY_NO_RESULT_RECEIVE", + "current_implementation": false, + "reason": "The manager connects, sends, and closes without a receive callback." + }, + { + "id": "D3_LOADER_OWNED_STATUS_RECORD", + "status": "UNPROVEN_REQUIRES_LOADER_STATE_AND_PROPAGATION_CHANGE", + "current_implementation": false, + "reason": "The current waiter discards status and no per-request record is propagated." + }, + { + "id": "D4_PROCESS_EXIT_STATUS", + "status": "REJECTED_WAIT_STATUS_DISCARDED_AND_AMBIGUOUS", + "current_implementation": false, + "reason": "Exit/crash/signal status is discarded and cannot carry the observation dataset." + } + ], + "host_protocol": { + "model": "tests/phase09c_feasibility_model.py", + "host_only": true, + "target_implemented": false, + "magic": "CHG09C01", + "version": 1, + "header_size": 256, + "maximum_output_size": 4096, + "maximum_body_size": 3840, + "integer_encoding": "unsigned_big_endian", + "execution_nonce_bytes": 16, + "request_id_bytes": 16, + "firmware_field_bytes": 8, + "artifact_hash_algorithm": "sha256", + "body_checksum_algorithm": "sha256", + "result_checksum_algorithm": "sha256", + "completion_marker": "COMPLETE", + "result_checksum_zeroed_fields": [ + "result_checksum", + "completion_marker" + ], + "required_fields": [ + "magic", + "protocol_version", + "execution_nonce", + "request_id", + "firmware_source_1", + "firmware_source_2", + "observer_version", + "maximum_output_size", + "actual_output_size", + "status", + "requested_capability_bitmap", + "observed_capability_bitmap", + "unsupported_capability_bitmap", + "truncation_flag", + "artifact_sha256", + "body_checksum", + "result_checksum", + "deadline_monotonic_ns", + "completion_marker", + "cleanup_status" + ], + "pointers_present": false, + "dynamic_growth": false, + "fail_closed_conditions": [ + "unknown_version", + "bad_size_or_checked_arithmetic_failure", + "stale_nonce_or_request_id", + "duplicate_result", + "missing_or_conflicting_firmware_source", + "artifact_hash_mismatch", + "checksum_failure", + "timeout", + "incomplete_completion_marker", + "truncation", + "observer_failure", + "cleanup_not_proven", + "capability_bitmap_incomplete_or_conflicting" + ] + }, + "firmware": { + "expected": "9.60", + "source_one": { + "identity": "sdk_kernel_get_fw_version_libSceLibcInternal_sdk_ps5_ver", + "status": "SOURCE_PRESENT_RUNTIME_UNPROVEN_NOT_INDEPENDENT", + "nonce_bound_runtime_result_present": false + }, + "source_two": { + "identity": null, + "status": "ABSENT", + "export_name_candidate_accepted": false, + "nonce_bound_runtime_result_present": false + }, + "agreement_proven": false, + "gate": "BLOCKED_FIRMWARE_SOURCE_INCOMPLETE", + "conflict_policy": "BLOCKED_NO_SOURCE_PREFERENCE" + }, + "side_effect_model": { + "no_persistent_content_write_is_side_effect_free": false, + "read_only_flag_is_side_effect_free": false, + "all_planned_observations_proven_side_effect_free": false, + "dimensions": [ + "content", + "metadata", + "atime", + "audit", + "cache", + "counters", + "service_state", + "security_monitoring", + "open_bookkeeping", + "process_accounting", + "object_lifetime", + "races" + ], + "classifications": [ + "SEMANTICALLY_READONLY", + "METADATA_EFFECT_POSSIBLE", + "ATIME_EFFECT_POSSIBLE", + "AUDIT_EFFECT_POSSIBLE", + "CACHE_EFFECT_POSSIBLE", + "COUNTER_EFFECT_POSSIBLE", + "SERVICE_STATE_EFFECT_POSSIBLE", + "SECURITY_MONITORING_EFFECT_POSSIBLE", + "OPEN_BOOKKEEPING_EFFECT_POSSIBLE", + "PROCESS_ACCOUNTING_EFFECT_POSSIBLE", + "OBJECT_LIFETIME_EFFECT_POSSIBLE", + "OBJECT_RACE_POSSIBLE", + "NOT_PROVABLY_SIDE_EFFECT_FREE" + ], + "gate": "BLOCKED_OBSERVATION_SIDE_EFFECTS_UNBOUNDED" + }, + "capability_closure": [ + {"id": "runtime_self_identity", "source_evidence": "future_protocol_fields_only", "host_evidence": "binding_modelled", "target_evidence": "ABSENT", "implementation_allowed": false, "execution_allowed": false, "blocker": "no_target_artifact_or_runtime_binding"}, + {"id": "firmware_source_1", "source_evidence": "sdk_crt_kernel_c_148_170", "host_evidence": "result_binding_modelled", "target_evidence": "ABSENT", "implementation_allowed": false, "execution_allowed": false, "blocker": "runtime_unproven_and_not_independent"}, + {"id": "firmware_source_2", "source_evidence": "name_only_stub_rejected", "host_evidence": "missing_and_conflict_fail_closed", "target_evidence": "ABSENT", "implementation_allowed": false, "execution_allowed": false, "blocker": "accepted_independent_source_absent"}, + {"id": "mount_query", "source_evidence": "sdk_sample_only", "host_evidence": "unsupported_representable", "target_evidence": "ABSENT", "implementation_allowed": false, "execution_allowed": false, "blocker": "abi_completeness_runtime_and_effects_unproven"}, + {"id": "metadata", "source_evidence": "manager_no_follow_pattern", "host_evidence": "error_framing_modelled", "target_evidence": "ABSENT", "implementation_allowed": false, "execution_allowed": false, "blocker": "target_semantics_and_effects_unproven"}, + {"id": "object_id", "source_evidence": "manager_stat_comparison", "host_evidence": "completeness_modelled", "target_evidence": "ABSENT", "implementation_allowed": false, "execution_allowed": false, "blocker": "stable_identity_and_race_contract_unproven"}, + {"id": "size", "source_evidence": "manager_exact_size_comparison", "host_evidence": "checked_lengths_modelled", "target_evidence": "ABSENT", "implementation_allowed": false, "execution_allowed": false, "blocker": "current_stable_object_absent"}, + {"id": "sha256", "source_evidence": "manager_bounded_same_fd_hash", "host_evidence": "checksum_failures_rejected", "target_evidence": "ABSENT", "implementation_allowed": false, "execution_allowed": false, "blocker": "read_effects_paths_and_stable_object_absent"}, + {"id": "processes", "source_evidence": "partial_sdk_sample", "host_evidence": "unsupported_representable", "target_evidence": "ABSENT", "implementation_allowed": false, "execution_allowed": false, "blocker": "complete_bounded_abi_absent"}, + {"id": "services", "source_evidence": "none_complete", "host_evidence": "unsupported_representable", "target_evidence": "ABSENT", "implementation_allowed": false, "execution_allowed": false, "blocker": "service_inventory_mapping_absent"}, + {"id": "listeners", "source_evidence": "none_accepted", "host_evidence": "unsupported_representable", "target_evidence": "ABSENT", "implementation_allowed": false, "execution_allowed": false, "blocker": "listener_owner_api_absent"}, + {"id": "autoload", "source_evidence": "stock_routes_incomplete_or_side_effecting", "host_evidence": "missing_source_blocked", "target_evidence": "ABSENT", "implementation_allowed": false, "execution_allowed": false, "blocker": "complete_authoritative_safe_sources_absent"}, + {"id": "rollback_objects", "source_evidence": "phase08r_phase09a_contract_only", "host_evidence": "false_promotion_rejected", "target_evidence": "ABSENT", "implementation_allowed": false, "execution_allowed": false, "blocker": "current_backups_absent_manager_backup_hard_blocker"}, + {"id": "monotonic_time", "source_evidence": "loader_owned_clock_only", "host_evidence": "deadline_modelled", "target_evidence": "ABSENT_IN_OBSERVER_ABI", "implementation_allowed": false, "execution_allowed": false, "blocker": "freestanding_callable_clock_absent"}, + {"id": "startup", "source_evidence": "full_sdk_and_loader_graph", "host_evidence": "classifications_validated", "target_evidence": "ABSENT", "implementation_allowed": false, "execution_allowed": false, "blocker": "normal_writes_and_freestanding_closure_incomplete"}, + {"id": "output", "source_evidence": "current_send_only_route", "host_evidence": "fixed_d1_contract_modelled", "target_evidence": "ABSENT", "implementation_allowed": false, "execution_allowed": false, "blocker": "no_caller_owned_target_buffer_or_delivery"}, + {"id": "normal_exit", "source_evidence": "wait_reap_status_discarded", "host_evidence": "no_safe_terminal", "target_evidence": "ABSENT", "implementation_allowed": false, "execution_allowed": false, "blocker": "return_exit_teardown_and_status_unproven"}, + {"id": "error_exit", "source_evidence": "crt_error_longjmp_trap_paths", "host_evidence": "error_not_empty_success", "target_evidence": "ABSENT", "implementation_allowed": false, "execution_allowed": false, "blocker": "partial_unwind_status_cleanup_unproven"}, + {"id": "timeout", "source_evidence": "loader_signal_watchdog", "host_evidence": "timeout_rejected", "target_evidence": "ABSENT", "implementation_allowed": false, "execution_allowed": false, "blocker": "kill_is_not_safe_exit"}, + {"id": "cleanup", "source_evidence": "pre_detach_cleanup_post_detach_process_dependency", "host_evidence": "failed_cleanup_rejected", "target_evidence": "ABSENT", "implementation_allowed": false, "execution_allowed": false, "blocker": "joint_resource_loader_manager_cleanup_unproven"}, + {"id": "recovery_independence", "source_evidence": "phase09a_contract_only", "host_evidence": "blocker_preserved", "target_evidence": "ABSENT", "implementation_allowed": false, "execution_allowed": false, "blocker": "executor_and_exact_rollback_objects_unproven"} + ], + "implementation": { + "target_observer_source_present": false, + "observer_target_declared": false, + "target_assembly_present": false, + "target_elf_present": false, + "linker_map_present": false, + "lifecycle_package_present": false, + "installation_package_present": false, + "autoload_package_present": false, + "loader_production_modified": false, + "payload_manager_production_modified": false, + "lifecycle_production_modified": false + }, + "artifact": { + "present": false, + "path": null, + "sha256": null, + "size": null, + "execution_eligible": false, + "execution_authorized": false + }, + "static_audit": { + "status": "NOT_APPLICABLE_NO_TARGET_SOURCE_OR_ARTIFACT", + "target_build": "NOT_PERFORMED", + "elf_headers": "NOT_APPLICABLE", + "imports": "NOT_APPLICABLE", + "dynamic_dependencies": "NOT_APPLICABLE", + "relocations": "NOT_APPLICABLE", + "linker_map": "NOT_CREATED", + "target_callgraph": "NOT_CREATED", + "target_disassembly": "NOT_CREATED", + "package": "NOT_CREATED", + "host_model_only": true + }, + "final_decision": { + "positive_classification_allowed": false, + "classification": "BLOCKED_MULTIPLE_FOUNDATIONAL_CONTRACTS", + "foundational_blockers": [ + "BLOCKED_STARTUP_ABI_UNPROVEN", + "BLOCKED_EXIT_CLEANUP_UNPROVEN", + "BLOCKED_NO_BOUNDED_OUTPUT_CHANNEL", + "BLOCKED_FIRMWARE_SOURCE_INCOMPLETE", + "BLOCKED_OBSERVATION_SIDE_EFFECTS_UNBOUNDED" + ], + "next_phase_automatic": false + }, + "prohibited_actions": [ + "connect_to_ps5_or_other_device", + "network_scan", + "usb_transfer", + "build_or_assemble_target_observer", + "build_target_elf", + "transfer_or_execute_payload", + "install_or_enable_autoload", + "modify_hardened_elfldr_production_code", + "modify_controlled_payload_manager_production_code", + "modify_lifecycle_installer_or_rollback_production_code", + "credential_capability_or_kernel_patch", + "ptrace_experiment", + "module_loading_experiment", + "new_target_listener_or_filesystem_output", + "graphics_sdl_retroarch_gnm_or_videoout_work", + "external_publication_pull_request_or_upstream_contact" + ] +} diff --git a/manifests/runtime/phase-0.9c-feasibility.schema.json b/manifests/runtime/phase-0.9c-feasibility.schema.json new file mode 100644 index 0000000..3a7cf9c --- /dev/null +++ b/manifests/runtime/phase-0.9c-feasibility.schema.json @@ -0,0 +1,336 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://chimera.invalid/schema/phase-0.9c-feasibility-v1.json", + "title": "Chimera GFX Phase-0.9C offline feasibility record", + "description": "A non-authorizing offline record. It cannot represent a target observer, artifact, package, transfer, execution, installation, lifecycle action, autoload action, backup action, or retry.", + "type": "object", + "additionalProperties": false, + "required": [ + "schema_version", + "phase", + "date", + "scope", + "status", + "classification", + "baseline_commit", + "branch", + "authorization", + "canonical_state_preserved", + "permanent_denylist_binding", + "source_commits", + "source_tree_status", + "source_evidence", + "immutable_evidence", + "startup_exit", + "output_architectures", + "host_protocol", + "firmware", + "side_effect_model", + "capability_closure", + "implementation", + "artifact", + "static_audit", + "final_decision", + "prohibited_actions" + ], + "properties": { + "schema_version": {"const": 1}, + "phase": {"const": "0.9C"}, + "date": {"const": "2026-07-18"}, + "scope": {"const": "offline_observer_execution_feasibility_closure_only"}, + "status": {"const": "BLOCKED"}, + "classification": {"const": "BLOCKED_MULTIPLE_FOUNDATIONAL_CONTRACTS"}, + "baseline_commit": { + "const": "3ddc213ea67bb286256ae42e52c65e00488608ca" + }, + "branch": { + "const": "codex/chimera-gfx-phase09c-execution-feasibility" + }, + "authorization": { + "type": "object", + "additionalProperties": false, + "required": [ + "authorized", + "transfer_authorized", + "execution_authorized", + "installation_authorized", + "lifecycle_authorized", + "autoload_authorized", + "backup_creation_authorized", + "observer_build_authorized", + "automatic_retry" + ], + "properties": { + "authorized": {"const": false}, + "transfer_authorized": {"const": false}, + "execution_authorized": {"const": false}, + "installation_authorized": {"const": false}, + "lifecycle_authorized": {"const": false}, + "autoload_authorized": {"const": false}, + "backup_creation_authorized": {"const": false}, + "observer_build_authorized": {"const": false}, + "automatic_retry": {"const": false} + } + }, + "canonical_state_preserved": {"type": "object"}, + "permanent_denylist_binding": { + "type": "object", + "required": ["sha256", "status", "permanent", "execution_eligible"], + "properties": { + "sha256": { + "const": "4be1c17b4964f2b68c39b5145bc4af4619c32512d60269ecf5c39728b390fa63" + }, + "status": {"const": "BLOCKED"}, + "permanent": {"const": true}, + "execution_eligible": {"const": false} + } + }, + "source_commits": {"type": "object"}, + "source_tree_status": {"type": "object"}, + "source_evidence": { + "type": "array", + "minItems": 10, + "uniqueItems": true, + "items": {"type": "object"} + }, + "immutable_evidence": {"type": "object"}, + "startup_exit": { + "type": "object", + "required": [ + "normal_sdk_kernelwrite_free", + "freestanding_dependency_closure_proven", + "safe_return_proven", + "safe_process_exit_proven", + "complete_cleanup_proven", + "blockers" + ], + "properties": { + "normal_sdk_kernelwrite_free": {"const": false}, + "normal_sdk_status": {"const": "PROVEN_SIDE_EFFECTING"}, + "freestanding_mechanically_linkable": {"const": true}, + "freestanding_dependency_closure_proven": {"const": false}, + "stack_alignment_proven": {"const": false}, + "complete_relocation_and_bss_tls_contract_proven": {"const": false}, + "callable_read_and_time_abi_proven": {"const": false}, + "safe_return_proven": {"const": false}, + "safe_process_exit_proven": {"const": false}, + "error_exit_proven": {"const": false}, + "timeout_safe_exit_proven": {"const": false}, + "complete_cleanup_proven": {"const": false}, + "blockers": {"type": "array", "minItems": 1, "uniqueItems": true} + } + }, + "output_architectures": { + "type": "array", + "minItems": 4, + "maxItems": 4, + "items": { + "type": "object", + "required": ["id", "status", "current_implementation", "reason"], + "properties": { + "id": {"type": "string"}, + "status": {"type": "string"}, + "current_implementation": {"const": false}, + "reason": {"type": "string"} + } + } + }, + "host_protocol": { + "type": "object", + "required": [ + "host_only", + "target_implemented", + "magic", + "version", + "header_size", + "maximum_output_size", + "maximum_body_size", + "pointers_present", + "dynamic_growth", + "fail_closed_conditions" + ], + "properties": { + "model": {"const": "tests/phase09c_feasibility_model.py"}, + "host_only": {"const": true}, + "target_implemented": {"const": false}, + "magic": {"const": "CHG09C01"}, + "version": {"const": 1}, + "header_size": {"const": 256}, + "maximum_output_size": {"const": 4096}, + "maximum_body_size": {"const": 3840}, + "integer_encoding": {"const": "unsigned_big_endian"}, + "execution_nonce_bytes": {"const": 16}, + "request_id_bytes": {"const": 16}, + "firmware_field_bytes": {"const": 8}, + "artifact_hash_algorithm": {"const": "sha256"}, + "body_checksum_algorithm": {"const": "sha256"}, + "result_checksum_algorithm": {"const": "sha256"}, + "completion_marker": {"const": "COMPLETE"}, + "result_checksum_zeroed_fields": {"type": "array"}, + "required_fields": {"type": "array", "minItems": 16, "uniqueItems": true}, + "pointers_present": {"const": false}, + "dynamic_growth": {"const": false}, + "fail_closed_conditions": { + "type": "array", + "minItems": 10, + "uniqueItems": true + } + } + }, + "firmware": { + "type": "object", + "required": [ + "expected", + "source_one", + "source_two", + "agreement_proven", + "gate", + "conflict_policy" + ], + "properties": { + "expected": {"const": "9.60"}, + "source_one": {"type": "object"}, + "source_two": { + "type": "object", + "required": [ + "identity", + "status", + "export_name_candidate_accepted", + "nonce_bound_runtime_result_present" + ], + "properties": { + "identity": {"const": null}, + "status": {"const": "ABSENT"}, + "export_name_candidate_accepted": {"const": false}, + "nonce_bound_runtime_result_present": {"const": false} + } + }, + "agreement_proven": {"const": false}, + "gate": {"const": "BLOCKED_FIRMWARE_SOURCE_INCOMPLETE"}, + "conflict_policy": {"const": "BLOCKED_NO_SOURCE_PREFERENCE"} + } + }, + "side_effect_model": { + "type": "object", + "required": [ + "no_persistent_content_write_is_side_effect_free", + "read_only_flag_is_side_effect_free", + "all_planned_observations_proven_side_effect_free", + "dimensions", + "classifications", + "gate" + ], + "properties": { + "no_persistent_content_write_is_side_effect_free": {"const": false}, + "read_only_flag_is_side_effect_free": {"const": false}, + "all_planned_observations_proven_side_effect_free": {"const": false}, + "dimensions": {"type": "array", "minItems": 12, "uniqueItems": true}, + "classifications": {"type": "array", "minItems": 10, "uniqueItems": true}, + "gate": {"const": "BLOCKED_OBSERVATION_SIDE_EFFECTS_UNBOUNDED"} + } + }, + "capability_closure": { + "type": "array", + "minItems": 21, + "maxItems": 21, + "uniqueItems": true, + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "source_evidence", + "host_evidence", + "target_evidence", + "implementation_allowed", + "execution_allowed", + "blocker" + ], + "properties": { + "id": {"type": "string", "minLength": 1}, + "source_evidence": {"type": "string", "minLength": 1}, + "host_evidence": {"type": "string", "minLength": 1}, + "target_evidence": {"type": "string", "minLength": 1}, + "implementation_allowed": {"const": false}, + "execution_allowed": {"const": false}, + "blocker": {"type": "string", "minLength": 1} + } + } + }, + "implementation": { + "type": "object", + "additionalProperties": false, + "required": [ + "target_observer_source_present", + "observer_target_declared", + "target_assembly_present", + "target_elf_present", + "linker_map_present", + "lifecycle_package_present", + "installation_package_present", + "autoload_package_present", + "loader_production_modified", + "payload_manager_production_modified", + "lifecycle_production_modified" + ], + "properties": { + "target_observer_source_present": {"const": false}, + "observer_target_declared": {"const": false}, + "target_assembly_present": {"const": false}, + "target_elf_present": {"const": false}, + "linker_map_present": {"const": false}, + "lifecycle_package_present": {"const": false}, + "installation_package_present": {"const": false}, + "autoload_package_present": {"const": false}, + "loader_production_modified": {"const": false}, + "payload_manager_production_modified": {"const": false}, + "lifecycle_production_modified": {"const": false} + } + }, + "artifact": { + "type": "object", + "additionalProperties": false, + "required": [ + "present", + "path", + "sha256", + "size", + "execution_eligible", + "execution_authorized" + ], + "properties": { + "present": {"const": false}, + "path": {"const": null}, + "sha256": {"const": null}, + "size": {"const": null}, + "execution_eligible": {"const": false}, + "execution_authorized": {"const": false} + } + }, + "static_audit": {"type": "object"}, + "final_decision": { + "type": "object", + "required": [ + "positive_classification_allowed", + "classification", + "foundational_blockers", + "next_phase_automatic" + ], + "properties": { + "positive_classification_allowed": {"const": false}, + "classification": {"const": "BLOCKED_MULTIPLE_FOUNDATIONAL_CONTRACTS"}, + "foundational_blockers": { + "type": "array", + "minItems": 5, + "uniqueItems": true + }, + "next_phase_automatic": {"const": false} + } + }, + "prohibited_actions": { + "type": "array", + "minItems": 10, + "uniqueItems": true + } + } +} diff --git a/manifests/runtime/phase-0.9d-existing-stack-readback.json b/manifests/runtime/phase-0.9d-existing-stack-readback.json new file mode 100644 index 0000000..76e1782 --- /dev/null +++ b/manifests/runtime/phase-0.9d-existing-stack-readback.json @@ -0,0 +1,428 @@ +{ + "schema_version": 1, + "phase": "PHASE_0_9D_EXISTING_STACK_READBACK", + "date": "2026-07-18", + "status": "DESIGN_ONLY", + "baseline_commit": "e0e68829ab76977c845e7106ef93e6c01fbc966e", + "branch": "codex/chimera-gfx-phase09d-existing-stack-readback", + "scope": "offline_existing_stack_readback_and_recovery_feasibility", + "actions": { + "ps5_connected": false, + "device_request_performed": false, + "files_transferred": false, + "device_write_performed": false, + "target_execution_performed": false, + "target_artifact_created": false, + "target_build_performed": false, + "observer_created": false, + "device_client_created": false, + "backup_created": false, + "staging_performed": false + }, + "authorization": { + "installation_authorized": false, + "execution_authorized": false, + "lifecycle_authorized": false, + "automatic_retry": false, + "autoload_authorized": false, + "device_write_authorized": false, + "transfer_authorized": false, + "observer_build_authorized": false, + "backup_creation_authorized": false + }, + "canonical_state": { + "phase08_status": "READ_ONLY_PREFLIGHT_BLOCKED", + "phase09a_status": "DESIGN_ONLY", + "phase09b_status": "BLOCKED", + "phase09c_classification": "BLOCKED_MULTIPLE_FOUNDATIONAL_CONTRACTS", + "firmware_runtime_behavior": "UNPROVEN", + "stock_identities": "reference_only", + "payload_manager_backup": "HARD_BLOCKER_FOR_INSTALLATION", + "independent_recovery": "UNPROVEN", + "permanent_denylist_sha256": "e9603b0e3792781ad5b511afb22ef61e3d4fd4c5a16bf928f8609193bcd97783", + "permanently_blocked_artifact_sha256": "4be1c17b4964f2b68c39b5145bc4af4619c32512d60269ecf5c39728b390fa63" + }, + "source_commits": { + "hardened_elfldr": "197623058f509eddde18868dafcb92fdcac66464", + "controlled_payload_manager": "e23d94ff91233aa770e2342800c1467875bdef44", + "elfldr_public_base": "699e8bcff03e91e8d6ca6eba281af25c5a58d8c2", + "payload_manager_public_base": "cfbc70f30f419b09bf2b52283f7409e2d3117ee1", + "ps5_payload_sdk_v0_41": "d2e2e585740362976a39fdd5ccf390f199a7bc37", + "lifecycle_source": "fe08300339a13f899fb78ea404ada381a5cba87c" + }, + "source_tree_status": { + "hardened_elfldr": "clean", + "controlled_payload_manager": "clean", + "elfldr_public_base": "clean", + "payload_manager_public_base": "clean", + "ps5_payload_sdk_v0_41": "clean" + }, + "source_evidence": { + "../chimera-elfldr/README.md": "372aeb28dc971b2bd98093a47fdaf77c32f75bbdc3b3d7e8678900744b91eadb", + "../chimera-elfldr/main.c": "876389a26999073994e63ca29926982280d9594a1ee941244205b54f57e2b4d1", + "../chimera-elfldr/bootstrap.c": "5a8072ec0d6db919cb3a81a7028dc91fd8e2c3a0d69b0fa7e84836fa93b45381", + "../chimera-elfldr/socksrv.c": "d642ced3e9b4a296dd15e355050ebe956f53a6dfdaa6ac10109cd067a3bba3d7", + "../chimera-ps5-payload-manager/README.md": "a00277252da46c701326ef66e5ca0d13cadffd50b3c8adf6da89cd1948c97718", + "../chimera-ps5-payload-manager/DEVELOPMENT.md": "0c17bed71b07c9aadf31c47625bbe1ccb42e670e594fb64a1e07aa782d5eec31", + "../chimera-ps5-payload-manager/deploy.sh": "2facdc1ca70db57ba258265c07a8ae1d30d3d2fb429d6718d5d5088300788e52", + "../chimera-ps5-payload-manager/include/pldmgr.h": "8603b8338364cea60ffaf94f985f112fb2ddfda2f44b1ccc9a2bd7a15d1b229a", + "../chimera-ps5-payload-manager/src/http_server.c": "2c8ff2a4bc1028d71e3cc342839584d425b6e7502e55e18e2762cdbf62c59d40", + "../chimera-ps5-payload-manager/src/log_server.c": "659095f43df1bbe8eb24acb165f027edc277af1e60aabb26ba9e3920b233d6f1", + "../chimera-ps5-payload-manager/src/autoload.c": "7051cab3ee1a3e0b9f6498000565eb9e160b9c63efa1771f250e98ec3aa4ae67", + "../chimera-ps5-payload-manager/src/main.c": "b2374e8fb101587b15c8261c58cb4f0573d88c490214365051eb9facd60f6eed", + "../chimera-ps5-payload-manager/src/controlled_manager.c": "042b55b2cece32effed636529249fd18061a2fe3c5e70c7f755f2817cfb84b99", + "../chimera-ps5-payload-manager/src/verified_launcher.c": "066100ca4917c7acc560e2e85666ca136cd7ccfd9094417377048f41106dd56e", + "../chimera-ps5-payload-manager/src/ps5_launcher.c": "29c1a5fd01784a59e88b3698940f120cb03020071bc2b7d74a1da1a51524ef59", + "../chimera-ps5-payload-manager/src/payload_mgr.c": "d67e9ba33edc8ca3a45aae07923d4c4790348b5f8570307e581e16780abafcba", + "../chimera-ps5-payload-manager/src/repository.c": "8ba694ae6d4813573752acd82ce2dbfb715b51d4ec6155f783904d76dd115adb", + "../chimera-ps5-payload-manager/src/sources.c": "a7a4a5cafccfba74902d6ed21ba001e6a9f62d4dff840ac38882d04827162c96", + "work/upstream/elfldr-v0.23/socksrv.c": "500d3c7df7ed5eac1adc925c89344d75c469651b71143716fdb77bfb2209a40c", + "work/upstream/pldmgr-v0.3.1/include/pldmgr.h": "01c693a3248dce7a663dd4ed9c73ce5f3a4443b5f2bd210746d94993dee27b91", + "work/upstream/pldmgr-v0.3.1/src/http_server.c": "35cf5d8f0dd44cf64ceab5e4b0ecc09413c82d7e9946ba9de2ca4b1898631fdd", + "samples/lifecycle_probe/main.c": "1ae7df1fe921ccab2a252f77975d3d441ef7725e34535b024580c0d4a242d766" + }, + "immutable_evidence": { + "manifests/runtime/phase-0.8-read-only-preflight.json": "47d7f452f8799979fe99b3e6d56859f03544112725bf7e5b349eba5ed81b3322", + "manifests/runtime/phase-0.8-remediation.json": "a9dafed8c83722c43709dcf90ea117c21d6f996ff8fe233810ed08aa20cbe071", + "manifests/runtime/phase-0.9-anti-brick-design.json": "39fd7c70cae998d9d74e7caf1ff3c19f9f76f7de2e5a50bc09baede4ba53e9e9", + "manifests/runtime/phase-0.9b-observer.json": "104c4a667ad17f9827fc7276852c6faeef96effb8e3561a5ebc19a62c7d51634", + "manifests/runtime/phase-0.9c-feasibility.json": "84eb737ff3486d0c5d8b5ecf06809f93bc573497da4919595b4c1817fd34247c" + }, + "decisions": { + "new_observer_feasibility": "BLOCKED", + "existing_stack_manual_fact_collection": "PARTIAL", + "existing_stack_single_readback": "BLOCKED_NO_READBACK_PATH", + "existing_stack_repeat_readback": "BLOCKED", + "elfldr_independent_recovery": "PARTIAL", + "payload_manager_independent_recovery": "PARTIAL", + "side_by_side_feasibility": "BLOCKED", + "device_write": "NOT_AUTHORIZED", + "target_execution": "NOT_AUTHORIZED", + "installation": "NOT_AUTHORIZED" + }, + "bootstrap_graph": [ + { + "name": "external_exploit_host", + "commit": "ABSENT_FROM_AUDITED_TREES", + "persistence": "UNPROVEN", + "start_mechanism": "browser_or_host_manual", + "boot_critical": "UNPROVEN", + "dependencies": [], + "failure_impact": "port_9020_bootstrap_unavailable", + "recovery_executor": "external", + "depends_on_self": "UNPROVEN", + "depends_on_elfldr": false, + "depends_on_payload_manager": false, + "recovery_after_reboot": "UNPROVEN", + "evidence_level": "README_DESCRIPTION_ONLY" + }, + { + "name": "rudimentary_exploit_elf_loader_port_9020", + "commit": "ABSENT_FROM_AUDITED_TREES", + "persistence": "UNPROVEN", + "start_mechanism": "external_exploit", + "boot_critical": "UNPROVEN", + "dependencies": ["external_exploit_host"], + "failure_impact": "cannot_bootstrap_elfldr_through_described_route", + "recovery_executor": "external_exploit_host", + "depends_on_self": "UNPROVEN", + "depends_on_elfldr": false, + "depends_on_payload_manager": false, + "recovery_after_reboot": "UNPROVEN", + "evidence_level": "README_DESCRIPTION_ONLY" + }, + { + "name": "hardened_elfldr_bootstrap", + "commit": "197623058f509eddde18868dafcb92fdcac66464", + "persistence": "PROCESS_ONLY_IN_SOURCE", + "start_mechanism": "host_send_to_port_9020", + "boot_critical": "UNPROVEN", + "dependencies": ["rudimentary_exploit_elf_loader_port_9020"], + "failure_impact": "elfldr_service_unavailable", + "recovery_executor": "external_exploit_chain", + "depends_on_self": false, + "depends_on_elfldr": false, + "depends_on_payload_manager": false, + "recovery_after_reboot": "UNPROVEN", + "evidence_level": "PARTIAL" + }, + { + "name": "elfldr_socket_service_port_9021", + "commit": "197623058f509eddde18868dafcb92fdcac66464", + "persistence": "PROCESS_ONLY_IN_SOURCE", + "start_mechanism": "hardened_elfldr_bootstrap", + "boot_critical": "UNPROVEN", + "dependencies": ["hardened_elfldr_bootstrap"], + "failure_impact": "normal_payload_transport_unavailable", + "recovery_executor": "hardened_elfldr_bootstrap", + "depends_on_self": false, + "depends_on_elfldr": false, + "depends_on_payload_manager": false, + "recovery_after_reboot": "UNPROVEN", + "evidence_level": "PARTIAL" + }, + { + "name": "payload_manager", + "commit": "cfbc70f30f419b09bf2b52283f7409e2d3117ee1", + "persistence": "UNPROVEN_CURRENT_DEVICE", + "start_mechanism": "manual_elfldr_or_external_autoloader", + "boot_critical": "UNPROVEN", + "dependencies": ["elfldr_socket_service_port_9021_or_external_autoloader"], + "failure_impact": "manager_listener_and_ui_unavailable", + "recovery_executor": "elfldr_or_external_autoloader", + "depends_on_self": false, + "depends_on_elfldr": true, + "depends_on_payload_manager": false, + "recovery_after_reboot": "UNPROVEN", + "evidence_level": "PARTIAL" + } + ], + "endpoint_defaults": { + "authentication": "NONE", + "writes_server_active_flag": true, + "writes_log_ring": true, + "network_behavior": "HTTP_SOCKET_AND_REQUEST_STATE", + "timeout_behavior": "NO_FILE_TRANSFER_TIMEOUT_CONTRACT", + "retry_behavior": "NO_SERVER_AUTOMATIC_RETRY_PROVEN_CLIENT_RETRY_NOT_ACCEPTABLE", + "maximum_size": "UNPROVEN_UNLESS_HANDLER_SPECIFIC", + "short_read_behavior": "NO_HOST_FILE_READBACK_CONTRACT", + "readback_candidate": false, + "binary_safe_file_response": false, + "exact_returned_byte_count": false, + "partial_result_rejected": false + }, + "endpoint_matrix": [ + {"profile":"full","method":"OPTIONS","endpoint":"*","handler":"http_on_request","source":"../chimera-ps5-payload-manager/src/http_server.c","lines":"193-205","parameters":"none","response":"empty CORS response","open_flags":[],"reads_bytes":false,"writes_bytes":false,"creates_file":false,"removes_file":false,"renames_file":false,"reads_directory":false,"reads_metadata":false,"calculates_hash":false,"modifies_configuration":false,"writes_server_active_flag":false,"writes_autoload_triggered":false,"writes_log_ring":false,"launches_payload":false,"process_or_service_action":false,"network_behavior":"HTTP_PREFLIGHT","timeout_behavior":"library_default_unproven","retry_behavior":"none_in_handler","maximum_size":"zero_body","short_read_behavior":"not_applicable","error_behavior":"MHD_failure","effect_class":"LOW_RISK_TECHNICAL","readback_candidate":false,"observation_candidate":false,"binary_safe_file_response":false,"exact_returned_byte_count":false,"partial_result_rejected":false,"forbidden_reason":"not_a_file_route"}, + {"profile":"full","method":"ANY","endpoint":"/","handler":"static_asset","source":"../chimera-ps5-payload-manager/src/http_server.c","lines":"709-733","parameters":"none","response":"compiled HTML","open_flags":[],"reads_bytes":false,"writes_bytes":false,"creates_file":false,"removes_file":false,"renames_file":false,"reads_directory":false,"reads_metadata":false,"calculates_hash":false,"modifies_configuration":false,"writes_autoload_triggered":false,"launches_payload":false,"process_or_service_action":false,"error_behavior":"404_or_allocation_failure","effect_class":"LOW_VOLATILE","observation_candidate":true,"forbidden_reason":"static_asset_not_file_readback"}, + {"profile":"full","method":"ANY","endpoint":"/index.html","handler":"static_asset","source":"../chimera-ps5-payload-manager/src/http_server.c","lines":"709-733","parameters":"none","response":"compiled HTML","open_flags":[],"reads_bytes":false,"writes_bytes":false,"creates_file":false,"removes_file":false,"renames_file":false,"reads_directory":false,"reads_metadata":false,"calculates_hash":false,"modifies_configuration":false,"writes_autoload_triggered":false,"launches_payload":false,"process_or_service_action":false,"error_behavior":"404_or_allocation_failure","effect_class":"LOW_VOLATILE","observation_candidate":true,"forbidden_reason":"static_asset_not_file_readback"}, + {"profile":"full","method":"ANY","endpoint":"/cache.appcache","handler":"static_asset","source":"../chimera-ps5-payload-manager/src/http_server.c","lines":"709-733","parameters":"none","response":"compiled manifest","open_flags":[],"reads_bytes":false,"writes_bytes":false,"creates_file":false,"removes_file":false,"renames_file":false,"reads_directory":false,"reads_metadata":false,"calculates_hash":false,"modifies_configuration":false,"writes_autoload_triggered":false,"launches_payload":false,"process_or_service_action":false,"error_behavior":"404_or_allocation_failure","effect_class":"LOW_VOLATILE","observation_candidate":true,"forbidden_reason":"static_asset_not_file_readback"}, + {"profile":"full","method":"ANY","endpoint":"/favicon.svg","handler":"static_asset","source":"../chimera-ps5-payload-manager/src/http_server.c","lines":"709-733","parameters":"none","response":"compiled SVG","open_flags":[],"reads_bytes":false,"writes_bytes":false,"creates_file":false,"removes_file":false,"renames_file":false,"reads_directory":false,"reads_metadata":false,"calculates_hash":false,"modifies_configuration":false,"writes_autoload_triggered":false,"launches_payload":false,"process_or_service_action":false,"error_behavior":"404_or_allocation_failure","effect_class":"LOW_VOLATILE","observation_candidate":true,"forbidden_reason":"static_asset_not_file_readback"}, + {"profile":"full","method":"ANY","endpoint":"/icon.png","handler":"static_asset","source":"../chimera-ps5-payload-manager/src/http_server.c","lines":"709-733","parameters":"none","response":"compiled PNG","open_flags":[],"reads_bytes":false,"writes_bytes":false,"creates_file":false,"removes_file":false,"renames_file":false,"reads_directory":false,"reads_metadata":false,"calculates_hash":false,"modifies_configuration":false,"writes_autoload_triggered":false,"launches_payload":false,"process_or_service_action":false,"error_behavior":"404_or_allocation_failure","effect_class":"LOW_VOLATILE","observation_candidate":true,"forbidden_reason":"static_asset_not_file_readback"}, + {"profile":"full","method":"POST","endpoint":"/chimera:upload-controlled","handler":"controlled_upload","source":"../chimera-ps5-payload-manager/src/http_server.c","lines":"212-252,481-537","parameters":"filename_and_identity_headers_body","response":"text status","open_flags":["O_WRONLY","O_CREAT","O_EXCL","O_NOFOLLOW","O_CLOEXEC","verified_reopen_O_RDONLY"],"reads_bytes":true,"writes_bytes":true,"creates_file":true,"removes_file":true,"renames_file":true,"reads_directory":false,"reads_metadata":true,"calculates_hash":true,"modifies_configuration":false,"writes_autoload_triggered":false,"launches_payload":false,"process_or_service_action":false,"error_behavior":"reject_and_remove_partial","effect_class":"ANTI_BRICK_CRITICAL","observation_candidate":false,"forbidden_reason":"device_create_write_rename_remove"}, + {"profile":"full","method":"ANY","endpoint":"/manage:upload","handler":"legacy_upload","source":"../chimera-ps5-payload-manager/src/http_server.c","lines":"254-286,540-585","parameters":"filename_and_body","response":"text status","open_flags":["fopen(wb)"],"reads_bytes":false,"writes_bytes":true,"creates_file":true,"removes_file":true,"renames_file":true,"reads_directory":false,"reads_metadata":false,"calculates_hash":false,"modifies_configuration":false,"writes_autoload_triggered":false,"launches_payload":false,"process_or_service_action":false,"error_behavior":"partial_temp_and_import_errors","effect_class":"ANTI_BRICK_CRITICAL","observation_candidate":false,"forbidden_reason":"device_create_truncate_write_rename"}, + {"profile":"full","method":"POST","endpoint":"/set_config","handler":"set_config","source":"../chimera-ps5-payload-manager/src/http_server.c","lines":"288-295,363-398","parameters":"form_body","response":"text status","open_flags":["fopen(r)","fopen(w)"],"reads_bytes":true,"writes_bytes":true,"creates_file":true,"removes_file":false,"renames_file":false,"reads_directory":false,"reads_metadata":false,"calculates_hash":false,"modifies_configuration":true,"writes_autoload_triggered":false,"launches_payload":false,"process_or_service_action":false,"error_behavior":"validation_or_write_error","effect_class":"ANTI_BRICK_CRITICAL","observation_candidate":false,"forbidden_reason":"configuration_create_truncate_write"}, + {"profile":"full","method":"POST","endpoint":"/repository_push","handler":"repository_push","source":"../chimera-ps5-payload-manager/src/http_server.c","lines":"297-304,401-443","parameters":"JSON_body","response":"text status","open_flags":["fopen(wb)"],"reads_bytes":true,"writes_bytes":true,"creates_file":true,"removes_file":true,"renames_file":true,"reads_directory":false,"reads_metadata":false,"calculates_hash":false,"modifies_configuration":true,"writes_autoload_triggered":false,"launches_payload":false,"process_or_service_action":false,"error_behavior":"remove_temp_on_error","effect_class":"ANTI_BRICK_CRITICAL","observation_candidate":false,"forbidden_reason":"cache_and_configuration_write"}, + {"profile":"full","method":"POST","endpoint":"/sources_set","handler":"sources_set","source":"../chimera-ps5-payload-manager/src/http_server.c","lines":"306-313,446-478","parameters":"JSON_body","response":"text status","open_flags":["fopen(wb)"],"reads_bytes":true,"writes_bytes":true,"creates_file":true,"removes_file":false,"renames_file":false,"reads_directory":false,"reads_metadata":false,"calculates_hash":false,"modifies_configuration":true,"writes_autoload_triggered":false,"launches_payload":false,"process_or_service_action":false,"error_behavior":"parse_or_write_error","effect_class":"ANTI_BRICK_CRITICAL","observation_candidate":false,"forbidden_reason":"configuration_write"}, + {"profile":"full","method":"POST","endpoint":"/repository_install_push","handler":"repository_install_push","source":"../chimera-ps5-payload-manager/src/http_server.c","lines":"315-356,588-636","parameters":"filename_repo_url_body","response":"text status","open_flags":["fopen(wb)"],"reads_bytes":true,"writes_bytes":true,"creates_file":true,"removes_file":true,"renames_file":true,"reads_directory":false,"reads_metadata":true,"calculates_hash":false,"modifies_configuration":false,"writes_autoload_triggered":false,"launches_payload":false,"process_or_service_action":false,"error_behavior":"partial_install_cleanup","effect_class":"ANTI_BRICK_CRITICAL","observation_candidate":false,"forbidden_reason":"install_create_truncate_write_rename"}, + {"profile":"full","method":"ANY","endpoint":"/usb_move_check","handler":"payload_mgr_usb_check","source":"../chimera-ps5-payload-manager/src/http_server.c","lines":"648-674","parameters":"path","response":"JSON_path_size_exists","open_flags":[],"reads_bytes":false,"writes_bytes":false,"creates_file":false,"removes_file":false,"renames_file":false,"reads_directory":true,"reads_metadata":true,"calculates_hash":false,"modifies_configuration":false,"writes_autoload_triggered":false,"launches_payload":false,"process_or_service_action":false,"error_behavior":"JSON_error","effect_class":"LOW_VOLATILE","observation_candidate":true,"forbidden_reason":"USB_only_metadata_not_component_bytes"}, + {"profile":"full","method":"ANY","endpoint":"/usb_move_perform","handler":"payload_mgr_usb_move","source":"../chimera-ps5-payload-manager/src/http_server.c","lines":"676-706","parameters":"path_overwrite_keep","response":"JSON_status","open_flags":["fopen(rb)","fopen(wb)"],"reads_bytes":true,"writes_bytes":true,"creates_file":true,"removes_file":true,"renames_file":false,"reads_directory":true,"reads_metadata":true,"calculates_hash":false,"modifies_configuration":false,"writes_autoload_triggered":false,"launches_payload":false,"process_or_service_action":false,"error_behavior":"copy_error_may_leave_output","effect_class":"ANTI_BRICK_CRITICAL","observation_candidate":false,"forbidden_reason":"device_write_and_optional_source_delete"}, + {"profile":"full","method":"ANY","endpoint":"/manage:check","handler":"payload_mgr_check_existing","source":"../chimera-ps5-payload-manager/src/http_server.c","lines":"735-759","parameters":"filename","response":"JSON_exists","open_flags":[],"reads_bytes":false,"writes_bytes":false,"creates_file":false,"removes_file":false,"renames_file":false,"reads_directory":false,"reads_metadata":true,"calculates_hash":false,"modifies_configuration":false,"writes_autoload_triggered":false,"launches_payload":false,"process_or_service_action":false,"error_behavior":"JSON_error","effect_class":"LOW_VOLATILE","observation_candidate":true,"forbidden_reason":"existence_only_no_size_hash_bytes"}, + {"profile":"full","method":"ANY","endpoint":"/list_payloads","handler":"payload_mgr_list_json","source":"../chimera-ps5-payload-manager/src/http_server.c","lines":"760-767","parameters":"none","response":"JSON_payload_list","open_flags":["fopen(rb)_metadata_sidecars"],"reads_bytes":true,"writes_bytes":false,"creates_file":false,"removes_file":false,"renames_file":false,"reads_directory":true,"reads_metadata":true,"calculates_hash":false,"modifies_configuration":false,"writes_autoload_triggered":false,"launches_payload":false,"process_or_service_action":false,"error_behavior":"bounded_or_empty_JSON","effect_class":"LOW_VOLATILE","observation_candidate":true,"forbidden_reason":"listing_not_binary_file_response"}, + {"profile":"full","method":"ANY","endpoint":"/processes_list","handler":"stats_get_processes_json","source":"../chimera-ps5-payload-manager/src/http_server.c","lines":"768-775","parameters":"none","response":"JSON_process_list","open_flags":[],"reads_bytes":false,"writes_bytes":false,"creates_file":false,"removes_file":false,"renames_file":false,"reads_directory":false,"reads_metadata":false,"calculates_hash":false,"modifies_configuration":false,"writes_autoload_triggered":false,"launches_payload":false,"process_or_service_action":false,"error_behavior":"bounded_JSON","effect_class":"MEDIUM_FUNCTIONAL","observation_candidate":true,"forbidden_reason":"process_metadata_not_file_readback"}, + {"profile":"full","method":"ANY","endpoint":"/process_kill","handler":"kill_process","source":"../chimera-ps5-payload-manager/src/http_server.c","lines":"776-796","parameters":"pid","response":"text status","open_flags":[],"reads_bytes":false,"writes_bytes":false,"creates_file":false,"removes_file":false,"renames_file":false,"reads_directory":false,"reads_metadata":false,"calculates_hash":false,"modifies_configuration":false,"writes_autoload_triggered":false,"launches_payload":false,"process_or_service_action":true,"error_behavior":"signal_error","effect_class":"ANTI_BRICK_CRITICAL","observation_candidate":false,"forbidden_reason":"process_signal"}, + {"profile":"full","method":"ANY","endpoint":"/repository_payloads","handler":"repository_list_json","source":"../chimera-ps5-payload-manager/src/http_server.c","lines":"797-810","parameters":"none","response":"JSON_repository_list","open_flags":["fopen(rb)","possible_fopen(wb)"],"reads_bytes":true,"writes_bytes":true,"creates_file":true,"removes_file":true,"renames_file":true,"reads_directory":false,"reads_metadata":true,"calculates_hash":false,"modifies_configuration":true,"writes_autoload_triggered":false,"launches_payload":false,"process_or_service_action":false,"network_behavior":"MAY_REFRESH_NETWORK_SOURCE","error_behavior":"cache_missing_error","effect_class":"ANTI_BRICK_CRITICAL","observation_candidate":false,"forbidden_reason":"possible_refresh_cache_and_config_write"}, + {"profile":"full","method":"ANY","endpoint":"/repository_refresh","handler":"repository_refresh","source":"../chimera-ps5-payload-manager/src/http_server.c","lines":"811-824","parameters":"none","response":"JSON_repository_list","open_flags":["fopen(wb)","fopen(rb)"],"reads_bytes":true,"writes_bytes":true,"creates_file":true,"removes_file":true,"renames_file":true,"reads_directory":false,"reads_metadata":true,"calculates_hash":false,"modifies_configuration":true,"writes_autoload_triggered":false,"launches_payload":false,"process_or_service_action":false,"network_behavior":"EXTERNAL_REPOSITORY_DOWNLOAD","error_behavior":"remove_temp_and_error_JSON","effect_class":"ANTI_BRICK_CRITICAL","observation_candidate":false,"forbidden_reason":"network_cache_config_write"}, + {"profile":"full","method":"ANY","endpoint":"/repository_install","handler":"repository_install","source":"../chimera-ps5-payload-manager/src/http_server.c","lines":"825-865","parameters":"filename_source_id_detail","response":"JSON_status","open_flags":["fopen(rb)","fopen(wb)"],"reads_bytes":true,"writes_bytes":true,"creates_file":true,"removes_file":true,"renames_file":true,"reads_directory":false,"reads_metadata":true,"calculates_hash":false,"modifies_configuration":false,"writes_autoload_triggered":false,"launches_payload":false,"process_or_service_action":false,"network_behavior":"MAY_DOWNLOAD_EXTERNAL_SOURCE","error_behavior":"install_cleanup_error","effect_class":"ANTI_BRICK_CRITICAL","observation_candidate":false,"forbidden_reason":"install_write_rename"}, + {"profile":"full","method":"ANY","endpoint":"/sources_list","handler":"sources_list_json","source":"../chimera-ps5-payload-manager/src/http_server.c","lines":"866-873","parameters":"none","response":"JSON_sources","open_flags":["fopen(rb)"],"reads_bytes":true,"writes_bytes":false,"creates_file":false,"removes_file":false,"renames_file":false,"reads_directory":false,"reads_metadata":false,"calculates_hash":false,"modifies_configuration":false,"writes_autoload_triggered":false,"launches_payload":false,"process_or_service_action":false,"error_behavior":"default_or_error_JSON","effect_class":"LOW_VOLATILE","observation_candidate":true,"forbidden_reason":"configuration_response_not_component_file"}, + {"profile":"full","method":"ANY","endpoint":"/sources_add","handler":"sources_add","source":"../chimera-ps5-payload-manager/src/http_server.c","lines":"874-900","parameters":"url","response":"JSON_status","open_flags":["fopen(wb)"],"reads_bytes":true,"writes_bytes":true,"creates_file":true,"removes_file":false,"renames_file":false,"reads_directory":false,"reads_metadata":false,"calculates_hash":false,"modifies_configuration":true,"writes_autoload_triggered":false,"launches_payload":false,"process_or_service_action":false,"error_behavior":"validation_or_write_error","effect_class":"ANTI_BRICK_CRITICAL","observation_candidate":false,"forbidden_reason":"configuration_write"}, + {"profile":"full","method":"ANY","endpoint":"/sources_remove","handler":"sources_remove","source":"../chimera-ps5-payload-manager/src/http_server.c","lines":"901-921","parameters":"index","response":"JSON_status","open_flags":["fopen(wb)"],"reads_bytes":true,"writes_bytes":true,"creates_file":true,"removes_file":false,"renames_file":false,"reads_directory":false,"reads_metadata":false,"calculates_hash":false,"modifies_configuration":true,"writes_autoload_triggered":false,"launches_payload":false,"process_or_service_action":false,"error_behavior":"validation_or_write_error","effect_class":"ANTI_BRICK_CRITICAL","observation_candidate":false,"forbidden_reason":"configuration_write"}, + {"profile":"full","method":"ANY","endpoint":"/chimera:load-controlled","handler":"ps5_launch_elf_verified","source":"../chimera-ps5-payload-manager/src/http_server.c","lines":"922-951","parameters":"filename_and_identity_headers","response":"text status","open_flags":["O_RDONLY","O_NOFOLLOW","O_CLOEXEC"],"reads_bytes":true,"writes_bytes":false,"creates_file":false,"removes_file":false,"renames_file":false,"reads_directory":true,"reads_metadata":true,"calculates_hash":true,"modifies_configuration":false,"writes_autoload_triggered":false,"launches_payload":true,"process_or_service_action":true,"network_behavior":"LOOPBACK_SEND_TO_ELFLDR","error_behavior":"reject_without_retry","effect_class":"ANTI_BRICK_CRITICAL","observation_candidate":false,"forbidden_reason":"payload_launch"}, + {"profile":"full","method":"ANY","endpoint":"/loadpayload:","handler":"ps5_launch_elf","source":"../chimera-ps5-payload-manager/src/http_server.c","lines":"952-983","parameters":"path_suffix","response":"text status","open_flags":["O_RDONLY"],"reads_bytes":true,"writes_bytes":false,"creates_file":false,"removes_file":false,"renames_file":false,"reads_directory":true,"reads_metadata":true,"calculates_hash":false,"modifies_configuration":false,"writes_autoload_triggered":false,"launches_payload":true,"process_or_service_action":true,"network_behavior":"LOOPBACK_SEND_TO_ELFLDR","error_behavior":"launch_error","effect_class":"ANTI_BRICK_CRITICAL","observation_candidate":false,"forbidden_reason":"payload_launch"}, + {"profile":"full","method":"ANY","endpoint":"/manage:delete","handler":"payload_mgr_delete_payload_file","source":"../chimera-ps5-payload-manager/src/http_server.c","lines":"984-1008","parameters":"filename","response":"text status","open_flags":["fopen(w)_possible_autoload"],"reads_bytes":true,"writes_bytes":true,"creates_file":false,"removes_file":true,"renames_file":false,"reads_directory":true,"reads_metadata":true,"calculates_hash":false,"modifies_configuration":true,"writes_autoload_triggered":false,"launches_payload":false,"process_or_service_action":false,"error_behavior":"delete_error","effect_class":"ANTI_BRICK_CRITICAL","observation_candidate":false,"forbidden_reason":"unlink_and_possible_autoload_write"}, + {"profile":"full","method":"ANY","endpoint":"/shutdown","handler":"http_keep_running_zero","source":"../chimera-ps5-payload-manager/src/http_server.c","lines":"1009-1015","parameters":"none","response":"text status","open_flags":[],"reads_bytes":false,"writes_bytes":false,"creates_file":false,"removes_file":false,"renames_file":false,"reads_directory":false,"reads_metadata":false,"calculates_hash":false,"modifies_configuration":false,"writes_autoload_triggered":false,"launches_payload":false,"process_or_service_action":true,"error_behavior":"service_shutdown","effect_class":"ANTI_BRICK_CRITICAL","observation_candidate":false,"forbidden_reason":"service_state_change"}, + {"profile":"full","method":"ANY","endpoint":"/log","handler":"log_build_json","source":"../chimera-ps5-payload-manager/src/http_server.c","lines":"1016-1024","parameters":"none","response":"JSON_log_ring","open_flags":[],"reads_bytes":false,"writes_bytes":false,"creates_file":false,"removes_file":false,"renames_file":false,"reads_directory":false,"reads_metadata":false,"calculates_hash":false,"modifies_configuration":false,"writes_autoload_triggered":false,"launches_payload":false,"process_or_service_action":false,"error_behavior":"allocation_failure","effect_class":"LOW_VOLATILE","observation_candidate":true,"forbidden_reason":"log_not_component_file"}, + {"profile":"full","method":"ANY","endpoint":"/version","handler":"version","source":"../chimera-ps5-payload-manager/src/http_server.c","lines":"1025-1028","parameters":"none","response":"text version","open_flags":[],"reads_bytes":false,"writes_bytes":false,"creates_file":false,"removes_file":false,"renames_file":false,"reads_directory":false,"reads_metadata":false,"calculates_hash":false,"modifies_configuration":false,"writes_autoload_triggered":false,"launches_payload":false,"process_or_service_action":false,"error_behavior":"MHD_failure","effect_class":"LOW_VOLATILE","observation_candidate":true,"forbidden_reason":"version_not_component_file"}, + {"profile":"full","method":"ANY","endpoint":"/getip","handler":"pldmgr_get_local_ip","source":"../chimera-ps5-payload-manager/src/http_server.c","lines":"1029-1036","parameters":"none","response":"text IP","open_flags":[],"reads_bytes":false,"writes_bytes":false,"creates_file":false,"removes_file":false,"renames_file":false,"reads_directory":false,"reads_metadata":false,"calculates_hash":false,"modifies_configuration":false,"writes_autoload_triggered":false,"launches_payload":false,"process_or_service_action":false,"network_behavior":"INTERFACE_QUERY","error_behavior":"returns_0.0.0.0","effect_class":"LOW_VOLATILE","observation_candidate":true,"forbidden_reason":"network_metadata_not_file"}, + {"profile":"full","method":"ANY","endpoint":"/autoload_status","handler":"pldmgr_autoload_get_status","source":"../chimera-ps5-payload-manager/src/http_server.c","lines":"1037-1091","parameters":"none","response":"JSON_autoload_status","open_flags":["fopen(r)"],"reads_bytes":true,"writes_bytes":false,"creates_file":false,"removes_file":false,"renames_file":false,"reads_directory":false,"reads_metadata":false,"calculates_hash":false,"modifies_configuration":false,"writes_autoload_triggered":true,"launches_payload":false,"process_or_service_action":false,"error_behavior":"empty_list_or_allocation_failure","effect_class":"HIGH_FUNCTIONAL","observation_candidate":false,"forbidden_reason":"writes_autoload_triggered_excluded_windows_1_2"}, + {"profile":"full","method":"ANY","endpoint":"/autoload_clear","handler":"pldmgr_autoload_reset","source":"../chimera-ps5-payload-manager/src/http_server.c","lines":"1092-1097","parameters":"none","response":"text status","open_flags":[],"reads_bytes":false,"writes_bytes":false,"creates_file":false,"removes_file":false,"renames_file":false,"reads_directory":false,"reads_metadata":false,"calculates_hash":false,"modifies_configuration":false,"writes_autoload_triggered":true,"launches_payload":false,"process_or_service_action":false,"error_behavior":"reset_can_be_ignored_while_timer_active","effect_class":"HIGH_FUNCTIONAL","observation_candidate":false,"forbidden_reason":"autoload_process_state_mutation"}, + {"profile":"full","method":"ANY","endpoint":"/abort","handler":"pldmgr_autoload_abort","source":"../chimera-ps5-payload-manager/src/http_server.c","lines":"1098-1103","parameters":"none","response":"text status","open_flags":[],"reads_bytes":false,"writes_bytes":false,"creates_file":false,"removes_file":false,"renames_file":false,"reads_directory":false,"reads_metadata":false,"calculates_hash":false,"modifies_configuration":false,"writes_autoload_triggered":false,"launches_payload":false,"process_or_service_action":true,"error_behavior":"state_change_only","effect_class":"HIGH_FUNCTIONAL","observation_candidate":false,"forbidden_reason":"autoload_worker_state_change"}, + {"profile":"full","method":"UNREACHABLE","endpoint":"/autoload_status#duplicate","handler":"shadowed_duplicate","source":"../chimera-ps5-payload-manager/src/http_server.c","lines":"1104-1111","parameters":"none","response":"unreachable JSON","open_flags":[],"reads_bytes":false,"writes_bytes":false,"creates_file":false,"removes_file":false,"renames_file":false,"reads_directory":false,"reads_metadata":false,"calculates_hash":false,"modifies_configuration":false,"writes_server_active_flag":true,"writes_autoload_triggered":false,"launches_payload":false,"process_or_service_action":false,"error_behavior":"unreachable","effect_class":"UNPROVEN","observation_candidate":false,"forbidden_reason":"shadowed_by_prior_identical_endpoint"}, + {"profile":"full","method":"ANY","endpoint":"/get_config","handler":"config_read","source":"../chimera-ps5-payload-manager/src/http_server.c","lines":"1112-1155","parameters":"none","response":"JSON_configuration","open_flags":["fopen(r)"],"reads_bytes":true,"writes_bytes":false,"creates_file":false,"removes_file":false,"renames_file":false,"reads_directory":false,"reads_metadata":false,"calculates_hash":false,"modifies_configuration":false,"writes_autoload_triggered":false,"launches_payload":false,"process_or_service_action":false,"error_behavior":"defaults_or_allocation_failure","effect_class":"LOW_VOLATILE","observation_candidate":true,"forbidden_reason":"configuration_not_component_file"}, + {"profile":"full","method":"ANY","endpoint":"/events","handler":"log_stream_callback","source":"../chimera-ps5-payload-manager/src/http_server.c","lines":"1156-1165","parameters":"none","response":"SSE_log_stream","open_flags":[],"reads_bytes":false,"writes_bytes":false,"creates_file":false,"removes_file":false,"renames_file":false,"reads_directory":false,"reads_metadata":false,"calculates_hash":false,"modifies_configuration":false,"writes_autoload_triggered":false,"launches_payload":false,"process_or_service_action":false,"timeout_behavior":"INDEFINITE_STREAM_LIBRARY_DEPENDENT","error_behavior":"disconnect_cleanup","effect_class":"LOW_VOLATILE","observation_candidate":true,"forbidden_reason":"log_stream_not_file_readback"}, + {"profile":"minimal_controlled","method":"POST","endpoint":"/chimera/upload","handler":"handle_upload","source":"../chimera-ps5-payload-manager/src/controlled_manager.c","lines":"181-303,322-324","parameters":"filename_and_identity_headers_body","response":"text status","authentication":"IDENTITY_METADATA_NOT_CLIENT_AUTH","open_flags":["O_WRONLY","O_CREAT","O_EXCL","O_NOFOLLOW","O_CLOEXEC","verified_reopen_O_RDONLY"],"reads_bytes":true,"writes_bytes":true,"creates_file":true,"removes_file":true,"renames_file":true,"reads_directory":false,"reads_metadata":true,"calculates_hash":true,"modifies_configuration":false,"writes_server_active_flag":false,"writes_autoload_triggered":false,"writes_log_ring":false,"launches_payload":false,"process_or_service_action":false,"network_behavior":"HTTP_SOCKET","error_behavior":"remove_partial_and_reject","effect_class":"ANTI_BRICK_CRITICAL","observation_candidate":false,"forbidden_reason":"device_create_write_rename_remove"}, + {"profile":"minimal_controlled","method":"POST","endpoint":"/chimera/launch","handler":"handle_launch","source":"../chimera-ps5-payload-manager/src/controlled_manager.c","lines":"305-309,324-326","parameters":"filename_and_identity_headers","response":"text status","authentication":"IDENTITY_METADATA_NOT_CLIENT_AUTH","open_flags":["O_RDONLY","O_NOFOLLOW","O_CLOEXEC"],"reads_bytes":true,"writes_bytes":false,"creates_file":false,"removes_file":false,"renames_file":false,"reads_directory":false,"reads_metadata":true,"calculates_hash":true,"modifies_configuration":false,"writes_server_active_flag":false,"writes_autoload_triggered":false,"writes_log_ring":false,"launches_payload":true,"process_or_service_action":true,"network_behavior":"HTTP_AND_LOOPBACK_ELFLDR_SEND","error_behavior":"reject_without_retry","effect_class":"ANTI_BRICK_CRITICAL","observation_candidate":false,"forbidden_reason":"payload_launch"}, + {"profile":"minimal_controlled","method":"DELETE","endpoint":"/chimera/artifact","handler":"handle_delete","source":"../chimera-ps5-payload-manager/src/controlled_manager.c","lines":"311-327","parameters":"filename","response":"text status","authentication":"NONE","open_flags":[],"reads_bytes":false,"writes_bytes":false,"creates_file":false,"removes_file":true,"renames_file":false,"reads_directory":false,"reads_metadata":false,"calculates_hash":false,"modifies_configuration":false,"writes_server_active_flag":false,"writes_autoload_triggered":false,"writes_log_ring":false,"launches_payload":false,"process_or_service_action":false,"network_behavior":"HTTP_SOCKET","error_behavior":"unlink_status","effect_class":"ANTI_BRICK_CRITICAL","observation_candidate":false,"forbidden_reason":"device_unlink"} + ], + "flag_semantics": { + "server_active_flag": { + "initial_value": 0, + "writers": ["log_server_set_active"], + "readers": ["pldmgr_server_is_active", "autoload_worker_notification_suppression"], + "reset_path": "NONE_IN_PROCESS", + "lifetime": "PROCESS_LOCAL", + "thread_usage": "volatile_shared_flag", + "filesystem_effect": false, + "autoload_enable_effect": false, + "payload_launch_effect": false, + "shutdown_effect": false, + "disconnect_reset": false, + "process_exit_persistence": false, + "reboot_persistence": false, + "hardware_process_lifetime": "UNPROVEN", + "classification": "LOW_VOLATILE", + "fully_documented": true, + "route_without_reset_semantics_is_partial": true + }, + "autoload_triggered": { + "initial_value": 0, + "writers": ["pldmgr_autoload_get_status_sets_1", "pldmgr_autoload_reset_sets_0"], + "readers": ["autoload_worker_wait_logic"], + "reset_path": "explicit_reset_or_main_resume", + "lifetime": "PROCESS_LOCAL", + "can_change_launch_timing": true, + "disconnect_reset": false, + "process_exit_persistence": false, + "reboot_persistence": false, + "classification": "HIGH_FUNCTIONAL", + "excluded_windows": [1, 2] + } + }, + "readback_routes": [ + {"order":1,"route":"payload_manager_download","exists":false,"classification":"ABSENT","request_framing":"ABSENT","response_framing":"ABSENT","binary_safe":false,"maximum_length":"ABSENT","chunks":"ABSENT","ordering":"ABSENT","eof":"ABSENT","short_read":"ABSENT","timeout":"ABSENT","automatic_reconnect":false,"automatic_retry":false,"source_open_flags":[],"symlink_behavior":"ABSENT","object_stability":"ABSENT","metadata_before":"ABSENT","metadata_after":"ABSENT","exact_received_byte_count":false,"host_output":"ABSENT","local_reopen":false,"local_sha256":false,"source_can_change":"UNPROVEN","ps5_file_written":false,"functional_flags":["server_active_if_request_existed"],"anti_brick_risk":"NO_ROUTE","usable_once":false,"usable_twice":false}, + {"order":2,"route":"payload_manager_internal_file_read","exists":true,"classification":"REJECTED_NOT_RETURNED_TO_HOST","request_framing":"HTTP_ROUTE_SPECIFIC","response_framing":"TEXT_OR_JSON_ONLY","binary_safe":false,"maximum_length":"NO_FILE_RESPONSE","chunks":"NO_FILE_RESPONSE","ordering":"NO_FILE_RESPONSE","eof":"NO_FILE_RESPONSE","short_read":"NO_FILE_RESPONSE","timeout":"UNPROVEN","automatic_reconnect":false,"automatic_retry":false,"source_open_flags":["O_RDONLY","fopen(rb)"],"symlink_behavior":"MIXED","object_stability":"CONTROLLED_LAUNCH_ONLY","metadata_before":"INTERNAL_ONLY","metadata_after":"INTERNAL_ONLY","exact_received_byte_count":false,"host_output":"NO_FILE_BYTES","local_reopen":false,"local_sha256":false,"source_can_change":"UNPROVEN","ps5_file_written":false,"functional_flags":["server_active_flag","log_ring"],"anti_brick_risk":"LOW_VOLATILE_IF_NONMUTATING_ROUTE","usable_once":false,"usable_twice":false}, + {"order":3,"route":"elfldr_response_or_host_pipe","exists":true,"classification":"REJECTED_FIXED_STATUS_OR_REQUIRES_PAYLOAD","request_framing":"ELFLDR_PROTOCOL","response_framing":"FIXED_TEXT_ONLY_CONTROLLED","binary_safe":false,"maximum_length":"FIXED_STATUS_ONLY","chunks":"NO_FILE_RESPONSE","ordering":"NO_FILE_RESPONSE","eof":"SOCKET_CLOSE_NOT_FILE_EOF","short_read":"NO_FILE_RESPONSE","timeout":"WATCHDOG_FOR_CONTROLLED_PAYLOAD_NOT_READBACK","automatic_reconnect":false,"automatic_retry":false,"source_open_flags":[],"symlink_behavior":"NOT_APPLICABLE","object_stability":"NOT_APPLICABLE","metadata_before":"ABSENT","metadata_after":"ABSENT","exact_received_byte_count":false,"host_output":"FIXED_LOADER_STATUS","local_reopen":false,"local_sha256":false,"source_can_change":"UNPROVEN","ps5_file_written":false,"functional_flags":["loader_connection_state"],"anti_brick_risk":"PAYLOAD_EXECUTION_REQUIRED_FOR_LEGACY_STDIO","usable_once":false,"usable_twice":false}, + {"order":4,"route":"exploit_host","exists":"DESCRIPTION_ONLY","classification":"REJECTED_HOST_TO_PS5_ONLY_SOURCE_ABSENT","request_framing":"HOST_SEND","response_framing":"UNPROVEN","binary_safe":false,"maximum_length":"UNPROVEN","chunks":"UNPROVEN","ordering":"UNPROVEN","eof":"UNPROVEN","short_read":"UNPROVEN","timeout":"UNPROVEN","automatic_reconnect":"UNPROVEN","automatic_retry":"UNPROVEN","source_open_flags":[],"symlink_behavior":"UNPROVEN","object_stability":"UNPROVEN","metadata_before":"ABSENT","metadata_after":"ABSENT","exact_received_byte_count":false,"host_output":"ABSENT","local_reopen":false,"local_sha256":false,"source_can_change":"UNPROVEN","ps5_file_written":"UNPROVEN","functional_flags":[],"anti_brick_risk":"UNPROVEN_IMPLEMENTATION","usable_once":false,"usable_twice":false}, + {"order":5,"route":"browser_or_usb","exists":true,"classification":"REJECTED_REVERSE_DIRECTION_OR_MUTATING","request_framing":"UPLOAD_OR_USB_MOVE","response_framing":"STATUS_ONLY","binary_safe":false,"maximum_length":"NO_FILE_RESPONSE","chunks":"NO_FILE_RESPONSE","ordering":"NO_FILE_RESPONSE","eof":"NO_FILE_RESPONSE","short_read":"NO_FILE_RESPONSE","timeout":"UNPROVEN","automatic_reconnect":"CLIENT_DEPENDENT","automatic_retry":"CLIENT_UI_HAS_RETRIES","source_open_flags":["fopen(rb)_USB","fopen(wb)_DEVICE"],"symlink_behavior":"UNPROVEN","object_stability":"UNPROVEN","metadata_before":"PARTIAL_USB_STAT","metadata_after":"UNPROVEN","exact_received_byte_count":false,"host_output":"ABSENT","local_reopen":false,"local_sha256":false,"source_can_change":"UNPROVEN","ps5_file_written":true,"functional_flags":["server_active_flag","log_ring"],"anti_brick_risk":"MUTATING","usable_once":false,"usable_twice":false}, + {"order":6,"route":"repository_tools","exists":true,"classification":"REJECTED_HOST_TO_PS5_ONLY","request_framing":"NETCAT_SEND","response_framing":"FIXED_LOADER_STATUS","binary_safe":false,"maximum_length":"NO_FILE_RESPONSE","chunks":"NO_FILE_RESPONSE","ordering":"NO_FILE_RESPONSE","eof":"NO_FILE_RESPONSE","short_read":"NO_FILE_RESPONSE","timeout":"UNPROVEN","automatic_reconnect":false,"automatic_retry":false,"source_open_flags":[],"symlink_behavior":"NOT_APPLICABLE","object_stability":"NOT_APPLICABLE","metadata_before":"ABSENT","metadata_after":"ABSENT","exact_received_byte_count":false,"host_output":"ABSENT","local_reopen":false,"local_sha256":false,"source_can_change":"UNPROVEN","ps5_file_written":false,"functional_flags":["payload_launch"],"anti_brick_risk":"TARGET_EXECUTION","usable_once":false,"usable_twice":false}, + {"order":7,"route":"no_readback_path","exists":false,"classification":"SELECTED_FINAL_RESULT","request_framing":"ABSENT","response_framing":"ABSENT","binary_safe":false,"maximum_length":"ABSENT","chunks":"ABSENT","ordering":"ABSENT","eof":"ABSENT","short_read":"ABSENT","timeout":"ABSENT","automatic_reconnect":false,"automatic_retry":false,"source_open_flags":[],"symlink_behavior":"ABSENT","object_stability":"ABSENT","metadata_before":"ABSENT","metadata_after":"ABSENT","exact_received_byte_count":false,"host_output":"ABSENT","local_reopen":false,"local_sha256":false,"source_can_change":"UNPROVEN","ps5_file_written":false,"functional_flags":[],"anti_brick_risk":"NO_ROUTE","usable_once":false,"usable_twice":false} + ], + "path_inventory": [ + {"path":"/data/pldmgr","source":"../chimera-ps5-payload-manager/include/pldmgr.h:8","commit":"e23d94ff91233aa770e2342800c1467875bdef44","component":"payload_manager","kind":"configured_device_default","install_target":false,"rollback_target":false,"autoload_target":false,"staging_target":false,"live":false,"evidence":"SOURCE_LITERAL_ONLY","conflict":true}, + {"path":"/data/pldmgr/payloads","source":"../chimera-ps5-payload-manager/include/pldmgr.h:12","commit":"e23d94ff91233aa770e2342800c1467875bdef44","component":"payload_storage","kind":"configured_device_default","install_target":true,"rollback_target":false,"autoload_target":false,"staging_target":false,"live":false,"evidence":"SOURCE_LITERAL_ONLY","conflict":true}, + {"path":"/data/pldmgr/autoload.txt","source":"../chimera-ps5-payload-manager/include/pldmgr.h:9","commit":"e23d94ff91233aa770e2342800c1467875bdef44","component":"payload_manager_config","kind":"configured_device_default","install_target":false,"rollback_target":false,"autoload_target":true,"staging_target":false,"live":false,"evidence":"SOURCE_LITERAL_ONLY","conflict":true}, + {"path":"/data/pldmgr/pldmgr_config.txt","source":"../chimera-ps5-payload-manager/include/pldmgr.h:10","commit":"e23d94ff91233aa770e2342800c1467875bdef44","component":"payload_manager_config","kind":"configured_device_default","install_target":false,"rollback_target":false,"autoload_target":false,"staging_target":false,"live":false,"evidence":"SOURCE_LITERAL_ONLY","conflict":true}, + {"path":"/data/pldmgr/payloads/chimera-controlled","source":"../chimera-ps5-payload-manager/src/controlled_manager.c","commit":"e23d94ff91233aa770e2342800c1467875bdef44","component":"controlled_payload_manager","kind":"offline_candidate_staging","install_target":false,"rollback_target":false,"autoload_target":false,"staging_target":true,"live":false,"evidence":"NOT_INSTALLED","conflict":true}, + {"path":"/data/pldmgr/payloads/elfldr/elfldr_v0.23.elf","source":"manifests/runtime/phase-0.6-loader-runtime-audit.json","commit":"historical","component":"elfldr","kind":"reference_path","install_target":false,"rollback_target":false,"autoload_target":false,"staging_target":false,"live":false,"evidence":"REFERENCE_ONLY","conflict":true}, + {"path":"/data/pldmgr/payloads/pldmgr/pldmgr_v0.3.1.elf","source":"manifests/runtime/phase-0.6-loader-runtime-audit.json","commit":"historical","component":"payload_manager","kind":"reference_path","install_target":false,"rollback_target":false,"autoload_target":false,"staging_target":false,"live":false,"evidence":"REFERENCE_ONLY","conflict":true}, + {"path":"/data/payload.elf","source":"../chimera-elfldr/README.md","commit":"197623058f509eddde18868dafcb92fdcac66464","component":"generic_payload","kind":"documentation_example","install_target":false,"rollback_target":false,"autoload_target":false,"staging_target":false,"live":false,"evidence":"EXAMPLE_ONLY","conflict":true}, + {"path":"/user/temp/payload_.self","source":"../chimera-elfldr/selfldr.c","commit":"197623058f509eddde18868dafcb92fdcac66464","component":"elfldr_temporary_self","kind":"runtime_template","install_target":false,"rollback_target":false,"autoload_target":false,"staging_target":false,"live":false,"evidence":"SOURCE_TEMPLATE","conflict":false} + ], + "path_classification": "PATH_CONFLICT", + "runtime_observed_live_paths": [], + "host_backup_contract": { + "statuses": ["TRANSFER_NOT_STARTED","TRANSFER_INCOMPLETE","HOST_COPY_RECEIVED","HOST_COPY_REOPENED","HOST_COPY_HASHED","SECOND_COPY_CREATED","SECOND_COPY_REOPENED","SECOND_COPY_HASHED","COPIES_MATCH","SOURCE_MAPPING_PARTIAL","SOURCE_MAPPING_VERIFIED","INVALID"], + "one_component_per_session": true, + "new_exclusive_local_output": true, + "overwrite_existing_output": false, + "binary_mode": true, + "exact_received_byte_count_required": true, + "partial_transfer_status": "INVALID", + "automatic_resume": false, + "automatic_retry": false, + "close_reopen_before_hash": true, + "sha256_required": true, + "size_required": true, + "second_independent_connection": true, + "second_new_output": true, + "compare_size": true, + "compare_sha256": true, + "compare_every_byte": true, + "capture_raw_protocol_metadata": true, + "capture_literal_source_path": true, + "capture_device_and_session": true, + "capture_client_commit": true, + "contains_device_write_command": false, + "off_device_backup_valid_requires_copies_match": true, + "recovery_proven_allowed": false + }, + "recovery_dependencies": { + "elfldr": { + "classification": "PARTIAL", + "detailed": ["UNPROVEN","ABSENT"], + "write_mechanism": "ABSENT", + "executor": "external_port_9020_chain_description_only", + "works_if_elfldr_broken": "PARTIAL", + "works_if_payload_manager_broken": "PARTIAL", + "works_after_reboot": "UNPROVEN", + "works_without_autoload": "PARTIAL", + "works_without_normal_listener": "PARTIAL", + "exact_restore_target": "UNPROVEN", + "write_then_verify": false, + "old_file_remains_intact": "UNPROVEN", + "power_loss_safe": false, + "second_route": "ABSENT" + }, + "payload_manager": { + "classification": "PARTIAL", + "detailed": ["CROSS_DEPENDENT","SELF_DEPENDENT","POWER_LOSS_UNSAFE"], + "write_mechanism": "NO_INDEPENDENT_RESTORE_CONTRACT", + "executor": "elfldr_for_manual_candidate_launch_description_only", + "works_if_elfldr_broken": false, + "works_if_payload_manager_broken": "PARTIAL", + "works_after_reboot": "UNPROVEN", + "works_without_autoload": "PARTIAL", + "works_without_normal_listener": "PARTIAL", + "exact_restore_target": "UNPROVEN", + "write_then_verify": false, + "old_file_remains_intact": "UNPROVEN", + "power_loss_safe": false, + "second_route": "ABSENT" + } + }, + "side_by_side": { + "classification": "BLOCKED", + "existing_launch_mechanism": "host_to_elfldr_send", + "candidate_path": "UNPROVEN", + "candidate_path_source_bound": false, + "transfer_required": true, + "installation_required": "UNPROVEN", + "configuration_change_required": "UNPROVEN", + "autoload_required": false, + "live_file_remains_intact": "UNPROVEN", + "crash_impact": "UNPROVEN", + "hang_impact": "UNPROVEN", + "reboot_impact": "UNPROVEN", + "timeout": "CONTROLLED_ELFLDR_ONLY_NOT_FULL_PROCESS_PROOF", + "automatic_retry": false, + "cleanup": "UNPROVEN", + "independent_recovery": "UNPROVEN", + "listener_overlap": true, + "process_overlap": true, + "global_state": "UNPROVEN", + "grants_installation_authorization": false, + "grants_execution_authorization": false + }, + "operational_windows": [ + {"window":1,"name":"MANUAL_FACT_COLLECTION","status":"PARTIAL","device_network_request":false,"file_transfer":false,"device_write":false,"payload_launch":false,"autoload_status_route":false,"automatic_retry":false,"separate_authorization_required":true}, + {"window":2,"name":"SINGLE_OFF_DEVICE_READBACK","status":"BLOCKED_NO_READBACK_PATH","device_network_request":"NOT_AUTHORIZED","file_transfer":false,"device_write":false,"payload_launch":false,"autoload_status_route":false,"automatic_retry":false,"separate_authorization_required":true}, + {"window":3,"name":"INDEPENDENT_REPEAT_READBACK","status":"BLOCKED","device_network_request":"NOT_AUTHORIZED","file_transfer":false,"device_write":false,"payload_launch":false,"autoload_status_route":false,"automatic_retry":false,"automatic_third_attempt":false,"separate_authorization_required":true}, + {"window":4,"name":"SECOND_COMPONENT","status":"BLOCKED","component_session_separate":true,"device_network_request":"NOT_AUTHORIZED","file_transfer":false,"device_write":false,"payload_launch":false,"autoload_status_route":false,"automatic_retry":false,"separate_authorization_required":true}, + {"window":5,"name":"RECOVERY_CONTRACT_REVIEW","status":"OFFLINE_ONLY_UNPROVEN","device_network_request":false,"file_transfer":false,"device_write":false,"payload_launch":false,"autoload_status_route":false,"automatic_retry":false,"separate_authorization_required":true}, + {"window":6,"name":"NON_LIVE_STAGING","status":"BLOCKED_OUTSIDE_PHASE","device_network_request":false,"file_transfer":false,"device_write":false,"payload_launch":false,"autoload_status_route":false,"automatic_retry":false,"separate_authorization_required":true}, + {"window":7,"name":"SIDE_BY_SIDE_EXECUTION","status":"BLOCKED_OUTSIDE_PHASE","device_network_request":false,"file_transfer":false,"device_write":false,"payload_launch":false,"autoload_status_route":false,"automatic_retry":false,"separate_authorization_required":true} + ], + "acceptable_technical_effects_only_with_future_explicit_consent": [ + "server_active_flag", + "log_ring", + "stdout_logging", + "request_counters", + "connection_state", + "temporary_manager_state", + "temporary_buffers", + "sockets", + "atime", + "audit_record", + "cache", + "scheduler_activity", + "process_accounting", + "network_counters", + "heap_allocation" + ], + "hard_stops": [ + "NO_EXISTING_PS5_TO_HOST_FILE_READBACK_ROUTE", + "NO_RUNTIME_OBSERVED_LIVE_COMPONENT_PATH", + "CURRENT_INSTALLED_IDENTITIES_UNPROVEN", + "EXTERNAL_EXPLOIT_EXECUTOR_SOURCE_ABSENT", + "INDEPENDENT_BYTE_EXACT_RESTORE_ABSENT", + "POWER_LOSS_SAFE_RESTORE_UNPROVEN", + "PAYLOAD_MANAGER_BACKUP_HARD_BLOCKER_FOR_INSTALLATION", + "INDEPENDENT_RECOVERY_HARD_BLOCKER_FOR_EVERY_DEVICE_WRITE" + ], + "open_hardware_questions": [ + "current_live_elfldr_path_and_identity", + "current_live_payload_manager_path_and_identity", + "current_boot_and_autoload_configuration", + "current_exploit_chain_repeatability", + "post_reboot_component_persistence", + "actual_listener_and_process_state", + "actual_server_active_process_lifetime", + "independent_restore_executor", + "power_loss_recovery_behavior" + ], + "final_decision": { + "classification": "BLOCKED", + "reason": "NO_EXISTING_PS5_TO_HOST_FILE_READBACK_ROUTE; CURRENT_LIVE_PATH_MAPPING_AND_INDEPENDENT_RECOVERY_REMAIN_UNPROVEN", + "hardware_evidence_claimed": false, + "device_action_authorized": false + } +} diff --git a/manifests/runtime/phase-0.9d-existing-stack-readback.schema.json b/manifests/runtime/phase-0.9d-existing-stack-readback.schema.json new file mode 100644 index 0000000..b4fa0f6 --- /dev/null +++ b/manifests/runtime/phase-0.9d-existing-stack-readback.schema.json @@ -0,0 +1,155 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://chimera.invalid/schema/phase-0.9d-existing-stack-readback-v1.json", + "title": "Chimera GFX Phase-0.9D existing-stack readback audit", + "type": "object", + "additionalProperties": false, + "required": [ + "schema_version", + "phase", + "date", + "status", + "baseline_commit", + "branch", + "scope", + "actions", + "authorization", + "canonical_state", + "source_commits", + "source_tree_status", + "source_evidence", + "immutable_evidence", + "decisions", + "bootstrap_graph", + "endpoint_defaults", + "endpoint_matrix", + "flag_semantics", + "readback_routes", + "path_inventory", + "path_classification", + "runtime_observed_live_paths", + "host_backup_contract", + "recovery_dependencies", + "side_by_side", + "operational_windows", + "acceptable_technical_effects_only_with_future_explicit_consent", + "hard_stops", + "open_hardware_questions", + "final_decision" + ], + "properties": { + "schema_version": {"const": 1}, + "phase": {"const": "PHASE_0_9D_EXISTING_STACK_READBACK"}, + "date": {"type": "string"}, + "status": {"const": "DESIGN_ONLY"}, + "baseline_commit": {"const": "e0e68829ab76977c845e7106ef93e6c01fbc966e"}, + "branch": {"const": "codex/chimera-gfx-phase09d-existing-stack-readback"}, + "scope": {"const": "offline_existing_stack_readback_and_recovery_feasibility"}, + "actions": { + "type": "object", + "additionalProperties": {"const": false}, + "minProperties": 10 + }, + "authorization": { + "type": "object", + "additionalProperties": {"const": false}, + "minProperties": 9 + }, + "canonical_state": {"type": "object"}, + "source_commits": {"type": "object", "minProperties": 6}, + "source_tree_status": { + "type": "object", + "additionalProperties": {"const": "clean"}, + "minProperties": 5 + }, + "source_evidence": {"type": "object", "minProperties": 20}, + "immutable_evidence": {"type": "object", "minProperties": 5}, + "decisions": { + "type": "object", + "required": [ + "new_observer_feasibility", + "existing_stack_manual_fact_collection", + "existing_stack_single_readback", + "existing_stack_repeat_readback", + "elfldr_independent_recovery", + "payload_manager_independent_recovery", + "side_by_side_feasibility", + "device_write", + "target_execution", + "installation" + ], + "properties": { + "new_observer_feasibility": {"const": "BLOCKED"}, + "existing_stack_manual_fact_collection": {"enum": ["READY_FOR_REVIEW", "PARTIAL", "BLOCKED"]}, + "existing_stack_single_readback": {"enum": ["READY_FOR_REVIEW", "PARTIAL", "BLOCKED_NO_READBACK_PATH", "BLOCKED_MUTATING_ONLY_PATH", "BLOCKED_PROTOCOL_INCOMPLETE"]}, + "existing_stack_repeat_readback": {"enum": ["READY_FOR_REVIEW", "PARTIAL", "BLOCKED"]}, + "elfldr_independent_recovery": {"enum": ["PROVEN_FROM_SOURCE", "PARTIAL", "SELF_DEPENDENT", "UNPROVEN", "ABSENT"]}, + "payload_manager_independent_recovery": {"enum": ["PROVEN_FROM_SOURCE", "PARTIAL", "SELF_DEPENDENT", "UNPROVEN", "ABSENT"]}, + "side_by_side_feasibility": {"enum": ["READY_FOR_FUTURE_REVIEW", "PARTIAL", "BLOCKED"]}, + "device_write": {"const": "NOT_AUTHORIZED"}, + "target_execution": {"const": "NOT_AUTHORIZED"}, + "installation": {"const": "NOT_AUTHORIZED"} + }, + "additionalProperties": false + }, + "bootstrap_graph": {"type": "array", "minItems": 5}, + "endpoint_defaults": {"type": "object"}, + "endpoint_matrix": { + "type": "array", + "minItems": 40, + "items": { + "type": "object", + "required": [ + "profile", + "method", + "endpoint", + "handler", + "source", + "lines", + "parameters", + "response", + "open_flags", + "reads_bytes", + "writes_bytes", + "creates_file", + "removes_file", + "renames_file", + "reads_directory", + "reads_metadata", + "calculates_hash", + "modifies_configuration", + "writes_autoload_triggered", + "launches_payload", + "process_or_service_action", + "error_behavior", + "effect_class", + "observation_candidate", + "forbidden_reason" + ] + } + }, + "flag_semantics": {"type": "object"}, + "readback_routes": {"type": "array", "minItems": 7, "maxItems": 7}, + "path_inventory": {"type": "array", "minItems": 9}, + "path_classification": {"const": "PATH_CONFLICT"}, + "runtime_observed_live_paths": {"type": "array", "maxItems": 0}, + "host_backup_contract": {"type": "object"}, + "recovery_dependencies": {"type": "object"}, + "side_by_side": {"type": "object"}, + "operational_windows": {"type": "array", "minItems": 7, "maxItems": 7}, + "acceptable_technical_effects_only_with_future_explicit_consent": {"type": "array", "minItems": 10}, + "hard_stops": {"type": "array", "minItems": 8}, + "open_hardware_questions": {"type": "array", "minItems": 8}, + "final_decision": { + "type": "object", + "required": ["classification", "reason", "hardware_evidence_claimed", "device_action_authorized"], + "properties": { + "classification": {"const": "BLOCKED"}, + "reason": {"type": "string"}, + "hardware_evidence_claimed": {"const": false}, + "device_action_authorized": {"const": false} + }, + "additionalProperties": false + } + } +} diff --git a/manifests/runtime/phase-0.9e-bootstrap-provenance.json b/manifests/runtime/phase-0.9e-bootstrap-provenance.json new file mode 100644 index 0000000..4ce2a28 --- /dev/null +++ b/manifests/runtime/phase-0.9e-bootstrap-provenance.json @@ -0,0 +1,685 @@ +{ + "schema_version": 1, + "phase": "PHASE_0_9E_BOOTSTRAP_RESCUE_PROVENANCE", + "date": "2026-07-18", + "status": "DESIGN_ONLY", + "baseline_commit": "e5304e394efb0790e1939bef87404f19b58caa43", + "branch": "codex/chimera-gfx-phase09e-bootstrap-rescue-provenance", + "canonical_state": { + "phase09d_decision": "BLOCKED_NO_READBACK_PATH", + "phase09c_new_observer": "BLOCKED_MULTIPLE_FOUNDATIONAL_CONTRACTS", + "payload_manager_backup": "HARD_BLOCKER_FOR_INSTALLATION", + "independent_recovery": "UNPROVEN", + "firmware_runtime_behavior": "UNPROVEN", + "permanent_denylist_sha256": "e9603b0e3792781ad5b511afb22ef61e3d4fd4c5a16bf928f8609193bcd97783", + "permanently_blocked_artifact_sha256": "4be1c17b4964f2b68c39b5145bc4af4619c32512d60269ecf5c39728b390fa63" + }, + "authorization": { + "device_action_authorized": false, + "execution_authorized": false, + "installation_authorized": false, + "lifecycle_authorized": false, + "autoload_authorized": false, + "device_write_authorized": false, + "transfer_authorized": false, + "automatic_retry": false, + "target_build_authorized": false, + "rescue_payload_design_authorized": false + }, + "actions": { + "internet_used": false, + "fetch_performed": false, + "clone_performed": false, + "package_installed": false, + "ps5_connected": false, + "device_request_performed": false, + "files_transferred": false, + "device_write_performed": false, + "target_execution_performed": false, + "target_build_performed": false, + "target_artifact_created": false, + "rescue_payload_created": false, + "readback_payload_created": false, + "device_client_created": false, + "backup_created": false, + "staging_performed": false + }, + "decisions": { + "actual_bootstrap_package_available": false, + "actual_bootstrap_identity": null, + "bootstrap_provenance": "POSSIBLE", + "independent_from_elfldr": "unproven", + "independent_from_payload_manager": "unproven", + "restartable_after_reboot": "unproven", + "host_to_memory": "UNKNOWN", + "output_channel_for_future_rescue": "UNKNOWN", + "independent_rescue_classification": "BOOTSTRAP_IMPLEMENTATION_MISSING", + "phase09f_rescue_payload_design_allowed": false, + "device_action_authorized": false, + "execution_authorized": false, + "installation_authorized": false, + "automatic_retry": false + }, + "source_commits": { + "hardened_elfldr": "197623058f509eddde18868dafcb92fdcac66464", + "controlled_payload_manager": "e23d94ff91233aa770e2342800c1467875bdef44", + "elfldr_public_base": "699e8bcff03e91e8d6ca6eba281af25c5a58d8c2", + "payload_manager_public_base": "cfbc70f30f419b09bf2b52283f7409e2d3117ee1", + "ps5_payload_sdk_v0_41": "d2e2e585740362976a39fdd5ccf390f199a7bc37" + }, + "source_tree_status": { + "hardened_elfldr": "clean", + "controlled_payload_manager": "clean", + "elfldr_public_base": "clean", + "payload_manager_public_base": "clean", + "ps5_payload_sdk_v0_41": "clean" + }, + "search_scope": { + "roots": [ + "chimera-gfx", + "chimera-gfx/work/upstream", + "../chimera-elfldr", + "../chimera-ps5-payload-manager", + "~/.codex/attachments", + "~/Downloads exact Y2JB filename" + ], + "full_computer_scan": false, + "arbitrary_public_upstream_substitution": false, + "network_search": false + }, + "candidate_archive_audit": { + "outer_path": "~/Downloads/Y2JB-Autoloader-403-1240.zip", + "outer_size": 504159435, + "outer_sha256": "805e3f87f0c371223619ffc7d3a7b3c0d41a1fae8a8b1171d9e2f162659e8291", + "zip_integrity": "PASS", + "zip_entry_count": 5, + "inner_path": "PS5/EXPORT/BACKUP/202606102126_00/archive.dat", + "inner_size": 504365056, + "inner_sha256": "6439834e8856d45b6d6fe699b74c35ca6985a199ea8ecf3e398c018d37be2d55", + "inner_magic_ascii": "SIECAF", + "inner_entropy_bits_per_byte": 7.999987, + "plaintext_marker_counts": { + "Y2JB_uppercase": 0, + "y2jb_lowercase": 0, + "9020": 0, + "elfldr": 0, + "autoload": 0, + "html": 0, + "wasm": 0, + "ELF_magic": 0 + }, + "acceptable_local_parser_present": false, + "decryption_attempted": false, + "source_identity_proven": false, + "deployed_use_proven": false, + "classification": "POSSIBLE" + }, + "artifacts": [ + { + "logical_name": "y2jb_named_outer_backup", + "artifact_role": "possible_exploit_autoloader_backup_package", + "local_relative_path": "~/Downloads/Y2JB-Autoloader-403-1240.zip", + "size": 504159435, + "sha256": "805e3f87f0c371223619ffc7d3a7b3c0d41a1fae8a8b1171d9e2f162659e8291", + "file_type": "ZIP", + "source_repository": null, + "source_commit": null, + "build_identity": null, + "version": "filename_claim_4.03_to_12.40_only", + "obtained_from": "local_user_download_exact_origin_unknown", + "evidence_that_it_is_deployed_or_used": "historical_phase06_local_backup_candidate_only", + "confidence": "POSSIBLE", + "immutable": false, + "executable": false, + "persistent_on_device": "unproven", + "transferred_per_session": "unproven", + "required_for_bootstrap": "unproven", + "required_for_recovery": "unproven" + }, + { + "logical_name": "opaque_siecaf_archive", + "artifact_role": "opaque_console_backup_container", + "local_relative_path": "~/Downloads/Y2JB-Autoloader-403-1240.zip::PS5/EXPORT/BACKUP/202606102126_00/archive.dat", + "size": 504365056, + "sha256": "6439834e8856d45b6d6fe699b74c35ca6985a199ea8ecf3e398c018d37be2d55", + "file_type": "SIECAF_OPAQUE_BINARY", + "source_repository": null, + "source_commit": null, + "build_identity": null, + "version": null, + "obtained_from": "single_file_inside_local_candidate_zip", + "evidence_that_it_is_deployed_or_used": "none", + "confidence": "UNKNOWN", + "immutable": false, + "executable": false, + "persistent_on_device": "unproven", + "transferred_per_session": "unproven", + "required_for_bootstrap": "unproven", + "required_for_recovery": "unproven" + }, + { + "logical_name": "hardened_elfldr_final", + "artifact_role": "post_9020_elfldr_bootstrap_payload", + "local_relative_path": "../chimera-elfldr/elfldr-ps5.elf", + "size": 397000, + "sha256": "63e810982471eb40cae3a20aa9df9a0a02892f420e429874fae4e99aa400b561", + "file_type": "ELF64_PIE_PS5", + "source_repository": "private-gitea/chimera-elfldr", + "source_commit": "197623058f509eddde18868dafcb92fdcac66464", + "build_identity": "chimera-elfldr-phase07-fw960-v1", + "version": "0.23-chimera-phase07", + "obtained_from": "local_reproducible_phase07_build", + "evidence_that_it_is_deployed_or_used": "artifact_manifest_transferred_false_executed_false", + "confidence": "REFERENCE_ONLY", + "immutable": false, + "executable": true, + "persistent_on_device": false, + "transferred_per_session": false, + "required_for_bootstrap": true, + "required_for_recovery": false + }, + { + "logical_name": "hardened_elfldr_final_map", + "artifact_role": "linker_map_for_post_9020_payload", + "local_relative_path": "../chimera-elfldr/elfldr-ps5.elf.map", + "size": 142905, + "sha256": "2ea5ff73299db6e61d5fd998c358ee910bc4e8fa1e6e7c73dbaa842bc813124f", + "file_type": "LINKER_MAP_TEXT", + "source_repository": "private-gitea/chimera-elfldr", + "source_commit": "197623058f509eddde18868dafcb92fdcac66464", + "build_identity": "chimera-elfldr-phase07-fw960-v1", + "version": "0.23-chimera-phase07", + "obtained_from": "local_phase07_build", + "evidence_that_it_is_deployed_or_used": "offline_build_evidence_only", + "confidence": "REFERENCE_ONLY", + "immutable": false, + "executable": false, + "persistent_on_device": false, + "transferred_per_session": false, + "required_for_bootstrap": false, + "required_for_recovery": false + }, + { + "logical_name": "embedded_bootstrap_map", + "artifact_role": "linker_map_for_embedded_bootstrap_stage", + "local_relative_path": "../chimera-elfldr/bootstrap.elf.map", + "size": 143081, + "sha256": "ec4ae17abeb8270948d8a7d4196593ba94cbdb69376b8b9f7f009b25ab5b3323", + "file_type": "LINKER_MAP_TEXT", + "source_repository": "private-gitea/chimera-elfldr", + "source_commit": "197623058f509eddde18868dafcb92fdcac66464", + "build_identity": "phase07_intermediate_map", + "version": "0.23-chimera-phase07", + "obtained_from": "local_phase07_build", + "evidence_that_it_is_deployed_or_used": "intermediate_ELF_absent_and_final_artifact_not_transferred", + "confidence": "REFERENCE_ONLY", + "immutable": false, + "executable": false, + "persistent_on_device": false, + "transferred_per_session": false, + "required_for_bootstrap": false, + "required_for_recovery": false + }, + { + "logical_name": "embedded_socket_service_map", + "artifact_role": "linker_map_for_embedded_port_9021_service", + "local_relative_path": "../chimera-elfldr/socksrv.elf.map", + "size": 145072, + "sha256": "bf74db6ee68a60652426936e7037bb5a143078f44cc7774fa04c9bc4bc5447c1", + "file_type": "LINKER_MAP_TEXT", + "source_repository": "private-gitea/chimera-elfldr", + "source_commit": "197623058f509eddde18868dafcb92fdcac66464", + "build_identity": "phase07_intermediate_map", + "version": "0.23-chimera-phase07", + "obtained_from": "local_phase07_build", + "evidence_that_it_is_deployed_or_used": "intermediate_ELF_absent_and_final_artifact_not_transferred", + "confidence": "REFERENCE_ONLY", + "immutable": false, + "executable": false, + "persistent_on_device": false, + "transferred_per_session": false, + "required_for_bootstrap": false, + "required_for_recovery": false + }, + { + "logical_name": "hardened_elfldr_makefile", + "artifact_role": "build_and_port_default_source", + "local_relative_path": "../chimera-elfldr/Makefile", + "size": 1902, + "sha256": "3df93b48fc61ec67907b136e44ea2085baacf08347864d32befc072a7a783384", + "file_type": "MAKEFILE", + "source_repository": "private-gitea/chimera-elfldr", + "source_commit": "197623058f509eddde18868dafcb92fdcac66464", + "build_identity": "commit_bound_source", + "version": "0.23-chimera-phase07", + "obtained_from": "clean_local_source_tree", + "evidence_that_it_is_deployed_or_used": "none", + "confidence": "REFERENCE_ONLY", + "immutable": true, + "executable": false, + "persistent_on_device": false, + "transferred_per_session": false, + "required_for_bootstrap": false, + "required_for_recovery": false + }, + { + "logical_name": "hardened_elfldr_readme", + "artifact_role": "architectural_9020_sender_description", + "local_relative_path": "../chimera-elfldr/README.md", + "size": 2821, + "sha256": "372aeb28dc971b2bd98093a47fdaf77c32f75bbdc3b3d7e8678900744b91eadb", + "file_type": "MARKDOWN", + "source_repository": "private-gitea/chimera-elfldr", + "source_commit": "197623058f509eddde18868dafcb92fdcac66464", + "build_identity": "commit_bound_documentation", + "version": "0.23-chimera-phase07", + "obtained_from": "clean_local_source_tree", + "evidence_that_it_is_deployed_or_used": "none", + "confidence": "REFERENCE_ONLY", + "immutable": true, + "executable": false, + "persistent_on_device": false, + "transferred_per_session": false, + "required_for_bootstrap": false, + "required_for_recovery": false + }, + { + "logical_name": "hardened_elfldr_main_source", + "artifact_role": "post_9020_first_stage_source", + "local_relative_path": "../chimera-elfldr/main.c", + "size": 2473, + "sha256": "876389a26999073994e63ca29926982280d9594a1ee941244205b54f57e2b4d1", + "file_type": "C_SOURCE", + "source_repository": "private-gitea/chimera-elfldr", + "source_commit": "197623058f509eddde18868dafcb92fdcac66464", + "build_identity": "commit_bound_source", + "version": "0.23-chimera-phase07", + "obtained_from": "clean_local_source_tree", + "evidence_that_it_is_deployed_or_used": "final_artifact_not_transferred", + "confidence": "REFERENCE_ONLY", + "immutable": true, + "executable": false, + "persistent_on_device": false, + "transferred_per_session": false, + "required_for_bootstrap": true, + "required_for_recovery": false + }, + { + "logical_name": "hardened_embedded_bootstrap_source", + "artifact_role": "SceRedisServer_embedded_stage_source", + "local_relative_path": "../chimera-elfldr/bootstrap.c", + "size": 2081, + "sha256": "5a8072ec0d6db919cb3a81a7028dc91fd8e2c3a0d69b0fa7e84836fa93b45381", + "file_type": "C_SOURCE", + "source_repository": "private-gitea/chimera-elfldr", + "source_commit": "197623058f509eddde18868dafcb92fdcac66464", + "build_identity": "commit_bound_source", + "version": "0.23-chimera-phase07", + "obtained_from": "clean_local_source_tree", + "evidence_that_it_is_deployed_or_used": "final_artifact_not_transferred", + "confidence": "REFERENCE_ONLY", + "immutable": true, + "executable": false, + "persistent_on_device": false, + "transferred_per_session": false, + "required_for_bootstrap": true, + "required_for_recovery": false + }, + { + "logical_name": "hardened_port_9021_service_source", + "artifact_role": "post_bootstrap_socket_service_source", + "local_relative_path": "../chimera-elfldr/socksrv.c", + "size": 11556, + "sha256": "d642ced3e9b4a296dd15e355050ebe956f53a6dfdaa6ac10109cd067a3bba3d7", + "file_type": "C_SOURCE", + "source_repository": "private-gitea/chimera-elfldr", + "source_commit": "197623058f509eddde18868dafcb92fdcac66464", + "build_identity": "commit_bound_source", + "version": "0.23-chimera-phase07", + "obtained_from": "clean_local_source_tree", + "evidence_that_it_is_deployed_or_used": "final_artifact_not_transferred", + "confidence": "REFERENCE_ONLY", + "immutable": true, + "executable": false, + "persistent_on_device": false, + "transferred_per_session": false, + "required_for_bootstrap": true, + "required_for_recovery": false + }, + { + "logical_name": "public_elfldr_v023_asset", + "artifact_role": "historical_public_release_reference", + "local_relative_path": "work/upstream/release-assets/elfldr-ps5-v0.23.elf", + "size": 397000, + "sha256": "092d16ee0ede0c494947efd38d1a17bbd7cc4b022d3858ea898833c188c703e8", + "file_type": "ELF64_PIE_PS5", + "source_repository": "ps5-payload-dev/elfldr", + "source_commit": "699e8bcff03e91e8d6ca6eba281af25c5a58d8c2", + "build_identity": "public_release_v0.23", + "version": "v0.23", + "obtained_from": "existing_local_upstream_release_asset", + "evidence_that_it_is_deployed_or_used": "historical_phase06_identity_only_not_current_external_bootstrap", + "confidence": "REFERENCE_ONLY", + "immutable": true, + "executable": true, + "persistent_on_device": "historical_reference_only", + "transferred_per_session": "historical_reference_only", + "required_for_bootstrap": false, + "required_for_recovery": false + }, + { + "logical_name": "controlled_payload_manager_elf", + "artifact_role": "final_chain_manager_reference", + "local_relative_path": "../chimera-ps5-payload-manager/pldmgr-controlled.elf", + "size": 99560, + "sha256": "8fecf8241a46246eddbd21e8bb4d875f5d76f1f4f4c6a11384df1f131aa5e5b1", + "file_type": "ELF64_PIE_PS5", + "source_repository": "private-gitea/chimera-ps5-payload-manager", + "source_commit": "e23d94ff91233aa770e2342800c1467875bdef44", + "build_identity": "chimera-controlled-payload-manager-phase07", + "version": "phase07", + "obtained_from": "local_reproducible_phase07_build", + "evidence_that_it_is_deployed_or_used": "controlled_runtime_profile_installed_false", + "confidence": "REFERENCE_ONLY", + "immutable": false, + "executable": true, + "persistent_on_device": false, + "transferred_per_session": false, + "required_for_bootstrap": false, + "required_for_recovery": false + } + ], + "exact_used_artifacts": [], + "bootstrap_dependency_graph": [ + { + "name": "operator_trigger", + "exact_artifact_identity": null, + "execution_environment": "host_or_browser_unknown", + "persistent_or_temporary": "unknown", + "side": "host", + "start_mechanism": "unknown", + "input_channel": "unknown", + "output_channel": "unknown", + "network_port": null, + "protocol": "unknown", + "dependencies": ["actual_y2jb_or_other_exploit_package"], + "device_file_required": "unproven", + "elfldr_required": "unproven", + "payload_manager_required": "unproven", + "autoload_required": "unproven", + "reboot_resilient": "unproven", + "restartable_after_reboot": "unproven", + "failure_impact": "bootstrap_unavailable", + "recovery_role": "fresh_session_trigger", + "evidence_level": "UNKNOWN" + }, + { + "name": "y2jb_or_other_exploit_host", + "exact_artifact_identity": null, + "execution_environment": "browser_or_host_unknown", + "persistent_or_temporary": "unknown", + "side": "host_and_device_browser_unknown", + "start_mechanism": "operator_trigger_unknown", + "input_channel": "opaque_candidate_package", + "output_channel": "unknown", + "network_port": null, + "protocol": "unknown", + "dependencies": ["operator_trigger"], + "device_file_required": "unproven", + "elfldr_required": "architecturally_no_but_unproven", + "payload_manager_required": "architecturally_no_but_unproven", + "autoload_required": "unproven", + "reboot_resilient": "unproven", + "restartable_after_reboot": "unproven", + "failure_impact": "port_9020_listener_not_created", + "recovery_role": "possible_independent_bootstrap", + "evidence_level": "POSSIBLE" + }, + { + "name": "rudimentary_loader_port_9020", + "exact_artifact_identity": null, + "execution_environment": "PS5_process_or_kernel_context_unknown", + "persistent_or_temporary": "unknown", + "side": "device", + "start_mechanism": "exploit_chain_description_only", + "input_channel": "host_TCP_stream_assumed", + "output_channel": "unknown", + "network_port": 9020, + "protocol": "UNPROVEN_IMPLEMENTATION_MISSING", + "dependencies": ["y2jb_or_other_exploit_host"], + "device_file_required": "unproven", + "elfldr_required": false, + "payload_manager_required": false, + "autoload_required": "unproven", + "reboot_resilient": "unproven", + "restartable_after_reboot": "unproven", + "failure_impact": "cannot_launch_any_post_exploit_ELF", + "recovery_role": "candidate_rescue_executor", + "evidence_level": "IMPLEMENTATION_MISSING" + }, + { + "name": "conceptual_host_sender", + "exact_artifact_identity": null, + "execution_environment": "host_stream_tool", + "persistent_or_temporary": "temporary", + "side": "host", + "start_mechanism": "manual_README_pipeline", + "input_channel": "local_or_downloaded_ELF_bytes", + "output_channel": "TCP_to_device_9020", + "network_port": 9020, + "protocol": "raw_stream_shape_only", + "dependencies": ["rudimentary_loader_port_9020"], + "device_file_required": false, + "elfldr_required": false, + "payload_manager_required": false, + "autoload_required": false, + "reboot_resilient": "not_applicable", + "restartable_after_reboot": "depends_on_missing_listener_chain", + "failure_impact": "no_ELF_delivery", + "recovery_role": "possible_rescue_sender", + "evidence_level": "ARCHITECTURE_ONLY" + }, + { + "name": "hardened_elfldr_first_stage", + "exact_artifact_identity": "63e810982471eb40cae3a20aa9df9a0a02892f420e429874fae4e99aa400b561", + "execution_environment": "bdj.elf_then_SceRedisServer_by_source", + "persistent_or_temporary": "process_stages", + "side": "device", + "start_mechanism": "port_9020_execution_assumed", + "input_channel": "ELF_from_9020", + "output_channel": "embedded_bootstrap_execution", + "network_port": null, + "protocol": "payload_args_and_ptrace_source_bound_after_entry", + "dependencies": ["rudimentary_loader_port_9020"], + "device_file_required": false, + "elfldr_required": false, + "payload_manager_required": false, + "autoload_required": false, + "reboot_resilient": false, + "restartable_after_reboot": "requires_missing_exploit_chain", + "failure_impact": "port_9021_service_absent", + "recovery_role": "normal_loader_bootstrap_not_independent_rescue", + "evidence_level": "REFERENCE_ONLY_NOT_DEPLOYED" + }, + { + "name": "elfldr_service_port_9021", + "exact_artifact_identity": "embedded_in_63e810982471eb40cae3a20aa9df9a0a02892f420e429874fae4e99aa400b561", + "execution_environment": "new_PS5_process_by_source", + "persistent_or_temporary": "process_local_background", + "side": "device", + "start_mechanism": "embedded_bootstrap_stage", + "input_channel": "TCP_listener", + "output_channel": "fixed_status_or_legacy_stdio", + "network_port": 9021, + "protocol": "source_bound_but_not_9020", + "dependencies": ["hardened_elfldr_first_stage"], + "device_file_required": false, + "elfldr_required": true, + "payload_manager_required": false, + "autoload_required": false, + "reboot_resilient": false, + "restartable_after_reboot": "requires_missing_exploit_chain", + "failure_impact": "normal_payload_launch_unavailable", + "recovery_role": "not_independent_from_elfldr", + "evidence_level": "REFERENCE_ONLY_NOT_DEPLOYED" + }, + { + "name": "controlled_payload_manager", + "exact_artifact_identity": "8fecf8241a46246eddbd21e8bb4d875f5d76f1f4f4c6a11384df1f131aa5e5b1", + "execution_environment": "PS5_process_by_source", + "persistent_or_temporary": "not_installed_in_evidence", + "side": "device", + "start_mechanism": "elfldr_launch_if_later_authorized", + "input_channel": "controlled_HTTP", + "output_channel": "fixed_status", + "network_port": 8085, + "protocol": "source_bound_controlled_profile", + "dependencies": ["elfldr_service_port_9021"], + "device_file_required": true, + "elfldr_required": true, + "payload_manager_required": false, + "autoload_required": false, + "reboot_resilient": false, + "restartable_after_reboot": "unproven", + "failure_impact": "management_layer_unavailable", + "recovery_role": "component_to_recover_not_rescue_executor", + "evidence_level": "REFERENCE_ONLY_NOT_DEPLOYED" + } + ], + "host_to_memory_analysis": { + "classification": "UNKNOWN", + "receive_code": "MISSING", + "storage_location": "UNKNOWN", + "temporary_device_file_created": "UNKNOWN", + "mapping_code": "MISSING", + "mapping_permissions": "UNKNOWN", + "entrypoint_selection": "UNKNOWN", + "argument_contract": "UNKNOWN", + "cleanup_before_transfer": "UNKNOWN", + "cleanup_after_transfer": "UNKNOWN", + "parser_error_behavior": "UNKNOWN", + "crash_behavior": "UNKNOWN", + "hang_behavior": "UNKNOWN", + "live_installation_untouched": "UNPROVEN", + "autoload_created": "UNPROVEN", + "persistent_configuration_changed": "UNPROVEN" + }, + "reboot_and_crash": { + "classification": "REBOOT_RECOVERY_UNPROVEN", + "exploit_state": "UNKNOWN", + "port_9020_state": "UNKNOWN", + "port_9021_state": "PROCESS_LOCAL_BY_SOURCE", + "filesystem_state": "UNKNOWN_FOR_ACTUAL_BOOTSTRAP", + "autoload_state": "UNKNOWN_FOR_ACTUAL_BOOTSTRAP", + "fresh_jailbreak_after_reboot": "UNPROVEN", + "bad_payload_persistence": "UNPROVEN", + "automatic_retry": false + }, + "security_findings": [ + { + "id": "opaque_candidate_container", + "classification": "UNKNOWN", + "finding": "Candidate package content cannot be source-bound or safely parsed with present local evidence." + }, + { + "id": "missing_9020_bounds_and_length", + "classification": "UNKNOWN", + "finding": "Maximum length, allocation, integer overflow, bounds and short-read handling are unavailable." + }, + { + "id": "readme_sender_has_no_integrity_contract", + "classification": "PROTOCOL_INTEGRITY_RISK", + "finding": "The conceptual raw stream sender documents no length, hash, acknowledgement or error frame." + }, + { + "id": "unknown_9020_crash_cleanup", + "classification": "SESSION_CRASH_RISK", + "finding": "Listener and payload crash, hang, cleanup and restart behavior are unavailable." + }, + { + "id": "unknown_live_filesystem_effect", + "classification": "BRICK_RELEVANT", + "finding": "Whether the actual loader stages or replaces a live file is unknown and blocks device use." + }, + { + "id": "unknown_autoload_effect", + "classification": "BRICK_RELEVANT", + "finding": "Actual package configuration and autoload effects are unavailable and must remain disabled." + }, + { + "id": "temporary_socket_not_automatically_brick_relevant", + "classification": "HOST_ONLY", + "finding": "A bounded temporary socket alone would not modify live boot state, but no such exact design is established." + } + ], + "future_rescue_contract": { + "requirements_total": 20, + "bootstrap_supported_now": [], + "payload_policy_requirements_only": [ + "not_installed", + "no_autoload", + "no_component_modification", + "read_only_target_open", + "no_automatic_retry", + "one_file_per_session", + "no_mutating_or_control_operations", + "no_side_effect_free_claim" + ], + "missing_bootstrap_or_output_requirements": [ + "independent_bootstrap", + "no_bootstrap_device_file", + "ps5_to_host_only", + "binary_safe_framing", + "exact_byte_count", + "sha256_or_complete_host_hash", + "partial_transfer_rejection", + "hard_deadline", + "host_abort", + "live_installation_untouched", + "derived_output_channel", + "crash_hang_model" + ], + "target_source_allowed": false + }, + "output_options": [ + {"id":"A_SAME_9020_DUPLEX","existing":"UNKNOWN","protocol_change":"UNKNOWN","host_change":"UNKNOWN","target_payload_change":true,"listener_needed":false,"outbound_connection_needed":false,"persistent_device_file_needed":false,"cleanup_model":"UNKNOWN","crash_impact":"UNKNOWN","reboot_impact":"UNKNOWN","anti_brick_risk":"UNKNOWN","feasibility":"UNKNOWN"}, + {"id":"B_TEMPORARY_OUTBOUND","existing":false,"protocol_change":true,"host_change":true,"target_payload_change":true,"listener_needed":"HOST_ONLY","outbound_connection_needed":true,"persistent_device_file_needed":false,"cleanup_model":"REQUIRED","crash_impact":"SESSION_RISK","reboot_impact":"UNPROVEN","anti_brick_risk":"NOT_AUTOMATIC_FROM_SOCKET","feasibility":"UNPROVEN"}, + {"id":"C_TEMPORARY_LISTENER","existing":false,"protocol_change":true,"host_change":true,"target_payload_change":true,"listener_needed":true,"outbound_connection_needed":false,"persistent_device_file_needed":false,"cleanup_model":"REQUIRED","crash_impact":"LISTENER_LIFETIME_RISK","reboot_impact":"UNPROVEN","anti_brick_risk":"NOT_AUTOMATIC_FROM_SOCKET","feasibility":"UNPROVEN"}, + {"id":"D_CALLER_OWNED_BUFFER","existing":false,"protocol_change":true,"host_change":true,"target_payload_change":true,"listener_needed":false,"outbound_connection_needed":false,"persistent_device_file_needed":false,"cleanup_model":"CALLER_FINALIZATION_REQUIRED","crash_impact":"OWNERSHIP_UNPROVEN","reboot_impact":"UNPROVEN","anti_brick_risk":"LOW_PERSISTENT_IF_PROVEN","feasibility":"CURRENT_LOADER_HAS_NO_PATH"}, + {"id":"E_NO_OUTPUT","existing":"CONSISTENT_WITH_EVIDENCE","protocol_change":false,"host_change":false,"target_payload_change":false,"listener_needed":false,"outbound_connection_needed":false,"persistent_device_file_needed":false,"cleanup_model":"NOT_APPLICABLE","crash_impact":"NO_RESULT","reboot_impact":"NOT_APPLICABLE","anti_brick_risk":"NO_RECOVERY_CAPABILITY","feasibility":"ONLY_CURRENT_OPERATIONAL_ASSUMPTION"} + ], + "missing_actual_files": [ + "exact_Y2JB_or_other_host_HTML", + "exact_exploit_JavaScript", + "exact_exploit_WebAssembly", + "exact_payload_data", + "exact_host_configuration", + "exact_cache_or_service_worker", + "exact_host_manifest_and_version", + "exact_port_9020_listener_source", + "exact_port_9020_listener_binary", + "port_9020_build_script_and_linker_input", + "exact_port_9020_protocol_client_or_sender", + "external_autoloader_source_and_configuration", + "operator_evidence_binding_hashes_to_current_use" + ], + "hard_stops": [ + "ACTUAL_BOOTSTRAP_IMPLEMENTATION_MISSING", + "ACTUAL_USED_IDENTITY_NULL", + "PORT_9020_SERVER_PROTOCOL_UNPROVEN", + "HOST_TO_MEMORY_UNKNOWN", + "LIVE_FILESYSTEM_EFFECT_UNKNOWN", + "AUTOLOAD_EFFECT_UNKNOWN", + "REBOOT_RESTARTABILITY_UNPROVEN", + "OUTPUT_CHANNEL_UNKNOWN", + "PHASE09F_DESIGN_NOT_ALLOWED", + "PAYLOAD_MANAGER_BACKUP_HARD_BLOCKER_FOR_INSTALLATION", + "INDEPENDENT_RECOVERY_UNPROVEN" + ], + "final_decision": { + "classification": "BOOTSTRAP_IMPLEMENTATION_MISSING", + "hardware_evidence_claimed": false, + "device_action_authorized": false, + "reason": "EXACT_USED_Y2JB_HOST_AND_PORT_9020_LISTENER_IMPLEMENTATION_ARE_NOT_AVAILABLE; THE_ONLY_MATCHING_LOCAL_PACKAGE_IS_AN_OPAQUE_UNBOUND_SIECAF_BACKUP" + } +} diff --git a/manifests/runtime/phase-0.9e-bootstrap-provenance.schema.json b/manifests/runtime/phase-0.9e-bootstrap-provenance.schema.json new file mode 100644 index 0000000..f3faa61 --- /dev/null +++ b/manifests/runtime/phase-0.9e-bootstrap-provenance.schema.json @@ -0,0 +1,181 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://chimera-gfx.invalid/schemas/phase-0.9e-bootstrap-provenance.schema.json", + "title": "Chimera GFX Phase 0.9E bootstrap provenance", + "type": "object", + "required": [ + "schema_version", + "phase", + "status", + "baseline_commit", + "branch", + "canonical_state", + "authorization", + "actions", + "decisions", + "source_commits", + "candidate_archive_audit", + "artifacts", + "exact_used_artifacts", + "bootstrap_dependency_graph", + "host_to_memory_analysis", + "reboot_and_crash", + "security_findings", + "future_rescue_contract", + "output_options", + "missing_actual_files", + "hard_stops", + "final_decision" + ], + "properties": { + "schema_version": { + "const": 1 + }, + "phase": { + "const": "PHASE_0_9E_BOOTSTRAP_RESCUE_PROVENANCE" + }, + "status": { + "const": "DESIGN_ONLY" + }, + "baseline_commit": { + "const": "e5304e394efb0790e1939bef87404f19b58caa43" + }, + "branch": { + "const": "codex/chimera-gfx-phase09e-bootstrap-rescue-provenance" + }, + "authorization": { + "type": "object", + "additionalProperties": { + "const": false + } + }, + "actions": { + "type": "object", + "additionalProperties": { + "const": false + } + }, + "decisions": { + "type": "object", + "required": [ + "actual_bootstrap_package_available", + "actual_bootstrap_identity", + "bootstrap_provenance", + "independent_from_elfldr", + "independent_from_payload_manager", + "restartable_after_reboot", + "host_to_memory", + "output_channel_for_future_rescue", + "independent_rescue_classification", + "phase09f_rescue_payload_design_allowed", + "device_action_authorized", + "execution_authorized", + "installation_authorized", + "automatic_retry" + ], + "properties": { + "actual_bootstrap_package_available": { + "const": false + }, + "actual_bootstrap_identity": { + "type": "null" + }, + "bootstrap_provenance": { + "const": "POSSIBLE" + }, + "independent_from_elfldr": { + "const": "unproven" + }, + "independent_from_payload_manager": { + "const": "unproven" + }, + "restartable_after_reboot": { + "const": "unproven" + }, + "host_to_memory": { + "const": "UNKNOWN" + }, + "output_channel_for_future_rescue": { + "const": "UNKNOWN" + }, + "independent_rescue_classification": { + "const": "BOOTSTRAP_IMPLEMENTATION_MISSING" + }, + "phase09f_rescue_payload_design_allowed": { + "const": false + }, + "device_action_authorized": { + "const": false + }, + "execution_authorized": { + "const": false + }, + "installation_authorized": { + "const": false + }, + "automatic_retry": { + "const": false + } + }, + "additionalProperties": false + }, + "artifacts": { + "type": "array", + "items": { + "type": "object", + "required": [ + "logical_name", + "artifact_role", + "local_relative_path", + "size", + "sha256", + "file_type", + "source_repository", + "source_commit", + "build_identity", + "version", + "obtained_from", + "evidence_that_it_is_deployed_or_used", + "confidence", + "immutable", + "executable", + "persistent_on_device", + "transferred_per_session", + "required_for_bootstrap", + "required_for_recovery" + ], + "properties": { + "sha256": { + "type": "string", + "pattern": "^[0-9a-f]{64}$" + }, + "confidence": { + "enum": [ + "EXACT_USED", + "STRONG_MATCH", + "POSSIBLE", + "REFERENCE_ONLY", + "UNKNOWN" + ] + } + } + } + }, + "exact_used_artifacts": { + "type": "array", + "maxItems": 0 + }, + "final_decision": { + "type": "object", + "required": [ + "classification", + "reason" + ], + "properties": { + "classification": { + "const": "BOOTSTRAP_IMPLEMENTATION_MISSING" + } + } + } + } +} diff --git a/manifests/runtime/phase-0.9e-loader-protocol.json b/manifests/runtime/phase-0.9e-loader-protocol.json new file mode 100644 index 0000000..12cb803 --- /dev/null +++ b/manifests/runtime/phase-0.9e-loader-protocol.json @@ -0,0 +1,79 @@ +{ + "schema_version": 1, + "phase": "PHASE_0_9E_LOADER_9020_PROTOCOL", + "date": "2026-07-18", + "status": "UNPROVEN_IMPLEMENTATION_MISSING", + "exact_server_source_present": false, + "exact_server_binary_present": false, + "exact_host_client_present": false, + "protocol_identity": null, + "evidence": { + "documentation_path": "../chimera-elfldr/README.md", + "documentation_sha256": "372aeb28dc971b2bd98093a47fdaf77c32f75bbdc3b3d7e8678900744b91eadb", + "documentation_lines": "11-19", + "makefile_path": "../chimera-elfldr/Makefile", + "makefile_sha256": "3df93b48fc61ec67907b136e44ea2085baacf08347864d32befc072a7a783384", + "makefile_lines": "17-18", + "evidence_level": "ARCHITECTURE_ONLY" + }, + "protocol": { + "server_role": "PS5_RUDIMENTARY_ELF_LOADER_DESCRIPTION_ONLY", + "client_role": "GENERIC_HOST_STREAM_SENDER_DESCRIPTION_ONLY", + "transport": "TCP_STRONGLY_INDICATED_NOT_SERVER_PROVEN", + "port": 9020, + "bind_address": "UNKNOWN", + "connect_direction": "HOST_TO_PS5", + "handshake": "UNKNOWN", + "magic": "UNKNOWN", + "version": "UNKNOWN", + "endianness": "UNKNOWN", + "headers": "UNKNOWN", + "length_fields": "UNKNOWN", + "maximum_payload_size": "UNKNOWN", + "chunking": "TCP_STREAM_ONLY_APPLICATION_UNKNOWN", + "eof": "SENDER_CLOSE_EXPECTED_LISTENER_SEMANTICS_UNKNOWN", + "acknowledgements": "UNKNOWN", + "checksum_or_hash": "UNKNOWN", + "timeout": "UNKNOWN", + "retry": "UNKNOWN", + "reconnect": "UNKNOWN", + "error_frames": "UNKNOWN", + "close_behavior": "UNKNOWN", + "upload_direction": "HOST_TO_PS5", + "response_direction": "UNKNOWN", + "accepted_object": "ELF_ASSUMED_BY_DOCUMENTATION_VALIDATION_UNKNOWN", + "post_receive_execution": "ASSUMED_BY_BOOTSTRAP_DESCRIPTION_NOT_SOURCE_PROVEN", + "parser": "MISSING", + "bounds_checks": "UNKNOWN", + "short_read_detection": "UNKNOWN", + "short_write_detection": "UNKNOWN", + "integer_overflow_checks": "UNKNOWN", + "allocations": "UNKNOWN", + "mappings": "UNKNOWN", + "permissions": "UNKNOWN", + "entrypoint_validation": "UNKNOWN", + "filesystem_staging": "UNKNOWN", + "cleanup": "UNKNOWN" + }, + "host_model": { + "allowed": false, + "created": false, + "reason": "EXACT_PROTOCOL_SOURCE_OR_AUDITABLE_BINARY_IS_ABSENT", + "network_port_opened": false, + "device_connection_performed": false, + "payload_sent": false, + "binary_executed": false + }, + "missing_evidence": [ + "exact_listener_source_or_binary", + "exact_host_sender", + "complete_framing", + "maximum_length", + "recv_and_eof_state_machine", + "accepted_format_and_entrypoint_validation", + "response_error_and_close_behavior", + "timeout_retry_and_reconnect_rules", + "mapping_permissions_and_cleanup" + ], + "classification": "CONCEPTUAL_9020_DESCRIPTION_IS_NOT_PROTOCOL_PROOF" +} diff --git a/manifests/runtime/phase-0.9e-loader-protocol.schema.json b/manifests/runtime/phase-0.9e-loader-protocol.schema.json new file mode 100644 index 0000000..aee11c2 --- /dev/null +++ b/manifests/runtime/phase-0.9e-loader-protocol.schema.json @@ -0,0 +1,125 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://chimera-gfx.invalid/schemas/phase-0.9e-loader-protocol.schema.json", + "title": "Chimera GFX Phase 0.9E port-9020 protocol evidence", + "type": "object", + "required": [ + "schema_version", + "phase", + "status", + "exact_server_source_present", + "exact_server_binary_present", + "exact_host_client_present", + "protocol_identity", + "evidence", + "protocol", + "host_model", + "missing_evidence", + "classification" + ], + "properties": { + "schema_version": { + "const": 1 + }, + "phase": { + "const": "PHASE_0_9E_LOADER_9020_PROTOCOL" + }, + "status": { + "const": "UNPROVEN_IMPLEMENTATION_MISSING" + }, + "exact_server_source_present": { + "const": false + }, + "exact_server_binary_present": { + "const": false + }, + "exact_host_client_present": { + "const": false + }, + "protocol_identity": { + "type": "null" + }, + "protocol": { + "type": "object", + "required": [ + "server_role", + "client_role", + "transport", + "port", + "bind_address", + "connect_direction", + "handshake", + "magic", + "version", + "endianness", + "headers", + "length_fields", + "maximum_payload_size", + "chunking", + "eof", + "acknowledgements", + "checksum_or_hash", + "timeout", + "retry", + "reconnect", + "error_frames", + "close_behavior", + "upload_direction", + "response_direction", + "accepted_object", + "post_receive_execution", + "parser", + "bounds_checks", + "short_read_detection", + "short_write_detection", + "integer_overflow_checks", + "allocations", + "mappings", + "permissions", + "entrypoint_validation", + "filesystem_staging", + "cleanup" + ], + "properties": { + "port": { + "const": 9020 + } + } + }, + "host_model": { + "type": "object", + "required": [ + "allowed", + "created", + "reason", + "network_port_opened", + "device_connection_performed", + "payload_sent", + "binary_executed" + ], + "properties": { + "allowed": { + "const": false + }, + "created": { + "const": false + }, + "network_port_opened": { + "const": false + }, + "device_connection_performed": { + "const": false + }, + "payload_sent": { + "const": false + }, + "binary_executed": { + "const": false + } + } + }, + "classification": { + "const": "CONCEPTUAL_9020_DESCRIPTION_IS_NOT_PROTOCOL_PROOF" + } + } +} diff --git a/manifests/runtime/phase-0.9e-r-port9020-audit.json b/manifests/runtime/phase-0.9e-r-port9020-audit.json new file mode 100644 index 0000000..d547242 --- /dev/null +++ b/manifests/runtime/phase-0.9e-r-port9020-audit.json @@ -0,0 +1,105 @@ +{ + "$schema": "phase-0.9e-r-port9020-audit.schema.json", + "schema_version": 1, + "phase": "PHASE_0_9E_R_OFFICIAL_Y2JB_CORRELATION", + "status": "DESIGN_ONLY", + "release_commit": "0dbbf4e7e0203af7e5d101a3256c634edf4e3ba2", + "port_9020_classification": "PORT_9020_REFERENCE_ONLY", + "port_9020_references": [ + { + "path": "README.md", + "role": "hostsender usage example", + "implementation": false + }, + { + "path": "payload_sender.py", + "line": 29, + "role": "hostsender usage example", + "implementation": false + } + ], + "port_9020_listener_found": false, + "port_9020_parser_found": false, + "port_9020_mapping_found": false, + "official_remote_js_loader": { + "classification": "IMPLEMENTATION_FOUND_DYNAMIC_PORT_NOT_9020", + "path": "download0/cache/splash_screen/aHR0cHM6Ly93d3cueW91dHViZS5jb20vdHY=/remotejsloader.js", + "sha256": "30cc6d1535549b2a49b47a9e0c85a3444cf84177be54398691694b7c6505f38e", + "git_blob": "dd4957530e7a88c889877a7dcb3433642a71e019", + "server_side": "PS5_JAVASCRIPT_RUNTIME", + "transport": "TCP_IPV4", + "bind_address": "INADDR_ANY", + "desired_dynamic_port": 50000, + "port_is_guaranteed": false, + "listen_backlog": 3, + "maximum_receive_bytes": 512000, + "framing": "CONNECTION_EOF_OR_BUFFER_LIMIT", + "declared_length": false, + "authentication": false, + "checksum_or_hash": false, + "timeout": false, + "partial_read_accumulation": true, + "full_limit_eof_proof": false, + "content": "UTF8_JAVASCRIPT", + "elf_magic_rejected": true, + "dispatch": "EVAL", + "native_elf_mapping": false, + "filesystem_write_in_receiver": false, + "internal_listener_retry": true, + "automatic_retry_authorized": false, + "response": "SAME_CONNECTION_LOG_WRITES", + "crash_risks": [ + "UNTRUSTED_JAVASCRIPT_EVAL", + "BLOCKING_READ_WITHOUT_DEADLINE", + "UNBOUNDED_ACCEPT_LIFETIME", + "FULL_BUFFER_PREFIX_MAY_DISPATCH_WITHOUT_EOF" + ] + }, + "lapse_chain": { + "architecture": "REMOTE_JS_LOADER_TO_LAPSE_TO_AIOSHELLCODE_TO_EMBEDDED_KEXP_AND_ELFLDR", + "firmware_upper_bound": "10.01", + "firmware_9_60_source_selection": "ELIGIBLE_BY_VERSION_COMPARISON", + "firmware_9_60_runtime_behavior": "UNPROVEN", + "filesystem_side_effect": "lapse.fail marker", + "kernel_exploit_present": true, + "kernel_write_behavior_present": true, + "device_use_authorized": false + }, + "port_9021_relation": { + "loopback_probe_source": "misc.js", + "host_instruction_source": "README.md", + "listener_container": "EMBEDDED_ELFLDR_BINARY", + "listener_source_in_y2jb": false, + "listener_generator_in_y2jb": false, + "release_1_6_embedded_elf": "download0/cache/splash_screen/aHR0cHM6Ly93d3cueW91dHViZS5jb20vdHY=/elfldr-ps5-1340.elf", + "release_1_6_embedded_elf_size": 397000, + "release_1_6_embedded_elf_sha256": "30478bcadb6439e1247451c4ac706b6e1385044dd0f12d6486b6d7057929453b", + "source_commit": null + }, + "official_sender": { + "path": "payload_sender.py", + "default_port": 50000, + "explicit_port": true, + "binary_read": true, + "sendall": true, + "response_read": false, + "duplex": false, + "timeout": false, + "retry": false, + "maximum_size": null, + "peer_byte_count": false, + "hash_validation": false + }, + "phase09f_offline_design_allowed": false, + "authorization": { + "device_action_authorized": false, + "target_build_authorized": false, + "transfer_authorized": false, + "execution_authorized": false, + "installation_authorized": false, + "lifecycle_authorized": false, + "autoload_authorized": false, + "device_write_authorized": false, + "automatic_retry": false + } +} diff --git a/manifests/runtime/phase-0.9e-r-port9020-audit.schema.json b/manifests/runtime/phase-0.9e-r-port9020-audit.schema.json new file mode 100644 index 0000000..e8340ad --- /dev/null +++ b/manifests/runtime/phase-0.9e-r-port9020-audit.schema.json @@ -0,0 +1,42 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "phase-0.9e-r-port9020-audit.schema.json", + "type": "object", + "required": [ + "schema_version", + "phase", + "status", + "release_commit", + "port_9020_classification", + "official_remote_js_loader", + "port_9021_relation", + "official_sender", + "phase09f_offline_design_allowed", + "authorization" + ], + "properties": { + "schema_version": { "const": 1 }, + "phase": { "const": "PHASE_0_9E_R_OFFICIAL_Y2JB_CORRELATION" }, + "status": { "const": "DESIGN_ONLY" }, + "release_commit": { + "const": "0dbbf4e7e0203af7e5d101a3256c634edf4e3ba2" + }, + "port_9020_classification": { + "enum": [ + "PORT_9020_IMPLEMENTATION_FOUND", + "PORT_9020_IMPLEMENTATION_PARTIAL", + "PORT_9020_REFERENCE_ONLY", + "PORT_9020_IMPLEMENTATION_ABSENT" + ] + }, + "port_9020_listener_found": { "const": false }, + "official_remote_js_loader": { "type": "object" }, + "port_9021_relation": { "type": "object" }, + "official_sender": { "type": "object" }, + "phase09f_offline_design_allowed": { "const": false }, + "authorization": { + "type": "object", + "additionalProperties": { "const": false } + } + } +} diff --git a/manifests/runtime/phase-0.9e-r-release-correlation.json b/manifests/runtime/phase-0.9e-r-release-correlation.json new file mode 100644 index 0000000..311b86d --- /dev/null +++ b/manifests/runtime/phase-0.9e-r-release-correlation.json @@ -0,0 +1,446 @@ +{ + "$schema": "phase-0.9e-r-release-correlation.schema.json", + "schema_version": 1, + "phase": "PHASE_0_9E_R_OFFICIAL_Y2JB_CORRELATION", + "status": "DESIGN_ONLY", + "date": "2026-07-18", + "baseline_commit": "3d3151bd12ff0f786c1e1b9af75d7174408e3d2f", + "official_repository": "https://github.com/Gezine/Y2JB", + "release_correlation": "OFFICIAL_RELEASE_NO_MATCH", + "final_classification": "LOCAL_BACKUP_NOT_CORRELATED", + "identified_release": null, + "identified_release_asset": null, + "release_to_source_classification": "NO_LOCAL_RELEASE_ASSOCIATION", + "inner_content_binding": "SIECAF_OPAQUE_UNBOUND", + "local_candidate": { + "file_name": "Y2JB-Autoloader-403-1240.zip", + "path": "/Y2JB-Autoloader-403-1240.zip", + "size": 504159435, + "sha256": "805e3f87f0c371223619ffc7d3a7b3c0d41a1fae8a8b1171d9e2f162659e8291", + "creation_utc": "2026-06-23T14:10:39.0466596Z", + "modification_utc": "2026-06-23T14:11:10.6992328Z", + "zip_comment": "", + "zone_identifier_size": 327, + "zone_identifier_sha256": "70928a3d66cbb9f584596414c4fe535b498dd5ed7c5f55bbeae4efeb146ea841", + "download_referrer_classification": "MEDIAFIRE_NON_OFFICIAL", + "first_64_bytes_hex": "504b030414000000000041abca5c000000000000000000000000040000005053352f504b030414000000000041abca5c0000000000000000000000000b000000", + "last_64_bytes_hex": "76652e6461740a0020000000000001001800c065678d17f9dc0100000000000000000000000000000000504b050600000000050005000a020000abda0c1e0000", + "browser_history_candidates_checked": 5, + "exact_browser_record_found": false + }, + "inner_archive": { + "entry": "PS5/EXPORT/BACKUP/202606102126_00/archive.dat", + "size": 504365056, + "compressed_size": 504158629, + "crc32": "522808c8", + "sha256": "6439834e8856d45b6d6fe699b74c35ca6985a199ea8ecf3e398c018d37be2d55", + "magic": "SIECAF", + "classification": "OPAQUE_UNBOUND", + "further_reverse_engineering_performed": false + }, + "official_metadata": { + "repository_api_sha256": "eac34f242bb5fb1a65c2fc440e601e6cbdc1e1dc1b06c2e1e3ac9822f4cb7daf", + "releases_api_sha256": "b90013ccac140daded6a3ed8f46577033ed8b0404c79aa51f3fb8f9a7910ce37", + "tags_api_sha256": "1933a77b5f42321fbb374d8767ece625683bc2eafad5bb2e11c93d5c88cb0bf6", + "release_count": 3, + "tag_count": 5 + }, + "release_inventory": [ + { + "version": "1.2", + "release": null, + "tag": null, + "commit": null, + "state": "CHANGELOG_REFERENCE_ONLY" + }, + { + "version": "1.2.1", + "release": null, + "tag": "Y2JB-1.2.1", + "commit": "6210f98c23552e6eb3c4c8981f72a200bb6121b2", + "state": "TAG_ONLY" + }, + { + "version": "1.3", + "release": "Y2JB 1.3 (deprecated)", + "tag": "1.3", + "commit": "82e6bbf1935d5f41c3bd5bd9e6598cf90bb2e022", + "published_at": "2026-05-25T14:29:41Z", + "state": "OFFICIAL_RELEASE" + }, + { + "version": "1.4", + "release": null, + "tag": "1.4", + "commit": "02eda2e772df629ecfe5a46459769bd8c994ef3e", + "state": "TAG_ONLY" + }, + { + "version": "1.5", + "release": "Y2JB 1.5 (deprecated)", + "tag": "1.5", + "commit": "5fa556e6281229057dace3254b099f95261df386", + "published_at": "2026-05-22T15:06:30Z", + "state": "OFFICIAL_RELEASE" + }, + { + "version": "1.6", + "release": "Y2JB 1.6", + "tag": "1.6", + "commit": "0dbbf4e7e0203af7e5d101a3256c634edf4e3ba2", + "published_at": "2026-06-09T14:54:35Z", + "state": "OFFICIAL_RELEASE" + } + ], + "tags": [ + { + "tag": "Y2JB-1.2.1", + "release_present": false, + "commit": "6210f98c23552e6eb3c4c8981f72a200bb6121b2", + "tree": "928ceb979881a063457270dde4961ea4a06bc1fa", + "source_archive_url": "https://api.github.com/repos/Gezine/Y2JB/zipball/Y2JB-1.2.1", + "source_archive_size": 142038, + "source_archive_sha256": "dd7b05234aec35696b6d00ea4420f7bc1e09d57a4fe64363fdaa6728a37b7201", + "source_archive_tree_files_matched": "20/20" + }, + { + "tag": "1.3", + "release_present": true, + "commit": "82e6bbf1935d5f41c3bd5bd9e6598cf90bb2e022", + "tree": "df29ffe4d8131b5f8b0da1874e34b95a41f0660d", + "source_archive_url": "https://api.github.com/repos/Gezine/Y2JB/zipball/1.3", + "source_archive_size": 185243, + "source_archive_sha256": "661993fbeec566bdfc0c5d9b89eeddc4a1f21f4a8c0b7392fddea4f73ce636d6", + "source_archive_tree_files_matched": "22/22" + }, + { + "tag": "1.4", + "release_present": false, + "commit": "02eda2e772df629ecfe5a46459769bd8c994ef3e", + "tree": "ec06403b45f7b68e01eb5053f81277dee4910dd2", + "source_archive_url": "https://api.github.com/repos/Gezine/Y2JB/zipball/1.4", + "source_archive_size": 151619, + "source_archive_sha256": "53d7fb1e665d0534d446cfe980841a0b8eed7eda35424c2f9c4abad32d5aaec5", + "source_archive_tree_files_matched": "19/19" + }, + { + "tag": "1.5", + "release_present": true, + "commit": "5fa556e6281229057dace3254b099f95261df386", + "tree": "db364529c347f34b8ddb186f57bb61dab91113db", + "source_archive_url": "https://api.github.com/repos/Gezine/Y2JB/zipball/1.5", + "source_archive_size": 149934, + "source_archive_sha256": "5d964faaf1b941c2fc8a006b10ae909b15d8f2c27d120e08b37196f1e464ad6d", + "source_archive_tree_files_matched": "19/19" + }, + { + "tag": "1.6", + "release_present": true, + "commit": "0dbbf4e7e0203af7e5d101a3256c634edf4e3ba2", + "tree": "c4344f43af7c268337437e6419548dba6f6bc211", + "source_archive_url": "https://api.github.com/repos/Gezine/Y2JB/zipball/1.6", + "source_archive_size": 150964, + "source_archive_sha256": "8385c439458c6954fd1198760ab0ef8245543235c83db90d041591362027cae4", + "source_archive_tree_files_matched": "19/19" + } + ], + "assets": [ + { + "release": "1.6", + "release_id": 336305480, + "asset_id": 442805805, + "name": "Y2JB_backup_1.6.12.20.zip", + "size": 498991521, + "sha256": "9c097ed9000792507bcf307eff0802f40ee64dc047ab6de3d38acccc62d39dcc", + "url": "https://github.com/Gezine/Y2JB/releases/download/1.6/Y2JB_backup_1.6.12.20.zip", + "created_at": "2026-06-09T14:52:35Z", + "updated_at": "2026-06-09T14:53:10Z", + "classification": "NO_MATCH", + "downloaded": false + }, + { + "release": "1.6", + "release_id": 336305480, + "asset_id": 442358421, + "name": "Y2JB_backup_1.6.4.03.zip", + "size": 504395044, + "sha256": "b01b4f442327f9eca90ffc4506dfa58249e4ac70cb9d7488c856c9e8dfaf37b4", + "url": "https://github.com/Gezine/Y2JB/releases/download/1.6/Y2JB_backup_1.6.4.03.zip", + "created_at": "2026-06-09T03:33:57Z", + "updated_at": "2026-06-09T03:34:40Z", + "classification": "NO_MATCH", + "downloaded": false + }, + { + "release": "1.6", + "release_id": 336305480, + "asset_id": 442358388, + "name": "Y2JB_download0_1.6.zip", + "size": 315229037, + "sha256": "d032954033c17acda47c41179a22fa09016ddd40a274f4e9afbb051e5d08d3bd", + "url": "https://github.com/Gezine/Y2JB/releases/download/1.6/Y2JB_download0_1.6.zip", + "created_at": "2026-06-09T03:33:55Z", + "updated_at": "2026-06-09T03:34:30Z", + "classification": "NO_MATCH", + "downloaded": false + }, + { + "release": "1.5", + "release_id": 327961476, + "asset_id": 429350761, + "name": "Y2JB_backup_1.5.zip", + "size": 503863926, + "sha256": "91bc9fb38e62d2b34d0009dbfd6a46dffb379277eef3915dd0a1956f65462a45", + "url": "https://github.com/Gezine/Y2JB/releases/download/1.5/Y2JB_backup_1.5.zip", + "created_at": "2026-05-25T12:46:49Z", + "updated_at": "2026-05-25T12:47:34Z", + "classification": "NO_MATCH", + "downloaded": false + }, + { + "release": "1.5", + "release_id": 327961476, + "asset_id": 429351154, + "name": "Y2JB_download0_1.5.zip", + "size": 336514975, + "sha256": "f7e5debb2e4c6b3fe7224e549c4aaf9b273a178543cd0bfe84e37bc0521d590a", + "url": "https://github.com/Gezine/Y2JB/releases/download/1.5/Y2JB_download0_1.5.zip", + "created_at": "2026-05-25T12:47:34Z", + "updated_at": "2026-05-25T12:47:56Z", + "classification": "NO_MATCH", + "downloaded": false + }, + { + "release": "1.3", + "release_id": 328911049, + "asset_id": 429415428, + "name": "Y2JB_backup_1.3.7z", + "size": 488117068, + "sha256": "e63a6b8fe134ad93b8a6a3b332b98810598aff7dfb6703a226523143c3844dfb", + "url": "https://github.com/Gezine/Y2JB/releases/download/1.3/Y2JB_backup_1.3.7z", + "created_at": "2026-05-25T14:27:41Z", + "updated_at": "2026-05-25T14:28:29Z", + "classification": "NO_MATCH", + "downloaded": false + }, + { + "release": "1.3", + "release_id": 328911049, + "asset_id": 429415862, + "name": "Y2JB_download0_1.3.zip", + "size": 336789664, + "sha256": "c64ef5bc508e4d81395928a8a54b2832f2b37c9cd1603ec9e7e3bd6f5ad6b526", + "url": "https://github.com/Gezine/Y2JB/releases/download/1.3/Y2JB_download0_1.3.zip", + "created_at": "2026-05-25T14:28:29Z", + "updated_at": "2026-05-25T14:29:18Z", + "classification": "NO_MATCH", + "downloaded": false + } + ], + "upstream_worktree": { + "path": "work/upstream/Y2JB-official", + "remote": "https://github.com/Gezine/Y2JB.git", + "tag": "1.6", + "commit": "0dbbf4e7e0203af7e5d101a3256c634edf4e3ba2", + "tree": "c4344f43af7c268337437e6419548dba6f6bc211", + "clean": true, + "detached": true, + "submodules": 0, + "git_lfs_pointers": 0, + "generated_loader_source_present": false + }, + "official_host_sender": { + "path": "payload_sender.py", + "size": 1064, + "sha256": "8c87920c41dbdbd66b9f36ca9509f0d6bef9170f351dd97ff831cfb98e642ec6", + "blob": "4ad80085bd8f2cf9ca3520d8ff1353b9c764fb75", + "originating_commit": "edf0d631dd6e4dbd5a3db54ebdae47934d8bd994", + "default_port": 50000, + "binary_mode": true, + "sendall": true, + "response_read": false, + "duplex": false, + "timeout": false, + "retry": false, + "maximum_size": null, + "hash_validation": false, + "deployed_use_attested": false + }, + "operator_attestation": { + "path": "docs/approvals/phase-0.9e-r-y2jb-deployed-use-attestation.md", + "attested": false, + "classification_if_completed": "OPERATOR_ATTESTED_DEPLOYED_USE", + "runtime_verified": false + }, + "phase09f_offline_design_allowed": false, + "authorization": { + "device_action_authorized": false, + "target_build_authorized": false, + "transfer_authorized": false, + "execution_authorized": false, + "installation_authorized": false, + "lifecycle_authorized": false, + "autoload_authorized": false, + "device_write_authorized": false, + "automatic_retry": false + }, + "actions": { + "official_network_used": true, + "large_release_assets_downloaded": 0, + "large_release_asset_bytes_downloaded": 0, + "source_archives_downloaded": 5, + "source_archive_bytes_downloaded": 779798, + "official_clone_created": true, + "downloaded_code_executed": false, + "dependency_installed": false, + "ps5_connected": false, + "ps5_ip_used": false, + "device_request_performed": false, + "files_transferred_to_or_from_ps5": false, + "target_build_performed": false, + "target_code_created": false, + "target_artifact_created": false, + "payload_created": false, + "device_client_created": false + }, + "temporary_research_storage": { + "path": "/chimera-gfx-phase09er", + "peak_file_count": 19, + "peak_bytes": 923698, + "cleaned": true, + "cleanup_verified": true + }, + "artifact_provenance": [ + { + "logical_name": "local_y2jb_outer_zip", + "artifact_role": "operator_supplied_backup_candidate", + "local_relative_path": null, + "size": 504159435, + "sha256": "805e3f87f0c371223619ffc7d3a7b3c0d41a1fae8a8b1171d9e2f162659e8291", + "file_type": "ZIP", + "source_repository": null, + "source_commit": null, + "build_identity": null, + "version": null, + "obtained_from": "local Downloads; Zone.Identifier indicates MediaFire", + "evidence_that_it_is_deployed_or_used": "none; operator attestation is empty", + "confidence": "UNKNOWN", + "immutable": true, + "executable": false, + "persistent_on_device": "unproven", + "transferred_per_session": "unproven", + "required_for_bootstrap": "unproven", + "required_for_recovery": "unproven" + }, + { + "logical_name": "local_y2jb_inner_archive", + "artifact_role": "opaque_console_backup_container", + "local_relative_path": null, + "size": 504365056, + "sha256": "6439834e8856d45b6d6fe699b74c35ca6985a199ea8ecf3e398c018d37be2d55", + "file_type": "SIECAF", + "source_repository": null, + "source_commit": null, + "build_identity": null, + "version": null, + "obtained_from": "single outer ZIP entry", + "evidence_that_it_is_deployed_or_used": "none", + "confidence": "UNKNOWN", + "immutable": true, + "executable": false, + "persistent_on_device": "unproven", + "transferred_per_session": "unproven", + "required_for_bootstrap": "unproven", + "required_for_recovery": "unproven" + }, + { + "logical_name": "official_y2jb_source_1_6", + "artifact_role": "official_release_source_tree", + "local_relative_path": "work/upstream/Y2JB-official", + "size": null, + "sha256": null, + "file_type": "GIT_TREE", + "source_repository": "https://github.com/Gezine/Y2JB", + "source_commit": "0dbbf4e7e0203af7e5d101a3256c634edf4e3ba2", + "build_identity": "tree c4344f43af7c268337437e6419548dba6f6bc211", + "version": "1.6", + "obtained_from": "official GitHub repository", + "evidence_that_it_is_deployed_or_used": "none", + "confidence": "REFERENCE_ONLY", + "immutable": true, + "executable": false, + "persistent_on_device": "unproven", + "transferred_per_session": "unproven", + "required_for_bootstrap": "source architecture only", + "required_for_recovery": false + }, + { + "logical_name": "official_payload_sender_1_6", + "artifact_role": "official_host_sender", + "local_relative_path": "work/upstream/Y2JB-official/payload_sender.py", + "size": 1064, + "sha256": "8c87920c41dbdbd66b9f36ca9509f0d6bef9170f351dd97ff831cfb98e642ec6", + "file_type": "PYTHON_SOURCE", + "source_repository": "https://github.com/Gezine/Y2JB", + "source_commit": "0dbbf4e7e0203af7e5d101a3256c634edf4e3ba2", + "build_identity": "git blob 4ad80085bd8f2cf9ca3520d8ff1353b9c764fb75", + "version": "1.6", + "obtained_from": "official Git tree", + "evidence_that_it_is_deployed_or_used": "none", + "confidence": "REFERENCE_ONLY", + "immutable": true, + "executable": true, + "persistent_on_device": false, + "transferred_per_session": false, + "required_for_bootstrap": "host workflow only", + "required_for_recovery": "unproven" + }, + { + "logical_name": "official_remote_js_loader_1_6", + "artifact_role": "official_dynamic_port_javascript_listener", + "local_relative_path": "work/upstream/Y2JB-official/download0/cache/splash_screen/aHR0cHM6Ly93d3cueW91dHViZS5jb20vdHY=/remotejsloader.js", + "size": 7132, + "sha256": "30cc6d1535549b2a49b47a9e0c85a3444cf84177be54398691694b7c6505f38e", + "file_type": "JAVASCRIPT_SOURCE", + "source_repository": "https://github.com/Gezine/Y2JB", + "source_commit": "0dbbf4e7e0203af7e5d101a3256c634edf4e3ba2", + "build_identity": "git blob dd4957530e7a88c889877a7dcb3433642a71e019", + "version": "1.6", + "obtained_from": "official Git tree", + "evidence_that_it_is_deployed_or_used": "none", + "confidence": "REFERENCE_ONLY", + "immutable": true, + "executable": true, + "persistent_on_device": "source architecture says appdata/cache; deployed use unproven", + "transferred_per_session": false, + "required_for_bootstrap": true, + "required_for_recovery": "possible" + }, + { + "logical_name": "official_embedded_elfldr_1_6", + "artifact_role": "embedded_port_9021_loader_binary", + "local_relative_path": "work/upstream/Y2JB-official/download0/cache/splash_screen/aHR0cHM6Ly93d3cueW91dHViZS5jb20vdHY=/elfldr-ps5-1340.elf", + "size": 397000, + "sha256": "30478bcadb6439e1247451c4ac706b6e1385044dd0f12d6486b6d7057929453b", + "file_type": "ELF64_X86_64_PIE", + "source_repository": "https://github.com/Gezine/Y2JB", + "source_commit": null, + "build_identity": "binary blob in Y2JB release commit 0dbbf4e7e0203af7e5d101a3256c634edf4e3ba2", + "version": "Y2JB 1.6 embedded binary", + "obtained_from": "official Git tree", + "evidence_that_it_is_deployed_or_used": "none", + "confidence": "REFERENCE_ONLY", + "immutable": true, + "executable": true, + "persistent_on_device": "unproven", + "transferred_per_session": "source architecture loads from sandbox", + "required_for_bootstrap": true, + "required_for_recovery": "possible" + } + ], + "blocking_state": { + "payload_manager_backup": "HARD_BLOCKER_FOR_INSTALLATION", + "independent_recovery": "UNPROVEN", + "runtime_firmware_9_60": "UNPROVEN" + } +} diff --git a/manifests/runtime/phase-0.9e-r-release-correlation.schema.json b/manifests/runtime/phase-0.9e-r-release-correlation.schema.json new file mode 100644 index 0000000..40b1915 --- /dev/null +++ b/manifests/runtime/phase-0.9e-r-release-correlation.schema.json @@ -0,0 +1,90 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "phase-0.9e-r-release-correlation.schema.json", + "type": "object", + "required": [ + "schema_version", + "phase", + "status", + "baseline_commit", + "release_correlation", + "final_classification", + "local_candidate", + "inner_archive", + "release_inventory", + "tags", + "assets", + "upstream_worktree", + "official_host_sender", + "operator_attestation", + "phase09f_offline_design_allowed", + "authorization", + "actions", + "artifact_provenance", + "blocking_state" + ], + "properties": { + "schema_version": { "const": 1 }, + "phase": { "const": "PHASE_0_9E_R_OFFICIAL_Y2JB_CORRELATION" }, + "status": { "const": "DESIGN_ONLY" }, + "baseline_commit": { + "const": "3d3151bd12ff0f786c1e1b9af75d7174408e3d2f" + }, + "release_correlation": { + "enum": [ + "OFFICIAL_RELEASE_BYTE_MATCH", + "OFFICIAL_RELEASE_METADATA_MATCH", + "OFFICIAL_RELEASE_NO_MATCH", + "OFFICIAL_RELEASE_UNRESOLVED" + ] + }, + "final_classification": { + "enum": [ + "PROVENANCE_READY_FOR_OPERATOR_BINDING", + "OFFICIAL_ASSET_IDENTIFIED_IMPLEMENTATION_PARTIAL", + "OFFICIAL_ASSET_IDENTIFIED_IMPLEMENTATION_MISSING", + "LOCAL_BACKUP_NOT_CORRELATED", + "OFFICIAL_RELEASE_RESEARCH_INCOMPLETE" + ] + }, + "identified_release": { "type": ["string", "null"] }, + "identified_release_asset": { "type": ["string", "null"] }, + "local_candidate": { + "type": "object", + "required": ["file_name", "size", "sha256"], + "properties": { + "file_name": { "const": "Y2JB-Autoloader-403-1240.zip" }, + "size": { "const": 504159435 }, + "sha256": { + "const": "805e3f87f0c371223619ffc7d3a7b3c0d41a1fae8a8b1171d9e2f162659e8291" + } + } + }, + "inner_archive": { + "type": "object", + "required": ["size", "sha256", "magic", "classification"], + "properties": { + "size": { "const": 504365056 }, + "sha256": { + "const": "6439834e8856d45b6d6fe699b74c35ca6985a199ea8ecf3e398c018d37be2d55" + }, + "magic": { "const": "SIECAF" }, + "classification": { "const": "OPAQUE_UNBOUND" } + } + }, + "release_inventory": { "type": "array", "minItems": 6 }, + "tags": { "type": "array", "minItems": 5 }, + "assets": { "type": "array", "minItems": 7 }, + "upstream_worktree": { "type": "object" }, + "official_host_sender": { "type": "object" }, + "operator_attestation": { "type": "object" }, + "phase09f_offline_design_allowed": { "const": false }, + "authorization": { + "type": "object", + "additionalProperties": { "const": false } + }, + "actions": { "type": "object" }, + "artifact_provenance": { "type": "array", "minItems": 6 }, + "blocking_state": { "type": "object" } + } +} diff --git a/manifests/runtime/phase-0.9e-r2-inner-correlation.json b/manifests/runtime/phase-0.9e-r2-inner-correlation.json new file mode 100644 index 0000000..4e9fe42 --- /dev/null +++ b/manifests/runtime/phase-0.9e-r2-inner-correlation.json @@ -0,0 +1,477 @@ +{ + "schema_version": 1, + "phase": "PHASE_0_9E_R2_INNER_BACKUP_CORRELATION", + "status": "HOST_ONLY_COMPLETE", + "date": "2026-07-19", + "baseline_commit": "48714bb542f3ea5b9893d0461ba4c9d938cd6d8d", + "prior_phase_classification": "LOCAL_BACKUP_NOT_CORRELATED", + "primary_provenance_classification": "LOCAL_BACKUP_UNCORRELATED", + "outer_zip_official": false, + "outer_provenance": "MEDIAFIRE_OBJECT_METADATA_BOUND", + "inner_provenance": "UNBOUND", + "runtime_deployment_verified": false, + "current_device_contents_verified": false, + "runtime_firmware_9_60": "UNPROVEN", + "local_candidate": { + "outer": { + "name": "Y2JB-Autoloader-403-1240.zip", + "size": 504159435, + "sha256": "805e3f87f0c371223619ffc7d3a7b3c0d41a1fae8a8b1171d9e2f162659e8291", + "creation_utc": "2026-06-23T14:10:39.0466596Z", + "modification_utc": "2026-06-23T14:11:10.6992328Z", + "zip_comment": "" + }, + "inner": { + "entry": "PS5/EXPORT/BACKUP/202606102126_00/archive.dat", + "size": 504365056, + "compressed_size": 504158629, + "crc32": "522808c8", + "sha256": "6439834e8856d45b6d6fe699b74c35ca6985a199ea8ecf3e398c018d37be2d55", + "format": "SIECAF" + } + }, + "local_download_provenance": { + "classification": "MEDIAFIRE_URL_EXACT", + "zone_identifier": { + "stream_size": 327, + "stream_sha256": "70928a3d66cbb9f584596414c4fe535b498dd5ed7c5f55bbeae4efeb146ea841", + "section": "ZoneTransfer", + "zone_id": 3, + "referrer_url": { + "host": "www.mediafire.com", + "path": "/", + "query_present": false, + "full_value_sha256": "9a0ff5a3e34286b2e53192003fca8b6c7b00ad6df3c681765da04ccaa54c0919" + }, + "host_url": { + "host": "download2434.mediafire.com", + "path_redacted": "//jq3fcutuwbb1mrb/Y2JB-Autoloader-403-1240.zip%00", + "query_present": false, + "full_value_sha256": "a39f05923d0a5368ea5138c1974ac109bb09f8ab795c6edb8e470d8dfe4d784c" + }, + "mediafire_file_key": "jq3fcutuwbb1mrb", + "last_writer_package_family_name": null, + "app_zone_id": null, + "other_fields": [] + }, + "browser_history": { + "classification": "NO_BROWSER_HISTORY_MATCH", + "search_scope": [ + "Chrome Default", + "Chrome System Profile", + "Edge Default" + ], + "absent_browsers": [ + "Brave", + "Chromium", + "Opera", + "Opera GX" + ], + "matching_records": 0, + "full_history_exported": false, + "original_databases_modified": false, + "temporary_read_only_copies_created": 3, + "temporary_copies_cleaned": true + } + }, + "mediafire_object": { + "page": "https://www.mediafire.com/file/jq3fcutuwbb1mrb/Y2JB-Autoloader-403-1240.zip/file", + "page_found": true, + "page_html_size": 333321, + "page_html_sha256": "655e4c3c2e5c423019b0195ca863ee8906f7ca02601b773b39688a590bf7fcb7", + "displayed_name": "Y2JB-Autoloader-403-1240.zip", + "displayed_size": "480.8MB", + "exact_server_byte_count_displayed": false, + "uploaded_display_value": "2026-06-11 15:32:16", + "uploaded_country_display_value": "United Kingdom", + "public_uploader_or_account": null, + "user_description": null, + "linked_tutorial_or_maker": null, + "direct_download_identity_contains_file_key": true, + "local_ads_contains_same_file_key_and_name": true, + "redownload_performed": false, + "classification": "MEDIAFIRE_OBJECT_METADATA_BOUND", + "byte_binding_reason": "No server digest or exact server byte count was exposed and no duplicate MediaFire download was needed or performed." + }, + "official_candidate": { + "repository": "https://github.com/Gezine/Y2JB", + "release": "Y2JB 1.6", + "tag": "1.6", + "release_commit": "0dbbf4e7e0203af7e5d101a3256c634edf4e3ba2", + "asset_id": 442358421, + "asset_name": "Y2JB_backup_1.6.4.03.zip", + "asset_url": "https://github.com/Gezine/Y2JB/releases/download/1.6/Y2JB_backup_1.6.4.03.zip", + "official_size": 504395044, + "official_sha256": "b01b4f442327f9eca90ffc4506dfa58249e4ac70cb9d7488c856c9e8dfaf37b4", + "local_download_size": 504395044, + "local_download_sha256": "b01b4f442327f9eca90ffc4506dfa58249e4ac70cb9d7488c856c9e8dfaf37b4", + "download_attempts": 1, + "resume": false, + "automatic_retry": false, + "download_valid": true, + "executed": false, + "restored": false, + "zip_comment": "", + "entries": [ + { + "name": "PS5/", + "compressed_size": 0, + "uncompressed_size": 0, + "crc32": "00000000", + "compress_type": 0, + "timestamp": "2026-06-09T04:15:20", + "create_system": 3, + "create_version": 20, + "extract_version": 20, + "flag_bits": 0, + "internal_attr": 0, + "external_attr": 1107230720, + "header_offset": 0, + "comment_hex": "", + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "name": "PS5/EXPORT/", + "compressed_size": 0, + "uncompressed_size": 0, + "crc32": "00000000", + "compress_type": 0, + "timestamp": "2026-06-09T04:15:20", + "create_system": 3, + "create_version": 20, + "extract_version": 20, + "flag_bits": 0, + "internal_attr": 0, + "external_attr": 1107230720, + "header_offset": 66, + "comment_hex": "", + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "name": "PS5/EXPORT/BACKUP/", + "compressed_size": 0, + "uncompressed_size": 0, + "crc32": "00000000", + "compress_type": 0, + "timestamp": "2026-06-09T04:15:20", + "create_system": 3, + "create_version": 20, + "extract_version": 20, + "flag_bits": 0, + "internal_attr": 0, + "external_attr": 1107230720, + "header_offset": 139, + "comment_hex": "", + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "name": "PS5/EXPORT/BACKUP/202606090510_00/", + "compressed_size": 0, + "uncompressed_size": 0, + "crc32": "00000000", + "compress_type": 0, + "timestamp": "2026-06-09T04:15:20", + "create_system": 3, + "create_version": 20, + "extract_version": 20, + "flag_bits": 0, + "internal_attr": 0, + "external_attr": 1107230720, + "header_offset": 219, + "comment_hex": "", + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "name": "PS5/EXPORT/BACKUP/202606090510_00/archive.dat", + "compressed_size": 504394122, + "uncompressed_size": 504496128, + "crc32": "7db3b89a", + "compress_type": 8, + "timestamp": "2026-06-09T05:15:58", + "create_system": 3, + "create_version": 20, + "extract_version": 20, + "flag_bits": 8, + "internal_attr": 0, + "external_attr": 2176188416, + "header_offset": 315, + "comment_hex": "", + "sha256": "3e1f0867d5d8362cbf722d51d0e68c26243d6b33d78460d978580b510c854212" + } + ], + "inner_comparison": { + "classification": "INNER_ARCHIVE_HASH_MISMATCH", + "official_inner_size": 504496128, + "official_inner_sha256": "3e1f0867d5d8362cbf722d51d0e68c26243d6b33d78460d978580b510c854212", + "local_inner_size": 504365056, + "local_inner_sha256": "6439834e8856d45b6d6fe699b74c35ca6985a199ea8ecf3e398c018d37be2d55", + "full_byte_equal": false, + "bytes_compared": 504496128 + } + }, + "community_families": [ + { + "repository": "https://github.com/itsPLK/ps5-y2jb-autoloader", + "relevant_tag": "v0.8-a0146a2", + "commit": "a0146a2a9229fa5df5b5127257bf2e11ffc6d2de", + "tree": "c128bafe81ea0819a5d5571a38df75fb8b9bac0c", + "published_at": "2026-06-10T09:05:51Z", + "firmware_range": "4.03-12.70", + "system_backup_distributed_by_project": false, + "release_assets": [ + { + "name": "download0.dat", + "size": 336789504, + "sha256": "268c82f7a8f581c804949a86c259c982953ff479c13a3dc9f9da02c354b32d6e" + }, + { + "name": "y2jb_update.zip", + "size": 1152857, + "sha256": "301599b05e7eb7224025ef9ed6ebc896d86d2c14bba5c32ced5a88bdf2c3517e" + } + ], + "y2jb_basis": "Fork declared; exact base commit not declared at this tag", + "lapse_identity": "git blob 190996c683498fd3dddefbd48bb5ea73b7598299", + "p2jb_identity": "git blob 510bf4b5cbb05bc41f06f20ec28e114de162866f", + "elfldr_identity": "submodule commit 148b71c2fb9155d2550ef6a14eb03433e23acaeb", + "payload_manager_identity": "submodule commit 5d33ce1c8bfef0c765aa3c4cfc7e4537dc706288", + "autoload_behavior": "Kernel exploit and localhost-only elfldr load automatically; user autoload.txt controls later payloads; Payload Manager is included but not proof of local-backup content.", + "expected_backup_names": [], + "classification": "THIRD_PARTY_BUILD_FROM_PUBLIC_SOURCE_POSSIBLE", + "match_reason": "Firmware-range and autoloader naming align; tag predates the local inner timestamp; README credits Rush for backup creation.", + "exclusion_reason": "Project explicitly says it does not distribute system backups and no public asset or hash binds the MediaFire object.", + "large_backup_downloaded": false + }, + { + "repository": "https://github.com/owendswang/Y2JB-P2JB-bdj_unpatch", + "relevant_tag": "v1.4-autoloader", + "commit": "9990e917cdb24c249ce24f4ff18e6e869e99cf51", + "tree": "940f68cf87470840647d2dea54981cf9380f0c8a", + "published_at": "2026-06-10T09:01:19Z", + "firmware_range": "9.00-12.40 disc edition in tag README; asset is named for 7.61", + "system_backup_distributed_by_project": true, + "y2jb_basis": "Gezine/Y2JB declared; exact base commit not declared", + "lapse_identity": "payloads/lapse.js blob 942fa0985b7bae97477fc81522093ff723b67fc4", + "p2jb_identity": "payloads/p2jb-matem6.js blob 67f14fa020fc8ce32ffece624cda942174c65452", + "elfldr_identity": "embedded elfldr-ps5-0.23.elf blob 64271848265ddc64359cced28ae4ce8f956a8ca5", + "payload_manager_identity": null, + "autoload_behavior": "Release is explicitly an autoloader backup; exact live behavior was not executed or inferred beyond source and release text.", + "release_assets": [ + { + "asset_id": 443441800, + "name": "download0_Y2JB+autoloader-20260609.zip", + "size": 333151616, + "sha256": "57b0b6f6702cbf8ab5528bab0a341e2ff3c04282067fab92de210e13ef59b5af" + }, + { + "asset_id": 443444036, + "name": "PS5-12.20-backup-Y2JB+autoloader-20260609-en.zip", + "size": 498991549, + "sha256": "dd3c2ed7b4f7e6a7d78f3188271aca4cd98eb7f0a65a0c9986de7d39d7851955" + }, + { + "asset_id": 443442879, + "name": "PS5-7.61-backup-Y2JB+autoloader-20260609-en.zip", + "size": 504425435, + "sha256": "8552d7c3c7bb3f078d967f56dfe1b488b37e3ed13e10c5fe97ae222482d56897" + } + ], + "match_reason": "The June 9/10 autoloader backup is date-adjacent and size-adjacent to the local archive.", + "exclusion_reason": "The downloaded community inner differs in size, SHA-256, every byte comparison, version, segment count, normalized layout, and normalized hash blocks.", + "large_backup_downloaded": true, + "downloaded_asset": { + "asset_id": 443442879, + "name": "PS5-7.61-backup-Y2JB+autoloader-20260609-en.zip", + "official_size": 504425435, + "official_sha256": "8552d7c3c7bb3f078d967f56dfe1b488b37e3ed13e10c5fe97ae222482d56897", + "local_size": 504425435, + "local_sha256": "8552d7c3c7bb3f078d967f56dfe1b488b37e3ed13e10c5fe97ae222482d56897", + "download_attempts": 1, + "resume": false, + "automatic_retry": false, + "executed": false, + "restored": false, + "inner_entry": "PS5/EXPORT/BACKUP/202606100638_00/archive.dat", + "inner_size": 504627200, + "inner_sha256": "d4f9b1ba4e32af175a3431a7734085d5420370ebb79f9a32852eb824a90e190e", + "inner_crc32": "cf138df3", + "inner_compressed_size": 504424629, + "inner_comparison": "INNER_ARCHIVE_HASH_MISMATCH", + "full_byte_equal": false, + "bytes_compared": 504627200 + } + }, + { + "repository": "https://github.com/lz-anonz/Y2X", + "relevant_tag": "v1.3", + "commit": "2ae881259be647738713b83a6a91ffe693ccf9af", + "tree": "6d3baaf099d81760bd259af88b34d2fb26083e65", + "published_at": "2026-06-10T21:58:27Z", + "firmware_range": "4.03-12.70", + "system_backup_distributed_by_project": false, + "release_assets": [ + { + "asset_id": 444080103, + "name": "USB-ps5_autoloader.rar", + "size": 904337, + "sha256": "9ca55573d9c5c463a227f95524d818fc0f4522a8eed481e886192f13cf1fad20" + }, + { + "asset_id": 444106189, + "name": "v1.3-download0-Y2X-P2JB+Lapse.rar", + "size": 521801, + "sha256": "48340487be1a0ec9c9d1faea4187c8005a754ddb93a62e8713ac2777a9df01a6" + } + ], + "y2jb_basis": "Fork declared; exact base commit not declared", + "lapse_identity": "src/v1 lapse.js blob dbd48d50ff8263f6e87f71a457e8d52175c10f74", + "p2jb_identity": "src/v1 p2jb.js blob 8a038ca76e9291e0dea4a871a05b7239aed61b05", + "elfldr_identity": "v1 elfldr blob fff162517d12af4ab44064a05992499187c757ba; v2 elfldr blob 276ecf71d5e384f997242ad8949b33a62793bbaf", + "payload_manager_identity": "pldmgr_v0.3.1.elf blob c49bf9e18ccb23ebbf6ab42927276eb313058e8d", + "autoload_behavior": "v1 permits USB payload autoload; v2 automatically loads Payload Manager and FTP before closing YouTube; README warns against adding kstuff to v2 autoload.", + "expected_backup_names": [], + "classification": "NO_PUBLIC_SYSTEM_BACKUP_ASSET_AT_RELEVANT_RELEASE", + "match_reason": "Firmware range and publication date overlap the local filename and inner timestamp.", + "exclusion_reason": "Current v1.3 assets are only sub-megabyte USB/download0 archives; README backup wording is not a public byte identity.", + "large_backup_downloaded": false + } + ], + "community_download_zip_entries": [ + { + "name": "PS5/", + "compressed_size": 0, + "uncompressed_size": 0, + "crc32": "00000000", + "compress_type": 0, + "timestamp": "2026-06-10T13:51:48", + "create_system": 0, + "create_version": 31, + "extract_version": 10, + "flag_bits": 0, + "external_attr": 16, + "header_offset": 0, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "name": "PS5/EXPORT/", + "compressed_size": 0, + "uncompressed_size": 0, + "crc32": "00000000", + "compress_type": 0, + "timestamp": "2026-06-10T13:51:52", + "create_system": 0, + "create_version": 31, + "extract_version": 10, + "flag_bits": 0, + "external_attr": 16, + "header_offset": 34, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "name": "PS5/EXPORT/BACKUP/", + "compressed_size": 0, + "uncompressed_size": 0, + "crc32": "00000000", + "compress_type": 0, + "timestamp": "2026-06-10T13:52:00", + "create_system": 0, + "create_version": 31, + "extract_version": 10, + "flag_bits": 0, + "external_attr": 16, + "header_offset": 75, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "name": "PS5/EXPORT/BACKUP/202606100638_00/", + "compressed_size": 0, + "uncompressed_size": 0, + "crc32": "00000000", + "compress_type": 0, + "timestamp": "2026-06-10T13:52:00", + "create_system": 0, + "create_version": 31, + "extract_version": 10, + "flag_bits": 0, + "external_attr": 16, + "header_offset": 123, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "name": "PS5/EXPORT/BACKUP/202606100638_00/archive.dat", + "compressed_size": 504424629, + "uncompressed_size": 504627200, + "crc32": "cf138df3", + "compress_type": 8, + "timestamp": "2026-06-10T13:39:42", + "create_system": 0, + "create_version": 31, + "extract_version": 20, + "flag_bits": 0, + "external_attr": 32, + "header_offset": 187, + "sha256": "d4f9b1ba4e32af175a3431a7734085d5420370ebb79f9a32852eb824a90e190e" + } + ], + "siecaf": { + "fingerprints_manifest": "manifests/runtime/phase-0.9e-r2-siecaf-fingerprints.json", + "parser_source": "tools/inspect_siecaf_header.py", + "public_layout_commit": "36d014672bc87577a6e0d750c2cccadc3fae0854", + "public_header_blob": "fdbc368353a7797464873ada306f0297257eb95e", + "local_vs_official": "SIECAF_LAYOUT_DIFFERENT", + "local_vs_community": "SIECAF_LAYOUT_DIFFERENT", + "official_vs_community": "SIECAF_LAYOUT_DIFFERENT", + "decryption_attempted": false, + "content_extraction_attempted": false, + "ps5_bar_tool_executed": false + }, + "phase09f_offline_design_reconsideration_allowed": false, + "phase09f_reason": "Neither inner bytes nor MediaFire maker/source are bound, and the actual deployed bootstrap implementation remains unaudited.", + "authorization": { + "device_action_authorized": false, + "target_build_authorized": false, + "transfer_authorized": false, + "execution_authorized": false, + "installation_authorized": false, + "lifecycle_authorized": false, + "autoload_authorized": false, + "device_write_authorized": false, + "rescue_payload_design_authorized": false, + "automatic_retry": false + }, + "blocking_state": { + "payload_manager_backup": "HARD_BLOCKER_FOR_INSTALLATION", + "independent_recovery": "UNPROVEN", + "actual_bootstrap_implementation": "UNPROVEN", + "local_backup_source_family": "UNBOUND", + "runtime_firmware_9_60": "UNPROVEN" + }, + "actions": { + "official_large_backup_downloads": 1, + "community_large_backup_downloads": 1, + "mediafire_large_backup_downloads": 0, + "large_backup_bytes_downloaded": 1008820479, + "download_resume_used": false, + "automatic_retry_used": false, + "downloaded_file_executed": false, + "downloaded_file_restored": false, + "ps5_bar_tool_executed": false, + "ps5_connected": false, + "ps5_ip_used": false, + "device_request_performed": false, + "files_transferred_to_or_from_ps5": false, + "target_build_performed": false, + "target_code_created": false, + "target_artifact_created": false, + "payload_created": false, + "device_client_created": false + }, + "temporary_research_storage": { + "peak_file_count": 12, + "peak_bytes": 1029326564, + "large_files_tracked": false, + "browser_database_copies_tracked": false, + "cleaned": true, + "cleanup_verified": true + } +} diff --git a/manifests/runtime/phase-0.9e-r2-siecaf-fingerprints.json b/manifests/runtime/phase-0.9e-r2-siecaf-fingerprints.json new file mode 100644 index 0000000..2c392c1 --- /dev/null +++ b/manifests/runtime/phase-0.9e-r2-siecaf-fingerprints.json @@ -0,0 +1,4420 @@ +{ + "archives": { + "community_owendswang_v1_4_autoloader_7_61": { + "classification": "SIECAF_VALID_STRUCTURE", + "duplicate_hash_section_ids": [], + "duplicate_metadata_section_ids": [], + "duplicate_metadata_section_keys": [], + "errors": [], + "gaps": [], + "header": { + "file_offset": 65536, + "file_size": 504561664, + "iv_12_hex": "825a7e00835a7e00845a7e00", + "key_or_unknown_16_redacted": true, + "key_or_unknown_16_sha256": "298bf95f03a1ecb014fb2a4d2aad0090b899bb22bd62b3f85e69d904fea30349", + "magic_ascii": "SIECAF", + "magic_hex": "5349454341460000", + "mode_i32": 1, + "padding_1_u32": 0, + "padding_2_u32": 0, + "padding_3_u32": 0, + "raw_sha256": "db2a9677f051b372e208261d767ac45f653374426a5594ce3306568138ebd3ae", + "segment_count": 58, + "unknown_u64": 1, + "version_i32": 6 + }, + "normalized_hash_blocks_sha256": "434b209865af8c5815b1277073db59348fddf65086908b2fd00dee5324459bcb", + "normalized_layout_sha256": "be7fdf87361c17fa52d3b4b263f4ee0cce69e924f8dab0ab66c554b4d9557549", + "normalized_segment_table_sha256": "f0aed4ae115866010902671a511983c3e6e21d1a7ab187bae7f36566979beb89", + "overlaps": [], + "repeated_metadata_section_ids": [ + 10050 + ], + "section_hashes": [ + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "ed78a9c18e093cb0468bfdf95a20ca21ba509864273d48427c8c10c54ed61efc", + "section_hash_128_hex": "faeb6e17b3b87394ef61c3a8e3c057b8", + "section_id": 0, + "section_type": 0, + "table_index": 0 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "0123f777dcac528e1808516765761f552d80a71245ddb6468349e7614cfdc9ea", + "section_hash_128_hex": "a4c5a889d0c153c5d1e57069c3bbe958", + "section_id": 1, + "section_type": 0, + "table_index": 1 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "8ab5a8d1ad6a41bb17f0d5e51783a6a5298515d4f792c420d067011c51adf08f", + "section_hash_128_hex": "82591bea2a5427b38bc433205452b1a4", + "section_id": 2, + "section_type": 0, + "table_index": 2 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "b5e44cc32de7cb359ae3b91d601dee7a4983752d8528bbb62a8399491b8fb8d4", + "section_hash_128_hex": "4ee6f95a7a602dfc095c576a3ef1d462", + "section_id": 3, + "section_type": 0, + "table_index": 3 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "77da224d9cc441fdd6e6b1e19e82fb885ddcaeaf3fa13435195d746fb3ffbdb2", + "section_hash_128_hex": "dccc5901d40dcbf07c2867a7fa5f96ad", + "section_id": 4, + "section_type": 0, + "table_index": 4 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "8a83fef1af4d25a4cfe9516b5bec890da8f0d09c7659b301d3efd93dbe0e4bf1", + "section_hash_128_hex": "cc0c88c1ba8c5d6c535aa0f194386f8c", + "section_id": 5, + "section_type": 0, + "table_index": 5 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "a52c36dcf661ab430fa73f1f5040b22ec6f37d2658a997dc8c42def3d22d230d", + "section_hash_128_hex": "7e93e08f3dd9f7ccca0cd338ae6b4ffa", + "section_id": 6, + "section_type": 0, + "table_index": 6 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "2a862ebeb2c85a5fef3f4ff5917b458579503d4756d3af693bb9e9451955740b", + "section_hash_128_hex": "9ee9744a87e70271eb9b57c1a2634bde", + "section_id": 7, + "section_type": 0, + "table_index": 7 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "f6071bd729c54c14ac694412b66f9ae5d4aeeb3c21082eb7ef884323b999493f", + "section_hash_128_hex": "bee9a2f50757fe57db82240936b06d79", + "section_id": 8, + "section_type": 0, + "table_index": 8 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "737ddf9c52622cd2540225ded5c691734d29700434e3f07068236460a42fd5c3", + "section_hash_128_hex": "9e9f3f5a46415fa8bbdb0533c670f008", + "section_id": 9, + "section_type": 0, + "table_index": 9 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "313a8fdc862ce4c9bd79fe8e7c9b6a1a03bb31cbc24f9c9a638157635b5e50de", + "section_hash_128_hex": "54a7132dfa75f90efcf9f3fc4edd5ffe", + "section_id": 10, + "section_type": 0, + "table_index": 10 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "2bb46f772c2b9a8609e3e5b0ba30050c2cd14d2fbc5f53aae092735db4489630", + "section_hash_128_hex": "728989b02747423a925f629f131c3360", + "section_id": 11, + "section_type": 0, + "table_index": 11 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "b1290b37facfe465d8b7f27fc8a45d48060fd361efadab2d72d3a7a2ce8c0573", + "section_hash_128_hex": "3aaeecf5e8295b2ace827065630bf8fd", + "section_id": 12, + "section_type": 0, + "table_index": 12 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "71cbc0b0a56d4c2770727801a4baab3c51493e76218f9a6a78ba291a078e61b4", + "section_hash_128_hex": "8c0cd2bdf6d427df756d4d5548e50070", + "section_id": 13, + "section_type": 0, + "table_index": 13 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "d975e55958b00b8d3b794a6d6972431c12e038265a0b014bb91bc9df1b2c5822", + "section_hash_128_hex": "827757c0bbb28b0a9ddd732e3e545ae0", + "section_id": 14, + "section_type": 0, + "table_index": 14 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "b4dd81a8cb936ced8f6e13fc414ad525df453f3ae074725abcad357e072b11bb", + "section_hash_128_hex": "eb76921c7c6da9493ed5c7bb7050323f", + "section_id": 15, + "section_type": 0, + "table_index": 15 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "c299433d5e832a89060ed77bb85acf81220a13d565b428107b80032af45270fc", + "section_hash_128_hex": "9a8171bebdb751c900d60cf102ef342a", + "section_id": 16, + "section_type": 0, + "table_index": 16 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "e99e20b155537012776fcf4262869be25368c2278c691515c282aedd41a5ac86", + "section_hash_128_hex": "76b5f659e7ad6a9c4bf0fc950afce57b", + "section_id": 17, + "section_type": 0, + "table_index": 17 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "a528744d4d91eada9d919187a807dd4b52dc07f0f8310cb3c5b0fb471355a197", + "section_hash_128_hex": "ccdc0162cef49cc2849b7a77fc38a1dc", + "section_id": 18, + "section_type": 0, + "table_index": 18 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "703820f71de72501abf643c1081e3808e63f9b0ebe78d931abedc0f000500a90", + "section_hash_128_hex": "f3ed677d0e33189bab587d106d45b439", + "section_id": 19, + "section_type": 0, + "table_index": 19 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "2b293cbb3153492eb2629137d682aa685a7357c692ee54344604ca14b486b92e", + "section_hash_128_hex": "d213380a34bdeab68694a2224757c6a2", + "section_id": 20, + "section_type": 0, + "table_index": 20 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "aad3a9227807d8eee52c6d2fb2dbd16ad4713d079af28005194a8593b034402f", + "section_hash_128_hex": "17319d36aa30ee0fa09316fd4540bd8c", + "section_id": 21, + "section_type": 0, + "table_index": 21 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "e6dbdfa466d67e1ec255a99859eb01a79915b7f620de10a29a63e3316606a837", + "section_hash_128_hex": "b84f4bede32ce07cb9aaa98cd137ded7", + "section_id": 22, + "section_type": 0, + "table_index": 22 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "217e6d6c5b5b8c7876d75c382f105735141e0837ae2fd900045a94624635a60f", + "section_hash_128_hex": "ab6fec13e48fd5f33a65752e7f70d567", + "section_id": 23, + "section_type": 0, + "table_index": 23 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "055a9385d5f9556267b905bda294bb3c0614465a2e37c88c2a2addbda98b2860", + "section_hash_128_hex": "4d60731c1338ffc7a5ef8911dad44fc4", + "section_id": 24, + "section_type": 0, + "table_index": 24 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "ee3a58bd6186082becba88a00d2c663ca8a1bead492ee0747ba8c749dd64960d", + "section_hash_128_hex": "085850ede0aff59800f480983a42f309", + "section_id": 25, + "section_type": 0, + "table_index": 25 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "551e1fc94055ef31d279ab5975260bd227169a7243fccbefdfb2d0938230d2c8", + "section_hash_128_hex": "a2022db18e09b0d58531a2d889b579dd", + "section_id": 26, + "section_type": 0, + "table_index": 26 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "ee85cabe8c610c39f6afe06e1355ab732de8b89c8a5a3a2a471fdf473d4033b0", + "section_hash_128_hex": "8d2b755f718563265d3ee7d7cf33578e", + "section_id": 27, + "section_type": 0, + "table_index": 27 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "00260d604aacdd8441a699139f3878ec393ae1fe17c473d64a2872353c6b3581", + "section_hash_128_hex": "3e7878bdf0ad639f24e68912e53ebd34", + "section_id": 28, + "section_type": 0, + "table_index": 28 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "de48b145b0b4e2c96a3ec2a783dfea02d86a064d79d905871578c2f10f494176", + "section_hash_128_hex": "0c229f00ad67434179223609f8a33fed", + "section_id": 29, + "section_type": 0, + "table_index": 29 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "76018d7f59acb910f0bdfa94911eef21e4bebd24970beec284f57ac748b99ee5", + "section_hash_128_hex": "cef903046cf8fe6f1a28d022856f5a03", + "section_id": 30, + "section_type": 0, + "table_index": 30 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "bc273d87ded8283716505c587273d86a024c2e9dfae90c6d8b0d1587531e1a2b", + "section_hash_128_hex": "5eff8a182b53d8a56574bd879b239505", + "section_id": 31, + "section_type": 0, + "table_index": 31 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "814460962383e20a7ecee627b96fb4dd907a71dd9b05011cb8ef8155d9e8cb74", + "section_hash_128_hex": "3f9fe4edba2fa93d5bf32d2389e1ad4d", + "section_id": 32, + "section_type": 0, + "table_index": 32 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "3f7ddc33e320e9487986c8847f5bf6291169b6ddc10653356e33c78b7e733354", + "section_hash_128_hex": "17319d36aa30ee0fa09316fd4540bd8c", + "section_id": 33, + "section_type": 0, + "table_index": 33 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "d943a6bbde7b1d8965aaf44447b6928961281fcd21dddb8520218ca9b3a0e48e", + "section_hash_128_hex": "c3273851ee3df68413046992a6dc4715", + "section_id": 34, + "section_type": 0, + "table_index": 34 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "a02e45427811344923f21987b71ea2a44870f8fcf3b01c1969d94f85d0c5ee23", + "section_hash_128_hex": "b5e4e5bd9d5237883d13f6bb00f5a421", + "section_id": 35, + "section_type": 0, + "table_index": 35 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "a0a4dfe1c33df445d4cbfe393c5cb315a286553b9463188ead1d740b63853c6b", + "section_hash_128_hex": "c15b8039a02f4b5c1448bcc074e3f3ed", + "section_id": 36, + "section_type": 0, + "table_index": 36 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "09035938824695adb089025bb7985f3247e1fa38ade1348f7d48fdeb2146d91b", + "section_hash_128_hex": "3687ddac35b71a23e18e1e899ecc6794", + "section_id": 37, + "section_type": 0, + "table_index": 37 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "fc7df76584b1d02df1638f95b17e6383accc888d0d3c0d18aae3c03a720b858f", + "section_hash_128_hex": "8b5bbe7e02378a60ab60f99c7f4110f0", + "section_id": 38, + "section_type": 0, + "table_index": 38 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "6a91ae41484e15aa99554ce4893750c91fd345b2dbb69d5bcac584a158f548aa", + "section_hash_128_hex": "b8e5a881eadddfdddb0d5dc2b46bf610", + "section_id": 39, + "section_type": 0, + "table_index": 39 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "4bb9bddd53f5d13e2c6694d4fac6771ae4bd8d99ee9c20ff13f1b47a226b9fef", + "section_hash_128_hex": "018ab18680d426abd030f71e319778b5", + "section_id": 40, + "section_type": 0, + "table_index": 40 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "d2129b7ad16127f1d1b70365c77801266837572c43efcbec422ebb5e0daa072d", + "section_hash_128_hex": "3f9fe4edba2fa93d5bf32d2389e1ad4d", + "section_id": 41, + "section_type": 0, + "table_index": 41 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "d643940bb72cd08c6134382789aabc705463188aeecc07d1bfddaea23af041d9", + "section_hash_128_hex": "370bb82b0f07a59f2c33a726d285227c", + "section_id": 42, + "section_type": 0, + "table_index": 42 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "7eb88c32c02414ddf5dd2cb2c165a8d931f66e722726e36e1e1061a0953aca79", + "section_hash_128_hex": "349268ac266a9f5c41ffdceaa33dbef8", + "section_id": 43, + "section_type": 0, + "table_index": 43 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "3e7804d8effddbfe5ab61eb1e9a0772b415fa3c7df8206b25972b7bbb378b2f8", + "section_hash_128_hex": "f3d95c8b133118e257f0e4ed0dae0f6b", + "section_id": 44, + "section_type": 0, + "table_index": 44 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "d02711b568c6393d449565b28ed6f8a25c1fd0a2591637ca144a29a3393b5b06", + "section_hash_128_hex": "87293dc22ff038a369f18d3a6c1e60ca", + "section_id": 45, + "section_type": 0, + "table_index": 45 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "766c65a0e002553995ca2972c531aadd3de28de3b7fabbd17d98f132125333c4", + "section_hash_128_hex": "a952082f92afa78d9f3fe024339c801c", + "section_id": 46, + "section_type": 0, + "table_index": 46 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "8b568f49a7458992d3732278c06c1191222f0684645a90e35a9bab15fec85d75", + "section_hash_128_hex": "7c35ad375ae1462536fa463509fa5fd4", + "section_id": 47, + "section_type": 0, + "table_index": 47 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "bb491cfb6bcb85dd235f07e41558c8171c7fda394557738c9dead29e4c14e94f", + "section_hash_128_hex": "342e0818afc9b0022da6ed299cf3d879", + "section_id": 48, + "section_type": 0, + "table_index": 48 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "1c62535e29826a86f22f6730efaf5d347814d056b1c6688f80f6ed3b9a98a607", + "section_hash_128_hex": "36b11f7720b5a93bc230d09dbbcd116c", + "section_id": 49, + "section_type": 0, + "table_index": 49 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "29217d6f527abe84f392b54b3d667e2af82dd964759fc8e291187f6d373dbc4a", + "section_hash_128_hex": "6e0ae48cb1ff4d22f55bdeea0025683e", + "section_id": 50, + "section_type": 0, + "table_index": 50 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "2a4950b2f1fc35829668f73c1152798fc6b772e98b276b4e1ea329f77e6d9d1d", + "section_hash_128_hex": "6e0ae48cb1ff4d22f55bdeea0025683e", + "section_id": 51, + "section_type": 0, + "table_index": 51 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "cfe78ebcaee638110b91e3c1dd34e03acecd1c4ab13f5b880b6b4cbb29a1eb78", + "section_hash_128_hex": "94222b98d53cbfd72a73c077499ddcc8", + "section_id": 52, + "section_type": 0, + "table_index": 52 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "ce6ae9e729b8e1e5a7cd6fdd6fc01c35ea45de7c2e15ac1c656db43a36de296a", + "section_hash_128_hex": "d6cf5cca675302c5b4670f04086c851d", + "section_id": 53, + "section_type": 0, + "table_index": 53 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "9cf3a4d0fd0949c6b6ba66167ed1b6eee1084874f8f8dedec30cb42f831b9b99", + "section_hash_128_hex": "bd0e20b3a6e2eadef9422577a0b0013f", + "section_id": 54, + "section_type": 0, + "table_index": 54 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "f408f06647ae0c61ac182603c3d66e70403a34b823bd9ec8cdaeaa4a8e607311", + "section_hash_128_hex": "0d5760432f23dd0dbe5c5141c0b6621a", + "section_id": 55, + "section_type": 0, + "table_index": 55 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "19d761ff9628f3c10bfc39a39f84bf0cbcfc24c927e0e5b0b457d37ac2603f83", + "section_hash_128_hex": "2a25799647f4aa4fcfb59c4a10d03503", + "section_id": 56, + "section_type": 0, + "table_index": 56 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "e791f8db5b5474578038b3766041817f5962b3a783a6f8ea39afafc2dba0bbc8", + "section_hash_128_hex": "98525f9da01f81ae22ff1c53ae28a64e", + "section_id": 57, + "section_type": 0, + "table_index": 57 + } + ], + "segments": [ + { + "aligned_length": 131072, + "data_offset": 65536, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "6716d895a8d8adece108c80d9c042f1c69e93ac4c4122a92f5127a9a115cb809", + "section_hash_128_hex": "faeb6e17b3b87394ef61c3a8e3c057b8", + "section_id": 2, + "section_type": 0, + "table_index": 0, + "unaligned_length": 98968 + }, + { + "aligned_length": 65536, + "data_offset": 196608, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "1d9437fb6667d0e16ca80b646d86d615333ce94645fd45ab4cbe1f5ebb847fc0", + "section_hash_128_hex": "a4c5a889d0c153c5d1e57069c3bbe958", + "section_id": 3, + "section_type": 0, + "table_index": 1, + "unaligned_length": 56712 + }, + { + "aligned_length": 1048576, + "data_offset": 262144, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "4e328be0fbd7fd113975063f45503dd1d84c83475da7a3d54071eea478bf6b4b", + "section_hash_128_hex": "82591bea2a5427b38bc433205452b1a4", + "section_id": 4, + "section_type": 0, + "table_index": 2, + "unaligned_length": 999872 + }, + { + "aligned_length": 196608, + "data_offset": 1310720, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "bbc4f43e81099ebfb71e9eb80be32303e00ae04670490714f2420b103aedadd3", + "section_hash_128_hex": "4ee6f95a7a602dfc095c576a3ef1d462", + "section_id": 5, + "section_type": 0, + "table_index": 3, + "unaligned_length": 168208 + }, + { + "aligned_length": 131072, + "data_offset": 1507328, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "d0abea037a02548e6691b4ac46ffa194c8b64b948ce8d60759d7e4b649779673", + "section_hash_128_hex": "dccc5901d40dcbf07c2867a7fa5f96ad", + "section_id": 10000, + "section_type": 0, + "table_index": 4, + "unaligned_length": 81920 + }, + { + "aligned_length": 16777216, + "data_offset": 1638400, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "c1b6a85b8789d22457a353b9f627afa502764588c40bbbde70aaa2ae942f31b1", + "section_hash_128_hex": "cc0c88c1ba8c5d6c535aa0f194386f8c", + "section_id": 10001, + "section_type": 0, + "table_index": 5, + "unaligned_length": 16777216 + }, + { + "aligned_length": 65536, + "data_offset": 18415616, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "1865e8931eed93933744e63d801e4087d91f27995c7abec8b1d44b85b7b2443a", + "section_hash_128_hex": "7e93e08f3dd9f7ccca0cd338ae6b4ffa", + "section_id": 10002, + "section_type": 0, + "table_index": 6, + "unaligned_length": 20 + }, + { + "aligned_length": 65536, + "data_offset": 18481152, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "abbc00e3806e63a535b7970f539a59bffebd534f4926c171d5894459839a6b3e", + "section_hash_128_hex": "9ee9744a87e70271eb9b57c1a2634bde", + "section_id": 10003, + "section_type": 0, + "table_index": 7, + "unaligned_length": 10238 + }, + { + "aligned_length": 65536, + "data_offset": 18546688, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "d46bb0dbebf470548031e6f548c09e67f40b63c9ffcb59bbaa17d1ca34f62781", + "section_hash_128_hex": "bee9a2f50757fe57db82240936b06d79", + "section_id": 10004, + "section_type": 0, + "table_index": 8, + "unaligned_length": 28672 + }, + { + "aligned_length": 65536, + "data_offset": 18612224, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "1e35a420f511956c762c2f079d63952d8dfd9d9cfe1331408331d7c089237569", + "section_hash_128_hex": "9e9f3f5a46415fa8bbdb0533c670f008", + "section_id": 10005, + "section_type": 0, + "table_index": 9, + "unaligned_length": 49152 + }, + { + "aligned_length": 65536, + "data_offset": 18677760, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "c74db8267534664fb10dc6d41ee76099e81318ae6180a6c3bf4fe6baa9f6c517", + "section_hash_128_hex": "54a7132dfa75f90efcf9f3fc4edd5ffe", + "section_id": 10006, + "section_type": 0, + "table_index": 10, + "unaligned_length": 20480 + }, + { + "aligned_length": 65536, + "data_offset": 18743296, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "a96575da301cbdcb56a4a48623e72e6cc8fe030b83e6fe85f2a330c806344d5c", + "section_hash_128_hex": "728989b02747423a925f629f131c3360", + "section_id": 10007, + "section_type": 0, + "table_index": 11, + "unaligned_length": 20480 + }, + { + "aligned_length": 458752, + "data_offset": 18808832, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "0f7b884caaf4592e4678399c02e7daa428090e51389bf22ddbce1af129a83909", + "section_hash_128_hex": "3aaeecf5e8295b2ace827065630bf8fd", + "section_id": 10008, + "section_type": 0, + "table_index": 12, + "unaligned_length": 450560 + }, + { + "aligned_length": 393216, + "data_offset": 19267584, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "6f809ea543579bf5bb944d643ba6293ad8aaa5b9b9062cea64401349ae732329", + "section_hash_128_hex": "8c0cd2bdf6d427df756d4d5548e50070", + "section_id": 10009, + "section_type": 0, + "table_index": 13, + "unaligned_length": 372736 + }, + { + "aligned_length": 65536, + "data_offset": 19660800, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "bc6172a7fd350af9829b5d8ecd1145b409b01b48320f51591b2db96058b02696", + "section_hash_128_hex": "827757c0bbb28b0a9ddd732e3e545ae0", + "section_id": 10010, + "section_type": 0, + "table_index": 14, + "unaligned_length": 16 + }, + { + "aligned_length": 65536, + "data_offset": 19726336, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "aa9cc91837ec17627dfc28846b97119156d41a005e2fa5b0154e4b58086e0761", + "section_hash_128_hex": "eb76921c7c6da9493ed5c7bb7050323f", + "section_id": 10011, + "section_type": 0, + "table_index": 15, + "unaligned_length": 12288 + }, + { + "aligned_length": 65536, + "data_offset": 19791872, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "9a8cce12cc8db0da09f6b9c8354b077771733cb764d9fa315a518e8e80484c7d", + "section_hash_128_hex": "9a8171bebdb751c900d60cf102ef342a", + "section_id": 10012, + "section_type": 0, + "table_index": 16, + "unaligned_length": 16384 + }, + { + "aligned_length": 65536, + "data_offset": 19857408, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "4e31b4cc6ebf86d1d247cb70afe3913b5777f758177582fbf1c5e0dd3f519097", + "section_hash_128_hex": "76b5f659e7ad6a9c4bf0fc950afce57b", + "section_id": 10013, + "section_type": 0, + "table_index": 17, + "unaligned_length": 12288 + }, + { + "aligned_length": 65536, + "data_offset": 19922944, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "c99bb734c18d78c141ec02d965697d93d533bf5787b9e9a51aed27af9eb2a833", + "section_hash_128_hex": "ccdc0162cef49cc2849b7a77fc38a1dc", + "section_id": 10014, + "section_type": 0, + "table_index": 18, + "unaligned_length": 160 + }, + { + "aligned_length": 65536, + "data_offset": 19988480, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "9bc433e555e143b8e36a818411727262a7b296404ca087db8b30977ce322c8a5", + "section_hash_128_hex": "f3ed677d0e33189bab587d106d45b439", + "section_id": 10015, + "section_type": 0, + "table_index": 19, + "unaligned_length": 160 + }, + { + "aligned_length": 65536, + "data_offset": 20054016, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "b4166032710d38bb8f9c9d9e768acb088c3b55631214781dfb4e082473d26126", + "section_hash_128_hex": "d213380a34bdeab68694a2224757c6a2", + "section_id": 10016, + "section_type": 0, + "table_index": 20, + "unaligned_length": 160 + }, + { + "aligned_length": 65536, + "data_offset": 20119552, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "5f89b152e4fcbf63f4ef61179fcafa06ea1974a41d2e54058f568ee3e11e6ba4", + "section_hash_128_hex": "17319d36aa30ee0fa09316fd4540bd8c", + "section_id": 10017, + "section_type": 0, + "table_index": 21, + "unaligned_length": 2200 + }, + { + "aligned_length": 65536, + "data_offset": 20185088, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "60f1b41208439ebc5ebea34d4b4d9c9b48e3ef4e13173bc60203d3850a6817c9", + "section_hash_128_hex": "b84f4bede32ce07cb9aaa98cd137ded7", + "section_id": 10018, + "section_type": 0, + "table_index": 22, + "unaligned_length": 158 + }, + { + "aligned_length": 65536, + "data_offset": 20250624, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "ea032d6fd57067f33568c8eea03b51a4674f1c82033b516e5f897ed45bf072e0", + "section_hash_128_hex": "ab6fec13e48fd5f33a65752e7f70d567", + "section_id": 10019, + "section_type": 0, + "table_index": 23, + "unaligned_length": 68 + }, + { + "aligned_length": 65536, + "data_offset": 20316160, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "84b3ef8ea1a4781ff29a18274b8d674d09cb0167b24bd7df0f45924d6448c425", + "section_hash_128_hex": "4d60731c1338ffc7a5ef8911dad44fc4", + "section_id": 10020, + "section_type": 0, + "table_index": 24, + "unaligned_length": 16384 + }, + { + "aligned_length": 65536, + "data_offset": 20381696, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "4494d51fd69baacd1d01f937aafb894f2d35da5a98bf70be4cf565248279c85e", + "section_hash_128_hex": "085850ede0aff59800f480983a42f309", + "section_id": 10021, + "section_type": 0, + "table_index": 25, + "unaligned_length": 16384 + }, + { + "aligned_length": 65536, + "data_offset": 20447232, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "a29785768d612f3979cdbab1d70a58fe575c97ea5262b17afabf79669a3fb9a8", + "section_hash_128_hex": "a2022db18e09b0d58531a2d889b579dd", + "section_id": 10022, + "section_type": 0, + "table_index": 26, + "unaligned_length": 5721 + }, + { + "aligned_length": 327680, + "data_offset": 20512768, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "6375d5dccb0c0e346b72a5def9da43e4fa8a8505fe02805ae1e0890b8ff7190f", + "section_hash_128_hex": "8d2b755f718563265d3ee7d7cf33578e", + "section_id": 10023, + "section_type": 0, + "table_index": 27, + "unaligned_length": 262292 + }, + { + "aligned_length": 327680, + "data_offset": 20840448, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "bc0c8458decde1027dee917237e225f014b124b9ca2e7fdf1061b78f5b8cc11d", + "section_hash_128_hex": "3e7878bdf0ad639f24e68912e53ebd34", + "section_id": 10024, + "section_type": 0, + "table_index": 28, + "unaligned_length": 322065 + }, + { + "aligned_length": 8323072, + "data_offset": 21168128, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "030e2647176d4606871801b2aa300fb935bf8b2c56e38a044f1c55c07d9afbee", + "section_hash_128_hex": "0c229f00ad67434179223609f8a33fed", + "section_id": 10025, + "section_type": 0, + "table_index": 29, + "unaligned_length": 8294548 + }, + { + "aligned_length": 8323072, + "data_offset": 29491200, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "020a55617c8858abd6d4e725e5091697e445f29b351eba3a8a4a8a4edf905c6d", + "section_hash_128_hex": "cef903046cf8fe6f1a28d022856f5a03", + "section_id": 10026, + "section_type": 0, + "table_index": 30, + "unaligned_length": 8294548 + }, + { + "aligned_length": 8323072, + "data_offset": 37814272, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "72fb060a7ce0e4ed08be355a10148fb3acbde192304b4fb3f2549b3992f0f6e5", + "section_hash_128_hex": "5eff8a182b53d8a56574bd879b239505", + "section_id": 10027, + "section_type": 0, + "table_index": 31, + "unaligned_length": 8294548 + }, + { + "aligned_length": 65536, + "data_offset": 46137344, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "1c2d07f076e4c0d6a47af3081b9d3bc4380f2744ab7e04ccc34a82d8624a2fdc", + "section_hash_128_hex": "3f9fe4edba2fa93d5bf32d2389e1ad4d", + "section_id": 10028, + "section_type": 0, + "table_index": 32, + "unaligned_length": 32091 + }, + { + "aligned_length": 65536, + "data_offset": 46202880, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "fe5f0fbc553220b3989e446c1e37ab70304b17fb6af6fdaea9d877cb1ea51968", + "section_hash_128_hex": "17319d36aa30ee0fa09316fd4540bd8c", + "section_id": 10029, + "section_type": 0, + "table_index": 33, + "unaligned_length": 2200 + }, + { + "aligned_length": 327680, + "data_offset": 46268416, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "dc74210fe9ff5436771b19cc7aa21f962da84a529530e8268286b5a234ff0ac2", + "section_hash_128_hex": "c3273851ee3df68413046992a6dc4715", + "section_id": 10030, + "section_type": 0, + "table_index": 34, + "unaligned_length": 270084 + }, + { + "aligned_length": 65536, + "data_offset": 46596096, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "38da3249d2b6ff373b065c4c4ecacd96d721297ac6de2c4b4ff8951adc06ef4b", + "section_hash_128_hex": "b5e4e5bd9d5237883d13f6bb00f5a421", + "section_id": 10031, + "section_type": 0, + "table_index": 35, + "unaligned_length": 916 + }, + { + "aligned_length": 65536, + "data_offset": 46661632, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "7a93d1da2d31024c6515047d03e6da8df1faeff9365bbf3c00a75db73186ec4c", + "section_hash_128_hex": "c15b8039a02f4b5c1448bcc074e3f3ed", + "section_id": 10032, + "section_type": 0, + "table_index": 36, + "unaligned_length": 134 + }, + { + "aligned_length": 65536, + "data_offset": 46727168, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "860754f96d9a4fcdbd117f99d45f130925b4fe21e52bb5a20e26fb6d08436fd9", + "section_hash_128_hex": "3687ddac35b71a23e18e1e899ecc6794", + "section_id": 10033, + "section_type": 0, + "table_index": 37, + "unaligned_length": 68 + }, + { + "aligned_length": 6291456, + "data_offset": 46792704, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "688d3483d4db02f9760995c0d5ba6bfa0e6911af44cff528953eabb1743dc079", + "section_hash_128_hex": "8b5bbe7e02378a60ab60f99c7f4110f0", + "section_id": 10034, + "section_type": 0, + "table_index": 38, + "unaligned_length": 6291456 + }, + { + "aligned_length": 3145728, + "data_offset": 53084160, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "c0cc4ae1e9989db6937a23cef57846f6627e48bc6680b6a12f9d8c8840feb8c1", + "section_hash_128_hex": "b8e5a881eadddfdddb0d5dc2b46bf610", + "section_id": 10035, + "section_type": 0, + "table_index": 39, + "unaligned_length": 3145728 + }, + { + "aligned_length": 3145728, + "data_offset": 56229888, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "9ef2b02b4b2ad6e6b9329775a39774df84df57ab64693e8f5db32f20cd681532", + "section_hash_128_hex": "018ab18680d426abd030f71e319778b5", + "section_id": 10036, + "section_type": 0, + "table_index": 40, + "unaligned_length": 3145728 + }, + { + "aligned_length": 65536, + "data_offset": 59375616, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "4425dc8071b4642e9939fbf6058998ae2ca172d48b1a04f4a878d6f80ab868df", + "section_hash_128_hex": "3f9fe4edba2fa93d5bf32d2389e1ad4d", + "section_id": 10037, + "section_type": 0, + "table_index": 41, + "unaligned_length": 32091 + }, + { + "aligned_length": 65536, + "data_offset": 59441152, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "3e932f1ad4fd9b475da188930a4062674500804a9e1c5cc26bbd03f75f9b22b8", + "section_hash_128_hex": "370bb82b0f07a59f2c33a726d285227c", + "section_id": 10038, + "section_type": 0, + "table_index": 42, + "unaligned_length": 2860 + }, + { + "aligned_length": 65536, + "data_offset": 59506688, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "d59401740aed22b99d7939de7ecf5e3b32a5128e0b6c1c259c3e3a55f24f76d3", + "section_hash_128_hex": "349268ac266a9f5c41ffdceaa33dbef8", + "section_id": 10039, + "section_type": 0, + "table_index": 43, + "unaligned_length": 32016 + }, + { + "aligned_length": 65536, + "data_offset": 59572224, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "7637f7b03989c6853bb6070538daa226319be9c11fe5ee9f93cb3735d3507cf1", + "section_hash_128_hex": "f3d95c8b133118e257f0e4ed0dae0f6b", + "section_id": 10040, + "section_type": 0, + "table_index": 44, + "unaligned_length": 264 + }, + { + "aligned_length": 65536, + "data_offset": 59637760, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "7e3bf1f5d7f3c775479e49502f51e93fe8569ff9b2fbc89314bc978ceefd4dd1", + "section_hash_128_hex": "87293dc22ff038a369f18d3a6c1e60ca", + "section_id": 10041, + "section_type": 0, + "table_index": 45, + "unaligned_length": 128 + }, + { + "aligned_length": 65536, + "data_offset": 59703296, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "fda6682a29a6d8a46f4f9373949e4f2b8173859352f8310da2c23c2d94e97b9f", + "section_hash_128_hex": "a952082f92afa78d9f3fe024339c801c", + "section_id": 10042, + "section_type": 0, + "table_index": 46, + "unaligned_length": 1090 + }, + { + "aligned_length": 0, + "data_offset": 59768832, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "7a0d9f789fcdc0e87e3efdf8fcdaafd38be5b2241af48486a2769a142e3579c4", + "section_hash_128_hex": "7c35ad375ae1462536fa463509fa5fd4", + "section_id": 10043, + "section_type": 0, + "table_index": 47, + "unaligned_length": 0 + }, + { + "aligned_length": 65536, + "data_offset": 59768832, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "a7d2089d1f763fee00aeb56849f436b4f5deb85971b275c2cb8e4fb75f17a7ef", + "section_hash_128_hex": "342e0818afc9b0022da6ed299cf3d879", + "section_id": 10044, + "section_type": 0, + "table_index": 48, + "unaligned_length": 1090 + }, + { + "aligned_length": 336789504, + "data_offset": 59834368, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "114da6f60fe1a4c22ff63781cbc1ca8f04ea8cec517468441bfcb075bf85597e", + "section_hash_128_hex": "36b11f7720b5a93bc230d09dbbcd116c", + "section_id": 10045, + "section_type": 0, + "table_index": 49, + "unaligned_length": 336789504 + }, + { + "aligned_length": 65536, + "data_offset": 396623872, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "72d7f22707e7751f47fd936832de644876b7af5918a15645b3b22d6d222d9c97", + "section_hash_128_hex": "6e0ae48cb1ff4d22f55bdeea0025683e", + "section_id": 10046, + "section_type": 0, + "table_index": 50, + "unaligned_length": 494 + }, + { + "aligned_length": 65536, + "data_offset": 396689408, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "54c93ed1fa7373c86ca5860fcb27d68d20cc7d09c9823677c0ad5daa24d9e4f0", + "section_hash_128_hex": "6e0ae48cb1ff4d22f55bdeea0025683e", + "section_id": 10047, + "section_type": 0, + "table_index": 51, + "unaligned_length": 494 + }, + { + "aligned_length": 65536, + "data_offset": 396754944, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "847fcb3226a9b200705ce1e3bb37b3ac2f16764ab3ed174a90e9cb7588f05c17", + "section_hash_128_hex": "94222b98d53cbfd72a73c077499ddcc8", + "section_id": 10048, + "section_type": 0, + "table_index": 52, + "unaligned_length": 242 + }, + { + "aligned_length": 65536, + "data_offset": 396820480, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "4c4378e6b8513930371c60150b8f9ca4a9c7570b6e0e826a0c7103603cba5954", + "section_hash_128_hex": "d6cf5cca675302c5b4670f04086c851d", + "section_id": 10049, + "section_type": 0, + "table_index": 53, + "unaligned_length": 298 + }, + { + "aligned_length": 107544576, + "data_offset": 396886016, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "4de3e73fec093fdebd7145e346e67d823c493bae129466176a51aafaead69968", + "section_hash_128_hex": "bd0e20b3a6e2eadef9422577a0b0013f", + "section_id": 10050, + "section_type": 0, + "table_index": 54, + "unaligned_length": 107544576 + }, + { + "aligned_length": 65536, + "data_offset": 504430592, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 1, + "raw_sha256": "8ed25b4c210d280fb44711efa3f86b223390fbe8b5a0b8d4a99ca0f0259a8138", + "section_hash_128_hex": "0d5760432f23dd0dbe5c5141c0b6621a", + "section_id": 10050, + "section_type": 0, + "table_index": 55, + "unaligned_length": 600 + }, + { + "aligned_length": 65536, + "data_offset": 504496128, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 2, + "raw_sha256": "c953fad3c2c910d8fb4717885ab1e3675606e03aac735a186e26d4754660498a", + "section_hash_128_hex": "2a25799647f4aa4fcfb59c4a10d03503", + "section_id": 10050, + "section_type": 0, + "table_index": 56, + "unaligned_length": 6820 + }, + { + "aligned_length": 65536, + "data_offset": 504561664, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "aa60886b534c05fdd6b4c37e2f8fb1c2543ab98a8a6b5fc330c69b4ec9303b63", + "section_hash_128_hex": "98525f9da01f81ae22ff1c53ae28a64e", + "section_id": 1, + "section_type": 0, + "table_index": 57, + "unaligned_length": 1024 + } + ], + "source": "community-owendswang-v1.4-autoloader-7.61-inner", + "source_size": 504627200, + "structural_fingerprint_sha256": "4884c54e280969371ae1d0b84533be5a7c50a61dff6931cf595fdb5862655218", + "table_padding_bytes": 58952, + "tables_end": 6584, + "trailing_data_bytes": 0, + "warnings": [] + }, + "local": { + "classification": "SIECAF_VALID_STRUCTURE", + "duplicate_hash_section_ids": [], + "duplicate_metadata_section_ids": [], + "duplicate_metadata_section_keys": [], + "errors": [], + "gaps": [], + "header": { + "file_offset": 65536, + "file_size": 504299520, + "iv_12_hex": "e5158700e6158700e7158700", + "key_or_unknown_16_redacted": true, + "key_or_unknown_16_sha256": "a53d65f4ee4fa8cd0806e9492bb3398526082ddf07f519da06ade470a867e52c", + "magic_ascii": "SIECAF", + "magic_hex": "5349454341460000", + "mode_i32": 1, + "padding_1_u32": 0, + "padding_2_u32": 0, + "padding_3_u32": 0, + "raw_sha256": "4454809c9831770901491368f08b648129aef7ee3148aff17df8716ff3a5cd76", + "segment_count": 62, + "unknown_u64": 1, + "version_i32": 3 + }, + "normalized_hash_blocks_sha256": "6e052f186dc8e683d501e376d79dcec1ba0d3268c257b6071e96eae738a7be24", + "normalized_layout_sha256": "c6dac8313ef99accba68b4a36871f0d8ae99676df0b721d4447ecb05a12a6539", + "normalized_segment_table_sha256": "ee35a92fe5e46c307310715fc5bd0edb9ac46c2046b7d77e9c1c437446e399ea", + "overlaps": [], + "repeated_metadata_section_ids": [ + 10054 + ], + "section_hashes": [ + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "2688511fcefc66146393cf85e48bec4cc61115b9ccd871a6811b927bcde041cd", + "section_hash_128_hex": "0b9a66074bf03cdb2e82d13f61f16a9d", + "section_id": 0, + "section_type": 0, + "table_index": 0 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "f084a69fa49cf1ce63e36be2725130b797c8a582a2ae4675212c50582ce2d022", + "section_hash_128_hex": "9e63385ac9aefb0eee3dd75386553309", + "section_id": 1, + "section_type": 0, + "table_index": 1 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "26cf49530a4ec24139138d43b226c28038f1db0d56a0116831f7daff502b68b5", + "section_hash_128_hex": "295d92e8afb6e379bae90c09a9ef11a7", + "section_id": 2, + "section_type": 0, + "table_index": 2 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "8fef53087507d6554329c0bc1cd7770677fcb95c5640915a981d0d0a0ea45762", + "section_hash_128_hex": "dbac683bafaa8a8596d987e530492b26", + "section_id": 3, + "section_type": 0, + "table_index": 3 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "55e8324185419fdb97c53ca266bd6f21052f3e2b2d30603dcb4be6e12f929316", + "section_hash_128_hex": "b76e15575068304cd8859af113fb6bf2", + "section_id": 4, + "section_type": 0, + "table_index": 4 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "149744c7b0d821c492f2e1bb0c64c71c24a751e08d4784537d10368e64d09019", + "section_hash_128_hex": "f288134447f1b61857b11ee845381c6b", + "section_id": 5, + "section_type": 0, + "table_index": 5 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "29f6d4e9f40eb21cb03cda7c842bb941a23770f48d55806ca8987d9049d4b90a", + "section_hash_128_hex": "efe428697ff8141ff0e4e173098cb844", + "section_id": 6, + "section_type": 0, + "table_index": 6 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "aab8b3a4aaa8ca35a4576bf6703814097cb84682776a52caa8ecc85932f8b34d", + "section_hash_128_hex": "feb97144a865c53dc1f1481542cc9d96", + "section_id": 7, + "section_type": 0, + "table_index": 7 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "a7f92a5ce8acc170451351e0b3079cc76478508151d1d9782f1d2965eab5f0ac", + "section_hash_128_hex": "22d563577e767602d1e79d2ac6a3bb8e", + "section_id": 8, + "section_type": 0, + "table_index": 8 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "1bea87b8168b875ec333648bc864bd2155801399bb6c5549e9c5f63420ba0056", + "section_hash_128_hex": "e2d1cc1515445b5004c38566a6ff9080", + "section_id": 9, + "section_type": 0, + "table_index": 9 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "902a4c2778e8b3ec2b12760e6ae7e453a7ad6f5961b76f384aa5f53c7d3fd9f9", + "section_hash_128_hex": "e2d1cc1515445b5004c38566a6ff9080", + "section_id": 10, + "section_type": 0, + "table_index": 10 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "81a0c2f9412d75b66290dc6045bd38c0d13138910fd818f21e3b4fea8d637458", + "section_hash_128_hex": "ccf7543a53fdbe16fbaf2363d755b2f0", + "section_id": 11, + "section_type": 0, + "table_index": 11 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "8efbbadd20b7acd19051d8db8b85f1b3fe0a1732209d8458355603dccf171087", + "section_hash_128_hex": "92ca62731e0e9bbf885813308fa6e3dc", + "section_id": 12, + "section_type": 0, + "table_index": 12 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "5b7ba0d2079f0e5a4345dd04ea703224e36e7c6042479ff1e4a1543649ef1281", + "section_hash_128_hex": "39b806d6b83e7eabbfed4e1fd198b421", + "section_id": 13, + "section_type": 0, + "table_index": 13 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "6a7bcdd37d4bf5b00a288f78682f3e2dcf592aacdb30aa887941d70ea34383e8", + "section_hash_128_hex": "17ae6805f5274458ebe8fbda07f37088", + "section_id": 14, + "section_type": 0, + "table_index": 14 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "9742b174ee76c1dcdbf88a600fc965a61374883a179a4889e313535ae2cbe79f", + "section_hash_128_hex": "b86b32a59053c7446cad0e84531e6c6c", + "section_id": 15, + "section_type": 0, + "table_index": 15 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "2131842f0781f2ec956186552c593a94178e490e85bb0a10fb4d5fb5e93d6b8f", + "section_hash_128_hex": "e259596e115dcb686ad740c3742d4e03", + "section_id": 16, + "section_type": 0, + "table_index": 16 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "5e6efcbbb58e871042f7fc31372522e77c70a49accd3107ebcecb96102b315f2", + "section_hash_128_hex": "b8bccf3a85708756b0f0e9c3ca272dbb", + "section_id": 17, + "section_type": 0, + "table_index": 17 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "90344e8533481e1ad3cfacd9c715abab59c83c0e413a317fc9eabbdf88c9654e", + "section_hash_128_hex": "fc0acf0f14aaf4e6499effd30c9931de", + "section_id": 18, + "section_type": 0, + "table_index": 18 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "a8b5a5d1a36c7befa1b73cf4d58758508bfb7e8a98ed4a8ac79a3e2bb17dd143", + "section_hash_128_hex": "24fdbd2972535b76224de11443959c0b", + "section_id": 19, + "section_type": 0, + "table_index": 19 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "33b7eebda5cfcf7eaf6b4aa2ed6eaca3b47537ced4d06410379680df33034e41", + "section_hash_128_hex": "7102edd1b8063fa8e4620ea02aadf13b", + "section_id": 20, + "section_type": 0, + "table_index": 20 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "3d3a33561a6be4661712ec7e70a7edf72994905ca3c79afda27ffe0becd99f65", + "section_hash_128_hex": "85166b6e59f1472f2eff0952528fbbc5", + "section_id": 21, + "section_type": 0, + "table_index": 21 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "dcac2965146a6aeca74374de6e1f0921151a15618efcb636a6293541a9942394", + "section_hash_128_hex": "a4646f80bac70f9ad9d81f34a5528a12", + "section_id": 22, + "section_type": 0, + "table_index": 22 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "09da98dab31fdf5e4d2bdf6d1d6348ead066fc51dc1dd769ea6c81e32e633b1e", + "section_hash_128_hex": "6913bef589ffa8eb25749e1e4b7322a0", + "section_id": 23, + "section_type": 0, + "table_index": 23 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "22c68c04c46a5b120319b3852c20551a52ff168e26feb65f10b64587af1ae0c6", + "section_hash_128_hex": "9876ac5d41703819efcd6aeb7945e327", + "section_id": 24, + "section_type": 0, + "table_index": 24 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "dce5fa58ac2ea9ff81edb7700f1ed4cb7d9f0df6ac7edaee6e6bb883f815673c", + "section_hash_128_hex": "4495b86d5171aa76105519f20709d427", + "section_id": 25, + "section_type": 0, + "table_index": 25 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "4a203c7f1ba7891a45b6b3c2c5eb028bd2b46f8e8a8bbf315ea32ed211978728", + "section_hash_128_hex": "942bb66d169e098872dc7197d7912f43", + "section_id": 26, + "section_type": 0, + "table_index": 26 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "93acdf8624d9c3373b78dd1e964402b45c187d0013bfb41cabb058124cc67e8a", + "section_hash_128_hex": "0dec0e97620b11032bf06d4ca4dafb2f", + "section_id": 27, + "section_type": 0, + "table_index": 27 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "ec7e0c23db64dbc3c2fce2d19ad7bca89dd6c1717e5bb8f63e766ee211a6f7d7", + "section_hash_128_hex": "10733e49e778e5ca3d1b91c5caf9fdb0", + "section_id": 28, + "section_type": 0, + "table_index": 28 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "3ab6f4b22afffeb5374d43ccec2559cb616be638514953b02275175def905c77", + "section_hash_128_hex": "18b88893367fec945f037d89177f142d", + "section_id": 29, + "section_type": 0, + "table_index": 29 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "b4df5bf8b8232e393e7e449d99e043f63f5b3bdeeafc2580e032ca5daea253ae", + "section_hash_128_hex": "e5289dc99b7f129329615002a80bf542", + "section_id": 30, + "section_type": 0, + "table_index": 30 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "913b76741db62e68c428e782093bb20e4cf087ae381a8b9cb893159581130154", + "section_hash_128_hex": "1175c59f19a03b706bc5a2c3bf575e4c", + "section_id": 31, + "section_type": 0, + "table_index": 31 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "409e697971a376c4d9e3483899438cccce9568552444854758903ad5c1867209", + "section_hash_128_hex": "2912ca54a75987bad9fe5a23272884ec", + "section_id": 32, + "section_type": 0, + "table_index": 32 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "58f06a427dccd602f388917f6291442300097c0bf07b94dd6e590ed9243f4c25", + "section_hash_128_hex": "0bf87bbe55467cb673ffc196507a208e", + "section_id": 33, + "section_type": 0, + "table_index": 33 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "5b9f334b7b190441c6d51c884b50f0fe2d18e21b02fe2aaf6bde244040a2d845", + "section_hash_128_hex": "4d12d094fc7e50df2ea1c89f780d6c93", + "section_id": 34, + "section_type": 0, + "table_index": 34 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "ce354e9541d83f26f50c12e2a171c9fb61ee933b8cb92ce5a6ed1877f0bf1c46", + "section_hash_128_hex": "65b9ebaadfc0805628baace4a38b988a", + "section_id": 35, + "section_type": 0, + "table_index": 35 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "de01c781c2d3d23ec08369a35e629cea071f4b54f0e0a658e9edb99fd4587b7f", + "section_hash_128_hex": "655938af9d155518ff24d4cb0cf45fa1", + "section_id": 36, + "section_type": 0, + "table_index": 36 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "b0aba31b385e5c04bcacb98d2d8e7c1222467b71966a0489b1cd8a72f97a894d", + "section_hash_128_hex": "942bb66d169e098872dc7197d7912f43", + "section_id": 37, + "section_type": 0, + "table_index": 37 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "4c578c69f22dd103689bb4a26a4c701d50dd3402b847b7f6b4fcf91c3a58937a", + "section_hash_128_hex": "2b56a5c7b71570d3a31c7f73c5879d90", + "section_id": 38, + "section_type": 0, + "table_index": 38 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "b05cca3b46dd90764f13c20e6ef226bdf18eba8e27c75b906750bde982e67452", + "section_hash_128_hex": "fa19dc93ba23919e2e9372bff321821c", + "section_id": 39, + "section_type": 0, + "table_index": 39 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "032ed2ccfa44643d3d6c5693227b83d928d096553ee2951d49a1f3bc54c100d4", + "section_hash_128_hex": "a6f49b5d09602a2ef21a0db5fb49ed8d", + "section_id": 40, + "section_type": 0, + "table_index": 40 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "9a41195b4d46e14e6893b8caac5a9e452d9cd1fe6edcfa23cb6cf394421369e6", + "section_hash_128_hex": "d311e1f8863d40615f9a17b6340535e9", + "section_id": 41, + "section_type": 0, + "table_index": 41 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "3f7336fc85eaeb2a566ed23444a98225bc46df64b3ba6a21217d419c34a90994", + "section_hash_128_hex": "ff379ba34b0f93a6ac097319d20d0145", + "section_id": 42, + "section_type": 0, + "table_index": 42 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "b250dadd3d53fb2907c9e7469762c2dc3966099896459e54765cb7b8b39aca63", + "section_hash_128_hex": "77216771cc6dd0995469ad6b4cc101bc", + "section_id": 43, + "section_type": 0, + "table_index": 43 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "4dd691d9ce4917f46f552bcf0997ddc1a16a3de4789ca94b4db5796236d852a0", + "section_hash_128_hex": "3452468f8bed9fe7ee03b172696b5960", + "section_id": 44, + "section_type": 0, + "table_index": 44 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "7446d96f9d72b12ad1700fdc89a4503d1edbfb0218ecc779847c60957bd90062", + "section_hash_128_hex": "770861f7bf103689c80ec74b49a1ea39", + "section_id": 45, + "section_type": 0, + "table_index": 45 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "6aaf7418fb428f8729187262619e274e75f0792855113821262e84c5016a3058", + "section_hash_128_hex": "2b56a5c7b71570d3a31c7f73c5879d90", + "section_id": 46, + "section_type": 0, + "table_index": 46 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "832db9d6b8159aa26bec7b331f719d06fb7398ec4eb1911e2b5ebd887974ef84", + "section_hash_128_hex": "e7f7756ba4d5dbb8a0ccc41cf2742124", + "section_id": 47, + "section_type": 0, + "table_index": 47 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "b3afdc9d7fa56e2343265f6122c138214d1c8afc84197eb42c90bad86e7a0680", + "section_hash_128_hex": "ca372db503f8a4786e2408cc588c152d", + "section_id": 48, + "section_type": 0, + "table_index": 48 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "351c721a1feadec3ea09938c3bb96f4497144ab0d531b83e375b8d9caf461045", + "section_hash_128_hex": "7031a8fb56959004ca0dab9fad092ee1", + "section_id": 49, + "section_type": 0, + "table_index": 49 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "ab2ef4d158a88b21eb5296939677d69b09103fa98cbf61ea1ed928ebb61aa928", + "section_hash_128_hex": "9f178de8d922edec06c278c8ee2b778f", + "section_id": 50, + "section_type": 0, + "table_index": 50 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "e43f0e1eebe5d64c69528b4d73a2487a8aa9b673613542b1d3efaf577aa9b350", + "section_hash_128_hex": "e2d1cc1515445b5004c38566a6ff9080", + "section_id": 51, + "section_type": 0, + "table_index": 51 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "196219ca63031694f882a93fa9057b5157a1a46ac874bef72200fd6acaceac8a", + "section_hash_128_hex": "6f8b5479dff8de425b05ba2125dd1612", + "section_id": 52, + "section_type": 0, + "table_index": 52 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "6f1c5813c8dd923704297f0395a7b754392972c64eeed1b13b4d766a4776d1de", + "section_hash_128_hex": "ae0a9f7c85278b2732e715d01e76f859", + "section_id": 53, + "section_type": 0, + "table_index": 53 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "f5e67be18b566495460b9d7f0ce8dfda8734aec8ea9d917ce9a43bcf3c273324", + "section_hash_128_hex": "4f11f8e8553e71308503754ea9560c96", + "section_id": 54, + "section_type": 0, + "table_index": 54 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "881b837cce154df2d79483eafa3e66191622e4f36803ab9609fdae6a1bd7b525", + "section_hash_128_hex": "4f11f8e8553e71308503754ea9560c96", + "section_id": 55, + "section_type": 0, + "table_index": 55 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "a8ba9eac53b652908452be2c6414ad4deea6139bb8576d142a16f90a58986f2b", + "section_hash_128_hex": "f1c8cabd977903698df31992760bfc8b", + "section_id": 56, + "section_type": 0, + "table_index": 56 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "a49a4e35925d99cdef900bfad4a45dc015c376e88628f068ac5e2b97567f7395", + "section_hash_128_hex": "dba2a9bbac06143a70b4c719b2a2cd9b", + "section_id": 57, + "section_type": 0, + "table_index": 57 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "8dcbb517cac94789270f66eca9283f132f1986e014793e39f8fb416088fe398b", + "section_hash_128_hex": "f30fde0c7857ab4b6206f1bbcb67f0c3", + "section_id": 58, + "section_type": 0, + "table_index": 58 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "f5e98c8feb41c1721be0a5bf6d737f87914f75aec21cf27bcf010ee58db712a4", + "section_hash_128_hex": "4eacff17aa6a9b7d7fe13ff2acd2252e", + "section_id": 59, + "section_type": 0, + "table_index": 59 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "59dffa4270cf1f5bae9d0c88c42e4c3518610fdb5149e586f4a54fdb345df61c", + "section_hash_128_hex": "cd55d4e916eb15a153cf3b0306620ac1", + "section_id": 60, + "section_type": 0, + "table_index": 60 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "1a786276e4b99b8d38a5f7bb9381b4df8e2a7a89ded5526f6e87da9d13366e3e", + "section_hash_128_hex": "25effd4e5d992d3d1344e731067b747d", + "section_id": 61, + "section_type": 0, + "table_index": 61 + } + ], + "segments": [ + { + "aligned_length": 131072, + "data_offset": 65536, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "032b3744f41b9dc9ac6a323671947fdc622f0bb4e270e088278e2e53bbd6e29e", + "section_hash_128_hex": "0b9a66074bf03cdb2e82d13f61f16a9d", + "section_id": 2, + "section_type": 0, + "table_index": 0, + "unaligned_length": 101192 + }, + { + "aligned_length": 65536, + "data_offset": 196608, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "336b47ea6a2c22d41f070610020bab044f09b0967c38cf847769fa6f7ce8e4e8", + "section_hash_128_hex": "9e63385ac9aefb0eee3dd75386553309", + "section_id": 3, + "section_type": 0, + "table_index": 1, + "unaligned_length": 61160 + }, + { + "aligned_length": 851968, + "data_offset": 262144, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "05053620e233d16a87995801837cb9d36b0f3c366d9d59d226e4f256b6f46ad6", + "section_hash_128_hex": "295d92e8afb6e379bae90c09a9ef11a7", + "section_id": 4, + "section_type": 0, + "table_index": 2, + "unaligned_length": 822272 + }, + { + "aligned_length": 65536, + "data_offset": 1114112, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "22dbb022966418f0c79f036a2eb9bbac7d080d0bb4eedae6b867a5611cedc865", + "section_hash_128_hex": "dbac683bafaa8a8596d987e530492b26", + "section_id": 5, + "section_type": 0, + "table_index": 3, + "unaligned_length": 40016 + }, + { + "aligned_length": 131072, + "data_offset": 1179648, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "a235aa8f9cf425e2370e151c9dfd93347289ec9de0ffdd4466d9fbf58e7e5e77", + "section_hash_128_hex": "b76e15575068304cd8859af113fb6bf2", + "section_id": 10000, + "section_type": 0, + "table_index": 4, + "unaligned_length": 81920 + }, + { + "aligned_length": 16777216, + "data_offset": 1310720, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "da2fb82cd690e7babf123431b0b5cfe159773b86492cf81bfb6a151919d41166", + "section_hash_128_hex": "f288134447f1b61857b11ee845381c6b", + "section_id": 10001, + "section_type": 0, + "table_index": 5, + "unaligned_length": 16777216 + }, + { + "aligned_length": 65536, + "data_offset": 18087936, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "23d3684e17fb73431ad8939aaf2453fa389ec71df726803c22be57c2ab2fda4d", + "section_hash_128_hex": "efe428697ff8141ff0e4e173098cb844", + "section_id": 10002, + "section_type": 0, + "table_index": 6, + "unaligned_length": 20 + }, + { + "aligned_length": 65536, + "data_offset": 18153472, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "ee3faf83f2c2629451c914429d5b7be597edbae395f61c628854ac729f81ebd2", + "section_hash_128_hex": "feb97144a865c53dc1f1481542cc9d96", + "section_id": 10003, + "section_type": 0, + "table_index": 7, + "unaligned_length": 8948 + }, + { + "aligned_length": 65536, + "data_offset": 18219008, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "dafe03ce5db3f18602ba194ff410c029baa191972dc7b4e0324b8ea082c1de7b", + "section_hash_128_hex": "22d563577e767602d1e79d2ac6a3bb8e", + "section_id": 10004, + "section_type": 0, + "table_index": 8, + "unaligned_length": 28672 + }, + { + "aligned_length": 0, + "data_offset": 18284544, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "69e27418c1b1e28c0106f30b76f921344aae4ba853d41fae1fb1025d721b3af5", + "section_hash_128_hex": "e2d1cc1515445b5004c38566a6ff9080", + "section_id": 10005, + "section_type": 0, + "table_index": 9, + "unaligned_length": 0 + }, + { + "aligned_length": 0, + "data_offset": 18284544, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "5e96ba047d6328462d99357afd74bea132d6c26fd3c2d6edaa30f0d32d620a8f", + "section_hash_128_hex": "e2d1cc1515445b5004c38566a6ff9080", + "section_id": 10006, + "section_type": 0, + "table_index": 10, + "unaligned_length": 0 + }, + { + "aligned_length": 65536, + "data_offset": 18284544, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "2e0cf593d7def72c1abcf22a2a33acb38f8d404b0c7c7a3c800f9bee3e4c4363", + "section_hash_128_hex": "ccf7543a53fdbe16fbaf2363d755b2f0", + "section_id": 10007, + "section_type": 0, + "table_index": 11, + "unaligned_length": 49152 + }, + { + "aligned_length": 65536, + "data_offset": 18350080, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "03eb76dcce687fc0384faa298d8795ce1bf8b23574aac4d047ada37454b90b9a", + "section_hash_128_hex": "92ca62731e0e9bbf885813308fa6e3dc", + "section_id": 10008, + "section_type": 0, + "table_index": 12, + "unaligned_length": 20480 + }, + { + "aligned_length": 65536, + "data_offset": 18415616, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "ddf066d572d18be3c60a33aa8caf9c6f5f0e79ff2d601960278b49dcb91559be", + "section_hash_128_hex": "39b806d6b83e7eabbfed4e1fd198b421", + "section_id": 10009, + "section_type": 0, + "table_index": 13, + "unaligned_length": 20480 + }, + { + "aligned_length": 393216, + "data_offset": 18481152, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "b1fbba52582ec9a6c3a3b413cef7b2de4c6342d3994cc235b946a809743519d4", + "section_hash_128_hex": "17ae6805f5274458ebe8fbda07f37088", + "section_id": 10010, + "section_type": 0, + "table_index": 14, + "unaligned_length": 376832 + }, + { + "aligned_length": 262144, + "data_offset": 18874368, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "bf5c95272b1afa7491f64b8beb982ba5eabe803b1d3598970ec8157100e1ba93", + "section_hash_128_hex": "b86b32a59053c7446cad0e84531e6c6c", + "section_id": 10011, + "section_type": 0, + "table_index": 15, + "unaligned_length": 229376 + }, + { + "aligned_length": 65536, + "data_offset": 19136512, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "d93f10012e6c0a413624573b813430e8a37b8d0340e87335fba807e93f801906", + "section_hash_128_hex": "e259596e115dcb686ad740c3742d4e03", + "section_id": 10012, + "section_type": 0, + "table_index": 16, + "unaligned_length": 16 + }, + { + "aligned_length": 65536, + "data_offset": 19202048, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "95b9bcd552d478ce82e9b443d05b9e4d800b1958ef2bc4b3828239e747d5eb23", + "section_hash_128_hex": "b8bccf3a85708756b0f0e9c3ca272dbb", + "section_id": 10013, + "section_type": 0, + "table_index": 17, + "unaligned_length": 36864 + }, + { + "aligned_length": 196608, + "data_offset": 19267584, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "358ae6d3aec0a816515969acabe2d0c371da4c9c6ec3f7571e11d65496199fdd", + "section_hash_128_hex": "fc0acf0f14aaf4e6499effd30c9931de", + "section_id": 10014, + "section_type": 0, + "table_index": 18, + "unaligned_length": 176128 + }, + { + "aligned_length": 65536, + "data_offset": 19464192, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "62d6963e02fec2734ac1272cd53d64299a1895fb9b3f3bd8ceb82cf546f6b6a1", + "section_hash_128_hex": "24fdbd2972535b76224de11443959c0b", + "section_id": 10015, + "section_type": 0, + "table_index": 19, + "unaligned_length": 12288 + }, + { + "aligned_length": 65536, + "data_offset": 19529728, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "d3668149c68e188efed8c0262acbab910fda67b31d1a22bcfa887078bbf1000f", + "section_hash_128_hex": "7102edd1b8063fa8e4620ea02aadf13b", + "section_id": 10016, + "section_type": 0, + "table_index": 20, + "unaligned_length": 16384 + }, + { + "aligned_length": 65536, + "data_offset": 19595264, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "9e6b525e5526ccc5ed2d2ad982deaf48613e8b8dbf061ef1454f3869033a40f8", + "section_hash_128_hex": "85166b6e59f1472f2eff0952528fbbc5", + "section_id": 10017, + "section_type": 0, + "table_index": 21, + "unaligned_length": 128 + }, + { + "aligned_length": 65536, + "data_offset": 19660800, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "0d28143a72bcc65621c8301ef589368c74b544d7a7f4f617a138416b678813cb", + "section_hash_128_hex": "a4646f80bac70f9ad9d81f34a5528a12", + "section_id": 10018, + "section_type": 0, + "table_index": 22, + "unaligned_length": 12288 + }, + { + "aligned_length": 65536, + "data_offset": 19726336, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "65efb24aed4a88eed4d5219bb5923921b0941b96f2c51b7e77c8a081cfe4ab65", + "section_hash_128_hex": "6913bef589ffa8eb25749e1e4b7322a0", + "section_id": 10019, + "section_type": 0, + "table_index": 23, + "unaligned_length": 160 + }, + { + "aligned_length": 65536, + "data_offset": 19791872, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "3b6a03f5eed9536b4ffcf0e744ec221a56b5da623bb48f4210708929df1d98ed", + "section_hash_128_hex": "9876ac5d41703819efcd6aeb7945e327", + "section_id": 10020, + "section_type": 0, + "table_index": 24, + "unaligned_length": 160 + }, + { + "aligned_length": 65536, + "data_offset": 19857408, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "73798df5da57aeb74655627bc78f1ca1b4636016a5d139ae328543e44cc793f8", + "section_hash_128_hex": "4495b86d5171aa76105519f20709d427", + "section_id": 10021, + "section_type": 0, + "table_index": 25, + "unaligned_length": 160 + }, + { + "aligned_length": 65536, + "data_offset": 19922944, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "a37a4f7dfecf09c3d37e1ed32558138f9099ef5604b3f27197dd64b7759c566d", + "section_hash_128_hex": "942bb66d169e098872dc7197d7912f43", + "section_id": 10022, + "section_type": 0, + "table_index": 26, + "unaligned_length": 2200 + }, + { + "aligned_length": 65536, + "data_offset": 19988480, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "f007d0a0d61d786761cef304a1dd19b9fc8b308e220ecfcf5c0ec7e1b93baedb", + "section_hash_128_hex": "0dec0e97620b11032bf06d4ca4dafb2f", + "section_id": 10023, + "section_type": 0, + "table_index": 27, + "unaligned_length": 158 + }, + { + "aligned_length": 65536, + "data_offset": 20054016, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "2395cbee51dbb1542ca993f220a3fdbca288a5e1ffc9e5c9965e840d9731315c", + "section_hash_128_hex": "10733e49e778e5ca3d1b91c5caf9fdb0", + "section_id": 10024, + "section_type": 0, + "table_index": 28, + "unaligned_length": 16384 + }, + { + "aligned_length": 65536, + "data_offset": 20119552, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "e08e2b060b65fc2c2238a25dc24a382f25b1a9cd16d213db23f903a4f63fa586", + "section_hash_128_hex": "18b88893367fec945f037d89177f142d", + "section_id": 10025, + "section_type": 0, + "table_index": 29, + "unaligned_length": 68 + }, + { + "aligned_length": 65536, + "data_offset": 20185088, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "1ebba50f2d6b7cae75af96eae91c153531c3c5af7c45cb6b25e771171976089c", + "section_hash_128_hex": "e5289dc99b7f129329615002a80bf542", + "section_id": 10026, + "section_type": 0, + "table_index": 30, + "unaligned_length": 16384 + }, + { + "aligned_length": 65536, + "data_offset": 20250624, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "0109e2bebbb815116a917b6856dacfa5b3503cdda968df435e15c1d3102b3a75", + "section_hash_128_hex": "1175c59f19a03b706bc5a2c3bf575e4c", + "section_id": 10027, + "section_type": 0, + "table_index": 31, + "unaligned_length": 15236 + }, + { + "aligned_length": 327680, + "data_offset": 20316160, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "c3394048f4f62fb3c8f0e38dc211381f696fff9c9433e023799f0a974791b08c", + "section_hash_128_hex": "2912ca54a75987bad9fe5a23272884ec", + "section_id": 10028, + "section_type": 0, + "table_index": 32, + "unaligned_length": 262292 + }, + { + "aligned_length": 327680, + "data_offset": 20643840, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "302f066c0260ba757b36418a3d26cd6ce3fc1050425825124853361369c1238a", + "section_hash_128_hex": "0bf87bbe55467cb673ffc196507a208e", + "section_id": 10029, + "section_type": 0, + "table_index": 33, + "unaligned_length": 322065 + }, + { + "aligned_length": 8323072, + "data_offset": 20971520, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "c7f61e99ae4a7f7ba4d71310d544b960dbcb8e829da945b5f2a855a7efb19eee", + "section_hash_128_hex": "4d12d094fc7e50df2ea1c89f780d6c93", + "section_id": 10030, + "section_type": 0, + "table_index": 34, + "unaligned_length": 8294548 + }, + { + "aligned_length": 8323072, + "data_offset": 29294592, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "37436d1fa619a3b1bfccc02a48e6732d04788796d048b9759df561031f5d17d1", + "section_hash_128_hex": "65b9ebaadfc0805628baace4a38b988a", + "section_id": 10031, + "section_type": 0, + "table_index": 35, + "unaligned_length": 8294548 + }, + { + "aligned_length": 8323072, + "data_offset": 37617664, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "694ba0c7dc64972457b60eec2e6174239b6e637d52ed72373f894a12c56af600", + "section_hash_128_hex": "655938af9d155518ff24d4cb0cf45fa1", + "section_id": 10032, + "section_type": 0, + "table_index": 36, + "unaligned_length": 8294548 + }, + { + "aligned_length": 65536, + "data_offset": 45940736, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "b18c8b751fd1013c80f55e20bd99e8d36a515b6fb38823d1b96cc3cfa743ebde", + "section_hash_128_hex": "942bb66d169e098872dc7197d7912f43", + "section_id": 10033, + "section_type": 0, + "table_index": 37, + "unaligned_length": 2200 + }, + { + "aligned_length": 65536, + "data_offset": 46006272, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "43adf29b36a0a805525f81539b294624d2c9425e194ef5ab2aee9ca54728a2c6", + "section_hash_128_hex": "2b56a5c7b71570d3a31c7f73c5879d90", + "section_id": 10034, + "section_type": 0, + "table_index": 38, + "unaligned_length": 32091 + }, + { + "aligned_length": 65536, + "data_offset": 46071808, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "3e2e7bc6d5b2934f84543eeeac2028f8f1d0ecdada7525adad0fa77f93140820", + "section_hash_128_hex": "fa19dc93ba23919e2e9372bff321821c", + "section_id": 10035, + "section_type": 0, + "table_index": 39, + "unaligned_length": 916 + }, + { + "aligned_length": 327680, + "data_offset": 46137344, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "964b8c0ab5836e6e7e45c9caab4e131798f9a877ef7c39ed2a1bcff79f932a4b", + "section_hash_128_hex": "a6f49b5d09602a2ef21a0db5fb49ed8d", + "section_id": 10036, + "section_type": 0, + "table_index": 40, + "unaligned_length": 270084 + }, + { + "aligned_length": 65536, + "data_offset": 46465024, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "17d953a54c5a31bb0c3ed1b206e69e7989782d602d0902d13bd9605fc1209841", + "section_hash_128_hex": "d311e1f8863d40615f9a17b6340535e9", + "section_id": 10037, + "section_type": 0, + "table_index": 41, + "unaligned_length": 134 + }, + { + "aligned_length": 65536, + "data_offset": 46530560, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "c3d24fda59c5fc1aa72ed569e80692a976c504dc41d9b8a710e416c2291fa2a4", + "section_hash_128_hex": "ff379ba34b0f93a6ac097319d20d0145", + "section_id": 10038, + "section_type": 0, + "table_index": 42, + "unaligned_length": 68 + }, + { + "aligned_length": 6291456, + "data_offset": 46596096, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "6e61b1319f29923ea0c349a99824a8f488bcfa25606d0352df4b34a6b19f360b", + "section_hash_128_hex": "77216771cc6dd0995469ad6b4cc101bc", + "section_id": 10039, + "section_type": 0, + "table_index": 43, + "unaligned_length": 6291456 + }, + { + "aligned_length": 3145728, + "data_offset": 52887552, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "5d071479551a680a928a032b45e60a5047a89e8984c58ad35932567fb60c9013", + "section_hash_128_hex": "3452468f8bed9fe7ee03b172696b5960", + "section_id": 10040, + "section_type": 0, + "table_index": 44, + "unaligned_length": 3145728 + }, + { + "aligned_length": 3145728, + "data_offset": 56033280, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "b46712d9303fb903447887fc8e55007c2c1e9e4a04f9dda7b89b637c6155927f", + "section_hash_128_hex": "770861f7bf103689c80ec74b49a1ea39", + "section_id": 10041, + "section_type": 0, + "table_index": 45, + "unaligned_length": 3145728 + }, + { + "aligned_length": 65536, + "data_offset": 59179008, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "feb602e89422ee8c709a166df3957525aea67dbce59494ba5166d90578bccd9e", + "section_hash_128_hex": "2b56a5c7b71570d3a31c7f73c5879d90", + "section_id": 10042, + "section_type": 0, + "table_index": 46, + "unaligned_length": 32091 + }, + { + "aligned_length": 65536, + "data_offset": 59244544, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "f3d64a9113991fd46b074f82960a613b005fec8c159c06c59944d898166cdccd", + "section_hash_128_hex": "e7f7756ba4d5dbb8a0ccc41cf2742124", + "section_id": 10043, + "section_type": 0, + "table_index": 47, + "unaligned_length": 2860 + }, + { + "aligned_length": 65536, + "data_offset": 59310080, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "d1d857e78c0282432aa7194d350f0ba53ffcea1b18227cf97125f69599f6f2f8", + "section_hash_128_hex": "ca372db503f8a4786e2408cc588c152d", + "section_id": 10044, + "section_type": 0, + "table_index": 48, + "unaligned_length": 32016 + }, + { + "aligned_length": 65536, + "data_offset": 59375616, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "62ecc81f14923ef3d24d57e77387cdf776d3dc9c6353eef9db8f68e5edd450b4", + "section_hash_128_hex": "7031a8fb56959004ca0dab9fad092ee1", + "section_id": 10045, + "section_type": 0, + "table_index": 49, + "unaligned_length": 128 + }, + { + "aligned_length": 65536, + "data_offset": 59441152, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "c9ea9097cc480e959ac215bbe67cdff775b27661eedc9645125b104b73edd2bc", + "section_hash_128_hex": "9f178de8d922edec06c278c8ee2b778f", + "section_id": 10046, + "section_type": 0, + "table_index": 50, + "unaligned_length": 1090 + }, + { + "aligned_length": 0, + "data_offset": 59506688, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "7d990d9be76b527124453ff1f92cbf6e8646259a1ae456a6698d5a47b5f90778", + "section_hash_128_hex": "e2d1cc1515445b5004c38566a6ff9080", + "section_id": 10047, + "section_type": 0, + "table_index": 51, + "unaligned_length": 0 + }, + { + "aligned_length": 65536, + "data_offset": 59506688, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "90cf476ba9df8e6ebceeb4ae4bc7e72375c5b486f316154eb5579209ac273080", + "section_hash_128_hex": "6f8b5479dff8de425b05ba2125dd1612", + "section_id": 10048, + "section_type": 0, + "table_index": 52, + "unaligned_length": 1090 + }, + { + "aligned_length": 336789504, + "data_offset": 59572224, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "83bf2c900283bc3c737c85915c04c82cc2d73ff7a0900ce25e385ec967545c43", + "section_hash_128_hex": "ae0a9f7c85278b2732e715d01e76f859", + "section_id": 10049, + "section_type": 0, + "table_index": 53, + "unaligned_length": 336789504 + }, + { + "aligned_length": 65536, + "data_offset": 396361728, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "3b4cb128f6cd3f83f09e90e0e7e3b6256275e4bb3d2a92b5436ca73e892c6a5d", + "section_hash_128_hex": "4f11f8e8553e71308503754ea9560c96", + "section_id": 10050, + "section_type": 0, + "table_index": 54, + "unaligned_length": 494 + }, + { + "aligned_length": 65536, + "data_offset": 396427264, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "1dba27e28765490b5292e931cea6dac972f744b9ddef797a357200334a7b3c07", + "section_hash_128_hex": "4f11f8e8553e71308503754ea9560c96", + "section_id": 10051, + "section_type": 0, + "table_index": 55, + "unaligned_length": 494 + }, + { + "aligned_length": 65536, + "data_offset": 396492800, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "68adff87283b154464f62fd21e0a742640868088bf598008b54674974e9bf7dc", + "section_hash_128_hex": "f1c8cabd977903698df31992760bfc8b", + "section_id": 10052, + "section_type": 0, + "table_index": 56, + "unaligned_length": 242 + }, + { + "aligned_length": 65536, + "data_offset": 396558336, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "d35ec0efe43784983dcaa5995145fc0fa4347de5d502025959a023be07f25596", + "section_hash_128_hex": "dba2a9bbac06143a70b4c719b2a2cd9b", + "section_id": 10053, + "section_type": 0, + "table_index": 57, + "unaligned_length": 298 + }, + { + "aligned_length": 107544576, + "data_offset": 396623872, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "e9628b2657dc7105e8eaf66a4042302c00fd8af701e6bed4202e9cf4e47881ee", + "section_hash_128_hex": "f30fde0c7857ab4b6206f1bbcb67f0c3", + "section_id": 10054, + "section_type": 0, + "table_index": 58, + "unaligned_length": 107544576 + }, + { + "aligned_length": 65536, + "data_offset": 504168448, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 1, + "raw_sha256": "8e743850edb64808bb264c7042dc68bd85ebfdf0f9005a283bd1ebb65e769963", + "section_hash_128_hex": "4eacff17aa6a9b7d7fe13ff2acd2252e", + "section_id": 10054, + "section_type": 0, + "table_index": 59, + "unaligned_length": 600 + }, + { + "aligned_length": 65536, + "data_offset": 504233984, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 2, + "raw_sha256": "a59974796490c0be644672e411862edb8c39a744338a0fe22dcd429fe6690596", + "section_hash_128_hex": "cd55d4e916eb15a153cf3b0306620ac1", + "section_id": 10054, + "section_type": 0, + "table_index": 60, + "unaligned_length": 6820 + }, + { + "aligned_length": 65536, + "data_offset": 504299520, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "e2a50589e6bf7c8d5d9487500cf26055a3dfe4b226c8ed27d4982e00d7c2b050", + "section_hash_128_hex": "25effd4e5d992d3d1344e731067b747d", + "section_id": 1, + "section_type": 0, + "table_index": 61, + "unaligned_length": 1024 + } + ], + "source": "local-mediafire-inner", + "source_size": 504365056, + "structural_fingerprint_sha256": "7bb1b70187d1f48370b9bae218429af79105585bd043307824c0d84982e6922f", + "table_padding_bytes": 58504, + "tables_end": 7032, + "trailing_data_bytes": 0, + "warnings": [] + }, + "official_y2jb_1_6_4_03": { + "classification": "SIECAF_VALID_STRUCTURE", + "duplicate_hash_section_ids": [], + "duplicate_metadata_section_ids": [], + "duplicate_metadata_section_keys": [], + "errors": [], + "gaps": [], + "header": { + "file_offset": 65536, + "file_size": 504430592, + "iv_12_hex": "048e8200058e8200068e8200", + "key_or_unknown_16_redacted": true, + "key_or_unknown_16_sha256": "8d97a2f5439f79258750503b77102650ea7289e02e18e481bf0e1aaa4e5f71aa", + "magic_ascii": "SIECAF", + "magic_hex": "5349454341460000", + "mode_i32": 1, + "padding_1_u32": 0, + "padding_2_u32": 0, + "padding_3_u32": 0, + "raw_sha256": "1af2bd937e5b6a2386331893f7ea1295661858cb91c90ee728141a4fec2dbf9b", + "segment_count": 64, + "unknown_u64": 1, + "version_i32": 3 + }, + "normalized_hash_blocks_sha256": "c3236f5d85a4d7741be5a1fda5ce96d2bb7f005b0e48fed043af5ea48fb045d2", + "normalized_layout_sha256": "de6cc47686e5e301b725267b9b777c80a07a788e51f214cfb6e9d7f6e2e11110", + "normalized_segment_table_sha256": "d31def8e731d7bba0eaf23177f7c7245ef17f5d37ade53c9ca18744154bc8e25", + "overlaps": [], + "repeated_metadata_section_ids": [ + 10056 + ], + "section_hashes": [ + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "1506e357a88dced1f4869875d2d5ba08e3a23ad97c2dadd0f55e7519263fd310", + "section_hash_128_hex": "9c1ec5575d364bb8c07e1a352b6df5bf", + "section_id": 0, + "section_type": 0, + "table_index": 0 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "cda98abfbab6580ec381f94fd36cb189088fb0c04cc71ce498de4f2bab99f9e8", + "section_hash_128_hex": "f24fd080bfe9bc885bbb621355ef6e3c", + "section_id": 1, + "section_type": 0, + "table_index": 1 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "6737233cf89fe10bf272326e260ba967c7bd04016ac15a51441684ed152f490b", + "section_hash_128_hex": "7ded46ccffb2b9db7443e3698044314f", + "section_id": 2, + "section_type": 0, + "table_index": 2 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "c6420023bc160ab1cf43f6c8c0e30baa1dd11875975e894343e7dc538191d55e", + "section_hash_128_hex": "a89e1dfaa2321a9be50830127cbc164e", + "section_id": 3, + "section_type": 0, + "table_index": 3 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "d0bfacc3b7b9f223ff1452e85409a53e2383a75f4f155532f20068576d521d51", + "section_hash_128_hex": "bcb676db9821311f55ea66f860948129", + "section_id": 4, + "section_type": 0, + "table_index": 4 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "b476759eaefc35a8843aaa2b99005243549894553ac92fb04b4d5b9cb2b59d3e", + "section_hash_128_hex": "3d0b697da42f2f47f10480d796feab10", + "section_id": 5, + "section_type": 0, + "table_index": 5 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "db3eaba201fb19ff9dcdd0a0be01b5ae7c5bc12c130438e07cc306550db21d1a", + "section_hash_128_hex": "f65fdcb4699d9c9a9ad5e6acb6eb7637", + "section_id": 6, + "section_type": 0, + "table_index": 6 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "d6044b8c45d62bf48a5779e5e1aa51e3747d1cdeea6eb494815e4e7e7bf59e9a", + "section_hash_128_hex": "178785ab89e3f444aeceba9c9455a9a7", + "section_id": 7, + "section_type": 0, + "table_index": 7 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "2356ec8db72cb7acb22c4d37aa7328900d8bdc76cb619c2c23d3bb8e0f75bc6f", + "section_hash_128_hex": "ba9bded33dcb23f4863f1bfe88b4f011", + "section_id": 8, + "section_type": 0, + "table_index": 8 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "b31dd17061acf277281bb1bb7a54c6b267b212df672102f252e02fbd1e9443d0", + "section_hash_128_hex": "8e68ff4d7bcba57a541a447bdc08134a", + "section_id": 9, + "section_type": 0, + "table_index": 9 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "d53916173b3781f9097e801c16fc6d55f811ed7f7161e860a1eec32c62880359", + "section_hash_128_hex": "ed6275647aaecb3f9db2a463d343d983", + "section_id": 10, + "section_type": 0, + "table_index": 10 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "e62ef8b0e0e3586db6a3e68f80bb33338e831d9a9b5ee0d66847a6169245852c", + "section_hash_128_hex": "97ea4dcaf0f620b412b1f26c13f80983", + "section_id": 11, + "section_type": 0, + "table_index": 11 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "77a163b7542a0177c0185fab9aece62db793e1fe4b1db13dec909b28d6cb75bf", + "section_hash_128_hex": "065153ac9837d946f88c8ecc4ea5e85b", + "section_id": 12, + "section_type": 0, + "table_index": 12 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "171d9c5396359f6ebe8a5daa3682526858524cd002a7685c3d31d7f00fcc3a5e", + "section_hash_128_hex": "8ffb4a6e1836c7fa5b1edc661f020033", + "section_id": 13, + "section_type": 0, + "table_index": 13 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "8bcf92c955c384cd8f508bbbc83bdd9c849c734a996af48421b42f7284fc1c63", + "section_hash_128_hex": "eb540565033187562e0623a000c7c453", + "section_id": 14, + "section_type": 0, + "table_index": 14 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "a309a7b6b5b246af459d464c191e77b3b5790c2b9cc8d4fd308a2eebe9bfca88", + "section_hash_128_hex": "a32de48376c0c7d4f74d417d199be6f2", + "section_id": 15, + "section_type": 0, + "table_index": 15 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "ea6b0971a716817974ba153bf7080b7d0610555b61c44527846e27f6b27adf1f", + "section_hash_128_hex": "cc018508ff8be9010229f0971fdfd860", + "section_id": 16, + "section_type": 0, + "table_index": 16 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "ee4301c16defbf93265553c91abc50cdec2dae0e426f7792d14d0a905afcdb34", + "section_hash_128_hex": "5a207108bd9880a2fabe491872c078eb", + "section_id": 17, + "section_type": 0, + "table_index": 17 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "e7324c91fcbdc0a7ac3b70df76a46a30ce08a1f9e1ff0f147dbdbd25bdf1b732", + "section_hash_128_hex": "d41fdb620c23e1072254a785181a5c19", + "section_id": 18, + "section_type": 0, + "table_index": 18 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "e6c6645251a9e2aad24431931a3e16d75629d49d61b0b1083e648975abe1d408", + "section_hash_128_hex": "747d6153585441e01915095735b9f49c", + "section_id": 19, + "section_type": 0, + "table_index": 19 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "5998a89844fcf6f5e2e02077d0d0cb02d03a460ed7fbbd56ecfbe6b70685b4f3", + "section_hash_128_hex": "747d6153585441e01915095735b9f49c", + "section_id": 20, + "section_type": 0, + "table_index": 20 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "c92f148720a3bf63b0bc04b5ff58342a1acf4e7f334e4e9fa3b842130a9eb8ec", + "section_hash_128_hex": "4f15f1da5819524dc32d58c0f5d318f1", + "section_id": 21, + "section_type": 0, + "table_index": 21 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "b51d6789586d1a6eff2832b4df3dcaa686679d9f6c337b3673c31d63e97292d1", + "section_hash_128_hex": "d79b8888e5ede1ded97f1263278456b1", + "section_id": 22, + "section_type": 0, + "table_index": 22 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "6d6a1882d32ea255cb5b1501d5cbdcef7fa1cd142c22bbd57cfcf752c3617cb8", + "section_hash_128_hex": "2061ef963a5e3ff12e54bd0bd0e83a07", + "section_id": 23, + "section_type": 0, + "table_index": 23 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "6dce9d12420eb4c6c5689dc8096df19330c8bb12a2c96fd3ddc336d1b102d9ef", + "section_hash_128_hex": "29ea4b5e9a86f817bb4169ffa32f960b", + "section_id": 24, + "section_type": 0, + "table_index": 24 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "299e70743fc122ab9ecff9945727d003f0335712ad055e821761e0a906450bd1", + "section_hash_128_hex": "76e9ff1ea6589b6f79807ef88e2002ef", + "section_id": 25, + "section_type": 0, + "table_index": 25 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "c8c23e624b2c36089d7574e60a30f47354b127bfb7b6515e42d1cdbf4bbeadd1", + "section_hash_128_hex": "823d209719487ab389ba36634e3fa7d5", + "section_id": 26, + "section_type": 0, + "table_index": 26 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "da7f448d2b631d7f66b705f2771fb8fd8230a93a5277a7ba695a4dd5e6efa3af", + "section_hash_128_hex": "8000ab3d2114168390deee91a5eb54bf", + "section_id": 27, + "section_type": 0, + "table_index": 27 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "3ba4b4822f9a068bff77c247670dedca2e21be208052c6751044557f4a71f6b4", + "section_hash_128_hex": "740841eec5e95dc53a897e4bba6c230e", + "section_id": 28, + "section_type": 0, + "table_index": 28 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "b7d142a34a63beff042f71364e10fa4fc4f5585bdb9ef530df1edce49e834cc0", + "section_hash_128_hex": "350e91a813f09b523684ea6937885572", + "section_id": 29, + "section_type": 0, + "table_index": 29 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "d810046a05af2f1fd93471ec907e322b90380c6566fc69cce0c3e6ea2aa8931d", + "section_hash_128_hex": "f16426bacbdbf47d1000b72c59d9651f", + "section_id": 30, + "section_type": 0, + "table_index": 30 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "aa153dce756c2749dedcd3efc75b5f99b21d2f49d66a3fe48f5a98870b14d585", + "section_hash_128_hex": "2c1bd8b6e62e3b778724efb8b41d79fa", + "section_id": 31, + "section_type": 0, + "table_index": 31 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "6da03e3593d7d0d6637b30f4ce73757a99558352e55599578b206b2a04689fe9", + "section_hash_128_hex": "098744e15b9c586e8785e2788082c525", + "section_id": 32, + "section_type": 0, + "table_index": 32 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "f48fa6155cf1c1c9ee4471aceb0a5d91a5d03500578bcffa93f815601cedb0b8", + "section_hash_128_hex": "8a979c716c8c90ecdf9911fcefb26117", + "section_id": 33, + "section_type": 0, + "table_index": 33 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "09b8e7bef0d2ec985c84d1abed65fc175c99da993495d4220a606b2f739a78b7", + "section_hash_128_hex": "90a6606925d1a5de78327a09f52a5ef4", + "section_id": 34, + "section_type": 0, + "table_index": 34 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "46272c47516825223e85bca418cb0526a4a4e5ed6ca6b1cb7537da66dba01651", + "section_hash_128_hex": "2cc2b558edcf1f6648288da6ef5da486", + "section_id": 35, + "section_type": 0, + "table_index": 35 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "7bef41e5f4b180cf2338c788aa38bfa9e4701aeb5afb01d92833dd5b12b5a5c3", + "section_hash_128_hex": "f33b59ae49d0fa2c88f3af7b7d553577", + "section_id": 36, + "section_type": 0, + "table_index": 36 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "9b0bcd037915c2bcbdb80b723313f19616ee7fb9859a0289134bbb45fb1a94d4", + "section_hash_128_hex": "d1c02e140418bb73e012d8c662344619", + "section_id": 37, + "section_type": 0, + "table_index": 37 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "25e76eacc7e00a776cada8021274e433daf5384abba321363d245f5f2be8547f", + "section_hash_128_hex": "17214735b9ea1121d8ae484c2fe04819", + "section_id": 38, + "section_type": 0, + "table_index": 38 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "96d47e6116f1f305cf689809d7ed6ad1142ff5dd3d46712aaffaa284ee2a436a", + "section_hash_128_hex": "8000ab3d2114168390deee91a5eb54bf", + "section_id": 39, + "section_type": 0, + "table_index": 39 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "cf242d154aa4122f9396e2ecc7510d7792e350a432f822d4d2b09c64fba9a5bd", + "section_hash_128_hex": "8b3b175ba66d83df482c4086e19e2954", + "section_id": 40, + "section_type": 0, + "table_index": 40 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "d2433f6108f71c75857a7c78200c6a3dbbdc308e1dc0b3de7e0228cd7e0b32e7", + "section_hash_128_hex": "4e2cf75486764ea27e29bb72b93d72a8", + "section_id": 41, + "section_type": 0, + "table_index": 41 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "254e64bf6b3134a3fcd8ca575620014d5766c452ead3c6782700f1093a1bf8d8", + "section_hash_128_hex": "75e801f61c082d644809bd1389e4fd21", + "section_id": 42, + "section_type": 0, + "table_index": 42 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "b77b3fe53556ff97aaf0aaf236afdf8e98fca86bf8792ff2bcb6a894ef5f88f5", + "section_hash_128_hex": "8520f4a71b9282f57ce5d1320234bfea", + "section_id": 43, + "section_type": 0, + "table_index": 43 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "21beb82db657129e39636cf9041646e0a85db43ccd408cfb9b516a93ac40e32e", + "section_hash_128_hex": "de89d48c58545101aae859324ee4f051", + "section_id": 44, + "section_type": 0, + "table_index": 44 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "669591228d2d7a7e3186df72d5802501e1ad44027a1caf7ef1acf23e56b2691f", + "section_hash_128_hex": "5e851f836b22577d2160debf35c84b50", + "section_id": 45, + "section_type": 0, + "table_index": 45 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "633f32094c55d65e2f580f945d049d2bcb647ef79d6f45792d3347be1251fbe9", + "section_hash_128_hex": "49e0879a3430c956827301db2cb0d7fa", + "section_id": 46, + "section_type": 0, + "table_index": 46 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "a4faa3e8d2ab9bfadd47fa9f1cc082c78e169a116142f7b65c39da49f60e64df", + "section_hash_128_hex": "17214735b9ea1121d8ae484c2fe04819", + "section_id": 47, + "section_type": 0, + "table_index": 47 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "ca5589d272ac71b5b29426cbde1a20fa15d9ec5b453034957f93a4fd887b0dc6", + "section_hash_128_hex": "d1d539a122c0b080e45de941d4e3ac54", + "section_id": 48, + "section_type": 0, + "table_index": 48 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "0aebd31e90ab9f2d990001b6d3f5c857b9ef2484b4e1519e4db67b3cec718357", + "section_hash_128_hex": "1f5549a7a01c9eac8896965a265135e8", + "section_id": 49, + "section_type": 0, + "table_index": 49 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "6994b9b8d9a057306f99c0db8d2e9781b054d510adf142a45dbc601ea1c69dd5", + "section_hash_128_hex": "82a3283934c072a0a2f487307388f2b5", + "section_id": 50, + "section_type": 0, + "table_index": 50 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "8e3aca35a3740be2fe40468154956acfac7fc0f0e7be73cc84bb5398c7785f31", + "section_hash_128_hex": "e37fec642b1e3e185f1678ab45c381cb", + "section_id": 51, + "section_type": 0, + "table_index": 51 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "ce0ead7a5cb153bf7ccfa0daa675eb2c7c45c39c5021a399ae6d2e3bd9403ada", + "section_hash_128_hex": "7f71b19ad11f0947833eb3403a786275", + "section_id": 52, + "section_type": 0, + "table_index": 52 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "dd9729df74fe7474ec8a42de827ecbfa044fe83f3cf1a21326f2694ad430cfdb", + "section_hash_128_hex": "747d6153585441e01915095735b9f49c", + "section_id": 53, + "section_type": 0, + "table_index": 53 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "8e701fa7c643f8509b4d621bdd9aa71f280a70ef8259a8123d8efe2e861a0922", + "section_hash_128_hex": "c0fbd62a09dedba99163ce6c18048f3c", + "section_id": 54, + "section_type": 0, + "table_index": 54 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "c43bdfb11c15ccef4d831eec523fb7e862f8ade99153747d9a5fbb9ed368c7ae", + "section_hash_128_hex": "43e0ac41d2c1dad9a6ac1945de2be9a4", + "section_id": 55, + "section_type": 0, + "table_index": 55 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "9dad06934c1db29b3deed572370d47447dd2a532f7843a874e302e60144adc8f", + "section_hash_128_hex": "53e9fa90e2b21f523848347f80167e0a", + "section_id": 56, + "section_type": 0, + "table_index": 56 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "533df9b5472d65cd881c880db4ba4636d618a3394654e7e44d92118c1f6bfcd1", + "section_hash_128_hex": "53e9fa90e2b21f523848347f80167e0a", + "section_id": 57, + "section_type": 0, + "table_index": 57 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "1a09a78677de670769b149fa0f112e772ea7b674107afda28e03c94f636be6a9", + "section_hash_128_hex": "f421c60211096dc420badf271e90f8b0", + "section_id": 58, + "section_type": 0, + "table_index": 58 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "b867c53c7e7b64052a4cff269440d82fb4e5d193bc82e049d45ccb0e0458499e", + "section_hash_128_hex": "58f9eb19090b741ae46004fb064057be", + "section_id": 59, + "section_type": 0, + "table_index": 59 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "6047e09f41b2d51ef914952d897b492f026d38e7e4c52051ca8b3a33fd5be546", + "section_hash_128_hex": "fc2df50aa31c0342696cfc45acceaa06", + "section_id": 60, + "section_type": 0, + "table_index": 60 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "a8e5de60315f4e34cb4897bb5a17df4d8dc997781194cf31905c050d111a15ba", + "section_hash_128_hex": "f2a5568c9a47b14a8e436a5b1aeb3dfe", + "section_id": 61, + "section_type": 0, + "table_index": 61 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "90cda931859db5c2f889d92c4430e1e9ff6116d9bc462ac97508f6ab383097a9", + "section_hash_128_hex": "638515dd1f53808a6fb5bba73328f710", + "section_id": 62, + "section_type": 0, + "table_index": 62 + }, + { + "padding_hex": "000000000000000000000000000000000000000000000000", + "raw_sha256": "22582df0e17e465c633f368a5797d82d7a453fbec4dffb207cf277670dc561e0", + "section_hash_128_hex": "06aa8f8fea3d2c14c19f257d3bf8502f", + "section_id": 63, + "section_type": 0, + "table_index": 63 + } + ], + "segments": [ + { + "aligned_length": 131072, + "data_offset": 65536, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "01386defa3875025f7ea2dd8e86c4d490432167ff9f147d00cd91085402b17f7", + "section_hash_128_hex": "9c1ec5575d364bb8c07e1a352b6df5bf", + "section_id": 2, + "section_type": 0, + "table_index": 0, + "unaligned_length": 103416 + }, + { + "aligned_length": 65536, + "data_offset": 196608, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "4d68beb6fa5af26517743f925e3513cda7496f9410e3b3043b36fed50847240e", + "section_hash_128_hex": "f24fd080bfe9bc885bbb621355ef6e3c", + "section_id": 3, + "section_type": 0, + "table_index": 1, + "unaligned_length": 63384 + }, + { + "aligned_length": 851968, + "data_offset": 262144, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "05053620e233d16a87995801837cb9d36b0f3c366d9d59d226e4f256b6f46ad6", + "section_hash_128_hex": "7ded46ccffb2b9db7443e3698044314f", + "section_id": 4, + "section_type": 0, + "table_index": 2, + "unaligned_length": 822272 + }, + { + "aligned_length": 65536, + "data_offset": 1114112, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "22dbb022966418f0c79f036a2eb9bbac7d080d0bb4eedae6b867a5611cedc865", + "section_hash_128_hex": "a89e1dfaa2321a9be50830127cbc164e", + "section_id": 5, + "section_type": 0, + "table_index": 3, + "unaligned_length": 40016 + }, + { + "aligned_length": 131072, + "data_offset": 1179648, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "a235aa8f9cf425e2370e151c9dfd93347289ec9de0ffdd4466d9fbf58e7e5e77", + "section_hash_128_hex": "bcb676db9821311f55ea66f860948129", + "section_id": 10000, + "section_type": 0, + "table_index": 4, + "unaligned_length": 81920 + }, + { + "aligned_length": 16777216, + "data_offset": 1310720, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "da2fb82cd690e7babf123431b0b5cfe159773b86492cf81bfb6a151919d41166", + "section_hash_128_hex": "3d0b697da42f2f47f10480d796feab10", + "section_id": 10001, + "section_type": 0, + "table_index": 5, + "unaligned_length": 16777216 + }, + { + "aligned_length": 65536, + "data_offset": 18087936, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "23d3684e17fb73431ad8939aaf2453fa389ec71df726803c22be57c2ab2fda4d", + "section_hash_128_hex": "f65fdcb4699d9c9a9ad5e6acb6eb7637", + "section_id": 10002, + "section_type": 0, + "table_index": 6, + "unaligned_length": 20 + }, + { + "aligned_length": 65536, + "data_offset": 18153472, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "ee3faf83f2c2629451c914429d5b7be597edbae395f61c628854ac729f81ebd2", + "section_hash_128_hex": "178785ab89e3f444aeceba9c9455a9a7", + "section_id": 10003, + "section_type": 0, + "table_index": 7, + "unaligned_length": 8948 + }, + { + "aligned_length": 65536, + "data_offset": 18219008, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "9bddf4588a85b4480e1159622b5462be724c0b1d901837b04d034dd20323d5bd", + "section_hash_128_hex": "ba9bded33dcb23f4863f1bfe88b4f011", + "section_id": 10004, + "section_type": 0, + "table_index": 8, + "unaligned_length": 75 + }, + { + "aligned_length": 65536, + "data_offset": 18284544, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "cdb1715c3490faea4855ba004e497275eec659323aff017bbf15643e5a07088a", + "section_hash_128_hex": "8e68ff4d7bcba57a541a447bdc08134a", + "section_id": 10005, + "section_type": 0, + "table_index": 9, + "unaligned_length": 28672 + }, + { + "aligned_length": 65536, + "data_offset": 18350080, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "d8e783b83a260344eb1a10adbb482610f42dbd758d4dc7c20b4e9ea8ef763915", + "section_hash_128_hex": "ed6275647aaecb3f9db2a463d343d983", + "section_id": 10006, + "section_type": 0, + "table_index": 10, + "unaligned_length": 49152 + }, + { + "aligned_length": 65536, + "data_offset": 18415616, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "e5524833acf855ae369e1e2d7f3be316537ad36950c3fc88ec6ab9301d6e38fe", + "section_hash_128_hex": "97ea4dcaf0f620b412b1f26c13f80983", + "section_id": 10007, + "section_type": 0, + "table_index": 11, + "unaligned_length": 20480 + }, + { + "aligned_length": 65536, + "data_offset": 18481152, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "c948bb9251da960457a278ef1b7f132fbe17eb068a77ff88864c42d552ba8b7d", + "section_hash_128_hex": "065153ac9837d946f88c8ecc4ea5e85b", + "section_id": 10008, + "section_type": 0, + "table_index": 12, + "unaligned_length": 20480 + }, + { + "aligned_length": 393216, + "data_offset": 18546688, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "e739dc24f8639ebd9aeb0615d0cfdbaabfd6b18e1ff84df61f4f1a657cb0aa0e", + "section_hash_128_hex": "8ffb4a6e1836c7fa5b1edc661f020033", + "section_id": 10009, + "section_type": 0, + "table_index": 13, + "unaligned_length": 385024 + }, + { + "aligned_length": 262144, + "data_offset": 18939904, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "ddca057aeb951c98b04a42514d4ef823fe4991b758dd77e4af668576fd0abbfb", + "section_hash_128_hex": "eb540565033187562e0623a000c7c453", + "section_id": 10010, + "section_type": 0, + "table_index": 14, + "unaligned_length": 237568 + }, + { + "aligned_length": 65536, + "data_offset": 19202048, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "e046ab04dd86a4c8c491c92a9ce8b354f74c383aa4e0020df5ff766c88323e28", + "section_hash_128_hex": "a32de48376c0c7d4f74d417d199be6f2", + "section_id": 10011, + "section_type": 0, + "table_index": 15, + "unaligned_length": 24576 + }, + { + "aligned_length": 65536, + "data_offset": 19267584, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "0c0b3ab54b8277d6bf702fcaed18cfb9973068cb8151c87e602e0f9d838c8d93", + "section_hash_128_hex": "cc018508ff8be9010229f0971fdfd860", + "section_id": 10012, + "section_type": 0, + "table_index": 16, + "unaligned_length": 16 + }, + { + "aligned_length": 65536, + "data_offset": 19333120, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "6c09192b11d26bdf67314190a4b9a2237e351f85be2b4ff547dc83c75967881c", + "section_hash_128_hex": "5a207108bd9880a2fabe491872c078eb", + "section_id": 10013, + "section_type": 0, + "table_index": 17, + "unaligned_length": 36864 + }, + { + "aligned_length": 196608, + "data_offset": 19398656, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "fcbf5b03746eb5e1d939f4a78697ff9f4b58e4629e01c90c15904b17125a9cbf", + "section_hash_128_hex": "d41fdb620c23e1072254a785181a5c19", + "section_id": 10014, + "section_type": 0, + "table_index": 18, + "unaligned_length": 176128 + }, + { + "aligned_length": 0, + "data_offset": 19595264, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "b311b49d6209b240f6efa1c7574cadd8b1283faf1d13aecd37ade4c8b3ed2bb4", + "section_hash_128_hex": "747d6153585441e01915095735b9f49c", + "section_id": 10015, + "section_type": 0, + "table_index": 19, + "unaligned_length": 0 + }, + { + "aligned_length": 0, + "data_offset": 19595264, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "95eecb846529389faf06ed8201028bfab37add5f91f886877c59c3d67b7f2c30", + "section_hash_128_hex": "747d6153585441e01915095735b9f49c", + "section_id": 10016, + "section_type": 0, + "table_index": 20, + "unaligned_length": 0 + }, + { + "aligned_length": 65536, + "data_offset": 19595264, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "6598b06fdef64f7739635d3bbfaa27698387a8332fa6840f8f4e0d60b5f7b7cd", + "section_hash_128_hex": "4f15f1da5819524dc32d58c0f5d318f1", + "section_id": 10017, + "section_type": 0, + "table_index": 21, + "unaligned_length": 16384 + }, + { + "aligned_length": 65536, + "data_offset": 19660800, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "d2c034a7a262d8d666e5114528cd8bf45b588f8789a15b22e13e93ada8cbd4dd", + "section_hash_128_hex": "d79b8888e5ede1ded97f1263278456b1", + "section_id": 10018, + "section_type": 0, + "table_index": 22, + "unaligned_length": 128 + }, + { + "aligned_length": 65536, + "data_offset": 19726336, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "03695a2c3bf77e5cf4e9cca7261afa2fe34650313d1c7110ed3a1618f87b4246", + "section_hash_128_hex": "2061ef963a5e3ff12e54bd0bd0e83a07", + "section_id": 10019, + "section_type": 0, + "table_index": 23, + "unaligned_length": 12288 + }, + { + "aligned_length": 65536, + "data_offset": 19791872, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "3b6a03f5eed9536b4ffcf0e744ec221a56b5da623bb48f4210708929df1d98ed", + "section_hash_128_hex": "29ea4b5e9a86f817bb4169ffa32f960b", + "section_id": 10020, + "section_type": 0, + "table_index": 24, + "unaligned_length": 160 + }, + { + "aligned_length": 65536, + "data_offset": 19857408, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "73798df5da57aeb74655627bc78f1ca1b4636016a5d139ae328543e44cc793f8", + "section_hash_128_hex": "76e9ff1ea6589b6f79807ef88e2002ef", + "section_id": 10021, + "section_type": 0, + "table_index": 25, + "unaligned_length": 160 + }, + { + "aligned_length": 65536, + "data_offset": 19922944, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "4b7c36be6cd86e033fc21ac112dc477b5f6b1d0c1a3290406543c16290cc64bd", + "section_hash_128_hex": "823d209719487ab389ba36634e3fa7d5", + "section_id": 10022, + "section_type": 0, + "table_index": 26, + "unaligned_length": 160 + }, + { + "aligned_length": 65536, + "data_offset": 19988480, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "694e21b5b10aff58009f79ddac06d4d685ba4ef852d1fa0523c4e7da5894ab2f", + "section_hash_128_hex": "8000ab3d2114168390deee91a5eb54bf", + "section_id": 10023, + "section_type": 0, + "table_index": 27, + "unaligned_length": 2200 + }, + { + "aligned_length": 65536, + "data_offset": 20054016, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "78d8c55392172ae30193ea3215a64993fe6243975e24c0ced537765df042b56a", + "section_hash_128_hex": "740841eec5e95dc53a897e4bba6c230e", + "section_id": 10024, + "section_type": 0, + "table_index": 28, + "unaligned_length": 158 + }, + { + "aligned_length": 65536, + "data_offset": 20119552, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "e08e2b060b65fc2c2238a25dc24a382f25b1a9cd16d213db23f903a4f63fa586", + "section_hash_128_hex": "350e91a813f09b523684ea6937885572", + "section_id": 10025, + "section_type": 0, + "table_index": 29, + "unaligned_length": 68 + }, + { + "aligned_length": 65536, + "data_offset": 20185088, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "41f66a60a7d65b9c2cd2434f5051eb2cc2dd3ae6b87f4d4f994450fc5768e4ab", + "section_hash_128_hex": "f16426bacbdbf47d1000b72c59d9651f", + "section_id": 10026, + "section_type": 0, + "table_index": 30, + "unaligned_length": 128 + }, + { + "aligned_length": 65536, + "data_offset": 20250624, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "dd14d6f18c76be8bbb8a5767cb30e1e07e4b521b53c9c97c354cef7feaaea1a0", + "section_hash_128_hex": "2c1bd8b6e62e3b778724efb8b41d79fa", + "section_id": 10027, + "section_type": 0, + "table_index": 31, + "unaligned_length": 16384 + }, + { + "aligned_length": 65536, + "data_offset": 20316160, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "a2a101b2c717f648e16430c446c795cc0c4593db97d9fb18250aae7f7191e1cb", + "section_hash_128_hex": "098744e15b9c586e8785e2788082c525", + "section_id": 10028, + "section_type": 0, + "table_index": 32, + "unaligned_length": 5721 + }, + { + "aligned_length": 327680, + "data_offset": 20381696, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "c73a4e6e7ac6c30cb883e2acf2a42bd4d199fc0031502972a8dd6146132b6403", + "section_hash_128_hex": "8a979c716c8c90ecdf9911fcefb26117", + "section_id": 10029, + "section_type": 0, + "table_index": 33, + "unaligned_length": 262292 + }, + { + "aligned_length": 327680, + "data_offset": 20709376, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "c43fc01537934f2950a9bb01363a537f5cf1e913347a1a31ef5ed8d97ccff1a8", + "section_hash_128_hex": "90a6606925d1a5de78327a09f52a5ef4", + "section_id": 10030, + "section_type": 0, + "table_index": 34, + "unaligned_length": 322065 + }, + { + "aligned_length": 8323072, + "data_offset": 21037056, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "d2a9e90d8509a4ac9c8f33decfc378f4ff6adc7a084b25ba0228f80714a24171", + "section_hash_128_hex": "2cc2b558edcf1f6648288da6ef5da486", + "section_id": 10031, + "section_type": 0, + "table_index": 35, + "unaligned_length": 8294548 + }, + { + "aligned_length": 8323072, + "data_offset": 29360128, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "6e18d222b8503a853c70c070c2fb2c87d0d0a6c68ebb93c49cf6d550a69e0862", + "section_hash_128_hex": "f33b59ae49d0fa2c88f3af7b7d553577", + "section_id": 10032, + "section_type": 0, + "table_index": 36, + "unaligned_length": 8294548 + }, + { + "aligned_length": 8323072, + "data_offset": 37683200, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "4ac820e973c08ae436fdac40255322520c81b9da84723a4f2c3799acc73d92e8", + "section_hash_128_hex": "d1c02e140418bb73e012d8c662344619", + "section_id": 10033, + "section_type": 0, + "table_index": 37, + "unaligned_length": 8294548 + }, + { + "aligned_length": 65536, + "data_offset": 46006272, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "43adf29b36a0a805525f81539b294624d2c9425e194ef5ab2aee9ca54728a2c6", + "section_hash_128_hex": "17214735b9ea1121d8ae484c2fe04819", + "section_id": 10034, + "section_type": 0, + "table_index": 38, + "unaligned_length": 32091 + }, + { + "aligned_length": 65536, + "data_offset": 46071808, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "87b4d5e5e9ce96267bacecc78486e46a9ae513e7bc1b8baa5b988b7ded1f6970", + "section_hash_128_hex": "8000ab3d2114168390deee91a5eb54bf", + "section_id": 10035, + "section_type": 0, + "table_index": 39, + "unaligned_length": 2200 + }, + { + "aligned_length": 65536, + "data_offset": 46137344, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "8971b91886884635b229785d7a7e4ee82764c621171f24717531582a17f9817d", + "section_hash_128_hex": "8b3b175ba66d83df482c4086e19e2954", + "section_id": 10036, + "section_type": 0, + "table_index": 40, + "unaligned_length": 916 + }, + { + "aligned_length": 327680, + "data_offset": 46202880, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "63571f574e50e534f9ec36c817133a92b72447458cff6fcb66d1b0bc3ea23f75", + "section_hash_128_hex": "4e2cf75486764ea27e29bb72b93d72a8", + "section_id": 10037, + "section_type": 0, + "table_index": 41, + "unaligned_length": 270084 + }, + { + "aligned_length": 65536, + "data_offset": 46530560, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "fab351c6666d5caf65abc4a4ba8c51fce55ded3ea1ee653a12c4e9df9bc4ad46", + "section_hash_128_hex": "75e801f61c082d644809bd1389e4fd21", + "section_id": 10038, + "section_type": 0, + "table_index": 42, + "unaligned_length": 134 + }, + { + "aligned_length": 65536, + "data_offset": 46596096, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "459956fc7368ee8c36695b7b451470d9cc662badab25fd3977ac025b47d68de5", + "section_hash_128_hex": "8520f4a71b9282f57ce5d1320234bfea", + "section_id": 10039, + "section_type": 0, + "table_index": 43, + "unaligned_length": 68 + }, + { + "aligned_length": 6291456, + "data_offset": 46661632, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "965253b1eadb1cc8803b3d4c77a63107001bd16616fe60f71ca17b7db10d63eb", + "section_hash_128_hex": "de89d48c58545101aae859324ee4f051", + "section_id": 10040, + "section_type": 0, + "table_index": 44, + "unaligned_length": 6291456 + }, + { + "aligned_length": 3145728, + "data_offset": 52953088, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "f16d9380803a0d6efaeb6f6189dba09d8fbc0e4fdc1f69593e91e3ee7f82d250", + "section_hash_128_hex": "5e851f836b22577d2160debf35c84b50", + "section_id": 10041, + "section_type": 0, + "table_index": 45, + "unaligned_length": 3145728 + }, + { + "aligned_length": 3145728, + "data_offset": 56098816, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "63e2c7e87ab783795eae2c3d04e42eb6eefddac3713456d6b64a1920c20791f0", + "section_hash_128_hex": "49e0879a3430c956827301db2cb0d7fa", + "section_id": 10042, + "section_type": 0, + "table_index": 46, + "unaligned_length": 3145728 + }, + { + "aligned_length": 65536, + "data_offset": 59244544, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "730fb81ea6f89222a514b51df0b4df70b76c34ef05a4f0d02a95ac40d660cfe7", + "section_hash_128_hex": "17214735b9ea1121d8ae484c2fe04819", + "section_id": 10043, + "section_type": 0, + "table_index": 47, + "unaligned_length": 32091 + }, + { + "aligned_length": 65536, + "data_offset": 59310080, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "91329b8fc39010b8d7c8ee61aa73132095d168f626a0b2321312e3f017184869", + "section_hash_128_hex": "d1d539a122c0b080e45de941d4e3ac54", + "section_id": 10044, + "section_type": 0, + "table_index": 48, + "unaligned_length": 2860 + }, + { + "aligned_length": 65536, + "data_offset": 59375616, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "7af7e769a9707cc16e8cf3fb4704919506754755fded7ccdcd15c2bc5b13f057", + "section_hash_128_hex": "1f5549a7a01c9eac8896965a265135e8", + "section_id": 10045, + "section_type": 0, + "table_index": 49, + "unaligned_length": 32016 + }, + { + "aligned_length": 65536, + "data_offset": 59441152, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "daedf595ae5ec2ba9618d16d03169f9de6a3fb0a889d1962ee768241c759fcad", + "section_hash_128_hex": "82a3283934c072a0a2f487307388f2b5", + "section_id": 10046, + "section_type": 0, + "table_index": 50, + "unaligned_length": 128 + }, + { + "aligned_length": 65536, + "data_offset": 59506688, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "b6d36d787329e7292e1f7a546a63b13b014a5aae414466f71f6a6f995d28b13d", + "section_hash_128_hex": "e37fec642b1e3e185f1678ab45c381cb", + "section_id": 10047, + "section_type": 0, + "table_index": 51, + "unaligned_length": 264 + }, + { + "aligned_length": 65536, + "data_offset": 59572224, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "35355bd5217409bd6846a84807ff228e4ef24487c9ba59d70623ccc22f625e83", + "section_hash_128_hex": "7f71b19ad11f0947833eb3403a786275", + "section_id": 10048, + "section_type": 0, + "table_index": 52, + "unaligned_length": 1090 + }, + { + "aligned_length": 0, + "data_offset": 59637760, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "27f84fe1a220aefc0694448135a273c1b1a6c37f30e266a089705221efef8a2b", + "section_hash_128_hex": "747d6153585441e01915095735b9f49c", + "section_id": 10049, + "section_type": 0, + "table_index": 53, + "unaligned_length": 0 + }, + { + "aligned_length": 65536, + "data_offset": 59637760, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "8433ec472378865941fb737de2c54d8eacb121c3b623947eff2ac3e4c2a307e0", + "section_hash_128_hex": "c0fbd62a09dedba99163ce6c18048f3c", + "section_id": 10050, + "section_type": 0, + "table_index": 54, + "unaligned_length": 1090 + }, + { + "aligned_length": 336789504, + "data_offset": 59703296, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "3fd8ec059db435c2e8b8d8a4cb410b3b188b9cddc4dd4b28bf8d7f558859d243", + "section_hash_128_hex": "43e0ac41d2c1dad9a6ac1945de2be9a4", + "section_id": 10051, + "section_type": 0, + "table_index": 55, + "unaligned_length": 336789504 + }, + { + "aligned_length": 65536, + "data_offset": 396492800, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "67d1278bdf4b716a2eb2b779a1e9a577ae3b6aa1d079f8343c7b133ec7c392bf", + "section_hash_128_hex": "53e9fa90e2b21f523848347f80167e0a", + "section_id": 10052, + "section_type": 0, + "table_index": 56, + "unaligned_length": 494 + }, + { + "aligned_length": 65536, + "data_offset": 396558336, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "5f2555d71b5b8616de9093472a75d15a8eb80781bfa361ab29d506b6d5e56e32", + "section_hash_128_hex": "53e9fa90e2b21f523848347f80167e0a", + "section_id": 10053, + "section_type": 0, + "table_index": 57, + "unaligned_length": 494 + }, + { + "aligned_length": 65536, + "data_offset": 396623872, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "2b57b9d2a8eef03e579f228044eba943c3cd345f494e3627f3789a331efccd21", + "section_hash_128_hex": "f421c60211096dc420badf271e90f8b0", + "section_id": 10054, + "section_type": 0, + "table_index": 58, + "unaligned_length": 242 + }, + { + "aligned_length": 65536, + "data_offset": 396689408, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "e45d18bd892880252433e4f9cd6b475e373a2cc0f4ccf24e0e1c980b2fb4f39e", + "section_hash_128_hex": "58f9eb19090b741ae46004fb064057be", + "section_id": 10055, + "section_type": 0, + "table_index": 59, + "unaligned_length": 298 + }, + { + "aligned_length": 107544576, + "data_offset": 396754944, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "6f0d929bb8ba71e4a1d6f2455ce7b75894fb09c9673cec65421c003d08885202", + "section_hash_128_hex": "fc2df50aa31c0342696cfc45acceaa06", + "section_id": 10056, + "section_type": 0, + "table_index": 60, + "unaligned_length": 107544576 + }, + { + "aligned_length": 65536, + "data_offset": 504299520, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 1, + "raw_sha256": "2be655e80613b5c54f088733016525e325dbee481e1197c9d2aa9c9f005387cb", + "section_hash_128_hex": "f2a5568c9a47b14a8e436a5b1aeb3dfe", + "section_id": 10056, + "section_type": 0, + "table_index": 61, + "unaligned_length": 600 + }, + { + "aligned_length": 65536, + "data_offset": 504365056, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 2, + "raw_sha256": "e7feeecb7b3bbee76e1ca62077dad3b76845877f76a3f491f9f2eb33316cc4f1", + "section_hash_128_hex": "638515dd1f53808a6fb5bba73328f710", + "section_id": 10056, + "section_type": 0, + "table_index": 62, + "unaligned_length": 6820 + }, + { + "aligned_length": 65536, + "data_offset": 504430592, + "encryption_key_id_or_algorithm_version": 1, + "hash_key_id_or_algorithm_type": 3, + "iv_hex": "00000000000000000000000000000000", + "padding_1_u16": 0, + "part_number": 0, + "raw_sha256": "2d8c980e4242e5c1ccba1edcf4b419fd0e8467324596d4f2175216b964408566", + "section_hash_128_hex": "06aa8f8fea3d2c14c19f257d3bf8502f", + "section_id": 1, + "section_type": 0, + "table_index": 63, + "unaligned_length": 1024 + } + ], + "source": "official-gezine-y2jb-1.6-4.03-inner", + "source_size": 504496128, + "structural_fingerprint_sha256": "9c453e1fa99ef1f1e6fe1d992f9b0a2ede4dbd4b9a2ad8551c08c8c61ed9be2a", + "table_padding_bytes": 58280, + "tables_end": 7256, + "trailing_data_bytes": 0, + "warnings": [] + } + }, + "authorization": { + "autoload_authorized": false, + "automatic_retry": false, + "device_action_authorized": false, + "device_write_authorized": false, + "execution_authorized": false, + "installation_authorized": false, + "lifecycle_authorized": false, + "rescue_payload_design_authorized": false, + "target_build_authorized": false, + "transfer_authorized": false + }, + "comparisons": { + "local_vs_community_owendswang_v1_4_autoloader_7_61": { + "classification": "SIECAF_LAYOUT_DIFFERENT", + "left_hash_blocks_sha256": "6e052f186dc8e683d501e376d79dcec1ba0d3268c257b6071e96eae738a7be24", + "left_layout_sha256": "c6dac8313ef99accba68b4a36871f0d8ae99676df0b721d4447ecb05a12a6539", + "left_structural_fingerprint_sha256": "7bb1b70187d1f48370b9bae218429af79105585bd043307824c0d84982e6922f", + "right_hash_blocks_sha256": "434b209865af8c5815b1277073db59348fddf65086908b2fd00dee5324459bcb", + "right_layout_sha256": "be7fdf87361c17fa52d3b4b263f4ee0cce69e924f8dab0ab66c554b4d9557549", + "right_structural_fingerprint_sha256": "4884c54e280969371ae1d0b84533be5a7c50a61dff6931cf595fdb5862655218" + }, + "local_vs_official_y2jb_1_6_4_03": { + "classification": "SIECAF_LAYOUT_DIFFERENT", + "left_hash_blocks_sha256": "6e052f186dc8e683d501e376d79dcec1ba0d3268c257b6071e96eae738a7be24", + "left_layout_sha256": "c6dac8313ef99accba68b4a36871f0d8ae99676df0b721d4447ecb05a12a6539", + "left_structural_fingerprint_sha256": "7bb1b70187d1f48370b9bae218429af79105585bd043307824c0d84982e6922f", + "right_hash_blocks_sha256": "c3236f5d85a4d7741be5a1fda5ce96d2bb7f005b0e48fed043af5ea48fb045d2", + "right_layout_sha256": "de6cc47686e5e301b725267b9b777c80a07a788e51f214cfb6e9d7f6e2e11110", + "right_structural_fingerprint_sha256": "9c453e1fa99ef1f1e6fe1d992f9b0a2ede4dbd4b9a2ad8551c08c8c61ed9be2a" + }, + "official_vs_community": { + "classification": "SIECAF_LAYOUT_DIFFERENT", + "left_hash_blocks_sha256": "c3236f5d85a4d7741be5a1fda5ce96d2bb7f005b0e48fed043af5ea48fb045d2", + "left_layout_sha256": "de6cc47686e5e301b725267b9b777c80a07a788e51f214cfb6e9d7f6e2e11110", + "left_structural_fingerprint_sha256": "9c453e1fa99ef1f1e6fe1d992f9b0a2ede4dbd4b9a2ad8551c08c8c61ed9be2a", + "right_hash_blocks_sha256": "434b209865af8c5815b1277073db59348fddf65086908b2fd00dee5324459bcb", + "right_layout_sha256": "be7fdf87361c17fa52d3b4b263f4ee0cce69e924f8dab0ab66c554b4d9557549", + "right_structural_fingerprint_sha256": "4884c54e280969371ae1d0b84533be5a7c50a61dff6931cf595fdb5862655218" + } + }, + "parser_evidence": { + "byte_order": "little-endian", + "content_extraction_attempted": false, + "decryption_attempted": false, + "documentation": "https://www.psdevwiki.com/ps5/Archive.dat", + "header_blob": "fdbc368353a7797464873ada306f0297257eb95e", + "ps5_bar_tool_executed": false, + "source_commit": "36d014672bc87577a6e0d750c2cccadc3fae0854", + "source_repository": "https://github.com/c0w-ar/ps5-bar-tool", + "source_tree": "d3e973b60432fdc32bd16e3b7424e82dcc38a23f" + }, + "phase": "PHASE_0_9E_R2_INNER_BACKUP_CORRELATION", + "schema_version": 1, + "status": "HOST_ONLY_EVIDENCE" +} diff --git a/manifests/sbom.spdx.json b/manifests/sbom.spdx.json new file mode 100644 index 0000000..68f9d2a --- /dev/null +++ b/manifests/sbom.spdx.json @@ -0,0 +1,136 @@ +{ + "spdxVersion": "SPDX-2.3", + "dataLicense": "CC0-1.0", + "SPDXID": "SPDXRef-DOCUMENT", + "name": "chimera-gfx-phase0-sbom", + "documentNamespace": "https://chimera-gfx.invalid/spdx/phase0-2026-07-17", + "creationInfo": { + "created": "2026-07-17T00:00:00Z", + "creators": ["Tool: Codex", "Organization: chimera-gfx contributors"] + }, + "packages": [ + { + "name": "chimera-gfx", + "SPDXID": "SPDXRef-Package-chimera-gfx", + "versionInfo": "0.1.0", + "downloadLocation": "NOASSERTION", + "filesAnalyzed": false, + "licenseConcluded": "GPL-3.0-or-later", + "licenseDeclared": "GPL-3.0-or-later", + "copyrightText": "NOASSERTION", + "primaryPackagePurpose": "LIBRARY" + }, + { + "name": "PS5 Payload SDK", + "SPDXID": "SPDXRef-Package-ps5-payload-sdk", + "versionInfo": "v0.41-d2e2e585740362976a39fdd5ccf390f199a7bc37", + "downloadLocation": "https://github.com/ps5-payload-dev/sdk", + "filesAnalyzed": false, + "licenseConcluded": "NOASSERTION", + "licenseDeclared": "GPL-3.0-or-later AND BSD-2-Clause", + "copyrightText": "NOASSERTION", + "primaryPackagePurpose": "BUILD_TOOL" + }, + { + "name": "PS5 Payload Manager", + "SPDXID": "SPDXRef-Package-ps5-payload-manager", + "versionInfo": "v0.3.1-cfbc70f30f419b09bf2b52283f7409e2d3117ee1", + "downloadLocation": "https://github.com/itsPLK/ps5-payload-manager", + "filesAnalyzed": false, + "licenseConcluded": "GPL-3.0-or-later", + "licenseDeclared": "GPL-3.0-or-later", + "copyrightText": "NOASSERTION", + "primaryPackagePurpose": "APPLICATION" + }, + { + "name": "PS5 ELF Loader", + "SPDXID": "SPDXRef-Package-ps5-elfldr", + "versionInfo": "v0.23-699e8bcff03e91e8d6ca6eba281af25c5a58d8c2", + "downloadLocation": "https://github.com/ps5-payload-dev/elfldr", + "filesAnalyzed": false, + "licenseConcluded": "GPL-3.0-or-later", + "licenseDeclared": "GPL-3.0-or-later", + "copyrightText": "Copyright (C) 2024 John Tornblom", + "primaryPackagePurpose": "APPLICATION" + }, + { + "name": "PS5 SDL2 fork", + "SPDXID": "SPDXRef-Package-ps5-sdl2", + "versionInfo": "0baf4ac49382b537ba449901b5b6d0d189bb1fbb", + "downloadLocation": "https://github.com/ps5-payload-dev/SDL", + "filesAnalyzed": false, + "licenseConcluded": "Zlib", + "licenseDeclared": "Zlib", + "copyrightText": "Copyright (C) SDL contributors", + "primaryPackagePurpose": "LIBRARY" + }, + { + "name": "RetroArch", + "SPDXID": "SPDXRef-Package-retroarch", + "versionInfo": "v1.22.2-69a4f0ea1e8aaf442ae4858f2e7f2b31a1776576", + "downloadLocation": "https://github.com/libretro/RetroArch", + "filesAnalyzed": false, + "licenseConcluded": "GPL-3.0-only", + "licenseDeclared": "GPL-3.0-only", + "copyrightText": "NOASSERTION", + "primaryPackagePurpose": "APPLICATION" + }, + { + "name": "actions/checkout", + "SPDXID": "SPDXRef-Package-actions-checkout", + "versionInfo": "11bd71901bbe5b1630ceea73d27597364c9af683", + "downloadLocation": "https://github.com/actions/checkout", + "filesAnalyzed": false, + "licenseConcluded": "MIT", + "licenseDeclared": "MIT", + "copyrightText": "Copyright GitHub", + "primaryPackagePurpose": "BUILD_TOOL" + }, + { + "name": "Ubuntu container image", + "SPDXID": "SPDXRef-Package-ubuntu-container", + "versionInfo": "26.04-sha256-651ba3fe3a830441e3deaf70fafac40d808a6bd2800a6f2c43130055159f23e6", + "downloadLocation": "https://hub.docker.com/_/ubuntu", + "filesAnalyzed": false, + "licenseConcluded": "NOASSERTION", + "licenseDeclared": "NOASSERTION", + "copyrightText": "NOASSERTION", + "primaryPackagePurpose": "CONTAINER" + } + ], + "relationships": [ + { + "spdxElementId": "SPDXRef-DOCUMENT", + "relationshipType": "DESCRIBES", + "relatedSpdxElement": "SPDXRef-Package-chimera-gfx" + }, + { + "spdxElementId": "SPDXRef-Package-ps5-payload-sdk", + "relationshipType": "BUILD_DEPENDENCY_OF", + "relatedSpdxElement": "SPDXRef-Package-chimera-gfx" + }, + { + "spdxElementId": "SPDXRef-Package-ps5-payload-manager", + "relationshipType": "OTHER", + "relatedSpdxElement": "SPDXRef-Package-chimera-gfx", + "comment": "External runtime-control evidence; not linked or redistributed." + }, + { + "spdxElementId": "SPDXRef-Package-ps5-elfldr", + "relationshipType": "OTHER", + "relatedSpdxElement": "SPDXRef-Package-chimera-gfx", + "comment": "External loader evidence; not linked or redistributed." + }, + { + "spdxElementId": "SPDXRef-Package-ps5-sdl2", + "relationshipType": "OPTIONAL_DEPENDENCY_OF", + "relatedSpdxElement": "SPDXRef-Package-chimera-gfx" + }, + { + "spdxElementId": "SPDXRef-Package-retroarch", + "relationshipType": "OTHER", + "relatedSpdxElement": "SPDXRef-Package-chimera-gfx", + "comment": "Separate private Phase 1.0A port fork; no RetroArch source or target artifact is vendored in chimera-gfx." + } + ] +} diff --git a/manifests/upstreams.lock.json b/manifests/upstreams.lock.json new file mode 100644 index 0000000..f6fd336 --- /dev/null +++ b/manifests/upstreams.lock.json @@ -0,0 +1,83 @@ +{ + "schema_version": 1, + "verified_at": "2026-07-17", + "sources": { + "ps5_payload_sdk": { + "repository": "https://github.com/ps5-payload-dev/sdk.git", + "release": "v0.41", + "commit": "d2e2e585740362976a39fdd5ccf390f199a7bc37", + "release_published_at": "2026-06-28T15:42:59Z", + "asset": { + "name": "ps5-payload-sdk.zip", + "size": 8810966, + "url": "https://github.com/ps5-payload-dev/sdk/releases/download/v0.41/ps5-payload-sdk.zip", + "sha256": "ebfb0acb5260511951a80e17db41650c62d20a8caf8659a230b928dc85005984" + } + }, + "ps5_payload_manager": { + "repository": "https://github.com/itsPLK/ps5-payload-manager.git", + "release": "v0.3.1", + "commit": "cfbc70f30f419b09bf2b52283f7409e2d3117ee1", + "installed_identity": { + "observed_version": "0.3.1", + "release_asset_hash_match": true + }, + "asset": { + "name": "pldmgr_v0.3.1.elf", + "size": 2050320, + "url": "https://github.com/itsPLK/ps5-payload-manager/releases/download/v0.3.1/pldmgr_v0.3.1.elf", + "sha256": "518740adbacccb9094fadb07dd424c53ee290f38306449ccc9d6957fdf813c0b" + } + }, + "ps5_elfldr_installed": { + "repository": "https://github.com/ps5-payload-dev/elfldr.git", + "release": "v0.23", + "commit": "699e8bcff03e91e8d6ca6eba281af25c5a58d8c2", + "installed_identity": { + "observed_version": "v0.23", + "release_asset_hash_match": true + }, + "asset": { + "name": "elfldr-ps5.elf", + "size": 397000, + "url": "https://github.com/ps5-payload-dev/elfldr/releases/download/v0.23/elfldr-ps5.elf", + "sha256": "092d16ee0ede0c494947efd38d1a17bbd7cc4b022d3858ea898833c188c703e8" + } + }, + "ps5_elfldr_candidate_not_installed": { + "repository": "https://github.com/itsPLK/ps5-elfldr.git", + "release": "v0.23.2-148b71c", + "commit": "148b71c2fb9155d2550ef6a14eb03433e23acaeb", + "installed_identity": { + "release_asset_hash_match": false, + "status": "excluded_by_installed_hash" + }, + "asset": { + "name": "elfldr-ps5-v0.23.2-148b71c.elf", + "size": 397000, + "url": "https://github.com/itsPLK/ps5-elfldr/releases/download/v0.23.2-148b71c/elfldr-ps5-v0.23.2-148b71c.elf", + "sha256": "2c28f847c859d3126b0f089ee23b72b84a733548da222ceafd91ae2f3df8f933" + } + }, + "ps5_sdl": { + "repository": "https://github.com/ps5-payload-dev/SDL.git", + "commit": "0baf4ac49382b537ba449901b5b6d0d189bb1fbb", + "commit_date": "2026-07-07T19:02:16+02:00" + }, + "retroarch": { + "repository": "https://github.com/libretro/RetroArch.git", + "commit": "32ee70cef5d4bdc32a4ca3b3b261209ce74b6e81", + "commit_date": "2026-07-16T21:21:31+02:00" + }, + "pacbrew": { + "repository": "https://github.com/ps5-payload-dev/pacbrew-repo.git", + "commit": "c2abcfcb60f569128abd0e8e70ad03a67bee5ea7", + "commit_date": "2026-07-07T23:27:12+02:00" + }, + "ps5_linux_loader": { + "repository": "https://github.com/ps5-linux/ps5-linux-loader.git", + "commit": "8e7dd40df6144bed6194d165d48aa9468a7e13f3", + "commit_date": "2026-07-10T23:08:28+02:00" + } + } +} diff --git a/packaging/Dockerfile b/packaging/Dockerfile new file mode 100644 index 0000000..eb8b75f --- /dev/null +++ b/packaging/Dockerfile @@ -0,0 +1,52 @@ +# syntax=docker/dockerfile:1.7 +# SPDX-License-Identifier: GPL-3.0-or-later +ARG UBUNTU_IMAGE=ubuntu:26.04@sha256:651ba3fe3a830441e3deaf70fafac40d808a6bd2800a6f2c43130055159f23e6 +FROM ${UBUNTU_IMAGE} AS build + +ARG DEBIAN_FRONTEND=noninteractive +RUN apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates \ + bsdextrautils \ + clang-18 \ + clang-format-18 \ + clang-tidy-18 \ + cmake \ + curl \ + git \ + lld-18 \ + llvm-18-dev \ + ninja-build \ + python3 \ + unzip \ + && rm -rf /var/lib/apt/lists/* + +ARG PS5_SDK_URL=https://github.com/ps5-payload-dev/sdk/releases/download/v0.41/ps5-payload-sdk.zip +ARG PS5_SDK_SHA256=ebfb0acb5260511951a80e17db41650c62d20a8caf8659a230b928dc85005984 +RUN curl -fL --retry 3 --output /tmp/ps5-payload-sdk.zip "${PS5_SDK_URL}" \ + && echo "${PS5_SDK_SHA256} /tmp/ps5-payload-sdk.zip" | sha256sum --check --strict \ + && unzip -q /tmp/ps5-payload-sdk.zip -d /opt \ + && rm /tmp/ps5-payload-sdk.zip + +ENV PS5_PAYLOAD_SDK=/opt/ps5-payload-sdk +ENV LLVM_CONFIG=/usr/bin/llvm-config-18 + +WORKDIR /src +COPY . /src + +RUN cmake -S /src -B /build/host -G Ninja \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_C_COMPILER=clang-18 \ + -DCHIMERA_GFX_ENABLE_CLANG_TIDY=ON \ + -DBUILD_TESTING=ON \ + && cmake --build /build/host \ + && ctest --test-dir /build/host --output-on-failure + +RUN cmake -S /src -B /build/ps5 -G Ninja \ + -DCMAKE_TOOLCHAIN_FILE=/opt/ps5-payload-sdk/toolchain/prospero.cmake \ + -DCHIMERA_GFX_BUILD_PS5_PROBE=ON \ + -DCHIMERA_GFX_PS5_ALLOWED_FIRMWARE=NONE \ + -DBUILD_TESTING=OFF \ + && cmake --build /build/ps5 + +FROM scratch AS artifacts +COPY --from=build /build/ps5/chimera-gfx-capability-probe.elf / diff --git a/packaging/README.md b/packaging/README.md new file mode 100644 index 0000000..c6a8870 --- /dev/null +++ b/packaging/README.md @@ -0,0 +1,15 @@ +# Packaging + +`Dockerfile` provides a pinned build environment for host tests and the +compile-only PS5 capability probe. It produces build artifacts only; it has no +network route or command for deployment to a PS5. + +`patches/sdl2-phase1-video-only.patch` is the sole reviewed overlay used by the +optional offline Phase-1 build. It removes keyboard/IME initialization from the +pinned SDL video backend and retains SDL's Zlib licensing. The build script +stages it in an ignored worktree and rejects any additional source change. + +`phase07/` contains the source-of-truth notices copied into the ignored +hash-bound installation-review and rollback archives. Those archives are +offline review material only and deliberately contain no PS5 connection, +transfer, installation, or execution command. diff --git a/packaging/patches/sdl2-phase1-video-only.patch b/packaging/patches/sdl2-phase1-video-only.patch new file mode 100644 index 0000000..6f7ab82 --- /dev/null +++ b/packaging/patches/sdl2-phase1-video-only.patch @@ -0,0 +1,57 @@ +diff --git a/src/video/ps5/SDL_ps5video.c b/src/video/ps5/SDL_ps5video.c +--- a/src/video/ps5/SDL_ps5video.c ++++ b/src/video/ps5/SDL_ps5video.c +@@ -29,6 +29,5 @@ + #include "SDL_ps5tilemap.inc" + #include "SDL_ps5video.h" +-#include "SDL_ps5keyboard.h" + #include "SDL_ps5osmesa.h" + + #define PS5_THREAD_COUNT 12 +@@ -305,46 +304,33 @@ static void PS5_DestroyDevice(SDL_VideoDevice *device) + static void PS5_DestroyWindow(_THIS, SDL_Window *window) + { + } + +-static void PS5_PumpEvents(_THIS) +-{ +- PS5_Keyboard_PumpEvents(); +-} +- + static SDL_VideoDevice *PS5_CreateDevice(void) + { + SDL_VideoDevice *device; + + device = (SDL_VideoDevice *)SDL_calloc(1, sizeof(SDL_VideoDevice)); + if (device == NULL) { + SDL_OutOfMemory(); + return NULL; + } + + device->driverdata = SDL_calloc(1, sizeof(PS5_DeviceData)); + if (device->driverdata == NULL) { + SDL_free(device); + SDL_OutOfMemory(); + return NULL; + } + +- PS5_Keyboard_Init(); +- PS5_Keyboard_Open(); +- + device->VideoInit = PS5_VideoInit; + device->VideoQuit = PS5_VideoQuit; + device->GetDisplayModes = PS5_GetDisplayModes; + device->SetDisplayMode = PS5_SetDisplayMode; +- device->PumpEvents = PS5_PumpEvents; + device->CreateSDLWindow = PS5_CreateWindow; + device->DestroyWindow = PS5_DestroyWindow; + device->CreateWindowFramebuffer = PS5_CreateWindowFramebuffer; + device->UpdateWindowFramebuffer = PS5_UpdateWindowFramebuffer; + device->DestroyWindowFramebuffer = PS5_DestroyWindowFramebuffer; +- device->HasScreenKeyboardSupport = PS5_HasScreenKeyboardSupport; +- device->ShowScreenKeyboard = PS5_ShowScreenKeyboard; +- device->HideScreenKeyboard = PS5_HideScreenKeyboard; +- device->IsScreenKeyboardShown = PS5_IsScreenKeyboardShown; + device->free = PS5_DestroyDevice; + + #ifdef SDL_VIDEO_OPENGL_OSMESA diff --git a/packaging/phase07/README-installation-review.md b/packaging/phase07/README-installation-review.md new file mode 100644 index 0000000..053ca0b --- /dev/null +++ b/packaging/phase07/README-installation-review.md @@ -0,0 +1,19 @@ +# Phase-0.7 offline installation review package + +Status: **DO NOT INSTALL**. This package contains exact offline-reviewed +artifacts and evidence only. It contains no deployment command and grants no +authority to contact a PS5. + +The package inventory must bind these exact binaries: + +- `chimera-elfldr-phase07.elf`: 397000 bytes, + SHA-256 `63e810982471eb40cae3a20aa9df9a0a02892f420e429874fae4e99aa400b561`; +- `chimera-payload-manager-phase07.elf`: 99560 bytes, + SHA-256 `8fecf8241a46246eddbd21e8bb4d875f5d76f1f4f4c6a11384df1f131aa5e5b1`; +- `chimera-gfx-lifecycle-probe.elf`: 112680 bytes, + SHA-256 `bfb4a5cc768e162fe4c2fddf41c3978e152722918a39085277fd172cb95a7182`. + +Before a later installation, the existing on-device paths, sizes, and SHA-256 +values must be captured and backed up. Installation permission for the two +runtime components does not authorize transferring or executing the lifecycle +probe. diff --git a/packaging/phase07/README-rollback.md b/packaging/phase07/README-rollback.md new file mode 100644 index 0000000..0d42bd7 --- /dev/null +++ b/packaging/phase07/README-rollback.md @@ -0,0 +1,25 @@ +# Phase-0.7 rollback preparation + +Status: **OFFLINE PREPARED; NOT DEPLOYED**. + +The stock elfldr v0.23 release asset is included when the offline package is +generated: + +- size: 397000 bytes; +- SHA-256: + `092d16ee0ede0c494947efd38d1a17bbd7cc4b022d3858ea898833c188c703e8`. + +The exact stock Payload Manager v0.3.1 binary is not present in the offline +workspace. Its previously verified installed/release identity is: + +- size: 2050320 bytes; +- SHA-256: + `518740adbacccb9094fadb07dd424c53ee290f38306449ccc9d6957fdf813c0b`. + +Therefore any later installation must first make a byte-for-byte backup of the +actual installed Payload Manager and verify that exact hash. If it differs, +stop. The rollback set is not complete until that on-device backup exists. + +Rollback authorization is separate from installation authorization. This +document intentionally contains no connection, copy, service-start, or +execution command. diff --git a/packaging/phase09b/SHA256SUMS.txt b/packaging/phase09b/SHA256SUMS.txt new file mode 100644 index 0000000..b8b744f --- /dev/null +++ b/packaging/phase09b/SHA256SUMS.txt @@ -0,0 +1,10 @@ +65b781814e501aea5a78276db658b820d130f643d84e38aee1792c56ef9c52ea docs/approvals/phase-0.9b-observer-execution-template.md +7b6d26d4bc1b62f8dea4905caef78c9341c98139e78024ee4b7dfe680ced737c docs/runtime/phase-0.9b-bounded-observer-design.md +3aaa44de37e892657e8a31230e9fee22f00bd7865103d1bc8abef93591ee2984 docs/runtime/phase-0.9b-observer-limitations.md +be7c2ca486b36f3299a1e30d28b0ca95ca1f0589e272c05fb6999c608f437108 docs/runtime/phase-0.9b-observer-result-contract.md +e5d41cc9801bdf491c1423fb9cd36efbb84327e812a5624a46497ee323632998 docs/runtime/phase-0.9b-observer-static-audit.md +efcea3b0001ef5b2da65c372ceb93ee2fec09c9331b2e4cbb6008212504c0918 manifests/runtime/phase-0.9b-observation-plan.schema.json +104c4a667ad17f9827fc7276852c6faeef96effb8e3561a5ebc19a62c7d51634 manifests/runtime/phase-0.9b-observer.json +31545d7d1d6a54fe99d420afbd7ba8b66ed9017dfbf4697fe99874ea7816f34f tests/phase09b_observer_model.py +38f49333b92b23eec3a82ee832e602baf3cd7fff9d6b93d04864e61d209942f3 tests/test_phase09b_observer_audit.py +e7bf50c3437a8749b56a5986efce1b6e768a6faa024560cd7261d508f92d8844 tools/validate_phase09b_observer_audit.py diff --git a/packaging/phase09c/SHA256SUMS.txt b/packaging/phase09c/SHA256SUMS.txt new file mode 100644 index 0000000..a915266 --- /dev/null +++ b/packaging/phase09c/SHA256SUMS.txt @@ -0,0 +1,11 @@ +b1e5ec2501870800c260517401199d5a83f48c5222e9f8671d44db68d55ac49f docs/runtime/phase-0.9c-capability-closure.md +7e2d32865d747ffaa370bb01103f6aca95dfa77c8225784ef7be7196f3863cb0 docs/runtime/phase-0.9c-output-channel-feasibility.md +8c67055409ef766a6be9f5409b5349650f48f2f80ac3253e2867373e21d753fd docs/runtime/phase-0.9c-side-effect-model.md +23a2f4b7312219afc6cb2d97773fdc33c9d222a8eb21a1ba8bd4c8ed7280b03e docs/runtime/phase-0.9c-startup-exit-feasibility.md +8dfd5da1149f3af0af9d5a70b096ad6e49ab93d99ed85e986083ecefc3f40a03 docs/runtime/phase-0.9c-static-audit.md +84eb737ff3486d0c5d8b5ecf06809f93bc573497da4919595b4c1817fd34247c manifests/runtime/phase-0.9c-feasibility.json +31cca2363069789dc0da7561204ae0bf02b39b616655bc5eb3302505549cd101 manifests/runtime/phase-0.9c-feasibility.schema.json +144c5ab0b33a0ef4cdc6fcfaa52a576f090af61e79a5706f60ec21cb02d9686f tests/phase09c_feasibility_model.py +bd910012b5e2d6d79d9067e29410d30ac53f834c3c93ee4e60495f11543f7c9f tests/test_phase09c_feasibility.py +abc73d05a1a72e60ce588917fac5e183091175d5ad85ec680fa9f3672c1dd8b5 tests/test_phase09c_protocol.py +ab1e078bd35d9abc923621e21a07f4f2544debdac71e7532d54fb200cee1eb13 tools/validate_phase09c_feasibility.py diff --git a/packaging/phase09e/SHA256SUMS.txt b/packaging/phase09e/SHA256SUMS.txt new file mode 100644 index 0000000..74e37e3 --- /dev/null +++ b/packaging/phase09e/SHA256SUMS.txt @@ -0,0 +1,24 @@ +# Phase 0.9E offline provenance evidence. +# The two ~/Downloads records are audit inputs, not repository artifacts. +4cbfff8517f03960e98a5922da0f3645c99426d742989500635cb22719e635f4 docs/runtime/phase-0.9e-bootstrap-provenance.md +90e008313386a70966a191a62a45d7bfe9f64a12f408cfbfea41f2a095d283fc docs/runtime/phase-0.9e-independent-rescue-chain.md +ac9fd1462280f3b6a8fb465c547ff0ec20384901df3da8a51b45a43b39dd8a61 docs/runtime/phase-0.9e-loader-9020-protocol.md +35cf63a6816d43188dcfd48bff6166ad13b2592301df172ab75a9bc6f680f870 docs/runtime/phase-0.9e-reboot-and-crash-model.md +ef7e144a37a0d771e4249401104b84cbd0ad5c0611d0f92a5de6599a9568e23b docs/runtime/phase-0.9e-future-rescue-payload-contract.md +ad97d281dc722c89de7618a74702ff94470003d246ce08228ea77b6690d4cf30 docs/runtime/phase-0.9e-output-architecture-options.md +5dfa9bfe2ae751b2f0ea0e03c60c1a4471a35452389cf471456e6c54eb4601cf manifests/runtime/phase-0.9e-bootstrap-provenance.json +00b15c2ad3ff91aade8f0355803248d2388193fbbd78c0f86043601daf2c9df9 manifests/runtime/phase-0.9e-bootstrap-provenance.schema.json +a7ca8b4e8072cb60ad4cd4869f6c508e8014059c8d81ad3b94a8d9e8cd0db4cb manifests/runtime/phase-0.9e-loader-protocol.json +f51d0d3fe7712991beff4a3db60ef2dee9889dfb18f512e8ffd1e8090f6e644a manifests/runtime/phase-0.9e-loader-protocol.schema.json +a8e41a42b04cfc64182bfc1b18a4401a8e87b9a806749235bd0fa9941589f6b7 tools/validate_phase09e_bootstrap.py +a458d1e71711b4fc856f6bd869429a9e08f45358bec6bc28bb97a3c7c903e79c tests/test_phase09e_bootstrap.py +4ede255495e6ed9bded98029465516023cd94a7d478e2009933bfd9097b6713e CMakeLists.txt +b14d922ebc3431412cab3de9fbddf2734e637540adaa16f02b86f68e2e59345f ARCHITECTURE.md +d12cb1f8378f71008b9903c7b29eca78eeb3d453065ba7e9eb5fa09c79d8cee4 SAFETY.md +c3560a3ac0f7c0970e60f5dc3497670537254d3ebaf46dff3cb97ee9e47c8afc TEST_PLAN.md +cd6e857e9f5accf4fba81a2d318de825e82f0d057a6ab1e7ddd556906c8ba79a FIRMWARE_COMPATIBILITY.md +a7c1a640cfa077082c2569c3eb1aba79c44be64a8d67b81f2975ffa8c77fdc28 README.md +51cc27f18accacf0ea4bd42fef721789591b3563f2436d5bd16d64115e156deb ROADMAP.md +b7a07bfe6b07d380d6c24fef582b01d1577cd5a01efc0736f5b3dc8e720376a3 AGENTS.md +805e3f87f0c371223619ffc7d3a7b3c0d41a1fae8a8b1171d9e2f162659e8291 ~/Downloads/Y2JB-Autoloader-403-1240.zip +6439834e8856d45b6d6fe699b74c35ca6985a199ea8ecf3e398c018d37be2d55 ~/Downloads/Y2JB-Autoloader-403-1240.zip::PS5/EXPORT/BACKUP/202606102126_00/archive.dat diff --git a/packaging/phase09er/SHA256SUMS.txt b/packaging/phase09er/SHA256SUMS.txt new file mode 100644 index 0000000..58f6353 --- /dev/null +++ b/packaging/phase09er/SHA256SUMS.txt @@ -0,0 +1,22 @@ +# Phase 0.9E-R host-only provenance evidence. +# Official release assets and opaque SIECAF data are deliberately not packaged. +3a56fb1a2852309609e87d0f389b8c2867bad808d0083c6b24cb6b67a88cecd5 docs/runtime/phase-0.9e-r-official-release-correlation.md +b358f3ad9dda971a0484e4efb9e7218dc810e1b884b9c7a239c9ce3ba323ae0f docs/runtime/phase-0.9e-r-release-source-binding.md +9aaa11ee2e69a37f14bb94f88b47fc5c0c3655298d25d7eb78884dfcb7e7e106 docs/runtime/phase-0.9e-r-port9020-source-audit.md +4bc6960014dd91873cc3448631220e23637f8bca663106b74005716bdf2fc2eb docs/runtime/phase-0.9e-r-official-hostsender-audit.md +9b2ad3c4ef4ce7dd6e503e4a02796112575883efde5eda07ca1ab093288e8c0c docs/runtime/phase-0.9e-r-provenance-gaps.md +530cd9c8c62bff167c942d4195cedc12d81f948c8e72885bbe8e50105ff76fd9 docs/approvals/phase-0.9e-r-y2jb-deployed-use-attestation.md +09f916b20def51fd519690b6d940de5f93818a4d9bd5eebef462fe2a828b88be manifests/runtime/phase-0.9e-r-release-correlation.json +0bc4df92ad14b7a42901697ac129a0f8f9261c9e73efb81fbfdcb2746689af52 manifests/runtime/phase-0.9e-r-release-correlation.schema.json +ea84f1885ad4286670a401cbf73e9a371cdcd03a1f247908b90ea27553ee820e manifests/runtime/phase-0.9e-r-port9020-audit.json +8ace6eaef67765fd5ec9d7ce511536a84b39d2464fb25e50f8f7d1111ef96477 manifests/runtime/phase-0.9e-r-port9020-audit.schema.json +9b4a65f25bb51a91f535b31022e146fb1f5d79b4664bbbd6f5236162e3085fd2 tools/validate_phase09er_provenance.py +71d0a87e4127afe28aa184855b7f16fd1fdd242d65dba0a924260ae36908ebe6 tests/test_phase09er_provenance.py +9291451a77a697bcd42387e5e44077e70d5c9fb555230babe0ae6be1c8d841f3 CMakeLists.txt +61c658b3a32fca8d54700f8f1c95f186de5f083a2a270f81cf7c1138019ca792 AGENTS.md +882e2d6d65071de4503de46256ceba252942256ab8989e0efe74df53a6d7d5d3 ARCHITECTURE.md +bd4f6fba4b6e68dbabce6627cf2d8c7a36de635c4a7588143fbf2ace913bdef4 SAFETY.md +119cb8995a41338630dbf268eabe334896ea7f8b13bd96e3726b0a99d644b3e4 TEST_PLAN.md +9ad50f8377c92f8d6d60833fea320c752d4cecba1b2eeaf834d841e81bccbab5 FIRMWARE_COMPATIBILITY.md +7f8e6bc3dc9b49fdd6ecc0ec98cfecc7bd264cfdeccb46c7faa33edd3bbd33b8 README.md +731a1c6b07370f2863d4b5957aade3ab43b89bf498d339ab032f4c705db55b32 ROADMAP.md diff --git a/packaging/phase09er2/SHA256SUMS.txt b/packaging/phase09er2/SHA256SUMS.txt new file mode 100644 index 0000000..e088e4c --- /dev/null +++ b/packaging/phase09er2/SHA256SUMS.txt @@ -0,0 +1,21 @@ +# Phase 0.9E-R2 host-only provenance evidence. +# Downloaded backups, browser databases, MediaFire HTML, and SIECAF data are not packaged. +04ca2d4f7d5c3f841593b2cbe2f1665678ab08de2f6a6ac0dfb228366ba4fc98 docs/runtime/phase-0.9e-r2-local-download-provenance.md +860220412196e3c10a357f85427492e03e30e8287df872cefe846d9b0eee594e docs/runtime/phase-0.9e-r2-inner-archive-correlation.md +a906f43cecbda0cf82d41af197dd6d04dec89f7116b541a8db1cf39e9dcef553 docs/runtime/phase-0.9e-r2-siecaf-structural-analysis.md +6d3b63dd02fc521d1d1132df4fadecdeb4a87fdc378b8a553ce67e94dc9d048c docs/runtime/phase-0.9e-r2-community-backup-correlation.md +e19f7ecf5f665b7f4a7ac4425fe12548d5a837a0b667b73f7576b8f1b0762580 docs/runtime/phase-0.9e-r2-final-provenance-decision.md +68713fd95c5d3662354380bb14be7461029449d119f2dedacfe463ea58668780 manifests/runtime/phase-0.9e-r2-inner-correlation.json +18f47668c69501e26080b93fb17f5610c7037a10b971f0ee9059b66a81f98c4a manifests/runtime/phase-0.9e-r2-siecaf-fingerprints.json +c34574ba024b863d3ce1ad97ba555b56894a21836d7b418b0d5140b3cb65fab3 tools/inspect_siecaf_header.py +bb551d17540844919326e6df56f7f7b9092787214b7ce73701e33031630c55e0 tools/validate_phase09er2_correlation.py +8c013795d2706b1d1c2f3f097701667f51e5d3d3a2eaa592ed1836f9c143f633 tests/test_phase09er2_correlation.py +953aa89ac468a14fa3f8168599a162e737c60828276d0a24862484a7dfeb0b78 tests/test_siecaf_header_parser.py +a9583c5e6bad017d56892dd499d898e6e645577fd90f8954c08c0f74ecaa180f CMakeLists.txt +5788b12f33b33f49e61f1b9b5adf12bf9173dc49317c41f9335c7d8fa3fb08d5 AGENTS.md +bdc3b34b4ce36c870fd68a375f33e0f702eccf465908078400cacb91fe7894fe README.md +55f684b6a5e7d03d4e7a6789ce00d125eb467f8a1ed1fc633bd4a527db7eacc5 ARCHITECTURE.md +d4d28eb9023aa0187684c1f2317cd44f765a77545bf51c1de3f7b9e2142d78ea SAFETY.md +672b5fe036ea5f6a778a518ee9f7b6dcb807f7650e6ff6e436b4a89cc91da9c2 TEST_PLAN.md +5d6cb3757f6e043af4e428cb43e9cdc0e98a97fc434de25b9ee5a83d6b10001c FIRMWARE_COMPATIBILITY.md +44da814bceccc7e9c540b52cfcf7b890d7c661e3fcd2bafc30e7fdd4fa05b13b ROADMAP.md diff --git a/packaging/retroarch/phase10a/SHA256SUMS.txt b/packaging/retroarch/phase10a/SHA256SUMS.txt new file mode 100644 index 0000000..ee362fe --- /dev/null +++ b/packaging/retroarch/phase10a/SHA256SUMS.txt @@ -0,0 +1,6 @@ +fd595a826f64d18598be0b55e539bb524b33bd469b98f62c958ee50acb544628 retroarch_ps5_headless.elf +fb935f7a768c91408a87290f03bad8ed59f5061de75232d5660957eff8ec017f retroarch_ps5_headless.map +7beb09592404b5c1fb4161c632171d2901f3715db26e59458998690e8c49f3fc retroarch_ps5_software.elf +1573a9951fa53bfa1bf304e0038e6f049f859fbfbe92f6a895196dc283c27597 retroarch_ps5_software.map +353065505f54e71fa8f7fff41e090dce52f39fe2aaafab85c4653648fc5f1b56 libSDL2.a +b547260d8af40ce2360575ab7831c009036c1f35f8b525cad6fd376dc5ca9d6b sdl2-ps5-no-keyboard-ime.patch diff --git a/packaging/retroarch/phase10b/SHA256SUMS.txt b/packaging/retroarch/phase10b/SHA256SUMS.txt new file mode 100644 index 0000000..4b7fac2 --- /dev/null +++ b/packaging/retroarch/phase10b/SHA256SUMS.txt @@ -0,0 +1,4 @@ +16c04afe26c490b5071388dc9a32524d09d50d181b1baffbbc54bd96f0c2c688 retroarch_ps5_software_smoke.elf +21dfdaa44261f8d546fa9ea71102162dc880a8ca8037526019a04fece5b21313 retroarch_ps5_software_smoke.map +fc814681c54e02092591165b657c33b12c4f34a3919c80384139aa70b752553d libSDL2.a +e5e7413266e4d4491b6c2000fbd26dacc1e25e65c337c4e0b0f21bd4c3f85a4e sdl2-ps5-smoke-hardening.patch diff --git a/packaging/retroarch/phase10c/SHA256SUMS.txt b/packaging/retroarch/phase10c/SHA256SUMS.txt new file mode 100644 index 0000000..89a0aea --- /dev/null +++ b/packaging/retroarch/phase10c/SHA256SUMS.txt @@ -0,0 +1,5 @@ +16c04afe26c490b5071388dc9a32524d09d50d181b1baffbbc54bd96f0c2c688 retroarch_ps5_software_smoke.elf +21dfdaa44261f8d546fa9ea71102162dc880a8ca8037526019a04fece5b21313 retroarch_ps5_software_smoke.map +8ee4305bc2d7e5d75d3f7a4b0b335de39c68932ca49c199310cda9f2279dd69f manifests/retroarch/phase-1.0b-build.json +49ae3623fef1f9d6291141262cfd536c5da6100ee586a365f32b5f2788190e96 docs/retroarch/phase-1.0c-device-smoke-result.md +63b62ca819431f739d45be14c7453c66f23b78ae5ca52ece28ffef911d38f671 manifests/retroarch/phase-1.0c-device-smoke-result.json diff --git a/packaging/retroarch/phase10d/SHA256SUMS.txt b/packaging/retroarch/phase10d/SHA256SUMS.txt new file mode 100644 index 0000000..d64f61c --- /dev/null +++ b/packaging/retroarch/phase10d/SHA256SUMS.txt @@ -0,0 +1,4 @@ +c84c0fb43392ce17abc1e7e6be38b522f1b32a681fd516d684c3dbbf7b34b743 chimera_ps5_crt_entry_canary.elf +96132ef015d1743606a86906c810b9e9b2832889a6d49863653b03dddb75cf07 chimera_ps5_crt_entry_canary.map +092bcabc0f8717a648030c592aa0dc900f878952fb14ee453851a29f298ea7ee retroarch_ps5_early_diag.elf +0d4d4614826fa4cbbf11e7374a2d2ecb574a00b55ad9deac17272f9504b254a0 retroarch_ps5_early_diag.map diff --git a/packaging/retroarch/phase10e/SHA256SUMS.txt b/packaging/retroarch/phase10e/SHA256SUMS.txt new file mode 100644 index 0000000..5669870 --- /dev/null +++ b/packaging/retroarch/phase10e/SHA256SUMS.txt @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-3.0-or-later +# Hash records only. ELF and linker map remain ignored and are not packaged. +1049c78099a60b472a3fb0e2999e3393b6ad76337a28532a7e53872e7772dedf retroarch_ps5_result_diag.elf +ae9739f6f578953bc8dc562bb55967ba587912d161b2d6787438450addec3b44 retroarch_ps5_result_diag.map +# Ignored local evidence; contains the session address and is not packaged. +4ff27a0eac48283cdc4c7ff964226def2689e808e3adea6594d0e77674a676f0 run-c-device-trace.json diff --git a/packaging/retroarch/phase10f/SHA256SUMS.txt b/packaging/retroarch/phase10f/SHA256SUMS.txt new file mode 100644 index 0000000..6e79dd5 --- /dev/null +++ b/packaging/retroarch/phase10f/SHA256SUMS.txt @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-3.0-or-later +# Hash records only. ELF and linker map remain ignored and are not packaged. +e8bfc01c61bfb14b5814280a6e5442f1a5ad05ace5439d1c09e7e5ee00cd0055 retroarch_ps5_interval_diag.elf +638642b750b8d5b108cf6c73215a3f1759bcb6da0b29ee0a0ade5c47e8b7b2d5 retroarch_ps5_interval_diag.map diff --git a/packaging/retroarch/phase10g/SHA256SUMS.txt b/packaging/retroarch/phase10g/SHA256SUMS.txt new file mode 100644 index 0000000..6ea76b5 --- /dev/null +++ b/packaging/retroarch/phase10g/SHA256SUMS.txt @@ -0,0 +1,5 @@ +# Hash-only identity record; no artifact is stored in this repository. +e8bfc01c61bfb14b5814280a6e5442f1a5ad05ace5439d1c09e7e5ee00cd0055 retroarch_ps5_interval_diag.elf +df72af4e738ab5969b78c42cf71d376c2a95b43e26b3d445cd7be9d88cadbf19 tools/ps5_diag_duplex.py +1d21072d9bae6d67e38e3908737326bc5b9eceefd90a659ae3d601833f543944 tests/test_ps5_phase10e_tools.py +c65373df687328243d20184ddb65e589b1f0d2e6460058062054a9dbc8e3c185 pkg/ps5/validate_port.py diff --git a/packaging/retroarch/phase10h/SHA256SUMS.txt b/packaging/retroarch/phase10h/SHA256SUMS.txt new file mode 100644 index 0000000..6b4104e --- /dev/null +++ b/packaging/retroarch/phase10h/SHA256SUMS.txt @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-3.0-or-later +# Hash records only. ELF and linker map remain ignored and are not packaged. +822f2cf1f4d33a514d2bdd88fde40ad580dda5d85f537362ef6dff2eafcb56b6 retroarch_ps5_startup_args_diag.elf +638642b750b8d5b108cf6c73215a3f1759bcb6da0b29ee0a0ade5c47e8b7b2d5 retroarch_ps5_startup_args_diag.map diff --git a/packaging/retroarch/phase10j/SHA256SUMS.txt b/packaging/retroarch/phase10j/SHA256SUMS.txt new file mode 100644 index 0000000..36e56f9 --- /dev/null +++ b/packaging/retroarch/phase10j/SHA256SUMS.txt @@ -0,0 +1,3 @@ +# Phase-1.0J ignored local build products; no artifact is packaged or tracked. +6ff0f7ea391da5f15ea43512a871078133e896a6900ae9f8f3fa75711abb8009 retroarch_ps5_write_diag.elf +19f1cf851ad8f99d31d5de3a14591f81faa83589c18e93cd60be96ebef5d7719 retroarch_ps5_write_diag.map diff --git a/packaging/retroarch/phase10k/SHA256SUMS.txt b/packaging/retroarch/phase10k/SHA256SUMS.txt new file mode 100644 index 0000000..6001b4b --- /dev/null +++ b/packaging/retroarch/phase10k/SHA256SUMS.txt @@ -0,0 +1,4 @@ +# Phase-1.0K records identities only; no artifact or execution package is tracked. +6ff0f7ea391da5f15ea43512a871078133e896a6900ae9f8f3fa75711abb8009 retroarch_ps5_write_diag.elf +19f1cf851ad8f99d31d5de3a14591f81faa83589c18e93cd60be96ebef5d7719 retroarch_ps5_write_diag.map +4ee58f08ff51cff3624cbc072c0e915e8c415eb8cd98185fa0d6a20c02b7c330 tools/ps5_diag_duplex.py diff --git a/packaging/retroarch/phase10m/SHA256SUMS.txt b/packaging/retroarch/phase10m/SHA256SUMS.txt new file mode 100644 index 0000000..c46c52c --- /dev/null +++ b/packaging/retroarch/phase10m/SHA256SUMS.txt @@ -0,0 +1,3 @@ +# Phase-1.0M ignored local build products; no artifact is packaged or tracked. +c99a0856309a357ad2667d89b4924e4063ad214cae09c8a419457b0732f583cd retroarch_ps5_write_diag.elf +6768ffc7267b9b362c3b953571e5dfeed1e70004a3e19f41ef500c3f26272719 retroarch_ps5_write_diag.map diff --git a/packaging/retroarch/phase10n/SHA256SUMS.txt b/packaging/retroarch/phase10n/SHA256SUMS.txt new file mode 100644 index 0000000..7448e83 --- /dev/null +++ b/packaging/retroarch/phase10n/SHA256SUMS.txt @@ -0,0 +1,4 @@ +# Phase-1.0N records identities only; no artifact or execution package is tracked. +c99a0856309a357ad2667d89b4924e4063ad214cae09c8a419457b0732f583cd retroarch_ps5_write_diag.elf +6768ffc7267b9b362c3b953571e5dfeed1e70004a3e19f41ef500c3f26272719 retroarch_ps5_write_diag.map +1d46510369349c1e75ef3b5f983a2ef6fa5896fdd4398788bf58708821d0385d tools/ps5_diag_duplex.py diff --git a/samples/capability_probe/main.c b/samples/capability_probe/main.c new file mode 100644 index 0000000..bd6b47f --- /dev/null +++ b/samples/capability_probe/main.c @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +#include "firmware_gate.h" +#include "probe_platform.h" + +#include +#include + +#ifndef CHIMERA_GFX_PS5_ALLOWED_FIRMWARE +#define CHIMERA_GFX_PS5_ALLOWED_FIRMWARE "NONE" +#endif + +static void write_log(void *userdata, const char *line) { + FILE *stream = (FILE *)userdata; + (void)fprintf(stream, "%s\n", line); +} + +int main(int argc, char **argv) { + chimera_gfx_ps5_loader loader; + chimera_gfx_probe_ops ops; + chimera_gfx_probe_report report; + chimera_gfx_status status; + + if (argc != 4 || strcmp(argv[1], "--firmware") != 0 || + strcmp(argv[3], "--acknowledge-read-only-probe") != 0) { + (void)fprintf( + stderr, "refusing: exact firmware and acknowledgement required\n"); + return 64; + } + if (!chimera_gfx_firmware_gate_allows(CHIMERA_GFX_PS5_ALLOWED_FIRMWARE, + argv[2])) { + (void)fprintf(stderr, "refusing: firmware is not allowlisted\n"); + return 77; + } + + chimera_gfx_ps5_make_loader_ops(&loader, write_log, stdout, &ops); + status = chimera_gfx_ps5_probe_symbols(&ops, &report); + (void)fprintf(stdout, + "{\"event\":\"summary\",\"resolved\":%lu," + "\"total\":%lu,\"status\":\"%s\"}\n", + (unsigned long)report.resolved_count, + (unsigned long)report.symbol_count, + chimera_gfx_status_string(status)); + return status == CHIMERA_GFX_STATUS_OK ? 0 : 1; +} diff --git a/samples/clear_screen/README.md b/samples/clear_screen/README.md new file mode 100644 index 0000000..0ed9da0 --- /dev/null +++ b/samples/clear_screen/README.md @@ -0,0 +1,7 @@ +# Legacy clear-screen placeholder (permanently disabled) + +This directory deliberately contains no source and is not referenced by the +build. The reviewable Phase-1 design lives in `samples/phase1_videoout_clear/` +behind `CHIMERA_GFX_BUILD_PHASE1_VIDEOOUT_CLEAR=OFF` by default. Keeping this +placeholder sourceless prevents an older target name from becoming runnable by +accident. diff --git a/samples/lifecycle_probe/main.c b/samples/lifecycle_probe/main.c new file mode 100644 index 0000000..654e665 --- /dev/null +++ b/samples/lifecycle_probe/main.c @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +#include +#include + +#ifndef CHIMERA_GFX_PS5_ALLOWED_FIRMWARE +#error "The exact firmware build gate is required" +#endif + +#ifndef CHIMERA_GFX_LIFECYCLE_BUILD_ID +#error "A deterministic lifecycle build ID is required" +#endif + +typedef struct notify_request { + char reserved[45]; + char message[3075]; +} notify_request_t; + +int sceKernelSendNotificationRequest(int device, notify_request_t *request, + size_t size, int flags); + +static notify_request_t request = { + .message = "Chimera GFX lifecycle " CHIMERA_GFX_LIFECYCLE_BUILD_ID + " fw " CHIMERA_GFX_PS5_ALLOWED_FIRMWARE}; + +int main(void) { + const int result = + sceKernelSendNotificationRequest(0, &request, sizeof(request), 0); + + _exit(result == 0 ? 0 : 1); +} diff --git a/samples/phase1_videoout_clear/main.c b/samples/phase1_videoout_clear/main.c new file mode 100644 index 0000000..0569da0 --- /dev/null +++ b/samples/phase1_videoout_clear/main.c @@ -0,0 +1,88 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +#define SDL_MAIN_HANDLED +#include + +#include "firmware_gate.h" + +#include +#include + +#ifndef CHIMERA_GFX_PS5_ALLOWED_FIRMWARE +#define CHIMERA_GFX_PS5_ALLOWED_FIRMWARE "NONE" +#endif + +#define CHIMERA_GFX_PHASE1_WIDTH 1920 +#define CHIMERA_GFX_PHASE1_HEIGHT 1080 +#define CHIMERA_GFX_PHASE1_HOLD_MS 1000u + +static void log_stage(const char *stage, int success) { + (void)fprintf(stdout, "{\"stage\":\"%s\",\"success\":%s}\n", stage, + success != 0 ? "true" : "false"); +} + +int main(int argc, char **argv) { + SDL_Window *window = NULL; + SDL_Surface *surface = NULL; + Uint32 color; + int result = 1; + + if (argc != 4 || strcmp(argv[1], "--firmware") != 0 || + strcmp(argv[3], "--acknowledge-phase1-videoout-clear") != 0) { + (void)fprintf( + stderr, "refusing: exact firmware and acknowledgement required\n"); + return 64; + } + if (!chimera_gfx_firmware_gate_allows(CHIMERA_GFX_PS5_ALLOWED_FIRMWARE, + argv[2])) { + (void)fprintf(stderr, "refusing: firmware is not allowlisted\n"); + return 77; + } + + log_stage("firmware_gate", 1); + SDL_SetMainReady(); + if (SDL_Init(SDL_INIT_VIDEO) != 0) { + log_stage("video_init", 0); + goto cleanup; + } + log_stage("video_init", 1); + + window = SDL_CreateWindow("chimera-gfx controlled clear", 0, 0, + CHIMERA_GFX_PHASE1_WIDTH, + CHIMERA_GFX_PHASE1_HEIGHT, SDL_WINDOW_SHOWN); + if (window == NULL) { + log_stage("window_create", 0); + goto cleanup; + } + log_stage("window_create", 1); + + surface = SDL_GetWindowSurface(window); + if (surface == NULL) { + log_stage("surface_acquire", 0); + goto cleanup; + } + log_stage("surface_acquire", 1); + + color = SDL_MapRGBA(surface->format, 0x18u, 0x2au, 0x41u, 0xffu); + if (SDL_FillRect(surface, NULL, color) != 0) { + log_stage("cpu_fill", 0); + goto cleanup; + } + log_stage("cpu_fill", 1); + + /* This is the only requested display mutation and the hardware gate. */ + if (SDL_UpdateWindowSurface(window) != 0) { + log_stage("single_present", 0); + goto cleanup; + } + log_stage("single_present", 1); + SDL_Delay(CHIMERA_GFX_PHASE1_HOLD_MS); + result = 0; + +cleanup: + if (window != NULL) { + SDL_DestroyWindow(window); + } + SDL_Quit(); + log_stage("cleanup", 1); + return result; +} diff --git a/src/backends/mock/mock_backend.c b/src/backends/mock/mock_backend.c new file mode 100644 index 0000000..848d48c --- /dev/null +++ b/src/backends/mock/mock_backend.c @@ -0,0 +1,139 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +#include "../../core/internal.h" + +#include +#include +#include + +#define CHIMERA_GFX_MOCK_MAX_DIMENSION 4096u +#define CHIMERA_GFX_MOCK_MAX_RESOURCES 16u +#define CHIMERA_GFX_RGBA8_BYTES_PER_PIXEL 4u + +static uint64_t hash_bytes(const unsigned char *bytes, size_t size) { + uint64_t hash = UINT64_C(14695981039346656037); + size_t index; + + for (index = 0u; index < size; ++index) { + hash ^= bytes[index]; + hash *= UINT64_C(1099511628211); + } + return hash; +} + +void chimera_gfx_mock_initialize(chimera_gfx_context *context) { + context->capabilities.struct_size = sizeof(context->capabilities); + context->capabilities.api_version = CHIMERA_GFX_API_VERSION; + context->capabilities.backend = CHIMERA_GFX_BACKEND_MOCK; + context->capabilities.flags = CHIMERA_GFX_CAPABILITY_BACKEND_AVAILABLE | + CHIMERA_GFX_CAPABILITY_NON_RENDERING | + CHIMERA_GFX_CAPABILITY_HOST_TEST_ONLY | + CHIMERA_GFX_CAPABILITY_SURFACE_LIFECYCLE | + CHIMERA_GFX_CAPABILITY_TEXTURE_LIFECYCLE | + CHIMERA_GFX_CAPABILITY_TEXTURE_UPLOAD | + CHIMERA_GFX_CAPABILITY_PRESENT_MODEL; + context->capabilities.max_surface_width = CHIMERA_GFX_MOCK_MAX_DIMENSION; + context->capabilities.max_surface_height = CHIMERA_GFX_MOCK_MAX_DIMENSION; + context->capabilities.max_texture_width = CHIMERA_GFX_MOCK_MAX_DIMENSION; + context->capabilities.max_texture_height = CHIMERA_GFX_MOCK_MAX_DIMENSION; + context->capabilities.max_live_surfaces = CHIMERA_GFX_MOCK_MAX_RESOURCES; + context->capabilities.max_live_textures = CHIMERA_GFX_MOCK_MAX_RESOURCES; +} + +chimera_gfx_status +chimera_gfx_mock_surface_create(chimera_gfx_context *context, + const chimera_gfx_surface_desc *desc, + chimera_gfx_surface **out_surface) { + chimera_gfx_surface *surface = + (chimera_gfx_surface *)calloc(1u, sizeof(*surface)); + + if (surface == NULL) { + return CHIMERA_GFX_STATUS_OUT_OF_MEMORY; + } + surface->magic = CHIMERA_GFX_SURFACE_MAGIC; + surface->context = context; + surface->desc = *desc; + context->live_surfaces += 1u; + *out_surface = surface; + return CHIMERA_GFX_STATUS_OK; +} + +chimera_gfx_status +chimera_gfx_mock_texture_create(chimera_gfx_context *context, + const chimera_gfx_texture_desc *desc, + chimera_gfx_texture **out_texture) { + chimera_gfx_texture *texture = + (chimera_gfx_texture *)calloc(1u, sizeof(*texture)); + + if (texture == NULL) { + return CHIMERA_GFX_STATUS_OUT_OF_MEMORY; + } + texture->magic = CHIMERA_GFX_TEXTURE_MAGIC; + texture->context = context; + texture->desc = *desc; + context->live_textures += 1u; + *out_texture = texture; + return CHIMERA_GFX_STATUS_OK; +} + +chimera_gfx_status +chimera_gfx_mock_texture_upload(chimera_gfx_texture *texture, + const chimera_gfx_texture_upload_info *upload) { + const size_t row_size = + (size_t)texture->desc.width * CHIMERA_GFX_RGBA8_BYTES_PER_PIXEL; + const size_t height = (size_t)texture->desc.height; + const unsigned char *source = (const unsigned char *)upload->pixels; + unsigned char *replacement; + size_t required_size; + size_t packed_size; + size_t row; + + if (upload->row_pitch < row_size) { + return CHIMERA_GFX_STATUS_INVALID_ARGUMENT; + } + if (height > 1u && + upload->row_pitch > (SIZE_MAX - row_size) / (height - 1u)) { + return CHIMERA_GFX_STATUS_LIMIT_EXCEEDED; + } + required_size = (height - 1u) * upload->row_pitch + row_size; + if (upload->data_size < required_size || height > SIZE_MAX / row_size) { + return CHIMERA_GFX_STATUS_INVALID_ARGUMENT; + } + packed_size = height * row_size; + replacement = (unsigned char *)malloc(packed_size); + if (replacement == NULL) { + return CHIMERA_GFX_STATUS_OUT_OF_MEMORY; + } + for (row = 0u; row < height; ++row) { + (void)memcpy(replacement + row * row_size, + source + row * upload->row_pitch, row_size); + } + + free(texture->pixels); + texture->pixels = replacement; + texture->data_size = packed_size; + texture->content_hash = hash_bytes(replacement, packed_size); + texture->uploaded = 1; + return CHIMERA_GFX_STATUS_OK; +} + +chimera_gfx_status +chimera_gfx_mock_present(chimera_gfx_context *context, + const chimera_gfx_present_info *present_info) { + chimera_gfx_surface *surface = present_info->surface; + chimera_gfx_texture *texture = present_info->texture; + + if (texture->uploaded == 0) { + return CHIMERA_GFX_STATUS_INVALID_STATE; + } + if (surface->desc.width != texture->desc.width || + surface->desc.height != texture->desc.height || + surface->desc.format != texture->desc.format) { + return CHIMERA_GFX_STATUS_INVALID_ARGUMENT; + } + + surface->present_count += 1u; + surface->last_present_serial = context->next_present_serial; + surface->last_texture_hash = texture->content_hash; + context->next_present_serial += 1u; + return CHIMERA_GFX_STATUS_OK; +} diff --git a/src/backends/ps5/firmware_gate.c b/src/backends/ps5/firmware_gate.c new file mode 100644 index 0000000..fd5b4f1 --- /dev/null +++ b/src/backends/ps5/firmware_gate.c @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +#include "firmware_gate.h" + +#include +#include + +int chimera_gfx_firmware_gate_allows(const char *embedded_firmware, + const char *requested_firmware) { + if (embedded_firmware == NULL || requested_firmware == NULL) { + return 0; + } + return embedded_firmware[0] != '\0' && requested_firmware[0] != '\0' && + strcmp(embedded_firmware, "NONE") != 0 && + strcmp(embedded_firmware, requested_firmware) == 0 + ? 1 + : 0; +} diff --git a/src/backends/ps5/firmware_gate.h b/src/backends/ps5/firmware_gate.h new file mode 100644 index 0000000..b4253c6 --- /dev/null +++ b/src/backends/ps5/firmware_gate.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +#ifndef CHIMERA_GFX_PS5_FIRMWARE_GATE_H +#define CHIMERA_GFX_PS5_FIRMWARE_GATE_H + +int chimera_gfx_firmware_gate_allows(const char *embedded_firmware, + const char *requested_firmware); + +#endif diff --git a/src/backends/ps5/generated_probe_symbols.inc b/src/backends/ps5/generated_probe_symbols.inc new file mode 100644 index 0000000..0fb2823 --- /dev/null +++ b/src/backends/ps5/generated_probe_symbols.inc @@ -0,0 +1,22 @@ +/* Generated from manifests/ps5_gnm_symbols.json. Do not edit. */ +CHIMERA_GNM_SYMBOL("sceGnmAreSubmitsAllowed") +CHIMERA_GNM_SYMBOL("sceGnmComputeWaitOnAddress") +CHIMERA_GNM_SYMBOL("sceGnmComputeWaitSemaphore") +CHIMERA_GNM_SYMBOL("sceGnmDispatchDirect") +CHIMERA_GNM_SYMBOL("sceGnmDispatchIndirect") +CHIMERA_GNM_SYMBOL("sceGnmDrawIndex") +CHIMERA_GNM_SYMBOL("sceGnmDrawIndexAuto") +CHIMERA_GNM_SYMBOL("sceGnmDrawIndirect") +CHIMERA_GNM_SYMBOL("sceGnmInsertWaitFlipDone") +CHIMERA_GNM_SYMBOL("sceGnmMapComputeQueue") +CHIMERA_GNM_SYMBOL("sceGnmRegisterOwner") +CHIMERA_GNM_SYMBOL("sceGnmRegisterResource") +CHIMERA_GNM_SYMBOL("sceGnmRequestFlipAndSubmitDone") +CHIMERA_GNM_SYMBOL("sceGnmSetEmbeddedPsShader") +CHIMERA_GNM_SYMBOL("sceGnmSetEmbeddedVsShader") +CHIMERA_GNM_SYMBOL("sceGnmSubmitAndFlipCommandBuffers") +CHIMERA_GNM_SYMBOL("sceGnmSubmitCommandBuffers") +CHIMERA_GNM_SYMBOL("sceGnmSubmitDone") +CHIMERA_GNM_SYMBOL("sceGnmUnmapComputeQueue") +CHIMERA_GNM_SYMBOL("sceGnmUnregisterOwnerAndResources") +CHIMERA_GNM_SYMBOL("sceGnmUnregisterResource") diff --git a/src/backends/ps5/probe.h b/src/backends/ps5/probe.h new file mode 100644 index 0000000..698d9ec --- /dev/null +++ b/src/backends/ps5/probe.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +#ifndef CHIMERA_GFX_PS5_PROBE_H +#define CHIMERA_GFX_PS5_PROBE_H + +#include + +#include + +typedef void (*chimera_gfx_probe_log_fn)(void *userdata, const char *line); + +typedef struct chimera_gfx_probe_ops { + void *userdata; + int (*open_module)(void *userdata, const char *module_name); + int (*resolve_symbol)(void *userdata, const char *symbol_name, + int *present); + int (*close_module)(void *userdata); + chimera_gfx_probe_log_fn log_line; +} chimera_gfx_probe_ops; + +typedef struct chimera_gfx_probe_report { + size_t symbol_count; + size_t resolved_count; + int module_opened; +} chimera_gfx_probe_report; + +chimera_gfx_status +chimera_gfx_ps5_probe_symbols(const chimera_gfx_probe_ops *ops, + chimera_gfx_probe_report *out_report); + +#endif diff --git a/src/backends/ps5/probe_logic.c b/src/backends/ps5/probe_logic.c new file mode 100644 index 0000000..784d8d5 --- /dev/null +++ b/src/backends/ps5/probe_logic.c @@ -0,0 +1,83 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +#include "probe.h" + +#include + +#define CHIMERA_GNM_SYMBOL(name) name, +static const char *const chimera_gnm_symbol_names[] = { +#include "generated_probe_symbols.inc" +}; +#undef CHIMERA_GNM_SYMBOL + +static void log_line(const chimera_gfx_probe_ops *ops, const char *line) { + if (ops->log_line != NULL) { + ops->log_line(ops->userdata, line); + } +} + +chimera_gfx_status +chimera_gfx_ps5_probe_symbols(const chimera_gfx_probe_ops *ops, + chimera_gfx_probe_report *out_report) { + const size_t symbol_count = + sizeof(chimera_gnm_symbol_names) / sizeof(chimera_gnm_symbol_names[0]); + size_t index; + char line[192]; + + if (ops == NULL || out_report == NULL || ops->open_module == NULL || + ops->resolve_symbol == NULL || ops->close_module == NULL) { + return CHIMERA_GFX_STATUS_INVALID_ARGUMENT; + } + + out_report->symbol_count = symbol_count; + out_report->resolved_count = 0u; + out_report->module_opened = 0; + + log_line(ops, "{\"event\":\"probe_start\",\"safety_profile\":" + "\"symbol-discovery-only\"}"); + + if (ops->open_module(ops->userdata, "libSceGnmDriver.sprx") != 0) { + log_line(ops, "{\"event\":\"module\",\"opened\":false}"); + return CHIMERA_GFX_STATUS_BACKEND_UNAVAILABLE; + } + out_report->module_opened = 1; + log_line(ops, "{\"event\":\"module\",\"opened\":true}"); + + for (index = 0u; index < symbol_count; ++index) { + int present = 0; + int written; + + if (ops->resolve_symbol(ops->userdata, chimera_gnm_symbol_names[index], + &present) != 0) { + if (ops->close_module(ops->userdata) == 0) { + out_report->module_opened = 0; + } + log_line(ops, "{\"event\":\"probe_end\",\"status\":" + "\"lookup-error\"}"); + return CHIMERA_GFX_STATUS_INTERNAL_ERROR; + } + if (present != 0) { + out_report->resolved_count += 1u; + } + + written = snprintf(line, sizeof(line), + "{\"event\":\"symbol\",\"name\":\"%s\"," + "\"resolved\":%s}", + chimera_gnm_symbol_names[index], + present != 0 ? "true" : "false"); + if (written < 0 || (size_t)written >= sizeof(line)) { + if (ops->close_module(ops->userdata) == 0) { + out_report->module_opened = 0; + } + return CHIMERA_GFX_STATUS_INTERNAL_ERROR; + } + log_line(ops, line); + } + + if (ops->close_module(ops->userdata) != 0) { + log_line(ops, "{\"event\":\"probe_end\",\"status\":\"cleanup-error\"}"); + return CHIMERA_GFX_STATUS_INTERNAL_ERROR; + } + out_report->module_opened = 0; + log_line(ops, "{\"event\":\"probe_end\",\"status\":\"complete\"}"); + return CHIMERA_GFX_STATUS_OK; +} diff --git a/src/backends/ps5/probe_platform.c b/src/backends/ps5/probe_platform.c new file mode 100644 index 0000000..f73b582 --- /dev/null +++ b/src/backends/ps5/probe_platform.c @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +#include "probe_platform.h" + +#include + +static int open_module(void *userdata, const char *module_name) { + chimera_gfx_ps5_loader *loader = (chimera_gfx_ps5_loader *)userdata; + + loader->module = dlopen(module_name, RTLD_LAZY | RTLD_LOCAL); + return loader->module == NULL ? -1 : 0; +} + +static int resolve_symbol(void *userdata, const char *symbol_name, + int *present) { + chimera_gfx_ps5_loader *loader = (chimera_gfx_ps5_loader *)userdata; + void *address; + + (void)dlerror(); + address = dlsym(loader->module, symbol_name); + *present = address != NULL && dlerror() == NULL ? 1 : 0; + address = NULL; + return 0; +} + +static int close_module(void *userdata) { + chimera_gfx_ps5_loader *loader = (chimera_gfx_ps5_loader *)userdata; + + if (loader->module != NULL) { + if (dlclose(loader->module) != 0) { + return -1; + } + loader->module = NULL; + } + return 0; +} + +static void forward_log(void *userdata, const char *line) { + chimera_gfx_ps5_loader *loader = (chimera_gfx_ps5_loader *)userdata; + + if (loader->logger != NULL) { + loader->logger(loader->logger_userdata, line); + } +} + +void chimera_gfx_ps5_make_loader_ops(chimera_gfx_ps5_loader *loader, + chimera_gfx_probe_log_fn logger, + void *logger_userdata, + chimera_gfx_probe_ops *out_ops) { + loader->module = NULL; + loader->logger = logger; + loader->logger_userdata = logger_userdata; + + out_ops->userdata = loader; + out_ops->open_module = open_module; + out_ops->resolve_symbol = resolve_symbol; + out_ops->close_module = close_module; + out_ops->log_line = forward_log; +} diff --git a/src/backends/ps5/probe_platform.h b/src/backends/ps5/probe_platform.h new file mode 100644 index 0000000..8416753 --- /dev/null +++ b/src/backends/ps5/probe_platform.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +#ifndef CHIMERA_GFX_PS5_PROBE_PLATFORM_H +#define CHIMERA_GFX_PS5_PROBE_PLATFORM_H + +#include "probe.h" + +typedef struct chimera_gfx_ps5_loader { + void *module; + chimera_gfx_probe_log_fn logger; + void *logger_userdata; +} chimera_gfx_ps5_loader; + +void chimera_gfx_ps5_make_loader_ops(chimera_gfx_ps5_loader *loader, + chimera_gfx_probe_log_fn logger, + void *logger_userdata, + chimera_gfx_probe_ops *out_ops); + +#endif diff --git a/src/backends/ps5/ps5_backend.c b/src/backends/ps5/ps5_backend.c new file mode 100644 index 0000000..ca00956 --- /dev/null +++ b/src/backends/ps5/ps5_backend.c @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +#include "../../core/internal.h" + +chimera_gfx_status +chimera_gfx_ps5_backend_create(const chimera_gfx_config *config, + chimera_gfx_context **out_context) { + (void)config; + *out_context = NULL; + return CHIMERA_GFX_STATUS_SAFETY_POLICY; +} diff --git a/tests/phase09b_observer_model.py b/tests/phase09b_observer_model.py new file mode 100644 index 0000000..8fde0d4 --- /dev/null +++ b/tests/phase09b_observer_model.py @@ -0,0 +1,140 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Host-only contract model for the blocked Phase-0.9B observer design. + +This module performs no filesystem or network I/O. It is not PS5 observer +source and must never be added to a target build. +""" + +from __future__ import annotations + +from dataclasses import dataclass, field +import hashlib + + +@dataclass(frozen=True) +class MockObject: + path_known: bool = True + path_conflict: bool = False + symlink: bool = False + object_id_before: str = "dev:1/ino:1" + object_id_after: str = "dev:1/ino:1" + size_before: int = 0 + size_after: int = 0 + chunks: tuple[bytes, ...] = () + read_error: bool = False + expected_sha256: str | None = None + + +@dataclass +class ObserverMock: + output_limit: int = 64 + error_limit: int = 8 + emitted: list[dict[str, object]] = field(default_factory=list) + errors: int = 0 + retry_count: int = 0 + persistent_write_count: int = 0 + service_or_process_mutation_count: int = 0 + listener_count: int = 0 + lifecycle_call_count: int = 0 + installer_call_count: int = 0 + graphics_or_retroarch_call_count: int = 0 + exit_reached: bool = False + + def emit(self, category: str, confidence: str, raw_error: str | None) -> bool: + if len(self.emitted) >= self.output_limit: + self.errors += 1 + return False + if raw_error is not None: + self.errors += 1 + if self.errors > self.error_limit: + return False + self.emitted.append( + { + "category": category, + "confidence": confidence, + "raw_error": raw_error, + "persistent_mutation_performed": False, + "retry_performed": False, + } + ) + return True + + def finish(self) -> None: + self.exit_reached = True + + +def evaluate_firmware(source_one: str | None, source_two: str | None) -> str: + if source_one is None or source_two is None: + return "UNPROVEN" + if source_one != source_two: + return "CONFLICT" + return "OBSERVED" + + +def evaluate_object(obj: MockObject) -> tuple[str, str | None]: + if not obj.path_known: + return "UNPROVEN", "UNKNOWN_PATH" + if obj.path_conflict: + return "CONFLICT", "PATH_CONFLICT" + if obj.symlink: + return "UNPROVEN", "PATH_SYMLINK_SAFETY_UNPROVEN" + if obj.object_id_before != obj.object_id_after: + return "ERROR", "OBJECT_ID_CHANGED" + if obj.size_before != obj.size_after: + return "ERROR", "SIZE_CHANGED" + if obj.read_error: + return "ERROR", "READ_ERROR" + + data = b"".join(obj.chunks) + if len(data) != obj.size_before: + return "ERROR", "SHORT_READ" + digest = hashlib.sha256(data).hexdigest() + if obj.expected_sha256 is not None and digest != obj.expected_sha256: + return "CONFLICT", "HASH_MISMATCH" + return "OBSERVED", None + + +def evaluate_live_backup(live: MockObject, backup: MockObject | None) -> str: + if backup is None: + return "BACKUP_MISSING" + if live.object_id_before == backup.object_id_before: + return "SAME_OBJECT" + return "SEPARATE_OBJECTS" + + +def unsupported_query(supported: bool) -> str: + return "OBSERVED" if supported else "UNSUPPORTED_OR_UNPROVEN" + + +def evaluate_autoload(source_present: bool, parse_ok: bool) -> str: + if not source_present: + return "UNPROVEN" + return "OBSERVED" if parse_ok else "ERROR" + + +def run_terminal_scenario( + *, + output_channel_ok: bool = True, + deadline_reached: bool = False, + record_count: int = 1, + error_count: int = 0, +) -> ObserverMock: + observer = ObserverMock() + if not output_channel_ok: + observer.emit("output", "ERROR", "OUTPUT_CHANNEL_FAILED") + observer.finish() + return observer + if deadline_reached: + observer.emit("deadline", "ERROR", "DEADLINE_REACHED") + observer.finish() + return observer + + for index in range(record_count): + if not observer.emit(f"record-{index}", "OBSERVED", None): + break + for index in range(error_count): + if not observer.emit(f"error-{index}", "ERROR", "MOCK_ERROR"): + break + observer.finish() + return observer diff --git a/tests/phase09c_feasibility_model.py b/tests/phase09c_feasibility_model.py new file mode 100644 index 0000000..d4df2bd --- /dev/null +++ b/tests/phase09c_feasibility_model.py @@ -0,0 +1,579 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Host-only Phase-0.9C feasibility and result-protocol model. + +This module performs no filesystem, network, compiler, or target operation. +It is not observer source and must never be part of a PS5 target. +""" + +from __future__ import annotations + +from dataclasses import dataclass, field +import hashlib +import struct + + +PROOF_STATES = { + "PROVEN_SAFE", + "PROVEN_SIDE_EFFECTING", + "UNPROVEN", + "NOT_APPLICABLE", +} + +STARTUP_PATHS: dict[str, tuple[tuple[str, str], ...]] = { + "normal_sdk": ( + ("loader_entry_transfer", "PROVEN_SIDE_EFFECTING"), + ("bss_clear", "PROVEN_SIDE_EFFECTING"), + ("syscall_kernel_klog_init", "PROVEN_SIDE_EFFECTING"), + ("libc_isthreaded_write", "PROVEN_SIDE_EFFECTING"), + ("__patch_init", "PROVEN_SIDE_EFFECTING"), + ("rtld_init", "PROVEN_SIDE_EFFECTING"), + ("constructors", "PROVEN_SIDE_EFFECTING"), + ("main", "UNPROVEN"), + ("destructors_and_rtld_close", "UNPROVEN"), + ("terminate_branch", "UNPROVEN"), + ), + "freestanding_theoretical": ( + ("loader_entry_transfer", "PROVEN_SIDE_EFFECTING"), + ("stack_alignment", "UNPROVEN"), + ("bss_initialization", "UNPROVEN"), + ("relative_relocations_only", "PROVEN_SAFE"), + ("tls", "UNPROVEN"), + ("read_abi", "UNPROVEN"), + ("monotonic_time_abi", "UNPROVEN"), + ("return_continuation", "UNPROVEN"), + ("process_exit_abi", "UNPROVEN"), + ("cleanup", "UNPROVEN"), + ), + "normal_return": ( + ("ret_to_saved_rip", "PROVEN_SIDE_EFFECTING"), + ("original_process_continuation", "UNPROVEN"), + ("process_termination", "UNPROVEN"), + ("loader_cleanup", "UNPROVEN"), + ), + "normal_process_exit": ( + ("exit_import_or_raw_syscall", "UNPROVEN"), + ("process_teardown", "UNPROVEN"), + ("waitpid_reap", "PROVEN_SAFE"), + ("exit_status_delivery", "UNPROVEN"), + ("result_copyout", "UNPROVEN"), + ), + "error_or_crash": ( + ("partial_runtime_unwind", "UNPROVEN"), + ("waitpid_status_discarded", "PROVEN_SIDE_EFFECTING"), + ("fixed_ambiguous_response", "PROVEN_SIDE_EFFECTING"), + ), + "timeout": ( + ("sigterm", "PROVEN_SIDE_EFFECTING"), + ("sigkill", "PROVEN_SIDE_EFFECTING"), + ("reap", "PROVEN_SAFE"), + ("safe_exit", "UNPROVEN"), + ), +} + +PROHIBITED_STARTUP_EFFECTS = { + "__patch_init", + "kernel_copyin", + "kernel_set_ucred_caps", + "kernel_set_ucred_attrs", + "syscall_permission_bound_write", + "dynamic_module_loading", +} + +NORMAL_SDK_REACHABLE_PROHIBITED_EFFECTS = { + "__patch_init", + "kernel_copyin", + "kernel_set_ucred_caps", + "kernel_set_ucred_attrs", + "syscall_permission_bound_write", + "dynamic_module_loading", +} + +FREESTANDING_DEPENDENCY_CLOSURE = { + "entry_address": "PROVEN_SAFE", + "rdi_argument": "PROVEN_SAFE", + "stack_alignment": "UNPROVEN", + "saved_rip_continuation": "UNPROVEN", + "bss_zero_fill": "UNPROVEN", + "relative_relocation_subset": "PROVEN_SAFE", + "complete_relocation_set": "UNPROVEN", + "tls": "UNPROVEN", + "constructors": "NOT_APPLICABLE", + "libc": "NOT_APPLICABLE", + "heap": "NOT_APPLICABLE", + "callable_read_abi": "UNPROVEN", + "callable_monotonic_time_abi": "UNPROVEN", + "process_exit_abi": "UNPROVEN", + "return_cleanup": "UNPROVEN", + "bounded_result_copyout": "UNPROVEN", +} + +EXIT_PATHS = { + "return": ( + "ENTRY", + "RET_TO_SAVED_RIP", + "ORIGINAL_PROCESS_CONTINUATION_UNPROVEN", + "NO_SAFE_TERMINAL", + ), + "process_exit": ( + "ENTRY", + "EXIT_ABI_UNPROVEN", + "CHILD_REAPED_STATUS_DISCARDED", + "NO_RESULT_COPYOUT", + ), + "crash": ( + "ENTRY", + "FAULT", + "CHILD_REAPED_STATUS_DISCARDED", + "AMBIGUOUS_FIXED_RESPONSE", + ), + "timeout": ( + "ENTRY", + "DEADLINE", + "SIGTERM", + "SIGKILL", + "REAPED", + "INADMISSIBLE_TERMINATION", + ), +} + +SAFE_EXIT_TERMINALS: frozenset[str] = frozenset() + +OUTPUT_ARCHITECTURES = { + "D1_CALLER_OWNED_BOUNDED_BUFFER": ( + "CONCEPT_FEASIBLE_REQUIRES_LOADER_CHANGE_AND_EXIT_PROOF" + ), + "D2_EXISTING_REQUEST_RESPONSE": "REJECTED_SEND_ONLY_NO_RESULT_RECEIVE", + "D3_LOADER_OWNED_STATUS_RECORD": ( + "UNPROVEN_REQUIRES_LOADER_STATE_AND_PROPAGATION_CHANGE" + ), + "D4_PROCESS_EXIT_STATUS": "REJECTED_WAIT_STATUS_DISCARDED_AND_AMBIGUOUS", +} + + +def validate_startup_model() -> list[str]: + errors: list[str] = [] + required_paths = { + "normal_sdk", + "freestanding_theoretical", + "normal_return", + "normal_process_exit", + "error_or_crash", + "timeout", + } + if set(STARTUP_PATHS) != required_paths: + errors.append("startup path inventory differs") + for path_name, steps in STARTUP_PATHS.items(): + if not steps: + errors.append(f"{path_name} has no steps") + for step, classification in steps: + if not step or classification not in PROOF_STATES: + errors.append(f"{path_name} has invalid step {step}") + if not ( + PROHIBITED_STARTUP_EFFECTS & NORMAL_SDK_REACHABLE_PROHIBITED_EFFECTS + ): + errors.append("normal SDK prohibited effects were hidden") + if SAFE_EXIT_TERMINALS: + errors.append("an unproven safe exit terminal was added") + return errors + + +def freestanding_blockers() -> tuple[str, ...]: + return tuple( + key + for key, classification in FREESTANDING_DEPENDENCY_CLOSURE.items() + if classification == "UNPROVEN" + ) + + +def exit_path_is_safe(path: str) -> bool: + states = EXIT_PATHS[path] + return bool(states and states[-1] in SAFE_EXIT_TERMINALS) + + +MAGIC = b"CHG09C01" +PROTOCOL_VERSION = 1 +HEADER_SIZE = 256 +MAX_OUTPUT_SIZE = 4096 +MAX_BODY_SIZE = MAX_OUTPUT_SIZE - HEADER_SIZE +COMPLETION_MARKER = b"COMPLETE" +FIRMWARE_FIELD_SIZE = 8 +NONCE_SIZE = 16 +REQUEST_ID_SIZE = 16 + +STATUS_SUCCESS = 1 +STATUS_OBSERVER_ERROR = 2 +STATUS_TIMEOUT = 3 +STATUS_FIRMWARE_CONFLICT = 4 + +FLAG_TRUNCATED = 1 << 0 + +CLEANUP_INCOMPLETE = 0 +CLEANUP_CLEAN = 1 +CLEANUP_FAILED = 2 + +OFFSET_MAGIC = 0 +OFFSET_VERSION = 8 +OFFSET_HEADER_SIZE = 10 +OFFSET_MAX_OUTPUT = 12 +OFFSET_ACTUAL_OUTPUT = 16 +OFFSET_OBSERVER_VERSION = 20 +OFFSET_STATUS = 24 +OFFSET_FLAGS = 28 +OFFSET_CLEANUP = 32 +OFFSET_RESERVED_WORD = 36 +OFFSET_REQUESTED = 40 +OFFSET_OBSERVED = 48 +OFFSET_UNSUPPORTED = 56 +OFFSET_DEADLINE = 64 +OFFSET_NONCE = 72 +OFFSET_REQUEST_ID = 88 +OFFSET_FIRMWARE_ONE = 104 +OFFSET_FIRMWARE_TWO = 112 +OFFSET_ARTIFACT_HASH = 120 +OFFSET_BODY_CHECKSUM = 152 +OFFSET_RESULT_CHECKSUM = 184 +OFFSET_COMPLETION = 216 +OFFSET_RESERVED = 224 + + +@dataclass(frozen=True) +class ResultRequest: + execution_nonce: bytes + request_id: bytes + firmware_source_one: str | None + firmware_source_two: str | None + observer_version: int + requested_capabilities: int + artifact_sha256: bytes + deadline_monotonic_ns: int + + def validate(self) -> None: + if len(self.execution_nonce) != NONCE_SIZE: + raise ValueError("execution nonce must be 16 bytes") + if len(self.request_id) != REQUEST_ID_SIZE: + raise ValueError("request ID must be 16 bytes") + if len(self.artifact_sha256) != 32: + raise ValueError("artifact SHA-256 must be 32 bytes") + if self.observer_version <= 0: + raise ValueError("observer version must be positive") + if not 0 <= self.requested_capabilities <= 0xFFFFFFFFFFFFFFFF: + raise ValueError("requested capability bitmap is out of range") + if not 0 < self.deadline_monotonic_ns <= 0xFFFFFFFFFFFFFFFF: + raise ValueError("deadline is out of range") + _encode_firmware(self.firmware_source_one) + _encode_firmware(self.firmware_source_two) + + +def _encode_firmware(value: str | None) -> bytes: + if value is None: + return b"\0" * FIRMWARE_FIELD_SIZE + encoded = value.encode("ascii") + if not encoded or b"\0" in encoded or len(encoded) >= FIRMWARE_FIELD_SIZE: + raise ValueError("firmware ID is not canonical") + return encoded.ljust(FIRMWARE_FIELD_SIZE, b"\0") + + +def _decode_firmware(value: bytes) -> str | None: + if value == b"\0" * FIRMWARE_FIELD_SIZE: + return None + first_nul = value.find(b"\0") + if first_nul < 1 or any(value[first_nul:]): + raise ValueError("firmware ID is not canonically padded") + return value[:first_nul].decode("ascii") + + +def _put_u16(buffer: bytearray, offset: int, value: int) -> None: + struct.pack_into(">H", buffer, offset, value) + + +def _put_u32(buffer: bytearray, offset: int, value: int) -> None: + struct.pack_into(">I", buffer, offset, value) + + +def _put_u64(buffer: bytearray, offset: int, value: int) -> None: + struct.pack_into(">Q", buffer, offset, value) + + +def _get_u16(buffer: bytes, offset: int) -> int: + return struct.unpack_from(">H", buffer, offset)[0] + + +def _get_u32(buffer: bytes, offset: int) -> int: + return struct.unpack_from(">I", buffer, offset)[0] + + +def _get_u64(buffer: bytes, offset: int) -> int: + return struct.unpack_from(">Q", buffer, offset)[0] + + +def _result_checksum(buffer: bytes, actual_output_size: int) -> bytes: + candidate = bytearray(buffer[:actual_output_size]) + candidate[OFFSET_RESULT_CHECKSUM : OFFSET_RESULT_CHECKSUM + 32] = b"\0" * 32 + candidate[OFFSET_COMPLETION : OFFSET_COMPLETION + 8] = b"\0" * 8 + return hashlib.sha256(candidate).digest() + + +def build_result( + request: ResultRequest, + body: bytes, + *, + status: int = STATUS_SUCCESS, + observed_capabilities: int = 0, + unsupported_capabilities: int = 0, + cleanup_status: int = CLEANUP_CLEAN, + truncate: bool = False, + complete: bool = True, + protocol_version: int = PROTOCOL_VERSION, +) -> bytes: + """Build a deterministic host record as if observer then caller finalized it.""" + + request.validate() + if not isinstance(body, bytes): + raise TypeError("body must be bytes") + if truncate and len(body) > MAX_BODY_SIZE: + body = body[:MAX_BODY_SIZE] + elif len(body) > MAX_BODY_SIZE: + raise ValueError("body exceeds fixed result buffer") + for value in (observed_capabilities, unsupported_capabilities): + if not 0 <= value <= 0xFFFFFFFFFFFFFFFF: + raise ValueError("capability bitmap is out of range") + + actual_output_size = HEADER_SIZE + len(body) + if actual_output_size < HEADER_SIZE or actual_output_size > MAX_OUTPUT_SIZE: + raise ValueError("checked output-size arithmetic failed") + + flags = FLAG_TRUNCATED if truncate else 0 + buffer = bytearray(MAX_OUTPUT_SIZE) + buffer[OFFSET_MAGIC : OFFSET_MAGIC + 8] = MAGIC + _put_u16(buffer, OFFSET_VERSION, protocol_version) + _put_u16(buffer, OFFSET_HEADER_SIZE, HEADER_SIZE) + _put_u32(buffer, OFFSET_MAX_OUTPUT, MAX_OUTPUT_SIZE) + _put_u32(buffer, OFFSET_ACTUAL_OUTPUT, actual_output_size) + _put_u32(buffer, OFFSET_OBSERVER_VERSION, request.observer_version) + _put_u32(buffer, OFFSET_STATUS, status) + _put_u32(buffer, OFFSET_FLAGS, flags) + _put_u32(buffer, OFFSET_CLEANUP, cleanup_status) + _put_u64(buffer, OFFSET_REQUESTED, request.requested_capabilities) + _put_u64(buffer, OFFSET_OBSERVED, observed_capabilities) + _put_u64(buffer, OFFSET_UNSUPPORTED, unsupported_capabilities) + _put_u64(buffer, OFFSET_DEADLINE, request.deadline_monotonic_ns) + buffer[OFFSET_NONCE : OFFSET_NONCE + NONCE_SIZE] = request.execution_nonce + buffer[OFFSET_REQUEST_ID : OFFSET_REQUEST_ID + REQUEST_ID_SIZE] = ( + request.request_id + ) + buffer[OFFSET_FIRMWARE_ONE : OFFSET_FIRMWARE_ONE + FIRMWARE_FIELD_SIZE] = ( + _encode_firmware(request.firmware_source_one) + ) + buffer[OFFSET_FIRMWARE_TWO : OFFSET_FIRMWARE_TWO + FIRMWARE_FIELD_SIZE] = ( + _encode_firmware(request.firmware_source_two) + ) + buffer[OFFSET_ARTIFACT_HASH : OFFSET_ARTIFACT_HASH + 32] = ( + request.artifact_sha256 + ) + buffer[HEADER_SIZE:actual_output_size] = body + buffer[OFFSET_BODY_CHECKSUM : OFFSET_BODY_CHECKSUM + 32] = hashlib.sha256( + body + ).digest() + buffer[OFFSET_RESULT_CHECKSUM : OFFSET_RESULT_CHECKSUM + 32] = ( + _result_checksum(buffer, actual_output_size) + ) + if complete: + buffer[OFFSET_COMPLETION : OFFSET_COMPLETION + 8] = COMPLETION_MARKER + return bytes(buffer) + + +def validate_result( + record: bytes, request: ResultRequest, *, now_monotonic_ns: int +) -> str: + """Validate one fixed caller-owned record and return a fail-closed decision.""" + + try: + request.validate() + except (TypeError, ValueError): + return "BLOCKED_INVALID_REQUEST" + if len(record) != MAX_OUTPUT_SIZE: + return "BLOCKED_WRONG_BUFFER_SIZE" + if record[OFFSET_MAGIC : OFFSET_MAGIC + 8] != MAGIC: + return "BLOCKED_BAD_MAGIC" + if _get_u16(record, OFFSET_VERSION) != PROTOCOL_VERSION: + return "BLOCKED_UNKNOWN_VERSION" + if _get_u16(record, OFFSET_HEADER_SIZE) != HEADER_SIZE: + return "BLOCKED_BAD_HEADER_SIZE" + if _get_u32(record, OFFSET_MAX_OUTPUT) != MAX_OUTPUT_SIZE: + return "BLOCKED_MAXIMUM_MISMATCH" + + actual_output_size = _get_u32(record, OFFSET_ACTUAL_OUTPUT) + if not HEADER_SIZE <= actual_output_size <= MAX_OUTPUT_SIZE: + return "BLOCKED_ACTUAL_SIZE" + if any(record[actual_output_size:]): + return "BLOCKED_NONZERO_UNUSED_BYTES" + if _get_u32(record, OFFSET_RESERVED_WORD) != 0 or any( + record[OFFSET_RESERVED:HEADER_SIZE] + ): + return "BLOCKED_RESERVED_DATA" + if record[OFFSET_COMPLETION : OFFSET_COMPLETION + 8] != COMPLETION_MARKER: + return "BLOCKED_INCOMPLETE" + if ( + record[OFFSET_NONCE : OFFSET_NONCE + NONCE_SIZE] + != request.execution_nonce + ): + return "BLOCKED_STALE_NONCE" + if ( + record[OFFSET_REQUEST_ID : OFFSET_REQUEST_ID + REQUEST_ID_SIZE] + != request.request_id + ): + return "BLOCKED_STALE_REQUEST_ID" + if _get_u32(record, OFFSET_OBSERVER_VERSION) != request.observer_version: + return "BLOCKED_OBSERVER_VERSION" + if _get_u64(record, OFFSET_DEADLINE) != request.deadline_monotonic_ns: + return "BLOCKED_DEADLINE_BINDING" + if now_monotonic_ns > request.deadline_monotonic_ns: + return "BLOCKED_TIMEOUT" + if ( + record[OFFSET_ARTIFACT_HASH : OFFSET_ARTIFACT_HASH + 32] + != request.artifact_sha256 + ): + return "BLOCKED_ARTIFACT_HASH" + + try: + firmware_one = _decode_firmware( + record[ + OFFSET_FIRMWARE_ONE : OFFSET_FIRMWARE_ONE + FIRMWARE_FIELD_SIZE + ] + ) + firmware_two = _decode_firmware( + record[ + OFFSET_FIRMWARE_TWO : OFFSET_FIRMWARE_TWO + FIRMWARE_FIELD_SIZE + ] + ) + except (UnicodeDecodeError, ValueError): + return "BLOCKED_FIRMWARE_ENCODING" + if firmware_one is None: + return "BLOCKED_FIRMWARE_SOURCE_1_ABSENT" + if firmware_two is None: + return "BLOCKED_FIRMWARE_SOURCE_2_ABSENT" + if firmware_one != firmware_two: + return "BLOCKED_FIRMWARE_CONFLICT" + if firmware_one != "9.60": + return "BLOCKED_FIRMWARE_MISMATCH" + if ( + firmware_one != request.firmware_source_one + or firmware_two != request.firmware_source_two + ): + return "BLOCKED_FIRMWARE_BINDING" + + expected_result_checksum = _result_checksum(record, actual_output_size) + if ( + record[OFFSET_RESULT_CHECKSUM : OFFSET_RESULT_CHECKSUM + 32] + != expected_result_checksum + ): + return "BLOCKED_RESULT_CHECKSUM" + body = record[HEADER_SIZE:actual_output_size] + if ( + record[OFFSET_BODY_CHECKSUM : OFFSET_BODY_CHECKSUM + 32] + != hashlib.sha256(body).digest() + ): + return "BLOCKED_BODY_CHECKSUM" + if _get_u32(record, OFFSET_FLAGS) & ~FLAG_TRUNCATED: + return "BLOCKED_UNKNOWN_FLAGS" + if _get_u32(record, OFFSET_FLAGS) & FLAG_TRUNCATED: + return "BLOCKED_TRUNCATED" + if _get_u32(record, OFFSET_CLEANUP) != CLEANUP_CLEAN: + return "BLOCKED_CLEANUP_NOT_PROVEN" + if _get_u32(record, OFFSET_STATUS) != STATUS_SUCCESS: + return "BLOCKED_OBSERVER_FAILURE" + + requested = _get_u64(record, OFFSET_REQUESTED) + observed = _get_u64(record, OFFSET_OBSERVED) + unsupported = _get_u64(record, OFFSET_UNSUPPORTED) + if requested != request.requested_capabilities: + return "BLOCKED_CAPABILITY_REQUEST_BINDING" + if observed & unsupported: + return "BLOCKED_CAPABILITY_BITMAP_CONFLICT" + if (observed | unsupported) & ~requested: + return "BLOCKED_UNREQUESTED_CAPABILITY" + if (observed | unsupported) != requested: + return "BLOCKED_INCOMPLETE_CAPABILITY_RESULT" + if unsupported: + return "VALID_RECORD_WITH_UNSUPPORTED_CAPABILITIES" + return "VALID_COMPLETE_RESULT" + + +@dataclass +class ResultConsumer: + consumed: set[tuple[bytes, bytes]] = field(default_factory=set) + + def consume( + self, record: bytes, request: ResultRequest, *, now_monotonic_ns: int + ) -> str: + key = (request.execution_nonce, request.request_id) + if key in self.consumed: + return "BLOCKED_DUPLICATE_RESULT" + result = validate_result( + record, request, now_monotonic_ns=now_monotonic_ns + ) + if result in { + "VALID_COMPLETE_RESULT", + "VALID_RECORD_WITH_UNSUPPORTED_CAPABILITIES", + }: + self.consumed.add(key) + return result + + +SIDE_EFFECT_MODEL = { + "runtime_self_identity": { + "semantic_readonly", + "cache_effect_possible", + "audit_effect_possible", + }, + "firmware_query": { + "semantic_readonly", + "cache_effect_possible", + "audit_effect_possible", + "service_or_security_counter_effect_possible", + }, + "filesystem_metadata": { + "semantic_readonly", + "metadata_effect_possible", + "cache_effect_possible", + "audit_effect_possible", + "open_bookkeeping_effect_possible", + "object_race_possible", + }, + "filesystem_content_hash": { + "semantic_readonly", + "metadata_effect_possible", + "atime_effect_possible", + "cache_effect_possible", + "audit_effect_possible", + "open_bookkeeping_effect_possible", + "object_race_possible", + }, + "process_service_listener_snapshot": { + "semantic_readonly", + "cache_effect_possible", + "audit_effect_possible", + "counter_effect_possible", + "process_accounting_effect_possible", + "object_race_possible", + }, + "autoload_read": { + "semantic_readonly", + "metadata_effect_possible", + "atime_effect_possible", + "cache_effect_possible", + "audit_effect_possible", + "service_state_effect_possible", + "object_race_possible", + }, +} + + +def classify_side_effects(operation: str) -> frozenset[str]: + return frozenset(SIDE_EFFECT_MODEL[operation]) + + +def is_proven_side_effect_free(operation: str) -> bool: + _ = SIDE_EFFECT_MODEL[operation] + return False diff --git a/tests/phase10w_fake_transport.py b/tests/phase10w_fake_transport.py new file mode 100644 index 0000000..5f2d3c7 --- /dev/null +++ b/tests/phase10w_fake_transport.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""In-memory transport double for Phase-1.0W policy tests only.""" + +from __future__ import annotations + +from typing import Any + + +class FakeTransportError(RuntimeError): + """Synthetic sequence error.""" + + +class FakeTransport: + """Record one synthetic session without any network primitives.""" + + def __init__(self, inbound_chunks: list[bytes]) -> None: + self._inbound = list(inbound_chunks) + self._plan: Any = None + self._command_index = 0 + self._opened_once = False + self._closed = False + self.events: list[str] = [] + + def open_once(self, plan: Any) -> None: + if self._opened_once: + raise FakeTransportError("fake transport already opened") + self._opened_once = True + self._plan = plan + self.events.append("OPEN") + + def send_command_token(self, command: str) -> None: + if not self._opened_once or self._closed or self._plan is None: + raise FakeTransportError("fake transport is not open") + if self._command_index >= len(self._plan.commands) or \ + command != self._plan.commands[self._command_index]: + raise FakeTransportError("unexpected synthetic command") + self._command_index += 1 + self.events.append(f"COMMAND_{command.upper()}") + + def receive_chunk(self) -> bytes | None: + if not self._opened_once or self._closed: + raise FakeTransportError("fake transport is not open") + if not self._inbound: + return None + self.events.append("RECEIVE") + return self._inbound.pop(0) + + def close_once(self) -> None: + if not self._opened_once or self._closed: + raise FakeTransportError("fake transport cannot close") + self._closed = True + self.events.append("CLOSE") diff --git a/tests/test_adapters.c b/tests/test_adapters.c new file mode 100644 index 0000000..2f161e5 --- /dev/null +++ b/tests/test_adapters.c @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +#include +#include + +#include + +static int failures; + +#define CHECK(expression) \ + do { \ + if (!(expression)) { \ + (void)fprintf(stderr, "%s:%d: check failed: %s\n", __FILE__, \ + __LINE__, #expression); \ + failures += 1; \ + } \ + } while (0) + +int main(void) { + chimera_gfx_config config = CHIMERA_GFX_CONFIG_INIT; + chimera_gfx_context *context = NULL; + chimera_gfx_retroarch_adapter_info retroarch = + CHIMERA_GFX_RETROARCH_ADAPTER_INFO_INIT; + chimera_gfx_sdl2_adapter_info sdl2 = CHIMERA_GFX_SDL2_ADAPTER_INFO_INIT; + + CHECK(chimera_gfx_retroarch_query_scaffold(NULL) == + CHIMERA_GFX_STATUS_INVALID_ARGUMENT); + retroarch.struct_size = sizeof(retroarch) - 1u; + CHECK(chimera_gfx_retroarch_query_scaffold(&retroarch) == + CHIMERA_GFX_STATUS_INVALID_ARGUMENT); + retroarch = (chimera_gfx_retroarch_adapter_info) + CHIMERA_GFX_RETROARCH_ADAPTER_INFO_INIT; + CHECK(chimera_gfx_retroarch_query_scaffold(&retroarch) == + CHIMERA_GFX_STATUS_OK); + CHECK(retroarch.supports_rgui == 1u); + CHECK(retroarch.supports_hardware_contexts == 0u); + CHECK(retroarch.accepts_software_frames == 0u); + CHECK(chimera_gfx_sdl2_query_scaffold(NULL) == + CHIMERA_GFX_STATUS_INVALID_ARGUMENT); + sdl2.struct_size = sizeof(sdl2) - 1u; + CHECK(chimera_gfx_sdl2_query_scaffold(&sdl2) == + CHIMERA_GFX_STATUS_INVALID_ARGUMENT); + sdl2 = (chimera_gfx_sdl2_adapter_info)CHIMERA_GFX_SDL2_ADAPTER_INFO_INIT; + CHECK(chimera_gfx_sdl2_query_scaffold(&sdl2) == CHIMERA_GFX_STATUS_OK); + CHECK(sdl2.accelerated_renderer_available == 0u); + CHECK(sdl2.software_fallback_required == 1u); + + CHECK(chimera_gfx_create(&config, &context) == CHIMERA_GFX_STATUS_OK); + CHECK(chimera_gfx_retroarch_bind_scaffold(NULL, 0u) == + CHIMERA_GFX_STATUS_INVALID_ARGUMENT); + CHECK(chimera_gfx_retroarch_bind_scaffold(context, 0u) == + CHIMERA_GFX_STATUS_UNSUPPORTED); + CHECK(chimera_gfx_retroarch_bind_scaffold(context, 1u) == + CHIMERA_GFX_STATUS_SAFETY_POLICY); + CHECK(chimera_gfx_sdl2_create_renderer_scaffold(NULL, 0u) == + CHIMERA_GFX_STATUS_INVALID_ARGUMENT); + CHECK(chimera_gfx_sdl2_create_renderer_scaffold(context, 0u) == + CHIMERA_GFX_STATUS_UNSUPPORTED); + CHECK(chimera_gfx_sdl2_create_renderer_scaffold(context, 1u) == + CHIMERA_GFX_STATUS_SAFETY_POLICY); + CHECK(chimera_gfx_context_destroy(context) == CHIMERA_GFX_STATUS_OK); + return failures == 0 ? 0 : 1; +} diff --git a/tests/test_artifact_audit.py b/tests/test_artifact_audit.py new file mode 100644 index 0000000..3370b26 --- /dev/null +++ b/tests/test_artifact_audit.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Unit test for strict PS5 ELF import parsing.""" + +from __future__ import annotations + +import argparse +import importlib.util +from pathlib import Path + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + tool_path = args.root.resolve() / "tools/audit_ps5_artifacts.py" + specification = importlib.util.spec_from_file_location("artifact_audit", tool_path) + if specification is None or specification.loader is None: + raise RuntimeError("could not load artifact-audit module") + module = importlib.util.module_from_spec(specification) + specification.loader.exec_module(module) + + fixture = """ + U fprintf + U sceKernelCreateEqueue + U sceVideoOutOpen +0000000000001234 T sceVideoOutClose +""" + imports = module.extract_sce_imports(fixture) + if imports != {"sceKernelCreateEqueue", "sceVideoOutOpen"}: + raise RuntimeError(f"unexpected parsed imports: {imports}") + if len(module.EXPECTED_PHASE1_SCE_IMPORTS) != 15: + raise RuntimeError("reviewed Phase-1 import inventory changed") + if any(name.startswith("sceGnm") for name in module.EXPECTED_PHASE1_SCE_IMPORTS): + raise RuntimeError("reviewed Phase-1 import inventory contains GNM") + all_imports = module.extract_undefined_imports(fixture) + if all_imports != {"fprintf", "sceKernelCreateEqueue", "sceVideoOutOpen"}: + raise RuntimeError(f"unexpected full import inventory: {all_imports}") + if module.EXPECTED_PROBE_UNDEFINED_IMPORTS != { + "__stderrp", + "__stdoutp", + "fprintf", + "fwrite", + "snprintf", + "strcmp", + }: + raise RuntimeError("reviewed probe import inventory changed") + dynamic_fixture = """ + 0x0000000000000001 (NEEDED) Shared library: [libkernel_web.sprx] + 0x0000000000000001 (NEEDED) Shared library: [libSceLibcInternal.sprx] + 0x0000000000000001 (NEEDED) Shared library: [libSceNet.sprx] +""" + if module.extract_needed(dynamic_fixture) != module.EXPECTED_PROBE_NEEDED: + raise RuntimeError("reviewed probe DT_NEEDED inventory changed") + print("PS5 artifact-audit parser passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_core.c b/tests/test_core.c new file mode 100644 index 0000000..40c5f39 --- /dev/null +++ b/tests/test_core.c @@ -0,0 +1,108 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +#include + +#include +#include + +static int failures; + +#define CHECK(expression) \ + do { \ + if (!(expression)) { \ + (void)fprintf(stderr, "%s:%d: check failed: %s\n", __FILE__, \ + __LINE__, #expression); \ + failures += 1; \ + } \ + } while (0) + +static void test_valid_mock_context(void) { + chimera_gfx_config config = CHIMERA_GFX_CONFIG_INIT; + chimera_gfx_context *context = NULL; + chimera_gfx_capabilities capabilities = CHIMERA_GFX_CAPABILITIES_INIT; + + CHECK(chimera_gfx_create(&config, &context) == CHIMERA_GFX_STATUS_OK); + CHECK(context != NULL); + CHECK(chimera_gfx_get_capabilities(context, &capabilities) == + CHIMERA_GFX_STATUS_OK); + CHECK(capabilities.api_version == CHIMERA_GFX_API_VERSION); + CHECK(capabilities.backend == CHIMERA_GFX_BACKEND_MOCK); + CHECK((capabilities.flags & CHIMERA_GFX_CAPABILITY_BACKEND_AVAILABLE) != + 0u); + CHECK((capabilities.flags & CHIMERA_GFX_CAPABILITY_NON_RENDERING) != 0u); + CHECK((capabilities.flags & CHIMERA_GFX_CAPABILITY_HOST_TEST_ONLY) != 0u); + CHECK((capabilities.flags & CHIMERA_GFX_CAPABILITY_PRESENT_MODEL) != 0u); + CHECK(capabilities.max_live_surfaces == 16u); + CHECK(capabilities.max_live_textures == 16u); + CHECK(chimera_gfx_context_destroy(context) == CHIMERA_GFX_STATUS_OK); + chimera_gfx_destroy(NULL); +} + +static void test_context_validation(void) { + chimera_gfx_config config = CHIMERA_GFX_CONFIG_INIT; + chimera_gfx_context *context = NULL; + + CHECK(chimera_gfx_create(NULL, &context) == + CHIMERA_GFX_STATUS_INVALID_ARGUMENT); + CHECK(context == NULL); + CHECK(chimera_gfx_create(&config, NULL) == + CHIMERA_GFX_STATUS_INVALID_ARGUMENT); + + config.struct_size = sizeof(config) - 1u; + CHECK(chimera_gfx_create(&config, &context) == + CHIMERA_GFX_STATUS_INVALID_ARGUMENT); + config.struct_size = sizeof(config); + + config.api_version += 1u; + CHECK(chimera_gfx_create(&config, &context) == + CHIMERA_GFX_STATUS_VERSION_MISMATCH); + config.api_version = CHIMERA_GFX_API_VERSION; + + config.flags = CHIMERA_GFX_CONFIG_ALLOW_HARDWARE_RENDERING; + CHECK(chimera_gfx_create(&config, &context) == + CHIMERA_GFX_STATUS_SAFETY_POLICY); + config.flags = 0u; + + config.backend = CHIMERA_GFX_BACKEND_PS5; + CHECK(chimera_gfx_create(&config, &context) == + CHIMERA_GFX_STATUS_SAFETY_POLICY); + CHECK(context == NULL); + config.backend = 99u; + CHECK(chimera_gfx_create(&config, &context) == + CHIMERA_GFX_STATUS_UNSUPPORTED); +} + +static void test_capability_validation(void) { + chimera_gfx_config config = CHIMERA_GFX_CONFIG_INIT; + chimera_gfx_context *context = NULL; + chimera_gfx_capabilities capabilities = CHIMERA_GFX_CAPABILITIES_INIT; + + CHECK(chimera_gfx_create(&config, &context) == CHIMERA_GFX_STATUS_OK); + CHECK(chimera_gfx_get_capabilities(NULL, &capabilities) == + CHIMERA_GFX_STATUS_INVALID_ARGUMENT); + CHECK(chimera_gfx_get_capabilities(context, NULL) == + CHIMERA_GFX_STATUS_INVALID_ARGUMENT); + capabilities.struct_size = sizeof(capabilities) - 1u; + CHECK(chimera_gfx_get_capabilities(context, &capabilities) == + CHIMERA_GFX_STATUS_INVALID_ARGUMENT); + CHECK(chimera_gfx_context_destroy(context) == CHIMERA_GFX_STATUS_OK); +} + +static void test_status_strings(void) { + chimera_gfx_status status; + + for (status = CHIMERA_GFX_STATUS_OK; + status <= CHIMERA_GFX_STATUS_LIMIT_EXCEEDED; + status = (chimera_gfx_status)((int)status + 1)) { + CHECK(strcmp(chimera_gfx_status_string(status), "unknown status") != 0); + } + CHECK(strcmp(chimera_gfx_status_string((chimera_gfx_status)999), + "unknown status") == 0); +} + +int main(void) { + test_valid_mock_context(); + test_context_validation(); + test_capability_validation(); + test_status_strings(); + return failures == 0 ? 0 : 1; +} diff --git a/tests/test_execution_policy.py b/tests/test_execution_policy.py new file mode 100644 index 0000000..0cc54f5 --- /dev/null +++ b/tests/test_execution_policy.py @@ -0,0 +1,327 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Exercise the fail-closed artifact execution policy gate.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +import subprocess +import sys +import tempfile +from pathlib import Path + + +BLOCKED_SHA256 = "4be1c17b4964f2b68c39b5145bc4af4619c32512d60269ecf5c39728b390fa63" + + +def run(command: list[str], expected_decision: str) -> dict[str, object]: + result = subprocess.run(command, check=False, capture_output=True, text=True) + line = result.stdout.strip().splitlines()[-1] + document = json.loads(line) + if document.get("decision") != expected_decision: + raise RuntimeError(f"unexpected decision: {document}") + if expected_decision == "DENY" and result.returncode == 0: + raise RuntimeError("denied input returned success") + if expected_decision != "DENY" and result.returncode != 0: + raise RuntimeError(f"eligible input failed: {result.stderr}") + if document.get("execution_authorized") is not False: + raise RuntimeError("policy gate must never grant execution authority") + return document + + +def without_option(command: list[str], option: str) -> list[str]: + index = command.index(option) + return command[:index] + command[index + 2 :] + + +def write_manifest(path: Path, artifact: Path, eligible: bool) -> str: + source_commit = "1" * 40 + path.write_text( + json.dumps( + { + "artifact": { + "filename": artifact.name, + "id": "execution-policy-test", + "sha256": hashlib.sha256(artifact.read_bytes()).hexdigest(), + "size": artifact.stat().st_size, + "target": "test", + "version": "1", + }, + "execution": { + "authorized": False, + "executed": False, + "execution_eligible": eligible, + "transferred": False, + }, + "schema_version": 1, + "source": { + "commit": source_commit, + "dirty": False, + "repository": "private-gitea-test", + }, + }, + indent=2, + sort_keys=True, + ) + + "\n", + encoding="utf-8", + ) + return source_commit + + +def write_runtime_profile( + path: Path, + artifact: Path, + source_commit: str, + *, + decision: str = "READY_FOR_HARDENED_RUNTIME_DEPLOYMENT", + firmware: str = "9.60", + effect: str = "PAYLOAD_PROCESS_LOCAL", +) -> None: + path.write_text( + json.dumps( + { + "artifact": { + "built": True, + "filename": artifact.name, + "id": "execution-policy-test", + "sha256": hashlib.sha256(artifact.read_bytes()).hexdigest(), + "size": artifact.stat().st_size, + "source_commit": source_commit, + }, + "budgets": { + "automatic_retry": False, + "filesystem_write_budget": "controlled_artifact_directory_only", + "maximum_runtime_ms": 2000, + "payload_network_access": "none", + "persistent_write_budget": "controlled_artifact_removable", + }, + "decision": decision, + "deployment": { + "installed": False, + "ready_for_installation": True, + "rollback_prepared": True, + }, + "effects": [{"classification": effect, "id": "test_effect"}], + "execution_authorized": False, + "expected_volatile_effects": ( + ["test_effect"] + if effect == "EXPECTED_VOLATILE_RUNTIME_EFFECT" + else [] + ), + "execution": { + "authorized": False, + "executed": False, + "execution_eligible": True, + "transferred": False, + }, + "firmware": { + "device_attested": False, + "evidence": "jens_explicitly_confirmed_exact_9.60", + "exact": firmware, + }, + "hard_blockers": [], + "payload_manager": { + "base_commit": "cfbc70f30f419b09bf2b52283f7409e2d3117ee1", + "hardened_commit": "e23d94ff91233aa770e2342800c1467875bdef44", + "installed": False, + "release": "v0.3.1-chimera-controlled-phase07", + "reproducible": True, + "sha256": ( + "8fecf8241a46246eddbd21e8bb4d875f5d76f1f4f4c6a11384df1f131aa5e5b1" + ), + "size": 99560, + }, + "loader": { + "base_commit": "699e8bcff03e91e8d6ca6eba281af25c5a58d8c2", + "hardened_commit": "197623058f509eddde18868dafcb92fdcac66464", + "installed": False, + "release": "v0.23-chimera-phase07", + "reproducible": True, + "sha256": ( + "63e810982471eb40cae3a20aa9df9a0a02892f420e429874fae4e99aa400b561" + ), + "size": 397000, + }, + "profile": "controlled-ps5-runtime", + "schema_version": 1, + "sdk": { + "commit": "d2e2e585740362976a39fdd5ccf390f199a7bc37", + "release": "v0.41", + }, + }, + indent=2, + sort_keys=True, + ) + + "\n", + encoding="utf-8", + ) + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + root = args.root.resolve() + denylist_path = root / "manifests/artifact-denylist.json" + denylist = json.loads(denylist_path.read_text(encoding="utf-8")) + if [entry["sha256"] for entry in denylist["entries"]] != [BLOCKED_SHA256]: + raise RuntimeError("permanent denylist hash changed or is absent") + + with tempfile.TemporaryDirectory() as directory: + temporary = Path(directory) + artifact = temporary / "test.elf" + artifact.write_bytes(b"chimera-execution-policy-test\n") + manifest = temporary / "manifest.json" + runtime_profile = temporary / "runtime-profile.json" + command = [ + sys.executable, + str(root / "tools/check_artifact_execution_policy.py"), + "--manifest", + str(manifest), + "--denylist", + str(denylist_path), + "--artifact", + str(artifact), + "--runtime-profile", + str(runtime_profile), + "--firmware", + "9.60", + ] + + source_commit = write_manifest(manifest, artifact, eligible=False) + write_runtime_profile( + runtime_profile, + artifact, + source_commit, + decision="BLOCKED_VERSION_OR_UNBOUNDED_EFFECT", + ) + denied = run(command, "DENY") + if "MANIFEST_EXECUTION_INELIGIBLE" not in denied["reason_codes"]: + raise RuntimeError("execution-ineligible manifest was not refused") + + source_commit = write_manifest(manifest, artifact, eligible=True) + write_runtime_profile(runtime_profile, artifact, source_commit) + run(command, "PASS_STATIC_DEPLOYMENT_ELIGIBILITY_GATE") + + no_artifact = without_option(command, "--artifact") + denied = run(no_artifact, "DENY") + if "ARTIFACT_BYTES_NOT_SUPPLIED" not in denied["reason_codes"]: + raise RuntimeError("missing artifact bytes did not fail closed") + + no_profile = without_option(command, "--runtime-profile") + denied = run(no_profile, "DENY") + if "CONTROLLED_RUNTIME_PROFILE_REQUIRED" not in denied["reason_codes"]: + raise RuntimeError("missing controlled runtime profile did not fail closed") + + write_runtime_profile( + runtime_profile, + artifact, + source_commit, + effect="UNBOUNDED_OR_UNKNOWN", + ) + denied = run(command, "DENY") + if "RUNTIME_PROFILE_HARD_EFFECT" not in denied["reason_codes"]: + raise RuntimeError("unbounded runtime effect did not fail closed") + + write_runtime_profile(runtime_profile, artifact, source_commit) + document = json.loads(runtime_profile.read_text(encoding="utf-8")) + document["expected_volatile_effects"] = ["not_the_classified_effect"] + runtime_profile.write_text(json.dumps(document), encoding="utf-8") + denied = run(command, "DENY") + if ( + "RUNTIME_PROFILE_VOLATILE_EFFECTS_MISMATCH" + not in denied["reason_codes"] + ): + raise RuntimeError("volatile-effect mismatch did not fail closed") + + write_runtime_profile(runtime_profile, artifact, source_commit) + document = json.loads(runtime_profile.read_text(encoding="utf-8")) + del document["expected_volatile_effects"] + runtime_profile.write_text(json.dumps(document), encoding="utf-8") + denied = run(command, "DENY") + if denied["reason_codes"] != ["INVALID_OR_INCOMPLETE_POLICY_INPUT"]: + raise RuntimeError("missing volatile-effect declaration did not fail closed") + + write_runtime_profile(runtime_profile, artifact, source_commit) + document = json.loads(runtime_profile.read_text(encoding="utf-8")) + document["execution_authorized"] = True + runtime_profile.write_text(json.dumps(document), encoding="utf-8") + denied = run(command, "DENY") + if ( + "RUNTIME_PROFILE_EXECUTION_STATE_INVALID" + not in denied["reason_codes"] + ): + raise RuntimeError("runtime authorization widening did not fail closed") + + write_runtime_profile(runtime_profile, artifact, source_commit) + document = json.loads(runtime_profile.read_text(encoding="utf-8")) + document["payload_manager"]["hardened_commit"] = "0" * 40 + runtime_profile.write_text(json.dumps(document), encoding="utf-8") + denied = run(command, "DENY") + if "PAYLOAD_MANAGER_IDENTITY_MISMATCH" not in denied["reason_codes"]: + raise RuntimeError("Payload Manager identity mismatch did not fail closed") + + write_runtime_profile( + runtime_profile, + artifact, + source_commit, + effect="NOT_A_CLASSIFICATION", + ) + denied = run(command, "DENY") + if denied["reason_codes"] != ["INVALID_OR_INCOMPLETE_POLICY_INPUT"]: + raise RuntimeError("unknown effect classification did not fail closed") + + write_runtime_profile(runtime_profile, artifact, source_commit) + document = json.loads(runtime_profile.read_text(encoding="utf-8")) + document["profile"] = "controlled-ps5-lifecycle-v1" + runtime_profile.write_text(json.dumps(document), encoding="utf-8") + denied = run(command, "DENY") + if denied["reason_codes"] != ["INVALID_OR_INCOMPLETE_POLICY_INPUT"]: + raise RuntimeError("wrong runtime profile name did not fail closed") + + write_runtime_profile(runtime_profile, artifact, source_commit) + document = json.loads(manifest.read_text(encoding="utf-8")) + document["execution"]["transferred"] = True + manifest.write_text(json.dumps(document), encoding="utf-8") + denied = run(command, "DENY") + if "MANIFEST_EXECUTION_STATE_INVALID" not in denied["reason_codes"]: + raise RuntimeError("manifest transfer claim did not fail closed") + + source_commit = write_manifest(manifest, artifact, eligible=True) + write_runtime_profile(runtime_profile, artifact, source_commit) + mismatched_firmware = command.copy() + mismatched_firmware[-1] = "9.40" + denied = run(mismatched_firmware, "DENY") + if "FIRMWARE_MISMATCH" not in denied["reason_codes"]: + raise RuntimeError("firmware mismatch did not fail closed") + + document = json.loads(manifest.read_text(encoding="utf-8")) + document["artifact"]["sha256"] = BLOCKED_SHA256 + manifest.write_text(json.dumps(document), encoding="utf-8") + denied = run(no_artifact, "DENY") + if "ARTIFACT_PERMANENTLY_DENYLISTED" not in denied["reason_codes"]: + raise RuntimeError("denylisted hash was not refused") + + del document["execution"]["execution_eligible"] + manifest.write_text(json.dumps(document), encoding="utf-8") + denied = run(no_artifact, "DENY") + if denied["reason_codes"] != ["INVALID_OR_INCOMPLETE_POLICY_INPUT"]: + raise RuntimeError("missing eligibility did not fail closed") + + artifact.write_bytes(b"changed\n") + source_commit = write_manifest(manifest, artifact, eligible=True) + write_runtime_profile(runtime_profile, artifact, source_commit) + artifact.write_bytes(b"changed-again\n") + denied = run(command, "DENY") + if "ARTIFACT_DIGEST_MISMATCH" not in denied["reason_codes"]: + raise RuntimeError("changed bytes were not refused") + + print("artifact execution policy gate passed all refusal tests") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_firmware_gate.c b/tests/test_firmware_gate.c new file mode 100644 index 0000000..c5e2161 --- /dev/null +++ b/tests/test_firmware_gate.c @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +#include "firmware_gate.h" + +#include + +static int failures; + +#define CHECK(expression) \ + do { \ + if (!(expression)) { \ + (void)fprintf(stderr, "%s:%d: check failed: %s\n", __FILE__, \ + __LINE__, #expression); \ + failures += 1; \ + } \ + } while (0) + +int main(void) { + CHECK(chimera_gfx_firmware_gate_allows("NONE", "NONE") == 0); + CHECK(chimera_gfx_firmware_gate_allows("NONE", "13.40") == 0); + CHECK(chimera_gfx_firmware_gate_allows("13.40", "13.40") == 1); + CHECK(chimera_gfx_firmware_gate_allows("13.40", "13.4") == 0); + CHECK(chimera_gfx_firmware_gate_allows("", "") == 0); + CHECK(chimera_gfx_firmware_gate_allows("13.40", "") == 0); + CHECK(chimera_gfx_firmware_gate_allows(NULL, "13.40") == 0); + CHECK(chimera_gfx_firmware_gate_allows("13.40", NULL) == 0); + return failures == 0 ? 0 : 1; +} diff --git a/tests/test_manifest_tools.py b/tests/test_manifest_tools.py new file mode 100644 index 0000000..6c36ff0 --- /dev/null +++ b/tests/test_manifest_tools.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Integration test for deterministic artifact generation and verification.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +import subprocess +import sys +import tempfile +from pathlib import Path + + +def expect_verification_failure(command: list[str], description: str) -> None: + result = subprocess.run(command, check=False, capture_output=True, text=True) + if result.returncode == 0: + raise RuntimeError(f"verification unexpectedly accepted {description}") + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + root = args.root.resolve() + + with tempfile.TemporaryDirectory() as directory: + temporary = Path(directory) + artifact = temporary / "test.elf" + manifest = temporary / "test.json" + payload = b"chimera-gfx-test-artifact\n" + artifact.write_bytes(payload) + subprocess.run( + [ + sys.executable, + str(root / "tools/generate_artifact_manifest.py"), + "--artifact", + str(artifact), + "--output", + str(manifest), + "--id", + "manifest-tool-test", + "--version", + "1", + "--source-repository", + "test://chimera-gfx", + "--source-commit", + "0" * 40, + "--target", + "test", + "--profile", + "unit-test", + ], + check=True, + ) + document = json.loads(manifest.read_text(encoding="utf-8")) + if document["artifact"]["sha256"] != hashlib.sha256(payload).hexdigest(): + raise RuntimeError("generated digest differs from expected digest") + if document["execution"].get("execution_eligible") is not False: + raise RuntimeError("new artifacts must default to execution-ineligible") + verify_command = [ + sys.executable, + str(root / "tools/verify_artifact_manifest.py"), + "--manifest", + str(manifest), + "--artifact", + str(artifact), + ] + subprocess.run(verify_command, check=True) + + artifact.write_bytes(payload + b"changed") + expect_verification_failure(verify_command, "changed artifact bytes") + artifact.write_bytes(payload) + + document["execution"]["authorized"] = True + manifest.write_text( + json.dumps(document, indent=2, sort_keys=True) + "\n", encoding="utf-8" + ) + expect_verification_failure(verify_command, "execution authority") + + document["execution"]["authorized"] = False + del document["execution"]["execution_eligible"] + manifest.write_text( + json.dumps(document, indent=2, sort_keys=True) + "\n", encoding="utf-8" + ) + expect_verification_failure(verify_command, "missing execution eligibility") + print("artifact manifest generation and verification passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_minimal_startup_block.py b/tests/test_minimal_startup_block.py new file mode 100644 index 0000000..9b0c9a9 --- /dev/null +++ b/tests/test_minimal_startup_block.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Prove that an unreviewed minimal PS5 startup cannot enter the build graph.""" + +from __future__ import annotations + +import argparse +import subprocess +import tempfile +from pathlib import Path + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + parser.add_argument("--cmake", type=Path, required=True) + parser.add_argument("--compiler", type=Path, required=True) + args = parser.parse_args() + + with tempfile.TemporaryDirectory() as directory: + result = subprocess.run( + [ + str(args.cmake), + "-S", + str(args.root.resolve()), + "-B", + directory, + "-G", + "Ninja", + f"-DCMAKE_C_COMPILER={args.compiler}", + "-DBUILD_TESTING=OFF", + "-DCHIMERA_GFX_BUILD_PS5_MINIMAL_STARTUP=ON", + ], + check=False, + capture_output=True, + text=True, + ) + output = result.stdout + result.stderr + if result.returncode == 0: + raise RuntimeError("minimal startup configure unexpectedly succeeded") + required = ( + "BLOCKED", + "no pinned public loader caller", + "no minimal PS5 ELF may be built", + ) + if any(snippet not in output for snippet in required): + raise RuntimeError(f"configure failed without the policy reason:\n{output}") + print("minimal PS5 startup build remains fail-closed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_mock_integration.c b/tests/test_mock_integration.c new file mode 100644 index 0000000..c551e71 --- /dev/null +++ b/tests/test_mock_integration.c @@ -0,0 +1,169 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +#include + +#include +#include + +static int failures; + +#define CHECK(expression) \ + do { \ + if (!(expression)) { \ + (void)fprintf(stderr, "%s:%d: check failed: %s\n", __FILE__, \ + __LINE__, #expression); \ + failures += 1; \ + } \ + } while (0) + +static void test_complete_lifecycle(void) { + static const uint32_t pixels[4] = { + UINT32_C(0xff0000ff), UINT32_C(0xff00ff00), UINT32_C(0xffff0000), + UINT32_C(0xffffffff)}; + chimera_gfx_config config = CHIMERA_GFX_CONFIG_INIT; + chimera_gfx_context *context = NULL; + chimera_gfx_surface *surface = NULL; + chimera_gfx_texture *texture = NULL; + chimera_gfx_surface_desc surface_desc = + CHIMERA_GFX_SURFACE_DESC_INIT(2u, 2u); + chimera_gfx_texture_desc texture_desc = + CHIMERA_GFX_TEXTURE_DESC_INIT(2u, 2u); + chimera_gfx_texture_upload_info upload = CHIMERA_GFX_TEXTURE_UPLOAD_INIT( + pixels, 2u * sizeof(uint32_t), sizeof(pixels)); + chimera_gfx_present_info present_info; + chimera_gfx_surface_stats stats = CHIMERA_GFX_SURFACE_STATS_INIT; + + CHECK(chimera_gfx_create(&config, &context) == CHIMERA_GFX_STATUS_OK); + CHECK(chimera_gfx_surface_create(context, &surface_desc, &surface) == + CHIMERA_GFX_STATUS_OK); + CHECK(chimera_gfx_texture_create(context, &texture_desc, &texture) == + CHIMERA_GFX_STATUS_OK); + present_info = (chimera_gfx_present_info)CHIMERA_GFX_PRESENT_INFO_INIT( + surface, texture); + CHECK(chimera_gfx_present(context, &present_info) == + CHIMERA_GFX_STATUS_INVALID_STATE); + CHECK(chimera_gfx_texture_upload(texture, &upload) == + CHIMERA_GFX_STATUS_OK); + CHECK(chimera_gfx_present(context, &present_info) == CHIMERA_GFX_STATUS_OK); + CHECK(chimera_gfx_present(context, &present_info) == CHIMERA_GFX_STATUS_OK); + CHECK(chimera_gfx_surface_get_stats(surface, &stats) == + CHIMERA_GFX_STATUS_OK); + CHECK(stats.api_version == CHIMERA_GFX_API_VERSION); + CHECK(stats.present_count == 2u); + CHECK(stats.last_present_serial == 2u); + CHECK(stats.last_texture_hash != 0u); + CHECK(chimera_gfx_context_destroy(context) == + CHIMERA_GFX_STATUS_RESOURCE_BUSY); + CHECK(chimera_gfx_texture_destroy(texture) == CHIMERA_GFX_STATUS_OK); + CHECK(chimera_gfx_surface_destroy(surface) == CHIMERA_GFX_STATUS_OK); + CHECK(chimera_gfx_texture_destroy(NULL) == CHIMERA_GFX_STATUS_OK); + CHECK(chimera_gfx_surface_destroy(NULL) == CHIMERA_GFX_STATUS_OK); + CHECK(chimera_gfx_context_destroy(context) == CHIMERA_GFX_STATUS_OK); +} + +static void test_resource_validation(void) { + static const uint32_t pixels[4] = {0u, 0u, 0u, 0u}; + chimera_gfx_config config = CHIMERA_GFX_CONFIG_INIT; + chimera_gfx_context *context = NULL; + chimera_gfx_surface *surface = NULL; + chimera_gfx_texture *texture = NULL; + chimera_gfx_texture *invalid_texture = NULL; + chimera_gfx_surface_desc surface_desc = + CHIMERA_GFX_SURFACE_DESC_INIT(2u, 2u); + chimera_gfx_texture_desc texture_desc = + CHIMERA_GFX_TEXTURE_DESC_INIT(2u, 2u); + chimera_gfx_texture_upload_info upload = CHIMERA_GFX_TEXTURE_UPLOAD_INIT( + pixels, sizeof(uint32_t), sizeof(pixels)); + + CHECK(chimera_gfx_create(&config, &context) == CHIMERA_GFX_STATUS_OK); + CHECK(chimera_gfx_surface_create(context, &surface_desc, &surface) == + CHIMERA_GFX_STATUS_OK); + CHECK(chimera_gfx_texture_create(context, &texture_desc, &texture) == + CHIMERA_GFX_STATUS_OK); + CHECK(chimera_gfx_texture_upload(texture, &upload) == + CHIMERA_GFX_STATUS_INVALID_ARGUMENT); + texture_desc.api_version += 1u; + CHECK( + chimera_gfx_texture_create(context, &texture_desc, &invalid_texture) == + CHIMERA_GFX_STATUS_VERSION_MISMATCH); + CHECK(invalid_texture == NULL); + CHECK(chimera_gfx_surface_destroy(surface) == CHIMERA_GFX_STATUS_OK); + CHECK(chimera_gfx_texture_destroy(texture) == CHIMERA_GFX_STATUS_OK); + CHECK(chimera_gfx_context_destroy(context) == CHIMERA_GFX_STATUS_OK); +} + +static void test_ownership_dimensions_and_limits(void) { + static const uint32_t pixels[2] = {0u, 0u}; + chimera_gfx_config config = CHIMERA_GFX_CONFIG_INIT; + chimera_gfx_context *first_context = NULL; + chimera_gfx_context *second_context = NULL; + chimera_gfx_surface *surface = NULL; + chimera_gfx_surface *surfaces[17] = {NULL}; + chimera_gfx_texture *texture = NULL; + chimera_gfx_surface_desc surface_desc = + CHIMERA_GFX_SURFACE_DESC_INIT(2u, 2u); + chimera_gfx_texture_desc texture_desc = + CHIMERA_GFX_TEXTURE_DESC_INIT(1u, 2u); + chimera_gfx_texture_upload_info upload = CHIMERA_GFX_TEXTURE_UPLOAD_INIT( + pixels, sizeof(uint32_t), sizeof(pixels)); + chimera_gfx_present_info present_info; + chimera_gfx_surface_stats stats = CHIMERA_GFX_SURFACE_STATS_INIT; + size_t index; + + CHECK(chimera_gfx_create(&config, &first_context) == CHIMERA_GFX_STATUS_OK); + CHECK(chimera_gfx_create(&config, &second_context) == + CHIMERA_GFX_STATUS_OK); + CHECK(chimera_gfx_surface_create(first_context, &surface_desc, &surface) == + CHIMERA_GFX_STATUS_OK); + CHECK(chimera_gfx_texture_create(second_context, &texture_desc, &texture) == + CHIMERA_GFX_STATUS_OK); + CHECK(chimera_gfx_texture_upload(texture, &upload) == + CHIMERA_GFX_STATUS_OK); + present_info = (chimera_gfx_present_info)CHIMERA_GFX_PRESENT_INFO_INIT( + surface, texture); + CHECK(chimera_gfx_present(first_context, &present_info) == + CHIMERA_GFX_STATUS_INVALID_ARGUMENT); + CHECK(chimera_gfx_texture_destroy(texture) == CHIMERA_GFX_STATUS_OK); + CHECK(chimera_gfx_context_destroy(second_context) == CHIMERA_GFX_STATUS_OK); + texture = NULL; + CHECK(chimera_gfx_texture_create(first_context, &texture_desc, &texture) == + CHIMERA_GFX_STATUS_OK); + CHECK(chimera_gfx_texture_upload(texture, &upload) == + CHIMERA_GFX_STATUS_OK); + present_info = (chimera_gfx_present_info)CHIMERA_GFX_PRESENT_INFO_INIT( + surface, texture); + CHECK(chimera_gfx_present(first_context, &present_info) == + CHIMERA_GFX_STATUS_INVALID_ARGUMENT); + CHECK(chimera_gfx_texture_destroy(texture) == CHIMERA_GFX_STATUS_OK); + stats.struct_size = sizeof(stats) - 1u; + CHECK(chimera_gfx_surface_get_stats(surface, &stats) == + CHIMERA_GFX_STATUS_INVALID_ARGUMENT); + CHECK(chimera_gfx_surface_destroy(surface) == CHIMERA_GFX_STATUS_OK); + + surface_desc = + (chimera_gfx_surface_desc)CHIMERA_GFX_SURFACE_DESC_INIT(1u, 1u); + for (index = 0u; index < 16u; ++index) { + CHECK(chimera_gfx_surface_create(first_context, &surface_desc, + &surfaces[index]) == + CHIMERA_GFX_STATUS_OK); + } + CHECK(chimera_gfx_surface_create(first_context, &surface_desc, + &surfaces[16]) == + CHIMERA_GFX_STATUS_LIMIT_EXCEEDED); + CHECK(surfaces[16] == NULL); + for (index = 0u; index < 16u; ++index) { + CHECK(chimera_gfx_surface_destroy(surfaces[index]) == + CHIMERA_GFX_STATUS_OK); + } + surface_desc.width = 4097u; + CHECK(chimera_gfx_surface_create(first_context, &surface_desc, &surface) == + CHIMERA_GFX_STATUS_LIMIT_EXCEEDED); + CHECK(surface == NULL); + CHECK(chimera_gfx_context_destroy(first_context) == CHIMERA_GFX_STATUS_OK); +} + +int main(void) { + test_complete_lifecycle(); + test_resource_validation(); + test_ownership_dimensions_and_limits(); + return failures == 0 ? 0 : 1; +} diff --git a/tests/test_phase05_audit.py b/tests/test_phase05_audit.py new file mode 100644 index 0000000..282ff8d --- /dev/null +++ b/tests/test_phase05_audit.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate the fail-closed Phase-0.5 machine-readable decision records.""" + +from __future__ import annotations + +import argparse +import json +from pathlib import Path + + +BLOCKED_SHA256 = "4be1c17b4964f2b68c39b5145bc4af4619c32512d60269ecf5c39728b390fa63" + + +def load(path: Path) -> dict[str, object]: + document = json.loads(path.read_text(encoding="utf-8")) + if not isinstance(document, dict): + raise RuntimeError(f"{path}: expected object") + return document + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + root = args.root.resolve() + + audit = load(root / "manifests/runtime/phase-0.5-startup-audit.json") + decision = load(root / "manifests/runtime/minimal-startup-artifact-decision.json") + matrix = load(root / "manifests/runtime/kernelwrite-proof-matrix.json") + denylist = load(root / "manifests/artifact-denylist.json") + + if audit.get("decision") != "BLOCKED" or audit["artifact"]["built"] is not False: + raise RuntimeError("startup audit no longer blocks artifact construction") + if audit["artifact"]["execution_eligible"] is not False: + raise RuntimeError("startup audit claims execution eligibility") + if audit["loader_evidence"]["caller_source_present"] is not False: + raise RuntimeError("audit claims an unreviewed loader caller") + required_reachable = { + "__patch_init", + "kernel_copyin", + "kernel_copyout", + "kernel_set_ucred_attrs", + "kernel_set_ucred_caps", + } + reachable = set(audit["crt1_static_evidence"]["reachable_prohibited_functions"]) + if reachable != required_reachable: + raise RuntimeError(f"reachable kernel-write inventory changed: {reachable}") + + if decision.get("decision") != "BLOCKED" or decision["artifact"]["built"] is not False: + raise RuntimeError("non-build decision changed") + if decision["execution"]["execution_eligible"] is not False: + raise RuntimeError("non-build record claims execution eligibility") + if any(value is not None for value in ( + decision["artifact"]["filename"], + decision["artifact"]["sha256"], + decision["artifact"]["size"], + )): + raise RuntimeError("non-build decision fabricates artifact bytes") + + statuses = {entry["status"] for entry in matrix["entries"]} + if statuses != {"SAFE", "UNSAFE", "UNPROVEN"}: + raise RuntimeError(f"review matrix lacks a status class: {statuses}") + if [entry["sha256"] for entry in denylist["entries"]] != [BLOCKED_SHA256]: + raise RuntimeError("permanent blocked hash changed") + print("Phase-0.5 machine-readable BLOCKED decision is consistent") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_phase06_audit.py b/tests/test_phase06_audit.py new file mode 100644 index 0000000..3e903ba --- /dev/null +++ b/tests/test_phase06_audit.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate the fail-closed Phase-0.6 exact-loader evidence.""" + +from __future__ import annotations + +import argparse +import json +from pathlib import Path + + +def load(path: Path) -> dict[str, object]: + document = json.loads(path.read_text(encoding="utf-8")) + if not isinstance(document, dict): + raise RuntimeError(f"{path}: expected an object") + return document + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + root = args.root.resolve() + + audit = load(root / "manifests/runtime/phase-0.6-loader-runtime-audit.json") + denylist = load(root / "manifests/artifact-denylist.json") + + decision = "BLOCKED_VERSION_OR_UNBOUNDED_EFFECT" + if audit.get("decision") != decision: + raise RuntimeError("Phase-0.6 no longer fails closed") + if audit["artifact"]["built"] is not False: + raise RuntimeError("Phase-0.6 unexpectedly produced an ELF") + if audit["identity"]["elfldr"]["installed_asset_hash_match"] is not True: + raise RuntimeError("installed elfldr exact identity is no longer proven") + if audit["identity"]["payload_manager"]["installed_asset_hash_match"] is not True: + raise RuntimeError("installed Payload Manager exact identity is no longer proven") + if audit["identity"]["exact_exploit_autoloader"]["identified"] is not False: + raise RuntimeError("unproven exploit identity was promoted without evidence") + if any(audit["no_console_actions"].values()): + raise RuntimeError("Phase-0.6 records a forbidden console action") + + effects = {item["id"]: item for item in audit["effects"]} + for required in ( + "ptrace_single_step_completion", + "payload_runtime_limit", + "payload_manager_launch_hash_binding", + "payload_manager_upload", + ): + if required not in effects or effects[required]["blocker"] is not True: + raise RuntimeError(f"hard blocker disappeared: {required}") + if effects["payload_manager_upload"]["classification"] != "PERSISTENT_WRITE": + raise RuntimeError("Payload Manager upload write was reclassified") + if ( + effects["ptrace_single_step_completion"]["classification"] + != "UNBOUNDED_OR_UNKNOWN" + ): + raise RuntimeError("elfldr single-step loop was reclassified") + + blocked_hash = ( + "4be1c17b4964f2b68c39b5145bc4af4619c32512d60269ecf5c39728b390fa63" + ) + if [entry["sha256"] for entry in denylist["entries"]] != [blocked_hash]: + raise RuntimeError("permanent denylist changed") + + print("Phase-0.6 exact-loader audit remains fail-closed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_phase07_audit.py b/tests/test_phase07_audit.py new file mode 100644 index 0000000..5628b97 --- /dev/null +++ b/tests/test_phase07_audit.py @@ -0,0 +1,160 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate the committed Phase-0.7 offline deployment evidence.""" + +from __future__ import annotations + +import argparse +import json +from pathlib import Path + + +def load(path: Path) -> dict[str, object]: + document = json.loads(path.read_text(encoding="utf-8")) + if not isinstance(document, dict): + raise RuntimeError(f"{path}: expected an object") + return document + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + root = args.root.resolve() + audit = load(root / "manifests/runtime/phase-0.7-offline-audit.json") + profile = load(root / "manifests/runtime/controlled-ps5-runtime-profile.json") + manifest = load( + root + / "manifests/artifacts/chimera-gfx-lifecycle-probe-phase07-fw-9.60.json" + ) + loader_manifest = load( + root / "manifests/artifacts/chimera-elfldr-phase07-fw-9.60.json" + ) + manager_manifest = load( + root + / "manifests/artifacts/chimera-payload-manager-phase07-fw-9.60.json" + ) + proof = load( + root / "manifests/runtime/phase-0.7-kernelwrite-proof-matrix.json" + ) + denylist = load(root / "manifests/artifact-denylist.json") + + decision = "READY_FOR_HARDENED_RUNTIME_DEPLOYMENT" + if audit.get("decision") != decision or profile.get("decision") != decision: + raise RuntimeError("Phase-0.7 deployment decision is inconsistent") + if proof.get("decision") != decision: + raise RuntimeError("Phase-0.7 proof-matrix decision is inconsistent") + if proof.get("kernelwrite_free_claim") is not False: + raise RuntimeError("Phase-0.7 incorrectly claims kernelwrite-free startup") + if any(audit["ps5_actions"].values()): + raise RuntimeError("Phase-0.7 records a forbidden PS5 action") + if audit["firmware"] != "9.60": + raise RuntimeError("Phase-0.7 firmware gate changed") + + lifecycle = audit["artifacts"]["lifecycle"] + if lifecycle["imports"] != ["_exit", "sceKernelSendNotificationRequest"]: + raise RuntimeError("lifecycle import inventory changed") + if lifecycle["dt_needed"] != [ + "libSceLibcInternal.sprx", + "libkernel_web.sprx", + ]: + raise RuntimeError("lifecycle DT_NEEDED inventory changed") + if lifecycle["byte_identical_clean_builds"] is not True: + raise RuntimeError("lifecycle reproducibility evidence is absent") + sensitive = lifecycle["sensitive_static_inventory"] + if sensitive["direct_call_reachability_available"] is not True: + raise RuntimeError("lifecycle direct reachability evidence is absent") + kernel_runtime = sensitive["categories"]["kernel_runtime_write"] + for required in ( + "__patch_init", + "kernel_copyin", + "kernel_copyout", + "kernel_set_ucred_attrs", + "kernel_set_ucred_caps", + ): + if required not in kernel_runtime["directly_reachable_from_entrypoint"]: + raise RuntimeError(f"lifecycle hides reachable CRT symbol {required}") + dynamic_loading = sensitive["categories"]["dynamic_loading"]["linked"] + for required in ("sceKernelLoadStartModule", "sceKernelStopUnloadModule"): + if required not in dynamic_loading: + raise RuntimeError(f"lifecycle hides linked rtld symbol {required}") + if sensitive["categories"]["graphics_or_display"]["linked"]: + raise RuntimeError("lifecycle links a graphics/display-sensitive symbol") + if lifecycle["sha256"] != manifest["artifact"]["sha256"]: + raise RuntimeError("lifecycle audit/manifest hash mismatch") + if lifecycle["size"] != manifest["artifact"]["size"]: + raise RuntimeError("lifecycle audit/manifest size mismatch") + loader = audit["artifacts"]["loader"] + if ( + loader["sha256"] != loader_manifest["artifact"]["sha256"] + or loader["size"] != loader_manifest["artifact"]["size"] + ): + raise RuntimeError("loader audit/manifest identity mismatch") + manager = audit["artifacts"]["manager"] + if ( + manager["sha256"] != manager_manifest["artifact"]["sha256"] + or manager["size"] != manager_manifest["artifact"]["size"] + ): + raise RuntimeError("manager audit/manifest identity mismatch") + for reviewed_manifest in (loader_manifest, manager_manifest, manifest): + if reviewed_manifest["execution"]["execution_eligible"] is not True: + raise RuntimeError("Phase-0.7 exact artifact is not statically eligible") + if any( + reviewed_manifest["execution"][key] is not False + for key in ("authorized", "transferred", "executed") + ): + raise RuntimeError("Phase-0.7 manifest claims a forbidden action") + for stripped in (loader, manager): + inventory = stripped["sensitive_static_inventory"] + if inventory["direct_call_reachability_available"] is not False: + raise RuntimeError("stripped binary reachability is overstated") + if inventory["categories"]["graphics_or_display"]["linked"]: + raise RuntimeError("runtime links a graphics/display-sensitive symbol") + if manifest["execution"]["execution_eligible"] is not True: + raise RuntimeError("new lifecycle artifact is not statically eligible") + if any( + manifest["execution"][key] is not False + for key in ("authorized", "transferred", "executed") + ): + raise RuntimeError("lifecycle manifest claims a forbidden action") + + if profile["hard_blockers"]: + raise RuntimeError("Phase-0.7 profile still has hard blockers") + classifications = { + item["classification"] for item in profile["effects"] + } + if {"PERSISTENT_WRITE", "UNBOUNDED_OR_UNKNOWN"} & classifications: + raise RuntimeError("Phase-0.7 profile still has a hard effect") + if profile["firmware"]["evidence"] != "jens_explicitly_confirmed_exact_9.60": + raise RuntimeError("explicit firmware confirmation is absent") + if profile["deployment"] != { + "installed": False, + "ready_for_installation": True, + "rollback_prepared": True, + }: + raise RuntimeError("Phase-0.7 deployment state changed") + + blocked = ( + "4be1c17b4964f2b68c39b5145bc4af4619c32512d60269ecf5c39728b390fa63" + ) + if [entry["sha256"] for entry in denylist["entries"]] != [blocked]: + raise RuntimeError("permanent denylist changed") + proof_status = { + item["component"]: item["status"] for item in proof["reviews"] + } + if proof_status.get("sdk_patch_init") != "UNSAFE": + raise RuntimeError("normal CRT process-local write is hidden") + if proof_status.get("firmware_9_60_runtime_behavior") != "UNPROVEN": + raise RuntimeError("offline audit claims hardware evidence") + if any( + proof["no_ps5_actions"][key] is not False + for key in ("connected", "installed", "transferred", "executed") + ): + raise RuntimeError("proof matrix claims a forbidden PS5 action") + + print("Phase-0.7 offline audit is deployment-ready and execution-unauthorized") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_phase08_preflight.py b/tests/test_phase08_preflight.py new file mode 100644 index 0000000..d452957 --- /dev/null +++ b/tests/test_phase08_preflight.py @@ -0,0 +1,110 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate the fail-closed Phase-0.8 preflight record.""" + +from __future__ import annotations + +import argparse +import json +from pathlib import Path + + +def load(path: Path) -> dict[str, object]: + document = json.loads(path.read_text(encoding="utf-8")) + if not isinstance(document, dict): + raise RuntimeError(f"{path}: expected an object") + return document + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + root = args.root.resolve() + + audit = load(root / "manifests/runtime/phase-0.8-read-only-preflight.json") + profile = load(root / "manifests/runtime/controlled-ps5-runtime-profile.json") + locks = load(root / "manifests/upstreams.lock.json") + + if audit.get("decision") != "READ_ONLY_PREFLIGHT_BLOCKED": + raise RuntimeError("Phase-0.8 no longer fails closed") + if audit.get("scope") != "offline_admissibility_audit_only": + raise RuntimeError("Phase-0.8 scope was widened") + if audit.get("on_device_session_started") is not False: + raise RuntimeError("Phase-0.8 incorrectly claims an on-device session") + if audit.get("dataset_complete") is not False: + raise RuntimeError("Phase-0.8 incorrectly claims a complete dataset") + if audit.get("open_stop_ro") is not True or audit.get("open_stop_gate") is not True: + raise RuntimeError("Phase-0.8 stop state is incomplete") + + authorization = audit["authorization"] + false_authorizations = ( + "explicit_read_only_preflight_permission_recorded", + "connection_authorized", + "installation_authorized", + "lifecycle_authorized", + "execution_authorized", + "automatic_retry", + ) + if any(authorization[key] is not False for key in false_authorizations): + raise RuntimeError("Phase-0.8 records unauthorized authority") + if ( + authorization["permission_reference"] is not None + or authorization["permission_exact_text"] is not None + ): + raise RuntimeError("Phase-0.8 invents a permission record") + + if any(audit["ps5_actions"].values()): + raise RuntimeError("Phase-0.8 records an on-device action") + + collector = audit["collector_assessment"] + if collector["selected_collector"] is not None: + raise RuntimeError("Phase-0.8 selected an inadmissible collector") + if collector["can_prove_no_atime_audit_cache_or_metadata_change"] is not False: + raise RuntimeError("Phase-0.8 overstates collector side-effect proof") + candidates = {item["id"]: item for item in collector["candidates"]} + payload_manager = candidates["payload_manager_v0_3_1_http"] + if payload_manager["usable"] is not False or payload_manager["result"] != "STOP-RO": + raise RuntimeError("stock Payload Manager HTTP was promoted") + + manager_lock = locks["sources"]["ps5_payload_manager"] + if payload_manager["source_commit"] != manager_lock["commit"]: + raise RuntimeError("Payload Manager preflight source is not pinned") + + required_blockers = { + "explicit_permission_record_absent": "STOP-RO", + "collector_side_effect_freedom_unproven": "STOP-RO", + "payload_manager_http_mutates_runtime_state": "STOP-RO", + "two_source_firmware_attestation_absent": "STOP-GATE", + "current_live_identity_and_topology_absent": "STOP-GATE", + "autoload_startup_retry_state_absent": "STOP-GATE", + "stock_elfldr_backup_unproven": "STOP-GATE", + "stock_payload_manager_backup_unproven": "HARD_STOP-GATE", + } + blockers = {item["id"]: item["severity"] for item in audit["blockers"]} + if blockers != required_blockers: + raise RuntimeError("Phase-0.8 blocker set changed") + + if audit["firmware"]["two_current_sources_agree"] != "UNPROVEN": + raise RuntimeError("Phase-0.8 invents a second firmware source") + if ( + audit["rollback_preconditions"]["stock_payload_manager_backup"]["result"] + != "HARD_STOP-GATE" + ): + raise RuntimeError("Payload Manager rollback hard gate disappeared") + if audit["payload_manager_backup_exactly_present"] != "UNPROVEN": + raise RuntimeError("Phase-0.8 invents an exact manager backup") + if audit["historical_evidence_is_not_current_preflight_evidence"] is not True: + raise RuntimeError("historical observations were promoted to current evidence") + + if profile["deployment"]["installed"] is not False: + raise RuntimeError("Phase-0.8 incorrectly marks the hardened runtime installed") + if profile["execution_authorized"] is not False: + raise RuntimeError("Phase-0.8 widened execution authority") + + print("Phase-0.8 read-only preflight remains blocked without PS5 contact") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_phase08_remediation.py b/tests/test_phase08_remediation.py new file mode 100644 index 0000000..7013f26 --- /dev/null +++ b/tests/test_phase08_remediation.py @@ -0,0 +1,193 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Regression guardrails for the offline Phase-0.8R remediation contract.""" + +from __future__ import annotations + +import argparse +import copy +import importlib.util +from pathlib import Path +from types import ModuleType +from typing import Any + + +def load_validator(root: Path) -> ModuleType: + path = root / "tools/validate_phase08_remediation.py" + spec = importlib.util.spec_from_file_location("phase08r_validator", path) + if spec is None or spec.loader is None: + raise RuntimeError("could not load Phase-0.8R validator") + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + + +def require_invalid(errors: list[str], scenario: str) -> None: + if not errors: + raise RuntimeError(f"unsafe mutation passed validation: {scenario}") + + +def validate_manifest_mutation( + validator: ModuleType, + manifest: dict[str, Any], + denylist: dict[str, Any], + scenario: str, +) -> None: + require_invalid(validator.validate_manifest(manifest, denylist), scenario) + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + root = args.root.resolve() + validator = load_validator(root) + + errors = validator.collect_errors(root, require_local_source=False) + if errors: + raise RuntimeError("; ".join(errors)) + + manifest = validator.load_json( + root / "manifests/runtime/phase-0.8-remediation.json" + ) + denylist = validator.load_json(root / "manifests/artifact-denylist.json") + doc_contract = validator.extract_json_contract( + root / "docs/runtime/phase-0.8-remediation.md", "PHASE08R_CONTRACT" + ) + template = validator.extract_json_contract( + root / "docs/approvals/phase-0.8-bounded-observation-template.md", + "PHASE08_BOUNDED_OBSERVATION_TEMPLATE", + ) + + ready = copy.deepcopy(manifest) + ready["status"] = "READY" + validate_manifest_mutation( + validator, ready, denylist, "general READY status overrides blockers" + ) + + authorized = copy.deepcopy(manifest) + authorized["authorization"]["execution_authorized"] = True + validate_manifest_mutation( + validator, authorized, denylist, "execution authorization became true" + ) + + retry = copy.deepcopy(manifest) + retry["authorization"]["automatic_retry"] = True + validate_manifest_mutation( + validator, retry, denylist, "automatic retry became true" + ) + + current_stock = copy.deepcopy(manifest) + current_stock["stock_identities"]["classification"] = "current_device_identity" + current_stock["stock_identities"]["current_device_observed"] = True + validate_manifest_mutation( + validator, current_stock, denylist, "reference-only hashes were promoted" + ) + + backup_ready = copy.deepcopy(manifest) + backup_ready["payload_manager_backup"]["classification"] = "ready" + backup_ready["payload_manager_backup"]["on_device_proven"] = True + backup_ready["payload_manager_backup"]["byte_exact_proven"] = True + backup_ready["payload_manager_backup"]["result"] = "PASS" + validate_manifest_mutation( + validator, backup_ready, denylist, "manager backup hard blocker disappeared" + ) + + missing_deny = copy.deepcopy(denylist) + missing_deny["entries"] = [] + validate_manifest_mutation( + validator, manifest, missing_deny, "permanent denylist entry disappeared" + ) + + non_options_read_only = copy.deepcopy(manifest) + for finding in non_options_read_only["side_effect_findings"]: + if finding["id"] == "get_version": + finding["writes_server_active_flag"] = False + finding["strict_read_only_preflight_suitable"] = True + validate_manifest_mutation( + validator, + non_options_read_only, + denylist, + "non-OPTIONS request was called strict read-only", + ) + + autoload_read_only = copy.deepcopy(manifest) + for finding in autoload_read_only["side_effect_findings"]: + if finding["id"] == "get_autoload_status": + finding["writes_autoload_triggered"] = False + finding["reads_filesystem_or_configuration"] = False + validate_manifest_mutation( + validator, + autoload_read_only, + denylist, + "/autoload_status mutations and reads were hidden", + ) + + options_suitable = copy.deepcopy(manifest) + for finding in options_suitable["side_effect_findings"]: + if finding["id"] == "options_any_endpoint": + finding["strict_read_only_preflight_suitable"] = True + validate_manifest_mutation( + validator, options_suitable, denylist, "OPTIONS was promoted to collector" + ) + + hardware_claim = copy.deepcopy(manifest) + hardware_claim["claim_boundaries"]["hardware_safety_proven"] = True + hardware_claim["firmware_runtime_behavior"] = "PROVEN_SAFE" + validate_manifest_mutation( + validator, hardware_claim, denylist, "host evidence became hardware proof" + ) + + retroarch_active = copy.deepcopy(manifest) + retroarch_active["retroarch"]["active_phase"] = True + retroarch_active["retroarch"]["work_started"] = True + validate_manifest_mutation( + validator, retroarch_active, denylist, "RetroArch became active work" + ) + + template_authorized = copy.deepcopy(template) + template_authorized["authorized"] = True + require_invalid( + validator.validate_template(template_authorized), + "bounded-observation template became authorization", + ) + + template_prefilled = copy.deepcopy(template) + template_prefilled["required_fields"]["collector_sha256"] = "0" * 64 + require_invalid( + validator.validate_template(template_prefilled), + "template invented an artifact hash", + ) + + merged_approvals = copy.deepcopy(manifest) + del merged_approvals["authorization"]["lifecycle_authorized"] + validate_manifest_mutation( + validator, + merged_approvals, + denylist, + "installation and lifecycle authorization were merged", + ) + + doc_without_hard_blocker = copy.deepcopy(doc_contract) + doc_without_hard_blocker["blockers"].remove( + "payload_manager_backup_not_byte_exact_on_device" + ) + require_invalid( + validator.validate_doc_contract(doc_without_hard_blocker, manifest), + "documentation omitted manager backup hard blocker", + ) + + if validator.validate_changed_files(root): + raise RuntimeError("; ".join(validator.validate_changed_files(root))) + + print( + "Phase-0.8R regression guardrails passed: immutable hashes, blocked " + "status, false authorizations, denylist, reference-only stock hashes, " + "hard backup gate, route effects, template denial, phase separation, " + "RetroArch deferral, and no target artifact" + ) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_phase09_transaction.py b/tests/test_phase09_transaction.py new file mode 100644 index 0000000..badac67 --- /dev/null +++ b/tests/test_phase09_transaction.py @@ -0,0 +1,763 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Host-only regression tests for the Phase-0.9A anti-brick design.""" + +from __future__ import annotations + +import argparse +import hashlib +import importlib.util +import json +import sys +from pathlib import Path +from types import ModuleType +from typing import Any, Callable + + +IMMUTABLE = { + "docs/runtime/phase-0.8-read-only-preflight.md": ( + "3fbe086175a6048176075f447ec1482074928e3b5282db97ea2169395fe1d508" + ), + "manifests/runtime/phase-0.8-read-only-preflight.json": ( + "47d7f452f8799979fe99b3e6d56859f03544112725bf7e5b349eba5ed81b3322" + ), + "tests/test_phase08_preflight.py": ( + "8a4ad7c70de28ffe3148fd3fd1f68c36a872c53c691c9068e1ff163970863c48" + ), +} + +DENIED_SHA256 = ( + "4be1c17b4964f2b68c39b5145bc4af4619c32512d60269ecf5c39728b390fa63" +) + +TEMPLATES = { + "docs/approvals/phase-0.9-observation-template.md": ( + "PHASE09_OBSERVATION_TEMPLATE", + "observation", + ), + "docs/approvals/phase-0.9-backup-creation-template.md": ( + "PHASE09_BACKUP_CREATION_TEMPLATE", + "backup_creation", + ), + "docs/approvals/phase-0.9-staging-template.md": ( + "PHASE09_STAGING_TEMPLATE", + "staging", + ), + "docs/approvals/phase-0.9-switch-template.md": ( + "PHASE09_SWITCH_TEMPLATE", + "live_switch", + ), + "docs/approvals/phase-0.9-one-shot-execution-template.md": ( + "PHASE09_ONE_SHOT_EXECUTION_TEMPLATE", + "one_shot_execution", + ), +} + +FALSE_AUTH_FIELDS = ( + "authorized", + "installation_authorized", + "execution_authorized", + "transfer_authorized", + "lifecycle_authorized", + "automatic_retry", +) + + +def sha256(path: Path) -> str: + digest = hashlib.sha256() + with path.open("rb") as stream: + for block in iter(lambda: stream.read(1024 * 1024), b""): + digest.update(block) + return digest.hexdigest() + + +def load_json(path: Path) -> dict[str, Any]: + result = json.loads(path.read_text(encoding="utf-8")) + if not isinstance(result, dict): + raise RuntimeError(f"{path}: expected JSON object") + return result + + +def extract_contract(path: Path, name: str) -> dict[str, Any]: + text = path.read_text(encoding="utf-8") + begin = f"" + end = f"" + if text.count(begin) != 1 or text.count(end) != 1: + raise RuntimeError(f"{path}: expected one {name} contract") + fenced = text.split(begin, 1)[1].split(end, 1)[0].strip() + if not fenced.startswith("```json\n") or not fenced.endswith("\n```"): + raise RuntimeError(f"{path}: malformed JSON fence") + result = json.loads(fenced[len("```json\n") : -len("\n```")]) + if not isinstance(result, dict): + raise RuntimeError(f"{path}: expected object in contract") + return result + + +def load_simulator(root: Path) -> ModuleType: + path = root / "tools/simulate_phase09_transaction.py" + spec = importlib.util.spec_from_file_location("phase09_simulator", path) + if spec is None or spec.loader is None: + raise RuntimeError("could not load Phase-0.9A simulator") + module = importlib.util.module_from_spec(spec) + sys.modules[spec.name] = module + previous = sys.dont_write_bytecode + sys.dont_write_bytecode = True + try: + spec.loader.exec_module(module) + finally: + sys.dont_write_bytecode = previous + return module + + +def require(condition: bool, message: str) -> None: + if not condition: + raise RuntimeError(message) + + +def require_blocked(report: dict[str, Any], scenario: str) -> None: + require( + report["state"] in {"BLOCKED", "ROLLBACK_REQUIRED"}, + f"{scenario}: state did not fail closed: {report['state']}", + ) + require( + report["target_execution_performed"] is False, + f"{scenario}: target execution was claimed", + ) + require( + report["automatic_retry"] is False, + f"{scenario}: automatic retry was enabled", + ) + require( + report["crash_invariant"] + in { + "A_OLD_LIVE_COMPLETE", + "B_NEW_LIVE_COMPLETE_AND_BACKUP_INTACT", + "C_REJECTED_UNSAFE_OR_UNPROVEN", + }, + f"{scenario}: crash invariant missing", + ) + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + root = args.root.resolve() + manifest = load_json( + root / "manifests/runtime/phase-0.9-anti-brick-design.json" + ) + denylist = load_json(root / "manifests/artifact-denylist.json") + simulator = load_simulator(root) + passed: list[str] = [] + + def check(name: str, test: Callable[[], None]) -> None: + test() + passed.append(name) + + def immutable_evidence() -> None: + for relative, expected in IMMUTABLE.items(): + require(sha256(root / relative) == expected, f"immutable drift: {relative}") + + check("immutable Phase-0.8 evidence", immutable_evidence) + + check( + "design-only top-level state", + lambda: require( + manifest["status"] == "DESIGN_ONLY" + and manifest["phase"] == "PHASE_0_9A_OFFLINE_ANTI_BRICK" + and manifest["historical_status"] == "READ_ONLY_PREFLIGHT_BLOCKED", + "design or historical status changed", + ), + ) + + def false_activity_and_authority() -> None: + for field in ( + "hardware_observed", + "ps5_connected", + "device_write_performed", + "files_transferred", + "target_execution_performed", + "target_artifact_created", + "installation_authorized", + "lifecycle_authorized", + "execution_authorized", + "transfer_authorized", + "automatic_retry", + "lifecycle_probe_in_scope", + "graphics_in_scope", + "retroarch_in_scope", + "host_simulation_is_hardware_evidence", + ): + require(manifest.get(field) is False, f"{field} is not false") + + check("all activity and authorization fields false", false_activity_and_authority) + + def nested_authorizations_false() -> None: + controlled = { + "authorized", + "installation_authorized", + "execution_authorized", + "transfer_authorized", + "lifecycle_authorized", + "automatic_retry", + } + + def visit(value: Any, location: str) -> None: + if isinstance(value, dict): + for key, child in value.items(): + if key in controlled: + require(child is False, f"{location}.{key} is not false") + visit(child, f"{location}.{key}") + elif isinstance(value, list): + for index, child in enumerate(value): + visit(child, f"{location}[{index}]") + + visit(manifest, "manifest") + + check("nested authorization and retry fields false", nested_authorizations_false) + + def permanent_denylist() -> None: + entries = denylist["entries"] + require(denylist["fail_closed"] is True, "denylist is not fail closed") + require(len(entries) == 1, "denylist entry count changed") + entry = entries[0] + require(entry["sha256"] == DENIED_SHA256, "denied hash changed") + require(entry["status"] == "BLOCKED", "denied hash not blocked") + require(entry["permanent"] is True, "denied hash not permanent") + require(entry["execution_eligible"] is False, "denied hash eligible") + require( + manifest["permanent_denylist_binding"] + == { + "sha256": DENIED_SHA256, + "status": "BLOCKED", + "permanent": True, + "execution_eligible": False, + }, + "manifest denylist binding changed", + ) + + check("permanent denylist remains exact", permanent_denylist) + + check( + "firmware and stock identities remain unproven", + lambda: require( + manifest["firmware_runtime_behavior"] == "UNPROVEN" + and manifest["stock_identification"] == "reference_only", + "firmware or stock evidence was promoted", + ), + ) + check( + "Payload Manager backup remains hard blocker", + lambda: require( + manifest["payload_manager_backup"] == "HARD_BLOCKER", + "Payload Manager backup hard blocker changed", + ), + ) + + def threat_model_complete() -> None: + items = manifest["threat_model_items"] + require(len(items) == 56, f"expected 56 threat items, got {len(items)}") + ids = {item["id"] for item in items} + require(len(ids) == len(items), "duplicate threat item ID") + require( + {item["profile"] for item in items} + == { + "A_WRONG_TARGET", + "B_WRONG_PREIMAGE", + "C_BACKUP_FAILURE", + "D_WRITE_POWER_LOSS", + "E_PROCESS_LIFECYCLE", + "F_ROLLBACK_FAILURE", + "G_OPERATOR_ERROR", + }, + "threat profiles incomplete", + ) + require( + all( + item["severity"] in {"CATASTROPHIC", "HIGH", "MEDIUM", "LOW"} + and item["reason"] + for item in items + ), + "threat severity or reason missing", + ) + + check("complete classified threat model", threat_model_complete) + + def invariants_complete() -> None: + invariants = manifest["anti_brick_invariants"] + require(len(invariants) == 20, "anti-brick invariant count changed") + require( + [item["id"] for item in invariants] + == [f"AB-{number:03d}" for number in range(1, 21)], + "anti-brick invariant IDs changed", + ) + + check("AB-001 through AB-020 present", invariants_complete) + check( + "transaction state set exact", + lambda: require( + tuple(manifest["transaction_states"]) == simulator.STATES, + "manifest and simulator state sets differ", + ), + ) + + def transition_skip_rejected() -> None: + machine = simulator.StateMachine() + try: + machine.transition("LIVE_OBJECTS_VERIFIED") + except simulator.TransitionError: + return + raise RuntimeError("state machine accepted an approval-gate skip") + + check("authorization states cannot be skipped", transition_skip_rejected) + check( + "no retry, autoload or combined transition", + lambda: require( + { + "automatic_retry", + "autoload", + "combined_component_installation", + "lifecycle_transition", + "graphics_transition", + "retroarch_transition", + }.issubset(set(manifest["forbidden_transitions"])), + "forbidden transition set incomplete", + ), + ) + + def templates_fail_closed() -> None: + for relative, (marker, action) in TEMPLATES.items(): + template = extract_contract(root / relative, marker) + require(template["template_only"] is True, f"{relative}: not template") + require( + template["template_action"] == action, + f"{relative}: wrong action binding", + ) + for field in FALSE_AUTH_FIELDS: + require(template[field] is False, f"{relative}: {field} not false") + required = template["required_fields"] + require(required, f"{relative}: required fields absent") + require( + all(value is None for value in required.values()), + f"{relative}: request data was prefilled", + ) + require( + template["fixed_exclusions"]["automatic_retry"] is True, + f"{relative}: retry exclusion missing", + ) + require( + template["fixed_exclusions"]["autoload"] is True, + f"{relative}: autoload exclusion missing", + ) + require( + template["fixed_exclusions"]["lifecycle_probe"] is True, + f"{relative}: lifecycle exclusion missing", + ) + + check("all five templates are false and unfilled", templates_fail_closed) + + def exact_live_identity_required() -> None: + for fault in ( + "missing_live_path", + "missing_mount_id", + "missing_object_id", + "wrong_size", + "wrong_preimage_hash", + ): + report = simulator.simulate_transaction( + "hardened_elfldr", [fault], prove_virtual_switch_model=True + ) + require_blocked(report, fault) + require(report["virtual_writes"] == [], f"{fault}: write occurred") + + check("no write without exact full live identity", exact_live_identity_required) + + check( + "reference-only stock hash never authorizes write", + lambda: require_blocked( + simulator.simulate_transaction( + "hardened_elfldr", + ["reference_only_preimage"], + prove_virtual_switch_model=True, + ), + "reference_only_preimage", + ), + ) + + def staging_needs_backup() -> None: + for fault in ( + "backup_same_object_as_live", + "short_backup_write", + "backup_hash_mismatch", + "backup_not_reopened", + ): + report = simulator.simulate_transaction( + "controlled_payload_manager", + [fault], + prove_virtual_switch_model=True, + ) + require_blocked(report, fault) + require( + "virtual_candidate_stage" not in report["virtual_writes"], + f"{fault}: staging occurred without verified backup", + ) + + check("no staging without separate reopened backup", staging_needs_backup) + + def separate_approvals() -> None: + for fault in ( + "authorization_missing", + "authorization_wrong_hash", + "authorization_expired", + ): + require_blocked( + simulator.simulate_transaction( + "hardened_elfldr", [fault], prove_virtual_switch_model=True + ), + fault, + ) + + check("missing mismatched or expired approval stops", separate_approvals) + + check( + "no switch while atomicity is unproven", + lambda: require_blocked( + simulator.simulate_transaction("hardened_elfldr"), + "default unproven platform", + ), + ) + check( + "directory durability unknown stops", + lambda: require_blocked( + simulator.simulate_transaction( + "hardened_elfldr", + ["directory_durability_unknown"], + prove_virtual_switch_model=True, + ), + "directory_durability_unknown", + ), + ) + + def synthetic_happy_path_stops_before_execution() -> None: + report = simulator.simulate_transaction( + "hardened_elfldr", prove_virtual_switch_model=True + ) + require( + report["status"] == "DESIGN_MODEL_STOP_BEFORE_EXECUTION", + "synthetic model did not stop before execution", + ) + require( + report["state"] == "MANUAL_EXECUTION_NOT_AUTHORIZED", + "execution approval gate was bypassed", + ) + require( + report["crash_invariant"] + == "B_NEW_LIVE_COMPLETE_AND_BACKUP_INTACT", + "synthetic old/new invariant failed", + ) + require(report["target_execution_performed"] is False, "execution claimed") + + check("post-switch model stops before execution", synthetic_happy_path_stops_before_execution) + + def no_retry_or_autoload() -> None: + for fault in ("retry_active", "autoload_active", "timeout"): + report = simulator.simulate_transaction( + "hardened_elfldr", [fault], prove_virtual_switch_model=True + ) + require_blocked(report, fault) + require(report["automatic_retry"] is False, f"{fault}: retry enabled") + require(report["autoload"] is False, f"{fault}: autoload enabled") + + check("autoload retry and timeout always stop", no_retry_or_autoload) + + check( + "component transactions cannot be combined", + lambda: require( + set(simulator.COMPONENTS) + == {"hardened_elfldr", "controlled_payload_manager"} + and manifest["component_order"]["combined_install_all"] is False, + "combined transaction surface exists", + ), + ) + check( + "second component waits for acceptance or rollback", + lambda: require_blocked( + simulator.simulate_transaction( + "controlled_payload_manager", + ["second_component_before_first_accepted"], + prove_virtual_switch_model=True, + ), + "second_component_before_first_accepted", + ), + ) + check( + "lifecycle probe excluded from candidate set", + lambda: require_blocked( + simulator.simulate_transaction( + "hardened_elfldr", + ["lifecycle_probe_candidate"], + prove_virtual_switch_model=True, + ), + "lifecycle_probe_candidate", + ), + ) + check( + "graphics SDL and RetroArch inactive", + lambda: require( + manifest["graphics_in_scope"] is False + and manifest["retroarch_in_scope"] is False + and manifest["lifecycle_probe_in_scope"] is False, + "later-phase scope became active", + ), + ) + + def power_loss_contract_complete() -> None: + boundaries = manifest["power_loss_boundaries"] + require(len(boundaries) == 14, "power-loss boundary count changed") + require( + [item["id"] for item in boundaries] + == list(simulator.POWER_LOSS_BOUNDARIES), + "power-loss boundary order or identity changed", + ) + require( + all(item["result"] == "UNPROVEN" for item in boundaries), + "a PS5 power-loss boundary was promoted", + ) + require( + any(item["result"] in {"UNSAFE", "UNPROVEN"} for item in boundaries), + "unproven boundaries no longer block", + ) + + check("all power-loss boundaries explicit and blocking", power_loss_contract_complete) + + def virtual_power_loss_invariant() -> None: + for boundary in simulator.POWER_LOSS_BOUNDARIES: + report = simulator.simulate_power_loss_boundary( + "hardened_elfldr", + boundary, + prove_virtual_switch_model=True, + ) + require( + report["crash_invariant"] + in { + "A_OLD_LIVE_COMPLETE", + "B_NEW_LIVE_COMPLETE_AND_BACKUP_INTACT", + }, + f"{boundary}: virtual model lost old/new invariant", + ) + require(report["automatic_start"] is False, f"{boundary}: auto start") + require(report["automatic_retry"] is False, f"{boundary}: retry") + + check("virtual power-loss old-or-new invariant", virtual_power_loss_invariant) + + check( + "in-place overwrite forbidden", + lambda: require_blocked( + simulator.simulate_transaction( + "hardened_elfldr", + ["in_place_overwrite"], + prove_virtual_switch_model=True, + ), + "in_place_overwrite", + ), + ) + check( + "two-step rename gap forbidden", + lambda: require_blocked( + simulator.simulate_transaction( + "hardened_elfldr", + ["two_step_rename_gap"], + prove_virtual_switch_model=True, + ), + "two_step_rename_gap", + ), + ) + + def identity_race_rejected() -> None: + for fault in ("symlink_substitution", "object_swap_after_preflight"): + require_blocked( + simulator.simulate_transaction( + "hardened_elfldr", [fault], prove_virtual_switch_model=True + ), + fault, + ) + + check("symlink and object-swap races rejected", identity_race_rejected) + check( + "active target rejected", + lambda: require_blocked( + simulator.simulate_transaction( + "hardened_elfldr", + ["target_process_active"], + prove_virtual_switch_model=True, + ), + "target_process_active", + ), + ) + check( + "candidate mismatch rejected", + lambda: require_blocked( + simulator.simulate_transaction( + "controlled_payload_manager", + ["candidate_hash_mismatch"], + prove_virtual_switch_model=True, + ), + "candidate_hash_mismatch", + ), + ) + check( + "wrong component mapping rejected", + lambda: require_blocked( + simulator.simulate_transaction( + "controlled_payload_manager", + ["wrong_component_artifact_mapping"], + prove_virtual_switch_model=True, + ), + "wrong_component_artifact_mapping", + ), + ) + check( + "unknown firmware rejected", + lambda: require_blocked( + simulator.simulate_transaction( + "hardened_elfldr", + ["unknown_firmware"], + prove_virtual_switch_model=True, + ), + "unknown_firmware", + ), + ) + check( + "live hash mismatch never promotes backup", + lambda: require( + simulator.simulate_transaction( + "hardened_elfldr", + ["wrong_preimage_hash"], + prove_virtual_switch_model=True, + )["virtual_writes"] + == [], + "mismatched live hash reached backup creation", + ), + ) + check( + "recovery dependency on replaced component rejected", + lambda: require_blocked( + simulator.simulate_transaction( + "controlled_payload_manager", + ["recovery_depends_on_replaced_component"], + prove_virtual_switch_model=True, + ), + "recovery_depends_on_replaced_component", + ), + ) + + def failed_post_switch_goes_to_rollback_required() -> None: + report = simulator.simulate_transaction( + "hardened_elfldr", + ["live_verification_failure"], + prove_virtual_switch_model=True, + ) + require( + report["state"] == "ROLLBACK_REQUIRED", + "failed live verification did not require rollback", + ) + require(report["target_execution_performed"] is False, "execution claimed") + + check("post-switch failure requires rollback", failed_post_switch_goes_to_rollback_required) + + def rollback_mismatch_catastrophic() -> None: + report = simulator.simulate_transaction( + "hardened_elfldr", + ["rollback_hash_mismatch"], + prove_virtual_switch_model=True, + ) + require(report["state"] == "BLOCKED", "rollback mismatch not blocked") + require(report["risk"] == "CATASTROPHIC", "rollback risk not catastrophic") + require( + report["blockers"][0]["code"] == "ROLLBACK_VERIFY_FAILED", + "rollback mismatch blocker changed", + ) + + check("rollback hash mismatch catastrophic blocked", rollback_mismatch_catastrophic) + + def complete_fault_suite() -> None: + for component in simulator.COMPONENTS: + suite = simulator.run_fault_suite(component) + require( + len(suite["fault_results"]) == len(simulator.FAULTS), + f"{component}: fault suite incomplete", + ) + require( + len(suite["power_loss_results"]) + == len(simulator.POWER_LOSS_BOUNDARIES), + f"{component}: power-loss suite incomplete", + ) + require(suite["hardware_evidence"] is False, "hardware proof claimed") + require(suite["device_write_performed"] is False, "device write claimed") + require( + suite["target_execution_performed"] is False, + "target execution claimed", + ) + require(suite["automatic_retry"] is False, "suite retry enabled") + + check("all declared faults injected for both components", complete_fault_suite) + + def simulator_has_no_io_or_target_surface() -> None: + source = ( + root / "tools/simulate_phase09_transaction.py" + ).read_text(encoding="utf-8") + prohibited = ( + "import socket", + "import subprocess", + "import requests", + "urllib", + "ctypes", + "os.system", + ".write_text(", + ".write_bytes(", + "open(", + "9021", + "8084", + "8085", + ) + for token in prohibited: + require(token not in source, f"simulator contains prohibited surface: {token}") + + check("simulator cannot perform filesystem network or target I/O", simulator_has_no_io_or_target_surface) + + def docs_keep_platform_unproven() -> None: + docs = "\n".join( + (root / relative).read_text(encoding="utf-8") + for relative in ( + "docs/runtime/phase-0.9-anti-brick-threat-model.md", + "docs/runtime/phase-0.9-installation-transaction-design.md", + "docs/runtime/phase-0.9-recovery-and-rollback-contract.md", + ) + ) + require( + "BLOCKER: NO PROVEN POWER-LOSS-SAFE SWITCH" in docs, + "power-loss-safe switch blocker missing", + ) + require( + "Host simulation is not hardware evidence" in docs + or "Host simulation is not hardware" in docs, + "host/hardware evidence boundary missing", + ) + for forbidden in ("READY_FOR_INSTALLATION", "DEPLOYMENT_READY"): + require(forbidden not in docs, f"forbidden positive status: {forbidden}") + + check("documentation never promotes host model to PS5 proof", docs_keep_platform_unproven) + + require(len(passed) >= 28, "too few Phase-0.9A guardrail tests") + print( + "Phase-0.9A host-only anti-brick tests passed: " + f"{len(passed)} guardrails, {len(simulator.FAULTS)} fault types x " + f"{len(simulator.COMPONENTS)} components, " + f"{len(simulator.POWER_LOSS_BOUNDARIES)} power-loss boundaries; " + "hardware evidence not claimed" + ) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_phase09b_observer_audit.py b/tests/test_phase09b_observer_audit.py new file mode 100644 index 0000000..8e3d55d --- /dev/null +++ b/tests/test_phase09b_observer_audit.py @@ -0,0 +1,302 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Host guardrails for the blocked Phase-0.9B observer audit.""" + +from __future__ import annotations + +import argparse +import copy +import hashlib +import importlib.util +import json +from pathlib import Path +import sys +from types import ModuleType +from typing import Any + + +def load_module(name: str, path: Path) -> ModuleType: + spec = importlib.util.spec_from_file_location(name, path) + if spec is None or spec.loader is None: + raise RuntimeError(f"could not load {path}") + module = importlib.util.module_from_spec(spec) + sys.modules[name] = module + spec.loader.exec_module(module) + return module + + +def require(condition: bool, message: str) -> None: + if not condition: + raise RuntimeError(message) + + +def require_invalid(errors: list[str], scenario: str) -> None: + require(bool(errors), f"unsafe Phase-0.9B mutation passed: {scenario}") + + +def synthetic_artifact_findings( + *, + imports: list[str], + strings: list[str], + open_flags: list[str], + reachable_calls: list[str], +) -> list[str]: + denied = ( + "kernel_copyin", + "kernel_copyout", + "kernel_set_ucred", + "ptrace", + "dlopen", + "dlsym", + "sceKernelLoadStartModule", + "socket", + "connect", + "bind", + "listen", + "accept", + "kill", + "exec", + "spawn", + "rename", + "unlink", + "chmod", + "chown", + "mkdir", + "mount", + "reboot", + "videoout", + "gnm", + "sdl", + "retroarch", + ) + findings: list[str] = [] + for category, values in ( + ("import", imports), + ("string", strings), + ("reachable", reachable_calls), + ): + for value in values: + lowered = value.lower() + if any(token.lower() in lowered for token in denied): + findings.append(f"{category}:{value}") + for value in open_flags: + if value in {"O_WRONLY", "O_RDWR", "O_CREAT", "O_TRUNC", "O_APPEND"}: + findings.append(f"open_flag:{value}") + return findings + + +def test_host_model(model: ModuleType) -> None: + require(model.evaluate_firmware("9.60", "9.60") == "OBSERVED", "equal firmware") + require(model.evaluate_firmware("9.60", "9.61") == "CONFLICT", "firmware conflict") + require(model.evaluate_firmware("9.60", None) == "UNPROVEN", "missing firmware") + + empty_digest = hashlib.sha256(b"").hexdigest() + regular = model.MockObject(expected_sha256=empty_digest) + require(model.evaluate_object(regular) == ("OBSERVED", None), "regular object") + require( + model.evaluate_object(model.MockObject(symlink=True))[1] + == "PATH_SYMLINK_SAFETY_UNPROVEN", + "symlink", + ) + require( + model.evaluate_object(model.MockObject(object_id_after="dev:1/ino:2"))[1] + == "OBJECT_ID_CHANGED", + "object ID change", + ) + require( + model.evaluate_object(model.MockObject(size_after=1))[1] == "SIZE_CHANGED", + "size change", + ) + require( + model.evaluate_object(model.MockObject(size_before=1, size_after=1))[1] + == "SHORT_READ", + "short read", + ) + require( + model.evaluate_object(model.MockObject(read_error=True))[1] == "READ_ERROR", + "read error", + ) + require( + model.evaluate_object(model.MockObject(expected_sha256="0" * 64))[1] + == "HASH_MISMATCH", + "hash mismatch", + ) + require( + model.evaluate_object(model.MockObject(path_known=False))[1] == "UNKNOWN_PATH", + "unknown path", + ) + require( + model.evaluate_object(model.MockObject(path_conflict=True))[1] == "PATH_CONFLICT", + "path conflict", + ) + + backup = model.MockObject(object_id_before="dev:1/ino:2") + require(model.evaluate_live_backup(regular, backup) == "SEPARATE_OBJECTS", "backup") + require( + model.evaluate_live_backup(regular, regular) == "SAME_OBJECT", + "same live and backup object", + ) + require( + model.evaluate_live_backup(regular, None) == "BACKUP_MISSING", + "backup missing", + ) + + for category in ("mount", "process", "listener"): + require( + model.unsupported_query(False) == "UNSUPPORTED_OR_UNPROVEN", + f"unsupported {category} query", + ) + require(model.evaluate_autoload(False, False) == "UNPROVEN", "autoload missing") + require(model.evaluate_autoload(True, False) == "ERROR", "autoload parse") + + output_limited = model.run_terminal_scenario(record_count=100) + require(len(output_limited.emitted) == 64, "output limit") + require(output_limited.exit_reached, "exit after output limit") + + error_limited = model.run_terminal_scenario(error_count=20) + require(error_limited.errors == 9, "error limit") + require(error_limited.exit_reached, "exit after error limit") + + deadline = model.run_terminal_scenario(deadline_reached=True) + require(deadline.emitted[0]["raw_error"] == "DEADLINE_REACHED", "deadline") + require(deadline.exit_reached, "exit after deadline") + + output_failure = model.run_terminal_scenario(output_channel_ok=False) + require( + output_failure.emitted[0]["raw_error"] == "OUTPUT_CHANNEL_FAILED", + "output failure", + ) + require(output_failure.exit_reached, "exit after output failure") + + for observer in (output_limited, error_limited, deadline, output_failure): + require(observer.retry_count == 0, "retry occurred") + require(observer.persistent_write_count == 0, "persistent write occurred") + require( + observer.service_or_process_mutation_count == 0, + "service/process mutation occurred", + ) + require(observer.listener_count == 0, "listener occurred") + require(observer.lifecycle_call_count == 0, "lifecycle call occurred") + require(observer.installer_call_count == 0, "installer call occurred") + require( + observer.graphics_or_retroarch_call_count == 0, + "graphics/RetroArch call occurred", + ) + + +def test_negative_policy( + validator: ModuleType, root: Path, manifest: dict[str, Any] +) -> None: + ready = copy.deepcopy(manifest) + ready["status"] = "READY" + require_invalid(validator.validate_manifest(root, ready), "READY status") + + for field in validator.AUTHORIZATION_FIELDS: + changed = copy.deepcopy(manifest) + changed["authorization"][field] = True + require_invalid( + validator.validate_manifest(root, changed), f"authorization {field}=true" + ) + + built = copy.deepcopy(manifest) + built["build_gate"]["observer_source_created"] = True + built["build_gate"]["observer_target_declared"] = True + built["build_gate"]["target_build_performed"] = True + built["artifact"]["present"] = True + built["artifact"]["path"] = "observer.elf" + built["artifact"]["sha256"] = "1" * 64 + built["artifact"]["size"] = 1 + require_invalid(validator.validate_manifest(root, built), "artifact appeared") + + startup_claim = copy.deepcopy(manifest) + startup_claim["build_gate"]["startup_and_exit_abi_proven"] = True + require_invalid(validator.validate_manifest(root, startup_claim), "startup proof") + + output_claim = copy.deepcopy(manifest) + output_claim["build_gate"]["non_persistent_output_channel_proven"] = True + require_invalid(validator.validate_manifest(root, output_claim), "output proof") + + implementation = copy.deepcopy(manifest) + implementation["implementation"]["observer_logic_implemented"] = True + implementation["implementation"]["observations_implemented"] = ["firmware"] + require_invalid( + validator.validate_manifest(root, implementation), "target implementation" + ) + + reproducible = copy.deepcopy(manifest) + reproducible["reproducibility"]["status"] = "PASSED" + reproducible["reproducibility"]["build_1_sha256"] = "1" * 64 + reproducible["reproducibility"]["build_2_sha256"] = "1" * 64 + reproducible["reproducibility"]["byte_identical"] = True + require_invalid( + validator.validate_manifest(root, reproducible), + "unperformed build became reproducible", + ) + + +def test_artifact_audit_guardrails(manifest: dict[str, Any]) -> None: + require(manifest["artifact"]["present"] is False, "blocked artifact exists") + require(manifest["artifact"]["path"] is None, "blocked artifact path exists") + require( + manifest["static_artifact_audit"]["status"] + == "NOT_PERFORMED_BLOCKED_BEFORE_BUILD", + "missing-artifact audit was promoted", + ) + + findings = synthetic_artifact_findings( + imports=["kernel_copyin", "bind", "sceKernelLoadStartModule"], + strings=["/autoload_status", "RetroArch", "VideoOut"], + open_flags=["O_RDONLY", "O_WRONLY", "O_CREAT"], + reachable_calls=["_start->ptrace", "observer->rename", "observer->kill"], + ) + require(len(findings) >= 10, "denied-capability scanner missed synthetic cases") + require( + synthetic_artifact_findings( + imports=[], + strings=[], + open_flags=["O_RDONLY", "O_NOFOLLOW", "O_CLOEXEC"], + reachable_calls=[], + ) + == [], + "read-only synthetic audit produced a false positive", + ) + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + root = args.root.resolve() + model = load_module( + "phase09b_observer_model", root / "tests/phase09b_observer_model.py" + ) + validator = load_module( + "phase09b_validator", root / "tools/validate_phase09b_observer_audit.py" + ) + manifest = validator.load_json( + root / "manifests/runtime/phase-0.9b-observer.json" + ) + + errors = validator.collect_errors(root) + if errors: + raise RuntimeError("; ".join(errors)) + test_host_model(model) + test_negative_policy(validator, root, manifest) + test_artifact_audit_guardrails(manifest) + + schema = validator.load_json( + root / "manifests/runtime/phase-0.9b-observation-plan.schema.json" + ) + default = schema["x-chimera-default-plan"] + require(default["read_paths"] == [], "default plan contains paths") + require(default["device_address"] is None, "default plan contains address") + require(default["maximum_execution_count"] == 0, "default plan permits execution") + for field in validator.AUTHORIZATION_FIELDS: + require(default[field] is False, f"default plan {field} is not false") + + print("Phase-0.9B observer host and artifact guardrails: PASS") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_phase09c_feasibility.py b/tests/test_phase09c_feasibility.py new file mode 100644 index 0000000..5cc6113 --- /dev/null +++ b/tests/test_phase09c_feasibility.py @@ -0,0 +1,199 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Host guardrails for the blocked Phase-0.9C feasibility closure.""" + +from __future__ import annotations + +import argparse +import copy +import importlib.util +from pathlib import Path +import sys +from types import ModuleType +from typing import Callable + + +def load_module(name: str, path: Path) -> ModuleType: + spec = importlib.util.spec_from_file_location(name, path) + if spec is None or spec.loader is None: + raise RuntimeError(f"could not load {path}") + module = importlib.util.module_from_spec(spec) + sys.modules[name] = module + spec.loader.exec_module(module) + return module + + +def require(condition: bool, message: str) -> None: + if not condition: + raise RuntimeError(message) + + +def require_invalid(errors: list[str], scenario: str) -> None: + require(bool(errors), f"unsafe Phase-0.9C mutation passed: {scenario}") + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + root = args.root.resolve() + validator = load_module( + "phase09c_validator", root / "tools/validate_phase09c_feasibility.py" + ) + manifest = validator.load_json( + root / "manifests/runtime/phase-0.9c-feasibility.json" + ) + schema = validator.load_json( + root / "manifests/runtime/phase-0.9c-feasibility.schema.json" + ) + cases: list[tuple[str, Callable[[], None]]] = [] + + def case(name: str) -> Callable[[Callable[[], None]], Callable[[], None]]: + def register(function: Callable[[], None]) -> Callable[[], None]: + cases.append((name, function)) + return function + + return register + + @case("complete offline validator") + def _() -> None: + require(validator.collect_errors(root) == [], "current audit does not validate") + + @case("manifest and schema") + def _() -> None: + require(validator.validate_manifest(manifest) == [], "manifest invalid") + require( + validator.validate_schema_instance(schema, manifest) == [], + "schema rejected manifest", + ) + + @case("authorization remains false") + def _() -> None: + for field in validator.AUTHORIZATION_FIELDS: + changed = copy.deepcopy(manifest) + changed["authorization"][field] = True + require_invalid(validator.validate_manifest(changed), field) + + @case("positive classification rejected") + def _() -> None: + changed = copy.deepcopy(manifest) + changed["status"] = "READY" + changed["classification"] = ( + "FEASIBILITY_CONTRACT_PROVEN_NO_TARGET_IMPLEMENTATION" + ) + changed["final_decision"]["positive_classification_allowed"] = True + changed["final_decision"]["classification"] = changed["classification"] + require_invalid(validator.validate_manifest(changed), "positive decision") + + @case("startup and cleanup promotion rejected") + def _() -> None: + for field in ( + "normal_sdk_kernelwrite_free", + "freestanding_dependency_closure_proven", + "safe_return_proven", + "safe_process_exit_proven", + "error_exit_proven", + "timeout_safe_exit_proven", + "complete_cleanup_proven", + ): + changed = copy.deepcopy(manifest) + changed["startup_exit"][field] = True + require_invalid(validator.validate_manifest(changed), field) + + @case("fabricated firmware source rejected") + def _() -> None: + changed = copy.deepcopy(manifest) + changed["firmware"]["source_two"]["identity"] = "invented" + changed["firmware"]["source_two"]["status"] = "PROVEN" + changed["firmware"]["agreement_proven"] = True + require_invalid(validator.validate_manifest(changed), "firmware source two") + + @case("output implementation promotion rejected") + def _() -> None: + changed = copy.deepcopy(manifest) + changed["output_architectures"][0]["current_implementation"] = True + changed["host_protocol"]["target_implemented"] = True + require_invalid(validator.validate_manifest(changed), "output implementation") + + @case("capability implementation and execution rejected") + def _() -> None: + changed = copy.deepcopy(manifest) + changed["capability_closure"][0]["implementation_allowed"] = True + changed["capability_closure"][0]["execution_allowed"] = True + changed["capability_closure"][0]["target_evidence"] = "PROVEN" + require_invalid(validator.validate_manifest(changed), "capability promotion") + + @case("artifact and package rejected") + def _() -> None: + changed = copy.deepcopy(manifest) + changed["artifact"] = { + "present": True, + "path": "phase09c-observer.elf", + "sha256": "1" * 64, + "size": 1, + "execution_eligible": False, + "execution_authorized": False, + } + changed["implementation"]["target_elf_present"] = True + require_invalid(validator.validate_manifest(changed), "artifact") + for path in ( + "samples/phase09c_observer/main.c", + "outputs/phase09c-observer.elf", + "outputs/phase-0.9c-observer.map", + "packaging/phase09c/install.zip", + "packaging/phase09c/lifecycle.pkg", + "packaging/phase09c/autoload.json", + ): + require(validator.forbidden_repository_path(path), path) + + @case("host files remain permitted") + def _() -> None: + for path in ( + "docs/runtime/phase-0.9c-static-audit.md", + "tests/phase09c_feasibility_model.py", + "tests/test_phase09c_protocol.py", + "tools/validate_phase09c_feasibility.py", + "packaging/phase09c/SHA256SUMS.txt", + ): + require(not validator.forbidden_repository_path(path), path) + + @case("side-effect false promotion rejected") + def _() -> None: + for field in ( + "no_persistent_content_write_is_side_effect_free", + "read_only_flag_is_side_effect_free", + "all_planned_observations_proven_side_effect_free", + ): + changed = copy.deepcopy(manifest) + changed["side_effect_model"][field] = True + require_invalid(validator.validate_manifest(changed), field) + + @case("permanent denylist and immutable evidence") + def _() -> None: + require( + validator.validate_immutable_evidence(root) == [], + "immutable evidence changed", + ) + denylist = validator.load_json(root / "manifests/artifact-denylist.json") + entry = denylist["entries"][0] + require(entry["sha256"] == validator.BLOCKED_HASH, "denylist hash changed") + require(entry["permanent"] is True, "denylist is not permanent") + require( + entry["execution_eligible"] is False, + "denylisted artifact became eligible", + ) + + for name, function in cases: + try: + function() + except Exception as error: + raise RuntimeError( + f"Phase-0.9C feasibility case failed: {name}: {error}" + ) from error + + print(f"Phase-0.9C feasibility host tests: {len(cases)}/{len(cases)} PASS") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_phase09c_protocol.py b/tests/test_phase09c_protocol.py new file mode 100644 index 0000000..109205b --- /dev/null +++ b/tests/test_phase09c_protocol.py @@ -0,0 +1,362 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Deterministic, networkless tests for the Phase-0.9C host protocol model.""" + +from __future__ import annotations + +import argparse +import importlib.util +from pathlib import Path +import sys +from types import ModuleType +from typing import Callable + + +def load_module(path: Path) -> ModuleType: + spec = importlib.util.spec_from_file_location("phase09c_model", path) + if spec is None or spec.loader is None: + raise RuntimeError(f"could not load {path}") + module = importlib.util.module_from_spec(spec) + sys.modules[spec.name] = module + spec.loader.exec_module(module) + return module + + +def require(condition: bool, message: str) -> None: + if not condition: + raise RuntimeError(message) + + +def request( + model: ModuleType, + *, + firmware_one: str | None = "9.60", + firmware_two: str | None = "9.60", + requested: int = 0b11, +) -> object: + return model.ResultRequest( + execution_nonce=b"N" * 16, + request_id=b"R" * 16, + firmware_source_one=firmware_one, + firmware_source_two=firmware_two, + observer_version=1, + requested_capabilities=requested, + artifact_sha256=bytes.fromhex("11" * 32), + deadline_monotonic_ns=10_000, + ) + + +def mutate(record: bytes, offset: int, value: int) -> bytes: + changed = bytearray(record) + changed[offset] ^= value + return bytes(changed) + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + root = args.root.resolve() + model = load_module(root / "tests/phase09c_feasibility_model.py") + cases: list[tuple[str, Callable[[], None]]] = [] + + def case(name: str) -> Callable[[Callable[[], None]], Callable[[], None]]: + def register(function: Callable[[], None]) -> Callable[[], None]: + cases.append((name, function)) + return function + + return register + + @case("startup callgraph") + def _() -> None: + require(model.validate_startup_model() == [], "startup model invalid") + + @case("prohibited startup effects") + def _() -> None: + require( + model.PROHIBITED_STARTUP_EFFECTS + <= model.NORMAL_SDK_REACHABLE_PROHIBITED_EFFECTS, + "normal SDK effects hidden", + ) + + @case("freestanding dependency closure") + def _() -> None: + blockers = set(model.freestanding_blockers()) + require( + { + "stack_alignment", + "callable_read_abi", + "callable_monotonic_time_abi", + "process_exit_abi", + "return_cleanup", + "bounded_result_copyout", + } + <= blockers, + "freestanding blockers missing", + ) + + @case("exit state machine") + def _() -> None: + for path in model.EXIT_PATHS: + require(not model.exit_path_is_safe(path), f"{path} became safe") + require("SIGKILL" in model.EXIT_PATHS["timeout"], "timeout kill hidden") + + @case("output framing") + def _() -> None: + expected = request(model) + record = model.build_result( + expected, b"ok", observed_capabilities=0b11 + ) + require(len(record) == 4096, "record is not fixed-size") + require( + model.validate_result(record, expected, now_monotonic_ns=1) + == "VALID_COMPLETE_RESULT", + "valid result rejected", + ) + + @case("maximum lengths") + def _() -> None: + expected = request(model, requested=0) + record = model.build_result(expected, b"x" * model.MAX_BODY_SIZE) + require( + model.validate_result(record, expected, now_monotonic_ns=1) + == "VALID_COMPLETE_RESULT", + "maximum body rejected", + ) + try: + model.build_result(expected, b"x" * (model.MAX_BODY_SIZE + 1)) + except ValueError: + return + raise RuntimeError("oversized body accepted") + + @case("truncation") + def _() -> None: + expected = request(model, requested=0) + record = model.build_result( + expected, b"x" * (model.MAX_BODY_SIZE + 1), truncate=True + ) + require( + model.validate_result(record, expected, now_monotonic_ns=1) + == "BLOCKED_TRUNCATED", + "truncation accepted", + ) + + @case("stale nonce") + def _() -> None: + expected = request(model) + record = model.build_result( + expected, b"", observed_capabilities=0b11 + ) + stale = model.ResultRequest( + execution_nonce=b"S" * 16, + request_id=expected.request_id, + firmware_source_one="9.60", + firmware_source_two="9.60", + observer_version=1, + requested_capabilities=0b11, + artifact_sha256=expected.artifact_sha256, + deadline_monotonic_ns=10_000, + ) + require( + model.validate_result(record, stale, now_monotonic_ns=1) + == "BLOCKED_STALE_NONCE", + "stale nonce accepted", + ) + + @case("duplicate result") + def _() -> None: + expected = request(model) + record = model.build_result( + expected, b"", observed_capabilities=0b11 + ) + consumer = model.ResultConsumer() + require( + consumer.consume(record, expected, now_monotonic_ns=1) + == "VALID_COMPLETE_RESULT", + "first result rejected", + ) + require( + consumer.consume(record, expected, now_monotonic_ns=1) + == "BLOCKED_DUPLICATE_RESULT", + "duplicate accepted", + ) + + @case("result checksum failure") + def _() -> None: + expected = request(model) + record = model.build_result( + expected, b"body", observed_capabilities=0b11 + ) + require( + model.validate_result( + mutate(record, model.HEADER_SIZE, 1), + expected, + now_monotonic_ns=1, + ) + == "BLOCKED_RESULT_CHECKSUM", + "checksum corruption accepted", + ) + + @case("timeout") + def _() -> None: + expected = request(model) + record = model.build_result( + expected, b"", observed_capabilities=0b11 + ) + require( + model.validate_result(record, expected, now_monotonic_ns=10_001) + == "BLOCKED_TIMEOUT", + "expired result accepted", + ) + + @case("incomplete completion marker") + def _() -> None: + expected = request(model) + record = model.build_result( + expected, b"", observed_capabilities=0b11, complete=False + ) + require( + model.validate_result(record, expected, now_monotonic_ns=1) + == "BLOCKED_INCOMPLETE", + "incomplete record accepted", + ) + + @case("unsupported capability") + def _() -> None: + expected = request(model) + record = model.build_result( + expected, + b"", + observed_capabilities=0b01, + unsupported_capabilities=0b10, + ) + require( + model.validate_result(record, expected, now_monotonic_ns=1) + == "VALID_RECORD_WITH_UNSUPPORTED_CAPABILITIES", + "explicit unsupported result lost", + ) + + @case("error versus empty success") + def _() -> None: + expected = request(model, requested=0) + error = model.build_result( + expected, b"", status=model.STATUS_OBSERVER_ERROR + ) + empty = model.build_result(expected, b"") + require( + model.validate_result(error, expected, now_monotonic_ns=1) + == "BLOCKED_OBSERVER_FAILURE", + "empty failure became success", + ) + require( + model.validate_result(empty, expected, now_monotonic_ns=1) + == "VALID_COMPLETE_RESULT", + "valid no-capability empty result rejected", + ) + + @case("cleanup status") + def _() -> None: + expected = request(model) + record = model.build_result( + expected, + b"", + observed_capabilities=0b11, + cleanup_status=model.CLEANUP_FAILED, + ) + require( + model.validate_result(record, expected, now_monotonic_ns=1) + == "BLOCKED_CLEANUP_NOT_PROVEN", + "failed cleanup accepted", + ) + + @case("conflicting firmware sources") + def _() -> None: + expected = request( + model, firmware_one="9.60", firmware_two="9.61" + ) + record = model.build_result( + expected, b"", observed_capabilities=0b11 + ) + require( + model.validate_result(record, expected, now_monotonic_ns=1) + == "BLOCKED_FIRMWARE_CONFLICT", + "firmware conflict accepted", + ) + + @case("absent firmware source two") + def _() -> None: + expected = request(model, firmware_two=None) + record = model.build_result( + expected, b"", observed_capabilities=0b11 + ) + require( + model.validate_result(record, expected, now_monotonic_ns=1) + == "BLOCKED_FIRMWARE_SOURCE_2_ABSENT", + "missing firmware source accepted", + ) + + @case("unknown protocol version") + def _() -> None: + expected = request(model) + record = model.build_result( + expected, + b"", + observed_capabilities=0b11, + protocol_version=2, + ) + require( + model.validate_result(record, expected, now_monotonic_ns=1) + == "BLOCKED_UNKNOWN_VERSION", + "unknown version accepted", + ) + + @case("capability completeness") + def _() -> None: + expected = request(model) + record = model.build_result( + expected, b"", observed_capabilities=0b01 + ) + require( + model.validate_result(record, expected, now_monotonic_ns=1) + == "BLOCKED_INCOMPLETE_CAPABILITY_RESULT", + "partial capability bitmap accepted", + ) + + @case("side-effect classification") + def _() -> None: + effects = model.classify_side_effects("filesystem_content_hash") + require("semantic_readonly" in effects, "semantic read missing") + require("atime_effect_possible" in effects, "atime risk hidden") + require("audit_effect_possible" in effects, "audit risk hidden") + require("cache_effect_possible" in effects, "cache risk hidden") + require("object_race_possible" in effects, "race risk hidden") + require( + not model.is_proven_side_effect_free("filesystem_content_hash"), + "read was promoted to side-effect-free", + ) + + @case("output architecture ordering") + def _() -> None: + require( + list(model.OUTPUT_ARCHITECTURES) + == [ + "D1_CALLER_OWNED_BOUNDED_BUFFER", + "D2_EXISTING_REQUEST_RESPONSE", + "D3_LOADER_OWNED_STATUS_RECORD", + "D4_PROCESS_EXIT_STATUS", + ], + "output architectures reordered", + ) + + for name, function in cases: + try: + function() + except Exception as error: + raise RuntimeError(f"Phase-0.9C protocol case failed: {name}: {error}") from error + + print(f"Phase-0.9C protocol host tests: {len(cases)}/{len(cases)} PASS") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_phase09d_readback.py b/tests/test_phase09d_readback.py new file mode 100644 index 0000000..83a21c6 --- /dev/null +++ b/tests/test_phase09d_readback.py @@ -0,0 +1,405 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Thirty host-only guardrails for the Phase-0.9D readback design.""" + +from __future__ import annotations + +import argparse +import copy +import importlib.util +from pathlib import Path +import sys +from types import ModuleType +from typing import Callable + + +def load_module(name: str, path: Path) -> ModuleType: + spec = importlib.util.spec_from_file_location(name, path) + if spec is None or spec.loader is None: + raise RuntimeError(f"could not load {path}") + module = importlib.util.module_from_spec(spec) + sys.modules[name] = module + spec.loader.exec_module(module) + return module + + +def require(condition: bool, message: str) -> None: + if not condition: + raise RuntimeError(message) + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + root = args.root.resolve() + validator = load_module( + "phase09d_validator", root / "tools/validate_phase09d_readback.py" + ) + manifest = validator.load_json( + root / "manifests/runtime/phase-0.9d-existing-stack-readback.json" + ) + schema = validator.load_json( + root + / "manifests/runtime/phase-0.9d-existing-stack-readback.schema.json" + ) + defaults = manifest["endpoint_defaults"] + cases: list[tuple[str, Callable[[], None]]] = [] + + def case(name: str) -> Callable[[Callable[[], None]], Callable[[], None]]: + def register(function: Callable[[], None]) -> Callable[[], None]: + cases.append((name, function)) + return function + + return register + + def candidate(**changes: object) -> dict[str, object]: + route: dict[str, object] = { + "readback_candidate": True, + "open_flags": ["O_RDONLY", "O_NOFOLLOW"], + "writes_bytes": False, + "creates_file": False, + "removes_file": False, + "renames_file": False, + "modifies_configuration": False, + "launches_payload": False, + "process_or_service_action": False, + "writes_autoload_triggered": False, + "binary_safe_file_response": True, + "exact_returned_byte_count": True, + "partial_result_rejected": True, + "short_read_behavior": "DETECTED_AND_INVALID", + "automatic_retry": False, + "automatic_resume": False, + } + route.update(changes) + return route + + def invalid_manifest(changed: dict[str, object], message: str) -> None: + require(bool(validator.validate_manifest(changed)), message) + + @case("01 device write can never be a readback candidate") + def _() -> None: + errors = validator.route_readback_errors( + candidate(writes_bytes=True), defaults + ) + require(bool(errors), "device-write candidate passed") + + @case("02 rename unlink create and truncate are rejected") + def _() -> None: + mutations = ( + {"renames_file": True}, + {"removes_file": True}, + {"creates_file": True}, + {"open_flags": ["fopen(wb)"]}, + ) + for mutation in mutations: + require( + bool( + validator.route_readback_errors( + candidate(**mutation), defaults + ) + ), + f"mutating route passed: {mutation}", + ) + + @case("03 payload launch is rejected") + def _() -> None: + require( + bool( + validator.route_readback_errors( + candidate(launches_payload=True), defaults + ) + ), + "launch route passed", + ) + + @case("04 autoload_triggered routes are excluded") + def _() -> None: + require( + bool( + validator.route_readback_errors( + candidate(writes_autoload_triggered=True), defaults + ) + ), + "autoload mutation passed", + ) + require( + manifest["flag_semantics"]["autoload_triggered"]["excluded_windows"] + == [1, 2], + "autoload route is not excluded from Windows 1 and 2", + ) + + @case("05 server_active alone is not anti-brick critical") + def _() -> None: + semantics = manifest["flag_semantics"]["server_active_flag"] + require( + semantics["classification"] == "LOW_VOLATILE", + "server_active is overclassified", + ) + require( + validator.server_active_observation_status(semantics) == "PARTIAL", + "server_active route should remain partial", + ) + + @case("06 server_active semantics must be complete") + def _() -> None: + changed = copy.deepcopy(manifest) + changed["flag_semantics"]["server_active_flag"]["fully_documented"] = False + invalid_manifest(changed, "incomplete server_active semantics passed") + + @case("07 missing reset semantics blocks observation") + def _() -> None: + semantics = copy.deepcopy( + manifest["flag_semantics"]["server_active_flag"] + ) + semantics["reset_path"] = "UNPROVEN" + require( + validator.server_active_observation_status(semantics) == "BLOCKED", + "unknown reset semantics did not block", + ) + + @case("08 file read without binary framing is invalid") + def _() -> None: + require( + bool( + validator.route_readback_errors( + candidate(binary_safe_file_response=False), defaults + ) + ), + "text-framed file route passed", + ) + + @case("09 file read without short-read detection is invalid") + def _() -> None: + require( + bool( + validator.route_readback_errors( + candidate(short_read_behavior="UNPROVEN"), defaults + ) + ), + "short-read-unsafe route passed", + ) + + @case("10 partial host transfer is invalid") + def _() -> None: + record = { + "status": "TRANSFER_INCOMPLETE", + "transfer_complete": False, + "automatic_resume": False, + "automatic_retry": False, + "recovery_proven": False, + } + require( + bool(validator.backup_record_errors(record)), + "partial transfer was not invalidated", + ) + + @case("11 automatic resume is forbidden") + def _() -> None: + record = { + "status": "INVALID", + "automatic_resume": True, + "automatic_retry": False, + "recovery_proven": False, + } + require( + bool(validator.backup_record_errors(record)), + "automatic resume passed", + ) + + @case("12 automatic retry is forbidden") + def _() -> None: + record = { + "status": "INVALID", + "automatic_resume": False, + "automatic_retry": True, + "recovery_proven": False, + } + require( + bool(validator.backup_record_errors(record)), + "automatic retry passed", + ) + + @case("13 two readbacks must match size hash and bytes") + def _() -> None: + record = { + "status": "COPIES_MATCH", + "transfer_complete": True, + "automatic_resume": False, + "automatic_retry": False, + "closed_and_reopened": True, + "exact_byte_count": 8, + "sha256": "0" * 64, + "sizes_match": True, + "hashes_match": True, + "bytes_match": False, + "recovery_proven": False, + } + require( + bool(validator.backup_record_errors(record)), + "byte mismatch passed", + ) + + @case("14 hash without exact byte count is insufficient") + def _() -> None: + record = { + "status": "HOST_COPY_HASHED", + "transfer_complete": True, + "automatic_resume": False, + "automatic_retry": False, + "closed_and_reopened": True, + "sha256": "0" * 64, + "recovery_proven": False, + } + require( + bool(validator.backup_record_errors(record)), + "hash without count passed", + ) + + @case("15 host backup is not recovery proof") + def _() -> None: + record = { + "status": "COPIES_MATCH", + "transfer_complete": True, + "automatic_resume": False, + "automatic_retry": False, + "closed_and_reopened": True, + "exact_byte_count": 8, + "sha256": "0" * 64, + "sizes_match": True, + "hashes_match": True, + "bytes_match": True, + "recovery_proven": True, + } + require( + bool(validator.backup_record_errors(record)), + "backup incorrectly proved recovery", + ) + + @case("16 replacement-dependent recovery is self-dependent") + def _() -> None: + require( + validator.recovery_dependency_classification( + "payload_manager", "payload_manager" + ) + == "SELF_DEPENDENT", + "self-dependence was not detected", + ) + + @case("17 package path cannot become live") + def _() -> None: + changed = copy.deepcopy(manifest) + changed["path_inventory"][0]["live"] = True + changed["runtime_observed_live_paths"] = [ + changed["path_inventory"][0]["path"] + ] + invalid_manifest(changed, "offline path was promoted to live") + + @case("18 conflicting paths retain PATH_CONFLICT") + def _() -> None: + changed = copy.deepcopy(manifest) + changed["path_classification"] = "RESOLVED" + invalid_manifest(changed, "path conflict was silently resolved") + + @case("19 Window 1 contains no file transfer") + def _() -> None: + changed = copy.deepcopy(manifest) + changed["operational_windows"][0]["file_transfer"] = True + invalid_manifest(changed, "Window 1 transfer passed") + + @case("20 Window 2 contains no device write") + def _() -> None: + changed = copy.deepcopy(manifest) + changed["operational_windows"][1]["device_write"] = True + invalid_manifest(changed, "Window 2 write passed") + + @case("21 Window 2 contains no launch") + def _() -> None: + changed = copy.deepcopy(manifest) + changed["operational_windows"][1]["payload_launch"] = True + invalid_manifest(changed, "Window 2 launch passed") + + @case("22 Window 2 excludes autoload_status") + def _() -> None: + changed = copy.deepcopy(manifest) + changed["operational_windows"][1]["autoload_status_route"] = True + invalid_manifest(changed, "Window 2 autoload_status passed") + + @case("23 Window 3 has no automatic third attempt") + def _() -> None: + changed = copy.deepcopy(manifest) + changed["operational_windows"][2]["automatic_third_attempt"] = True + invalid_manifest(changed, "automatic third attempt passed") + + @case("24 components have separate windows") + def _() -> None: + changed = copy.deepcopy(manifest) + changed["operational_windows"][3]["component_session_separate"] = False + invalid_manifest(changed, "combined component window passed") + + @case("25 side-by-side does not authorize installation") + def _() -> None: + changed = copy.deepcopy(manifest) + changed["side_by_side"]["grants_installation_authorization"] = True + invalid_manifest(changed, "side-by-side authorized installation") + + @case("26 no observer artifact appears") + def _() -> None: + require( + manifest["actions"]["observer_created"] is False + and manifest["actions"]["target_artifact_created"] is False, + "observer artifact recorded", + ) + require(validator.collect_errors(root) == [], "offline audit invalid") + + @case("27 no target code appears") + def _() -> None: + forbidden = {".c", ".cc", ".cpp", ".s", ".asm", ".ld", ".elf"} + for relative in validator._phase09d_paths(root): + require( + Path(relative).suffix.lower() not in forbidden, + f"target code/artifact appeared: {relative}", + ) + + @case("28 host tests are not hardware evidence") + def _() -> None: + changed = copy.deepcopy(manifest) + changed["final_decision"]["hardware_evidence_claimed"] = True + invalid_manifest(changed, "host test became hardware proof") + + @case("29 every authorization field stays false") + def _() -> None: + for field in validator.AUTHORIZATION_FIELDS: + changed = copy.deepcopy(manifest) + changed["authorization"][field] = True + invalid_manifest(changed, f"authorization passed: {field}") + + @case("30 Manager backup remains installation blocker") + def _() -> None: + changed = copy.deepcopy(manifest) + changed["canonical_state"]["payload_manager_backup"] = "COMPLETE" + invalid_manifest(changed, "Manager backup blocker was removed") + + require(len(cases) == 30, f"expected 30 guardrails, found {len(cases)}") + require( + validator.validate_schema_instance(schema, manifest) == [], + "manifest does not satisfy schema", + ) + + failures: list[str] = [] + for name, function in cases: + try: + function() + except Exception as error: # noqa: BLE001 - standalone test harness + failures.append(f"{name}: {error}") + if failures: + for failure in failures: + print(f"FAIL: {failure}") + return 1 + print("Phase-0.9D readback guardrails: 30/30 PASS") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_phase09e_bootstrap.py b/tests/test_phase09e_bootstrap.py new file mode 100644 index 0000000..b515fdf --- /dev/null +++ b/tests/test_phase09e_bootstrap.py @@ -0,0 +1,302 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Twenty host-only guardrails for the Phase-0.9E provenance audit.""" + +from __future__ import annotations + +import argparse +import copy +import importlib.util +from pathlib import Path +import sys +from types import ModuleType +from typing import Callable + + +def load_module(name: str, path: Path) -> ModuleType: + spec = importlib.util.spec_from_file_location(name, path) + if spec is None or spec.loader is None: + raise RuntimeError(f"could not load {path}") + module = importlib.util.module_from_spec(spec) + sys.modules[name] = module + spec.loader.exec_module(module) + return module + + +def require(condition: bool, message: str) -> None: + if not condition: + raise RuntimeError(message) + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + root = args.root.resolve() + validator = load_module( + "phase09e_validator", root / "tools/validate_phase09e_bootstrap.py" + ) + manifest = validator.load_json( + root / "manifests/runtime/phase-0.9e-bootstrap-provenance.json" + ) + protocol_manifest = validator.load_json( + root / "manifests/runtime/phase-0.9e-loader-protocol.json" + ) + cases: list[tuple[str, Callable[[], None]]] = [] + + def case(name: str) -> Callable[[Callable[[], None]], Callable[[], None]]: + def register(function: Callable[[], None]) -> Callable[[], None]: + cases.append((name, function)) + return function + + return register + + @case("01 public upstream is not exact-used without provenance") + def _() -> None: + artifact = copy.deepcopy(manifest["artifacts"][11]) + artifact["confidence"] = "EXACT_USED" + require( + bool(validator.provenance_errors(artifact)), + "public upstream was promoted to exact-used", + ) + + @case("02 opaque binary gets no invented source commit") + def _() -> None: + artifact = copy.deepcopy(manifest["artifacts"][1]) + artifact["source_commit"] = "0" * 40 + require( + bool(validator.provenance_errors(artifact)), + "opaque binary accepted an invented source commit", + ) + + @case("03 missing bootstrap implementation is classified") + def _() -> None: + require( + validator.rescue_classification(actual_package_available=False) + == "BOOTSTRAP_IMPLEMENTATION_MISSING", + "missing implementation was not blocked", + ) + + @case("04 an elfldr dependency is not independent") + def _() -> None: + require( + validator.rescue_classification( + actual_package_available=True, requires_elfldr=True + ) + == "SELF_OR_CROSS_DEPENDENT", + "elfldr-dependent bootstrap was marked independent", + ) + + @case("05 a Payload Manager dependency is not independent") + def _() -> None: + require( + validator.rescue_classification( + actual_package_available=True, requires_payload_manager=True + ) + == "SELF_OR_CROSS_DEPENDENT", + "manager-dependent bootstrap was marked independent", + ) + + @case("06 live replacement cannot be a safe rescue executor") + def _() -> None: + require( + validator.rescue_classification( + actual_package_available=True, replaces_live_component=True + ) + == "NO_INDEPENDENT_RESCUE_PATH", + "live-replacement bootstrap was accepted", + ) + + @case("07 host-to-memory needs receive mapping and entrypoint code") + def _() -> None: + partials = ( + (True, False, False), + (False, True, False), + (False, False, True), + (True, True, False), + ) + for receive, mapping, entrypoint in partials: + require( + validator.host_to_memory_classification( + receive_code=receive, + mapping_code=mapping, + entrypoint_code=entrypoint, + ) + != "PROVEN_FROM_SOURCE", + "incomplete host-to-memory evidence passed", + ) + + @case("08 conceptual port-9020 text is not protocol proof") + def _() -> None: + require( + bool(validator.validate_protocol(protocol_manifest)) is False, + "canonical unknown protocol does not validate", + ) + require( + protocol_manifest["classification"] + == "CONCEPTUAL_9020_DESCRIPTION_IS_NOT_PROTOCOL_PROOF", + "conceptual protocol was promoted", + ) + + @case("09 full protocol model requires framing length and partial I/O") + def _() -> None: + protocol = copy.deepcopy(protocol_manifest["protocol"]) + protocol["maximum_payload_size"] = 1024 + protocol["headers"] = "FIXED" + protocol["length_fields"] = "U32" + protocol["bounds_checks"] = "PRESENT" + protocol["short_read_detection"] = "PRESENT" + require( + validator.protocol_model_complete(protocol) is False, + "protocol without short-write handling passed", + ) + protocol["short_write_detection"] = "PRESENT" + require( + validator.protocol_model_complete(protocol) is True, + "complete synthetic protocol shape was rejected", + ) + + @case("10 a temporary socket is not automatically brick relevant") + def _() -> None: + require( + validator.risk_classification(temporary_socket=True) + != "BRICK_RELEVANT", + "temporary socket was overclassified", + ) + + @case("11 live filesystem write is brick relevant") + def _() -> None: + require( + validator.risk_classification(live_filesystem_write=True) + == "BRICK_RELEVANT", + "live filesystem write was underclassified", + ) + + @case("12 autoload activation is brick relevant") + def _() -> None: + require( + validator.risk_classification(autoload_activation=True) + == "BRICK_RELEVANT", + "autoload activation was underclassified", + ) + + @case("13 automatic retry remains forbidden") + def _() -> None: + changed = copy.deepcopy(manifest) + changed["authorization"]["automatic_retry"] = True + changed["decisions"]["automatic_retry"] = True + require( + bool(validator.validate_manifest(changed)), + "automatic retry was accepted", + ) + + @case("14 general expectation cannot prove reboot recovery") + def _() -> None: + require( + validator.reboot_classification( + exact_package=False, + source_design_restartable=False, + hardware_observed=False, + ) + == "REBOOT_RECOVERY_UNPROVEN", + "general reboot expectation was promoted to proof", + ) + + @case("15 restartable description without exact package is at most partial") + def _() -> None: + require( + validator.rescue_classification( + actual_package_available=False, + all_required_properties_proven=True, + ) + != "INDEPENDENT_RESCUE_EXECUTOR_CANDIDATE", + "description-only chain was marked independent", + ) + + @case("16 Phase 0.9F is blocked when implementation is missing") + def _() -> None: + require( + validator.phase09f_design_allowed( + actual_package_available=False, + independent_from_elfldr=True, + independent_from_payload_manager=True, + no_live_replacement=True, + ) + is False, + "Phase 0.9F was allowed without implementation", + ) + + @case("17 Phase 0.9F is blocked by component dependency") + def _() -> None: + require( + validator.phase09f_design_allowed( + actual_package_available=True, + independent_from_elfldr=False, + independent_from_payload_manager=True, + no_live_replacement=True, + ) + is False, + "Phase 0.9F was allowed with elfldr dependency", + ) + + @case("18 no target source is added") + def _() -> None: + errors = validator.phase09e_path_errors( + {"docs/runtime/phase-0.9e-note.md", "src/backends/ps5/rescue.c"} + ) + require( + any("target path" in error or "target artifact/source" in error for error in errors), + "target source guard did not fire", + ) + require( + not validator.phase09e_path_errors( + {"docs/runtime/phase-0.9e-note.md"} + ), + "documentation was rejected as target source", + ) + + @case("19 no target artifact is added") + def _() -> None: + require( + bool( + validator.phase09e_path_errors( + {"packaging/phase09e/rescue.elf"} + ) + ), + "target artifact guard did not fire", + ) + + @case("20 every authorization field remains false") + def _() -> None: + require( + all( + manifest["authorization"].get(field) is False + for field in validator.AUTHORIZATION_FIELDS + ), + "authorization field became true", + ) + changed = copy.deepcopy(manifest) + changed["authorization"]["transfer_authorized"] = True + require( + bool(validator.validate_manifest(changed)), + "true authorization was accepted", + ) + + failures: list[str] = [] + for name, function in cases: + try: + function() + except Exception as error: # noqa: BLE001 - test harness reports all cases. + failures.append(f"{name}: {error}") + if len(cases) != 20: + failures.append(f"expected 20 cases, found {len(cases)}") + if failures: + for failure in failures: + print(f"FAIL: {failure}") + return 1 + print("Phase-0.9E bootstrap guardrails: 20/20 PASS") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_phase09er2_correlation.py b/tests/test_phase09er2_correlation.py new file mode 100644 index 0000000..c7f6a05 --- /dev/null +++ b/tests/test_phase09er2_correlation.py @@ -0,0 +1,404 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Twenty-four host-only guardrails for Phase-0.9E-R2.""" + +from __future__ import annotations + +import argparse +from io import BytesIO +import hashlib +import importlib.util +from pathlib import Path +import sys +import tempfile +from types import ModuleType +from typing import Callable +import zipfile + + +def load_module(name: str, path: Path) -> ModuleType: + spec = importlib.util.spec_from_file_location(name, path) + if spec is None or spec.loader is None: + raise RuntimeError(f"could not load {path}") + module = importlib.util.module_from_spec(spec) + sys.modules[name] = module + spec.loader.exec_module(module) + return module + + +def require(condition: bool, message: str) -> None: + if not condition: + raise RuntimeError(message) + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + root = args.root.resolve() + validator = load_module( + "phase09er2_validator", + root / "tools/validate_phase09er2_correlation.py", + ) + inspector = load_module( + "phase09er2_inspector", root / "tools/inspect_siecaf_header.py" + ) + manifest = validator.load_json( + root / "manifests/runtime/phase-0.9e-r2-inner-correlation.json" + ) + fingerprints = validator.load_json( + root / "manifests/runtime/phase-0.9e-r2-siecaf-fingerprints.json" + ) + cases: list[tuple[str, Callable[[], None]]] = [] + + def case(name: str) -> Callable[[Callable[[], None]], Callable[[], None]]: + def register(function: Callable[[], None]) -> Callable[[], None]: + cases.append((name, function)) + return function + + return register + + def build_archive( + *, + count: int = 1, + metadata_ids: list[int] | None = None, + offsets: list[int] | None = None, + payload_byte: int = 0xA5, + ) -> bytes: + alignment = inspector.ALIGNMENT + file_offset = alignment + file_size = count * alignment + metadata_ids = metadata_ids or list(range(10000, 10000 + count)) + offsets = offsets or [file_offset + index * alignment for index in range(count)] + header = inspector.HEADER.pack( + inspector.MAGIC, + 1, + 1, + 0, + 3, + 0, + bytes(range(16)), + bytes(range(12)), + 0, + count, + file_offset, + file_size, + ) + metadata = bytearray() + hashes = bytearray() + for index in range(count): + metadata.extend( + inspector.SEGMENT_META.pack( + metadata_ids[index], + 0, + 0, + offsets[index], + alignment, + 3, + 1, + b"\x00" * 12, + 0, + 123, + ) + ) + hashes.extend( + inspector.SECTION_HASH.pack( + index, + 0, + bytes([index + 1]) * 16, + b"\x00" * 24, + ) + ) + tables = header + metadata + hashes + return ( + tables + + b"\x00" * (file_offset - len(tables)) + + bytes([payload_byte]) * file_size + ) + + @case("01 outer mismatch does not exclude an inner match") + def _() -> None: + require( + "a" * 64 != "b" * 64 + and validator.inner_byte_match( + left_size=10, + right_size=10, + left_sha256="c" * 64, + right_sha256="c" * 64, + full_byte_equal=True, + ), + "outer mismatch incorrectly excluded an independent inner match", + ) + + @case("02 outer filename is not inner provenance") + def _() -> None: + require( + not validator.inner_byte_match( + left_size=10, + right_size=10, + left_sha256="a" * 64, + right_sha256="b" * 64, + full_byte_equal=False, + ), + "filename-equivalent containers established inner provenance", + ) + + @case("03 recompression changes outer bytes without changing inner") + def _() -> None: + payload = b"same-inner-content" * 128 + with tempfile.TemporaryDirectory() as directory: + first = Path(directory) / "first.zip" + second = Path(directory) / "second.zip" + with zipfile.ZipFile(first, "w", compression=zipfile.ZIP_STORED) as out: + out.writestr("archive.dat", payload) + with zipfile.ZipFile(second, "w", compression=zipfile.ZIP_DEFLATED) as out: + out.writestr("archive.dat", payload) + require( + hashlib.sha256(first.read_bytes()).digest() + != hashlib.sha256(second.read_bytes()).digest(), + "different ZIP encodings unexpectedly matched", + ) + with zipfile.ZipFile(first) as left, zipfile.ZipFile(second) as right: + require( + left.read("archive.dat") == right.read("archive.dat"), + "recompression changed the inner bytes", + ) + + @case("04 inner byte match requires size hash and complete bytes") + def _() -> None: + require( + validator.inner_byte_match( + left_size=1, + right_size=1, + left_sha256="a" * 64, + right_sha256="a" * 64, + full_byte_equal=True, + ) + and not validator.inner_byte_match( + left_size=1, + right_size=1, + left_sha256="a" * 64, + right_sha256="a" * 64, + full_byte_equal=False, + ), + "complete-byte requirement failed", + ) + + @case("05 size-only match is not a byte match") + def _() -> None: + require( + validator.classify_inner_comparison( + download_valid=True, + inner_present=True, + left_size=10, + right_size=10, + left_sha256="a" * 64, + right_sha256="b" * 64, + full_byte_equal=False, + ) + == "INNER_ARCHIVE_SIZE_ONLY_MATCH", + "size-only result was promoted", + ) + + @case("06 structural equality is not content equality") + def _() -> None: + left = build_archive(payload_byte=0x11) + right = build_archive(payload_byte=0x22) + left_result = inspector.inspect_siecaf(BytesIO(left), len(left)) + right_result = inspector.inspect_siecaf(BytesIO(right), len(right)) + require( + inspector.compare_structures(left_result, right_result)["classification"] + == "SIECAF_STRUCTURAL_EXACT" + and hashlib.sha256(left).digest() != hashlib.sha256(right).digest(), + "structure was treated as decrypted/content identity", + ) + + @case("07 malformed segment table is rejected") + def _() -> None: + malformed = build_archive()[: inspector.HEADER.size + 5] + result = inspector.inspect_siecaf(BytesIO(malformed), len(malformed)) + require(result["classification"] == "SIECAF_MALFORMED", str(result)) + + @case("08 integer overflow is rejected") + def _() -> None: + header = inspector.HEADER.pack( + inspector.MAGIC, + 1, + 1, + 0, + 3, + 0, + b"\x00" * 16, + b"\x00" * 12, + 0, + inspector.UINT64_MAX, + inspector.ALIGNMENT, + inspector.ALIGNMENT, + ) + result = inspector.inspect_siecaf(BytesIO(header), len(header)) + require(result["classification"] == "SIECAF_MALFORMED", str(result)) + + @case("09 out-of-range offset is rejected") + def _() -> None: + value = build_archive(offsets=[inspector.ALIGNMENT * 2]) + result = inspector.inspect_siecaf(BytesIO(value), len(value)) + require(result["classification"] == "SIECAF_MALFORMED", str(result)) + + @case("10 overlapping ranges are reported") + def _() -> None: + value = build_archive( + count=2, offsets=[inspector.ALIGNMENT, inspector.ALIGNMENT] + ) + result = inspector.inspect_siecaf(BytesIO(value), len(value)) + require( + result["classification"] == "SIECAF_MALFORMED" and result["overlaps"], + str(result), + ) + + @case("11 duplicate section identity is reported") + def _() -> None: + value = build_archive(count=2, metadata_ids=[10000, 10000]) + result = inspector.inspect_siecaf(BytesIO(value), len(value)) + require( + result["classification"] == "SIECAF_MALFORMED" + and result["duplicate_metadata_section_keys"], + str(result), + ) + + @case("12 official inner match would not make MediaFire outer official") + def _() -> None: + require( + manifest["outer_zip_official"] is False, + "outer MediaFire ZIP was promoted to official", + ) + + @case("13 community source family is not an official community asset") + def _() -> None: + itsplk = manifest["community_families"][0] + require( + itsplk["classification"] == "THIRD_PARTY_BUILD_FROM_PUBLIC_SOURCE_POSSIBLE" + and itsplk["system_backup_distributed_by_project"] is False, + "third-party build possibility became an official release", + ) + + @case("14 browser history is not fully exported") + def _() -> None: + browser = manifest["local_download_provenance"]["browser_history"] + require( + browser["full_history_exported"] is False + and browser["matching_records"] == 0, + "browser-history boundary changed", + ) + + @case("15 signed URL material is redacted") + def _() -> None: + host = manifest["local_download_provenance"]["zone_identifier"]["host_url"] + require( + "" in host["path_redacted"] + and len(host["full_value_sha256"]) == 64, + "signed path was not safely represented", + ) + + @case("16 no large backup is tracked") + def _() -> None: + for relative in validator.git(root, "ls-files").splitlines(): + path = root / relative + require( + not path.is_file() + or path.stat().st_size <= validator.MAX_TRACKED_FILE_SIZE, + f"large tracked file: {relative}", + ) + + @case("17 no downloaded file was executed") + def _() -> None: + require( + manifest["actions"]["downloaded_file_executed"] is False, + "download execution was recorded", + ) + + @case("18 ps5-bar-tool was not executed") + def _() -> None: + require( + manifest["actions"]["ps5_bar_tool_executed"] is False + and fingerprints["parser_evidence"]["ps5_bar_tool_executed"] is False, + "ps5-bar-tool execution was recorded", + ) + + @case("19 no PS5 hostname or IP was used") + def _() -> None: + require( + manifest["actions"]["ps5_connected"] is False + and manifest["actions"]["ps5_ip_used"] is False, + "PS5 network use was recorded", + ) + + @case("20 no target source appears") + def _() -> None: + changed = set( + filter( + None, + validator.git( + root, + "diff", + "--name-only", + "--diff-filter=ACMR", + validator.BASELINE, + ).splitlines(), + ) + ) + for relative in changed: + normalized = relative.replace("\\", "/") + require( + not normalized.startswith(validator.FORBIDDEN_PREFIXES) + and Path(normalized).suffix.lower() not in validator.FORBIDDEN_SUFFIXES, + f"target/binary material appeared: {normalized}", + ) + + @case("21 every authorization remains false") + def _() -> None: + for value in (manifest, fingerprints): + for field in validator.AUTHORIZATION_FIELDS: + require( + value["authorization"][field] is False, + f"authorization changed: {field}", + ) + + @case("22 automatic retry remains false") + def _() -> None: + require( + manifest["authorization"]["automatic_retry"] is False + and manifest["actions"]["automatic_retry_used"] is False, + "automatic retry was enabled or used", + ) + + @case("23 outer metadata alone cannot open Phase 0.9F") + def _() -> None: + require( + not validator.phase09f_reconsideration_allowed( + inner_source_bound=False, + mediafire_maker_source_bound=False, + auditable_bootstrap_closure=True, + ), + "outer metadata opened Phase 0.9F", + ) + + @case("24 runtime deployment remains unproven") + def _() -> None: + require( + manifest["runtime_deployment_verified"] is False + and manifest["current_device_contents_verified"] is False + and manifest["runtime_firmware_9_60"] == "UNPROVEN", + "host-only evidence became runtime proof", + ) + + for name, function in cases: + try: + function() + except Exception as error: + raise RuntimeError(f"{name}: {error}") from error + print(f"Phase-0.9E-R2 guardrails: {len(cases)}/{len(cases)} PASS") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_phase09er_provenance.py b/tests/test_phase09er_provenance.py new file mode 100644 index 0000000..2b76aa0 --- /dev/null +++ b/tests/test_phase09er_provenance.py @@ -0,0 +1,323 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Twenty-two host-only guardrails for Phase 0.9E-R.""" + +from __future__ import annotations + +import argparse +import copy +import importlib.util +from pathlib import Path +import sys +from types import ModuleType +from typing import Callable + + +def load_module(name: str, path: Path) -> ModuleType: + spec = importlib.util.spec_from_file_location(name, path) + if spec is None or spec.loader is None: + raise RuntimeError(f"could not load {path}") + module = importlib.util.module_from_spec(spec) + sys.modules[name] = module + spec.loader.exec_module(module) + return module + + +def require(condition: bool, message: str) -> None: + if not condition: + raise RuntimeError(message) + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + root = args.root.resolve() + validator = load_module( + "phase09er_validator", root / "tools/validate_phase09er_provenance.py" + ) + manifest = validator.load_json( + root / "manifests/runtime/phase-0.9e-r-release-correlation.json" + ) + port_manifest = validator.load_json( + root / "manifests/runtime/phase-0.9e-r-port9020-audit.json" + ) + cases: list[tuple[str, Callable[[], None]]] = [] + + def case(name: str) -> Callable[[Callable[[], None]], Callable[[], None]]: + def register(function: Callable[[], None]) -> Callable[[], None]: + cases.append((name, function)) + return function + + return register + + @case("01 name match alone is not a byte match") + def _() -> None: + require( + not validator.official_byte_match( + official_source=True, + local_name="same.zip", + local_size=1, + local_sha256="a" * 64, + asset_name="same.zip", + asset_size=2, + asset_sha256="b" * 64, + ), + "name-only candidate became a byte match", + ) + + @case("02 size match alone is not a byte match") + def _() -> None: + require( + not validator.official_byte_match( + official_source=True, + local_name="local.zip", + local_size=10, + local_sha256="a" * 64, + asset_name="asset.zip", + asset_size=10, + asset_sha256="b" * 64, + ), + "size-only candidate became a byte match", + ) + + @case("03 hash match also requires exact byte count") + def _() -> None: + require( + not validator.official_byte_match( + official_source=True, + local_name="same.zip", + local_size=10, + local_sha256="a" * 64, + asset_name="same.zip", + asset_size=11, + asset_sha256="a" * 64, + ), + "hash with a different byte count became a match", + ) + + @case("04 a mirror cannot establish an official byte match") + def _() -> None: + require( + not validator.official_byte_match( + official_source=False, + local_name="same.zip", + local_size=10, + local_sha256="a" * 64, + asset_name="same.zip", + asset_size=10, + asset_sha256="a" * 64, + ), + "non-official mirror established an official match", + ) + + @case("05 release association is not inner-content binding") + def _() -> None: + require( + validator.source_binding( + release_associated=True, + inner_bytes_matched=False, + inner_opaque=True, + ) + == "SOURCE_ONLY_ASSOCIATION", + "release association was promoted to reproducible content", + ) + + @case("06 opaque SIECAF receives no invented provenance") + def _() -> None: + require( + manifest["inner_archive"]["classification"] == "OPAQUE_UNBOUND" + and manifest["inner_archive"]["further_reverse_engineering_performed"] + is False, + "opaque inner archive provenance was overclaimed", + ) + + @case("07 public release commits are exact") + def _() -> None: + for tag in manifest["tags"]: + require( + len(tag["commit"]) == 40 + and all(character in "0123456789abcdef" for character in tag["commit"]), + f"release commit is not exact: {tag['tag']}", + ) + + @case("08 each source archive is hashed") + def _() -> None: + for tag in manifest["tags"]: + require( + len(tag["source_archive_sha256"]) == 64, + f"source archive hash missing: {tag['tag']}", + ) + + @case("09 upstream worktree must be clean") + def _() -> None: + changed = copy.deepcopy(manifest) + changed["upstream_worktree"]["clean"] = False + require( + bool(validator.validate_release_manifest(changed)), + "dirty upstream worktree passed", + ) + + @case("10 port reference is not an implementation") + def _() -> None: + require( + not validator.port_implementation_sufficient("PORT_9020_REFERENCE_ONLY"), + "reference-only text was accepted as implementation", + ) + + @case("11 embedded bytes receive no invented source commit") + def _() -> None: + changed = copy.deepcopy(port_manifest) + changed["port_9021_relation"]["source_commit"] = "0" * 40 + require( + bool(validator.validate_port_manifest(changed)), + "embedded loader bytes accepted an invented source commit", + ) + + @case("12 a sender without receive is not duplex") + def _() -> None: + require( + not validator.sender_duplex(sends=True, receives=False), + "one-way sender became duplex", + ) + + @case("13 missing short-send handling is a deficiency") + def _() -> None: + require( + validator.sender_short_send_deficiency( + uses_sendall=False, explicit_send_loop=False + ), + "missing short-send handling was accepted", + ) + require( + not validator.sender_short_send_deficiency( + uses_sendall=True, explicit_send_loop=False + ), + "sendall was incorrectly marked as lacking short-send handling", + ) + + @case("14 operator attestation is not runtime evidence") + def _() -> None: + require( + not validator.attestation_is_runtime_proof( + attested=True, hardware_observed=False + ), + "operator attestation became runtime proof", + ) + + @case("15 empty attestation authorizes nothing") + def _() -> None: + text = ( + root + / "docs/approvals/phase-0.9e-r-y2jb-deployed-use-attestation.md" + ).read_text(encoding="utf-8") + require("attested: false" in text, "template became attested") + for field in validator.AUTHORIZATION_FIELDS: + require(f"{field}: false" in text, f"template omits false {field}") + + @case("16 Phase 0.9F blocks without official match") + def _() -> None: + require( + not validator.phase09f_design_allowed( + correlation="OFFICIAL_RELEASE_NO_MATCH", + release_commit_known=True, + upstream_clean=True, + port_classification="PORT_9020_IMPLEMENTATION_FOUND", + sender_identified=True, + attestation_available=True, + all_authorizations_false=True, + ), + "Phase 0.9F passed without an official asset match", + ) + + @case("17 Phase 0.9F blocks without found or partial loader") + def _() -> None: + require( + not validator.phase09f_design_allowed( + correlation="OFFICIAL_RELEASE_BYTE_MATCH", + release_commit_known=True, + upstream_clean=True, + port_classification="PORT_9020_REFERENCE_ONLY", + sender_identified=True, + attestation_available=True, + all_authorizations_false=True, + ), + "Phase 0.9F passed with a reference-only loader", + ) + + @case("18 no target source appears") + def _() -> None: + errors = validator.phase09er_path_errors( + {"docs/runtime/phase-0.9e-r-note.md", "src/backends/ps5/rescue.c"} + ) + require(any("target" in error for error in errors), "target source guard failed") + require( + not validator.phase09er_path_errors( + {"docs/runtime/phase-0.9e-r-note.md"} + ), + "documentation was rejected as target source", + ) + + @case("19 no target artifact appears") + def _() -> None: + require( + bool(validator.phase09er_path_errors({"packaging/rescue.elf"})), + "target artifact guard failed", + ) + + @case("20 all authorization fields remain false") + def _() -> None: + changed = copy.deepcopy(manifest) + changed["authorization"]["execution_authorized"] = True + require( + bool(validator.validate_release_manifest(changed)), + "true authorization passed", + ) + + @case("21 automatic retry remains false") + def _() -> None: + require( + manifest["authorization"]["automatic_retry"] is False + and port_manifest["authorization"]["automatic_retry"] is False + and port_manifest["official_remote_js_loader"][ + "automatic_retry_authorized" + ] + is False, + "automatic retry was authorized", + ) + + @case("22 no large release asset enters Git") + def _() -> None: + actions = manifest["actions"] + require( + actions["large_release_assets_downloaded"] == 0 + and actions["large_release_asset_bytes_downloaded"] == 0, + "a large official release asset was recorded as downloaded", + ) + for path in root.rglob("*"): + if ( + path.is_file() + and ".git" not in path.parts + and "work" not in path.parts + ): + require( + path.stat().st_size <= 50 * 1024 * 1024, + f"large release-like file entered repository: {path}", + ) + + failures: list[str] = [] + for name, function in cases: + try: + function() + except Exception as error: # guardrail harness reports all failures + failures.append(f"{name}: {error}") + if failures: + for failure in failures: + print(f"FAIL: {failure}") + return 1 + print(f"Phase-0.9E-R provenance guardrails: {len(cases)}/{len(cases)} PASS") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_phase10aa_offline_fake_batch.py b/tests/test_phase10aa_offline_fake_batch.py new file mode 100644 index 0000000..51f94c4 --- /dev/null +++ b/tests/test_phase10aa_offline_fake_batch.py @@ -0,0 +1,226 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Host-only integration tests for the Phase-1.0AA exact fake adapter.""" + +from __future__ import annotations + +import argparse +from datetime import datetime, timezone +import importlib.util +import json +from pathlib import Path +import sys +import tempfile + + +CURRENT_COMMANDS = "authid browse cat cd chgrp chmod chown chroot cmp cp df echo env exec exit export file find grep hbdbg hbldr hexdump http2_get id kill launch ln ls mkdir mknod mount mv notify pkg_install procstat ps pwd reptyr rm rmdir sfocreate sfoinfo sleep stat sum suspend sync sysctl touch umount".split() + + +def load(path: Path): + spec = importlib.util.spec_from_file_location("phase10aa_fake", path) + assert spec and spec.loader + module = importlib.util.module_from_spec(spec) + sys.modules[spec.name] = module + spec.loader.exec_module(module) + return module + + +def require(value: bool, message: str) -> None: + if not value: + raise RuntimeError(message) + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + root = args.root.resolve() + sys.path.insert(0, str(root / "tools")) + module = load(root / "tools/phase10aa_offline_fake_batch.py") + from phase10w_shsrv_client_policy import SessionPlan + + def plan(window="T2_GREETING_AND_HELP", path=None): + commands = ("help",) if window == "T2_GREETING_AND_HELP" else ("stat", "sum") + return SessionPlan("synthetic_aa", "must-not-persist.invalid", 2323, + window, path, commands, 10, + datetime(2030, 1, 1, tzinfo=timezone.utc)) + + def greeting(extra="", eol="\n"): + text = ("Welcome to shsrv.elf running on pid 1, compiled Jul 22 2026 at 12:34:56\n" + "Model: synthetic\nS/N: SECRET-SERIAL\nS/W: 9.60\n" + "SoC temp: 40 C\nCPU temp: 41 C\nCPU freq: 3500 MHz\n" + extra) + return text.replace("\n", eol).encode("ascii") + + def help_text(): + return "Builtin commands:\n" + "".join( + f" {command} - synthetic\n" for command in CURRENT_COMMANDS) + "\n" + + def events(data, data_advance=1.0, deadline_advance=9.0): + return (module.FakeReceiveEvent(module.EVENT_DATA, data, data_advance), + module.FakeReceiveEvent(module.EVENT_HARD_DEADLINE, b"", deadline_advance)) + + def execute(directory, session_plan=None, scripted=None): + p = session_plan or plan(); clock = module.OfflineFakeClock() + adapter = module.OfflineFakeBatchAdapter(clock, scripted or events(greeting(help_text()))) + store = module.OfflineFakeEvidenceStore(Path(directory)) + return module.run_offline_fake_batch(p, adapter, clock, store), adapter + + def expect_failure(function): + try: function() + except (module.OfflineIntegrationError, module.FakeAdapterError): return + raise RuntimeError("invalid fake integration was accepted") + + cases = [] + def case(name): + def register(function): cases.append((name, function)); return function + return register + + @case("01 exact fake help integration succeeds") + def _(): + with tempfile.TemporaryDirectory() as d: + outcome, adapter = execute(d) + require(outcome.classification == "SOURCE_FAMILY_FINGERPRINT_ONLY" and adapter.send_count == 1, "help integration failed") + + @case("02 exact path integration sends one batch") + def _(): + path = "/data/a.elf"; data = greeting(f"filename: {path}\nsize: 123\n12345 {path}\n") + with tempfile.TemporaryDirectory() as d: + outcome, adapter = execute(d, plan("T3_ONE_EXACT_PATH", path), events(data)) + require(outcome.classification == "WEAK_FILE_CORRELATION_ONLY" and adapter.send_count == 1, "path integration failed") + + @case("03 receipt precedes fake open") + def _(): + with tempfile.TemporaryDirectory() as d: + outcome, _ = execute(d) + require(outcome.trace[:3] == ("RECEIPT_CREATED", "FAKE_OPEN", "FAKE_SEND_ONE_BATCH"), "ordering mismatch") + + @case("04 deadline is the only seal event") + def _(): + with tempfile.TemporaryDirectory() as d: + outcome, _ = execute(d) + output = json.loads(outcome.output.path.read_text()) + require(output["result"]["passive_batch_contract"]["sealed_by_synthetic_hard_deadline"] is True, "deadline seal missing") + + @case("05 early deadline fails") + def _(): + scripted = (module.FakeReceiveEvent(module.EVENT_DATA, greeting(help_text()), 1), module.FakeReceiveEvent(module.EVENT_HARD_DEADLINE, b"", 1)) + with tempfile.TemporaryDirectory() as d: expect_failure(lambda: execute(d, scripted=scripted)) + + @case("06 remote EOF fails") + def _(): + scripted = (module.FakeReceiveEvent(module.EVENT_DATA, greeting(help_text()), 1), module.FakeReceiveEvent(module.EVENT_REMOTE_EOF, b"", 1)) + with tempfile.TemporaryDirectory() as d: expect_failure(lambda: execute(d, scripted=scripted)) + + @case("07 blocked fake receive fails") + def _(): + scripted = (module.FakeReceiveEvent(module.EVENT_BLOCKED, b"", 10),) + with tempfile.TemporaryDirectory() as d: expect_failure(lambda: execute(d, scripted=scripted)) + + @case("08 data at deadline fails") + def _(): + scripted = (module.FakeReceiveEvent(module.EVENT_DATA, greeting(help_text()), 10),) + with tempfile.TemporaryDirectory() as d: expect_failure(lambda: execute(d, scripted=scripted)) + + @case("09 missing deadline event fails") + def _(): + scripted = (module.FakeReceiveEvent(module.EVENT_DATA, greeting(help_text()), 1),) + with tempfile.TemporaryDirectory() as d: expect_failure(lambda: execute(d, scripted=scripted)) + + @case("10 incoming IAC fails") + def _(): + with tempfile.TemporaryDirectory() as d: expect_failure(lambda: execute(d, scripted=events(b"x\xffy"))) + + @case("11 partial help fails at deadline") + def _(): + with tempfile.TemporaryDirectory() as d: expect_failure(lambda: execute(d, scripted=events(greeting("Builtin commands:\n help - partial\n\n")))) + + @case("12 CRLF transcript succeeds") + def _(): + with tempfile.TemporaryDirectory() as d: + outcome, _ = execute(d, scripted=events(greeting(help_text(), "\r\n"))) + require(outcome.exact_identity is False, "CRLF result promoted") + + @case("13 close occurs after success") + def _(): + with tempfile.TemporaryDirectory() as d: + _, adapter = execute(d); require(adapter.close_count == 1 and adapter.state == "CLOSED", "success not closed") + + @case("14 close occurs after failure") + def _(): + clock = module.OfflineFakeClock(); adapter = module.OfflineFakeBatchAdapter(clock, (module.FakeReceiveEvent(module.EVENT_REMOTE_EOF),)) + with tempfile.TemporaryDirectory() as d: + store = module.OfflineFakeEvidenceStore(Path(d)); expect_failure(lambda: module.run_offline_fake_batch(plan(), adapter, clock, store)) + require(adapter.close_count == 1 and adapter.state == "CLOSED", "failure not closed") + + @case("15 second fake send is impossible") + def _(): + with tempfile.TemporaryDirectory() as d: + _, adapter = execute(d); expect_failure(lambda: adapter.send_one_batch(None)) + + @case("16 exclusive receipt collision fails") + def _(): + with tempfile.TemporaryDirectory() as d: + execute(d); expect_failure(lambda: execute(d)) + + @case("17 target is absent from evidence") + def _(): + with tempfile.TemporaryDirectory() as d: + outcome, _ = execute(d); combined = outcome.receipt.path.read_text() + outcome.output.path.read_text() + require("must-not-persist" not in combined and "target_address" not in combined, "target retained") + + @case("18 serial and raw transcript are absent from output") + def _(): + with tempfile.TemporaryDirectory() as d: + outcome, _ = execute(d); value = outcome.output.path.read_text() + require("SECRET-SERIAL" not in value and "Welcome to shsrv" not in value and '"raw_transcript_persisted":false' in value, "sensitive input retained") + + @case("19 receipt binds batch hash and size") + def _(): + with tempfile.TemporaryDirectory() as d: + outcome, _ = execute(d); receipt = json.loads(outcome.receipt.path.read_text()) + require(receipt["batch_sha256"] == outcome.batch_sha256 and receipt["batch_size"] == 5, "batch binding mismatch") + + @case("20 failure leaves consumed receipt and no output") + def _(): + with tempfile.TemporaryDirectory() as d: + expect_failure(lambda: execute(d, scripted=(module.FakeReceiveEvent(module.EVENT_REMOTE_EOF),))) + files = sorted(path.name for path in Path(d).iterdir()) + require(files == ["synthetic_aa.aa-consumed.json"], "failure evidence mismatch") + + @case("21 fake event buffer is logically discarded") + def _(): + with tempfile.TemporaryDirectory() as d: + outcome, adapter = execute(d); output = json.loads(outcome.output.path.read_text()) + require(adapter.logical_event_buffer_discarded and output["result"]["phase10aa_fake_integration"]["physical_memory_erasure_proven"] is False, "erasure promoted") + + @case("22 exact identity and device proof stay false") + def _(): + with tempfile.TemporaryDirectory() as d: + outcome, _ = execute(d); require(outcome.exact_identity is False and outcome.device_behavior_proven is False, "proof promoted") + + @case("23 custom adapter is rejected") + def _(): + class Custom: pass + clock = module.OfflineFakeClock() + with tempfile.TemporaryDirectory() as d: + store = module.OfflineFakeEvidenceStore(Path(d)); expect_failure(lambda: module.run_offline_fake_batch(plan(), Custom(), clock, store)) + + @case("24 fake event validation is bounded") + def _(): + expect_failure(lambda: module.FakeReceiveEvent(module.EVENT_DATA, b"x", float("inf"))) + + @case("25 no live API or address exists") + def _(): + names = set(dir(module)); require(not ({"connect", "send", "recv", "main"} & names), "live API exists") + + failures = [] + for name, function in cases: + try: function(); print(f"PASS {name}") + except Exception as error: failures.append(f"{name}: {error}"); print(f"FAIL {name}: {error}") + if failures: return 1 + print(f"Phase-1.0AA offline fake-batch tests passed: {len(cases)}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_phase10ab_nonblocking_trace_model.py b/tests/test_phase10ab_nonblocking_trace_model.py new file mode 100644 index 0000000..b0bb40e --- /dev/null +++ b/tests/test_phase10ab_nonblocking_trace_model.py @@ -0,0 +1,120 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Synthetic tests for Phase-1.0AB nonblocking lifecycle traces.""" + +from __future__ import annotations + +import argparse +import importlib.util +from pathlib import Path +import sys + + +def load(path: Path): + spec = importlib.util.spec_from_file_location("phase10ab_trace", path) + assert spec and spec.loader + module = importlib.util.module_from_spec(spec); sys.modules[spec.name] = module + spec.loader.exec_module(module); return module + + +def main() -> int: + parser = argparse.ArgumentParser(); parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args(); module = load(args.root.resolve() / "tools/phase10ab_nonblocking_trace_model.py") + + def event(op, at, value=0): return module.TraceEvent(op, at, value) + def valid(pending=True, sends=(2, 3), waits=()): + values = [event(module.RECEIPT_CREATED, 0), event(module.SOCKET_CREATED, 0), event(module.SET_NONBLOCKING, 0)] + if pending: + values += [event(module.CONNECT_PENDING, .1), event(module.READY_WRITE, .2), event(module.SO_ERROR_ZERO, .2)] + else: values += [event(module.CONNECT_IMMEDIATE, .1)] + at = .3 + for count in sends: + values += [event(module.READY_WRITE, at), event(module.SEND_BYTES, at, count)]; at += .1 + for op in waits: values.append(event(op, at)); at += .1 + values += [event(module.READY_READ, at), event(module.RECV_BYTES, at, 100), event(module.DEADLINE_REACHED, 10), event(module.SANITIZER_ACCEPTED, 10), event(module.LOCAL_CLOSE, 10), event(module.OUTPUT_CREATED, 10)] + return tuple(values) + def expect_failure(function): + try: function() + except module.TraceModelError: return + raise RuntimeError("invalid trace accepted") + cases = [] + def case(name): + def register(function): cases.append((name, function)); return function + return register + + @case("01 pending connect complete trace succeeds") + def _(): + result = module.assess_nonblocking_trace(5, 10, valid()); assert result.batch_bytes_sent == 5 + @case("02 immediate connect complete trace succeeds") + def _(): assert module.assess_nonblocking_trace(5, 10, valid(False)).classification == "OFFLINE_NONBLOCKING_SEQUENCE_FEASIBLE" + @case("03 partial sends require repeated readiness") + def _(): assert module.assess_nonblocking_trace(5, 10, valid(sends=(1, 1, 3))).complete_send_loop + @case("04 interrupted waits are recomputed") + def _(): assert module.assess_nonblocking_trace(5, 10, valid(waits=(module.WAIT_INTERRUPTED,))).deadline_only_completion + @case("05 timeout before deadline is not completion") + def _(): assert module.assess_nonblocking_trace(5, 10, valid(waits=(module.WAIT_TIMEOUT,))).local_close_observed + @case("06 receipt must precede creation") + def _(): expect_failure(lambda: module.assess_nonblocking_trace(5, 10, valid()[1:])) + @case("07 nonblocking must precede connect") + def _(): + trace = tuple(e for e in valid() if e.operation != module.SET_NONBLOCKING); expect_failure(lambda: module.assess_nonblocking_trace(5, 10, trace)) + @case("08 pending connect requires readiness") + def _(): + trace = tuple(e for e in valid() if e.operation != module.READY_WRITE or e.at_seconds != .2); expect_failure(lambda: module.assess_nonblocking_trace(5, 10, trace)) + @case("09 pending connect requires SO_ERROR") + def _(): + trace = tuple(e for e in valid() if e.operation != module.SO_ERROR_ZERO); expect_failure(lambda: module.assess_nonblocking_trace(5, 10, trace)) + @case("10 send requires write readiness") + def _(): + trace = tuple(e for e in valid() if not (e.operation == module.READY_WRITE and e.at_seconds == .3)); expect_failure(lambda: module.assess_nonblocking_trace(5, 10, trace)) + @case("11 send cannot exceed exact batch") + def _(): expect_failure(lambda: module.assess_nonblocking_trace(5, 10, valid(sends=(6,)))) + @case("12 incomplete send fails") + def _(): expect_failure(lambda: module.assess_nonblocking_trace(5, 10, valid(sends=(2, 2)))) + @case("13 receive requires read readiness") + def _(): + trace = tuple(e for e in valid() if e.operation != module.READY_READ); expect_failure(lambda: module.assess_nonblocking_trace(5, 10, trace)) + @case("14 receive bound is enforced") + def _(): + trace = list(valid()); index = next(i for i,e in enumerate(trace) if e.operation == module.RECV_BYTES); trace[index] = event(module.RECV_BYTES, trace[index].at_seconds, 65537); expect_failure(lambda: module.assess_nonblocking_trace(5, 10, tuple(trace))) + @case("15 EOF never completes") + def _(): + trace = list(valid()); index = next(i for i,e in enumerate(trace) if e.operation == module.RECV_BYTES); trace[index] = event(module.RECV_EOF, trace[index].at_seconds); expect_failure(lambda: module.assess_nonblocking_trace(5, 10, tuple(trace))) + @case("16 early deadline fails") + def _(): + trace = tuple(event(e.operation, 9 if e.operation in {module.DEADLINE_REACHED,module.SANITIZER_ACCEPTED,module.LOCAL_CLOSE,module.OUTPUT_CREATED} else e.at_seconds, e.value) for e in valid()); expect_failure(lambda: module.assess_nonblocking_trace(5, 10, trace)) + @case("17 data at deadline fails") + def _(): + trace = list(valid()); index = next(i for i,e in enumerate(trace) if e.operation == module.RECV_BYTES); trace[index] = event(module.RECV_BYTES, 10, 100); expect_failure(lambda: module.assess_nonblocking_trace(5, 10, tuple(trace))) + @case("18 deadline requires received data") + def _(): + trace = tuple(e for e in valid() if e.operation not in {module.READY_READ,module.RECV_BYTES}); expect_failure(lambda: module.assess_nonblocking_trace(5, 10, trace)) + @case("19 sanitizer must follow deadline") + def _(): + trace = tuple(e for e in valid() if e.operation != module.DEADLINE_REACHED); expect_failure(lambda: module.assess_nonblocking_trace(5, 10, trace)) + @case("20 close must follow sanitizer") + def _(): + trace = tuple(e for e in valid() if e.operation != module.SANITIZER_ACCEPTED); expect_failure(lambda: module.assess_nonblocking_trace(5, 10, trace)) + @case("21 output must follow close") + def _(): + trace = tuple(e for e in valid() if e.operation != module.LOCAL_CLOSE); expect_failure(lambda: module.assess_nonblocking_trace(5, 10, trace)) + @case("22 backward time fails") + def _(): + trace = list(valid()); trace[2] = event(module.SET_NONBLOCKING, -.0); trace[1] = event(module.SOCKET_CREATED, .1); expect_failure(lambda: module.assess_nonblocking_trace(5, 10, tuple(trace))) + @case("23 trace event bound is enforced") + def _(): expect_failure(lambda: module.assess_nonblocking_trace(5, 10, tuple(event(module.RECEIPT_CREATED, 0) for _ in range(513)))) + @case("24 result never claims device proof") + def _(): + result = module.assess_nonblocking_trace(5, 10, valid()); assert not result.device_behavior_proven and not result.live_transport_present + @case("25 no live API exists") + def _(): assert not ({"connect", "send", "recv", "main"} & set(dir(module))) + + failures=[] + for name,function in cases: + try: function(); print(f"PASS {name}") + except Exception as error: failures.append(f"{name}: {error}"); print(f"FAIL {name}: {error}") + if failures: return 1 + print(f"Phase-1.0AB nonblocking-trace tests passed: {len(cases)}"); return 0 + + +if __name__ == "__main__": raise SystemExit(main()) diff --git a/tests/test_phase10ac_dormant_adapter.py b/tests/test_phase10ac_dormant_adapter.py new file mode 100644 index 0000000..e4b5f10 --- /dev/null +++ b/tests/test_phase10ac_dormant_adapter.py @@ -0,0 +1,267 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Host-only tests for the Phase-1.0AC dormant fake-syscall adapter.""" + +from __future__ import annotations + +import argparse +from datetime import datetime, timezone +import importlib.util +from pathlib import Path +import sys + + +CURRENT_COMMANDS = "authid browse cat cd chgrp chmod chown chroot cmp cp df echo env exec exit export file find grep hbdbg hbldr hexdump http2_get id kill launch ln ls mkdir mknod mount mv notify pkg_install procstat ps pwd reptyr rm rmdir sfocreate sfoinfo sleep stat sum suspend sync sysctl touch umount".split() + + +def load(path: Path): + spec = importlib.util.spec_from_file_location("phase10ac_dormant", path) + assert spec and spec.loader + module = importlib.util.module_from_spec(spec) + sys.modules[spec.name] = module + spec.loader.exec_module(module) + return module + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + root = args.root.resolve() + sys.path.insert(0, str(root / "tools")) + module = load(root / "tools/phase10ac_dormant_adapter.py") + from phase10w_shsrv_client_policy import SessionPlan + from phase10z_passive_batch_contract import build_passive_batch + + def plan(window="T2_GREETING_AND_HELP", path=None): + commands = ("help",) if window == "T2_GREETING_AND_HELP" else ("stat", "sum") + return SessionPlan("synthetic_ac", "not-retained.invalid", 2323, + window, path, commands, 10, + datetime(2030, 1, 1, tzinfo=timezone.utc)) + + def greeting(extra=""): + return ("Welcome to shsrv.elf running on pid 1, compiled Jul 22 2026 at 12:34:56\n" + "Model: synthetic\nS/N: SECRET-SERIAL\nS/W: 9.60\n" + "SoC temp: 40 C\nCPU temp: 41 C\nCPU freq: 3500 MHz\n" + extra).encode("ascii") + + def help_text(): + return "Builtin commands:\n" + "".join( + f" {command} - synthetic\n" for command in CURRENT_COMMANDS) + "\n" + + def step(op, result, value=0, data=b"", advance=0.0): + return module.FakeSyscallStep(op, result, value, data, advance) + + def valid_steps(data=None, pending=True, writes=(2, 3), extra=()): + payload = data or greeting(help_text()) + values = [step(module.CREATE_STREAM, module.OK), + step(module.SET_NONBLOCKING, module.OK)] + if pending: + values += [step(module.START_CONNECT, module.PENDING), + step(module.WAIT_WRITE, module.READY, advance=.1), + step(module.GET_SO_ERROR, module.ZERO)] + else: + values += [step(module.START_CONNECT, module.IMMEDIATE, advance=.1)] + for count in writes: + values += [step(module.WAIT_WRITE, module.READY, advance=.1), + step(module.WRITE_BYTES, module.PROGRESS, count)] + values += list(extra) + values += [step(module.WAIT_READ, module.READY, advance=.1), + step(module.READ_BYTES, module.PROGRESS, len(payload), payload), + step(module.WAIT_READ, module.TIMEOUT, advance=9.6)] + return tuple(values) + + def execute(steps=None, batch=None, close_result=module.OK, receipt=True): + clock = module.OfflineFakeClock() + actual_batch = batch or build_passive_batch(plan()) + facade = module.OfflineFakeSyscallFacade( + clock, steps or valid_steps(writes=(2, 3)), close_result) + return module.run_dormant_adapter( + actual_batch, facade, clock, receipt), facade + + def expect_failure(function): + try: + function() + except (module.DormantAdapterError, module.FakeSyscallError): + return + raise RuntimeError("invalid dormant adapter scenario was accepted") + + cases = [] + def case(name): + def register(function): + cases.append((name, function)); return function + return register + + @case("01 pending connect lifecycle succeeds") + def _(): + outcome, facade = execute() + assert outcome.classification == "OFFLINE_DORMANT_FAKE_SYSCALL_ADAPTER_COMPLETE" and facade.close_count == 1 + + @case("02 immediate connect lifecycle succeeds") + def _(): + outcome, _ = execute(valid_steps(pending=False)) + assert outcome.batch_bytes_sent == 5 + + @case("03 partial writes complete exact batch") + def _(): + outcome, _ = execute(valid_steps(writes=(1, 1, 3))) + assert outcome.write_calls == 3 and outcome.batch_bytes_sent == 5 + + @case("04 interrupted connect wait is bounded") + def _(): + prefix = (step(module.CREATE_STREAM, module.OK), step(module.SET_NONBLOCKING, module.OK), + step(module.START_CONNECT, module.PENDING), step(module.WAIT_WRITE, module.INTERRUPTED, advance=.1), + step(module.WAIT_WRITE, module.READY, advance=.1), step(module.GET_SO_ERROR, module.ZERO)) + suffix = valid_steps(pending=False)[3:] + outcome, _ = execute(prefix + suffix) + assert outcome.interrupted_waits == 1 + + @case("05 interrupted read wait is bounded") + def _(): + extra = (step(module.WAIT_READ, module.INTERRUPTED, advance=.1),) + outcome, _ = execute(valid_steps(extra=extra)) + assert outcome.interrupted_waits == 1 + + @case("06 timeout only seals at deadline") + def _(): + outcome, _ = execute() + assert outcome.timed_out_waits == 1 and outcome.result_classification == "SOURCE_FAMILY_FINGERPRINT_ONLY" + + @case("07 receipt is required before create") + def _(): expect_failure(lambda: execute(receipt=False)) + + @case("08 exact fake facade type is required") + def _(): + batch = build_passive_batch(plan()); clock = module.OfflineFakeClock() + expect_failure(lambda: module.run_dormant_adapter(batch, object(), clock, True)) + + @case("09 exact fake clock type is required") + def _(): + class Derived(module.OfflineFakeClock): pass + expect_failure(lambda: module.OfflineFakeSyscallFacade(Derived(), valid_steps())) + + @case("10 create failure does not close an unowned descriptor") + def _(): + steps = (step(module.CREATE_STREAM, module.ERROR),) + clock = module.OfflineFakeClock(); facade = module.OfflineFakeSyscallFacade(clock, steps) + expect_failure(lambda: module.run_dormant_adapter(build_passive_batch(plan()), facade, clock, True)) + assert facade.close_count == 0 + + @case("11 nonblocking failure closes once") + def _(): + steps = (step(module.CREATE_STREAM, module.OK), step(module.SET_NONBLOCKING, module.ERROR)) + _, facade = None, module.OfflineFakeSyscallFacade(module.OfflineFakeClock(), steps) + expect_failure(lambda: module.run_dormant_adapter(build_passive_batch(plan()), facade, facade.clock, True)) + assert facade.close_count == 1 + + @case("12 pending connect requires write readiness") + def _(): + values = list(valid_steps()); values[3] = step(module.GET_SO_ERROR, module.ZERO) + expect_failure(lambda: execute(tuple(values))) + + @case("13 pending connect requires zero SO_ERROR") + def _(): + values = list(valid_steps()); values[4] = step(module.GET_SO_ERROR, module.NONZERO) + expect_failure(lambda: execute(tuple(values))) + + @case("14 connect error fails") + def _(): + values = list(valid_steps()); values[2] = step(module.START_CONNECT, module.ERROR) + expect_failure(lambda: execute(tuple(values))) + + @case("15 write requires readiness") + def _(): + values = list(valid_steps()); del values[5] + expect_failure(lambda: execute(tuple(values))) + + @case("16 zero write fails") + def _(): + values = list(valid_steps()); values[6] = step(module.WRITE_BYTES, module.ZERO) + expect_failure(lambda: execute(tuple(values))) + + @case("17 excess write count fails") + def _(): + values = list(valid_steps(writes=(6,))); expect_failure(lambda: execute(tuple(values))) + + @case("18 incomplete write script fails") + def _(): + values = valid_steps(writes=(2, 2)); expect_failure(lambda: execute(values)) + + @case("19 read requires readiness") + def _(): + values = list(valid_steps()); index = next(i for i, item in enumerate(values) if item.operation == module.WAIT_READ); del values[index] + expect_failure(lambda: execute(tuple(values))) + + @case("20 EOF is never completion") + def _(): + values = list(valid_steps()); index = next(i for i, item in enumerate(values) if item.operation == module.READ_BYTES); values[index] = step(module.READ_BYTES, module.EOF) + expect_failure(lambda: execute(tuple(values))) + + @case("21 receive bound is enforced") + def _(): + data = b"x" * 65537; expect_failure(lambda: execute(valid_steps(data=data))) + + @case("22 data at deadline fails") + def _(): + values = list(valid_steps()); index = next(i for i, item in enumerate(values) if item.operation == module.READ_BYTES); item = values[index]; values[index] = step(item.operation, item.result, item.value, item.data, 9.6) + expect_failure(lambda: execute(tuple(values))) + + @case("23 deadline wins readiness race") + def _(): + values = list(valid_steps()); values[-1] = step(module.WAIT_READ, module.READY, advance=9.6) + expect_failure(lambda: execute(tuple(values))) + + @case("24 early script exhaustion fails") + def _(): expect_failure(lambda: execute(valid_steps()[:-1])) + + @case("25 close failure invalidates success") + def _(): expect_failure(lambda: execute(close_result=module.ERROR)) + + @case("26 unused post-deadline steps are discarded") + def _(): + values = valid_steps() + (step(module.READ_BYTES, module.ERROR),) + outcome, facade = execute(values) + assert outcome.discarded_steps_after_close == 1 and facade.discarded_steps == 1 + + @case("27 malformed result fails at deadline") + def _(): expect_failure(lambda: execute(valid_steps(data=greeting("partial\n")))) + + @case("28 path result is sanitized") + def _(): + path = "/data/a.elf" + data = greeting(f"filename: {path}\nsize: 123\n12345 {path}\n") + batch = build_passive_batch(plan("T3_ONE_EXACT_PATH", path)) + outcome, _ = execute(valid_steps(data=data, writes=(10, 10, len(batch.payload) - 20)), batch) + assert outcome.result_classification == "WEAK_FILE_CORRELATION_ONLY" and not outcome.exact_identity + + @case("29 outcome retains no target and proves no device") + def _(): + outcome, _ = execute() + assert not outcome.target_retained and not outcome.live_transport_present and not outcome.device_behavior_proven + + @case("30 no live API exists") + def _(): + names = set(dir(module)) + assert not ({"connect", "send", "recv", "main"} & names) + + @case("31 fake step validation rejects hidden data") + def _(): expect_failure(lambda: step(module.CREATE_STREAM, module.OK, data=b"x")) + + @case("32 fake script bound is enforced") + def _(): + clock = module.OfflineFakeClock(); one = step(module.CREATE_STREAM, module.OK) + expect_failure(lambda: module.OfflineFakeSyscallFacade(clock, tuple(one for _ in range(1025)))) + + failures = [] + for name, function in cases: + try: + function(); print(f"PASS {name}") + except Exception as error: + failures.append(f"{name}: {error}"); print(f"FAIL {name}: {error}") + if failures: + return 1 + print(f"Phase-1.0AC dormant-adapter tests passed: {len(cases)}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_phase10ad_activation_contract.py b/tests/test_phase10ad_activation_contract.py new file mode 100644 index 0000000..8aa3bd4 --- /dev/null +++ b/tests/test_phase10ad_activation_contract.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Host tests for the data-only Phase-1.0AD activation contract.""" + +from __future__ import annotations + +import argparse +from dataclasses import replace +from pathlib import Path +import sys +import unittest + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + return parser.parse_args() + + +ARGS = parse_args() +sys.path.insert(0, str(ARGS.root / "tools")) + +from phase10ad_activation_contract import ( # noqa: E402 + ActivationContractError, + ActivationRecord, + PHASE, + validate_candidate, + validate_inactive, + validate_numeric_target, +) + + +def inactive() -> ActivationRecord: + return ActivationRecord(PHASE, False, None, None, None, None, None, None, + None, None, True, False, False, False, False, + False, False) + + +def candidate() -> ActivationRecord: + return ActivationRecord( + PHASE, True, "192.168.1.50", 2323, "CHIMERA_AD_001", + "2026-07-29T12:00:00Z", "2026-07-29T12:05:00Z", "a" * 64, + "b" * 64, "c" * 64, True, False, False, False, False, False, False) + + +class ActivationContractTests(unittest.TestCase): + def test_tracked_record_is_inactive(self) -> None: + validate_inactive(inactive()) + + def test_inactive_record_rejects_every_binding(self) -> None: + for field, value in (("target_address", "192.168.1.50"), + ("target_port", 2323), ("run_id", "RUN_ID_001"), + ("launcher_sha256", "a" * 64)): + with self.subTest(field=field), self.assertRaises(ActivationContractError): + validate_inactive(replace(inactive(), **{field: value})) + + def test_private_canonical_ipv4(self) -> None: + for address in ("10.0.0.2", "172.16.1.2", "192.168.1.50"): + self.assertEqual(validate_numeric_target(address), address) + + def test_rejects_names_public_and_special_addresses(self) -> None: + for address in ("ps5", "8.8.8.8", "127.0.0.1", "169.254.1.1", + "0.0.0.0", "192.168.001.050", "::1"): + with self.subTest(address=address), self.assertRaises(ActivationContractError): + validate_numeric_target(address) + + def test_complete_candidate_is_data_valid(self) -> None: + validate_candidate(candidate()) + + def test_candidate_rejects_wrong_port(self) -> None: + with self.assertRaises(ActivationContractError): + validate_candidate(replace(candidate(), target_port=9021)) + + def test_candidate_rejects_long_or_reversed_window(self) -> None: + for end in ("2026-07-29T12:05:01Z", "2026-07-29T11:59:59Z"): + with self.subTest(end=end), self.assertRaises(ActivationContractError): + validate_candidate(replace(candidate(), expires_at=end)) + + def test_candidate_rejects_missing_or_bad_hashes(self) -> None: + for field, value in (("launcher_sha256", None), + ("payload_sha256", "A" * 64), + ("approval_sha256", "0" * 63)): + with self.subTest(field=field), self.assertRaises(ActivationContractError): + validate_candidate(replace(candidate(), **{field: value})) + + def test_candidate_rejects_retry_and_effects(self) -> None: + for field in ("automatic_retry", "reconnect", "resume", + "device_write_authorized", "app_termination_authorized", + "system_remount_authorized"): + with self.subTest(field=field), self.assertRaises(ActivationContractError): + validate_candidate(replace(candidate(), **{field: True})) + + +if __name__ == "__main__": + unittest.main(argv=[sys.argv[0]]) diff --git a/tests/test_phase10af_bigapp_lifecycle_model.py b/tests/test_phase10af_bigapp_lifecycle_model.py new file mode 100644 index 0000000..c9ffa2d --- /dev/null +++ b/tests/test_phase10af_bigapp_lifecycle_model.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Failure-injection tests for the Phase-1.0AF lifecycle model.""" + +from __future__ import annotations + +import argparse +from pathlib import Path +import sys +import unittest + + +PARSER = argparse.ArgumentParser() +PARSER.add_argument("--root", type=Path, required=True) +ROOT = PARSER.parse_args().root +sys.path.insert(0, str(ROOT / "tools")) + +from phase10af_bigapp_lifecycle_model import * # noqa: E402,F403 + + +def happy() -> list[FakeEvent]: + return [FakeEvent(CHECK_NO_BIGAPP, NONE), FakeEvent(ATTACH_PARENT, OK), + FakeEvent(ARM_FORK, OK), FakeEvent(CONTINUE_PARENT, OK), + FakeEvent(LAUNCH_FIXED_TITLE, OK), + FakeEvent(AWAIT_UNIQUE_CHILD, CHILD, child_id=4242), + FakeEvent(DETACH_PARENT, OK), FakeEvent(ARM_EXEC, OK), + FakeEvent(CONTINUE_CHILD, OK), FakeEvent(AWAIT_EXEC, OK), + FakeEvent(REPLACE_EXACT_PAYLOAD, OK), + FakeEvent(RESTORE_MUTATIONS, OK), FakeEvent(DETACH_CHILD, OK), + FakeEvent(EMIT_RESULT, OK)] + + +class LifecycleTests(unittest.TestCase): + def test_success(self) -> None: + result = run_lifecycle(FakeLifecycleFacade(tuple(happy()))) + self.assertTrue(result.success) + self.assertEqual(result.child_id, 4242) + self.assertTrue(result.parent_detached and result.child_detached) + self.assertTrue(result.mutations_restored) + self.assertFalse(result.child_terminated or result.existing_bigapp_killed) + + def test_existing_bigapp_fails_without_kill(self) -> None: + result = run_lifecycle(FakeLifecycleFacade((FakeEvent(CHECK_NO_BIGAPP, EXISTS),))) + self.assertFalse(result.success) + self.assertFalse(result.existing_bigapp_killed) + self.assertEqual(result.child_id, 0) + + def test_parent_stage_failures_detach(self) -> None: + for index in range(2, 5): + events = happy()[:index + 1] + events[index] = FakeEvent(events[index].operation, ERROR) + events = events[:index + 1] + [FakeEvent(DETACH_PARENT, OK)] + with self.subTest(index=index): + result = run_lifecycle(FakeLifecycleFacade(tuple(events))) + self.assertFalse(result.success) + self.assertTrue(result.parent_detached) + + def test_child_stage_failures_terminate_only_child(self) -> None: + for index in range(7, 10): + events = happy()[:index + 1] + events[index] = FakeEvent(events[index].operation, ERROR) + events = events[:index + 1] + [FakeEvent(TERMINATE_CHILD, OK)] + with self.subTest(index=index): + result = run_lifecycle(FakeLifecycleFacade(tuple(events))) + self.assertFalse(result.success) + self.assertEqual(result.child_id, 4242) + self.assertTrue(result.child_terminated) + + def test_replace_failure_restores_then_terminates(self) -> None: + events = happy()[:11] + events[10] = FakeEvent(REPLACE_EXACT_PAYLOAD, ERROR) + events += [FakeEvent(RESTORE_MUTATIONS, OK), FakeEvent(TERMINATE_CHILD, OK)] + result = run_lifecycle(FakeLifecycleFacade(tuple(events))) + self.assertFalse(result.success) + self.assertTrue(result.mutations_restored and result.child_terminated) + self.assertLess(result.trace.index("RESTORE_MUTATIONS:OK"), + result.trace.index("TERMINATE_CHILD:OK")) + + def test_timeout_is_failure(self) -> None: + events = happy()[:6] + events[5] = FakeEvent(AWAIT_UNIQUE_CHILD, TIMEOUT) + events += [FakeEvent(DETACH_PARENT, OK)] + result = run_lifecycle(FakeLifecycleFacade(tuple(events))) + self.assertFalse(result.success) + + def test_main_tick_budget(self) -> None: + events = happy() + events[0] = FakeEvent(CHECK_NO_BIGAPP, NONE, ticks=64) + events = events[:2] + [FakeEvent(DETACH_PARENT, OK)] + with self.assertRaises(LifecycleModelError): + run_lifecycle(FakeLifecycleFacade(tuple(events))) + + def test_cleanup_failure_is_hard_error(self) -> None: + events = happy()[:3] + events[2] = FakeEvent(ARM_FORK, ERROR) + events += [FakeEvent(DETACH_PARENT, ERROR)] + with self.assertRaises(LifecycleModelError): + run_lifecycle(FakeLifecycleFacade(tuple(events))) + + def test_unused_or_wrong_order_is_rejected(self) -> None: + result = run_lifecycle(FakeLifecycleFacade((FakeEvent(ATTACH_PARENT, OK),))) + self.assertFalse(result.success) + with self.assertRaises(LifecycleModelError): + run_lifecycle(FakeLifecycleFacade(tuple(happy() + [FakeEvent(EMIT_RESULT, OK)]))) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_phase10ag_bounded_elf.py b/tests/test_phase10ag_bounded_elf.py new file mode 100644 index 0000000..de7f7f0 --- /dev/null +++ b/tests/test_phase10ag_bounded_elf.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Synthetic byte tests for the Phase-1.0AG ELF admission contract.""" + +from __future__ import annotations + +import argparse +import hashlib +from pathlib import Path +import struct +import sys +import unittest + + +PARSER = argparse.ArgumentParser() +PARSER.add_argument("--root", type=Path, required=True) +ROOT = PARSER.parse_args().root +sys.path.insert(0, str(ROOT / "tools")) + +from phase10ag_bounded_elf import * # noqa: E402,F403 + + +def make_elf(headers: list[tuple[int, int, int, int, int, int, int, int]] | None = None, + entry: int = 0x1000, elf_type: int = ET_DYN, + machine: int = EM_X86_64) -> bytes: + if headers is None: + headers = [(PT_LOAD, PF_R | PF_X, 0x1000, 0x1000, 0, 0x100, 0x100, 0x1000), + (PT_LOAD, PF_R | PF_W, 0x2000, 0x3000, 0, 0x80, 0x180, 0x1000)] + size = max([0x3000] + [item[2] + item[5] for item in headers]) + payload = bytearray(size) + ident = bytearray(16) + ident[:7] = b"\x7fELF\x02\x01\x01" + ELF_HEADER.pack_into(payload, 0, bytes(ident), elf_type, machine, 1, entry, + 64, 0, 0, 64, 56, len(headers), 64, 0, 0) + for index, header in enumerate(headers): + PROGRAM_HEADER.pack_into(payload, 64 + index * 56, *header) + return bytes(payload) + + +def assess(payload: bytes): + return assess_elf(payload, hashlib.sha256(payload).hexdigest()) + + +class BoundedElfTests(unittest.TestCase): + def test_valid_pie(self) -> None: + result = assess(make_elf()) + self.assertEqual(len(result.load_segments), 2) + self.assertFalse(result.writable_executable_segment) + self.assertFalse(result.execution_performed) + + def test_hash_must_match(self) -> None: + with self.assertRaises(ElfContractError): + assess_elf(make_elf(), "0" * 64) + + def test_header_identity_type_and_machine(self) -> None: + for payload in (b"not-elf" + b"\0" * 100, make_elf(elf_type=2), + make_elf(machine=3)): + with self.subTest(), self.assertRaises(ElfContractError): + assess(payload) + + def test_rejects_interpreter(self) -> None: + headers = [(PT_INTERP, PF_R, 0x300, 0x300, 0, 8, 8, 1), + (PT_LOAD, PF_R | PF_X, 0x1000, 0x1000, 0, 0x100, 0x100, 0x1000)] + with self.assertRaises(ElfContractError): + assess(make_elf(headers)) + + def test_rejects_writable_executable(self) -> None: + headers = [(PT_LOAD, PF_R | PF_W | PF_X, 0x1000, 0x1000, 0, + 0x100, 0x100, 0x1000)] + with self.assertRaises(ElfContractError): + assess(make_elf(headers)) + + def test_rejects_file_and_memory_bounds(self) -> None: + cases = [ + [(PT_LOAD, PF_R | PF_X, 0x2f80, 0x1000, 0, 0x100, 0x100, 0x1000)], + [(PT_LOAD, PF_R | PF_X, 0x1000, 0x1000, 0, 0x200, 0x100, 0x1000)], + [(PT_LOAD, PF_R | PF_X, 0x1000, 0x1000, 0, 0x100, + MAX_TOTAL_LOAD_MEMORY + 1, 0x1000)], + ] + for headers in cases: + with self.subTest(headers=headers), self.assertRaises(ElfContractError): + assess(make_elf(headers)) + + def test_rejects_bad_alignment(self) -> None: + for alignment in (0, 3, MAX_ALIGNMENT * 2): + headers = [(PT_LOAD, PF_R | PF_X, 0x1000, 0x1000, 0, + 0x100, 0x100, alignment)] + with self.subTest(alignment=alignment), self.assertRaises(ElfContractError): + assess(make_elf(headers)) + + def test_rejects_overlapping_virtual_ranges(self) -> None: + headers = [(PT_LOAD, PF_R | PF_X, 0x1000, 0x1000, 0, 0x100, 0x1800, 0x1000), + (PT_LOAD, PF_R | PF_W, 0x2000, 0x2000, 0, 0x100, 0x100, 0x1000)] + with self.assertRaises(ElfContractError): + assess(make_elf(headers)) + + def test_entry_must_be_executable(self) -> None: + with self.assertRaises(ElfContractError): + assess(make_elf(entry=0x3000)) + + def test_program_header_count_is_bounded(self) -> None: + headers = [(PT_LOAD, PF_R | PF_X, 0x1000, 0x1000, 0, 1, 1, 0x1000)] * 33 + with self.assertRaises(ElfContractError): + assess(make_elf(headers)) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_phase10ah_dynamic_contract.py b/tests/test_phase10ah_dynamic_contract.py new file mode 100644 index 0000000..498ac93 --- /dev/null +++ b/tests/test_phase10ah_dynamic_contract.py @@ -0,0 +1,120 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Synthetic tests for the Phase-1.0AH dynamic/relocation contract.""" + +from __future__ import annotations + +import argparse +import hashlib +from pathlib import Path +import sys +import unittest + + +PARSER = argparse.ArgumentParser() +PARSER.add_argument("--root", type=Path, required=True) +ROOT = PARSER.parse_args().root +sys.path.insert(0, str(ROOT / "tools")) + +from phase10ag_bounded_elf import * # noqa: E402,F403 +from phase10ah_dynamic_contract import * # noqa: E402,F403 + + +NEEDED = ("libSceLibcInternal.sprx", "libkernel_web.sprx") + + +def make_dynamic(relocations=None, needed=NEEDED, dynamic_terminated=True) -> bytes: + strings = b"\0" + b"".join(name.encode("ascii") + b"\0" for name in needed) + offsets = [] + position = 1 + for name in needed: + offsets.append(position) + position += len(name) + 1 + dynamic = b"".join(DYNAMIC_ENTRY.pack(DT_NEEDED, offset) for offset in offsets) + if dynamic_terminated: + dynamic += DYNAMIC_ENTRY.pack(DT_NULL, 0) + if relocations is None: + relocations = [(0x2100, R_X86_64_RELATIVE, 0, 0x100), + (0x2108, R_X86_64_GLOB_DAT, 1, 0)] + rela = b"".join(RELA_ENTRY.pack(target, symbol << 32 | kind, addend) + for target, kind, symbol, addend in relocations) + payload = bytearray(0x3400) + ident = bytearray(16) + ident[:7] = b"\x7fELF\x02\x01\x01" + ELF_HEADER.pack_into(payload, 0, bytes(ident), ET_DYN, EM_X86_64, 1, 0x100, + 64, 0x3000, 0, 64, 56, 2, 64, 4, 0) + PROGRAM_HEADER.pack_into(payload, 64, PT_LOAD, PF_R | PF_X, 0, 0, 0, + 0x1000, 0x1000, 0x1000) + PROGRAM_HEADER.pack_into(payload, 120, PT_LOAD, PF_R | PF_W, 0x1000, + 0x2000, 0, 0x1000, 0x2000, 0x1000) + payload[0x1800:0x1800 + len(strings)] = strings + payload[0x1900:0x1900 + len(dynamic)] = dynamic + payload[0x1a00:0x1a00 + len(rela)] = rela + sections = [ + (0, 0, 0, 0, 0, 0, 0, 0, 0, 0), + (0, SHT_STRTAB, 0, 0, 0x1800, len(strings), 0, 0, 1, 0), + (0, SHT_DYNAMIC, 0, 0, 0x1900, len(dynamic), 1, 0, 8, 16), + (0, SHT_RELA, 0, 0, 0x1a00, len(rela), 0, 0, 8, 24), + ] + for index, section in enumerate(sections): + SECTION_HEADER.pack_into(payload, 0x3000 + index * 64, *section) + return bytes(payload) + + +def assess(payload: bytes, needed=NEEDED): + return assess_dynamic(payload, hashlib.sha256(payload).hexdigest(), needed) + + +class DynamicContractTests(unittest.TestCase): + def test_valid_split(self) -> None: + result = assess(make_dynamic()) + self.assertEqual(result.relative_count, 1) + self.assertEqual(result.glob_dat_count, 1) + self.assertEqual(result.loader_applied_types, (R_X86_64_RELATIVE,)) + self.assertEqual(result.crt_applied_types, (R_X86_64_GLOB_DAT,)) + self.assertFalse(result.target_mapping_performed) + + def test_needed_inventory_is_exact_and_allowlisted(self) -> None: + payload = make_dynamic() + with self.assertRaises(DynamicContractError): + assess(payload, tuple(reversed(NEEDED))) + with self.assertRaises(DynamicContractError): + assess(make_dynamic(needed=("evil.sprx",)), ("evil.sprx",)) + + def test_dynamic_table_must_terminate(self) -> None: + with self.assertRaises(DynamicContractError): + assess(make_dynamic(dynamic_terminated=False)) + + def test_unknown_relocation_type_is_rejected(self) -> None: + with self.assertRaises(DynamicContractError): + assess(make_dynamic([(0x2100, 7, 0, 0x100)])) + + def test_targets_must_be_aligned_and_writable(self) -> None: + for target in (0x100, 0x2101, 0x5000): + with self.subTest(target=target), self.assertRaises(DynamicContractError): + assess(make_dynamic([(target, R_X86_64_RELATIVE, 0, 0x100)])) + + def test_relative_requires_zero_symbol_and_mapped_addend(self) -> None: + for symbol, addend in ((1, 0x100), (0, -1), (0, 0x9000)): + with self.subTest(), self.assertRaises(DynamicContractError): + assess(make_dynamic([(0x2100, R_X86_64_RELATIVE, symbol, addend)])) + + def test_glob_dat_requires_symbol_and_zero_addend(self) -> None: + for symbol, addend in ((0, 0), (1, 1)): + relocs = [(0x2100, R_X86_64_RELATIVE, 0, 0x100), + (0x2108, R_X86_64_GLOB_DAT, symbol, addend)] + with self.subTest(), self.assertRaises(DynamicContractError): + assess(make_dynamic(relocs)) + + def test_relative_closure_is_required(self) -> None: + with self.assertRaises(DynamicContractError): + assess(make_dynamic([(0x2108, R_X86_64_GLOB_DAT, 1, 0)])) + + def test_base_elf_hash_and_shape_remain_enforced(self) -> None: + payload = make_dynamic() + with self.assertRaises(DynamicContractError): + assess_dynamic(payload, "0" * 64, NEEDED) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_phase10ai_mapping_model.py b/tests/test_phase10ai_mapping_model.py new file mode 100644 index 0000000..511cf0f --- /dev/null +++ b/tests/test_phase10ai_mapping_model.py @@ -0,0 +1,133 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Failure-injection tests for Phase-1.0AI mapping transactions.""" + +from __future__ import annotations + +import argparse +from pathlib import Path +import sys +import unittest + + +PARSER = argparse.ArgumentParser() +PARSER.add_argument("--root", type=Path, required=True) +ROOT = PARSER.parse_args().root +sys.path.insert(0, str(ROOT / "tools")) + +from phase10ag_bounded_elf import LoadSegment, PF_R, PF_W, PF_X # noqa: E402 +from phase10ai_mapping_model import * # noqa: E402,F403 + + +def plan() -> MappingPlan: + return MappingPlan(( + LoadSegment(PF_R | PF_X, 0x4000, 0, 0x1800, 0x1800, 0x4000), + LoadSegment(PF_R, 0x8000, 0x8000, 0x1000, 0x1000, 0x4000), + LoadSegment(PF_R | PF_W, 0xc000, 0xc000, 0x800, 0x1800, 0x4000), + ), 7) + + +def happy(mapping: MappingPlan | None = None) -> list[FakeMappingEvent]: + mapping = mapping or plan() + events = [FakeMappingEvent(RESERVE_CHILD, OK, value=mapping.region_size), + FakeMappingEvent(CREATE_MIRROR, OK, value=mapping.region_size)] + for index, segment in enumerate(mapping.segments): + if segment.file_size: + events.append(FakeMappingEvent(COPY_FILE_BYTES, OK, index, + segment.file_size)) + if segment.memory_size > segment.file_size: + events.append(FakeMappingEvent(ZERO_BSS, OK, index, + segment.memory_size - segment.file_size)) + events.append(FakeMappingEvent(APPLY_RELATIVE, OK, + value=mapping.relative_relocations)) + events.append(FakeMappingEvent(COPY_MIRROR_TO_CHILD, OK, + value=mapping.region_size)) + for index, segment in enumerate(mapping.segments): + events.append(FakeMappingEvent(SET_FINAL_PROTECTION, OK, index, + segment.flags)) + events += [FakeMappingEvent(SYNC_IMAGE, OK, value=mapping.region_size), + FakeMappingEvent(RELEASE_MIRROR, OK)] + return events + + +class MappingModelTests(unittest.TestCase): + def test_success(self) -> None: + mapping = plan() + result = run_mapping(mapping, FakeMappingFacade(tuple(happy(mapping)))) + self.assertTrue(result.success and result.child_region_retained) + self.assertTrue(result.mirror_released) + self.assertFalse(result.child_region_unmapped or result.target_mapping_performed) + self.assertEqual(result.zeroed_bss_bytes, 0x1000) + + def test_reserve_failure_has_no_cleanup(self) -> None: + mapping = plan() + events = (FakeMappingEvent(RESERVE_CHILD, ERROR, value=mapping.region_size),) + result = run_mapping(mapping, FakeMappingFacade(events)) + self.assertFalse(result.success or result.child_region_unmapped) + + def test_mirror_creation_failure_unmaps_child(self) -> None: + mapping = plan() + events = (FakeMappingEvent(RESERVE_CHILD, OK, value=mapping.region_size), + FakeMappingEvent(CREATE_MIRROR, ERROR, value=mapping.region_size), + FakeMappingEvent(UNMAP_CHILD, OK, value=mapping.region_size)) + result = run_mapping(mapping, FakeMappingFacade(events)) + self.assertFalse(result.success) + self.assertTrue(result.child_region_unmapped) + + def test_every_post_mirror_failure_releases_and_unmaps(self) -> None: + mapping = plan() + baseline = happy(mapping) + for index in range(2, len(baseline)): + failed = baseline[:index + 1] + event = failed[index] + failed[index] = FakeMappingEvent(event.operation, ERROR, + event.segment_index, event.value) + failed += [FakeMappingEvent(RELEASE_MIRROR, OK), + FakeMappingEvent(UNMAP_CHILD, OK, value=mapping.region_size)] + with self.subTest(operation=event.operation, index=index): + result = run_mapping(mapping, FakeMappingFacade(tuple(failed))) + self.assertFalse(result.success) + self.assertTrue(result.mirror_released) + self.assertTrue(result.child_region_unmapped) + + def test_cleanup_failure_is_hard_error(self) -> None: + mapping = plan() + events = happy(mapping)[:3] + event = events[-1] + events[-1] = FakeMappingEvent(event.operation, ERROR, + event.segment_index, event.value) + events.append(FakeMappingEvent(RELEASE_MIRROR, ERROR)) + with self.assertRaises(MappingModelError): + run_mapping(mapping, FakeMappingFacade(tuple(events))) + + def test_tick_budget_is_enforced_and_rolled_back(self) -> None: + mapping = plan() + events = happy(mapping)[:2] + first = events[0] + events[0] = FakeMappingEvent(first.operation, OK, value=first.value, + ticks=128) + events += [FakeMappingEvent(UNMAP_CHILD, OK, value=mapping.region_size)] + result = run_mapping(mapping, FakeMappingFacade(tuple(events))) + self.assertFalse(result.success) + self.assertTrue(result.child_region_unmapped) + + def test_plan_rejects_wx_and_page_protection_overlap(self) -> None: + with self.assertRaises(MappingModelError): + MappingPlan((LoadSegment(PF_R | PF_W | PF_X, 0, 0, 1, 1, 0x4000),), 1) + with self.assertRaises(MappingModelError): + MappingPlan((LoadSegment(PF_R | PF_X, 0, 0, 1, 0x3000, 0x4000), + LoadSegment(PF_R | PF_W, 0x3000, 0x3000, 1, 1, 0x4000)), 1) + + def test_wrong_binding_and_unused_events_are_rejected(self) -> None: + mapping = plan() + events = happy(mapping) + events[2] = FakeMappingEvent(COPY_FILE_BYTES, OK, 0, 1) + with self.assertRaises(MappingModelError): + run_mapping(mapping, FakeMappingFacade(tuple(events))) + with self.assertRaises(MappingModelError): + run_mapping(mapping, FakeMappingFacade(tuple( + happy(mapping) + [FakeMappingEvent(RELEASE_MIRROR, OK)]))) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_phase10ak_hybrid_composition.py b/tests/test_phase10ak_hybrid_composition.py new file mode 100644 index 0000000..b86df10 --- /dev/null +++ b/tests/test_phase10ak_hybrid_composition.py @@ -0,0 +1,120 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Exhaustive ownership tests for Phase-1.0AK.""" + +from __future__ import annotations + +import argparse +from pathlib import Path +import sys +import unittest + + +PARSER = argparse.ArgumentParser() +PARSER.add_argument("--root", type=Path, required=True) +ROOT = PARSER.parse_args().root +sys.path.insert(0, str(ROOT / "tools")) + +from phase10ak_hybrid_composition import * # noqa: E402,F403 + + +def happy(count: int = 2) -> list[FakeEvent]: + events = [FakeEvent(CREATE_CHILD, OK), FakeEvent(RESERVE_REGION, OK), + FakeEvent(CREATE_MIRROR, OK)] + for segment in range(count): + events += [FakeEvent(CREATE_JIT_MASTER, OK, segment), + FakeEvent(MAP_EXECUTABLE, OK, segment), + FakeEvent(CREATE_JIT_ALIAS, OK, segment), + FakeEvent(MAP_HOST_ALIAS, OK, segment), + FakeEvent(MAP_REMOTE_ALIAS, OK, segment), + FakeEvent(COPY_ALIAS, OK, segment), + FakeEvent(UNMAP_REMOTE_ALIAS, OK, segment), + FakeEvent(UNMAP_HOST_ALIAS, OK, segment), + FakeEvent(CLOSE_JIT_ALIAS, OK, segment), + FakeEvent(CLOSE_JIT_MASTER, OK, segment)] + return events + [FakeEvent(FINALIZE_IMAGE, OK), FakeEvent(RELEASE_MIRROR, OK)] + + +def cleanup_for(prefix: list[FakeEvent]) -> list[FakeEvent]: + acquired = {name: set() for name in ("masters", "aliases", "host", "remote")} + child = region = mirror = False + for event in prefix: + if event.result != OK: + break + if event.operation == CREATE_CHILD: child = True + elif event.operation == RESERVE_REGION: region = True + elif event.operation == CREATE_MIRROR: mirror = True + elif event.operation == CREATE_JIT_MASTER: acquired["masters"].add(event.segment) + elif event.operation == CREATE_JIT_ALIAS: acquired["aliases"].add(event.segment) + elif event.operation == MAP_HOST_ALIAS: acquired["host"].add(event.segment) + elif event.operation == MAP_REMOTE_ALIAS: acquired["remote"].add(event.segment) + elif event.operation == UNMAP_REMOTE_ALIAS: acquired["remote"].remove(event.segment) + elif event.operation == UNMAP_HOST_ALIAS: acquired["host"].remove(event.segment) + elif event.operation == CLOSE_JIT_ALIAS: acquired["aliases"].remove(event.segment) + elif event.operation == CLOSE_JIT_MASTER: acquired["masters"].remove(event.segment) + elif event.operation == RELEASE_MIRROR: mirror = False + result: list[FakeEvent] = [] + for key, operation in (("remote", UNMAP_REMOTE_ALIAS), + ("host", UNMAP_HOST_ALIAS), + ("aliases", CLOSE_JIT_ALIAS), + ("masters", CLOSE_JIT_MASTER)): + result += [FakeEvent(operation, OK, item) + for item in sorted(acquired[key], reverse=True)] + if mirror: result.append(FakeEvent(RELEASE_MIRROR, OK)) + if region: result.append(FakeEvent(UNMAP_REGION, OK)) + if child: result.append(FakeEvent(KILL_AND_REAP_CHILD, OK)) + return result + + +class HybridCompositionTests(unittest.TestCase): + def test_success_releases_every_temporary_resource(self) -> None: + outcome = run_composition(CompositionPlan(2), FakeFacade(tuple(happy()))) + self.assertTrue(outcome.success and outcome.child_alive and outcome.image_retained) + self.assertEqual(outcome.resources_open, 0) + self.assertFalse(outcome.target_action_performed or outcome.firmware_behavior_proven) + + def test_every_forward_failure_terminates_fail_closed(self) -> None: + baseline = happy() + for index, original in enumerate(baseline): + failed = baseline[:index] + [FakeEvent(original.operation, ERROR, + original.segment)] + failed += cleanup_for(failed) + with self.subTest(index=index, operation=original.operation): + outcome = run_composition(CompositionPlan(2), FakeFacade(tuple(failed))) + self.assertFalse(outcome.success or outcome.child_alive) + self.assertEqual(outcome.fail_closed_termination, index > 0) + self.assertEqual(outcome.resources_open, 0) + + def test_cleanup_failure_is_contained_by_child_termination(self) -> None: + failed = happy()[:9] + original = failed[-1] + failed[-1] = FakeEvent(original.operation, ERROR, original.segment) + cleanup = cleanup_for(failed) + cleanup[0] = FakeEvent(cleanup[0].operation, ERROR, cleanup[0].segment) + outcome = run_composition(CompositionPlan(2), FakeFacade(tuple(failed + cleanup))) + self.assertEqual(outcome.classification, + "OFFLINE_FAIL_CLOSED_AFTER_CLEANUP_FAILURE") + self.assertFalse(outcome.child_alive) + + def test_failed_termination_is_a_hard_error(self) -> None: + events = [FakeEvent(CREATE_CHILD, OK), FakeEvent(RESERVE_REGION, ERROR), + FakeEvent(KILL_AND_REAP_CHILD, ERROR)] + with self.assertRaises(CompositionError): + run_composition(CompositionPlan(1), FakeFacade(tuple(events))) + + def test_bounds_wrong_order_deadline_and_unused_events(self) -> None: + for value in (0, 9, True): + with self.subTest(value=value), self.assertRaises(CompositionError): + CompositionPlan(value) + events = happy(1) + events[0] = FakeEvent(CREATE_CHILD, OK, ticks=256) + events = events[:2] + [FakeEvent(KILL_AND_REAP_CHILD, OK)] + outcome = run_composition(CompositionPlan(1), FakeFacade(tuple(events))) + self.assertFalse(outcome.success) + with self.assertRaises(CompositionError): + run_composition(CompositionPlan(1), FakeFacade(tuple( + happy(1) + [FakeEvent(KILL_AND_REAP_CHILD, OK)]))) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_phase10am_bounded_copy_model.py b/tests/test_phase10am_bounded_copy_model.py new file mode 100644 index 0000000..07c7afa --- /dev/null +++ b/tests/test_phase10am_bounded_copy_model.py @@ -0,0 +1,103 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Failure-injection tests for Phase-1.0AM bounded copy/restore.""" + +from __future__ import annotations + +import argparse +from pathlib import Path +import sys +import unittest + + +PARSER = argparse.ArgumentParser() +PARSER.add_argument("--root", type=Path, required=True) +ROOT = PARSER.parse_args().root +sys.path.insert(0, str(ROOT / "tools")) + +from phase10am_bounded_copy_model import * # noqa: E402,F403 + + +def prefix() -> list[FakeCopyEvent]: + return [FakeCopyEvent(BACKUP_AUTHID, OK), FakeCopyEvent(BACKUP_CAPS, OK), + FakeCopyEvent(SET_PRIV_AUTHID, OK), FakeCopyEvent(SET_PRIV_CAPS, OK)] + + +class BoundedCopyTests(unittest.TestCase): + def test_exact_multichunk_success_restores_both_fields(self) -> None: + events = prefix() + [FakeCopyEvent(COPY_CHUNK, OK, 4, MORE), + FakeCopyEvent(COPY_CHUNK, OK, 6, COMPLETE), + FakeCopyEvent(RESTORE_CAPS, OK), + FakeCopyEvent(RESTORE_AUTHID, OK)] + result = run_copy(CopyPlan(100, 200, 10), FakeCopyFacade(tuple(events))) + self.assertTrue(result.success) + self.assertEqual(result.copied, 10) + self.assertTrue(result.child_alive) + self.assertFalse(result.target_copy_performed) + self.assertTrue(result.service_available) + + def test_partial_error_is_reported_and_child_is_reaped(self) -> None: + events = prefix() + [FakeCopyEvent(COPY_CHUNK, OK, 4, MORE), + FakeCopyEvent(COPY_CHUNK, ERROR, 0, MORE), + FakeCopyEvent(RESTORE_CAPS, OK), + FakeCopyEvent(RESTORE_AUTHID, OK), + FakeCopyEvent(KILL_AND_REAP_CHILD, OK)] + result = run_copy(CopyPlan(0, 32, 10), FakeCopyFacade(tuple(events))) + self.assertEqual(result.classification, "OFFLINE_PARTIAL_COPY_CONTAINED") + self.assertEqual(result.copied, 4) + self.assertFalse(result.child_alive) + + def test_zero_and_oversized_progress_never_complete(self) -> None: + for progress in (0, 11): + events = prefix() + [FakeCopyEvent(COPY_CHUNK, OK, progress, MORE), + FakeCopyEvent(RESTORE_CAPS, OK), + FakeCopyEvent(RESTORE_AUTHID, OK)] + with self.subTest(progress=progress): + result = run_copy(CopyPlan(0, 32, 10), FakeCopyFacade(tuple(events))) + self.assertFalse(result.success) + self.assertEqual(result.copied, 0) + + def test_both_restores_are_attempted_and_failure_contains_service(self) -> None: + events = prefix() + [FakeCopyEvent(COPY_CHUNK, ERROR, 0, MORE), + FakeCopyEvent(RESTORE_CAPS, ERROR), + FakeCopyEvent(RESTORE_AUTHID, ERROR), + FakeCopyEvent(KILL_AND_REAP_CHILD, OK), + FakeCopyEvent(TERMINATE_SERVICE, OK)] + result = run_copy(CopyPlan(0, 32, 10), FakeCopyFacade(tuple(events))) + self.assertEqual(result.restore_failure_bits, 3) + self.assertFalse(result.child_alive or result.service_available) + + def test_caps_set_failure_still_restores_authid(self) -> None: + events = prefix()[:3] + [FakeCopyEvent(SET_PRIV_CAPS, ERROR), + FakeCopyEvent(RESTORE_AUTHID, OK)] + result = run_copy(CopyPlan(0, 32, 10), FakeCopyFacade(tuple(events))) + self.assertEqual(result.classification, + "OFFLINE_COPY_REJECTED_BEFORE_MUTATION") + self.assertTrue(result.child_alive and result.service_available) + + def test_restore_or_child_termination_failure_is_hard(self) -> None: + events = prefix() + [FakeCopyEvent(COPY_CHUNK, OK, 1, MORE), + FakeCopyEvent(RESTORE_CAPS, OK), + FakeCopyEvent(RESTORE_AUTHID, OK), + FakeCopyEvent(KILL_AND_REAP_CHILD, ERROR)] + with self.assertRaises(CopyModelError): + run_copy(CopyPlan(0, 32, 10), FakeCopyFacade(tuple(events))) + + def test_bounds_deadline_status_and_arithmetic(self) -> None: + for args in ((0, 0, 0), (MAX_U64, 0, 2), (0, MAX_U64, 2)): + with self.subTest(args=args), self.assertRaises(CopyModelError): + CopyPlan(*args) + events = prefix() + events[0] = FakeCopyEvent(BACKUP_AUTHID, OK, ticks=MAX_TICKS) + result = run_copy(CopyPlan(0, 32, 10), FakeCopyFacade(tuple(events[:2]))) + self.assertFalse(result.success) + incomplete = prefix() + [FakeCopyEvent(COPY_CHUNK, OK, 9, COMPLETE), + FakeCopyEvent(RESTORE_CAPS, OK), + FakeCopyEvent(RESTORE_AUTHID, OK), + FakeCopyEvent(KILL_AND_REAP_CHILD, OK)] + result = run_copy(CopyPlan(0, 32, 10), FakeCopyFacade(tuple(incomplete))) + self.assertFalse(result.success) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_phase10ao_worker_supervisor_model.py b/tests/test_phase10ao_worker_supervisor_model.py new file mode 100644 index 0000000..026d130 --- /dev/null +++ b/tests/test_phase10ao_worker_supervisor_model.py @@ -0,0 +1,105 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Failure tests for Phase-1.0AO worker supervision.""" + +from __future__ import annotations + +import argparse +from pathlib import Path +import sys +import unittest + + +PARSER = argparse.ArgumentParser() +PARSER.add_argument("--root", type=Path, required=True) +ROOT = PARSER.parse_args().root +sys.path.insert(0, str(ROOT / "tools")) + +from phase10ao_worker_supervisor_model import * # noqa: E402,F403 + + +PLAN = WorkerPlan(101, 202, 4096) + + +class WorkerSupervisorTests(unittest.TestCase): + def test_exact_success_reaps_worker_and_retains_child(self) -> None: + result = WorkerResult(101, SUCCESS, 4096, 0) + events = (FakeSupervisorEvent(CREATE_WORKER, OK), + FakeSupervisorEvent(VERIFY_WORKER, OK), + FakeSupervisorEvent(START_COPY, OK), + FakeSupervisorEvent(RECEIVE_RESULT, OK, result), + FakeSupervisorEvent(REAP_WORKER, OK)) + outcome = run_supervisor(PLAN, FakeSupervisorFacade(events)) + self.assertTrue(outcome.success and outcome.service_alive and outcome.child_alive) + self.assertFalse(outcome.worker_alive or outcome.automatic_restart) + self.assertFalse(outcome.target_action_performed) + + def test_deadline_terminates_worker_and_child(self) -> None: + events = (FakeSupervisorEvent(CREATE_WORKER, OK), + FakeSupervisorEvent(VERIFY_WORKER, OK), + FakeSupervisorEvent(START_COPY, OK), + FakeSupervisorEvent(DEADLINE, OK), + FakeSupervisorEvent(TERMINATE_WORKER, OK), + FakeSupervisorEvent(REAP_WORKER, OK), + FakeSupervisorEvent(TERMINATE_CHILD, OK), + FakeSupervisorEvent(REAP_CHILD, OK)) + outcome = run_supervisor(PLAN, FakeSupervisorFacade(events), True) + self.assertEqual(outcome.classification, "OFFLINE_DEADLINE_CONTAINED") + self.assertTrue(outcome.service_alive) + self.assertFalse(outcome.worker_alive or outcome.child_alive) + + def test_wrong_identity_partial_and_restore_failure_are_contained(self) -> None: + results = (WorkerResult(999, SUCCESS, 4096, 0), + WorkerResult(101, COPY_ERROR, 7, 0), + WorkerResult(101, RESTORE_ERROR, 4096, 1)) + for result in results: + events = (FakeSupervisorEvent(CREATE_WORKER, OK), + FakeSupervisorEvent(VERIFY_WORKER, OK), + FakeSupervisorEvent(START_COPY, OK), + FakeSupervisorEvent(RECEIVE_RESULT, OK, result), + FakeSupervisorEvent(TERMINATE_WORKER, OK), + FakeSupervisorEvent(REAP_WORKER, OK), + FakeSupervisorEvent(TERMINATE_CHILD, OK), + FakeSupervisorEvent(REAP_CHILD, OK)) + with self.subTest(result=result): + outcome = run_supervisor(PLAN, FakeSupervisorFacade(events)) + self.assertFalse(outcome.success or outcome.child_alive) + + def test_start_ambiguity_contains_both_processes(self) -> None: + events = (FakeSupervisorEvent(CREATE_WORKER, OK), + FakeSupervisorEvent(VERIFY_WORKER, OK), + FakeSupervisorEvent(START_COPY, ERROR), + FakeSupervisorEvent(TERMINATE_WORKER, OK), + FakeSupervisorEvent(REAP_WORKER, OK), + FakeSupervisorEvent(TERMINATE_CHILD, OK), + FakeSupervisorEvent(REAP_CHILD, OK)) + outcome = run_supervisor(PLAN, FakeSupervisorFacade(events)) + self.assertFalse(outcome.child_alive) + + def test_pre_start_failure_does_not_kill_untouched_child(self) -> None: + events = (FakeSupervisorEvent(CREATE_WORKER, OK), + FakeSupervisorEvent(VERIFY_WORKER, ERROR), + FakeSupervisorEvent(TERMINATE_WORKER, OK), + FakeSupervisorEvent(REAP_WORKER, OK)) + outcome = run_supervisor(PLAN, FakeSupervisorFacade(events)) + self.assertTrue(outcome.child_alive) + + def test_any_terminal_failure_is_hard(self) -> None: + events = (FakeSupervisorEvent(CREATE_WORKER, OK), + FakeSupervisorEvent(VERIFY_WORKER, ERROR), + FakeSupervisorEvent(TERMINATE_WORKER, ERROR)) + with self.assertRaises(SupervisorError): + run_supervisor(PLAN, FakeSupervisorFacade(events)) + + def test_bounds_and_unused_events_are_rejected(self) -> None: + for values in ((0, 2, 1), (1, 1, 1), (1, 2, MAX_COPY_SIZE + 1)): + with self.subTest(values=values), self.assertRaises(SupervisorError): + WorkerPlan(*values) + events = (FakeSupervisorEvent(CREATE_WORKER, ERROR), + FakeSupervisorEvent(REAP_WORKER, OK)) + with self.assertRaises(SupervisorError): + run_supervisor(PLAN, FakeSupervisorFacade(events)) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_phase10aq_worker_result_record.py b/tests/test_phase10aq_worker_result_record.py new file mode 100644 index 0000000..df583a4 --- /dev/null +++ b/tests/test_phase10aq_worker_result_record.py @@ -0,0 +1,103 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Byte mutation tests for Phase-1.0AQ worker records.""" + +from __future__ import annotations + +import argparse +import hashlib +from pathlib import Path +import struct +import sys +import unittest + + +PARSER = argparse.ArgumentParser() +PARSER.add_argument("--root", type=Path, required=True) +ROOT = PARSER.parse_args().root +sys.path.insert(0, str(ROOT / "tools")) + +from phase10aq_worker_result_record import * # noqa: E402,F403 + + +def precommit() -> WorkerPrecommit: + return WorkerPrecommit(bytes.fromhex("00112233445566778899aabbccddeeff"), + bytes.fromhex("ffeeddccbbaa99887766554433221100"), + 101, 202, 7, 4096) + + +class WorkerRecordTests(unittest.TestCase): + def test_exact_success_roundtrip(self) -> None: + expected = precommit() + raw = encode_record(expected, STATUS_SUCCESS, 4096) + self.assertEqual(len(raw), RECORD_SIZE) + record = parse_record(raw, expected) + self.assertTrue(record.successful) + self.assertEqual(record.generation, 7) + + def test_every_byte_mutation_is_rejected(self) -> None: + expected = precommit() + raw = encode_record(expected, STATUS_SUCCESS, 4096) + for index in range(len(raw)): + mutated = bytearray(raw) + mutated[index] ^= 1 + with self.subTest(index=index), self.assertRaises(WorkerRecordError): + parse_record(bytes(mutated), expected) + + def test_truncation_extension_and_nonbytes_are_rejected(self) -> None: + expected = precommit() + raw = encode_record(expected, STATUS_SUCCESS, 4096) + for candidate in (raw[:-1], raw + b"\0", bytearray(raw)): + with self.subTest(length=len(candidate)), self.assertRaises(WorkerRecordError): + parse_record(candidate, expected) # type: ignore[arg-type] + + def test_pid_nonce_generation_and_attempt_must_all_match(self) -> None: + expected = precommit() + raw = encode_record(expected, STATUS_SUCCESS, 4096) + variants = ( + WorkerPrecommit(expected.attempt_id, expected.nonce, 102, 202, 7, 4096), + WorkerPrecommit(expected.attempt_id, expected.nonce, 101, 202, 8, 4096), + WorkerPrecommit(expected.attempt_id, b"x" * 16, 101, 202, 7, 4096), + WorkerPrecommit(b"y" * 16, expected.nonce, 101, 202, 7, 4096), + ) + for variant in variants: + with self.subTest(variant=variant), self.assertRaises(WorkerRecordError): + parse_record(raw, variant) + + def test_failure_records_preserve_exact_progress_and_restore_bits(self) -> None: + expected = precommit() + copy_error = parse_record( + encode_record(expected, STATUS_COPY_ERROR, 123), expected) + self.assertFalse(copy_error.successful) + self.assertEqual(copy_error.copied, 123) + restore_error = parse_record( + encode_record(expected, STATUS_RESTORE_ERROR, 4096, 3), expected) + self.assertEqual(restore_error.restore_failure_bits, 3) + + def test_forged_rehashed_invalid_fields_are_rejected(self) -> None: + expected = precommit() + raw = bytearray(encode_record(expected, STATUS_COPY_ERROR, 1)) + raw[88] = 1 + raw[HASHED_SIZE:] = hashlib.sha256(raw[:HASHED_SIZE]).digest() + with self.assertRaises(WorkerRecordError): + parse_record(bytes(raw), expected) + raw = bytearray(encode_record(expected, STATUS_COPY_ERROR, 1)) + struct.pack_into(" None: + with self.assertRaises(WorkerRecordError): + WorkerPrecommit(bytes(16), b"x" * 16, 1, 2, 1, 1) + with self.assertRaises(WorkerRecordError): + WorkerPrecommit(b"a" * 16, b"b" * 16, 1, 1, 1, 1) + with self.assertRaises(WorkerRecordError): + encode_record(precommit(), STATUS_SUCCESS, 4095) + with self.assertRaises(WorkerRecordError): + encode_record(precommit(), STATUS_COPY_ERROR, 4097) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_phase10ar_result_channel_model.py b/tests/test_phase10ar_result_channel_model.py new file mode 100644 index 0000000..11b0927 --- /dev/null +++ b/tests/test_phase10ar_result_channel_model.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Partial-read and deadline tests for Phase-1.0AR.""" + +from __future__ import annotations + +import argparse +from pathlib import Path +import sys +import unittest + + +PARSER = argparse.ArgumentParser() +PARSER.add_argument("--root", type=Path, required=True) +ROOT = PARSER.parse_args().root +sys.path.insert(0, str(ROOT / "tools")) + +from phase10aq_worker_result_record import * # noqa: E402,F403 +from phase10ar_result_channel_model import * # noqa: E402,F403 + + +def setup() -> tuple[WorkerPrecommit, bytes, ChannelPlan]: + precommit = WorkerPrecommit(b"a" * 16, b"b" * 16, 101, 202, 7, 4096) + raw = encode_record(precommit, STATUS_SUCCESS, 4096) + return precommit, raw, ChannelPlan(precommit, 101, 7, b"b" * 16) + + +class ResultChannelTests(unittest.TestCase): + def test_every_split_point_assembles_exact_record(self) -> None: + _, raw, plan = setup() + for split in range(1, RECORD_SIZE): + events = (FakeReadEvent(DATA, raw[:split]), + FakeReadEvent(DATA, raw[split:])) + with self.subTest(split=split): + outcome = receive_record(plan, events) + self.assertTrue(outcome.accepted) + self.assertFalse(outcome.eof_is_success or outcome.live_transport_present) + + def test_byte_at_a_time_is_bounded_and_accepted(self) -> None: + _, raw, plan = setup() + events = tuple(FakeReadEvent(DATA, bytes((value,))) for value in raw) + outcome = receive_record(plan, events) + self.assertTrue(outcome.accepted) + self.assertEqual(outcome.buffered, RECORD_SIZE) + + def test_eof_deadline_and_silent_incomplete_are_never_success(self) -> None: + _, raw, plan = setup() + scripts = ((FakeReadEvent(DATA, raw[:50]), FakeReadEvent(EOF)), + (FakeReadEvent(DATA, raw[:50]), FakeReadEvent(DEADLINE)), + (FakeReadEvent(DATA, raw[:50]),)) + for events in scripts: + with self.subTest(events=events): + outcome = receive_record(plan, events) + self.assertFalse(outcome.accepted or outcome.eof_is_success) + self.assertTrue(outcome.containment_required) + + def test_overflow_and_digest_or_identity_mismatch_require_containment(self) -> None: + precommit, raw, plan = setup() + overflow = receive_record(plan, (FakeReadEvent(DATA, raw[:100]), + FakeReadEvent(DATA, raw[100:] + b"x"),)) + self.assertEqual(overflow.classification, "OFFLINE_CHANNEL_OVERFLOW") + damaged = bytearray(raw) + damaged[10] ^= 1 + rejected = receive_record(plan, (FakeReadEvent(DATA, bytes(damaged)),)) + self.assertEqual(rejected.classification, "OFFLINE_RECORD_REJECTED") + other = WorkerPrecommit(precommit.attempt_id, b"c" * 16, + 101, 202, 7, 4096) + other_plan = ChannelPlan(other, 101, 7, b"c" * 16) + rejected = receive_record(other_plan, (FakeReadEvent(DATA, raw),)) + self.assertFalse(rejected.accepted) + + def test_writer_precommit_must_be_exclusive_and_exact(self) -> None: + precommit, _, _ = setup() + with self.assertRaises(ChannelModelError): + ChannelPlan(precommit, 102, 7, b"b" * 16) + with self.assertRaises(ChannelModelError): + ChannelPlan(precommit, 101, 7, b"b" * 16, False) + + def test_deadline_preempts_crossing_read(self) -> None: + _, raw, plan = setup() + events = (FakeReadEvent(DATA, raw[:64], ticks=256), + FakeReadEvent(DATA, raw[64:])) + outcome = receive_record(plan, events) + self.assertEqual(outcome.classification, "OFFLINE_CHANNEL_DEADLINE") + self.assertEqual(outcome.buffered, 64) + + def test_extra_event_after_boundary_and_invalid_event_are_rejected(self) -> None: + _, raw, plan = setup() + with self.assertRaises(ChannelModelError): + receive_record(plan, (FakeReadEvent(DATA, raw), FakeReadEvent(EOF))) + with self.assertRaises(ChannelModelError): + FakeReadEvent(DATA, b"") + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_phase10at_fd_deadline_model.py b/tests/test_phase10at_fd_deadline_model.py new file mode 100644 index 0000000..48adb42 --- /dev/null +++ b/tests/test_phase10at_fd_deadline_model.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Failure injection for Phase-1.0AT descriptor ownership.""" + +from __future__ import annotations + +import argparse +from pathlib import Path +import sys +import unittest + + +PARSER = argparse.ArgumentParser() +PARSER.add_argument("--root", type=Path, required=True) +ROOT = PARSER.parse_args().root +sys.path.insert(0, str(ROOT / "tools")) + +from phase10aq_worker_result_record import * # noqa: E402,F403 +from phase10at_fd_deadline_model import * # noqa: E402,F403 + + +PRECOMMIT = WorkerPrecommit(b"a" * 16, b"b" * 16, 101, 202, 7, 4096) +RAW = encode_record(PRECOMMIT, STATUS_SUCCESS, 4096) + + +def success_events() -> list[FakeFdEvent]: + return [FakeFdEvent(CREATE_PIPE, OK), + FakeFdEvent(SET_PARENT_READ_NONBLOCK, OK), + FakeFdEvent(SPAWN_RFFDG, OK), + FakeFdEvent(PARENT_CLOSE_WRITE, OK), + FakeFdEvent(CHILD_CLOSE_READ, OK), + FakeFdEvent(CHILD_CLOSE_WRITE, OK), + FakeFdEvent(REAP_CHILD, OK), + FakeFdEvent(PARENT_CLOSE_READ, OK)] + + +class FdDeadlineTests(unittest.TestCase): + def test_success_with_partial_reads_eintr_and_would_block(self) -> None: + reads = (FakeRead(EINTR), FakeRead(WOULD_BLOCK), + FakeRead(DATA, RAW[:40]), FakeRead(EINTR), + FakeRead(DATA, RAW[40:])) + result = run_fd_transaction( + PRECOMMIT, FakeFdFacade(tuple(success_events())), reads) + self.assertTrue(result.success and result.rffdg_used) + self.assertEqual(result.eintr_count, 2) + self.assertEqual(result.parent_fds_open + result.child_fds_open, 0) + self.assertFalse(result.worker_alive or result.live_fd_present) + + def test_every_setup_failure_closes_only_acquired_resources(self) -> None: + expected_cleanup = { + 0: [], + 1: [PARENT_CLOSE_WRITE, PARENT_CLOSE_READ], + 2: [PARENT_CLOSE_WRITE, PARENT_CLOSE_READ], + 3: [TERMINATE_CHILD, CHILD_CLOSE_READ, CHILD_CLOSE_WRITE, + REAP_CHILD, PARENT_CLOSE_WRITE, PARENT_CLOSE_READ], + 4: [TERMINATE_CHILD, CHILD_CLOSE_READ, CHILD_CLOSE_WRITE, + REAP_CHILD, PARENT_CLOSE_READ], + } + baseline = success_events()[:5] + for index, event in enumerate(baseline): + prefix = baseline[:index] + [FakeFdEvent(event.operation, ERROR)] + prefix += [FakeFdEvent(operation, OK) + for operation in expected_cleanup[index]] + with self.subTest(index=index): + result = run_fd_transaction( + PRECOMMIT, FakeFdFacade(tuple(prefix)), + (FakeRead(DATA, RAW),)) + self.assertFalse(result.success) + self.assertEqual(result.parent_fds_open + result.child_fds_open, 0) + + def test_deadline_eof_overflow_and_bad_record_contain_worker(self) -> None: + scripts = ( + (FakeRead(DATA, RAW[:10], ticks=252), FakeRead(DATA, RAW[10:])), + (FakeRead(DATA, RAW[:10]), FakeRead(EOF)), + (FakeRead(DATA, RAW[:100]), FakeRead(DATA, RAW[100:] + b"x")), + (FakeRead(DATA, RAW), FakeRead(WOULD_BLOCK)), + (FakeRead(DATA, bytes(bytearray(RAW[:1]) + RAW[1:])),), + ) + # Make the final script genuinely invalid. + damaged = bytearray(RAW) + damaged[0] ^= 1 + scripts = scripts[:-1] + ((FakeRead(DATA, bytes(damaged)),),) + cleanup = [FakeFdEvent(TERMINATE_CHILD, OK), + FakeFdEvent(CHILD_CLOSE_WRITE, OK), + FakeFdEvent(REAP_CHILD, OK), + FakeFdEvent(PARENT_CLOSE_READ, OK)] + for reads in scripts: + events = success_events()[:5] + cleanup + with self.subTest(reads=reads): + result = run_fd_transaction( + PRECOMMIT, FakeFdFacade(tuple(events)), reads) + self.assertFalse(result.success or result.worker_alive) + self.assertTrue(result.containment_required) + + def test_cleanup_failure_and_unused_operations_are_hard_errors(self) -> None: + events = [FakeFdEvent(CREATE_PIPE, OK), + FakeFdEvent(SET_PARENT_READ_NONBLOCK, ERROR), + FakeFdEvent(PARENT_CLOSE_WRITE, ERROR)] + with self.assertRaises(FdModelError): + run_fd_transaction(PRECOMMIT, FakeFdFacade(tuple(events)), + (FakeRead(DATA, RAW),)) + with self.assertRaises(FdModelError): + run_fd_transaction( + PRECOMMIT, + FakeFdFacade(tuple(success_events() + + [FakeFdEvent(PARENT_CLOSE_READ, OK)])), + (FakeRead(DATA, RAW),)) + + def test_invalid_read_and_boundary_are_rejected(self) -> None: + with self.assertRaises(FdModelError): + FakeRead(DATA, b"") + with self.assertRaises(FdModelError): + run_fd_transaction(PRECOMMIT, FakeFdFacade(tuple(success_events())), ()) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_phase10av_launch_context_canary.py b/tests/test_phase10av_launch_context_canary.py new file mode 100644 index 0000000..aafe1f5 --- /dev/null +++ b/tests/test_phase10av_launch_context_canary.py @@ -0,0 +1,113 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Host tests for the target-free Phase-1.0AV canary contract.""" + +from __future__ import annotations + +import argparse +from dataclasses import replace +from pathlib import Path +import sys +import unittest + + +PARSER = argparse.ArgumentParser() +PARSER.add_argument("--root", type=Path, required=True) +ROOT = PARSER.parse_args().root +sys.path.insert(0, str(ROOT / "tools")) + +from phase10av_launch_context_canary import * # noqa: E402,F403 + + +def plan() -> CanaryPairPlan: + return CanaryPairPlan( + PHASE, FIRMWARE, PROTOCOL_MAGIC, "a" * 64, + (CanaryArm(RAW_ELFLDR, "b" * 64, "CHIMERA_AV_RAW_0001", "c" * 64), + CanaryArm(BIGAPP_CANDIDATE, "d" * 64, + "CHIMERA_AV_BIGAPP_01", "e" * 64)), + True, False, False, False, False, False, False, False, False, False) + + +def observation(arm: CanaryArm, submit_result: int) -> CanaryObservation: + return CanaryObservation( + arm.kind, arm.launcher_sha256, "a" * 64, arm.run_id, PROTOCOL_MAGIC, + True, -1, 20, True, submit_result, 0, 10, True, 30, True, False, 0, 0) + + +class LaunchContextCanaryTests(unittest.TestCase): + def test_complete_plan_and_equal_results(self) -> None: + value = plan() + validate_plan(value) + result = classify_pair( + value, tuple(observation(arm, -1) for arm in value.arms)) + self.assertTrue(result.pair_comparable) + self.assertEqual(result.status, "NO_SUBMIT_RETURN_DIFFERENCE") + self.assertFalse(result.root_cause_proven or result.visible_output_proven) + + def test_changed_return_is_candidate_only(self) -> None: + value = plan() + result = classify_pair( + value, (observation(value.arms[0], -1), + observation(value.arms[1], 0))) + self.assertTrue(result.launch_context_candidate) + self.assertFalse(result.root_cause_proven) + self.assertFalse(result.visible_output_proven) + self.assertFalse(result.firmware_behavior_proven) + self.assertFalse(result.device_action_authorized) + + def test_plan_requires_distinct_arms_and_approvals(self) -> None: + value = plan() + for field, replacement in ( + ("launcher_sha256", value.arms[0].launcher_sha256), + ("run_id", value.arms[0].run_id), + ("approval_sha256", value.arms[0].approval_sha256)): + arms = (value.arms[0], replace(value.arms[1], **{field: replacement})) + with self.subTest(field=field), self.assertRaises(CanaryContractError): + validate_plan(replace(value, arms=arms)) + + def test_plan_rejects_every_authority_and_retry(self) -> None: + for field in ("automatic_retry", "reconnect", "resume", "installation", + "autoload", "device_write_authorized", + "app_termination_authorized", "result_reception_authorized", + "activation_authorized"): + with self.subTest(field=field), self.assertRaises(CanaryContractError): + validate_plan(replace(plan(), **{field: True})) + + def test_missing_or_early_terminal_is_incomplete(self) -> None: + value = plan() + baseline = observation(value.arms[0], -1) + for candidate in ( + replace(observation(value.arms[1], 0), terminal_seen=False), + replace(observation(value.arms[1], 0), cleanup_complete=False), + replace(observation(value.arms[1], 0), terminal_sequence=20), + replace(observation(value.arms[1], 0), submit_sequence=20), + replace(observation(value.arms[1], 0), d04_seen=False), + replace(observation(value.arms[1], 0), submit_seen=False)): + with self.subTest(candidate=candidate): + result = classify_pair(value, (baseline, candidate)) + self.assertEqual(result.status, "INCOMPLETE_NO_CAUSAL_COMPARISON") + + def test_identity_drift_is_hard_failure(self) -> None: + value = plan() + baseline = observation(value.arms[0], -1) + candidate = observation(value.arms[1], 0) + for field, replacement in (("payload_sha256", "f" * 64), + ("run_id", "CHIMERA_AV_WRONG_001"), + ("protocol_magic", "CHD10OLD"), + ("launcher_sha256", "f" * 64)): + with self.subTest(field=field), self.assertRaises(CanaryContractError): + classify_pair(value, (baseline, replace(candidate, **{field: replacement}))) + + def test_forbidden_result_claims_are_hard_failure(self) -> None: + value = plan() + baseline = observation(value.arms[0], -1) + candidate = observation(value.arms[1], 0) + for field, replacement in (("retry_count", 1), + ("persistent_write_count", 1), + ("visible_output_observed", True)): + with self.subTest(field=field), self.assertRaises(CanaryContractError): + classify_pair(value, (baseline, replace(candidate, **{field: replacement}))) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_phase10ax_canary_protocol_model.py b/tests/test_phase10ax_canary_protocol_model.py new file mode 100644 index 0000000..dcfeee6 --- /dev/null +++ b/tests/test_phase10ax_canary_protocol_model.py @@ -0,0 +1,119 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Failure injection for the host-only Phase-1.0AX AV protocol.""" + +from __future__ import annotations + +import argparse +from dataclasses import replace +from pathlib import Path +import sys +import unittest + + +PARSER = argparse.ArgumentParser() +PARSER.add_argument("--root", type=Path, required=True) +ROOT = PARSER.parse_args().root +sys.path.insert(0, str(ROOT / "tools")) + +from phase10ax_canary_protocol_model import * # noqa: E402,F403 + + +def frame(sequence: int, stage: int, value: int) -> bytes: + return encode_frame(Frame(sequence, stage, KIND_RAW, RAW0_VALID, + value, 0, 0, 0, 0)) + + +def snapshot() -> CleanupSnapshot: + return CleanupSnapshot(True, True, S15_COMPLETE, 0, 0x2f, 0, 0, 1) + + +def trace() -> tuple[bytes, ...]: + terminal = build_cleanup_terminal(4, snapshot()) + assert terminal is not None + return (frame(1, D07, -1), frame(2, D12, 5), + frame(3, D04, -1), terminal) + + +class CanaryProtocolTests(unittest.TestCase): + def test_exact_trace_is_complete_without_visibility_claim(self) -> None: + result = validate_trace(trace()) + self.assertTrue(result.complete) + self.assertEqual((result.submit_result, result.sdl_result), (-1, -1)) + self.assertEqual(result.cleaned_mask, 0x2f) + self.assertFalse(result.visible_output_proven) + self.assertFalse(result.firmware_behavior_proven) + self.assertFalse(result.device_action_authorized) + + def test_every_single_byte_mutation_breaks_frame(self) -> None: + raw = frame(1, D07, -1) + for index in range(FRAME_SIZE): + damaged = bytearray(raw) + damaged[index] ^= 1 + with self.subTest(index=index), self.assertRaises(CanaryProtocolError): + parse_frame(bytes(damaged)) + + def test_d12_cannot_be_terminal(self) -> None: + with self.assertRaises(CanaryProtocolError): + encode_frame(Frame(2, D12, KIND_PAIR, + RAW0_VALID | RAW1_VALID | TERMINAL, + 5, 104, 0, 0, 0)) + + def test_only_exact_cleanup_state_emits_d14(self) -> None: + base = snapshot() + self.assertIsNotNone(build_cleanup_terminal(1, base)) + cases = (("rarch_main_returned", False), ("d04_emitted", False), + ("phase", 14), ("initialized_mask", 1), + ("cleanup_order_errors", 1), ("cleanup_failure_count", 1)) + for field, value in cases: + with self.subTest(field=field): + self.assertIsNone(build_cleanup_terminal( + 1, replace(base, **{field: value}))) + + def test_cleanup_failure_is_independent_of_first_runtime_error(self) -> None: + base = replace(snapshot(), rarch_main_result=-1, + cleanup_failure_count=1) + self.assertIsNone(build_cleanup_terminal(1, base)) + + def test_trace_rejects_order_duplicates_and_post_terminal_data(self) -> None: + base = trace() + cases = (base[:-1], (base[2], base[0], base[3]), + (base[0], base[0], base[2], base[3]), + base + (frame(5, 1, 0),), + (frame(2, D07, -1), frame(1, D04, -1), base[-1])) + for candidate in cases: + with self.subTest(candidate=candidate), self.assertRaises(CanaryProtocolError): + validate_trace(candidate) + + def test_recomputed_crc_cannot_hide_cleanup_claim(self) -> None: + bad = encode_frame(Frame(4, D14, KIND_PAIR, + RAW0_VALID | RAW1_VALID | TERMINAL, + 1, 0x2f, 1, 0, 0)) + candidate = trace()[:-1] + (bad,) + with self.assertRaises(CanaryProtocolError): + validate_trace(candidate) + + def test_valid_crc_with_wrong_stage_semantics_fails(self) -> None: + base = trace() + wrong_submit = encode_frame(Frame(1, D07, KIND_PAIR, + RAW0_VALID | RAW1_VALID, + -1, 0, 0, 0, 0)) + terminal = build_cleanup_terminal(5, snapshot()) + assert terminal is not None + duplicate_d12 = (frame(1, D07, -1), frame(2, D12, 5), + frame(3, D12, 5), frame(4, D04, -1), terminal) + for candidate in ((wrong_submit,) + base[1:], duplicate_d12): + with self.subTest(candidate=candidate), self.assertRaises(CanaryProtocolError): + validate_trace(candidate) + + def test_invalid_numeric_and_boolean_boundaries_fail(self) -> None: + with self.assertRaises(CanaryProtocolError): + encode_frame(Frame(0, D07, KIND_RAW, RAW0_VALID, 0, 0, 0, 0, 0)) + with self.assertRaises(CanaryProtocolError): + build_cleanup_terminal(1, replace(snapshot(), d04_emitted=1)) + with self.assertRaises(CanaryProtocolError): + build_cleanup_terminal(1, replace(snapshot(), cleaned_mask=-1)) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_phase10dc_bigapp_gate_contract.py b/tests/test_phase10dc_bigapp_gate_contract.py new file mode 100644 index 0000000..1683cbf --- /dev/null +++ b/tests/test_phase10dc_bigapp_gate_contract.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Host tests for the pure Phase-1.0DC BigApp gate.""" +from __future__ import annotations +import argparse +from dataclasses import replace +from pathlib import Path +import sys, unittest + +P=argparse.ArgumentParser();P.add_argument("--root",type=Path,required=True);ROOT=P.parse_args().root +sys.path.insert(0,str(ROOT/"tools")) +from phase10dc_bigapp_gate_contract import (BigAppGateError,BigAppGateRecord,PHASE,validate_candidate,validate_inactive) # noqa:E402 + +def inactive(): + return BigAppGateRecord(PHASE,False,*([None]*14),*([False]*13)) + +def candidate(): + return BigAppGateRecord(PHASE,True,"9.60","PPSA01659","CHIMERA_DC_001","2030-01-01T00:00:00Z","2030-01-01T00:05:00Z","chimera_bigapp_canary_launcher.elf",65536,"a"*64,"retroarch_ps5_launch_canary.elf",1845240,"8dadce9d9faaef21ea129a3d216c768eea9a3ca9bf8ecb8d852e376b58a9bf95","b"*64,"CHD10AV1","D14",True,True,False,False,False,False,False,False,False,False,True,True,True) + +class Tests(unittest.TestCase): + def test_inactive(self):validate_inactive(inactive()) + def test_candidate_data(self):validate_candidate(candidate()) + def test_exact_payload(self): + with self.assertRaises(BigAppGateError):validate_candidate(replace(candidate(),payload_sha256="c"*64)) + def test_no_existing_bigapp(self): + with self.assertRaises(BigAppGateError):validate_candidate(replace(candidate(),no_running_bigapp_attested=False)) + def test_effect_acceptance(self): + with self.assertRaises(BigAppGateError):validate_candidate(replace(candidate(),kernel_ptrace_effects_accepted=False)) + def test_forbidden_effects(self): + for field in ("app_termination_authorized","persistent_write_authorized","system_remount_authorized","installation_authorized","autoload_authorized","automatic_retry","reconnect","fallback_title"): + with self.subTest(field=field),self.assertRaises(BigAppGateError):validate_candidate(replace(candidate(),**{field:True})) + def test_every_bounded_proof(self): + for field in ("bounded_parent_detach_proven","bounded_child_cleanup_proven","bounded_result_channel_proven"): + with self.subTest(field=field),self.assertRaises(BigAppGateError):validate_candidate(replace(candidate(),**{field:False})) + def test_window_and_title(self): + with self.assertRaises(BigAppGateError):validate_candidate(replace(candidate(),expires_at="2030-01-01T00:05:01Z")) + with self.assertRaises(BigAppGateError):validate_candidate(replace(candidate(),title_id="FAKE00000")) + +if __name__=="__main__":unittest.main(argv=[__file__]) diff --git a/tests/test_phase10df_title_observer_contract.py b/tests/test_phase10df_title_observer_contract.py new file mode 100644 index 0000000..29ed312 --- /dev/null +++ b/tests/test_phase10df_title_observer_contract.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +from __future__ import annotations +import argparse +from dataclasses import replace +from pathlib import Path +import sys,unittest +P=argparse.ArgumentParser();P.add_argument("--root",type=Path,required=True);ROOT=P.parse_args().root;sys.path.insert(0,str(ROOT/"tools")) +from phase10df_title_observer_contract import ObserverError,ObserverPlan,Outcome,PHASE,SyntheticResult,classify_synthetic,validate_candidate,validate_inactive # noqa:E402 +def inactive():return ObserverPlan(PHASE,False,*([None]*8),*([False]*9)) +def query():return ObserverPlan(PHASE,True,"9.60","PPSA01659","SOURCE_BOUND_QUERY","a"*64,None,None,"b"*64,4096,True,True,False,False,False,False,False,False,False) +def result(**changes): + value=SyntheticResult("9.60","PPSA01659","SOURCE_BOUND_QUERY","b"*64,True,64,True,False,None) + return replace(value,**changes) +class Tests(unittest.TestCase): + def test_inactive(self):validate_inactive(inactive()) + def test_source_query_candidate(self):validate_candidate(query()) + def test_path_requires_provenance(self): + plan=replace(query(),method="EXACT_PATH_METADATA",exact_literal_path="/fixed/path",path_provenance_sha256="c"*64);validate_candidate(plan) + with self.assertRaises(ObserverError):validate_candidate(replace(plan,path_provenance_sha256=None)) + def test_no_shell_or_enumeration(self): + for field in ("shell_present","directory_enumeration","title_launch","app_termination","device_write","retry","reconnect"): + with self.subTest(field=field),self.assertRaises(ObserverError):validate_candidate(replace(query(),**{field:True})) + def test_explicit_results(self): + self.assertEqual(classify_synthetic(query(),result()),Outcome.PRESENT) + self.assertEqual(classify_synthetic(query(),result(explicit_present=False,explicit_absent=True)),Outcome.ABSENT) + def test_error_and_incomplete_are_unknown(self): + self.assertEqual(classify_synthetic(query(),result(error_code=-1)),Outcome.UNKNOWN) + self.assertEqual(classify_synthetic(query(),result(complete=False)),Outcome.UNKNOWN) + def test_ambiguous_and_oversize_rejected(self): + with self.assertRaises(ObserverError):classify_synthetic(query(),result(explicit_absent=True)) + with self.assertRaises(ObserverError):classify_synthetic(query(),result(result_bytes=4097)) + def test_binding_mismatch_rejected(self): + with self.assertRaises(ObserverError):classify_synthetic(query(),result(title_id="OTHER")) +if __name__=="__main__":unittest.main(argv=[__file__]) diff --git a/tests/test_phase10dh_snapshot_query.py b/tests/test_phase10dh_snapshot_query.py new file mode 100644 index 0000000..522fd90 --- /dev/null +++ b/tests/test_phase10dh_snapshot_query.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +from __future__ import annotations +import argparse,hashlib,sqlite3,sys,tempfile,unittest +from pathlib import Path +P=argparse.ArgumentParser();P.add_argument("--root",type=Path,required=True);ROOT=P.parse_args().root;sys.path.insert(0,str(ROOT/"tools")) +from phase10dh_snapshot_query import Outcome,PHASE,SnapshotBinding,SnapshotError,query_snapshot # noqa:E402 +def binding(path):data=path.read_bytes();return SnapshotBinding(PHASE,"PPSA01659",len(data),hashlib.sha256(data).hexdigest()) +class Phase10DH(unittest.TestCase): + def make(self,rows=(),schema=True): + temp=tempfile.TemporaryDirectory();path=Path(temp.name)/"snapshot.db";db=sqlite3.connect(path) + if schema: + db.execute("CREATE TABLE tbl_appinfo (titleId TEXT, key TEXT, val TEXT)");db.executemany("INSERT INTO tbl_appinfo VALUES (?, 'K', 'V')",[(x,) for x in rows]) + else:db.execute("CREATE TABLE other (value TEXT)") + db.commit();db.close();return temp,path + def test_present_and_no_sidecars(self): + temp,path=self.make(["PPSA01659"]) + with temp:self.assertEqual(query_snapshot(path,binding(path)),Outcome.PRESENT);self.assertEqual([p.name for p in Path(temp.name).iterdir()],["snapshot.db"]) + def test_absent(self): + temp,path=self.make(["PPSA01650"]) + with temp:self.assertEqual(query_snapshot(path,binding(path)),Outcome.ABSENT) + def test_schema_mismatch_unknown(self): + temp,path=self.make(schema=False) + with temp:self.assertEqual(query_snapshot(path,binding(path)),Outcome.UNKNOWN) + def test_duplicate_is_unknown(self): + temp,path=self.make(["PPSA01659","PPSA01659"]) + with temp:self.assertEqual(query_snapshot(path,binding(path)),Outcome.UNKNOWN) + def test_hash_mismatch_rejected(self): + temp,path=self.make() + with temp: + bad=binding(path);bad=SnapshotBinding(bad.phase,bad.title_id,bad.size,"0"*64) + with self.assertRaises(SnapshotError):query_snapshot(path,bad) + def test_non_database_rejected(self): + with tempfile.TemporaryDirectory() as name: + path=Path(name)/"x";path.write_bytes(b"not sqlite") + with self.assertRaises(SnapshotError):query_snapshot(path,binding(path)) +if __name__=="__main__":unittest.main(argv=[__file__]) diff --git a/tests/test_phase10dm_snapshot_protocol.py b/tests/test_phase10dm_snapshot_protocol.py new file mode 100644 index 0000000..1c882ec --- /dev/null +++ b/tests/test_phase10dm_snapshot_protocol.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +from __future__ import annotations +import argparse,struct,sys,unittest +from pathlib import Path +P=argparse.ArgumentParser();P.add_argument("--root",type=Path,required=True);R=P.parse_args().root;sys.path.insert(0,str(R/"tools")) +from phase10dm_snapshot_protocol import ProtocolError,parse_stream # noqa:E402 +F=struct.Struct("<8sIIiiQQQQq") +def frame(kind,status=0,size=32,sent=0,dev=1,ino=2,mtime=3):return F.pack(b"CHS10DM1",1,kind,status,0,size,sent,dev,ino,mtime) +def valid(): + data=b"SQLite format 3\x00"+b"x"*16 + return frame(1,size=len(data))+data+frame(2,size=len(data),sent=len(data)) +class Tests(unittest.TestCase): + def test_valid(self):self.assertEqual(parse_stream(valid()).size,32) + def test_truncated(self): + with self.assertRaises(ProtocolError):parse_stream(valid()[:-1]) + def test_error(self): + with self.assertRaises(ProtocolError):parse_stream(frame(3,status=1)+b"x"*64) + def test_metadata_change(self): + raw=valid();raw=raw[:-64]+frame(2,size=32,sent=32,ino=9) + with self.assertRaises(ProtocolError):parse_stream(raw) + def test_non_sqlite(self): + raw=frame(1)+b"z"*32+frame(2,sent=32) + with self.assertRaises(ProtocolError):parse_stream(raw) +if __name__=="__main__":unittest.main(argv=[__file__]) diff --git a/tests/test_phase10dn_snapshot_receiver.py b/tests/test_phase10dn_snapshot_receiver.py new file mode 100644 index 0000000..7efb8c2 --- /dev/null +++ b/tests/test_phase10dn_snapshot_receiver.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +from __future__ import annotations +import argparse,struct,sys,tempfile,unittest +from pathlib import Path +P=argparse.ArgumentParser();P.add_argument("--root",type=Path,required=True);R=P.parse_args().root;sys.path.insert(0,str(R/"tools")) +from phase10dm_snapshot_protocol import ProtocolError # noqa:E402 +from phase10dn_snapshot_receiver import SnapshotReceiver # noqa:E402 +F=struct.Struct("<8sIIiiQQQQq");DATA=b"SQLite format 3\x00"+b"x"*16 +def frame(k,ino=2):return F.pack(b"CHS10DM1",1,k,0,0,len(DATA),len(DATA) if k==2 else 0,1,ino,3) +class Tests(unittest.TestCase): + def test_chunked_success(self): + with tempfile.TemporaryDirectory() as d: + p=Path(d)/"snapshot.db";r=SnapshotReceiver(p);raw=frame(1)+DATA+frame(2) + for byte in raw:r.feed(bytes([byte])) + self.assertEqual(r.finish().size,len(DATA));self.assertEqual(p.read_bytes(),DATA) + def test_existing_rejected(self): + with tempfile.TemporaryDirectory() as d: + p=Path(d)/"x";p.write_bytes(b"x") + with self.assertRaises(ProtocolError):SnapshotReceiver(p) + def test_truncated_aborts(self): + with tempfile.TemporaryDirectory() as d: + r=SnapshotReceiver(Path(d)/"x");r.feed(frame(1)+DATA) + with self.assertRaises(ProtocolError):r.finish() + self.assertEqual(r.state,"ABORTED") + def test_metadata_mismatch(self): + with tempfile.TemporaryDirectory() as d: + r=SnapshotReceiver(Path(d)/"x");r.feed(frame(1)+DATA+frame(2,ino=9)) + with self.assertRaises(ProtocolError):r.finish() + def test_trailing_rejected(self): + with tempfile.TemporaryDirectory() as d: + r=SnapshotReceiver(Path(d)/"x") + with self.assertRaises(ProtocolError):r.feed(frame(1)+DATA+frame(2)+b"x") +if __name__=="__main__":unittest.main(argv=[__file__]) diff --git a/tests/test_phase10do_one_shot_snapshot_runner.py b/tests/test_phase10do_one_shot_snapshot_runner.py new file mode 100644 index 0000000..67f83ed --- /dev/null +++ b/tests/test_phase10do_one_shot_snapshot_runner.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +from __future__ import annotations +import argparse,json,sys,tempfile,unittest +from pathlib import Path +P=argparse.ArgumentParser();P.add_argument("--root",type=Path,required=True);R=P.parse_args().root;sys.path.insert(0,str(R/"tools")) +import phase10do_one_shot_snapshot_runner as runner # noqa:E402 + +def record(base:Path): + return {"active":True,"run_id":"DM_TEST","target":"192.0.2.1","port":9021,"artifact_size":runner.ARTIFACT_SIZE,"artifact_sha256":runner.ARTIFACT_SHA256,"snapshot_path":str((base/"snapshot.db").resolve()),"receipt_path":str((base/"consumed.json").resolve()),"not_before":99.0,"not_after":101.0,"one_connection":True,"one_transfer":True,"one_execution":True,"result_receive":True,"target_file_read":True,"device_write":False,"installation":False,"autoload":False,"retry":False,"reconnect":False} +class Tests(unittest.TestCase): + def test_inactive_tracked_manifest_rejected(self): + manifest=json.loads((R/"manifests/retroarch/phase-1.0do-inactive-snapshot-runner.json").read_text()) + with self.assertRaises(runner.RunnerError):runner.validate_records(manifest,manifest,100.0) + def test_exact_active_pair_validates(self): + with tempfile.TemporaryDirectory() as d: + value=record(Path(d));self.assertEqual(runner.validate_records(value,dict(value),100.0)["run_id"],"DM_TEST") + def test_mismatch_and_forbidden_authority_rejected(self): + with tempfile.TemporaryDirectory() as d: + value=record(Path(d));other=dict(value);other["run_id"]="OTHER" + with self.assertRaises(runner.RunnerError):runner.validate_records(value,other,100.0) + value["device_write"]=True + with self.assertRaises(runner.RunnerError):runner.validate_records(value,value,100.0) + def test_receipt_is_exclusive(self): + with tempfile.TemporaryDirectory() as d: + value=record(Path(d));runner._consume(value) + with self.assertRaises(FileExistsError):runner._consume(value) +if __name__=="__main__":unittest.main(argv=[__file__]) diff --git a/tests/test_phase10dq_inventory_protocol.py b/tests/test_phase10dq_inventory_protocol.py new file mode 100644 index 0000000..ba42337 --- /dev/null +++ b/tests/test_phase10dq_inventory_protocol.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +from __future__ import annotations +import argparse,struct,sys,unittest +from pathlib import Path +P=argparse.ArgumentParser();P.add_argument("--root",type=Path,required=True);R=P.parse_args().root;sys.path.insert(0,str(R/"tools")) +from phase10dq_inventory_protocol import ProtocolError,parse_stream # noqa:E402 +F=struct.Struct("<8sIIiiIIQ24s");E=struct.Struct("<256sIIIIQQq24s") +def frame(kind,count=0,transferred=0,status=0):return F.pack(b"CHI10DQ1",1,kind,status,0,count,0,transferred,bytes(24)) +def entry(name="eboot.bin"): + raw=name.encode();return E.pack(raw+bytes(256-len(raw)),len(raw),8,0o100555,0,123,9,10,bytes(24)) +class Tests(unittest.TestCase): + def test_success(self): + value=parse_stream(frame(1)+entry()+frame(2,1,320));self.assertEqual(value[0].name,"eboot.bin");self.assertEqual(value[0].size,123) + def test_truncated(self): + with self.assertRaises(ProtocolError):parse_stream(frame(1)+entry()) + def test_target_error(self): + with self.assertRaisesRegex(ProtocolError,"target error"):parse_stream(frame(3,status=1)+frame(2)) + def test_traversal_rejected(self): + with self.assertRaises(ProtocolError):parse_stream(frame(1)+entry("../x")+frame(2,1,320)) + def test_terminal_count_rejected(self): + with self.assertRaises(ProtocolError):parse_stream(frame(1)+entry()+frame(2,2,320)) +if __name__=="__main__":unittest.main(argv=[__file__]) diff --git a/tests/test_phase10dr_inventory_runner.py b/tests/test_phase10dr_inventory_runner.py new file mode 100644 index 0000000..78853f9 --- /dev/null +++ b/tests/test_phase10dr_inventory_runner.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +from __future__ import annotations +import argparse,json,sys,tempfile,unittest +from pathlib import Path +P=argparse.ArgumentParser();P.add_argument("--root",type=Path,required=True);R=P.parse_args().root;sys.path.insert(0,str(R/"tools")) +import phase10dr_inventory_runner as runner # noqa:E402 +def record(root):return {"active":True,"run_id":"TEST","target":"192.0.2.1","port":9021,"artifact_size":runner.ARTIFACT_SIZE,"artifact_sha256":runner.ARTIFACT_SHA256,"output_path":str((root/"out.json").resolve()),"receipt_path":str((root/"receipt.json").resolve()),"not_before":1.0,"not_after":3.0,"one_connection":True,"one_transfer":True,"one_execution":True,"result_receive":True,"directory_inventory":True,"possible_atime_effect_acknowledged":True,"device_file_content_read":False,"persistent_device_write":False,"installation":False,"autoload":False,"retry":False,"reconnect":False} +class Tests(unittest.TestCase): + def test_tracked_inactive(self): + value=json.loads((R/"manifests/retroarch/phase-1.0dr-inactive-inventory-runner.json").read_text()) + with self.assertRaises(runner.RunnerError):runner.validate_records(value,value,2.0) + def test_exact_pair(self): + with tempfile.TemporaryDirectory() as d: + value=record(Path(d));self.assertEqual(runner.validate_records(value,dict(value),2.0)["run_id"],"TEST") + def test_atime_ack_required(self): + with tempfile.TemporaryDirectory() as d: + value=record(Path(d));value["possible_atime_effect_acknowledged"]=False + with self.assertRaises(runner.RunnerError):runner.validate_records(value,value,2.0) + def test_forbidden_content_read(self): + with tempfile.TemporaryDirectory() as d: + value=record(Path(d));value["device_file_content_read"]=True + with self.assertRaises(runner.RunnerError):runner.validate_records(value,value,2.0) +if __name__=="__main__":unittest.main(argv=[__file__]) diff --git a/tests/test_phase10ds_metadata_protocol.py b/tests/test_phase10ds_metadata_protocol.py new file mode 100644 index 0000000..8a662bc --- /dev/null +++ b/tests/test_phase10ds_metadata_protocol.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +import argparse,struct,sys,unittest +from pathlib import Path +P=argparse.ArgumentParser();P.add_argument("--root",type=Path,required=True);R=P.parse_args().root;sys.path.insert(0,str(R/"tools")) +from phase10ds_metadata_protocol import ProtocolError,parse_stream # noqa:E402 +F=struct.Struct("<8sIIiiIIQ24s");DATA=[b"a",b"{}",b"",b"crc"] +def frame(k,index=0,length=0,total=0,status=0):return F.pack(b"CHM10DS1",1,k,status,0,index,length,total,bytes(24)) +def stream(): + raw=frame(1,4);total=0 + for i,data in enumerate(DATA):raw+=frame(2,i,len(data),total)+data;total+=len(data) + return raw+frame(3,4,0,total) +class Tests(unittest.TestCase): + def test_success(self):self.assertEqual(parse_stream(stream())["app.json"],b"{}") + def test_truncated(self): + with self.assertRaises(ProtocolError):parse_stream(stream()[:-1]) + def test_wrong_index(self): + raw=bytearray(stream());raw[64+24:64+28]=(3).to_bytes(4,"little") + with self.assertRaises(ProtocolError):parse_stream(bytes(raw)) + def test_target_error(self): + with self.assertRaisesRegex(ProtocolError,"target error"):parse_stream(frame(4,status=1)+bytes(64)) +if __name__=="__main__":unittest.main(argv=[__file__]) diff --git a/tests/test_phase10dt_metadata_runner.py b/tests/test_phase10dt_metadata_runner.py new file mode 100644 index 0000000..42b893c --- /dev/null +++ b/tests/test_phase10dt_metadata_runner.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +import argparse,json,sys,tempfile,unittest +from pathlib import Path +P=argparse.ArgumentParser();P.add_argument("--root",type=Path,required=True);R=P.parse_args().root;sys.path.insert(0,str(R/"tools")) +import phase10dt_metadata_runner as m # noqa:E402 +def rec(d):return {"active":True,"run_id":"T","target":"192.0.2.1","port":9021,"artifact_size":m.ARTIFACT_SIZE,"artifact_sha256":m.ARTIFACT_SHA256,"output_path":str((d/"o").resolve()),"receipt_path":str((d/"r").resolve()),"not_before":1,"not_after":3,"one_connection":True,"one_transfer":True,"one_execution":True,"result_receive":True,"four_exact_metadata_reads":True,"possible_atime_effect_acknowledged":True,"app_pkg_read":False,"backup_read":False,"persistent_device_write":False,"installation":False,"autoload":False,"retry":False,"reconnect":False} +class Tests(unittest.TestCase): + def test_inactive(self): + v=json.loads((R/"manifests/retroarch/phase-1.0dt-inactive-metadata-runner.json").read_text()) + with self.assertRaises(m.RunnerError):m.validate(v,v,2) + def test_exact(self): + with tempfile.TemporaryDirectory() as x:v=rec(Path(x));self.assertEqual(m.validate(v,dict(v),2)["run_id"],"T") + def test_package_forbidden(self): + with tempfile.TemporaryDirectory() as x: + v=rec(Path(x));v["app_pkg_read"]=True + with self.assertRaises(m.RunnerError):m.validate(v,v,2) +if __name__=="__main__":unittest.main(argv=[__file__]) diff --git a/tests/test_phase10dv_package_stat_runner.py b/tests/test_phase10dv_package_stat_runner.py new file mode 100644 index 0000000..f6c0103 --- /dev/null +++ b/tests/test_phase10dv_package_stat_runner.py @@ -0,0 +1,12 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +import argparse,json,sys,unittest +from pathlib import Path +P=argparse.ArgumentParser();P.add_argument("--root",type=Path,required=True);R=P.parse_args().root;sys.path.insert(0,str(R/"tools")) +import phase10dv_package_stat_runner as m # noqa:E402 +class Tests(unittest.TestCase): + def test_tracked_record_is_inactive(self): + v=json.loads((R/"manifests/retroarch/phase-1.0dv-inactive-package-stat-runner.json").read_text()) + with self.assertRaises(m.Error):m.validate(v,v,0) + def test_record_size(self):self.assertEqual(m.REC.size,72) +if __name__=="__main__":unittest.main(argv=[__file__]) diff --git a/tests/test_phase10dx_package_readback_runner.py b/tests/test_phase10dx_package_readback_runner.py new file mode 100644 index 0000000..774f3a5 --- /dev/null +++ b/tests/test_phase10dx_package_readback_runner.py @@ -0,0 +1,12 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +import argparse,json,sys,unittest +from pathlib import Path +P=argparse.ArgumentParser();P.add_argument("--root",type=Path,required=True);R=P.parse_args().root;sys.path.insert(0,str(R/"tools")) +import phase10dx_package_readback_runner as m # noqa:E402 +class Tests(unittest.TestCase): + def test_inactive_manifest_rejected(self): + v=json.loads((R/"manifests/retroarch/phase-1.0dx-inactive-package-readback-runner.json").read_text()) + with self.assertRaises(m.Error):m.validate(v,v,0) + def test_protocol_constants(self):self.assertEqual(m.FRAME.size,64);self.assertEqual(m.MAX_WIRE,18153600) +if __name__=="__main__":unittest.main(argv=[__file__]) diff --git a/tests/test_phase10t_shsrv_transcript.py b/tests/test_phase10t_shsrv_transcript.py new file mode 100644 index 0000000..09bc8fb --- /dev/null +++ b/tests/test_phase10t_shsrv_transcript.py @@ -0,0 +1,128 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Offline synthetic-transcript tests for Phase-1.0T sanitization.""" + +from __future__ import annotations + +import argparse +import importlib.util +import json +from pathlib import Path +import sys + + +def load(path: Path): + spec = importlib.util.spec_from_file_location("phase10t_transcript", path) + assert spec and spec.loader + module = importlib.util.module_from_spec(spec) + sys.modules[spec.name] = module + spec.loader.exec_module(module) + return module + + +def require(value: bool, message: str) -> None: + if not value: + raise RuntimeError(message) + + +def greeting(extra: str = "") -> str: + return ( + "Welcome to shsrv.elf running on pid 123, compiled Jul 22 2026 at 12:34:56\n" + "Model: synthetic-model\n" + "S/N: SYNTHETIC-SERIAL-DO-NOT-RETAIN\n" + "S/W: 9.60\n" + "SoC temp: 40 C\n" + "CPU temp: 41 C\n" + "CPU freq: 3500 MHz\n" + extra) + + +def help_text(commands: list[str]) -> str: + return "Builtin commands:\n" + "".join( + f" {command} - synthetic\n" for command in commands) + "\n" + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + module = load(args.root.resolve() / "tools/phase10t_shsrv_transcript.py") + current_commands = "authid browse cat cd chgrp chmod chown chroot cmp cp df echo env exec exit export file find grep hbdbg hbldr hexdump http2_get id kill launch ln ls mkdir mknod mount mv notify pkg_install procstat ps pwd reptyr rm rmdir sfocreate sfoinfo sleep stat sum suspend sync sysctl touch umount".split() + v07_commands = "browse cat cd chgrp chmod chown chroot cmp cp echo env exec exit export file find grep hbldr help hexdump http2_get id kill launch ln ls mkdir mknod mount mv notify ps pwd rm rmdir sfocreate sfoinfo sleep stat sum sync sysctl touch umount".split() + cases = [] + + def case(name): + def register(function): + cases.append((name, function)) + return function + return register + + @case("01 empty transcript is invalid and non-exact") + def _(): + result = module.parse_transcript("") + require(result["classification"] == "INVALID_OR_INCOMPLETE" and result["exact_identity"] is False, "empty input accepted") + + @case("02 greeting is compile metadata only") + def _(): require(module.parse_transcript(greeting())["classification"] == "COMPILE_METADATA_ONLY", "greeting promoted") + + @case("03 serial value is discarded") + def _(): + result = module.parse_transcript(greeting()) + require(result["sensitive_input"]["serial_line_seen"] is True and "SYNTHETIC-SERIAL" not in json.dumps(result), "serial retained") + + @case("04 telemetry values are discarded") + def _(): + result = module.parse_transcript(greeting()) + require(result["sensitive_input"]["telemetry_line_seen"] is True and "3500" not in json.dumps(result), "telemetry retained") + + @case("05 firmware metadata is retained") + def _(): require(module.parse_transcript(greeting())["compile_metadata"]["firmware"] == "9.60", "firmware lost") + + @case("06 v0.19 help matches only a source family") + def _(): + result = module.parse_transcript(greeting(help_text(current_commands))) + require(result["command_fingerprint"]["sha256"] == module.CURRENT_COMMAND_HASH and result["command_fingerprint"]["source_family_match"] == "OFFICIAL_V019_SOURCE_FAMILY_CANDIDATE" and result["command_fingerprint"]["proves_exact_binary"] is False, "v0.19 fingerprint mismatch") + + @case("07 v0.7 help matches only a source family") + def _(): + result = module.parse_transcript(greeting(help_text(v07_commands))) + require(result["command_fingerprint"]["sha256"] == module.V07_COMMAND_HASH and result["command_fingerprint"]["source_family_match"] == "OFFICIAL_V07_SOURCE_FAMILY_CANDIDATE", "v0.7 fingerprint mismatch") + + @case("08 altered help stays unresolved") + def _(): require(module.parse_transcript(greeting(help_text(["help", "unknown"])))["command_fingerprint"]["source_family_match"] == "UNRESOLVED", "unknown family promoted") + + @case("09 unknown stat path is discarded") + def _(): require(module.parse_transcript(greeting("filename: /unknown\nsize: 123\n"), {"/approved"})["file_observations"] == [], "unknown path retained") + + @case("10 approved stat path is retained") + def _(): + result = module.parse_transcript(greeting("filename: /approved\nsize: 123\nmtime: 456\n"), {"/approved"}) + require(result["file_observations"] == [{"path": "/approved", "metadata_seen": True, "size": 123, "mtime": 456, "proves_exact_binary": False}], "approved metadata mismatch") + + @case("11 sum is labeled weak and non-cryptographic") + def _(): + result = module.parse_transcript(greeting("12345 /approved\n"), {"/approved"}) + observation = result["file_observations"][0] + require(observation["weak_checksum_algorithm"] == "BSD_ROTATE_16" and observation["cryptographic_checksum"] is False, "weak sum promoted") + + @case("12 all combined metadata remains non-exact") + def _(): + text = greeting(help_text(current_commands) + "filename: /approved\nsize: 123\n12345 /approved\n") + result = module.parse_transcript(text, {"/approved"}) + require(result["exact_identity"] is False and all(not item["proves_exact_binary"] for item in result["file_observations"]), "combined metadata promoted") + + failures = [] + for name, function in cases: + try: + function() + print(f"PASS {name}") + except Exception as error: # noqa: BLE001 - test harness + failures.append(f"{name}: {error}") + print(f"FAIL {name}: {error}") + if failures: + return 1 + print(f"Phase-1.0T transcript tests passed: {len(cases)}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_phase10v_shsrv_collector_model.py b/tests/test_phase10v_shsrv_collector_model.py new file mode 100644 index 0000000..3533f99 --- /dev/null +++ b/tests/test_phase10v_shsrv_collector_model.py @@ -0,0 +1,202 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Synthetic offline tests for the Phase-1.0V collector model.""" + +from __future__ import annotations + +import argparse +import importlib.util +import json +from pathlib import Path +import sys + + +def load(path: Path): + spec = importlib.util.spec_from_file_location("phase10v_model", path) + assert spec and spec.loader + module = importlib.util.module_from_spec(spec) + sys.modules[spec.name] = module + spec.loader.exec_module(module) + return module + + +def require(value: bool, message: str) -> None: + if not value: + raise RuntimeError(message) + + +def greeting(extra: str = "") -> bytes: + return ( + "Welcome to shsrv.elf running on pid 123, compiled Jul 22 2026 at 12:34:56\n" + "Model: synthetic-model\n" + "S/N: SYNTHETIC-SERIAL-NEVER-RETAIN\n" + "S/W: 9.60\n" + "SoC temp: 40 C\n" + "CPU temp: 41 C\n" + "CPU freq: 3500 MHz\n" + extra).encode("utf-8") + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + root = args.root.resolve() + sys.path.insert(0, str(root / "tools")) + module = load(root / "tools/phase10v_shsrv_collector_model.py") + cases = [] + + def case(name): + def register(function): + cases.append((name, function)) + return function + return register + + @case("01 plain greeting is parsed offline") + def _(): + collector = module.OfflineCollector(); collector.feed(greeting()) + require(collector.finalize()["classification"] == "COMPILE_METADATA_ONLY", "greeting rejected") + + @case("02 Telnet negotiation is removed") + def _(): + collector = module.OfflineCollector(); collector.feed(bytes([255, 251, 1]) + greeting()) + require(collector.finalize()["compile_metadata"]["firmware"] == "9.60", "negotiation leaked") + + @case("03 fragmented Telnet negotiation is handled") + def _(): + collector = module.OfflineCollector(); collector.feed(bytes([255])); collector.feed(bytes([253])); collector.feed(bytes([3]) + greeting()) + require(collector.finalize()["classification"] == "COMPILE_METADATA_ONLY", "fragmentation failed") + + @case("04 Telnet subnegotiation is removed") + def _(): + control = bytes([255, 250, 24, 1, 2, 3, 255, 240]) + collector = module.OfflineCollector(); collector.feed(control + greeting()) + require(collector.finalize()["classification"] == "COMPILE_METADATA_ONLY", "subnegotiation leaked") + + @case("04b doubled IAC stays inside subnegotiation") + def _(): + control = bytes([255, 250, 24, 255, 255, 1, 255, 240]) + collector = module.OfflineCollector(); collector.feed(control + greeting()) + require(collector.finalize()["classification"] == "COMPILE_METADATA_ONLY", "doubled IAC state failed") + + @case("05 serial is absent from output") + def _(): + collector = module.OfflineCollector(); collector.feed(greeting()); result = collector.finalize() + require("SYNTHETIC-SERIAL" not in json.dumps(result), "serial retained") + + @case("06 telemetry values are absent from output") + def _(): + collector = module.OfflineCollector(); collector.feed(greeting()); result = collector.finalize() + require("3500" not in json.dumps(result), "telemetry retained") + + @case("07 exact identity always remains false") + def _(): + collector = module.OfflineCollector(); collector.feed(greeting()) + require(collector.finalize()["exact_identity"] is False, "identity promoted") + + @case("08 raw byte limit is enforced") + def _(): + collector = module.OfflineCollector() + try: collector.feed(b"x" * (module.MAX_RAW_BYTES + 1)) + except module.CollectorError: return + raise RuntimeError("oversized input accepted") + + @case("09 chunk limit is enforced") + def _(): + collector = module.OfflineCollector() + try: + for _index in range(module.MAX_CHUNKS + 1): collector.feed(b"x") + except module.CollectorError: return + raise RuntimeError("excess chunks accepted") + + @case("09b empty chunks do not consume the limit") + def _(): + collector = module.OfflineCollector() + for _index in range(module.MAX_CHUNKS + 1): collector.feed(b"") + require(collector.chunk_count == 0, "empty chunks counted") + + @case("10 incomplete Telnet sequence is rejected") + def _(): + collector = module.OfflineCollector(); collector.feed(bytes([255])) + try: collector.finalize() + except module.CollectorError: return + raise RuntimeError("incomplete control accepted") + + @case("11 invalid UTF-8 is rejected") + def _(): + collector = module.OfflineCollector(); collector.feed(bytes([0xC3, 0x28])) + try: collector.finalize() + except module.CollectorError: return + raise RuntimeError("invalid UTF-8 accepted") + + @case("12 collector finalizes only once") + def _(): + collector = module.OfflineCollector(); collector.feed(greeting()); collector.finalize() + try: collector.finalize() + except module.CollectorError: return + raise RuntimeError("second finalize accepted") + + @case("13 feed after finalization is rejected") + def _(): + collector = module.OfflineCollector(); collector.finalize() + try: collector.feed(b"later") + except module.CollectorError: return + raise RuntimeError("post-finalize feed accepted") + + @case("14 abort prevents output") + def _(): + collector = module.OfflineCollector(); collector.feed(greeting()); collector.abort() + try: collector.finalize() + except module.CollectorError: return + raise RuntimeError("aborted collector finalized") + + @case("15 approved literal-path metadata is retained") + def _(): + collector = module.OfflineCollector(); collector.feed(greeting("filename: /approved\nsize: 123\n")) + result = collector.finalize({"/approved"}) + require(result["file_observations"][0]["size"] == 123, "approved metadata lost") + + @case("16 unknown path is discarded and memory erasure unproven") + def _(): + collector = module.OfflineCollector(); collector.feed(greeting("filename: /unknown\nsize: 123\n")) + result = collector.finalize({"/approved"}) + require(result["file_observations"] == [] and result["collector_model"]["physical_memory_erasure_proven"] is False, "boundary promoted") + + @case("17 unsafe or non-normalized expected paths are rejected") + def _(): + for path in ("relative", "/safe/../escape", "/wild*card", "/line\nfeed"): + collector = module.OfflineCollector(); collector.feed(greeting()) + try: collector.finalize({path}) + except module.CollectorError: continue + raise RuntimeError(f"unsafe path accepted: {path!r}") + + @case("18 unexpected firmware metadata is rejected") + def _(): + collector = module.OfflineCollector(); collector.feed(greeting().replace(b"S/W: 9.60", b"S/W: secret")) + try: collector.finalize() + except module.CollectorError: return + raise RuntimeError("unexpected firmware accepted") + + @case("19 malformed compile metadata is rejected") + def _(): + data = greeting().replace(b"compiled Jul 22 2026", b"compiled SERIAL-IN-DATE") + collector = module.OfflineCollector(); collector.feed(data) + try: collector.finalize() + except module.CollectorError: return + raise RuntimeError("malformed compile metadata accepted") + + failures = [] + for name, function in cases: + try: + function() + print(f"PASS {name}") + except Exception as error: # noqa: BLE001 - synthetic harness + failures.append(f"{name}: {error}") + print(f"FAIL {name}: {error}") + if failures: + return 1 + print(f"Phase-1.0V collector-model tests passed: {len(cases)}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_phase10w_shsrv_client_policy.py b/tests/test_phase10w_shsrv_client_policy.py new file mode 100644 index 0000000..53738d0 --- /dev/null +++ b/tests/test_phase10w_shsrv_client_policy.py @@ -0,0 +1,269 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Host-only policy tests for the inactive Phase-1.0W architecture.""" + +from __future__ import annotations + +import argparse +from datetime import datetime, timezone +import importlib.util +from pathlib import Path +import sys + + +def load(path: Path): + spec = importlib.util.spec_from_file_location("phase10w_policy", path) + assert spec and spec.loader + module = importlib.util.module_from_spec(spec) + sys.modules[spec.name] = module + spec.loader.exec_module(module) + return module + + +def require(value: bool, message: str) -> None: + if not value: + raise RuntimeError(message) + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + root = args.root.resolve() + sys.path.insert(0, str(root / "tools")) + sys.path.insert(0, str(root / "tests")) + module = load(root / "tools/phase10w_shsrv_client_policy.py") + from phase10w_fake_transport import FakeTransport, FakeTransportError + now = datetime(2026, 7, 22, 12, 0, tzinfo=timezone.utc) + + def records(window="T2_GREETING_AND_HELP"): + path = None if window == "T2_GREETING_AND_HELP" else "/data/exact.elf" + commands = list(module.WINDOW_COMMANDS[window]) + common = { + "active": True, + "policy_sha256": "a" * 64, + "collector_sha256": module.COLLECTOR_SHA256, + "run_id": "synthetic_run_001", + "target_address": "device.invalid", + "target_port": 2323, + "window": window, + "exact_literal_path": path, + "commands": commands, + "deadline_seconds": 10, + "expires_at": "2026-07-22T12:10:00Z", + } + approval = dict(common) + approval.update({ + "attested": True, + "listener_already_running_attested": True, + "ps5_connection_authorized": True, + "device_request_authorized": True, + "result_receive_authorized": True, + "spawned_shell_effects_accepted": True, + "automatic_serial_query_accepted": True, + "automatic_telemetry_query_accepted": True, + "sanitized_output_only_accepted": True, + "physical_memory_erasure_unproven_accepted": True, + "target_build_authorized": False, + "device_transfer_authorized": False, + "device_execution_authorized": False, + "installation_authorized": False, + "autoload_authorized": False, + "device_write_authorized": False, + "automatic_retry": False, + "reconnect_authorized": False, + "resume_authorized": False, + "fallback_authorized": False, + }) + return common, approval + + cases = [] + + def case(name): + def register(function): + cases.append((name, function)) + return function + return register + + @case("01 tracked inactive shape is inert") + def _(): require(module.inactive_record_is_inert({"active": False, "policy_sha256": None, "collector_sha256": None, "run_id": None, "target_address": None, "target_port": None, "window": None, "exact_literal_path": None, "commands": [], "deadline_seconds": None, "expires_at": None}), "inactive shape rejected") + + @case("02 exact help window produces immutable plan") + def _(): + activation, approval = records(); plan = module.build_session_plan(activation, approval, now) + require(plan.commands == ("help",) and plan.target_port == 2323, "help plan mismatch") + + @case("03 exact-path window validates path") + def _(): + activation, approval = records("T3_ONE_EXACT_PATH"); plan = module.build_session_plan(activation, approval, now) + require(plan.exact_literal_path == "/data/exact.elf", "path lost") + + @case("04 inactive record is rejected") + def _(): + activation, approval = records(); activation["active"] = False + try: module.build_session_plan(activation, approval, now) + except module.PolicyError: return + raise RuntimeError("inactive activation accepted") + + @case("05 missing attestation is rejected") + def _(): + activation, approval = records(); approval["attested"] = False + try: module.build_session_plan(activation, approval, now) + except module.PolicyError: return + raise RuntimeError("missing attestation accepted") + + @case("06 record mismatch is rejected") + def _(): + activation, approval = records(); approval["run_id"] = "different_run" + try: module.build_session_plan(activation, approval, now) + except module.PolicyError: return + raise RuntimeError("mismatch accepted") + + @case("07 collector hash mismatch is rejected") + def _(): + activation, approval = records(); activation["collector_sha256"] = approval["collector_sha256"] = "0" * 64 + try: module.build_session_plan(activation, approval, now) + except module.PolicyError: return + raise RuntimeError("wrong hash accepted") + + @case("08 retry is rejected") + def _(): + activation, approval = records(); approval["automatic_retry"] = True + try: module.build_session_plan(activation, approval, now) + except module.PolicyError: return + raise RuntimeError("retry accepted") + + @case("09 execution authority is rejected") + def _(): + activation, approval = records(); approval["device_execution_authorized"] = True + try: module.build_session_plan(activation, approval, now) + except module.PolicyError: return + raise RuntimeError("execution authority accepted") + + @case("10 missing side-effect acceptance is rejected") + def _(): + activation, approval = records(); approval["automatic_serial_query_accepted"] = False + try: module.build_session_plan(activation, approval, now) + except module.PolicyError: return + raise RuntimeError("serial effect hidden") + + @case("11 command injection is rejected") + def _(): + activation, approval = records(); activation["commands"] = approval["commands"] = ["help; hbldr"] + try: module.build_session_plan(activation, approval, now) + except module.PolicyError: return + raise RuntimeError("command injection accepted") + + @case("12 target syntax injection is rejected") + def _(): + activation, approval = records(); activation["target_address"] = approval["target_address"] = "device.invalid\nother" + try: module.build_session_plan(activation, approval, now) + except module.PolicyError: return + raise RuntimeError("target injection accepted") + + @case("13 wrong port is rejected") + def _(): + activation, approval = records(); activation["target_port"] = approval["target_port"] = 9999 + try: module.build_session_plan(activation, approval, now) + except module.PolicyError: return + raise RuntimeError("wrong port accepted") + + @case("14 unsafe path is rejected") + def _(): + activation, approval = records("T3_ONE_EXACT_PATH"); activation["exact_literal_path"] = approval["exact_literal_path"] = "/data/../escape" + try: module.build_session_plan(activation, approval, now) + except (module.PolicyError, RuntimeError): return + raise RuntimeError("unsafe path accepted") + + @case("15 excessive deadline is rejected") + def _(): + activation, approval = records(); activation["deadline_seconds"] = approval["deadline_seconds"] = 11 + try: module.build_session_plan(activation, approval, now) + except module.PolicyError: return + raise RuntimeError("deadline relaxation accepted") + + @case("16 expired approval is rejected") + def _(): + activation, approval = records(); activation["expires_at"] = approval["expires_at"] = "2026-07-22T11:59:00Z" + try: module.build_session_plan(activation, approval, now) + except module.PolicyError: return + raise RuntimeError("expired approval accepted") + + @case("17 overlong approval lifetime is rejected") + def _(): + activation, approval = records(); activation["expires_at"] = approval["expires_at"] = "2026-07-22T12:16:00Z" + try: module.build_session_plan(activation, approval, now) + except module.PolicyError: return + raise RuntimeError("long approval accepted") + + @case("18 policy plan exposes no transport method") + def _(): + activation, approval = records(); plan = module.build_session_plan(activation, approval, now) + require(not any(hasattr(plan, name) for name in ("connect", "send", "recv", "open")), "transport method present") + + @case("18b unknown approval field is rejected") + def _(): + activation, approval = records(); approval["unexpected_authority"] = True + try: module.build_session_plan(activation, approval, now) + except module.PolicyError: return + raise RuntimeError("unknown approval field accepted") + + @case("18c missing listener attestation is rejected") + def _(): + activation, approval = records(); approval["listener_already_running_attested"] = False + try: module.build_session_plan(activation, approval, now) + except module.PolicyError: return + raise RuntimeError("missing listener attestation accepted") + + @case("18d policy hash mismatch is rejected") + def _(): + activation, approval = records(); approval["policy_sha256"] = "b" * 64 + try: module.build_session_plan(activation, approval, now) + except module.PolicyError: return + raise RuntimeError("policy hash mismatch accepted") + + @case("19 fake transport models exactly one session") + def _(): + activation, approval = records(); plan = module.build_session_plan(activation, approval, now) + transport = FakeTransport([b"synthetic"]); transport.open_once(plan) + transport.send_command_token("help"); require(transport.receive_chunk() == b"synthetic", "fake input lost") + require(transport.receive_chunk() is None, "fake EOF missing"); transport.close_once() + require(transport.events == ["OPEN", "COMMAND_HELP", "RECEIVE", "CLOSE"], "event sequence mismatch") + + @case("20 fake second open is rejected") + def _(): + activation, approval = records(); plan = module.build_session_plan(activation, approval, now) + transport = FakeTransport([]); transport.open_once(plan) + try: transport.open_once(plan) + except FakeTransportError: return + raise RuntimeError("second fake open accepted") + + @case("21 fake unexpected command is rejected") + def _(): + activation, approval = records(); plan = module.build_session_plan(activation, approval, now) + transport = FakeTransport([]); transport.open_once(plan) + try: transport.send_command_token("hbldr") + except FakeTransportError: return + raise RuntimeError("unexpected fake command accepted") + + @case("22 fake transport has no retry or reconnect API") + def _(): + transport = FakeTransport([]) + require(not any(hasattr(transport, name) for name in ("retry", "reconnect", "resume")), "retry API present") + + failures = [] + for name, function in cases: + try: + function() + print(f"PASS {name}") + except Exception as error: # noqa: BLE001 - synthetic harness + failures.append(f"{name}: {error}") + print(f"FAIL {name}: {error}") + if failures: + return 1 + print(f"Phase-1.0W client-policy tests passed: {len(cases)}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_phase10x_inactive_transport.py b/tests/test_phase10x_inactive_transport.py new file mode 100644 index 0000000..b434c4b --- /dev/null +++ b/tests/test_phase10x_inactive_transport.py @@ -0,0 +1,284 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Fault-injection tests for the inactive Phase-1.0X transport layer.""" + +from __future__ import annotations + +import argparse +from datetime import datetime, timezone +import importlib.util +import json +from pathlib import Path +import sys +import tempfile + + +def load(path: Path, name: str): + spec = importlib.util.spec_from_file_location(name, path) + assert spec and spec.loader + module = importlib.util.module_from_spec(spec) + sys.modules[spec.name] = module + spec.loader.exec_module(module) + return module + + +def require(value: bool, message: str) -> None: + if not value: + raise RuntimeError(message) + + +class FakeClock: + def __init__(self) -> None: + self.value = 100.0 + + def monotonic(self) -> float: + return self.value + + +class FakeAdapter: + def __init__(self, boundaries, events, clock, advance=0.0, fail=None): + self.boundaries = dict(boundaries) + self.events = events + self.clock = clock + self.advance = advance + self.fail = fail + self.open_count = 0 + self.closed = False + + def open_once(self, _plan, _remaining): + self.events.append("OPEN") + self.open_count += 1 + if self.fail == "OPEN": raise RuntimeError("SENSITIVE RAW OPEN ERROR") + + def receive_boundary(self, boundary, _remaining): + self.events.append(boundary) + self.clock.value += self.advance + if self.fail == boundary: raise RuntimeError("SENSITIVE RAW RECEIVE ERROR") + return self.boundaries.get(boundary, []) + + def send_command_token(self, command, _path, _remaining): + self.events.append(f"SEND_{command.upper()}") + if self.fail == f"SEND_{command.upper()}": + raise RuntimeError("SENSITIVE RAW SEND ERROR") + + def close_once(self): + self.events.append("CLOSE") + self.closed = True + if self.fail == "CLOSE": raise RuntimeError("SENSITIVE RAW CLOSE ERROR") + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + root = args.root.resolve() + sys.path.insert(0, str(root / "tools")) + policy = load(root / "tools/phase10w_shsrv_client_policy.py", "phase10w_policy_x") + transport = load(root / "tools/phase10x_inactive_transport.py", "phase10x_transport") + now = datetime(2026, 7, 22, 12, 0, tzinfo=timezone.utc) + + def plan(window="T2_GREETING_AND_HELP", run_id="synthetic_run_x"): + path = None if window == "T2_GREETING_AND_HELP" else "/data/exact.elf" + common = { + "active": True, "policy_sha256": "a" * 64, + "collector_sha256": policy.COLLECTOR_SHA256, "run_id": run_id, + "target_address": "device.invalid", "target_port": 2323, + "window": window, "exact_literal_path": path, + "commands": list(policy.WINDOW_COMMANDS[window]), + "deadline_seconds": 10, "expires_at": "2026-07-22T12:10:00Z", + } + approval = dict(common) + approval.update({ + "attested": True, "listener_already_running_attested": True, + "ps5_connection_authorized": True, "device_request_authorized": True, + "result_receive_authorized": True, "spawned_shell_effects_accepted": True, + "automatic_serial_query_accepted": True, + "automatic_telemetry_query_accepted": True, + "sanitized_output_only_accepted": True, + "physical_memory_erasure_unproven_accepted": True, + "target_build_authorized": False, "device_transfer_authorized": False, + "device_execution_authorized": False, "installation_authorized": False, + "autoload_authorized": False, "device_write_authorized": False, + "automatic_retry": False, "reconnect_authorized": False, + "resume_authorized": False, "fallback_authorized": False, + }) + return policy.build_session_plan(common, approval, now) + + greeting = ( + b"Welcome to shsrv.elf running on pid 1, compiled Jul 22 2026 at 12:34:56\n" + b"S/N: SENSITIVE-SERIAL\nS/W: 9.60\nCPU freq: 3500 MHz\n") + help_output = b"Builtin commands:\n help\n\n" + cases = [] + + def case(name): + def register(function): + cases.append((name, function)) + return function + return register + + def execute(directory, session_plan=None, adapter=None, clock=None): + session_plan = session_plan or plan() + clock = clock or FakeClock() + events = [] + adapter = adapter or FakeAdapter({"INITIAL_PROMPT": [greeting], "AFTER_HELP": [help_output]}, events, clock) + store = transport.ExclusiveEvidenceStore(Path(directory)) + return transport.run_injected_session(session_plan, adapter, clock, store), events + + @case("01 receipt exists before fake open") + def _(): + with tempfile.TemporaryDirectory() as directory: + session_plan = plan(); clock = FakeClock(); events = [] + class InspectAdapter(FakeAdapter): + def open_once(self, p, remaining): + require((Path(directory) / f"{p.run_id}.consumed.json").exists(), "receipt missing before open") + super().open_once(p, remaining) + adapter = InspectAdapter({"INITIAL_PROMPT": [greeting], "AFTER_HELP": [help_output]}, events, clock) + execute(directory, session_plan, adapter, clock) + + @case("02 help flow is one shot") + def _(): + with tempfile.TemporaryDirectory() as directory: + outcome, events = execute(directory) + require(events == ["OPEN", "INITIAL_PROMPT", "SEND_HELP", "AFTER_HELP", "CLOSE"] and outcome.exact_identity is False, "help sequence mismatch") + + @case("03 exact-path flow preserves command order") + def _(): + with tempfile.TemporaryDirectory() as directory: + p = plan("T3_ONE_EXACT_PATH"); clock = FakeClock(); events = [] + adapter = FakeAdapter({"INITIAL_PROMPT": [greeting], "AFTER_STAT": [b"filename: /data/exact.elf\nsize: 123\n"], "AFTER_SUM": [b"12345 /data/exact.elf\n"]}, events, clock) + outcome, _ = execute(directory, p, adapter, clock) + require(events == ["OPEN", "INITIAL_PROMPT", "SEND_STAT", "AFTER_STAT", "SEND_SUM", "AFTER_SUM", "CLOSE"] and outcome.classification == "WEAK_FILE_CORRELATION_ONLY", "path sequence mismatch") + + @case("04 sanitized output excludes serial and telemetry") + def _(): + with tempfile.TemporaryDirectory() as directory: + outcome, _ = execute(directory); data = outcome.output.path.read_text(encoding="ascii") + require("SENSITIVE-SERIAL" not in data and "3500" not in data, "sensitive output retained") + + @case("05 receipt excludes target") + def _(): + with tempfile.TemporaryDirectory() as directory: + outcome, _ = execute(directory); data = outcome.receipt.path.read_text(encoding="ascii") + require("device.invalid" not in data and '"target_retained":false' in data, "target retained") + + @case("06 second run is blocked before fake open") + def _(): + with tempfile.TemporaryDirectory() as directory: + execute(directory); clock = FakeClock(); events = [] + adapter = FakeAdapter({}, events, clock) + try: execute(directory, plan(), adapter, clock) + except transport.EvidenceFailure: require(events == [], "adapter opened after consumed receipt"); return + raise RuntimeError("second run accepted") + + @case("07 existing output cannot be overwritten") + def _(): + with tempfile.TemporaryDirectory() as directory: + p = plan(); store = transport.ExclusiveEvidenceStore(Path(directory)); receipt = store.create_consumed_receipt(p, 100.0) + store.create_sanitized_output(p, receipt, {"classification": "x", "exact_identity": False}) + try: store.create_sanitized_output(p, receipt, {"classification": "y"}) + except transport.EvidenceFailure: return + raise RuntimeError("output overwrite accepted") + + @case("08 deadline expiry fails and closes") + def _(): + with tempfile.TemporaryDirectory() as directory: + p = plan(); clock = FakeClock(); events = [] + adapter = FakeAdapter({"INITIAL_PROMPT": [greeting]}, events, clock, advance=11.0) + try: execute(directory, p, adapter, clock) + except transport.SessionFailure: require(adapter.closed, "adapter not closed"); return + raise RuntimeError("expired session accepted") + + @case("09 adapter open failure is normalized and closed") + def _(): + with tempfile.TemporaryDirectory() as directory: + p = plan(); clock = FakeClock(); events = [] + adapter = FakeAdapter({}, events, clock, fail="OPEN") + try: execute(directory, p, adapter, clock) + except transport.SessionFailure as error: + require( + "SENSITIVE" not in str(error) + and error.__cause__ is None + and error.__context__ is None + and adapter.closed, + "adapter detail/cause leaked or close skipped") + return + raise RuntimeError("adapter failure accepted") + + @case("10 adapter closes after send failure") + def _(): + with tempfile.TemporaryDirectory() as directory: + p = plan(); clock = FakeClock(); events = [] + adapter = FakeAdapter({"INITIAL_PROMPT": [greeting]}, events, clock, fail="SEND_HELP") + try: execute(directory, p, adapter, clock) + except transport.SessionFailure: require(adapter.closed and adapter.open_count == 1, "cleanup/retry mismatch"); return + raise RuntimeError("send failure accepted") + + @case("11 excessive boundary chunks are rejected") + def _(): + with tempfile.TemporaryDirectory() as directory: + p = plan(); clock = FakeClock(); events = [] + adapter = FakeAdapter({"INITIAL_PROMPT": [b"x"] * 65}, events, clock) + try: execute(directory, p, adapter, clock) + except transport.SessionFailure: return + raise RuntimeError("excess chunks accepted") + + @case("12 malformed transcript creates no output") + def _(): + with tempfile.TemporaryDirectory() as directory: + p = plan(); clock = FakeClock(); events = [] + adapter = FakeAdapter({"INITIAL_PROMPT": [b"\xff"]}, events, clock) + try: execute(directory, p, adapter, clock) + except transport.SessionFailure: + require(not (Path(directory) / f"{p.run_id}.sanitized.json").exists(), "invalid output created"); return + raise RuntimeError("malformed transcript accepted") + + @case("13 output reopens as valid JSON") + def _(): + with tempfile.TemporaryDirectory() as directory: + outcome, _ = execute(directory) + receipt = json.loads(outcome.receipt.path.read_text(encoding="ascii")) + value = json.loads(outcome.output.path.read_text(encoding="ascii")) + require( + receipt["status"] == "CONSUMED_BEFORE_ADAPTER_OPEN" + and value["status"] == "SANITIZED_OUTPUT_COMPLETE" + and value["receipt_sha256"] == outcome.receipt.sha256, + "output binding mismatch") + + @case("14 output byte count and hash match reopen") + def _(): + with tempfile.TemporaryDirectory() as directory: + outcome, _ = execute(directory); payload = outcome.output.path.read_bytes() + import hashlib + require(len(payload) == outcome.output.size and hashlib.sha256(payload).hexdigest() == outcome.output.sha256, "reopen identity mismatch") + + @case("15 evidence store exposes no cleanup") + def _(): + require(not any(hasattr(transport.ExclusiveEvidenceStore, name) for name in ("delete", "cleanup", "overwrite")), "cleanup API present") + + @case("16 transport module exposes no live CLI") + def _(): require(not hasattr(transport, "main"), "live CLI present") + + @case("17 transport module imports no socket") + def _(): + source = (root / "tools/phase10x_inactive_transport.py").read_text(encoding="utf-8") + require("import socket" not in source and "from socket" not in source, "socket import present") + + @case("18 host-only result is not exact identity") + def _(): + with tempfile.TemporaryDirectory() as directory: + outcome, _ = execute(directory); require(outcome.exact_identity is False, "identity promoted") + + failures = [] + for name, function in cases: + try: + function(); print(f"PASS {name}") + except Exception as error: # noqa: BLE001 - fault harness + failures.append(f"{name}: {error}"); print(f"FAIL {name}: {error}") + if failures: + return 1 + print(f"Phase-1.0X inactive-transport tests passed: {len(cases)}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_phase10y_shsrv_framing_model.py b/tests/test_phase10y_shsrv_framing_model.py new file mode 100644 index 0000000..59ba59a --- /dev/null +++ b/tests/test_phase10y_shsrv_framing_model.py @@ -0,0 +1,157 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Host-only tests for the Phase-1.0Y shsrv framing model.""" + +from __future__ import annotations + +import argparse +import importlib.util +from pathlib import Path +import sys + + +def load(path: Path): + spec = importlib.util.spec_from_file_location("phase10y_framing", path) + assert spec and spec.loader + module = importlib.util.module_from_spec(spec) + sys.modules[spec.name] = module + spec.loader.exec_module(module) + return module + + +def require(value: bool, message: str) -> None: + if not value: + raise RuntimeError(message) + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + model = load(args.root.resolve() / "tools/phase10y_shsrv_framing_model.py") + cases = [] + + def case(name): + def register(function): + cases.append((name, function)) + return function + return register + + def decode(family, chunks): + decoder = model.ClientWireDecoder(family) + for chunk in chunks: + decoder.feed(chunk) + return decoder.finalize() + + @case("01 legacy family passes raw bytes") + def _(): require(decode(model.LEGACY_RAW, [b"help\r\n"]).application == b"help\r\n", "legacy transformed") + + @case("02 legacy family passes Telnet controls to shell") + def _(): + raw = bytes((model.IAC, model.WILL, 1)) + value = decode(model.LEGACY_RAW, [raw]) + require(value.application == raw and not value.negotiation_replies, "legacy negotiated") + + @case("03 current family maps CRLF to LF") + def _(): require(decode(model.LIBTELNET_NVT, [b"help\r\n"]).application == b"help\n", "CRLF mismatch") + + @case("04 current family maps CRNUL to CR") + def _(): require(decode(model.LIBTELNET_NVT, [b"x\r\x00"]).application == b"x\r", "CRNUL mismatch") + + @case("05 current family preserves CR followed by data") + def _(): require(decode(model.LIBTELNET_NVT, [b"x\ry"]).application == b"x\ry", "CR data mismatch") + + @case("06 doubled IAC becomes one application byte") + def _(): + value = decode(model.LIBTELNET_NVT, [bytes((model.IAC, model.IAC))]) + require(value.application == bytes((model.IAC,)), "IAC escape mismatch") + + @case("07 WILL receives DONT") + def _(): + value = decode(model.LIBTELNET_NVT, [bytes((model.IAC, model.WILL, 1))]) + require(value.negotiation_replies == (bytes((model.IAC, model.DONT, 1)),), "WILL reply mismatch") + + @case("08 DO receives WONT") + def _(): + value = decode(model.LIBTELNET_NVT, [bytes((model.IAC, model.DO, 3))]) + require(value.negotiation_replies == (bytes((model.IAC, model.WONT, 3)),), "DO reply mismatch") + + @case("09 initial WONT produces no reply") + def _(): require(not decode(model.LIBTELNET_NVT, [bytes((model.IAC, model.WONT, 1))]).negotiation_replies, "WONT replied") + + @case("10 initial DONT produces no reply") + def _(): require(not decode(model.LIBTELNET_NVT, [bytes((model.IAC, model.DONT, 1))]).negotiation_replies, "DONT replied") + + @case("11 fragmented negotiation is modeled") + def _(): + value = decode(model.LIBTELNET_NVT, [bytes((model.IAC,)), bytes((model.WILL,)), b"\x01"]) + require(value.negotiation_replies == (bytes((model.IAC, model.DONT, 1)),), "fragment reply mismatch") + + @case("12 subnegotiation is removed") + def _(): + frame = bytes((model.IAC, model.SB, 31, 0, 80, model.IAC, model.SE)) + require(decode(model.LIBTELNET_NVT, [frame[:2], frame[2:]]).application == b"", "subnegotiation leaked") + + @case("13 incomplete control fails closed") + def _(): + decoder = model.ClientWireDecoder(model.LIBTELNET_NVT); decoder.feed(bytes((model.IAC,))) + try: decoder.finalize() + except model.FramingError: return + raise RuntimeError("incomplete control accepted") + + @case("14 outgoing newline becomes CRLF only in current family") + def _(): + require(model.encode_server_text(model.LIBTELNET_NVT, b"x\n") == b"x\r\n" and model.encode_server_text(model.LEGACY_RAW, b"x\n") == b"x\n", "newline encoding mismatch") + + @case("15 outgoing CR becomes CRNUL") + def _(): require(model.encode_server_text(model.LIBTELNET_NVT, b"x\r") == b"x\r\x00", "CR encoding mismatch") + + @case("16 outgoing IAC is doubled") + def _(): require(model.encode_server_text(model.LIBTELNET_NVT, bytes((model.IAC,))) == bytes((model.IAC, model.IAC)), "outgoing IAC mismatch") + + @case("17 neither family proactively negotiates") + def _(): require(all(model.initial_server_bytes(family) == b"" for family in model.SOURCE_FAMILIES), "proactive bytes invented") + + @case("18 source-shaped prompt remains candidate only") + def _(): + value = model.assess_prompt_candidates(b"greeting\r\n/$ ") + require(value.classification == "SOURCE_SHAPE_CANDIDATE_ONLY" and value.exact_completion_proven is False, "prompt promoted") + + @case("19 embedded prompt shape is ambiguous") + def _(): + value = model.assess_prompt_candidates(b"/tmp/$ embedded$ ") + require(value.classification == "AMBIGUOUS_PROMPT_CANDIDATES" and len(value.candidate_offsets) == 2, "ambiguity missed") + + @case("20 nonterminal prompt shape is incomplete") + def _(): + value = model.assess_prompt_candidates(b"/$ output") + require(value.classification == "NO_TERMINAL_PROMPT_CANDIDATE", "nonterminal candidate accepted") + + @case("21 model byte bound fails closed") + def _(): + decoder = model.ClientWireDecoder(model.LEGACY_RAW) + try: decoder.feed(b"x" * (model.MAX_MODEL_BYTES + 1)) + except model.FramingError: return + raise RuntimeError("oversize wire accepted") + + @case("22 IAC command is recorded, not application data") + def _(): + value = decode(model.LIBTELNET_NVT, [bytes((model.IAC, 244))]) + require(value.application == b"" and value.iac_commands == (244,), "IAC command mismatch") + + failures = [] + for name, function in cases: + try: + function() + print(f"PASS {name}") + except Exception as error: # noqa: BLE001 - test harness + failures.append(f"{name}: {error}") + print(f"FAIL {name}: {error}") + if failures: + return 1 + print(f"Phase-1.0Y framing-model tests passed: {len(cases)}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_phase10z_passive_batch_contract.py b/tests/test_phase10z_passive_batch_contract.py new file mode 100644 index 0000000..1a0e11d --- /dev/null +++ b/tests/test_phase10z_passive_batch_contract.py @@ -0,0 +1,206 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Synthetic host-only tests for the Phase-1.0Z passive batch contract.""" + +from __future__ import annotations + +import argparse +from dataclasses import asdict, replace +from datetime import datetime, timezone +import importlib.util +from pathlib import Path +import sys + + +CURRENT_COMMANDS = "authid browse cat cd chgrp chmod chown chroot cmp cp df echo env exec exit export file find grep hbdbg hbldr hexdump http2_get id kill launch ln ls mkdir mknod mount mv notify pkg_install procstat ps pwd reptyr rm rmdir sfocreate sfoinfo sleep stat sum suspend sync sysctl touch umount".split() + + +def load(path: Path): + spec = importlib.util.spec_from_file_location("phase10z_contract", path) + assert spec and spec.loader + module = importlib.util.module_from_spec(spec) + sys.modules[spec.name] = module + spec.loader.exec_module(module) + return module + + +def require(value: bool, message: str) -> None: + if not value: + raise RuntimeError(message) + + +def greeting(extra: str = "", eol: str = "\n") -> bytes: + text = ( + "Welcome to shsrv.elf running on pid 123, compiled Jul 22 2026 at 12:34:56\n" + "Model: synthetic-model\nS/N: SYNTHETIC-SERIAL-NEVER-RETAIN\n" + "S/W: 9.60\nSoC temp: 40 C\nCPU temp: 41 C\n" + "CPU freq: 3500 MHz\n" + extra) + return text.replace("\n", eol).encode("ascii") + + +def help_output() -> str: + return "Builtin commands:\n" + "".join( + f" {command} - synthetic\n" for command in CURRENT_COMMANDS) + "\n" + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + root = args.root.resolve() + sys.path.insert(0, str(root / "tools")) + module = load(root / "tools/phase10z_passive_batch_contract.py") + from phase10w_shsrv_client_policy import SessionPlan + + def plan(window="T2_GREETING_AND_HELP", path=None): + commands = ("help",) if window == "T2_GREETING_AND_HELP" else ("stat", "sum") + return SessionPlan("synthetic_run", "not-retained.invalid", 2323, + window, path, commands, 10, + datetime(2030, 1, 1, tzinfo=timezone.utc)) + + def expect_failure(function): + try: + function() + except module.PassiveContractError: + return + raise RuntimeError("invalid input was accepted") + + cases = [] + + def case(name): + def register(function): + cases.append((name, function)); return function + return register + + @case("01 help batch is exact plain LF") + def _(): require(module.build_passive_batch(plan()).payload == b"help\n", "help bytes differ") + + @case("02 stat and sum form one ordered batch") + def _(): require(module.build_passive_batch(plan("T3_ONE_EXACT_PATH", "/data/a.elf")).payload == b"stat /data/a.elf\nsum /data/a.elf\n", "path batch differs") + + @case("03 maximum safe path reaches exact batch bound") + def _(): + batch = module.build_passive_batch(plan("T3_ONE_EXACT_PATH", "/" + "a" * 511)) + require(len(batch.payload) == module.MAX_BATCH_BYTES == 1035, "batch bound differs") + + @case("04 batch contains no CR NUL IAC or shell separators") + def _(): + payload = module.build_passive_batch(plan("T3_ONE_EXACT_PATH", "/data/a.elf")).payload + require(not any(value in payload for value in (0, 13, 255, ord(";"), ord("|"), ord("&"))), "forbidden byte present") + + @case("05 target is not retained") + def _(): require("target" not in asdict(module.build_passive_batch(plan())) and "not-retained" not in repr(module.build_passive_batch(plan())), "target retained") + + @case("06 wrong command sequence is rejected") + def _(): expect_failure(lambda: module.build_passive_batch(replace(plan(), commands=("stat",)))) + + @case("07 help path is rejected") + def _(): expect_failure(lambda: module.build_passive_batch(replace(plan(), exact_literal_path="/x"))) + + @case("08 missing exact path is rejected") + def _(): expect_failure(lambda: module.build_passive_batch(plan("T3_ONE_EXACT_PATH"))) + + @case("09 path injection and non ASCII are rejected") + def _(): + for path in ("/x;id", "/x y", "/x\nhelp", "/café"): + expect_failure(lambda path=path: module.build_passive_batch(plan("T3_ONE_EXACT_PATH", path))) + + @case("10 incoming IAC fails closed") + def _(): + value = module.PassiveResultAccumulator(module.build_passive_batch(plan())) + expect_failure(lambda: value.feed_supplied_chunk(b"x\xffy")) + + @case("10b forged direct batch is rejected") + def _(): + forged = module.PassiveBatch("T3_ONE_EXACT_PATH", b"help\n", (), 2, 10) + expect_failure(lambda: module.PassiveResultAccumulator(forged)) + + @case("10c resumed direct batch is rejected") + def _(): + batch = module.build_passive_batch(plan()) + expect_failure(lambda: module.PassiveResultAccumulator(replace(batch, resume_allowed=True))) + + @case("11 legacy LF help transcript seals at deadline") + def _(): + value = module.PassiveResultAccumulator(module.build_passive_batch(plan())) + value.feed_supplied_chunk(greeting(help_output())) + require(value.seal_at_hard_deadline(True)["classification"] == "SOURCE_FAMILY_FINGERPRINT_ONLY", "legacy transcript failed") + + @case("12 current CRLF help transcript seals at deadline") + def _(): + value = module.PassiveResultAccumulator(module.build_passive_batch(plan())) + value.feed_supplied_chunk(greeting(help_output(), "\r\n")) + require(value.seal_at_hard_deadline(True)["passive_batch_contract"]["source_family_selected"] is False, "current transcript failed") + + @case("13 exact path transcript requires stat and sum") + def _(): + path = "/data/a.elf"; value = module.PassiveResultAccumulator(module.build_passive_batch(plan("T3_ONE_EXACT_PATH", path))) + value.feed_supplied_chunk(greeting(f"filename: {path}\nsize: 123\nmtime: 456\n12345 {path}\n")) + result = value.seal_at_hard_deadline(True) + require(result["file_observations"][0]["size"] == 123 and result["exact_identity"] is False, "path result promoted or lost") + + @case("14 partial help fails closed") + def _(): + value = module.PassiveResultAccumulator(module.build_passive_batch(plan())); value.feed_supplied_chunk(greeting("Builtin commands:\n help - partial\n\n")) + expect_failure(lambda: value.seal_at_hard_deadline(True)) + + @case("15 stat without sum fails closed") + def _(): + path = "/data/a.elf"; value = module.PassiveResultAccumulator(module.build_passive_batch(plan("T3_ONE_EXACT_PATH", path))) + value.feed_supplied_chunk(greeting(f"filename: {path}\nsize: 123\n")); expect_failure(lambda: value.seal_at_hard_deadline(True)) + + @case("16 sum without stat fails closed") + def _(): + path = "/data/a.elf"; value = module.PassiveResultAccumulator(module.build_passive_batch(plan("T3_ONE_EXACT_PATH", path))) + value.feed_supplied_chunk(greeting(f"12345 {path}\n")); expect_failure(lambda: value.seal_at_hard_deadline(True)) + + @case("16b extra fields cannot mask missing size") + def _(): + path = "/data/a.elf"; value = module.PassiveResultAccumulator(module.build_passive_batch(plan("T3_ONE_EXACT_PATH", path))) + observation = {"path": path, "metadata_seen": True, "weak_checksum": "12345", "weak_checksum_algorithm": "BSD_ROTATE_16", "cryptographic_checksum": False, "proves_exact_binary": False, "extra": 1} + expect_failure(lambda: value._validate_complete_result({"classification": "WEAK_FILE_CORRELATION_ONLY", "file_observations": [observation]})) + + @case("17 prompt is not an early completion event") + def _(): + value = module.PassiveResultAccumulator(module.build_passive_batch(plan())); value.feed_supplied_chunk(b"/$ ") + require(value.state == "RECEIVING" and not hasattr(value, "seal_at_prompt"), "prompt completion exists") + + @case("18 absent deadline event fails closed") + def _(): + value = module.PassiveResultAccumulator(module.build_passive_batch(plan())); value.feed_supplied_chunk(greeting(help_output())) + expect_failure(lambda: value.seal_at_hard_deadline(False)) + + @case("19 seal is one shot") + def _(): + value = module.PassiveResultAccumulator(module.build_passive_batch(plan())); value.feed_supplied_chunk(greeting(help_output())); value.seal_at_hard_deadline(True) + expect_failure(lambda: value.seal_at_hard_deadline(True)) + + @case("20 feed after seal fails") + def _(): + value = module.PassiveResultAccumulator(module.build_passive_batch(plan())); value.feed_supplied_chunk(greeting(help_output())); value.seal_at_hard_deadline(True) + expect_failure(lambda: value.feed_supplied_chunk(b"later")) + + @case("21 abort cannot produce a result") + def _(): + value = module.PassiveResultAccumulator(module.build_passive_batch(plan())); value.abort() + expect_failure(lambda: value.seal_at_hard_deadline(True)) + + @case("22 no EOF prompt transport CLI or exact proof API exists") + def _(): + names = set(dir(module.PassiveResultAccumulator)) | set(dir(module)) + require(not ({"seal_at_eof", "seal_at_prompt", "connect", "main"} & names), "forbidden API exists") + + failures = [] + for name, function in cases: + try: + function(); print(f"PASS {name}") + except Exception as error: # noqa: BLE001 - synthetic harness + failures.append(f"{name}: {error}"); print(f"FAIL {name}: {error}") + if failures: + return 1 + print(f"Phase-1.0Z passive-batch tests passed: {len(cases)}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_probe.c b/tests/test_probe.c new file mode 100644 index 0000000..1042e0f --- /dev/null +++ b/tests/test_probe.c @@ -0,0 +1,121 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +#include "probe.h" + +#include +#include + +typedef struct fake_probe { + size_t resolve_calls; + size_t close_calls; + size_t log_calls; + int fail_open; + int fail_lookup; + int fail_close; + int saw_address_text; +} fake_probe; + +static int failures; + +#define CHECK(expression) \ + do { \ + if (!(expression)) { \ + (void)fprintf(stderr, "%s:%d: check failed: %s\n", __FILE__, \ + __LINE__, #expression); \ + failures += 1; \ + } \ + } while (0) + +static int fake_open(void *userdata, const char *module_name) { + fake_probe *probe = (fake_probe *)userdata; + CHECK(strcmp(module_name, "libSceGnmDriver.sprx") == 0); + return probe->fail_open != 0 ? -1 : 0; +} + +static int fake_resolve(void *userdata, const char *symbol_name, int *present) { + fake_probe *probe = (fake_probe *)userdata; + + probe->resolve_calls += 1u; + if (probe->fail_lookup != 0) { + return -1; + } + *present = strcmp(symbol_name, "sceGnmSubmitCommandBuffers") != 0 ? 1 : 0; + return 0; +} + +static int fake_close(void *userdata) { + fake_probe *probe = (fake_probe *)userdata; + probe->close_calls += 1u; + return probe->fail_close != 0 ? -1 : 0; +} + +static void fake_log(void *userdata, const char *line) { + fake_probe *probe = (fake_probe *)userdata; + probe->log_calls += 1u; + if (strstr(line, "0x") != NULL) { + probe->saw_address_text = 1; + } +} + +static chimera_gfx_probe_ops make_ops(fake_probe *probe) { + chimera_gfx_probe_ops ops; + ops.userdata = probe; + ops.open_module = fake_open; + ops.resolve_symbol = fake_resolve; + ops.close_module = fake_close; + ops.log_line = fake_log; + return ops; +} + +static void test_partial_success(void) { + fake_probe probe = {0}; + chimera_gfx_probe_ops ops = make_ops(&probe); + chimera_gfx_probe_report report; + + CHECK(chimera_gfx_ps5_probe_symbols(&ops, &report) == + CHIMERA_GFX_STATUS_OK); + CHECK(report.symbol_count == 21u); + CHECK(report.resolved_count == 20u); + CHECK(report.module_opened == 0); + CHECK(probe.resolve_calls == report.symbol_count); + CHECK(probe.close_calls == 1u); + CHECK(probe.log_calls == report.symbol_count + 3u); + CHECK(probe.saw_address_text == 0); +} + +static void test_fail_closed_paths(void) { + fake_probe probe = {0}; + chimera_gfx_probe_ops ops = make_ops(&probe); + chimera_gfx_probe_report report; + + probe.fail_open = 1; + CHECK(chimera_gfx_ps5_probe_symbols(&ops, &report) == + CHIMERA_GFX_STATUS_BACKEND_UNAVAILABLE); + CHECK(probe.resolve_calls == 0u); + CHECK(probe.close_calls == 0u); + + probe = (fake_probe){0}; + probe.fail_lookup = 1; + ops = make_ops(&probe); + CHECK(chimera_gfx_ps5_probe_symbols(&ops, &report) == + CHIMERA_GFX_STATUS_INTERNAL_ERROR); + CHECK(probe.resolve_calls == 1u); + CHECK(probe.close_calls == 1u); + + probe = (fake_probe){0}; + probe.fail_close = 1; + ops = make_ops(&probe); + CHECK(chimera_gfx_ps5_probe_symbols(&ops, &report) == + CHIMERA_GFX_STATUS_INTERNAL_ERROR); + CHECK(report.module_opened == 1); + CHECK(probe.resolve_calls == report.symbol_count); + CHECK(probe.close_calls == 1u); + + CHECK(chimera_gfx_ps5_probe_symbols(NULL, &report) == + CHIMERA_GFX_STATUS_INVALID_ARGUMENT); +} + +int main(void) { + test_partial_success(); + test_fail_closed_paths(); + return failures == 0 ? 0 : 1; +} diff --git a/tests/test_retroarch_phase10a.py b/tests/test_retroarch_phase10a.py new file mode 100644 index 0000000..1b57b67 --- /dev/null +++ b/tests/test_retroarch_phase10a.py @@ -0,0 +1,205 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Twenty-five host-only guardrails for Phase 1.0A.""" + +from __future__ import annotations + +import argparse +import importlib.util +from pathlib import Path +import sys +from types import ModuleType +from typing import Callable + + +def load_module(name: str, path: Path) -> ModuleType: + spec = importlib.util.spec_from_file_location(name, path) + if spec is None or spec.loader is None: + raise RuntimeError(f"could not load {path}") + module = importlib.util.module_from_spec(spec) + sys.modules[name] = module + spec.loader.exec_module(module) + return module + + +def require(condition: bool, message: str) -> None: + if not condition: + raise RuntimeError(message) + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + root = args.root.resolve() + validator = load_module( + "phase10a_validator", root / "tools/validate_retroarch_phase10a.py" + ) + cases: list[tuple[str, Callable[[], None]]] = [] + + def case(name: str) -> Callable[[Callable[[], None]], Callable[[], None]]: + def register(function: Callable[[], None]) -> Callable[[], None]: + cases.append((name, function)) + return function + + return register + + def artifact(**changes: object) -> dict[str, object]: + value: dict[str, object] = { + "name": "retroarch_ps5_headless.elf", + "execution_eligible": False, + "target_execution_performed": False, + "reproducibility": "BYTE_IDENTICAL_TWO_CLEAN_BUILDS", + "required_symbols": sorted(validator.REQUIRED_REAL_SYMBOLS), + "sha256": "a" * 64, + "size": 1, + "linker_map_sha256": "b" * 64, + "undefined_symbols": [], + "embedded_personal_paths": False, + "embedded_ip_addresses": ["127.0.0.1"], + "embedded_device_ip_addresses": False, + "forbidden_markers_found": [], + } + value.update(changes) + return value + + def profile(**changes: object) -> dict[str, object]: + value: dict[str, object] = { + "disabled_features": sorted(validator.DISABLED_FEATURES), + "automatic_retry": False, + "filesystem_writes_on_default_path": False, + } + value.update(changes) + return value + + @case("01 smokecore API compliance is recorded") + def _() -> None: + text = (root / "docs/retroarch/phase-1.0a-driver-status.md").read_text() + require("libretro" in text.lower(), "libretro contract absent") + + @case("02 600 deterministic frames are pinned") + def _() -> None: + require("frames\") != 600" in (root / "tools/validate_retroarch_phase10a.py").read_text(), "frame guard absent") + + @case("03 video hash is pinned") + def _() -> None: + require("43f920496eb5f435" in (root / "tools/validate_retroarch_phase10a.py").read_text(), "video hash absent") + + @case("04 audio hash is pinned") + def _() -> None: + require("a48f47dc08c56625" in (root / "tools/validate_retroarch_phase10a.py").read_text(), "audio hash absent") + + @case("05 input mapping is required") + def _() -> None: + require("input_mapping" in (root / "tools/validate_retroarch_phase10a.py").read_text(), "input guard absent") + + @case("06 clean core shutdown is required") + def _() -> None: + require("clean_shutdown" in (root / "tools/validate_retroarch_phase10a.py").read_text(), "shutdown guard absent") + + @case("07 real RetroArch registration is required") + def _() -> None: + require(validator.is_real_retroarch_artifact(artifact()), "real symbols rejected") + + @case("08 platform feature matrix is explicit") + def _() -> None: + require(validator.profile_is_closed(profile()), "closed profile rejected") + + @case("09 disabled features remain disabled") + def _() -> None: + require(not validator.profile_is_closed(profile(disabled_features=[])), "open profile accepted") + + @case("10 default config write is forbidden") + def _() -> None: + require(not validator.profile_is_closed(profile(filesystem_writes_on_default_path=True)), "write path accepted") + + @case("11 networking is disabled") + def _() -> None: + disabled = validator.DISABLED_FEATURES - {"networking"} + require(not validator.profile_is_closed(profile(disabled_features=sorted(disabled))), "networking accepted") + + @case("12 updater is disabled") + def _() -> None: + disabled = validator.DISABLED_FEATURES - {"online_updater"} + require(not validator.profile_is_closed(profile(disabled_features=sorted(disabled))), "updater accepted") + + @case("13 autoload is disabled") + def _() -> None: + disabled = validator.DISABLED_FEATURES - {"autoload"} + require(not validator.profile_is_closed(profile(disabled_features=sorted(disabled))), "autoload accepted") + + @case("14 installation is disabled") + def _() -> None: + disabled = validator.DISABLED_FEATURES - {"installation"} + require(not validator.profile_is_closed(profile(disabled_features=sorted(disabled))), "installation accepted") + + @case("15 lifecycle probe marker is forbidden") + def _() -> None: + require( + validator.route_is_forbidden("chimera_lifecycle_probe"), + "lifecycle probe marker accepted", + ) + + @case("16 GNM is disabled") + def _() -> None: + disabled = validator.DISABLED_FEATURES - {"gnm"} + require(not validator.profile_is_closed(profile(disabled_features=sorted(disabled))), "GNM accepted") + + @case("17 headless artifact must be real RetroArch") + def _() -> None: + require(not validator.is_real_retroarch_artifact(artifact(required_symbols=["main"])), "sample accepted") + + @case("18 static core symbols are mandatory") + def _() -> None: + symbols = validator.REQUIRED_REAL_SYMBOLS - {"retro_run"} + require(not validator.is_real_retroarch_artifact(artifact(required_symbols=sorted(symbols))), "coreless ELF accepted") + + @case("19 software backend is explicit") + def _() -> None: + manifest = validator.load_json(root / "manifests/retroarch/phase-1.0a-build.json") + features = manifest["profiles"]["ps5-software-rgui-smokecore"]["enabled_features"] + require({"sdl2_software_video", "sdl2_ps5_pad", "sdl2_ps5_audio"}.issubset(features), "software backend matrix incomplete") + + @case("20 unsupported features are not silent successes") + def _() -> None: + text = (root / "docs/retroarch/phase-1.0a-driver-status.md").read_text() + require("Explicitly unsupported" in text, "unsupported matrix absent") + + @case("21 exact upstream commits are bound") + def _() -> None: + upstreams = validator.load_json(root / "manifests/retroarch/upstreams.json") + require(upstreams["sources"]["retroarch"]["commit"] == validator.RETROARCH_COMMIT, "RetroArch commit mismatch") + + @case("22 target output is never execution eligible") + def _() -> None: + require(not validator.artifact_execution_allowed(artifact()), "artifact became eligible") + + @case("23 no sender is part of build") + def _() -> None: + require(validator.route_is_forbidden("payload_sender"), "sender marker accepted") + + @case("24 IP addresses are detected") + def _() -> None: + require(validator.has_ip_address(["connect 192.0.2.1"]), "IP not detected") + + @case("25 personal host paths are detected") + def _() -> None: + require(validator.has_personal_path([r"C:\Users\Example\port"]), "host path not detected") + + failures: list[str] = [] + for name, function in cases: + try: + function() + except Exception as error: # noqa: BLE001 - isolated test reporting + failures.append(f"{name}: {error}") + if len(cases) != 25: + failures.append(f"expected 25 cases, found {len(cases)}") + if failures: + print("\n".join(failures), file=sys.stderr) + return 1 + print("25 Phase 1.0A guardrails passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_retroarch_phase10aa.py b/tests/test_retroarch_phase10aa.py new file mode 100644 index 0000000..c576c1b --- /dev/null +++ b/tests/test_retroarch_phase10aa.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Mutation guardrails for Phase-1.0AA offline fake integration.""" + +from __future__ import annotations + +import argparse +from copy import deepcopy +import importlib.util +from pathlib import Path +import sys + + +def load(path: Path): + spec = importlib.util.spec_from_file_location("phase10aa_validator", path) + assert spec and spec.loader + module = importlib.util.module_from_spec(spec); sys.modules[spec.name] = module + spec.loader.exec_module(module); return module + + +def main() -> int: + parser = argparse.ArgumentParser(); parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args(); root = args.root.resolve() + validator = load(root / "tools/validate_retroarch_phase10aa.py") + original = validator.load_json(root / "manifests/retroarch/phase-1.0aa-offline-fake-adapter.json") + cases = [] + def mutation(name, function): cases.append((name, function)) + + mutation("01 activation rejected", lambda r: r["activation"].update(active=True)) + mutation("02 target rejected", lambda r: r["activation"].update(target_address="device.invalid")) + mutation("03 connection authority rejected", lambda r: r["authorizations"].update(ps5_connection_authorized=True)) + mutation("04 request authority rejected", lambda r: r["authorizations"].update(device_request_authorized=True)) + mutation("05 result authority rejected", lambda r: r["authorizations"].update(result_receive_authorized=True)) + mutation("06 retry rejected", lambda r: r["authorizations"].update(automatic_retry=True)) + mutation("07 reconnect rejected", lambda r: r["authorizations"].update(reconnect_authorized=True)) + mutation("08 resume rejected", lambda r: r["authorizations"].update(resume_authorized=True)) + mutation("09 adapter subclass rejected", lambda r: r["fake_boundary"].update(adapter_subclasses_allowed=True)) + mutation("10 live protocol rejected", lambda r: r["fake_boundary"].update(live_adapter_protocol_present=True)) + mutation("11 network import rejected", lambda r: r["fake_boundary"].update(network_import_present=True)) + mutation("12 real clock rejected", lambda r: r["fake_boundary"].update(real_clock_present=True)) + mutation("13 second send rejected", lambda r: r["ordering_contract"].update(complete_batch_send_count=2)) + mutation("14 EOF completion rejected", lambda r: r["ordering_contract"].update(remote_eof="COMPLETE")) + mutation("15 early deadline rejected", lambda r: r["ordering_contract"].update(early_deadline="COMPLETE")) + mutation("16 target retention rejected", lambda r: r["evidence_contract"].update(target_retained=True)) + mutation("17 physical erasure proof rejected", lambda r: r["evidence_contract"].update(physical_memory_erasure_proven=True)) + mutation("18 live adapter decision rejected", lambda r: r["decision"].update(live_adapter_allowed=True)) + mutation("19 device action rejected", lambda r: r["performed_actions"].update(ps5_connected=True)) + mutation("20 hardware proof rejected", lambda r: r["tests"].update(hardware_claim_from_host_test=True)) + + failures = [] + for name, mutate in cases: + value = deepcopy(original); mutate(value) + if validator.validate_record(value): print(f"PASS {name}") + else: failures.append(name); print(f"FAIL {name}: mutation accepted") + if failures: return 1 + print(f"Phase-1.0AA guardrails passed: {len(cases)}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_retroarch_phase10ab.py b/tests/test_retroarch_phase10ab.py new file mode 100644 index 0000000..d135377 --- /dev/null +++ b/tests/test_retroarch_phase10ab.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Mutation guardrails for Phase-1.0AB feasibility evidence.""" + +from __future__ import annotations +import argparse +from copy import deepcopy +import importlib.util +from pathlib import Path +import sys + +def load(path): + spec=importlib.util.spec_from_file_location("phase10ab_validator",path);assert spec and spec.loader + module=importlib.util.module_from_spec(spec);sys.modules[spec.name]=module;spec.loader.exec_module(module);return module + +def main(): + parser=argparse.ArgumentParser();parser.add_argument("--root",type=Path,required=True);args=parser.parse_args();root=args.root.resolve() + validator=load(root / "tools/validate_retroarch_phase10ab.py");original=validator.load_json(root / "manifests/retroarch/phase-1.0ab-live-adapter-feasibility.json") + cases=[] + def mutation(name,function): cases.append((name,function)) + mutation("01 activation rejected",lambda r:r["activation"].update(active=True)) + mutation("02 target rejected",lambda r:r["activation"].update(target_address="device.invalid")) + mutation("03 connection authority rejected",lambda r:r["authorizations"].update(ps5_connection_authorized=True)) + mutation("04 request authority rejected",lambda r:r["authorizations"].update(device_request_authorized=True)) + mutation("05 result authority rejected",lambda r:r["authorizations"].update(result_receive_authorized=True)) + mutation("06 retry rejected",lambda r:r["authorizations"].update(automatic_retry=True)) + mutation("07 reconnect rejected",lambda r:r["authorizations"].update(reconnect_authorized=True)) + mutation("08 resume rejected",lambda r:r["authorizations"].update(resume_authorized=True)) + mutation("09 runtime identity change rejected",lambda r:r["local_runtime"].update(python_version="latest")) + mutation("10 connect promotion rejected",lambda r:r["feasibility"].update(pending_connect="PROVEN")) + mutation("11 deadline promotion rejected",lambda r:r["feasibility"].update(hard_wall_clock_deadline="PROVEN")) + mutation("12 remote cleanup promotion rejected",lambda r:r["feasibility"].update(remote_shell_cleanup="PROVEN")) + mutation("13 network import rejected",lambda r:r["trace_model"].update(network_import_present=True)) + mutation("14 real clock rejected",lambda r:r["trace_model"].update(real_clock_present=True)) + mutation("15 live implementation stop required",lambda r:r["hard_stops"].update(live_adapter_implementation=False)) + mutation("16 DNS stop required",lambda r:r["hard_stops"].update(dns=False)) + mutation("17 live adapter decision rejected",lambda r:r["decision"].update(live_adapter_allowed=True)) + mutation("18 overall promotion rejected",lambda r:r["decision"].update(overall="PROVEN")) + mutation("19 device action rejected",lambda r:r["performed_actions"].update(ps5_connected=True)) + mutation("20 hardware proof rejected",lambda r:r["tests"].update(hardware_claim_from_host_test=True)) + failures=[] + for name,mutate in cases: + value=deepcopy(original);mutate(value) + if validator.validate_record(value):print(f"PASS {name}") + else:failures.append(name);print(f"FAIL {name}: mutation accepted") + if failures:return 1 + print(f"Phase-1.0AB guardrails passed: {len(cases)}");return 0 + +if __name__ == "__main__":raise SystemExit(main()) diff --git a/tests/test_retroarch_phase10ac.py b/tests/test_retroarch_phase10ac.py new file mode 100644 index 0000000..18a2d49 --- /dev/null +++ b/tests/test_retroarch_phase10ac.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Mutation guardrails for Phase-1.0AC dormant-adapter evidence.""" + +from __future__ import annotations + +import argparse +from copy import deepcopy +import importlib.util +from pathlib import Path +import sys + + +def load(path: Path): + spec = importlib.util.spec_from_file_location("phase10ac_validator", path) + assert spec and spec.loader + module = importlib.util.module_from_spec(spec) + sys.modules[spec.name] = module + spec.loader.exec_module(module) + return module + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + root = args.root.resolve() + validator = load(root / "tools/validate_retroarch_phase10ac.py") + original = validator.load_json( + root / "manifests/retroarch/phase-1.0ac-dormant-adapter.json") + cases = [] + def mutation(name, function): cases.append((name, function)) + + mutation("01 activation rejected", lambda r: r["activation"].update(active=True)) + mutation("02 target rejected", lambda r: r["activation"].update(target_address="device.invalid")) + mutation("03 adapter activation hash rejected", lambda r: r["activation"].update(adapter_sha256="00" * 32)) + mutation("04 connection authority rejected", lambda r: r["authorizations"].update(ps5_connection_authorized=True)) + mutation("05 request authority rejected", lambda r: r["authorizations"].update(device_request_authorized=True)) + mutation("06 result authority rejected", lambda r: r["authorizations"].update(result_receive_authorized=True)) + mutation("07 retry rejected", lambda r: r["authorizations"].update(automatic_retry=True)) + mutation("08 reconnect rejected", lambda r: r["authorizations"].update(reconnect_authorized=True)) + mutation("09 live protocol rejected", lambda r: r["adapter"].update(live_adapter_protocol_present=True)) + mutation("10 network import rejected", lambda r: r["adapter"].update(network_import_present=True)) + mutation("11 real clock rejected", lambda r: r["adapter"].update(real_clock_present=True)) + mutation("12 address rejected", lambda r: r["adapter"].update(address_present=True)) + mutation("13 receipt requirement preserved", lambda r: r["adapter"].update(precommitted_receipt_required=False)) + mutation("14 EOF rejection preserved", lambda r: r["lifecycle"].update(remote_eof_rejected=False)) + mutation("15 deadline race rule preserved", lambda r: r["lifecycle"].update(deadline_wins_readiness_race=False)) + mutation("16 remote cleanup not promoted", lambda r: r["lifecycle"].update(remote_cleanup_proven=True)) + mutation("17 live implementation stop required", lambda r: r["hard_stops"].update(live_adapter_implementation=False)) + mutation("18 live adapter decision rejected", lambda r: r["decision"].update(live_adapter_allowed=True)) + mutation("19 performed device action rejected", lambda r: r["performed_actions"].update(ps5_connected=True)) + mutation("20 hardware proof rejected", lambda r: r["tests"].update(hardware_claim_from_host_test=True)) + + failures = [] + for name, mutate in cases: + value = deepcopy(original); mutate(value) + if validator.validate_record(value): + print(f"PASS {name}") + else: + failures.append(name); print(f"FAIL {name}: mutation accepted") + if failures: + return 1 + print(f"Phase-1.0AC guardrails passed: {len(cases)}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_retroarch_phase10ad.py b/tests/test_retroarch_phase10ad.py new file mode 100644 index 0000000..b1305ba --- /dev/null +++ b/tests/test_retroarch_phase10ad.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Guardrails for the tracked Phase-1.0AD boundary.""" + +from __future__ import annotations + +import argparse +import json +from pathlib import Path +import unittest + + +PARSER = argparse.ArgumentParser() +PARSER.add_argument("--root", type=Path, required=True) +ROOT = PARSER.parse_args().root + + +class Phase10ADGuardrails(unittest.TestCase): + def setUp(self) -> None: + self.manifest = json.loads((ROOT / "manifests/retroarch/phase-1.0ad-inactive-activation.json").read_text(encoding="utf-8")) + + def test_activation_is_empty(self) -> None: + activation = self.manifest["activation"] + self.assertFalse(activation["active"]) + for name in ("target_address", "target_port", "run_id", "not_before", + "expires_at", "launcher_sha256", "payload_sha256", + "approval_sha256"): + self.assertIsNone(activation[name]) + + def test_no_authority_exists(self) -> None: + self.assertFalse(any(self.manifest["authorizations"].values())) + self.assertFalse(self.manifest["decision"]["device_action_allowed"]) + self.assertFalse(self.manifest["decision"]["bigapp_launcher_implementation_allowed"]) + + def test_contract_has_no_live_import(self) -> None: + source = (ROOT / "tools/phase10ad_activation_contract.py").read_text(encoding="utf-8") + for statement in ("import socket", "import selectors", "import requests", + "from socket", "subprocess"): + self.assertNotIn(statement, source) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10ae.py b/tests/test_retroarch_phase10ae.py new file mode 100644 index 0000000..fccc7f8 --- /dev/null +++ b/tests/test_retroarch_phase10ae.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Tracked guardrails for Phase-1.0AE.""" + +from __future__ import annotations + +import argparse +import json +from pathlib import Path +import unittest + + +PARSER = argparse.ArgumentParser() +PARSER.add_argument("--root", type=Path, required=True) +ROOT = PARSER.parse_args().root + + +class Phase10AEGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.data = json.loads((ROOT / "manifests/retroarch/phase-1.0ae-launcher-architecture.json").read_text(encoding="utf-8")) + + def test_no_authority_or_target_implementation(self) -> None: + self.assertFalse(any(self.data["authorizations"].values())) + self.assertFalse(self.data["decision"]["target_implementation_allowed"]) + self.assertFalse(self.data["decision"]["device_action_allowed"]) + + def test_persistent_and_destructive_behavior_is_forbidden(self) -> None: + policy = self.data["mandatory_policy"] + for key in ("kill_existing_bigapp", "automatic_retry", "fallback_title", + "persistent_write", "system_remount", "installation", "autoload"): + self.assertFalse(policy[key]) + + def test_hard_blockers_remain_explicit(self) -> None: + self.assertGreaterEqual(len(self.data["hard_blockers"]), 8) + self.assertFalse(self.data["lineage_decision"]["root_cause_proven"]) + self.assertFalse(self.data["lineage_decision"]["videoout_ownership_proven"]) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10af.py b/tests/test_retroarch_phase10af.py new file mode 100644 index 0000000..af3016b --- /dev/null +++ b/tests/test_retroarch_phase10af.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Manifest guardrails for Phase-1.0AF.""" + +from __future__ import annotations + +import argparse +import json +from pathlib import Path +import unittest + + +PARSER = argparse.ArgumentParser() +PARSER.add_argument("--root", type=Path, required=True) +ROOT = PARSER.parse_args().root + + +class Phase10AFGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.data = json.loads((ROOT / "manifests/retroarch/phase-1.0af-bigapp-lifecycle-model.json").read_text(encoding="utf-8")) + + def test_no_authority_or_target_claim(self) -> None: + self.assertFalse(any(self.data["authorizations"].values())) + self.assertFalse(self.data["decision"]["target_implementation_allowed"]) + self.assertFalse(self.data["decision"]["device_action_allowed"]) + + def test_model_is_closed(self) -> None: + model = self.data["model"] + for key in ("target_code_present", "socket_present", "process_api_present", + "syscall_present", "real_clock_present", "filesystem_output_present", + "existing_bigapp_killed"): + self.assertFalse(model[key]) + self.assertTrue(model["require_no_existing_bigapp"]) + self.assertTrue(model["restore_before_child_termination"]) + + def test_runtime_blockers_remain(self) -> None: + self.assertGreaterEqual(len(self.data["remaining_blockers"]), 7) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10ag.py b/tests/test_retroarch_phase10ag.py new file mode 100644 index 0000000..39a9021 --- /dev/null +++ b/tests/test_retroarch_phase10ag.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Tracked guardrails for Phase-1.0AG.""" + +from __future__ import annotations + +import argparse +import json +from pathlib import Path +import unittest + + +PARSER = argparse.ArgumentParser() +PARSER.add_argument("--root", type=Path, required=True) +ROOT = PARSER.parse_args().root + + +class Phase10AGGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.data = json.loads((ROOT / "manifests/retroarch/phase-1.0ag-bounded-elf-contract.json").read_text(encoding="utf-8")) + + def test_parser_does_not_admit_missing_artifact(self) -> None: + reference = self.data["historical_reference"] + self.assertFalse(reference["bytes_present_in_scanned_workspace"]) + self.assertFalse(reference["validated_by_phase10ag"]) + self.assertFalse(self.data["decision"]["historical_artifact_admitted"]) + + def test_permissions_and_bounds_fail_closed(self) -> None: + admission = self.data["admission"] + self.assertFalse(admission["writable_executable_load_allowed"]) + self.assertFalse(admission["interpreter_allowed"]) + self.assertFalse(admission["overlapping_load_ranges_allowed"]) + self.assertFalse(admission["file_interface_present"]) + self.assertFalse(admission["execution_interface_present"]) + + def test_no_authority_or_target_claim(self) -> None: + self.assertFalse(any(self.data["authorizations"].values())) + self.assertFalse(self.data["decision"]["target_implementation_allowed"]) + self.assertFalse(self.data["decision"]["device_action_allowed"]) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10ah.py b/tests/test_retroarch_phase10ah.py new file mode 100644 index 0000000..c94eb61 --- /dev/null +++ b/tests/test_retroarch_phase10ah.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Tracked guardrails for Phase-1.0AH.""" + +from __future__ import annotations + +import argparse +import json +from pathlib import Path +import unittest + + +PARSER = argparse.ArgumentParser() +PARSER.add_argument("--root", type=Path, required=True) +ROOT = PARSER.parse_args().root + + +class Phase10AHGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.data = json.loads((ROOT / "manifests/retroarch/phase-1.0ah-dynamic-contract.json").read_text(encoding="utf-8")) + + def test_relocation_split_is_exact(self) -> None: + contract = self.data["contract"] + self.assertEqual(contract["loader_applied_relocation_types"], + ["R_X86_64_RELATIVE"]) + self.assertEqual(contract["crt_applied_relocation_types"], + ["R_X86_64_GLOB_DAT"]) + self.assertFalse(contract["unknown_relocation_types_allowed"]) + + def test_historical_metadata_is_not_byte_evidence(self) -> None: + reference = self.data["historical_phase10m_reference"] + self.assertFalse(reference["exact_bytes_present"]) + self.assertFalse(reference["validated_by_phase10ah"]) + + def test_no_mapping_or_authority(self) -> None: + self.assertFalse(any(self.data["authorizations"].values())) + self.assertFalse(self.data["decision"]["target_mapping_allowed"]) + self.assertFalse(self.data["decision"]["target_implementation_allowed"]) + self.assertFalse(self.data["decision"]["device_action_allowed"]) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10ai.py b/tests/test_retroarch_phase10ai.py new file mode 100644 index 0000000..4cd45b7 --- /dev/null +++ b/tests/test_retroarch_phase10ai.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Tracked guardrails for Phase-1.0AI.""" + +from __future__ import annotations + +import argparse +import json +from pathlib import Path +import unittest + + +PARSER = argparse.ArgumentParser() +PARSER.add_argument("--root", type=Path, required=True) +ROOT = PARSER.parse_args().root + + +class Phase10AIGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.data = json.loads((ROOT / "manifests/retroarch/phase-1.0ai-mapping-model.json").read_text(encoding="utf-8")) + + def test_failure_never_retains_partial_mapping(self) -> None: + transaction = self.data["transaction"] + self.assertTrue(transaction["failure_unmaps_entire_new_child_region"]) + self.assertTrue(transaction["failure_releases_mirror"]) + self.assertFalse(transaction["partial_mapping_retained_on_failure"]) + + def test_model_has_no_mapping_capability(self) -> None: + transaction = self.data["transaction"] + self.assertFalse(transaction["target_memory_interface_present"]) + self.assertFalse(transaction["host_memory_mapping_present"]) + self.assertFalse(transaction["process_interface_present"]) + + def test_no_authority_or_target_claim(self) -> None: + self.assertFalse(any(self.data["authorizations"].values())) + self.assertFalse(self.data["decision"]["target_mapping_allowed"]) + self.assertFalse(self.data["decision"]["target_implementation_allowed"]) + self.assertFalse(self.data["decision"]["device_action_allowed"]) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10aj.py b/tests/test_retroarch_phase10aj.py new file mode 100644 index 0000000..f165d09 --- /dev/null +++ b/tests/test_retroarch_phase10aj.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Tracked guardrails for Phase-1.0AJ.""" + +from __future__ import annotations + +import argparse +import json +from pathlib import Path +import unittest + + +PARSER = argparse.ArgumentParser() +PARSER.add_argument("--root", type=Path, required=True) +ROOT = PARSER.parse_args().root + + +class Phase10AJGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.data = json.loads((ROOT / "manifests/retroarch/phase-1.0aj-primitive-audit.json").read_text(encoding="utf-8")) + + def test_unhardened_loader_is_rejected(self) -> None: + matrix = self.data["primitive_matrix"] + self.assertEqual(matrix["mirror_and_jit_alias_transaction"], + "SHSRV_V07_UNHARDENED_NOT_REUSABLE") + self.assertEqual(matrix["complete_bigapp_composition"], "ABSENT") + + def test_gaps_remain_explicit(self) -> None: + blockers = self.data["remaining_blockers"] + self.assertIn("JIT_FD_ALIAS_AND_HOST_MIRROR_ACQUISITION_NOT_IN_CENTRAL_CLEANUP_STATE", blockers) + self.assertIn("CLEANUP_FAILURE_OWNERSHIP_AND_TERMINATION_POLICY_NEEDS_COMPOSITION_MODEL", blockers) + + def test_no_authority_or_target_implementation(self) -> None: + self.assertFalse(any(self.data["authorizations"].values())) + self.assertFalse(self.data["decision"]["copy_shsrv_loader_code_allowed"]) + self.assertFalse(self.data["decision"]["target_implementation_allowed"]) + self.assertFalse(self.data["decision"]["device_action_allowed"]) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10ak.py b/tests/test_retroarch_phase10ak.py new file mode 100644 index 0000000..b20f203 --- /dev/null +++ b/tests/test_retroarch_phase10ak.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Tracked guardrails for Phase-1.0AK.""" + +from __future__ import annotations + +import argparse +import json +from pathlib import Path +import unittest + + +PARSER = argparse.ArgumentParser() +PARSER.add_argument("--root", type=Path, required=True) +ROOT = PARSER.parse_args().root + + +class Phase10AKGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.data = json.loads((ROOT / "manifests/retroarch/phase-1.0ak-hybrid-composition.json").read_text(encoding="utf-8")) + + def test_every_temporary_resource_has_explicit_ownership(self) -> None: + ownership = self.data["ownership"] + for key in ("host_mirror", "jit_master_descriptors", + "jit_alias_descriptors", "host_alias_mappings", + "remote_alias_mappings"): + self.assertNotEqual(ownership[key], "ABSENT") + self.assertTrue(ownership["reverse_cleanup"]) + + def test_cleanup_failure_is_fail_closed(self) -> None: + ownership = self.data["ownership"] + self.assertTrue(ownership["cleanup_failure_requires_child_termination"]) + self.assertTrue(ownership["failed_child_termination_is_hard_error"]) + self.assertFalse(ownership["partial_success_allowed"]) + + def test_no_target_or_device_authority(self) -> None: + self.assertFalse(any(self.data["authorizations"].values())) + self.assertFalse(self.data["decision"]["target_implementation_allowed"]) + self.assertFalse(self.data["decision"]["device_action_allowed"]) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10al.py b/tests/test_retroarch_phase10al.py new file mode 100644 index 0000000..81cac41 --- /dev/null +++ b/tests/test_retroarch_phase10al.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Tracked guardrails for Phase-1.0AL.""" + +from __future__ import annotations + +import argparse +import json +from pathlib import Path +import unittest + + +PARSER = argparse.ArgumentParser() +PARSER.add_argument("--root", type=Path, required=True) +ROOT = PARSER.parse_args().root + + +class Phase10ALGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.data = json.loads((ROOT / "manifests/retroarch/phase-1.0al-mdbg-copy-audit.json").read_text(encoding="utf-8")) + + def test_known_restore_and_progress_gaps_remain_blocking(self) -> None: + findings = self.data["findings"] + self.assertFalse(findings["caps_set_failure_restores_authid"]) + self.assertFalse(findings["restore_attempts_all_fields_after_one_failure"]) + self.assertFalse(findings["partial_progress_exposed_to_caller"]) + self.assertFalse(findings["return_zero_proves_complete_copy"]) + + def test_replacement_contract_is_fail_closed(self) -> None: + required = self.data["required_replacement_contract"] + self.assertIn("REPORT_EXACT_PARTIAL_BYTE_COUNT", required) + self.assertIn("RESTORE_EVERY_CHANGED_FIELD_ON_EVERY_EXIT", required) + self.assertIn("KILL_AND_REAP_CHILD_AFTER_PARTIAL_COPY_OR_RESTORE_FAILURE", required) + + def test_no_direct_reuse_or_authority(self) -> None: + self.assertFalse(any(self.data["authorizations"].values())) + self.assertFalse(self.data["decision"]["direct_sdk_mdbg_copy_reuse_allowed"]) + self.assertFalse(self.data["decision"]["target_implementation_allowed"]) + self.assertFalse(self.data["decision"]["device_action_allowed"]) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10am.py b/tests/test_retroarch_phase10am.py new file mode 100644 index 0000000..54e54ff --- /dev/null +++ b/tests/test_retroarch_phase10am.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Tracked guardrails for Phase-1.0AM.""" + +from __future__ import annotations + +import argparse +import json +from pathlib import Path +import unittest + + +PARSER = argparse.ArgumentParser() +PARSER.add_argument("--root", type=Path, required=True) +ROOT = PARSER.parse_args().root + + +class Phase10AMGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.data = json.loads((ROOT / "manifests/retroarch/phase-1.0am-bounded-copy-model.json").read_text(encoding="utf-8")) + + def test_progress_and_restoration_are_exact(self) -> None: + contract = self.data["contract"] + self.assertTrue(contract["exact_progress_reported"]) + self.assertTrue(contract["complete_status_requires_exact_length"]) + self.assertTrue(contract["restore_caps_and_authid_on_every_changed_exit"]) + self.assertTrue(contract["independent_restore_failure_bits"]) + + def test_failure_containment_includes_service(self) -> None: + contract = self.data["contract"] + self.assertTrue(contract["partial_copy_kills_and_reaps_child"]) + self.assertTrue(contract["restore_failure_kills_child_and_terminates_service"]) + self.assertTrue(contract["terminal_cleanup_failure_is_hard_error"]) + + def test_no_real_capability_or_authority(self) -> None: + contract = self.data["contract"] + for key in ("credential_interface_present", "process_interface_present", + "memory_interface_present", "clock_interface_present"): + self.assertFalse(contract[key]) + self.assertFalse(any(self.data["authorizations"].values())) + self.assertFalse(self.data["decision"]["target_implementation_allowed"]) + self.assertFalse(self.data["decision"]["device_action_allowed"]) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10an.py b/tests/test_retroarch_phase10an.py new file mode 100644 index 0000000..4ac4db1 --- /dev/null +++ b/tests/test_retroarch_phase10an.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Tracked guardrails for Phase-1.0AN.""" + +from __future__ import annotations + +import argparse +import json +from pathlib import Path +import unittest + + +PARSER = argparse.ArgumentParser() +PARSER.add_argument("--root", type=Path, required=True) +ROOT = PARSER.parse_args().root + + +class Phase10ANGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.data = json.loads((ROOT / "manifests/retroarch/phase-1.0an-service-lifecycle-audit.json").read_text(encoding="utf-8")) + + def test_fail_stop_is_not_promoted_to_recovery(self) -> None: + lifecycle = self.data["service_lifecycle"] + self.assertTrue(lifecycle["ptrace_auth_restore_failure_latched"]) + self.assertTrue(lifecycle["request_handler_process_exits_125"]) + self.assertFalse(lifecycle["service_restart_owner_present"]) + self.assertFalse(lifecycle["restart_identity_verification_present"]) + + def test_pt_copyin_remains_unproven(self) -> None: + copy = self.data["copy_path"] + self.assertFalse(copy["ptrace_io_descriptor_progress_checked"]) + self.assertFalse(copy["hard_deadline_or_preemption_present"]) + self.assertFalse(copy["safe_replacement_for_mdbg_copy_proven"]) + + def test_no_restart_reuse_or_authority(self) -> None: + self.assertFalse(any(self.data["authorizations"].values())) + self.assertFalse(self.data["decision"]["hardened_pt_copyin_reuse_allowed"]) + self.assertFalse(self.data["decision"]["automatic_service_restart_allowed"]) + self.assertFalse(self.data["decision"]["target_implementation_allowed"]) + self.assertFalse(self.data["decision"]["device_action_allowed"]) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10ao.py b/tests/test_retroarch_phase10ao.py new file mode 100644 index 0000000..86b27ef --- /dev/null +++ b/tests/test_retroarch_phase10ao.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Tracked guardrails for Phase-1.0AO.""" + +from __future__ import annotations + +import argparse +import json +from pathlib import Path +import unittest + + +PARSER = argparse.ArgumentParser() +PARSER.add_argument("--root", type=Path, required=True) +ROOT = PARSER.parse_args().root + + +class Phase10AOGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.data = json.loads((ROOT / "manifests/retroarch/phase-1.0ao-worker-supervisor-model.json").read_text(encoding="utf-8")) + + def test_worker_result_and_containment_are_exact(self) -> None: + architecture = self.data["architecture"] + self.assertTrue(architecture["worker_identity_must_match"]) + self.assertTrue(architecture["exact_copy_length_required"]) + self.assertTrue(architecture["zero_restore_failure_bits_required"]) + self.assertTrue(architecture["ambiguous_or_partial_attempt_terminates_child"]) + + def test_no_restart_retry_or_real_capability(self) -> None: + architecture = self.data["architecture"] + self.assertFalse(architecture["automatic_restart"]) + self.assertFalse(architecture["retry"]) + for key in ("real_process_interface_present", "real_signal_interface_present", + "real_clock_present", "real_ipc_present"): + self.assertFalse(architecture[key]) + + def test_target_feasibility_and_authority_remain_false(self) -> None: + self.assertFalse(any(self.data["authorizations"].values())) + self.assertFalse(self.data["decision"]["target_architecture_feasible"]) + self.assertFalse(self.data["decision"]["target_implementation_allowed"]) + self.assertFalse(self.data["decision"]["device_action_allowed"]) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10ap.py b/tests/test_retroarch_phase10ap.py new file mode 100644 index 0000000..31fa2c6 --- /dev/null +++ b/tests/test_retroarch_phase10ap.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Tracked guardrails for Phase-1.0AP.""" + +from __future__ import annotations + +import argparse +import json +from pathlib import Path +import unittest + + +PARSER = argparse.ArgumentParser() +PARSER.add_argument("--root", type=Path, required=True) +ROOT = PARSER.parse_args().root + + +class Phase10APGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.data = json.loads((ROOT / "manifests/retroarch/phase-1.0ap-worker-feasibility-audit.json").read_text(encoding="utf-8")) + + def test_candidate_is_not_runtime_proof(self) -> None: + worker = self.data["worker_creation"] + self.assertTrue(worker["rfork_thread_declared_by_public_sdk"]) + self.assertTrue(worker["rfork_thread_used_by_official_shsrv"]) + self.assertFalse(worker["pid_birth_identity_or_generation_token_present"]) + self.assertFalse(worker["firmware_960_behavior_proven"]) + + def test_preemption_and_result_remain_blocked(self) -> None: + self.assertFalse(self.data["preemption"]["waitpid_calls_are_bounded"]) + self.assertFalse(self.data["preemption"]["blocked_mdbg_or_ptrace_io_kill_completion_proven"]) + self.assertFalse(self.data["result_channel"]["fixed_size_worker_result_record_present"]) + self.assertFalse(self.data["result_channel"]["bounded_receive_and_deadline_present"]) + + def test_no_source_copy_target_or_device_authority(self) -> None: + self.assertFalse(any(self.data["authorizations"].values())) + self.assertFalse(self.data["decision"]["copy_current_shsrv_code_allowed"]) + self.assertFalse(self.data["decision"]["target_worker_architecture_feasible"]) + self.assertFalse(self.data["decision"]["target_implementation_allowed"]) + self.assertFalse(self.data["decision"]["device_action_allowed"]) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10aq.py b/tests/test_retroarch_phase10aq.py new file mode 100644 index 0000000..dc4186c --- /dev/null +++ b/tests/test_retroarch_phase10aq.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Tracked guardrails for Phase-1.0AQ.""" + +from __future__ import annotations + +import argparse +import json +from pathlib import Path +import unittest + + +PARSER = argparse.ArgumentParser() +PARSER.add_argument("--root", type=Path, required=True) +ROOT = PARSER.parse_args().root + + +class Phase10AQGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.data = json.loads((ROOT / "manifests/retroarch/phase-1.0aq-worker-result-record.json").read_text(encoding="utf-8")) + + def test_identity_is_more_than_pid(self) -> None: + identity = self.data["identity"] + self.assertFalse(identity["pid_alone_is_identity"]) + self.assertTrue(identity["monotonic_generation_required"]) + self.assertTrue(identity["all_identity_fields_precommitted"]) + self.assertEqual(identity["worker_nonce_bytes"], 16) + + def test_success_and_integrity_are_strict(self) -> None: + result = self.data["result"] + self.assertTrue(result["success_requires_exact_copy"]) + self.assertTrue(result["success_requires_zero_restore_bits"]) + self.assertTrue(result["every_single_byte_mutation_tested"]) + self.assertFalse(result["digest_is_authentication"]) + + def test_no_transport_target_or_authority(self) -> None: + self.assertFalse(any(self.data["capabilities"].values())) + self.assertFalse(any(self.data["authorizations"].values())) + self.assertFalse(self.data["decision"]["transport_implementation_allowed"]) + self.assertFalse(self.data["decision"]["target_implementation_allowed"]) + self.assertFalse(self.data["decision"]["device_action_allowed"]) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10ar.py b/tests/test_retroarch_phase10ar.py new file mode 100644 index 0000000..fbcb727 --- /dev/null +++ b/tests/test_retroarch_phase10ar.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Tracked guardrails for Phase-1.0AR.""" + +from __future__ import annotations + +import argparse +import json +from pathlib import Path +import unittest + + +PARSER = argparse.ArgumentParser() +PARSER.add_argument("--root", type=Path, required=True) +ROOT = PARSER.parse_args().root + + +class Phase10ARGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.data = json.loads((ROOT / "manifests/retroarch/phase-1.0ar-result-channel-model.json").read_text(encoding="utf-8")) + + def test_completion_not_eof_is_boundary(self) -> None: + channel = self.data["channel"] + self.assertFalse(channel["eof_is_success"]) + self.assertFalse(channel["silent_incomplete_is_success"]) + self.assertTrue(channel["record_completion_is_success_boundary"]) + self.assertTrue(channel["deadline_preempts_crossing_read"]) + + def test_partial_reads_do_not_require_atomic_write(self) -> None: + channel = self.data["channel"] + self.assertTrue(channel["arbitrary_partial_reads_supported"]) + self.assertTrue(channel["byte_at_a_time_supported"]) + self.assertTrue(channel["record_overflow_rejected"]) + + def test_no_live_channel_target_or_authority(self) -> None: + channel = self.data["channel"] + for key in ("live_pipe_present", "live_fd_present", "real_clock_present", + "process_interface_present"): + self.assertFalse(channel[key]) + self.assertFalse(any(self.data["authorizations"].values())) + self.assertFalse(self.data["decision"]["live_channel_implementation_allowed"]) + self.assertFalse(self.data["decision"]["target_implementation_allowed"]) + self.assertFalse(self.data["decision"]["device_action_allowed"]) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10as.py b/tests/test_retroarch_phase10as.py new file mode 100644 index 0000000..eb98598 --- /dev/null +++ b/tests/test_retroarch_phase10as.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Tracked guardrails for Phase-1.0AS.""" + +from __future__ import annotations + +import argparse +import json +from pathlib import Path +import unittest + + +PARSER = argparse.ArgumentParser() +PARSER.add_argument("--root", type=Path, required=True) +ROOT = PARSER.parse_args().root + + +class Phase10ASGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.data = json.loads((ROOT / "manifests/retroarch/phase-1.0as-channel-primitive-audit.json").read_text(encoding="utf-8")) + + def test_candidates_are_not_composition_proof(self) -> None: + primitives = self.data["channel_primitives"] + self.assertTrue(primitives["pipe_used_by_official_shsrv"]) + self.assertTrue(primitives["poll_used_by_official_shsrv"]) + self.assertFalse(primitives["pipe_poll_monotonic_deadline_composition_present"]) + self.assertFalse(primitives["nonblocking_result_read_callsite_present"]) + + def test_current_worker_closes_result_fd(self) -> None: + ownership = self.data["fd_ownership"] + self.assertTrue(ownership["rfcfdg_documented_as_close_all_fds"]) + self.assertTrue(ownership["official_shsrv_worker_uses_rfcfdg"]) + self.assertFalse(ownership["result_fd_inherited_by_worker"]) + self.assertFalse(ownership["exclusive_parent_child_end_close_order_present"]) + + def test_no_live_target_or_authority(self) -> None: + self.assertFalse(any(self.data["authorizations"].values())) + self.assertFalse(self.data["decision"]["live_channel_architecture_feasible"]) + self.assertFalse(self.data["decision"]["copy_current_shsrv_code_allowed"]) + self.assertFalse(self.data["decision"]["target_implementation_allowed"]) + self.assertFalse(self.data["decision"]["device_action_allowed"]) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10at.py b/tests/test_retroarch_phase10at.py new file mode 100644 index 0000000..33168e8 --- /dev/null +++ b/tests/test_retroarch_phase10at.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Tracked guardrails for Phase-1.0AT.""" + +from __future__ import annotations + +import argparse +import json +from pathlib import Path +import unittest + + +PARSER = argparse.ArgumentParser() +PARSER.add_argument("--root", type=Path, required=True) +ROOT = PARSER.parse_args().root + + +class Phase10ATGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.data = json.loads((ROOT / "manifests/retroarch/phase-1.0at-fd-deadline-model.json").read_text(encoding="utf-8")) + + def test_model_has_no_live_capability(self) -> None: + boundary = self.data["model_boundary"] + self.assertTrue(boundary["fake_facade_only"]) + self.assertFalse(boundary["os_imports_present"]) + self.assertFalse(boundary["process_api_present"]) + self.assertFalse(boundary["clock_api_present"]) + self.assertFalse(boundary["network_api_present"]) + self.assertFalse(boundary["target_address_present"]) + + def test_ownership_and_deadline_fail_closed(self) -> None: + ownership = self.data["ownership_contract"] + deadline = self.data["deadline_contract"] + self.assertTrue(ownership["parent_write_end_closed_before_read"]) + self.assertTrue(ownership["all_acquired_ends_closed_on_failure"]) + self.assertTrue(ownership["started_worker_terminated_and_reaped_on_failure"]) + self.assertTrue(deadline["one_absolute_budget_across_setup_and_reads"]) + self.assertTrue(deadline["trailing_read_event_fails"]) + + def test_live_and_device_gates_stay_closed(self) -> None: + self.assertFalse(any(self.data["authorizations"].values())) + self.assertFalse(self.data["decision"]["live_channel_architecture_feasible"]) + self.assertFalse(self.data["decision"]["target_implementation_allowed"]) + self.assertFalse(self.data["decision"]["device_action_allowed"]) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10au.py b/tests/test_retroarch_phase10au.py new file mode 100644 index 0000000..9d00d72 --- /dev/null +++ b/tests/test_retroarch_phase10au.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Tracked guardrails for Phase-1.0AU.""" + +from __future__ import annotations + +import argparse +import json +from pathlib import Path +import unittest + + +PARSER = argparse.ArgumentParser() +PARSER.add_argument("--root", type=Path, required=True) +ROOT = PARSER.parse_args().root + + +class Phase10AUGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + path = ROOT / "manifests/retroarch/phase-1.0au-live-channel-feasibility.json" + cls.data = json.loads(path.read_text(encoding="utf-8")) + + def test_declarations_do_not_become_runtime_proof(self) -> None: + contracts = self.data["public_source_contracts"] + self.assertTrue(contracts["source_level_design_inputs_complete"]) + limits = self.data["evidence_limits"] + self.assertFalse(limits["headers_prove_firmware_960_runtime"]) + self.assertFalse(limits["phase10at_fake_model_proves_live_cleanup"]) + self.assertFalse(limits["launch_context_fix_proven"]) + self.assertFalse(limits["visible_flip_proven"]) + + def test_current_shsrv_composition_is_rejected(self) -> None: + audit = self.data["official_composition_audit"] + self.assertFalse(audit["worker_uses_rffdg"]) + self.assertTrue(audit["worker_uses_rfcfdg_close_all"]) + self.assertFalse(audit["bounded_kill_and_reap_present"]) + self.assertTrue(audit["service_has_unbounded_restart_loop"]) + self.assertFalse(audit["direct_shsrv_reuse_allowed"]) + + def test_only_offline_canary_design_opens(self) -> None: + decision = self.data["decision"] + self.assertFalse(any(self.data["authorizations"].values())) + self.assertFalse(decision["live_result_channel_implementation_allowed"]) + self.assertFalse(decision["target_implementation_allowed"]) + self.assertFalse(decision["device_action_allowed"]) + self.assertTrue(decision["offline_canary_contract_design_allowed"]) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10av.py b/tests/test_retroarch_phase10av.py new file mode 100644 index 0000000..4dc8cfa --- /dev/null +++ b/tests/test_retroarch_phase10av.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Tracked manifest guardrails for Phase-1.0AV.""" + +from __future__ import annotations + +import argparse +import json +from pathlib import Path +import unittest + + +PARSER = argparse.ArgumentParser() +PARSER.add_argument("--root", type=Path, required=True) +ROOT = PARSER.parse_args().root + + +class Phase10AVGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + path = ROOT / "manifests/retroarch/phase-1.0av-launch-context-canary-contract.json" + cls.data = json.loads(path.read_text(encoding="utf-8")) + + def test_pair_is_exact_and_independently_authorized(self) -> None: + pair = self.data["pair_contract"] + self.assertTrue(pair["same_payload_sha256_required"]) + self.assertTrue(pair["distinct_launcher_sha256_required"]) + self.assertTrue(pair["distinct_run_id_required"]) + self.assertTrue(pair["distinct_approval_sha256_required"]) + self.assertTrue(pair["one_shot_each"]) + self.assertFalse(pair["automatic_retry"]) + + def test_terminal_and_interpretation_fail_closed(self) -> None: + result = self.data["result_contract"] + limits = self.data["interpretation_limits"] + self.assertTrue(result["submit_before_d04_required"]) + self.assertTrue(result["distinct_terminal_after_d04_required"]) + self.assertTrue(result["incomplete_arm_is_not_comparable"]) + self.assertTrue(limits["submit_return_difference_is_candidate_only"]) + self.assertFalse(limits["submit_zero_means_visible_flip"]) + self.assertFalse(limits["launch_context_root_cause_proven"]) + + def test_no_artifact_target_or_authority(self) -> None: + state = self.data["tracked_state"] + self.assertFalse(state["target_source_present"]) + self.assertFalse(state["target_artifact_present"]) + self.assertFalse(state["execution_eligible"]) + self.assertFalse(any(self.data["authorizations"].values())) + self.assertFalse(self.data["decision"]["target_implementation_allowed"]) + self.assertFalse(self.data["decision"]["device_action_allowed"]) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10aw.py b/tests/test_retroarch_phase10aw.py new file mode 100644 index 0000000..e50e513 --- /dev/null +++ b/tests/test_retroarch_phase10aw.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Tracked guardrails for Phase-1.0AW.""" + +from __future__ import annotations + +import argparse +import json +from pathlib import Path +import unittest + + +PARSER = argparse.ArgumentParser() +PARSER.add_argument("--root", type=Path, required=True) +ROOT = PARSER.parse_args().root + + +class Phase10AWGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + path = ROOT / "manifests/retroarch/phase-1.0aw-canary-source-delta-audit.json" + cls.data = json.loads(path.read_text(encoding="utf-8")) + + def test_old_payload_is_not_promoted(self) -> None: + old = self.data["historical_payload"] + self.assertFalse(old["bytes_present"]) + self.assertEqual(old["protocol_magic"], "CHD10J01") + self.assertFalse(old["distinct_post_d04_terminal_present"]) + self.assertFalse(old["reusable_as_av_canary"]) + + def test_source_delta_preserves_causal_identity_and_cleanup(self) -> None: + delta = self.data["required_payload_source_delta"] + self.assertEqual(delta["new_protocol_magic"], "CHD10AV1") + self.assertTrue(delta["d12_must_not_be_lifecycle_terminal"]) + self.assertTrue(delta["d14_emitted_only_after_s15_complete"]) + self.assertTrue(delta["new_cleanup_failure_counter_required"]) + self.assertTrue(delta["same_elf_bytes_for_both_arms_required"]) + self.assertTrue(delta["launcher_identity_compile_define_forbidden"]) + + def test_result_candidate_does_not_erase_launcher_effects(self) -> None: + result = self.data["bigapp_result_path"] + effects = self.data["launcher_effects"] + self.assertTrue(result["binary_frame_source_candidate"]) + self.assertFalse(result["live_result_path_proven"]) + self.assertTrue(effects["kills_running_bigapp"]) + self.assertTrue(effects["unbounded_child_discovery_wait"]) + self.assertFalse(effects["bounded_cleanup_on_every_failure"]) + self.assertFalse(effects["direct_v07_reuse_allowed"]) + + def test_artifact_and_device_gates_remain_closed(self) -> None: + self.assertFalse(any(self.data["authorizations"].values())) + self.assertFalse(self.data["decision"]["target_artifact_build_allowed"]) + self.assertFalse(self.data["decision"]["device_action_allowed"]) + self.assertTrue(self.data["decision"]["host_tested_source_design_allowed"]) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10ax.py b/tests/test_retroarch_phase10ax.py new file mode 100644 index 0000000..b84d427 --- /dev/null +++ b/tests/test_retroarch_phase10ax.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Tracked manifest guardrails for Phase-1.0AX.""" + +from __future__ import annotations + +import argparse +import json +from pathlib import Path +import unittest + + +PARSER = argparse.ArgumentParser() +PARSER.add_argument("--root", type=Path, required=True) +ROOT = PARSER.parse_args().root + + +class Phase10AXGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + path = ROOT / "manifests/retroarch/phase-1.0ax-canary-protocol-model.json" + cls.data = json.loads(path.read_text(encoding="utf-8")) + + def test_only_d14_can_complete(self) -> None: + frame = self.data["frame_contract"] + self.assertEqual(frame["magic"], "CHD10AV1") + self.assertTrue(frame["only_d14_terminal"]) + self.assertTrue(frame["d12_terminal_forbidden"]) + self.assertEqual(frame["d14_stage_value"], 30) + + def test_cleanup_predicate_is_complete(self) -> None: + cleanup = self.data["cleanup_terminal_contract"] + for key in ("rarch_main_returned_required", "d04_emitted_required", + "s15_complete_required", "initialized_mask_zero_required", + "cleanup_order_errors_zero_required", + "cleanup_failure_count_zero_required"): + self.assertTrue(cleanup[key]) + + def test_model_does_not_become_target_or_firmware_evidence(self) -> None: + boundary = self.data["model_boundary"] + self.assertTrue(boundary["python_bytes_only"]) + self.assertFalse(boundary["target_source_present"]) + self.assertFalse(boundary["target_artifact_present"]) + self.assertFalse(self.data["trace_contract"]["host_trace_means_firmware_behavior"]) + self.assertFalse(any(self.data["authorizations"].values())) + self.assertFalse(self.data["decision"]["target_artifact_build_allowed"]) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10ay.py b/tests/test_retroarch_phase10ay.py new file mode 100644 index 0000000..6dae92d --- /dev/null +++ b/tests/test_retroarch_phase10ay.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Tracked guardrails for Phase-1.0AY.""" + +from __future__ import annotations + +import argparse +import json +from pathlib import Path +import unittest + + +PARSER = argparse.ArgumentParser() +PARSER.add_argument("--root", type=Path, required=True) +ROOT = PARSER.parse_args().root + + +class Phase10AYGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + path = ROOT / "manifests/retroarch/phase-1.0ay-target-source-base.json" + cls.data = json.loads(path.read_text(encoding="utf-8")) + + def test_base_preserves_m_and_inactive_n(self) -> None: + base = self.data["base_selection"] + self.assertTrue(base["selected_commit_descends_from_phase10m_source"]) + self.assertTrue(base["target_source_blobs_unchanged_from_phase10m"]) + self.assertTrue(base["selected_commit_contains_inactive_phase10n_runner"]) + self.assertTrue(base["existing_runner_must_remain_inactive"]) + self.assertTrue(base["separate_worktree_required"]) + + def test_scope_is_host_only(self) -> None: + scope = self.data["permitted_patch_scope"] + self.assertTrue(scope["host_tests_only"]) + for key in ("launcher_compile_define", "target_profile", "cross_build", + "artifact", "live_runner_activation"): + self.assertFalse(scope[key]) + + def test_authority_and_artifact_gates_are_closed(self) -> None: + self.assertFalse(any(self.data["authorizations"].values())) + self.assertFalse(self.data["decision"]["target_profile_allowed"]) + self.assertFalse(self.data["decision"]["target_artifact_build_allowed"]) + self.assertFalse(self.data["decision"]["device_action_allowed"]) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10az.py b/tests/test_retroarch_phase10az.py new file mode 100644 index 0000000..f521424 --- /dev/null +++ b/tests/test_retroarch_phase10az.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Tracked fail-closed guardrails for Phase-1.0AZ.""" + +from __future__ import annotations + +import argparse +import json +from pathlib import Path +import unittest + + +PARSER = argparse.ArgumentParser() +PARSER.add_argument("--root", type=Path, required=True) +ROOT = PARSER.parse_args().root + + +class Phase10AZGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + path = ROOT / "manifests/retroarch/phase-1.0az-host-av-source.json" + cls.data = json.loads(path.read_text(encoding="utf-8")) + + def test_terminal_contract_is_strict(self) -> None: + protocol = self.data["protocol"] + self.assertEqual(protocol["magic"], "CHD10AV1") + self.assertEqual(protocol["d14_numeric_stage"], 30) + self.assertFalse(protocol["d12_terminal"]) + for key, value in protocol.items(): + if key not in ("magic", "d14_numeric_stage", "d12_terminal"): + self.assertTrue(value, key) + + def test_scope_remains_host_only(self) -> None: + self.assertTrue(self.data["decision"]["host_source_structure_complete"]) + self.assertFalse(any(self.data["scope"].values())) + self.assertFalse(any(self.data["authorizations"].values())) + + def test_remote_and_next_gate_remain_closed(self) -> None: + self.assertTrue(self.data["source_bindings"]["remote_push_verified"]) + decision = self.data["decision"] + self.assertTrue(decision["remote_source_binding_complete"]) + self.assertTrue(decision["target_profile_reassessment_allowed"]) + self.assertFalse(decision["target_artifact_build_allowed"]) + self.assertFalse(decision["device_action_allowed"]) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10b.py b/tests/test_retroarch_phase10b.py new file mode 100644 index 0000000..248fe4e --- /dev/null +++ b/tests/test_retroarch_phase10b.py @@ -0,0 +1,216 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Host-only policy tests for the Phase-1.0B smoke candidate.""" + +from __future__ import annotations + +import argparse +import importlib.util +from pathlib import Path +import sys +from types import ModuleType +from typing import Callable + + +def load_module(name: str, path: Path) -> ModuleType: + spec = importlib.util.spec_from_file_location(name, path) + if spec is None or spec.loader is None: + raise RuntimeError(f"could not load {path}") + module = importlib.util.module_from_spec(spec) + sys.modules[name] = module + spec.loader.exec_module(module) + return module + + +def require(condition: bool, message: str) -> None: + if not condition: + raise RuntimeError(message) + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + root = args.root.resolve() + validator = load_module( + "phase10b_validator", root / "tools/validate_retroarch_phase10b.py" + ) + cases: list[tuple[str, Callable[[], None]]] = [] + + def case(name: str) -> Callable[[Callable[[], None]], Callable[[], None]]: + def register(function: Callable[[], None]) -> Callable[[], None]: + cases.append((name, function)) + return function + + return register + + def profile(**changes: object) -> dict[str, object]: + value: dict[str, object] = { + "disabled_features": sorted(validator.DISABLED_FEATURES), + "persistent_writes_allowed": False, + "content_required": False, + "config_required": False, + "networking": False, + "autoload": False, + "automatic_retry": False, + } + value.update(changes) + return value + + def route(**changes: object) -> dict[str, object]: + value: dict[str, object] = { + "write": False, + "create": False, + "append": False, + "truncate": False, + "rename": False, + "unlink": False, + "mkdir": False, + "retry": False, + } + value.update(changes) + return value + + @case("01 profile is closed") + def _() -> None: + require(validator.profile_is_closed(profile()), "closed profile rejected") + + @case("02 networking is rejected") + def _() -> None: + require(not validator.profile_is_closed(profile(networking=True)), "network accepted") + + @case("03 content is not required") + def _() -> None: + require(not validator.profile_is_closed(profile(content_required=True)), "content accepted") + + @case("04 config is not required") + def _() -> None: + require(not validator.profile_is_closed(profile(config_required=True)), "config accepted") + + @case("05 automatic retry is rejected") + def _() -> None: + require(not validator.profile_is_closed(profile(automatic_retry=True)), "retry accepted") + + @case("06 autoload is rejected") + def _() -> None: + require(not validator.profile_is_closed(profile(autoload=True)), "autoload accepted") + + @case("07 write is rejected") + def _() -> None: + require(not validator.route_is_read_only(route(write=True)), "write accepted") + + @case("08 create is rejected") + def _() -> None: + require(not validator.route_is_read_only(route(create=True)), "create accepted") + + @case("09 append is rejected") + def _() -> None: + require(not validator.route_is_read_only(route(append=True)), "append accepted") + + @case("10 truncate is rejected") + def _() -> None: + require(not validator.route_is_read_only(route(truncate=True)), "truncate accepted") + + @case("11 rename is rejected") + def _() -> None: + require(not validator.route_is_read_only(route(rename=True)), "rename accepted") + + @case("12 unlink is rejected") + def _() -> None: + require(not validator.route_is_read_only(route(unlink=True)), "unlink accepted") + + @case("13 mkdir is rejected") + def _() -> None: + require(not validator.route_is_read_only(route(mkdir=True)), "mkdir accepted") + + @case("14 route retry is rejected") + def _() -> None: + require(not validator.route_is_read_only(route(retry=True)), "route retry accepted") + + @case("15 RX/R/RW headers pass") + def _() -> None: + headers = [ + {"type": "LOAD", "flags": "R E"}, + {"type": "LOAD", "flags": "R"}, + {"type": "LOAD", "flags": "RW"}, + ] + require(validator.program_headers_are_wx_closed(headers), "W^X headers rejected") + + @case("16 RWE header fails") + def _() -> None: + headers = [ + {"type": "LOAD", "flags": "RWE"}, + {"type": "LOAD", "flags": "RW"}, + {"type": "LOAD", "flags": "RW"}, + ] + require(not validator.program_headers_are_wx_closed(headers), "RWE accepted") + + @case("17 SceNet import fails") + def _() -> None: + require(not validator.imports_are_closed(["sceNetSocket"]), "SceNet accepted") + + @case("18 GNM import fails") + def _() -> None: + require(not validator.imports_are_closed(["sceGnmSubmitCommandBuffers"]), "GNM accepted") + + @case("19 module loading fails") + def _() -> None: + require(not validator.imports_are_closed(["sceKernelLoadStartModule"]), "module loader accepted") + + @case("20 rumble fails") + def _() -> None: + require(not validator.imports_are_closed(["scePadSetVibration"]), "rumble accepted") + + @case("21 lightbar fails") + def _() -> None: + require(not validator.imports_are_closed(["scePadSetLightBar"]), "lightbar accepted") + + @case("22 socket import fails") + def _() -> None: + require(not validator.imports_are_closed(["socket"]), "socket accepted") + + @case("23 expected imports pass") + def _() -> None: + require( + validator.imports_are_closed( + ["sceVideoOutOpen", "scePadOpen", "sceAudioOutOpen", "_Exit"] + ), + "expected imports rejected", + ) + + @case("24 runtime contract pins both deadlines") + def _() -> None: + text = (root / "docs/retroarch/phase-1.0b-smoke-candidate-design.md").read_text() + require("60,000 ms" in text and "3,600" in text, "deadlines absent") + + @case("25 exit risk remains explicit") + def _() -> None: + text = (root / "docs/retroarch/phase-1.0b-runtime-and-exit-contract.md").read_text() + require("unproven" in text.lower() and "_Exit" in text, "exit risk hidden") + + @case("26 host evidence is not hardware proof") + def _() -> None: + text = (root / "docs/retroarch/phase-1.0b-smoke-candidate-design.md").read_text() + require( + "no host test or static audit" in text.lower() + and "is hardware evidence" in text.lower(), + "hardware disclaimer absent", + ) + + failures: list[str] = [] + for name, function in cases: + try: + function() + except Exception as error: # noqa: BLE001 - isolated reporting + failures.append(f"{name}: {error}") + if len(cases) != 26: + failures.append(f"expected 26 cases, found {len(cases)}") + if failures: + print("\n".join(failures), file=sys.stderr) + return 1 + print("26 Phase 1.0B guardrails passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_retroarch_phase10ba.py b/tests/test_retroarch_phase10ba.py new file mode 100644 index 0000000..350dd37 --- /dev/null +++ b/tests/test_retroarch_phase10ba.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Fail-closed Phase-1.0BA guardrails.""" + +import argparse +import json +from pathlib import Path +import unittest + +parser = argparse.ArgumentParser() +parser.add_argument("--root", type=Path, required=True) +root = parser.parse_args().root + + +class Phase10BAGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.data = json.loads((root / "manifests/retroarch/phase-1.0ba-target-profile-callsite-audit.json").read_text(encoding="utf-8")) + + def test_callsite_is_post_cleanup_pre_exit(self) -> None: + call = self.data["callsite_contract"] + for key in ("rarch_main_return_required", "s15_before_d14", + "d14_before_process_exit", "early_return_initialized_mask_rejected"): + self.assertTrue(call[key]) + self.assertFalse(call["terminal_emit_result_changes_exit_code"]) + + def test_profile_is_launcher_independent(self) -> None: + profile = self.data["profile_delta"] + self.assertEqual(profile["profile_name"], "launch-canary") + self.assertTrue(profile["inherits_write_diag_behavior"]) + self.assertTrue(profile["same_elf_for_both_launch_arms"]) + self.assertTrue(profile["adds_only_av_target_define"]) + for key in ("launcher_define_forbidden", "new_socket_or_address_forbidden", + "runner_activation_forbidden"): + self.assertTrue(profile[key]) + + def test_build_and_device_gates_stay_closed(self) -> None: + auth = self.data["authorizations"] + self.assertTrue(auth["target_source_patch_authorized"]) + self.assertTrue(auth["target_profile_addition_authorized"]) + for key, value in auth.items(): + if key not in ("target_source_patch_authorized", "target_profile_addition_authorized"): + self.assertFalse(value, key) + self.assertFalse(self.data["decision"]["cross_build_allowed"]) + self.assertFalse(self.data["decision"]["device_action_allowed"]) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10bb.py b/tests/test_retroarch_phase10bb.py new file mode 100644 index 0000000..508d6b8 --- /dev/null +++ b/tests/test_retroarch_phase10bb.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Phase-1.0BB repository guardrails.""" + +import argparse +import json +from pathlib import Path +import unittest + +parser = argparse.ArgumentParser() +parser.add_argument("--root", type=Path, required=True) +root = parser.parse_args().root + + +class Phase10BBGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.data = json.loads((root / "manifests/retroarch/phase-1.0bb-source-only-launch-canary-profile.json").read_text(encoding="utf-8")) + + def test_source_properties(self) -> None: + props = self.data["source_properties"] + for key in ("post_s15_pre_exit_call", "distinct_launch_canary_profile", + "inherits_write_diag", "launcher_independent_payload"): + self.assertTrue(props[key]) + self.assertFalse(props["new_network_or_device_path"]) + self.assertFalse(props["runner_activation"]) + + def test_only_prerequisite_audit_is_open(self) -> None: + auth = self.data["authorizations"] + self.assertTrue(auth["build_prerequisite_audit_authorized"]) + for key, value in auth.items(): + if key != "build_prerequisite_audit_authorized": + self.assertFalse(value, key) + self.assertFalse(self.data["decision"]["cross_build_allowed"]) + self.assertFalse(self.data["decision"]["artifact_allowed"]) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10bc.py b/tests/test_retroarch_phase10bc.py new file mode 100644 index 0000000..15e83b9 --- /dev/null +++ b/tests/test_retroarch_phase10bc.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Phase-1.0BC prerequisite gate tests.""" + +import argparse +import hashlib +import json +from pathlib import Path +import unittest + +parser = argparse.ArgumentParser() +parser.add_argument("--root", type=Path, required=True) +root = parser.parse_args().root + + +class Phase10BCGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.data = json.loads((root / "manifests/retroarch/phase-1.0bc-cross-build-prerequisite-audit.json").read_text(encoding="utf-8")) + + def test_parent_binding(self) -> None: + parent = root / "manifests/retroarch/phase-1.0bb-source-only-launch-canary-profile.json" + self.assertEqual(hashlib.sha256(parent.read_bytes()).hexdigest(), + self.data["source_bindings"]["phase10bb_manifest_sha256"]) + + def test_missing_prerequisites_are_explicit(self) -> None: + observed = self.data["observed_prerequisites"] + for key in ("wsl_cmake_present", "wsl_ninja_present", + "exact_sdl_archive_present", "launch_canary_elf_present", + "launch_canary_map_present"): + self.assertFalse(observed[key]) + + def test_only_materializer_source_is_open(self) -> None: + auth = self.data["authorizations"] + self.assertTrue(auth["materializer_source_authorized"]) + for key, value in auth.items(): + if key != "materializer_source_authorized": + self.assertFalse(value, key) + decision = self.data["decision"] + self.assertFalse(decision["prerequisites_complete"]) + self.assertFalse(decision["unknown_archive_reuse_allowed"]) + self.assertFalse(decision["cross_build_allowed"]) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10bd.py b/tests/test_retroarch_phase10bd.py new file mode 100644 index 0000000..5376957 --- /dev/null +++ b/tests/test_retroarch_phase10bd.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Phase-1.0BD fail-closed repository tests.""" + +import argparse +import json +from pathlib import Path +import unittest + +parser = argparse.ArgumentParser() +parser.add_argument("--root", type=Path, required=True) +root = parser.parse_args().root + + +class Phase10BDGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.data = json.loads((root / "manifests/retroarch/phase-1.0bd-dormant-sdl-materializer-policy.json").read_text(encoding="utf-8")) + + def test_policy_is_dormant(self) -> None: + props = self.data["policy_properties"] + for key in ("exact_identity_validation", "clean_absent_output_required", + "immutable_plan", "exact_three_file_patch_scope"): + self.assertTrue(props[key]) + for key in ("process_adapter_present", "filesystem_adapter_present", + "network_present", "cli_present", "retroarch_build_included", + "device_action_included"): + self.assertFalse(props[key]) + + def test_only_injected_adapter_source_is_open(self) -> None: + auth = self.data["authorizations"] + self.assertTrue(auth["injected_adapter_source_authorized"]) + for key, value in auth.items(): + if key != "injected_adapter_source_authorized": + self.assertFalse(value, key) + self.assertFalse(self.data["decision"]["materialization_allowed"]) + self.assertFalse(self.data["decision"]["cross_build_allowed"]) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10be.py b/tests/test_retroarch_phase10be.py new file mode 100644 index 0000000..05eb5bc --- /dev/null +++ b/tests/test_retroarch_phase10be.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Phase-1.0BE repository guardrails.""" + +import argparse, hashlib, json, subprocess +from pathlib import Path +import unittest + +parser = argparse.ArgumentParser() +parser.add_argument("--root", type=Path, required=True) +parser.add_argument("--retroarch-root", type=Path) +args = parser.parse_args() + + +class Phase10BEGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.data = json.loads((args.root / "manifests/retroarch/phase-1.0be-fake-only-sdl-materializer.json").read_text(encoding="utf-8")) + + def test_parent_and_optional_source_binding(self) -> None: + parent = args.root / "manifests/retroarch/phase-1.0bd-dormant-sdl-materializer-policy.json" + bind = self.data["source_bindings"] + self.assertEqual(hashlib.sha256(parent.read_bytes()).hexdigest(), bind["phase10bd_manifest_sha256"]) + if args.retroarch_root: + def git(*values: str) -> str: + return subprocess.run(["git", *values], cwd=args.retroarch_root, + check=True, capture_output=True, text=True).stdout.strip() + self.assertEqual(git("rev-parse", "HEAD"), bind["retroarch_commit"]) + self.assertEqual(git("rev-parse", bind["remote_ref"]), bind["retroarch_commit"]) + paths = {"makefile": "Makefile.ps5", + "adapter": "tools/phase10be_sdl_materializer_fake_adapter.py", + "validator": "tools/validate_ps5_phase10be.py", + "tests": "tests/test_ps5_phase10be_fake_materializer.py", + "source_doc": "docs/ps5-phase10be-fake-sdl-materializer.md"} + for key, source in paths.items(): + self.assertEqual(git("rev-parse", f'{bind["retroarch_commit"]}:{source}'), + bind[f"{key}_blob"]) + + def test_fake_only_and_live_gates(self) -> None: + props = self.data["properties"] + self.assertTrue(props["exact_fake_type_only"]) + self.assertEqual(props["ordered_operations"], 9) + for key in ("process_capability", "filesystem_capability", + "network_capability", "retroarch_build_capability", + "device_capability"): + self.assertFalse(props[key]) + auth = self.data["authorizations"] + self.assertTrue(auth["live_adapter_source_reassessment_authorized"]) + for key, value in auth.items(): + if key != "live_adapter_source_reassessment_authorized": + self.assertFalse(value, key) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10bf.py b/tests/test_retroarch_phase10bf.py new file mode 100644 index 0000000..9de0038 --- /dev/null +++ b/tests/test_retroarch_phase10bf.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Phase-1.0BF live-adapter boundary tests.""" + +import argparse, hashlib, json +from pathlib import Path +import unittest + +parser = argparse.ArgumentParser() +parser.add_argument("--root", type=Path, required=True) +root = parser.parse_args().root + + +class Phase10BFGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.data = json.loads((root / "manifests/retroarch/phase-1.0bf-live-sdl-adapter-boundary-audit.json").read_text(encoding="utf-8")) + + def test_parent_binding(self) -> None: + parent = root / "manifests/retroarch/phase-1.0be-fake-only-sdl-materializer.json" + self.assertEqual(hashlib.sha256(parent.read_bytes()).hexdigest(), + self.data["source_bindings"]["phase10be_manifest_sha256"]) + + def test_adapter_contract_is_fail_closed(self) -> None: + contract = self.data["adapter_contract"] + for key, value in contract.items(): + if key in ("automatic_retry", "automatic_cleanup", "network_capability", + "retroarch_build_capability", "device_capability"): + self.assertFalse(value, key) + else: + self.assertTrue(value, key) + + def test_only_dormant_source_is_open(self) -> None: + auth = self.data["authorizations"] + self.assertTrue(auth["dormant_live_adapter_source_authorized"]) + for key, value in auth.items(): + if key != "dormant_live_adapter_source_authorized": + self.assertFalse(value, key) + self.assertFalse(self.data["decision"]["tool_install_allowed"]) + self.assertFalse(self.data["decision"]["materialization_allowed"]) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10bg.py b/tests/test_retroarch_phase10bg.py new file mode 100644 index 0000000..d72b1cd --- /dev/null +++ b/tests/test_retroarch_phase10bg.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Phase-1.0BG repository guardrails.""" + +import argparse, hashlib, json, subprocess +from pathlib import Path +import unittest + +parser = argparse.ArgumentParser() +parser.add_argument("--root", type=Path, required=True) +parser.add_argument("--retroarch-root", type=Path) +args = parser.parse_args() + + +class Phase10BGGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.data = json.loads((args.root / "manifests/retroarch/phase-1.0bg-dormant-sdl-request-compiler.json").read_text(encoding="utf-8")) + + def test_parent_and_optional_source(self) -> None: + bind = self.data["source_bindings"] + parent = args.root / "manifests/retroarch/phase-1.0bf-live-sdl-adapter-boundary-audit.json" + self.assertEqual(hashlib.sha256(parent.read_bytes()).hexdigest(), bind["phase10bf_manifest_sha256"]) + if args.retroarch_root: + def git(*values: str) -> str: + return subprocess.run(["git", *values], cwd=args.retroarch_root, + check=True, capture_output=True, text=True).stdout.strip() + self.assertEqual(git("rev-parse", "HEAD"), bind["retroarch_commit"]) + self.assertEqual(git("rev-parse", bind["remote_ref"]), bind["retroarch_commit"]) + + def test_graph_and_authority(self) -> None: + graph = self.data["request_graph"] + self.assertEqual(graph["operation_count"], 8) + self.assertTrue(graph["fixed_argv"]) + self.assertTrue(graph["canonical_output_containment"]) + for key in ("shell_present", "package_manager_present", "retroarch_build_present", + "network_present", "device_action_present", "executor_present"): + self.assertFalse(graph[key]) + auth = self.data["authorizations"] + self.assertTrue(auth["bounded_executor_source_authorized"]) + for key, value in auth.items(): + if key != "bounded_executor_source_authorized": + self.assertFalse(value, key) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10bh.py b/tests/test_retroarch_phase10bh.py new file mode 100644 index 0000000..e59f29c --- /dev/null +++ b/tests/test_retroarch_phase10bh.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Phase-1.0BH repository guardrails.""" + +import argparse, hashlib, json +from pathlib import Path +import unittest + +parser = argparse.ArgumentParser() +parser.add_argument("--root", type=Path, required=True) +root = parser.parse_args().root + + +class Phase10BHGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.data = json.loads((root / "manifests/retroarch/phase-1.0bh-bounded-sdl-executor.json").read_text(encoding="utf-8")) + + def test_parent_and_executor(self) -> None: + parent = root / "manifests/retroarch/phase-1.0bg-dormant-sdl-request-compiler.json" + self.assertEqual(hashlib.sha256(parent.read_bytes()).hexdigest(), + self.data["source_bindings"]["phase10bg_manifest_sha256"]) + executor = self.data["executor"] + for key in ("attempt_consumed_before_preflight", "exact_request_order", + "exit_output_utf8_semantics_bounded", "success_reuse_forbidden", + "failure_reuse_forbidden", "exact_fake_facades_only"): + self.assertTrue(executor[key]) + for key in ("real_process_present", "real_filesystem_present", "cli_present"): + self.assertFalse(executor[key]) + + def test_only_reassessment_is_open(self) -> None: + auth = self.data["authorizations"] + self.assertTrue(auth["real_facade_reassessment_authorized"]) + for key, value in auth.items(): + if key != "real_facade_reassessment_authorized": + self.assertFalse(value, key) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10bi.py b/tests/test_retroarch_phase10bi.py new file mode 100644 index 0000000..ade9687 --- /dev/null +++ b/tests/test_retroarch_phase10bi.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Phase-1.0BI audit guardrails.""" + +import argparse, hashlib, json +from pathlib import Path +import unittest + +parser = argparse.ArgumentParser(); parser.add_argument("--root", type=Path, required=True) +root = parser.parse_args().root + + +class Phase10BIGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.data = json.loads((root / "manifests/retroarch/phase-1.0bi-real-facade-and-tool-install-audit.json").read_text(encoding="utf-8")) + + def test_parent_and_facade_contract(self) -> None: + parent = root / "manifests/retroarch/phase-1.0bh-bounded-sdl-executor.json" + self.assertEqual(hashlib.sha256(parent.read_bytes()).hexdigest(), self.data["source_bindings"]["phase10bh_manifest_sha256"]) + contract = self.data["facade_contract"] + for key, value in contract.items(): + if key in ("cli_present", "retry_present", "cleanup_present", "network_present", "device_present"): + self.assertFalse(value, key) + else: + self.assertTrue(value, key) + + def test_narrow_authority(self) -> None: + auth = self.data["authorizations"] + self.assertTrue(auth["real_facade_source_authorized"]) + self.assertTrue(auth["exact_package_install_authorized"]) + for key, value in auth.items(): + if key not in ("real_facade_source_authorized", "exact_package_install_authorized"): + self.assertFalse(value, key) + self.assertFalse(self.data["decision"]["materialization_allowed"]) + + +if __name__ == "__main__": unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10bj.py b/tests/test_retroarch_phase10bj.py new file mode 100644 index 0000000..73bb927 --- /dev/null +++ b/tests/test_retroarch_phase10bj.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Phase-1.0BJ installation-result guardrails.""" + +import argparse +import hashlib +import json +from pathlib import Path +import unittest + +parser = argparse.ArgumentParser() +parser.add_argument("--root", type=Path, required=True) +root = parser.parse_args().root + + +class Phase10BJGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.data = json.loads((root / "manifests/retroarch/phase-1.0bj-exact-host-tool-install-result.json").read_text(encoding="utf-8")) + + def test_parent_and_exact_installation(self) -> None: + parent = root / "manifests/retroarch/phase-1.0bi-real-facade-and-tool-install-audit.json" + self.assertEqual(hashlib.sha256(parent.read_bytes()).hexdigest(), self.data["source_bindings"]["phase10bi_manifest_sha256"]) + install = self.data["installation"] + self.assertEqual(install["attempts"], 1) + self.assertTrue(install["completed"]) + self.assertEqual(install["cmake_version"], "4.2.3-2ubuntu2") + self.assertEqual(install["ninja_build_version"], "1.13.2-1") + for key in ("cmake_executable_sha256", "ninja_executable_sha256"): + self.assertRegex(install[key], r"^[0-9a-f]{64}$") + bindings = self.data["source_bindings"] + self.assertRegex(bindings["retroarch_commit"], r"^[0-9a-f]{40}$") + for key in ("real_facade_sha256", "static_validator_sha256", "source_test_sha256"): + self.assertRegex(bindings[key], r"^[0-9a-f]{64}$") + + def test_every_execution_path_remains_closed(self) -> None: + auth = self.data["authorizations"] + self.assertTrue(auth["real_facade_source_authorized"]) + for key, value in auth.items(): + if key != "real_facade_source_authorized": + self.assertFalse(value, key) + self.assertFalse(self.data["decision"]["materialization_allowed"]) + self.assertTrue(self.data["decision"]["dormant_real_facade_source_present"]) + self.assertFalse(self.data["decision"]["real_facade_invoked"]) + self.assertFalse(self.data["decision"]["cross_build_allowed"]) + self.assertFalse(self.data["decision"]["device_action_allowed"]) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10bk.py b/tests/test_retroarch_phase10bk.py new file mode 100644 index 0000000..fe0921e --- /dev/null +++ b/tests/test_retroarch_phase10bk.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Phase-1.0BK exact host-fixture gate guardrails.""" + +import argparse +import hashlib +import json +from pathlib import Path +import unittest + +parser = argparse.ArgumentParser() +parser.add_argument("--root", type=Path, required=True) +root = parser.parse_args().root + + +class Phase10BKGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.data = json.loads((root / "manifests/retroarch/phase-1.0bk-bounded-real-facade-host-fixture-gate.json").read_text(encoding="utf-8")) + + def test_exact_parent_and_runtime(self) -> None: + parent = root / "manifests/retroarch/phase-1.0bj-exact-host-tool-install-result.json" + bindings = self.data["source_bindings"] + self.assertEqual(hashlib.sha256(parent.read_bytes()).hexdigest(), bindings["phase10bj_manifest_sha256"]) + self.assertEqual(bindings["python_path"], "/usr/bin/python3.14") + self.assertRegex(bindings["python_sha256"], r"^[0-9a-f]{64}$") + + def test_four_exact_bounded_fixtures(self) -> None: + fixtures = self.data["fixture_contract"] + self.assertEqual(fixtures["suite_attempts"], 1) + self.assertEqual(fixtures["per_fixture_attempts"], 1) + self.assertEqual(set(fixtures) - {"suite_attempts", "per_fixture_attempts"}, {"success", "nonzero", "overflow", "timeout"}) + for name in ("success", "nonzero", "overflow", "timeout"): + fixture = fixtures[name] + self.assertEqual(fixture["argv"][0], "/usr/bin/python3.14") + self.assertLessEqual(fixture["timeout_seconds"], 2) + self.assertEqual(fixture["output_limit"], 64) + + def test_authority_stays_host_fixture_only(self) -> None: + auth = self.data["authorizations"] + self.assertTrue(auth["exact_host_fixture_suite_authorized"]) + for key, value in auth.items(): + if key != "exact_host_fixture_suite_authorized": + self.assertFalse(value, key) + self.assertFalse(self.data["decision"]["materialization_allowed"]) + self.assertFalse(self.data["decision"]["device_action_allowed"]) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10bl.py b/tests/test_retroarch_phase10bl.py new file mode 100644 index 0000000..ef9e740 --- /dev/null +++ b/tests/test_retroarch_phase10bl.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Phase-1.0BL consumed-attempt and new-gate guardrails.""" + +import argparse +import hashlib +import json +from pathlib import Path +import unittest + +parser = argparse.ArgumentParser() +parser.add_argument("--root", type=Path, required=True) +root = parser.parse_args().root + + +class Phase10BLGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.data = json.loads((root / "manifests/retroarch/phase-1.0bl-fixture-import-failure-and-new-gate.json").read_text(encoding="utf-8")) + + def test_parent_and_corrected_source_bound(self) -> None: + parent = root / "manifests/retroarch/phase-1.0bk-bounded-real-facade-host-fixture-gate.json" + bindings = self.data["source_bindings"] + self.assertEqual(hashlib.sha256(parent.read_bytes()).hexdigest(), bindings["phase10bk_manifest_sha256"]) + self.assertRegex(bindings["corrected_runner_sha256"], r"^[0-9a-f]{64}$") + self.assertRegex(bindings["retroarch_commit"], r"^[0-9a-f]{40}$") + + def test_bk_was_consumed_before_any_fixture(self) -> None: + result = self.data["consumed_bk_attempt"] + self.assertEqual(result["suite_attempts"], 1) + self.assertEqual(result["exit_code"], 1) + self.assertFalse(result["main_entered"]) + self.assertFalse(result["facade_constructed"]) + self.assertEqual(result["fixture_processes_started"], 0) + for key in ("materializer_invoked", "sdl_build_invoked", "retroarch_build_invoked", "device_action_invoked"): + self.assertFalse(result[key], key) + + def test_new_permission_is_one_exact_nonautomatic_suite(self) -> None: + gate = self.data["new_bl_gate"] + self.assertEqual(gate["suite_attempts_authorized"], 1) + self.assertEqual(len(gate["fixtures"]), 4) + self.assertTrue(gate["contract_identical_to_phase10bk"]) + self.assertFalse(gate["automatic_retry"]) + auth = self.data["authorizations"] + self.assertTrue(auth["exact_corrected_host_fixture_suite_authorized"]) + for key, value in auth.items(): + if key != "exact_corrected_host_fixture_suite_authorized": + self.assertFalse(value, key) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10bm.py b/tests/test_retroarch_phase10bm.py new file mode 100644 index 0000000..7f99086 --- /dev/null +++ b/tests/test_retroarch_phase10bm.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Phase-1.0BM real-facade fixture-result guardrails.""" + +import argparse +import hashlib +import json +from pathlib import Path +import unittest + +parser = argparse.ArgumentParser() +parser.add_argument("--root", type=Path, required=True) +root = parser.parse_args().root + + +class Phase10BMGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.data = json.loads((root / "manifests/retroarch/phase-1.0bm-real-facade-fixture-result.json").read_text(encoding="utf-8")) + + def test_parent_and_exact_success_result(self) -> None: + parent = root / "manifests/retroarch/phase-1.0bl-fixture-import-failure-and-new-gate.json" + self.assertEqual(hashlib.sha256(parent.read_bytes()).hexdigest(), self.data["source_bindings"]["phase10bl_manifest_sha256"]) + result = self.data["result"] + self.assertEqual((result["suite_attempts"], result["fixture_attempts"], result["exit_code"]), (1, 4, 0)) + self.assertEqual((result["success_returncode"], result["success_output_hex"]), (0, "4f4b0a")) + self.assertEqual((result["nonzero_returncode"], result["nonzero_output_hex"]), (7, "45370a")) + self.assertEqual(result["overflow_error"], "output limit exceeded: fixture_overflow") + self.assertEqual(result["timeout_error"], "process timeout: fixture_timeout") + + def test_only_dormant_composition_source_is_open(self) -> None: + for key in ("materializer_invoked", "sdl_build_invoked", "retroarch_build_invoked", "device_action_invoked"): + self.assertFalse(self.data["result"][key], key) + auth = self.data["authorizations"] + self.assertTrue(auth["dormant_real_composition_source_authorized"]) + for key, value in auth.items(): + if key != "dormant_real_composition_source_authorized": + self.assertFalse(value, key) + self.assertFalse(self.data["decision"]["materialization_allowed"]) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10bn.py b/tests/test_retroarch_phase10bn.py new file mode 100644 index 0000000..1cab910 --- /dev/null +++ b/tests/test_retroarch_phase10bn.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Phase-1.0BN materializer preflight guardrails.""" + +import argparse +import hashlib +import json +from pathlib import Path +import unittest + +parser = argparse.ArgumentParser() +parser.add_argument("--root", type=Path, required=True) +root = parser.parse_args().root + + +class Phase10BNGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.data = json.loads((root / "manifests/retroarch/phase-1.0bn-materializer-preflight-patch-chain-audit.json").read_text(encoding="utf-8")) + + def test_parent_and_exact_inputs(self) -> None: + parent = root / "manifests/retroarch/phase-1.0bm-real-facade-fixture-result.json" + self.assertEqual(hashlib.sha256(parent.read_bytes()).hexdigest(), self.data["source_bindings"]["phase10bm_manifest_sha256"]) + for key in ("smoke_patch_sha256", "early_diag_patch_sha256", "composition_sha256"): + self.assertRegex(self.data["source_bindings"][key], r"^[0-9a-f]{64}$") + + def test_preflight_captures_real_blocker(self) -> None: + preflight = self.data["preflight"] + for key in ("sdl_commit_exact", "sdl_source_clean", "stage_absent", "build_absent", "archive_absent", "smoke_patch_applies_to_clean_source"): + self.assertTrue(preflight[key], key) + for key in ("early_diag_patch_applies_directly_to_clean_source", "current_request_graph_has_complete_patch_chain", "current_changed_file_set_complete", "current_cmake_contract_complete"): + self.assertFalse(preflight[key], key) + self.assertFalse(self.data["decision"]["preflight_passed"]) + + def test_only_offline_remediation_is_open(self) -> None: + auth = self.data["authorizations"] + self.assertTrue(auth["temporary_offline_patch_rebase_authorized"]) + self.assertTrue(auth["corrected_contract_source_authorized"]) + for key, value in auth.items(): + if key not in ("temporary_offline_patch_rebase_authorized", "corrected_contract_source_authorized"): + self.assertFalse(value, key) + self.assertFalse(self.data["decision"]["materialization_allowed"]) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10bo.py b/tests/test_retroarch_phase10bo.py new file mode 100644 index 0000000..39c9b7b --- /dev/null +++ b/tests/test_retroarch_phase10bo.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Phase-1.0BO patch-chain remediation guardrails.""" + +import argparse +import hashlib +import json +from pathlib import Path +import unittest + +parser = argparse.ArgumentParser() +parser.add_argument("--root", type=Path, required=True) +root = parser.parse_args().root + + +class Phase10BOGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.data = json.loads((root / "manifests/retroarch/phase-1.0bo-patch-chain-remediation-result.json").read_text(encoding="utf-8")) + + def test_parent_and_corrected_sources_bound(self) -> None: + parent = root / "manifests/retroarch/phase-1.0bn-materializer-preflight-patch-chain-audit.json" + self.assertEqual(hashlib.sha256(parent.read_bytes()).hexdigest(), self.data["source_bindings"]["phase10bn_manifest_sha256"]) + for key in ("policy_sha256", "request_graph_sha256", "composition_sha256"): + self.assertRegex(self.data["source_bindings"][key], r"^[0-9a-f]{64}$") + + def test_rebase_and_contract_are_complete(self) -> None: + evidence = self.data["rebase_evidence"] + for key in ("windows_checkout_crlf_rejected", "first_wsl_checkout_incomplete_rejected", "smoke_patch_dry_run_passed", "smoke_patch_applied", "early_diag_patch_dry_run_passed_after_smoke", "early_diag_patch_applied_after_smoke", "final_diff_check_passed"): + self.assertTrue(evidence[key], key) + self.assertEqual(len(evidence["exact_changed_files"]), 4) + contract = self.data["corrected_contract"] + self.assertEqual(contract["request_count"], 10) + for key, value in contract.items(): + if key in ("retroarch_build_included", "device_action_included"): + self.assertFalse(value, key) + elif key != "request_count": + self.assertTrue(value, key) + + def test_only_runner_source_is_open(self) -> None: + auth = self.data["authorizations"] + self.assertTrue(auth["one_shot_runner_source_authorized"]) + for key, value in auth.items(): + if key != "one_shot_runner_source_authorized": + self.assertFalse(value, key) + self.assertFalse(self.data["decision"]["materialization_allowed"]) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10bp.py b/tests/test_retroarch_phase10bp.py new file mode 100644 index 0000000..da20bea --- /dev/null +++ b/tests/test_retroarch_phase10bp.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Phase-1.0BP one-shot materialization gate guardrails.""" + +import argparse +import hashlib +import json +from pathlib import Path +import unittest + +parser = argparse.ArgumentParser() +parser.add_argument("--root", type=Path, required=True) +root = parser.parse_args().root + + +class Phase10BPGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.data = json.loads((root / "manifests/retroarch/phase-1.0bp-one-shot-sdl-materialization-gate.json").read_text(encoding="utf-8")) + + def test_parent_runner_and_exact_preflight(self) -> None: + parent = root / "manifests/retroarch/phase-1.0bo-patch-chain-remediation-result.json" + self.assertEqual(hashlib.sha256(parent.read_bytes()).hexdigest(), self.data["source_bindings"]["phase10bo_manifest_sha256"]) + self.assertRegex(self.data["source_bindings"]["runner_sha256"], r"^[0-9a-f]{64}$") + preflight = self.data["preflight"] + for key in ("sdl_source_clean", "stage_absent", "build_absent", "archive_absent"): + self.assertTrue(preflight[key], key) + self.assertEqual(preflight["request_count"], 10) + self.assertEqual(len(preflight["exact_output_paths"]), 3) + + def test_invocation_is_one_shot_without_cleanup(self) -> None: + invocation = self.data["invocation"] + self.assertEqual(invocation["attempts_authorized"], 1) + self.assertFalse(invocation["automatic_retry"]) + self.assertFalse(invocation["cleanup_authorized"]) + self.assertEqual(invocation["argv"][0], "/usr/bin/python3.14") + self.assertIn("run_once", invocation["argv"][2]) + + def test_authority_ends_at_sdl_archive(self) -> None: + auth = self.data["authorizations"] + for key in ("exact_materializer_invocation_authorized", "cmake_invocation_authorized", "sdl_build_authorized"): + self.assertTrue(auth[key], key) + for key, value in auth.items(): + if key not in ("exact_materializer_invocation_authorized", "cmake_invocation_authorized", "sdl_build_authorized"): + self.assertFalse(value, key) + self.assertFalse(self.data["decision"]["retroarch_cross_build_allowed"]) + self.assertFalse(self.data["decision"]["device_action_allowed"]) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10bq.py b/tests/test_retroarch_phase10bq.py new file mode 100644 index 0000000..7ff0146 --- /dev/null +++ b/tests/test_retroarch_phase10bq.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Phase-1.0BQ invocation-failure guardrails.""" + +import argparse +import hashlib +import json +from pathlib import Path +import unittest + +parser = argparse.ArgumentParser() +parser.add_argument("--root", type=Path, required=True) +root = parser.parse_args().root + + +class Phase10BQGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.data = json.loads((root / "manifests/retroarch/phase-1.0bq-inline-invocation-failure.json").read_text(encoding="utf-8")) + + def test_parent_and_pre_python_stop(self) -> None: + parent = root / "manifests/retroarch/phase-1.0bp-one-shot-sdl-materialization-gate.json" + self.assertEqual(hashlib.sha256(parent.read_bytes()).hexdigest(), self.data["source_bindings"]["phase10bp_manifest_sha256"]) + result = self.data["consumed_attempt"] + self.assertEqual((result["attempts"], result["exit_code"]), (1, 1)) + for key in ("python_started", "runner_imported", "runner_consumed", "retroarch_build_invoked", "device_action_invoked"): + self.assertFalse(result[key], key) + self.assertEqual(result["materializer_requests_started"], 0) + for key in ("stage_absent_after", "build_absent_after", "archive_absent_after"): + self.assertTrue(result[key], key) + + def test_only_entrypoint_source_is_open(self) -> None: + auth = self.data["authorizations"] + self.assertTrue(auth["exact_entrypoint_source_authorized"]) + for key, value in auth.items(): + if key != "exact_entrypoint_source_authorized": + self.assertFalse(value, key) + self.assertFalse(self.data["decision"]["materialization_allowed"]) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10br.py b/tests/test_retroarch_phase10br.py new file mode 100644 index 0000000..b08baa7 --- /dev/null +++ b/tests/test_retroarch_phase10br.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Phase-1.0BR exact script-entrypoint gate guardrails.""" + +import argparse +import hashlib +import json +from pathlib import Path +import unittest + +parser = argparse.ArgumentParser() +parser.add_argument("--root", type=Path, required=True) +root = parser.parse_args().root + + +class Phase10BRGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.data = json.loads((root / "manifests/retroarch/phase-1.0br-script-entrypoint-one-shot-gate.json").read_text(encoding="utf-8")) + + def test_parent_entrypoint_and_absent_outputs(self) -> None: + parent = root / "manifests/retroarch/phase-1.0bq-inline-invocation-failure.json" + self.assertEqual(hashlib.sha256(parent.read_bytes()).hexdigest(), self.data["source_bindings"]["phase10bq_manifest_sha256"]) + self.assertRegex(self.data["source_bindings"]["entrypoint_sha256"], r"^[0-9a-f]{64}$") + preflight = self.data["preflight"] + self.assertTrue(preflight["entrypoint_argument_free"]) + self.assertEqual(preflight["run_once_call_count"], 1) + for key in ("stage_absent", "build_absent", "archive_absent"): + self.assertTrue(preflight[key], key) + + def test_new_gate_is_exactly_one_nonautomatic_attempt(self) -> None: + invocation = self.data["invocation"] + self.assertEqual(invocation["argv"], ["/usr/bin/python3.14", "tools/phase10bq_sdl_materializer_entrypoint.py"]) + self.assertEqual(invocation["attempts_authorized"], 1) + self.assertFalse(invocation["automatic_retry"]) + self.assertFalse(invocation["cleanup_authorized"]) + self.assertFalse(self.data["decision"]["new_attempt_is_automatic_retry"]) + + def test_authority_stops_before_retroarch_and_device(self) -> None: + auth = self.data["authorizations"] + for key in ("exact_script_entrypoint_invocation_authorized", "materializer_invocation_authorized", "cmake_invocation_authorized", "sdl_build_authorized"): + self.assertTrue(auth[key], key) + for key, value in auth.items(): + if key not in ("exact_script_entrypoint_invocation_authorized", "materializer_invocation_authorized", "cmake_invocation_authorized", "sdl_build_authorized"): + self.assertFalse(value, key) + self.assertFalse(self.data["decision"]["device_action_allowed"]) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10bs.py b/tests/test_retroarch_phase10bs.py new file mode 100644 index 0000000..4fa9f81 --- /dev/null +++ b/tests/test_retroarch_phase10bs.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Phase-1.0BS status-timeout guardrails.""" + +import argparse +import hashlib +import json +from pathlib import Path +import unittest + +parser = argparse.ArgumentParser() +parser.add_argument("--root", type=Path, required=True) +root = parser.parse_args().root + + +class Phase10BSGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.data = json.loads((root / "manifests/retroarch/phase-1.0bs-read-only-status-timeout.json").read_text(encoding="utf-8")) + + def test_parent_and_exact_stop(self) -> None: + parent = root / "manifests/retroarch/phase-1.0br-script-entrypoint-one-shot-gate.json" + self.assertEqual(hashlib.sha256(parent.read_bytes()).hexdigest(), self.data["source_bindings"]["phase10br_manifest_sha256"]) + result = self.data["consumed_attempt"] + self.assertEqual(result["completed_operations"], ["verify_sdl_commit"]) + self.assertEqual(result["failed_operation"], "verify_sdl_clean") + self.assertTrue(result["process_group_terminated"]) + for key in ("stage_created", "cmake_invoked", "sdl_build_invoked", "retroarch_build_invoked", "device_action_invoked"): + self.assertFalse(result[key], key) + for key in ("stage_absent_after", "build_absent_after", "archive_absent_after"): + self.assertTrue(result[key], key) + + def test_correction_and_authority_are_narrow(self) -> None: + correction = self.data["correction_contract"] + self.assertEqual(correction["operation"], "verify_sdl_clean") + self.assertTrue(correction["argv_unchanged"]) + self.assertTrue(correction["output_limit_unchanged"]) + self.assertEqual(correction["new_timeout_seconds"], 120) + auth = self.data["authorizations"] + self.assertTrue(auth["status_timeout_source_correction_authorized"]) + for key, value in auth.items(): + if key != "status_timeout_source_correction_authorized": + self.assertFalse(value, key) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10bt.py b/tests/test_retroarch_phase10bt.py new file mode 100644 index 0000000..71c6d0a --- /dev/null +++ b/tests/test_retroarch_phase10bt.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Phase-1.0BT corrected-timeout gate guardrails.""" + +import argparse +import hashlib +import json +from pathlib import Path +import unittest + +parser = argparse.ArgumentParser() +parser.add_argument("--root", type=Path, required=True) +root = parser.parse_args().root + + +class Phase10BTGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.data = json.loads((root / "manifests/retroarch/phase-1.0bt-corrected-timeout-one-shot-gate.json").read_text(encoding="utf-8")) + + def test_parent_and_only_timeout_changed(self) -> None: + parent = root / "manifests/retroarch/phase-1.0bs-read-only-status-timeout.json" + self.assertEqual(hashlib.sha256(parent.read_bytes()).hexdigest(), self.data["source_bindings"]["phase10bs_manifest_sha256"]) + correction = self.data["correction"] + self.assertEqual((correction["operation"], correction["timeout_seconds"], correction["output_limit"]), ("verify_sdl_clean", 120, 4096)) + self.assertTrue(correction["argv_unchanged"]) + self.assertTrue(correction["all_other_requests_unchanged"]) + + def test_one_shot_scope(self) -> None: + for value in self.data["preflight"].values(): + self.assertTrue(value) + invocation = self.data["invocation"] + self.assertEqual(invocation["attempts_authorized"], 1) + self.assertFalse(invocation["automatic_retry"]) + self.assertFalse(invocation["cleanup_authorized"]) + auth = self.data["authorizations"] + for key, value in auth.items(): + if key in ("exact_script_entrypoint_invocation_authorized", "materializer_invocation_authorized", "cmake_invocation_authorized", "sdl_build_authorized"): + self.assertTrue(value, key) + else: + self.assertFalse(value, key) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10bu.py b/tests/test_retroarch_phase10bu.py new file mode 100644 index 0000000..3111a28 --- /dev/null +++ b/tests/test_retroarch_phase10bu.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Phase-1.0BU Windows-Git fixture gate guardrails.""" + +import argparse +import hashlib +import json +from pathlib import Path +import unittest + +parser = argparse.ArgumentParser() +parser.add_argument("--root", type=Path, required=True) +root = parser.parse_args().root + + +class Phase10BUGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.data = json.loads((root / "manifests/retroarch/phase-1.0bu-wsl-git-status-timeout-and-windows-git-fixture-gate.json").read_text(encoding="utf-8")) + + def test_parent_and_second_bounded_stop(self) -> None: + parent = root / "manifests/retroarch/phase-1.0bt-corrected-timeout-one-shot-gate.json" + self.assertEqual(hashlib.sha256(parent.read_bytes()).hexdigest(), self.data["source_bindings"]["phase10bt_manifest_sha256"]) + result = self.data["consumed_attempt"] + self.assertEqual(result["failed_operation"], "verify_sdl_clean") + self.assertEqual(result["timeout_seconds"], 120) + self.assertTrue(result["process_group_terminated"]) + for key in ("stage_absent_after", "build_absent_after", "archive_absent_after"): + self.assertTrue(result[key], key) + + def test_fixture_is_two_exact_read_only_commands(self) -> None: + fixture = self.data["fixture"] + self.assertEqual(fixture["attempts_authorized"], 1) + self.assertEqual(len(fixture["commands"]), 2) + for command in fixture["commands"]: + self.assertEqual(command[0], "/mnt/c/Program Files/Git/cmd/git.exe") + self.assertNotIn("worktree", command) + self.assertEqual(fixture["commands"][0][-2:], ["rev-parse", "HEAD"]) + self.assertEqual(fixture["commands"][1][-2:], ["status", "--porcelain"]) + self.assertFalse(fixture["automatic_retry"]) + + def test_only_read_only_fixture_is_open(self) -> None: + auth = self.data["authorizations"] + self.assertTrue(auth["exact_windows_git_read_only_fixture_authorized"]) + for key, value in auth.items(): + if key != "exact_windows_git_read_only_fixture_authorized": + self.assertFalse(value, key) + self.assertFalse(self.data["decision"]["materialization_allowed"]) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10bv.py b/tests/test_retroarch_phase10bv.py new file mode 100644 index 0000000..9946226 --- /dev/null +++ b/tests/test_retroarch_phase10bv.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Phase-1.0BV request-correction gate guardrails.""" + +import argparse +import hashlib +import json +from pathlib import Path +import unittest + +parser = argparse.ArgumentParser() +parser.add_argument("--root", type=Path, required=True) +root = parser.parse_args().root + + +class Phase10BVGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.data = json.loads((root / "manifests/retroarch/phase-1.0bv-windows-git-fixture-result-and-request-correction-gate.json").read_text(encoding="utf-8")) + + def test_parent_and_fixture_result_are_exact(self) -> None: + parent = root / "manifests/retroarch/phase-1.0bu-wsl-git-status-timeout-and-windows-git-fixture-gate.json" + self.assertEqual(hashlib.sha256(parent.read_bytes()).hexdigest(), self.data["source_bindings"]["phase10bu_manifest_sha256"]) + result = self.data["fixture_result"] + self.assertEqual(result["attempts"], 1) + self.assertEqual(result["observed_commit"], "0baf4ac49382b537ba449901b5b6d0d189bb1fbb") + self.assertEqual(result["porcelain_status"], "") + self.assertEqual(result["rev_parse_exit_code"], 0) + self.assertEqual(result["status_exit_code"], 0) + for key in ("worktree_created", "patch_invoked", "cmake_invoked", "sdl_build_invoked", "retroarch_build_invoked", "device_action_invoked"): + self.assertFalse(result[key], key) + + def test_correction_is_exact_and_offline(self) -> None: + correction = self.data["authorized_source_correction"] + self.assertEqual(correction["read_only_git_executable"], "/mnt/c/Program Files/Git/cmd/git.exe") + self.assertEqual(correction["read_only_git_source_path"], "C:/Projects/chimera-gfx/work/upstream/SDL") + self.assertEqual(correction["read_only_timeout_seconds"], 30) + self.assertEqual(correction["read_only_output_limit"], 4096) + self.assertEqual(correction["detached_stage_timeout_seconds"], 900) + self.assertTrue(correction["hash_bind_windows_git_in_runner"]) + self.assertTrue(correction["detached_stage_add_quiet"]) + self.assertFalse(correction["automatic_retry"]) + + def test_only_source_change_is_open(self) -> None: + auth = self.data["authorizations"] + self.assertTrue(auth["request_graph_source_change_authorized"]) + for key, value in auth.items(): + if key != "request_graph_source_change_authorized": + self.assertFalse(value, key) + self.assertFalse(self.data["decision"]["materialization_allowed"]) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10bw.py b/tests/test_retroarch_phase10bw.py new file mode 100644 index 0000000..a4c31a0 --- /dev/null +++ b/tests/test_retroarch_phase10bw.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Phase-1.0BW corrected materializer one-shot guardrails.""" + +import argparse +import hashlib +import json +from pathlib import Path +import unittest + +parser = argparse.ArgumentParser() +parser.add_argument("--root", type=Path, required=True) +root = parser.parse_args().root + + +class Phase10BWGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.data = json.loads((root / "manifests/retroarch/phase-1.0bw-corrected-sdl-materializer-one-shot-gate.json").read_text(encoding="utf-8")) + + def test_parent_and_sources_are_exactly_bound(self) -> None: + parent = root / "manifests/retroarch/phase-1.0bv-windows-git-fixture-result-and-request-correction-gate.json" + self.assertEqual(hashlib.sha256(parent.read_bytes()).hexdigest(), self.data["source_bindings"]["phase10bv_manifest_sha256"]) + bindings = self.data["source_bindings"] + self.assertEqual(bindings["retroarch_commit"], "3974ee13ddac828109b3f558067378be3515f10b") + for key, value in bindings.items(): + if key.endswith("_sha256"): + self.assertRegex(value, r"^[0-9a-f]{64}$", key) + + def test_one_shot_is_exact_and_nonretrying(self) -> None: + one_shot = self.data["one_shot"] + self.assertEqual(one_shot["attempts_authorized"], 1) + self.assertEqual(one_shot["argv"], [ + "/usr/bin/python3.14", + "tools/phase10bq_sdl_materializer_entrypoint.py"]) + self.assertEqual(one_shot["environment"], { + "PYTHONDONTWRITEBYTECODE": "1"}) + self.assertFalse(one_shot["automatic_retry"]) + self.assertFalse(one_shot["cleanup_on_failure_authorized"]) + self.assertFalse(one_shot["output_path_reuse_authorized"]) + + def test_scope_ends_at_offline_sdl_archive(self) -> None: + auth = self.data["authorizations"] + for key in ("materializer_invocation_authorized", + "detached_sdl_stage_authorized", + "offline_patch_application_authorized", + "cmake_invocation_authorized", + "sdl_build_authorized"): + self.assertTrue(auth[key], key) + for key in ("retroarch_target_build_authorized", + "network_access_authorized", + "ps5_connection_authorized", + "device_transfer_authorized", + "device_execution_authorized"): + self.assertFalse(auth[key], key) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10bx.py b/tests/test_retroarch_phase10bx.py new file mode 100644 index 0000000..a04a34f --- /dev/null +++ b/tests/test_retroarch_phase10bx.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Phase-1.0BX read-only stage-fixture guardrails.""" + +import argparse +import hashlib +import json +from pathlib import Path +import unittest + +parser = argparse.ArgumentParser() +parser.add_argument("--root", type=Path, required=True) +root = parser.parse_args().root + + +class Phase10BXGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.data = json.loads((root / "manifests/retroarch/phase-1.0bx-changed-files-timeout-and-read-only-fixture-gate.json").read_text(encoding="utf-8")) + + def test_parent_and_consumed_stop(self) -> None: + parent = root / "manifests/retroarch/phase-1.0bw-corrected-sdl-materializer-one-shot-gate.json" + self.assertEqual(hashlib.sha256(parent.read_bytes()).hexdigest(), self.data["source_bindings"]["phase10bw_manifest_sha256"]) + result = self.data["consumed_attempt"] + self.assertEqual(result["failed_operation"], "verify_changed_files") + self.assertEqual(result["timeout_seconds"], 10) + self.assertTrue(result["process_group_terminated"]) + self.assertTrue(result["stage_exists_after"]) + self.assertTrue(result["build_absent_after"]) + self.assertTrue(result["archive_absent_after"]) + + def test_fixture_is_one_exact_read_only_command(self) -> None: + fixture = self.data["fixture"] + self.assertEqual(fixture["attempts_authorized"], 1) + self.assertEqual(fixture["argv"][-2:], ["diff", "--name-only"]) + self.assertEqual(fixture["timeout_seconds"], 30) + self.assertEqual(len(fixture["expected_changed_files"]), 4) + self.assertFalse(fixture["automatic_retry"]) + + def test_only_fixture_is_open(self) -> None: + auth = self.data["authorizations"] + self.assertTrue(auth["exact_windows_git_read_only_fixture_authorized"]) + for key, value in auth.items(): + if key != "exact_windows_git_read_only_fixture_authorized": + self.assertFalse(value, key) + self.assertFalse(self.data["decision"]["cleanup_allowed"]) + self.assertFalse(self.data["decision"]["materialization_allowed"]) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10by.py b/tests/test_retroarch_phase10by.py new file mode 100644 index 0000000..03719a8 --- /dev/null +++ b/tests/test_retroarch_phase10by.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Phase-1.0BY exact file-hash fixture guardrails.""" + +import argparse +import hashlib +import json +from pathlib import Path +import unittest + +parser = argparse.ArgumentParser() +parser.add_argument("--root", type=Path, required=True) +root = parser.parse_args().root + + +class Phase10BYGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.data = json.loads((root / "manifests/retroarch/phase-1.0by-windows-git-stage-fixture-failure-and-file-hash-gate.json").read_text(encoding="utf-8")) + + def test_parent_and_consumed_fixture(self) -> None: + parent = root / "manifests/retroarch/phase-1.0bx-changed-files-timeout-and-read-only-fixture-gate.json" + self.assertEqual(hashlib.sha256(parent.read_bytes()).hexdigest(), self.data["source_bindings"]["phase10bx_manifest_sha256"]) + result = self.data["consumed_fixture"] + self.assertEqual(result["attempts"], 1) + self.assertEqual(result["exit_code"], 1) + self.assertEqual(result["failure"], "fatal: not a git repository: (NULL)") + for key in ("stage_mutated", "cleanup_invoked", "cmake_invoked", + "sdl_build_invoked", "device_action_invoked"): + self.assertFalse(result[key], key) + + def test_fixture_is_four_exact_files_only(self) -> None: + fixture = self.data["file_hash_fixture"] + self.assertEqual(fixture["attempts_authorized"], 1) + self.assertEqual(len(fixture["sha256_paths"]), 4) + self.assertTrue(fixture["read_git_indirection"]) + self.assertFalse(fixture["directory_enumeration_authorized"]) + self.assertFalse(fixture["git_invocation_authorized"]) + self.assertFalse(fixture["automatic_retry"]) + + def test_only_file_hash_fixture_is_open(self) -> None: + auth = self.data["authorizations"] + self.assertTrue(auth["exact_file_hash_fixture_authorized"]) + for key, value in auth.items(): + if key != "exact_file_hash_fixture_authorized": + self.assertFalse(value, key) + self.assertFalse(self.data["decision"]["cleanup_allowed"]) + self.assertFalse(self.data["decision"]["materialization_allowed"]) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10bz.py b/tests/test_retroarch_phase10bz.py new file mode 100644 index 0000000..9aac2c8 --- /dev/null +++ b/tests/test_retroarch_phase10bz.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Phase-1.0BZ exact failed-stage cleanup guardrails.""" + +import argparse +import hashlib +import json +from pathlib import Path +import unittest + +parser = argparse.ArgumentParser() +parser.add_argument("--root", type=Path, required=True) +root = parser.parse_args().root + + +class Phase10BZGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.data = json.loads((root / "manifests/retroarch/phase-1.0bz-exact-sdl-stage-cleanup-gate.json").read_text(encoding="utf-8")) + + def test_parent_and_four_hashes_are_exact(self) -> None: + parent = root / "manifests/retroarch/phase-1.0by-windows-git-stage-fixture-failure-and-file-hash-gate.json" + self.assertEqual(hashlib.sha256(parent.read_bytes()).hexdigest(), self.data["source_bindings"]["phase10by_manifest_sha256"]) + hashes = self.data["fixture_result"]["patch_target_sha256"] + self.assertEqual(len(hashes), 4) + for value in hashes.values(): + self.assertRegex(value, r"^[0-9a-f]{64}$") + + def test_cleanup_is_one_exact_worktree_only(self) -> None: + cleanup = self.data["cleanup"] + self.assertEqual(cleanup["attempts_authorized"], 1) + self.assertEqual(cleanup["argv"][-3:], [ + "remove", "--force", + "/mnt/c/Projects/chimera-retroarch-phase10az/build/phase10bd/sdl-source"]) + self.assertEqual(cleanup["timeout_seconds"], 900) + self.assertFalse(cleanup["automatic_retry"]) + + def test_only_exact_cleanup_is_open(self) -> None: + auth = self.data["authorizations"] + self.assertTrue(auth["exact_failed_stage_cleanup_authorized"]) + for key, value in auth.items(): + if key != "exact_failed_stage_cleanup_authorized": + self.assertFalse(value, key) + self.assertFalse(self.data["decision"]["source_change_allowed"]) + self.assertFalse(self.data["decision"]["materialization_allowed"]) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10ca.py b/tests/test_retroarch_phase10ca.py new file mode 100644 index 0000000..c11c7fd --- /dev/null +++ b/tests/test_retroarch_phase10ca.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Phase-1.0CA hash-verifier correction guardrails.""" + +import argparse +import hashlib +import json +from pathlib import Path +import unittest + +parser = argparse.ArgumentParser() +parser.add_argument("--root", type=Path, required=True) +root = parser.parse_args().root + + +class Phase10CAGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.data = json.loads((root / "manifests/retroarch/phase-1.0ca-cleanup-result-and-hash-verifier-correction-gate.json").read_text(encoding="utf-8")) + + def test_parent_and_cleanup_result(self) -> None: + parent = root / "manifests/retroarch/phase-1.0bz-exact-sdl-stage-cleanup-gate.json" + self.assertEqual(hashlib.sha256(parent.read_bytes()).hexdigest(), self.data["source_bindings"]["phase10bz_manifest_sha256"]) + result = self.data["cleanup_result"] + self.assertEqual(result["attempts"], 1) + self.assertEqual(result["exit_code"], 0) + self.assertTrue(result["stage_absent_after"]) + self.assertTrue(result["worktree_metadata_absent_after"]) + self.assertTrue(result["source_clean_after"]) + self.assertFalse(result["other_worktrees_removed"]) + + def test_hash_verifier_is_exact(self) -> None: + correction = self.data["authorized_source_correction"] + self.assertEqual(correction["operation"], "verify_patch_target_hashes") + self.assertEqual(correction["executable"], "/usr/bin/sha256sum") + self.assertRegex(correction["executable_sha256"], r"^[0-9a-f]{64}$") + self.assertEqual(len(correction["expected_sha256"]), 4) + self.assertEqual(correction["timeout_seconds"], 30) + self.assertTrue(correction["hash_bind_executable_in_runner"]) + self.assertFalse(correction["automatic_retry"]) + + def test_only_source_change_is_open(self) -> None: + auth = self.data["authorizations"] + self.assertTrue(auth["request_graph_source_change_authorized"]) + for key, value in auth.items(): + if key != "request_graph_source_change_authorized": + self.assertFalse(value, key) + self.assertFalse(self.data["decision"]["materialization_allowed"]) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10cb.py b/tests/test_retroarch_phase10cb.py new file mode 100644 index 0000000..ee5f0a5 --- /dev/null +++ b/tests/test_retroarch_phase10cb.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Phase-1.0CB hash-verified materializer gate guardrails.""" + +import argparse +import hashlib +import json +from pathlib import Path +import unittest + +parser = argparse.ArgumentParser() +parser.add_argument("--root", type=Path, required=True) +root = parser.parse_args().root + + +class Phase10CBGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.data = json.loads((root / "manifests/retroarch/phase-1.0cb-hash-verified-sdl-materializer-one-shot-gate.json").read_text(encoding="utf-8")) + + def test_parent_and_sources_are_bound(self) -> None: + parent = root / "manifests/retroarch/phase-1.0ca-cleanup-result-and-hash-verifier-correction-gate.json" + self.assertEqual(hashlib.sha256(parent.read_bytes()).hexdigest(), self.data["source_bindings"]["phase10ca_manifest_sha256"]) + bindings = self.data["source_bindings"] + self.assertEqual(bindings["retroarch_commit"], "a4b1fb0ca125b23c6a22069f2937f69b055b6cab") + for key, value in bindings.items(): + if key.endswith("_sha256"): + self.assertRegex(value, r"^[0-9a-f]{64}$", key) + + def test_one_shot_is_exact_and_nonretrying(self) -> None: + one_shot = self.data["one_shot"] + self.assertEqual(one_shot["attempts_authorized"], 1) + self.assertEqual(one_shot["argv"][-1], + "tools/phase10bq_sdl_materializer_entrypoint.py") + self.assertFalse(one_shot["automatic_retry"]) + self.assertFalse(one_shot["cleanup_on_failure_authorized"]) + + def test_scope_ends_at_offline_sdl(self) -> None: + auth = self.data["authorizations"] + for key in ("output_path_reuse_authorized", + "materializer_invocation_authorized", + "detached_sdl_stage_authorized", + "offline_patch_application_authorized", + "cmake_invocation_authorized", + "sdl_build_authorized"): + self.assertTrue(auth[key], key) + for key in ("retroarch_target_build_authorized", + "network_access_authorized", + "ps5_connection_authorized", + "device_transfer_authorized", + "device_execution_authorized"): + self.assertFalse(auth[key], key) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10cc.py b/tests/test_retroarch_phase10cc.py new file mode 100644 index 0000000..8940e9e --- /dev/null +++ b/tests/test_retroarch_phase10cc.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Phase-1.0CC exact derived-output cleanup guardrails.""" + +import argparse +import hashlib +import json +from pathlib import Path +import unittest + +parser = argparse.ArgumentParser() +parser.add_argument("--root", type=Path, required=True) +root = parser.parse_args().root + + +class Phase10CCGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.data = json.loads((root / "manifests/retroarch/phase-1.0cc-cmake-timeout-and-exact-output-cleanup-gate.json").read_text(encoding="utf-8")) + + def test_parent_and_bounded_stop(self) -> None: + parent = root / "manifests/retroarch/phase-1.0cb-hash-verified-sdl-materializer-one-shot-gate.json" + self.assertEqual(hashlib.sha256(parent.read_bytes()).hexdigest(), self.data["source_bindings"]["phase10cb_manifest_sha256"]) + result = self.data["consumed_attempt"] + self.assertEqual(result["failed_operation"], "cmake_configure_sdl_only") + self.assertEqual(result["timeout_seconds"], 120) + self.assertTrue(result["process_group_terminated"]) + self.assertTrue(result["cmake_cache_absent_after"]) + self.assertTrue(result["archive_absent_after"]) + self.assertRegex(result["configure_log_sha256"], r"^[0-9a-f]{64}$") + + def test_cleanup_is_exact_and_nonretrying(self) -> None: + cleanup = self.data["cleanup"] + self.assertEqual(cleanup["attempts_authorized"], 1) + self.assertTrue(cleanup["stage"].endswith( + "/build/phase10bd/sdl-source")) + self.assertTrue(cleanup["build"].endswith( + "/build/phase10bd/sdl-build")) + self.assertTrue(cleanup["stage_git_worktree_remove_force"]) + self.assertTrue(cleanup["build_recursive_remove_after_absolute_containment_check"]) + self.assertFalse(cleanup["other_worktree_cleanup_authorized"]) + self.assertFalse(cleanup["automatic_retry"]) + + def test_only_exact_cleanup_is_open(self) -> None: + auth = self.data["authorizations"] + self.assertTrue(auth["exact_derived_output_cleanup_authorized"]) + for key, value in auth.items(): + if key != "exact_derived_output_cleanup_authorized": + self.assertFalse(value, key) + self.assertFalse(self.data["decision"]["source_change_allowed"]) + self.assertFalse(self.data["decision"]["materialization_allowed"]) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10cd.py b/tests/test_retroarch_phase10cd.py new file mode 100644 index 0000000..745167f --- /dev/null +++ b/tests/test_retroarch_phase10cd.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Phase-1.0CD configure-timeout correction guardrails.""" + +import argparse +import hashlib +import json +from pathlib import Path +import unittest + +parser = argparse.ArgumentParser() +parser.add_argument("--root", type=Path, required=True) +root = parser.parse_args().root + + +class Phase10CDGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.data = json.loads((root / "manifests/retroarch/phase-1.0cd-cleanup-result-and-configure-timeout-correction-gate.json").read_text(encoding="utf-8")) + + def test_parent_and_cleanup_result(self) -> None: + parent = root / "manifests/retroarch/phase-1.0cc-cmake-timeout-and-exact-output-cleanup-gate.json" + self.assertEqual(hashlib.sha256(parent.read_bytes()).hexdigest(), self.data["source_bindings"]["phase10cc_manifest_sha256"]) + result = self.data["cleanup_result"] + self.assertEqual(result["attempts"], 1) + self.assertEqual(result["exit_code"], 0) + for key in ("stage_absent_after", "build_absent_after", + "worktree_metadata_absent_after", "source_clean_after"): + self.assertTrue(result[key], key) + self.assertFalse(result["other_worktrees_removed"]) + + def test_only_configure_timeout_may_change(self) -> None: + correction = self.data["authorized_source_correction"] + self.assertEqual(correction["operation"], "cmake_configure_sdl_only") + self.assertEqual(correction["old_timeout_seconds"], 120) + self.assertEqual(correction["new_timeout_seconds"], 600) + self.assertFalse(correction["argv_change_authorized"]) + self.assertFalse(correction["output_limit_change_authorized"]) + self.assertFalse(correction["build_timeout_change_authorized"]) + self.assertFalse(correction["automatic_retry"]) + + def test_only_source_change_is_open(self) -> None: + auth = self.data["authorizations"] + self.assertTrue(auth["request_graph_source_change_authorized"]) + for key, value in auth.items(): + if key != "request_graph_source_change_authorized": + self.assertFalse(value, key) + self.assertFalse(self.data["decision"]["materialization_allowed"]) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10ce.py b/tests/test_retroarch_phase10ce.py new file mode 100644 index 0000000..6257f7c --- /dev/null +++ b/tests/test_retroarch_phase10ce.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Phase-1.0CE extended-configure one-shot guardrails.""" + +import argparse +import hashlib +import json +from pathlib import Path +import unittest + +parser = argparse.ArgumentParser() +parser.add_argument("--root", type=Path, required=True) +root = parser.parse_args().root + + +class Phase10CEGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.data = json.loads((root / "manifests/retroarch/phase-1.0ce-extended-configure-sdl-materializer-one-shot-gate.json").read_text(encoding="utf-8")) + + def test_parent_and_sources_are_exact(self) -> None: + parent = root / "manifests/retroarch/phase-1.0cd-cleanup-result-and-configure-timeout-correction-gate.json" + self.assertEqual(hashlib.sha256(parent.read_bytes()).hexdigest(), self.data["source_bindings"]["phase10cd_manifest_sha256"]) + self.assertEqual(self.data["source_bindings"]["retroarch_commit"], "e936a1fc36058a5df40cef1a805e6ae571fc2f4a") + + def test_exact_bounded_one_shot(self) -> None: + one = self.data["one_shot"] + self.assertEqual(one["attempts_authorized"], 1) + self.assertEqual(one["configure_timeout_seconds"], 600) + self.assertEqual(one["build_timeout_seconds"], 600) + self.assertFalse(one["automatic_retry"]) + self.assertFalse(one["cleanup_on_failure_authorized"]) + + def test_no_target_or_device_scope(self) -> None: + auth = self.data["authorizations"] + self.assertTrue(auth["materializer_invocation_authorized"]) + self.assertTrue(auth["sdl_build_authorized"]) + for key in ("retroarch_target_build_authorized", "network_access_authorized", + "ps5_connection_authorized", "device_transfer_authorized", + "device_execution_authorized"): + self.assertFalse(auth[key], key) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10cf.py b/tests/test_retroarch_phase10cf.py new file mode 100644 index 0000000..c403073 --- /dev/null +++ b/tests/test_retroarch_phase10cf.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Phase-1.0CF repeated-timeout cleanup guardrails.""" + +import argparse +import hashlib +import json +from pathlib import Path +import unittest + +parser = argparse.ArgumentParser() +parser.add_argument("--root", type=Path, required=True) +root = parser.parse_args().root + + +class Phase10CFGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.data = json.loads((root / "manifests/retroarch/phase-1.0cf-repeated-configure-timeout-and-cleanup-gate.json").read_text(encoding="utf-8")) + + def test_parent_and_repeated_stop(self) -> None: + parent = root / "manifests/retroarch/phase-1.0ce-extended-configure-sdl-materializer-one-shot-gate.json" + self.assertEqual(hashlib.sha256(parent.read_bytes()).hexdigest(), self.data["source_bindings"]["phase10ce_manifest_sha256"]) + result = self.data["consumed_attempt"] + self.assertEqual(result["timeout_seconds"], 600) + self.assertTrue(result["all_preconfigure_operations_completed"]) + self.assertTrue(result["successful_try_compile_observed"]) + self.assertFalse(result["sdl_build_invoked"]) + + def test_only_cleanup_is_open(self) -> None: + cleanup = self.data["cleanup"] + self.assertEqual(cleanup["attempts_authorized"], 1) + self.assertFalse(cleanup["automatic_retry"]) + auth = self.data["authorizations"] + self.assertTrue(auth["exact_derived_output_cleanup_authorized"]) + for key, value in auth.items(): + if key != "exact_derived_output_cleanup_authorized": + self.assertFalse(value, key) + + def test_mounted_root_is_rejected(self) -> None: + self.assertFalse(self.data["decision"]["mounted_buildroot_suitable"]) + self.assertFalse(self.data["decision"]["materialization_allowed"]) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10cg.py b/tests/test_retroarch_phase10cg.py new file mode 100644 index 0000000..f91c3ba --- /dev/null +++ b/tests/test_retroarch_phase10cg.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Phase-1.0CG native WSL design guardrails.""" + +import argparse +import hashlib +import json +from pathlib import Path +import unittest + +parser = argparse.ArgumentParser() +parser.add_argument("--root", type=Path, required=True) +root = parser.parse_args().root + + +class Phase10CGGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.data = json.loads((root / "manifests/retroarch/phase-1.0cg-native-wsl-sdl-materializer-design-gate.json").read_text(encoding="utf-8")) + + def test_parent_and_native_root_are_bound(self) -> None: + parent = root / "manifests/retroarch/phase-1.0cf-repeated-configure-timeout-and-cleanup-gate.json" + self.assertEqual(hashlib.sha256(parent.read_bytes()).hexdigest(), self.data["source_bindings"]["phase10cf_manifest_sha256"]) + observation = self.data["native_root_observation"] + self.assertEqual(observation["path"], "/tmp/chimera-gfx-phase10ch") + self.assertEqual(observation["parent_filesystem"], "tmpfs") + self.assertTrue(observation["root_absent"]) + + def test_design_has_no_export_or_target(self) -> None: + design = self.data["design_contract"] + for key in ("native_stage", "native_build", "native_archive"): + self.assertTrue(design[key].startswith( + "/tmp/chimera-gfx-phase10ch/"), key) + self.assertFalse(design["archive_export_included"]) + self.assertFalse(design["retroarch_build_included"]) + self.assertFalse(design["device_action_included"]) + + def test_only_design_and_fake_tests_are_open(self) -> None: + auth = self.data["authorizations"] + self.assertTrue(auth["native_wsl_source_design_authorized"]) + self.assertTrue(auth["fake_host_tests_authorized"]) + for key, value in auth.items(): + if key not in ("native_wsl_source_design_authorized", + "fake_host_tests_authorized"): + self.assertFalse(value, key) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10ch.py b/tests/test_retroarch_phase10ch.py new file mode 100644 index 0000000..4908909 --- /dev/null +++ b/tests/test_retroarch_phase10ch.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Phase-1.0CH native materializer gate guardrails.""" + +import argparse +import hashlib +import json +from pathlib import Path +import unittest + +parser = argparse.ArgumentParser() +parser.add_argument("--root", type=Path, required=True) +root = parser.parse_args().root + + +class Phase10CHGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.data = json.loads((root / "manifests/retroarch/phase-1.0ch-native-wsl-sdl-materializer-one-shot-gate.json").read_text(encoding="utf-8")) + + def test_parent_and_source_hashes(self) -> None: + parent = root / "manifests/retroarch/phase-1.0cg-native-wsl-sdl-materializer-design-gate.json" + self.assertEqual(hashlib.sha256(parent.read_bytes()).hexdigest(), self.data["source_bindings"]["phase10cg_manifest_sha256"]) + self.assertEqual(self.data["source_bindings"]["retroarch_commit"], "ab419bb85d4ce54aa0d9f59671498957615d1811") + + def test_exact_one_shot(self) -> None: + one = self.data["one_shot"] + self.assertEqual(one["attempts_authorized"], 1) + self.assertFalse(one["automatic_retry"]) + self.assertFalse(one["cleanup_on_failure_authorized"]) + self.assertTrue(self.data["preflight"]["native_root_absent"]) + + def test_scope_stops_before_export_and_target(self) -> None: + auth = self.data["authorizations"] + self.assertTrue(auth["native_root_creation_authorized"]) + self.assertTrue(auth["sdl_build_authorized"]) + for key in ("archive_export_authorized", "retroarch_target_build_authorized", + "network_access_authorized", "ps5_connection_authorized", + "device_transfer_authorized", "device_execution_authorized"): + self.assertFalse(auth[key], key) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10ci.py b/tests/test_retroarch_phase10ci.py new file mode 100644 index 0000000..b955f44 --- /dev/null +++ b/tests/test_retroarch_phase10ci.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Phase-1.0CI native-root cleanup guardrails.""" + +import argparse +import hashlib +import json +from pathlib import Path +import unittest + +parser = argparse.ArgumentParser() +parser.add_argument("--root", type=Path, required=True) +root = parser.parse_args().root + + +class Phase10CIGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.data = json.loads((root / "manifests/retroarch/phase-1.0ci-native-sdk-bottleneck-and-cleanup-gate.json").read_text(encoding="utf-8")) + + def test_parent_and_result(self) -> None: + parent = root / "manifests/retroarch/phase-1.0ch-native-wsl-sdl-materializer-one-shot-gate.json" + self.assertEqual(hashlib.sha256(parent.read_bytes()).hexdigest(), self.data["source_bindings"]["phase10ch_manifest_sha256"]) + result = self.data["consumed_attempt"] + self.assertEqual(result["timeout_seconds"], 600) + self.assertTrue(result["all_preconfigure_operations_completed"]) + self.assertTrue(result["successful_symbol_try_compile_observed"]) + + def test_cleanup_only(self) -> None: + cleanup = self.data["cleanup"] + self.assertEqual(cleanup["exact_root"], "/tmp/chimera-gfx-phase10ch") + self.assertTrue(cleanup["git_worktree_remove_first"]) + self.assertFalse(cleanup["automatic_retry"]) + auth = self.data["authorizations"] + self.assertTrue(auth["exact_native_root_cleanup_authorized"]) + for key, value in auth.items(): + if key != "exact_native_root_cleanup_authorized": + self.assertFalse(value, key) + + def test_no_timeout_increase(self) -> None: + self.assertFalse(self.data["decision"]["timeout_increase_allowed"]) + self.assertEqual(self.data["sdk_observation"]["total_bytes"], 29406271) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10cj.py b/tests/test_retroarch_phase10cj.py new file mode 100644 index 0000000..5b7794f --- /dev/null +++ b/tests/test_retroarch_phase10cj.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Phase-1.0CJ native SDK stage design guardrails.""" + +import argparse +import hashlib +import json +from pathlib import Path +import unittest + +parser = argparse.ArgumentParser() +parser.add_argument("--root", type=Path, required=True) +root = parser.parse_args().root + + +class Phase10CJGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.data = json.loads((root / "manifests/retroarch/phase-1.0cj-native-sdk-stage-design-gate.json").read_text(encoding="utf-8")) + + def test_parent_and_full_tree_binding(self) -> None: + parent = root / "manifests/retroarch/phase-1.0ci-native-sdk-bottleneck-and-cleanup-gate.json" + self.assertEqual(hashlib.sha256(parent.read_bytes()).hexdigest(), self.data["source_bindings"]["phase10ci_manifest_sha256"]) + tree = self.data["sdk_tree"] + self.assertEqual(tree["regular_files"], 2923) + self.assertEqual(tree["symbolic_links"], 0) + self.assertRegex(tree["normalized_tar_sha256"], r"^[0-9a-f]{64}$") + + def test_design_verifies_before_configure(self) -> None: + design = self.data["design_contract"] + self.assertTrue(design["copy_exclusive"]) + self.assertTrue(design["verify_complete_tree_before_configure"]) + self.assertTrue(design["configure_uses_native_sdk_only"]) + self.assertFalse(design["archive_export_included"]) + + def test_only_source_and_fake_tests_are_open(self) -> None: + auth = self.data["authorizations"] + self.assertTrue(auth["native_sdk_stage_source_design_authorized"]) + self.assertTrue(auth["fake_host_tests_authorized"]) + for key, value in auth.items(): + if key not in ("native_sdk_stage_source_design_authorized", + "fake_host_tests_authorized"): + self.assertFalse(value, key) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10ck.py b/tests/test_retroarch_phase10ck.py new file mode 100644 index 0000000..99b107d --- /dev/null +++ b/tests/test_retroarch_phase10ck.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Phase-1.0CK native-SDK materializer guardrails.""" + +import argparse +import hashlib +import json +from pathlib import Path +import unittest + +parser = argparse.ArgumentParser() +parser.add_argument("--root", type=Path, required=True) +root = parser.parse_args().root + + +class Phase10CKGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.data = json.loads((root / "manifests/retroarch/phase-1.0ck-native-sdk-sdl-materializer-one-shot-gate.json").read_text(encoding="utf-8")) + + def test_parent_and_sources(self) -> None: + parent = root / "manifests/retroarch/phase-1.0cj-native-sdk-stage-design-gate.json" + self.assertEqual(hashlib.sha256(parent.read_bytes()).hexdigest(), self.data["source_bindings"]["phase10cj_manifest_sha256"]) + self.assertEqual(self.data["source_bindings"]["retroarch_commit"], "57f00244a374b771cddf259a70c144a1dff96457") + + def test_one_attempt_without_retry(self) -> None: + one = self.data["one_shot"] + self.assertEqual(one["attempts_authorized"], 1) + self.assertFalse(one["automatic_retry"]) + self.assertFalse(one["cleanup_on_failure_authorized"]) + self.assertTrue(self.data["preflight"]["native_root_absent"]) + + def test_scope_stops_at_native_archive(self) -> None: + auth = self.data["authorizations"] + self.assertTrue(auth["native_sdk_stage_authorized"]) + self.assertTrue(auth["sdl_build_authorized"]) + for key in ("archive_export_authorized", "retroarch_target_build_authorized", + "network_access_authorized", "ps5_connection_authorized", + "device_transfer_authorized", "device_execution_authorized"): + self.assertFalse(auth[key], key) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10cl.py b/tests/test_retroarch_phase10cl.py new file mode 100644 index 0000000..0512773 --- /dev/null +++ b/tests/test_retroarch_phase10cl.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Phase-1.0CL native cleanup guardrails.""" + +import argparse +import hashlib +import json +from pathlib import Path +import unittest + +parser = argparse.ArgumentParser() +parser.add_argument("--root", type=Path, required=True) +root = parser.parse_args().root + + +class Phase10CLGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.data = json.loads((root / "manifests/retroarch/phase-1.0cl-native-sdk-configure-progress-and-cleanup-gate.json").read_text(encoding="utf-8")) + + def test_parent_and_progress(self) -> None: + parent = root / "manifests/retroarch/phase-1.0ck-native-sdk-sdl-materializer-one-shot-gate.json" + self.assertEqual(hashlib.sha256(parent.read_bytes()).hexdigest(), self.data["source_bindings"]["phase10ck_manifest_sha256"]) + result = self.data["consumed_attempt"] + self.assertEqual(result["completed_cmake_checks"], 96) + self.assertTrue(result["native_compiler_used"]) + self.assertTrue(result["native_sysroot_used"]) + self.assertFalse(result["sdl_build_invoked"]) + + def test_cleanup_only(self) -> None: + auth = self.data["authorizations"] + self.assertTrue(auth["exact_native_root_cleanup_authorized"]) + for key, value in auth.items(): + if key != "exact_native_root_cleanup_authorized": + self.assertFalse(value, key) + self.assertFalse(self.data["cleanup"]["automatic_retry"]) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10cm.py b/tests/test_retroarch_phase10cm.py new file mode 100644 index 0000000..4768e95 --- /dev/null +++ b/tests/test_retroarch_phase10cm.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Phase-1.0CM measured timeout guardrails.""" + +import argparse +import hashlib +import json +from pathlib import Path +import unittest + +parser = argparse.ArgumentParser() +parser.add_argument("--root", type=Path, required=True) +root = parser.parse_args().root + + +class Phase10CMGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.data = json.loads((root / "manifests/retroarch/phase-1.0cm-measured-configure-timeout-correction-gate.json").read_text(encoding="utf-8")) + + def test_parent_and_measurement(self) -> None: + parent = root / "manifests/retroarch/phase-1.0cl-native-sdk-configure-progress-and-cleanup-gate.json" + self.assertEqual(hashlib.sha256(parent.read_bytes()).hexdigest(), self.data["source_bindings"]["phase10cl_manifest_sha256"]) + self.assertEqual(self.data["measurement"]["completed_checks"], 96) + self.assertEqual(self.data["measurement"]["selected_bound_seconds"], 1800) + + def test_only_timeout_source_change(self) -> None: + correction = self.data["authorized_source_correction"] + self.assertEqual(correction["old_timeout_seconds"], 600) + self.assertEqual(correction["new_timeout_seconds"], 1800) + for key in ("argv_change_authorized", "output_limit_change_authorized", + "checkout_timeout_change_authorized", + "build_timeout_change_authorized", "automatic_retry"): + self.assertFalse(correction[key], key) + auth = self.data["authorizations"] + self.assertTrue(auth["request_graph_source_change_authorized"]) + for key, value in auth.items(): + if key != "request_graph_source_change_authorized": + self.assertFalse(value, key) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10cn.py b/tests/test_retroarch_phase10cn.py new file mode 100644 index 0000000..f8e656c --- /dev/null +++ b/tests/test_retroarch_phase10cn.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Phase-1.0CN measured native build guardrails.""" + +import argparse +import hashlib +import json +from pathlib import Path +import unittest + +parser = argparse.ArgumentParser() +parser.add_argument("--root", type=Path, required=True) +root = parser.parse_args().root + + +class Phase10CNGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.data = json.loads((root / "manifests/retroarch/phase-1.0cn-measured-native-sdl-materializer-one-shot-gate.json").read_text(encoding="utf-8")) + + def test_parent_and_exact_bound(self) -> None: + parent = root / "manifests/retroarch/phase-1.0cm-measured-configure-timeout-correction-gate.json" + self.assertEqual(hashlib.sha256(parent.read_bytes()).hexdigest(), self.data["source_bindings"]["phase10cm_manifest_sha256"]) + one = self.data["one_shot"] + self.assertEqual(one["attempts_authorized"], 1) + self.assertEqual(one["configure_timeout_seconds"], 1800) + self.assertEqual(one["build_timeout_seconds"], 600) + self.assertFalse(one["automatic_retry"]) + + def test_no_export_target_or_device(self) -> None: + auth = self.data["authorizations"] + for key in ("archive_export_authorized", "retroarch_target_build_authorized", + "network_access_authorized", "ps5_connection_authorized", + "device_transfer_authorized", "device_execution_authorized"): + self.assertFalse(auth[key], key) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10co.py b/tests/test_retroarch_phase10co.py new file mode 100644 index 0000000..4a391d5 --- /dev/null +++ b/tests/test_retroarch_phase10co.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Phase-1.0CO exact cleanup guardrails.""" + +import argparse, hashlib, json, unittest +from pathlib import Path +parser=argparse.ArgumentParser(); parser.add_argument("--root",type=Path,required=True); root=parser.parse_args().root + +class Phase10COGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.data=json.loads((root/"manifests/retroarch/phase-1.0co-configure-success-build-output-limit-cleanup-gate.json").read_text(encoding="utf-8")) + def test_parent_and_result(self): + parent=root/"manifests/retroarch/phase-1.0cn-measured-native-sdl-materializer-one-shot-gate.json" + self.assertEqual(hashlib.sha256(parent.read_bytes()).hexdigest(),self.data["source_bindings"]["phase10cn_manifest_sha256"]) + result=self.data["consumed_attempt"] + self.assertEqual(result["completed_through"],"cmake_configure_sdl_only") + self.assertEqual(result["object_files_after"],46) + self.assertTrue(result["archive_absent"]) + def test_cleanup_only(self): + auth=self.data["authorizations"] + self.assertTrue(auth["exact_native_root_cleanup_authorized"]) + for key,value in auth.items(): + if key!="exact_native_root_cleanup_authorized": self.assertFalse(value,key) + +if __name__=="__main__": unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10cp.py b/tests/test_retroarch_phase10cp.py new file mode 100644 index 0000000..a210836 --- /dev/null +++ b/tests/test_retroarch_phase10cp.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +import argparse, hashlib, json, unittest +from pathlib import Path +parser=argparse.ArgumentParser(); parser.add_argument("--root",type=Path,required=True); root=parser.parse_args().root +class Phase10CPGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls): cls.data=json.loads((root/"manifests/retroarch/phase-1.0cp-build-output-limit-correction-gate.json").read_text(encoding="utf-8")) + def test_contract(self): + parent=root/"manifests/retroarch/phase-1.0co-configure-success-build-output-limit-cleanup-gate.json" + self.assertEqual(hashlib.sha256(parent.read_bytes()).hexdigest(),self.data["source_bindings"]["phase10co_manifest_sha256"]) + c=self.data["authorized_source_correction"]; self.assertEqual(c["old_output_limit"],65536); self.assertEqual(c["new_output_limit"],1048576); self.assertFalse(c["argv_change_authorized"]); self.assertFalse(c["timeout_change_authorized"]) + a=self.data["authorizations"]; self.assertTrue(a["request_graph_source_change_authorized"]) + for k,v in a.items(): + if k!="request_graph_source_change_authorized": self.assertFalse(v,k) +if __name__=="__main__": unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10cq.py b/tests/test_retroarch_phase10cq.py new file mode 100644 index 0000000..fb12c7f --- /dev/null +++ b/tests/test_retroarch_phase10cq.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +import argparse,hashlib,json,unittest +from pathlib import Path +parser=argparse.ArgumentParser();parser.add_argument("--root",type=Path,required=True);root=parser.parse_args().root +class Phase10CQGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls):cls.data=json.loads((root/"manifests/retroarch/phase-1.0cq-full-output-native-sdl-one-shot-gate.json").read_text()) + def test_gate(self): + p=root/"manifests/retroarch/phase-1.0cp-build-output-limit-correction-gate.json";self.assertEqual(hashlib.sha256(p.read_bytes()).hexdigest(),self.data["source_bindings"]["phase10cp_manifest_sha256"]) + o=self.data["one_shot"];self.assertEqual(o["attempts_authorized"],1);self.assertEqual(o["build_output_limit"],1048576);self.assertFalse(o["automatic_retry"]) + a=self.data["authorizations"] + for k in ("archive_export_authorized","retroarch_target_build_authorized","network_access_authorized","ps5_connection_authorized","device_transfer_authorized","device_execution_authorized"):self.assertFalse(a[k],k) +if __name__=="__main__":unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10cr.py b/tests/test_retroarch_phase10cr.py new file mode 100644 index 0000000..5a419cf --- /dev/null +++ b/tests/test_retroarch_phase10cr.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +import argparse,json,unittest +from pathlib import Path +parser=argparse.ArgumentParser();parser.add_argument("--root",type=Path,required=True);root=parser.parse_args().root +class Phase10CRGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls):cls.data=json.loads((root/"manifests/retroarch/phase-1.0cr-audited-sdl-archive-export-gate.json").read_text()) + def test_gate(self): + self.assertEqual(self.data["source_bindings"]["phase10cq_manifest_sha256"],"8d120585502993c93d5475ce31b1f1b48461183037542e3dc202bcadf501094f") + a=self.data["archive"];self.assertEqual(a["sha256"],"4ced0a042c0b8a70424e42a14cfe9b2c4d30a4cd73b661dc1fcf8ad967a7f7e1");self.assertEqual(a["member_count"],143);self.assertEqual(a["ps5_member_count"],5);self.assertEqual(a["expected_undefined_chimera_hooks"],26) + o=self.data["one_shot"];self.assertEqual(o["attempts_authorized"],1);self.assertTrue(o["exclusive_leaf_create"] and o["file_fsync"] and o["reopen_rehash"]);self.assertFalse(o["automatic_retry"] or o["source_cleanup_authorized"]) + z=self.data["authorizations"];self.assertTrue(z["archive_export_authorized"]) + for k in ("retroarch_target_build_authorized","network_access_authorized","ps5_connection_authorized","device_transfer_authorized","device_execution_authorized"):self.assertFalse(z[k],k) +if __name__=="__main__":unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10cs.py b/tests/test_retroarch_phase10cs.py new file mode 100644 index 0000000..1349b8d --- /dev/null +++ b/tests/test_retroarch_phase10cs.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +import argparse,hashlib,json,unittest +from pathlib import Path +parser=argparse.ArgumentParser();parser.add_argument("--root",type=Path,required=True);root=parser.parse_args().root +class Phase10CSGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls):cls.data=json.loads((root/"manifests/retroarch/phase-1.0cs-export-parent-correction-gate.json").read_text()) + def test_gate(self): + p=root/"manifests/retroarch/phase-1.0cr-audited-sdl-archive-export-gate.json";self.assertEqual(hashlib.sha256(p.read_bytes()).hexdigest(),self.data["source_bindings"]["phase10cr_manifest_sha256"]) + c=self.data["consumed_attempt"];self.assertEqual(c["classification"],"REJECTED_BEFORE_DESTINATION_MUTATION");self.assertFalse(c["destination_leaf_created"] or c["retry_performed"]) + a=self.data["authorizations"];self.assertTrue(a["exact_parent_creation_authorized"] and a["archive_export_authorized"]) + for k in ("source_cleanup_authorized","retroarch_target_build_authorized","network_access_authorized","ps5_connection_authorized","device_transfer_authorized","device_execution_authorized"):self.assertFalse(a[k],k) +if __name__=="__main__":unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10ct.py b/tests/test_retroarch_phase10ct.py new file mode 100644 index 0000000..57b5435 --- /dev/null +++ b/tests/test_retroarch_phase10ct.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +import argparse,hashlib,json,unittest +from pathlib import Path +parser=argparse.ArgumentParser();parser.add_argument("--root",type=Path,required=True);root=parser.parse_args().root +class Phase10CTGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls):cls.data=json.loads((root/"manifests/retroarch/phase-1.0ct-sdl-archive-export-result.json").read_text()) + def test_result(self): + p=root/"manifests/retroarch/phase-1.0cs-export-parent-correction-gate.json";self.assertEqual(hashlib.sha256(p.read_bytes()).hexdigest(),self.data["source_bindings"]["phase10cs_manifest_sha256"]) + r=self.data["result"];self.assertTrue(r["exclusive_export_succeeded"] and r["source_retained"]);self.assertEqual(r["attempts_consumed"],1) + self.assertFalse(self.data["makefile_audit"]["exported_include_roots_present"]) + for value in self.data["authorizations"].values():self.assertFalse(value) +if __name__=="__main__":unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10cu.py b/tests/test_retroarch_phase10cu.py new file mode 100644 index 0000000..361f9cf --- /dev/null +++ b/tests/test_retroarch_phase10cu.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +import argparse,hashlib,json,unittest +from pathlib import Path +parser=argparse.ArgumentParser();parser.add_argument("--root",type=Path,required=True);root=parser.parse_args().root +class Phase10CUGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls):cls.data=json.loads((root/"manifests/retroarch/phase-1.0cu-exact-sdl-header-export-gate.json").read_text()) + def test_gate(self): + p=root/"manifests/retroarch/phase-1.0ct-sdl-archive-export-result.json";self.assertEqual(hashlib.sha256(p.read_bytes()).hexdigest(),self.data["source_bindings"]["phase10ct_manifest_sha256"]) + self.assertEqual(self.data["trees"]["include/SDL2"]["regular_files"],78);self.assertEqual(self.data["trees"]["include-config-release/SDL2"]["regular_files"],1) + o=self.data["one_shot"];self.assertEqual(o["attempts_authorized"],1);self.assertTrue(o["exclusive_stage"] and o["exclusive_files"] and o["file_fsync"] and o["reopen_rehash"]);self.assertFalse(o["automatic_retry"] or o["cleanup_on_failure_authorized"]) + a=self.data["authorizations"];self.assertTrue(a["sdl_header_export_authorized"]) + for k in ("retroarch_target_build_authorized","network_access_authorized","ps5_connection_authorized","device_transfer_authorized","device_execution_authorized"):self.assertFalse(a[k],k) +if __name__=="__main__":unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10cv.py b/tests/test_retroarch_phase10cv.py new file mode 100644 index 0000000..039c6d5 --- /dev/null +++ b/tests/test_retroarch_phase10cv.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +import argparse,hashlib,json,unittest +from pathlib import Path +parser=argparse.ArgumentParser();parser.add_argument("--root",type=Path,required=True);root=parser.parse_args().root +class Phase10CVGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls):cls.data=json.loads((root/"manifests/retroarch/phase-1.0cv-launch-canary-offline-build-gate.json").read_text()) + def test_gate(self): + p=root/"manifests/retroarch/phase-1.0cu-exact-sdl-header-export-gate.json";self.assertEqual(hashlib.sha256(p.read_bytes()).hexdigest(),self.data["source_bindings"]["phase10cu_manifest_sha256"]) + p=self.data["preflight"];self.assertEqual(p["stale_object_or_dependency_files"],0);self.assertTrue(p["target_elf_absent"] and p["target_map_absent"]);self.assertEqual(p["make_dry_run_commands"],121) + o=self.data["one_shot"];self.assertEqual(o["attempts_authorized"],1);self.assertFalse(o["automatic_retry"] or o["clean_authorized"]) + a=self.data["authorizations"];self.assertTrue(a["retroarch_target_build_authorized"]) + for k in ("artifact_execution_eligible","artifact_transfer_eligible","artifact_installation_eligible","network_access_authorized","ps5_connection_authorized","device_transfer_authorized","device_execution_authorized"):self.assertFalse(a[k],k) +if __name__=="__main__":unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10cw.py b/tests/test_retroarch_phase10cw.py new file mode 100644 index 0000000..004c1f6 --- /dev/null +++ b/tests/test_retroarch_phase10cw.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +import argparse,hashlib,json,unittest +from pathlib import Path +parser=argparse.ArgumentParser();parser.add_argument("--root",type=Path,required=True);root=parser.parse_args().root +class Phase10CWGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls):cls.data=json.loads((root/"manifests/retroarch/phase-1.0cw-launch-canary-artifact-audit.json").read_text()) + def test_audit(self): + p=root/"manifests/retroarch/phase-1.0cv-launch-canary-offline-build-gate.json";self.assertEqual(hashlib.sha256(p.read_bytes()).hexdigest(),self.data["source_bindings"]["phase10cv_manifest_sha256"]) + a=self.data["static_audit"];self.assertFalse(a["writable_executable_segment"]);self.assertEqual(len(a["needed"]),7);self.assertEqual(a["socket_constructor_imports"],[]);self.assertEqual(a["receive_imports"],[]);self.assertTrue(a["d14_emitter_present"] and a["diagnostic_stream_present"]) + l=self.data["limitations"];self.assertFalse(l["second_independent_build_performed"] or l["byte_reproducibility_proven"] or l["firmware_behavior_proven"]) + for value in self.data["authorizations"].values():self.assertFalse(value) +if __name__=="__main__":unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10cx.py b/tests/test_retroarch_phase10cx.py new file mode 100644 index 0000000..727e430 --- /dev/null +++ b/tests/test_retroarch_phase10cx.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +import argparse,hashlib,json,unittest +from pathlib import Path +parser=argparse.ArgumentParser();parser.add_argument("--root",type=Path,required=True);root=parser.parse_args().root +class Phase10CXGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls):cls.data=json.loads((root/"manifests/retroarch/phase-1.0cx-isolated-reproducibility-build-gate.json").read_text()) + def test_gate(self): + p=root/"manifests/retroarch/phase-1.0cw-launch-canary-artifact-audit.json";self.assertEqual(hashlib.sha256(p.read_bytes()).hexdigest(),self.data["source_bindings"]["phase10cw_manifest_sha256"]) + i=self.data["isolation"];self.assertTrue(i["mounted_stage_absent"] and i["native_root_absent"] and i["source_commit_exact"]);self.assertFalse(i["current_objects_reused"]) + o=self.data["one_shot"];self.assertEqual(o["attempts_authorized"],1);self.assertFalse(o["automatic_retry"] or o["cleanup_authorized"]) + a=self.data["authorizations"] + for k in ("artifact_execution_eligible","artifact_transfer_eligible","artifact_installation_eligible","network_access_authorized","ps5_connection_authorized","device_transfer_authorized","device_execution_authorized"):self.assertFalse(a[k],k) +if __name__=="__main__":unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10cy.py b/tests/test_retroarch_phase10cy.py new file mode 100644 index 0000000..6c7e6ab --- /dev/null +++ b/tests/test_retroarch_phase10cy.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +import argparse,hashlib,json,unittest +from pathlib import Path +parser=argparse.ArgumentParser();parser.add_argument("--root",type=Path,required=True);root=parser.parse_args().root +class Phase10CYGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls):cls.data=json.loads((root/"manifests/retroarch/phase-1.0cy-launch-canary-reproducibility-result.json").read_text()) + def test_result(self): + p=root/"manifests/retroarch/phase-1.0cx-isolated-reproducibility-build-gate.json";self.assertEqual(hashlib.sha256(p.read_bytes()).hexdigest(),self.data["source_bindings"]["phase10cx_manifest_sha256"]) + c=self.data["comparison"];self.assertTrue(c["elf_byte_exact"] and c["map_byte_exact"]);self.assertEqual(c["first_elf_sha256"],c["second_elf_sha256"]);self.assertEqual(c["first_map_sha256"],c["second_map_sha256"]) + b=self.data["independent_build"];self.assertFalse(b["current_objects_reused"] or b["network_action_invoked"] or b["device_action_invoked"]) + for value in self.data["authorizations"].values():self.assertFalse(value) +if __name__=="__main__":unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10cz.py b/tests/test_retroarch_phase10cz.py new file mode 100644 index 0000000..8c37ec7 --- /dev/null +++ b/tests/test_retroarch_phase10cz.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +import argparse,hashlib,json,unittest +from pathlib import Path +parser=argparse.ArgumentParser();parser.add_argument("--root",type=Path,required=True);root=parser.parse_args().root +class Phase10CZGuardrails(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.m=json.loads((root/"manifests/retroarch/phase-1.0cz-launch-canary-one-shot-runner.json").read_text());cls.a=json.loads((root/"manifests/retroarch/phase-1.0cz-one-shot-approval-template.json").read_text()) + def test_inactive_gate(self): + p=root/"manifests/retroarch/phase-1.0cy-launch-canary-reproducibility-result.json";self.assertEqual(hashlib.sha256(p.read_bytes()).hexdigest(),self.m["source_bindings"]["phase10cy_manifest_sha256"]) + p=self.m["result_protocol"];self.assertEqual(p["wire_stages"][-1],"D14");self.assertEqual(p["sole_terminal"],"D14");self.assertEqual(p["required_order"],["D07","D04","D14"]);self.assertFalse(p["post_terminal_bytes_allowed"]) + self.assertFalse(self.m["artifact"]["execution_eligible"] or self.m["artifact"]["transfer_eligible"]);self.assertIsNone(self.m["route"]["target"]);self.assertIsNone(self.m["route"]["port"]);self.assertIsNone(self.m["runner"]["run_id"]) + for value in self.m["authorizations"].values():self.assertFalse(value) + def test_approval_template_inactive(self): + self.assertFalse(self.a["authorized"] or self.a["consumed"]);self.assertIsNone(self.a["authorized_by"]);self.assertIsNone(self.a["target"]);self.assertIsNone(self.a["port"]);self.assertIsNone(self.a["run_id"]);self.assertEqual(self.a["connection_count"],0) +if __name__=="__main__":unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10d.py b/tests/test_retroarch_phase10d.py new file mode 100644 index 0000000..4031537 --- /dev/null +++ b/tests/test_retroarch_phase10d.py @@ -0,0 +1,330 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Host-only policy tests for the Phase-1.0D diagnostic ladder.""" + +from __future__ import annotations + +import argparse +import importlib.util +from pathlib import Path +import sys +from typing import Callable + + +def load_validator(path: Path): + spec = importlib.util.spec_from_file_location("phase10d_validator", path) + assert spec and spec.loader + module = importlib.util.module_from_spec(spec) + sys.modules[spec.name] = module + spec.loader.exec_module(module) + return module + + +def require(condition: bool, message: str) -> None: + if not condition: + raise RuntimeError(message) + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + root = args.root.resolve() + validator = load_validator(root / "tools/validate_retroarch_phase10d.py") + cases: list[tuple[str, Callable[[], None]]] = [] + + def case(name: str): + def register(function: Callable[[], None]) -> Callable[[], None]: + cases.append((name, function)) + return function + return register + + def canary() -> dict: + return { + "elf": { + "undefined_symbols": sorted(validator.CANARY_IMPORTS), + "dt_needed": sorted(validator.CANARY_NEEDED), + }, + "notification_abi": {"maximum_attempts": 1, "retry": False}, + "behavior": { + "sleep_attempts": 1, + "interrupted_sleep_retry": False, + **{ + field: False for field in ( + "retroarch", "sdl", "videoout", "pad", "audioout", + "filesystem", "networking", "threads", "autoload", + "installation", + ) + }, + }, + } + + def early() -> dict: + return { + "diagnostic": { + "stages": [f"D{index:02d}" for index in range(13)], + "notification_maximum_attempts_per_stage": 1, + "notification_retry": False, + "notification_failure_blocks_primary_path": False, + }, + "first_frame": { + "background": "MAGENTA", + "fixed_rectangle": "WHITE", + "embedded_pattern": "BLACK", + "submit_attempts": 1, + "retry": False, + "second_buffer_initialization": False, + }, + "profile_policy": { + "write_firewall": True, + **{ + field: False for field in ( + "filesystem_writes_allowed", "networking", + "dynamic_cores", "gnm", "autoload", "installation", + "payload_launch", "automatic_retry", + ) + }, + }, + } + + @case("01 false authorization set passes") + def _() -> None: + require( + validator.all_false( + {field: False for field in validator.AUTHORIZATION_FIELDS}, + validator.AUTHORIZATION_FIELDS, + ), + "closed authorization rejected", + ) + + @case("02 any authorization true fails") + def _() -> None: + values = {field: False for field in validator.AUTHORIZATION_FIELDS} + values["device_execution_authorized"] = True + require( + not validator.all_false(values, validator.AUTHORIZATION_FIELDS), + "execution authorization accepted", + ) + + @case("03 minimal canary passes") + def _() -> None: + require(validator.canary_is_minimal(canary()), "minimal canary rejected") + + @case("04 SDL canary import fails") + def _() -> None: + value = canary() + value["elf"]["undefined_symbols"].append("SDL_Init") + require(not validator.canary_is_minimal(value), "SDL canary accepted") + + @case("05 canary networking fails") + def _() -> None: + value = canary() + value["behavior"]["networking"] = True + require(not validator.canary_is_minimal(value), "network canary accepted") + + @case("06 canary notification retry fails") + def _() -> None: + value = canary() + value["notification_abi"]["retry"] = True + require(not validator.canary_is_minimal(value), "notification retry accepted") + + @case("07 complete D00-D12 ladder passes") + def _() -> None: + require(validator.early_ladder_is_closed(early()), "closed ladder rejected") + + @case("08 missing D07 fails") + def _() -> None: + value = early() + value["diagnostic"]["stages"].remove("D07") + require(not validator.early_ladder_is_closed(value), "missing D07 accepted") + + @case("09 duplicate notification policy fails") + def _() -> None: + value = early() + value["diagnostic"]["notification_maximum_attempts_per_stage"] = 2 + require(not validator.early_ladder_is_closed(value), "duplicate accepted") + + @case("10 first flip retry fails") + def _() -> None: + value = early() + value["first_frame"]["retry"] = True + require(not validator.early_ladder_is_closed(value), "flip retry accepted") + + @case("11 second buffer initialization fails") + def _() -> None: + value = early() + value["first_frame"]["second_buffer_initialization"] = True + require(not validator.early_ladder_is_closed(value), "second init accepted") + + @case("12 filesystem writes fail") + def _() -> None: + value = early() + value["profile_policy"]["filesystem_writes_allowed"] = True + require(not validator.early_ladder_is_closed(value), "write accepted") + + @case("13 GNM fails") + def _() -> None: + value = early() + value["profile_policy"]["gnm"] = True + require(not validator.early_ladder_is_closed(value), "GNM accepted") + + @case("14 RX R RW passes") + def _() -> None: + headers = [ + {"type": "LOAD", "flags": "RE"}, + {"type": "LOAD", "flags": "R"}, + {"type": "LOAD", "flags": "RW"}, + ] + require(validator.wx_closed(headers), "W^X layout rejected") + + @case("15 RWX fails") + def _() -> None: + headers = [ + {"type": "LOAD", "flags": "RWE"}, + {"type": "LOAD", "flags": "R"}, + {"type": "LOAD", "flags": "RW"}, + ] + require(not validator.wx_closed(headers), "RWX accepted") + + @case("16 exact four model inputs pass") + def _() -> None: + record = { + "model": {"hardware_evidence": False}, + "real_artifacts": [ + { + "name": name, + "sha256": "0" * 64, + "classification": "ACCEPTED_BY_STATIC_MODEL", + } + for name in validator.REAL_MODEL_ARTIFACTS + ], + } + require(validator.model_result_set_is_complete(record), "model set rejected") + + @case("17 missing model input fails") + def _() -> None: + names = list(validator.REAL_MODEL_ARTIFACTS)[:-1] + record = { + "model": {"hardware_evidence": False}, + "real_artifacts": [ + {"name": name, "sha256": "0" * 64, + "classification": "ACCEPTED_BY_STATIC_MODEL"} + for name in names + ], + } + require( + not validator.model_result_set_is_complete(record), + "incomplete model set accepted", + ) + + @case("18 static model cannot be hardware evidence") + def _() -> None: + record = { + "model": {"hardware_evidence": True}, + "real_artifacts": [ + {"name": name, "sha256": "0" * 64, + "classification": "ACCEPTED_BY_STATIC_MODEL"} + for name in validator.REAL_MODEL_ARTIFACTS + ], + } + require( + not validator.model_result_set_is_complete(record), + "hardware claim accepted", + ) + + def trace() -> dict: + return { + "connections": 1, "sendall_calls": 1, "hash_before_connect": True, + "shutdown_called": False, "response_read": False, "retry": False, + "reconnect": False, "probe": False, + "cannot_prove": ["REMOTE_ELF_RECEIPT", "REMOTE_EXECUTION"], + } + + @case("19 bounded sender trace passes") + def _() -> None: + require(validator.sender_trace_is_bounded(trace()), "bounded trace rejected") + + @case("20 sender retry fails") + def _() -> None: + value = trace() + value["retry"] = True + require(not validator.sender_trace_is_bounded(value), "sender retry accepted") + + @case("21 response read fails") + def _() -> None: + value = trace() + value["response_read"] = True + require(not validator.sender_trace_is_bounded(value), "response accepted") + + @case("22 reproducible hashes pass") + def _() -> None: + value = { + "artifact": { + "size": 1, "sha256": "a" * 64, + "clean_build_sha256": ["a" * 64, "a" * 64], + "linker_map_sha256": "b" * 64, + "clean_map_sha256": ["b" * 64, "b" * 64], + } + } + require(validator.reproducible_artifact(value), "reproducible rejected") + + @case("23 differing build hashes fail") + def _() -> None: + value = { + "artifact": { + "size": 1, "sha256": "a" * 64, + "clean_build_sha256": ["a" * 64, "c" * 64], + "linker_map_sha256": "b" * 64, + "clean_map_sha256": ["b" * 64, "b" * 64], + } + } + require(not validator.reproducible_artifact(value), "mismatch accepted") + + @case("24 future ladder remains non-authorizing") + def _() -> None: + text = ( + root / "docs/retroarch/phase-1.0d-next-device-test-ladder.md" + ).read_text(encoding="utf-8") + require( + "device_execution_authorized=false" in text + and "RUN A authority does not carry to RUN B" in text, + "authorization boundary absent", + ) + + @case("25 pre-main SDK writes remain explicit") + def _() -> None: + text = ( + root / "docs/retroarch/phase-1.0d-startup-import-closure.md" + ).read_text(encoding="utf-8") + require( + "__patch_init" in text and "not side-effect-free" in text, + "pre-main effect hidden", + ) + + @case("26 static acceptance disclaimer exists") + def _() -> None: + text = ( + root / "docs/retroarch/phase-1.0d-loader-static-model.md" + ).read_text(encoding="utf-8") + require( + "not proof" in text.lower() and "firmware" in text.lower(), + "hardware disclaimer absent", + ) + + failures: list[str] = [] + for name, function in cases: + try: + function() + except Exception as error: # noqa: BLE001 - isolated test report + failures.append(f"{name}: {error}") + if len(cases) != 26: + failures.append(f"expected 26 cases, found {len(cases)}") + if failures: + print("\n".join(failures), file=sys.stderr) + return 1 + print("26 Phase 1.0D guardrails passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_retroarch_phase10db.py b/tests/test_retroarch_phase10db.py new file mode 100644 index 0000000..1bef8b2 --- /dev/null +++ b/tests/test_retroarch_phase10db.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Guardrails for the post-CZ differential record.""" +from __future__ import annotations +import argparse, json +from pathlib import Path +import unittest + +P=argparse.ArgumentParser();P.add_argument("--root",type=Path,required=True);ROOT=P.parse_args().root + +class Phase10DB(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.record=json.loads((ROOT/"manifests/retroarch/phase-1.0db-post-cz-differential-analysis.json").read_text()) + def test_exact_run_facts(self): + runs=self.record["raw_runs"] + self.assertEqual(runs["phase10cz"]["videoout_open_raw_hex"],"0x80290009") + self.assertFalse(runs["phase10cz"]["flip_submit_reached"]) + self.assertEqual(runs["phase10o"]["flip_submit_raw"],-1) + def test_no_causal_overclaim(self): + evidence=self.record["evidence_classification"] + self.assertFalse(evidence["o_to_cz_difference_is_causal_evidence"]) + self.assertFalse(evidence["bigapp_context_root_cause_proven"]) + def test_gate_remains_closed(self): + self.assertFalse(any(self.record["authorizations"].values())) + self.assertFalse(self.record["bigapp_gate"]["existing_hbldr_direct_use_allowed"]) + self.assertFalse(self.record["decision"]["device_action_allowed"]) + +if __name__=="__main__":unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10dc.py b/tests/test_retroarch_phase10dc.py new file mode 100644 index 0000000..13f9197 --- /dev/null +++ b/tests/test_retroarch_phase10dc.py @@ -0,0 +1,12 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +from __future__ import annotations +import argparse,copy,importlib.util,json +from pathlib import Path +p=argparse.ArgumentParser();p.add_argument("--root",type=Path,required=True);root=p.parse_args().root.resolve();s=importlib.util.spec_from_file_location("v",root/"tools/validate_retroarch_phase10dc.py");assert s and s.loader;v=importlib.util.module_from_spec(s);s.loader.exec_module(v);original=json.loads((root/"manifests/retroarch/phase-1.0dc-inactive-bigapp-comparison-gate.json").read_text()) +mutations=[lambda r:r["activation"].update(active=True),lambda r:r["activation"].update(run_id="LIVE"),lambda r:r["authorizations"].update(ps5_connection_authorized=True),lambda r:r["implementation_boundary"].update(socket_present=True),lambda r:r["candidate_contract"].update(exact_payload_sha256="0"*64),lambda r:r["candidate_contract"].update(fixed_existing_title="FAKE00000"),lambda r:r["forbidden_effects"].update(automatic_retry=False),lambda r:r["decision"].update(device_action_allowed=True)] +for index,mutation in enumerate(mutations,1): + value=copy.deepcopy(original);mutation(value) + if not v.errors(value):print("FAIL",index);raise SystemExit(1) + print("PASS",index) +print("Phase-1.0DC guardrails passed:",len(mutations)) diff --git a/tests/test_retroarch_phase10dd.py b/tests/test_retroarch_phase10dd.py new file mode 100644 index 0000000..1d8ab3c --- /dev/null +++ b/tests/test_retroarch_phase10dd.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +from __future__ import annotations +import argparse,json,unittest +from pathlib import Path +P=argparse.ArgumentParser();P.add_argument("--root",type=Path,required=True);ROOT=P.parse_args().root +class Phase10DD(unittest.TestCase): + @classmethod + def setUpClass(cls):cls.data=json.loads((ROOT/"manifests/retroarch/phase-1.0dd-minimal-launcher-source-prerequisite-audit.json").read_text()) + def test_names_are_not_abi(self): + inventory=self.data["export_inventory"];self.assertTrue(inventory["sceSystemServiceLaunchApp_name_in_sdk_stub"]);self.assertFalse(inventory["sdk_stub_proves_function_signature"] or inventory["sdk_stub_proves_structure_layout"]) + def test_single_lineage_not_promoted(self): + lineage=self.data["single_lineage_declarations"];self.assertTrue(lineage["acceptable_as_architecture_reference"]);self.assertFalse(lineage["independent_public_abi_corroboration"] or lineage["acceptable_as_new_target_abi_contract"]) + def test_title_runtime_unproven(self): + title=self.data["title_contract"];self.assertEqual(title["fixed_candidate"],"PPSA01659");self.assertFalse(title["firmware_960_presence_proven"] or title["videoout_ownership_proven"] or title["fallback_title_allowed"]) + def test_gate_closed(self): + self.assertFalse(any(self.data["authorizations"].values()));self.assertFalse(self.data["decision"]["minimal_launcher_target_source_allowed"] or self.data["decision"]["device_action_allowed"]) + def test_public_search_not_overpromoted(self): + search=self.data["bounded_public_search"];self.assertTrue(search["sonicloader_candidate_found"]);self.assertFalse(search["sonicloader_accepted_as_independent_corroboration"] or search["global_absence_claimed"]) +if __name__=="__main__":unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10de.py b/tests/test_retroarch_phase10de.py new file mode 100644 index 0000000..6ff2b1d --- /dev/null +++ b/tests/test_retroarch_phase10de.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +from __future__ import annotations +import argparse,json,unittest +from pathlib import Path +P=argparse.ArgumentParser();P.add_argument("--root",type=Path,required=True);ROOT=P.parse_args().root +class Phase10DE(unittest.TestCase): + @classmethod + def setUpClass(cls):cls.data=json.loads((ROOT/"manifests/retroarch/phase-1.0de-independent-abi-provenance-search.json").read_text()) + def test_sonicloader_is_derivative(self): + item=self.data["sonicloader_lineage"];self.assertTrue(item["initial_normalized_declaration_block_equals_official_shsrv_v019"]);self.assertFalse(item["independent_abi_corroboration"] or item["acceptable_runtime_dependency"]) + def test_no_global_absence_claim(self):self.assertFalse(self.data["bounded_search"]["global_absence_claimed"]) + def test_title_unproven(self):self.assertFalse(any(self.data["title_evidence"].values())) + def test_no_authority(self):self.assertFalse(any(self.data["authorizations"].values()) or self.data["decision"]["minimal_launcher_source_allowed"] or self.data["decision"]["device_action_allowed"]) + def test_next_step_is_observer_design(self):self.assertEqual(self.data["decision"]["next_step"],"OFFLINE_FAIL_CLOSED_TITLE_PRESENCE_OBSERVER_GATE_DESIGN") +if __name__=="__main__":unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10df.py b/tests/test_retroarch_phase10df.py new file mode 100644 index 0000000..205bbf8 --- /dev/null +++ b/tests/test_retroarch_phase10df.py @@ -0,0 +1,12 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +from __future__ import annotations +import argparse,copy,importlib.util,json +from pathlib import Path +p=argparse.ArgumentParser();p.add_argument("--root",type=Path,required=True);root=p.parse_args().root.resolve();s=importlib.util.spec_from_file_location("v",root/"tools/validate_retroarch_phase10df.py");assert s and s.loader;v=importlib.util.module_from_spec(s);s.loader.exec_module(v);original=json.loads((root/"manifests/retroarch/phase-1.0df-inactive-title-presence-observer.json").read_text()) +mutations=[lambda r:r["activation"].update(active=True),lambda r:r["activation"].update(exact_literal_path="/guess"),lambda r:r["authorizations"].update(ps5_connection_authorized=True),lambda r:r["implementation_boundary"].update(socket_present=True),lambda r:r["current_evidence"].update(exact_literal_title_path_available=True),lambda r:r["observer_contract"].update(error_is_absence=True),lambda r:r["observer_contract"].update(directory_enumeration_forbidden=False),lambda r:r["decision"].update(live_observation_allowed=True)] +for index,mutation in enumerate(mutations,1): + value=copy.deepcopy(original);mutation(value) + if not v.errors(value):print("FAIL",index);raise SystemExit(1) + print("PASS",index) +print("Phase-1.0DF guardrails passed:",len(mutations)) diff --git a/tests/test_retroarch_phase10dg.py b/tests/test_retroarch_phase10dg.py new file mode 100644 index 0000000..8cb52b0 --- /dev/null +++ b/tests/test_retroarch_phase10dg.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +from __future__ import annotations +import argparse,json,unittest +from pathlib import Path +P=argparse.ArgumentParser();P.add_argument("--root",type=Path,required=True);ROOT=P.parse_args().root +class Phase10DG(unittest.TestCase): + @classmethod + def setUpClass(cls):cls.data=json.loads((ROOT/"manifests/retroarch/phase-1.0dg-title-presence-primitive-audit.json").read_text()) + def test_source_bound_candidate(self): + p=self.data["audited_primitive"];self.assertEqual(p["source_bound_device_path"],"/system_data/priv/mms/appinfo.db");self.assertTrue(p["parameterized_predicate_supported_by_source"] and p["query_is_minimal_inference_from_source_schema"]);self.assertFalse(p["query_is_present_in_upstream_verbatim"]) + def test_snapshot_only(self): + p=self.data["audited_primitive"];self.assertTrue(p["requires_already_acquired_byte_exact_snapshot"] and p["opens_snapshot_read_only"]);self.assertFalse(p["modifies_snapshot"] or p["modifies_device"]) + def test_live_evidence_remains_absent(self):self.assertFalse(any(v for k,v in self.data["evidence_limits"].items() if k not in {"y2jb_titles_are_ppsa01650_through_ppsa01652_not_ppsa01659","global_absence_claimed"}));self.assertFalse(self.data["evidence_limits"]["global_absence_claimed"]) + def test_forbidden_and_unauthorized(self):self.assertTrue(all(self.data["forbidden_scope"].values()));self.assertFalse(any(self.data["authorizations"].values())) + def test_decision(self): + d=self.data["decision"];self.assertTrue(d["offline_primitive_source_bound"] and d["df_exact_path_metadata_candidate_supported"]);self.assertFalse(d["live_request_formable"] or d["live_observation_allowed"] or d["device_action_allowed"]);self.assertEqual(d["next_step"],"OFFLINE_HASH_BOUND_SNAPSHOT_QUERY_CONTRACT") +if __name__=="__main__":unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10di.py b/tests/test_retroarch_phase10di.py new file mode 100644 index 0000000..d25f78a --- /dev/null +++ b/tests/test_retroarch_phase10di.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +from __future__ import annotations +import argparse,hashlib,json,unittest +from pathlib import Path +P=argparse.ArgumentParser();P.add_argument("--root",type=Path,required=True);ROOT=P.parse_args().root +class Phase10DI(unittest.TestCase): + @classmethod + def setUpClass(cls):cls.data=json.loads((ROOT/"manifests/retroarch/phase-1.0di-live-acquisition-boundary.json").read_text()) + def test_acquisition_is_blocked(self): + a=self.data["acquisition_audit"];self.assertFalse(a["controlled_elfldr_binary_readback"] or a["payload_manager_binary_readback"] or a["nvt_cat_is_binary_safe"] or a["direct_snapshot_acquisition_formable"] or a["offline_alternative_remaining"]) + def test_exact_inactive_window(self): + w=self.data["minimum_observation_window"];self.assertFalse(w["active"]);self.assertIsNone(w["target"]);self.assertEqual((w["port"],w["connections"],w["outbound_batches"],w["maximum_received_bytes"],w["hard_deadline_seconds"]),(2323,1,1,65536,10));self.assertEqual(hashlib.sha256(w["exact_command_ascii"].encode()).hexdigest(),w["exact_command_sha256"]) + def test_no_mutation_or_execution(self): + w=self.data["minimum_observation_window"];self.assertFalse(any(w[k] for k in ("device_file_read","device_write","payload_transfer","payload_execution","title_launch","app_termination","retry","reconnect"))) + def test_no_authority(self):self.assertFalse(any(self.data["authorizations"].values()) or self.data["decision"]["device_action_allowed"] or self.data["decision"]["tracked_activation_may_be_enabled"]) + def test_permission_is_next(self):self.assertTrue(self.data["decision"]["all_safe_offline_prerequisites_complete"] and self.data["decision"]["connection_is_next_required_action"]);self.assertEqual(self.data["decision"]["next_step"],"REQUEST_EXACT_DI_T2_CONSOLE_CONNECTION_PERMISSION") +if __name__=="__main__":unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10dj.py b/tests/test_retroarch_phase10dj.py new file mode 100644 index 0000000..cb251bf --- /dev/null +++ b/tests/test_retroarch_phase10dj.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +from __future__ import annotations +import argparse,json,unittest +from pathlib import Path +P=argparse.ArgumentParser();P.add_argument("--root",type=Path,required=True);ROOT=P.parse_args().root +class Phase10DJ(unittest.TestCase): + @classmethod + def setUpClass(cls):cls.data=json.loads((ROOT/"manifests/retroarch/phase-1.0dj-di-t2-result.json").read_text()) + def test_single_failed_attempt(self): + r=self.data["run"];self.assertEqual(r["connection_attempts"],1);self.assertFalse(r["connection_established"] or r["retry_performed"] or r["reconnect_performed"]);self.assertEqual((r["command_bytes_sent"],r["received_bytes"]),(0,0)) + def test_no_device_effect(self): + r=self.data["run"];self.assertFalse(any(r[k] for k in ("device_file_read","device_write","payload_transfer","payload_execution","raw_transcript_retained","serial_retained","telemetry_retained"))) + def test_no_promoted_evidence(self):self.assertFalse(any(self.data["evidence"].values())) + def test_authority_consumed(self): + a=self.data["authorization_state"];self.assertTrue(a["di_t2_authorization_consumed"]);self.assertFalse(any(v for k,v in a.items() if k!="di_t2_authorization_consumed")) + def test_next_step_requires_operator_and_new_permission(self): + d=self.data["decision"];self.assertTrue(d["operator_must_confirm_shsrv_listener_ready"] and d["new_exact_permission_required_after_readiness_change"]);self.assertFalse(d["retry_allowed"] or d["device_action_allowed"]) +if __name__=="__main__":unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10dk.py b/tests/test_retroarch_phase10dk.py new file mode 100644 index 0000000..637e5f4 --- /dev/null +++ b/tests/test_retroarch_phase10dk.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +from __future__ import annotations +import argparse,json,unittest +from pathlib import Path +P=argparse.ArgumentParser();P.add_argument("--root",type=Path,required=True);ROOT=P.parse_args().root +class Phase10DK(unittest.TestCase): + @classmethod + def setUpClass(cls):cls.data=json.loads((ROOT/"manifests/retroarch/phase-1.0dk-port-status-result.json").read_text()) + def test_exact_observations(self):self.assertEqual([(x["port"],x["classification"],x["attempts"],x["timeout_seconds"]) for x in self.data["observations"]],[(2323,"REFUSED",1,3),(9021,"OPEN",1,3)]) + def test_no_application_effect(self): + e=self.data["effects"];self.assertEqual((e["application_bytes_sent"],e["application_bytes_received"]),(0,0));self.assertFalse(any(v for k,v in e.items() if k not in {"application_bytes_sent","application_bytes_received"})) + def test_no_overclaim(self): + i=self.data["interpretation"];self.assertTrue(i["elfldr_listener_observed"]);self.assertFalse(any(v for k,v in i.items() if k!="elfldr_listener_observed")) + def test_authority_consumed(self): + a=self.data["authorization_state"];self.assertTrue(a["dk_authorization_consumed"]);self.assertFalse(any(v for k,v in a.items() if k!="dk_authorization_consumed")) + def test_next_is_offline(self):self.assertTrue(self.data["decision"]["offline_shsrv_artifact_provenance_audit_allowed"]);self.assertFalse(self.data["decision"]["device_action_allowed"]) +if __name__=="__main__":unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10dl.py b/tests/test_retroarch_phase10dl.py new file mode 100644 index 0000000..2149e52 --- /dev/null +++ b/tests/test_retroarch_phase10dl.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +from __future__ import annotations +import argparse,json,unittest +from pathlib import Path +P=argparse.ArgumentParser();P.add_argument("--root",type=Path,required=True);ROOT=P.parse_args().root +class Phase10DL(unittest.TestCase): + @classmethod + def setUpClass(cls):cls.data=json.loads((ROOT/"manifests/retroarch/phase-1.0dl-official-shsrv-artifact-audit.json").read_text()) + def test_exact_artifact(self): + a=self.data["artifact"];self.assertEqual((a["size"],a["sha256"]),(981872,"797827b50a93dbf327abdca45063d76ea24b17e561d927f397a0b73cadb2e2bd"));self.assertFalse(a["executed_on_host"] or a["sent_to_device"]) + def test_rwe_blocker(self):self.assertTrue(self.data["static_elf"]["writable_executable_segment_present"]);self.assertIn("WRITABLE_EXECUTABLE_LOAD_SEGMENT",self.data["hard_blockers"]) + def test_unbounded_blocker(self): + e=self.data["source_effects"];self.assertTrue(e["accept_loop_is_unbounded"]);self.assertFalse(e["automatic_self_termination_deadline"]);self.assertIn("UNBOUNDED_PROCESS_LIFETIME",self.data["hard_blockers"]) + def test_no_authority(self):self.assertFalse(any(self.data["authorizations"].values())) + def test_rejected_and_pivoted(self): + d=self.data["decision"];self.assertTrue(d["official_artifact_provenance_bound"] and d["direct_bounded_raw_elfldr_snapshot_reader_design_allowed"]);self.assertFalse(d["official_artifact_execution_eligible"] or d["official_artifact_transfer_eligible"] or d["shsrv_route_selected"] or d["device_action_allowed"]) +if __name__=="__main__":unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10dm.py b/tests/test_retroarch_phase10dm.py new file mode 100644 index 0000000..7c1ca69 --- /dev/null +++ b/tests/test_retroarch_phase10dm.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +from __future__ import annotations +import argparse,json,unittest +from pathlib import Path +P=argparse.ArgumentParser();P.add_argument("--root",type=Path,required=True);R=P.parse_args().root +class Tests(unittest.TestCase): + @classmethod + def setUpClass(cls):cls.m=json.loads((R/"manifests/retroarch/phase-1.0dm-bounded-snapshot-observer.json").read_text()) + def test_wx_closed(self):self.assertEqual(self.m["static_elf"]["load_segment_flags"],["RE","RW","RW"]);self.assertFalse(self.m["static_elf"]["writable_executable_segment_present"]) + def test_read_only_bounded(self): + c=self.m["target_contract"];self.assertEqual(c["open_flags"],["O_RDONLY","O_CLOEXEC","O_NOFOLLOW"]);self.assertEqual((c["maximum_snapshot_bytes"],c["total_alarm_seconds"]),(67108864,20));self.assertFalse(c["socket_creation"] or c["connect"] or c["listener"] or c["filesystem_write"] or c["retry"]) + def test_reproducible_inactive_artifact(self): + a=self.m["artifact"];self.assertTrue(a["two_consecutive_build_hashes_equal"]);self.assertFalse(a["execution_eligible"] or a["transfer_eligible"] or a["installation_eligible"]) + def test_gates_open(self):self.assertFalse(any(self.m["remaining_gates"].values())) + def test_next(self):self.assertFalse(self.m["decision"]["device_action_allowed"]);self.assertEqual(self.m["decision"]["next_step"],"INACTIVE_ONE_SHOT_STREAM_RECEIVER_AND_ACTIVATION_GATE") +if __name__=="__main__":unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10dn.py b/tests/test_retroarch_phase10dn.py new file mode 100644 index 0000000..f06d47a --- /dev/null +++ b/tests/test_retroarch_phase10dn.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +from __future__ import annotations +import argparse,json,unittest +from pathlib import Path +P=argparse.ArgumentParser();P.add_argument("--root",type=Path,required=True);R=P.parse_args().root +class Tests(unittest.TestCase): + @classmethod + def setUpClass(cls):cls.m=json.loads((R/"manifests/retroarch/phase-1.0dn-inactive-snapshot-receiver.json").read_text()) + def test_inactive(self):self.assertFalse(self.m["activation"]["active"]);self.assertTrue(all(v is None for k,v in self.m["activation"].items() if k!="active")) + def test_receiver(self): + r=self.m["receiver"];self.assertFalse(r["network_import_present"] or r["partial_is_success"] or r["overwrite_allowed"]);self.assertTrue(r["exclusive_output"] and r["incremental_sha256"] and r["exact_terminal_required"] and r["fsync_before_success"]) + def test_artifact_ineligible(self):self.assertFalse(self.m["artifact"]["execution_eligible"] or self.m["artifact"]["transfer_eligible"]) + def test_no_authority(self):self.assertFalse(any(self.m["authorizations"].values()) or self.m["decision"]["live_adapter_allowed"] or self.m["decision"]["device_action_allowed"]) + def test_next(self):self.assertEqual(self.m["decision"]["next_step"],"INACTIVE_EXACT_ARTIFACT_ONE_SHOT_LIVE_RUNNER") +if __name__=="__main__":unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10dz.py b/tests/test_retroarch_phase10dz.py new file mode 100644 index 0000000..a2c1798 --- /dev/null +++ b/tests/test_retroarch_phase10dz.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +from __future__ import annotations +import argparse, json, unittest +from pathlib import Path + +P = argparse.ArgumentParser() +P.add_argument("--root", type=Path, required=True) +ROOT = P.parse_args().root + + +class Phase10DZ(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.data = json.loads( + (ROOT / "manifests/retroarch/phase-1.0dz-direct-launcher-decision.json").read_text() + ) + + def test_observation_is_not_overpromoted(self): + evidence = self.data["consumed_evidence"] + self.assertTrue(evidence["fake00000_appinfo_present"]) + self.assertTrue(evidence["operator_observed_launcher_browser_reached"]) + self.assertFalse(evidence["port_8080_refusal_proves_general_listener_absence"]) + self.assertFalse(evidence["videoout_ownership_proven"]) + + def test_minimal_route_excludes_websrv_surface(self): + route = self.data["route_decision"] + self.assertFalse(route["loopback_web_server_required"]) + self.assertFalse(route["official_websrv_acceptable"]) + self.assertTrue(route["direct_embedded_hash_bound_retroarch_route_selected"]) + self.assertFalse(route["generic_endpoints_allowed"] or route["persistent_fake_app_setup_allowed"]) + self.assertFalse(route["implicit_bigapp_termination_allowed"] or route["retry_allowed"] or route["unbounded_wait_allowed"]) + + def test_missing_abi_and_cleanup_block_target(self): + self.assertFalse(any(self.data["remaining_blockers"].values())) + self.assertFalse(self.data["decision"]["target_source_allowed"]) + self.assertFalse(self.data["decision"]["target_build_allowed"]) + + def test_no_artifact_or_device_authority(self): + self.assertFalse(any(self.data["artifact"].values())) + self.assertFalse(any(self.data["authorization"].values())) + self.assertFalse(self.data["decision"]["device_action_allowed"]) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10e.py b/tests/test_retroarch_phase10e.py new file mode 100644 index 0000000..3550074 --- /dev/null +++ b/tests/test_retroarch_phase10e.py @@ -0,0 +1,276 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Host-only policy tests for Phase-1.0E.""" + +from __future__ import annotations + +import argparse +import importlib.util +from pathlib import Path +import sys +from typing import Callable + + +def load_validator(path: Path): + spec = importlib.util.spec_from_file_location("phase10e_validator", path) + assert spec and spec.loader + module = importlib.util.module_from_spec(spec) + sys.modules[spec.name] = module + spec.loader.exec_module(module) + return module + + +def require(condition: bool, message: str) -> None: + if not condition: + raise RuntimeError(message) + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + root = args.root.resolve() + validator = load_validator(root / "tools/validate_retroarch_phase10e.py") + record = validator.load_json( + root / "manifests/retroarch/phase-1.0e-result-channel.json" + ) + cases: list[tuple[str, Callable[[], None]]] = [] + + def case(name: str): + def register(function: Callable[[], None]) -> Callable[[], None]: + cases.append((name, function)) + return function + return register + + @case("01 consumed authorization is fully false") + def _() -> None: + require( + validator.all_false( + record["authorizations"], validator.AUTHORIZATION_FIELDS + ), + "consumed authorization remains active", + ) + + @case("02 any later authorization true fails") + def _() -> None: + values = dict(record["authorizations"]) + values["ps5_connection_authorized"] = True + require( + not validator.all_false(values, validator.AUTHORIZATION_FIELDS), + "later connection authorization accepted", + ) + + @case("03 reproducible exact artifact passes") + def _() -> None: + require(validator.reproducible_artifact(record["artifact"]), "artifact rejected") + + @case("04 differing clean build fails") + def _() -> None: + value = dict(record["artifact"]) + value["clean_build_sha256"] = [validator.ARTIFACT_SHA256, "0" * 64] + require(not validator.reproducible_artifact(value), "mismatch accepted") + + @case("05 renewed execution eligibility fails") + def _() -> None: + value = dict(record["artifact"]) + value["execution_eligible"] = True + require(not validator.reproducible_artifact(value), "renewed eligibility accepted") + + @case("06 bounded inherited transport passes") + def _() -> None: + require(validator.transport_is_bounded(record), "bounded transport rejected") + + @case("07 controlled route claim fails") + def _() -> None: + value = dict(record) + value["loader_transport"] = dict(record["loader_transport"]) + value["loader_transport"]["controlled_route_supported"] = True + require(not validator.transport_is_bounded(value), "controlled route accepted") + + @case("08 target socket fails") + def _() -> None: + value = dict(record) + value["loader_transport"] = dict(record["loader_transport"]) + value["loader_transport"]["new_target_socket"] = True + require(not validator.transport_is_bounded(value), "target socket accepted") + + @case("09 send retry fails") + def _() -> None: + value = dict(record) + value["result_protocol"] = dict(record["result_protocol"]) + value["result_protocol"]["target_write_retry"] = True + require(not validator.transport_is_bounded(value), "send retry accepted") + + @case("10 short write retry fails") + def _() -> None: + value = dict(record) + value["result_protocol"] = dict(record["result_protocol"]) + value["result_protocol"]["short_write_retry"] = True + require(not validator.transport_is_bounded(value), "short retry accepted") + + @case("11 second connection fails") + def _() -> None: + value = dict(record) + value["host_contract"] = dict(record["host_contract"]) + value["host_contract"]["connection_count"] = 2 + require(not validator.transport_is_bounded(value), "second connect accepted") + + @case("12 reconnect fails") + def _() -> None: + value = dict(record) + value["host_contract"] = dict(record["host_contract"]) + value["host_contract"]["reconnect"] = True + require(not validator.transport_is_bounded(value), "reconnect accepted") + + @case("13 trace overwrite fails") + def _() -> None: + value = dict(record) + value["host_contract"] = dict(record["host_contract"]) + value["host_contract"]["trace_overwrite"] = True + require(not validator.transport_is_bounded(value), "trace overwrite accepted") + + @case("14 partial result success fails") + def _() -> None: + value = dict(record) + value["host_contract"] = dict(record["host_contract"]) + value["host_contract"]["partial_result_is_success"] = True + require(not validator.transport_is_bounded(value), "partial success accepted") + + @case("15 prior observations stay bounded") + def _() -> None: + require(validator.observations_are_bounded(record), "observations rejected") + + @case("16 unread RUN B stage stays unclassified") + def _() -> None: + value = dict(record) + value["prior_device_observations"] = dict(record["prior_device_observations"]) + value["prior_device_observations"]["run_b"] = dict( + record["prior_device_observations"]["run_b"] + ) + value["prior_device_observations"]["run_b"]["classification"] = "D12_PROVEN" + require(not validator.observations_are_bounded(value), "invented D12 accepted") + + @case("17 W X separation passes") + def _() -> None: + require(validator.wx_closed(record["elf"]["program_headers"]), "W^X rejected") + + @case("18 RWX fails") + def _() -> None: + headers = [dict(item) for item in record["elf"]["program_headers"]] + headers[0]["flags"] = "RWE" + require(not validator.wx_closed(headers), "RWX accepted") + + @case("19 import delta is exactly send") + def _() -> None: + require(record["elf"]["undefined_symbol_delta_from_phase10d"] == ["send"], "delta changed") + + @case("20 SDK patch effects remain explicit") + def _() -> None: + effects = record["startup_effects"] + require( + effects["patch_init_reachable_from_start"] is True + and effects["side_effect_free"] is False, + "startup effect hidden", + ) + + @case("21 approval is consumed and non-authorizing") + def _() -> None: + text = (root / "docs/retroarch/phase-1.0e-next-device-test.md").read_text( + encoding="utf-8" + ) + require( + "result_receive_authorized=false" in text + and "consumed" in text.lower(), + "consumed authorization boundary absent", + ) + + @case("22 result document disclaims controlled route") + def _() -> None: + text = ( + root / "docs/retroarch/phase-1.0e-inherited-result-channel.md" + ).read_text(encoding="utf-8") + require("stdio=-1" in text and "does **not** apply" in text, "route overclaim") + + @case("23 no target artifact is tracked") + def _() -> None: + tracked = validator.git(root, "ls-files").splitlines() + require( + not any(path.lower().endswith((".elf", ".self", ".sprx", ".pkg")) for path in tracked), + "target artifact tracked", + ) + + @case("24 Phase-1.0E hardware evidence is exact and bounded") + def _() -> None: + require( + record["tests"]["hardware_evidence_from_phase10e"] is True + and validator.device_run_is_exact(record), + "RUN C evidence rejected", + ) + + @case("25 RUN C has no retry or reconnect") + def _() -> None: + run = record["device_run"] + require( + run["retry_count"] == 0 and run["reconnect_count"] == 0, + "repeat action", + ) + + @case("26 missing D03 cannot be promoted to SDL") + def _() -> None: + value = dict(record) + value["device_run"] = dict(record["device_run"]) + value["device_run"]["last_proven_stage"] = "D03" + require(not validator.device_run_is_exact(value), "invented D03 accepted") + + @case("27 missing terminal cannot be promoted") + def _() -> None: + value = dict(record) + value["device_run"] = dict(record["device_run"]) + value["device_run"]["terminal_stage"] = "D12" + require(not validator.device_run_is_exact(value), "invented terminal accepted") + + @case("28 performed and forbidden actions remain separate") + def _() -> None: + actions = record["phase_actions"] + require( + all(actions[field] is True for field in validator.PERFORMED_ACTION_FIELDS) + and validator.all_false(actions, validator.FORBIDDEN_ACTION_FIELDS), + "action record is inconsistent", + ) + + @case("29 offline stdout retention is bounded") + def _() -> None: + require( + validator.offline_followup_is_bounded(record), + "bounded offline follow-up rejected", + ) + + @case("30 offline follow-up cannot add target or authority") + def _() -> None: + value = dict(record) + value["offline_followup"] = dict(record["offline_followup"]) + value["offline_followup"]["target_code_changed"] = True + value["offline_followup"]["authorization_changed"] = True + require( + not validator.offline_followup_is_bounded(value), + "target or authorization expansion accepted", + ) + + failures: list[str] = [] + for name, function in cases: + try: + function() + except Exception as error: # noqa: BLE001 - isolated test report + failures.append(f"{name}: {error}") + if len(cases) != 30: + failures.append(f"expected 30 cases, got {len(cases)}") + for failure in failures: + print(f"FAIL: {failure}", file=sys.stderr) + if failures: + return 1 + print("Phase-1.0E guardrails passed (30 cases)") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_retroarch_phase10ea.py b/tests/test_retroarch_phase10ea.py new file mode 100644 index 0000000..0ffb2bc --- /dev/null +++ b/tests/test_retroarch_phase10ea.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +from __future__ import annotations +import argparse, json, unittest +from pathlib import Path + +P = argparse.ArgumentParser() +P.add_argument("--root", type=Path, required=True) +ROOT = P.parse_args().root + + +class Phase10EA(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.data = json.loads( + (ROOT / "manifests/retroarch/phase-1.0ea-expanded-launch-evidence.json").read_text() + ) + + def test_candidate_claim_is_not_promoted_to_abi(self): + candidate = self.data["candidate"] + self.assertTrue(candidate["claims_fw960_launch_worked"]) + self.assertTrue(candidate["uses_sceLncUtilLaunchApp"]) + self.assertFalse(candidate["independent_public_parameter_provenance_supplied"]) + self.assertFalse(candidate["replaces_title_process_with_embedded_elf"]) + self.assertFalse(candidate["acceptable_chimera_runtime_dependency"]) + + def test_cleanup_contract_remains_closed(self): + cleanup = self.data["cleanup_audit"] + self.assertFalse(any(cleanup.values())) + + def test_gates_remain_fail_closed(self): + gates = self.data["gate_state"] + self.assertTrue(gates["fake00000_presence_proven"]) + self.assertTrue(gates["operator_browser_reachability_observed"]) + self.assertFalse(gates["independent_system_service_abi_proven"]) + self.assertFalse(gates["independent_lnc_parameter_abi_proven"]) + self.assertFalse(gates["embedded_elf_replacement_abi_proven"]) + self.assertFalse(gates["target_source_allowed"] or gates["target_build_allowed"]) + self.assertFalse(gates["device_action_allowed"]) + + def test_no_artifact_or_authority(self): + self.assertFalse(any(self.data["artifact"].values())) + self.assertFalse(any(self.data["authorization"].values())) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10eb.py b/tests/test_retroarch_phase10eb.py new file mode 100644 index 0000000..0c17302 --- /dev/null +++ b/tests/test_retroarch_phase10eb.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +from __future__ import annotations +import argparse, json, unittest +from pathlib import Path + +P = argparse.ArgumentParser() +P.add_argument("--root", type=Path, required=True) +ROOT = P.parse_args().root + + +class Phase10EB(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.data = json.loads( + (ROOT / "manifests/retroarch/phase-1.0eb-public-launch-matrix.json").read_text() + ) + + def test_search_preserves_public_source_boundary(self): + scope = self.data["scope"] + self.assertTrue(scope["public_source_only"]) + self.assertFalse(scope["candidate_binaries_executed"]) + self.assertFalse(scope["proprietary_libraries_opened"]) + self.assertFalse(scope["ps5_or_device_network_used"]) + + def test_independent_lnc_candidates_conflict(self): + candidates = self.data["candidates"] + self.assertEqual(candidates["ps5upload"]["parameter_bytes"], 24) + self.assertEqual(candidates["ps5_upload_suite"]["parameter_bytes"], 256) + self.assertFalse(self.data["correlation"]["independent_lnc_candidates_agree_on_parameter_size"]) + self.assertTrue(candidates["ps5_upload_suite"]["multiple_fallback_calls"]) + + def test_no_independent_complete_replacement(self): + correlation = self.data["correlation"] + self.assertEqual(correlation["independent_bigapp_replacement_lineages"], 1) + self.assertFalse(correlation["complete_launch_replacement_abi_proven"]) + self.assertFalse(correlation["firmware_960_videoout_ownership_proven"]) + self.assertFalse(correlation["bounded_verified_cleanup_proven"]) + + def test_external_request_is_complete_and_gate_closed(self): + self.assertEqual(len(self.data["required_external_evidence"]), 7) + self.assertFalse(any(self.data["decision"][key] for key in ( + "new_adr_allowed", "target_source_allowed", "target_build_allowed", + "device_authorization_actionable", "device_action_allowed"))) + self.assertFalse(any(self.data["authorization"].values())) + + +if __name__ == "__main__": + unittest.main(argv=[__file__]) diff --git a/tests/test_retroarch_phase10f.py b/tests/test_retroarch_phase10f.py new file mode 100644 index 0000000..39f5dc3 --- /dev/null +++ b/tests/test_retroarch_phase10f.py @@ -0,0 +1,190 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Host-only policy mutation tests for Phase-1.0F.""" + +from __future__ import annotations + +import argparse +import importlib.util +from pathlib import Path +import sys +from typing import Callable + + +def load_validator(path: Path): + spec = importlib.util.spec_from_file_location("phase10f_validator", path) + assert spec and spec.loader + module = importlib.util.module_from_spec(spec) + sys.modules[spec.name] = module + spec.loader.exec_module(module) + return module + + +def require(condition: bool, message: str) -> None: + if not condition: + raise RuntimeError(message) + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + root = args.root.resolve() + validator = load_validator(root / "tools/validate_retroarch_phase10f.py") + record = validator.load_json( + root / "manifests/retroarch/phase-1.0f-startup-interval.json" + ) + phase10e = validator.load_json( + root / "manifests/retroarch/phase-1.0e-result-channel.json" + ) + cases: list[tuple[str, Callable[[], None]]] = [] + + def case(name: str): + def register(function: Callable[[], None]) -> Callable[[], None]: + cases.append((name, function)) + return function + return register + + @case("01 all authorizations are false") + def _() -> None: + require(validator.all_false(record["authorizations"], validator.AUTHORIZATION_FIELDS), "authorization active") + + @case("02 later connection authorization fails") + def _() -> None: + value = dict(record["authorizations"]) + value["ps5_connection_authorized"] = True + require(not validator.all_false(value, validator.AUTHORIZATION_FIELDS), "connection accepted") + + @case("03 no device action is recorded") + def _() -> None: + require(validator.all_false(record["phase_actions"], validator.ACTION_FIELDS), "device action recorded") + + @case("04 exact reproducible artifact passes") + def _() -> None: + require(validator.artifact_is_exact(record["artifact"]), "artifact rejected") + + @case("05 differing second build fails") + def _() -> None: + value = dict(record["artifact"]) + value["clean_build_sha256"] = [validator.ARTIFACT_SHA256, "0" * 64] + require(not validator.artifact_is_exact(value), "mismatch accepted") + + @case("06 execution eligibility fails") + def _() -> None: + value = dict(record["artifact"]) + value["execution_eligible"] = True + require(not validator.artifact_is_exact(value), "eligibility accepted") + + @case("07 bounded F protocol passes") + def _() -> None: + require(validator.protocol_is_bounded(record["result_protocol"]), "protocol rejected") + + @case("08 Phase E magic fails") + def _() -> None: + value = dict(record["result_protocol"]) + value["magic"] = "CHD10E01" + require(not validator.protocol_is_bounded(value), "old magic accepted") + + @case("09 interval notification fails") + def _() -> None: + value = dict(record["result_protocol"]) + value["interval_notification_calls"] = 1 + require(not validator.protocol_is_bounded(value), "notification accepted") + + @case("10 target retry fails") + def _() -> None: + value = dict(record["result_protocol"]) + value["target_write_retry"] = True + require(not validator.protocol_is_bounded(value), "retry accepted") + + @case("11 all fifteen interval callsites are bound") + def _() -> None: + require(validator.interval_callsites_are_complete(record["interval_callsites"]), "callsites rejected") + + @case("12 duplicate callsite address fails") + def _() -> None: + values = [dict(item) for item in record["interval_callsites"]] + values[-1]["address"] = values[0]["address"] + require(not validator.interval_callsites_are_complete(values), "duplicate accepted") + + @case("13 ELF and imports match Phase E") + def _() -> None: + require(validator.elf_is_closed(record["elf"], phase10e), "ELF rejected") + + @case("14 RWX segment fails") + def _() -> None: + value = dict(record["elf"]) + value["program_headers"] = [dict(item) for item in record["elf"]["program_headers"]] + value["program_headers"][0]["flags"] = "RWE" + require(not validator.elf_is_closed(value, phase10e), "RWX accepted") + + @case("15 new socket import fails") + def _() -> None: + value = dict(record["elf"]) + value["undefined_symbols"] = list(record["elf"]["undefined_symbols"]) + ["socket"] + value["undefined_symbol_count"] += 1 + require(not validator.elf_is_closed(value, phase10e), "socket accepted") + + @case("16 init array fails") + def _() -> None: + value = dict(record["elf"]) + value["init_array_size"] = 8 + require(not validator.elf_is_closed(value, phase10e), "init array accepted") + + @case("17 parser is offline only") + def _() -> None: + require(validator.parser_is_offline_only(record["host_parser"]), "parser rejected") + + @case("18 live interval activation fails") + def _() -> None: + value = dict(record["host_parser"]) + value["live_interval_activation_available"] = True + require(not validator.parser_is_offline_only(value), "live activation accepted") + + @case("19 prior RUN C authority is not inherited") + def _() -> None: + prior = record["prior_evidence"] + require(prior["authorization_consumed"] is True and prior["authority_inherited"] is False, "authority inherited") + + @case("20 Phase F tests are not hardware evidence") + def _() -> None: + require(record["tests"]["hardware_evidence_from_phase10f"] is False, "offline test promoted") + + @case("21 startup remains side-effecting") + def _() -> None: + require(record["startup_effects"]["side_effect_free"] is False, "side effects hidden") + + @case("22 inactive approval grants nothing") + def _() -> None: + text = (root / "docs/approvals/phase-1.0f-device-test-template.md").read_text(encoding="utf-8") + require("ps5_connection_authorized=false" in text and "result_receive_authorized=false" in text, "template authorizes action") + + @case("23 no target artifact is tracked") + def _() -> None: + tracked = validator.git(root, "ls-files").splitlines() + require(not any(path.lower().endswith((".elf", ".self", ".sprx", ".pkg", ".map")) for path in tracked), "artifact tracked") + + @case("24 normalized disassembly is reproducible") + def _() -> None: + require( + record["static_reachability"]["normalized_disassembly_sha256"] + == [validator.DISASSEMBLY_SHA256] * 2, + "disassembly mismatch", + ) + + failures: list[str] = [] + for name, function in cases: + try: + function() + print(f"PASS {name}") + except Exception as error: # noqa: BLE001 - mutation harness + failures.append(f"{name}: {error}") + print(f"FAIL {name}: {error}") + if failures: + return 1 + print(f"Phase-1.0F policy tests passed: {len(cases)}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_retroarch_phase10g.py b/tests/test_retroarch_phase10g.py new file mode 100644 index 0000000..31dcbce --- /dev/null +++ b/tests/test_retroarch_phase10g.py @@ -0,0 +1,195 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Host-only policy mutation tests for Phase-1.0G.""" + +from __future__ import annotations + +import argparse +import importlib.util +from pathlib import Path +import sys +from typing import Callable + + +def load_validator(path: Path): + spec = importlib.util.spec_from_file_location("phase10g_validator", path) + assert spec and spec.loader + module = importlib.util.module_from_spec(spec) + sys.modules[spec.name] = module + spec.loader.exec_module(module) + return module + + +def require(condition: bool, message: str) -> None: + if not condition: + raise RuntimeError(message) + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + root = args.root.resolve() + validator = load_validator(root / "tools/validate_retroarch_phase10g.py") + record = validator.load_json(root / "manifests/retroarch/phase-1.0g-one-shot-runner.json") + approval = validator.load_json(root / "manifests/retroarch/phase-1.0g-one-shot-approval-template.json") + phase10f = validator.load_json(root / "manifests/retroarch/phase-1.0f-startup-interval.json") + cases: list[tuple[str, Callable[[], None]]] = [] + + def case(name: str): + def register(function: Callable[[], None]) -> Callable[[], None]: + cases.append((name, function)) + return function + return register + + @case("01 all authorizations remain false") + def _() -> None: + require(validator.all_false(record["authorizations"], validator.AUTHORIZATION_FIELDS), "authorization active") + + @case("02 connection authorization fails inactive policy") + def _() -> None: + value = dict(record["authorizations"]) + value["ps5_connection_authorized"] = True + require(not validator.all_false(value, validator.AUTHORIZATION_FIELDS), "connection accepted") + + @case("03 no device actions occurred") + def _() -> None: + require(validator.all_false(record["phase_actions"], validator.ACTION_FIELDS), "device action recorded") + + @case("04 unchanged inactive artifact passes") + def _() -> None: + require(validator.artifact_is_inactive(record["artifact"], phase10f), "artifact rejected") + + @case("05 artifact hash mismatch fails") + def _() -> None: + value = dict(record["artifact"]) + value["sha256"] = "0" * 64 + require(not validator.artifact_is_inactive(value, phase10f), "hash mismatch accepted") + + @case("06 execution eligibility fails") + def _() -> None: + value = dict(record["artifact"]) + value["execution_eligible"] = True + require(not validator.artifact_is_inactive(value, phase10f), "eligible artifact accepted") + + @case("07 exact F protocol passes") + def _() -> None: + require(validator.protocol_is_exact(record["result_protocol"]), "protocol rejected") + + @case("08 E protocol fails") + def _() -> None: + value = dict(record["result_protocol"]) + value["magic"] = "CHD10E01" + require(not validator.protocol_is_exact(value), "old protocol accepted") + + @case("09 inactive bounded runner passes") + def _() -> None: + require(validator.runner_is_inactive_and_bounded(record["runner"]), "runner rejected") + + @case("10 protocol activation fails") + def _() -> None: + value = dict(record["runner"]) + value["protocol_activation_authorized"] = True + require(not validator.runner_is_inactive_and_bounded(value), "activation accepted") + + @case("11 tracked run id fails") + def _() -> None: + value = dict(record["runner"]) + value["run_id"] = "run-1" + require(not validator.runner_is_inactive_and_bounded(value), "run id accepted") + + @case("12 tracked target fails") + def _() -> None: + value = dict(record["runner"]) + value["tracked_target"] = "device" + require(not validator.runner_is_inactive_and_bounded(value), "target accepted") + + @case("13 missing attempt receipt fails") + def _() -> None: + value = dict(record["runner"]) + value["attempt_receipt_required"] = False + require(not validator.runner_is_inactive_and_bounded(value), "missing receipt accepted") + + @case("14 receipt after connect fails") + def _() -> None: + value = dict(record["runner"]) + value["attempt_receipt_written_before_connect"] = False + require(not validator.runner_is_inactive_and_bounded(value), "late receipt accepted") + + @case("15 nondurable receipt fails") + def _() -> None: + value = dict(record["runner"]) + value["attempt_receipt_durable_fsync"] = False + require(not validator.runner_is_inactive_and_bounded(value), "nondurable receipt accepted") + + @case("16 retry fails") + def _() -> None: + value = dict(record["runner"]) + value["retry"] = True + require(not validator.runner_is_inactive_and_bounded(value), "retry accepted") + + @case("17 reconnect fails") + def _() -> None: + value = dict(record["runner"]) + value["reconnect"] = True + require(not validator.runner_is_inactive_and_bounded(value), "reconnect accepted") + + @case("18 approval template is inactive") + def _() -> None: + require(validator.approval_template_is_inactive(approval), "approval rejected") + + @case("19 authorized tracked approval fails") + def _() -> None: + value = dict(approval) + value["authorized"] = True + require(not validator.approval_template_is_inactive(value), "active approval accepted") + + @case("20 target in tracked approval fails") + def _() -> None: + value = dict(approval) + value["target"] = "device" + require(not validator.approval_template_is_inactive(value), "approval target accepted") + + @case("21 approval run id fails") + def _() -> None: + value = dict(approval) + value["run_id"] = "run-1" + require(not validator.approval_template_is_inactive(value), "approval run id accepted") + + @case("22 approval retry fails") + def _() -> None: + value = dict(approval) + value["automatic_retry"] = True + require(not validator.approval_template_is_inactive(value), "approval retry accepted") + + @case("23 all four future maxima are exactly one") + def _() -> None: + require(all(record["runner"][field] == 1 for field in ( + "maximum_connections", "maximum_transfers", "maximum_executions", "maximum_result_receives" + )), "one-shot maximum changed") + + @case("24 host tests are not hardware evidence") + def _() -> None: + require(record["tests"]["hardware_evidence_from_phase10g"] is False, "host result promoted") + + @case("25 no target artifact is tracked") + def _() -> None: + tracked = validator.git(root, "ls-files").splitlines() + require(not any(path.lower().endswith((".elf", ".self", ".sprx", ".pkg", ".map")) for path in tracked), "artifact tracked") + + failures: list[str] = [] + for name, function in cases: + try: + function() + print(f"PASS {name}") + except Exception as error: # noqa: BLE001 - mutation harness + failures.append(f"{name}: {error}") + print(f"FAIL {name}: {error}") + if failures: + return 1 + print(f"Phase-1.0G policy tests passed: {len(cases)}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_retroarch_phase10g_result.py b/tests/test_retroarch_phase10g_result.py new file mode 100644 index 0000000..196a7d7 --- /dev/null +++ b/tests/test_retroarch_phase10g_result.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Mutation guardrails for the consumed Phase-1.0G result.""" + +from __future__ import annotations + +import argparse +import importlib.util +from pathlib import Path +import sys + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + root = args.root.resolve() + spec = importlib.util.spec_from_file_location( + "phase10g_result", root / "tools/validate_retroarch_phase10g_result.py" + ) + assert spec and spec.loader + module = importlib.util.module_from_spec(spec) + sys.modules[spec.name] = module + spec.loader.exec_module(module) + record = module.load(root / "manifests/retroarch/phase-1.0g-device-result.json") + cases = [] + + def check(name, condition): + cases.append((name, bool(condition))) + + check("current authorizations false", module.all_false(record["current_authorizations"])) + active = dict(record["current_authorizations"]) + active["ps5_connection_authorized"] = True + check("future connection rejected", not module.all_false(active)) + check("one-shot transport exact", module.transport_is_exact(record["transport"])) + retry = dict(record["transport"]) + retry["retry_count"] = 1 + check("retry rejected", not module.transport_is_exact(retry)) + reconnect = dict(record["transport"]) + reconnect["reconnect_count"] = 1 + check("reconnect rejected", not module.transport_is_exact(reconnect)) + truncated = dict(record["transport"]) + truncated["raw_stream_truncated"] = True + check("truncation rejected", not module.transport_is_exact(truncated)) + check("frame sequence exact", module.protocol_is_exact(record["protocol_result"])) + invented = dict(record["protocol_result"]) + invented["stages"] = list(invented["stages"]) + ["I04"] + invented["frame_count"] = 8 + check("invented I04 rejected", not module.protocol_is_exact(invented)) + terminal = dict(record["protocol_result"]) + terminal["terminal_stage"] = "D11" + check("invented terminal rejected", not module.protocol_is_exact(terminal)) + check("authorization consumed", record["authorization"]["consumed"] is True) + check("authority not inherited", record["authorization"]["authority_inherited_by_future_action"] is False) + check("no device write", record["performed_actions"]["device_write_performed"] is False) + check("no installation", record["performed_actions"]["installation_performed"] is False) + check("no autoload", record["performed_actions"]["autoload_performed"] is False) + check("SDL not claimed", record["source_binding"]["sdl_videoout_reached"] is False) + check("rendering not claimed", record["source_binding"]["rendering_reached"] is False) + check("cleanup unproven", record["source_binding"]["terminal_cleanup_proven"] is False) + + failed = [name for name, passed in cases if not passed] + for name, passed in cases: + print(f"{'PASS' if passed else 'FAIL'} {name}") + if failed: + return 1 + print(f"Phase-1.0G result guardrails passed: {len(cases)}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_retroarch_phase10h.py b/tests/test_retroarch_phase10h.py new file mode 100644 index 0000000..c20d466 --- /dev/null +++ b/tests/test_retroarch_phase10h.py @@ -0,0 +1,158 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Host-only policy mutation tests for Phase-1.0H.""" + +from __future__ import annotations + +import argparse +import importlib.util +from pathlib import Path +import sys +from typing import Callable + + +def load_validator(path: Path): + spec = importlib.util.spec_from_file_location("phase10h_validator", path) + assert spec and spec.loader + module = importlib.util.module_from_spec(spec) + sys.modules[spec.name] = module + spec.loader.exec_module(module) + return module + + +def require(condition: bool, message: str) -> None: + if not condition: + raise RuntimeError(message) + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + root = args.root.resolve() + validator = load_validator(root / "tools/validate_retroarch_phase10h.py") + record = validator.load_json(root / "manifests/retroarch/phase-1.0h-startup-args.json") + phase10f = validator.load_json(root / "manifests/retroarch/phase-1.0f-startup-interval.json") + cases: list[tuple[str, Callable[[], None]]] = [] + + def case(name: str): + def register(function: Callable[[], None]) -> Callable[[], None]: + cases.append((name, function)) + return function + return register + + @case("01 all authorizations remain false") + def _() -> None: + require(validator.all_false(record["authorizations"], validator.AUTHORIZATION_FIELDS), "authorization active") + + @case("02 connection authorization fails") + def _() -> None: + value = dict(record["authorizations"]); value["ps5_connection_authorized"] = True + require(not validator.all_false(value, validator.AUTHORIZATION_FIELDS), "connection accepted") + + @case("03 no device action occurred") + def _() -> None: + require(validator.all_false(record["phase_actions"], validator.ACTION_FIELDS), "device action recorded") + + @case("04 exact artifact passes") + def _() -> None: + require(validator.artifact_is_exact(record["artifact"]), "artifact rejected") + + @case("05 different second build fails") + def _() -> None: + value = dict(record["artifact"]); value["clean_build_sha256"] = [validator.ARTIFACT_SHA256, "0" * 64] + require(not validator.artifact_is_exact(value), "mismatch accepted") + + @case("06 execution eligibility fails") + def _() -> None: + value = dict(record["artifact"]); value["execution_eligible"] = True + require(not validator.artifact_is_exact(value), "eligible artifact accepted") + + @case("07 minimal correction passes") + def _() -> None: + require(validator.correction_is_minimal(record["correction"]), "correction rejected") + + @case("08 content path fails") + def _() -> None: + value = dict(record["correction"]); value["content_path"] = "/data/content" + require(not validator.correction_is_minimal(value), "content path accepted") + + @case("09 menu enable fails") + def _() -> None: + value = dict(record["correction"]); value["have_menu"] = True + require(not validator.correction_is_minimal(value), "menu accepted") + + @case("10 argc one fails") + def _() -> None: + value = dict(record["correction"]); value["modeled_argc"] = 1 + require(not validator.correction_is_minimal(value), "argc one accepted") + + @case("11 exact H protocol passes") + def _() -> None: + require(validator.protocol_is_fail_closed(record["result_protocol"]), "protocol rejected") + + @case("12 F magic fails") + def _() -> None: + value = dict(record["result_protocol"]); value["magic"] = "CHD10F01" + require(not validator.protocol_is_fail_closed(value), "F magic accepted") + + @case("13 ungated protocol activation fails") + def _() -> None: + value = dict(record["result_protocol"]); value["protocol_activation_authorized"] = True + require(not validator.protocol_is_fail_closed(value), "active tracked protocol accepted") + + @case("14 retry fails") + def _() -> None: + value = dict(record["result_protocol"]); value["target_write_retry"] = True + require(not validator.protocol_is_fail_closed(value), "retry accepted") + + @case("15 ELF matches Phase F") + def _() -> None: + require(validator.elf_matches_phase10f(record["elf"], phase10f), "ELF rejected") + + @case("16 RWX fails") + def _() -> None: + value = dict(record["elf"]); value["rwx_load_segment_count"] = 1 + require(not validator.elf_matches_phase10f(value, phase10f), "RWX accepted") + + @case("17 new DT_NEEDED fails") + def _() -> None: + value = dict(record["elf"]); value["dt_needed"] = list(value["dt_needed"]) + ["unexpected.sprx"] + require(not validator.elf_matches_phase10f(value, phase10f), "module accepted") + + @case("18 Phase G authority is consumed") + def _() -> None: + require(record["prior_evidence"]["authorization_consumed"] is True and record["prior_evidence"]["authority_inherited"] is False, "authority inherited") + + @case("19 host result is not hardware evidence") + def _() -> None: + require(record["tests"]["hardware_evidence_from_phase10h"] is False, "host evidence promoted") + + @case("20 inactive approval grants nothing") + def _() -> None: + text = (root / "docs/approvals/phase-1.0h-device-test-template.md").read_text(encoding="utf-8") + require("ps5_connection_authorized=false" in text and "result_receive_authorized=false" in text, "template authorizes action") + + @case("21 no target artifact is tracked") + def _() -> None: + tracked = validator.git(root, "ls-files").splitlines() + require(not any(path.lower().endswith((".elf", ".self", ".sprx", ".pkg", ".map")) for path in tracked), "artifact tracked") + + @case("22 normalized disassembly is reproducible") + def _() -> None: + require(record["elf"]["normalized_disassembly_sha256"] == [validator.DISASSEMBLY_SHA256] * 2, "disassembly differs") + + failures: list[str] = [] + for name, function in cases: + try: + function(); print(f"PASS {name}") + except Exception as error: # noqa: BLE001 - mutation harness + failures.append(f"{name}: {error}"); print(f"FAIL {name}: {error}") + if failures: + return 1 + print(f"Phase-1.0H policy tests passed: {len(cases)}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_retroarch_phase10h_result.py b/tests/test_retroarch_phase10h_result.py new file mode 100644 index 0000000..12234a7 --- /dev/null +++ b/tests/test_retroarch_phase10h_result.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Mutation guardrails for the consumed Phase-1.0H result.""" + +from __future__ import annotations + +import argparse +import importlib.util +from pathlib import Path +import sys + + +def load(path: Path): + spec = importlib.util.spec_from_file_location("phase10h_result", path) + assert spec and spec.loader + module = importlib.util.module_from_spec(spec) + sys.modules[spec.name] = module + spec.loader.exec_module(module) + return module + + +def require(value: bool, message: str) -> None: + if not value: + raise RuntimeError(message) + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + root = args.root.resolve() + validator = load(root / "tools/validate_retroarch_phase10h_result.py") + record = validator.load_json(root / "manifests/retroarch/phase-1.0h-device-result.json") + cases = [] + + def case(name): + def register(function): + cases.append((name, function)); return function + return register + + @case("01 result sequence is exact") + def _(): require(validator.result_is_exact(record["protocol_result"]), "result rejected") + + @case("02 invented D11 fails") + def _(): + value = dict(record["protocol_result"]); value["stages"] = list(value["stages"]) + ["D11"]; value["frame_count"] += 1 + require(not validator.result_is_exact(value), "D11 accepted") + + @case("03 successful flip claim fails") + def _(): + value = dict(record["protocol_result"]); value["raw_results"] = dict(value["raw_results"]); value["raw_results"]["D07"] = 0 + require(not validator.result_is_exact(value), "successful flip accepted") + + @case("04 transport is one shot") + def _(): require(validator.transport_is_one_shot(record["transport"]), "transport rejected") + + @case("05 retry fails") + def _(): + value = dict(record["transport"]); value["retry_count"] = 1 + require(not validator.transport_is_one_shot(value), "retry accepted") + + @case("06 reconnect fails") + def _(): + value = dict(record["transport"]); value["reconnect_count"] = 1 + require(not validator.transport_is_one_shot(value), "reconnect accepted") + + @case("07 all current authorizations are false") + def _(): require(validator.all_false(record["current_authorizations"], validator.AUTHORIZATION_FIELDS), "authority active") + + @case("08 future execution authority fails") + def _(): + value = dict(record["current_authorizations"]); value["device_execution_authorized"] = True + require(not validator.all_false(value, validator.AUTHORIZATION_FIELDS), "authority accepted") + + @case("09 authorization is consumed") + def _(): require(record["authorization"]["consumed"] is True and record["authorization"]["authority_inherited_by_future_action"] is False, "authority reusable") + + @case("10 visible presentation remains unproven") + def _(): require(record["source_binding"]["visible_presentation"] == "UNPROVEN", "visibility invented") + + @case("11 cleanup remains unproven") + def _(): require(record["source_binding"]["complete_cleanup"] == "UNPROVEN", "cleanup invented") + + @case("12 write operation remains unobserved") + def _(): require(record["source_binding"]["exact_blocked_write_operation"] == "UNOBSERVED", "write operation invented") + + failures = [] + for name, function in cases: + try: + function(); print(f"PASS {name}") + except Exception as error: # noqa: BLE001 - mutation harness + failures.append(f"{name}: {error}"); print(f"FAIL {name}: {error}") + if failures: + return 1 + print(f"Phase-1.0H result guardrails passed: {len(cases)}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_retroarch_phase10i.py b/tests/test_retroarch_phase10i.py new file mode 100644 index 0000000..a16af58 --- /dev/null +++ b/tests/test_retroarch_phase10i.py @@ -0,0 +1,118 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Mutation guardrails for the Phase-1.0I offline postmortem.""" + +from __future__ import annotations + +import argparse +import importlib.util +from pathlib import Path +import sys + + +def load(path: Path): + spec = importlib.util.spec_from_file_location("phase10i", path) + assert spec and spec.loader + module = importlib.util.module_from_spec(spec) + sys.modules[spec.name] = module + spec.loader.exec_module(module) + return module + + +def require(value: bool, message: str) -> None: + if not value: + raise RuntimeError(message) + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + root = args.root.resolve() + validator = load(root / "tools/validate_retroarch_phase10i.py") + record = validator.load_json(root / "manifests/retroarch/phase-1.0i-flip-and-write-analysis.json") + cases = [] + + def case(name): + def register(function): + cases.append((name, function)) + return function + return register + + @case("01 all authorizations remain false") + def _(): require(validator.all_false(record["current_authorizations"], validator.AUTHORIZATION_FIELDS), "authorization active") + + @case("02 a device authorization fails") + def _(): + value = dict(record["current_authorizations"]); value["device_execution_authorized"] = True + require(not validator.all_false(value, validator.AUTHORIZATION_FIELDS), "device authorization accepted") + + @case("03 no Phase I target or device action occurred") + def _(): require(validator.all_false(record["phase_actions"], validator.ACTION_FIELDS), "action recorded") + + @case("04 write interval and candidates are bounded") + def _(): require(validator.write_analysis_is_bounded(record["write_firewall"]), "write analysis rejected") + + @case("05 exact write operation claim fails") + def _(): + value = dict(record["write_firewall"]); value["exact_operation"] = "OPEN" + require(not validator.write_analysis_is_bounded(value), "invented operation accepted") + + @case("06 successful device write claim fails") + def _(): + value = dict(record["write_firewall"]); value["write_succeeded"] = True + require(not validator.write_analysis_is_bounded(value), "device write accepted") + + @case("07 later initialization was not stopped") + def _(): require(record["write_firewall"]["shutdown_stopped_later_initialization"] is False, "shutdown behavior invented") + + @case("08 flip tuple and return are exact") + def _(): require(validator.flip_analysis_is_exact(record["first_flip"]), "flip evidence rejected") + + @case("09 successful flip claim fails") + def _(): + value = dict(record["first_flip"]); value["submit_raw"] = 0 + require(not validator.flip_analysis_is_exact(value), "successful flip accepted") + + @case("10 event wait claim fails") + def _(): + value = dict(record["first_flip"]); value["event_wait_attempted"] = True + require(not validator.flip_analysis_is_exact(value), "event wait accepted") + + @case("11 errno remains unproven") + def _(): require(record["first_flip"]["original_errno"] == "UNPROVEN", "errno invented") + + @case("12 frame mismatch is not root cause") + def _(): require(record["source_inconsistency"]["classification"] == "STRONG_SOURCE_CANDIDATE_NOT_PROVEN_ROOT_CAUSE", "root cause promoted") + + @case("13 public SDK prototype remains absent") + def _(): require(record["abi_evidence"]["public_sdk_prototype_present"] is False, "SDK prototype invented") + + @case("14 decision remains fail closed") + def _(): require(validator.decision_is_fail_closed(record["decision"]), "decision opened") + + @case("15 device test readiness fails") + def _(): + value = dict(record["decision"]); value["next_device_test_ready"] = True + require(not validator.decision_is_fail_closed(value), "device retest opened") + + @case("16 no new artifact exists") + def _(): require(record["decision"]["new_artifact_created_in_phase10i"] is False, "artifact invented") + + @case("17 host tests are not hardware evidence") + def _(): require(record["tests"]["hardware_evidence_from_phase10i"] is False, "host test promoted") + + failures = [] + for name, function in cases: + try: + function(); print(f"PASS {name}") + except Exception as error: # noqa: BLE001 - mutation harness + failures.append(f"{name}: {error}"); print(f"FAIL {name}: {error}") + if failures: + return 1 + print(f"Phase-1.0I analysis guardrails passed: {len(cases)}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_retroarch_phase10j.py b/tests/test_retroarch_phase10j.py new file mode 100644 index 0000000..bb5dd89 --- /dev/null +++ b/tests/test_retroarch_phase10j.py @@ -0,0 +1,138 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Mutation guardrails for the Phase-1.0J offline artifact.""" + +from __future__ import annotations + +import argparse +import copy +import importlib.util +from pathlib import Path +import sys + + +def load(path: Path): + spec = importlib.util.spec_from_file_location("phase10j", path) + assert spec and spec.loader + module = importlib.util.module_from_spec(spec) + sys.modules[spec.name] = module + spec.loader.exec_module(module) + return module + + +def require(value: bool, message: str) -> None: + if not value: + raise RuntimeError(message) + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + root = args.root.resolve() + validator = load(root / "tools/validate_retroarch_phase10j.py") + record = validator.load_json(root / "manifests/retroarch/phase-1.0j-write-firewall-diagnostic.json") + cases = [] + + def case(name): + def register(function): + cases.append((name, function)) + return function + return register + + @case("01 artifact is exact and ineligible") + def _(): require(validator.artifact_is_exact_and_ineligible(record["artifact"]), "artifact rejected") + + @case("02 changed artifact hash fails") + def _(): + value = dict(record["artifact"]); value["sha256"] = "0" * 64 + require(not validator.artifact_is_exact_and_ineligible(value), "changed hash accepted") + + @case("03 execution eligibility fails") + def _(): + value = dict(record["artifact"]); value["execution_eligible"] = True + require(not validator.artifact_is_exact_and_ineligible(value), "eligible artifact accepted") + + @case("04 all current authorizations are false") + def _(): require(validator.all_false(record["current_authorizations"], validator.AUTHORIZATION_FIELDS), "authorization active") + + @case("05 connection authorization fails") + def _(): + value = dict(record["current_authorizations"]); value["ps5_connection_authorized"] = True + require(not validator.all_false(value, validator.AUTHORIZATION_FIELDS), "connection authorized") + + @case("06 no device action occurred") + def _(): require(validator.all_false(record["phase_actions"], validator.DEVICE_ACTION_FIELDS), "device action recorded") + + @case("07 offline build is recorded") + def _(): require(record["phase_actions"]["target_build_performed"] is True, "build hidden") + + @case("08 J protocol is exact") + def _(): require(validator.protocol_is_bounded(record["protocol"]), "protocol rejected") + + @case("09 protocol magic mutation fails") + def _(): + value = dict(record["protocol"]); value["magic"] = "CHD10H01" + require(not validator.protocol_is_bounded(value), "old magic accepted") + + @case("10 target socket claim fails") + def _(): + value = dict(record["protocol"]); value["target_socket_created"] = True + require(not validator.protocol_is_bounded(value), "socket creation accepted") + + @case("11 live runner J activation fails") + def _(): + value = dict(record["protocol"]); value["live_runner_supports_j"] = True + require(not validator.protocol_is_bounded(value), "live J runner accepted") + + @case("12 firewall stops before I04") + def _(): require(validator.firewall_is_fail_closed(record["write_firewall"]), "firewall rejected") + + @case("13 successful write claim fails") + def _(): + value = copy.deepcopy(record["write_firewall"]); value["write_succeeds"] = True + require(not validator.firewall_is_fail_closed(value), "successful write accepted") + + @case("14 late stop claim fails") + def _(): + value = copy.deepcopy(record["write_firewall"]); value["stop_before"] = "D04" + require(not validator.firewall_is_fail_closed(value), "late stop accepted") + + @case("15 flip instrumentation is bounded") + def _(): require(validator.flip_instrumentation_is_bounded(record["flip_diagnostic"]), "flip instrumentation rejected") + + @case("16 multiple submit claim fails") + def _(): + value = dict(record["flip_diagnostic"]); value["submit_count_maximum_per_reached_helper"] = 2 + require(not validator.flip_instrumentation_is_bounded(value), "multiple submit accepted") + + @case("17 reporting before errno capture fails") + def _(): + value = dict(record["flip_diagnostic"]); value["reporting_before_errno_save"] = True + require(not validator.flip_instrumentation_is_bounded(value), "clobbered errno accepted") + + @case("18 frame zero is not a root cause claim") + def _(): require(record["flip_diagnostic"]["root_cause_claimed"] is False, "root cause invented") + + @case("19 static audit rejects a receive import") + def _(): + value = copy.deepcopy(record["artifact_audit"]); value["receive_import"] = True + require(not validator.audit_is_bounded(value), "receive import accepted") + + @case("20 host tests are not hardware evidence") + def _(): require(record["tests"]["hardware_evidence_from_phase10j"] is False, "hardware proof invented") + + failures = [] + for name, function in cases: + try: + function(); print(f"PASS {name}") + except Exception as error: # noqa: BLE001 - mutation harness + failures.append(f"{name}: {error}"); print(f"FAIL {name}: {error}") + if failures: + return 1 + print(f"Phase-1.0J guardrails passed: {len(cases)}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_retroarch_phase10k.py b/tests/test_retroarch_phase10k.py new file mode 100644 index 0000000..ac80f55 --- /dev/null +++ b/tests/test_retroarch_phase10k.py @@ -0,0 +1,143 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Mutation guardrails for the inactive Phase-1.0K runner record.""" + +from __future__ import annotations + +import argparse +import copy +import importlib.util +from pathlib import Path +import sys + + +def load(path: Path): + spec = importlib.util.spec_from_file_location("phase10k", path) + assert spec and spec.loader + module = importlib.util.module_from_spec(spec) + sys.modules[spec.name] = module + spec.loader.exec_module(module) + return module + + +def require(value: bool, message: str) -> None: + if not value: + raise RuntimeError(message) + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + root = args.root.resolve() + validator = load(root / "tools/validate_retroarch_phase10k.py") + record = validator.load_json(root / "manifests/retroarch/phase-1.0k-write-diag-one-shot-runner.json") + approval = validator.load_json(root / "manifests/retroarch/phase-1.0k-one-shot-approval-template.json") + cases = [] + + def case(name): + def register(function): + cases.append((name, function)) + return function + return register + + @case("01 all authorizations remain false") + def _(): require(validator.all_false(record["authorizations"], validator.AUTHORIZATION_FIELDS), "authorization active") + + @case("02 connection authorization fails") + def _(): + value = dict(record["authorizations"]); value["ps5_connection_authorized"] = True + require(not validator.all_false(value, validator.AUTHORIZATION_FIELDS), "connection accepted") + + @case("03 no target or device action occurred") + def _(): require(validator.all_false(record["phase_actions"], validator.ACTION_FIELDS), "action recorded") + + @case("04 artifact remains ineligible") + def _(): require(validator.artifact_is_ineligible(record["artifact"]), "artifact rejected") + + @case("05 execution eligibility fails") + def _(): + value = dict(record["artifact"]); value["execution_eligible"] = True + require(not validator.artifact_is_ineligible(value), "eligible artifact accepted") + + @case("06 changed artifact hash fails") + def _(): + value = dict(record["artifact"]); value["sha256"] = "0" * 64 + require(not validator.artifact_is_ineligible(value), "changed hash accepted") + + @case("07 wire table is exact and inactive") + def _(): require(validator.protocol_is_exact_and_inactive(record["result_protocol"]), "protocol rejected") + + @case("08 alphabetical D13 placement fails") + def _(): + value = copy.deepcopy(record["result_protocol"]); value["wire_stages"][-2:] = ["D13", "C1"] + require(not validator.protocol_is_exact_and_inactive(value), "wrong table accepted") + + @case("09 D13 index mutation fails") + def _(): + value = dict(record["result_protocol"]); value["d13_wire_index"] = 13 + require(not validator.protocol_is_exact_and_inactive(value), "wrong D13 index accepted") + + @case("10 tracked target fails") + def _(): + value = dict(record["result_protocol"]); value["tracked_target"] = "TEST-NET" + require(not validator.protocol_is_exact_and_inactive(value), "tracked target accepted") + + @case("11 protocol activation fails") + def _(): + value = dict(record["result_protocol"]); value["protocol_activation_authorized"] = True + require(not validator.protocol_is_exact_and_inactive(value), "activation accepted") + + @case("12 runner is fail closed") + def _(): require(validator.runner_is_fail_closed(record["runner"]), "runner rejected") + + @case("13 free protocol selector fails") + def _(): + value = dict(record["runner"]); value["free_protocol_selector"] = True + require(not validator.runner_is_fail_closed(value), "free selector accepted") + + @case("14 retry fails") + def _(): + value = dict(record["runner"]); value["retry"] = True + require(not validator.runner_is_fail_closed(value), "retry accepted") + + @case("15 multiple connections fail") + def _(): + value = dict(record["runner"]); value["maximum_connections"] = 2 + require(not validator.runner_is_fail_closed(value), "multiple connections accepted") + + @case("16 receipt after connect fails") + def _(): + value = dict(record["runner"]); value["attempt_receipt_written_before_connect"] = False + require(not validator.runner_is_fail_closed(value), "late receipt accepted") + + @case("17 approval template is inactive") + def _(): require(validator.approval_is_inactive(approval), "approval rejected") + + @case("18 activated approval fails") + def _(): + value = dict(approval); value["authorized"] = True + require(not validator.approval_is_inactive(value), "active approval accepted") + + @case("19 nonzero action counts fail") + def _(): + value = dict(approval); value["execution_count"] = 1 + require(not validator.approval_is_inactive(value), "execution count accepted") + + @case("20 host tests are not hardware evidence") + def _(): require(record["tests"]["hardware_evidence_from_phase10k"] is False, "hardware proof invented") + + failures = [] + for name, function in cases: + try: + function(); print(f"PASS {name}") + except Exception as error: # noqa: BLE001 - mutation harness + failures.append(f"{name}: {error}"); print(f"FAIL {name}: {error}") + if failures: + return 1 + print(f"Phase-1.0K guardrails passed: {len(cases)}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_retroarch_phase10l.py b/tests/test_retroarch_phase10l.py new file mode 100644 index 0000000..a0ffade --- /dev/null +++ b/tests/test_retroarch_phase10l.py @@ -0,0 +1,119 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Mutation guardrails for the consumed Phase-1.0K device result.""" + +from __future__ import annotations + +import argparse +import importlib.util +from pathlib import Path +import sys + + +def load(path: Path): + spec = importlib.util.spec_from_file_location("phase10l", path) + assert spec and spec.loader + module = importlib.util.module_from_spec(spec) + sys.modules[spec.name] = module + spec.loader.exec_module(module) + return module + + +def require(value: bool, message: str) -> None: + if not value: + raise RuntimeError(message) + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + root = args.root.resolve() + validator = load(root / "tools/validate_retroarch_phase10l.py") + record = validator.load_json( + root / "manifests/retroarch/phase-1.0l-write-firewall-result-analysis.json" + ) + cases = [] + + def case(name): + def register(function): + cases.append((name, function)) + return function + return register + + @case("01 protocol result is exact") + def _(): require(validator.result_is_exact(record["protocol_result"]), "result rejected") + + @case("02 OPEN cannot replace observed MKDIR") + def _(): + value = dict(record["protocol_result"]); value["raw_results"] = dict(value["raw_results"]); value["raw_results"]["D13_first_blocked_write"] = 1 + require(not validator.result_is_exact(value), "invented OPEN accepted") + + @case("03 write count must remain one") + def _(): + value = dict(record["protocol_result"]); value["raw_results"] = dict(value["raw_results"]); value["raw_results"]["D13_write_block_count"] = 2 + require(not validator.result_is_exact(value), "invented second write accepted") + + @case("04 I04 cannot be invented") + def _(): + value = dict(record["protocol_result"]); value["stages"] = list(value["stages"]); value["stages"].insert(-2, "I04"); value["frame_count"] = 10 + require(not validator.result_is_exact(value), "invented I04 accepted") + + @case("05 transport is exact one shot") + def _(): require(validator.transport_is_one_shot(record["transport"]), "transport rejected") + + @case("06 retry fails") + def _(): + value = dict(record["transport"]); value["retry_count"] = 1 + require(not validator.transport_is_one_shot(value), "retry accepted") + + @case("07 reconnect fails") + def _(): + value = dict(record["transport"]); value["reconnect_count"] = 1 + require(not validator.transport_is_one_shot(value), "reconnect accepted") + + @case("08 all current authorizations are false") + def _(): require(validator.all_false(record["current_authorizations"], validator.AUTHORIZATION_FIELDS), "authority active") + + @case("09 future execution authority fails") + def _(): + value = dict(record["current_authorizations"]); value["device_execution_authorized"] = True + require(not validator.all_false(value, validator.AUTHORIZATION_FIELDS), "authority accepted") + + @case("10 authorization is consumed") + def _(): require(record["authorization"]["consumed"] is True and record["authorization"]["authority_inherited_by_future_action"] is False, "authority reusable") + + @case("11 source binding remains bounded") + def _(): require(validator.source_binding_is_bounded(record["source_binding"]), "source binding rejected") + + @case("12 runtime path remains unobserved") + def _(): require(record["source_binding"]["runtime_path"] == "UNOBSERVED", "runtime path invented") + + @case("13 SDL remains not reached") + def _(): require(record["source_binding"]["sdl_video"] == "NOT_REACHED", "SDL invented") + + @case("14 cleanup remains unproven") + def _(): require(record["source_binding"]["complete_cleanup"] == "UNPROVEN", "cleanup invented") + + @case("15 next candidate preserves firewall") + def _(): require(record["next_offline_candidate"]["preserve_global_write_firewall"] is True, "firewall weakened") + + @case("16 next target build remains unauthorized") + def _(): require(record["next_offline_candidate"]["target_build_authorized"] is False and record["next_offline_candidate"]["device_action_authorized"] is False, "new authority invented") + + failures = [] + for name, function in cases: + try: + function() + print(f"PASS {name}") + except Exception as error: # noqa: BLE001 - mutation harness + failures.append(f"{name}: {error}") + print(f"FAIL {name}: {error}") + if failures: + return 1 + print(f"Phase-1.0L result guardrails passed: {len(cases)}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_retroarch_phase10m.py b/tests/test_retroarch_phase10m.py new file mode 100644 index 0000000..b4ff853 --- /dev/null +++ b/tests/test_retroarch_phase10m.py @@ -0,0 +1,144 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Mutation guardrails for the Phase-1.0M offline artifact.""" + +from __future__ import annotations + +import argparse +import copy +import importlib.util +from pathlib import Path +import sys + + +def load(path: Path): + spec = importlib.util.spec_from_file_location("phase10m", path) + assert spec and spec.loader + module = importlib.util.module_from_spec(spec) + sys.modules[spec.name] = module + spec.loader.exec_module(module) + return module + + +def require(value: bool, message: str) -> None: + if not value: + raise RuntimeError(message) + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + root = args.root.resolve() + validator = load(root / "tools/validate_retroarch_phase10m.py") + record = validator.load_json( + root / "manifests/retroarch/phase-1.0m-write-free-defaults.json" + ) + cases = [] + + def case(name): + def register(function): + cases.append((name, function)) + return function + return register + + @case("01 artifact is exact and ineligible") + def _(): require(validator.artifact_is_exact_and_ineligible(record["artifact"]), "artifact rejected") + + @case("02 changed hash fails") + def _(): + value = dict(record["artifact"]); value["sha256"] = "0" * 64 + require(not validator.artifact_is_exact_and_ineligible(value), "changed hash accepted") + + @case("03 execution eligibility fails") + def _(): + value = dict(record["artifact"]); value["execution_eligible"] = True + require(not validator.artifact_is_exact_and_ineligible(value), "eligible artifact accepted") + + @case("04 correction is narrow") + def _(): require(validator.correction_is_narrow(record["source_correction"]), "correction rejected") + + @case("05 lost path derivation fails") + def _(): + value = dict(record["source_correction"]); value["path_derivation_preserved"] = False + require(not validator.correction_is_narrow(value), "lost derivation accepted") + + @case("06 runtime mkdir claim fails") + def _(): + value = dict(record["source_correction"]); value["runtime_effect"] = "PROVEN" + require(not validator.correction_is_narrow(value), "runtime claim accepted") + + @case("07 firewall is preserved") + def _(): require(validator.firewall_is_preserved(record["write_firewall"]), "firewall rejected") + + @case("08 missing wrapper fails") + def _(): + value = copy.deepcopy(record["write_firewall"]); value["linker_wrap_option_count"] = 16 + require(not validator.firewall_is_preserved(value), "weakened firewall accepted") + + @case("09 successful write semantics fail") + def _(): + value = copy.deepcopy(record["write_firewall"]); value["wrapper_errno"] = "SUCCESS" + require(not validator.firewall_is_preserved(value), "successful write accepted") + + @case("10 artifact audit is exact") + def _(): require(validator.audit_is_exact(record["artifact_audit"]), "audit rejected") + + @case("11 RWX segment fails") + def _(): + value = copy.deepcopy(record["artifact_audit"]); value["rwx_load_segments"] = 1 + require(not validator.audit_is_exact(value), "RWX accepted") + + @case("12 receive import fails") + def _(): + value = copy.deepcopy(record["artifact_audit"]); value["receive_import"] = True + require(not validator.audit_is_exact(value), "receive import accepted") + + @case("13 GNM import fails") + def _(): + value = copy.deepcopy(record["artifact_audit"]); value["gnm_imports"] = ["sceGnmSubmit"] + require(not validator.audit_is_exact(value), "GNM import accepted") + + @case("14 hardware evidence claim fails") + def _(): + value = copy.deepcopy(record["artifact_audit"]); value["hardware_evidence"] = True + require(not validator.audit_is_exact(value), "hardware claim accepted") + + @case("15 all current authorizations are false") + def _(): require(validator.all_false(record["current_authorizations"], validator.AUTHORIZATION_FIELDS), "authority active") + + @case("16 transfer authority fails") + def _(): + value = dict(record["current_authorizations"]); value["device_transfer_authorized"] = True + require(not validator.all_false(value, validator.AUTHORIZATION_FIELDS), "transfer authority accepted") + + @case("17 execution authority fails") + def _(): + value = dict(record["current_authorizations"]); value["device_execution_authorized"] = True + require(not validator.all_false(value, validator.AUTHORIZATION_FIELDS), "execution authority accepted") + + @case("18 no device action occurred") + def _(): require(validator.all_false(record["phase_actions"], validator.DEVICE_ACTION_FIELDS), "device action recorded") + + @case("19 offline build is recorded") + def _(): require(record["phase_actions"]["target_build_performed"] is True, "build omitted") + + @case("20 I04 remains unproven") + def _(): require(record["inherited_runtime_contract"]["i04"] == "UNPROVEN", "I04 invented") + + failures = [] + for name, function in cases: + try: + function() + print(f"PASS {name}") + except Exception as error: # noqa: BLE001 - mutation harness + failures.append(f"{name}: {error}") + print(f"FAIL {name}: {error}") + if failures: + return 1 + print(f"Phase-1.0M guardrails passed: {len(cases)}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_retroarch_phase10n.py b/tests/test_retroarch_phase10n.py new file mode 100644 index 0000000..8decc85 --- /dev/null +++ b/tests/test_retroarch_phase10n.py @@ -0,0 +1,142 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Mutation guardrails for the inactive Phase-1.0N runner record.""" + +from __future__ import annotations + +import argparse +import copy +import importlib.util +from pathlib import Path +import sys + + +def load(path: Path): + spec = importlib.util.spec_from_file_location("phase10n", path) + assert spec and spec.loader + module = importlib.util.module_from_spec(spec) + sys.modules[spec.name] = module + spec.loader.exec_module(module) + return module + + +def require(value: bool, message: str) -> None: + if not value: + raise RuntimeError(message) + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + root = args.root.resolve() + validator = load(root / "tools/validate_retroarch_phase10n.py") + record = validator.load_json(root / "manifests/retroarch/phase-1.0n-write-free-one-shot-runner.json") + approval = validator.load_json(root / "manifests/retroarch/phase-1.0n-one-shot-approval-template.json") + cases = [] + + def case(name): + def register(function): + cases.append((name, function)); return function + return register + + @case("01 all authorizations remain false") + def _(): require(validator.all_false(record["authorizations"], validator.AUTHORIZATION_FIELDS), "authorization active") + + @case("02 connection authorization fails") + def _(): + value = dict(record["authorizations"]); value["ps5_connection_authorized"] = True + require(not validator.all_false(value, validator.AUTHORIZATION_FIELDS), "connection accepted") + + @case("03 no target or device action occurred") + def _(): require(validator.all_false(record["phase_actions"], validator.ACTION_FIELDS), "action recorded") + + @case("04 artifact remains exact and ineligible") + def _(): require(validator.artifact_is_ineligible(record["artifact"]), "artifact rejected") + + @case("05 execution eligibility fails") + def _(): + value = dict(record["artifact"]); value["execution_eligible"] = True + require(not validator.artifact_is_ineligible(value), "eligible artifact accepted") + + @case("06 changed artifact hash fails") + def _(): + value = dict(record["artifact"]); value["sha256"] = "0" * 64 + require(not validator.artifact_is_ineligible(value), "changed hash accepted") + + @case("07 protocol is exact and inactive") + def _(): require(validator.protocol_is_exact_and_inactive(record["result_protocol"]), "protocol rejected") + + @case("08 phase K protocol label fails") + def _(): + value = dict(record["result_protocol"]); value["name"] = "PHASE_1_0K_WRITE_DIAG" + require(not validator.protocol_is_exact_and_inactive(value), "K label accepted") + + @case("09 alphabetical D13 placement fails") + def _(): + value = copy.deepcopy(record["result_protocol"]); value["wire_stages"][-2:] = ["D13", "C1"] + require(not validator.protocol_is_exact_and_inactive(value), "wrong table accepted") + + @case("10 tracked target fails") + def _(): + value = dict(record["result_protocol"]); value["tracked_target"] = "TEST-NET" + require(not validator.protocol_is_exact_and_inactive(value), "tracked target accepted") + + @case("11 protocol activation fails") + def _(): + value = dict(record["result_protocol"]); value["protocol_activation_authorized"] = True + require(not validator.protocol_is_exact_and_inactive(value), "activation accepted") + + @case("12 runner is fail closed") + def _(): require(validator.runner_is_fail_closed(record["runner"]), "runner rejected") + + @case("13 changed runner hash fails") + def _(): + value = dict(record["runner"]); value["source_sha256"] = "0" * 64 + require(not validator.runner_is_fail_closed(value), "changed runner accepted") + + @case("14 free protocol selector fails") + def _(): + value = dict(record["runner"]); value["free_protocol_selector"] = True + require(not validator.runner_is_fail_closed(value), "free selector accepted") + + @case("15 retry fails") + def _(): + value = dict(record["runner"]); value["retry"] = True + require(not validator.runner_is_fail_closed(value), "retry accepted") + + @case("16 multiple connections fail") + def _(): + value = dict(record["runner"]); value["maximum_connections"] = 2 + require(not validator.runner_is_fail_closed(value), "multiple connections accepted") + + @case("17 approval template is inactive") + def _(): require(validator.approval_is_inactive(approval), "approval rejected") + + @case("18 activated approval fails") + def _(): + value = dict(approval); value["authorized"] = True + require(not validator.approval_is_inactive(value), "active approval accepted") + + @case("19 Phase K scope fails") + def _(): + value = dict(approval); value["authorization_scope"] = "EXACT_ONE_SHOT_PHASE_1_0K" + require(not validator.approval_is_inactive(value), "consumed K scope accepted") + + @case("20 host tests are not hardware evidence") + def _(): require(record["tests"]["hardware_evidence_from_phase10n"] is False, "hardware proof invented") + + failures = [] + for name, function in cases: + try: + function(); print(f"PASS {name}") + except Exception as error: # noqa: BLE001 - mutation harness + failures.append(f"{name}: {error}"); print(f"FAIL {name}: {error}") + if failures: + return 1 + print(f"Phase-1.0N guardrails passed: {len(cases)}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_retroarch_phase10o.py b/tests/test_retroarch_phase10o.py new file mode 100644 index 0000000..e9f5c9a --- /dev/null +++ b/tests/test_retroarch_phase10o.py @@ -0,0 +1,137 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Mutation guardrails for the consumed Phase-1.0O result.""" + +from __future__ import annotations + +import argparse +import copy +import importlib.util +from pathlib import Path +import sys + + +def load(path: Path): + spec = importlib.util.spec_from_file_location("phase10o", path) + assert spec and spec.loader + module = importlib.util.module_from_spec(spec) + sys.modules[spec.name] = module + spec.loader.exec_module(module) + return module + + +def require(value: bool, message: str) -> None: + if not value: + raise RuntimeError(message) + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + root = args.root.resolve() + validator = load(root / "tools/validate_retroarch_phase10o.py") + record = validator.load_json(root / "manifests/retroarch/phase-1.0o-write-free-device-result.json") + cases = [] + + def case(name): + def register(function): + cases.append((name, function)); return function + return register + + @case("01 all current authorizations are false") + def _(): require(validator.all_false(record["current_authorizations"], validator.AUTHORIZATION_FIELDS), "authority active") + + @case("02 reauthorization fails") + def _(): + value = dict(record["current_authorizations"]); value["device_execution_authorized"] = True + require(not validator.all_false(value, validator.AUTHORIZATION_FIELDS), "reauthorization accepted") + + @case("03 artifact is consumed and ineligible") + def _(): require(validator.artifact_is_consumed_and_ineligible(record["artifact"]), "artifact rejected") + + @case("04 artifact eligibility fails") + def _(): + value = dict(record["artifact"]); value["execution_eligible"] = True + require(not validator.artifact_is_consumed_and_ineligible(value), "eligible artifact accepted") + + @case("05 artifact hash mutation fails") + def _(): + value = dict(record["artifact"]); value["sha256"] = "0" * 64 + require(not validator.artifact_is_consumed_and_ineligible(value), "wrong artifact accepted") + + @case("06 authorization is consumed") + def _(): require(validator.authorization_is_consumed(record["authorization"]), "receipt rejected") + + @case("07 reusable authority fails") + def _(): + value = dict(record["authorization"]); value["authority_inherited_by_future_action"] = True + require(not validator.authorization_is_consumed(value), "reusable authority accepted") + + @case("08 transport is exactly one shot") + def _(): require(validator.transport_is_exact_one_shot(record["transport"]), "transport rejected") + + @case("09 retry fails") + def _(): + value = dict(record["transport"]); value["retry_count"] = 1 + require(not validator.transport_is_exact_one_shot(value), "retry accepted") + + @case("10 reconnect fails") + def _(): + value = dict(record["transport"]); value["reconnect_count"] = 1 + require(not validator.transport_is_exact_one_shot(value), "reconnect accepted") + + @case("11 protocol result is exact") + def _(): require(validator.protocol_result_is_exact(record["protocol_result"]), "protocol rejected") + + @case("12 invented D13 fails") + def _(): + value = copy.deepcopy(record["protocol_result"]); value["stages"].insert(-2, "D13"); value["frame_count"] += 1 + require(not validator.protocol_result_is_exact(value), "invented D13 accepted") + + @case("13 invented successful flip fails") + def _(): + value = copy.deepcopy(record["protocol_result"]); value["raw_results"]["D07_flip_submit"] = 0 + require(not validator.protocol_result_is_exact(value), "successful flip invented") + + @case("14 invented errno fails") + def _(): + value = copy.deepcopy(record["protocol_result"]); value["raw_results"]["D07_saved_errno"] = 22 + require(not validator.protocol_result_is_exact(value), "errno invented") + + @case("15 terminal ordering is preserved") + def _(): require(record["protocol_result"]["frame_after_terminal"] == "D04_SEQUENCE_25", "ordering lost") + + @case("16 successful runner terminal classification is forbidden") + def _(): require(record["protocol_result"]["valid_terminal_frame_received_classification"] is False, "terminal success invented") + + @case("17 source binding is bounded") + def _(): require(validator.source_binding_is_bounded(record["source_binding"]), "source binding rejected") + + @case("18 visible presentation remains unproven") + def _(): + value = dict(record["source_binding"]); value["visible_presentation"] = "PROVEN" + require(not validator.source_binding_is_bounded(value), "visibility invented") + + @case("19 cleanup remains unproven") + def _(): + value = dict(record["source_binding"]); value["complete_cleanup"] = "PROVEN" + require(not validator.source_binding_is_bounded(value), "cleanup invented") + + @case("20 E104 remains a generic post-flip label") + def _(): require(record["source_binding"]["e104_is_generic_framebuffer_fail_label_after_early_flip_failure"] is True, "E104 overclaimed") + + failures = [] + for name, function in cases: + try: + function(); print(f"PASS {name}") + except Exception as error: # noqa: BLE001 - mutation harness + failures.append(f"{name}: {error}"); print(f"FAIL {name}: {error}") + if failures: + return 1 + print(f"Phase-1.0O guardrails passed: {len(cases)}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_retroarch_phase10p.py b/tests/test_retroarch_phase10p.py new file mode 100644 index 0000000..1109004 --- /dev/null +++ b/tests/test_retroarch_phase10p.py @@ -0,0 +1,141 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Mutation guardrails for the Phase-1.0P offline analysis.""" + +from __future__ import annotations + +import argparse +import copy +import importlib.util +from pathlib import Path +import sys + + +def load(path: Path): + spec = importlib.util.spec_from_file_location("phase10p", path) + assert spec and spec.loader + module = importlib.util.module_from_spec(spec) + sys.modules[spec.name] = module + spec.loader.exec_module(module) + return module + + +def require(value: bool, message: str) -> None: + if not value: + raise RuntimeError(message) + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + root = args.root.resolve() + validator = load(root / "tools/validate_retroarch_phase10p.py") + record = validator.load_json( + root / "manifests/retroarch/phase-1.0p-videoout-submit-analysis.json") + cases = [] + + def case(name): + def register(function): + cases.append((name, function)) + return function + return register + + @case("01 all authorizations are false") + def _(): require(validator.all_false(record["current_authorizations"], validator.AUTHORIZATION_FIELDS), "authority active") + + @case("02 execution authorization fails") + def _(): + value = dict(record["current_authorizations"]); value["device_execution_authorized"] = True + require(not validator.all_false(value, validator.AUTHORIZATION_FIELDS), "execution authority accepted") + + @case("03 automatic retry fails") + def _(): + value = dict(record["current_authorizations"]); value["automatic_retry"] = True + require(not validator.all_false(value, validator.AUTHORIZATION_FIELDS), "retry accepted") + + @case("04 bound inputs are exact") + def _(): require(validator.bound_inputs_are_exact(record["bound_inputs"]), "bound inputs rejected") + + @case("05 artifact hash mutation fails") + def _(): + value = dict(record["bound_inputs"]); value["artifact_sha256"] = "0" * 64 + require(not validator.bound_inputs_are_exact(value), "wrong artifact accepted") + + @case("06 trace hash mutation fails") + def _(): + value = dict(record["bound_inputs"]); value["trace_sha256"] = "0" * 64 + require(not validator.bound_inputs_are_exact(value), "wrong trace accepted") + + @case("07 exact call is bounded") + def _(): require(validator.exact_call_is_bounded(record["exact_call"]), "call rejected") + + @case("08 guessed buffer index fails") + def _(): + value = copy.deepcopy(record["exact_call"]); value["registers"]["esi"] = 1 + require(not validator.exact_call_is_bounded(value), "guessed index accepted") + + @case("09 invented successful submit fails") + def _(): + value = dict(record["exact_call"]); value["runtime_return"] = 0 + require(not validator.exact_call_is_bounded(value), "success invented") + + @case("10 invented errno fails") + def _(): + value = dict(record["exact_call"]); value["saved_errno"] = 22 + require(not validator.exact_call_is_bounded(value), "errno invented") + + @case("11 ABI claims fail closed") + def _(): require(validator.abi_claims_fail_closed(record["abi_evidence"]), "ABI record rejected") + + @case("12 semantic promotion fails") + def _(): + value = dict(record["abi_evidence"]); value["flip_mode_semantics"] = "PROVEN" + require(not validator.abi_claims_fail_closed(value), "mode promoted") + + @case("13 symbol is not ABI evidence") + def _(): require(record["decision"]["new_videoout_export_call_allowed"] is False, "new call allowed") + + @case("14 frame mismatch is rejected only as current cause") + def _(): require(record["candidate_matrix"]["diagnostic_normal_first_index_mismatch"] == "REJECTED_CURRENT_CAUSE", "candidate overclaim") + + @case("15 terminal ordering is preserved") + def _(): require(validator.terminal_order_is_not_promoted(record["terminal_ordering"]), "order rejected") + + @case("16 D12 cleanup promotion fails") + def _(): + value = dict(record["terminal_ordering"]); value["d12_proves_cleanup_complete"] = True + require(not validator.terminal_order_is_not_promoted(value), "cleanup invented") + + @case("17 parser relaxation fails") + def _(): + value = dict(record["terminal_ordering"]); value["host_parser_relaxation_allowed"] = True + require(not validator.terminal_order_is_not_promoted(value), "parser relaxation accepted") + + @case("18 decision is fail closed") + def _(): require(validator.decision_is_fail_closed(record["decision"]), "decision rejected") + + @case("19 parameter experiment fails") + def _(): + value = dict(record["decision"]); value["parameter_experiment_allowed"] = True + require(not validator.decision_is_fail_closed(value), "experiment allowed") + + @case("20 host analysis is not device evidence") + def _(): require(record["tests"]["hardware_claim_from_host_test"] is False, "host test promoted") + + failures = [] + for name, function in cases: + try: + function() + print(f"PASS {name}") + except Exception as error: # noqa: BLE001 - mutation harness + failures.append(f"{name}: {error}") + print(f"FAIL {name}: {error}") + if failures: + return 1 + print(f"Phase-1.0P guardrails passed: {len(cases)}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_retroarch_phase10q.py b/tests/test_retroarch_phase10q.py new file mode 100644 index 0000000..3ec5b49 --- /dev/null +++ b/tests/test_retroarch_phase10q.py @@ -0,0 +1,141 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Mutation guardrails for Phase-1.0Q public VideoOut evidence.""" + +from __future__ import annotations + +import argparse +import copy +import importlib.util +from pathlib import Path +import sys + + +def load(path: Path): + spec = importlib.util.spec_from_file_location("phase10q", path) + assert spec and spec.loader + module = importlib.util.module_from_spec(spec) + sys.modules[spec.name] = module + spec.loader.exec_module(module) + return module + + +def require(value: bool, message: str) -> None: + if not value: + raise RuntimeError(message) + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + root = args.root.resolve() + validator = load(root / "tools/validate_retroarch_phase10q.py") + record = validator.load_json( + root / "manifests/retroarch/phase-1.0q-public-videoout-evidence.json") + cases = [] + + def case(name): + def register(function): + cases.append((name, function)) + return function + return register + + @case("01 authorizations remain false") + def _(): require(validator.all_false(record["authorizations"], validator.AUTHORIZATION_FIELDS), "authority active") + + @case("02 target build authorization fails") + def _(): + value = dict(record["authorizations"]); value["target_build_authorized"] = True + require(not validator.all_false(value, validator.AUTHORIZATION_FIELDS), "target build allowed") + + @case("03 automatic retry fails") + def _(): + value = dict(record["authorizations"]); value["automatic_retry"] = True + require(not validator.all_false(value, validator.AUTHORIZATION_FIELDS), "retry allowed") + + @case("04 network scope is bounded") + def _(): require(validator.network_scope_is_bounded(record["network_scope"]), "scope rejected") + + @case("05 arbitrary mirror fails") + def _(): + value = dict(record["network_scope"]); value["arbitrary_mirrors"] = True + require(not validator.network_scope_is_bounded(value), "mirror accepted") + + @case("06 executed download fails") + def _(): + value = dict(record["network_scope"]); value["downloaded_code_executed"] = True + require(not validator.network_scope_is_bounded(value), "execution accepted") + + @case("07 SDK remains symbol only") + def _(): require(validator.sdk_evidence_is_symbol_only(record["sdk"]), "SDK rejected") + + @case("08 invented SDK header fails") + def _(): + value = dict(record["sdk"]); value["public_videoout_header"] = True + require(not validator.sdk_evidence_is_symbol_only(value), "header invented") + + @case("09 SDL remains one lineage") + def _(): require(validator.sdl_lineage_is_single(record["sdl_lineage"]), "SDL lineage rejected") + + @case("10 invented independent source fails") + def _(): + value = dict(record["sdl_lineage"]); value["independent_abi_source_cited"] = True + require(not validator.sdl_lineage_is_single(value), "independence invented") + + @case("11 official archives do not corroborate") + def _(): require(validator.archives_do_not_corroborate(record["official_project_archives"]), "archives rejected") + + @case("12 invented direct hit fails") + def _(): + value = copy.deepcopy(record["official_project_archives"]); value[0]["direct_videoout_hits"] = 1 + require(not validator.archives_do_not_corroborate(value), "direct hit invented") + + @case("13 PS4 analogue is not promoted") + def _(): require(validator.ps4_analogue_is_not_promoted(record["ps4_analogue"]), "PS4 boundary rejected") + + @case("14 PS4 promotion fails") + def _(): + value = dict(record["ps4_analogue"]); value["accepted_as_ps5_abi"] = True + require(not validator.ps4_analogue_is_not_promoted(value), "PS4 accepted as PS5") + + @case("15 evidence matrix is fail closed") + def _(): require(validator.evidence_matrix_is_fail_closed(record["evidence_matrix"]), "matrix rejected") + + @case("16 mode semantics promotion fails") + def _(): + value = dict(record["evidence_matrix"]); value["mode_one_semantics"] = "PROVEN_ON_PS5" + require(not validator.evidence_matrix_is_fail_closed(value), "mode promoted") + + @case("17 decision is blocked") + def _(): require(validator.decision_is_blocked(record["decision"]), "decision rejected") + + @case("18 parameter change fails") + def _(): + value = dict(record["decision"]); value["parameter_change_allowed"] = True + require(not validator.decision_is_blocked(value), "parameter change allowed") + + @case("19 new VideoOut call fails") + def _(): + value = dict(record["decision"]); value["new_videoout_call_allowed"] = True + require(not validator.decision_is_blocked(value), "new call allowed") + + @case("20 host research is not hardware evidence") + def _(): require(record["tests"]["hardware_claim_from_host_test"] is False, "host research promoted") + + failures = [] + for name, function in cases: + try: + function() + print(f"PASS {name}") + except Exception as error: # noqa: BLE001 - mutation harness + failures.append(f"{name}: {error}") + print(f"FAIL {name}: {error}") + if failures: + return 1 + print(f"Phase-1.0Q guardrails passed: {len(cases)}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_retroarch_phase10r.py b/tests/test_retroarch_phase10r.py new file mode 100644 index 0000000..8208eaa --- /dev/null +++ b/tests/test_retroarch_phase10r.py @@ -0,0 +1,128 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Mutation guardrails for Phase-1.0R launch-context analysis.""" + +from __future__ import annotations + +import argparse +import importlib.util +from pathlib import Path +import sys + + +def load(path: Path): + spec = importlib.util.spec_from_file_location("phase10r", path) + assert spec and spec.loader + module = importlib.util.module_from_spec(spec) + sys.modules[spec.name] = module + spec.loader.exec_module(module) + return module + + +def require(value: bool, message: str) -> None: + if not value: + raise RuntimeError(message) + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + root = args.root.resolve() + validator = load(root / "tools/validate_retroarch_phase10r.py") + record = validator.load_json( + root / "manifests/retroarch/phase-1.0r-launch-context-analysis.json") + cases = [] + + def case(name): + def register(function): + cases.append((name, function)) + return function + return register + + @case("01 authorizations remain false") + def _(): require(validator.all_false(record["authorizations"], validator.AUTHORIZATION_FIELDS), "authority active") + + @case("02 target build authorization fails") + def _(): + value = dict(record["authorizations"]); value["target_build_authorized"] = True + require(not validator.all_false(value, validator.AUTHORIZATION_FIELDS), "target build allowed") + + @case("03 automatic retry fails") + def _(): + value = dict(record["authorizations"]); value["automatic_retry"] = True + require(not validator.all_false(value, validator.AUTHORIZATION_FIELDS), "retry allowed") + + @case("04 source identities are exact") + def _(): require(validator.source_identities_are_exact(record["source_identities"]), "identity rejected") + + @case("05 SDL2main remains lifecycle only") + def _(): require(validator.sdl2main_is_lifecycle_only(record["sdl2main"]), "wrapper rejected") + + @case("06 invented app registration fails") + def _(): + value = dict(record["sdl2main"]); value["adds_application_registration"] = True + require(not validator.sdl2main_is_lifecycle_only(value), "registration invented") + + @case("07 LoadExec cannot become pre-submit") + def _(): + value = dict(record["sdl2main"]); value["post_return_action"] = "PRE_SUBMIT_LOAD_EXEC" + require(not validator.sdl2main_is_lifecycle_only(value), "timing promoted") + + @case("08 exact RetroArch path omits SDL2main") + def _(): require(validator.retroarch_path_is_exact(record["exact_retroarch_path"]), "path rejected") + + @case("09 exact RetroArch path retains splash hide") + def _(): require(record["exact_retroarch_path"]["system_service_hide_splash_imported"] is True, "hide missing") + + @case("10 exact RetroArch path links SDK CRT") + def _(): require(record["exact_retroarch_path"]["sdk_crt1_linked"] is True, "CRT missing") + + @case("11 direct and manager routes share elfldr") + def _(): require(validator.launch_routes_are_fail_closed(record["launch_routes"]), "routes rejected") + + @case("12 manager route cannot become distinct context") + def _(): + value = dict(record["launch_routes"]); value["controlled_route_creates_distinct_app_context"] = True + require(not validator.launch_routes_are_fail_closed(value), "context invented") + + @case("13 PacBrew is not promoted to launcher") + def _(): require(validator.packaging_is_not_launcher_proof(record["packaging_and_ports"]), "packaging rejected") + + @case("14 homebrew descriptor is not registration") + def _(): require(record["packaging_and_ports"]["homebrew_js_is_app_registration"] is False, "descriptor promoted") + + @case("15 unbound hbldr keeps launcher partial") + def _(): require(record["launch_routes"]["port_launcher_contract"] == "PARTIAL_UNBOUND", "launcher promoted") + + @case("16 LNC log is not root cause") + def _(): require(validator.runtime_observation_is_not_promoted(record["runtime_observation"]), "runtime promoted") + + @case("17 root cause remains unresolved") + def _(): require(record["decision"]["root_cause_resolved"] is False, "root cause invented") + + @case("18 parameter and VideoOut changes remain blocked") + def _(): require(validator.decision_is_blocked(record["decision"]), "decision rejected") + + @case("19 target source and artifact remain absent") + def _(): require(record["performed_actions"]["target_source_changed"] is False and record["performed_actions"]["target_artifact_created"] is False, "target action claimed") + + @case("20 host audit is not hardware evidence") + def _(): require(record["tests"]["hardware_claim_from_host_test"] is False, "host audit promoted") + + failures = [] + for name, function in cases: + try: + function() + print(f"PASS {name}") + except Exception as error: # noqa: BLE001 - mutation harness + failures.append(f"{name}: {error}") + print(f"FAIL {name}: {error}") + if failures: + return 1 + print(f"Phase-1.0R guardrails passed: {len(cases)}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_retroarch_phase10s.py b/tests/test_retroarch_phase10s.py new file mode 100644 index 0000000..4e619ac --- /dev/null +++ b/tests/test_retroarch_phase10s.py @@ -0,0 +1,132 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Mutation guardrails for Phase-1.0S shsrv/hbldr provenance.""" + +from __future__ import annotations + +import argparse +import importlib.util +from pathlib import Path +import sys + + +def load(path: Path): + spec = importlib.util.spec_from_file_location("phase10s", path) + assert spec and spec.loader + module = importlib.util.module_from_spec(spec) + sys.modules[spec.name] = module + spec.loader.exec_module(module) + return module + + +def require(value: bool, message: str) -> None: + if not value: + raise RuntimeError(message) + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + root = args.root.resolve() + validator = load(root / "tools/validate_retroarch_phase10s.py") + record = validator.load_json( + root / "manifests/retroarch/phase-1.0s-launcher-provenance.json") + cases = [] + + def case(name): + def register(function): + cases.append((name, function)) + return function + return register + + @case("01 authorizations remain false") + def _(): require(validator.all_false(record["authorizations"], validator.AUTHORIZATION_FIELDS), "authority active") + + @case("02 target build authorization fails") + def _(): + value = dict(record["authorizations"]); value["target_build_authorized"] = True + require(not validator.all_false(value, validator.AUTHORIZATION_FIELDS), "target build allowed") + + @case("03 device and remount authorization fail") + def _(): + value = dict(record["authorizations"]); value["system_remount_authorized"] = True + require(not validator.all_false(value, validator.AUTHORIZATION_FIELDS), "remount allowed") + + @case("04 source acquisition remains official and static") + def _(): require(validator.acquisition_is_bounded(record["source_acquisition"]), "scope rejected") + + @case("05 executed upstream source fails") + def _(): + value = dict(record["source_acquisition"]); value["downloaded_code_executed"] = True + require(not validator.acquisition_is_bounded(value), "execution accepted") + + @case("06 deployed identity remains unproven") + def _(): require(validator.deployed_identity_is_unproven(record["deployed_identity"]), "identity promoted") + + @case("07 official upstream is not deployed identity") + def _(): + value = dict(record["deployed_identity"]); value["classification"] = "EXACT_USED" + require(not validator.deployed_identity_is_unproven(value), "upstream promoted") + + @case("08 launch callgraph is exact") + def _(): require(validator.callgraph_is_exact(record["launch_callgraph"]), "callgraph rejected") + + @case("09 BigApp context difference is source proven") + def _(): require(validator.context_difference_is_source_only(record["launch_context"]), "context rejected") + + @case("10 VideoOut permission cannot be promoted") + def _(): + value = dict(record["launch_context"]); value["videoout_permission_proven"] = True + require(not validator.context_difference_is_source_only(value), "VideoOut promoted") + + @case("11 firmware runtime cannot be promoted") + def _(): + value = dict(record["launch_context"]); value["firmware_9_60_runtime_proven"] = True + require(not validator.context_difference_is_source_only(value), "runtime promoted") + + @case("12 target ELF requires a device path") + def _(): require(record["effects"]["target_elf_must_exist_on_device"] is True, "staging hidden") + + @case("13 current system-ex mutation remains blocker") + def _(): require(validator.effects_block_device_use(record["effects"]), "effects rejected") + + @case("14 fake-app write cannot be suppressed") + def _(): + value = dict(record["effects"]); value["v019_persistent_fakeapp_creation_possible"] = False + require(not validator.effects_block_device_use(value), "persistent write hidden") + + @case("15 BigApp termination remains visible") + def _(): require(record["effects"]["running_bigapp_may_be_killed"] is True, "termination hidden") + + @case("16 hard deadline remains absent") + def _(): require(record["effects"]["hard_deadline_present"] is False, "deadline invented") + + @case("17 root cause remains a candidate only") + def _(): require(record["decision"]["root_cause_resolved"] is False, "root cause invented") + + @case("18 existing hbldr route remains blocked") + def _(): require(validator.decision_is_blocked(record["decision"]), "decision rejected") + + @case("19 no target or device action occurred") + def _(): require(all(value is False for value in record["performed_actions"].values()), "action occurred") + + @case("20 host source audit is not hardware proof") + def _(): require(record["tests"]["hardware_claim_from_host_test"] is False, "host audit promoted") + + failures = [] + for name, function in cases: + try: + function() + print(f"PASS {name}") + except Exception as error: # noqa: BLE001 - mutation harness + failures.append(f"{name}: {error}") + print(f"FAIL {name}: {error}") + if failures: + return 1 + print(f"Phase-1.0S guardrails passed: {len(cases)}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_retroarch_phase10t.py b/tests/test_retroarch_phase10t.py new file mode 100644 index 0000000..4cac9cc --- /dev/null +++ b/tests/test_retroarch_phase10t.py @@ -0,0 +1,130 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Mutation guardrails for the inactive Phase-1.0T identity gate.""" + +from __future__ import annotations + +import argparse +import importlib.util +from pathlib import Path +import sys + + +def load(path: Path): + spec = importlib.util.spec_from_file_location("phase10t_validator", path) + assert spec and spec.loader + module = importlib.util.module_from_spec(spec) + sys.modules[spec.name] = module + spec.loader.exec_module(module) + return module + + +def require(value: bool, message: str) -> None: + if not value: + raise RuntimeError(message) + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + root = args.root.resolve() + validator = load(root / "tools/validate_retroarch_phase10t.py") + record = validator.load_json( + root / "manifests/retroarch/phase-1.0t-shsrv-identity-gate.json") + cases = [] + + def case(name): + def register(function): + cases.append((name, function)) + return function + return register + + @case("01 all authorizations remain false") + def _(): require(validator.all_false(record["authorizations"]), "authority active") + + @case("02 connection authorization is rejected") + def _(): + value = dict(record["authorizations"]); value["ps5_connection_authorized"] = True + require(not validator.all_false(value), "connection authority accepted") + + @case("03 automatic retry is rejected") + def _(): + value = dict(record["authorizations"]); value["automatic_retry"] = True + require(not validator.all_false(value), "retry accepted") + + @case("04 activation is inert") + def _(): require(validator.activation_is_inactive(record["activation"]), "activation rejected") + + @case("05 target address activates and fails the gate") + def _(): + value = dict(record["activation"]); value["target_address"] = "example.invalid" + require(not validator.activation_is_inactive(value), "target accepted") + + @case("06 mandatory connection effects are complete") + def _(): require(validator.connection_effects_are_complete(record["mandatory_connection_effects"]), "effects rejected") + + @case("07 serial transmission cannot be hidden") + def _(): + value = dict(record["mandatory_connection_effects"]); value["serial_queried_and_transmitted"] = False + require(not validator.connection_effects_are_complete(value), "serial effect hidden") + + @case("08 identity capability remains non-exact") + def _(): require(validator.identity_is_non_exact(record["identity_capabilities"]), "identity rejected") + + @case("09 SHA-256 availability cannot be invented") + def _(): + value = dict(record["identity_capabilities"]); value["sha256_command_available"] = True + require(not validator.identity_is_non_exact(value), "SHA-256 invented") + + @case("10 help fingerprint never proves exact binary") + def _(): require(all(not item["proves_exact_binary"] for item in record["command_fingerprints"].values()), "fingerprint promoted") + + @case("11 command policy is fail closed") + def _(): require(validator.command_policy_is_fail_closed(record["command_policy"]), "policy rejected") + + @case("12 hbldr remains forbidden") + def _(): require("hbldr" in record["command_policy"]["forbidden_mutating_or_launch_commands"], "hbldr allowed") + + @case("13 wildcard paths remain forbidden") + def _(): require(record["command_policy"]["wildcards_allowed"] is False, "wildcard allowed") + + @case("14 sanitization is strict") + def _(): require(validator.sanitization_is_strict(record["sanitization"]), "sanitization rejected") + + @case("15 raw transcript persistence fails") + def _(): + value = dict(record["sanitization"]); value["raw_transcript_persistence_allowed"] = True + require(not validator.sanitization_is_strict(value), "raw transcript accepted") + + @case("16 all future windows remain inactive") + def _(): require(validator.future_windows_are_inactive(record["future_windows"]), "window active") + + @case("17 decision remains inactive") + def _(): require(validator.decision_is_inactive(record["decision"]), "decision rejected") + + @case("18 no network client exists") + def _(): require(record["decision"]["network_client_created"] is False, "client created") + + @case("19 no target or device action occurred") + def _(): require(all(value is False for value in record["performed_actions"].values()), "action occurred") + + @case("20 host tests are not hardware proof") + def _(): require(record["tests"]["hardware_claim_from_host_test"] is False, "host result promoted") + + failures = [] + for name, function in cases: + try: + function() + print(f"PASS {name}") + except Exception as error: # noqa: BLE001 - mutation harness + failures.append(f"{name}: {error}") + print(f"FAIL {name}: {error}") + if failures: + return 1 + print(f"Phase-1.0T guardrails passed: {len(cases)}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_retroarch_phase10u.py b/tests/test_retroarch_phase10u.py new file mode 100644 index 0000000..1f93a15 --- /dev/null +++ b/tests/test_retroarch_phase10u.py @@ -0,0 +1,114 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Mutation guardrails for the bounded Phase-1.0U local inventory.""" + +from __future__ import annotations + +import argparse +import importlib.util +from pathlib import Path +import sys + + +def load(path: Path): + spec = importlib.util.spec_from_file_location("phase10u_validator", path) + assert spec and spec.loader + module = importlib.util.module_from_spec(spec) + sys.modules[spec.name] = module + spec.loader.exec_module(module) + return module + + +def require(value: bool, message: str) -> None: + if not value: + raise RuntimeError(message) + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + root = args.root.resolve() + validator = load(root / "tools/validate_retroarch_phase10u.py") + record = validator.load_json( + root / "manifests/retroarch/phase-1.0u-local-shsrv-inventory.json") + cases = [] + + def case(name): + def register(function): + cases.append((name, function)) + return function + return register + + @case("01 authorizations remain false") + def _(): require(validator.all_authorizations_false(record["authorizations"]), "authority active") + + @case("02 connection authorization is rejected") + def _(): + value = dict(record["authorizations"]); value["ps5_connection_authorized"] = True + require(not validator.all_authorizations_false(value), "connection allowed") + + @case("03 retry authorization is rejected") + def _(): + value = dict(record["authorizations"]); value["automatic_retry"] = True + require(not validator.all_authorizations_false(value), "retry allowed") + + @case("04 scan scope remains bounded") + def _(): require(validator.scope_is_bounded(record["scope"]), "scope rejected") + + @case("05 full computer scan cannot be claimed") + def _(): require(record["scope"]["full_computer_scan_performed"] is False, "global scan claimed") + + @case("06 inventory methods remain static") + def _(): require(validator.methods_are_static(record["methods"]), "methods rejected") + + @case("07 executing host wrapper fails policy") + def _(): + value = dict(record["methods"]); value["host_sender_executed"] = True + require(not validator.methods_are_static(value), "sender execution accepted") + + @case("08 results mean scoped absence only") + def _(): require(validator.result_is_scoped_absence(record["results"]), "result rejected") + + @case("09 global absence cannot be promoted") + def _(): + value = dict(record["results"]); value["global_absence_proven"] = True + require(not validator.result_is_scoped_absence(value), "global absence invented") + + @case("10 all reference objects remain non-deployed") + def _(): require(validator.references_are_non_deployed(record["reference_objects"]), "reference promoted") + + @case("11 source checkout is not deployed identity") + def _(): require(record["reference_objects"][0]["deployed_identity"] is False, "source promoted") + + @case("12 host wrapper is not target binary") + def _(): require(record["reference_objects"][2]["classification"] == "HOST_WRAPPER_NOT_TARGET_BINARY", "wrapper promoted") + + @case("13 package recipe is not a receipt") + def _(): require(record["reference_objects"][3]["classification"] == "UNPINNED_RECIPE_NOT_PACKAGE_RECEIPT", "recipe promoted") + + @case("14 exact identity remains unproven") + def _(): require(validator.decision_is_blocked(record["decision"]), "decision rejected") + + @case("15 no target or device action occurred") + def _(): require(all(value is False for value in record["performed_actions"].values()), "action occurred") + + @case("16 host inventory is not hardware proof") + def _(): require(record["tests"]["hardware_claim_from_host_test"] is False, "host result promoted") + + failures = [] + for name, function in cases: + try: + function() + print(f"PASS {name}") + except Exception as error: # noqa: BLE001 - mutation harness + failures.append(f"{name}: {error}") + print(f"FAIL {name}: {error}") + if failures: + return 1 + print(f"Phase-1.0U guardrails passed: {len(cases)}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_retroarch_phase10v.py b/tests/test_retroarch_phase10v.py new file mode 100644 index 0000000..255f342 --- /dev/null +++ b/tests/test_retroarch_phase10v.py @@ -0,0 +1,126 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Mutation guardrails for the inactive Phase-1.0V collector model.""" + +from __future__ import annotations + +import argparse +import importlib.util +from pathlib import Path +import sys + + +def load(path: Path): + spec = importlib.util.spec_from_file_location("phase10v_validator", path) + assert spec and spec.loader + module = importlib.util.module_from_spec(spec) + sys.modules[spec.name] = module + spec.loader.exec_module(module) + return module + + +def require(value: bool, message: str) -> None: + if not value: + raise RuntimeError(message) + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + root = args.root.resolve() + validator = load(root / "tools/validate_retroarch_phase10v.py") + record = validator.load_json( + root / "manifests/retroarch/phase-1.0v-inactive-shsrv-collector.json") + cases = [] + + def case(name): + def register(function): + cases.append((name, function)) + return function + return register + + @case("01 all authorizations remain false") + def _(): require(validator.all_authorizations_false(record["authorizations"]), "authority active") + + @case("02 connection authorization is rejected") + def _(): + value = dict(record["authorizations"]); value["ps5_connection_authorized"] = True + require(not validator.all_authorizations_false(value), "connection allowed") + + @case("03 automatic retry is rejected") + def _(): + value = dict(record["authorizations"]); value["automatic_retry"] = True + require(not validator.all_authorizations_false(value), "retry allowed") + + @case("04 activation remains inert") + def _(): require(validator.activation_is_inactive(record["activation"]), "activation rejected") + + @case("05 target address activates and fails gate") + def _(): + value = dict(record["activation"]); value["target_address"] = "example.invalid" + require(not validator.activation_is_inactive(value), "target accepted") + + @case("06 command activation fails gate") + def _(): + value = dict(record["activation"]); value["commands"] = ["help"] + require(not validator.activation_is_inactive(value), "command accepted") + + @case("07 model has no network transport") + def _(): require(validator.model_is_offline(record["model"]), "model rejected") + + @case("08 live port argument remains absent") + def _(): require(record["model"]["port_argument_present"] is False, "port present") + + @case("09 collector bounds are exact") + def _(): require(validator.bounds_are_fail_closed(record["bounds"]), "bounds rejected") + + @case("10 oversized bound relaxation fails") + def _(): + value = dict(record["bounds"]); value["max_raw_bytes"] = 131072 + require(not validator.bounds_are_fail_closed(value), "larger input accepted") + + @case("11 Telnet model remains offline and partial") + def _(): require(validator.telnet_is_offline_partial(record["telnet_model"]), "Telnet boundary rejected") + + @case("12 invented negotiation replies fail") + def _(): + value = dict(record["telnet_model"]); value["negotiation_replies_sent"] = True + require(not validator.telnet_is_offline_partial(value), "reply behavior invented") + + @case("13 sanitization is strict") + def _(): require(validator.sanitization_is_strict(record["sanitization"]), "sanitization rejected") + + @case("14 raw transcript output fails") + def _(): + value = dict(record["sanitization"]); value["raw_transcript_output"] = True + require(not validator.sanitization_is_strict(value), "raw transcript allowed") + + @case("15 physical memory erasure remains unproven") + def _(): require(record["sanitization"]["physical_memory_erasure_proven"] is False, "erasure invented") + + @case("16 exact identity remains false") + def _(): require(record["sanitization"]["exact_identity_output"] is False, "identity promoted") + + @case("17 decision requires human review and no live client") + def _(): require(validator.decision_requires_review(record["decision"]), "decision enabled") + + @case("18 no device action and no hardware proof") + def _(): require(all(value is False for value in record["performed_actions"].values()) and record["tests"]["hardware_claim_from_host_test"] is False, "host model promoted") + + failures = [] + for name, function in cases: + try: + function() + print(f"PASS {name}") + except Exception as error: # noqa: BLE001 - mutation harness + failures.append(f"{name}: {error}") + print(f"FAIL {name}: {error}") + if failures: + return 1 + print(f"Phase-1.0V guardrails passed: {len(cases)}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_retroarch_phase10w.py b/tests/test_retroarch_phase10w.py new file mode 100644 index 0000000..0aab440 --- /dev/null +++ b/tests/test_retroarch_phase10w.py @@ -0,0 +1,128 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Mutation guardrails for Phase-1.0W inactive client architecture.""" + +from __future__ import annotations + +import argparse +import importlib.util +from pathlib import Path +import sys + + +def load(path: Path): + spec = importlib.util.spec_from_file_location("phase10w_validator", path) + assert spec and spec.loader + module = importlib.util.module_from_spec(spec) + sys.modules[spec.name] = module + spec.loader.exec_module(module) + return module + + +def require(value: bool, message: str) -> None: + if not value: + raise RuntimeError(message) + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + root = args.root.resolve() + validator = load(root / "tools/validate_retroarch_phase10w.py") + record = validator.load_json( + root / "manifests/retroarch/phase-1.0w-inactive-client-architecture.json") + cases = [] + + def case(name): + def register(function): + cases.append((name, function)) + return function + return register + + @case("01 all authorizations remain false") + def _(): require(validator.all_authorizations_false(record["authorizations"]), "authority active") + + @case("02 connection authority is rejected") + def _(): + value = dict(record["authorizations"]); value["ps5_connection_authorized"] = True + require(not validator.all_authorizations_false(value), "connection allowed") + + @case("03 tracked activation is inert") + def _(): require(validator.activation_is_inactive(record["activation"]), "activation rejected") + + @case("04 target value fails inactive gate") + def _(): + value = dict(record["activation"]); value["target_address"] = "device.invalid" + require(not validator.activation_is_inactive(value), "target accepted") + + @case("05 command value fails inactive gate") + def _(): + value = dict(record["activation"]); value["commands"] = ["help"] + require(not validator.activation_is_inactive(value), "command accepted") + + @case("06 architecture has no transport") + def _(): require(validator.architecture_has_no_transport(record["architecture"]), "architecture rejected") + + @case("07 invented socket fails architecture") + def _(): + value = dict(record["architecture"]); value["socket_import_present"] = True + require(not validator.architecture_has_no_transport(value), "socket accepted") + + @case("08 future contract is bounded") + def _(): require(validator.future_contract_is_bounded(record["future_contract"]), "contract rejected") + + @case("09 second connection fails contract") + def _(): + value = dict(record["future_contract"]); value["maximum_connections"] = 2 + require(not validator.future_contract_is_bounded(value), "second connection accepted") + + @case("10 deadline relaxation fails contract") + def _(): + value = dict(record["future_contract"]); value["maximum_deadline_seconds"] = 11 + require(not validator.future_contract_is_bounded(value), "deadline relaxed") + + @case("11 serial effect acceptance remains mandatory") + def _(): require(record["future_contract"]["serial_query_acceptance_required"] is True, "serial effect hidden") + + @case("12 retry remains forbidden") + def _(): require(record["future_contract"]["automatic_retry"] is False, "retry allowed") + + @case("13 live components remain missing") + def _(): require(validator.live_components_remain_missing(record["missing_live_components"]), "missing set rejected") + + @case("14 receipt cannot be claimed implemented") + def _(): + value = dict(record["missing_live_components"]); value["consumed_attempt_receipt"] = False + require(not validator.live_components_remain_missing(value), "receipt invented") + + @case("15 remediated physical memory erasure remains unproven") + def _(): + phase_v = validator.load_json(root / "manifests/retroarch/phase-1.0v-inactive-shsrv-collector.json") + require(phase_v["review_remediation"]["physical_memory_erasure_proven"] is False, "memory erasure invented") + + @case("16 exact deployed identity remains unproven") + def _(): require(record["decision"]["exact_deployed_shsrv_identity"] == "UNPROVEN", "identity promoted") + + @case("17 next phase is offline implementation only") + def _(): require(validator.decision_is_offline_only(record["decision"]), "decision enabled") + + @case("18 no device action and no hardware proof") + def _(): require(all(value is False for value in record["performed_actions"].values()) and record["tests"]["hardware_claim_from_host_test"] is False, "host architecture promoted") + + failures = [] + for name, function in cases: + try: + function() + print(f"PASS {name}") + except Exception as error: # noqa: BLE001 - mutation harness + failures.append(f"{name}: {error}") + print(f"FAIL {name}: {error}") + if failures: + return 1 + print(f"Phase-1.0W guardrails passed: {len(cases)}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_retroarch_phase10x.py b/tests/test_retroarch_phase10x.py new file mode 100644 index 0000000..919dacd --- /dev/null +++ b/tests/test_retroarch_phase10x.py @@ -0,0 +1,126 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Mutation guardrails for Phase-1.0X inactive injected transport.""" + +from __future__ import annotations + +import argparse +import importlib.util +from pathlib import Path +import sys + + +def load(path: Path): + spec = importlib.util.spec_from_file_location("phase10x_validator", path) + assert spec and spec.loader + module = importlib.util.module_from_spec(spec) + sys.modules[spec.name] = module + spec.loader.exec_module(module) + return module + + +def require(value: bool, message: str) -> None: + if not value: + raise RuntimeError(message) + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + root = args.root.resolve() + validator = load(root / "tools/validate_retroarch_phase10x.py") + record = validator.load_json( + root / "manifests/retroarch/phase-1.0x-inactive-transport.json") + cases = [] + + def case(name): + def register(function): + cases.append((name, function)) + return function + return register + + @case("01 all authorizations remain false") + def _(): require(validator.all_authorizations_false(record["authorizations"]), "authority active") + + @case("02 connection authority is rejected") + def _(): + value = dict(record["authorizations"]) + value["ps5_connection_authorized"] = True + require(not validator.all_authorizations_false(value), "connection accepted") + + @case("03 activation is inert") + def _(): require(validator.activation_is_inactive(record["activation"]), "activation rejected") + + @case("04 target or command activates failure") + def _(): + value = dict(record["activation"]); value["commands"] = ["help"] + require(not validator.activation_is_inactive(value), "command accepted") + + @case("05 architecture is injected only") + def _(): require(validator.architecture_is_injected_only(record["architecture"]), "architecture rejected") + + @case("06 socket capability is rejected") + def _(): + value = dict(record["architecture"]); value["socket_import_present"] = True + require(not validator.architecture_is_injected_only(value), "socket accepted") + + @case("07 receipt is before adapter open") + def _(): require(record["local_evidence"]["receipt_before_adapter_open"] is True, "receipt order missing") + + @case("08 receipt excludes target") + def _(): require(record["local_evidence"]["target_address_persisted"] is False, "target retained") + + @case("09 exclusive creation is required") + def _(): require(record["local_evidence"]["exclusive_leaf_create"] is True, "exclusive create missing") + + @case("10 overwrite and cleanup remain absent") + def _(): + local = record["local_evidence"] + require(local["overwrite_supported"] is False and local["delete_or_cleanup_supported"] is False, "mutation enabled") + + @case("11 partial evidence remains invalid") + def _(): require(record["local_evidence"]["partial_file_is_valid_evidence"] is False, "partial accepted") + + @case("12 directory durability remains unproven") + def _(): require(record["local_evidence"]["directory_entry_durability"] == "UNPROVEN", "durability promoted") + + @case("13 blocking-call preemption remains unproven") + def _(): require(record["deadline_and_cleanup"]["blocking_adapter_call_preemption"] is False, "preemption invented") + + @case("14 retry and second open remain absent") + def _(): + deadline = record["deadline_and_cleanup"] + require(deadline["retry_loop_present"] is False and deadline["second_open_present"] is False, "retry enabled") + + @case("15 exact prompt and Telnet framing remain unproven") + def _(): + evidence = record["source_protocol_evidence"] + require(evidence["exact_prompt_framing_proven"] is False and evidence["exact_telnet_reply_contract_proven"] is False, "framing promoted") + + @case("16 exact deployed identity remains unproven") + def _(): require(record["decision"]["exact_deployed_shsrv_identity"] == "UNPROVEN", "identity promoted") + + @case("17 next step remains offline audit") + def _(): require(validator.decision_is_offline_only(record["decision"]), "live step enabled") + + @case("18 no action and no hardware proof") + def _(): + require(all(value is False for value in record["performed_actions"].values()) and record["tests"]["hardware_claim_from_host_test"] is False, "host evidence promoted") + + failures = [] + for name, function in cases: + try: + function() + print(f"PASS {name}") + except Exception as error: # noqa: BLE001 - mutation harness + failures.append(f"{name}: {error}") + print(f"FAIL {name}: {error}") + if failures: + return 1 + print(f"Phase-1.0X guardrails passed: {len(cases)}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_retroarch_phase10y.py b/tests/test_retroarch_phase10y.py new file mode 100644 index 0000000..d805e84 --- /dev/null +++ b/tests/test_retroarch_phase10y.py @@ -0,0 +1,120 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Mutation guardrails for Phase-1.0Y offline shsrv framing.""" + +from __future__ import annotations + +import argparse +import importlib.util +from pathlib import Path +import sys + + +def load(path: Path): + spec = importlib.util.spec_from_file_location("phase10y_validator", path) + assert spec and spec.loader + module = importlib.util.module_from_spec(spec) + sys.modules[spec.name] = module + spec.loader.exec_module(module) + return module + + +def require(value: bool, message: str) -> None: + if not value: + raise RuntimeError(message) + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + root = args.root.resolve() + validator = load(root / "tools/validate_retroarch_phase10y.py") + record = validator.load_json( + root / "manifests/retroarch/phase-1.0y-shsrv-framing.json") + cases = [] + + def case(name): + def register(function): + cases.append((name, function)) + return function + return register + + @case("01 all authorizations remain false") + def _(): require(validator.all_authorizations_false(record["authorizations"]), "authority active") + + @case("02 connection authority is rejected") + def _(): + value = dict(record["authorizations"]); value["ps5_connection_authorized"] = True + require(not validator.all_authorizations_false(value), "connection accepted") + + @case("03 activation is inert") + def _(): require(validator.activation_is_inactive(record["activation"]), "activation rejected") + + @case("04 source family activation is rejected") + def _(): + value = dict(record["activation"]); value["source_family"] = "LIBTELNET_NVT_V09_V019" + require(not validator.activation_is_inactive(value), "family activated") + + @case("05 model remains offline") + def _(): require(validator.model_is_offline(record["offline_model"]), "model rejected") + + @case("06 network import claim is rejected") + def _(): + value = dict(record["offline_model"]); value["network_import_present"] = True + require(not validator.model_is_offline(value), "network accepted") + + @case("07 two source families remain distinct") + def _(): require(set(record["source_families"]) == {"LEGACY_RAW_V07_V08", "LIBTELNET_NVT_V09_V019"}, "families collapsed") + + @case("08 legacy controls pass to shell") + def _(): require(record["source_families"]["LEGACY_RAW_V07_V08"]["telnet_controls_pass_to_shell"] is True, "legacy behavior hidden") + + @case("09 current server does not proactively negotiate") + def _(): require(record["source_families"]["LIBTELNET_NVT_V09_V019"]["proactive_negotiation"] is False, "negotiation invented") + + @case("10 current unsupported WILL and DO replies remain exact") + def _(): + current = record["source_families"]["LIBTELNET_NVT_V09_V019"] + require(current["will_reply"] == "IAC_DONT" and current["do_reply"] == "IAC_WONT", "reply mismatch") + + @case("11 server echo remains absent") + def _(): require(all(value["server_side_echo"] is False for value in record["source_families"].values()), "echo invented") + + @case("12 prompt completion remains unproven") + def _(): require(validator.prompt_remains_unproven(record["prompt_and_completion"]), "prompt promoted") + + @case("13 short-write loop remains absent") + def _(): require(record["prompt_and_completion"]["short_write_completion_loop"] is False, "short writes hidden") + + @case("14 IAC signal commands remain excluded") + def _(): require(record["risks"]["iac_signal_commands"] == "HIGH_FUNCTIONAL_EXCLUDED_INPUT", "IAC effect weakened") + + @case("15 deployed family remains unproven") + def _(): require(record["decision"]["exact_deployed_shsrv_identity"] == "UNPROVEN", "identity promoted") + + @case("16 no live client is allowed") + def _(): require(record["decision"]["live_client_implementation_allowed"] is False, "live implementation allowed") + + @case("17 next step remains offline passive contract") + def _(): require(validator.decision_is_offline_only(record["decision"]), "live next step enabled") + + @case("18 no action and no hardware proof") + def _(): require(all(value is False for value in record["performed_actions"].values()) and record["tests"]["hardware_claim_from_host_test"] is False, "host evidence promoted") + + failures = [] + for name, function in cases: + try: + function() + print(f"PASS {name}") + except Exception as error: # noqa: BLE001 - mutation harness + failures.append(f"{name}: {error}") + print(f"FAIL {name}: {error}") + if failures: + return 1 + print(f"Phase-1.0Y guardrails passed: {len(cases)}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_retroarch_phase10z.py b/tests/test_retroarch_phase10z.py new file mode 100644 index 0000000..a5c2377 --- /dev/null +++ b/tests/test_retroarch_phase10z.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Mutation guardrails for Phase-1.0Z passive-batch evidence.""" + +from __future__ import annotations + +import argparse +from copy import deepcopy +import importlib.util +from pathlib import Path +import sys + + +def load(path: Path): + spec = importlib.util.spec_from_file_location("phase10z_validator", path) + assert spec and spec.loader + module = importlib.util.module_from_spec(spec) + sys.modules[spec.name] = module + spec.loader.exec_module(module) + return module + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + root = args.root.resolve() + validator = load(root / "tools/validate_retroarch_phase10z.py") + original = validator.load_json( + root / "manifests/retroarch/phase-1.0z-passive-batch.json") + cases = [] + + def mutation(name, function): + cases.append((name, function)) + + mutation("01 active record rejected", lambda r: r["activation"].update(active=True)) + mutation("02 target rejected", lambda r: r["activation"].update(target_address="device.invalid")) + mutation("03 connection authority rejected", lambda r: r["authorizations"].update(ps5_connection_authorized=True)) + mutation("04 request authority rejected", lambda r: r["authorizations"].update(device_request_authorized=True)) + mutation("05 receive authority rejected", lambda r: r["authorizations"].update(result_receive_authorized=True)) + mutation("06 retry rejected", lambda r: r["authorizations"].update(automatic_retry=True)) + mutation("07 reconnect rejected", lambda r: r["authorizations"].update(reconnect_authorized=True)) + mutation("08 second outbound batch rejected", lambda r: r["batch_contract"].update(outbound_batches=2)) + mutation("09 CR framing rejected", lambda r: r["batch_contract"].update(cr_allowed=True)) + mutation("10 IAC framing rejected", lambda r: r["batch_contract"].update(iac_allowed=True)) + mutation("11 prompt completion rejected", lambda r: r["receive_contract"].update(prompt_completion_used=True)) + mutation("12 EOF completion rejected", lambda r: r["receive_contract"].update(remote_eof_completion_used=True)) + mutation("13 nondeadline completion rejected", lambda r: r["receive_contract"].update(completion_event="PROMPT")) + mutation("14 partial acceptance rejected", lambda r: r["receive_contract"].update(partial_result="ACCEPT")) + mutation("15 source-family promotion rejected", lambda r: r["receive_contract"].update(source_family_selected=True)) + mutation("16 hardware proof rejected", lambda r: r["tests"].update(hardware_claim_from_host_test=True)) + mutation("17 device action rejected", lambda r: r["performed_actions"].update(ps5_connected=True)) + mutation("18 live next step rejected", lambda r: r["decision"].update(next_step="LIVE_CLIENT")) + + failures = [] + for name, mutate in cases: + value = deepcopy(original) + mutate(value) + errors = validator.validate_record(value) + if errors: + print(f"PASS {name}") + else: + failures.append(name); print(f"FAIL {name}: mutation accepted") + if failures: + return 1 + print(f"Phase-1.0Z guardrails passed: {len(cases)}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_siecaf_header_parser.py b/tests/test_siecaf_header_parser.py new file mode 100644 index 0000000..9bd2f30 --- /dev/null +++ b/tests/test_siecaf_header_parser.py @@ -0,0 +1,324 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Host-only malformed-input tests for the read-only SIECAF parser.""" + +from __future__ import annotations + +import argparse +from io import BytesIO +import importlib.util +from pathlib import Path +import sys +from types import ModuleType +from typing import Callable + + +def load_module(name: str, path: Path) -> ModuleType: + spec = importlib.util.spec_from_file_location(name, path) + if spec is None or spec.loader is None: + raise RuntimeError(f"could not load {path}") + module = importlib.util.module_from_spec(spec) + sys.modules[name] = module + spec.loader.exec_module(module) + return module + + +def require(condition: bool, message: str) -> None: + if not condition: + raise RuntimeError(message) + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + module = load_module( + "siecaf_inspector", args.root / "tools/inspect_siecaf_header.py" + ) + + def build_archive( + *, + count: int = 1, + magic: bytes | None = None, + unknown_u64: int = 1, + version: int = 3, + metadata_ids: list[int] | None = None, + hash_ids: list[int] | None = None, + overlap: bool = False, + out_of_range: bool = False, + hash_seed: int = 1, + trailing: int = 0, + aligned_length: int | None = None, + unaligned_length: int = 123, + ) -> bytes: + alignment = module.ALIGNMENT + file_offset = alignment + segment_length = alignment if aligned_length is None else aligned_length + data_size = count * alignment + source_size = file_offset + data_size + trailing + metadata_ids = metadata_ids or list(range(count)) + hash_ids = hash_ids or list(range(count)) + header = module.HEADER.pack( + magic or module.MAGIC, + unknown_u64, + 1, + 0, + version, + 0, + bytes(range(16)), + bytes(range(12)), + 0, + count, + file_offset, + data_size, + ) + metadata = bytearray() + for index in range(count): + offset = file_offset + index * alignment + if overlap and index == 1: + offset = file_offset + if out_of_range and index == count - 1: + offset = file_offset + data_size + metadata.extend( + module.SEGMENT_META.pack( + metadata_ids[index], + 0, + 0, + offset, + segment_length, + 3, + 1, + b"\x00" * 12, + 0, + unaligned_length, + ) + ) + hashes = bytearray() + for index in range(count): + hashes.extend( + module.SECTION_HASH.pack( + hash_ids[index], + 0, + bytes([(hash_seed + index) % 256]) * 16, + b"\x00" * 24, + ) + ) + tables = header + metadata + hashes + require(len(tables) <= file_offset, "synthetic tables exceed preamble") + return ( + tables + + b"\x00" * (file_offset - len(tables)) + + b"\xa5" * data_size + + b"\xee" * trailing + )[:source_size] + + def inspect(value: bytes) -> dict[str, object]: + return module.inspect_siecaf( + BytesIO(value), len(value), source_label="synthetic" + ) + + cases: list[tuple[str, Callable[[], None]]] = [] + + def case(name: str) -> Callable[[Callable[[], None]], Callable[[], None]]: + def register(function: Callable[[], None]) -> Callable[[], None]: + cases.append((name, function)) + return function + + return register + + @case("01 valid structure parses") + def _() -> None: + result = inspect(build_archive()) + require( + result["classification"] == "SIECAF_VALID_STRUCTURE", + str(result), + ) + require(not result["errors"], "valid structure returned errors") + require( + "key_or_unknown_16_hex" not in result["header"] + and "raw_hex" not in result["header"] + and result["header"]["key_or_unknown_16_redacted"] is True, + "cryptographic header material was exposed", + ) + + @case("02 magic must be exact") + def _() -> None: + result = inspect(build_archive(magic=b"BADCAF\x00\x00")) + require(result["classification"] == "SIECAF_MALFORMED", str(result)) + + @case("03 unsupported version fails closed") + def _() -> None: + result = inspect(build_archive(version=7)) + require( + result["classification"] == "SIECAF_UNSUPPORTED_VERSION", + str(result), + ) + + @case("04 version six uses the published fixed-width layout") + def _() -> None: + result = inspect(build_archive(version=6)) + require( + result["classification"] == "SIECAF_VALID_STRUCTURE", + str(result), + ) + + @case("05 truncated header is rejected") + def _() -> None: + result = inspect(b"SIECAF\x00\x00") + require(result["classification"] == "SIECAF_MALFORMED", str(result)) + + @case("06 segment-count multiplication overflow is rejected") + def _() -> None: + header = module.HEADER.pack( + module.MAGIC, + 1, + 1, + 0, + 3, + 0, + b"\x00" * 16, + b"\x00" * 12, + 0, + module.UINT64_MAX, + module.ALIGNMENT, + module.ALIGNMENT, + ) + result = module.inspect_siecaf( + BytesIO(header), len(header), source_label="overflow" + ) + require(result["classification"] == "SIECAF_MALFORMED", str(result)) + require( + any("overflow" in error for error in result["errors"]), + "overflow was not reported", + ) + + @case("06 out-of-range offsets are rejected") + def _() -> None: + result = inspect(build_archive(out_of_range=True)) + require(result["classification"] == "SIECAF_MALFORMED", str(result)) + require( + any("outside" in error for error in result["errors"]), + "out-of-range section was not reported", + ) + + @case("07 overlaps are reported") + def _() -> None: + result = inspect(build_archive(count=2, overlap=True)) + require(result["classification"] == "SIECAF_MALFORMED", str(result)) + require(bool(result["overlaps"]), "overlap record is absent") + + @case("08 duplicate metadata IDs are reported") + def _() -> None: + result = inspect(build_archive(count=2, metadata_ids=[0, 0], hash_ids=[0, 1])) + require(result["classification"] == "SIECAF_MALFORMED", str(result)) + require( + result["duplicate_metadata_section_ids"] == [0], + "duplicate metadata ID was not preserved", + ) + + @case("09 metadata and hash ID sets must agree") + def _() -> None: + result = inspect(build_archive(count=2, metadata_ids=[0, 1], hash_ids=[0, 2])) + require(result["classification"] == "SIECAF_MALFORMED", str(result)) + require( + any("table indexes" in error for error in result["errors"]), + "mismatched ID sets were not reported", + ) + + @case("10 repeated section IDs are valid when part numbers differ") + def _() -> None: + value = bytearray(build_archive(count=2, metadata_ids=[7, 7])) + second_meta = module.HEADER.size + module.SEGMENT_META.size + fields = list( + module.SEGMENT_META.unpack( + value[second_meta : second_meta + module.SEGMENT_META.size] + ) + ) + fields[2] = 1 + value[second_meta : second_meta + module.SEGMENT_META.size] = ( + module.SEGMENT_META.pack(*fields) + ) + result = inspect(bytes(value)) + require( + result["classification"] == "SIECAF_VALID_STRUCTURE", + str(result), + ) + require( + result["repeated_metadata_section_ids"] == [7], + "multi-part ID was not reported", + ) + + @case("11 aligned length must be coherent") + def _() -> None: + result = inspect(build_archive(aligned_length=module.ALIGNMENT - 1)) + require(result["classification"] == "SIECAF_MALFORMED", str(result)) + + @case("12 unaligned length cannot exceed aligned length") + def _() -> None: + result = inspect(build_archive(unaligned_length=module.ALIGNMENT + 1)) + require(result["classification"] == "SIECAF_MALFORMED", str(result)) + + @case("13 zero-length sections are coherent") + def _() -> None: + result = inspect(build_archive(aligned_length=0, unaligned_length=0)) + require( + result["classification"] == "SIECAF_VALID_STRUCTURE", + str(result), + ) + + @case("14 trailing data is visible but not silently discarded") + def _() -> None: + result = inspect(build_archive(trailing=7)) + require( + result["classification"] == "SIECAF_VALID_STRUCTURE", + str(result), + ) + require(result["trailing_data_bytes"] == 7, "trailing bytes not counted") + require(bool(result["warnings"]), "trailing bytes not warned") + + @case("15 structural exact requires all structural hashes") + def _() -> None: + left = inspect(build_archive()) + right = inspect(build_archive()) + comparison = module.compare_structures(left, right) + require( + comparison["classification"] == "SIECAF_STRUCTURAL_EXACT", + str(comparison), + ) + + @case("16 equal layout with different hashes is not structural exact") + def _() -> None: + left = inspect(build_archive(hash_seed=1)) + right = inspect(build_archive(hash_seed=9)) + comparison = module.compare_structures(left, right) + require( + comparison["classification"] == "SIECAF_LAYOUT_MATCH_HASHES_DIFFER", + str(comparison), + ) + + @case("17 different segment layout is classified") + def _() -> None: + left = inspect(build_archive(count=1)) + right = inspect(build_archive(count=2)) + comparison = module.compare_structures(left, right) + require( + comparison["classification"] == "SIECAF_LAYOUT_DIFFERENT", + str(comparison), + ) + + failures: list[str] = [] + for name, function in cases: + try: + function() + except Exception as error: + failures.append(f"{name}: {error}") + if failures: + for failure in failures: + print(f"FAIL: {failure}") + return 1 + print(f"SIECAF header parser tests: {len(cases)}/{len(cases)} PASS") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/audit_phase07_artifacts.py b/tools/audit_phase07_artifacts.py new file mode 100644 index 0000000..4fcb969 --- /dev/null +++ b/tools/audit_phase07_artifacts.py @@ -0,0 +1,390 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Generate deterministic Phase-0.7 static ELF evidence without execution.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +import re +import subprocess +from pathlib import Path +from typing import Any + + +EXPECTED_COMMITS = { + "lifecycle": "fe08300339a13f899fb78ea404ada381a5cba87c", + "loader": "197623058f509eddde18868dafcb92fdcac66464", + "manager": "e23d94ff91233aa770e2342800c1467875bdef44", +} +EXPECTED_HASHES = { + "lifecycle": "bfb4a5cc768e162fe4c2fddf41c3978e152722918a39085277fd172cb95a7182", + "loader": "63e810982471eb40cae3a20aa9df9a0a02892f420e429874fae4e99aa400b561", + "manager": "8fecf8241a46246eddbd21e8bb4d875f5d76f1f4f4c6a11384df1f131aa5e5b1", +} +EXPECTED_SIZES = {"lifecycle": 112680, "loader": 397000, "manager": 99560} +LIFECYCLE_IMPORTS = {"_exit", "sceKernelSendNotificationRequest"} +LIFECYCLE_NEEDED = {"libSceLibcInternal.sprx", "libkernel_web.sprx"} +LIFECYCLE_FORBIDDEN = re.compile( + r"(sceGnm|VideoOut|SDL_|sceNet|socket|connect|dlopen|dlsym|" + r"kernel_copyin|kernel_copyout|kernel_set_ucred|__patch_init)" +) +SENSITIVE_CATEGORIES = { + "dynamic_loading": re.compile( + r"(^|_)(dlopen|dlsym)$|^__(dlopen|dlsym)$|dynlib|" + r"LoadStartModule|StopUnloadModule" + ), + "graphics_or_display": re.compile( + r"sceGnm|[Vv]ideoOut|SDL_|[Gg]pu|[Mm][Mm][Ii][Oo]" + ), + "kernel_runtime_write": re.compile( + r"__patch_init|kernel_copy(in|out)|kernel_set_ucred|" + r"kernel_overlap_sockets|syscall.*(patch|set)|" + r"(patch|set).*syscall" + ), + "network": re.compile(r"(^|_)(socket|connect|listen|accept)$|sceNet"), + "ptrace_or_jit": re.compile(r"ptrace|PT_[A-Z]|[Jj][Ii][Tt]"), +} + + +def sha256_bytes(data: bytes) -> str: + return hashlib.sha256(data).hexdigest() + + +def sha256_file(path: Path) -> str: + return sha256_bytes(path.read_bytes()) + + +def run(command: list[str]) -> str: + result = subprocess.run( + command, + check=True, + capture_output=True, + text=True, + encoding="utf-8", + errors="replace", + ) + return result.stdout.replace("\r\n", "\n") + + +def git_head(path: Path, expected: str, lifecycle_source: bool = False) -> str: + git = [ + "git", + "-c", + "core.autocrlf=true", + "-c", + "core.fileMode=false", + "-C", + str(path), + ] + head = run([*git, "rev-parse", "HEAD"]).strip() + if lifecycle_source: + subprocess.run( + [*git, "merge-base", "--is-ancestor", expected, head], + check=True, + ) + changed = subprocess.run( + [ + *git, + "diff", + "--quiet", + expected, + "--", + "samples/lifecycle_probe/main.c", + ], + check=False, + ) + if changed.returncode != 0: + raise ValueError(f"{path}: lifecycle source differs from {expected}") + return expected + if head != expected: + raise ValueError(f"{path}: expected {expected}, got {head}") + if run([*git, "status", "--porcelain"]): + raise ValueError(f"{path}: source tree is dirty") + return head + + +def parse_undefined(symbols: str) -> list[str]: + imports: set[str] = set() + for line in symbols.splitlines(): + match = re.search(r"\bU\s+(\S+)\s*$", line) + if match: + imports.add(match.group(1)) + return sorted(imports) + + +def parse_needed(dynamic: str) -> list[str]: + return sorted(set(re.findall(r"Shared library: \[([^\]]+)\]", dynamic))) + + +def parse_callgraph(disassembly: str) -> list[dict[str, Any]]: + current = "" + edges: list[dict[str, Any]] = [] + for line in disassembly.splitlines(): + function = re.match(r"^[0-9a-fA-F]+\s+<([^>]+)>:$", line) + if function: + current = function.group(1) + continue + call = re.match( + r"^\s*([0-9a-fA-F]+):.*\bcallq?\b\s+(.+?)\s*$", line + ) + if not call: + continue + operand = call.group(2).strip() + target_match = re.search(r"<([^>]+)>", operand) + edges.append( + { + "address": f"0x{call.group(1).lower()}", + "from": current, + "indirect": "*" in operand or target_match is None, + "target": target_match.group(1) if target_match else operand, + } + ) + return edges + + +def function_name(name: str) -> str: + return re.sub(r"\+0x[0-9a-fA-F]+$", "", name) + + +def defined_functions(disassembly: str) -> set[str]: + return { + match.group(1) + for match in re.finditer( + r"^[0-9a-fA-F]+\s+<([^>]+)>:$", disassembly, re.MULTILINE + ) + } + + +def map_symbols(linker_map: str) -> set[str]: + symbols = set() + pattern = re.compile( + r"^\s*[0-9a-fA-F]+\s+[0-9a-fA-F]+\s+[0-9a-fA-F]+" + r"\s+\d+\s+([A-Za-z_][A-Za-z0-9_.$@]*)\s*$" + ) + for line in linker_map.splitlines(): + match = pattern.match(line) + if match: + symbols.add(match.group(1)) + return symbols + + +def direct_reachable( + callgraph: list[dict[str, Any]], entrypoint: str +) -> set[str]: + adjacency: dict[str, set[str]] = {} + for edge in callgraph: + if edge["indirect"]: + continue + source = function_name(edge["from"]) + target = function_name(edge["target"]) + adjacency.setdefault(source, set()).add(target) + reachable = {entrypoint} + pending = [entrypoint] + while pending: + source = pending.pop() + for target in adjacency.get(source, set()): + if target not in reachable: + reachable.add(target) + pending.append(target) + return reachable + + +def sensitive_inventory( + disassembly: str, + linker_map: str, + callgraph: list[dict[str, Any]], + entrypoint: str, +) -> dict[str, Any]: + defined = defined_functions(disassembly) + mapped = map_symbols(linker_map) + reachable = direct_reachable(callgraph, entrypoint) + categories = {} + for category, pattern in SENSITIVE_CATEGORIES.items(): + disassembly_linked = sorted( + name for name in defined if pattern.search(name) + ) + map_linked = sorted(name for name in mapped if pattern.search(name)) + categories[category] = { + "directly_reachable_from_entrypoint": sorted( + name for name in disassembly_linked if name in reachable + ), + "linked": sorted(set(disassembly_linked) | set(map_linked)), + "linked_in_disassembly": disassembly_linked, + "linked_in_linker_map": map_linked, + } + return { + "categories": categories, + "direct_call_reachability_available": entrypoint in defined, + "direct_call_reachability_only": True, + "entrypoint": entrypoint, + "indirect_call_edges_retained_but_not_resolved": sum( + 1 for edge in callgraph if edge["indirect"] + ), + } + + +def normalize_map(data: str) -> str: + return re.sub( + r"/tmp/([A-Za-z0-9_]+)-[0-9a-f]{6}\.o", + r"/tmp/\1-.o", + data.replace("\r\n", "\n"), + ) + + +def analyze( + *, + name: str, + first: Path, + second: Path, + map_path: Path, + nm: Path, + readelf: Path, + objdump: Path, + output: Path, +) -> dict[str, Any]: + first_bytes = first.read_bytes() + second_bytes = second.read_bytes() + if first_bytes != second_bytes: + raise ValueError(f"{name}: the two clean builds are not byte-identical") + digest = sha256_bytes(first_bytes) + if digest != EXPECTED_HASHES[name] or len(first_bytes) != EXPECTED_SIZES[name]: + raise ValueError(f"{name}: identity differs from the reviewed profile") + + dynamic = run([str(readelf), "-d", str(first)]) + symbols = run([str(nm), "-u", str(first)]) + headers = run([str(readelf), "-h", "-l", "-S", str(first)]) + relocations = run([str(readelf), "-r", str(first)]) + all_symbols = run([str(readelf), "-Ws", str(first)]) + disassembly = run([str(objdump), "-d", str(first)]) + callgraph = parse_callgraph(disassembly) + normalized_map = normalize_map(map_path.read_text(encoding="utf-8")) + sensitive = sensitive_inventory( + disassembly, normalized_map, callgraph, "_start" + ) + + reports = { + "callgraph": json.dumps(callgraph, indent=2, sort_keys=True) + "\n", + "disassembly": disassembly, + "dynamic": dynamic, + "headers_sections": headers, + "linker_map_normalized": normalized_map, + "relocations": relocations, + "symbols": all_symbols, + "undefined": symbols, + } + report_hashes: dict[str, str] = {} + for report_name, content in reports.items(): + report_path = output / f"{name}.{report_name}.txt" + report_path.write_text(content, encoding="utf-8", newline="\n") + report_hashes[report_name] = sha256_file(report_path) + + imports = parse_undefined(symbols) + needed = parse_needed(dynamic) + if name == "lifecycle": + if set(imports) != LIFECYCLE_IMPORTS: + raise ValueError(f"lifecycle: unexpected imports {imports}") + if set(needed) != LIFECYCLE_NEEDED: + raise ValueError(f"lifecycle: unexpected DT_NEEDED {needed}") + forbidden_matches = sorted(set(LIFECYCLE_FORBIDDEN.findall(symbols))) + if forbidden_matches: + raise ValueError(f"lifecycle: forbidden import {forbidden_matches}") + if b"phase07-fw960-v1" not in first_bytes or b"9.60" not in first_bytes: + raise ValueError("lifecycle: firmware/build gates are absent") + + return { + "byte_identical_clean_builds": True, + "call_edges": callgraph, + "dt_needed": needed, + "filename": first.name, + "imports": imports, + "report_sha256": report_hashes, + "sensitive_static_inventory": sensitive, + "sha256": digest, + "size": len(first_bytes), + } + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + parser.add_argument("--loader-source", type=Path, required=True) + parser.add_argument("--manager-source", type=Path, required=True) + parser.add_argument("--artifact-dir", type=Path, required=True) + parser.add_argument("--output", type=Path, required=True) + parser.add_argument("--manifest-output", type=Path, required=True) + parser.add_argument("--nm", type=Path, required=True) + parser.add_argument("--readelf", type=Path, required=True) + parser.add_argument("--objdump", type=Path, required=True) + args = parser.parse_args() + + root = args.root.resolve() + sources = { + "lifecycle": root, + "loader": args.loader_source.resolve(), + "manager": args.manager_source.resolve(), + } + source_commits = { + name: git_head( + path, EXPECTED_COMMITS[name], lifecycle_source=name == "lifecycle" + ) + for name, path in sources.items() + } + artifact_dir = args.artifact_dir.resolve() + output = args.output.resolve() + output.mkdir(parents=True, exist_ok=True) + artifacts: dict[str, Any] = {} + for name, stem in { + "lifecycle": "chimera-gfx-lifecycle-probe-phase07", + "loader": "chimera-elfldr-phase07", + "manager": "chimera-pldmgr-phase07", + }.items(): + artifacts[name] = analyze( + name=name, + first=artifact_dir / f"{stem}-a.elf", + second=artifact_dir / f"{stem}-b.elf", + map_path=artifact_dir / f"{stem}-b.map", + nm=args.nm.resolve(strict=True), + readelf=args.readelf.resolve(strict=True), + objdump=args.objdump.resolve(strict=True), + output=output, + ) + + document = { + "schema_version": 1, + "decision": "READY_FOR_HARDENED_RUNTIME_DEPLOYMENT", + "firmware": "9.60", + "offline_only": True, + "ps5_actions": { + "connected": False, + "installed": False, + "transferred": False, + "executed": False, + }, + "source_commits": source_commits, + "artifacts": artifacts, + "forbidden_lifecycle_imports": [], + "permanent_denylist_sha256": ( + "4be1c17b4964f2b68c39b5145bc4af4619c32512d60269ecf5c39728b390fa63" + ), + } + machine_output = output / "phase-0.7-offline-audit.json" + content = json.dumps(document, indent=2, sort_keys=True) + "\n" + machine_output.write_text(content, encoding="utf-8", newline="\n") + args.manifest_output.resolve().write_text( + content, encoding="utf-8", newline="\n" + ) + print( + "Phase-0.7 audit passed: three byte-identical ELF pairs, exact " + "firmware 9.60 lifecycle imports, and no PS5 action" + ) + return 0 + + +if __name__ == "__main__": + try: + raise SystemExit(main()) + except (OSError, subprocess.CalledProcessError, ValueError) as error: + print(f"Phase-0.7 artifact audit failed: {error}") + raise SystemExit(1) from error diff --git a/tools/audit_phase0_safety.py b/tools/audit_phase0_safety.py new file mode 100644 index 0000000..4299a91 --- /dev/null +++ b/tools/audit_phase0_safety.py @@ -0,0 +1,135 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Conservative source/build audit for the Phase-0 non-rendering boundary.""" + +from __future__ import annotations + +import argparse +import json +import re +from pathlib import Path + + +FORBIDDEN_CALLS = ( + r"\bsceGnm[A-Za-z0-9_]*\s*\(", + r"\bsceVideoOutSubmitFlip\s*\(", + r"\b(mmap|mprotect|ioctl)\s*\(", +) + +FORBIDDEN_TERMS = ( + "MMIO", + "SCE_PROSPERO_SDK_DIR", # should exist only in the external SDK, not project code + "smu_", + "fan_control", + "clock_boost", +) + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + root = args.root.resolve() + errors: list[str] = [] + + phase0_sources = ( + sorted((root / "src").rglob("*.c")) + + sorted((root / "adapters").rglob("*.c")) + + sorted((root / "samples/capability_probe").rglob("*.c")) + ) + all_sources = phase0_sources + sorted( + (root / "samples/phase1_videoout_clear").rglob("*.c") + ) + combined = "\n".join(path.read_text(encoding="utf-8") for path in all_sources) + + for pattern in FORBIDDEN_CALLS: + if re.search(pattern, combined): + errors.append(f"forbidden Phase-0 call expression matched: {pattern}") + for term in FORBIDDEN_TERMS: + if term.lower() in combined.lower(): + errors.append(f"forbidden Phase-0 implementation term: {term}") + + platform = (root / "src/backends/ps5/probe_platform.c").read_text(encoding="utf-8") + if platform.count("dlsym(") != 1: + errors.append("PS5 loader must contain exactly one dlsym call site") + if "address = dlsym(" not in platform or "address = NULL;" not in platform: + errors.append("resolved address must be local and explicitly discarded") + if re.search(r"\([^\n;]*\(\s*\*[^)]*\)\s*\)\s*dlsym", platform): + errors.append("dlsym result is cast to a callable function pointer") + if "fprintf" in platform or "%p" in combined: + errors.append("platform shim must not log addresses or loader details") + + cmake = (root / "CMakeLists.txt").read_text(encoding="utf-8") + for term in ("prospero-deploy", "PS5_HOST", "PS5_PORT", "add_custom_target(deploy", + "add_custom_target(upload", "add_custom_target(run"): + if term.lower() in cmake.lower(): + errors.append(f"deployment behavior in build graph: {term}") + if 'set(CHIMERA_GFX_PS5_ALLOWED_FIRMWARE "NONE"' not in cmake: + errors.append("default firmware gate is not NONE") + if 'option(CHIMERA_GFX_BUILD_PHASE1_VIDEOOUT_CLEAR' not in cmake: + errors.append("Phase-1 candidate does not have an explicit build gate") + if 'option(CHIMERA_GFX_BUILD_PS5_MINIMAL_STARTUP' not in cmake: + errors.append("Phase-0.5 minimal startup lacks an explicit build gate") + if "no minimal PS5 ELF may be built" not in cmake: + errors.append("Phase-0.5 minimal startup is not explicitly blocked") + + phase1 = (root / "samples/phase1_videoout_clear/main.c").read_text( + encoding="utf-8" + ) + gate_position = phase1.find("if (!chimera_gfx_firmware_gate_allows(") + init_position = phase1.find("SDL_Init(SDL_INIT_VIDEO)") + present_position = phase1.find("SDL_UpdateWindowSurface(window)") + if min(gate_position, init_position, present_position) < 0: + errors.append("Phase-1 source is missing its reviewed gate/init/present sequence") + elif not gate_position < init_position < present_position: + errors.append("Phase-1 firmware gate must precede SDL init and present") + if "SDL2main" in cmake: + errors.append("Phase-1 target must not link the pre-gate SDL2main wrapper") + if any((root / "samples/clear_screen").rglob("*.c")): + errors.append("legacy clear-screen placeholder unexpectedly contains source") + + compatibility = (root / "FIRMWARE_COMPATIBILITY.md").read_text(encoding="utf-8") + if "| _none_ |" not in compatibility or "| `9.60` |" not in compatibility: + errors.append("firmware compatibility table lacks NONE or exact 9.60 rows") + if '"firmware_allowlist": ["9.60"]' not in ( + root / "manifests/ps5_gnm_symbols.json" + ).read_text(encoding="utf-8"): + errors.append("discovery-only firmware allowlist is not exactly 9.60") + + denylist = json.loads( + (root / "manifests/artifact-denylist.json").read_text(encoding="utf-8") + ) + blocked_hash = ( + "4be1c17b4964f2b68c39b5145bc4af4619c32512d60269ecf5c39728b390fa63" + ) + entries = denylist.get("entries", []) + if len(entries) != 1 or entries[0].get("sha256") != blocked_hash: + errors.append("permanent firmware-9.60 artifact denial is absent") + elif ( + entries[0].get("permanent") is not True + or entries[0].get("execution_eligible") is not False + ): + errors.append("firmware-9.60 artifact denial is not permanent and ineligible") + + artifact_manifest = json.loads( + ( + root + / "manifests/artifacts/chimera-gfx-capability-probe-0.1.0-fw-9.60.json" + ).read_text(encoding="utf-8") + ) + if artifact_manifest["execution"].get("execution_eligible") is not False: + errors.append("firmware-9.60 artifact manifest is not execution-ineligible") + + if errors: + for error in errors: + print(f"safety audit failed: {error}") + return 1 + print( + f"Phase-0/disabled-Phase-1 safety audit passed across " + f"{len(all_sources)} C source files" + ) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/audit_ps5_artifacts.py b/tools/audit_ps5_artifacts.py new file mode 100644 index 0000000..208ea1e --- /dev/null +++ b/tools/audit_ps5_artifacts.py @@ -0,0 +1,225 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Statically audit the fail-closed PS5 ELFs without executing them.""" + +from __future__ import annotations + +import argparse +import json +import re +import subprocess +from pathlib import Path + + +EXPECTED_PHASE1_SCE_IMPORTS = { + "sceKernelAllocateMainDirectMemory", + "sceKernelCreateEqueue", + "sceKernelDeleteEqueue", + "sceKernelMapDirectMemory", + "sceKernelReleaseDirectMemory", + "sceKernelWaitEqueue", + "sceSystemServiceHideSplashScreen", + "sceVideoOutAddFlipEvent", + "sceVideoOutClose", + "sceVideoOutDeleteFlipEvent", + "sceVideoOutOpen", + "sceVideoOutRegisterBuffers2", + "sceVideoOutSetBufferAttribute2", + "sceVideoOutSetFlipRate", + "sceVideoOutSubmitFlip", +} + +EXPECTED_PROBE_UNDEFINED_IMPORTS = { + "__stderrp", + "__stdoutp", + "fprintf", + "fwrite", + "snprintf", + "strcmp", +} + +EXPECTED_PROBE_NEEDED = { + "libkernel_web.sprx", + "libSceLibcInternal.sprx", + "libSceNet.sprx", +} + + +def run_nm(nm: Path, artifact: Path, undefined_only: bool) -> str: + command = [str(nm)] + command.append("-u" if undefined_only else "-a") + command.append(str(artifact)) + result = subprocess.run(command, check=True, capture_output=True, text=True) + return result.stdout + + +def run_readelf(readelf: Path, artifact: Path) -> str: + result = subprocess.run( + [str(readelf), "--dynamic-table", str(artifact)], + check=True, + capture_output=True, + text=True, + ) + return result.stdout + + +def extract_sce_imports(undefined_symbols: str) -> set[str]: + imports: set[str] = set() + for line in undefined_symbols.splitlines(): + match = re.search(r"\bU\s+(sce[A-Za-z0-9_]+)\s*$", line) + if match is not None: + imports.add(match.group(1)) + return imports + + +def extract_undefined_imports(undefined_symbols: str) -> set[str]: + imports: set[str] = set() + for line in undefined_symbols.splitlines(): + match = re.search(r"\bU\s+(\S+)\s*$", line) + if match is not None: + imports.add(match.group(1)) + return imports + + +def extract_needed(dynamic_table: str) -> set[str]: + return set(re.findall(r"Shared library: \[([^\]]+)\]", dynamic_table)) + + +def require_symbols(symbols: str, names: set[str], artifact: Path) -> None: + missing = sorted(name for name in names if name not in symbols) + if missing: + raise ValueError(f"{artifact.name}: missing retained symbols: {', '.join(missing)}") + + +def require_bytes(artifact: Path, values: set[bytes]) -> None: + content = artifact.read_bytes() + missing = sorted(value.decode("ascii") for value in values if value not in content) + if missing: + raise ValueError(f"{artifact.name}: missing gate strings: {', '.join(missing)}") + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--nm", type=Path, required=True) + parser.add_argument("--readelf", type=Path, required=True) + parser.add_argument("--probe", type=Path, required=True) + parser.add_argument("--symbol-manifest", type=Path, required=True) + parser.add_argument("--phase1", type=Path) + parser.add_argument("--firmware", default="NONE") + args = parser.parse_args() + + nm = args.nm.resolve(strict=True) + readelf = args.readelf.resolve(strict=True) + probe = args.probe.resolve(strict=True) + symbol_manifest = json.loads( + args.symbol_manifest.read_text(encoding="utf-8") + ) + probe_undefined = run_nm(nm, probe, undefined_only=True) + probe_all_imports = extract_undefined_imports(probe_undefined) + probe_imports = extract_sce_imports(probe_undefined) + + if probe_imports: + raise ValueError( + f"{probe.name}: unexpected direct Sce imports: " + f"{', '.join(sorted(probe_imports))}" + ) + if probe_all_imports != EXPECTED_PROBE_UNDEFINED_IMPORTS: + missing = sorted(EXPECTED_PROBE_UNDEFINED_IMPORTS - probe_all_imports) + unexpected = sorted(probe_all_imports - EXPECTED_PROBE_UNDEFINED_IMPORTS) + raise ValueError( + f"{probe.name}: undefined import inventory changed; " + f"missing={missing}, unexpected={unexpected}" + ) + dynamic_table = run_readelf(readelf, probe) + needed = extract_needed(dynamic_table) + if needed != EXPECTED_PROBE_NEEDED: + missing = sorted(EXPECTED_PROBE_NEEDED - needed) + unexpected = sorted(needed - EXPECTED_PROBE_NEEDED) + raise ValueError( + f"{probe.name}: DT_NEEDED inventory changed; " + f"missing={missing}, unexpected={unexpected}" + ) + for array in ("INIT_ARRAYSZ", "FINI_ARRAYSZ"): + if not re.search( + rf"(?:\({array}\)|{array})\s+0 \(bytes\)", dynamic_table + ): + raise ValueError(f"{probe.name}: {array} is not empty") + + require_symbols( + run_nm(nm, probe, undefined_only=False), + { + "chimera_gfx_firmware_gate_allows", + "chimera_gfx_ps5_make_loader_ops", + "chimera_gfx_ps5_probe_symbols", + "dlclose", + "dlopen", + "dlsym", + }, + probe, + ) + require_bytes( + probe, + { + args.firmware.encode("ascii"), + b"--acknowledge-read-only-probe", + b"firmware is not allowlisted", + b"libSceGnmDriver.sprx", + } + | { + entry["name"].encode("ascii") + for entry in symbol_manifest["symbols"] + }, + ) + + if args.phase1 is not None: + phase1 = args.phase1.resolve(strict=True) + phase1_undefined = run_nm(nm, phase1, undefined_only=True) + phase1_imports = extract_sce_imports(phase1_undefined) + if phase1_imports != EXPECTED_PHASE1_SCE_IMPORTS: + missing = sorted(EXPECTED_PHASE1_SCE_IMPORTS - phase1_imports) + unexpected = sorted(phase1_imports - EXPECTED_PHASE1_SCE_IMPORTS) + raise ValueError( + f"{phase1.name}: Sce import inventory changed; " + f"missing={missing}, unexpected={unexpected}" + ) + if any(name.startswith("sceGnm") for name in phase1_imports): + raise ValueError(f"{phase1.name}: direct GNM import detected") + require_symbols( + run_nm(nm, phase1, undefined_only=False), + { + "chimera_gfx_firmware_gate_allows", + "SDL_Init", + "SDL_UpdateWindowSurface", + }, + phase1, + ) + require_bytes( + phase1, + { + args.firmware.encode("ascii"), + b"--acknowledge-phase1-videoout-clear", + b"firmware is not allowlisted", + }, + ) + print( + "PS5 artifact audit passed: probe has exactly 6 reviewed undefined " + "imports, 3 reviewed DT_NEEDED modules, empty init/fini arrays, and " + f"0 direct Sce imports; Phase-1 has {len(phase1_imports)} reviewed " + "Sce imports and 0 GNM imports" + ) + else: + print( + "PS5 artifact audit passed: probe has exactly 6 reviewed undefined " + "imports, 3 reviewed DT_NEEDED modules, empty init/fini arrays, all " + "manifest names, and 0 direct Sce/GNM imports" + ) + return 0 + + +if __name__ == "__main__": + try: + raise SystemExit(main()) + except (OSError, subprocess.CalledProcessError, ValueError, json.JSONDecodeError, + KeyError, TypeError) as error: + print(f"PS5 artifact audit failed: {error}") + raise SystemExit(1) from error diff --git a/tools/audit_ps5_loader_runtime.py b/tools/audit_ps5_loader_runtime.py new file mode 100644 index 0000000..c322ca1 --- /dev/null +++ b/tools/audit_ps5_loader_runtime.py @@ -0,0 +1,573 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Generate the exact Phase-0.6 PS5 loader/runtime audit without execution.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +import re +import subprocess +from pathlib import Path +from typing import Any + + +ELFLDR_COMMIT = "699e8bcff03e91e8d6ca6eba281af25c5a58d8c2" +ELFLDR_SHA256 = "092d16ee0ede0c494947efd38d1a17bbd7cc4b022d3858ea898833c188c703e8" +ELFLDR_SIZE = 397000 +PLDMGR_COMMIT = "cfbc70f30f419b09bf2b52283f7409e2d3117ee1" +PLDMGR_SHA256 = "518740adbacccb9094fadb07dd424c53ee290f38306449ccc9d6957fdf813c0b" +SDK_COMMIT = "d2e2e585740362976a39fdd5ccf390f199a7bc37" + + +def digest_bytes(data: bytes) -> str: + return hashlib.sha256(data).hexdigest() + + +def digest_file(path: Path) -> str: + return digest_bytes(path.read_bytes()) + + +def run(command: list[str]) -> str: + result = subprocess.run( + command, + check=True, + capture_output=True, + text=True, + encoding="utf-8", + errors="replace", + ) + return result.stdout.replace("\r\n", "\n") + + +def git_identity(path: Path, expected: str) -> dict[str, Any]: + git = ["git", "-c", "core.autocrlf=true", "-C", str(path)] + commit = run([*git, "rev-parse", "HEAD"]).strip() + if commit != expected: + raise ValueError(f"{path}: expected commit {expected}, got {commit}") + status = run([*git, "status", "--porcelain"]) + if status: + raise ValueError(f"{path}: source checkout is dirty") + return {"commit": commit, "dirty": False} + + +def source_record(root: Path, path: Path) -> dict[str, Any]: + data = path.read_bytes().replace(b"\r\n", b"\n") + return { + "normalization": "lf", + "path": path.relative_to(root).as_posix(), + "sha256": digest_bytes(data), + "size": len(data), + } + + +def require_tokens(path: Path, tokens: list[str]) -> None: + text = path.read_text(encoding="utf-8") + missing = [token for token in tokens if token not in text] + if missing: + raise ValueError(f"{path}: required evidence missing: {missing}") + + +def parse_binary(readelf_output: str, disassembly: str) -> dict[str, Any]: + entry_match = re.search(r"Entry point address:\s+(0x[0-9a-f]+)", readelf_output) + relocation_match = re.search( + r"Relocation section '\.rela\.dyn'.*contains (\d+) entries", + readelf_output, + ) + relative_match = re.search(r"\(RELACOUNT\)\s+(\d+)", readelf_output) + if entry_match is None or relocation_match is None or relative_match is None: + raise ValueError("readelf output lacks required header/relocation evidence") + + needed = sorted(re.findall(r"\(NEEDED\).*\[([^\]]+)\]", readelf_output)) + dynsym_match = re.search( + r"Symbol table '\.dynsym'.*?\n(?P.*?)(?:\nSymbol table|\Z)", + readelf_output, + flags=re.DOTALL, + ) + if dynsym_match is None: + raise ValueError("readelf output lacks .dynsym") + undefined: list[str] = [] + for line in dynsym_match.group("body").splitlines(): + match = re.search(r"\bUND\s+(\S+)\s*$", line) + if match and match.group(1): + undefined.append(match.group(1)) + + array_sizes: dict[str, int] = {} + for name in ("PREINIT_ARRAY", "INIT_ARRAY", "FINI_ARRAY"): + match = re.search(rf"\({name}SZ\)\s+(\d+)", readelf_output) + if match is None: + raise ValueError(f"readelf output lacks {name}SZ") + array_sizes[name.lower()] = int(match.group(1)) + + load_segments = [] + for line in readelf_output.splitlines(): + match = re.match( + r"\s*LOAD\s+\S+\s+\S+\s+\S+\s+(\S+)\s+(\S+)\s+([RWE ]+)\s+\S+", + line, + ) + if match: + load_segments.append( + { + "file_size": int(match.group(1), 16), + "memory_size": int(match.group(2), 16), + "permissions": match.group(3).replace(" ", ""), + } + ) + + return { + "disassembly_sha256": digest_bytes(disassembly.encode("utf-8")), + "dt_needed": needed, + "entry_point": entry_match.group(1), + "init_fini_array_sizes": array_sizes, + "load_segments": load_segments, + "readelf_report_sha256": digest_bytes(readelf_output.encode("utf-8")), + "relocations": { + "relative": int(relative_match.group(1)), + "total": int(relocation_match.group(1)), + }, + "tls_present": bool( + re.search(r"^\s*TLS\s", readelf_output, flags=re.MULTILINE) + or re.search(r"\.(?:tdata|tbss)\b", readelf_output) + ), + "undefined_dynamic_symbols": sorted(undefined), + } + + +def effect( + effect_id: str, + classification: str, + scope: str, + evidence: list[str], + blocker: bool, +) -> dict[str, Any]: + return { + "blocker": blocker, + "classification": classification, + "evidence": evidence, + "id": effect_id, + "scope": scope, + } + + +def build_audit(args: argparse.Namespace) -> dict[str, Any]: + root = args.root.resolve() + loader_source = args.loader_source.resolve() + manager_source = args.payload_manager_source.resolve() + sdk_source = args.sdk_source.resolve() + asset = args.loader_asset.resolve() + + loader_git = git_identity(loader_source, ELFLDR_COMMIT) + manager_git = git_identity(manager_source, PLDMGR_COMMIT) + sdk_git = git_identity(sdk_source, SDK_COMMIT) + if asset.stat().st_size != ELFLDR_SIZE or digest_file(asset) != ELFLDR_SHA256: + raise ValueError("elfldr release asset identity mismatch") + + loader_files = [ + loader_source / name + for name in ("main.c", "elfldr.c", "elfldr.h", "pt.c", "pt.h", "socksrv.c", "Makefile") + ] + manager_files = [ + manager_source / name + for name in ( + "src/ps5_launcher.c", + "src/http_server.c", + "src/autoload.c", + "src/main.c", + "src/payload_mgr.c", + "include/pldmgr.h", + "Makefile", + ) + ] + sdk_files = [ + sdk_source / name + for name in ( + "crt/crt.c", + "crt/patch.c", + "crt/kernel.c", + "include/ps5/payload.h", + "crt/Makefile", + ) + ] + + require_tokens( + loader_source / "elfldr.c", + [ + "rfork_thread(RFPROC | RFCFDG | RFMEM", + "execve(SceSpZeroConf, argv, 0)", + "pt_setlong(pid, r.r_rsp-8, r.r_rip)", + "r.r_rip = entry", + "r.r_rdi = args", + "kernel_overlap_sockets", + "kernel_set_ucred_uid(pid, 0)", + "pt_detach(pid, 0)", + ], + ) + require_tokens( + loader_source / "pt.c", + [ + "kernel_set_ucred_authid(mypid, 0x4800000000010003l)", + "kernel_set_ucred_authid(mypid, authid)", + "while(jmp_reg.r_rsp <= bak_reg.r_rsp)", + ], + ) + require_tokens( + loader_source / "main.c", + ["kernel_set_qaflags(qa_flags)", "elfldr_raise_privileges(mypid)"], + ) + require_tokens( + loader_source / "socksrv.c", + ["signal(SIGCHLD, SIG_IGN)", "while(1)", "serve_elfldr(port)"], + ) + require_tokens( + manager_source / "src/ps5_launcher.c", + ['server_addr.sin_addr.s_addr = inet_addr("127.0.0.1")', "send(sock"], + ) + require_tokens( + manager_source / "src/http_server.c", + [ + "ps5_launch_elf(final_path)", + "fopen(path, \"wb\")", + "payload_mgr_import_to_storage", + ], + ) + require_tokens( + sdk_source / "crt/crt.c", + ["__patch_init()", "payload_terminate(void)", "_start(payload_args_t *args)"], + ) + + readelf_output = run( + [ + str(args.readelf), + "-h", + "-l", + "-S", + "-d", + "-r", + "-s", + "-W", + str(asset), + ] + ) + disassembly = run( + [str(args.objdump), "-d", "--no-show-raw-insn", str(asset)] + ) + binary = parse_binary(readelf_output, disassembly) + + effects = [ + effect( + "elfldr_first_stage_qaflags_enable", + "EXPECTED_VOLATILE_RUNTIME_EFFECT", + "existing_loader_bootstrap", + ["elfldr/main.c:48-59"], + False, + ), + effect( + "elfldr_first_stage_privilege_restore", + "UNBOUNDED_OR_UNKNOWN", + "existing_exploit_host_process", + [ + "elfldr/main.c:61-105 restores jail/root/caps/authid", + "UID is changed by elfldr_raise_privileges but is not backed up or restored", + ], + True, + ), + effect( + "payload_process_creation", + "PAYLOAD_PROCESS_LOCAL", + "new_SceSpZeroConf_child", + ["elfldr/elfldr.c:570-710"], + False, + ), + effect( + "ptrace_authid_restore_success_path", + "RESTORED_BY_LOADER", + "elfldr_service_process", + ["elfldr/pt.c:35-54"], + False, + ), + effect( + "ptrace_authid_restore_failure_path", + "UNBOUNDED_OR_UNKNOWN", + "elfldr_service_process", + [ + "elfldr/pt.c:50-51 returns after failed restoration", + "no second restoration or process shutdown is present", + ], + True, + ), + effect( + "ptrace_single_step_completion", + "UNBOUNDED_OR_UNKNOWN", + "loader_control_path", + ["elfldr/pt.c:238-246", "elfldr/pt.c:291-299"], + True, + ), + effect( + "breakpoint_byte", + "RESTORED_BY_LOADER", + "payload_child", + ["elfldr/elfldr.c:675-700"], + False, + ), + effect( + "breakpoint_page_permissions", + "PAYLOAD_PROCESS_LOCAL", + "payload_child", + [ + "elfldr/elfldr.c:669 changes page to RWX", + "no source edge restores the original protection", + ], + False, + ), + effect( + "payload_credentials", + "UNBOUNDED_OR_UNKNOWN", + "payload_child", + [ + "elfldr/elfldr.c:447-513 restores jail/root/caps/authid", + "UID is set to zero and is not restored", + ], + True, + ), + effect( + "payload_mapping_args_sockets_pipes", + "UNBOUNDED_OR_UNKNOWN", + "payload_child", + [ + "elfldr/elfldr.c:143-343", + "successful detach has no explicit unmap/close cleanup", + "cleanup depends on an unproven child termination path", + ], + True, + ), + effect( + "sdk_patch_init", + "PAYLOAD_PROCESS_LOCAL", + "payload_child", + ["sdk/crt/crt.c", "sdk/crt/patch.c"], + False, + ), + effect( + "sdk_termination_branch", + "UNBOUNDED_OR_UNKNOWN", + "payload_child", + [ + "sdk/crt/crt.c payload_terminate may return, call exit, or trap", + "the exact branch for the injected SceSpZeroConf child is not proven", + ], + True, + ), + effect( + "payload_runtime_limit", + "UNBOUNDED_OR_UNKNOWN", + "detached_payload_child", + [ + "elfldr/elfldr.c:703-710 detaches and returns the PID", + "no 2000 ms watchdog, wait, kill, or retry budget is present", + ], + True, + ), + effect( + "child_reaping", + "UNBOUNDED_OR_UNKNOWN", + "elfldr_service_process", + [ + "elfldr/socksrv.c:400 ignores SIGCHLD", + "post-detach exit/resource cleanup semantics are not documented", + ], + True, + ), + effect( + "payload_manager_launch_hash_binding", + "UNBOUNDED_OR_UNKNOWN", + "payload_manager_to_elfldr", + [ + "pldmgr/http_server.c resolves a path then calls ps5_launch_elf", + "pldmgr/ps5_launcher.c streams bytes without calculating or checking SHA-256", + ], + True, + ), + effect( + "payload_manager_upload", + "PERSISTENT_WRITE", + "payload_manager_storage", + [ + "pldmgr/http_server.c writes /data/pldmgr/payloads/.tmp", + "the upload is committed into payload storage", + ], + True, + ), + ] + + source_files = { + "elfldr": [source_record(root, path) for path in loader_files], + "payload_manager": [source_record(root, path) for path in manager_files], + "sdk": [source_record(root, path) for path in sdk_files], + } + hard_blockers = [ + item["id"] + for item in effects + if item["classification"] in ("PERSISTENT_WRITE", "UNBOUNDED_OR_UNKNOWN") + and item["blocker"] + ] + hard_blockers.extend( + [ + "exact_exploit_and_autoloader_identity_unproven", + "firmware_9_60_not_independently_device_attested", + "return_continuation_after_payload_start_unproven", + ] + ) + + return { + "artifact": { + "built": False, + "execution_eligible": False, + "filename": None, + "sha256": None, + "size": None, + }, + "binary_evidence": binary, + "callgraph": { + "entry": "Payload Manager /loadpayload:", + "edges": [ + ["Payload Manager /loadpayload:", "ps5_launch_elf"], + ["ps5_launch_elf", "connect 127.0.0.1:9021"], + ["ps5_launch_elf", "send ELF bytes"], + ["serve_elfldr", "elfldr_spawn"], + ["elfldr_spawn", "rfork_thread"], + ["rfork_thread child", "elfldr_rfork_entry"], + ["elfldr_rfork_entry", "ptrace PT_TRACE_ME"], + ["elfldr_rfork_entry", "execve SceSpZeroConf"], + ["elfldr_spawn parent", "pt_syscall 599"], + ["elfldr_spawn parent", "install then restore INT3 byte"], + ["elfldr_spawn parent", "elfldr_exec"], + ["elfldr_exec", "elfldr_raise_privileges"], + ["elfldr_exec", "elfldr_prepare_exec"], + ["elfldr_prepare_exec", "elfldr_load"], + ["elfldr_prepare_exec", "elfldr_payload_args"], + ["elfldr_prepare_exec", "push observed RIP at RSP-8"], + ["elfldr_prepare_exec", "set RIP=payload entry"], + ["elfldr_prepare_exec", "set RDI=payload_args"], + ["elfldr_exec", "restore subset of credentials"], + ["elfldr_exec", "ptrace PT_DETACH"], + ["payload _start", "SDK __patch_init"], + ["payload _start", "payload main"], + ["payload _start", "payload_terminate"], + ["payload_terminate", "return or exit or trap"], + ], + "extraction": "reviewed source edges with required-token assertions", + }, + "decision": "BLOCKED_VERSION_OR_UNBOUNDED_EFFECT", + "effects": effects, + "firmware": { + "device_attested": False, + "exact": "9.60", + "evidence": "user_provided_only", + }, + "hard_blockers": sorted(hard_blockers), + "identity": { + "elfldr": { + **loader_git, + "installed_asset_hash_match": True, + "observed_inventory_path": ( + "/data/pldmgr/payloads/elfldr/elfldr_v0.23.elf" + ), + "observed_inventory_sha256": ELFLDR_SHA256, + "observed_inventory_version": "v0.23", + "release": "v0.23", + "release_asset_sha256": ELFLDR_SHA256, + "release_asset_size": ELFLDR_SIZE, + "repository": "https://github.com/ps5-payload-dev/elfldr.git", + }, + "exact_exploit_autoloader": { + "identified": False, + "local_candidate": { + "filename": "Y2JB-Autoloader-403-1240.zip", + "sha256": ( + "805e3f87f0c371223619ffc7d3a7b3c0d41a1fae8a8b1171d9e2f162659e8291" + ), + "size": 504159435, + "status": "local_backup_candidate_not_installed_identity_proof", + }, + "status": "UNPROVEN", + }, + "payload_manager": { + **manager_git, + "installed_asset_hash_match": True, + "observed_inventory_path": ( + "/data/pldmgr/payloads/pldmgr/pldmgr_v0.3.1.elf" + ), + "observed_inventory_sha256": PLDMGR_SHA256, + "observed_inventory_version": "v0.3.1", + "observed_version_endpoint": "0.3.1", + "release": "v0.3.1", + "release_asset_sha256": PLDMGR_SHA256, + "repository": "https://github.com/itsPLK/ps5-payload-manager.git", + }, + "sdk": { + **sdk_git, + "release": "v0.41", + "repository": "https://github.com/ps5-payload-dev/sdk.git", + }, + }, + "no_console_actions": { + "elf_executed": False, + "elf_transferred": False, + "gnm": False, + "raw_port_9021_contacted": False, + "rendering": False, + "videoout": False, + }, + "observation_scope": { + "date": "2026-07-17", + "payload_manager_routes": [ + "/autoload_status", + "/get_config", + "/list_payloads", + "/log", + "/sources_list", + "/version", + ], + "payload_manager_routes_read_only": True, + "strict_status_port_744_result": "ECONNREFUSED", + }, + "phase": "0.6", + "schema_version": 1, + "source_evidence": source_files, + } + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + parser.add_argument("--loader-source", type=Path, required=True) + parser.add_argument("--loader-asset", type=Path, required=True) + parser.add_argument("--payload-manager-source", type=Path, required=True) + parser.add_argument("--sdk-source", type=Path, required=True) + parser.add_argument("--readelf", type=Path, required=True) + parser.add_argument("--objdump", type=Path, required=True) + parser.add_argument("--output", type=Path, required=True) + args = parser.parse_args() + + try: + document = build_audit(args) + output = args.output.resolve() + output.parent.mkdir(parents=True, exist_ok=True) + output.write_text( + json.dumps(document, indent=2, sort_keys=True) + "\n", + encoding="utf-8", + newline="\n", + ) + print( + "Phase-0.6 loader audit: BLOCKED_VERSION_OR_UNBOUNDED_EFFECT; " + f"{len(document['hard_blockers'])} hard blockers" + ) + return 0 + except (OSError, subprocess.CalledProcessError, ValueError) as error: + print(f"Phase-0.6 loader audit failed: {error}") + return 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/audit_ps5_sdk_runtime.py b/tools/audit_ps5_sdk_runtime.py new file mode 100755 index 0000000..9fd9ae7 --- /dev/null +++ b/tools/audit_ps5_sdk_runtime.py @@ -0,0 +1,193 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Audit the pinned SDK startup/loader chain without executing a PS5 ELF.""" + +from __future__ import annotations + +import argparse +import json +import subprocess +from pathlib import Path + + +EXPECTED_SDK_COMMIT = "d2e2e585740362976a39fdd5ccf390f199a7bc37" + + +def require_in_order(text: str, snippets: tuple[str, ...], source: Path) -> None: + position = -1 + for snippet in snippets: + position = text.find(snippet, position + 1) + if position < 0: + raise ValueError(f"{source}: missing or reordered evidence: {snippet}") + + +def read(source: Path, relative: str) -> str: + path = source / relative + return path.read_text(encoding="utf-8") + + +def git_head(source: Path) -> str: + result = subprocess.run( + ["git", "-C", str(source), "rev-parse", "HEAD"], + check=True, + capture_output=True, + text=True, + ) + return result.stdout.strip() + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--sdk-source", type=Path, required=True) + parser.add_argument("--project-root", type=Path, required=True) + parser.add_argument("--output", type=Path, required=True) + args = parser.parse_args() + + sdk = args.sdk_source.resolve(strict=True) + project = args.project_root.resolve(strict=True) + if git_head(sdk) != EXPECTED_SDK_COMMIT: + raise ValueError("SDK source checkout differs from the pinned v0.41 commit") + + crt = read(sdk, "crt/crt.c") + patch = read(sdk, "crt/patch.c") + rtld = read(sdk, "crt/rtld.c") + dlfcn = read(sdk, "crt/rtld_dlfcn.c") + sprx = read(sdk, "crt/rtld_sprx.c") + kernel = read(sdk, "crt/kernel.c") + probe_main = read(project, "samples/capability_probe/main.c") + probe_platform = read(project, "src/backends/ps5/probe_platform.c") + + require_in_order( + crt, + ( + "__crt_syscall_init(args)", + "__kernel_init(args)", + "__klog_init()", + "__patch_init()", + "__rtld_init()", + "*payload_args->payloadout = main(argc, argv, environ)", + ), + sdk / "crt/crt.c", + ) + require_in_order( + patch, + ( + "patch_kernel_ucred()", + "patch_syscall_permissions()", + ), + sdk / "crt/patch.c", + ) + for snippet in ( + "kernel_set_ucred_caps(pid, caps)", + "kernel_set_ucred_attrs(pid, attrs)", + "kernel_copyin(&uaddr, kaddr + 0xf0, sizeof(uaddr))", + "kernel_copyin(&uaddr, kaddr + 0xf8, sizeof(uaddr))", + ): + if snippet not in patch: + raise ValueError(f"crt/patch.c: missing expected write: {snippet}") + require_in_order( + rtld, + ("__rtld_sprx_init()", "__rtld_dlfcn_init()"), + sdk / "crt/rtld.c", + ) + for snippet in ( + "sceKernelLoadStartModule(\"/system/common/lib/libSceSysmodule.sprx\"", + "sceKernelLoadStartModule(path, 0, 0, 0, 0, 0)", + "sceKernelStopUnloadModule(lib->handle, 0, 0, 0, 0, 0)", + ): + if snippet not in sprx: + raise ValueError(f"crt/rtld_sprx.c: missing loader evidence: {snippet}") + require_in_order( + dlfcn, + ("__rtld_lib_open(lib)", "__rtld_lib_init(lib, getargc(), getargv(), environ)"), + sdk / "crt/rtld_dlfcn.c", + ) + require_in_order( + dlfcn, + ("__rtld_lib_fini(lib)", "__rtld_lib_close(lib)", "__rtld_lib_destroy(lib)"), + sdk / "crt/rtld_dlfcn.c", + ) + for snippet in ("int\nkernel_copyin", "int\nkernel_copyout"): + if snippet not in kernel: + raise ValueError(f"crt/kernel.c: missing kernel I/O primitive: {snippet}") + require_in_order( + probe_main, + ( + "chimera_gfx_firmware_gate_allows", + "chimera_gfx_ps5_make_loader_ops", + "chimera_gfx_ps5_probe_symbols", + ), + project / "samples/capability_probe/main.c", + ) + for snippet in ( + "dlopen(module_name, RTLD_LAZY | RTLD_LOCAL)", + "address = dlsym(loader->module, symbol_name)", + "address = NULL;", + "dlclose(loader->module)", + ): + if snippet not in probe_platform: + raise ValueError(f"probe_platform.c: missing loader boundary: {snippet}") + + document = { + "schema_version": 1, + "sdk": { + "release": "v0.41", + "commit": EXPECTED_SDK_COMMIT, + }, + "execution_eligible_under_project_policy": False, + "blocking_side_effects_before_main": [ + "__patch_init calls patch_kernel_ucred", + "patch_kernel_ucred writes process capability and attribute fields", + "__patch_init calls patch_syscall_permissions", + "patch_syscall_permissions writes the process syscall-address bounds", + ], + "startup_calls_before_main": [ + "__crt_syscall_init", + "__kernel_init", + "__klog_init", + "__patch_init", + "__rtld_init", + "__rtld_sprx_init", + "__rtld_dlfcn_init", + "payload constructors", + ], + "project_requested_calls_after_firmware_gate": [ + "fprintf", + "chimera_gfx_ps5_make_loader_ops", + "dlopen", + "dlerror", + "dlsym (21 bounded lookups; returned addresses discarded)", + "snprintf", + "dlclose", + ], + "loader_side_effects": [ + "SDK rtld startup may load and start libSceSysmodule.sprx", + "probe dlopen may load and start libSceGnmDriver.sprx", + "module open allocates user memory and copies symbol/string tables", + ], + "cleanup_side_effects": [ + "dlclose invokes rtld fini, close, and destroy paths", + "a newly loaded SPRX is stopped/unloaded with sceKernelStopUnloadModule", + "SDK sprx_init and sprx_fini are empty at the pinned commit", + "cleanup is not guaranteed after a hang, crash, or loader failure", + ], + "conclusion": ( + "Project code requests no GNM call, rendering, or GPU mutation, but the " + "linked SDK payload CRT performs prohibited kernel writes before main. " + "The resulting ELF must not be transferred or executed." + ), + } + args.output.parent.mkdir(parents=True, exist_ok=True) + args.output.write_text( + json.dumps(document, indent=2, sort_keys=True) + "\n", encoding="utf-8" + ) + print("SDK runtime audit completed: execution blocked by pre-main kernel writes") + return 0 + + +if __name__ == "__main__": + try: + raise SystemExit(main()) + except (OSError, subprocess.CalledProcessError, ValueError) as error: + print(f"SDK runtime audit failed: {error}") + raise SystemExit(1) from error diff --git a/tools/audit_ps5_startup_feasibility.py b/tools/audit_ps5_startup_feasibility.py new file mode 100644 index 0000000..cddbd67 --- /dev/null +++ b/tools/audit_ps5_startup_feasibility.py @@ -0,0 +1,612 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Audit SDK v0.41 startup and decide whether a minimal ELF may be built.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +import re +import subprocess +import tempfile +from collections import deque +from pathlib import Path +from typing import Any + + +EXPECTED_SDK_COMMIT = "d2e2e585740362976a39fdd5ccf390f199a7bc37" +CRT_OBJECTS = { + "crt.o": "crt/crt.c", + "syscall.o": "crt/syscall.c", + "klog.o": "crt/klog.c", + "nid.o": "crt/nid.c", + "kernel.o": "crt/kernel.c", + "rtld.o": "crt/rtld.c", + "rtld_so.o": "crt/rtld_so.c", + "rtld_sprx.o": "crt/rtld_sprx.c", + "rtld_payload.o": "crt/rtld_payload.c", + "rtld_dlfcn.o": "crt/rtld_dlfcn.c", + "mdbg.o": "crt/mdbg.c", + "patch.o": "crt/patch.c", +} +EMPTY_CRT_ARCHIVES = ( + "crti.o", + "crtn.o", + "crtbegin.o", + "crtend.o", + "crtbeginS.o", + "crtendS.o", +) +DANGEROUS_REACHABLE = { + "__patch_init", + "kernel_copyin", + "kernel_copyout", + "kernel_set_ucred_attrs", + "kernel_set_ucred_caps", +} +LINKED_PROHIBITED = DANGEROUS_REACHABLE | { + "__dlopen", + "__dlsym", + "kernel_mprotect", + "kernel_overlap_sockets", + "kernel_set_vmem_protection", +} + + +def run(command: list[str], allowed: tuple[int, ...] = (0,)) -> str: + result = subprocess.run(command, check=False, capture_output=True, text=True) + if result.returncode not in allowed: + raise ValueError( + f"command failed ({result.returncode}): {' '.join(command)}\n" + f"{result.stdout}{result.stderr}" + ) + return result.stdout + result.stderr + + +def sha256_bytes(content: bytes) -> str: + return hashlib.sha256(content).hexdigest() + + +def sha256_file(path: Path) -> str: + digest = hashlib.sha256() + with path.open("rb") as stream: + for chunk in iter(lambda: stream.read(1024 * 1024), b""): + digest.update(chunk) + return digest.hexdigest() + + +def require(text: str, snippets: tuple[str, ...], source: str) -> None: + for snippet in snippets: + if snippet not in text: + raise ValueError(f"{source}: missing pinned evidence: {snippet}") + + +def normalize_target(symbol: str) -> str: + symbol = re.sub(r"[-+]0x[0-9a-f]+$", "", symbol) + symbol = re.sub(r"[-+][0-9a-f]+$", "", symbol) + return symbol + + +def parse_symbols(nm_output: str) -> tuple[set[str], set[str]]: + functions: set[str] = set() + undefined: set[str] = set() + for line in nm_output.splitlines(): + parts = line.split() + if len(parts) < 2: + continue + name, kind = parts[0], parts[1] + if kind in {"T", "t"}: + functions.add(name) + elif kind.lower() == "u" or kind == "w": + undefined.add(name) + return functions, undefined + + +def parse_callgraph(disassembly: str) -> list[dict[str, str]]: + label = re.compile(r"^[0-9a-f]+ <([^>]+)>:$") + instruction = re.compile( + r"^\s*([0-9a-f]+):\s+([a-z][a-z0-9.]*)\s*(.*?)\s*$" + ) + relocation = re.compile(r"^\s*[0-9a-f]+:\s+R_X86_64_\S+\s+(\S+)") + current: str | None = None + pending: dict[str, str] | None = None + edges: list[dict[str, str]] = [] + + def flush() -> None: + nonlocal pending + if pending is None: + return + operand = pending.pop("operand") + direct = re.search(r"<([^>]+)>", operand) + if "target" not in pending: + if direct is not None and "+0x" not in direct.group(1): + pending["target"] = direct.group(1) + else: + pending["target"] = f"INDIRECT:{operand}" + edges.append(pending) + pending = None + + for raw_line in disassembly.splitlines(): + line = raw_line.rstrip() + match = label.match(line) + if match is not None: + flush() + current = match.group(1) + continue + match = relocation.match(line) + if match is not None and pending is not None: + pending["target"] = normalize_target(match.group(1)) + flush() + continue + match = instruction.match(line) + if match is None: + continue + flush() + if current is None: + continue + address, mnemonic, operand = match.groups() + if mnemonic in {"call", "callq"}: + pending = { + "address": address, + "caller": current, + "kind": "call", + "operand": operand, + } + elif mnemonic in {"jmp", "jmpq"} and ( + operand.startswith("*") or ("<" in operand and "+0x" not in operand) + ): + pending = { + "address": address, + "caller": current, + "kind": "tail_call", + "operand": operand, + } + flush() + return edges + + +def reachable_callgraph( + edges: list[dict[str, str]], functions: set[str] +) -> tuple[list[str], list[dict[str, str]]]: + by_caller: dict[str, list[dict[str, str]]] = {} + for edge in edges: + by_caller.setdefault(edge["caller"], []).append(edge) + visited: set[str] = set() + selected: list[dict[str, str]] = [] + queue: deque[str] = deque(["_start"]) + while queue: + caller = queue.popleft() + if caller in visited: + continue + visited.add(caller) + for edge in by_caller.get(caller, []): + selected.append(edge) + target = edge["target"] + if target in functions and target not in visited: + queue.append(target) + return sorted(visited), sorted( + selected, key=lambda item: (item["caller"], item["address"], item["target"]) + ) + + +def driver_trace(compiler: Path, source: Path, freestanding: bool) -> str: + command = [str(compiler), "-###", "-Qunused-arguments", "-Werror"] + if freestanding: + command.extend(["-nostartfiles", "-nodefaultlibs"]) + command.extend([str(source), "-o", str(source.with_suffix(".never.elf"))]) + trace = run(command) + if source.with_suffix(".never.elf").exists(): + raise ValueError("compiler -### unexpectedly produced an ELF") + trace = trace.replace(str(source.parent), "") + return re.sub(r"trace-[0-9a-f]+\.o", "trace-.o", trace) + + +def git_grep_loader_contract(checkout: Path) -> list[str]: + if not (checkout / ".git").exists(): + return [] + output = run( + [ + "git", + "-C", + str(checkout), + "grep", + "-n", + "-e", + "payload_args_t", + "-e", + "payloadout", + "--", + ], + allowed=(0, 1), + ) + return sorted(line for line in output.splitlines() if line.strip()) + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + parser.add_argument("--sdk-source", type=Path, required=True) + parser.add_argument("--sdk-install", type=Path, required=True) + parser.add_argument("--objdump", type=Path, required=True) + parser.add_argument("--readelf", type=Path, required=True) + parser.add_argument("--output", type=Path, required=True) + args = parser.parse_args() + + root = args.root.resolve(strict=True) + sdk_source = args.sdk_source.resolve(strict=True) + sdk_install = args.sdk_install.resolve(strict=True) + sdk_head = run(["git", "-C", str(sdk_source), "rev-parse", "HEAD"]).strip() + if sdk_head != EXPECTED_SDK_COMMIT: + raise ValueError("SDK source does not match the pinned v0.41 commit") + + source_files = set(CRT_OBJECTS.values()) | { + "Makefile.inc", + "crt/Makefile", + "crt/payload.h", + "include/ps5/payload.h", + "host/bin/prospero-clang", + "host/bin/prospero-lld", + "host/elf_x86_64.x", + "host/Makefile", + "host/toolchain/prospero.cmake", + "host/toolchain/prospero.mk", + "host/toolchain/prospero.sh", + } + source_files.update( + str(path.relative_to(sdk_source)).replace("\\", "/") + for path in (sdk_source / "crt").glob("*.h") + ) + source_hashes = { + relative: sha256_file(sdk_source / relative) + for relative in sorted(source_files) + } + + makefile = (sdk_source / "crt/Makefile").read_text(encoding="utf-8") + object_text_match = re.search(r"OBJ := (.+?)\n\n", makefile, re.DOTALL) + if object_text_match is None: + raise ValueError("could not parse CRT object inventory") + makefile_objects = object_text_match.group(1).replace("\\\n", " ").split() + if makefile_objects != list(CRT_OBJECTS): + raise ValueError(f"CRT object inventory changed: {makefile_objects}") + require( + makefile, + ( + "-ffreestanding -fno-builtin -nostdlib -fPIC", + "-target x86_64-sie-ps5", + "crt1.o: $(OBJ)", + "$(LD) -m elf_x86_64 -r -o $@ $^", + ), + "crt/Makefile", + ) + + crt = (sdk_source / "crt/crt.c").read_text(encoding="utf-8") + patch = (sdk_source / "crt/patch.c").read_text(encoding="utf-8") + payload_header = (sdk_source / "include/ps5/payload.h").read_text( + encoding="utf-8" + ) + linker_script = (sdk_source / "host/elf_x86_64.x").read_text(encoding="utf-8") + compiler_wrapper = (sdk_source / "host/bin/prospero-clang").read_text( + encoding="utf-8" + ) + require( + crt, + ( + "_start(payload_args_t *args)", + "for(unsigned char* bss=__bss_start; bss<__bss_end; bss++)", + "__crt_syscall_init(args)", + "__kernel_init(args)", + "__klog_init()", + "__patch_init()", + "__rtld_init()", + "return payload_terminate()", + ), + "crt/crt.c", + ) + require( + patch, + ( + "kernel_set_ucred_caps(pid, caps)", + "kernel_set_ucred_attrs(pid, attrs)", + "caps[15] |= 0x40", + "attrs[3] |= 0x80", + "kernel_copyin(&uaddr, kaddr + 0xf0, sizeof(uaddr))", + "kernel_copyin(&uaddr, kaddr + 0xf8, sizeof(uaddr))", + ), + "crt/patch.c", + ) + require( + payload_header, + ( + "int (*sys_dynlib_dlsym)(int, const char*, void*)", + "int* rwpipe", + "int* rwpair", + "intptr_t kpipe_addr", + "intptr_t kdata_base_addr", + "int* payloadout", + ), + "include/ps5/payload.h", + ) + require( + linker_script, + ( + "OUTPUT_ARCH(i386:x86-64)", + "PROVIDE_HIDDEN (__bss_start = .)", + "PROVIDE_HIDDEN (__bss_end = .)", + ".init_array", + ".fini_array", + "PT_DYNAMIC", + ), + "host/elf_x86_64.x", + ) + require( + compiler_wrapper, + ( + 'LIBS_CRT="${PS5_PAYLOAD_SDK}/target/lib/crt1.o"', + 'if [[ "$ARG" == "-nostdlibc" ||', + '"$ARG" == "-nodefaultlibs"', + 'if [[ "$ARG" == "-nostartfiles" ||', + "-target x86_64-sie-ps5", + ), + "host/bin/prospero-clang", + ) + + crt1 = sdk_install / "target/lib/crt1.o" + nm = sdk_install / "bin/llvm-nm" + ar = sdk_install / "bin/llvm-ar" + compiler = sdk_install / "bin/prospero-clang" + linker = sdk_install / "bin/prospero-lld" + for path in (crt1, nm, ar, compiler, linker, args.objdump, args.readelf): + path.resolve(strict=True) + + nm_output = run([str(nm), "--format=posix", str(crt1)]) + functions, undefined = parse_symbols(nm_output) + expected_undefined = { + "_DYNAMIC", + "__bss_end", + "__bss_start", + "__fini_array_end", + "__fini_array_start", + "__image_end", + "__image_start", + "__init_array_end", + "__init_array_start", + "main", + } + if undefined != expected_undefined: + raise ValueError(f"crt1.o undefined symbol inventory changed: {undefined}") + if not DANGEROUS_REACHABLE.issubset(functions): + raise ValueError("crt1.o no longer contains the reviewed dangerous functions") + if not LINKED_PROHIBITED.issubset(functions): + raise ValueError("crt1.o linked prohibited-function inventory changed") + + empty_archives: dict[str, list[str]] = {} + for filename in EMPTY_CRT_ARCHIVES: + members = run([str(ar), "t", str(sdk_install / "target/lib" / filename)]) + empty_archives[filename] = members.splitlines() + if empty_archives[filename]: + raise ValueError(f"{filename} unexpectedly contains members") + + disassembly = run( + [str(args.objdump), "-dr", "--no-show-raw-insn", str(crt1)] + ) + all_edges = parse_callgraph(disassembly) + reachable_functions, reachable_edges = reachable_callgraph(all_edges, functions) + reachable_set = set(reachable_functions) + if not DANGEROUS_REACHABLE.issubset(reachable_set): + missing = sorted(DANGEROUS_REACHABLE - reachable_set) + raise ValueError(f"disassembly call graph lost expected reachable writes: {missing}") + + section_table = run([str(args.readelf), "-SW", str(crt1)]) + relocation_table = run([str(args.readelf), "-Wr", str(crt1)]) + elf_header = run([str(args.readelf), "-h", str(crt1)]) + if ".text._start" not in section_table or ".rela.text._start" not in section_table: + raise ValueError("crt1.o lacks the audited _start sections") + if "__patch_init" not in relocation_table or "kernel_copyin" not in relocation_table: + raise ValueError("crt1.o relocation evidence no longer exposes the write path") + tls_sections = sorted( + match.group(1) + for line in section_table.splitlines() + if (match := re.search(r"\]\s+(\.\S+)\s+\S+.*\sT\s", line)) is not None + ) + + with tempfile.TemporaryDirectory() as directory: + trace_source = Path(directory) / "trace.c" + trace_source.write_text("int main(void) { return 0; }\n", encoding="utf-8") + default_trace = driver_trace(compiler, trace_source, freestanding=False) + free_trace = driver_trace(compiler, trace_source, freestanding=True) + for expected in ("crt1.o", '"-lc"', '"-lkernel_web"', '"-lSceLibcInternal"', '"-lSceNet"'): + if expected not in default_trace: + raise ValueError(f"default driver trace lacks {expected}") + for forbidden in ("crt1.o", '"-lc"', '"-lkernel_web"', '"-lSceLibcInternal"', '"-lSceNet"'): + if forbidden in free_trace: + raise ValueError(f"freestanding driver trace retains {forbidden}") + + upstream = root / "work/upstream" + loader_search: dict[str, list[str]] = {} + for name in ("SDL", "RetroArch", "pacbrew-repo", "ps5-linux-loader"): + loader_search[name] = git_grep_loader_contract(upstream / name) + if any(loader_search.values()): + raise ValueError("an unreviewed local loader caller appeared in the evidence set") + + document: dict[str, Any] = { + "schema_version": 1, + "audit_date": "2026-07-17", + "decision": "BLOCKED", + "scope": "phase-0.5-kernelwrite-free-startup-offline-only", + "artifact": { + "built": False, + "execution_eligible": False, + "reason": "safe loader return and cleanup are not proven", + "sha256": None, + }, + "sdk": { + "release": "v0.41", + "commit": EXPECTED_SDK_COMMIT, + "target": "x86_64-sie-ps5", + "source_hashes": source_hashes, + }, + "tool_versions": { + "prospero_clang": run([str(compiler), "--version"]).splitlines()[0], + "prospero_lld": run([str(linker), "--version"]).splitlines()[0], + "objdump": run([str(args.objdump), "--version"]).splitlines()[0], + "readelf": run([str(args.readelf), "--version"]).splitlines()[0], + }, + "startup_linkage": { + "default_driver_additions": [ + "target/lib/crt1.o", + "libc", + "libkernel_web", + "libSceLibcInternal", + "libSceNet", + ], + "default_driver_trace_sha256": sha256_bytes(default_trace.encode()), + "freestanding_flags": ["-nostartfiles", "-nodefaultlibs"], + "freestanding_driver_additions": [], + "freestanding_driver_trace_sha256": sha256_bytes(free_trace.encode()), + "crt1_sha256": sha256_file(crt1), + "installed_linker_script_sha256": sha256_file( + sdk_install / "ldscripts/elf_x86_64.x" + ), + "installed_main_script_sha256": sha256_file( + sdk_install / "target/lib/main.script" + ), + "installed_prx_script_sha256": sha256_file( + sdk_install / "target/lib/prx.script" + ), + "crt1_composition": [ + {"object": obj, "source": source} + for obj, source in CRT_OBJECTS.items() + ], + "empty_startup_archives": empty_archives, + "crt1_undefined_symbols": sorted(undefined), + }, + "crt1_static_evidence": { + "disassembly_sha256": sha256_bytes(disassembly.encode()), + "elf_header_sha256": sha256_bytes(elf_header.encode()), + "section_table_sha256": sha256_bytes(section_table.encode()), + "relocation_table_sha256": sha256_bytes(relocation_table.encode()), + "relocation_count": len(re.findall(r"R_X86_64_", relocation_table)), + "tls_sections": tls_sections, + "reachable_functions": reachable_functions, + "reachable_callgraph": reachable_edges, + "reachable_prohibited_functions": sorted( + DANGEROUS_REACHABLE & reachable_set + ), + "linked_prohibited_functions": sorted(LINKED_PROHIBITED & functions), + "linked_but_not_startup_reachable": sorted( + LINKED_PROHIBITED - reachable_set + ), + "source_token_inventory": { + "GNM": any( + "sceGnm" in (sdk_source / source).read_text(encoding="utf-8") + for source in CRT_OBJECTS.values() + ), + "SDL": any( + "SDL_" in (sdk_source / source).read_text(encoding="utf-8") + for source in CRT_OBJECTS.values() + ), + "VideoOut": any( + "sceVideoOut" in (sdk_source / source).read_text(encoding="utf-8") + for source in CRT_OBJECTS.values() + ), + "module_loading": True, + "network_socket_helper": True, + }, + }, + "loader_evidence": { + "sdk_readme_loader_candidates": [ + "ps5-payload-dev/elfldr", + "cryonumb/elfloader via ps5-jar-loader", + "shahrilnet/remote_lua_loader", + ], + "local_non_sdk_contract_hits": loader_search, + "exact_loader_used_for_firmware_9_60_identified": False, + "caller_source_present": False, + "entry_argument_layout": "SAFE_SOURCE_FACT", + "callee_first_argument_register_rdi": "SAFE_DISASSEMBLY_FACT", + "caller_stack_alignment": "UNPROVEN", + "payloadout_lifetime_and_writability": "UNPROVEN", + "return_address_and_return_consumption": "UNPROVEN", + "post_return_cleanup": "UNPROVEN", + "crash_cleanup": "UNPROVEN", + "pre_entry_loader_process_changes": "UNPROVEN", + }, + "review": [ + { + "component": "payload_args_t field order and 48-byte x86_64 layout", + "status": "SAFE", + "basis": "public SDK header plus _start payloadout access at offset 0x28", + }, + { + "component": "SDK v0.41 default crt1.o", + "status": "UNSAFE", + "basis": "source, relocation, and reachable disassembly prove kernel-write paths", + }, + { + "component": "-nostartfiles -nodefaultlibs removes SDK CRT/default libs", + "status": "SAFE", + "basis": "compiler -### trace only; no output ELF was produced", + }, + { + "component": "custom BSS zeroing implementation", + "status": "SAFE", + "basis": "linker symbols and byte-zero loop can be freestanding", + }, + { + "component": "loader entry stack and ABI preconditions", + "status": "UNPROVEN", + "basis": "callee disassembly does not prove caller behavior", + }, + { + "component": "return, exit, crash, and cleanup behavior", + "status": "UNPROVEN", + "basis": "no exact pinned public loader caller source is locally available", + }, + { + "component": "loader changes before _start", + "status": "UNPROVEN", + "basis": "payload args expose pre-existing kernel access but not how it was established", + }, + ], + "custom_artifact_verification": { + "linker_map": "NOT_PERFORMED_BLOCKED_BEFORE_BUILD", + "disassembly": "NOT_PERFORMED_BLOCKED_BEFORE_BUILD", + "undefined_symbols": "NOT_PERFORMED_BLOCKED_BEFORE_BUILD", + "dt_needed": "NOT_PERFORMED_BLOCKED_BEFORE_BUILD", + "relocations": "NOT_PERFORMED_BLOCKED_BEFORE_BUILD", + "init_fini_arrays": "NOT_PERFORMED_BLOCKED_BEFORE_BUILD", + "tls": "NOT_PERFORMED_BLOCKED_BEFORE_BUILD", + "double_clean_build": "NOT_PERFORMED_BLOCKED_BEFORE_BUILD", + "artifact_sha256": None, + }, + "minimal_missing_evidence": [ + "the exact loader and pinned source commit used on firmware 9.60", + "the caller instruction sequence that establishes RDI, RSP alignment, and return address", + "payload_args_t allocation, payloadout lifetime, writability, and ownership", + "the caller path after _start returns, including restoration and cleanup", + "crash/fault behavior before and during _start", + "all process, credential, syscall, module, and memory changes made before entry", + ], + "safe_alternatives": [ + "continue libchimera-gfx through mock and software backends", + "develop a separately scoped Linux-on-PS5 backend as a long-term track", + ], + } + args.output.parent.mkdir(parents=True, exist_ok=True) + args.output.write_text( + json.dumps(document, indent=2, sort_keys=True) + "\n", encoding="utf-8" + ) + print( + "Phase-0.5 startup audit: BLOCKED; loader return contract is unproven; " + "no PS5 ELF built" + ) + return 0 + + +if __name__ == "__main__": + try: + raise SystemExit(main()) + except (KeyError, OSError, TypeError, ValueError) as error: + print(f"startup feasibility audit failed: {error}") + raise SystemExit(1) from error diff --git a/tools/bootstrap-ps5-sdk.ps1 b/tools/bootstrap-ps5-sdk.ps1 new file mode 100644 index 0000000..1e523ce --- /dev/null +++ b/tools/bootstrap-ps5-sdk.ps1 @@ -0,0 +1,52 @@ +# SPDX-License-Identifier: GPL-3.0-or-later +[CmdletBinding()] +param( + [string]$Destination = "" +) + +$ErrorActionPreference = "Stop" +$root = Split-Path -Parent $PSScriptRoot +if ([string]::IsNullOrWhiteSpace($Destination)) { + $Destination = Join-Path $root "work/toolchains/ps5-payload-sdk-v0.41" +} + +$cache = Join-Path $root "work/cache" +$archive = Join-Path $cache "ps5-payload-sdk-v0.41.zip" +$url = "https://github.com/ps5-payload-dev/sdk/releases/download/v0.41/ps5-payload-sdk.zip" +$expectedHash = "ebfb0acb5260511951a80e17db41650c62d20a8caf8659a230b928dc85005984" +$expectedSize = 8810966 + +New-Item -ItemType Directory -Force -Path $cache | Out-Null +if (-not (Test-Path -LiteralPath $archive)) { + & curl.exe -fL --retry 3 --output $archive $url + if ($LASTEXITCODE -ne 0) { + throw "SDK download failed" + } +} + +$file = Get-Item -LiteralPath $archive +if ($file.Length -ne $expectedSize) { + throw "SDK size mismatch: expected $expectedSize, got $($file.Length)" +} +$actualHash = (Get-FileHash -Algorithm SHA256 -LiteralPath $archive).Hash.ToLowerInvariant() +if ($actualHash -ne $expectedHash) { + throw "SDK SHA-256 mismatch" +} + +if (-not (Test-Path -LiteralPath $Destination)) { + $temporary = Join-Path $root "work/toolchains/sdk-extract" + $resolvedRoot = [System.IO.Path]::GetFullPath($root).TrimEnd('\') + '\' + $resolvedTemporary = [System.IO.Path]::GetFullPath($temporary) + if (-not $resolvedTemporary.StartsWith($resolvedRoot, + [System.StringComparison]::OrdinalIgnoreCase)) { + throw "Refusing temporary cleanup outside the workspace" + } + New-Item -ItemType Directory -Force -Path $temporary | Out-Null + Expand-Archive -LiteralPath $archive -DestinationPath $temporary -Force + $extracted = Join-Path $temporary "ps5-payload-sdk" + New-Item -ItemType Directory -Force -Path (Split-Path -Parent $Destination) | Out-Null + Move-Item -LiteralPath $extracted -Destination $Destination + Remove-Item -LiteralPath $temporary -Recurse -Force +} + +Write-Output "Verified PS5 Payload SDK v0.41 at $Destination" diff --git a/tools/build-firmware-probe.sh b/tools/build-firmware-probe.sh new file mode 100755 index 0000000..2bccf74 --- /dev/null +++ b/tools/build-firmware-probe.sh @@ -0,0 +1,91 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: GPL-3.0-or-later +set -euo pipefail + +readonly allowed_firmware="9.60" +readonly expected_sdk_commit="d2e2e585740362976a39fdd5ccf390f199a7bc37" +readonly script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +readonly root="$(cd "${script_dir}/.." && pwd)" +readonly sdk="${PS5_PAYLOAD_SDK:-${root}/work/toolchains/ps5-payload-sdk-v0.41}" +readonly sdk_source="${CHIMERA_GFX_SDK_SOURCE:-${root}/work/upstream/sdk}" +readonly build="${root}/build/probe-9.60" +readonly outputs="${root}/outputs" +readonly artifact_name="chimera-gfx-capability-probe-0.1.0-fw-9.60-offline-audit-only.elf" +readonly manifest_name="chimera-gfx-capability-probe-0.1.0-fw-9.60.json" + +if [[ "${1:-}" != "${allowed_firmware}" || $# -ne 1 ]]; then + echo "usage: $0 9.60" >&2 + exit 64 +fi +if [[ ! -f "${sdk}/toolchain/prospero.cmake" || + ! -x "${sdk}/bin/prospero-nm" ]]; then + echo "missing verified PS5 Payload SDK v0.41 toolchain" >&2 + exit 2 +fi +if ! command -v llvm-readelf-18 >/dev/null 2>&1; then + echo "missing llvm-readelf-18 for the strict dynamic-table audit" >&2 + exit 2 +fi +if [[ ! -d "${sdk_source}/.git" || + "$(git -C "${sdk_source}" rev-parse HEAD)" != "${expected_sdk_commit}" ]]; then + echo "SDK source checkout is not at the locked v0.41 commit" >&2 + exit 2 +fi +if ! git -C "${root}" diff --quiet || + ! git -C "${root}" diff --cached --quiet; then + echo "refusing provenance build from a dirty project tree" >&2 + exit 2 +fi + +mkdir -p "${build}" "${outputs}" +python3 "${root}/tools/audit_ps5_sdk_runtime.py" \ + --sdk-source "${sdk_source}" \ + --project-root "${root}" \ + --output "${build}/sdk-runtime-audit.json" + +export LLVM_CONFIG="${LLVM_CONFIG:-/usr/bin/llvm-config-18}" +cmake -S "${root}" -B "${build}" -G Ninja \ + -DCMAKE_TOOLCHAIN_FILE="${sdk}/toolchain/prospero.cmake" \ + -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_TESTING=OFF \ + -DCHIMERA_GFX_BUILD_PS5_PROBE=ON \ + -DCHIMERA_GFX_BUILD_PHASE1_VIDEOOUT_CLEAR=OFF \ + -DCHIMERA_GFX_PS5_ALLOWED_FIRMWARE="${allowed_firmware}" +cmake --build "${build}" --clean-first \ + --target chimera-gfx-capability-probe + +cmake -E copy_if_different \ + "${build}/chimera-gfx-capability-probe.elf" \ + "${outputs}/${artifact_name}" +cmake -E copy_if_different \ + "${build}/sdk-runtime-audit.json" \ + "${outputs}/sdk-runtime-audit-v0.41.json" + +python3 "${root}/tools/audit_ps5_artifacts.py" \ + --nm "${sdk}/bin/prospero-nm" \ + --readelf "$(command -v llvm-readelf-18)" \ + --probe "${outputs}/${artifact_name}" \ + --symbol-manifest "${root}/manifests/ps5_gnm_symbols.json" \ + --firmware "${allowed_firmware}" + +readonly source_commit="$(git -C "${root}" rev-parse HEAD)" +python3 "${root}/tools/generate_artifact_manifest.py" \ + --artifact "${outputs}/${artifact_name}" \ + --output "${outputs}/${manifest_name}" \ + --id "chimera-gfx-capability-probe-fw-9.60" \ + --version "0.1.0" \ + --source-repository \ + "https://gitea.itworx.tech/Jens/chimera-gfx.git" \ + --source-commit "${source_commit}" \ + --target "ps5-x86_64" \ + --firmware "${allowed_firmware}" \ + --profile "symbol-discovery-only-offline-audit" \ + --note "Transfer and execution are not authorized." \ + --note "Project code calls no resolved GNM symbol and requests no rendering or GPU mutation." \ + --note "SDK v0.41 CRT performs kernel credential and syscall-permission writes before main; execution is blocked by project policy." +python3 "${root}/tools/verify_artifact_manifest.py" \ + --manifest "${outputs}/${manifest_name}" \ + --artifact "${outputs}/${artifact_name}" + +echo "Built and audited offline only: ${outputs}/${artifact_name}" +echo "Execution eligibility: false; do not transfer or execute this ELF." diff --git a/tools/build-host.ps1 b/tools/build-host.ps1 new file mode 100644 index 0000000..784fee3 --- /dev/null +++ b/tools/build-host.ps1 @@ -0,0 +1,40 @@ +# SPDX-License-Identifier: GPL-3.0-or-later +[CmdletBinding()] +param( + [ValidateSet("Debug", "Release")] + [string]$Configuration = "Debug" +) + +$ErrorActionPreference = "Stop" +$root = Split-Path -Parent $PSScriptRoot +$cmake = "C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin\cmake.exe" +$ctest = "C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin\ctest.exe" +if (-not (Test-Path -LiteralPath $cmake)) { + $cmakeCommand = Get-Command cmake -ErrorAction Stop + $cmake = $cmakeCommand.Source + $ctestCommand = Get-Command ctest -ErrorAction Stop + $ctest = $ctestCommand.Source +} + +$rootBytes = [System.Text.Encoding]::UTF8.GetBytes( + [System.IO.Path]::GetFullPath($root).ToLowerInvariant()) +$sha256 = [System.Security.Cryptography.SHA256]::Create() +try { + $rootHashBytes = $sha256.ComputeHash($rootBytes) +} +finally { + $sha256.Dispose() +} +$rootHash = ( + [System.BitConverter]::ToString($rootHashBytes) -replace "-", "" +).Substring(0, 12).ToLowerInvariant() +$buildName = "build/windows-{0}-{1}" -f $Configuration.ToLowerInvariant(), $rootHash +$build = Join-Path $root $buildName + +& $cmake -S $root -B $build -G "Visual Studio 17 2022" -A x64 ` + -DBUILD_TESTING=ON -DCHIMERA_GFX_REGISTER_EXTERNAL_EVIDENCE_VALIDATORS=OFF +if ($LASTEXITCODE -ne 0) { throw "Host configure failed" } +& $cmake --build $build --config $Configuration +if ($LASTEXITCODE -ne 0) { throw "Host build failed" } +& $ctest --test-dir $build -C $Configuration --output-on-failure +if ($LASTEXITCODE -ne 0) { throw "Host tests failed" } diff --git a/tools/build-phase1-videoout.sh b/tools/build-phase1-videoout.sh new file mode 100644 index 0000000..5142ba4 --- /dev/null +++ b/tools/build-phase1-videoout.sh @@ -0,0 +1,97 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: GPL-3.0-or-later +set -euo pipefail + +readonly expected_sdl_commit="0baf4ac49382b537ba449901b5b6d0d189bb1fbb" +readonly script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +readonly root="$(cd "${script_dir}/.." && pwd)" +readonly sdk="${PS5_PAYLOAD_SDK:-${root}/work/toolchains/ps5-payload-sdk-v0.41}" +readonly sdl_source="${CHIMERA_GFX_SDL_SOURCE:-${root}/work/upstream/SDL}" +readonly sdl_stage="${root}/work/generated/SDL-phase1-video-only" +readonly sdl_build="${root}/build/phase1-sdl" +readonly project_build="${root}/build/phase1-videoout" +readonly sdl_patch="${root}/packaging/patches/sdl2-phase1-video-only.patch" + +if [[ ! -f "${sdk}/toolchain/prospero.cmake" ]]; then + echo "missing verified PS5 Payload SDK v0.41" >&2 + exit 2 +fi +if ! command -v llvm-readelf-18 >/dev/null 2>&1; then + echo "missing llvm-readelf-18 for the strict dynamic-table audit" >&2 + exit 2 +fi +if [[ ! -d "${sdl_source}/.git" ]]; then + echo "missing pinned PS5 SDL source checkout" >&2 + exit 2 +fi +if [[ "$(git -C "${sdl_source}" rev-parse HEAD)" != "${expected_sdl_commit}" ]]; then + echo "PS5 SDL checkout is not at the locked commit" >&2 + exit 2 +fi +if [[ ! -e "${sdl_stage}/.git" ]]; then + mkdir -p "$(dirname "${sdl_stage}")" + git -C "${sdl_source}" -c core.autocrlf=false -c core.eol=lf \ + worktree add --detach "${sdl_stage}" "${expected_sdl_commit}" +fi +if [[ "$(git -C "${sdl_stage}" rev-parse HEAD)" != "${expected_sdl_commit}" ]]; then + echo "staged PS5 SDL checkout is not at the locked commit" >&2 + exit 2 +fi +if git -C "${sdl_stage}" apply --check "${sdl_patch}" 2>/dev/null; then + git -C "${sdl_stage}" apply "${sdl_patch}" +elif ! git -C "${sdl_stage}" apply --reverse --check "${sdl_patch}" \ + 2>/dev/null; then + echo "staged PS5 SDL checkout differs from the reviewed overlay" >&2 + exit 2 +fi +if [[ "$(git -C "${sdl_stage}" diff --name-only)" != \ + "src/video/ps5/SDL_ps5video.c" ]]; then + echo "staged PS5 SDL checkout has changes outside the reviewed overlay" >&2 + exit 2 +fi + +export LLVM_CONFIG="${LLVM_CONFIG:-/usr/bin/llvm-config-18}" + +cmake -S "${sdl_stage}" -B "${sdl_build}" -G Ninja \ + -DCMAKE_TOOLCHAIN_FILE="${sdk}/toolchain/prospero.cmake" \ + -DCMAKE_BUILD_TYPE=Release \ + -DSDL_SHARED=OFF \ + -DSDL_STATIC=ON \ + -DSDL_TEST=OFF \ + -DSDL2_DISABLE_SDL2MAIN=ON \ + -DSDL_AUDIO=OFF \ + -DSDL_JOYSTICK=OFF \ + -DSDL_HAPTIC=OFF \ + -DSDL_SENSOR=OFF \ + -DSDL_POWER=OFF \ + -DSDL_FILE=OFF \ + -DSDL_FILESYSTEM=OFF \ + -DSDL_LOCALE=OFF \ + -DSDL_MISC=OFF \ + -DSDL_OPENGL=OFF \ + -DSDL_LOADSO=OFF \ + -DSDL_RENDER=OFF \ + -DSDL_VULKAN=OFF \ + -DSDL_DUMMYVIDEO=OFF \ + -DSDL_OFFSCREEN=OFF \ + -DSDL_HIDAPI=OFF +cmake --build "${sdl_build}" --target SDL2-static + +cmake -S "${root}" -B "${project_build}" -G Ninja \ + -DCMAKE_TOOLCHAIN_FILE="${sdk}/toolchain/prospero.cmake" \ + -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_TESTING=OFF \ + -DCHIMERA_GFX_BUILD_PS5_PROBE=ON \ + -DCHIMERA_GFX_BUILD_PHASE1_VIDEOOUT_CLEAR=ON \ + -DCHIMERA_GFX_PHASE1_SDL_BUILD="${sdl_build}" \ + -DCHIMERA_GFX_PS5_ALLOWED_FIRMWARE=NONE +cmake --build "${project_build}" --clean-first + +python3 "${root}/tools/audit_ps5_artifacts.py" \ + --nm "${sdk}/bin/prospero-nm" \ + --readelf "$(command -v llvm-readelf-18)" \ + --probe "${project_build}/chimera-gfx-capability-probe.elf" \ + --symbol-manifest "${root}/manifests/ps5_gnm_symbols.json" \ + --phase1 "${project_build}/chimera-gfx-phase1-videoout-clear.elf" + +echo "Built offline only; do not transfer or execute either ELF." diff --git a/tools/build_phase10dm_observer.py b/tools/build_phase10dm_observer.py new file mode 100644 index 0000000..e8561da --- /dev/null +++ b/tools/build_phase10dm_observer.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Derive the W^X linker script and build the offline Phase-1.0DM ELF.""" +from __future__ import annotations +import argparse,hashlib,subprocess +from pathlib import Path + +SDK_SCRIPT_SHA256="3bacf56a21602a1298752023a4c5b0c305954ddf6cdbce1464476fc9da9d9093" +def main(): + p=argparse.ArgumentParser();p.add_argument("--root",type=Path,required=True);a=p.parse_args();root=a.root.resolve();sdk=root/"work/toolchains/ps5-payload-sdk-v0.41";source=sdk/"target/lib/main.script";raw=source.read_bytes() + if hashlib.sha256(raw).hexdigest()!=SDK_SCRIPT_SHA256:raise SystemExit("SDK linker script identity mismatch") + text=raw.decode();needle="ph_text PT_LOAD FLAGS (0x7);" + if text.count(needle)!=1:raise SystemExit("SDK text PHDR differs") + derived=text.replace(needle,"ph_text PT_LOAD FLAGS (0x5);") + out=root/"build/phase10dm";out.mkdir(parents=True,exist_ok=True);script=out/"observer-wx.script";script.write_text(derived,newline="\n") + source=root.parent/"chimera-retroarch/pkg/ps5/chimera_ps5_title_snapshot_observer.c";artifact=out/"chimera_title_snapshot_observer.elf";linkmap=out/"chimera_title_snapshot_observer.map" + if not source.is_file():raise SystemExit("target source missing") + def wsl(path):return "/mnt/c/"+path.as_posix().split(":/",1)[1] + command=f"set -euo pipefail; '{wsl(sdk)}/bin/prospero-clang' -std=c11 -Wall -Wextra -Werror -O2 -fno-common -Wl,-T,'{wsl(script)}' -Wl,-Map,'{wsl(linkmap)}' -o '{wsl(artifact)}' '{wsl(source)}'" + subprocess.run(["wsl.exe","bash","-lc",command],check=True) + return 0 +if __name__=="__main__":raise SystemExit(main()) diff --git a/tools/build_phase10dq_inventory.py b/tools/build_phase10dq_inventory.py new file mode 100644 index 0000000..8a5cc13 --- /dev/null +++ b/tools/build_phase10dq_inventory.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Derive the W^X linker script and build the offline Phase-1.0DQ ELF.""" +from __future__ import annotations +import argparse,hashlib,subprocess +from pathlib import Path + +SDK_SCRIPT_SHA256="3bacf56a21602a1298752023a4c5b0c305954ddf6cdbce1464476fc9da9d9093" +def main(): + p=argparse.ArgumentParser();p.add_argument("--root",type=Path,required=True);a=p.parse_args();root=a.root.resolve();sdk=root/"work/toolchains/ps5-payload-sdk-v0.41";source_script=sdk/"target/lib/main.script";raw=source_script.read_bytes() + if hashlib.sha256(raw).hexdigest()!=SDK_SCRIPT_SHA256:raise SystemExit("SDK linker script identity mismatch") + text=raw.decode();needle="ph_text PT_LOAD FLAGS (0x7);" + if text.count(needle)!=1:raise SystemExit("SDK text PHDR differs") + out=root/"build/phase10dq";out.mkdir(parents=True,exist_ok=True);script=out/"observer-wx.script";script.write_text(text.replace(needle,"ph_text PT_LOAD FLAGS (0x5);"),newline="\n") + source=root.parent/"chimera-retroarch/pkg/ps5/chimera_ps5_fake00000_inventory_observer.c";artifact=out/"chimera_fake00000_inventory_observer.elf";linkmap=out/"chimera_fake00000_inventory_observer.map" + if not source.is_file():raise SystemExit("target source missing") + def wsl(path):return "/mnt/c/"+path.as_posix().split(":/",1)[1] + command=f"set -euo pipefail; '{wsl(sdk)}/bin/prospero-clang' -std=c11 -Wall -Wextra -Werror -O2 -fno-common -Wl,-T,'{wsl(script)}' -Wl,-Map,'{wsl(linkmap)}' -o '{wsl(artifact)}' '{wsl(source)}'" + subprocess.run(["wsl.exe","bash","-lc",command],check=True) + return 0 +if __name__=="__main__":raise SystemExit(main()) diff --git a/tools/build_phase10ds_metadata.py b/tools/build_phase10ds_metadata.py new file mode 100644 index 0000000..cab873a --- /dev/null +++ b/tools/build_phase10ds_metadata.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +from __future__ import annotations +import argparse,hashlib,subprocess +from pathlib import Path +SDK_SCRIPT_SHA256="3bacf56a21602a1298752023a4c5b0c305954ddf6cdbce1464476fc9da9d9093" +def main(): + p=argparse.ArgumentParser();p.add_argument("--root",type=Path,required=True);root=p.parse_args().root.resolve();sdk=root/"work/toolchains/ps5-payload-sdk-v0.41";raw=(sdk/"target/lib/main.script").read_bytes() + if hashlib.sha256(raw).hexdigest()!=SDK_SCRIPT_SHA256:raise SystemExit("SDK linker script identity mismatch") + text=raw.decode();needle="ph_text PT_LOAD FLAGS (0x7);" + if text.count(needle)!=1:raise SystemExit("SDK text PHDR differs") + out=root/"build/phase10ds";out.mkdir(parents=True,exist_ok=True);script=out/"observer-wx.script";script.write_text(text.replace(needle,"ph_text PT_LOAD FLAGS (0x5);"),newline="\n") + source=root.parent/"chimera-retroarch/pkg/ps5/chimera_ps5_fake00000_metadata_observer.c";artifact=out/"chimera_fake00000_metadata_observer.elf";linkmap=out/"chimera_fake00000_metadata_observer.map" + def wsl(path):return "/mnt/c/"+path.as_posix().split(":/",1)[1] + subprocess.run(["wsl.exe","bash","-lc",f"set -euo pipefail; '{wsl(sdk)}/bin/prospero-clang' -std=c11 -Wall -Wextra -Werror -O2 -fno-common -Wl,-T,'{wsl(script)}' -Wl,-Map,'{wsl(linkmap)}' -o '{wsl(artifact)}' '{wsl(source)}'"],check=True) +if __name__=="__main__":main() diff --git a/tools/build_phase10du_package_stat.py b/tools/build_phase10du_package_stat.py new file mode 100644 index 0000000..f3d6f28 --- /dev/null +++ b/tools/build_phase10du_package_stat.py @@ -0,0 +1,12 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +import argparse,hashlib,subprocess +from pathlib import Path +H="3bacf56a21602a1298752023a4c5b0c305954ddf6cdbce1464476fc9da9d9093" +def main(): + p=argparse.ArgumentParser();p.add_argument("--root",type=Path,required=True);r=p.parse_args().root.resolve();sdk=r/"work/toolchains/ps5-payload-sdk-v0.41";raw=(sdk/"target/lib/main.script").read_bytes() + if hashlib.sha256(raw).hexdigest()!=H:raise SystemExit("SDK identity") + out=r/"build/phase10du";out.mkdir(parents=True,exist_ok=True);script=out/"wx.script";script.write_text(raw.decode().replace("ph_text PT_LOAD FLAGS (0x7);","ph_text PT_LOAD FLAGS (0x5);"),newline="\n");src=r.parent/"chimera-retroarch/pkg/ps5/chimera_ps5_fake00000_package_stat.c";elf=out/"chimera_fake00000_package_stat.elf" + def w(p):return "/mnt/c/"+p.as_posix().split(":/",1)[1] + subprocess.run(["wsl.exe","bash","-lc",f"set -e; '{w(sdk)}/bin/prospero-clang' -std=c11 -Wall -Wextra -Werror -O2 -Wl,-T,'{w(script)}' -o '{w(elf)}' '{w(src)}'"],check=True) +if __name__=="__main__":main() diff --git a/tools/build_phase10dw_package_readback.py b/tools/build_phase10dw_package_readback.py new file mode 100644 index 0000000..93ffa02 --- /dev/null +++ b/tools/build_phase10dw_package_readback.py @@ -0,0 +1,12 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +import argparse,hashlib,subprocess +from pathlib import Path +H="3bacf56a21602a1298752023a4c5b0c305954ddf6cdbce1464476fc9da9d9093" +def main(): + p=argparse.ArgumentParser();p.add_argument("--root",type=Path,required=True);r=p.parse_args().root.resolve();sdk=r/"work/toolchains/ps5-payload-sdk-v0.41";raw=(sdk/"target/lib/main.script").read_bytes() + if hashlib.sha256(raw).hexdigest()!=H:raise SystemExit("SDK identity") + out=r/"build/phase10dw";out.mkdir(parents=True,exist_ok=True);script=out/"wx.script";script.write_text(raw.decode().replace("ph_text PT_LOAD FLAGS (0x7);","ph_text PT_LOAD FLAGS (0x5);"),newline="\n");src=r.parent/"chimera-retroarch/pkg/ps5/chimera_ps5_fake00000_package_readback.c";elf=out/"chimera_fake00000_package_readback.elf" + def w(p):return "/mnt/c/"+p.as_posix().split(":/",1)[1] + subprocess.run(["wsl.exe","bash","-lc",f"set -e; '{w(sdk)}/bin/prospero-clang' -std=c11 -Wall -Wextra -Werror -O2 -Wl,-T,'{w(script)}' -o '{w(elf)}' '{w(src)}'"],check=True) +if __name__=="__main__":main() diff --git a/tools/check_artifact_execution_policy.py b/tools/check_artifact_execution_policy.py new file mode 100644 index 0000000..6368ab0 --- /dev/null +++ b/tools/check_artifact_execution_policy.py @@ -0,0 +1,341 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Fail-closed static eligibility gate for artifact execution tooling.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +import re +from pathlib import Path +from typing import Any + + +SHA256_PATTERN = re.compile(r"[0-9a-f]{64}") +COMMIT_PATTERN = re.compile(r"[0-9a-f]{40}") +READY_DECISION = "READY_FOR_HARDENED_RUNTIME_DEPLOYMENT" +HARD_EFFECTS = {"PERSISTENT_WRITE", "UNBOUNDED_OR_UNKNOWN"} +ALLOWED_EFFECTS = { + "ALLOWED_APPLICATION_WRITE", + "BOUNDED_WATCHDOG", + "EXPLICIT_PROCESS_EXIT", + "EXPECTED_VOLATILE_RUNTIME_EFFECT", + "FAIL_CLOSED_TERMINATION", + "HASH_BOUND_SAME_FD", + "OS_RECLAIMED_ON_EXIT", + "RESTORED_BY_LOADER", + "PAYLOAD_PROCESS_LOCAL", + *HARD_EFFECTS, +} +EXPECTED_PAYLOAD_MANAGER = { + "base_commit": "cfbc70f30f419b09bf2b52283f7409e2d3117ee1", + "hardened_commit": "e23d94ff91233aa770e2342800c1467875bdef44", + "installed": False, + "release": "v0.3.1-chimera-controlled-phase07", + "reproducible": True, + "sha256": "8fecf8241a46246eddbd21e8bb4d875f5d76f1f4f4c6a11384df1f131aa5e5b1", + "size": 99560, +} +EXPECTED_LOADER = { + "base_commit": "699e8bcff03e91e8d6ca6eba281af25c5a58d8c2", + "hardened_commit": "197623058f509eddde18868dafcb92fdcac66464", + "installed": False, + "release": "v0.23-chimera-phase07", + "reproducible": True, + "sha256": "63e810982471eb40cae3a20aa9df9a0a02892f420e429874fae4e99aa400b561", + "size": 397000, +} +EXPECTED_SDK = { + "commit": "d2e2e585740362976a39fdd5ccf390f199a7bc37", + "release": "v0.41", +} + + +def hash_file(path: Path) -> str: + digest = hashlib.sha256() + with path.open("rb") as stream: + for chunk in iter(lambda: stream.read(1024 * 1024), b""): + digest.update(chunk) + return digest.hexdigest() + + +def deny(reason_codes: list[str], artifact_sha256: str | None = None) -> int: + result: dict[str, Any] = { + "decision": "DENY", + "execution_authorized": False, + "reason_codes": sorted(set(reason_codes)), + "schema_version": 1, + } + if artifact_sha256 is not None: + result["artifact_sha256"] = artifact_sha256 + print(json.dumps(result, sort_keys=True)) + return 2 + + +def load_json(path: Path) -> dict[str, Any]: + document = json.loads(path.read_text(encoding="utf-8")) + if not isinstance(document, dict): + raise ValueError(f"{path}: root must be an object") + return document + + +def validate_runtime_profile( + profile: dict[str, Any], + manifest: dict[str, Any], + firmware: str | None, +) -> list[str]: + if profile.get("schema_version") != 1: + raise ValueError("unsupported controlled runtime profile schema") + if profile.get("profile") != "controlled-ps5-runtime": + raise ValueError("unexpected controlled runtime profile") + + reasons: list[str] = [] + artifact = profile["artifact"] + execution = profile["execution"] + profile_firmware = profile["firmware"] + payload_manager = profile["payload_manager"] + loader = profile["loader"] + sdk = profile["sdk"] + budgets = profile["budgets"] + deployment = profile["deployment"] + effects = profile["effects"] + expected_volatile_effects = profile["expected_volatile_effects"] + blockers = profile["hard_blockers"] + if not all( + isinstance(item, dict) + for item in ( + artifact, + execution, + profile_firmware, + payload_manager, + loader, + sdk, + budgets, + deployment, + ) + ): + raise ValueError("controlled runtime profile contains a malformed object") + if ( + not isinstance(effects, list) + or not isinstance(expected_volatile_effects, list) + or not isinstance(blockers, list) + ): + raise ValueError("controlled runtime profile arrays are malformed") + + if profile.get("decision") != READY_DECISION: + reasons.append("RUNTIME_PROFILE_BLOCKED") + if ( + profile.get("execution_authorized") is not False + or execution.get("authorized") is not False + or execution.get("transferred") is not False + or execution.get("executed") is not False + ): + reasons.append("RUNTIME_PROFILE_EXECUTION_STATE_INVALID") + if execution.get("execution_eligible") is not True: + reasons.append("RUNTIME_PROFILE_EXECUTION_INELIGIBLE") + if artifact.get("built") is not True: + reasons.append("RUNTIME_PROFILE_ARTIFACT_NOT_BUILT") + if blockers: + reasons.append("RUNTIME_PROFILE_HARD_BLOCKERS_PRESENT") + + manifest_artifact = manifest["artifact"] + manifest_source = manifest.get("source") + if not isinstance(manifest_source, dict): + raise ValueError("artifact manifest source record is required") + for key in ("id", "filename", "sha256", "size"): + if artifact.get(key) != manifest_artifact.get(key): + reasons.append("RUNTIME_PROFILE_ARTIFACT_MISMATCH") + source_commit = artifact.get("source_commit") + if ( + not isinstance(source_commit, str) + or COMMIT_PATTERN.fullmatch(source_commit) is None + or source_commit != manifest_source.get("commit") + or manifest_source.get("dirty") is not False + ): + reasons.append("RUNTIME_PROFILE_SOURCE_COMMIT_MISMATCH") + + if firmware is None: + reasons.append("EXACT_FIRMWARE_REQUIRED") + if profile_firmware.get("exact") != "9.60" or firmware != "9.60": + reasons.append("FIRMWARE_MISMATCH") + if profile_firmware.get("evidence") != "jens_explicitly_confirmed_exact_9.60": + reasons.append("FIRMWARE_EVIDENCE_MISMATCH") + + for key, value in EXPECTED_PAYLOAD_MANAGER.items(): + if payload_manager.get(key) != value: + reasons.append("PAYLOAD_MANAGER_IDENTITY_MISMATCH") + for key, value in EXPECTED_LOADER.items(): + if loader.get(key) != value: + reasons.append("LOADER_IDENTITY_MISMATCH") + for key, value in EXPECTED_SDK.items(): + if sdk.get(key) != value: + reasons.append("SDK_IDENTITY_MISMATCH") + + expected_budgets = { + "automatic_retry": False, + "filesystem_write_budget": "controlled_artifact_directory_only", + "payload_network_access": "none", + "persistent_write_budget": "controlled_artifact_removable", + } + for key, value in expected_budgets.items(): + if budgets.get(key) != value: + reasons.append("RUNTIME_BUDGET_MISMATCH") + runtime = budgets.get("maximum_runtime_ms") + if ( + not isinstance(runtime, int) + or isinstance(runtime, bool) + or not (1 <= runtime <= 2000) + ): + reasons.append("RUNTIME_BUDGET_MISMATCH") + if deployment != { + "installed": False, + "ready_for_installation": True, + "rollback_prepared": True, + }: + reasons.append("DEPLOYMENT_STATE_MISMATCH") + + classified_volatile_effects: list[str] = [] + effect_ids: set[str] = set() + for item in effects: + if not isinstance(item, dict): + raise ValueError("controlled runtime effect must be an object") + effect_id = item.get("id") + if not isinstance(effect_id, str) or not effect_id or effect_id in effect_ids: + raise ValueError("controlled runtime effect IDs must be unique strings") + effect_ids.add(effect_id) + if item.get("classification") not in ALLOWED_EFFECTS: + raise ValueError("controlled runtime effect classification is invalid") + if item.get("classification") == "EXPECTED_VOLATILE_RUNTIME_EFFECT": + classified_volatile_effects.append(effect_id) + if item.get("classification") in HARD_EFFECTS: + reasons.append("RUNTIME_PROFILE_HARD_EFFECT") + if ( + not all( + isinstance(item, str) and item for item in expected_volatile_effects + ) + or len(set(expected_volatile_effects)) != len(expected_volatile_effects) + or sorted(expected_volatile_effects) != sorted(classified_volatile_effects) + ): + reasons.append("RUNTIME_PROFILE_VOLATILE_EFFECTS_MISMATCH") + + return reasons + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--manifest", type=Path, required=True) + parser.add_argument("--denylist", type=Path, required=True) + parser.add_argument("--artifact", type=Path) + parser.add_argument("--runtime-profile", type=Path) + parser.add_argument("--firmware") + args = parser.parse_args() + + try: + manifest = load_json(args.manifest) + denylist = load_json(args.denylist) + if manifest.get("schema_version") != 1: + raise ValueError("unsupported artifact manifest schema") + if denylist.get("schema_version") != 1: + raise ValueError("unsupported denylist schema") + if denylist.get("hash_algorithm") != "sha256": + raise ValueError("denylist hash algorithm must be sha256") + if denylist.get("fail_closed") is not True: + raise ValueError("denylist must be fail-closed") + + artifact_record = manifest["artifact"] + execution = manifest["execution"] + if not isinstance(artifact_record, dict) or not isinstance(execution, dict): + raise ValueError("manifest artifact and execution records must be objects") + digest = artifact_record["sha256"] + if not isinstance(digest, str) or SHA256_PATTERN.fullmatch(digest) is None: + raise ValueError("manifest artifact SHA-256 is invalid") + if not isinstance(execution.get("execution_eligible"), bool): + raise ValueError("execution_eligible must be an explicit boolean") + if any( + execution.get(key) is not False + for key in ("authorized", "transferred", "executed") + ): + reasons = ["MANIFEST_EXECUTION_STATE_INVALID"] + else: + reasons = [] + if not isinstance(artifact_record.get("filename"), str): + raise ValueError("artifact filename must be a string") + if ( + not isinstance(artifact_record.get("size"), int) + or artifact_record["size"] < 1 + ): + raise ValueError("artifact size must be a positive integer") + + blocked_hashes: set[str] = set() + entries = denylist["entries"] + if not isinstance(entries, list) or not entries: + raise ValueError("denylist entries must be a non-empty array") + for entry in entries: + entry_digest = entry["sha256"] + if not isinstance(entry_digest, str) or SHA256_PATTERN.fullmatch( + entry_digest + ) is None: + raise ValueError("denylist contains an invalid SHA-256") + if ( + entry.get("status") != "BLOCKED" + or entry.get("permanent") is not True + or entry.get("execution_eligible") is not False + ): + raise ValueError("denylist entry is not permanently blocked") + if entry_digest in blocked_hashes: + raise ValueError("denylist contains a duplicate SHA-256") + blocked_hashes.add(entry_digest) + + if execution["execution_eligible"] is not True: + reasons.append("MANIFEST_EXECUTION_INELIGIBLE") + if digest in blocked_hashes: + reasons.append("ARTIFACT_PERMANENTLY_DENYLISTED") + + if execution["execution_eligible"] is True: + if args.runtime_profile is None: + reasons.append("CONTROLLED_RUNTIME_PROFILE_REQUIRED") + else: + profile = load_json(args.runtime_profile) + reasons.extend( + validate_runtime_profile(profile, manifest, args.firmware) + ) + elif args.runtime_profile is not None: + profile = load_json(args.runtime_profile) + reasons.extend(validate_runtime_profile(profile, manifest, args.firmware)) + + if args.artifact is not None: + artifact = args.artifact.resolve(strict=True) + if not artifact.is_file(): + raise ValueError("artifact is not a regular file") + if artifact.name != artifact_record["filename"]: + reasons.append("ARTIFACT_FILENAME_MISMATCH") + if artifact.stat().st_size != artifact_record["size"]: + reasons.append("ARTIFACT_SIZE_MISMATCH") + if hash_file(artifact) != digest: + reasons.append("ARTIFACT_DIGEST_MISMATCH") + else: + reasons.append("ARTIFACT_BYTES_NOT_SUPPLIED") + + if reasons: + return deny(reasons, digest) + + print( + json.dumps( + { + "artifact_sha256": digest, + "decision": "PASS_STATIC_DEPLOYMENT_ELIGIBILITY_GATE", + "execution_authorized": False, + "reason_codes": [], + "schema_version": 1, + }, + sort_keys=True, + ) + ) + return 0 + except (KeyError, OSError, TypeError, ValueError, json.JSONDecodeError): + return deny(["INVALID_OR_INCOMPLETE_POLICY_INPUT"]) + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/check_format.py b/tools/check_format.py new file mode 100644 index 0000000..5f2c8d2 --- /dev/null +++ b/tools/check_format.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Check project-owned C and header files with clang-format.""" + +from __future__ import annotations + +import argparse +import subprocess +from pathlib import Path + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + parser.add_argument("--clang-format", required=True) + args = parser.parse_args() + + root = args.root.resolve() + files: list[Path] = [] + for directory in ("include", "src", "adapters", "samples", "tests"): + files.extend(sorted((root / directory).rglob("*.c"))) + files.extend(sorted((root / directory).rglob("*.h"))) + + result = subprocess.run( + [args.clang_format, "--dry-run", "--Werror", *map(str, files)], + cwd=root, + check=False, + ) + if result.returncode != 0: + return result.returncode + print(f"format check passed across {len(files)} C/header files") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/export-public-source.sh b/tools/export-public-source.sh new file mode 100755 index 0000000..43ee91a --- /dev/null +++ b/tools/export-public-source.sh @@ -0,0 +1,68 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: GPL-3.0-or-later + +set -euo pipefail + +if [ "$#" -ne 1 ]; then + echo "Usage: $0 OUTPUT_DIRECTORY" >&2 + exit 2 +fi + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +OUTPUT_DIR="$1" + +if [ -e "$OUTPUT_DIR" ]; then + echo "Output path already exists: $OUTPUT_DIR" >&2 + exit 1 +fi +if ! git -C "$ROOT_DIR" -c core.fileMode=false diff --ignore-space-at-eol --quiet \ + || ! git -C "$ROOT_DIR" -c core.fileMode=false diff --cached --quiet; then + echo "Commit or stash repository changes before creating a public export." >&2 + exit 1 +fi + +mkdir -p "$OUTPUT_DIR" +git -C "$ROOT_DIR" archive --format=tar HEAD | tar -xf - -C "$OUTPUT_DIR" + +# Agent instructions contain private research-session context and are not part +# of the distributable library or its safety evidence. +find "$OUTPUT_DIR" -type f -name AGENTS.md -delete + +for forbidden in \ + '.env' '*.pem' '*.key' '*.p12' '*.pfx' '*.db' '*.sqlite' '*.sqlite3' \ + '*.elf' '*.self' '*.bin' '*.dmp' '*.core' '*.zip' \ + 'secret.key' 'id_rsa' 'id_ed25519'; do + if find "$OUTPUT_DIR" -type f -name "$forbidden" -print -quit | grep -q .; then + echo "Forbidden file found in public export: $forbidden" >&2 + exit 1 + fi +done + +if grep -RIlE --exclude='export-public-source.sh' \ + '192\.168\.10\.150|NuklearRabbit|C:\\Users\\Jens' "$OUTPUT_DIR" >/dev/null; then + echo "Private operator marker found in public export." >&2 + exit 1 +fi +if find "$OUTPUT_DIR" -type f -size +10M -print -quit | grep -q .; then + echo "Unexpected file larger than 10 MiB found in public export." >&2 + exit 1 +fi + +git -C "$OUTPUT_DIR" init -q +git -C "$OUTPUT_DIR" add . +git -C "$OUTPUT_DIR" -c user.name='Chimera GFX release export' \ + -c user.email='release-export@invalid.example' \ + commit -q -m "Publish Chimera GFX source" + +( + cd "$OUTPUT_DIR" + git ls-files -z | sort -z | xargs -0 sha256sum > PUBLIC-SOURCE-MANIFEST.sha256 +) +git -C "$OUTPUT_DIR" add PUBLIC-SOURCE-MANIFEST.sha256 +git -C "$OUTPUT_DIR" -c user.name='Chimera GFX release export' \ + -c user.email='release-export@invalid.example' \ + commit -q --amend --no-edit +git -C "$OUTPUT_DIR" tag public-release-baseline + +echo "Public source export created at $OUTPUT_DIR" +echo "Commit: $(git -C "$OUTPUT_DIR" rev-parse HEAD)" diff --git a/tools/generate_artifact_manifest.py b/tools/generate_artifact_manifest.py new file mode 100644 index 0000000..6de5474 --- /dev/null +++ b/tools/generate_artifact_manifest.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Generate a deterministic manifest for a locally built, unexecuted artifact.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +from pathlib import Path + + +SDK_COMMIT = "d2e2e585740362976a39fdd5ccf390f199a7bc37" +SDL_COMMIT = "0baf4ac49382b537ba449901b5b6d0d189bb1fbb" + + +def sha256(path: Path) -> str: + digest = hashlib.sha256() + with path.open("rb") as stream: + for chunk in iter(lambda: stream.read(1024 * 1024), b""): + digest.update(chunk) + return digest.hexdigest() + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--artifact", type=Path, required=True) + parser.add_argument("--output", type=Path, required=True) + parser.add_argument("--id", required=True) + parser.add_argument("--version", required=True) + parser.add_argument("--source-repository", required=True) + parser.add_argument("--source-commit", required=True) + parser.add_argument("--target", required=True) + parser.add_argument("--firmware", default="NONE") + parser.add_argument("--profile", required=True) + parser.add_argument("--execution-eligible", action="store_true") + parser.add_argument("--with-sdl", action="store_true") + parser.add_argument("--note", action="append", default=[]) + args = parser.parse_args() + + artifact = args.artifact.resolve(strict=True) + if not artifact.is_file() or artifact.stat().st_size == 0: + raise SystemExit("artifact must be a non-empty regular file") + if len(args.source_commit) != 40 or any( + character not in "0123456789abcdef" for character in args.source_commit + ): + raise SystemExit("source commit must be a lowercase 40-character SHA-1") + + toolchain = { + "ps5_payload_sdk_release": "v0.41", + "ps5_payload_sdk_commit": SDK_COMMIT, + } + if args.with_sdl: + toolchain["sdl_commit"] = SDL_COMMIT + + document = { + "schema_version": 1, + "artifact": { + "id": args.id, + "version": args.version, + "filename": artifact.name, + "size": artifact.stat().st_size, + "sha256": sha256(artifact), + "target": args.target, + }, + "source": { + "repository": args.source_repository, + "commit": args.source_commit, + "dirty": False, + }, + "toolchain": toolchain, + "firmware_gate": { + "embedded_identifier": args.firmware, + "allowlisted": args.firmware != "NONE", + }, + "execution": { + "execution_eligible": args.execution_eligible, + "authorized": False, + "transferred": False, + "executed": False, + }, + "safety": { + "profile": args.profile, + "direct_gnm_imports": 0, + "notes": args.note, + }, + } + args.output.parent.mkdir(parents=True, exist_ok=True) + args.output.write_text( + json.dumps(document, indent=2, sort_keys=True) + "\n", encoding="utf-8" + ) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/generate_probe_symbols.py b/tools/generate_probe_symbols.py new file mode 100644 index 0000000..7be1089 --- /dev/null +++ b/tools/generate_probe_symbols.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Generate the C X-macro list from the read-only JSON manifest.""" + +from __future__ import annotations + +import argparse +import json +from pathlib import Path + + +def render(manifest_path: Path) -> str: + manifest = json.loads(manifest_path.read_text(encoding="utf-8")) + symbols = manifest["symbols"] + lines = ["/* Generated from manifests/ps5_gnm_symbols.json. Do not edit. */"] + lines.extend(f'CHIMERA_GNM_SYMBOL("{entry["name"]}")' for entry in symbols) + return "\n".join(lines) + "\n" + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--manifest", type=Path, required=True) + parser.add_argument("--output", type=Path, required=True) + parser.add_argument("--check", action="store_true") + args = parser.parse_args() + + expected = render(args.manifest) + if args.check: + if not args.output.exists() or args.output.read_text(encoding="utf-8") != expected: + print(f"stale generated file: {args.output}") + return 1 + print(f"generated file is current: {args.output}") + return 0 + + args.output.parent.mkdir(parents=True, exist_ok=True) + args.output.write_text(expected, encoding="utf-8", newline="\n") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/inspect_siecaf_header.py b/tools/inspect_siecaf_header.py new file mode 100644 index 0000000..a9bf33f --- /dev/null +++ b/tools/inspect_siecaf_header.py @@ -0,0 +1,509 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Read-only, host-only parser for bounded PS5 SIECAF structural metadata. + +This module never decrypts, extracts content, invokes ps5-bar-tool, opens a +network connection, or writes an output file. The byte layout is based on: + +* https://www.psdevwiki.com/ps5/Archive.dat +* c0w-ar/ps5-bar-tool include/bar_file.h at + 36d014672bc87577a6e0d750c2cccadc3fae0854 +""" + +from __future__ import annotations + +import argparse +from contextlib import contextmanager +import hashlib +import json +from pathlib import Path +import struct +from typing import Any, BinaryIO, Iterator +import zipfile + + +MAGIC = b"SIECAF\x00\x00" +HEADER = struct.Struct("<8sQiIiI16s12sIQQQ") +SEGMENT_META = struct.Struct(" str: + return hashlib.sha256(value).hexdigest() + + +def _checked_add(left: int, right: int, label: str) -> int: + result = left + right + if left < 0 or right < 0 or result > UINT64_MAX: + raise SiecafError(f"{label}: uint64 addition overflow") + return result + + +def _checked_mul(left: int, right: int, label: str) -> int: + result = left * right + if left < 0 or right < 0 or result > UINT64_MAX: + raise SiecafError(f"{label}: uint64 multiplication overflow") + return result + + +def _read_exact(stream: BinaryIO, size: int, label: str) -> bytes: + value = stream.read(size) + if len(value) != size: + raise SiecafError(f"{label}: truncated input") + return value + + +def _canonical_hash(records: list[dict[str, Any]]) -> str: + encoded = json.dumps( + records, sort_keys=True, separators=(",", ":"), ensure_ascii=True + ).encode("ascii") + return _sha256(encoded) + + +def inspect_siecaf( + stream: BinaryIO, source_size: int, *, source_label: str = "" +) -> dict[str, Any]: + """Parse structural metadata from an already-open, read-only stream.""" + if source_size < HEADER.size: + return { + "source": source_label, + "source_size": source_size, + "classification": "SIECAF_MALFORMED", + "errors": ["header: truncated input"], + "warnings": [], + } + stream.seek(0) + header_raw = _read_exact(stream, HEADER.size, "header") + ( + magic, + unknown_u64, + mode, + pad1, + version, + pad2, + key, + iv12, + pad3, + segment_count, + file_offset, + file_size, + ) = HEADER.unpack(header_raw) + header = { + "magic_hex": magic.hex(), + "magic_ascii": magic.rstrip(b"\x00").decode("ascii", errors="replace"), + "unknown_u64": unknown_u64, + "mode_i32": mode, + "padding_1_u32": pad1, + "version_i32": version, + "padding_2_u32": pad2, + "key_or_unknown_16_redacted": True, + "key_or_unknown_16_sha256": _sha256(key), + "iv_12_hex": iv12.hex(), + "padding_3_u32": pad3, + "segment_count": segment_count, + "file_offset": file_offset, + "file_size": file_size, + "raw_sha256": _sha256(header_raw), + } + if magic != MAGIC: + return { + "source": source_label, + "source_size": source_size, + "classification": "SIECAF_MALFORMED", + "header": header, + "errors": ["magic: expected SIECAF\\0\\0"], + "warnings": [], + } + if version not in SUPPORTED_VERSIONS: + return { + "source": source_label, + "source_size": source_size, + "classification": "SIECAF_UNSUPPORTED_VERSION", + "header": header, + "errors": [f"unsupported SIECAF header version: {version}"], + "warnings": [], + } + errors: list[str] = [] + warnings: list[str] = [] + if segment_count == 0: + errors.append("segment_count: zero") + try: + metadata_bytes = _checked_mul( + segment_count, SEGMENT_META.size, "segment metadata table" + ) + hash_bytes = _checked_mul( + segment_count, SECTION_HASH.size, "section hash table" + ) + metadata_end = _checked_add(HEADER.size, metadata_bytes, "metadata end") + tables_end = _checked_add(metadata_end, hash_bytes, "tables end") + declared_end = _checked_add(file_offset, file_size, "declared file end") + except SiecafError as error: + return { + "source": source_label, + "source_size": source_size, + "classification": "SIECAF_MALFORMED", + "header": header, + "errors": [str(error)], + "warnings": [], + } + if tables_end > source_size: + errors.append("tables: outside source file") + if tables_end > file_offset: + errors.append("tables: overlap declared data region") + if file_offset % ALIGNMENT != 0: + errors.append(f"file_offset: not aligned to {ALIGNMENT}") + if declared_end > source_size: + errors.append("declared file range: outside source file") + elif declared_end < source_size: + warnings.append( + f"trailing data after declared file range: {source_size - declared_end}" + ) + if errors: + return { + "source": source_label, + "source_size": source_size, + "classification": "SIECAF_MALFORMED", + "header": header, + "tables_end": tables_end, + "errors": errors, + "warnings": warnings, + } + + metadata: list[dict[str, Any]] = [] + for index in range(segment_count): + raw = _read_exact(stream, SEGMENT_META.size, f"segment metadata {index}") + ( + section_id, + padding_1, + part_number, + data_offset, + aligned_length, + hash_key_id, + encryption_key_id, + segment_iv12, + segment_iv_padding, + unaligned_length, + ) = SEGMENT_META.unpack(raw) + iv16 = segment_iv12 + struct.pack(" 1 + } + ) + duplicate_metadata_keys = sorted( + { + (section_id, part_number) + for section_id, part_number in metadata_keys + if metadata_keys.count((section_id, part_number)) > 1 + } + ) + repeated_metadata_ids = sorted( + {item for item in metadata_ids if metadata_ids.count(item) > 1} + ) + duplicate_hash_ids = sorted({item for item in hash_ids if hash_ids.count(item) > 1}) + if duplicate_metadata_ids: + errors.append( + f"duplicate metadata section ID/part keys: {duplicate_metadata_keys}" + ) + if duplicate_hash_ids: + errors.append(f"duplicate hash section IDs: {duplicate_hash_ids}") + if set(hash_ids) != set(range(segment_count)): + errors.append("hash section IDs do not cover metadata table indexes") + + hash_by_id = { + item["section_id"]: item + for item in hashes + if item["section_id"] not in duplicate_hash_ids + } + ranges: list[tuple[int, int, int]] = [] + for item in metadata: + section_id = item["section_id"] + start = item["data_offset"] + length = item["aligned_length"] + unaligned = item["unaligned_length"] + try: + end = _checked_add(start, length, f"section {section_id} end") + except SiecafError as error: + errors.append(str(error)) + continue + if section_id < 0: + errors.append(f"section {section_id}: negative ID") + if start % ALIGNMENT != 0: + errors.append(f"section {section_id}: unaligned data offset") + if length == 0 and unaligned != 0: + errors.append(f"section {section_id}: incoherent unaligned length") + if length > 0 and length % ALIGNMENT != 0: + errors.append(f"section {section_id}: incoherent aligned length") + if length > 0 and (unaligned > length or length - unaligned >= ALIGNMENT): + errors.append(f"section {section_id}: incoherent unaligned length") + if start < file_offset or end > declared_end or end > source_size: + errors.append(f"section {section_id}: range outside declared file data") + if length > 0: + ranges.append((start, end, section_id)) + hash_record = hash_by_id.get(item["table_index"]) + item["section_type"] = ( + hash_record["section_type"] if hash_record is not None else None + ) + item["section_hash_128_hex"] = ( + hash_record["section_hash_128_hex"] if hash_record is not None else None + ) + + ranges.sort() + overlaps: list[dict[str, int]] = [] + gaps: list[dict[str, int]] = [] + previous_end = file_offset + previous_id = -1 + for start, end, section_id in ranges: + if start < previous_end: + overlaps.append( + { + "left_section_id": previous_id, + "right_section_id": section_id, + "overlap_bytes": previous_end - start, + } + ) + elif start > previous_end: + gaps.append( + { + "after_section_id": previous_id, + "before_section_id": section_id, + "gap_bytes": start - previous_end, + } + ) + if end > previous_end: + previous_end = end + previous_id = section_id + if overlaps: + errors.append(f"overlapping section ranges: {len(overlaps)}") + if gaps: + warnings.append(f"gaps between section ranges: {len(gaps)}") + trailing_data = max(0, source_size - previous_end) + if trailing_data: + warnings.append(f"trailing data after final section: {trailing_data}") + if previous_end < declared_end: + warnings.append( + f"uncovered bytes inside declared data range: {declared_end - previous_end}" + ) + + normalized_segments = [ + { + key: item[key] + for key in ( + "section_id", + "part_number", + "data_offset", + "aligned_length", + "unaligned_length", + "hash_key_id_or_algorithm_type", + "encryption_key_id_or_algorithm_version", + "iv_hex", + ) + } + for item in sorted( + metadata, + key=lambda value: ( + value["section_id"], + value["part_number"], + value["data_offset"], + ), + ) + ] + normalized_hashes = [ + { + key: item[key] + for key in ("section_id", "section_type", "section_hash_128_hex") + } + for item in sorted( + hashes, key=lambda value: (value["section_id"], value["section_type"]) + ) + ] + normalized_layout = [ + { + key: item[key] + for key in ( + "section_id", + "part_number", + "data_offset", + "aligned_length", + "unaligned_length", + "hash_key_id_or_algorithm_type", + "encryption_key_id_or_algorithm_version", + ) + } + for item in normalized_segments + ] + normalized_segment_table_sha256 = _canonical_hash(normalized_segments) + normalized_hash_blocks_sha256 = _canonical_hash(normalized_hashes) + normalized_layout_sha256 = _canonical_hash( + [ + { + "unknown_u64": unknown_u64, + "version_i32": version, + "segment_count": segment_count, + "file_offset": file_offset, + "file_size": file_size, + }, + *normalized_layout, + ] + ) + structural_fingerprint_sha256 = _canonical_hash( + [ + { + "header_raw_sha256": header["raw_sha256"], + "normalized_segment_table_sha256": (normalized_segment_table_sha256), + "normalized_hash_blocks_sha256": normalized_hash_blocks_sha256, + } + ] + ) + return { + "source": source_label, + "source_size": source_size, + "classification": ("SIECAF_MALFORMED" if errors else "SIECAF_VALID_STRUCTURE"), + "header": header, + "tables_end": tables_end, + "table_padding_bytes": file_offset - tables_end, + "segments": sorted(metadata, key=lambda item: item["table_index"]), + "section_hashes": sorted(hashes, key=lambda item: item["table_index"]), + "normalized_segment_table_sha256": normalized_segment_table_sha256, + "normalized_hash_blocks_sha256": normalized_hash_blocks_sha256, + "normalized_layout_sha256": normalized_layout_sha256, + "structural_fingerprint_sha256": structural_fingerprint_sha256, + "duplicate_metadata_section_ids": duplicate_metadata_ids, + "duplicate_metadata_section_keys": [ + {"section_id": section_id, "part_number": part_number} + for section_id, part_number in duplicate_metadata_keys + ], + "repeated_metadata_section_ids": repeated_metadata_ids, + "duplicate_hash_section_ids": duplicate_hash_ids, + "overlaps": overlaps, + "gaps": gaps, + "trailing_data_bytes": trailing_data, + "errors": errors, + "warnings": warnings, + } + + +def compare_structures(left: dict[str, Any], right: dict[str, Any]) -> dict[str, Any]: + """Classify two already-parsed structural fingerprints.""" + if ( + left.get("classification") == "SIECAF_UNSUPPORTED_VERSION" + or right.get("classification") == "SIECAF_UNSUPPORTED_VERSION" + ): + classification = "SIECAF_UNSUPPORTED_VERSION" + elif ( + left.get("classification") != "SIECAF_VALID_STRUCTURE" + or right.get("classification") != "SIECAF_VALID_STRUCTURE" + ): + classification = "SIECAF_MALFORMED" + elif left.get("structural_fingerprint_sha256") == right.get( + "structural_fingerprint_sha256" + ): + classification = "SIECAF_STRUCTURAL_EXACT" + elif left.get("normalized_layout_sha256") == right.get("normalized_layout_sha256"): + classification = "SIECAF_LAYOUT_MATCH_HASHES_DIFFER" + else: + classification = "SIECAF_LAYOUT_DIFFERENT" + return { + "classification": classification, + "left_structural_fingerprint_sha256": left.get("structural_fingerprint_sha256"), + "right_structural_fingerprint_sha256": right.get( + "structural_fingerprint_sha256" + ), + "left_layout_sha256": left.get("normalized_layout_sha256"), + "right_layout_sha256": right.get("normalized_layout_sha256"), + "left_hash_blocks_sha256": left.get("normalized_hash_blocks_sha256"), + "right_hash_blocks_sha256": right.get("normalized_hash_blocks_sha256"), + } + + +@contextmanager +def open_source( + path: Path, zip_entry: str | None +) -> Iterator[tuple[BinaryIO, int, str]]: + if zip_entry is None: + with path.open("rb") as stream: + yield stream, path.stat().st_size, str(path) + return + with zipfile.ZipFile(path) as archive: + info = archive.getinfo(zip_entry) + with archive.open(info) as stream: + yield stream, info.file_size, f"{path}::{zip_entry}" + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("path", type=Path) + parser.add_argument("--zip-entry") + parser.add_argument("--label") + parser.add_argument("--pretty", action="store_true") + args = parser.parse_args() + try: + with open_source(args.path.resolve(), args.zip_entry) as ( + stream, + size, + default_label, + ): + result = inspect_siecaf( + stream, size, source_label=args.label or default_label + ) + except (OSError, KeyError, zipfile.BadZipFile, SiecafError) as error: + result = { + "source": args.label or str(args.path), + "classification": "SIECAF_MALFORMED", + "errors": [str(error)], + "warnings": [], + } + print( + json.dumps( + result, + sort_keys=True, + indent=2 if args.pretty else None, + ) + ) + return 0 if result["classification"] == "SIECAF_VALID_STRUCTURE" else 2 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/package_phase07_review.py b/tools/package_phase07_review.py new file mode 100644 index 0000000..6a4ddf6 --- /dev/null +++ b/tools/package_phase07_review.py @@ -0,0 +1,253 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Create deterministic, offline-only Phase-0.7 review archives.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +import shutil +import subprocess +import zipfile +from pathlib import Path + + +ARCHIVE_TIMESTAMP = (2026, 7, 17, 0, 0, 0) +ARTIFACTS = { + "chimera-elfldr-phase07.elf": { + "source": "outputs/phase07/artifacts/chimera-elfldr-phase07-a.elf", + "sha256": "63e810982471eb40cae3a20aa9df9a0a02892f420e429874fae4e99aa400b561", + "size": 397000, + }, + "chimera-gfx-lifecycle-probe.elf": { + "source": "outputs/phase07/artifacts/chimera-gfx-lifecycle-probe.elf", + "sha256": "bfb4a5cc768e162fe4c2fddf41c3978e152722918a39085277fd172cb95a7182", + "size": 112680, + }, + "chimera-payload-manager-phase07.elf": { + "source": "outputs/phase07/artifacts/chimera-pldmgr-phase07-a.elf", + "sha256": "8fecf8241a46246eddbd21e8bb4d875f5d76f1f4f4c6a11384df1f131aa5e5b1", + "size": 99560, + }, +} +STOCK_ELFLDR = { + "source": "work/upstream/release-assets/elfldr-ps5-v0.23.elf", + "sha256": "092d16ee0ede0c494947efd38d1a17bbd7cc4b022d3858ea898833c188c703e8", + "size": 397000, +} +BLOCKED_SHA256 = ( + "4be1c17b4964f2b68c39b5145bc4af4619c32512d60269ecf5c39728b390fa63" +) + + +def sha256(path: Path) -> str: + digest = hashlib.sha256() + with path.open("rb") as stream: + for block in iter(lambda: stream.read(1024 * 1024), b""): + digest.update(block) + return digest.hexdigest() + + +def verify(path: Path, expected: dict[str, object]) -> None: + if not path.is_file(): + raise RuntimeError(f"missing package input: {path}") + if path.stat().st_size != expected["size"]: + raise RuntimeError(f"unexpected size: {path}") + if sha256(path) != expected["sha256"]: + raise RuntimeError(f"unexpected SHA-256: {path}") + + +def copy(root: Path, relative_source: str, destination: Path) -> None: + source = root / relative_source + if not source.is_file(): + raise RuntimeError(f"missing package input: {source}") + destination.parent.mkdir(parents=True, exist_ok=True) + shutil.copyfile(source, destination) + + +def write_json(path: Path, document: object) -> None: + path.write_text( + json.dumps(document, indent=2, sort_keys=True) + "\n", + encoding="utf-8", + newline="\n", + ) + + +def write_sums(directory: Path) -> None: + entries = [] + for path in sorted(directory.rglob("*")): + if path.is_file() and path.name != "SHA256SUMS.txt": + relative = path.relative_to(directory).as_posix() + entries.append(f"{sha256(path)} {relative}") + (directory / "SHA256SUMS.txt").write_text( + "\n".join(entries) + "\n", encoding="utf-8", newline="\n" + ) + + +def deterministic_zip(source: Path, destination: Path) -> None: + with zipfile.ZipFile( + destination, "w", compression=zipfile.ZIP_DEFLATED, compresslevel=9 + ) as archive: + for path in sorted(source.rglob("*")): + if not path.is_file(): + continue + relative = (Path(source.name) / path.relative_to(source)).as_posix() + info = zipfile.ZipInfo(relative, ARCHIVE_TIMESTAMP) + info.compress_type = zipfile.ZIP_DEFLATED + info.external_attr = 0o100644 << 16 + archive.writestr(info, path.read_bytes(), compresslevel=9) + + +def clean_destination(root: Path, destination: Path) -> None: + resolved_root = root.resolve() + resolved = destination.resolve() + if resolved_root not in resolved.parents: + raise RuntimeError(f"refusing destination outside repository: {resolved}") + if destination.exists(): + shutil.rmtree(destination) + + +def git_head(root: Path) -> str: + status = subprocess.run( + ["git", "status", "--porcelain", "--untracked-files=no"], + cwd=root, + check=True, + capture_output=True, + text=True, + ) + if status.stdout.strip(): + raise RuntimeError("tracked worktree must be clean before packaging") + result = subprocess.run( + ["git", "rev-parse", "HEAD"], + cwd=root, + check=True, + capture_output=True, + text=True, + ) + return result.stdout.strip() + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + root = args.root.resolve() + head = git_head(root) + packages = root / "outputs" / "phase07" / "packages" + installation = packages / "phase07-installation-review" + rollback = packages / "phase07-rollback-review" + clean_destination(root, installation) + clean_destination(root, rollback) + packages.mkdir(parents=True, exist_ok=True) + + for filename, expected in ARTIFACTS.items(): + source = root / str(expected["source"]) + verify(source, expected) + copy(root, str(expected["source"]), installation / "artifacts" / filename) + + review_files = [ + "docs/approvals/phase07-hardened-installation-request.md", + "docs/approvals/phase07-lifecycle-transfer-execution-request.md", + "docs/runtime/phase-0.7-hardening.md", + "docs/runtime/controlled-runtime-policy.md", + "manifests/artifact-denylist.json", + "manifests/artifacts/chimera-elfldr-phase07-fw-9.60.json", + "manifests/artifacts/chimera-gfx-lifecycle-probe-phase07-fw-9.60.json", + "manifests/artifacts/chimera-payload-manager-phase07-fw-9.60.json", + "manifests/runtime/controlled-ps5-runtime-profile.json", + "manifests/runtime/phase-0.7-kernelwrite-proof-matrix.json", + "manifests/runtime/phase-0.7-offline-audit.json", + "packaging/phase07/README-installation-review.md", + ] + for relative in review_files: + copy(root, relative, installation / relative) + audit_directory = root / "outputs" / "phase07" / "audit" + if not audit_directory.is_dir(): + raise RuntimeError("full Phase-0.7 audit reports are absent") + (installation / "audit").mkdir(parents=True, exist_ok=True) + for report in sorted(audit_directory.glob("*")): + if report.is_file(): + shutil.copyfile(report, installation / "audit" / report.name) + + install_manifest = { + "artifacts": { + name: { + "sha256": item["sha256"], + "size": item["size"], + } + for name, item in ARTIFACTS.items() + }, + "blocked_sha256": BLOCKED_SHA256, + "decision": "READY_FOR_HARDENED_RUNTIME_DEPLOYMENT", + "execution_authorized": False, + "firmware": "9.60", + "installation_authorized": False, + "repository_head": head, + "schema_version": 1, + } + write_json(installation / "PACKAGE-MANIFEST.json", install_manifest) + write_sums(installation) + + stock_source = root / str(STOCK_ELFLDR["source"]) + verify(stock_source, STOCK_ELFLDR) + copy( + root, + str(STOCK_ELFLDR["source"]), + rollback / "stock" / "elfldr-ps5-v0.23.elf", + ) + copy( + root, + "packaging/phase07/README-rollback.md", + rollback / "README-rollback.md", + ) + rollback_manifest = { + "existing_payload_manager_backup": { + "available_offline": False, + "required_before_installation": True, + "sha256": "518740adbacccb9094fadb07dd424c53ee290f38306449ccc9d6957fdf813c0b", + "size": 2050320, + }, + "installation_authorized": False, + "repository_head": head, + "rollback_authorized": False, + "schema_version": 1, + "stock_elfldr": { + "available_offline": True, + "sha256": STOCK_ELFLDR["sha256"], + "size": STOCK_ELFLDR["size"], + }, + } + write_json(rollback / "ROLLBACK-MANIFEST.json", rollback_manifest) + write_sums(rollback) + + installation_zip = packages / "phase07-installation-review.zip" + rollback_zip = packages / "phase07-rollback-review.zip" + for archive in (installation_zip, rollback_zip): + if archive.exists(): + archive.unlink() + deterministic_zip(installation, installation_zip) + deterministic_zip(rollback, rollback_zip) + package_index = { + "archives": { + installation_zip.name: { + "sha256": sha256(installation_zip), + "size": installation_zip.stat().st_size, + }, + rollback_zip.name: { + "sha256": sha256(rollback_zip), + "size": rollback_zip.stat().st_size, + }, + }, + "execution_authorized": False, + "installation_authorized": False, + "repository_head": head, + "schema_version": 1, + } + write_json(packages / "package-index.json", package_index) + print(json.dumps(package_index, sort_keys=True)) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/phase10aa_offline_fake_batch.py b/tools/phase10aa_offline_fake_batch.py new file mode 100644 index 0000000..3cb9691 --- /dev/null +++ b/tools/phase10aa_offline_fake_batch.py @@ -0,0 +1,287 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Offline Phase-1.0AA integration for the passive batch contract. + +Only the exact built-in fake adapter and synthetic clock are accepted. This +module has no live adapter protocol, network import, address, CLI or real clock. +""" + +from __future__ import annotations + +from dataclasses import dataclass +import hashlib +import math +from typing import Any + +from phase10w_shsrv_client_policy import SessionPlan +from phase10x_inactive_transport import ( + EvidenceFailure, + EvidenceRecord, + ExclusiveEvidenceStore, +) +from phase10z_passive_batch_contract import ( + PassiveBatch, + PassiveContractError, + PassiveResultAccumulator, + build_passive_batch, +) + + +PHASE10Z_CONTRACT_SHA256 = \ + "0728c2be7f368e0a7f4b68efe86f6e0c5c2f50704a41d0e1992b0bfec19dde06" +MAX_FAKE_EVENTS = 257 +MAX_FAKE_ADVANCE_SECONDS = 60.0 +EVENT_DATA = "DATA" +EVENT_HARD_DEADLINE = "HARD_DEADLINE" +EVENT_REMOTE_EOF = "REMOTE_EOF" +EVENT_BLOCKED = "BLOCKED" +EVENT_KINDS = { + EVENT_DATA, EVENT_HARD_DEADLINE, EVENT_REMOTE_EOF, EVENT_BLOCKED, +} + + +class OfflineIntegrationError(RuntimeError): + """Normalized offline failure without supplied data, target or path.""" + + +class FakeAdapterError(RuntimeError): + """Built-in fake-adapter state failure.""" + + +@dataclass(frozen=True) +class FakeReceiveEvent: + """One caller-supplied synthetic event; never a live receive result.""" + + kind: str + data: bytes = b"" + advance_seconds: float = 0.0 + + def __post_init__(self) -> None: + if self.kind not in EVENT_KINDS: + raise FakeAdapterError("fake event kind is invalid") + if not isinstance(self.data, bytes): + raise FakeAdapterError("fake event data must be bytes") + if self.kind == EVENT_DATA and not self.data: + raise FakeAdapterError("fake data event is empty") + if self.kind != EVENT_DATA and self.data: + raise FakeAdapterError("fake control event contains data") + value = self.advance_seconds + if not isinstance(value, (int, float)) or isinstance(value, bool) or \ + not math.isfinite(value) or not 0.0 <= value <= \ + MAX_FAKE_ADVANCE_SECONDS: + raise FakeAdapterError("fake time advance is invalid") + + +class OfflineFakeClock: + """Explicit synthetic monotonic value; never acquires host time.""" + + def __init__(self, initial: float = 0.0) -> None: + if not isinstance(initial, (int, float)) or isinstance(initial, bool) or \ + not math.isfinite(initial): + raise FakeAdapterError("fake clock initial value is invalid") + self._value = float(initial) + + def monotonic(self) -> float: + return self._value + + def advance(self, seconds: float) -> None: + if not isinstance(seconds, (int, float)) or isinstance(seconds, bool) or \ + not math.isfinite(seconds) or not 0.0 <= seconds <= \ + MAX_FAKE_ADVANCE_SECONDS: + raise FakeAdapterError("fake clock advance is invalid") + self._value += float(seconds) + + +class OfflineFakeBatchAdapter: + """Closed fake only: one open, one exact batch and scripted events.""" + + def __init__( + self, clock: OfflineFakeClock, events: tuple[FakeReceiveEvent, ...], + ) -> None: + if type(clock) is not OfflineFakeClock: + raise FakeAdapterError("only the exact fake clock is accepted") + if not isinstance(events, tuple) or not 1 <= len(events) <= MAX_FAKE_EVENTS: + raise FakeAdapterError("fake event sequence is invalid") + if any(type(event) is not FakeReceiveEvent for event in events): + raise FakeAdapterError("fake event type is invalid") + self.clock = clock + self._events = list(events) + self.state = "NEW" + self.open_count = 0 + self.send_count = 0 + self.close_count = 0 + self.sent_sha256: str | None = None + self.trace: list[str] = [] + self.logical_event_buffer_discarded = False + + def open_once(self) -> None: + if self.state != "NEW": + raise FakeAdapterError("fake adapter cannot open") + self.open_count += 1 + self.trace.append("FAKE_OPEN") + self.state = "OPEN" + + def send_one_batch(self, batch: PassiveBatch) -> None: + if self.state != "OPEN" or type(batch) is not PassiveBatch: + raise FakeAdapterError("fake adapter cannot accept a batch") + self.send_count += 1 + if self.send_count != 1: + raise FakeAdapterError("second fake send is forbidden") + self.sent_sha256 = hashlib.sha256(batch.payload).hexdigest() + self.trace.append("FAKE_SEND_ONE_BATCH") + self.state = "SENT" + + def next_event(self) -> FakeReceiveEvent: + if self.state not in {"SENT", "RECEIVING"} or not self._events: + raise FakeAdapterError("fake adapter has no next event") + event = self._events.pop(0) + self.clock.advance(event.advance_seconds) + self.trace.append(f"FAKE_EVENT_{event.kind}") + self.state = "RECEIVING" + return event + + def close_once(self) -> None: + if self.state not in {"OPEN", "SENT", "RECEIVING"}: + raise FakeAdapterError("fake adapter cannot close") + self.close_count += 1 + if self.close_count != 1: + raise FakeAdapterError("second fake close is forbidden") + self._events.clear() + self.logical_event_buffer_discarded = True + self.trace.append("FAKE_CLOSE") + self.state = "CLOSED" + + +class OfflineFakeEvidenceStore(ExclusiveEvidenceStore): + """Phase-specific exclusive local evidence; no raw transcript.""" + + def create_fake_consumed_receipt( + self, plan: SessionPlan, batch: PassiveBatch, start: float, + ) -> EvidenceRecord: + return self._create(f"{plan.run_id}.aa-consumed.json", { + "schema_version": 1, + "status": "OFFLINE_FAKE_ATTEMPT_CONSUMED_BEFORE_OPEN", + "run_id": plan.run_id, + "phase10z_contract_sha256": PHASE10Z_CONTRACT_SHA256, + "window": batch.window, + "batch_sha256": hashlib.sha256(batch.payload).hexdigest(), + "batch_size": len(batch.payload), + "deadline_seconds": batch.deadline_seconds, + "created_fake_monotonic": start, + "target_retained": False, + "raw_transcript_persisted": False, + "retry_allowed": False, + "reconnect_allowed": False, + "resume_allowed": False, + "device_behavior_proven": False, + }) + + +@dataclass(frozen=True) +class OfflineFakeOutcome: + receipt: EvidenceRecord + output: EvidenceRecord + classification: str + exact_identity: bool + batch_sha256: str + trace: tuple[str, ...] + fake_only: bool = True + device_behavior_proven: bool = False + + +def run_offline_fake_batch( + plan: SessionPlan, + adapter: OfflineFakeBatchAdapter, + clock: OfflineFakeClock, + evidence: OfflineFakeEvidenceStore, +) -> OfflineFakeOutcome: + """Exercise Z end-to-end with exact built-in fakes and local evidence.""" + if type(adapter) is not OfflineFakeBatchAdapter or \ + type(clock) is not OfflineFakeClock or \ + type(evidence) is not OfflineFakeEvidenceStore or \ + adapter.clock is not clock: + raise OfflineIntegrationError("offline fake boundary type is invalid") + try: + batch = build_passive_batch(plan) + except PassiveContractError as error: + raise OfflineIntegrationError("passive batch preparation failed") from error + start = clock.monotonic() + deadline = start + batch.deadline_seconds + try: + receipt = evidence.create_fake_consumed_receipt(plan, batch, start) + except EvidenceFailure as error: + raise OfflineIntegrationError("offline receipt creation failed") from error + trace = ["RECEIPT_CREATED"] + accumulator = PassiveResultAccumulator(batch) + sanitized: dict[str, Any] | None = None + opened = False + failure: OfflineIntegrationError | None = None + try: + opened = True + adapter.open_once() + trace.extend(adapter.trace[-1:]) + if clock.monotonic() >= deadline: + raise OfflineIntegrationError("deadline reached before fake send") + adapter.send_one_batch(batch) + trace.extend(adapter.trace[-1:]) + for _index in range(MAX_FAKE_EVENTS): + event = adapter.next_event() + trace.extend(adapter.trace[-1:]) + now = clock.monotonic() + if event.kind == EVENT_DATA: + if now >= deadline: + raise OfflineIntegrationError("fake data reached deadline") + accumulator.feed_supplied_chunk(event.data) + continue + if event.kind == EVENT_HARD_DEADLINE: + if now < deadline: + raise OfflineIntegrationError("fake deadline arrived early") + sanitized = accumulator.seal_at_hard_deadline(True) + break + if event.kind == EVENT_REMOTE_EOF: + raise OfflineIntegrationError("remote EOF is not completion") + if event.kind == EVENT_BLOCKED: + raise OfflineIntegrationError("blocked fake receive is invalid") + if sanitized is None: + raise OfflineIntegrationError("hard deadline result is missing") + except Exception as error: # noqa: BLE001 - exact fake boundary normalization + failure = OfflineIntegrationError("offline fake integration failed") + failure.__cause__ = error + finally: + if opened: + try: + adapter.close_once() + trace.extend(adapter.trace[-1:]) + except FakeAdapterError as error: + if failure is None: + failure = OfflineIntegrationError("offline fake close failed") + failure.__cause__ = error + if failure is not None: + raise failure + if sanitized is None or adapter.sent_sha256 is None: + raise OfflineIntegrationError("offline fake result is incomplete") + sanitized["phase10aa_fake_integration"] = { + "offline_fake_only": True, + "exact_builtin_adapter_required": True, + "exact_builtin_clock_required": True, + "receipt_created_before_fake_open": True, + "one_fake_open": adapter.open_count == 1, + "one_fake_batch_send": adapter.send_count == 1, + "one_fake_close": adapter.close_count == 1, + "logical_event_buffer_discarded": adapter.logical_event_buffer_discarded, + "physical_memory_erasure_proven": False, + "network_transport_present": False, + "device_behavior_proven": False, + } + try: + output = evidence.create_sanitized_output(plan, receipt, sanitized) + except EvidenceFailure as error: + raise OfflineIntegrationError("offline sanitized output failed") from error + return OfflineFakeOutcome( + receipt=receipt, + output=output, + classification=str(sanitized["classification"]), + exact_identity=bool(sanitized["exact_identity"]), + batch_sha256=adapter.sent_sha256, + trace=tuple(trace), + ) diff --git a/tools/phase10ab_nonblocking_trace_model.py b/tools/phase10ab_nonblocking_trace_model.py new file mode 100644 index 0000000..081585f --- /dev/null +++ b/tools/phase10ab_nonblocking_trace_model.py @@ -0,0 +1,218 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Pure offline trace model for a future nonblocking host adapter. + +No socket, selector, address, DNS, OS call, real clock, CLI or file output is +present. The model only validates caller-supplied synthetic operation traces. +""" + +from __future__ import annotations + +from dataclasses import dataclass +import math + + +MAX_TRACE_EVENTS = 512 +MAX_BATCH_BYTES = 1035 +MAX_RECEIVE_BYTES = 65_536 +MAX_DEADLINE_SECONDS = 10.0 + +RECEIPT_CREATED = "RECEIPT_CREATED" +SOCKET_CREATED = "SOCKET_CREATED" +SET_NONBLOCKING = "SET_NONBLOCKING" +CONNECT_IMMEDIATE = "CONNECT_IMMEDIATE" +CONNECT_PENDING = "CONNECT_PENDING" +READY_WRITE = "READY_WRITE" +SO_ERROR_ZERO = "SO_ERROR_ZERO" +SEND_BYTES = "SEND_BYTES" +READY_READ = "READY_READ" +RECV_BYTES = "RECV_BYTES" +RECV_EOF = "RECV_EOF" +WAIT_INTERRUPTED = "WAIT_INTERRUPTED" +WAIT_TIMEOUT = "WAIT_TIMEOUT" +DEADLINE_REACHED = "DEADLINE_REACHED" +SANITIZER_ACCEPTED = "SANITIZER_ACCEPTED" +LOCAL_CLOSE = "LOCAL_CLOSE" +OUTPUT_CREATED = "OUTPUT_CREATED" + +OPERATIONS = { + RECEIPT_CREATED, SOCKET_CREATED, SET_NONBLOCKING, CONNECT_IMMEDIATE, + CONNECT_PENDING, READY_WRITE, SO_ERROR_ZERO, SEND_BYTES, READY_READ, + RECV_BYTES, RECV_EOF, WAIT_INTERRUPTED, WAIT_TIMEOUT, DEADLINE_REACHED, + SANITIZER_ACCEPTED, LOCAL_CLOSE, OUTPUT_CREATED, +} + + +class TraceModelError(RuntimeError): + """Fail-closed synthetic trace error.""" + + +@dataclass(frozen=True) +class TraceEvent: + operation: str + at_seconds: float + value: int = 0 + + def __post_init__(self) -> None: + if self.operation not in OPERATIONS: + raise TraceModelError("trace operation is invalid") + if not isinstance(self.at_seconds, (int, float)) or isinstance( + self.at_seconds, bool) or not math.isfinite(self.at_seconds) or \ + self.at_seconds < 0: + raise TraceModelError("trace time is invalid") + if not isinstance(self.value, int) or isinstance(self.value, bool) or \ + self.value < 0: + raise TraceModelError("trace value is invalid") + if self.operation not in {SEND_BYTES, RECV_BYTES} and self.value != 0: + raise TraceModelError("valueless trace operation has a value") + if self.operation in {SEND_BYTES, RECV_BYTES} and self.value == 0: + raise TraceModelError("byte operation has no progress") + + +@dataclass(frozen=True) +class TraceAssessment: + classification: str + batch_bytes_sent: int + receive_bytes: int + receipt_before_create: bool + nonblocking_before_connect: bool + complete_send_loop: bool + deadline_only_completion: bool + local_close_observed: bool + exact_identity_proven: bool = False + device_behavior_proven: bool = False + live_transport_present: bool = False + + +def assess_nonblocking_trace( + batch_size: int, deadline_seconds: float, events: tuple[TraceEvent, ...], +) -> TraceAssessment: + """Validate one exact synthetic lifecycle; raise on every ambiguity.""" + if not isinstance(batch_size, int) or isinstance(batch_size, bool) or \ + not 1 <= batch_size <= MAX_BATCH_BYTES: + raise TraceModelError("batch size is invalid") + if not isinstance(deadline_seconds, (int, float)) or isinstance( + deadline_seconds, bool) or not math.isfinite(deadline_seconds) or \ + not 0 < deadline_seconds <= MAX_DEADLINE_SECONDS: + raise TraceModelError("deadline is invalid") + if not isinstance(events, tuple) or not 1 <= len(events) <= MAX_TRACE_EVENTS or \ + any(type(event) is not TraceEvent for event in events): + raise TraceModelError("trace shape is invalid") + + state = "START" + last_time = -1.0 + sent = 0 + received = 0 + write_ready = False + read_ready = False + connected = False + deadline_seen = False + sanitizer_seen = False + close_seen = False + + for event in events: + if event.at_seconds < last_time: + raise TraceModelError("trace time moved backward") + last_time = float(event.at_seconds) + operation = event.operation + + if deadline_seen and operation not in { + SANITIZER_ACCEPTED, LOCAL_CLOSE, OUTPUT_CREATED}: + raise TraceModelError("I/O occurred after deadline") + if not deadline_seen and event.at_seconds >= deadline_seconds and \ + operation != DEADLINE_REACHED: + raise TraceModelError("nondeadline operation reached deadline") + + if operation == RECEIPT_CREATED: + if state != "START": + raise TraceModelError("receipt ordering is invalid") + state = "RECEIPT" + elif operation == SOCKET_CREATED: + if state != "RECEIPT": + raise TraceModelError("socket creation precedes receipt") + state = "CREATED" + elif operation == SET_NONBLOCKING: + if state != "CREATED": + raise TraceModelError("nonblocking setup ordering is invalid") + state = "NONBLOCKING" + elif operation == CONNECT_IMMEDIATE: + if state != "NONBLOCKING": + raise TraceModelError("immediate connect ordering is invalid") + connected = True + state = "CONNECTED" + elif operation == CONNECT_PENDING: + if state != "NONBLOCKING": + raise TraceModelError("pending connect ordering is invalid") + state = "CONNECT_PENDING" + elif operation == READY_WRITE: + if state == "CONNECT_PENDING": + state = "CONNECT_READY" + elif connected and not deadline_seen: + write_ready = True + else: + raise TraceModelError("write readiness is unexpected") + elif operation == SO_ERROR_ZERO: + if state != "CONNECT_READY": + raise TraceModelError("SO_ERROR ordering is invalid") + connected = True + state = "CONNECTED" + elif operation == SEND_BYTES: + if not connected or not write_ready or sent >= batch_size: + raise TraceModelError("send ordering is invalid") + if event.value > batch_size - sent: + raise TraceModelError("send exceeded exact batch") + sent += event.value + write_ready = False + elif operation == READY_READ: + if not connected or sent != batch_size or deadline_seen: + raise TraceModelError("read readiness is unexpected") + read_ready = True + elif operation == RECV_BYTES: + if not read_ready: + raise TraceModelError("receive occurred without readiness") + if received + event.value > MAX_RECEIVE_BYTES: + raise TraceModelError("receive bound exceeded") + received += event.value + read_ready = False + elif operation == RECV_EOF: + raise TraceModelError("EOF is not a completion event") + elif operation in {WAIT_INTERRUPTED, WAIT_TIMEOUT}: + if not connected or deadline_seen: + raise TraceModelError("wait event ordering is invalid") + write_ready = False + read_ready = False + elif operation == DEADLINE_REACHED: + if event.at_seconds < deadline_seconds or not connected or \ + sent != batch_size or received == 0: + raise TraceModelError("deadline preconditions are incomplete") + deadline_seen = True + write_ready = False + read_ready = False + elif operation == SANITIZER_ACCEPTED: + if not deadline_seen or sanitizer_seen: + raise TraceModelError("sanitizer ordering is invalid") + sanitizer_seen = True + elif operation == LOCAL_CLOSE: + if not deadline_seen or not sanitizer_seen or close_seen: + raise TraceModelError("local close ordering is invalid") + close_seen = True + elif operation == OUTPUT_CREATED: + if not close_seen or state == "COMPLETE": + raise TraceModelError("output ordering is invalid") + state = "COMPLETE" + else: + raise TraceModelError("unhandled trace operation") + + if state != "COMPLETE" or not deadline_seen or not sanitizer_seen or \ + not close_seen or sent != batch_size: + raise TraceModelError("trace is incomplete") + return TraceAssessment( + classification="OFFLINE_NONBLOCKING_SEQUENCE_FEASIBLE", + batch_bytes_sent=sent, + receive_bytes=received, + receipt_before_create=True, + nonblocking_before_connect=True, + complete_send_loop=True, + deadline_only_completion=True, + local_close_observed=True, + ) diff --git a/tools/phase10ac_dormant_adapter.py b/tools/phase10ac_dormant_adapter.py new file mode 100644 index 0000000..989a7c7 --- /dev/null +++ b/tools/phase10ac_dormant_adapter.py @@ -0,0 +1,348 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Dormant Phase-1.0AC adapter driven only by exact built-in fake syscalls. + +The module has no socket, selector, address, DNS, CLI, real clock or file +output. It exercises one nonblocking lifecycle with synthetic outcomes and +the existing passive batch/result contract. +""" + +from __future__ import annotations + +from dataclasses import dataclass +import math + +from phase10z_passive_batch_contract import ( + PassiveBatch, + PassiveContractError, + PassiveResultAccumulator, +) + + +MAX_FAKE_STEPS = 1024 +MAX_STEP_ADVANCE_SECONDS = 60.0 +MAX_RECEIVE_BYTES = 65536 + +CREATE_STREAM = "CREATE_STREAM" +SET_NONBLOCKING = "SET_NONBLOCKING" +START_CONNECT = "START_CONNECT" +WAIT_WRITE = "WAIT_WRITE" +GET_SO_ERROR = "GET_SO_ERROR" +WRITE_BYTES = "WRITE_BYTES" +WAIT_READ = "WAIT_READ" +READ_BYTES = "READ_BYTES" + +OK = "OK" +IMMEDIATE = "IMMEDIATE" +PENDING = "PENDING" +READY = "READY" +INTERRUPTED = "INTERRUPTED" +TIMEOUT = "TIMEOUT" +ZERO = "ZERO" +NONZERO = "NONZERO" +PROGRESS = "PROGRESS" +EOF = "EOF" +ERROR = "ERROR" + +ALLOWED_RESULTS = { + CREATE_STREAM: {OK, ERROR}, + SET_NONBLOCKING: {OK, ERROR}, + START_CONNECT: {IMMEDIATE, PENDING, ERROR}, + WAIT_WRITE: {READY, INTERRUPTED, TIMEOUT, ERROR}, + GET_SO_ERROR: {ZERO, NONZERO, ERROR}, + WRITE_BYTES: {PROGRESS, ZERO, ERROR}, + WAIT_READ: {READY, INTERRUPTED, TIMEOUT, ERROR}, + READ_BYTES: {PROGRESS, EOF, ERROR}, +} + + +class DormantAdapterError(RuntimeError): + """Normalized synthetic adapter failure without supplied bytes.""" + + +class FakeSyscallError(RuntimeError): + """Exact fake-facade state or script failure.""" + + +@dataclass(frozen=True) +class FakeSyscallStep: + """One synthetic syscall outcome; never an operating-system call.""" + + operation: str + result: str + value: int = 0 + data: bytes = b"" + advance_seconds: float = 0.0 + + def __post_init__(self) -> None: + if self.operation not in ALLOWED_RESULTS or \ + self.result not in ALLOWED_RESULTS[self.operation]: + raise FakeSyscallError("fake syscall operation/result is invalid") + if not isinstance(self.value, int) or isinstance(self.value, bool) or \ + self.value < 0: + raise FakeSyscallError("fake syscall value is invalid") + if not isinstance(self.data, bytes): + raise FakeSyscallError("fake syscall data is invalid") + advance = self.advance_seconds + if not isinstance(advance, (int, float)) or isinstance(advance, bool) or \ + not math.isfinite(advance) or not 0.0 <= advance <= \ + MAX_STEP_ADVANCE_SECONDS: + raise FakeSyscallError("fake syscall time advance is invalid") + if self.operation == WRITE_BYTES and self.result == PROGRESS: + if self.value <= 0 or self.data: + raise FakeSyscallError("fake write progress is invalid") + elif self.operation == READ_BYTES and self.result == PROGRESS: + if not self.data or self.value != len(self.data): + raise FakeSyscallError("fake read progress is invalid") + elif self.value != 0 or self.data: + raise FakeSyscallError("fake syscall carries unexpected data") + + +class OfflineFakeClock: + """Explicit synthetic monotonic value; never reads the host clock.""" + + def __init__(self, initial: float = 0.0) -> None: + if not isinstance(initial, (int, float)) or isinstance(initial, bool) or \ + not math.isfinite(initial): + raise FakeSyscallError("fake clock initial value is invalid") + self._value = float(initial) + + def monotonic(self) -> float: + return self._value + + def advance(self, seconds: float) -> None: + if not isinstance(seconds, (int, float)) or isinstance(seconds, bool) or \ + not math.isfinite(seconds) or not 0.0 <= seconds <= \ + MAX_STEP_ADVANCE_SECONDS: + raise FakeSyscallError("fake clock advance is invalid") + self._value += float(seconds) + + +class OfflineFakeSyscallFacade: + """Closed fake facade; it cannot retain a target or create a capability.""" + + def __init__( + self, clock: OfflineFakeClock, steps: tuple[FakeSyscallStep, ...], + close_result: str = OK, + ) -> None: + if type(clock) is not OfflineFakeClock: + raise FakeSyscallError("only the exact fake clock is accepted") + if not isinstance(steps, tuple) or not 1 <= len(steps) <= MAX_FAKE_STEPS \ + or any(type(step) is not FakeSyscallStep for step in steps): + raise FakeSyscallError("fake syscall script is invalid") + if close_result not in {OK, ERROR}: + raise FakeSyscallError("fake close result is invalid") + self.clock = clock + self._steps = list(steps) + self._close_result = close_result + self.state = "NEW" + self.invoke_count = 0 + self.close_count = 0 + self.discarded_steps = 0 + self.trace: list[str] = [] + + def invoke(self, operation: str) -> FakeSyscallStep: + if self.state == "CLOSED" or not self._steps: + raise FakeSyscallError("fake syscall script is exhausted") + step = self._steps.pop(0) + if step.operation != operation: + raise FakeSyscallError("fake syscall ordering is invalid") + self.clock.advance(step.advance_seconds) + self.invoke_count += 1 + self.trace.append(f"{operation}:{step.result}") + self.state = "ACTIVE" + return step + + def close_once(self) -> None: + if self.state == "CLOSED" or self.close_count != 0: + raise FakeSyscallError("fake facade cannot close") + self.close_count = 1 + self.discarded_steps = len(self._steps) + self._steps.clear() + self.trace.append(f"LOCAL_CLOSE:{self._close_result}") + self.state = "CLOSED" + if self._close_result != OK: + raise FakeSyscallError("synthetic local close failed") + + +@dataclass(frozen=True) +class DormantAdapterOutcome: + classification: str + result_classification: str + exact_identity: bool + batch_bytes_sent: int + received_bytes: int + write_calls: int + read_calls: int + interrupted_waits: int + timed_out_waits: int + discarded_steps_after_close: int + trace: tuple[str, ...] + target_retained: bool = False + live_transport_present: bool = False + device_behavior_proven: bool = False + + +def _before_deadline(clock: OfflineFakeClock, deadline: float) -> None: + if clock.monotonic() >= deadline: + raise DormantAdapterError("synthetic deadline reached") + + +def _wait_until_ready( + facade: OfflineFakeSyscallFacade, + clock: OfflineFakeClock, + deadline: float, + operation: str, +) -> tuple[bool, int, int]: + interrupted = 0 + timed_out = 0 + while True: + _before_deadline(clock, deadline) + step = facade.invoke(operation) + if clock.monotonic() >= deadline: + if step.result == READY: + raise DormantAdapterError( + "synthetic deadline won readiness race") + if step.result == INTERRUPTED: + return False, interrupted + 1, timed_out + if step.result == TIMEOUT: + return False, interrupted, timed_out + 1 + raise DormantAdapterError("synthetic wait failed at deadline") + if step.result == READY: + return True, interrupted, timed_out + if step.result == INTERRUPTED: + interrupted += 1 + continue + if step.result == TIMEOUT: + timed_out += 1 + continue + raise DormantAdapterError("synthetic readiness failed") + + +def run_dormant_adapter( + batch: PassiveBatch, + facade: OfflineFakeSyscallFacade, + clock: OfflineFakeClock, + receipt_precommitted: bool, +) -> DormantAdapterOutcome: + """Run one target-free fake-syscall lifecycle and normalize all failure.""" + if type(batch) is not PassiveBatch or \ + type(facade) is not OfflineFakeSyscallFacade or \ + type(clock) is not OfflineFakeClock or facade.clock is not clock or \ + receipt_precommitted is not True: + raise DormantAdapterError("dormant adapter boundary is invalid") + + start = clock.monotonic() + deadline = start + batch.deadline_seconds + accumulator: PassiveResultAccumulator + try: + accumulator = PassiveResultAccumulator(batch) + except PassiveContractError as error: + raise DormantAdapterError("passive batch is invalid") from error + + sent = 0 + received = 0 + write_calls = 0 + read_calls = 0 + interrupted_waits = 0 + timed_out_waits = 0 + opened = False + sanitized = None + primary_failure: DormantAdapterError | None = None + + try: + _before_deadline(clock, deadline) + if facade.invoke(CREATE_STREAM).result != OK: + raise DormantAdapterError("synthetic stream creation failed") + opened = True + _before_deadline(clock, deadline) + if facade.invoke(SET_NONBLOCKING).result != OK: + raise DormantAdapterError("synthetic nonblocking setup failed") + _before_deadline(clock, deadline) + connect_result = facade.invoke(START_CONNECT).result + if connect_result == PENDING: + ready, interrupted, timed_out = _wait_until_ready( + facade, clock, deadline, WAIT_WRITE) + interrupted_waits += interrupted + timed_out_waits += timed_out + if not ready: + raise DormantAdapterError( + "synthetic connect reached deadline") + if facade.invoke(GET_SO_ERROR).result != ZERO: + raise DormantAdapterError("synthetic pending connect failed") + _before_deadline(clock, deadline) + elif connect_result != IMMEDIATE: + raise DormantAdapterError("synthetic immediate connect failed") + + while sent < len(batch.payload): + ready, interrupted, timed_out = _wait_until_ready( + facade, clock, deadline, WAIT_WRITE) + interrupted_waits += interrupted + timed_out_waits += timed_out + if not ready: + raise DormantAdapterError("synthetic write reached deadline") + step = facade.invoke(WRITE_BYTES) + write_calls += 1 + if step.result != PROGRESS or step.value > len(batch.payload) - sent: + raise DormantAdapterError("synthetic write made invalid progress") + sent += step.value + _before_deadline(clock, deadline) + + while clock.monotonic() < deadline: + ready, interrupted, timed_out = _wait_until_ready( + facade, clock, deadline, WAIT_READ) + interrupted_waits += interrupted + timed_out_waits += timed_out + if not ready: + break + step = facade.invoke(READ_BYTES) + read_calls += 1 + if step.result == EOF: + raise DormantAdapterError("synthetic EOF is not completion") + if step.result != PROGRESS or received + len(step.data) > \ + MAX_RECEIVE_BYTES: + raise DormantAdapterError("synthetic read is invalid") + if clock.monotonic() >= deadline: + raise DormantAdapterError("synthetic data reached deadline") + try: + accumulator.feed_supplied_chunk(step.data) + except PassiveContractError as error: + raise DormantAdapterError("passive result rejected input") from error + received += len(step.data) + + if clock.monotonic() < deadline or received == 0: + raise DormantAdapterError("synthetic deadline result is incomplete") + try: + sanitized = accumulator.seal_at_hard_deadline(True) + except PassiveContractError as error: + raise DormantAdapterError("passive deadline result is invalid") from error + except Exception as error: # noqa: BLE001 - exact fake boundary normalization + primary_failure = DormantAdapterError("dormant fake-syscall run failed") + primary_failure.__cause__ = error + finally: + if opened: + try: + facade.close_once() + except FakeSyscallError as error: + if primary_failure is None: + primary_failure = DormantAdapterError( + "dormant fake-syscall cleanup failed") + primary_failure.__cause__ = error + + if primary_failure is not None: + raise primary_failure + if sanitized is None or facade.close_count != 1: + raise DormantAdapterError("dormant fake-syscall result is incomplete") + return DormantAdapterOutcome( + classification="OFFLINE_DORMANT_FAKE_SYSCALL_ADAPTER_COMPLETE", + result_classification=str(sanitized["classification"]), + exact_identity=bool(sanitized["exact_identity"]), + batch_bytes_sent=sent, + received_bytes=received, + write_calls=write_calls, + read_calls=read_calls, + interrupted_waits=interrupted_waits, + timed_out_waits=timed_out_waits, + discarded_steps_after_close=facade.discarded_steps, + trace=("CONSUMED_RECEIPT_PREEXISTS", *facade.trace), + ) diff --git a/tools/phase10ad_activation_contract.py b/tools/phase10ad_activation_contract.py new file mode 100644 index 0000000..26c980f --- /dev/null +++ b/tools/phase10ad_activation_contract.py @@ -0,0 +1,128 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Offline Phase-1.0AD activation-record contract. + +This module validates data only. It has no socket, DNS, clock, CLI, file +output, transport or device capability. +""" + +from __future__ import annotations + +from dataclasses import dataclass +from datetime import datetime, timezone +import ipaddress +import re + + +PHASE = "PHASE_1_0AD_INACTIVE_NUMERIC_TARGET_CONTRACT" +PORT = 2323 +MAX_WINDOW_SECONDS = 300 +RUN_ID = re.compile(r"^[A-Z0-9][A-Z0-9_-]{7,63}$") +SHA256 = re.compile(r"^[0-9a-f]{64}$") + + +class ActivationContractError(ValueError): + """An activation record is incomplete, ambiguous or unsafe.""" + + +def _utc(value: str) -> datetime: + if not isinstance(value, str) or not value.endswith("Z"): + raise ActivationContractError("timestamp must be UTC with Z suffix") + try: + parsed = datetime.fromisoformat(value[:-1] + "+00:00") + except ValueError as error: + raise ActivationContractError("timestamp is invalid") from error + if parsed.tzinfo != timezone.utc: + raise ActivationContractError("timestamp is not UTC") + return parsed + + +def validate_numeric_target(value: str) -> str: + """Return canonical private IPv4 text without resolving a name.""" + if not isinstance(value, str) or not value or value != value.strip(): + raise ActivationContractError("target must be exact numeric text") + if ":" in value or any(character.isalpha() for character in value): + raise ActivationContractError("only numeric IPv4 targets are allowed") + try: + address = ipaddress.IPv4Address(value) + except ipaddress.AddressValueError as error: + raise ActivationContractError("target is not canonical IPv4") from error + if str(address) != value or not address.is_private or address.is_loopback \ + or address.is_link_local or address.is_multicast \ + or address.is_unspecified: + raise ActivationContractError("target is not canonical private IPv4") + return value + + +@dataclass(frozen=True) +class ActivationRecord: + phase: str + active: bool + target_address: str | None + target_port: int | None + run_id: str | None + not_before: str | None + expires_at: str | None + launcher_sha256: str | None + payload_sha256: str | None + approval_sha256: str | None + one_shot: bool + automatic_retry: bool + reconnect: bool + resume: bool + device_write_authorized: bool + app_termination_authorized: bool + system_remount_authorized: bool + + +def validate_inactive(record: ActivationRecord) -> None: + """Validate the only tracked Phase-1.0AD state: entirely inactive.""" + if type(record) is not ActivationRecord or record.phase != PHASE: + raise ActivationContractError("activation phase is invalid") + if record.active is not False: + raise ActivationContractError("tracked activation must be inactive") + optional = ( + record.target_address, record.target_port, record.run_id, + record.not_before, record.expires_at, record.launcher_sha256, + record.payload_sha256, record.approval_sha256, + ) + if any(value is not None for value in optional): + raise ActivationContractError("inactive activation must be target-free") + if record.one_shot is not True or record.automatic_retry is not False \ + or record.reconnect is not False or record.resume is not False: + raise ActivationContractError("inactive one-shot policy is invalid") + if record.device_write_authorized or record.app_termination_authorized \ + or record.system_remount_authorized: + raise ActivationContractError("inactive device effects must be false") + + +def validate_candidate(record: ActivationRecord) -> None: + """Validate hypothetical activation data without activating anything.""" + if type(record) is not ActivationRecord or record.phase != PHASE \ + or record.active is not True: + raise ActivationContractError("candidate is not explicitly active") + validate_numeric_target(record.target_address) # type: ignore[arg-type] + if record.target_port != PORT: + raise ActivationContractError("candidate port is not source-bound") + if not isinstance(record.run_id, str) or not RUN_ID.fullmatch(record.run_id): + raise ActivationContractError("candidate run id is invalid") + if not isinstance(record.launcher_sha256, str) \ + or not SHA256.fullmatch(record.launcher_sha256): + raise ActivationContractError("launcher identity is invalid") + if not isinstance(record.payload_sha256, str) \ + or not SHA256.fullmatch(record.payload_sha256): + raise ActivationContractError("payload identity is invalid") + if not isinstance(record.approval_sha256, str) \ + or not SHA256.fullmatch(record.approval_sha256): + raise ActivationContractError("approval identity is invalid") + start = _utc(record.not_before) # type: ignore[arg-type] + end = _utc(record.expires_at) # type: ignore[arg-type] + duration = (end - start).total_seconds() + if not 0 < duration <= MAX_WINDOW_SECONDS: + raise ActivationContractError("candidate window is invalid") + if record.one_shot is not True or record.automatic_retry is not False \ + or record.reconnect is not False or record.resume is not False: + raise ActivationContractError("candidate one-shot policy is invalid") + if record.device_write_authorized or record.app_termination_authorized \ + or record.system_remount_authorized: + raise ActivationContractError("candidate requests forbidden effects") diff --git a/tools/phase10af_bigapp_lifecycle_model.py b/tools/phase10af_bigapp_lifecycle_model.py new file mode 100644 index 0000000..7e7fcbb --- /dev/null +++ b/tools/phase10af_bigapp_lifecycle_model.py @@ -0,0 +1,213 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Closed host-only model of one bounded BigApp launcher lifecycle. + +The model accepts only exact built-in fake operations. It imports no target +headers, socket, process, syscall, clock, CLI or filesystem interface. +""" + +from __future__ import annotations + +from dataclasses import dataclass + + +FIXED_TITLE = "PPSA01659" +MAX_MAIN_TICKS = 64 +MAX_FAKE_EVENTS = 64 +MAX_CLEANUP_EVENTS = 3 + +CHECK_NO_BIGAPP = "CHECK_NO_BIGAPP" +ATTACH_PARENT = "ATTACH_PARENT" +ARM_FORK = "ARM_FORK" +CONTINUE_PARENT = "CONTINUE_PARENT" +LAUNCH_FIXED_TITLE = "LAUNCH_FIXED_TITLE" +AWAIT_UNIQUE_CHILD = "AWAIT_UNIQUE_CHILD" +DETACH_PARENT = "DETACH_PARENT" +ARM_EXEC = "ARM_EXEC" +CONTINUE_CHILD = "CONTINUE_CHILD" +AWAIT_EXEC = "AWAIT_EXEC" +REPLACE_EXACT_PAYLOAD = "REPLACE_EXACT_PAYLOAD" +RESTORE_MUTATIONS = "RESTORE_MUTATIONS" +DETACH_CHILD = "DETACH_CHILD" +TERMINATE_CHILD = "TERMINATE_CHILD" +EMIT_RESULT = "EMIT_RESULT" + +OK = "OK" +NONE = "NONE" +EXISTS = "EXISTS" +CHILD = "CHILD" +TIMEOUT = "TIMEOUT" +ERROR = "ERROR" + +ALLOWED = { + CHECK_NO_BIGAPP: {NONE, EXISTS, ERROR}, + ATTACH_PARENT: {OK, ERROR}, ARM_FORK: {OK, ERROR}, + CONTINUE_PARENT: {OK, ERROR}, LAUNCH_FIXED_TITLE: {OK, ERROR}, + AWAIT_UNIQUE_CHILD: {CHILD, TIMEOUT, ERROR}, + DETACH_PARENT: {OK, ERROR}, ARM_EXEC: {OK, ERROR}, + CONTINUE_CHILD: {OK, ERROR}, AWAIT_EXEC: {OK, TIMEOUT, ERROR}, + REPLACE_EXACT_PAYLOAD: {OK, ERROR}, RESTORE_MUTATIONS: {OK, ERROR}, + DETACH_CHILD: {OK, ERROR}, TERMINATE_CHILD: {OK, ERROR}, + EMIT_RESULT: {OK, ERROR}, +} + + +class LifecycleModelError(RuntimeError): + """The synthetic lifecycle or its cleanup is invalid.""" + + +@dataclass(frozen=True) +class FakeEvent: + operation: str + result: str + ticks: int = 1 + child_id: int = 0 + + def __post_init__(self) -> None: + if self.operation not in ALLOWED or self.result not in ALLOWED[self.operation]: + raise LifecycleModelError("fake operation/result is invalid") + if not isinstance(self.ticks, int) or isinstance(self.ticks, bool) \ + or not 1 <= self.ticks <= MAX_MAIN_TICKS: + raise LifecycleModelError("fake ticks are invalid") + if self.operation == AWAIT_UNIQUE_CHILD and self.result == CHILD: + if not isinstance(self.child_id, int) or isinstance(self.child_id, bool) \ + or self.child_id <= 1: + raise LifecycleModelError("fake child identity is invalid") + elif self.child_id != 0: + raise LifecycleModelError("unexpected fake child identity") + + +class FakeLifecycleFacade: + """Exact closed script; it cannot execute an operation itself.""" + + def __init__(self, events: tuple[FakeEvent, ...]) -> None: + if not isinstance(events, tuple) or not 1 <= len(events) <= MAX_FAKE_EVENTS \ + or any(type(event) is not FakeEvent for event in events): + raise LifecycleModelError("fake lifecycle script is invalid") + self._events = list(events) + self.trace: list[str] = [] + self.ticks = 0 + + def invoke(self, operation: str, cleanup: bool = False) -> FakeEvent: + if not self._events: + raise LifecycleModelError("fake lifecycle script is exhausted") + event = self._events.pop(0) + if event.operation != operation: + raise LifecycleModelError("fake lifecycle ordering is invalid") + self.ticks += event.ticks + self.trace.append(f"{operation}:{event.result}") + if not cleanup and self.ticks > MAX_MAIN_TICKS: + raise LifecycleModelError("bounded main lifecycle expired") + return event + + @property + def remaining(self) -> int: + return len(self._events) + + +@dataclass(frozen=True) +class LifecycleOutcome: + classification: str + success: bool + child_id: int + parent_detached: bool + child_detached: bool + child_terminated: bool + mutations_restored: bool + existing_bigapp_killed: bool + fixed_title: str + trace: tuple[str, ...] + target_code_present: bool = False + device_behavior_proven: bool = False + + +def _require_ok(facade: FakeLifecycleFacade, operation: str) -> None: + if facade.invoke(operation).result != OK: + raise LifecycleModelError(f"{operation} failed") + + +def run_lifecycle(facade: FakeLifecycleFacade) -> LifecycleOutcome: + """Model one attempt and exhaustively unwind every acquired state.""" + if type(facade) is not FakeLifecycleFacade: + raise LifecycleModelError("only the exact fake facade is accepted") + + parent_attached = False + parent_detached = False + child_id = 0 + child_detached = False + child_terminated = False + mutations_started = False + mutations_restored = False + success = False + primary_error: Exception | None = None + cleanup_events = 0 + + try: + if facade.invoke(CHECK_NO_BIGAPP).result != NONE: + raise LifecycleModelError("pre-existing BigApp blocks launch") + _require_ok(facade, ATTACH_PARENT) + parent_attached = True + _require_ok(facade, ARM_FORK) + _require_ok(facade, CONTINUE_PARENT) + _require_ok(facade, LAUNCH_FIXED_TITLE) + child = facade.invoke(AWAIT_UNIQUE_CHILD) + if child.result != CHILD: + raise LifecycleModelError("unique child was not observed") + child_id = child.child_id + _require_ok(facade, DETACH_PARENT) + parent_attached = False + parent_detached = True + _require_ok(facade, ARM_EXEC) + _require_ok(facade, CONTINUE_CHILD) + _require_ok(facade, AWAIT_EXEC) + replace = facade.invoke(REPLACE_EXACT_PAYLOAD) + mutations_started = True + if replace.result != OK: + raise LifecycleModelError("exact payload replacement failed") + _require_ok(facade, RESTORE_MUTATIONS) + mutations_started = False + mutations_restored = True + _require_ok(facade, DETACH_CHILD) + child_detached = True + _require_ok(facade, EMIT_RESULT) + success = True + except Exception as error: # exact fake boundary normalization + primary_error = error + finally: + try: + if parent_attached: + cleanup_events += 1 + if facade.invoke(DETACH_PARENT, cleanup=True).result != OK: + raise LifecycleModelError("parent cleanup failed") + parent_attached = False + parent_detached = True + if mutations_started: + cleanup_events += 1 + if facade.invoke(RESTORE_MUTATIONS, cleanup=True).result != OK: + raise LifecycleModelError("mutation restoration failed") + mutations_started = False + mutations_restored = True + if child_id and not child_detached: + cleanup_events += 1 + if facade.invoke(TERMINATE_CHILD, cleanup=True).result != OK: + raise LifecycleModelError("new child cleanup failed") + child_terminated = True + if cleanup_events > MAX_CLEANUP_EVENTS: + raise LifecycleModelError("cleanup operation bound exceeded") + except Exception as cleanup_error: + raise LifecycleModelError("lifecycle cleanup is incomplete") from cleanup_error + + if facade.remaining: + raise LifecycleModelError("fake lifecycle has unused operations") + if primary_error is not None: + return LifecycleOutcome( + "OFFLINE_BIGAPP_LIFECYCLE_FAILED_CLEANLY", False, child_id, + parent_detached, child_detached, child_terminated, + mutations_restored, False, FIXED_TITLE, tuple(facade.trace)) + if not success or not parent_detached or not child_detached \ + or not mutations_restored or child_terminated: + raise LifecycleModelError("successful lifecycle invariants failed") + return LifecycleOutcome( + "OFFLINE_BIGAPP_LIFECYCLE_MODEL_COMPLETE", True, child_id, + parent_detached, child_detached, child_terminated, + mutations_restored, False, FIXED_TITLE, tuple(facade.trace)) diff --git a/tools/phase10ag_bounded_elf.py b/tools/phase10ag_bounded_elf.py new file mode 100644 index 0000000..a468973 --- /dev/null +++ b/tools/phase10ag_bounded_elf.py @@ -0,0 +1,148 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Bytes-only bounded ELF64 admission contract for a future launcher. + +No path, file, process, target, network or execution interface is present. +""" + +from __future__ import annotations + +from dataclasses import dataclass +import hashlib +import re +import struct + + +MAX_FILE_SIZE = 2 * 1024 * 1024 +MAX_PROGRAM_HEADERS = 32 +MAX_SECTION_HEADERS = 256 +MAX_LOAD_SEGMENTS = 8 +MAX_TOTAL_LOAD_MEMORY = 64 * 1024 * 1024 +MAX_LOAD_SPAN = 128 * 1024 * 1024 +MAX_ALIGNMENT = 2 * 1024 * 1024 + +ELF_HEADER = struct.Struct("<16sHHIQQQIHHHHHH") +PROGRAM_HEADER = struct.Struct(" None: + if offset < 0 or size < 0 or offset > limit or size > limit - offset: + raise ElfContractError(f"{label} range is outside supplied bytes") + + +def _power_of_two(value: int) -> bool: + return value > 0 and value & (value - 1) == 0 + + +def assess_elf(payload: bytes, expected_sha256: str) -> ElfAssessment: + """Validate exact supplied bytes without opening or executing anything.""" + if not isinstance(payload, bytes) or not ELF_HEADER.size <= len(payload) <= MAX_FILE_SIZE: + raise ElfContractError("payload size is outside the admission bound") + if not isinstance(expected_sha256, str) or not SHA256.fullmatch(expected_sha256): + raise ElfContractError("expected SHA-256 is invalid") + digest = hashlib.sha256(payload).hexdigest() + if digest != expected_sha256: + raise ElfContractError("payload SHA-256 does not match") + + (ident, elf_type, machine, version, entry, phoff, shoff, flags, + ehsize, phentsize, phnum, shentsize, shnum, shstrndx) = ELF_HEADER.unpack_from(payload) + del flags, shstrndx + if ident[:4] != b"\x7fELF" or ident[4] != 2 or ident[5] != 1 \ + or ident[6] != 1 or ident[7] not in {0, 9}: + raise ElfContractError("ELF identity is unsupported") + if elf_type != ET_DYN or machine != EM_X86_64 or version != 1: + raise ElfContractError("ELF type, machine or version is unsupported") + if ehsize != ELF_HEADER.size or phentsize != PROGRAM_HEADER.size \ + or not 1 <= phnum <= MAX_PROGRAM_HEADERS: + raise ElfContractError("ELF header sizing is invalid") + if phoff < ELF_HEADER.size: + raise ElfContractError("program header table overlaps ELF header") + _bounded_range(phoff, phnum * phentsize, len(payload), "program header table") + if shnum == 0: + if shoff != 0 or shentsize not in {0, SECTION_HEADER_SIZE}: + raise ElfContractError("absent section table is inconsistent") + else: + if not 1 <= shnum <= MAX_SECTION_HEADERS or shentsize != SECTION_HEADER_SIZE: + raise ElfContractError("section header sizing is invalid") + _bounded_range(shoff, shnum * shentsize, len(payload), "section header table") + + loads: list[LoadSegment] = [] + interpreter = False + for index in range(phnum): + values = PROGRAM_HEADER.unpack_from(payload, phoff + index * phentsize) + p_type, p_flags, p_offset, p_vaddr, _p_paddr, p_filesz, p_memsz, p_align = values + if p_type == PT_INTERP: + interpreter = True + if p_type != PT_LOAD: + continue + if len(loads) >= MAX_LOAD_SEGMENTS or p_memsz == 0 or p_filesz > p_memsz: + raise ElfContractError("load segment count or sizing is invalid") + _bounded_range(p_offset, p_filesz, len(payload), "load segment file") + if p_vaddr > (1 << 64) - 1 - p_memsz: + raise ElfContractError("load segment address overflows") + if p_flags & ~(PF_R | PF_W | PF_X) or p_flags & PF_W and p_flags & PF_X: + raise ElfContractError("load segment permissions are invalid") + if not _power_of_two(p_align) or p_align > MAX_ALIGNMENT \ + or p_offset % p_align != p_vaddr % p_align: + raise ElfContractError("load segment alignment is invalid") + loads.append(LoadSegment(p_flags, p_offset, p_vaddr, p_filesz, + p_memsz, p_align)) + if interpreter: + raise ElfContractError("interpreter segment is forbidden") + if not loads: + raise ElfContractError("ELF has no load segments") + + ordered = sorted(loads, key=lambda item: item.virtual_address) + for previous, current in zip(ordered, ordered[1:]): + if previous.virtual_address + previous.memory_size > current.virtual_address: + raise ElfContractError("load segment virtual ranges overlap") + total_memory = sum(item.memory_size for item in ordered) + span = ordered[-1].virtual_address + ordered[-1].memory_size - ordered[0].virtual_address + if total_memory > MAX_TOTAL_LOAD_MEMORY or span > MAX_LOAD_SPAN: + raise ElfContractError("load memory budget is exceeded") + executable = [item for item in ordered if item.flags & PF_X] + if not executable or not any(item.virtual_address <= entry < + item.virtual_address + item.memory_size + for item in executable): + raise ElfContractError("entry is not in an executable load segment") + return ElfAssessment(len(payload), digest, entry, phnum, tuple(ordered), + total_memory, span) diff --git a/tools/phase10ah_dynamic_contract.py b/tools/phase10ah_dynamic_contract.py new file mode 100644 index 0000000..da267e5 --- /dev/null +++ b/tools/phase10ah_dynamic_contract.py @@ -0,0 +1,175 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Bytes-only dynamic/relocation contract layered on Phase 1.0AG.""" + +from __future__ import annotations + +from dataclasses import dataclass +import struct + +from phase10ag_bounded_elf import ( + ELF_HEADER, ElfContractError, PF_W, assess_elf, +) + + +SECTION_HEADER = struct.Struct(" None: + if offset < 0 or size < 0 or offset > limit or size > limit - offset: + raise DynamicContractError(f"{label} is outside supplied bytes") + + +def _sections(payload: bytes) -> list[tuple[int, ...]]: + header = ELF_HEADER.unpack_from(payload) + shoff, shentsize, shnum = header[6], header[11], header[12] + if shnum == 0 or shentsize != SECTION_HEADER.size: + raise DynamicContractError("section table is required") + _range(shoff, shnum * shentsize, len(payload), "section table") + return [SECTION_HEADER.unpack_from(payload, shoff + index * shentsize) + for index in range(shnum)] + + +def _needed(payload: bytes, sections: list[tuple[int, ...]]) -> tuple[str, ...]: + dynamic_sections = [section for section in sections if section[1] == SHT_DYNAMIC] + if len(dynamic_sections) != 1: + raise DynamicContractError("exactly one dynamic section is required") + section = dynamic_sections[0] + offset, size, link, entsize = section[4], section[5], section[6], section[9] + if entsize != DYNAMIC_ENTRY.size or size % entsize or size // entsize > MAX_DYNAMIC_ENTRIES: + raise DynamicContractError("dynamic table sizing is invalid") + _range(offset, size, len(payload), "dynamic table") + if link >= len(sections) or sections[link][1] != SHT_STRTAB: + raise DynamicContractError("dynamic string table link is invalid") + strings = sections[link] + string_offset, string_size = strings[4], strings[5] + _range(string_offset, string_size, len(payload), "dynamic string table") + string_data = payload[string_offset:string_offset + string_size] + names: list[str] = [] + terminated = False + for position in range(offset, offset + size, entsize): + tag, value = DYNAMIC_ENTRY.unpack_from(payload, position) + if terminated: + if tag != DT_NULL or value != 0: + raise DynamicContractError("nonzero dynamic data follows DT_NULL") + continue + if tag == DT_NULL: + terminated = True + continue + if tag != DT_NEEDED: + continue + if len(names) >= MAX_NEEDED or value >= len(string_data): + raise DynamicContractError("DT_NEEDED count or offset is invalid") + end = string_data.find(b"\0", value, min(len(string_data), value + MAX_NEEDED_NAME + 1)) + if end < 0: + raise DynamicContractError("DT_NEEDED name is unterminated or too long") + try: + name = string_data[value:end].decode("ascii") + except UnicodeDecodeError as error: + raise DynamicContractError("DT_NEEDED name is not ASCII") from error + if name not in ALLOWED_MODULES or name in names: + raise DynamicContractError("DT_NEEDED module is unknown or duplicated") + names.append(name) + if not terminated: + raise DynamicContractError("dynamic table has no DT_NULL terminator") + return tuple(names) + + +def assess_dynamic(payload: bytes, expected_sha256: str, + expected_needed: tuple[str, ...]) -> DynamicAssessment: + """Validate exact dynamic dependencies and the bounded relocation split.""" + try: + elf = assess_elf(payload, expected_sha256) + except ElfContractError as error: + raise DynamicContractError("base ELF admission failed") from error + if not isinstance(expected_needed, tuple) or not 1 <= len(expected_needed) <= MAX_NEEDED \ + or any(not isinstance(item, str) for item in expected_needed) \ + or len(set(expected_needed)) != len(expected_needed) \ + or any(item not in ALLOWED_MODULES for item in expected_needed): + raise DynamicContractError("expected module inventory is invalid") + sections = _sections(payload) + needed = _needed(payload, sections) + if needed != expected_needed: + raise DynamicContractError("DT_NEEDED inventory or order differs") + + writable = [(item.virtual_address, item.virtual_address + item.memory_size) + for item in elf.load_segments if item.flags & PF_W] + image = [(item.virtual_address, item.virtual_address + item.memory_size) + for item in elf.load_segments] + relative = 0 + glob_dat = 0 + total = 0 + relocation_sections = 0 + for section in sections: + if section[1] != SHT_RELA: + continue + relocation_sections += 1 + if relocation_sections > MAX_RELOCATION_SECTIONS: + raise DynamicContractError("too many relocation sections") + offset, size, entsize = section[4], section[5], section[9] + if entsize != RELA_ENTRY.size or size % entsize: + raise DynamicContractError("relocation table sizing is invalid") + _range(offset, size, len(payload), "relocation table") + for position in range(offset, offset + size, entsize): + total += 1 + if total > MAX_RELOCATIONS: + raise DynamicContractError("relocation count is exceeded") + target, info, addend = RELA_ENTRY.unpack_from(payload, position) + relocation_type = info & 0xffffffff + symbol = info >> 32 + if target % 8 or not any(start <= target and target + 8 <= end + for start, end in writable): + raise DynamicContractError("relocation target is not aligned RW memory") + if relocation_type == R_X86_64_RELATIVE: + if symbol != 0 or addend < 0 or not any(start <= addend < end + for start, end in image): + raise DynamicContractError("relative relocation is invalid") + relative += 1 + elif relocation_type == R_X86_64_GLOB_DAT: + if symbol == 0 or addend != 0: + raise DynamicContractError("GLOB_DAT relocation is invalid") + glob_dat += 1 + else: + raise DynamicContractError("relocation type is outside the contract") + if relocation_sections == 0 or relative == 0: + raise DynamicContractError("relative relocation closure is absent") + return DynamicAssessment(needed, relative, glob_dat, total, + relocation_sections) diff --git a/tools/phase10ai_mapping_model.py b/tools/phase10ai_mapping_model.py new file mode 100644 index 0000000..adc1d22 --- /dev/null +++ b/tools/phase10ai_mapping_model.py @@ -0,0 +1,239 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Host-only transactional mapping/protection model for admitted ELF loads.""" + +from __future__ import annotations + +from dataclasses import dataclass + +from phase10ag_bounded_elf import LoadSegment, PF_R, PF_W, PF_X + + +PAGE_SIZE = 16 * 1024 +MAX_SEGMENTS = 8 +MAX_RELATIVE_RELOCATIONS = 4096 +MAX_EVENTS = 64 +MAX_MAIN_TICKS = 128 +MAX_REGION_SIZE = 128 * 1024 * 1024 + +RESERVE_CHILD = "RESERVE_CHILD" +CREATE_MIRROR = "CREATE_MIRROR" +COPY_FILE_BYTES = "COPY_FILE_BYTES" +ZERO_BSS = "ZERO_BSS" +APPLY_RELATIVE = "APPLY_RELATIVE" +COPY_MIRROR_TO_CHILD = "COPY_MIRROR_TO_CHILD" +SET_FINAL_PROTECTION = "SET_FINAL_PROTECTION" +SYNC_IMAGE = "SYNC_IMAGE" +RELEASE_MIRROR = "RELEASE_MIRROR" +UNMAP_CHILD = "UNMAP_CHILD" +OK = "OK" +ERROR = "ERROR" + +OPERATIONS = { + RESERVE_CHILD, CREATE_MIRROR, COPY_FILE_BYTES, ZERO_BSS, + APPLY_RELATIVE, COPY_MIRROR_TO_CHILD, SET_FINAL_PROTECTION, + SYNC_IMAGE, RELEASE_MIRROR, UNMAP_CHILD, +} + + +class MappingModelError(RuntimeError): + """The mapping transaction or its rollback is incomplete.""" + + +def _round_down(value: int) -> int: + return value & ~(PAGE_SIZE - 1) + + +def _round_up(value: int) -> int: + if value > (1 << 64) - PAGE_SIZE: + raise MappingModelError("mapping address cannot be rounded") + return (value + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1) + + +@dataclass(frozen=True) +class MappingPlan: + segments: tuple[LoadSegment, ...] + relative_relocations: int + + def __post_init__(self) -> None: + if not isinstance(self.segments, tuple) or not 1 <= len(self.segments) <= MAX_SEGMENTS \ + or any(type(item) is not LoadSegment for item in self.segments): + raise MappingModelError("mapping segments are invalid") + if tuple(sorted(self.segments, key=lambda item: item.virtual_address)) != self.segments: + raise MappingModelError("mapping segments are not ordered") + if not isinstance(self.relative_relocations, int) \ + or isinstance(self.relative_relocations, bool) \ + or not 1 <= self.relative_relocations <= MAX_RELATIVE_RELOCATIONS: + raise MappingModelError("relative relocation count is invalid") + protected: list[tuple[int, int]] = [] + for segment in self.segments: + if segment.memory_size <= 0 or segment.file_size < 0 \ + or segment.file_size > segment.memory_size \ + or segment.flags & ~(PF_R | PF_W | PF_X) \ + or segment.flags & PF_W and segment.flags & PF_X: + raise MappingModelError("mapping segment is unsafe") + start = _round_down(segment.virtual_address) + end = _round_up(segment.virtual_address + segment.memory_size) + if protected and protected[-1][1] > start: + raise MappingModelError("page-rounded protections overlap") + protected.append((start, end)) + if self.region_size > MAX_REGION_SIZE: + raise MappingModelError("mapping region exceeds budget") + + @property + def region_start(self) -> int: + return _round_down(self.segments[0].virtual_address) + + @property + def region_size(self) -> int: + end = _round_up(self.segments[-1].virtual_address + + self.segments[-1].memory_size) + return end - self.region_start + + +@dataclass(frozen=True) +class FakeMappingEvent: + operation: str + result: str + segment_index: int = -1 + value: int = 0 + ticks: int = 1 + + def __post_init__(self) -> None: + if self.operation not in OPERATIONS or self.result not in {OK, ERROR}: + raise MappingModelError("fake mapping operation/result is invalid") + if not isinstance(self.segment_index, int) or isinstance(self.segment_index, bool) \ + or self.segment_index < -1: + raise MappingModelError("fake segment index is invalid") + if not isinstance(self.value, int) or isinstance(self.value, bool) or self.value < 0: + raise MappingModelError("fake mapping value is invalid") + if not isinstance(self.ticks, int) or isinstance(self.ticks, bool) \ + or not 1 <= self.ticks <= MAX_MAIN_TICKS: + raise MappingModelError("fake mapping ticks are invalid") + indexed = self.operation in {COPY_FILE_BYTES, ZERO_BSS, SET_FINAL_PROTECTION} + if indexed != (self.segment_index >= 0): + raise MappingModelError("fake mapping segment binding is invalid") + + +class FakeMappingFacade: + """Exact scripted facade with no memory or process capability.""" + + def __init__(self, events: tuple[FakeMappingEvent, ...]) -> None: + if not isinstance(events, tuple) or not 1 <= len(events) <= MAX_EVENTS \ + or any(type(event) is not FakeMappingEvent for event in events): + raise MappingModelError("fake mapping script is invalid") + self._events = list(events) + self.trace: list[str] = [] + self.ticks = 0 + + def invoke(self, operation: str, segment_index: int = -1, + value: int = 0, cleanup: bool = False) -> FakeMappingEvent: + if not self._events: + raise MappingModelError("fake mapping script is exhausted") + event = self._events.pop(0) + if event.operation != operation or event.segment_index != segment_index \ + or event.value != value: + raise MappingModelError("fake mapping ordering or binding differs") + if not cleanup and self.ticks + event.ticks > MAX_MAIN_TICKS: + self.trace.append(f"DEADLINE_BEFORE:{operation}") + raise MappingModelError("mapping main tick budget expired before operation") + self.ticks += event.ticks + self.trace.append(f"{operation}:{segment_index}:{value}:{event.result}") + return event + + @property + def remaining(self) -> int: + return len(self._events) + + +@dataclass(frozen=True) +class MappingOutcome: + classification: str + success: bool + region_size: int + copied_file_bytes: int + zeroed_bss_bytes: int + relative_relocations: int + final_protections: tuple[int, ...] + mirror_released: bool + child_region_retained: bool + child_region_unmapped: bool + target_mapping_performed: bool = False + device_behavior_proven: bool = False + + +def _ok(facade: FakeMappingFacade, operation: str, segment_index: int = -1, + value: int = 0) -> None: + if facade.invoke(operation, segment_index, value).result != OK: + raise MappingModelError(f"{operation} failed") + + +def run_mapping(plan: MappingPlan, facade: FakeMappingFacade) -> MappingOutcome: + """Run one synthetic mapping transaction with full-region rollback.""" + if type(plan) is not MappingPlan or type(facade) is not FakeMappingFacade: + raise MappingModelError("mapping model boundary is invalid") + child_reserved = False + mirror_active = False + mirror_released = False + child_unmapped = False + committed = False + copied = 0 + zeroed = 0 + protections: list[int] = [] + primary_error: Exception | None = None + + try: + _ok(facade, RESERVE_CHILD, value=plan.region_size) + child_reserved = True + _ok(facade, CREATE_MIRROR, value=plan.region_size) + mirror_active = True + for index, segment in enumerate(plan.segments): + if segment.file_size: + _ok(facade, COPY_FILE_BYTES, index, segment.file_size) + copied += segment.file_size + bss = segment.memory_size - segment.file_size + if bss: + _ok(facade, ZERO_BSS, index, bss) + zeroed += bss + _ok(facade, APPLY_RELATIVE, value=plan.relative_relocations) + _ok(facade, COPY_MIRROR_TO_CHILD, value=plan.region_size) + for index, segment in enumerate(plan.segments): + _ok(facade, SET_FINAL_PROTECTION, index, segment.flags) + protections.append(segment.flags) + _ok(facade, SYNC_IMAGE, value=plan.region_size) + _ok(facade, RELEASE_MIRROR) + mirror_active = False + mirror_released = True + committed = True + except Exception as error: + primary_error = error + finally: + try: + if mirror_active: + if facade.invoke(RELEASE_MIRROR, cleanup=True).result != OK: + raise MappingModelError("mirror cleanup failed") + mirror_active = False + mirror_released = True + if child_reserved and not committed: + if facade.invoke(UNMAP_CHILD, value=plan.region_size, + cleanup=True).result != OK: + raise MappingModelError("child mapping rollback failed") + child_unmapped = True + child_reserved = False + except Exception as cleanup_error: + raise MappingModelError("mapping rollback is incomplete") from cleanup_error + + if facade.remaining: + raise MappingModelError("fake mapping script has unused operations") + if primary_error is not None: + return MappingOutcome( + "OFFLINE_MAPPING_TRANSACTION_ROLLED_BACK", False, plan.region_size, + copied, zeroed, plan.relative_relocations, tuple(protections), + mirror_released, False, child_unmapped) + if not committed or not mirror_released or child_unmapped \ + or tuple(protections) != tuple(item.flags for item in plan.segments): + raise MappingModelError("successful mapping invariants failed") + return MappingOutcome( + "OFFLINE_MAPPING_TRANSACTION_MODEL_COMPLETE", True, plan.region_size, + copied, zeroed, plan.relative_relocations, tuple(protections), True, + True, False) diff --git a/tools/phase10ak_hybrid_composition.py b/tools/phase10ak_hybrid_composition.py new file mode 100644 index 0000000..35280e6 --- /dev/null +++ b/tools/phase10ak_hybrid_composition.py @@ -0,0 +1,214 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Host-only ownership model for hybrid BigApp/JIT mapping composition.""" + +from __future__ import annotations + +from dataclasses import dataclass + + +MAX_EXECUTABLE_SEGMENTS = 8 +MAX_EVENTS = 256 +MAX_TICKS = 256 + +CREATE_CHILD = "CREATE_CHILD" +RESERVE_REGION = "RESERVE_REGION" +CREATE_MIRROR = "CREATE_MIRROR" +CREATE_JIT_MASTER = "CREATE_JIT_MASTER" +MAP_EXECUTABLE = "MAP_EXECUTABLE" +CREATE_JIT_ALIAS = "CREATE_JIT_ALIAS" +MAP_HOST_ALIAS = "MAP_HOST_ALIAS" +MAP_REMOTE_ALIAS = "MAP_REMOTE_ALIAS" +COPY_ALIAS = "COPY_ALIAS" +UNMAP_REMOTE_ALIAS = "UNMAP_REMOTE_ALIAS" +UNMAP_HOST_ALIAS = "UNMAP_HOST_ALIAS" +CLOSE_JIT_ALIAS = "CLOSE_JIT_ALIAS" +CLOSE_JIT_MASTER = "CLOSE_JIT_MASTER" +FINALIZE_IMAGE = "FINALIZE_IMAGE" +RELEASE_MIRROR = "RELEASE_MIRROR" +UNMAP_REGION = "UNMAP_REGION" +KILL_AND_REAP_CHILD = "KILL_AND_REAP_CHILD" +OK = "OK" +ERROR = "ERROR" + +INDEXED = { + CREATE_JIT_MASTER, MAP_EXECUTABLE, CREATE_JIT_ALIAS, MAP_HOST_ALIAS, + MAP_REMOTE_ALIAS, COPY_ALIAS, UNMAP_REMOTE_ALIAS, UNMAP_HOST_ALIAS, + CLOSE_JIT_ALIAS, CLOSE_JIT_MASTER, +} +OPERATIONS = INDEXED | { + CREATE_CHILD, RESERVE_REGION, CREATE_MIRROR, FINALIZE_IMAGE, + RELEASE_MIRROR, UNMAP_REGION, KILL_AND_REAP_CHILD, +} + + +class CompositionError(RuntimeError): + """The synthetic ownership transaction cannot close safely.""" + + +@dataclass(frozen=True) +class CompositionPlan: + executable_segments: int + + def __post_init__(self) -> None: + if not isinstance(self.executable_segments, int) \ + or isinstance(self.executable_segments, bool) \ + or not 1 <= self.executable_segments <= MAX_EXECUTABLE_SEGMENTS: + raise CompositionError("executable segment count is invalid") + + +@dataclass(frozen=True) +class FakeEvent: + operation: str + result: str + segment: int = -1 + ticks: int = 1 + + def __post_init__(self) -> None: + if self.operation not in OPERATIONS or self.result not in {OK, ERROR}: + raise CompositionError("event operation/result is invalid") + if (self.operation in INDEXED) != (self.segment >= 0): + raise CompositionError("event segment binding is invalid") + if not isinstance(self.segment, int) or isinstance(self.segment, bool): + raise CompositionError("event segment is invalid") + if not isinstance(self.ticks, int) or isinstance(self.ticks, bool) \ + or not 1 <= self.ticks <= MAX_TICKS: + raise CompositionError("event ticks are invalid") + + +class FakeFacade: + """Exact fake script with no process, mapping, clock or device access.""" + + def __init__(self, events: tuple[FakeEvent, ...]) -> None: + if not isinstance(events, tuple) or not 1 <= len(events) <= MAX_EVENTS \ + or any(type(event) is not FakeEvent for event in events): + raise CompositionError("fake composition script is invalid") + self._events = list(events) + self.ticks = 0 + + def invoke(self, operation: str, segment: int = -1, + cleanup: bool = False) -> str: + if not self._events: + raise CompositionError("fake composition script is exhausted") + event = self._events.pop(0) + if event.operation != operation or event.segment != segment: + raise CompositionError("fake composition ordering differs") + if not cleanup and self.ticks + event.ticks > MAX_TICKS: + raise CompositionError("composition deadline precedes operation") + self.ticks += event.ticks + return event.result + + @property + def remaining(self) -> int: + return len(self._events) + + +@dataclass(frozen=True) +class CompositionOutcome: + classification: str + success: bool + child_alive: bool + image_retained: bool + resources_open: int + fail_closed_termination: bool + target_action_performed: bool = False + firmware_behavior_proven: bool = False + + +def _require(facade: FakeFacade, operation: str, segment: int = -1) -> None: + if facade.invoke(operation, segment) != OK: + raise CompositionError(f"{operation} failed") + + +def run_composition(plan: CompositionPlan, facade: FakeFacade) -> CompositionOutcome: + """Compose synthetic resources; cleanup failure requires child termination.""" + if type(plan) is not CompositionPlan or type(facade) is not FakeFacade: + raise CompositionError("composition boundary is invalid") + child = region = mirror = committed = False + masters: set[int] = set() + aliases: set[int] = set() + host_aliases: set[int] = set() + remote_aliases: set[int] = set() + primary_failed = cleanup_failed = terminated = False + + try: + _require(facade, CREATE_CHILD) + child = True + _require(facade, RESERVE_REGION) + region = True + _require(facade, CREATE_MIRROR) + mirror = True + for segment in range(plan.executable_segments): + _require(facade, CREATE_JIT_MASTER, segment) + masters.add(segment) + _require(facade, MAP_EXECUTABLE, segment) + _require(facade, CREATE_JIT_ALIAS, segment) + aliases.add(segment) + _require(facade, MAP_HOST_ALIAS, segment) + host_aliases.add(segment) + _require(facade, MAP_REMOTE_ALIAS, segment) + remote_aliases.add(segment) + _require(facade, COPY_ALIAS, segment) + _require(facade, UNMAP_REMOTE_ALIAS, segment) + remote_aliases.remove(segment) + _require(facade, UNMAP_HOST_ALIAS, segment) + host_aliases.remove(segment) + _require(facade, CLOSE_JIT_ALIAS, segment) + aliases.remove(segment) + _require(facade, CLOSE_JIT_MASTER, segment) + masters.remove(segment) + _require(facade, FINALIZE_IMAGE) + _require(facade, RELEASE_MIRROR) + mirror = False + committed = True + except Exception: + primary_failed = True + + if primary_failed: + for resources, operation in ( + (remote_aliases, UNMAP_REMOTE_ALIAS), + (host_aliases, UNMAP_HOST_ALIAS), + (aliases, CLOSE_JIT_ALIAS), + (masters, CLOSE_JIT_MASTER), + ): + for segment in sorted(resources, reverse=True): + if facade.invoke(operation, segment, cleanup=True) != OK: + cleanup_failed = True + else: + resources.remove(segment) + if mirror: + if facade.invoke(RELEASE_MIRROR, cleanup=True) != OK: + cleanup_failed = True + else: + mirror = False + if region: + if facade.invoke(UNMAP_REGION, cleanup=True) != OK: + cleanup_failed = True + else: + region = False + if child: + if facade.invoke(KILL_AND_REAP_CHILD, cleanup=True) != OK: + raise CompositionError("fail-closed child termination failed") + child = False + terminated = True + masters.clear() + aliases.clear() + host_aliases.clear() + remote_aliases.clear() + mirror = region = False + + if facade.remaining: + raise CompositionError("fake composition script has unused operations") + open_count = (int(mirror) + int(region) + len(masters) + len(aliases) + + len(host_aliases) + len(remote_aliases)) + if primary_failed: + if child or open_count: + raise CompositionError("failed composition retained ownership") + classification = ("OFFLINE_FAIL_CLOSED_AFTER_CLEANUP_FAILURE" if cleanup_failed + else "OFFLINE_COMPOSITION_ROLLED_BACK") + return CompositionOutcome(classification, False, False, False, 0, + terminated) + if not committed or not child or not region or mirror or open_count != 1: + raise CompositionError("successful composition invariants failed") + return CompositionOutcome("OFFLINE_HYBRID_COMPOSITION_COMPLETE", True, + True, True, 0, False) diff --git a/tools/phase10am_bounded_copy_model.py b/tools/phase10am_bounded_copy_model.py new file mode 100644 index 0000000..fb2dd11 --- /dev/null +++ b/tools/phase10am_bounded_copy_model.py @@ -0,0 +1,197 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Capability-free exact-progress copy and credential restoration model.""" + +from __future__ import annotations + +from dataclasses import dataclass + + +MAX_COPY_SIZE = 128 * 1024 * 1024 +MAX_CHUNKS = 4096 +MAX_EVENTS = MAX_CHUNKS + 16 +MAX_TICKS = 8192 +MAX_U64 = (1 << 64) - 1 + +BACKUP_AUTHID = "BACKUP_AUTHID" +BACKUP_CAPS = "BACKUP_CAPS" +SET_PRIV_AUTHID = "SET_PRIV_AUTHID" +SET_PRIV_CAPS = "SET_PRIV_CAPS" +COPY_CHUNK = "COPY_CHUNK" +RESTORE_CAPS = "RESTORE_CAPS" +RESTORE_AUTHID = "RESTORE_AUTHID" +KILL_AND_REAP_CHILD = "KILL_AND_REAP_CHILD" +TERMINATE_SERVICE = "TERMINATE_SERVICE" +OK = "OK" +ERROR = "ERROR" +MORE = "MORE" +COMPLETE = "COMPLETE" + +OPERATIONS = { + BACKUP_AUTHID, BACKUP_CAPS, SET_PRIV_AUTHID, SET_PRIV_CAPS, COPY_CHUNK, + RESTORE_CAPS, RESTORE_AUTHID, KILL_AND_REAP_CHILD, TERMINATE_SERVICE, +} + + +class CopyModelError(RuntimeError): + """The synthetic copy contract cannot reach a bounded safe state.""" + + +@dataclass(frozen=True) +class CopyPlan: + source: int + destination: int + length: int + + def __post_init__(self) -> None: + for value in (self.source, self.destination, self.length): + if not isinstance(value, int) or isinstance(value, bool) or value < 0: + raise CopyModelError("copy plan value is invalid") + if not 1 <= self.length <= MAX_COPY_SIZE: + raise CopyModelError("copy length is invalid") + if self.source > MAX_U64 - self.length \ + or self.destination > MAX_U64 - self.length: + raise CopyModelError("copy range overflows") + + +@dataclass(frozen=True) +class FakeCopyEvent: + operation: str + result: str + progress: int = 0 + remote_status: str = "" + ticks: int = 1 + + def __post_init__(self) -> None: + if self.operation not in OPERATIONS or self.result not in {OK, ERROR}: + raise CopyModelError("copy event operation/result is invalid") + if not isinstance(self.progress, int) or isinstance(self.progress, bool) \ + or self.progress < 0: + raise CopyModelError("copy progress is invalid") + if self.operation == COPY_CHUNK: + if self.remote_status not in {MORE, COMPLETE}: + raise CopyModelError("copy status is invalid") + elif self.progress or self.remote_status: + raise CopyModelError("non-copy event carries copy result") + if not isinstance(self.ticks, int) or isinstance(self.ticks, bool) \ + or not 1 <= self.ticks <= MAX_TICKS: + raise CopyModelError("copy event ticks are invalid") + + +class FakeCopyFacade: + """Exact fake script with no credential, process, clock or memory access.""" + + def __init__(self, events: tuple[FakeCopyEvent, ...]) -> None: + if not isinstance(events, tuple) or not 1 <= len(events) <= MAX_EVENTS \ + or any(type(item) is not FakeCopyEvent for item in events): + raise CopyModelError("copy event script is invalid") + self._events = list(events) + self.ticks = 0 + + def invoke(self, operation: str, cleanup: bool = False) -> FakeCopyEvent: + if not self._events: + raise CopyModelError("copy event script is exhausted") + event = self._events.pop(0) + if event.operation != operation: + raise CopyModelError("copy event ordering differs") + if not cleanup and self.ticks + event.ticks > MAX_TICKS: + raise CopyModelError("copy deadline precedes operation") + self.ticks += event.ticks + return event + + @property + def remaining(self) -> int: + return len(self._events) + + +@dataclass(frozen=True) +class CopyOutcome: + classification: str + success: bool + copied: int + restore_failure_bits: int + child_alive: bool + service_available: bool + target_copy_performed: bool = False + firmware_behavior_proven: bool = False + + +def run_copy(plan: CopyPlan, facade: FakeCopyFacade) -> CopyOutcome: + """Run one bounded synthetic copy and attempt every required restore.""" + if type(plan) is not CopyPlan or type(facade) is not FakeCopyFacade: + raise CopyModelError("copy model boundary is invalid") + auth_changed = caps_changed = False + copied = chunks = restore_failures = 0 + failed = False + + try: + if facade.invoke(BACKUP_AUTHID).result != OK: + failed = True + elif facade.invoke(BACKUP_CAPS).result != OK: + failed = True + elif facade.invoke(SET_PRIV_AUTHID).result != OK: + failed = True + else: + auth_changed = True + if facade.invoke(SET_PRIV_CAPS).result != OK: + failed = True + else: + caps_changed = True + while copied < plan.length: + if chunks >= MAX_CHUNKS: + failed = True + break + event = facade.invoke(COPY_CHUNK) + chunks += 1 + remaining = plan.length - copied + if event.result != OK or event.progress == 0 \ + or event.progress > remaining: + failed = True + break + copied += event.progress + if event.remote_status == COMPLETE: + if copied != plan.length: + failed = True + break + if copied == plan.length: + failed = True + break + if copied != plan.length: + failed = True + except CopyModelError: + failed = True + + if caps_changed: + if facade.invoke(RESTORE_CAPS, cleanup=True).result != OK: + restore_failures |= 1 + caps_changed = False + if auth_changed: + if facade.invoke(RESTORE_AUTHID, cleanup=True).result != OK: + restore_failures |= 2 + auth_changed = False + + child_alive = True + service_available = True + if (failed and copied) or restore_failures: + if facade.invoke(KILL_AND_REAP_CHILD, cleanup=True).result != OK: + raise CopyModelError("child termination failed") + child_alive = False + if restore_failures: + if facade.invoke(TERMINATE_SERVICE, cleanup=True).result != OK: + raise CopyModelError("compromised service termination failed") + service_available = False + if facade.remaining: + raise CopyModelError("copy event script has unused operations") + + success = not failed and copied == plan.length and restore_failures == 0 + if success: + return CopyOutcome("OFFLINE_EXACT_COPY_COMPLETE", True, copied, 0, + True, True) + if restore_failures: + classification = "OFFLINE_RESTORE_FAILURE_CONTAINED" + elif copied: + classification = "OFFLINE_PARTIAL_COPY_CONTAINED" + else: + classification = "OFFLINE_COPY_REJECTED_BEFORE_MUTATION" + return CopyOutcome(classification, False, copied, restore_failures, + child_alive, service_available) diff --git a/tools/phase10ao_worker_supervisor_model.py b/tools/phase10ao_worker_supervisor_model.py new file mode 100644 index 0000000..36b3872 --- /dev/null +++ b/tools/phase10ao_worker_supervisor_model.py @@ -0,0 +1,188 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Host-only supervisor/worker preemption architecture model.""" + +from __future__ import annotations + +from dataclasses import dataclass + + +MAX_COPY_SIZE = 128 * 1024 * 1024 +MAX_TICKS = 256 +MAX_EVENTS = 16 + +CREATE_WORKER = "CREATE_WORKER" +VERIFY_WORKER = "VERIFY_WORKER" +START_COPY = "START_COPY" +RECEIVE_RESULT = "RECEIVE_RESULT" +DEADLINE = "DEADLINE" +TERMINATE_WORKER = "TERMINATE_WORKER" +REAP_WORKER = "REAP_WORKER" +TERMINATE_CHILD = "TERMINATE_CHILD" +REAP_CHILD = "REAP_CHILD" +OK = "OK" +ERROR = "ERROR" +SUCCESS = "SUCCESS" +COPY_ERROR = "COPY_ERROR" +RESTORE_ERROR = "RESTORE_ERROR" + +OPERATIONS = { + CREATE_WORKER, VERIFY_WORKER, START_COPY, RECEIVE_RESULT, DEADLINE, + TERMINATE_WORKER, REAP_WORKER, TERMINATE_CHILD, REAP_CHILD, +} + + +class SupervisorError(RuntimeError): + """The synthetic supervisor cannot prove containment.""" + + +@dataclass(frozen=True) +class WorkerPlan: + worker_id: int + child_id: int + copy_length: int + + def __post_init__(self) -> None: + for value in (self.worker_id, self.child_id, self.copy_length): + if not isinstance(value, int) or isinstance(value, bool) or value <= 0: + raise SupervisorError("worker plan value is invalid") + if self.worker_id == self.child_id or self.copy_length > MAX_COPY_SIZE: + raise SupervisorError("worker plan identity or size is invalid") + + +@dataclass(frozen=True) +class WorkerResult: + worker_id: int + status: str + copied: int + restore_failure_bits: int + + def __post_init__(self) -> None: + if not isinstance(self.worker_id, int) or isinstance(self.worker_id, bool) \ + or self.worker_id <= 0 or self.status not in { + SUCCESS, COPY_ERROR, RESTORE_ERROR}: + raise SupervisorError("worker result identity/status is invalid") + if not isinstance(self.copied, int) or isinstance(self.copied, bool) \ + or self.copied < 0 or not isinstance(self.restore_failure_bits, int) \ + or isinstance(self.restore_failure_bits, bool) \ + or not 0 <= self.restore_failure_bits <= 3: + raise SupervisorError("worker result fields are invalid") + + +@dataclass(frozen=True) +class FakeSupervisorEvent: + operation: str + result: str + worker_result: WorkerResult | None = None + ticks: int = 1 + + def __post_init__(self) -> None: + if self.operation not in OPERATIONS or self.result not in {OK, ERROR}: + raise SupervisorError("supervisor event is invalid") + if (self.operation == RECEIVE_RESULT) != (self.worker_result is not None): + raise SupervisorError("worker result binding is invalid") + if not isinstance(self.ticks, int) or isinstance(self.ticks, bool) \ + or not 1 <= self.ticks <= MAX_TICKS: + raise SupervisorError("supervisor event ticks are invalid") + + +class FakeSupervisorFacade: + """Exact fake operations with no process, signal, clock or IPC access.""" + + def __init__(self, events: tuple[FakeSupervisorEvent, ...]) -> None: + if not isinstance(events, tuple) or not 1 <= len(events) <= MAX_EVENTS \ + or any(type(item) is not FakeSupervisorEvent for item in events): + raise SupervisorError("supervisor script is invalid") + self._events = list(events) + self.ticks = 0 + + def invoke(self, operation: str, terminal: bool = False) -> FakeSupervisorEvent: + if not self._events: + raise SupervisorError("supervisor script is exhausted") + event = self._events.pop(0) + if event.operation != operation: + raise SupervisorError("supervisor event ordering differs") + if not terminal and self.ticks + event.ticks > MAX_TICKS: + raise SupervisorError("supervisor deadline precedes operation") + self.ticks += event.ticks + return event + + @property + def remaining(self) -> int: + return len(self._events) + + +@dataclass(frozen=True) +class SupervisorOutcome: + classification: str + success: bool + service_alive: bool + worker_alive: bool + child_alive: bool + copied: int + automatic_restart: bool = False + target_action_performed: bool = False + firmware_behavior_proven: bool = False + + +def _terminal(facade: FakeSupervisorFacade, operation: str) -> None: + if facade.invoke(operation, terminal=True).result != OK: + raise SupervisorError(f"terminal operation failed: {operation}") + + +def run_supervisor(plan: WorkerPlan, facade: FakeSupervisorFacade, + deadline_first: bool = False) -> SupervisorOutcome: + """Run one synthetic, non-restarting worker attempt.""" + if type(plan) is not WorkerPlan or type(facade) is not FakeSupervisorFacade \ + or type(deadline_first) is not bool: + raise SupervisorError("supervisor model boundary is invalid") + worker = started = False + copied = 0 + failed = deadline = False + + if facade.invoke(CREATE_WORKER).result != OK: + failed = True + else: + worker = True + if facade.invoke(VERIFY_WORKER).result != OK: + failed = True + elif facade.invoke(START_COPY).result != OK: + started = True + failed = True + else: + started = True + if deadline_first: + if facade.invoke(DEADLINE).result != OK: + raise SupervisorError("deadline event failed") + deadline = failed = True + else: + event = facade.invoke(RECEIVE_RESULT) + result = event.worker_result + if event.result != OK or result is None: + failed = True + else: + copied = result.copied + valid = (result.worker_id == plan.worker_id and + result.status == SUCCESS and + result.copied == plan.copy_length and + result.restore_failure_bits == 0) + failed = not valid + + if worker: + if failed: + _terminal(facade, TERMINATE_WORKER) + _terminal(facade, REAP_WORKER) + worker = False + if failed and started: + _terminal(facade, TERMINATE_CHILD) + _terminal(facade, REAP_CHILD) + if facade.remaining: + raise SupervisorError("supervisor script has unused operations") + + if not failed: + return SupervisorOutcome("OFFLINE_WORKER_RESULT_ACCEPTED", True, True, + False, True, copied) + classification = ("OFFLINE_DEADLINE_CONTAINED" if deadline else + "OFFLINE_WORKER_FAILURE_CONTAINED") + return SupervisorOutcome(classification, False, True, False, + not started, copied) diff --git a/tools/phase10aq_worker_result_record.py b/tools/phase10aq_worker_result_record.py new file mode 100644 index 0000000..5b78150 --- /dev/null +++ b/tools/phase10aq_worker_result_record.py @@ -0,0 +1,127 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Bytes-only fixed worker result record and generation identity contract.""" + +from __future__ import annotations + +from dataclasses import dataclass +import hashlib +import struct + + +MAGIC = b"CHW10AQ1" +VERSION = 1 +RECORD_SIZE = 128 +HASHED_SIZE = 96 +MAX_COPY_SIZE = 128 * 1024 * 1024 +STATUS_SUCCESS = 0 +STATUS_COPY_ERROR = 1 +STATUS_RESTORE_ERROR = 2 +STATUS_DEADLINE = 3 +VALID_STATUSES = {STATUS_SUCCESS, STATUS_COPY_ERROR, + STATUS_RESTORE_ERROR, STATUS_DEADLINE} +PREFIX = struct.Struct("<8sHHIIIQQQII16s16s8s") + + +class WorkerRecordError(ValueError): + """The fixed worker record is malformed or not precommitted.""" + + +def _token(value: bytes, name: str) -> bytes: + if type(value) is not bytes or len(value) != 16 or not any(value): + raise WorkerRecordError(f"{name} must be a nonzero 16-byte token") + return value + + +@dataclass(frozen=True) +class WorkerPrecommit: + attempt_id: bytes + nonce: bytes + worker_pid: int + child_pid: int + generation: int + requested: int + + def __post_init__(self) -> None: + _token(self.attempt_id, "attempt ID") + _token(self.nonce, "worker nonce") + for value in (self.worker_pid, self.child_pid, self.generation, + self.requested): + if not isinstance(value, int) or isinstance(value, bool) or value <= 0: + raise WorkerRecordError("precommit integer is invalid") + if self.worker_pid == self.child_pid or self.worker_pid > 0xffffffff \ + or self.child_pid > 0xffffffff or self.generation > 0xffffffffffffffff \ + or self.requested > MAX_COPY_SIZE: + raise WorkerRecordError("precommit identity or size is invalid") + + +@dataclass(frozen=True) +class WorkerRecord: + status: int + worker_pid: int + child_pid: int + generation: int + requested: int + copied: int + restore_failure_bits: int + flags: int + attempt_id: bytes + nonce: bytes + + @property + def successful(self) -> bool: + return (self.status == STATUS_SUCCESS and self.copied == self.requested + and self.restore_failure_bits == 0 and self.flags == 0) + + +def encode_record(precommit: WorkerPrecommit, status: int, copied: int, + restore_failure_bits: int = 0, flags: int = 0) -> bytes: + """Encode synthetic bytes; this function has no transport capability.""" + if type(precommit) is not WorkerPrecommit or status not in VALID_STATUSES: + raise WorkerRecordError("record input is invalid") + for value in (copied, restore_failure_bits, flags): + if not isinstance(value, int) or isinstance(value, bool) or value < 0: + raise WorkerRecordError("record numeric field is invalid") + if copied > precommit.requested or restore_failure_bits > 3 \ + or flags > 0xffffffff: + raise WorkerRecordError("record progress, restore bits or flags are invalid") + if status == STATUS_SUCCESS and (copied != precommit.requested + or restore_failure_bits or flags): + raise WorkerRecordError("success record is not exact") + prefix = PREFIX.pack(MAGIC, VERSION, RECORD_SIZE, status, + precommit.worker_pid, precommit.child_pid, + precommit.generation, precommit.requested, copied, + restore_failure_bits, flags, precommit.attempt_id, + precommit.nonce, bytes(8)) + if len(prefix) != HASHED_SIZE: + raise WorkerRecordError("internal record layout differs") + return prefix + hashlib.sha256(prefix).digest() + + +def parse_record(raw: bytes, expected: WorkerPrecommit) -> WorkerRecord: + """Parse exactly one complete record and enforce the full precommit.""" + if type(raw) is not bytes or len(raw) != RECORD_SIZE \ + or type(expected) is not WorkerPrecommit: + raise WorkerRecordError("record boundary is invalid") + prefix, digest = raw[:HASHED_SIZE], raw[HASHED_SIZE:] + if not hashlib.sha256(prefix).digest() == digest: + raise WorkerRecordError("record digest differs") + (magic, version, size, status, worker_pid, child_pid, generation, + requested, copied, restore_bits, flags, attempt_id, nonce, + reserved) = PREFIX.unpack(prefix) + if magic != MAGIC or version != VERSION or size != RECORD_SIZE \ + or status not in VALID_STATUSES or any(reserved): + raise WorkerRecordError("record header is invalid") + if (worker_pid, child_pid, generation, requested, attempt_id, nonce) != ( + expected.worker_pid, expected.child_pid, expected.generation, + expected.requested, expected.attempt_id, expected.nonce): + raise WorkerRecordError("record identity differs from precommit") + if copied > requested or restore_bits > 3 or flags != 0: + raise WorkerRecordError("record result fields are invalid") + record = WorkerRecord(status, worker_pid, child_pid, generation, requested, + copied, restore_bits, flags, attempt_id, nonce) + if status == STATUS_SUCCESS and not record.successful: + raise WorkerRecordError("success record is incomplete") + if status == STATUS_RESTORE_ERROR and restore_bits == 0: + raise WorkerRecordError("restore error has no failure bits") + return record diff --git a/tools/phase10ar_result_channel_model.py b/tools/phase10ar_result_channel_model.py new file mode 100644 index 0000000..1c49f45 --- /dev/null +++ b/tools/phase10ar_result_channel_model.py @@ -0,0 +1,105 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Transport-free partial-read/deadline model for one worker result record.""" + +from __future__ import annotations + +from dataclasses import dataclass + +from phase10aq_worker_result_record import ( + RECORD_SIZE, WorkerPrecommit, WorkerRecord, WorkerRecordError, parse_record, +) + + +MAX_CHUNKS = 128 +MAX_TICKS = 256 +DATA = "DATA" +EOF = "EOF" +DEADLINE = "DEADLINE" +EVENTS = {DATA, EOF, DEADLINE} + + +class ChannelModelError(RuntimeError): + """The synthetic channel script or boundary is invalid.""" + + +@dataclass(frozen=True) +class FakeReadEvent: + kind: str + data: bytes = b"" + ticks: int = 1 + + def __post_init__(self) -> None: + if self.kind not in EVENTS or type(self.data) is not bytes: + raise ChannelModelError("read event is invalid") + if (self.kind == DATA) != bool(self.data) or len(self.data) > RECORD_SIZE: + raise ChannelModelError("read event payload is invalid") + if not isinstance(self.ticks, int) or isinstance(self.ticks, bool) \ + or not 1 <= self.ticks <= MAX_TICKS: + raise ChannelModelError("read event ticks are invalid") + + +@dataclass(frozen=True) +class ChannelPlan: + precommit: WorkerPrecommit + writer_pid: int + writer_generation: int + writer_nonce: bytes + exclusive_writer: bool = True + + def __post_init__(self) -> None: + if type(self.precommit) is not WorkerPrecommit \ + or self.writer_pid != self.precommit.worker_pid \ + or self.writer_generation != self.precommit.generation \ + or self.writer_nonce != self.precommit.nonce \ + or self.exclusive_writer is not True: + raise ChannelModelError("exclusive writer precommit differs") + + +@dataclass(frozen=True) +class ChannelOutcome: + classification: str + accepted: bool + buffered: int + record: WorkerRecord | None + containment_required: bool + eof_is_success: bool = False + live_transport_present: bool = False + + +def receive_record(plan: ChannelPlan, + events: tuple[FakeReadEvent, ...]) -> ChannelOutcome: + """Assemble one exact record from supplied bytes; no I/O is performed.""" + if type(plan) is not ChannelPlan or not isinstance(events, tuple) \ + or not 1 <= len(events) <= MAX_CHUNKS \ + or any(type(item) is not FakeReadEvent for item in events): + raise ChannelModelError("channel model boundary is invalid") + buffer = bytearray() + ticks = 0 + for index, event in enumerate(events): + if ticks + event.ticks > MAX_TICKS: + return ChannelOutcome("OFFLINE_CHANNEL_DEADLINE", False, + len(buffer), None, True) + ticks += event.ticks + if event.kind == DEADLINE: + return ChannelOutcome("OFFLINE_CHANNEL_DEADLINE", False, + len(buffer), None, True) + if event.kind == EOF: + return ChannelOutcome("OFFLINE_EOF_BEFORE_COMPLETE_RECORD", False, + len(buffer), None, True) + if len(buffer) + len(event.data) > RECORD_SIZE: + return ChannelOutcome("OFFLINE_CHANNEL_OVERFLOW", False, + len(buffer), None, True) + buffer.extend(event.data) + if len(buffer) == RECORD_SIZE: + if index != len(events) - 1: + raise ChannelModelError("events remain after record boundary") + try: + record = parse_record(bytes(buffer), plan.precommit) + except WorkerRecordError: + return ChannelOutcome("OFFLINE_RECORD_REJECTED", False, + RECORD_SIZE, None, True) + return ChannelOutcome("OFFLINE_EXACT_RECORD_ACCEPTED", True, + RECORD_SIZE, record, False) + return ChannelOutcome("OFFLINE_INCOMPLETE_WITHOUT_DEADLINE", False, + len(buffer), None, True) diff --git a/tools/phase10at_fd_deadline_model.py b/tools/phase10at_fd_deadline_model.py new file mode 100644 index 0000000..c3bc4b8 --- /dev/null +++ b/tools/phase10at_fd_deadline_model.py @@ -0,0 +1,205 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Host-only RFFDG descriptor ownership and absolute deadline model.""" + +from __future__ import annotations + +from dataclasses import dataclass + +from phase10aq_worker_result_record import ( + RECORD_SIZE, WorkerPrecommit, WorkerRecordError, parse_record, +) + + +MAX_TICKS = 256 +MAX_READ_EVENTS = 256 +CREATE_PIPE = "CREATE_PIPE" +SET_PARENT_READ_NONBLOCK = "SET_PARENT_READ_NONBLOCK" +SPAWN_RFFDG = "SPAWN_RFFDG" +PARENT_CLOSE_WRITE = "PARENT_CLOSE_WRITE" +CHILD_CLOSE_READ = "CHILD_CLOSE_READ" +CHILD_CLOSE_WRITE = "CHILD_CLOSE_WRITE" +PARENT_CLOSE_READ = "PARENT_CLOSE_READ" +TERMINATE_CHILD = "TERMINATE_CHILD" +REAP_CHILD = "REAP_CHILD" +OK = "OK" +ERROR = "ERROR" +DATA = "DATA" +EINTR = "EINTR" +WOULD_BLOCK = "WOULD_BLOCK" +EOF = "EOF" + +OPERATIONS = { + CREATE_PIPE, SET_PARENT_READ_NONBLOCK, SPAWN_RFFDG, + PARENT_CLOSE_WRITE, CHILD_CLOSE_READ, CHILD_CLOSE_WRITE, + PARENT_CLOSE_READ, TERMINATE_CHILD, REAP_CHILD, +} +READ_KINDS = {DATA, EINTR, WOULD_BLOCK, EOF} + + +class FdModelError(RuntimeError): + """The synthetic descriptor transaction cannot close safely.""" + + +@dataclass(frozen=True) +class FakeFdEvent: + operation: str + result: str + ticks: int = 1 + + def __post_init__(self) -> None: + if self.operation not in OPERATIONS or self.result not in {OK, ERROR}: + raise FdModelError("FD event is invalid") + if not isinstance(self.ticks, int) or isinstance(self.ticks, bool) \ + or not 1 <= self.ticks <= MAX_TICKS: + raise FdModelError("FD event ticks are invalid") + + +@dataclass(frozen=True) +class FakeRead: + kind: str + data: bytes = b"" + ticks: int = 1 + + def __post_init__(self) -> None: + if self.kind not in READ_KINDS or type(self.data) is not bytes: + raise FdModelError("read event is invalid") + if (self.kind == DATA) != bool(self.data) or len(self.data) > RECORD_SIZE: + raise FdModelError("read payload is invalid") + if not isinstance(self.ticks, int) or isinstance(self.ticks, bool) \ + or not 1 <= self.ticks <= MAX_TICKS: + raise FdModelError("read ticks are invalid") + + +class FakeFdFacade: + """Exact scripted FD facade with no OS, process, clock or I/O imports.""" + + def __init__(self, events: tuple[FakeFdEvent, ...]) -> None: + if not isinstance(events, tuple) or not events \ + or any(type(item) is not FakeFdEvent for item in events): + raise FdModelError("FD script is invalid") + self._events = list(events) + self.ticks = 0 + + def invoke(self, operation: str, cleanup: bool = False) -> str: + if not self._events: + raise FdModelError("FD script is exhausted") + event = self._events.pop(0) + if event.operation != operation: + raise FdModelError("FD operation ordering differs") + if not cleanup and self.ticks + event.ticks > MAX_TICKS: + raise FdModelError("absolute deadline precedes FD operation") + self.ticks += event.ticks + return event.result + + @property + def remaining(self) -> int: + return len(self._events) + + +@dataclass(frozen=True) +class FdOutcome: + classification: str + success: bool + buffered: int + eintr_count: int + parent_fds_open: int + child_fds_open: int + worker_alive: bool + containment_required: bool + rffdg_used: bool = True + live_fd_present: bool = False + + +def _close(facade: FakeFdFacade, operation: str) -> None: + if facade.invoke(operation, cleanup=True) != OK: + raise FdModelError(f"terminal cleanup failed: {operation}") + + +def run_fd_transaction(precommit: WorkerPrecommit, facade: FakeFdFacade, + reads: tuple[FakeRead, ...]) -> FdOutcome: + """Model exact descriptor ownership and one absolute deadline.""" + if type(precommit) is not WorkerPrecommit or type(facade) is not FakeFdFacade \ + or not isinstance(reads, tuple) or not 1 <= len(reads) <= MAX_READ_EVENTS \ + or any(type(item) is not FakeRead for item in reads): + raise FdModelError("FD model boundary is invalid") + parent_read = parent_write = child_read = child_write = worker = False + failed = False + buffer = bytearray() + eintr_count = 0 + + try: + if facade.invoke(CREATE_PIPE) != OK: + failed = True + else: + parent_read = parent_write = True + if facade.invoke(SET_PARENT_READ_NONBLOCK) != OK: + failed = True + elif facade.invoke(SPAWN_RFFDG) != OK: + failed = True + else: + worker = child_read = child_write = True + if facade.invoke(PARENT_CLOSE_WRITE) != OK: + failed = True + else: + parent_write = False + if facade.invoke(CHILD_CLOSE_READ) != OK: + failed = True + else: + child_read = False + for index, read in enumerate(reads): + if facade.ticks + read.ticks > MAX_TICKS: + failed = True + break + facade.ticks += read.ticks + if read.kind == EINTR: + eintr_count += 1 + continue + if read.kind == WOULD_BLOCK: + continue + if read.kind == EOF: + failed = True + break + if len(buffer) + len(read.data) > RECORD_SIZE: + failed = True + break + buffer.extend(read.data) + if len(buffer) == RECORD_SIZE: + if index != len(reads) - 1: + failed = True + break + try: + parse_record(bytes(buffer), precommit) + except WorkerRecordError: + failed = True + break + if len(buffer) != RECORD_SIZE: + failed = True + except FdModelError: + failed = True + + if worker: + if failed: + _close(facade, TERMINATE_CHILD) + if child_read: + _close(facade, CHILD_CLOSE_READ) + child_read = False + if child_write: + _close(facade, CHILD_CLOSE_WRITE) + child_write = False + _close(facade, REAP_CHILD) + worker = False + if parent_write: + _close(facade, PARENT_CLOSE_WRITE) + parent_write = False + if parent_read: + _close(facade, PARENT_CLOSE_READ) + parent_read = False + if facade.remaining: + raise FdModelError("FD script has unused operations") + + if failed: + return FdOutcome("OFFLINE_FD_TRANSACTION_CONTAINED", False, + len(buffer), eintr_count, 0, 0, False, True) + return FdOutcome("OFFLINE_FD_TRANSACTION_COMPLETE", True, + RECORD_SIZE, eintr_count, 0, 0, False, False) diff --git a/tools/phase10av_launch_context_canary.py b/tools/phase10av_launch_context_canary.py new file mode 100644 index 0000000..3085790 --- /dev/null +++ b/tools/phase10av_launch_context_canary.py @@ -0,0 +1,179 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Target-free launch-context A/B canary contract. + +This module validates immutable synthetic data only. It has no target source, +artifact, socket, process, clock, filesystem output or device capability. +""" + +from __future__ import annotations + +from dataclasses import dataclass +import re + + +PHASE = "PHASE_1_0AV_TARGET_FREE_LAUNCH_CONTEXT_CANARY" +FIRMWARE = "9.60" +PROTOCOL_MAGIC = "CHD10AV1" +RAW_ELFLDR = "RAW_ELFLDR" +BIGAPP_CANDIDATE = "BIGAPP_CANDIDATE" +ARM_KINDS = (RAW_ELFLDR, BIGAPP_CANDIDATE) +SHA256 = re.compile(r"^[0-9a-f]{64}$") +RUN_ID = re.compile(r"^CHIMERA_AV_[A-Z0-9_-]{8,48}$") +RETURN_MIN = -(1 << 31) +RETURN_MAX = (1 << 31) - 1 + + +class CanaryContractError(ValueError): + """The offline canary contract is incomplete, ambiguous or unsafe.""" + + +def _exact_hash(value: str) -> bool: + return isinstance(value, str) and SHA256.fullmatch(value) is not None + + +@dataclass(frozen=True) +class CanaryArm: + kind: str + launcher_sha256: str + run_id: str + approval_sha256: str + + +@dataclass(frozen=True) +class CanaryPairPlan: + phase: str + firmware: str + protocol_magic: str + payload_sha256: str + arms: tuple[CanaryArm, CanaryArm] + one_shot_each: bool + automatic_retry: bool + reconnect: bool + resume: bool + installation: bool + autoload: bool + device_write_authorized: bool + app_termination_authorized: bool + result_reception_authorized: bool + activation_authorized: bool + + +@dataclass(frozen=True) +class CanaryObservation: + kind: str + launcher_sha256: str + payload_sha256: str + run_id: str + protocol_magic: str + d04_seen: bool + d04_result: int + d04_sequence: int + submit_seen: bool + submit_result: int + submit_errno: int + submit_sequence: int + terminal_seen: bool + terminal_sequence: int + cleanup_complete: bool + visible_output_observed: bool + retry_count: int + persistent_write_count: int + + +@dataclass(frozen=True) +class CanaryClassification: + status: str + pair_comparable: bool + submit_return_differs: bool + launch_context_candidate: bool + root_cause_proven: bool = False + visible_output_proven: bool = False + firmware_behavior_proven: bool = False + device_action_authorized: bool = False + + +def validate_plan(plan: CanaryPairPlan) -> None: + """Validate a hypothetical pair without activating either arm.""" + if type(plan) is not CanaryPairPlan or plan.phase != PHASE \ + or plan.firmware != FIRMWARE or plan.protocol_magic != PROTOCOL_MAGIC: + raise CanaryContractError("pair identity is invalid") + if not _exact_hash(plan.payload_sha256) or type(plan.arms) is not tuple \ + or len(plan.arms) != 2 \ + or any(type(arm) is not CanaryArm for arm in plan.arms): + raise CanaryContractError("pair binding is invalid") + if tuple(arm.kind for arm in plan.arms) != ARM_KINDS: + raise CanaryContractError("arm order or kind is invalid") + for arm in plan.arms: + if not _exact_hash(arm.launcher_sha256) \ + or not _exact_hash(arm.approval_sha256) \ + or not isinstance(arm.run_id, str) \ + or RUN_ID.fullmatch(arm.run_id) is None: + raise CanaryContractError("arm identity is invalid") + if plan.arms[0].launcher_sha256 == plan.arms[1].launcher_sha256 \ + or plan.arms[0].run_id == plan.arms[1].run_id \ + or plan.arms[0].approval_sha256 == plan.arms[1].approval_sha256: + raise CanaryContractError("arms are not independently authorized") + if plan.one_shot_each is not True or plan.automatic_retry is not False \ + or plan.reconnect is not False or plan.resume is not False \ + or plan.installation is not False or plan.autoload is not False: + raise CanaryContractError("pair one-shot policy is invalid") + if plan.device_write_authorized or plan.app_termination_authorized \ + or plan.result_reception_authorized or plan.activation_authorized: + raise CanaryContractError("offline plan grants device authority") + + +def _validate_observation(plan: CanaryPairPlan, arm: CanaryArm, + observation: CanaryObservation) -> bool: + if type(observation) is not CanaryObservation \ + or observation.kind != arm.kind \ + or observation.launcher_sha256 != arm.launcher_sha256 \ + or observation.payload_sha256 != plan.payload_sha256 \ + or observation.run_id != arm.run_id \ + or observation.protocol_magic != PROTOCOL_MAGIC: + raise CanaryContractError("observation identity differs") + booleans = (observation.d04_seen, observation.submit_seen, + observation.terminal_seen, observation.cleanup_complete, + observation.visible_output_observed) + if any(type(value) is not bool for value in booleans): + raise CanaryContractError("observation boolean field is invalid") + numeric = (observation.d04_result, observation.submit_result, + observation.submit_errno) + if any(type(value) is not int or not RETURN_MIN <= value <= RETURN_MAX + for value in numeric): + raise CanaryContractError("observation numeric field is invalid") + counts = (observation.submit_sequence, observation.d04_sequence, + observation.terminal_sequence, + observation.retry_count, observation.persistent_write_count) + if any(type(value) is not int or value < 0 for value in counts): + raise CanaryContractError("observation count is invalid") + if observation.retry_count != 0 or observation.persistent_write_count != 0: + raise CanaryContractError("observation reports forbidden effects") + if observation.visible_output_observed: + raise CanaryContractError("unscoped visible-output claim is forbidden") + complete = observation.d04_seen and observation.submit_seen \ + and observation.terminal_seen and observation.cleanup_complete \ + and observation.submit_sequence < observation.d04_sequence \ + and observation.d04_sequence < observation.terminal_sequence + return complete + + +def classify_pair(plan: CanaryPairPlan, + observations: tuple[CanaryObservation, CanaryObservation] + ) -> CanaryClassification: + """Classify supplied synthetic results without interpreting VideoOut ABI.""" + validate_plan(plan) + if type(observations) is not tuple or len(observations) != 2: + raise CanaryContractError("observation pair is invalid") + complete = tuple(_validate_observation(plan, arm, observation) + for arm, observation in zip(plan.arms, observations)) + if not all(complete): + return CanaryClassification("INCOMPLETE_NO_CAUSAL_COMPARISON", + False, False, False) + differs = observations[0].submit_result != observations[1].submit_result + if not differs: + return CanaryClassification("NO_SUBMIT_RETURN_DIFFERENCE", + True, False, False) + return CanaryClassification( + "LAUNCH_CONTEXT_SUBMIT_RETURN_DIFFERENCE_CANDIDATE_ONLY", + True, True, True) diff --git a/tools/phase10ax_canary_protocol_model.py b/tools/phase10ax_canary_protocol_model.py new file mode 100644 index 0000000..85a3b2c --- /dev/null +++ b/tools/phase10ax_canary_protocol_model.py @@ -0,0 +1,189 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Host-only CHD10AV1 frame and cleanup-terminal reference model.""" + +from __future__ import annotations + +from dataclasses import dataclass +import struct +import zlib + + +MAGIC = b"CHD10AV1" +VERSION = 1 +FRAME_SIZE = 64 +D04 = 4 +D07 = 7 +D12 = 12 +D14 = 30 +KIND_RAW = 1 +KIND_PAIR = 2 +RAW0_VALID = 1 << 0 +RAW1_VALID = 1 << 1 +TERMINAL = 1 << 2 +ALLOWED_FLAGS = RAW0_VALID | RAW1_VALID | TERMINAL +S15_COMPLETE = 15 +UINT32_MAX = (1 << 32) - 1 +INT32_MIN = -(1 << 31) +INT32_MAX = (1 << 31) - 1 + + +class CanaryProtocolError(ValueError): + """A synthetic AV frame or cleanup state is invalid.""" + + +@dataclass(frozen=True) +class Frame: + sequence: int + stage: int + kind: int + flags: int + raw0: int + raw1: int + result: int + aux0: int + aux1: int + + +@dataclass(frozen=True) +class CleanupSnapshot: + rarch_main_returned: bool + d04_emitted: bool + phase: int + initialized_mask: int + cleaned_mask: int + cleanup_order_errors: int + cleanup_failure_count: int + rarch_main_result: int + + +@dataclass(frozen=True) +class TraceResult: + complete: bool + submit_result: int + sdl_result: int + rarch_main_result: int + cleaned_mask: int + visible_output_proven: bool = False + firmware_behavior_proven: bool = False + device_action_authorized: bool = False + + +def _u32(value: int, label: str) -> int: + if type(value) is not int or not 0 <= value <= UINT32_MAX: + raise CanaryProtocolError(f"{label} is not uint32") + return value + + +def _i32(value: int, label: str) -> int: + if type(value) is not int or not INT32_MIN <= value <= INT32_MAX: + raise CanaryProtocolError(f"{label} is not int32") + return value + + +def encode_frame(frame: Frame) -> bytes: + """Encode one exact frame; AV has exactly one possible terminal stage.""" + if type(frame) is not Frame: + raise CanaryProtocolError("frame type is invalid") + _u32(frame.sequence, "sequence") + if frame.sequence == 0 or type(frame.stage) is not int \ + or not 0 <= frame.stage <= D14 or frame.kind not in {KIND_RAW, KIND_PAIR}: + raise CanaryProtocolError("frame identity is invalid") + if type(frame.flags) is not int or frame.flags & ~ALLOWED_FLAGS: + raise CanaryProtocolError("frame flags are invalid") + if frame.stage == D14: + if frame.kind != KIND_PAIR or frame.flags != (RAW0_VALID | RAW1_VALID | TERMINAL): + raise CanaryProtocolError("D14 is not the exact terminal frame") + elif frame.flags & TERMINAL: + raise CanaryProtocolError("only D14 may be terminal") + for value, label in ((frame.raw0, "raw0"), (frame.raw1, "raw1"), + (frame.result, "result")): + _i32(value, label) + _u32(frame.aux0, "aux0") + _u32(frame.aux1, "aux1") + output = bytearray(FRAME_SIZE) + output[:8] = MAGIC + struct.pack_into(">HHIBBHiiiII", output, 8, VERSION, FRAME_SIZE, + frame.sequence, frame.stage, frame.kind, frame.flags, + frame.raw0, frame.raw1, frame.result, + frame.aux0, frame.aux1) + struct.pack_into(">I", output, 60, zlib.crc32(output[:60]) & UINT32_MAX) + return bytes(output) + + +def parse_frame(raw: bytes) -> Frame: + if type(raw) is not bytes or len(raw) != FRAME_SIZE or raw[:8] != MAGIC: + raise CanaryProtocolError("frame envelope is invalid") + if any(raw[40:60]): + raise CanaryProtocolError("reserved frame bytes are nonzero") + expected = zlib.crc32(raw[:60]) & UINT32_MAX + if struct.unpack_from(">I", raw, 60)[0] != expected: + raise CanaryProtocolError("frame CRC differs") + version, size, sequence, stage, kind, flags, raw0, raw1, result, aux0, aux1 = \ + struct.unpack_from(">HHIBBHiiiII", raw, 8) + if version != VERSION or size != FRAME_SIZE: + raise CanaryProtocolError("frame version or size differs") + frame = Frame(sequence, stage, kind, flags, raw0, raw1, result, aux0, aux1) + if encode_frame(frame) != raw: + raise CanaryProtocolError("frame is not canonical") + return frame + + +def build_cleanup_terminal(sequence: int, snapshot: CleanupSnapshot) -> bytes | None: + """Return D14 only when the modeled lifecycle is completely closed.""" + if type(snapshot) is not CleanupSnapshot: + raise CanaryProtocolError("cleanup snapshot type is invalid") + booleans = (snapshot.rarch_main_returned, snapshot.d04_emitted) + if any(type(value) is not bool for value in booleans): + raise CanaryProtocolError("cleanup booleans are invalid") + for value, label in ((snapshot.phase, "phase"), + (snapshot.initialized_mask, "initialized mask"), + (snapshot.cleaned_mask, "cleaned mask"), + (snapshot.cleanup_order_errors, "cleanup order errors"), + (snapshot.cleanup_failure_count, "cleanup failure count")): + _u32(value, label) + _i32(snapshot.rarch_main_result, "rarch_main result") + complete = snapshot.rarch_main_returned and snapshot.d04_emitted \ + and snapshot.phase == S15_COMPLETE and snapshot.initialized_mask == 0 \ + and snapshot.cleanup_order_errors == 0 \ + and snapshot.cleanup_failure_count == 0 + if not complete: + return None + return encode_frame(Frame(sequence, D14, KIND_PAIR, + RAW0_VALID | RAW1_VALID | TERMINAL, + snapshot.initialized_mask, snapshot.cleaned_mask, + snapshot.rarch_main_result, + snapshot.cleanup_order_errors, + snapshot.cleanup_failure_count)) + + +def validate_trace(raw_frames: tuple[bytes, ...]) -> TraceResult: + """Require one ordered D07/D04/D14 path and no bytes after terminal.""" + if type(raw_frames) is not tuple or not raw_frames: + raise CanaryProtocolError("trace is empty or not immutable") + frames = tuple(parse_frame(raw) for raw in raw_frames) + if any(right.sequence <= left.sequence for left, right in zip(frames, frames[1:])): + raise CanaryProtocolError("frame sequence is not strictly increasing") + terminal_indexes = [index for index, frame in enumerate(frames) + if frame.flags & TERMINAL] + if terminal_indexes != [len(frames) - 1]: + raise CanaryProtocolError("terminal is absent, duplicated or not final") + for stage in (D07, D04, D14): + if sum(frame.stage == stage for frame in frames) != 1: + raise CanaryProtocolError("required stage is absent or duplicated") + if sum(frame.stage == D12 for frame in frames) > 1: + raise CanaryProtocolError("D12 is duplicated") + positions = {frame.stage: index for index, frame in enumerate(frames) + if frame.stage in {D07, D04, D14}} + if not positions[D07] < positions[D04] < positions[D14]: + raise CanaryProtocolError("submit/D04/D14 order differs") + terminal = frames[-1] + if terminal.raw0 != 0 or terminal.aux0 != 0 or terminal.aux1 != 0: + raise CanaryProtocolError("D14 cleanup predicate differs") + submit = frames[positions[D07]] + sdl = frames[positions[D04]] + if any(frame.kind != KIND_RAW or frame.flags != RAW0_VALID + for frame in (submit, sdl)): + raise CanaryProtocolError("D07 or D04 semantics differ") + return TraceResult(True, submit.raw0, sdl.raw0, terminal.result, + terminal.raw1) diff --git a/tools/phase10dc_bigapp_gate_contract.py b/tools/phase10dc_bigapp_gate_contract.py new file mode 100644 index 0000000..fd6148d --- /dev/null +++ b/tools/phase10dc_bigapp_gate_contract.py @@ -0,0 +1,138 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Pure, inactive Phase-1.0DC BigApp comparison-gate contract.""" + +from __future__ import annotations + +from dataclasses import dataclass +from datetime import datetime, timezone +import re + + +PHASE = "PHASE_1_0DC_INACTIVE_BIGAPP_COMPARISON_GATE" +FIRMWARE = "9.60" +TITLE_ID = "PPSA01659" +PAYLOAD_NAME = "retroarch_ps5_launch_canary.elf" +PAYLOAD_SIZE = 1845240 +PAYLOAD_SHA256 = "8dadce9d9faaef21ea129a3d216c768eea9a3ca9bf8ecb8d852e376b58a9bf95" +PROTOCOL_MAGIC = "CHD10AV1" +PROTOCOL_TERMINAL = "D14" +MAX_WINDOW_SECONDS = 300 +SHA256 = re.compile(r"^[0-9a-f]{64}$") +RUN_ID = re.compile(r"^[A-Z0-9][A-Z0-9_-]{7,63}$") + + +class BigAppGateError(ValueError): + """Candidate data is incomplete, ambiguous, or unsafe.""" + + +@dataclass(frozen=True) +class BigAppGateRecord: + phase: str + active: bool + firmware: str | None + title_id: str | None + run_id: str | None + not_before: str | None + expires_at: str | None + launcher_name: str | None + launcher_size: int | None + launcher_sha256: str | None + payload_name: str | None + payload_size: int | None + payload_sha256: str | None + approval_sha256: str | None + result_magic: str | None + result_terminal: str | None + no_running_bigapp_attested: bool + kernel_ptrace_effects_accepted: bool + app_termination_authorized: bool + persistent_write_authorized: bool + system_remount_authorized: bool + installation_authorized: bool + autoload_authorized: bool + automatic_retry: bool + reconnect: bool + fallback_title: bool + bounded_parent_detach_proven: bool + bounded_child_cleanup_proven: bool + bounded_result_channel_proven: bool + + +def _utc(value: str | None) -> datetime: + if not isinstance(value, str) or not value.endswith("Z"): + raise BigAppGateError("timestamp must be exact UTC") + try: + result = datetime.fromisoformat(value[:-1] + "+00:00") + except ValueError as error: + raise BigAppGateError("timestamp is invalid") from error + if result.tzinfo != timezone.utc: + raise BigAppGateError("timestamp is not UTC") + return result + + +def validate_inactive(record: BigAppGateRecord) -> None: + """Require the tracked record to contain no live identity or authority.""" + if type(record) is not BigAppGateRecord or record.phase != PHASE or record.active: + raise BigAppGateError("tracked gate is not inert") + identities = ( + record.firmware, record.title_id, record.run_id, record.not_before, + record.expires_at, record.launcher_name, record.launcher_size, + record.launcher_sha256, record.payload_name, record.payload_size, + record.payload_sha256, record.approval_sha256, record.result_magic, + record.result_terminal, + ) + if any(value is not None for value in identities): + raise BigAppGateError("inactive gate contains an identity") + if any((record.no_running_bigapp_attested, + record.kernel_ptrace_effects_accepted, + record.app_termination_authorized, + record.persistent_write_authorized, + record.system_remount_authorized, + record.installation_authorized, record.autoload_authorized, + record.automatic_retry, record.reconnect, record.fallback_title, + record.bounded_parent_detach_proven, + record.bounded_child_cleanup_proven, + record.bounded_result_channel_proven)): + raise BigAppGateError("inactive gate contains authority or proof") + + +def validate_candidate(record: BigAppGateRecord) -> None: + """Validate hypothetical data; this function grants no authorization.""" + if type(record) is not BigAppGateRecord or record.phase != PHASE or not record.active: + raise BigAppGateError("candidate is not explicitly active") + if record.firmware != FIRMWARE or record.title_id != TITLE_ID: + raise BigAppGateError("firmware or fixed title mismatch") + if not isinstance(record.run_id, str) or not RUN_ID.fullmatch(record.run_id): + raise BigAppGateError("run identity is invalid") + start, end = _utc(record.not_before), _utc(record.expires_at) + if not 0 < (end - start).total_seconds() <= MAX_WINDOW_SECONDS: + raise BigAppGateError("activation window is invalid") + if record.launcher_name != "chimera_bigapp_canary_launcher.elf" or \ + not isinstance(record.launcher_size, int) or isinstance(record.launcher_size, bool) or \ + not 1 <= record.launcher_size <= 1048576 or \ + not isinstance(record.launcher_sha256, str) or not SHA256.fullmatch(record.launcher_sha256): + raise BigAppGateError("launcher identity is invalid") + if (record.payload_name, record.payload_size, record.payload_sha256) != \ + (PAYLOAD_NAME, PAYLOAD_SIZE, PAYLOAD_SHA256): + raise BigAppGateError("payload identity is not the unchanged CZ artifact") + if not isinstance(record.approval_sha256, str) or not SHA256.fullmatch(record.approval_sha256): + raise BigAppGateError("separate approval identity is invalid") + if (record.result_magic, record.result_terminal) != \ + (PROTOCOL_MAGIC, PROTOCOL_TERMINAL): + raise BigAppGateError("result protocol is invalid") + required = (record.no_running_bigapp_attested, + record.kernel_ptrace_effects_accepted, + record.bounded_parent_detach_proven, + record.bounded_child_cleanup_proven, + record.bounded_result_channel_proven) + if any(value is not True for value in required): + raise BigAppGateError("required attestation or bounded proof is absent") + forbidden = (record.app_termination_authorized, + record.persistent_write_authorized, + record.system_remount_authorized, + record.installation_authorized, record.autoload_authorized, + record.automatic_retry, record.reconnect, + record.fallback_title) + if any(value is not False for value in forbidden): + raise BigAppGateError("candidate permits a forbidden effect") diff --git a/tools/phase10df_title_observer_contract.py b/tools/phase10df_title_observer_contract.py new file mode 100644 index 0000000..289cba1 --- /dev/null +++ b/tools/phase10df_title_observer_contract.py @@ -0,0 +1,136 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Pure, inactive title-presence observer contract for Phase 1.0DF.""" + +from __future__ import annotations + +from dataclasses import dataclass +from enum import Enum +import re + + +PHASE = "PHASE_1_0DF_INACTIVE_TITLE_PRESENCE_OBSERVER" +FIRMWARE = "9.60" +TITLE_ID = "PPSA01659" +MAX_RESULT_BYTES = 4096 +SHA256 = re.compile(r"^[0-9a-f]{64}$") + + +class ObserverError(ValueError): + """An observer plan or result is ambiguous or unsafe.""" + + +class Outcome(Enum): + PRESENT = "PRESENT" + ABSENT = "ABSENT" + UNKNOWN = "UNKNOWN" + + +@dataclass(frozen=True) +class ObserverPlan: + phase: str + active: bool + firmware: str | None + title_id: str | None + method: str | None + query_contract_sha256: str | None + exact_literal_path: str | None + path_provenance_sha256: str | None + request_bytes_sha256: str | None + maximum_result_bytes: int | None + one_request: bool + read_only: bool + shell_present: bool + directory_enumeration: bool + title_launch: bool + app_termination: bool + device_write: bool + retry: bool + reconnect: bool + + +@dataclass(frozen=True) +class SyntheticResult: + firmware: str + title_id: str + method: str + request_bytes_sha256: str + complete: bool + result_bytes: int + explicit_present: bool + explicit_absent: bool + error_code: int | None + + +def validate_inactive(plan: ObserverPlan) -> None: + """Require the tracked plan to be empty and incapable of a request.""" + if type(plan) is not ObserverPlan or plan.phase != PHASE or plan.active: + raise ObserverError("tracked observer is not inactive") + optional = (plan.firmware, plan.title_id, plan.method, + plan.query_contract_sha256, plan.exact_literal_path, + plan.path_provenance_sha256, plan.request_bytes_sha256, + plan.maximum_result_bytes) + if any(value is not None for value in optional): + raise ObserverError("inactive observer contains request data") + if any((plan.one_request, plan.read_only, plan.shell_present, + plan.directory_enumeration, plan.title_launch, + plan.app_termination, plan.device_write, plan.retry, + plan.reconnect)): + raise ObserverError("inactive observer contains capability") + + +def validate_candidate(plan: ObserverPlan) -> None: + """Validate hypothetical data without creating or authorizing a request.""" + if type(plan) is not ObserverPlan or plan.phase != PHASE or not plan.active: + raise ObserverError("candidate is not active data") + if plan.firmware != FIRMWARE or plan.title_id != TITLE_ID: + raise ObserverError("firmware or title mismatch") + if plan.method not in {"SOURCE_BOUND_QUERY", "EXACT_PATH_METADATA"}: + raise ObserverError("observer method is not allowlisted") + if not isinstance(plan.query_contract_sha256, str) or \ + not SHA256.fullmatch(plan.query_contract_sha256): + raise ObserverError("query contract identity is absent") + if not isinstance(plan.request_bytes_sha256, str) or \ + not SHA256.fullmatch(plan.request_bytes_sha256): + raise ObserverError("request bytes are not bound") + if plan.maximum_result_bytes != MAX_RESULT_BYTES: + raise ObserverError("result bound is not exact") + if plan.method == "SOURCE_BOUND_QUERY": + if plan.exact_literal_path is not None or \ + plan.path_provenance_sha256 is not None: + raise ObserverError("query method must not carry a path") + else: + if not isinstance(plan.exact_literal_path, str) or \ + not plan.exact_literal_path.startswith("/") or \ + ".." in plan.exact_literal_path or \ + not isinstance(plan.path_provenance_sha256, str) or \ + not SHA256.fullmatch(plan.path_provenance_sha256): + raise ObserverError("exact path provenance is absent") + if plan.one_request is not True or plan.read_only is not True: + raise ObserverError("one read-only request is not exact") + if any((plan.shell_present, plan.directory_enumeration, + plan.title_launch, plan.app_termination, plan.device_write, + plan.retry, plan.reconnect)): + raise ObserverError("candidate contains a forbidden capability") + + +def classify_synthetic(plan: ObserverPlan, result: SyntheticResult) -> Outcome: + """Classify supplied bytes; errors and incomplete data remain UNKNOWN.""" + validate_candidate(plan) + if type(result) is not SyntheticResult or \ + (result.firmware, result.title_id, result.method, + result.request_bytes_sha256) != \ + (plan.firmware, plan.title_id, plan.method, + plan.request_bytes_sha256): + raise ObserverError("result binding mismatch") + if result.result_bytes < 0 or result.result_bytes > MAX_RESULT_BYTES: + raise ObserverError("result bound exceeded") + if result.explicit_present and result.explicit_absent: + raise ObserverError("contradictory result") + if not result.complete or result.error_code is not None: + return Outcome.UNKNOWN + if result.explicit_present: + return Outcome.PRESENT + if result.explicit_absent: + return Outcome.ABSENT + return Outcome.UNKNOWN diff --git a/tools/phase10dh_snapshot_query.py b/tools/phase10dh_snapshot_query.py new file mode 100644 index 0000000..0f95698 --- /dev/null +++ b/tools/phase10dh_snapshot_query.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Hash-bound, offline-only appinfo snapshot query for Phase 1.0DH.""" + +from __future__ import annotations + +from dataclasses import dataclass +from enum import Enum +import hashlib +from pathlib import Path +import re +import sqlite3 +from urllib.parse import quote + + +PHASE = "PHASE_1_0DH_HASH_BOUND_SNAPSHOT_QUERY" +TITLE_ID = "PPSA01659" +MAX_SNAPSHOT_BYTES = 64 * 1024 * 1024 +SHA256 = re.compile(r"^[0-9a-f]{64}$") + + +class SnapshotError(ValueError): + """The supplied snapshot or query contract is invalid.""" + + +class Outcome(Enum): + PRESENT = "PRESENT" + ABSENT = "ABSENT" + UNKNOWN = "UNKNOWN" + + +@dataclass(frozen=True) +class SnapshotBinding: + phase: str + title_id: str + size: int + sha256: str + + +def _digest(path: Path) -> tuple[int, str]: + total = 0 + digest = hashlib.sha256() + with path.open("rb") as stream: + while block := stream.read(64 * 1024): + total += len(block) + if total > MAX_SNAPSHOT_BYTES: + raise SnapshotError("snapshot exceeds size ceiling") + digest.update(block) + return total, digest.hexdigest() + + +def validate_binding(binding: SnapshotBinding) -> None: + if type(binding) is not SnapshotBinding or binding.phase != PHASE: + raise SnapshotError("phase binding mismatch") + if binding.title_id != TITLE_ID: + raise SnapshotError("title binding mismatch") + if not isinstance(binding.size, int) or isinstance(binding.size, bool) or \ + binding.size <= 0 or binding.size > MAX_SNAPSHOT_BYTES: + raise SnapshotError("snapshot size is invalid") + if not isinstance(binding.sha256, str) or not SHA256.fullmatch(binding.sha256): + raise SnapshotError("snapshot hash is invalid") + + +def query_snapshot(path: Path, binding: SnapshotBinding) -> Outcome: + """Query a byte-exact local snapshot without creating SQLite sidecars.""" + validate_binding(binding) + path = path.resolve(strict=True) + if not path.is_file() or _digest(path) != (binding.size, binding.sha256): + raise SnapshotError("snapshot identity mismatch") + if path.read_bytes()[:16] != b"SQLite format 3\x00": + raise SnapshotError("snapshot is not SQLite 3") + + uri = f"file:{quote(path.as_posix(), safe='/:')}?mode=ro&immutable=1" + try: + connection = sqlite3.connect(uri, uri=True) + try: + connection.execute("PRAGMA query_only = ON") + columns = { + row[1] for row in connection.execute( + "PRAGMA table_info(tbl_appinfo)") + } + if "titleId" not in columns: + return Outcome.UNKNOWN + rows = connection.execute( + "SELECT 1 FROM tbl_appinfo WHERE titleId = ? LIMIT 2", + (TITLE_ID,), + ).fetchall() + if len(rows) == 1: + return Outcome.PRESENT + if len(rows) == 0: + return Outcome.ABSENT + return Outcome.UNKNOWN + finally: + connection.close() + except sqlite3.Error: + return Outcome.UNKNOWN diff --git a/tools/phase10dm_snapshot_protocol.py b/tools/phase10dm_snapshot_protocol.py new file mode 100644 index 0000000..6a5b59f --- /dev/null +++ b/tools/phase10dm_snapshot_protocol.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Offline parser for the bounded Phase-1.0DM snapshot stream.""" +from __future__ import annotations +from dataclasses import dataclass +import hashlib,struct + +MAGIC=b"CHS10DM1";FRAME=struct.Struct("<8sIIiiQQQQq");MAX_BYTES=64*1024*1024 +BEGIN=1;END=2;ERROR=3 +class ProtocolError(ValueError):pass +@dataclass(frozen=True) +class SnapshotResult: + data:bytes + sha256:str + size:int + device:int + inode:int + mtime:int + +def _frame(data:bytes): + if len(data)!=64:raise ProtocolError("frame size") + values=FRAME.unpack(data) + if values[0]!=MAGIC or values[1]!=1:raise ProtocolError("frame identity") + return values + +def parse_stream(raw:bytes)->SnapshotResult: + if not isinstance(raw,bytes) or len(raw)<128:raise ProtocolError("stream truncated") + begin=_frame(raw[:64]);kind,status,saved,size,sent,dev,ino,mtime=begin[2:] + if kind==ERROR:raise ProtocolError(f"target error {status}") + if kind!=BEGIN or status!=0 or saved!=0 or sent!=0 or not 0=64: + self.begin=_frame(bytes(self.buffer[:64]));del self.buffer[:64];kind,status,saved,size,sent,*_=self.begin[2:] + if kind!=BEGIN or status or saved or sent or not 064:self.abort();raise ProtocolError("trailing bytes") + def finish(self)->ReceivedSnapshot: + if self.state!="TERMINAL" or len(self.buffer)!=64 or self.begin is None:self.abort();raise ProtocolError("stream incomplete") + end=_frame(bytes(self.buffer));kind,status,saved,size,sent,dev,ino,mtime=end[2:];_,_,_,bsize,_,bdev,bino,bmtime=self.begin[2:] + if kind!=END or status or saved or size!=bsize or sent!=bsize or (dev,ino,mtime)!=(bdev,bino,bmtime):self.abort();raise ProtocolError("terminal invalid") + self.stream.flush();os.fsync(self.stream.fileno());self.stream.close();self.state="SEALED" + with self.path.open("rb") as check: + if check.read(16)!=b"SQLite format 3\x00":raise ProtocolError("not sqlite") + return ReceivedSnapshot(self.path,self.written,self.digest.hexdigest(),dev,ino,mtime) + def abort(self): + if not self.stream.closed:self.stream.flush();self.stream.close() + self.state="ABORTED" diff --git a/tools/phase10do_one_shot_snapshot_runner.py b/tools/phase10do_one_shot_snapshot_runner.py new file mode 100644 index 0000000..968498e --- /dev/null +++ b/tools/phase10do_one_shot_snapshot_runner.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Manifest-gated one-shot raw-elfldr snapshot runner.""" +from __future__ import annotations +import hashlib, ipaddress, json, os, socket, time +from pathlib import Path +from phase10dn_snapshot_receiver import SnapshotReceiver + +ARTIFACT_SIZE=109896 +ARTIFACT_SHA256="147b5bede0f0b5b7d2be903bc72ff0d0541a2cdc28eae7d86b6bf95e1978ebdf" +PORT=9021 +CONNECT_TIMEOUT=3.0 +TOTAL_TIMEOUT=30.0 +MAX_WIRE=67108992 +FIELDS={"active","run_id","target","port","artifact_size","artifact_sha256","snapshot_path","receipt_path","not_before","not_after","one_connection","one_transfer","one_execution","result_receive","target_file_read","device_write","installation","autoload","retry","reconnect"} + +class RunnerError(RuntimeError): + pass + +def _load(path: Path) -> dict: + value=json.loads(path.read_text(encoding="utf-8")) + if not isinstance(value,dict): raise RunnerError("record is not an object") + return value + +def validate_records(activation: dict,approval: dict,now: float)->dict: + if set(activation)!=FIELDS or set(approval)!=FIELDS: raise RunnerError("record fields differ") + if activation!=approval: raise RunnerError("activation and approval differ") + if activation["active"] is not True: raise RunnerError("activation is inactive") + if not activation["not_before"]<=now<=activation["not_after"]: raise RunnerError("approval window inactive") + if activation["port"]!=PORT or activation["artifact_size"]!=ARTIFACT_SIZE or activation["artifact_sha256"]!=ARTIFACT_SHA256: raise RunnerError("artifact route mismatch") + if any(activation[k] is not True for k in ("one_connection","one_transfer","one_execution","result_receive","target_file_read")): raise RunnerError("required authority absent") + if any(activation[k] is not False for k in ("device_write","installation","autoload","retry","reconnect")): raise RunnerError("forbidden authority present") + ipaddress.IPv4Address(activation["target"]) + for key in ("snapshot_path","receipt_path"): + path=Path(activation[key]) + if not path.is_absolute() or path.exists(): raise RunnerError("exclusive absolute evidence path required") + return activation + +def _consume(plan: dict)->None: + path=Path(plan["receipt_path"]) + with path.open("x",encoding="utf-8",newline="\n") as out: + json.dump({"run_id":plan["run_id"],"artifact_sha256":ARTIFACT_SHA256,"consumed_before_socket":True},out,sort_keys=True) + out.write("\n");out.flush();os.fsync(out.fileno()) + +def run(artifact:Path,activation_path:Path,approval_path:Path,socket_factory=socket.socket,clock=time.monotonic,wall_clock=time.time): + plan=validate_records(_load(activation_path),_load(approval_path),wall_clock()) + raw=artifact.read_bytes() + if len(raw)!=ARTIFACT_SIZE or hashlib.sha256(raw).hexdigest()!=ARTIFACT_SHA256: raise RunnerError("artifact identity mismatch") + _consume(plan) + receiver=SnapshotReceiver(Path(plan["snapshot_path"]));sock=None;received=0;deadline=clock()+TOTAL_TIMEOUT + try: + sock=socket_factory(socket.AF_INET,socket.SOCK_STREAM);sock.settimeout(CONNECT_TIMEOUT);sock.connect((plan["target"],PORT));sock.sendall(raw);sock.shutdown(socket.SHUT_WR) + while True: + remain=deadline-clock() + if remain<=0: raise RunnerError("result deadline") + sock.settimeout(remain);chunk=sock.recv(min(65536,MAX_WIRE-received)) + if not chunk: break + received+=len(chunk) + if received>MAX_WIRE: raise RunnerError("wire bound") + receiver.feed(chunk) + return receiver.finish() + except Exception: + receiver.abort();raise + finally: + if sock is not None: sock.close() diff --git a/tools/phase10dq_inventory_protocol.py b/tools/phase10dq_inventory_protocol.py new file mode 100644 index 0000000..d1c9ec0 --- /dev/null +++ b/tools/phase10dq_inventory_protocol.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Strict parser for the bounded Phase-1.0DQ inventory stream.""" +from __future__ import annotations +from dataclasses import dataclass +import struct + +MAGIC=b"CHI10DQ1";FRAME=struct.Struct("<8sIIiiIIQ24s");ENTRY=struct.Struct("<256sIIIIQQq24s") +HEADER=1;TERMINAL=2;ERROR=3;MAX_ENTRIES=256;MAX_WIRE=64+MAX_ENTRIES*320+64 +class ProtocolError(ValueError):pass +@dataclass(frozen=True) +class InventoryEntry:name:str;file_type:int;mode:int;size:int;inode:int;mtime:int +def _frame(raw:bytes): + if len(raw)!=FRAME.size:raise ProtocolError("frame size") + value=FRAME.unpack(raw) + if value[0]!=MAGIC or value[1]!=1 or value[8]!=bytes(24):raise ProtocolError("frame identity") + return value +def parse_stream(raw:bytes)->tuple[InventoryEntry,...]: + if not isinstance(raw,bytes) or not 128<=len(raw)<=MAX_WIRE or (len(raw)-128)%ENTRY.size:raise ProtocolError("stream length") + first=_frame(raw[:64]);kind,status,saved,count,reserved,transferred=first[2:8] + if kind==ERROR:raise ProtocolError(f"target error {status}:{saved}") + if kind!=HEADER or status or saved or count or reserved or transferred:raise ProtocolError("header invalid") + entries=[] + for offset in range(64,len(raw)-64,ENTRY.size): + name_raw,name_length,file_type,mode,reserved,size,inode,mtime,padding=ENTRY.unpack(raw[offset:offset+ENTRY.size]) + if not 0dict[str,bytes]: + if not isinstance(raw,bytes) or not 128<=len(raw)<=MAX_WIRE:raise ProtocolError("stream length") + first=frame(raw[:64]);kind,status,saved,index,length,total=first[2:8] + if kind==4:raise ProtocolError(f"target error {status}:{saved}") + if (kind,status,saved,index,length,total)!=(1,0,0,4,0,0):raise ProtocolError("header invalid") + offset=64;files={};transferred=0 + for expected,name in enumerate(NAMES): + if offset+64>len(raw):raise ProtocolError("file frame truncated") + value=frame(raw[offset:offset+64]);offset+=64;kind,status,saved,index,length,total=value[2:8] + if kind==4:raise ProtocolError(f"target error {status}:{saved}") + if kind!=2 or status or saved or index!=expected or length>MAX_FILE or total!=transferred:raise ProtocolError("file frame invalid") + if offset+length>len(raw):raise ProtocolError("file truncated") + files[name]=raw[offset:offset+length];offset+=length;transferred+=length + if offset+64!=len(raw):raise ProtocolError("terminal position") + value=frame(raw[offset:]);kind,status,saved,index,length,total=value[2:8] + if (kind,status,saved,index,length,total)!=(3,0,0,4,0,transferred):raise ProtocolError("terminal invalid") + return files diff --git a/tools/phase10dt_metadata_runner.py b/tools/phase10dt_metadata_runner.py new file mode 100644 index 0000000..bad4e60 --- /dev/null +++ b/tools/phase10dt_metadata_runner.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +from __future__ import annotations +import base64,hashlib,ipaddress,json,os,socket,time +from pathlib import Path +from phase10ds_metadata_protocol import MAX_WIRE,parse_stream +ARTIFACT_SIZE=109928;ARTIFACT_SHA256="077307b98e44f566fa1db82b08cd5e71bd56bd9792826fc7254965fa768c0dc7";PORT=9021 +FIELDS={"active","run_id","target","port","artifact_size","artifact_sha256","output_path","receipt_path","not_before","not_after","one_connection","one_transfer","one_execution","result_receive","four_exact_metadata_reads","possible_atime_effect_acknowledged","app_pkg_read","backup_read","persistent_device_write","installation","autoload","retry","reconnect"} +class RunnerError(RuntimeError):pass +def load(path): + v=json.loads(path.read_text(encoding="utf-8")) + if not isinstance(v,dict):raise RunnerError("record object") + return v +def validate(a,b,now): + if set(a)!=FIELDS or set(b)!=FIELDS or a!=b:raise RunnerError("record mismatch") + if a["active"] is not True or not a["not_before"]<=now<=a["not_after"]:raise RunnerError("inactive") + if a["port"]!=PORT or a["artifact_size"]!=ARTIFACT_SIZE or a["artifact_sha256"]!=ARTIFACT_SHA256:raise RunnerError("identity") + if any(a[k] is not True for k in ("one_connection","one_transfer","one_execution","result_receive","four_exact_metadata_reads","possible_atime_effect_acknowledged")):raise RunnerError("authority") + if any(a[k] is not False for k in ("app_pkg_read","backup_read","persistent_device_write","installation","autoload","retry","reconnect")):raise RunnerError("forbidden") + ipaddress.IPv4Address(a["target"]) + for key in ("output_path","receipt_path"): + p=Path(a[key]) + if not p.is_absolute() or p.exists():raise RunnerError("exclusive path") + return a +def exclusive(path,value): + with path.open("x",encoding="utf-8",newline="\n") as f:json.dump(value,f,sort_keys=True);f.write("\n");f.flush();os.fsync(f.fileno()) +def run(artifact,activation,approval,socket_factory=socket.socket,clock=time.monotonic,wall=time.time): + p=validate(load(activation),load(approval),wall());raw=artifact.read_bytes() + if len(raw)!=ARTIFACT_SIZE or hashlib.sha256(raw).hexdigest()!=ARTIFACT_SHA256:raise RunnerError("artifact") + exclusive(Path(p["receipt_path"]),{"artifact_sha256":ARTIFACT_SHA256,"consumed_before_socket":True,"run_id":p["run_id"]});wire=bytearray();s=None;deadline=clock()+20 + try: + s=socket_factory(socket.AF_INET,socket.SOCK_STREAM);s.settimeout(3);s.connect((p["target"],PORT));s.sendall(raw);s.shutdown(socket.SHUT_WR) + while True: + left=deadline-clock() + if left<=0:raise RunnerError("deadline") + s.settimeout(left);chunk=s.recv(min(4096,MAX_WIRE-len(wire))) + if not chunk:break + wire.extend(chunk) + if len(wire)==MAX_WIRE:break + files=parse_stream(bytes(wire));exclusive(Path(p["output_path"]),{"files":{k:{"base64":base64.b64encode(v).decode(),"sha256":hashlib.sha256(v).hexdigest(),"size":len(v)} for k,v in files.items()},"run_id":p["run_id"]});return files + finally: + for i in range(len(wire)):wire[i]=0 + if s is not None:s.close() diff --git a/tools/phase10dv_package_stat_runner.py b/tools/phase10dv_package_stat_runner.py new file mode 100644 index 0000000..77535dd --- /dev/null +++ b/tools/phase10dv_package_stat_runner.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +import hashlib,ipaddress,json,os,socket,struct,time +from pathlib import Path +SIZE=109688;SHA="25b972ac202050ab5a9c1c89651b27e841cdf788d19c93bf75f051c9c8acd403";REC=struct.Struct("<8sIIiiIIQQQqq");PORT=9021 +FIELDS={"active","run_id","target","port","artifact_size","artifact_sha256","output_path","receipt_path","not_before","not_after","one_connection","one_transfer","one_execution","result_receive","two_exact_lstat","file_content_read","device_write","retry","reconnect"} +class Error(RuntimeError):pass +def load(p):return json.loads(p.read_text()) +def validate(a,b,now): + if set(a)!=FIELDS or set(b)!=FIELDS or a!=b or a["active"] is not True:raise Error("inactive/mismatch") + if not a["not_before"]<=now<=a["not_after"] or a["port"]!=PORT or a["artifact_size"]!=SIZE or a["artifact_sha256"]!=SHA:raise Error("window/identity") + if any(a[k] is not True for k in ("one_connection","one_transfer","one_execution","result_receive","two_exact_lstat")) or any(a[k] is not False for k in ("file_content_read","device_write","retry","reconnect")):raise Error("authority") + ipaddress.IPv4Address(a["target"]) + for k in ("output_path","receipt_path"): + p=Path(a[k]); + if not p.is_absolute() or p.exists():raise Error("exclusive path") + return a +def put(p,v): + with p.open("x",encoding="utf-8",newline="\n") as f:json.dump(v,f,sort_keys=True);f.write("\n");f.flush();os.fsync(f.fileno()) +def run(elf,activation,approval,factory=socket.socket,wall=time.time): + p=validate(load(activation),load(approval),wall());raw=elf.read_bytes() + if len(raw)!=SIZE or hashlib.sha256(raw).hexdigest()!=SHA:raise Error("artifact") + put(Path(p["receipt_path"]),{"consumed_before_socket":True,"run_id":p["run_id"],"artifact_sha256":SHA});s=None;wire=bytearray() + try: + s=factory(socket.AF_INET,socket.SOCK_STREAM);s.settimeout(3);s.connect((p["target"],PORT));s.sendall(raw);s.shutdown(socket.SHUT_WR);s.settimeout(10) + while len(wire)<144: + c=s.recv(144-len(wire)) + if not c:break + wire.extend(c) + if len(wire)!=144 or s.recv(1):raise Error("result length") + result=[] + for i in range(2): + v=REC.unpack(wire[i*72:(i+1)*72]) + if v[0]!=b"CHP10DU1" or v[1]!=1 or v[2]!=i or v[6]!=0:raise Error("record") + result.append({"index":i,"exists":bool(v[3]),"errno":v[4],"mode":v[5],"size":v[7],"device":v[8],"inode":v[9],"mtime":v[10],"ctime":v[11]}) + put(Path(p["output_path"]),{"paths":["/user/app/FAKE00000/app.pkg","/mnt/usb0/IV9999-FAKE00000_00-HOMEBREWLOADER01.pkg"],"records":result,"run_id":p["run_id"]});return result + finally: + if s is not None:s.close() diff --git a/tools/phase10dx_package_readback_runner.py b/tools/phase10dx_package_readback_runner.py new file mode 100644 index 0000000..3cb16bf --- /dev/null +++ b/tools/phase10dx_package_readback_runner.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +import hashlib,ipaddress,json,os,socket,struct,time +from pathlib import Path +ELF_SIZE=109904;ELF_SHA="b41763f9261b838be2f83b0ee1c7b9bb5e07ced856e5638e3357de37e4405394";PACKAGE_SIZE=18153472;PACKAGE_SHA="dbcdd4dbc6303fc7a94aa0e8bb3e2c7de1d8b5770c6a30cf1ce50bc6e373aa7e";FRAME=struct.Struct("<8sIIiiQQQQq");PORT=9021;MAX_WIRE=PACKAGE_SIZE+128 +FIELDS={"active","run_id","target","port","artifact_size","artifact_sha256","output_path","receipt_path","not_before","not_after","one_connection","one_transfer","one_execution","result_receive","exact_package_read","possible_atime_effect_acknowledged","device_write","usb_read","backup_read","installation","autoload","retry","reconnect"} +class Error(RuntimeError):pass +def load(p):return json.loads(p.read_text()) +def validate(a,b,now): + if set(a)!=FIELDS or set(b)!=FIELDS or a!=b or a["active"] is not True:raise Error("inactive/mismatch") + if not a["not_before"]<=now<=a["not_after"] or a["port"]!=PORT or a["artifact_size"]!=ELF_SIZE or a["artifact_sha256"]!=ELF_SHA:raise Error("window/identity") + if any(a[k] is not True for k in ("one_connection","one_transfer","one_execution","result_receive","exact_package_read","possible_atime_effect_acknowledged")) or any(a[k] is not False for k in ("device_write","usb_read","backup_read","installation","autoload","retry","reconnect")):raise Error("authority") + ipaddress.IPv4Address(a["target"]) + for k in ("output_path","receipt_path"): + p=Path(a[k]); + if not p.is_absolute() or p.exists():raise Error("exclusive path") + return a +def put(p,v): + with p.open("x",encoding="utf-8",newline="\n") as f:json.dump(v,f,sort_keys=True);f.write("\n");f.flush();os.fsync(f.fileno()) +def parse_frame(raw): + v=FRAME.unpack(raw) + if v[0]!=b"CHP10DW1" or v[1]!=1:raise Error("frame identity") + return v +def run(elf,activation,approval,factory=socket.socket,clock=time.monotonic,wall=time.time): + p=validate(load(activation),load(approval),wall());raw=elf.read_bytes() + if len(raw)!=ELF_SIZE or hashlib.sha256(raw).hexdigest()!=ELF_SHA:raise Error("artifact") + put(Path(p["receipt_path"]),{"consumed_before_socket":True,"run_id":p["run_id"],"artifact_sha256":ELF_SHA});s=None;out=None;header=bytearray();terminal=bytearray();written=0;digest=hashlib.sha256();deadline=clock()+45 + try: + out=Path(p["output_path"]).open("xb");s=factory(socket.AF_INET,socket.SOCK_STREAM);s.settimeout(3);s.connect((p["target"],PORT));s.sendall(raw);s.shutdown(socket.SHUT_WR) + while len(header)<64: + s.settimeout(max(.001,deadline-clock()));c=s.recv(64-len(header)) + if not c:raise Error("header eof") + header.extend(c) + h=parse_frame(bytes(header)); + if h[2]!=1 or h[3] or h[4] or h[5]!=PACKAGE_SIZE or h[6]:raise Error("header") + while written=deadline:raise Error("deadline") + s.settimeout(max(.001,deadline-clock()));c=s.recv(min(65536,PACKAGE_SIZE-written)) + if not c:raise Error("payload eof") + out.write(c);digest.update(c);written+=len(c) + while len(terminal)<64: + s.settimeout(max(.001,deadline-clock()));c=s.recv(64-len(terminal)) + if not c:raise Error("terminal eof") + terminal.extend(c) + if s.recv(1):raise Error("trailing data") + t=parse_frame(bytes(terminal)); + if t[2]!=2 or t[3] or t[4] or t[5]!=PACKAGE_SIZE or t[6]!=PACKAGE_SIZE or t[7:10]!=h[7:10]:raise Error("terminal") + out.flush();os.fsync(out.fileno());out.close();out=None + if digest.hexdigest()!=PACKAGE_SHA:raise Error("package digest mismatch") + return {"size":written,"sha256":digest.hexdigest()} + finally: + if out is not None:out.close() + if s is not None:s.close() diff --git a/tools/phase10t_shsrv_transcript.py b/tools/phase10t_shsrv_transcript.py new file mode 100644 index 0000000..3c01df6 --- /dev/null +++ b/tools/phase10t_shsrv_transcript.py @@ -0,0 +1,160 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Sanitize and classify an already-supplied shsrv transcript offline. + +This tool has no networking and never preserves serial or telemetry values. +It cannot establish an exact deployed binary identity. +""" + +from __future__ import annotations + +import argparse +import hashlib +import json +import re +import sys +from typing import Any + + +CURRENT_COMMAND_HASH = ( + "f41168292e205590bda1d243cdf727044e0af280a89fb0c070f4c5d6c92f2fd7") +V07_COMMAND_HASH = ( + "40313637116b532f3c7f9bebe2c23c0018fe7d4093840cf463a22ba0314ca021") + + +def command_hash(commands: list[str]) -> str: + normalized = "\n".join(sorted(set(commands))) + return hashlib.sha256(normalized.encode("utf-8")).hexdigest() + + +def parse_transcript( + transcript: str, expected_paths: set[str] | None = None, +) -> dict[str, Any]: + """Return a sanitized, deliberately non-exact identity record.""" + allowed_paths = expected_paths or set() + lines = transcript.splitlines() + compile_date = None + compile_time = None + firmware = None + commands: list[str] = [] + observations: dict[str, dict[str, Any]] = {} + serial_discarded = False + telemetry_discarded = False + greeting_seen = False + in_help = False + current_observation_path = None + + greeting_pattern = re.compile( + r"Welcome to shsrv\.elf running on pid \d+, " + r"compiled (.+?) at ([0-9:]+)") + command_pattern = re.compile(r"^\s{2}([A-Za-z0-9_]+)(?:\s+-.*)?$") + weak_sum_pattern = re.compile(r"^([0-9]{5})\s+(.+)$") + + for line in lines: + greeting = greeting_pattern.search(line) + if greeting: + greeting_seen = True + compile_date = greeting.group(1).strip() + compile_time = greeting.group(2).strip() + continue + stripped = line.strip() + if stripped.startswith("S/N:"): + serial_discarded = True + continue + if stripped.startswith(("SoC temp:", "CPU temp:", "CPU freq:")): + telemetry_discarded = True + continue + if stripped.startswith("Model:"): + continue + if stripped.startswith("S/W:"): + firmware = stripped.split(":", 1)[1].strip() + continue + if stripped == "Builtin commands:": + in_help = True + continue + if in_help: + command = command_pattern.match(line) + if command: + commands.append(command.group(1)) + continue + if stripped == "": + in_help = False + if stripped.startswith("filename:"): + path = stripped.split(":", 1)[1].strip() + if path in allowed_paths: + observations.setdefault(path, {})["metadata_seen"] = True + current_observation_path = path + else: + current_observation_path = None + continue + if ":" in stripped and current_observation_path is not None: + key, value = (part.strip() for part in stripped.split(":", 1)) + if key in {"size", "mtime", "ctime"} and value.isdigit(): + observations[current_observation_path][key] = int(value) + continue + weak_sum = weak_sum_pattern.match(stripped) + if weak_sum and weak_sum.group(2) in allowed_paths: + path = weak_sum.group(2) + observation = observations.setdefault(path, {}) + observation["weak_checksum"] = weak_sum.group(1) + observation["weak_checksum_algorithm"] = "BSD_ROTATE_16" + observation["cryptographic_checksum"] = False + + normalized_commands = sorted(set(commands)) + fingerprint = command_hash(normalized_commands) if normalized_commands else None + family = "UNRESOLVED" + if fingerprint == CURRENT_COMMAND_HASH: + family = "OFFICIAL_V019_SOURCE_FAMILY_CANDIDATE" + elif fingerprint == V07_COMMAND_HASH: + family = "OFFICIAL_V07_SOURCE_FAMILY_CANDIDATE" + + classification = "INVALID_OR_INCOMPLETE" + if greeting_seen: + classification = "COMPILE_METADATA_ONLY" + if greeting_seen and normalized_commands: + classification = "SOURCE_FAMILY_FINGERPRINT_ONLY" + if greeting_seen and observations: + classification = "WEAK_FILE_CORRELATION_ONLY" + + return { + "schema_version": 1, + "classification": classification, + "exact_identity": False, + "compile_metadata": { + "date": compile_date, + "time": compile_time, + "firmware": firmware, + }, + "sensitive_input": { + "serial_line_seen": serial_discarded, + "serial_value_retained": False, + "telemetry_line_seen": telemetry_discarded, + "telemetry_values_retained": False, + }, + "command_fingerprint": { + "count": len(normalized_commands), + "sha256": fingerprint, + "source_family_match": family, + "commands": normalized_commands, + "proves_exact_binary": False, + }, + "file_observations": [ + {"path": path, **value, "proves_exact_binary": False} + for path, value in sorted(observations.items()) + ], + } + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument( + "--expected-path", action="append", default=[], + help="Literal pre-approved path whose metadata may be retained") + args = parser.parse_args() + result = parse_transcript(sys.stdin.read(), set(args.expected_path)) + print(json.dumps(result, indent=2, sort_keys=True)) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/phase10v_shsrv_collector_model.py b/tools/phase10v_shsrv_collector_model.py new file mode 100644 index 0000000..97c07cd --- /dev/null +++ b/tools/phase10v_shsrv_collector_model.py @@ -0,0 +1,207 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Offline-only, one-shot shsrv transcript collection model. + +This module has no network transport. It accepts bounded bytes from stdin or +tests, removes Telnet control traffic, and delegates sanitized classification +to the Phase-1.0T parser. It does not prove memory erasure or device behavior. +""" + +from __future__ import annotations + +import argparse +import json +import posixpath +import re +import sys +from typing import Any + +from phase10t_shsrv_transcript import parse_transcript + + +MAX_RAW_BYTES = 65_536 +MAX_SANITIZED_BYTES = 65_536 +MAX_CHUNKS = 256 +EXPECTED_FIRMWARE = "9.60" +IAC = 0xFF +SE = 0xF0 +SB = 0xFA +WILL = 0xFB +WONT = 0xFC +DO = 0xFD +DONT = 0xFE + + +class CollectorError(RuntimeError): + """Fail-closed model error that never embeds input bytes.""" + + +class TelnetFilter: + """Incrementally remove Telnet negotiations and subnegotiations.""" + + def __init__(self) -> None: + self.state = "DATA" + + def feed(self, chunk: bytes) -> bytes: + output = bytearray() + for value in chunk: + if self.state == "DATA": + if value == IAC: + self.state = "IAC" + else: + output.append(value) + elif self.state == "IAC": + if value == IAC: + output.append(IAC) + self.state = "DATA" + elif value in {WILL, WONT, DO, DONT}: + self.state = "NEGOTIATION_OPTION" + elif value == SB: + self.state = "SUBNEGOTIATION" + else: + self.state = "DATA" + elif self.state == "NEGOTIATION_OPTION": + self.state = "DATA" + elif self.state == "SUBNEGOTIATION": + if value == IAC: + self.state = "SUBNEGOTIATION_IAC" + elif self.state == "SUBNEGOTIATION_IAC": + if value == SE: + self.state = "DATA" + elif value == IAC: + self.state = "SUBNEGOTIATION" + else: + self.state = "SUBNEGOTIATION" + return bytes(output) + + def is_complete(self) -> bool: + return self.state == "DATA" + + +class OfflineCollector: + """Bounded one-shot state model with no connection capability.""" + + def __init__(self) -> None: + self.state = "READY" + self.raw_bytes_received = 0 + self.chunk_count = 0 + self._filter = TelnetFilter() + self._sanitized = bytearray() + + def _discard_buffer(self) -> None: + for index in range(len(self._sanitized)): + self._sanitized[index] = 0 + self._sanitized.clear() + + def _invalidate(self, message: str) -> None: + self._discard_buffer() + self.state = "INVALID" + raise CollectorError(message) + + def feed(self, chunk: bytes) -> None: + if self.state not in {"READY", "RECEIVING"}: + raise CollectorError("collector is not accepting input") + if not isinstance(chunk, bytes): + self._invalidate("input must be bytes") + if not chunk: + return + self.chunk_count += 1 + self.raw_bytes_received += len(chunk) + if self.chunk_count > MAX_CHUNKS: + self._invalidate("chunk limit exceeded") + if self.raw_bytes_received > MAX_RAW_BYTES: + self._invalidate("byte limit exceeded") + filtered = self._filter.feed(chunk) + if len(self._sanitized) + len(filtered) > MAX_SANITIZED_BYTES: + self._invalidate("sanitized byte limit exceeded") + self._sanitized.extend(filtered) + self.state = "RECEIVING" + + def abort(self) -> None: + if self.state in {"SEALED", "INVALID"}: + raise CollectorError("collector can no longer abort") + self._discard_buffer() + self.state = "ABORTED" + + @staticmethod + def _validate_expected_paths(expected_paths: set[str]) -> None: + path_pattern = re.compile(r"^/[A-Za-z0-9._/-]{1,511}$") + for path in expected_paths: + if not isinstance(path, str) or not path_pattern.fullmatch(path): + raise CollectorError("expected path is not a safe absolute path") + if posixpath.normpath(path) != path or "//" in path: + raise CollectorError("expected path is not normalized") + + @staticmethod + def _validate_metadata(result: dict[str, Any]) -> None: + metadata = result["compile_metadata"] + date = metadata["date"] + time = metadata["time"] + firmware = metadata["firmware"] + if date is not None and not re.fullmatch( + r"[A-Z][a-z]{2} [ 0-3][0-9] [0-9]{4}", date): + raise CollectorError("compile date format is invalid") + if time is not None and not re.fullmatch( + r"(?:[01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]", time): + raise CollectorError("compile time format is invalid") + if firmware is not None and firmware != EXPECTED_FIRMWARE: + raise CollectorError("firmware metadata does not match the gate") + + def finalize(self, expected_paths: set[str] | None = None) -> dict[str, Any]: + if self.state not in {"READY", "RECEIVING"}: + raise CollectorError("collector cannot be finalized") + if not self._filter.is_complete(): + self._invalidate("incomplete Telnet control sequence") + try: + transcript = bytes(self._sanitized).decode("utf-8", errors="strict") + except UnicodeDecodeError: + self._invalidate("transcript is not valid UTF-8") + allowed_paths = expected_paths or set() + try: + self._validate_expected_paths(allowed_paths) + result = parse_transcript(transcript, allowed_paths) + self._validate_metadata(result) + except (ValueError, OverflowError, CollectorError): + self._invalidate("transcript validation failed") + self._discard_buffer() + self.state = "SEALED" + result["collector_model"] = { + "offline_only": True, + "one_shot": True, + "network_transport_present": False, + "raw_bytes_received": self.raw_bytes_received, + "chunk_count": self.chunk_count, + "raw_transcript_persisted": False, + "logical_buffer_discard_performed": True, + "physical_memory_erasure_proven": False, + "device_behavior_proven": False, + } + return result + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument( + "--offline-transcript", action="store_true", required=True, + help="Confirm that stdin is an already-supplied offline transcript") + parser.add_argument( + "--expected-path", action="append", default=[], + help="Literal pre-approved path whose metadata may be retained") + args = parser.parse_args() + collector = OfflineCollector() + try: + while True: + chunk = sys.stdin.buffer.read(4096) + if not chunk: + break + collector.feed(chunk) + result = collector.finalize(set(args.expected_path)) + except CollectorError as error: + print(f"ERROR: {error}", file=sys.stderr) + return 1 + print(json.dumps(result, indent=2, sort_keys=True)) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/phase10w_shsrv_client_policy.py b/tools/phase10w_shsrv_client_policy.py new file mode 100644 index 0000000..0cd7190 --- /dev/null +++ b/tools/phase10w_shsrv_client_policy.py @@ -0,0 +1,180 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Offline policy model for a future one-shot shsrv client. + +There is intentionally no transport, CLI, socket, DNS lookup, file output or +clock acquisition. Tests supply synthetic records and an explicit host time. +""" + +from __future__ import annotations + +from dataclasses import dataclass +from datetime import datetime, timedelta +import re +from typing import Any + +from phase10v_shsrv_collector_model import ( + CollectorError as CollectorModelError, + OfflineCollector, +) + + +COLLECTOR_SHA256 = \ + "f8a306dafee5d135919bec5afda789dd741e57f39803b7683fb8747c186db25c" +SOURCE_BOUND_PORT = 2323 +MAX_DEADLINE_SECONDS = 10 +MAX_APPROVAL_LIFETIME = timedelta(minutes=15) +WINDOW_COMMANDS = { + "T2_GREETING_AND_HELP": ("help",), + "T3_ONE_EXACT_PATH": ("stat", "sum"), +} +ACTIVATION_FIELDS = { + "active", "policy_sha256", "collector_sha256", "run_id", + "target_address", "target_port", "window", "exact_literal_path", + "commands", "deadline_seconds", "expires_at", +} +REQUIRED_TRUE_FIELDS = { + "ps5_connection_authorized", "device_request_authorized", + "result_receive_authorized", "spawned_shell_effects_accepted", + "automatic_serial_query_accepted", "automatic_telemetry_query_accepted", + "sanitized_output_only_accepted", + "physical_memory_erasure_unproven_accepted", +} +REQUIRED_FALSE_FIELDS = { + "target_build_authorized", "device_transfer_authorized", + "device_execution_authorized", "installation_authorized", + "autoload_authorized", "device_write_authorized", "automatic_retry", + "reconnect_authorized", "resume_authorized", "fallback_authorized", +} +APPROVAL_FIELDS = ACTIVATION_FIELDS | REQUIRED_TRUE_FIELDS | \ + REQUIRED_FALSE_FIELDS | {"attested", "listener_already_running_attested"} + + +class PolicyError(RuntimeError): + """Fail-closed policy error without record values.""" + + +@dataclass(frozen=True) +class SessionPlan: + """Immutable plan only; it has no method capable of network I/O.""" + + run_id: str + target_address: str + target_port: int + window: str + exact_literal_path: str | None + commands: tuple[str, ...] + deadline_seconds: int + expires_at: datetime + + +def inactive_record_is_inert(record: dict[str, Any]) -> bool: + return record == { + "active": False, + "policy_sha256": None, + "collector_sha256": None, + "run_id": None, + "target_address": None, + "target_port": None, + "window": None, + "exact_literal_path": None, + "commands": [], + "deadline_seconds": None, + "expires_at": None, + } + + +def _parse_expiry(value: Any, now: datetime) -> datetime: + if not isinstance(value, str): + raise PolicyError("expiry is missing") + try: + expiry = datetime.fromisoformat(value.replace("Z", "+00:00")) + except ValueError as error: + raise PolicyError("expiry format is invalid") from error + if now.tzinfo is None or expiry.tzinfo is None: + raise PolicyError("expiry must be timezone aware") + if not now < expiry <= now + MAX_APPROVAL_LIFETIME: + raise PolicyError("approval is expired or too long") + return expiry + + +def _validate_target(value: Any) -> str: + if not isinstance(value, str) or not re.fullmatch( + r"[A-Za-z0-9](?:[A-Za-z0-9.-]{0,251}[A-Za-z0-9])?", value): + raise PolicyError("target syntax is invalid") + if ".." in value: + raise PolicyError("target syntax is invalid") + return value + + +def _validate_authority(record: dict[str, Any]) -> None: + if not all(record.get(field) is True for field in REQUIRED_TRUE_FIELDS): + raise PolicyError("required authority or effect acceptance is missing") + if not all(record.get(field) is False for field in REQUIRED_FALSE_FIELDS): + raise PolicyError("forbidden authority is active") + + +def build_session_plan( + activation: dict[str, Any], approval: dict[str, Any], now: datetime, +) -> SessionPlan: + """Validate dual synthetic records and return an immutable offline plan.""" + if set(activation) != ACTIVATION_FIELDS or set(approval) != APPROVAL_FIELDS: + raise PolicyError("record shape is not exact") + if activation.get("active") is not True or approval.get("active") is not True: + raise PolicyError("activation is inactive") + if approval.get("attested") is not True: + raise PolicyError("operator attestation is missing") + if approval.get("listener_already_running_attested") is not True: + raise PolicyError("listener attestation is missing") + _validate_authority(approval) + matching_fields = ( + "policy_sha256", "collector_sha256", "run_id", "target_address", "target_port", + "window", "exact_literal_path", "commands", "deadline_seconds", + "expires_at", + ) + if any(activation.get(field) != approval.get(field) + for field in matching_fields): + raise PolicyError("activation and approval do not match") + if activation.get("collector_sha256") != COLLECTOR_SHA256: + raise PolicyError("collector identity mismatch") + if not isinstance(activation.get("policy_sha256"), str) or \ + not re.fullmatch(r"[0-9a-f]{64}", activation["policy_sha256"]): + raise PolicyError("policy identity syntax is invalid") + run_id = activation.get("run_id") + if not isinstance(run_id, str) or not re.fullmatch( + r"[A-Za-z0-9_-]{8,64}", run_id): + raise PolicyError("run identifier is invalid") + target = _validate_target(activation.get("target_address")) + if activation.get("target_port") != SOURCE_BOUND_PORT: + raise PolicyError("target port mismatch") + window = activation.get("window") + if window not in WINDOW_COMMANDS: + raise PolicyError("window is not allowlisted") + commands = activation.get("commands") + if commands != list(WINDOW_COMMANDS[window]): + raise PolicyError("command sequence mismatch") + path = activation.get("exact_literal_path") + if window == "T2_GREETING_AND_HELP" and path is not None: + raise PolicyError("help window cannot include a path") + if window == "T3_ONE_EXACT_PATH": + if not isinstance(path, str): + raise PolicyError("exact path is missing") + try: + OfflineCollector._validate_expected_paths({path}) + except CollectorModelError as error: + raise PolicyError("exact path is invalid") from error + deadline = activation.get("deadline_seconds") + if not isinstance(deadline, int) or isinstance(deadline, bool) or \ + not 1 <= deadline <= MAX_DEADLINE_SECONDS: + raise PolicyError("deadline is invalid") + expiry = _parse_expiry(activation.get("expires_at"), now) + return SessionPlan( + run_id=run_id, + target_address=target, + target_port=SOURCE_BOUND_PORT, + window=window, + exact_literal_path=path, + commands=WINDOW_COMMANDS[window], + deadline_seconds=deadline, + expires_at=expiry, + ) diff --git a/tools/phase10x_inactive_transport.py b/tools/phase10x_inactive_transport.py new file mode 100644 index 0000000..c312795 --- /dev/null +++ b/tools/phase10x_inactive_transport.py @@ -0,0 +1,229 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Inactive Phase-1.0X transport orchestration with injected adapters only. + +This module has no socket, DNS, CLI, target address, byte-command formatter or +live prompt detector. It implements local exclusive evidence and orchestrates +synthetic adapter boundaries under an injected monotonic clock. +""" + +from __future__ import annotations + +from dataclasses import dataclass +import hashlib +import json +import os +from pathlib import Path +from typing import Any, Protocol + +from phase10v_shsrv_collector_model import CollectorError, OfflineCollector +from phase10w_shsrv_client_policy import ( + COLLECTOR_SHA256, + SessionPlan, +) + + +POLICY_SHA256 = \ + "747d23c88f2722e8e8846599c3ac1dae3826eb3fca881caaad36b251f30f3592" +MAX_BOUNDARY_CHUNKS = 64 + + +class SessionFailure(RuntimeError): + """Generic failure that never embeds adapter data or target values.""" + + +class EvidenceFailure(RuntimeError): + """Generic exclusive-evidence failure.""" + + +class MonotonicClock(Protocol): + def monotonic(self) -> float: ... + + +class InjectedSessionAdapter(Protocol): + def open_once(self, plan: SessionPlan, remaining_seconds: float) -> None: ... + def receive_boundary( + self, boundary: str, remaining_seconds: float, + ) -> list[bytes]: ... + def send_command_token( + self, command: str, exact_path: str | None, remaining_seconds: float, + ) -> None: ... + def close_once(self) -> None: ... + + +@dataclass(frozen=True) +class EvidenceRecord: + path: Path + size: int + sha256: str + + +@dataclass(frozen=True) +class SessionOutcome: + receipt: EvidenceRecord + output: EvidenceRecord + classification: str + exact_identity: bool + + +class ExclusiveEvidenceStore: + """Write local JSON once with O_EXCL; never overwrite or clean up.""" + + def __init__(self, root: Path) -> None: + self.root = root + + @staticmethod + def _encode(record: dict[str, Any]) -> bytes: + return (json.dumps( + record, sort_keys=True, separators=(",", ":"), ensure_ascii=True, + ) + "\n").encode("ascii") + + def _create(self, filename: str, record: dict[str, Any]) -> EvidenceRecord: + try: + self.root.mkdir(parents=True, exist_ok=True) + path = self.root / filename + payload = self._encode(record) + except (OSError, TypeError, ValueError) as error: + raise EvidenceFailure("exclusive evidence preparation failed") from error + flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL + if hasattr(os, "O_BINARY"): + flags |= os.O_BINARY + descriptor = -1 + close_failure: OSError | None = None + try: + descriptor = os.open(path, flags, 0o600) + offset = 0 + while offset < len(payload): + written = os.write(descriptor, payload[offset:]) + if written <= 0: + raise OSError("short local evidence write") + offset += written + os.fsync(descriptor) + except OSError as error: + raise EvidenceFailure("exclusive evidence creation failed") from error + finally: + if descriptor >= 0: + try: + os.close(descriptor) + except OSError as error: + close_failure = error + if close_failure is not None: + raise EvidenceFailure("exclusive evidence close failed") from close_failure + try: + reopened = path.read_bytes() + except OSError as error: + raise EvidenceFailure("exclusive evidence reopen failed") from error + if reopened != payload: + raise EvidenceFailure("exclusive evidence reopen mismatch") + return EvidenceRecord( + path=path, + size=len(reopened), + sha256=hashlib.sha256(reopened).hexdigest(), + ) + + def create_consumed_receipt( + self, plan: SessionPlan, monotonic_value: float, + ) -> EvidenceRecord: + return self._create(f"{plan.run_id}.consumed.json", { + "schema_version": 1, + "status": "CONSUMED_BEFORE_ADAPTER_OPEN", + "run_id": plan.run_id, + "policy_sha256": POLICY_SHA256, + "collector_sha256": COLLECTOR_SHA256, + "window": plan.window, + "deadline_seconds": plan.deadline_seconds, + "created_monotonic": monotonic_value, + "target_retained": False, + "retry_allowed": False, + }) + + def create_sanitized_output( + self, plan: SessionPlan, receipt: EvidenceRecord, + sanitized: dict[str, Any], + ) -> EvidenceRecord: + return self._create(f"{plan.run_id}.sanitized.json", { + "schema_version": 1, + "status": "SANITIZED_OUTPUT_COMPLETE", + "run_id": plan.run_id, + "receipt_sha256": receipt.sha256, + "raw_transcript_persisted": False, + "result": sanitized, + }) + + +def _remaining(clock: MonotonicClock, deadline: float) -> float: + remaining = deadline - clock.monotonic() + if remaining <= 0: + raise SessionFailure("session deadline reached") + return remaining + + +def _feed_boundary( + collector: OfflineCollector, chunks: list[bytes], +) -> None: + if not isinstance(chunks, list) or len(chunks) > MAX_BOUNDARY_CHUNKS: + raise SessionFailure("adapter boundary is invalid") + try: + for chunk in chunks: + collector.feed(chunk) + except CollectorError as error: + raise SessionFailure("collector rejected adapter input") from error + + +def run_injected_session( + plan: SessionPlan, + adapter: InjectedSessionAdapter, + clock: MonotonicClock, + evidence: ExclusiveEvidenceStore, +) -> SessionOutcome: + """Run one injected session; never retries and never performs live I/O.""" + start = clock.monotonic() + deadline = start + plan.deadline_seconds + receipt = evidence.create_consumed_receipt(plan, start) + collector = OfflineCollector() + open_attempted = False + sanitized: dict[str, Any] | None = None + primary_failure: SessionFailure | None = None + try: + open_attempted = True + adapter.open_once(plan, _remaining(clock, deadline)) + chunks = adapter.receive_boundary( + "INITIAL_PROMPT", _remaining(clock, deadline)) + _feed_boundary(collector, chunks) + for command in plan.commands: + adapter.send_command_token( + command, plan.exact_literal_path, _remaining(clock, deadline)) + chunks = adapter.receive_boundary( + f"AFTER_{command.upper()}", _remaining(clock, deadline)) + _feed_boundary(collector, chunks) + _remaining(clock, deadline) + expected_paths = ( + {plan.exact_literal_path} + if plan.exact_literal_path is not None else set() + ) + sanitized = collector.finalize(expected_paths) + except Exception: # noqa: BLE001 - injected adapter boundary + # Adapter exceptions are deliberately normalized; raw messages never + # become evidence or user output. + primary_failure = SessionFailure("injected session failed") + finally: + if open_attempted: + try: + adapter.close_once() + except Exception: # noqa: BLE001 - injected adapter + if primary_failure is None: + primary_failure = SessionFailure("injected session close failed") + if primary_failure is not None: + raise primary_failure from None + if sanitized is None: + raise SessionFailure("sanitized result is missing") + try: + output = evidence.create_sanitized_output(plan, receipt, sanitized) + except EvidenceFailure as error: + raise SessionFailure("sanitized output creation failed") from error + return SessionOutcome( + receipt=receipt, + output=output, + classification=str(sanitized["classification"]), + exact_identity=bool(sanitized["exact_identity"]), + ) diff --git a/tools/phase10y_shsrv_framing_model.py b/tools/phase10y_shsrv_framing_model.py new file mode 100644 index 0000000..d2b8342 --- /dev/null +++ b/tools/phase10y_shsrv_framing_model.py @@ -0,0 +1,204 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Offline source-family model for shsrv framing; never opens a transport.""" + +from __future__ import annotations + +from dataclasses import dataclass + + +LEGACY_RAW = "LEGACY_RAW_V07_V08" +LIBTELNET_NVT = "LIBTELNET_NVT_V09_V019" +SOURCE_FAMILIES = {LEGACY_RAW, LIBTELNET_NVT} +MAX_MODEL_BYTES = 65_536 +MAX_MODEL_CHUNKS = 256 + +IAC = 0xFF +SE = 0xF0 +SB = 0xFA +WILL = 0xFB +WONT = 0xFC +DO = 0xFD +DONT = 0xFE + + +class FramingError(RuntimeError): + """Fail-closed model error without input bytes.""" + + +@dataclass(frozen=True) +class DecodedFraming: + application: bytes + negotiation_replies: tuple[bytes, ...] + iac_commands: tuple[int, ...] + source_family: str + source_model_only: bool = True + device_behavior_proven: bool = False + + +@dataclass(frozen=True) +class PromptAssessment: + candidate_offsets: tuple[int, ...] + ends_with_candidate: bool + classification: str + exact_completion_proven: bool = False + + +def initial_server_bytes(family: str) -> bytes: + """Both audited families emit no proactive Telnet negotiation.""" + _require_family(family) + return b"" + + +def encode_server_text(family: str, application: bytes) -> bytes: + """Model the source-family transformation from stdout to peer bytes.""" + _require_family(family) + if not isinstance(application, bytes): + raise FramingError("application text must be bytes") + if len(application) > MAX_MODEL_BYTES: + raise FramingError("application text exceeds model limit") + if family == LEGACY_RAW: + return application + output = bytearray() + for value in application: + if value == IAC: + output.extend((IAC, IAC)) + elif value == 0x0D: + output.extend((0x0D, 0x00)) + elif value == 0x0A: + output.extend((0x0D, 0x0A)) + else: + output.append(value) + return bytes(output) + + +class ClientWireDecoder: + """Bounded incremental model of bytes received by the shell.""" + + def __init__(self, family: str) -> None: + _require_family(family) + self.family = family + self.state = "DATA" + self.negotiation_command: int | None = None + self.raw_bytes = 0 + self.chunks = 0 + self._application = bytearray() + self._replies: list[bytes] = [] + self._iac_commands: list[int] = [] + self._sealed = False + + def _append(self, value: int) -> None: + self._application.append(value) + if len(self._application) > MAX_MODEL_BYTES: + raise FramingError("decoded application exceeds model limit") + + def feed(self, chunk: bytes) -> None: + if self._sealed: + raise FramingError("model is sealed") + if not isinstance(chunk, bytes): + raise FramingError("wire chunk must be bytes") + if not chunk: + return + self.chunks += 1 + self.raw_bytes += len(chunk) + if self.chunks > MAX_MODEL_CHUNKS: + raise FramingError("wire chunk limit exceeded") + if self.raw_bytes > MAX_MODEL_BYTES: + raise FramingError("wire byte limit exceeded") + if self.family == LEGACY_RAW: + self._application.extend(chunk) + return + for value in chunk: + self._feed_libtelnet(value) + + def _feed_libtelnet(self, value: int) -> None: + if self.state == "DATA": + if value == IAC: + self.state = "IAC" + elif value == 0x0D: + self.state = "EOL" + else: + self._append(value) + elif self.state == "EOL": + if value == 0x0A: + self._append(0x0A) + else: + self._append(0x0D) + if value != 0x00: + self._append(value) + self.state = "DATA" + elif self.state == "IAC": + if value == IAC: + self._append(IAC) + self.state = "DATA" + elif value in {WILL, WONT, DO, DONT}: + self.negotiation_command = value + self.state = "NEGOTIATION" + elif value == SB: + self.state = "SUBNEGOTIATION_OPTION" + else: + self._iac_commands.append(value) + self.state = "DATA" + elif self.state == "NEGOTIATION": + command = self.negotiation_command + if command == WILL: + self._replies.append(bytes((IAC, DONT, value))) + elif command == DO: + self._replies.append(bytes((IAC, WONT, value))) + self.negotiation_command = None + self.state = "DATA" + elif self.state == "SUBNEGOTIATION_OPTION": + self.state = "SUBNEGOTIATION" + elif self.state == "SUBNEGOTIATION": + if value == IAC: + self.state = "SUBNEGOTIATION_IAC" + elif self.state == "SUBNEGOTIATION_IAC": + if value == SE: + self.state = "DATA" + elif value == IAC: + self.state = "SUBNEGOTIATION" + else: + self.state = "IAC" + self._feed_libtelnet(value) + else: + raise FramingError("unknown model state") + + def finalize(self) -> DecodedFraming: + if self._sealed: + raise FramingError("model is sealed") + self._sealed = True + if self.family == LIBTELNET_NVT and self.state != "DATA": + raise FramingError("incomplete Telnet or NVT sequence") + return DecodedFraming( + application=bytes(self._application), + negotiation_replies=tuple(self._replies), + iac_commands=tuple(self._iac_commands), + source_family=self.family, + ) + + +def assess_prompt_candidates(application: bytes) -> PromptAssessment: + """Find source-shaped prompt suffixes without promoting them to proof.""" + if not isinstance(application, bytes): + raise FramingError("application text must be bytes") + candidates: list[int] = [] + start = 0 + while True: + offset = application.find(b"$ ", start) + if offset < 0: + break + candidates.append(offset) + start = offset + 2 + ends = application.endswith(b"$ ") + if not ends: + classification = "NO_TERMINAL_PROMPT_CANDIDATE" + elif len(candidates) == 1: + classification = "SOURCE_SHAPE_CANDIDATE_ONLY" + else: + classification = "AMBIGUOUS_PROMPT_CANDIDATES" + return PromptAssessment(tuple(candidates), ends, classification) + + +def _require_family(family: str) -> None: + if family not in SOURCE_FAMILIES: + raise FramingError("unknown source family") diff --git a/tools/phase10z_passive_batch_contract.py b/tools/phase10z_passive_batch_contract.py new file mode 100644 index 0000000..3f05ca7 --- /dev/null +++ b/tools/phase10z_passive_batch_contract.py @@ -0,0 +1,240 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Offline-only passive LF-batch contract for synthetic shsrv inputs. + +The module has no transport, CLI, address, clock, file output or prompt +detector. It formats one bounded batch from an already validated Phase-1.0W +plan and seals supplied bytes only when the caller supplies a synthetic hard +deadline event. +""" + +from __future__ import annotations + +from dataclasses import dataclass +from typing import Any + +from phase10v_shsrv_collector_model import CollectorError, OfflineCollector +from phase10w_shsrv_client_policy import SessionPlan, WINDOW_COMMANDS + + +IAC = 0xFF +MAX_LITERAL_PATH_BYTES = 512 +MAX_BATCH_BYTES = 1035 +KNOWN_HELP_FAMILIES = { + "OFFICIAL_V07_SOURCE_FAMILY_CANDIDATE", + "OFFICIAL_V019_SOURCE_FAMILY_CANDIDATE", +} + + +class PassiveContractError(RuntimeError): + """Fail-closed contract error that never embeds supplied bytes or paths.""" + + +@dataclass(frozen=True) +class PassiveBatch: + """Target-free immutable outbound bytes for one synthetic session.""" + + window: str + payload: bytes + expected_paths: tuple[str, ...] + command_count: int + deadline_seconds: int + outbound_batches: int = 1 + lf_only: bool = True + telnet_negotiation: bool = False + retry_allowed: bool = False + reconnect_allowed: bool = False + resume_allowed: bool = False + + +def _validate_payload(payload: bytes) -> None: + if not payload or len(payload) > MAX_BATCH_BYTES: + raise PassiveContractError("batch length is invalid") + if any(value in payload for value in (0x00, 0x0D, IAC)): + raise PassiveContractError("batch contains a forbidden byte") + if any(value > 0x7F for value in payload): + raise PassiveContractError("batch is not ASCII") + if not payload.endswith(b"\n"): + raise PassiveContractError("batch is not LF terminated") + + +def _validate_batch(batch: PassiveBatch) -> None: + _validate_payload(batch.payload) + if batch.outbound_batches != 1 or batch.lf_only is not True or \ + batch.telnet_negotiation is not False or \ + batch.retry_allowed is not False or \ + batch.reconnect_allowed is not False or batch.resume_allowed is not False: + raise PassiveContractError("batch safety fields are invalid") + if not isinstance(batch.deadline_seconds, int) or isinstance( + batch.deadline_seconds, bool) or not 1 <= batch.deadline_seconds <= 10: + raise PassiveContractError("batch deadline is invalid") + if batch.window == "T2_GREETING_AND_HELP": + if batch.payload != b"help\n" or batch.expected_paths != () or \ + batch.command_count != 1: + raise PassiveContractError("help batch contract is invalid") + return + if batch.window == "T3_ONE_EXACT_PATH" and \ + len(batch.expected_paths) == 1 and batch.command_count == 2: + path = batch.expected_paths[0] + try: + OfflineCollector._validate_expected_paths({path}) + encoded = path.encode("ascii", errors="strict") + except (CollectorError, UnicodeEncodeError) as error: + raise PassiveContractError("path batch contract is invalid") from error + expected = b"stat " + encoded + b"\nsum " + encoded + b"\n" + if len(encoded) <= MAX_LITERAL_PATH_BYTES and batch.payload == expected: + return + raise PassiveContractError("path batch contract is invalid") + + +def build_passive_batch(plan: SessionPlan) -> PassiveBatch: + """Build one target-free LF batch from an exact Phase-1.0W plan.""" + if not isinstance(plan, SessionPlan): + raise PassiveContractError("session plan type is invalid") + expected_commands = WINDOW_COMMANDS.get(plan.window) + if expected_commands is None or plan.commands != expected_commands: + raise PassiveContractError("window command sequence is invalid") + if not isinstance(plan.deadline_seconds, int) or isinstance( + plan.deadline_seconds, bool) or not 1 <= plan.deadline_seconds <= 10: + raise PassiveContractError("deadline is invalid") + + expected_paths: tuple[str, ...] + if plan.window == "T2_GREETING_AND_HELP": + if plan.exact_literal_path is not None: + raise PassiveContractError("help window contains a path") + payload = b"help\n" + expected_paths = () + elif plan.window == "T3_ONE_EXACT_PATH": + path = plan.exact_literal_path + if not isinstance(path, str): + raise PassiveContractError("exact path is missing") + try: + OfflineCollector._validate_expected_paths({path}) + encoded = path.encode("ascii", errors="strict") + except (CollectorError, UnicodeEncodeError) as error: + raise PassiveContractError("exact path is invalid") from error + if len(encoded) > MAX_LITERAL_PATH_BYTES: + raise PassiveContractError("exact path is too long") + payload = b"stat " + encoded + b"\nsum " + encoded + b"\n" + expected_paths = (path,) + else: + raise PassiveContractError("window is not allowlisted") + + batch = PassiveBatch( + window=plan.window, + payload=payload, + expected_paths=expected_paths, + command_count=len(plan.commands), + deadline_seconds=plan.deadline_seconds, + ) + _validate_batch(batch) + return batch + + +class PassiveResultAccumulator: + """Consume supplied chunks once; never observes prompts or remote EOF.""" + + def __init__(self, batch: PassiveBatch) -> None: + if not isinstance(batch, PassiveBatch): + raise PassiveContractError("batch type is invalid") + _validate_batch(batch) + self.batch = batch + self._collector = OfflineCollector() + self.state = "READY" + + def _invalidate(self, message: str) -> None: + if self._collector.state not in {"SEALED", "INVALID", "ABORTED"}: + try: + self._collector.abort() + except CollectorError: + pass + self.state = "INVALID" + raise PassiveContractError(message) + + def feed_supplied_chunk(self, chunk: bytes) -> None: + if self.state not in {"READY", "RECEIVING"}: + raise PassiveContractError("accumulator is not accepting input") + if not isinstance(chunk, bytes): + self._invalidate("input must be bytes") + if IAC in chunk: + self._invalidate("unexpected Telnet control byte") + try: + self._collector.feed(chunk) + except CollectorError as error: + self._invalidate("collector rejected supplied input") + raise AssertionError("unreachable") from error + if chunk: + self.state = "RECEIVING" + + def abort(self) -> None: + if self.state not in {"READY", "RECEIVING"}: + raise PassiveContractError("accumulator can no longer abort") + try: + self._collector.abort() + except CollectorError as error: + raise PassiveContractError("collector abort failed") from error + self.state = "ABORTED" + + def _validate_complete_result(self, result: dict[str, Any]) -> None: + if self.batch.window == "T2_GREETING_AND_HELP": + fingerprint = result.get("command_fingerprint", {}) + if fingerprint.get("source_family_match") not in KNOWN_HELP_FAMILIES: + raise PassiveContractError("help response is incomplete or unknown") + if result.get("classification") != "SOURCE_FAMILY_FINGERPRINT_ONLY": + raise PassiveContractError("help response classification is invalid") + elif self.batch.window == "T3_ONE_EXACT_PATH": + observations = result.get("file_observations") + if not isinstance(observations, list) or len(observations) != 1: + raise PassiveContractError("file response is incomplete") + observation = observations[0] + required = { + "path", "metadata_seen", "size", "weak_checksum", + "weak_checksum_algorithm", "cryptographic_checksum", + "proves_exact_binary", + } + if not required.issubset(observation) or observation.get("path") != \ + self.batch.expected_paths[0]: + raise PassiveContractError("file response fields are incomplete") + if observation.get("metadata_seen") is not True or not isinstance( + observation.get("size"), int) or isinstance( + observation.get("size"), bool) or observation["size"] < 0: + raise PassiveContractError("stat response is incomplete") + if observation.get("weak_checksum_algorithm") != "BSD_ROTATE_16" or \ + observation.get("cryptographic_checksum") is not False: + raise PassiveContractError("sum response is incomplete") + if result.get("classification") != "WEAK_FILE_CORRELATION_ONLY": + raise PassiveContractError("file response classification is invalid") + else: + raise PassiveContractError("batch window is invalid") + + def seal_at_hard_deadline( + self, hard_deadline_reached: bool, + ) -> dict[str, Any]: + """Seal once only after an externally supplied synthetic deadline.""" + if hard_deadline_reached is not True: + self._invalidate("hard deadline event is absent") + if self.state not in {"READY", "RECEIVING"}: + raise PassiveContractError("accumulator cannot be sealed") + try: + result = self._collector.finalize(set(self.batch.expected_paths)) + self._validate_complete_result(result) + except (CollectorError, PassiveContractError) as error: + self.state = "INVALID" + raise PassiveContractError("deadline result is invalid") from error + self.state = "SEALED" + result["passive_batch_contract"] = { + "offline_only": True, + "one_outbound_batch": True, + "plain_lf_only": True, + "telnet_negotiation_emitted": False, + "telnet_control_received": False, + "prompt_completion_used": False, + "remote_eof_completion_used": False, + "sealed_by_synthetic_hard_deadline": True, + "source_family_selected": False, + "retry_allowed": False, + "reconnect_allowed": False, + "device_behavior_proven": False, + "exact_identity_proven": False, + } + return result diff --git a/tools/scan_secrets.py b/tools/scan_secrets.py new file mode 100644 index 0000000..0b7416a --- /dev/null +++ b/tools/scan_secrets.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Reject common credential material before it reaches Git history.""" + +from __future__ import annotations + +import argparse +import os +import re +from pathlib import Path + + +PATTERNS = { + "private key": re.compile(r"-----BEGIN [A-Z ]*PRIVATE KEY-----"), + "GitHub-style token": re.compile(r"\bgh[pousr]_[A-Za-z0-9]{30,}\b"), + "OpenAI-style key": re.compile(r"\bsk-(?:proj-)?[A-Za-z0-9_-]{20,}\b"), + "AWS access key": re.compile(r"\bAKIA[0-9A-Z]{16}\b"), + "credential in URL": re.compile(r"https?://[^\s/:@]+:[^\s/@]+@"), +} + +SKIP_PARTS = {".git", "work", "build", ".vs", ".idea", ".vscode"} + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + root = args.root.resolve() + errors: list[str] = [] + scanned = 0 + + for directory, subdirectories, filenames in os.walk(root): + subdirectories[:] = sorted( + name + for name in subdirectories + if name not in SKIP_PARTS and not name.startswith("build-") + ) + for filename in sorted(filenames): + path = Path(directory, filename) + relative = path.relative_to(root) + if path.stat().st_size > 2_000_000: + continue + try: + text = path.read_text(encoding="utf-8") + except UnicodeDecodeError: + continue + scanned += 1 + for label, pattern in PATTERNS.items(): + if pattern.search(text): + errors.append(f"{relative}: possible {label}") + + if errors: + for error in errors: + print(f"secret scan failed: {error}") + return 1 + print(f"secret scan passed across {scanned} text files") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/simulate_phase09_transaction.py b/tools/simulate_phase09_transaction.py new file mode 100644 index 0000000..f866e51 --- /dev/null +++ b/tools/simulate_phase09_transaction.py @@ -0,0 +1,1008 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Host-only virtual simulator for the Phase-0.9A anti-brick design. + +The model never opens a target artifact, touches a device path, starts a +compiler, or performs network I/O. All objects and writes are logical records +held in memory. A successful host-model result is not PS5 hardware evidence. +""" + +from __future__ import annotations + +import argparse +import copy +import json +from dataclasses import asdict, dataclass +from typing import Any, Iterable + + +STATES = ( + "OFFLINE_ONLY", + "OBSERVATION_NOT_AUTHORIZED", + "OBSERVATION_AUTHORIZED", + "DEVICE_IDENTITY_OBSERVED", + "LIVE_OBJECTS_VERIFIED", + "BACKUP_NOT_PRESENT", + "BACKUP_CREATION_NOT_AUTHORIZED", + "BACKUP_CREATION_AUTHORIZED", + "BACKUP_CREATED", + "BACKUP_REOPENED_AND_VERIFIED", + "RECOVERY_PATH_VERIFIED", + "CANDIDATE_NOT_AUTHORIZED", + "CANDIDATE_APPROVED_OFFLINE", + "STAGING_NOT_AUTHORIZED", + "STAGING_AUTHORIZED", + "CANDIDATE_STAGED", + "CANDIDATE_REOPENED_AND_VERIFIED", + "TARGET_NOT_QUIESCENT", + "TARGET_QUIESCENT", + "SWITCH_NOT_AUTHORIZED", + "SWITCH_AUTHORIZED", + "SWITCH_IN_PROGRESS", + "POST_SWITCH_VERIFY", + "MANUAL_EXECUTION_NOT_AUTHORIZED", + "MANUAL_EXECUTION_AUTHORIZED", + "ONE_SHOT_EXECUTION", + "ACCEPTED", + "ROLLBACK_REQUIRED", + "ROLLBACK_AUTHORIZED", + "ROLLBACK_IN_PROGRESS", + "ROLLBACK_VERIFIED", + "BLOCKED", +) + +ALLOWED_FORWARD_TRANSITIONS = { + ("OFFLINE_ONLY", "OBSERVATION_NOT_AUTHORIZED"), + ("OBSERVATION_NOT_AUTHORIZED", "OBSERVATION_AUTHORIZED"), + ("OBSERVATION_AUTHORIZED", "DEVICE_IDENTITY_OBSERVED"), + ("DEVICE_IDENTITY_OBSERVED", "LIVE_OBJECTS_VERIFIED"), + ("LIVE_OBJECTS_VERIFIED", "BACKUP_NOT_PRESENT"), + ("BACKUP_NOT_PRESENT", "BACKUP_CREATION_NOT_AUTHORIZED"), + ("BACKUP_CREATION_NOT_AUTHORIZED", "BACKUP_CREATION_AUTHORIZED"), + ("BACKUP_CREATION_AUTHORIZED", "BACKUP_CREATED"), + ("BACKUP_CREATED", "BACKUP_REOPENED_AND_VERIFIED"), + ("BACKUP_REOPENED_AND_VERIFIED", "RECOVERY_PATH_VERIFIED"), + ("RECOVERY_PATH_VERIFIED", "CANDIDATE_NOT_AUTHORIZED"), + ("CANDIDATE_NOT_AUTHORIZED", "CANDIDATE_APPROVED_OFFLINE"), + ("CANDIDATE_APPROVED_OFFLINE", "STAGING_NOT_AUTHORIZED"), + ("STAGING_NOT_AUTHORIZED", "STAGING_AUTHORIZED"), + ("STAGING_AUTHORIZED", "CANDIDATE_STAGED"), + ("CANDIDATE_STAGED", "CANDIDATE_REOPENED_AND_VERIFIED"), + ("CANDIDATE_REOPENED_AND_VERIFIED", "TARGET_NOT_QUIESCENT"), + ("TARGET_NOT_QUIESCENT", "TARGET_QUIESCENT"), + ("TARGET_QUIESCENT", "SWITCH_NOT_AUTHORIZED"), + ("SWITCH_NOT_AUTHORIZED", "SWITCH_AUTHORIZED"), + ("SWITCH_AUTHORIZED", "SWITCH_IN_PROGRESS"), + ("SWITCH_IN_PROGRESS", "POST_SWITCH_VERIFY"), + ("POST_SWITCH_VERIFY", "MANUAL_EXECUTION_NOT_AUTHORIZED"), + ("MANUAL_EXECUTION_NOT_AUTHORIZED", "MANUAL_EXECUTION_AUTHORIZED"), + ("MANUAL_EXECUTION_AUTHORIZED", "ONE_SHOT_EXECUTION"), + ("ONE_SHOT_EXECUTION", "ACCEPTED"), + ("POST_SWITCH_VERIFY", "ROLLBACK_REQUIRED"), + ("ROLLBACK_REQUIRED", "ROLLBACK_AUTHORIZED"), + ("ROLLBACK_AUTHORIZED", "ROLLBACK_IN_PROGRESS"), + ("ROLLBACK_IN_PROGRESS", "ROLLBACK_VERIFIED"), +} + +COMPONENTS = { + "hardened_elfldr": { + "stock_size": 397000, + "stock_sha256": ( + "092d16ee0ede0c494947efd38d1a17bbd7cc4b022d3858ea898833c188c703e8" + ), + "candidate_size": 397000, + "candidate_sha256": ( + "63e810982471eb40cae3a20aa9df9a0a02892f420e429874fae4e99aa400b561" + ), + "candidate_source_commit": "197623058f509eddde18868dafcb92fdcac66464", + }, + "controlled_payload_manager": { + "stock_size": 2050320, + "stock_sha256": ( + "518740adbacccb9094fadb07dd424c53ee290f38306449ccc9d6957fdf813c0b" + ), + "candidate_size": 99560, + "candidate_sha256": ( + "8fecf8241a46246eddbd21e8bb4d875f5d76f1f4f4c6a11384df1f131aa5e5b1" + ), + "candidate_source_commit": "e23d94ff91233aa770e2342800c1467875bdef44", + }, +} + +FAULTS = ( + "wrong_preimage_hash", + "wrong_size", + "missing_live_path", + "missing_mount_id", + "missing_object_id", + "reference_only_preimage", + "object_swap_after_preflight", + "symlink_substitution", + "backup_same_object_as_live", + "short_backup_write", + "backup_hash_mismatch", + "backup_not_reopened", + "insufficient_space", + "candidate_hash_mismatch", + "target_process_active", + "autoload_active", + "retry_active", + "switch_primitive_unknown", + "atomic_switch_failure", + "directory_durability_unknown", + "live_verification_failure", + "rollback_hash_mismatch", + "recovery_depends_on_replaced_component", + "wrong_component_artifact_mapping", + "lifecycle_probe_candidate", + "authorization_missing", + "authorization_wrong_hash", + "authorization_expired", + "second_component_before_first_accepted", + "timeout", + "unknown_firmware", + "in_place_overwrite", + "two_step_rename_gap", +) + +POWER_LOSS_BOUNDARIES = ( + "before_backup_write", + "during_backup_write", + "after_backup_write_before_flush", + "after_flush_before_reopen_hash", + "during_candidate_staging", + "after_candidate_staging_before_verification", + "immediately_before_live_switch", + "during_live_switch", + "immediately_after_live_switch", + "after_switch_before_live_hash", + "after_live_hash_before_execution", + "during_first_manual_execution", + "during_rollback", + "after_rollback_before_recovery_verification", +) + + +@dataclass +class VirtualObject: + """Logical file identity; it is never materialized on the host.""" + + path_token: str | None + mount_id: str | None + object_id: str | None + object_type: str + size: int + sha256: str + complete: bool = True + durable: bool = True + reopened: bool = False + + +@dataclass +class SyntheticAuthorization: + """A host-model gate token that explicitly grants no real-world authority.""" + + action: str + component: str + artifact_sha256: str + gate_token_present: bool + expires_at_tick: int + synthetic_host_model_only: bool = True + + def valid( + self, action: str, component: str, artifact_sha256: str, now_tick: int + ) -> bool: + return ( + self.synthetic_host_model_only + and self.gate_token_present + and self.action == action + and self.component == component + and self.artifact_sha256 == artifact_sha256 + and now_tick < self.expires_at_tick + ) + + +class TransitionError(RuntimeError): + """Raised when the state machine would skip a mandatory gate.""" + + +class StateMachine: + def __init__(self) -> None: + self.state = "OFFLINE_ONLY" + self.history = [self.state] + + def transition(self, destination: str) -> None: + if destination not in STATES: + raise TransitionError(f"unknown state: {destination}") + if destination == "BLOCKED": + self.state = destination + self.history.append(destination) + return + if (self.state, destination) not in ALLOWED_FORWARD_TRANSITIONS: + raise TransitionError( + f"forbidden transition: {self.state} -> {destination}" + ) + self.state = destination + self.history.append(destination) + + +def _authorization( + action: str, + component: str, + digest: str, + faults: set[str], +) -> SyntheticAuthorization: + gate_token_present = "authorization_missing" not in faults + scoped_digest = "0" * 64 if "authorization_wrong_hash" in faults else digest + expiry = 0 if "authorization_expired" in faults else 100 + return SyntheticAuthorization( + action=action, + component=component, + artifact_sha256=scoped_digest, + gate_token_present=gate_token_present, + expires_at_tick=expiry, + ) + + +def _base_objects(component: str) -> tuple[VirtualObject, VirtualObject]: + identity = COMPONENTS[component] + live = VirtualObject( + path_token=f"LIVE_SLOT::{component}", + mount_id=f"MOUNT::{component}", + object_id=f"OBJECT::stock::{component}", + object_type="regular", + size=identity["stock_size"], + sha256=identity["stock_sha256"], + ) + candidate = VirtualObject( + path_token=f"STAGING_SLOT::{component}", + mount_id=f"MOUNT::{component}", + object_id=f"OBJECT::candidate::{component}", + object_type="regular", + size=identity["candidate_size"], + sha256=identity["candidate_sha256"], + ) + return live, candidate + + +def _report( + machine: StateMachine, + component: str, + outcome: str, + blockers: list[dict[str, str]], + live: VirtualObject, + backup: VirtualObject | None, + candidate: VirtualObject, + writes: list[str], + risk: str | None = None, +) -> dict[str, Any]: + identity = COMPONENTS[component] + old_live_intact = ( + live.complete + and live.object_type == "regular" + and live.path_token == f"LIVE_SLOT::{component}" + and live.mount_id == f"MOUNT::{component}" + and live.object_id + in { + f"OBJECT::stock::{component}", + f"OBJECT::restored::{component}", + } + and live.sha256 == identity["stock_sha256"] + and live.size == identity["stock_size"] + ) + verified_new_live = ( + live.complete + and live.object_type == "regular" + and live.path_token == f"LIVE_SLOT::{component}" + and live.mount_id == f"MOUNT::{component}" + and live.object_id == f"OBJECT::live-candidate::{component}" + and live.sha256 == identity["candidate_sha256"] + and live.size == identity["candidate_size"] + ) + verified_backup_intact = ( + backup is not None + and backup.complete + and backup.reopened + and backup.sha256 == identity["stock_sha256"] + and backup.size == identity["stock_size"] + and backup.object_id != live.object_id + ) + crash_invariant = ( + "A_OLD_LIVE_COMPLETE" + if old_live_intact + else ( + "B_NEW_LIVE_COMPLETE_AND_BACKUP_INTACT" + if verified_new_live and verified_backup_intact + else "C_REJECTED_UNSAFE_OR_UNPROVEN" + ) + ) + return { + "schema_contract": "chimera-gfx-phase-0.9-host-simulator-v1", + "phase": "PHASE_0_9A_OFFLINE_ANTI_BRICK", + "status": outcome, + "component": component, + "state": machine.state, + "state_history": machine.history, + "blockers": blockers, + "risk": risk, + "virtual_writes": writes, + "virtual_live": asdict(live), + "virtual_backup": asdict(backup) if backup is not None else None, + "virtual_candidate": asdict(candidate), + "crash_invariant": crash_invariant, + "host_simulation_only": True, + "hardware_evidence": False, + "ps5_connected": False, + "device_write_performed": False, + "files_transferred": False, + "target_execution_performed": False, + "target_artifact_created": False, + "production_installation_code": False, + "synthetic_host_model_authorization_states": [ + state + for state in machine.history + if state + in { + "OBSERVATION_AUTHORIZED", + "BACKUP_CREATION_AUTHORIZED", + "STAGING_AUTHORIZED", + "SWITCH_AUTHORIZED", + "MANUAL_EXECUTION_AUTHORIZED", + "ROLLBACK_AUTHORIZED", + } + ], + "real_world_authority": False, + "installation_authorized": False, + "lifecycle_authorized": False, + "execution_authorized": False, + "transfer_authorized": False, + "automatic_retry": False, + "autoload": False, + } + + +def _blocked( + machine: StateMachine, + component: str, + code: str, + detail: str, + live: VirtualObject, + backup: VirtualObject | None, + candidate: VirtualObject, + writes: list[str], + risk: str = "HIGH", +) -> dict[str, Any]: + machine.transition("BLOCKED") + return _report( + machine, + component, + "BLOCKED", + [{"code": code, "detail": detail}], + live, + backup, + candidate, + writes, + risk, + ) + + +def simulate_transaction( + component: str, + faults: Iterable[str] = (), + *, + prove_virtual_switch_model: bool = False, +) -> dict[str, Any]: + """Run one component transaction entirely in memory. + + ``prove_virtual_switch_model`` proves only the synthetic model primitive. + It never promotes a PS5 filesystem property. + """ + + if component not in COMPONENTS: + raise ValueError("component must be hardened_elfldr or controlled_payload_manager") + fault_set = set(faults) + unknown = fault_set.difference(FAULTS) + if unknown: + raise ValueError(f"unknown fault(s): {', '.join(sorted(unknown))}") + + identity = COMPONENTS[component] + live, candidate = _base_objects(component) + expected = copy.deepcopy(live) + backup: VirtualObject | None = None + writes: list[str] = [] + machine = StateMachine() + machine.transition("OBSERVATION_NOT_AUTHORIZED") + + observation_auth = _authorization( + "observation", component, identity["stock_sha256"], fault_set + ) + if not observation_auth.valid( + "observation", component, identity["stock_sha256"], now_tick=1 + ): + return _blocked( + machine, + component, + "OBSERVATION_AUTHORIZATION_INVALID", + "The synthetic observation input is missing, expired or hash-mismatched.", + live, + backup, + candidate, + writes, + ) + machine.transition("OBSERVATION_AUTHORIZED") + + if "unknown_firmware" in fault_set: + return _blocked( + machine, + component, + "UNKNOWN_FIRMWARE", + "An unknown firmware always stops before mutation.", + live, + backup, + candidate, + writes, + ) + machine.transition("DEVICE_IDENTITY_OBSERVED") + + if "missing_live_path" in fault_set: + live.path_token = None + if "missing_mount_id" in fault_set: + live.mount_id = None + if "missing_object_id" in fault_set: + live.object_id = None + if "wrong_preimage_hash" in fault_set: + live.sha256 = "1" * 64 + if "wrong_size" in fault_set: + live.size += 1 + if "symlink_substitution" in fault_set: + live.object_type = "symlink" + if "reference_only_preimage" in fault_set: + return _blocked( + machine, + component, + "REFERENCE_ONLY_PREIMAGE", + "A stock reference was not promoted by an exact stable observation.", + live, + backup, + candidate, + writes, + ) + + exact_identity = all( + ( + live.path_token == expected.path_token, + live.mount_id == expected.mount_id, + live.object_id == expected.object_id, + live.object_type == "regular", + live.size == expected.size, + live.sha256 == expected.sha256, + live.complete, + ) + ) + if not exact_identity: + return _blocked( + machine, + component, + "LIVE_IDENTITY_MISMATCH", + "Path, mount, object, type, size and hash must all match.", + live, + backup, + candidate, + writes, + ) + machine.transition("LIVE_OBJECTS_VERIFIED") + machine.transition("BACKUP_NOT_PRESENT") + machine.transition("BACKUP_CREATION_NOT_AUTHORIZED") + + backup_auth = _authorization( + "backup_creation", component, identity["stock_sha256"], fault_set + ) + if not backup_auth.valid( + "backup_creation", component, identity["stock_sha256"], now_tick=1 + ): + return _blocked( + machine, + component, + "BACKUP_AUTHORIZATION_INVALID", + "Missing, expired or differently hash-bound backup authorization.", + live, + backup, + candidate, + writes, + ) + machine.transition("BACKUP_CREATION_AUTHORIZED") + + if "object_swap_after_preflight" in fault_set: + live.object_id = "OBJECT::substituted" + return _blocked( + machine, + component, + "OBJECT_CHANGED_AFTER_PREFLIGHT", + "The stable live object identity changed before the virtual write.", + live, + backup, + candidate, + writes, + ) + if "insufficient_space" in fault_set: + return _blocked( + machine, + component, + "INSUFFICIENT_SPACE", + "Capacity and metadata reserve are insufficient.", + live, + backup, + candidate, + writes, + ) + if "backup_same_object_as_live" in fault_set: + backup = copy.deepcopy(live) + return _blocked( + machine, + component, + "BACKUP_NOT_SEPARATE", + "The backup resolves to the live object.", + live, + backup, + candidate, + writes, + "CATASTROPHIC", + ) + + backup = VirtualObject( + path_token=f"BACKUP_SLOT::{component}", + mount_id=f"BACKUP_MOUNT::{component}", + object_id=f"OBJECT::backup::{component}", + object_type="regular", + size=live.size, + sha256=live.sha256, + durable=True, + ) + writes.append("virtual_backup_create") + machine.transition("BACKUP_CREATED") + if "short_backup_write" in fault_set: + backup.size -= 1 + backup.complete = False + return _blocked( + machine, + component, + "SHORT_BACKUP_WRITE", + "A partial backup is never promotable.", + live, + backup, + candidate, + writes, + "CATASTROPHIC", + ) + if "backup_hash_mismatch" in fault_set: + backup.sha256 = "2" * 64 + if "backup_not_reopened" not in fault_set: + backup.reopened = True + if ( + backup.sha256 != expected.sha256 + or backup.size != expected.size + or not backup.reopened + ): + return _blocked( + machine, + component, + "BACKUP_REOPEN_VERIFY_FAILED", + "The separate backup must survive close, reopen, size and hash checks.", + live, + backup, + candidate, + writes, + "CATASTROPHIC", + ) + machine.transition("BACKUP_REOPENED_AND_VERIFIED") + + if "recovery_depends_on_replaced_component" in fault_set: + return _blocked( + machine, + component, + "RECOVERY_NOT_INDEPENDENT", + "Recovery depends on the component being replaced.", + live, + backup, + candidate, + writes, + "CATASTROPHIC", + ) + machine.transition("RECOVERY_PATH_VERIFIED") + machine.transition("CANDIDATE_NOT_AUTHORIZED") + + if "lifecycle_probe_candidate" in fault_set: + return _blocked( + machine, + component, + "LIFECYCLE_PROBE_NOT_INSTALLABLE", + "The lifecycle probe is excluded from both component transactions.", + live, + backup, + candidate, + writes, + ) + if "wrong_component_artifact_mapping" in fault_set: + return _blocked( + machine, + component, + "COMPONENT_ARTIFACT_MAPPING_MISMATCH", + "The candidate is bound to a different component.", + live, + backup, + candidate, + writes, + ) + if "second_component_before_first_accepted" in fault_set: + return _blocked( + machine, + component, + "COMPONENT_SEQUENCE_VIOLATION", + "A second component cannot start before separate acceptance or rollback.", + live, + backup, + candidate, + writes, + ) + machine.transition("CANDIDATE_APPROVED_OFFLINE") + machine.transition("STAGING_NOT_AUTHORIZED") + stage_auth = _authorization( + "staging", component, identity["candidate_sha256"], fault_set + ) + if not stage_auth.valid( + "staging", component, identity["candidate_sha256"], now_tick=1 + ): + return _blocked( + machine, + component, + "STAGING_AUTHORIZATION_INVALID", + "Staging approval is missing, expired or bound to different bytes.", + live, + backup, + candidate, + writes, + ) + machine.transition("STAGING_AUTHORIZED") + + if "candidate_hash_mismatch" in fault_set: + candidate.sha256 = "3" * 64 + writes.append("virtual_candidate_stage") + machine.transition("CANDIDATE_STAGED") + candidate.reopened = True + if ( + candidate.sha256 != identity["candidate_sha256"] + or candidate.size != identity["candidate_size"] + or not candidate.complete + or not candidate.reopened + ): + return _blocked( + machine, + component, + "CANDIDATE_REOPEN_VERIFY_FAILED", + "Staged candidate bytes do not match the offline approved artifact.", + live, + backup, + candidate, + writes, + ) + machine.transition("CANDIDATE_REOPENED_AND_VERIFIED") + machine.transition("TARGET_NOT_QUIESCENT") + + if "target_process_active" in fault_set: + return _blocked( + machine, + component, + "TARGET_NOT_QUIESCENT", + "The target process or service is still active.", + live, + backup, + candidate, + writes, + ) + if "autoload_active" in fault_set: + return _blocked( + machine, + component, + "AUTOLOAD_ACTIVE", + "Autoload is forbidden.", + live, + backup, + candidate, + writes, + ) + if "retry_active" in fault_set: + return _blocked( + machine, + component, + "AUTOMATIC_RETRY_ACTIVE", + "Automatic retry is forbidden.", + live, + backup, + candidate, + writes, + ) + if "timeout" in fault_set: + return _blocked( + machine, + component, + "TIMEOUT_STOP", + "A timeout stops and never retries.", + live, + backup, + candidate, + writes, + ) + machine.transition("TARGET_QUIESCENT") + machine.transition("SWITCH_NOT_AUTHORIZED") + + switch_auth = _authorization( + "switch", component, identity["candidate_sha256"], fault_set + ) + if not switch_auth.valid( + "switch", component, identity["candidate_sha256"], now_tick=1 + ): + return _blocked( + machine, + component, + "SWITCH_AUTHORIZATION_INVALID", + "Switch approval is separate and hash-bound.", + live, + backup, + candidate, + writes, + ) + machine.transition("SWITCH_AUTHORIZED") + + platform_unproven = ( + not prove_virtual_switch_model + or "switch_primitive_unknown" in fault_set + or "directory_durability_unknown" in fault_set + or "in_place_overwrite" in fault_set + or "two_step_rename_gap" in fault_set + ) + if platform_unproven: + return _blocked( + machine, + component, + "NO_PROVEN_POWER_LOSS_SAFE_SWITCH", + "Atomicity and directory durability are not proven for the platform.", + live, + backup, + candidate, + writes, + "CATASTROPHIC", + ) + + machine.transition("SWITCH_IN_PROGRESS") + if "atomic_switch_failure" in fault_set: + return _blocked( + machine, + component, + "ATOMIC_SWITCH_FAILED", + "The virtual atomic switch failed and left the old live object intact.", + live, + backup, + candidate, + writes, + "CATASTROPHIC", + ) + + live = copy.deepcopy(candidate) + live.path_token = expected.path_token + live.object_id = f"OBJECT::live-candidate::{component}" + writes.append("virtual_atomic_switch") + machine.transition("POST_SWITCH_VERIFY") + + if "live_verification_failure" in fault_set: + machine.transition("ROLLBACK_REQUIRED") + return _report( + machine, + component, + "ROLLBACK_REQUIRED", + [ + { + "code": "POST_SWITCH_VERIFY_FAILED", + "detail": ( + "A separate rollback authorization is required; " + "execution remains forbidden." + ), + } + ], + live, + backup, + candidate, + writes, + "CATASTROPHIC", + ) + + if "rollback_hash_mismatch" in fault_set: + machine.transition("ROLLBACK_REQUIRED") + rollback_auth = _authorization( + "rollback", component, identity["stock_sha256"], set() + ) + if not rollback_auth.valid( + "rollback", component, identity["stock_sha256"], now_tick=1 + ): + return _blocked( + machine, + component, + "ROLLBACK_AUTHORIZATION_INVALID", + "Rollback authorization is absent.", + live, + backup, + candidate, + writes, + "CATASTROPHIC", + ) + machine.transition("ROLLBACK_AUTHORIZED") + machine.transition("ROLLBACK_IN_PROGRESS") + live = copy.deepcopy(backup) + live.path_token = expected.path_token + live.object_id = f"OBJECT::restored::{component}" + writes.append("virtual_atomic_rollback") + if "rollback_hash_mismatch" in fault_set: + live.sha256 = "4" * 64 + return _blocked( + machine, + component, + "ROLLBACK_VERIFY_FAILED", + "Rollback hash mismatch is catastrophic and remains blocked.", + live, + backup, + candidate, + writes, + "CATASTROPHIC", + ) + machine.transition("ROLLBACK_VERIFIED") + return _report( + machine, + component, + "ROLLBACK_VERIFIED", + [], + live, + backup, + candidate, + writes, + ) + + machine.transition("MANUAL_EXECUTION_NOT_AUTHORIZED") + return _report( + machine, + component, + "DESIGN_MODEL_STOP_BEFORE_EXECUTION", + [ + { + "code": "MANUAL_EXECUTION_NOT_AUTHORIZED", + "detail": "Execution is a later separate gate and is not simulated here.", + } + ], + live, + backup, + candidate, + writes, + ) + + +def simulate_power_loss_boundary( + component: str, boundary: str, *, prove_virtual_switch_model: bool = False +) -> dict[str, Any]: + """Classify one abstract interruption without performing any real write.""" + + if component not in COMPONENTS: + raise ValueError("unknown component") + if boundary not in POWER_LOSS_BOUNDARIES: + raise ValueError("unknown power-loss boundary") + if not prove_virtual_switch_model: + return { + "boundary": boundary, + "result": "UNPROVEN", + "implementation_blocked": True, + "reason": "PS5 atomicity and durability are not proven", + "host_simulation_only": True, + "hardware_evidence": False, + "automatic_start": False, + "automatic_retry": False, + "crash_invariant": "C_REJECTED_UNSAFE_OR_UNPROVEN", + } + after_switch = boundary in { + "immediately_after_live_switch", + "after_switch_before_live_hash", + "after_live_hash_before_execution", + "during_first_manual_execution", + "during_rollback", + } + return { + "boundary": boundary, + "result": "SAFE_IN_VIRTUAL_MODEL_ONLY", + "implementation_blocked": False, + "reason": "the synthetic atomic primitive chooses one complete identity", + "host_simulation_only": True, + "hardware_evidence": False, + "automatic_start": False, + "automatic_retry": False, + "crash_invariant": ( + "B_NEW_LIVE_COMPLETE_AND_BACKUP_INTACT" + if after_switch + else "A_OLD_LIVE_COMPLETE" + ), + } + + +def run_fault_suite(component: str) -> dict[str, Any]: + results: list[dict[str, Any]] = [] + for fault in FAULTS: + report = simulate_transaction( + component, + [fault], + prove_virtual_switch_model=True, + ) + results.append( + { + "fault": fault, + "status": report["status"], + "state": report["state"], + "risk": report["risk"], + "crash_invariant": report["crash_invariant"], + "target_execution_performed": report[ + "target_execution_performed" + ], + "automatic_retry": report["automatic_retry"], + } + ) + power_loss = [ + simulate_power_loss_boundary( + component, boundary, prove_virtual_switch_model=True + ) + for boundary in POWER_LOSS_BOUNDARIES + ] + return { + "schema_contract": "chimera-gfx-phase-0.9-host-fault-suite-v1", + "phase": "PHASE_0_9A_OFFLINE_ANTI_BRICK", + "component": component, + "fault_results": results, + "power_loss_results": power_loss, + "host_simulation_only": True, + "hardware_evidence": False, + "ps5_connected": False, + "device_write_performed": False, + "files_transferred": False, + "target_execution_performed": False, + "target_artifact_created": False, + "automatic_retry": False, + } + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument( + "--component", + choices=tuple(COMPONENTS), + required=True, + ) + parser.add_argument("--fault", choices=FAULTS, action="append", default=[]) + parser.add_argument( + "--power-loss-boundary", + choices=POWER_LOSS_BOUNDARIES, + ) + parser.add_argument("--run-fault-suite", action="store_true") + args = parser.parse_args() + + if args.run_fault_suite and (args.fault or args.power_loss_boundary): + parser.error("--run-fault-suite cannot be combined with another scenario") + if args.power_loss_boundary and args.fault: + parser.error("--power-loss-boundary cannot be combined with --fault") + + if args.run_fault_suite: + report = run_fault_suite(args.component) + elif args.power_loss_boundary: + report = simulate_power_loss_boundary( + args.component, args.power_loss_boundary + ) + else: + report = simulate_transaction(args.component, args.fault) + print(json.dumps(report, indent=2, sort_keys=True)) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/validate_phase08_remediation.py b/tools/validate_phase08_remediation.py new file mode 100644 index 0000000..96fe06a --- /dev/null +++ b/tools/validate_phase08_remediation.py @@ -0,0 +1,618 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate the offline, fail-closed Phase-0.8R evidence contract.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +import subprocess +from pathlib import Path +from typing import Any + + +STATUS = "READ_ONLY_PREFLIGHT_BLOCKED" +SOURCE_COMMIT = "2c944d6d65a08f7e1c02f518721cde061b999329" +PAYLOAD_MANAGER_COMMIT = "cfbc70f30f419b09bf2b52283f7409e2d3117ee1" +DENYLIST_SHA256 = ( + "4be1c17b4964f2b68c39b5145bc4af4619c32512d60269ecf5c39728b390fa63" +) +STOCK_ELFLDR_SHA256 = ( + "092d16ee0ede0c494947efd38d1a17bbd7cc4b022d3858ea898833c188c703e8" +) +STOCK_PAYLOAD_MANAGER_SHA256 = ( + "518740adbacccb9094fadb07dd424c53ee290f38306449ccc9d6957fdf813c0b" +) + +IMMUTABLE_EVIDENCE = { + "docs/runtime/phase-0.8-read-only-preflight.md": { + "role": "historical_human_readable_preflight_record", + "sha256": "3fbe086175a6048176075f447ec1482074928e3b5282db97ea2169395fe1d508", + }, + "manifests/runtime/phase-0.8-read-only-preflight.json": { + "role": "historical_machine_readable_preflight_record", + "sha256": "47d7f452f8799979fe99b3e6d56859f03544112725bf7e5b349eba5ed81b3322", + }, + "tests/test_phase08_preflight.py": { + "role": "historical_fail_closed_regression_test", + "sha256": "8a4ad7c70de28ffe3148fd3fd1f68c36a872c53c691c9068e1ff163970863c48", + }, +} + +AUTHORIZATION_FIELDS = ( + "authorized", + "installation_authorized", + "lifecycle_authorized", + "execution_authorized", + "transfer_authorized", + "automatic_retry", +) + +ACTIVITY_FIELDS = ( + "hardware_observed", + "ps5_connected", + "files_transferred", + "target_execution_performed", + "target_mutation_performed", + "target_artifact_created", + "collector_implemented", + "target_build_performed", +) + +REQUIRED_BLOCKERS = { + "exact_permission_quote_absent": "STOP-RO", + "collector_identity_absent": "STOP-RO", + "collector_side_effect_contract_absent": "STOP-RO", + "two_current_firmware_sources_absent": "STOP-GATE", + "live_object_identities_absent": "STOP-GATE", + "listeners_absent": "STOP-GATE", + "autoload_status_absent": "STOP-GATE", + "rollback_backups_absent": "STOP-GATE", + "payload_manager_backup_not_byte_exact_on_device": "HARD_STOP-GATE", + "unknown_result_is_stop": "STOP", + "timeout_is_stop": "STOP", + "deviation_is_stop": "STOP", + "automatic_retry_forbidden": "STOP", +} + +REQUIRED_FINDINGS = { + "options_any_endpoint", + "get_version", + "get_log", + "get_autoload_status", + "get_config", + "get_list_payloads", + "get_processes_list", + "get_sources_list", + "get_ip", +} + +REQUIRED_OBSERVATIONS = { + "firmware", + "live_paths", + "object_identities", + "file_sizes", + "sha256", + "processes_services", + "listeners", + "autoload", + "rollback_files", + "storage_precondition", +} + +REQUIRED_PROHIBITED_ACTIONS = { + "connect_to_ps5", + "probe_ip_port_or_device_interface", + "use_usb_or_removable_media", + "transfer_ps5_file", + "package_for_ps5_deployment", + "install_or_replace_target_component", + "execute_elf_or_payload", + "build_target_elf", + "start_cross_compiler", + "implement_or_build_collector", + "modify_payload_manager_production_code", + "modify_elfldr_production_code", + "modify_lifecycle_code", + "activate_or_modify_autoload", + "activate_retry", + "change_target_configuration", + "start_stop_or_signal_target_service_or_process", + "implement_gnm_videoout_sdl_audio_input_shaders_cores_or_retroarch", + "download_or_install_packages", + "contact_internet_gitea_or_other_remote", + "commit_or_push", +} + +TEMPLATE_REQUIRED_FIELDS = { + "exact_user_statement", + "authorization_date", + "expiration_time", + "device_identity", + "exact_purpose", + "exact_observations", + "method_or_collector_id", + "source_commit", + "collector_file_size", + "collector_sha256", + "firmware_gate", + "maximum_runtime_ms", + "maximum_execution_count", + "maximum_transfer_count", + "network_behavior", + "output_channel", + "allowed_volatile_effects", + "prohibited_persistent_effects", + "stop_criteria", + "cleanup_requirements", + "reporting_requirements", + "explicit_installation_exclusion", + "explicit_lifecycle_probe_exclusion", + "explicit_autoload_and_retry_exclusion", + "explicit_graphics_and_retroarch_exclusion", + "revocation_method", + "manual_confirmation_template_does_not_authorize", +} + +TARGET_SUFFIXES = { + ".elf", + ".self", + ".sprx", + ".pkg", + ".bin", + ".payload", + ".zip", + ".tar", + ".tgz", + ".7z", +} + + +def sha256(path: Path) -> str: + digest = hashlib.sha256() + with path.open("rb") as stream: + for block in iter(lambda: stream.read(1024 * 1024), b""): + digest.update(block) + return digest.hexdigest() + + +def load_json(path: Path) -> dict[str, Any]: + document = json.loads(path.read_text(encoding="utf-8")) + if not isinstance(document, dict): + raise ValueError(f"{path}: expected a JSON object") + return document + + +def extract_json_contract(path: Path, name: str) -> dict[str, Any]: + text = path.read_text(encoding="utf-8") + begin = f"" + end = f"" + if text.count(begin) != 1 or text.count(end) != 1: + raise ValueError(f"{path}: expected exactly one {name} contract") + block = text.split(begin, 1)[1].split(end, 1)[0].strip() + if not block.startswith("```json\n") or not block.endswith("\n```"): + raise ValueError(f"{path}: {name} must be one fenced JSON object") + document = json.loads(block[len("```json\n") : -len("\n```")]) + if not isinstance(document, dict): + raise ValueError(f"{path}: {name} must be a JSON object") + return document + + +def positive_status_values(value: Any) -> list[str]: + errors: list[str] = [] + positive = { + "READY", + "COMPLETE", + "COMPLETED", + "AUTHORIZED", + "PASS", + "PASSED", + "READ_ONLY_PREFLIGHT_DATA_COMPLETE", + "READY_FOR_HARDENED_RUNTIME_DEPLOYMENT", + } + if isinstance(value, dict): + for key, child in value.items(): + if key == "historical_validation_report": + continue + errors.extend(positive_status_values(child)) + elif isinstance(value, list): + for child in value: + errors.extend(positive_status_values(child)) + elif isinstance(value, str) and value.upper() in positive: + errors.append(value) + return errors + + +def validate_manifest( + manifest: dict[str, Any], denylist: dict[str, Any] +) -> list[str]: + errors: list[str] = [] + + if manifest.get("schema_version") != 1: + errors.append("unsupported remediation schema version") + schema_contract = manifest.get("schema_contract", {}) + if schema_contract.get("id") != "chimera-gfx-phase-0.8-remediation-v1": + errors.append("remediation schema-contract ID changed") + if schema_contract.get("shared_schema_available") is not False: + errors.append("remediation incorrectly claims a shared schema") + if schema_contract.get("validator") != "tools/validate_phase08_remediation.py": + errors.append("remediation validator binding changed") + + if manifest.get("phase_id") != "0.8R": + errors.append("remediation phase ID changed") + if manifest.get("phase") != "offline_remediation": + errors.append("remediation phase widened beyond offline") + if manifest.get("status") != STATUS: + errors.append("Phase-0.8 status is not fail-closed") + if positive_status_values(manifest): + errors.append("manifest contains an overriding positive status") + + historical = manifest.get("historical_identity", {}) + if historical.get("source_commit") != SOURCE_COMMIT: + errors.append("historical source commit changed") + if historical.get("status") != STATUS: + errors.append("historical Phase-0.8 status changed") + historical_report = historical.get("historical_validation_report", {}) + if ( + historical_report.get("classification") + != "historical_report_not_current_hardware_evidence" + ): + errors.append("historical tests were promoted to hardware evidence") + + immutable = { + item.get("path"): { + "role": item.get("role"), + "sha256": item.get("sha256"), + } + for item in manifest.get("immutable_evidence", []) + } + if immutable != IMMUTABLE_EVIDENCE: + errors.append("immutable Phase-0.8 evidence binding changed") + + authorization = manifest.get("authorization", {}) + if any(authorization.get(field) is not False for field in AUTHORIZATION_FIELDS): + errors.append("an authorization or retry field is not false") + activity = manifest.get("activity", {}) + if any(activity.get(field) is not False for field in ACTIVITY_FIELDS): + errors.append("manifest claims a prohibited target activity") + + if manifest.get("firmware_runtime_behavior") != "UNPROVEN": + errors.append("firmware runtime behavior was promoted") + claim_boundaries = manifest.get("claim_boundaries", {}) + required_false_claims = { + "hardware_safety_proven", + "firmware_behavior_proven", + "absence_of_volatile_effects_proven", + "no_persistent_write_found_equals_side_effect_free", + "host_tests_are_hardware_evidence", + "missing_observation_means_safe_absence", + } + if any(claim_boundaries.get(field) is not False for field in required_false_claims): + errors.append("a prohibited safety or evidence claim was enabled") + + stock = manifest.get("stock_identities", {}) + if ( + stock.get("classification") != "reference_only" + or stock.get("current_device_observed") is not False + ): + errors.append("stock identities were promoted from reference-only") + elfldr = stock.get("elfldr", {}) + if ( + elfldr.get("size") != 397000 + or elfldr.get("sha256") != STOCK_ELFLDR_SHA256 + or elfldr.get("current_device_match") != "UNPROVEN" + ): + errors.append("stock elfldr reference changed or was promoted") + manager = stock.get("payload_manager", {}) + if ( + manager.get("size") != 2050320 + or manager.get("sha256") != STOCK_PAYLOAD_MANAGER_SHA256 + or manager.get("current_device_match") != "UNPROVEN" + ): + errors.append("stock Payload Manager reference changed or was promoted") + + backup = manifest.get("payload_manager_backup", {}) + if backup != { + "classification": "hard_blocker", + "on_device_proven": False, + "byte_exact_proven": False, + "creation_allowed_in_strict_read_only_phase": False, + "result": "HARD_STOP-GATE", + }: + errors.append("Payload Manager backup hard blocker changed") + + entries = denylist.get("entries", []) + if ( + denylist.get("fail_closed") is not True + or len(entries) != 1 + or entries[0].get("sha256") != DENYLIST_SHA256 + or entries[0].get("status") != "BLOCKED" + or entries[0].get("permanent") is not True + or entries[0].get("execution_eligible") is not False + ): + errors.append("permanent denylist binding changed") + + blockers = { + item.get("id"): item.get("severity") for item in manifest.get("blockers", []) + } + if blockers != REQUIRED_BLOCKERS: + errors.append("remediation blocker set changed") + + findings = { + item.get("id"): item for item in manifest.get("side_effect_findings", []) + } + if set(findings) != REQUIRED_FINDINGS: + errors.append("Payload Manager side-effect finding set changed") + for finding_id, finding in findings.items(): + if finding.get("strict_read_only_preflight_suitable") is not False: + errors.append(f"{finding_id}: incorrectly marked strict-read-only suitable") + references = finding.get("source_references") + if not isinstance(references, list) or not references: + errors.append(f"{finding_id}: source references are absent") + if finding_id != "options_any_endpoint": + if finding.get("http_method") == "OPTIONS": + errors.append(f"{finding_id}: non-OPTIONS finding mislabeled") + if finding.get("writes_server_active_flag") is not True: + errors.append(f"{finding_id}: server_active_flag mutation hidden") + options = findings.get("options_any_endpoint", {}) + if ( + options.get("http_method") != "OPTIONS" + or options.get("writes_server_active_flag") is not False + or options.get("strict_read_only_preflight_suitable") is not False + ): + errors.append("OPTIONS route classification changed") + autoload = findings.get("get_autoload_status", {}) + if ( + autoload.get("endpoint") != "/autoload_status" + or autoload.get("writes_autoload_triggered") is not True + or autoload.get("reads_filesystem_or_configuration") is not True + ): + errors.append("/autoload_status mutations or reads were hidden") + + source = manifest.get("payload_manager_source", {}) + if ( + source.get("commit") != PAYLOAD_MANAGER_COMMIT + or source.get("release") != "v0.3.1" + ): + errors.append("Payload Manager source identity changed") + source_files = source.get("files") + if not isinstance(source_files, list) or len(source_files) < 3: + errors.append("Payload Manager source-file evidence is incomplete") + + observations = { + item.get("id"): item for item in manifest.get("evidence_contract", []) + } + if set(observations) != REQUIRED_OBSERVATIONS: + errors.append("future evidence-contract observation set changed") + for observation_id, observation in observations.items(): + if observation.get("confidence") != "UNPROVEN": + errors.append(f"{observation_id}: confidence was promoted") + if observation.get("timeout_ms") is not None: + errors.append(f"{observation_id}: timeout invented before tool review") + if observation.get("fail_closed_result") != "STOP": + errors.append(f"{observation_id}: fail-closed result changed") + identity = observation.get("required_collector_identity") + output = observation.get("reviewer_output") + if not isinstance(identity, list) or not identity: + errors.append(f"{observation_id}: collector identity contract absent") + if not isinstance(output, list) or not output: + errors.append(f"{observation_id}: reviewer output contract absent") + + prohibited = set(manifest.get("prohibited_actions", [])) + if prohibited != REQUIRED_PROHIBITED_ACTIONS: + errors.append("prohibited-action set changed") + future = manifest.get("future_activity", {}) + if ( + future.get("mode") != "design_only" + or future.get("bounded_observation_implemented") is not False + or future.get("collector_selected") is not False + or future.get("transfer_method_selected") is not False + or future.get("execution_method_selected") is not False + or future.get("new_explicit_authorization_required") is not True + ): + errors.append("future bounded observation was promoted beyond design") + retroarch = manifest.get("retroarch", {}) + if retroarch != { + "goal": "long_term_goal", + "active_phase": False, + "work_started": False, + "dependency_chain_only": True, + }: + errors.append("RetroArch was promoted into the active phase") + + return errors + + +def validate_template(template: dict[str, Any]) -> list[str]: + errors: list[str] = [] + if template.get("template_only") is not True: + errors.append("bounded-observation template is not template-only") + for field in ( + "authorized", + "execution_authorized", + "transfer_authorized", + "installation_authorized", + "lifecycle_authorized", + "automatic_retry", + ): + if template.get(field) is not False: + errors.append(f"template field {field} is not false") + required = template.get("required_fields", {}) + if set(required) != TEMPLATE_REQUIRED_FIELDS: + errors.append("bounded-observation required-field set changed") + elif any(value is not None for value in required.values()): + errors.append("bounded-observation template contains prefilled request data") + exclusions = template.get("fixed_exclusions", {}) + expected_exclusions = { + "installation", + "lifecycle_probe", + "autoload", + "automatic_retry", + "gnm", + "videoout", + "sdl", + "retroarch", + } + if set(exclusions) != expected_exclusions or any( + value is not True for value in exclusions.values() + ): + errors.append("bounded-observation fixed exclusions changed") + return errors + + +def validate_doc_contract( + contract: dict[str, Any], manifest: dict[str, Any] +) -> list[str]: + errors: list[str] = [] + if contract.get("status") != manifest.get("status"): + errors.append("documentation/manifest status mismatch") + if contract.get("authorization") != manifest.get("authorization"): + errors.append("documentation/manifest authorization mismatch") + blocker_ids = [item.get("id") for item in manifest.get("blockers", [])] + if contract.get("blockers") != blocker_ids: + errors.append("documentation/manifest blocker mismatch") + if contract.get("firmware_runtime_behavior") != "UNPROVEN": + errors.append("documentation promoted firmware behavior") + if contract.get("stock_identity_classification") != "reference_only": + errors.append("documentation promoted stock identities") + if contract.get("payload_manager_backup_classification") != "hard_blocker": + errors.append("documentation weakened the manager backup blocker") + if contract.get("retroarch_active_phase") is not False: + errors.append("documentation promoted RetroArch into the active phase") + return errors + + +def validate_immutable_evidence(root: Path) -> list[str]: + errors: list[str] = [] + for relative, expected in IMMUTABLE_EVIDENCE.items(): + path = root / relative + if not path.is_file(): + errors.append(f"immutable evidence missing: {relative}") + elif sha256(path) != expected["sha256"]: + errors.append(f"immutable evidence hash mismatch: {relative}") + return errors + + +def changed_paths(root: Path) -> list[Path]: + result = subprocess.run( + ["git", "status", "--porcelain=v1", "--untracked-files=all"], + cwd=root, + check=True, + capture_output=True, + text=True, + ) + paths: list[Path] = [] + for line in result.stdout.splitlines(): + value = line[3:] + for candidate in value.split(" -> "): + candidate = candidate.strip('"') + paths.append(Path(candidate)) + return paths + + +def validate_changed_files(root: Path) -> list[str]: + errors: list[str] = [] + for path in changed_paths(root): + if path.suffix.lower() in TARGET_SUFFIXES: + errors.append(f"target artifact appears in change set: {path}") + normalized = path.as_posix() + if normalized.startswith( + ("src/", "include/", "samples/", "adapters/", "work/upstream/") + ): + errors.append(f"production/runtime source changed in remediation: {path}") + return errors + + +def validate_local_payload_manager_source( + root: Path, manifest: dict[str, Any] +) -> list[str]: + errors: list[str] = [] + source_root = root / "work/upstream/pldmgr-v0.3.1" + if not source_root.is_dir(): + return ["required local Payload Manager source checkout is absent"] + head = subprocess.run( + ["git", "rev-parse", "HEAD"], + cwd=source_root, + check=True, + capture_output=True, + text=True, + ).stdout.strip() + if head != PAYLOAD_MANAGER_COMMIT: + errors.append("local Payload Manager source commit changed") + status = subprocess.run( + ["git", "status", "--porcelain"], + cwd=source_root, + check=True, + capture_output=True, + text=True, + ).stdout.strip() + if status: + errors.append("local Payload Manager source checkout is dirty") + for item in manifest["payload_manager_source"]["files"]: + path = source_root / item["path"] + if not path.is_file(): + errors.append(f"Payload Manager source file missing: {item['path']}") + elif sha256(path) != item["sha256"]: + errors.append(f"Payload Manager source hash mismatch: {item['path']}") + return errors + + +def collect_errors(root: Path, require_local_source: bool = False) -> list[str]: + manifest = load_json(root / "manifests/runtime/phase-0.8-remediation.json") + denylist = load_json(root / "manifests/artifact-denylist.json") + doc_contract = extract_json_contract( + root / "docs/runtime/phase-0.8-remediation.md", "PHASE08R_CONTRACT" + ) + template = extract_json_contract( + root / "docs/approvals/phase-0.8-bounded-observation-template.md", + "PHASE08_BOUNDED_OBSERVATION_TEMPLATE", + ) + errors = validate_immutable_evidence(root) + errors.extend(validate_manifest(manifest, denylist)) + errors.extend(validate_doc_contract(doc_contract, manifest)) + errors.extend(validate_template(template)) + errors.extend(validate_changed_files(root)) + if require_local_source: + errors.extend(validate_local_payload_manager_source(root, manifest)) + return errors + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + parser.add_argument( + "--require-local-source", + action="store_true", + help="also require and rehash the ignored pinned Payload Manager checkout", + ) + args = parser.parse_args() + root = args.root.resolve() + errors = collect_errors(root, args.require_local_source) + if errors: + for error in errors: + print(f"Phase-0.8R validation failed: {error}") + return 1 + print( + "Phase-0.8R remediation validation passed: " + f"{len(IMMUTABLE_EVIDENCE)} immutable files, " + f"{len(REQUIRED_FINDINGS)} side-effect findings, " + f"{len(REQUIRED_OBSERVATIONS)} observation contracts, " + f"{len(REQUIRED_BLOCKERS)} blockers; hardware evidence not claimed" + ) + return 0 + + +if __name__ == "__main__": + try: + raise SystemExit(main()) + except ( + KeyError, + OSError, + subprocess.CalledProcessError, + TypeError, + ValueError, + json.JSONDecodeError, + ) as error: + print(f"Phase-0.8R validation failed: {error}") + raise SystemExit(1) from error diff --git a/tools/validate_phase09b_observer_audit.py b/tools/validate_phase09b_observer_audit.py new file mode 100644 index 0000000..40fdcce --- /dev/null +++ b/tools/validate_phase09b_observer_audit.py @@ -0,0 +1,464 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate the blocked, offline-only Phase-0.9B observer audit.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +from pathlib import Path +import re +import subprocess +import sys +from typing import Any + + +EXPECTED_DECISION = { + "OBSERVER_STARTUP_OR_EXIT_ABI_UNPROVEN", + "NO_PROVEN_NON_PERSISTENT_OUTPUT_CHANNEL", +} +AUTHORIZATION_FIELDS = ( + "authorized", + "transfer_authorized", + "execution_authorized", + "installation_authorized", + "lifecycle_authorized", + "autoload_authorized", + "backup_creation_authorized", + "automatic_retry", +) +SOURCE_COMMITS = { + "hardened_elfldr": ("../chimera-elfldr", "197623058f509eddde18868dafcb92fdcac66464"), + "controlled_payload_manager": ( + "../chimera-ps5-payload-manager", + "e23d94ff91233aa770e2342800c1467875bdef44", + ), + "elfldr_public_base": ( + "work/upstream/elfldr-v0.23", + "699e8bcff03e91e8d6ca6eba281af25c5a58d8c2", + ), + "payload_manager_public_base": ( + "work/upstream/pldmgr-v0.3.1", + "cfbc70f30f419b09bf2b52283f7409e2d3117ee1", + ), + "ps5_payload_sdk_v0_41": ( + "work/upstream/sdk", + "d2e2e585740362976a39fdd5ccf390f199a7bc37", + ), +} + + +def load_json(path: Path) -> dict[str, Any]: + with path.open("r", encoding="utf-8") as handle: + value = json.load(handle) + if not isinstance(value, dict): + raise ValueError(f"{path} must contain a JSON object") + return value + + +def extract_json_contract(path: Path, marker: str) -> dict[str, Any]: + text = path.read_text(encoding="utf-8") + pattern = ( + rf"\s*```json\s*(.*?)\s*```\s*" + rf"" + ) + match = re.search(pattern, text, flags=re.DOTALL) + if not match: + raise ValueError(f"{path} is missing {marker}") + value = json.loads(match.group(1)) + if not isinstance(value, dict): + raise ValueError(f"{marker} must be a JSON object") + return value + + +def sha256_file(path: Path) -> str: + digest = hashlib.sha256() + with path.open("rb") as handle: + for chunk in iter(lambda: handle.read(1024 * 1024), b""): + digest.update(chunk) + return digest.hexdigest() + + +def git_output(root: Path, *args: str) -> str: + return subprocess.check_output( + ["git", *args], cwd=root, text=True, encoding="utf-8" + ).strip() + + +def validate_authorizations(value: dict[str, Any], prefix: str) -> list[str]: + errors: list[str] = [] + for field in AUTHORIZATION_FIELDS: + if value.get(field) is not False: + errors.append(f"{prefix}.{field} must be false") + return errors + + +def validate_schema(schema: dict[str, Any], manifest: dict[str, Any]) -> list[str]: + errors: list[str] = [] + properties = schema.get("properties", {}) + for field in AUTHORIZATION_FIELDS: + if properties.get(field, {}).get("const") is not False: + errors.append(f"schema {field} must be const false") + if properties.get("device_address", {}).get("const", "missing") is not None: + errors.append("schema device_address must be const null") + if properties.get("maximum_execution_count", {}).get("const") != 0: + errors.append("schema maximum_execution_count must be const zero") + + default = schema.get("x-chimera-default-plan") + if default != manifest.get("default_observation_plan"): + errors.append("schema default plan must equal manifest default plan") + if not isinstance(default, dict): + return errors + errors.extend(validate_authorizations(default, "default_plan")) + if default.get("device_address") is not None: + errors.append("default plan contains a device address") + if default.get("device_identity") is not None: + errors.append("default plan contains a device identity") + if default.get("read_paths") != []: + errors.append("default plan contains read paths") + if default.get("allowed_observations") != []: + errors.append("default plan contains observations") + if default.get("output_channel") is not None: + errors.append("default plan contains an output channel") + if default.get("maximum_execution_count") != 0: + errors.append("default plan permits an execution") + return errors + + +def validate_manifest(root: Path, manifest: dict[str, Any]) -> list[str]: + errors: list[str] = [] + if manifest.get("status") != "BLOCKED": + errors.append("Phase-0.9B status must be BLOCKED") + if set(manifest.get("decision", [])) != EXPECTED_DECISION: + errors.append("Phase-0.9B hard-gate decision changed") + errors.extend( + validate_authorizations(manifest.get("authorization", {}), "authorization") + ) + + canonical = manifest.get("canonical_state_preserved", {}) + expected_canonical = { + "historical_phase08_status": "READ_ONLY_PREFLIGHT_BLOCKED", + "phase09a_status": "DESIGN_ONLY", + "firmware_runtime_behavior": "UNPROVEN", + "stock_hashes": "reference_only", + "payload_manager_backup": "HARD_BLOCKER", + "device_contact_performed": False, + "device_transfer_performed": False, + "device_execution_performed": False, + } + for field, expected in expected_canonical.items(): + if canonical.get(field) != expected: + errors.append(f"canonical state {field} must be {expected!r}") + + deny_binding = manifest.get("permanent_denylist_binding", {}) + if deny_binding != { + "sha256": "4be1c17b4964f2b68c39b5145bc4af4619c32512d60269ecf5c39728b390fa63", + "status": "BLOCKED", + "permanent": True, + "execution_eligible": False, + }: + errors.append("permanent denylist binding changed") + + commits = manifest.get("source_commits", {}) + tree_status = manifest.get("source_tree_status", {}) + for key, (relative, expected_commit) in SOURCE_COMMITS.items(): + if commits.get(key) != expected_commit: + errors.append(f"manifest source commit mismatch: {key}") + path = (root / relative).resolve() + if not path.exists(): + errors.append(f"source tree missing: {relative}") + continue + try: + actual_commit = git_output(path, "rev-parse", "HEAD") + dirty = git_output(path, "status", "--porcelain") + except (OSError, subprocess.CalledProcessError) as exc: + errors.append(f"source tree unreadable: {relative}: {exc}") + continue + if actual_commit != expected_commit: + errors.append(f"source tree commit mismatch: {relative}") + if dirty: + errors.append(f"source tree is dirty: {relative}") + if tree_status.get(key) != "clean": + errors.append(f"manifest does not classify {key} as clean") + + for evidence in manifest.get("source_evidence", []): + relative = evidence.get("path") + expected_digest = evidence.get("sha256") + if not isinstance(relative, str) or not isinstance(expected_digest, str): + errors.append("source evidence entry lacks path or SHA-256") + continue + path = (root / relative).resolve() + if not path.is_file(): + errors.append(f"source evidence file missing: {relative}") + continue + if sha256_file(path) != expected_digest: + errors.append(f"source evidence digest mismatch: {relative}") + if "size" in evidence and path.stat().st_size != evidence["size"]: + errors.append(f"source evidence size mismatch: {relative}") + + matrix = manifest.get("capability_matrix", []) + expected_facts = { + "firmware_source_1", + "firmware_source_2", + "file_metadata", + "object_identity", + "sha256", + "mount_information", + "processes_services", + "listeners", + "autoload_configuration", + "output_channel", + "monotonic_time_deadline", + "process_exit", + } + if {entry.get("needed_fact") for entry in matrix} != expected_facts: + errors.append("capability matrix is incomplete or changed") + if any(entry.get("implement") is not False for entry in matrix): + errors.append("a blocked capability is marked for implementation") + + gate = manifest.get("build_gate", {}) + for field in ( + "startup_and_exit_abi_proven", + "non_persistent_output_channel_proven", + "normal_sdk_crt_kernelwrite_free", + "custom_freestanding_cleanup_proven", + "observer_source_created", + "observer_target_declared", + "target_build_performed", + "double_clean_build_performed", + ): + if gate.get(field) is not False: + errors.append(f"build gate {field} must be false") + if gate.get("reason") != "BLOCKED_BEFORE_SOURCE_AND_BUILD": + errors.append("build gate reason changed") + + implementation = manifest.get("implementation", {}) + if implementation.get("observer_logic_implemented") is not False: + errors.append("observer logic must remain unimplemented") + if implementation.get("observations_implemented") != []: + errors.append("target observations must remain unimplemented") + + artifact = manifest.get("artifact", {}) + if artifact.get("present") is not False: + errors.append("observer artifact must be absent") + for field in ("path", "sha256", "size"): + if artifact.get(field) is not None: + errors.append(f"artifact {field} must be null") + for field in ( + "imports", + "undefined_symbols", + "dynamic_dependencies", + "network_functions", + "filesystem_reads", + ): + if artifact.get(field) != []: + errors.append(f"artifact {field} must be empty") + for field in ( + "installation_eligible", + "lifecycle_eligible", + "autoload_eligible", + "execution_authorized", + "execution_eligible", + ): + if artifact.get(field) is not False: + errors.append(f"artifact {field} must be false") + + if ( + manifest.get("static_artifact_audit", {}).get("status") + != "NOT_PERFORMED_BLOCKED_BEFORE_BUILD" + ): + errors.append("artifact audit must be recorded as not performed") + reproducibility = manifest.get("reproducibility", {}) + if reproducibility.get("status") != "NOT_PERFORMED_BLOCKED_BEFORE_BUILD": + errors.append("reproducibility must be recorded as not performed") + for field in ("build_1_sha256", "build_2_sha256", "byte_identical"): + if reproducibility.get(field) is not None: + errors.append(f"reproducibility {field} must be null") + return errors + + +def validate_denylist(root: Path) -> list[str]: + denylist = load_json(root / "manifests/artifact-denylist.json") + for entry in denylist.get("entries", []): + if ( + entry.get("sha256") + == "4be1c17b4964f2b68c39b5145bc4af4619c32512d60269ecf5c39728b390fa63" + and entry.get("status") == "BLOCKED" + and entry.get("permanent") is True + and entry.get("execution_eligible") is False + ): + return [] + return ["permanent blocked artifact is missing from the denylist"] + + +def validate_repository_boundary(root: Path) -> list[str]: + errors: list[str] = [] + cmake = (root / "CMakeLists.txt").read_text(encoding="utf-8") + forbidden_cmake = ( + "CHIMERA_GFX_BUILD_PS5_OBSERVER", + "chimera-gfx-bounded-observer", + "phase09b-observer.elf", + ) + for value in forbidden_cmake: + if value in cmake: + errors.append(f"blocked observer target found in CMake: {value}") + + forbidden_paths = ( + root / "samples/phase09b_observer", + root / "samples/bounded_observer", + root / "src/observer", + root / "packaging/phase09b/observer.elf", + ) + for path in forbidden_paths: + if path.exists(): + errors.append(f"blocked observer source/artifact path exists: {path}") + + for base in (root / "samples", root / "src", root / "packaging"): + for path in base.rglob("*"): + if not path.is_file(): + continue + lowered = path.as_posix().lower() + if path.suffix.lower() in {".c", ".cc", ".cpp", ".s", ".asm"} and ( + "phase09b" in lowered or "bounded_observer" in lowered + ): + errors.append(f"blocked observer target source exists: {path}") + if path.suffix.lower() in {".elf", ".self", ".sprx", ".pkg", ".map"} and ( + "phase09b" in lowered or "observer" in lowered + ): + errors.append(f"blocked observer target artifact exists: {path}") + + try: + tracked = git_output(root, "ls-files").splitlines() + except (OSError, subprocess.CalledProcessError) as exc: + return [f"could not inspect tracked files: {exc}"] + for relative in tracked: + lowered = relative.lower() + if "phase09b" in lowered or "phase-0.9b" in lowered: + if lowered.endswith((".elf", ".self", ".sprx", ".pkg", ".map")): + errors.append(f"tracked Phase-0.9B target artifact exists: {relative}") + if "phase09b" in lowered and ( + "install" in lowered or "lifecycle-package" in lowered + ): + errors.append(f"Phase-0.9B install/lifecycle package exists: {relative}") + return errors + + +def validate_review_checksums(root: Path) -> list[str]: + errors: list[str] = [] + expected_paths = { + "docs/approvals/phase-0.9b-observer-execution-template.md", + "docs/runtime/phase-0.9b-bounded-observer-design.md", + "docs/runtime/phase-0.9b-observer-limitations.md", + "docs/runtime/phase-0.9b-observer-result-contract.md", + "docs/runtime/phase-0.9b-observer-static-audit.md", + "manifests/runtime/phase-0.9b-observation-plan.schema.json", + "manifests/runtime/phase-0.9b-observer.json", + "tests/phase09b_observer_model.py", + "tests/test_phase09b_observer_audit.py", + "tools/validate_phase09b_observer_audit.py", + } + checksum_path = root / "packaging/phase09b/SHA256SUMS.txt" + observed: set[str] = set() + for line_number, line in enumerate( + checksum_path.read_text(encoding="utf-8").splitlines(), start=1 + ): + match = re.fullmatch(r"([0-9a-f]{64}) ([^\r\n]+)", line) + if not match: + errors.append(f"invalid checksum line {line_number}") + continue + expected_digest, relative = match.groups() + if relative in observed: + errors.append(f"duplicate checksum path: {relative}") + continue + observed.add(relative) + path = root / relative + if not path.is_file(): + errors.append(f"checksummed file missing: {relative}") + elif sha256_file(path) != expected_digest: + errors.append(f"review checksum mismatch: {relative}") + if observed != expected_paths: + errors.append("Phase-0.9B review checksum inventory is incomplete or expanded") + return errors + + +def collect_errors(root: Path, require_source_trees: bool = True) -> list[str]: + errors: list[str] = [] + manifest_path = root / "manifests/runtime/phase-0.9b-observer.json" + schema_path = root / "manifests/runtime/phase-0.9b-observation-plan.schema.json" + required = [ + manifest_path, + schema_path, + root / "docs/runtime/phase-0.9b-bounded-observer-design.md", + root / "docs/runtime/phase-0.9b-observer-static-audit.md", + root / "docs/runtime/phase-0.9b-observer-result-contract.md", + root / "docs/runtime/phase-0.9b-observer-limitations.md", + root / "docs/approvals/phase-0.9b-observer-execution-template.md", + root / "tests/phase09b_observer_model.py", + root / "tests/test_phase09b_observer_audit.py", + root / "packaging/phase09b/SHA256SUMS.txt", + ] + for path in required: + if not path.is_file(): + errors.append(f"required Phase-0.9B file missing: {path}") + if errors: + return errors + + manifest = load_json(manifest_path) + schema = load_json(schema_path) + errors.extend(validate_manifest(root, manifest)) + if not require_source_trees: + errors = [ + error + for error in errors + if not error.startswith(("source tree missing:", "source tree unreadable:")) + ] + errors.extend(validate_schema(schema, manifest)) + errors.extend(validate_denylist(root)) + errors.extend(validate_repository_boundary(root)) + errors.extend(validate_review_checksums(root)) + + template = extract_json_contract( + root / "docs/approvals/phase-0.9b-observer-execution-template.md", + "PHASE09B_OBSERVER_EXECUTION_TEMPLATE", + ) + errors.extend(validate_authorizations(template, "execution_template")) + if template.get("status") != "BLOCKED": + errors.append("execution template must remain BLOCKED") + required_fields = template.get("required_fields", {}) + if any(value is not None for value in required_fields.values()): + errors.append("execution template contains prefilled request-specific values") + + for relative in ( + "docs/runtime/phase-0.9b-bounded-observer-design.md", + "docs/runtime/phase-0.9b-observer-limitations.md", + ): + text = (root / relative).read_text(encoding="utf-8") + normalized_text = re.sub(r"[^A-Z0-9]+", " ", text.upper()).strip() + for decision in EXPECTED_DECISION: + if decision.replace("_", " ") not in normalized_text: + errors.append(f"{relative} does not state blocker {decision}") + return errors + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + parser.add_argument( + "--no-source-tree-check", + action="store_true", + help="Skip only missing external source-tree errors for packaged review.", + ) + args = parser.parse_args() + root = args.root.resolve() + errors = collect_errors(root, require_source_trees=not args.no_source_tree_check) + if errors: + for error in errors: + print(f"ERROR: {error}", file=sys.stderr) + return 1 + print("Phase-0.9B blocked observer audit: PASS") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/validate_phase09c_feasibility.py b/tools/validate_phase09c_feasibility.py new file mode 100644 index 0000000..c9871a5 --- /dev/null +++ b/tools/validate_phase09c_feasibility.py @@ -0,0 +1,687 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate the blocked, host-only Phase-0.9C feasibility closure.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +from pathlib import Path +import re +import subprocess +from typing import Any + + +EXPECTED_CLASSIFICATION = "BLOCKED_MULTIPLE_FOUNDATIONAL_CONTRACTS" +EXPECTED_BASELINE = "3ddc213ea67bb286256ae42e52c65e00488608ca" +EXPECTED_BRANCH = "codex/chimera-gfx-phase09c-execution-feasibility" +BLOCKED_HASH = ( + "4be1c17b4964f2b68c39b5145bc4af4619c32512d60269ecf5c39728b390fa63" +) + +AUTHORIZATION_FIELDS = ( + "authorized", + "transfer_authorized", + "execution_authorized", + "installation_authorized", + "lifecycle_authorized", + "autoload_authorized", + "backup_creation_authorized", + "observer_build_authorized", + "automatic_retry", +) + +SOURCE_COMMITS = { + "hardened_elfldr": ( + "../chimera-elfldr", + "197623058f509eddde18868dafcb92fdcac66464", + ), + "controlled_payload_manager": ( + "../chimera-ps5-payload-manager", + "e23d94ff91233aa770e2342800c1467875bdef44", + ), + "elfldr_public_base": ( + "work/upstream/elfldr-v0.23", + "699e8bcff03e91e8d6ca6eba281af25c5a58d8c2", + ), + "payload_manager_public_base": ( + "work/upstream/pldmgr-v0.3.1", + "cfbc70f30f419b09bf2b52283f7409e2d3117ee1", + ), + "ps5_payload_sdk_v0_41": ( + "work/upstream/sdk", + "d2e2e585740362976a39fdd5ccf390f199a7bc37", + ), +} + +SOURCE_HASHES = { + "../chimera-elfldr/elfldr.c": ( + "9949f8e4037984d10f1f5aa498e4665593d1fac8a33614d7f2141349839bb803" + ), + "../chimera-elfldr/socksrv.c": ( + "d642ced3e9b4a296dd15e355050ebe956f53a6dfdaa6ac10109cd067a3bba3d7" + ), + "../chimera-elfldr/controlled_runtime.c": ( + "10145f3bbb3b54e3d715b1667e45c6f12d3c7f52a04342ab104ec8dd49e384c9" + ), + "../chimera-elfldr/ps5_controlled.c": ( + "68717ef1cc31c483743c5af325c59e73a90f66e93b880b989e349a4ddc748772" + ), + "../chimera-ps5-payload-manager/src/verified_launcher.c": ( + "066100ca4917c7acc560e2e85666ca136cd7ccfd9094417377048f41106dd56e" + ), + "../chimera-ps5-payload-manager/src/ps5_launcher.c": ( + "29c1a5fd01784a59e88b3698940f120cb03020071bc2b7d74a1da1a51524ef59" + ), + "work/upstream/sdk/crt/crt.c": ( + "3875f4739ec40b33f1f4967a1acbb585a527d2c281a360153e1f69c8b945932a" + ), + "work/upstream/sdk/crt/patch.c": ( + "4f76a677bba54f4641e1cf2755768c29afb7464a35b2f21e85db2aa2785eceac" + ), + "work/upstream/sdk/crt/Makefile": ( + "6a62f777f32ab05cbe7bff81c00cfd6639ba8bf440aba611a40ff0b62732568b" + ), + "work/upstream/sdk/host/bin/prospero-clang": ( + "0cf49ae43d6110a7606c0ee4d702fc4b5d5e1c3ae9a722945a48b80294e295ba" + ), + "work/upstream/sdk/host/elf_x86_64.x": ( + "169b80d01da601ef96bbc584986608dec1d9c01397eae81eccd4e1a66b0a6c6a" + ), + "work/upstream/sdk/crt/kernel.c": ( + "ac1c375aae8d3cb1be5fb8bad2f4e6492b6ec1f1450977b534202f46b5b70321" + ), + "work/upstream/sdk/sce_stubs/libkernel_web.c": ( + "dca70757a0680ede52502fe7db10060fe30506f7ed0adfc43ce00700145ab4ff" + ), + "samples/lifecycle_probe/main.c": ( + "1ae7df1fe921ccab2a252f77975d3d441ef7725e34535b024580c0d4a242d766" + ), +} + +IMMUTABLE_HASHES = { + "docs/runtime/phase-0.8-read-only-preflight.md": ( + "3fbe086175a6048176075f447ec1482074928e3b5282db97ea2169395fe1d508" + ), + "manifests/runtime/phase-0.8-read-only-preflight.json": ( + "47d7f452f8799979fe99b3e6d56859f03544112725bf7e5b349eba5ed81b3322" + ), + "tests/test_phase08_preflight.py": ( + "8a4ad7c70de28ffe3148fd3fd1f68c36a872c53c691c9068e1ff163970863c48" + ), + "manifests/runtime/phase-0.9-anti-brick-design.json": ( + "39fd7c70cae998d9d74e7caf1ff3c19f9f76f7de2e5a50bc09baede4ba53e9e9" + ), + "manifests/runtime/phase-0.9b-observer.json": ( + "104c4a667ad17f9827fc7276852c6faeef96effb8e3561a5ebc19a62c7d51634" + ), + "manifests/runtime/phase-0.9b-observation-plan.schema.json": ( + "efcea3b0001ef5b2da65c372ceb93ee2fec09c9331b2e4cbb6008212504c0918" + ), +} + +CHECKSUM_FILES = ( + "docs/runtime/phase-0.9c-capability-closure.md", + "docs/runtime/phase-0.9c-output-channel-feasibility.md", + "docs/runtime/phase-0.9c-side-effect-model.md", + "docs/runtime/phase-0.9c-startup-exit-feasibility.md", + "docs/runtime/phase-0.9c-static-audit.md", + "manifests/runtime/phase-0.9c-feasibility.json", + "manifests/runtime/phase-0.9c-feasibility.schema.json", + "tests/phase09c_feasibility_model.py", + "tests/test_phase09c_feasibility.py", + "tests/test_phase09c_protocol.py", + "tools/validate_phase09c_feasibility.py", +) + +CAPABILITIES = { + "runtime_self_identity", + "firmware_source_1", + "firmware_source_2", + "mount_query", + "metadata", + "object_id", + "size", + "sha256", + "processes", + "services", + "listeners", + "autoload", + "rollback_objects", + "monotonic_time", + "startup", + "output", + "normal_exit", + "error_exit", + "timeout", + "cleanup", + "recovery_independence", +} + +OUTPUT_ARCHITECTURES = ( + ( + "D1_CALLER_OWNED_BOUNDED_BUFFER", + "CONCEPT_FEASIBLE_REQUIRES_LOADER_CHANGE_AND_EXIT_PROOF", + ), + ("D2_EXISTING_REQUEST_RESPONSE", "REJECTED_SEND_ONLY_NO_RESULT_RECEIVE"), + ( + "D3_LOADER_OWNED_STATUS_RECORD", + "UNPROVEN_REQUIRES_LOADER_STATE_AND_PROPAGATION_CHANGE", + ), + ( + "D4_PROCESS_EXIT_STATUS", + "REJECTED_WAIT_STATUS_DISCARDED_AND_AMBIGUOUS", + ), +) + +FORBIDDEN_PHASE09C_SUFFIXES = { + ".c", + ".cc", + ".cpp", + ".cxx", + ".s", + ".asm", + ".o", + ".obj", + ".elf", + ".self", + ".sprx", + ".map", + ".pkg", + ".zip", + ".tar", + ".gz", +} + + +def load_json(path: Path) -> dict[str, Any]: + with path.open("r", encoding="utf-8") as handle: + value = json.load(handle) + if not isinstance(value, dict): + raise ValueError(f"{path} must contain a JSON object") + return value + + +def sha256_file(path: Path) -> str: + digest = hashlib.sha256() + with path.open("rb") as handle: + for chunk in iter(lambda: handle.read(1024 * 1024), b""): + digest.update(chunk) + return digest.hexdigest() + + +def git_output(root: Path, *args: str) -> str: + return subprocess.check_output( + ["git", *args], cwd=root, text=True, encoding="utf-8" + ).strip() + + +def _json_type_matches(expected: str, value: Any) -> bool: + if expected == "object": + return isinstance(value, dict) + if expected == "array": + return isinstance(value, list) + if expected == "string": + return isinstance(value, str) + if expected == "integer": + return isinstance(value, int) and not isinstance(value, bool) + if expected == "boolean": + return isinstance(value, bool) + if expected == "null": + return value is None + return True + + +def validate_schema_instance( + schema: dict[str, Any], value: Any, path: str = "$" +) -> list[str]: + """Validate the JSON-Schema features used by the Phase-0.9C schema.""" + + errors: list[str] = [] + if "const" in schema and value != schema["const"]: + errors.append(f"{path} differs from schema const") + return errors + expected_type = schema.get("type") + if isinstance(expected_type, str) and not _json_type_matches(expected_type, value): + errors.append(f"{path} is not {expected_type}") + return errors + + if isinstance(value, dict): + required = schema.get("required", []) + for key in required: + if key not in value: + errors.append(f"{path}.{key} is required") + properties = schema.get("properties", {}) + if schema.get("additionalProperties") is False: + for key in value: + if key not in properties: + errors.append(f"{path}.{key} is not allowed") + for key, child_schema in properties.items(): + if key in value: + errors.extend( + validate_schema_instance(child_schema, value[key], f"{path}.{key}") + ) + + if isinstance(value, list): + minimum = schema.get("minItems") + maximum = schema.get("maxItems") + if isinstance(minimum, int) and len(value) < minimum: + errors.append(f"{path} has fewer than {minimum} items") + if isinstance(maximum, int) and len(value) > maximum: + errors.append(f"{path} has more than {maximum} items") + if schema.get("uniqueItems") is True: + normalized = [json.dumps(item, sort_keys=True) for item in value] + if len(normalized) != len(set(normalized)): + errors.append(f"{path} contains duplicate items") + item_schema = schema.get("items") + if isinstance(item_schema, dict): + for index, item in enumerate(value): + errors.extend( + validate_schema_instance(item_schema, item, f"{path}[{index}]") + ) + return errors + + +def validate_authorizations(value: dict[str, Any]) -> list[str]: + errors: list[str] = [] + for field in AUTHORIZATION_FIELDS: + if value.get(field) is not False: + errors.append(f"authorization.{field} must be false") + return errors + + +def validate_manifest(manifest: dict[str, Any]) -> list[str]: + errors: list[str] = [] + if manifest.get("schema_version") != 1 or manifest.get("phase") != "0.9C": + errors.append("Phase-0.9C manifest identity changed") + if manifest.get("status") != "BLOCKED": + errors.append("Phase-0.9C status must remain BLOCKED") + if manifest.get("classification") != EXPECTED_CLASSIFICATION: + errors.append("Phase-0.9C classification changed") + if manifest.get("baseline_commit") != EXPECTED_BASELINE: + errors.append("Phase-0.9C baseline changed") + if manifest.get("branch") != EXPECTED_BRANCH: + errors.append("Phase-0.9C branch changed") + errors.extend(validate_authorizations(manifest.get("authorization", {}))) + + canonical = manifest.get("canonical_state_preserved", {}) + expected_canonical = { + "historical_phase08_status": "READ_ONLY_PREFLIGHT_BLOCKED", + "phase09a_status": "DESIGN_ONLY", + "phase09b_status": "BLOCKED", + "firmware_runtime_behavior": "UNPROVEN", + "stock_hashes": "reference_only", + "payload_manager_backup": "HARD_BLOCKER", + "device_contact_performed": False, + "device_transfer_performed": False, + "device_execution_performed": False, + "target_build_performed": False, + } + if canonical != expected_canonical: + errors.append("canonical Phase-0.8/0.9 state changed") + + deny = manifest.get("permanent_denylist_binding", {}) + if deny != { + "sha256": BLOCKED_HASH, + "status": "BLOCKED", + "permanent": True, + "execution_eligible": False, + }: + errors.append("permanent artifact denial changed") + + source_commits = manifest.get("source_commits", {}) + for key, (_, expected_commit) in SOURCE_COMMITS.items(): + if source_commits.get(key) != expected_commit: + errors.append(f"source commit changed: {key}") + if source_commits.get("lifecycle_probe_source") != ( + "fe08300339a13f899fb78ea404ada381a5cba87c" + ): + errors.append("lifecycle source commit changed") + if manifest.get("source_tree_status") != { + key: "clean" for key in SOURCE_COMMITS + }: + errors.append("source tree status record changed") + + evidence = { + item.get("path"): item.get("sha256") + for item in manifest.get("source_evidence", []) + if isinstance(item, dict) + } + if evidence != SOURCE_HASHES: + errors.append("source evidence path/hash inventory changed") + + immutable = manifest.get("immutable_evidence", {}) + if immutable.get("phase08", {}).get("files") != { + key: IMMUTABLE_HASHES[key] + for key in ( + "docs/runtime/phase-0.8-read-only-preflight.md", + "manifests/runtime/phase-0.8-read-only-preflight.json", + "tests/test_phase08_preflight.py", + ) + }: + errors.append("Phase-0.8 immutable manifest bindings changed") + if immutable.get("phase09a_manifest_sha256") != IMMUTABLE_HASHES[ + "manifests/runtime/phase-0.9-anti-brick-design.json" + ]: + errors.append("Phase-0.9A manifest binding changed") + if immutable.get("phase09b_manifest_sha256") != IMMUTABLE_HASHES[ + "manifests/runtime/phase-0.9b-observer.json" + ]: + errors.append("Phase-0.9B manifest binding changed") + if immutable.get("phase09b_schema_sha256") != IMMUTABLE_HASHES[ + "manifests/runtime/phase-0.9b-observation-plan.schema.json" + ]: + errors.append("Phase-0.9B schema binding changed") + + startup = manifest.get("startup_exit", {}) + false_startup_fields = ( + "normal_sdk_kernelwrite_free", + "freestanding_dependency_closure_proven", + "stack_alignment_proven", + "complete_relocation_and_bss_tls_contract_proven", + "callable_read_and_time_abi_proven", + "safe_return_proven", + "safe_process_exit_proven", + "error_exit_proven", + "timeout_safe_exit_proven", + "complete_cleanup_proven", + ) + for field in false_startup_fields: + if startup.get(field) is not False: + errors.append(f"startup_exit.{field} must be false") + if startup.get("normal_sdk_status") != "PROVEN_SIDE_EFFECTING": + errors.append("normal SDK startup side effects were hidden") + if not startup.get("blockers"): + errors.append("startup/exit blockers are absent") + + architectures = manifest.get("output_architectures", []) + observed_architectures = tuple( + (item.get("id"), item.get("status")) + for item in architectures + if isinstance(item, dict) + ) + if observed_architectures != OUTPUT_ARCHITECTURES: + errors.append("output architecture decisions changed or reordered") + if any(item.get("current_implementation") is not False for item in architectures): + errors.append("an output architecture claims current implementation") + + protocol = manifest.get("host_protocol", {}) + expected_protocol = { + "model": "tests/phase09c_feasibility_model.py", + "host_only": True, + "target_implemented": False, + "magic": "CHG09C01", + "version": 1, + "header_size": 256, + "maximum_output_size": 4096, + "maximum_body_size": 3840, + "integer_encoding": "unsigned_big_endian", + "execution_nonce_bytes": 16, + "request_id_bytes": 16, + "firmware_field_bytes": 8, + "artifact_hash_algorithm": "sha256", + "body_checksum_algorithm": "sha256", + "result_checksum_algorithm": "sha256", + "completion_marker": "COMPLETE", + "pointers_present": False, + "dynamic_growth": False, + } + for field, expected in expected_protocol.items(): + if protocol.get(field) != expected: + errors.append(f"host protocol field changed: {field}") + if len(protocol.get("required_fields", [])) < 16: + errors.append("host protocol required fields are incomplete") + if len(protocol.get("fail_closed_conditions", [])) < 10: + errors.append("host protocol fail-closed cases are incomplete") + + firmware = manifest.get("firmware", {}) + source_two = firmware.get("source_two", {}) + if firmware.get("expected") != "9.60": + errors.append("exact firmware gate changed") + if source_two != { + "identity": None, + "status": "ABSENT", + "export_name_candidate_accepted": False, + "nonce_bound_runtime_result_present": False, + }: + errors.append("firmware source two was fabricated or promoted") + if firmware.get("agreement_proven") is not False: + errors.append("firmware agreement was claimed") + if firmware.get("gate") != "BLOCKED_FIRMWARE_SOURCE_INCOMPLETE": + errors.append("firmware gate was promoted") + + side_effects = manifest.get("side_effect_model", {}) + for field in ( + "no_persistent_content_write_is_side_effect_free", + "read_only_flag_is_side_effect_free", + "all_planned_observations_proven_side_effect_free", + ): + if side_effects.get(field) is not False: + errors.append(f"side-effect claim must remain false: {field}") + if side_effects.get("gate") != "BLOCKED_OBSERVATION_SIDE_EFFECTS_UNBOUNDED": + errors.append("side-effect gate changed") + required_dimensions = { + "content", + "metadata", + "atime", + "audit", + "cache", + "counters", + "service_state", + "security_monitoring", + "open_bookkeeping", + "process_accounting", + "object_lifetime", + "races", + } + if set(side_effects.get("dimensions", [])) != required_dimensions: + errors.append("side-effect dimensions are incomplete") + + capabilities = manifest.get("capability_closure", []) + if {item.get("id") for item in capabilities if isinstance(item, dict)} != CAPABILITIES: + errors.append("capability closure inventory changed") + for item in capabilities: + if not isinstance(item, dict): + errors.append("capability entry is not an object") + continue + if item.get("implementation_allowed") is not False: + errors.append(f"capability implementation enabled: {item.get('id')}") + if item.get("execution_allowed") is not False: + errors.append(f"capability execution enabled: {item.get('id')}") + if item.get("target_evidence") in (None, "", "PROVEN"): + errors.append(f"capability target evidence invalid: {item.get('id')}") + if not item.get("blocker"): + errors.append(f"capability blocker absent: {item.get('id')}") + + implementation = manifest.get("implementation", {}) + if not implementation or any(value is not False for value in implementation.values()): + errors.append("target/runtime implementation state must be entirely false") + if manifest.get("artifact") != { + "present": False, + "path": None, + "sha256": None, + "size": None, + "execution_eligible": False, + "execution_authorized": False, + }: + errors.append("Phase-0.9C artifact must not exist") + static = manifest.get("static_audit", {}) + if static.get("status") != "NOT_APPLICABLE_NO_TARGET_SOURCE_OR_ARTIFACT": + errors.append("static target audit was falsely promoted") + if static.get("host_model_only") is not True: + errors.append("static audit is not explicitly host-only") + + decision = manifest.get("final_decision", {}) + if decision.get("positive_classification_allowed") is not False: + errors.append("positive classification was enabled") + if decision.get("classification") != EXPECTED_CLASSIFICATION: + errors.append("final decision differs from top-level classification") + expected_blockers = { + "BLOCKED_STARTUP_ABI_UNPROVEN", + "BLOCKED_EXIT_CLEANUP_UNPROVEN", + "BLOCKED_NO_BOUNDED_OUTPUT_CHANNEL", + "BLOCKED_FIRMWARE_SOURCE_INCOMPLETE", + "BLOCKED_OBSERVATION_SIDE_EFFECTS_UNBOUNDED", + } + if set(decision.get("foundational_blockers", [])) != expected_blockers: + errors.append("foundational blocker set changed") + if decision.get("next_phase_automatic") is not False: + errors.append("automatic next phase was enabled") + return errors + + +def validate_source_trees(root: Path) -> list[str]: + errors: list[str] = [] + for key, (relative, expected_commit) in SOURCE_COMMITS.items(): + source_root = (root / relative).resolve() + if not source_root.is_dir(): + errors.append(f"source tree missing: {key}") + continue + try: + commit = git_output(source_root, "rev-parse", "HEAD") + status = git_output(source_root, "status", "--short") + except subprocess.CalledProcessError: + errors.append(f"source tree is not readable Git: {key}") + continue + if commit != expected_commit: + errors.append(f"source tree commit mismatch: {key}") + if status: + errors.append(f"source tree is dirty: {key}") + + for relative, expected_hash in SOURCE_HASHES.items(): + path = (root / relative).resolve() + if not path.is_file(): + errors.append(f"source evidence missing: {relative}") + elif sha256_file(path) != expected_hash: + errors.append(f"source evidence hash mismatch: {relative}") + return errors + + +def validate_immutable_evidence(root: Path) -> list[str]: + errors: list[str] = [] + for relative, expected_hash in IMMUTABLE_HASHES.items(): + path = root / relative + if not path.is_file(): + errors.append(f"immutable evidence missing: {relative}") + elif sha256_file(path) != expected_hash: + errors.append(f"immutable evidence changed: {relative}") + return errors + + +def forbidden_repository_path(relative: str) -> bool: + normalized = relative.replace("\\", "/").lower() + path = Path(normalized) + phase_marker = "phase09c" in normalized or "phase-0.9c" in normalized + if phase_marker and path.suffix in FORBIDDEN_PHASE09C_SUFFIXES: + return True + if normalized.startswith( + ( + "samples/phase09c", + "samples/phase-0.9c", + "src/backends/ps5/phase09c", + "src/backends/ps5/observer", + ) + ): + return True + if normalized.startswith("packaging/phase09c/"): + return normalized != "packaging/phase09c/sha256sums.txt" + return False + + +def validate_repository_boundary(root: Path) -> list[str]: + errors: list[str] = [] + listed = git_output( + root, "ls-files", "--cached", "--others", "--exclude-standard" + ).splitlines() + for relative in listed: + if forbidden_repository_path(relative): + errors.append(f"forbidden Phase-0.9C target/package path: {relative}") + + cmake = (root / "CMakeLists.txt").read_text(encoding="utf-8") + if re.search( + r"add_(?:executable|library)\s*\([^)]*phase[-_]?0?9c", + cmake, + flags=re.IGNORECASE | re.DOTALL, + ): + errors.append("CMake declares a Phase-0.9C target") + forbidden_directories = ( + root / "samples/phase09c_observer", + root / "samples/phase-0.9c-observer", + root / "packaging/phase09c/lifecycle", + root / "packaging/phase09c/install", + root / "packaging/phase09c/autoload", + ) + for path in forbidden_directories: + if path.exists(): + errors.append(f"forbidden Phase-0.9C path exists: {path.relative_to(root)}") + return errors + + +def validate_checksums(root: Path) -> list[str]: + errors: list[str] = [] + checksum_path = root / "packaging/phase09c/SHA256SUMS.txt" + if not checksum_path.is_file(): + return ["Phase-0.9C checksum file is missing"] + observed: dict[str, str] = {} + for line in checksum_path.read_text(encoding="utf-8").splitlines(): + parts = line.split(" ", 1) + if len(parts) != 2 or not re.fullmatch(r"[0-9a-f]{64}", parts[0]): + errors.append("malformed Phase-0.9C checksum line") + continue + observed[parts[1]] = parts[0] + if tuple(observed) != CHECKSUM_FILES: + errors.append("Phase-0.9C checksum inventory or order changed") + for relative in CHECKSUM_FILES: + path = root / relative + if not path.is_file(): + errors.append(f"Phase-0.9C checksummed file missing: {relative}") + elif observed.get(relative) != sha256_file(path): + errors.append(f"Phase-0.9C checksum mismatch: {relative}") + return errors + + +def collect_errors(root: Path) -> list[str]: + manifest_path = root / "manifests/runtime/phase-0.9c-feasibility.json" + schema_path = root / "manifests/runtime/phase-0.9c-feasibility.schema.json" + manifest = load_json(manifest_path) + schema = load_json(schema_path) + errors = validate_manifest(manifest) + errors.extend(validate_schema_instance(schema, manifest)) + errors.extend(validate_source_trees(root)) + errors.extend(validate_immutable_evidence(root)) + errors.extend(validate_repository_boundary(root)) + errors.extend(validate_checksums(root)) + + denylist = load_json(root / "manifests/artifact-denylist.json") + entries = denylist.get("entries", []) + if ( + len(entries) != 1 + or entries[0].get("sha256") != BLOCKED_HASH + or entries[0].get("status") != "BLOCKED" + or entries[0].get("permanent") is not True + or entries[0].get("execution_eligible") is not False + ): + errors.append("permanent denylist no longer blocks the legacy artifact") + return errors + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + root = args.root.resolve() + errors = collect_errors(root) + if errors: + for error in errors: + print(f"Phase-0.9C validation failed: {error}") + return 1 + print( + "Phase-0.9C feasibility manifest, schema, sources, immutable evidence, " + "denylist, checksums, and no-target boundary: PASS" + ) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/validate_phase09d_readback.py b/tools/validate_phase09d_readback.py new file mode 100644 index 0000000..0750fb7 --- /dev/null +++ b/tools/validate_phase09d_readback.py @@ -0,0 +1,759 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate the offline-only Phase-0.9D readback and recovery audit.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +from pathlib import Path +import subprocess +from typing import Any + + +EXPECTED_BASELINE = "e0e68829ab76977c845e7106ef93e6c01fbc966e" +EXPECTED_BRANCH = "codex/chimera-gfx-phase09d-existing-stack-readback" +EXPECTED_PHASE = "PHASE_0_9D_EXISTING_STACK_READBACK" +EXPECTED_STATUS = "DESIGN_ONLY" +BLOCKED_HASH = ( + "4be1c17b4964f2b68c39b5145bc4af4619c32512d60269ecf5c39728b390fa63" +) +DENYLIST_HASH = ( + "e9603b0e3792781ad5b511afb22ef61e3d4fd4c5a16bf928f8609193bcd97783" +) + +AUTHORIZATION_FIELDS = ( + "installation_authorized", + "execution_authorized", + "lifecycle_authorized", + "automatic_retry", + "autoload_authorized", + "device_write_authorized", + "transfer_authorized", + "observer_build_authorized", + "backup_creation_authorized", +) + +ACTION_FIELDS = ( + "ps5_connected", + "device_request_performed", + "files_transferred", + "device_write_performed", + "target_execution_performed", + "target_artifact_created", + "target_build_performed", + "observer_created", + "device_client_created", + "backup_created", + "staging_performed", +) + +SOURCE_COMMITS = { + "hardened_elfldr": ( + "../chimera-elfldr", + "197623058f509eddde18868dafcb92fdcac66464", + ), + "controlled_payload_manager": ( + "../chimera-ps5-payload-manager", + "e23d94ff91233aa770e2342800c1467875bdef44", + ), + "elfldr_public_base": ( + "work/upstream/elfldr-v0.23", + "699e8bcff03e91e8d6ca6eba281af25c5a58d8c2", + ), + "payload_manager_public_base": ( + "work/upstream/pldmgr-v0.3.1", + "cfbc70f30f419b09bf2b52283f7409e2d3117ee1", + ), + "ps5_payload_sdk_v0_41": ( + "work/upstream/sdk", + "d2e2e585740362976a39fdd5ccf390f199a7bc37", + ), +} + +SOURCE_HASHES = { + "../chimera-elfldr/README.md": ( + "372aeb28dc971b2bd98093a47fdaf77c32f75bbdc3b3d7e8678900744b91eadb" + ), + "../chimera-elfldr/main.c": ( + "876389a26999073994e63ca29926982280d9594a1ee941244205b54f57e2b4d1" + ), + "../chimera-elfldr/bootstrap.c": ( + "5a8072ec0d6db919cb3a81a7028dc91fd8e2c3a0d69b0fa7e84836fa93b45381" + ), + "../chimera-elfldr/socksrv.c": ( + "d642ced3e9b4a296dd15e355050ebe956f53a6dfdaa6ac10109cd067a3bba3d7" + ), + "../chimera-ps5-payload-manager/README.md": ( + "a00277252da46c701326ef66e5ca0d13cadffd50b3c8adf6da89cd1948c97718" + ), + "../chimera-ps5-payload-manager/DEVELOPMENT.md": ( + "0c17bed71b07c9aadf31c47625bbe1ccb42e670e594fb64a1e07aa782d5eec31" + ), + "../chimera-ps5-payload-manager/deploy.sh": ( + "2facdc1ca70db57ba258265c07a8ae1d30d3d2fb429d6718d5d5088300788e52" + ), + "../chimera-ps5-payload-manager/include/pldmgr.h": ( + "8603b8338364cea60ffaf94f985f112fb2ddfda2f44b1ccc9a2bd7a15d1b229a" + ), + "../chimera-ps5-payload-manager/src/http_server.c": ( + "2c8ff2a4bc1028d71e3cc342839584d425b6e7502e55e18e2762cdbf62c59d40" + ), + "../chimera-ps5-payload-manager/src/log_server.c": ( + "659095f43df1bbe8eb24acb165f027edc277af1e60aabb26ba9e3920b233d6f1" + ), + "../chimera-ps5-payload-manager/src/autoload.c": ( + "7051cab3ee1a3e0b9f6498000565eb9e160b9c63efa1771f250e98ec3aa4ae67" + ), + "../chimera-ps5-payload-manager/src/main.c": ( + "b2374e8fb101587b15c8261c58cb4f0573d88c490214365051eb9facd60f6eed" + ), + "../chimera-ps5-payload-manager/src/controlled_manager.c": ( + "042b55b2cece32effed636529249fd18061a2fe3c5e70c7f755f2817cfb84b99" + ), + "../chimera-ps5-payload-manager/src/verified_launcher.c": ( + "066100ca4917c7acc560e2e85666ca136cd7ccfd9094417377048f41106dd56e" + ), + "../chimera-ps5-payload-manager/src/ps5_launcher.c": ( + "29c1a5fd01784a59e88b3698940f120cb03020071bc2b7d74a1da1a51524ef59" + ), + "../chimera-ps5-payload-manager/src/payload_mgr.c": ( + "d67e9ba33edc8ca3a45aae07923d4c4790348b5f8570307e581e16780abafcba" + ), + "../chimera-ps5-payload-manager/src/repository.c": ( + "8ba694ae6d4813573752acd82ce2dbfb715b51d4ec6155f783904d76dd115adb" + ), + "../chimera-ps5-payload-manager/src/sources.c": ( + "a7a4a5cafccfba74902d6ed21ba001e6a9f62d4dff840ac38882d04827162c96" + ), + "work/upstream/elfldr-v0.23/socksrv.c": ( + "500d3c7df7ed5eac1adc925c89344d75c469651b71143716fdb77bfb2209a40c" + ), + "work/upstream/pldmgr-v0.3.1/include/pldmgr.h": ( + "01c693a3248dce7a663dd4ed9c73ce5f3a4443b5f2bd210746d94993dee27b91" + ), + "work/upstream/pldmgr-v0.3.1/src/http_server.c": ( + "35cf5d8f0dd44cf64ceab5e4b0ecc09413c82d7e9946ba9de2ca4b1898631fdd" + ), + "samples/lifecycle_probe/main.c": ( + "1ae7df1fe921ccab2a252f77975d3d441ef7725e34535b024580c0d4a242d766" + ), +} + +IMMUTABLE_HASHES = { + "manifests/runtime/phase-0.8-read-only-preflight.json": ( + "47d7f452f8799979fe99b3e6d56859f03544112725bf7e5b349eba5ed81b3322" + ), + "manifests/runtime/phase-0.8-remediation.json": ( + "a9dafed8c83722c43709dcf90ea117c21d6f996ff8fe233810ed08aa20cbe071" + ), + "manifests/runtime/phase-0.9-anti-brick-design.json": ( + "39fd7c70cae998d9d74e7caf1ff3c19f9f76f7de2e5a50bc09baede4ba53e9e9" + ), + "manifests/runtime/phase-0.9b-observer.json": ( + "104c4a667ad17f9827fc7276852c6faeef96effb8e3561a5ebc19a62c7d51634" + ), + "manifests/runtime/phase-0.9c-feasibility.json": ( + "84eb737ff3486d0c5d8b5ecf06809f93bc573497da4919595b4c1817fd34247c" + ), +} + +DELIVERABLES = ( + "docs/runtime/phase-0.9d-bootstrap-recovery-chain.md", + "docs/runtime/phase-0.9d-existing-stack-endpoint-matrix.md", + "docs/runtime/phase-0.9d-readback-feasibility.md", + "docs/runtime/phase-0.9d-off-device-backup-contract.md", + "docs/runtime/phase-0.9d-independent-recovery-analysis.md", + "docs/runtime/phase-0.9d-operational-windows.md", + "manifests/runtime/phase-0.9d-existing-stack-readback.json", + "manifests/runtime/phase-0.9d-existing-stack-readback.schema.json", + "tools/validate_phase09d_readback.py", + "tests/test_phase09d_readback.py", +) + +EXPECTED_DECISIONS = { + "new_observer_feasibility": "BLOCKED", + "existing_stack_manual_fact_collection": "PARTIAL", + "existing_stack_single_readback": "BLOCKED_NO_READBACK_PATH", + "existing_stack_repeat_readback": "BLOCKED", + "elfldr_independent_recovery": "PARTIAL", + "payload_manager_independent_recovery": "PARTIAL", + "side_by_side_feasibility": "BLOCKED", + "device_write": "NOT_AUTHORIZED", + "target_execution": "NOT_AUTHORIZED", + "installation": "NOT_AUTHORIZED", +} + +REQUIRED_EFFECTIVE_ROUTE_FIELDS = ( + "method", + "endpoint", + "handler", + "source", + "lines", + "parameters", + "authentication", + "response", + "open_flags", + "reads_bytes", + "writes_bytes", + "creates_file", + "removes_file", + "renames_file", + "reads_directory", + "reads_metadata", + "calculates_hash", + "modifies_configuration", + "writes_server_active_flag", + "writes_autoload_triggered", + "writes_log_ring", + "launches_payload", + "process_or_service_action", + "network_behavior", + "timeout_behavior", + "retry_behavior", + "maximum_size", + "short_read_behavior", + "error_behavior", + "effect_class", + "readback_candidate", + "observation_candidate", + "binary_safe_file_response", + "exact_returned_byte_count", + "partial_result_rejected", + "forbidden_reason", +) + +BACKUP_STATES = ( + "TRANSFER_NOT_STARTED", + "TRANSFER_INCOMPLETE", + "HOST_COPY_RECEIVED", + "HOST_COPY_REOPENED", + "HOST_COPY_HASHED", + "SECOND_COPY_CREATED", + "SECOND_COPY_REOPENED", + "SECOND_COPY_HASHED", + "COPIES_MATCH", + "SOURCE_MAPPING_PARTIAL", + "SOURCE_MAPPING_VERIFIED", + "INVALID", +) + + +def load_json(path: Path) -> dict[str, Any]: + with path.open("r", encoding="utf-8") as stream: + value = json.load(stream) + if not isinstance(value, dict): + raise ValueError(f"{path} does not contain an object") + return value + + +def sha256_file(path: Path) -> str: + digest = hashlib.sha256() + with path.open("rb") as stream: + for chunk in iter(lambda: stream.read(1024 * 1024), b""): + digest.update(chunk) + return digest.hexdigest() + + +def git(root: Path, *args: str) -> str: + result = subprocess.run( + ["git", *args], + cwd=root, + check=False, + capture_output=True, + text=True, + ) + if result.returncode != 0: + raise RuntimeError(result.stderr.strip() or "git command failed") + return result.stdout.strip() + + +def effective_route( + route: dict[str, Any], defaults: dict[str, Any] +) -> dict[str, Any]: + merged = dict(defaults) + merged.update(route) + return merged + + +def route_readback_errors( + route: dict[str, Any], defaults: dict[str, Any] | None = None +) -> list[str]: + merged = effective_route(route, defaults or {}) + errors: list[str] = [] + flags = {str(flag).lower() for flag in merged.get("open_flags", [])} + mutating_open = any( + token in flag + for flag in flags + for token in ("o_wronly", "o_rdwr", "o_creat", "o_append", "wb", "(w)") + ) + mutating = any( + bool(merged.get(field)) + for field in ( + "writes_bytes", + "creates_file", + "removes_file", + "renames_file", + "modifies_configuration", + ) + ) or mutating_open + + if merged.get("readback_candidate"): + if mutating: + errors.append("readback candidate mutates a device file or configuration") + if merged.get("launches_payload"): + errors.append("readback candidate launches a payload") + if merged.get("process_or_service_action"): + errors.append("readback candidate performs a process/service action") + if merged.get("writes_autoload_triggered"): + errors.append("readback candidate writes autoload_triggered") + if not merged.get("binary_safe_file_response"): + errors.append("readback candidate lacks binary-safe framing") + if not merged.get("exact_returned_byte_count"): + errors.append("readback candidate lacks an exact byte count") + if not merged.get("partial_result_rejected"): + errors.append("readback candidate does not reject partial output") + if str(merged.get("short_read_behavior", "")).upper() in { + "", + "ABSENT", + "UNPROVEN", + "NO_HOST_FILE_READBACK_CONTRACT", + "NO_FILE_RESPONSE", + }: + errors.append("readback candidate lacks short-read detection") + if merged.get("automatic_retry") is True: + errors.append("readback candidate enables automatic retry") + if merged.get("automatic_resume") is True: + errors.append("readback candidate enables automatic resume") + return errors + + +def backup_record_errors(record: dict[str, Any]) -> list[str]: + errors: list[str] = [] + status = record.get("status") + if status not in BACKUP_STATES: + errors.append("unknown backup status") + if record.get("automatic_resume"): + errors.append("automatic resume is forbidden") + if record.get("automatic_retry"): + errors.append("automatic retry is forbidden") + if status in {"HOST_COPY_HASHED", "SECOND_COPY_HASHED", "COPIES_MATCH"}: + if not isinstance(record.get("exact_byte_count"), int): + errors.append("a hash requires an exact byte count") + if not record.get("closed_and_reopened"): + errors.append("hash requires close and reopen") + if not record.get("sha256"): + errors.append("hashed state requires SHA-256") + if status == "COPIES_MATCH": + if not all( + record.get(field) + for field in ("sizes_match", "hashes_match", "bytes_match") + ): + errors.append("COPIES_MATCH requires size, hash, and byte equality") + if record.get("transfer_complete") is False and status != "INVALID": + errors.append("partial transfer must be INVALID") + if record.get("recovery_proven"): + errors.append("host backup cannot prove recovery") + return errors + + +def server_active_observation_status(semantics: dict[str, Any]) -> str: + if ( + not semantics.get("fully_documented") + or semantics.get("reset_path") in {None, "UNPROVEN"} + ): + return "BLOCKED" + if semantics.get("reset_path") == "NONE_IN_PROCESS": + return "PARTIAL" + return "READY_FOR_REVIEW" + + +def recovery_dependency_classification( + component: str, recovery_executor: str +) -> str: + if component == recovery_executor: + return "SELF_DEPENDENT" + if recovery_executor in {"ABSENT", "UNPROVEN", ""}: + return recovery_executor + return "CROSS_DEPENDENT" + + +def _type_matches(value: Any, expected: str) -> bool: + return { + "object": isinstance(value, dict), + "array": isinstance(value, list), + "string": isinstance(value, str), + "boolean": isinstance(value, bool), + "integer": isinstance(value, int) and not isinstance(value, bool), + "number": isinstance(value, (int, float)) and not isinstance(value, bool), + "null": value is None, + }.get(expected, True) + + +def validate_schema_instance( + schema: dict[str, Any], instance: Any, path: str = "$" +) -> list[str]: + """Small offline validator for the schema features used by this record.""" + errors: list[str] = [] + expected_type = schema.get("type") + if expected_type and not _type_matches(instance, expected_type): + return [f"{path}: expected {expected_type}"] + if "const" in schema and instance != schema["const"]: + errors.append(f"{path}: expected constant {schema['const']!r}") + if "enum" in schema and instance not in schema["enum"]: + errors.append(f"{path}: value is outside enum") + + if isinstance(instance, dict): + required = schema.get("required", []) + for key in required: + if key not in instance: + errors.append(f"{path}: missing {key}") + if len(instance) < schema.get("minProperties", 0): + errors.append(f"{path}: too few properties") + properties = schema.get("properties", {}) + for key, value in instance.items(): + if key in properties: + errors.extend( + validate_schema_instance(properties[key], value, f"{path}.{key}") + ) + elif schema.get("additionalProperties") is False: + errors.append(f"{path}: unexpected property {key}") + elif isinstance(schema.get("additionalProperties"), dict): + errors.extend( + validate_schema_instance( + schema["additionalProperties"], value, f"{path}.{key}" + ) + ) + + if isinstance(instance, list): + if len(instance) < schema.get("minItems", 0): + errors.append(f"{path}: too few items") + if "maxItems" in schema and len(instance) > schema["maxItems"]: + errors.append(f"{path}: too many items") + item_schema = schema.get("items") + if isinstance(item_schema, dict): + for index, item in enumerate(instance): + errors.extend( + validate_schema_instance(item_schema, item, f"{path}[{index}]") + ) + return errors + + +def validate_manifest(manifest: dict[str, Any]) -> list[str]: + errors: list[str] = [] + if manifest.get("phase") != EXPECTED_PHASE: + errors.append("wrong phase") + if manifest.get("status") != EXPECTED_STATUS: + errors.append("status must remain DESIGN_ONLY") + if manifest.get("baseline_commit") != EXPECTED_BASELINE: + errors.append("wrong baseline") + if manifest.get("branch") != EXPECTED_BRANCH: + errors.append("wrong branch") + + for field in AUTHORIZATION_FIELDS: + if manifest.get("authorization", {}).get(field) is not False: + errors.append(f"authorization.{field} must be false") + for field in ACTION_FIELDS: + if manifest.get("actions", {}).get(field) is not False: + errors.append(f"actions.{field} must be false") + + canonical = manifest.get("canonical_state", {}) + expected_canonical = { + "phase08_status": "READ_ONLY_PREFLIGHT_BLOCKED", + "phase09a_status": "DESIGN_ONLY", + "phase09b_status": "BLOCKED", + "phase09c_classification": "BLOCKED_MULTIPLE_FOUNDATIONAL_CONTRACTS", + "firmware_runtime_behavior": "UNPROVEN", + "stock_identities": "reference_only", + "payload_manager_backup": "HARD_BLOCKER_FOR_INSTALLATION", + "independent_recovery": "UNPROVEN", + "permanent_denylist_sha256": DENYLIST_HASH, + "permanently_blocked_artifact_sha256": BLOCKED_HASH, + } + for field, expected in expected_canonical.items(): + if canonical.get(field) != expected: + errors.append(f"canonical_state.{field} changed") + + if manifest.get("decisions") != EXPECTED_DECISIONS: + errors.append("decision matrix changed") + if manifest.get("source_commits", {}).get("lifecycle_source") != ( + "fe08300339a13f899fb78ea404ada381a5cba87c" + ): + errors.append("lifecycle source binding changed") + for name, (_, expected) in SOURCE_COMMITS.items(): + if manifest.get("source_commits", {}).get(name) != expected: + errors.append(f"source commit changed: {name}") + if manifest.get("source_tree_status", {}).get(name) != "clean": + errors.append(f"source tree is not recorded clean: {name}") + + routes = manifest.get("endpoint_matrix", []) + if len(routes) != 40: + errors.append("endpoint matrix must contain all 40 audited route records") + defaults = manifest.get("endpoint_defaults", {}) + identities: set[tuple[str, str]] = set() + for route in routes: + effective = effective_route(route, defaults) + identity = (str(route.get("profile")), str(route.get("endpoint"))) + if identity in identities: + errors.append(f"duplicate route identity: {identity}") + identities.add(identity) + for field in REQUIRED_EFFECTIVE_ROUTE_FIELDS: + if field not in effective: + errors.append(f"{identity}: missing effective field {field}") + errors.extend( + f"{identity}: {error}" + for error in route_readback_errors(route, defaults) + ) + if effective.get("readback_candidate") is not False: + errors.append(f"{identity}: no audited route may be a readback candidate") + + autoload = next( + ( + effective_route(route, defaults) + for route in routes + if route.get("profile") == "full" + and route.get("endpoint") == "/autoload_status" + ), + None, + ) + if not autoload or not autoload.get("writes_autoload_triggered"): + errors.append("/autoload_status mutation is not recorded") + + flags = manifest.get("flag_semantics", {}) + server_active = flags.get("server_active_flag", {}) + if server_active.get("classification") == "ANTI_BRICK_CRITICAL": + errors.append("server_active_flag is incorrectly anti-brick critical") + if ( + not server_active.get("fully_documented") + or server_active.get("reset_path") != "NONE_IN_PROCESS" + or server_active.get("lifetime") != "PROCESS_LOCAL" + ): + errors.append("server_active_flag semantics are incomplete") + autoload_flag = flags.get("autoload_triggered", {}) + if autoload_flag.get("excluded_windows") != [1, 2]: + errors.append("autoload_triggered must be excluded from Windows 1 and 2") + + if len(manifest.get("readback_routes", [])) != 7: + errors.append("readback route search is incomplete") + for route in manifest.get("readback_routes", []): + if route.get("usable_once") or route.get("usable_twice"): + errors.append("a rejected readback route is marked usable") + if manifest.get("path_classification") != "PATH_CONFLICT": + errors.append("path conflict was removed") + if manifest.get("runtime_observed_live_paths") != []: + errors.append("offline package/config paths cannot become live paths") + + contract = manifest.get("host_backup_contract", {}) + if contract.get("statuses") != list(BACKUP_STATES): + errors.append("backup status vocabulary changed") + required_true = ( + "one_component_per_session", + "new_exclusive_local_output", + "binary_mode", + "exact_received_byte_count_required", + "close_reopen_before_hash", + "sha256_required", + "size_required", + "second_independent_connection", + "second_new_output", + "compare_size", + "compare_sha256", + "compare_every_byte", + "capture_raw_protocol_metadata", + "capture_literal_source_path", + "capture_device_and_session", + "capture_client_commit", + "off_device_backup_valid_requires_copies_match", + ) + for field in required_true: + if contract.get(field) is not True: + errors.append(f"host_backup_contract.{field} must be true") + required_false = ( + "overwrite_existing_output", + "automatic_resume", + "automatic_retry", + "contains_device_write_command", + "recovery_proven_allowed", + ) + for field in required_false: + if contract.get(field) is not False: + errors.append(f"host_backup_contract.{field} must be false") + if contract.get("partial_transfer_status") != "INVALID": + errors.append("partial host transfer must be INVALID") + + recovery = manifest.get("recovery_dependencies", {}) + if recovery.get("elfldr", {}).get("classification") != "PARTIAL": + errors.append("elfldr recovery must remain PARTIAL") + manager = recovery.get("payload_manager", {}) + if manager.get("classification") != "PARTIAL": + errors.append("Payload Manager recovery must remain PARTIAL") + if "CROSS_DEPENDENT" not in manager.get("detailed", []): + errors.append("Payload Manager cross-dependence is missing") + if manifest.get("side_by_side", {}).get("classification") != "BLOCKED": + errors.append("side-by-side must remain blocked") + if manifest.get("side_by_side", {}).get( + "grants_installation_authorization" + ): + errors.append("side-by-side cannot authorize installation") + + windows = manifest.get("operational_windows", []) + if [window.get("window") for window in windows] != list(range(1, 8)): + errors.append("operational windows are incomplete or reordered") + for window in windows: + if window.get("device_write") is not False: + errors.append(f"Window {window.get('window')} permits device write") + if window.get("payload_launch") is not False: + errors.append(f"Window {window.get('window')} permits payload launch") + if window.get("autoload_status_route") is not False: + errors.append(f"Window {window.get('window')} permits /autoload_status") + if window.get("automatic_retry") is not False: + errors.append(f"Window {window.get('window')} permits retry") + if windows and windows[0].get("file_transfer") is not False: + errors.append("Window 1 contains file transfer") + if len(windows) >= 3 and windows[2].get("automatic_third_attempt") is not False: + errors.append("Window 3 permits an automatic third attempt") + if len(windows) >= 4 and windows[3].get("component_session_separate") is not True: + errors.append("components do not have separate windows") + + final = manifest.get("final_decision", {}) + if ( + final.get("classification") != "BLOCKED" + or final.get("hardware_evidence_claimed") is not False + or final.get("device_action_authorized") is not False + ): + errors.append("final decision must remain an offline-only block") + return errors + + +def _phase09d_paths(root: Path) -> list[str]: + paths: set[str] = set() + for candidate in git(root, "ls-files").splitlines(): + normalized = candidate.replace("\\", "/") + lowered = normalized.lower() + if "phase-0.9d" in lowered or "phase09d" in lowered: + paths.add(normalized) + + output = git(root, "status", "--porcelain=v1", "--untracked-files=all") + for line in output.splitlines(): + if len(line) < 4: + continue + candidate = line[3:].replace("\\", "/") + if " -> " in candidate: + candidate = candidate.rsplit(" -> ", 1)[1] + lowered = candidate.lower() + if "phase-0.9d" in lowered or "phase09d" in lowered: + paths.add(candidate) + return sorted(paths) + + +def collect_errors(root: Path) -> list[str]: + errors: list[str] = [] + manifest_path = ( + root / "manifests/runtime/phase-0.9d-existing-stack-readback.json" + ) + schema_path = ( + root + / "manifests/runtime/phase-0.9d-existing-stack-readback.schema.json" + ) + try: + manifest = load_json(manifest_path) + schema = load_json(schema_path) + except (OSError, ValueError, json.JSONDecodeError) as error: + return [str(error)] + + errors.extend(validate_manifest(manifest)) + errors.extend(validate_schema_instance(schema, manifest)) + + for relative in DELIVERABLES: + if not (root / relative).is_file(): + errors.append(f"missing deliverable: {relative}") + + for relative, expected in SOURCE_HASHES.items(): + path = (root / relative).resolve() + if not path.is_file(): + errors.append(f"missing source evidence: {relative}") + elif sha256_file(path) != expected: + errors.append(f"source evidence changed: {relative}") + if manifest.get("source_evidence", {}).get(relative) != expected: + errors.append(f"manifest source hash mismatch: {relative}") + + for relative, expected in IMMUTABLE_HASHES.items(): + path = root / relative + if not path.is_file() or sha256_file(path) != expected: + errors.append(f"immutable evidence changed: {relative}") + if manifest.get("immutable_evidence", {}).get(relative) != expected: + errors.append(f"manifest immutable hash mismatch: {relative}") + + denylist = root / "manifests/artifact-denylist.json" + if not denylist.is_file() or sha256_file(denylist) != DENYLIST_HASH: + errors.append("permanent denylist changed") + else: + denylist_data = load_json(denylist) + entries = json.dumps(denylist_data, sort_keys=True) + if BLOCKED_HASH not in entries: + errors.append("permanently blocked artifact is absent from denylist") + + for name, (relative, expected) in SOURCE_COMMITS.items(): + source_root = (root / relative).resolve() + try: + if git(source_root, "rev-parse", "HEAD") != expected: + errors.append(f"source HEAD changed: {name}") + if git(source_root, "status", "--porcelain=v1"): + errors.append(f"source tree is dirty: {name}") + except RuntimeError as error: + errors.append(f"{name}: {error}") + + try: + if git(root, "branch", "--show-current") != EXPECTED_BRANCH: + errors.append("current branch is not the Phase-0.9D branch") + except RuntimeError as error: + errors.append(str(error)) + + allowed_suffixes = {".md", ".json", ".py"} + forbidden_roots = ("samples/", "src/", "include/", "packaging/", "adapters/") + for relative in _phase09d_paths(root): + lowered = relative.lower() + if Path(relative).suffix.lower() not in allowed_suffixes: + errors.append(f"forbidden Phase-0.9D artifact/source suffix: {relative}") + if lowered.startswith(forbidden_roots): + errors.append(f"forbidden Phase-0.9D target/product path: {relative}") + if lowered.endswith((".elf", ".o", ".a", ".so", ".map", ".s", ".asm", ".ld")): + errors.append(f"forbidden Phase-0.9D target artifact: {relative}") + + docs = "\n".join( + (root / relative).read_text(encoding="utf-8") + for relative in DELIVERABLES + if relative.endswith(".md") and (root / relative).is_file() + ) + for marker in ( + "BLOCKED_NO_READBACK_PATH", + "PATH_CONFLICT", + "server_active_flag", + "autoload_triggered", + "OFF_DEVICE_BACKUP_VALID", + "RECOVERY_PROVEN", + "No PS5 was contacted", + ): + if marker not in docs: + errors.append(f"documentation marker missing: {marker}") + return errors + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + errors = collect_errors(args.root.resolve()) + if errors: + for error in errors: + print(f"ERROR: {error}") + return 1 + print("Phase-0.9D existing-stack readback audit: PASS") + print("Decision: BLOCKED_NO_READBACK_PATH") + print("Device contact/write/execution/artifact creation: false") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/validate_phase09e_bootstrap.py b/tools/validate_phase09e_bootstrap.py new file mode 100644 index 0000000..1317c11 --- /dev/null +++ b/tools/validate_phase09e_bootstrap.py @@ -0,0 +1,798 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate the offline-only Phase-0.9E bootstrap provenance audit.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +from pathlib import Path +import re +import subprocess +from typing import Any +import zipfile + + +EXPECTED_BASELINE = "e5304e394efb0790e1939bef87404f19b58caa43" +EXPECTED_BRANCH = "codex/chimera-gfx-phase09e-bootstrap-rescue-provenance" +EXPECTED_PHASE = "PHASE_0_9E_BOOTSTRAP_RESCUE_PROVENANCE" +EXPECTED_STATUS = "DESIGN_ONLY" +EXPECTED_CLASSIFICATION = "BOOTSTRAP_IMPLEMENTATION_MISSING" +BLOCKED_HASH = ( + "4be1c17b4964f2b68c39b5145bc4af4619c32512d60269ecf5c39728b390fa63" +) +DENYLIST_HASH = ( + "e9603b0e3792781ad5b511afb22ef61e3d4fd4c5a16bf928f8609193bcd97783" +) +OUTER_NAME = "Y2JB-Autoloader-403-1240.zip" +OUTER_SIZE = 504159435 +OUTER_SHA256 = ( + "805e3f87f0c371223619ffc7d3a7b3c0d41a1fae8a8b1171d9e2f162659e8291" +) +INNER_NAME = "PS5/EXPORT/BACKUP/202606102126_00/archive.dat" +INNER_SIZE = 504365056 +INNER_SHA256 = ( + "6439834e8856d45b6d6fe699b74c35ca6985a199ea8ecf3e398c018d37be2d55" +) +ZIP_ENTRIES = ( + "PS5/", + "PS5/EXPORT/", + "PS5/EXPORT/BACKUP/", + "PS5/EXPORT/BACKUP/202606102126_00/", + INNER_NAME, +) + +AUTHORIZATION_FIELDS = ( + "device_action_authorized", + "execution_authorized", + "installation_authorized", + "lifecycle_authorized", + "autoload_authorized", + "device_write_authorized", + "transfer_authorized", + "automatic_retry", + "target_build_authorized", + "rescue_payload_design_authorized", +) +ACTION_FIELDS = ( + "internet_used", + "fetch_performed", + "clone_performed", + "package_installed", + "ps5_connected", + "device_request_performed", + "files_transferred", + "device_write_performed", + "target_execution_performed", + "target_build_performed", + "target_artifact_created", + "rescue_payload_created", + "readback_payload_created", + "device_client_created", + "backup_created", + "staging_performed", +) +PROVENANCE_FIELDS = ( + "logical_name", + "artifact_role", + "local_relative_path", + "size", + "sha256", + "file_type", + "source_repository", + "source_commit", + "build_identity", + "version", + "obtained_from", + "evidence_that_it_is_deployed_or_used", + "confidence", + "immutable", + "executable", + "persistent_on_device", + "transferred_per_session", + "required_for_bootstrap", + "required_for_recovery", +) +SOURCE_COMMITS = { + "hardened_elfldr": ( + "../chimera-elfldr", + "197623058f509eddde18868dafcb92fdcac66464", + ), + "controlled_payload_manager": ( + "../chimera-ps5-payload-manager", + "e23d94ff91233aa770e2342800c1467875bdef44", + ), + "elfldr_public_base": ( + "work/upstream/elfldr-v0.23", + "699e8bcff03e91e8d6ca6eba281af25c5a58d8c2", + ), + "payload_manager_public_base": ( + "work/upstream/pldmgr-v0.3.1", + "cfbc70f30f419b09bf2b52283f7409e2d3117ee1", + ), + "ps5_payload_sdk_v0_41": ( + "work/upstream/sdk", + "d2e2e585740362976a39fdd5ccf390f199a7bc37", + ), +} +SOURCE_HASHES = { + "../chimera-elfldr/elfldr-ps5.elf": ( + 397000, + "63e810982471eb40cae3a20aa9df9a0a02892f420e429874fae4e99aa400b561", + ), + "../chimera-elfldr/elfldr-ps5.elf.map": ( + 142905, + "2ea5ff73299db6e61d5fd998c358ee910bc4e8fa1e6e7c73dbaa842bc813124f", + ), + "../chimera-elfldr/bootstrap.elf.map": ( + 143081, + "ec4ae17abeb8270948d8a7d4196593ba94cbdb69376b8b9f7f009b25ab5b3323", + ), + "../chimera-elfldr/socksrv.elf.map": ( + 145072, + "bf74db6ee68a60652426936e7037bb5a143078f44cc7774fa04c9bc4bc5447c1", + ), + "../chimera-elfldr/Makefile": ( + 1902, + "3df93b48fc61ec67907b136e44ea2085baacf08347864d32befc072a7a783384", + ), + "../chimera-elfldr/README.md": ( + 2821, + "372aeb28dc971b2bd98093a47fdaf77c32f75bbdc3b3d7e8678900744b91eadb", + ), + "../chimera-elfldr/main.c": ( + 2473, + "876389a26999073994e63ca29926982280d9594a1ee941244205b54f57e2b4d1", + ), + "../chimera-elfldr/bootstrap.c": ( + 2081, + "5a8072ec0d6db919cb3a81a7028dc91fd8e2c3a0d69b0fa7e84836fa93b45381", + ), + "../chimera-elfldr/socksrv.c": ( + 11556, + "d642ced3e9b4a296dd15e355050ebe956f53a6dfdaa6ac10109cd067a3bba3d7", + ), + "work/upstream/release-assets/elfldr-ps5-v0.23.elf": ( + 397000, + "092d16ee0ede0c494947efd38d1a17bbd7cc4b022d3858ea898833c188c703e8", + ), + "../chimera-ps5-payload-manager/pldmgr-controlled.elf": ( + 99560, + "8fecf8241a46246eddbd21e8bb4d875f5d76f1f4f4c6a11384df1f131aa5e5b1", + ), +} +IMMUTABLE_HASHES = { + "manifests/runtime/phase-0.8-read-only-preflight.json": ( + "47d7f452f8799979fe99b3e6d56859f03544112725bf7e5b349eba5ed81b3322" + ), + "manifests/runtime/phase-0.8-remediation.json": ( + "a9dafed8c83722c43709dcf90ea117c21d6f996ff8fe233810ed08aa20cbe071" + ), + "manifests/runtime/phase-0.9-anti-brick-design.json": ( + "39fd7c70cae998d9d74e7caf1ff3c19f9f76f7de2e5a50bc09baede4ba53e9e9" + ), + "manifests/runtime/phase-0.9b-observer.json": ( + "104c4a667ad17f9827fc7276852c6faeef96effb8e3561a5ebc19a62c7d51634" + ), + "manifests/runtime/phase-0.9c-feasibility.json": ( + "84eb737ff3486d0c5d8b5ecf06809f93bc573497da4919595b4c1817fd34247c" + ), + "manifests/runtime/phase-0.9d-existing-stack-readback.json": ( + "86e5aaf034685dbe058b71ffeec645b682f0a8cc7d249e8ac0397155233991de" + ), +} +DELIVERABLES = ( + "docs/runtime/phase-0.9e-bootstrap-provenance.md", + "docs/runtime/phase-0.9e-independent-rescue-chain.md", + "docs/runtime/phase-0.9e-loader-9020-protocol.md", + "docs/runtime/phase-0.9e-reboot-and-crash-model.md", + "docs/runtime/phase-0.9e-future-rescue-payload-contract.md", + "docs/runtime/phase-0.9e-output-architecture-options.md", + "manifests/runtime/phase-0.9e-bootstrap-provenance.json", + "manifests/runtime/phase-0.9e-bootstrap-provenance.schema.json", + "manifests/runtime/phase-0.9e-loader-protocol.json", + "manifests/runtime/phase-0.9e-loader-protocol.schema.json", + "tools/validate_phase09e_bootstrap.py", + "tests/test_phase09e_bootstrap.py", + "packaging/phase09e/SHA256SUMS.txt", +) +FORBIDDEN_NEW_SUFFIXES = { + ".c", + ".cc", + ".cpp", + ".s", + ".asm", + ".ld", + ".elf", + ".self", + ".sprx", + ".pkg", + ".bin", +} +FORBIDDEN_PRODUCTION_PREFIXES = ( + "include/", + "src/", + "adapters/", + "samples/", +) + + +def load_json(path: Path) -> dict[str, Any]: + with path.open("r", encoding="utf-8") as stream: + value = json.load(stream) + if not isinstance(value, dict): + raise ValueError(f"{path} does not contain an object") + return value + + +def sha256_file(path: Path) -> str: + digest = hashlib.sha256() + with path.open("rb") as stream: + for chunk in iter(lambda: stream.read(1024 * 1024), b""): + digest.update(chunk) + return digest.hexdigest() + + +def candidate_archive_path(root: Path) -> Path: + """Resolve the same Windows user Downloads path from Windows or WSL.""" + windows_home_candidate = Path.home() / "Downloads" / OUTER_NAME + if windows_home_candidate.is_file(): + return windows_home_candidate + for ancestor in (root, *root.parents): + if ancestor.parent.name.lower() == "users": + return ancestor / "Downloads" / OUTER_NAME + return windows_home_candidate + + +def git(root: Path, *args: str) -> str: + result = subprocess.run( + ["git", *args], + cwd=root, + check=False, + capture_output=True, + text=True, + ) + if result.returncode != 0: + raise RuntimeError(result.stderr.strip() or "git command failed") + return result.stdout.strip() + + +def provenance_errors(artifact: dict[str, Any]) -> list[str]: + errors: list[str] = [] + for field in PROVENANCE_FIELDS: + if field not in artifact: + errors.append(f"missing provenance field {field}") + confidence = artifact.get("confidence") + if confidence not in { + "EXACT_USED", + "STRONG_MATCH", + "POSSIBLE", + "REFERENCE_ONLY", + "UNKNOWN", + }: + errors.append("invalid confidence") + obtained = str(artifact.get("obtained_from", "")).lower() + repository = str(artifact.get("source_repository") or "").lower() + if ( + "public" in obtained or "upstream" in obtained or "upstream" in repository + ) and confidence == "EXACT_USED": + errors.append("public upstream cannot be exact-used without provenance") + if ( + artifact.get("source_repository") is None + and artifact.get("build_identity") is None + and artifact.get("source_commit") is not None + ): + errors.append("opaque binary cannot carry an invented source commit") + if not re.fullmatch(r"[0-9a-f]{64}", str(artifact.get("sha256", ""))): + errors.append("invalid SHA-256") + if not isinstance(artifact.get("size"), int) or artifact.get("size", -1) < 0: + errors.append("invalid artifact size") + return errors + + +def rescue_classification( + *, + actual_package_available: bool, + requires_elfldr: bool = False, + requires_payload_manager: bool = False, + replaces_live_component: bool = False, + all_required_properties_proven: bool = False, +) -> str: + if not actual_package_available: + return "BOOTSTRAP_IMPLEMENTATION_MISSING" + if requires_elfldr or requires_payload_manager: + return "SELF_OR_CROSS_DEPENDENT" + if replaces_live_component: + return "NO_INDEPENDENT_RESCUE_PATH" + if all_required_properties_proven: + return "INDEPENDENT_RESCUE_EXECUTOR_CANDIDATE" + return "PARTIAL_RESCUE_EXECUTOR" + + +def host_to_memory_classification( + *, + receive_code: bool, + mapping_code: bool, + entrypoint_code: bool, + device_file_only: bool = False, +) -> str: + if device_file_only: + return "DEVICE_FILE_ONLY" + if receive_code and mapping_code and entrypoint_code: + return "PROVEN_FROM_SOURCE" + if receive_code or mapping_code or entrypoint_code: + return "PARTIAL" + return "UNKNOWN" + + +def protocol_model_complete(protocol: dict[str, Any]) -> bool: + required = ( + "maximum_payload_size", + "headers", + "length_fields", + "short_read_detection", + "short_write_detection", + "bounds_checks", + ) + return all(protocol.get(field) not in {None, "", "UNKNOWN"} for field in required) + + +def risk_classification( + *, + temporary_socket: bool = False, + live_filesystem_write: bool = False, + autoload_activation: bool = False, +) -> str: + if live_filesystem_write or autoload_activation: + return "BRICK_RELEVANT" + if temporary_socket: + return "LOW_TECHNICAL" + return "UNKNOWN" + + +def reboot_classification( + *, + exact_package: bool, + source_design_restartable: bool, + hardware_observed: bool, +) -> str: + if exact_package and source_design_restartable and hardware_observed: + return "REBOOT_RECOVERY_SUPPORTED_BY_DESIGN" + if exact_package and source_design_restartable: + return "REBOOT_RECOVERY_PLAUSIBLE" + return "REBOOT_RECOVERY_UNPROVEN" + + +def phase09f_design_allowed( + *, + actual_package_available: bool, + independent_from_elfldr: bool, + independent_from_payload_manager: bool, + no_live_replacement: bool, +) -> bool: + return all( + ( + actual_package_available, + independent_from_elfldr, + independent_from_payload_manager, + no_live_replacement, + ) + ) + + +def _type_matches(value: Any, expected: str) -> bool: + return { + "object": isinstance(value, dict), + "array": isinstance(value, list), + "string": isinstance(value, str), + "boolean": isinstance(value, bool), + "integer": isinstance(value, int) and not isinstance(value, bool), + "number": isinstance(value, (int, float)) and not isinstance(value, bool), + "null": value is None, + }.get(expected, True) + + +def validate_schema_instance( + schema: dict[str, Any], instance: Any, path: str = "$" +) -> list[str]: + """Small offline validator for the schema features used by this phase.""" + errors: list[str] = [] + expected_type = schema.get("type") + if expected_type and not _type_matches(instance, expected_type): + return [f"{path}: expected {expected_type}"] + if "const" in schema and instance != schema["const"]: + errors.append(f"{path}: expected constant {schema['const']!r}") + if "enum" in schema and instance not in schema["enum"]: + errors.append(f"{path}: value is outside enum") + pattern = schema.get("pattern") + if pattern and isinstance(instance, str) and not re.fullmatch(pattern, instance): + errors.append(f"{path}: pattern mismatch") + if isinstance(instance, dict): + for key in schema.get("required", []): + if key not in instance: + errors.append(f"{path}: missing {key}") + properties = schema.get("properties", {}) + for key, value in instance.items(): + if key in properties: + errors.extend( + validate_schema_instance(properties[key], value, f"{path}.{key}") + ) + elif schema.get("additionalProperties") is False: + errors.append(f"{path}: unexpected property {key}") + elif isinstance(schema.get("additionalProperties"), dict): + errors.extend( + validate_schema_instance( + schema["additionalProperties"], value, f"{path}.{key}" + ) + ) + if isinstance(instance, list): + if "maxItems" in schema and len(instance) > schema["maxItems"]: + errors.append(f"{path}: too many items") + item_schema = schema.get("items") + if isinstance(item_schema, dict): + for index, item in enumerate(instance): + errors.extend( + validate_schema_instance(item_schema, item, f"{path}[{index}]") + ) + return errors + + +def validate_manifest(manifest: dict[str, Any]) -> list[str]: + errors: list[str] = [] + expected_top = { + "phase": EXPECTED_PHASE, + "status": EXPECTED_STATUS, + "baseline_commit": EXPECTED_BASELINE, + "branch": EXPECTED_BRANCH, + } + for field, expected in expected_top.items(): + if manifest.get(field) != expected: + errors.append(f"{field} changed") + for field in AUTHORIZATION_FIELDS: + if manifest.get("authorization", {}).get(field) is not False: + errors.append(f"authorization.{field} must be false") + for field in ACTION_FIELDS: + if manifest.get("actions", {}).get(field) is not False: + errors.append(f"actions.{field} must be false") + + canonical = manifest.get("canonical_state", {}) + expected_canonical = { + "phase09d_decision": "BLOCKED_NO_READBACK_PATH", + "phase09c_new_observer": "BLOCKED_MULTIPLE_FOUNDATIONAL_CONTRACTS", + "payload_manager_backup": "HARD_BLOCKER_FOR_INSTALLATION", + "independent_recovery": "UNPROVEN", + "firmware_runtime_behavior": "UNPROVEN", + "permanent_denylist_sha256": DENYLIST_HASH, + "permanently_blocked_artifact_sha256": BLOCKED_HASH, + } + for field, expected in expected_canonical.items(): + if canonical.get(field) != expected: + errors.append(f"canonical_state.{field} changed") + + decisions = manifest.get("decisions", {}) + expected_decisions = { + "actual_bootstrap_package_available": False, + "actual_bootstrap_identity": None, + "bootstrap_provenance": "POSSIBLE", + "independent_from_elfldr": "unproven", + "independent_from_payload_manager": "unproven", + "restartable_after_reboot": "unproven", + "host_to_memory": "UNKNOWN", + "output_channel_for_future_rescue": "UNKNOWN", + "independent_rescue_classification": EXPECTED_CLASSIFICATION, + "phase09f_rescue_payload_design_allowed": False, + "device_action_authorized": False, + "execution_authorized": False, + "installation_authorized": False, + "automatic_retry": False, + } + if decisions != expected_decisions: + errors.append("decision matrix changed") + if rescue_classification(actual_package_available=False) != ( + EXPECTED_CLASSIFICATION + ): + errors.append("missing implementation is misclassified") + for name, (_, expected) in SOURCE_COMMITS.items(): + if manifest.get("source_commits", {}).get(name) != expected: + errors.append(f"source commit changed: {name}") + if manifest.get("source_tree_status", {}).get(name) != "clean": + errors.append(f"source tree not recorded clean: {name}") + + audit = manifest.get("candidate_archive_audit", {}) + expected_audit = { + "outer_size": OUTER_SIZE, + "outer_sha256": OUTER_SHA256, + "zip_entry_count": len(ZIP_ENTRIES), + "inner_path": INNER_NAME, + "inner_size": INNER_SIZE, + "inner_sha256": INNER_SHA256, + "inner_magic_ascii": "SIECAF", + "acceptable_local_parser_present": False, + "decryption_attempted": False, + "source_identity_proven": False, + "deployed_use_proven": False, + "classification": "POSSIBLE", + } + for field, expected in expected_audit.items(): + if audit.get(field) != expected: + errors.append(f"candidate_archive_audit.{field} changed") + + artifacts = manifest.get("artifacts", []) + if len(artifacts) != 13: + errors.append("artifact inventory must contain 13 records") + seen: set[str] = set() + for artifact in artifacts: + name = str(artifact.get("logical_name")) + if name in seen: + errors.append(f"duplicate artifact: {name}") + seen.add(name) + errors.extend(f"{name}: {error}" for error in provenance_errors(artifact)) + if artifact.get("confidence") == "EXACT_USED": + errors.append(f"{name}: no exact-used artifact is proven") + if manifest.get("exact_used_artifacts") != []: + errors.append("exact_used_artifacts must remain empty") + + host_memory = manifest.get("host_to_memory_analysis", {}) + if ( + host_memory.get("classification") != "UNKNOWN" + or host_memory.get("receive_code") != "MISSING" + or host_memory.get("mapping_code") != "MISSING" + or host_memory.get("entrypoint_selection") != "UNKNOWN" + ): + errors.append("host-to-memory is overclaimed") + reboot = manifest.get("reboot_and_crash", {}) + if ( + reboot.get("classification") != "REBOOT_RECOVERY_UNPROVEN" + or reboot.get("automatic_retry") is not False + ): + errors.append("reboot recovery is overclaimed or retry enabled") + + finding_classes = { + item.get("id"): item.get("classification") + for item in manifest.get("security_findings", []) + } + if finding_classes.get("unknown_live_filesystem_effect") != "BRICK_RELEVANT": + errors.append("unknown live filesystem effect is not brick relevant") + if finding_classes.get("unknown_autoload_effect") != "BRICK_RELEVANT": + errors.append("unknown autoload effect is not brick relevant") + if finding_classes.get( + "temporary_socket_not_automatically_brick_relevant" + ) == "BRICK_RELEVANT": + errors.append("temporary socket is automatically overclassified") + + contract = manifest.get("future_rescue_contract", {}) + if ( + contract.get("requirements_total") != 20 + or contract.get("bootstrap_supported_now") != [] + or contract.get("target_source_allowed") is not False + ): + errors.append("future rescue contract is not fully blocked") + if len(manifest.get("output_options", [])) != 5: + errors.append("output architecture options are incomplete") + if len(manifest.get("missing_actual_files", [])) < 5: + errors.append("missing actual bootstrap inputs are incomplete") + final = manifest.get("final_decision", {}) + if final.get("classification") != EXPECTED_CLASSIFICATION: + errors.append("final decision changed") + return errors + + +def validate_protocol(protocol_manifest: dict[str, Any]) -> list[str]: + errors: list[str] = [] + expected = { + "phase": "PHASE_0_9E_LOADER_9020_PROTOCOL", + "status": "UNPROVEN_IMPLEMENTATION_MISSING", + "exact_server_source_present": False, + "exact_server_binary_present": False, + "exact_host_client_present": False, + "protocol_identity": None, + "classification": "CONCEPTUAL_9020_DESCRIPTION_IS_NOT_PROTOCOL_PROOF", + } + for field, value in expected.items(): + if protocol_manifest.get(field) != value: + errors.append(f"protocol {field} changed") + protocol = protocol_manifest.get("protocol", {}) + if protocol.get("port") != 9020: + errors.append("protocol port changed") + if protocol_model_complete(protocol): + errors.append("missing implementation cannot yield a complete protocol") + for field in ( + "handshake", + "length_fields", + "maximum_payload_size", + "timeout", + "retry", + "response_direction", + "parser", + "mappings", + "entrypoint_validation", + "filesystem_staging", + "cleanup", + ): + if protocol.get(field) not in {"UNKNOWN", "MISSING"}: + errors.append(f"protocol.{field} is overclaimed") + host_model = protocol_manifest.get("host_model", {}) + for field in ( + "allowed", + "created", + "network_port_opened", + "device_connection_performed", + "payload_sent", + "binary_executed", + ): + if host_model.get(field) is not False: + errors.append(f"host_model.{field} must be false") + return errors + + +def _changed_paths(root: Path) -> set[str]: + paths = { + path + for path in git(root, "diff", "--name-only", EXPECTED_BASELINE).splitlines() + if path + } + status = git(root, "status", "--porcelain=v1", "--untracked-files=all") + for line in status.splitlines(): + if not line: + continue + path = line[3:] + if " -> " in path: + path = path.split(" -> ", 1)[1] + paths.add(path.replace("\\", "/")) + return paths + + +def phase09e_path_errors(paths: set[str]) -> list[str]: + errors: list[str] = [] + for path in paths: + normalized = path.replace("\\", "/") + suffix = Path(normalized).suffix.lower() + if normalized.startswith(FORBIDDEN_PRODUCTION_PREFIXES): + errors.append(f"production/target path changed: {normalized}") + if suffix in FORBIDDEN_NEW_SUFFIXES: + errors.append(f"forbidden target artifact/source added: {normalized}") + lowered = normalized.lower() + if any( + token in lowered + for token in ("rescue.elf", "readback.elf", "executionpackage", "installpackage") + ): + errors.append(f"forbidden Phase-0.9E output added: {normalized}") + if "tests/host/phase09e_protocol_model.py" in paths: + errors.append("protocol model created without exact protocol source") + return errors + + +def _validate_checksum_file(root: Path) -> list[str]: + path = root / "packaging/phase09e/SHA256SUMS.txt" + if not path.is_file(): + return ["missing packaging/phase09e/SHA256SUMS.txt"] + errors: list[str] = [] + for line in path.read_text(encoding="utf-8").splitlines(): + if not line or line.startswith("#"): + continue + parts = line.split(" ", 1) + if len(parts) != 2 or not re.fullmatch(r"[0-9a-f]{64}", parts[0]): + errors.append(f"invalid checksum line: {line}") + continue + expected, label = parts + if label == f"~/Downloads/{OUTER_NAME}": + actual = sha256_file(candidate_archive_path(root)) + elif label == f"~/Downloads/{OUTER_NAME}::{INNER_NAME}": + with zipfile.ZipFile(candidate_archive_path(root)) as archive: + digest = hashlib.sha256() + with archive.open(INNER_NAME) as stream: + for chunk in iter(lambda: stream.read(1024 * 1024), b""): + digest.update(chunk) + actual = digest.hexdigest() + else: + candidate = root / label + if not candidate.is_file(): + errors.append(f"checksum target missing: {label}") + continue + actual = sha256_file(candidate) + if actual != expected: + errors.append(f"checksum mismatch: {label}") + return errors + + +def collect_errors(root: Path) -> list[str]: + errors: list[str] = [] + manifest_path = ( + root / "manifests/runtime/phase-0.9e-bootstrap-provenance.json" + ) + protocol_path = root / "manifests/runtime/phase-0.9e-loader-protocol.json" + schema_path = ( + root / "manifests/runtime/phase-0.9e-bootstrap-provenance.schema.json" + ) + protocol_schema_path = ( + root / "manifests/runtime/phase-0.9e-loader-protocol.schema.json" + ) + try: + manifest = load_json(manifest_path) + protocol_manifest = load_json(protocol_path) + schema = load_json(schema_path) + protocol_schema = load_json(protocol_schema_path) + except (OSError, ValueError, json.JSONDecodeError) as error: + return [str(error)] + errors.extend(validate_schema_instance(schema, manifest)) + errors.extend(validate_schema_instance(protocol_schema, protocol_manifest)) + errors.extend(validate_manifest(manifest)) + errors.extend(validate_protocol(protocol_manifest)) + + for relative in DELIVERABLES: + if not (root / relative).is_file(): + errors.append(f"missing deliverable: {relative}") + if git(root, "rev-parse", "--abbrev-ref", "HEAD") != EXPECTED_BRANCH: + errors.append("current branch is not the Phase-0.9E branch") + if git(root, "merge-base", EXPECTED_BASELINE, "HEAD") != EXPECTED_BASELINE: + errors.append("Phase-0.9E branch no longer descends from baseline") + if sha256_file(root / "manifests/artifact-denylist.json") != DENYLIST_HASH: + errors.append("permanent denylist changed") + for relative, expected in IMMUTABLE_HASHES.items(): + path = root / relative + if not path.is_file() or sha256_file(path) != expected: + errors.append(f"immutable evidence changed: {relative}") + for name, (relative, expected) in SOURCE_COMMITS.items(): + source_root = (root / relative).resolve() + if git(source_root, "rev-parse", "HEAD") != expected: + errors.append(f"source HEAD changed: {name}") + if git(source_root, "status", "--porcelain"): + errors.append(f"source tree dirty: {name}") + for relative, (expected_size, expected_hash) in SOURCE_HASHES.items(): + path = (root / relative).resolve() + if not path.is_file(): + errors.append(f"source artifact missing: {relative}") + continue + if path.stat().st_size != expected_size or sha256_file(path) != expected_hash: + errors.append(f"source artifact identity changed: {relative}") + + outer = candidate_archive_path(root) + if not outer.is_file(): + errors.append(f"candidate archive missing: {outer}") + else: + if outer.stat().st_size != OUTER_SIZE or sha256_file(outer) != OUTER_SHA256: + errors.append("candidate outer archive identity changed") + try: + with zipfile.ZipFile(outer) as archive: + if tuple(item.filename for item in archive.infolist()) != ZIP_ENTRIES: + errors.append("candidate ZIP inventory changed") + info = archive.getinfo(INNER_NAME) + if info.file_size != INNER_SIZE: + errors.append("candidate inner size changed") + digest = hashlib.sha256() + magic = b"" + with archive.open(INNER_NAME) as stream: + while True: + chunk = stream.read(1024 * 1024) + if not chunk: + break + if not magic: + magic = chunk[:6] + digest.update(chunk) + if magic != b"SIECAF" or digest.hexdigest() != INNER_SHA256: + errors.append("candidate inner identity changed") + except (OSError, KeyError, zipfile.BadZipFile) as error: + errors.append(f"candidate ZIP audit failed: {error}") + errors.extend(phase09e_path_errors(_changed_paths(root))) + errors.extend(_validate_checksum_file(root)) + return errors + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + errors = collect_errors(args.root.resolve()) + if errors: + for error in errors: + print(f"ERROR: {error}") + return 1 + print("Phase-0.9E bootstrap provenance validation: PASS") + print(f"Decision: {EXPECTED_CLASSIFICATION}") + print("Device actions: NONE") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/validate_phase09er2_correlation.py b/tools/validate_phase09er2_correlation.py new file mode 100644 index 0000000..71edd14 --- /dev/null +++ b/tools/validate_phase09er2_correlation.py @@ -0,0 +1,658 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate the host-only Phase-0.9E-R2 inner-backup correlation.""" + +from __future__ import annotations + +import argparse +import hashlib +import importlib.util +import json +from pathlib import Path +import re +import subprocess +import sys +from types import ModuleType +from typing import Any +import zipfile + + +BASELINE = "48714bb542f3ea5b9893d0461ba4c9d938cd6d8d" +BRANCH = "codex/chimera-gfx-phase09e-r2-inner-correlation" +PHASE = "PHASE_0_9E_R2_INNER_BACKUP_CORRELATION" +OUTER_NAME = "Y2JB-Autoloader-403-1240.zip" +OUTER_SIZE = 504159435 +OUTER_SHA256 = "805e3f87f0c371223619ffc7d3a7b3c0d41a1fae8a8b1171d9e2f162659e8291" +INNER_NAME = "PS5/EXPORT/BACKUP/202606102126_00/archive.dat" +INNER_SIZE = 504365056 +INNER_SHA256 = "6439834e8856d45b6d6fe699b74c35ca6985a199ea8ecf3e398c018d37be2d55" +DENYLIST_SHA256 = "e9603b0e3792781ad5b511afb22ef61e3d4fd4c5a16bf928f8609193bcd97783" +PRIMARY_CLASSIFICATION = "LOCAL_BACKUP_UNCORRELATED" +AUTHORIZATION_FIELDS = ( + "device_action_authorized", + "target_build_authorized", + "transfer_authorized", + "execution_authorized", + "installation_authorized", + "lifecycle_authorized", + "autoload_authorized", + "device_write_authorized", + "rescue_payload_design_authorized", + "automatic_retry", +) +IMMUTABLE_HASHES = { + "manifests/runtime/phase-0.8-read-only-preflight.json": "47d7f452f8799979fe99b3e6d56859f03544112725bf7e5b349eba5ed81b3322", + "manifests/runtime/phase-0.8-remediation.json": "a9dafed8c83722c43709dcf90ea117c21d6f996ff8fe233810ed08aa20cbe071", + "manifests/runtime/phase-0.9-anti-brick-design.json": "39fd7c70cae998d9d74e7caf1ff3c19f9f76f7de2e5a50bc09baede4ba53e9e9", + "manifests/runtime/phase-0.9b-observer.json": "104c4a667ad17f9827fc7276852c6faeef96effb8e3561a5ebc19a62c7d51634", + "manifests/runtime/phase-0.9c-feasibility.json": "84eb737ff3486d0c5d8b5ecf06809f93bc573497da4919595b4c1817fd34247c", + "manifests/runtime/phase-0.9d-existing-stack-readback.json": "86e5aaf034685dbe058b71ffeec645b682f0a8cc7d249e8ac0397155233991de", + "manifests/runtime/phase-0.9e-bootstrap-provenance.json": "5dfa9bfe2ae751b2f0ea0e03c60c1a4471a35452389cf471456e6c54eb4601cf", + "manifests/runtime/phase-0.9e-loader-protocol.json": "a7ca8b4e8072cb60ad4cd4869f6c508e8014059c8d81ad3b94a8d9e8cd0db4cb", + "manifests/runtime/phase-0.9e-r-release-correlation.json": "09f916b20def51fd519690b6d940de5f93818a4d9bd5eebef462fe2a828b88be", + "manifests/runtime/phase-0.9e-r-port9020-audit.json": "ea84f1885ad4286670a401cbf73e9a371cdcd03a1f247908b90ea27553ee820e", +} +DELIVERABLES = ( + "docs/runtime/phase-0.9e-r2-local-download-provenance.md", + "docs/runtime/phase-0.9e-r2-inner-archive-correlation.md", + "docs/runtime/phase-0.9e-r2-siecaf-structural-analysis.md", + "docs/runtime/phase-0.9e-r2-community-backup-correlation.md", + "docs/runtime/phase-0.9e-r2-final-provenance-decision.md", + "manifests/runtime/phase-0.9e-r2-inner-correlation.json", + "manifests/runtime/phase-0.9e-r2-siecaf-fingerprints.json", + "tools/inspect_siecaf_header.py", + "tools/validate_phase09er2_correlation.py", + "tests/test_phase09er2_correlation.py", + "tests/test_siecaf_header_parser.py", + "packaging/phase09er2/SHA256SUMS.txt", +) +FORBIDDEN_SUFFIXES = { + ".c", + ".cc", + ".cpp", + ".s", + ".asm", + ".ld", + ".elf", + ".self", + ".sprx", + ".pkg", + ".bin", + ".wasm", + ".sqlite", + ".download", +} +FORBIDDEN_PREFIXES = ("include/", "src/", "adapters/", "samples/") +MAX_TRACKED_FILE_SIZE = 10 * 1024 * 1024 + + +def load_json(path: Path) -> dict[str, Any]: + with path.open("r", encoding="utf-8") as stream: + value = json.load(stream) + if not isinstance(value, dict): + raise ValueError(f"{path} is not a JSON object") + return value + + +def sha256_file(path: Path) -> str: + digest = hashlib.sha256() + with path.open("rb") as stream: + for chunk in iter(lambda: stream.read(1024 * 1024), b""): + digest.update(chunk) + return digest.hexdigest() + + +def sha256_zip_entry(path: Path, entry: str) -> tuple[int, str]: + digest = hashlib.sha256() + with zipfile.ZipFile(path) as archive: + info = archive.getinfo(entry) + with archive.open(info) as stream: + for chunk in iter(lambda: stream.read(1024 * 1024), b""): + digest.update(chunk) + return info.file_size, digest.hexdigest() + + +def git(root: Path, *args: str) -> str: + result = subprocess.run( + ["git", *args], + cwd=root, + check=False, + capture_output=True, + text=True, + ) + if result.returncode != 0: + raise RuntimeError(result.stderr.strip() or "git command failed") + return result.stdout.strip() + + +def load_module(name: str, path: Path) -> ModuleType: + spec = importlib.util.spec_from_file_location(name, path) + if spec is None or spec.loader is None: + raise RuntimeError(f"could not import {path}") + module = importlib.util.module_from_spec(spec) + sys.modules[name] = module + spec.loader.exec_module(module) + return module + + +def candidate_archive_path(root: Path) -> Path: + direct = Path.home() / "Downloads" / OUTER_NAME + if direct.is_file(): + return direct + for ancestor in (root, *root.parents): + if ancestor.parent.name.lower() == "users": + return ancestor / "Downloads" / OUTER_NAME + return direct + + +def inner_byte_match( + *, + left_size: int, + right_size: int, + left_sha256: str, + right_sha256: str, + full_byte_equal: bool, +) -> bool: + """Require count, digest, and complete comparison for an inner match.""" + return left_size == right_size and left_sha256 == right_sha256 and full_byte_equal + + +def classify_inner_comparison( + *, + download_valid: bool, + inner_present: bool, + left_size: int, + right_size: int, + left_sha256: str, + right_sha256: str, + full_byte_equal: bool, +) -> str: + if not download_valid: + return "OFFICIAL_ASSET_DOWNLOAD_INVALID" + if not inner_present: + return "EXPECTED_INNER_ARCHIVE_ABSENT" + if inner_byte_match( + left_size=left_size, + right_size=right_size, + left_sha256=left_sha256, + right_sha256=right_sha256, + full_byte_equal=full_byte_equal, + ): + return "INNER_ARCHIVE_BYTE_MATCH" + if left_size == right_size: + return "INNER_ARCHIVE_SIZE_ONLY_MATCH" + return "INNER_ARCHIVE_HASH_MISMATCH" + + +def phase09f_reconsideration_allowed( + *, + inner_source_bound: bool, + mediafire_maker_source_bound: bool, + auditable_bootstrap_closure: bool, +) -> bool: + return auditable_bootstrap_closure and ( + inner_source_bound or mediafire_maker_source_bound + ) + + +def _canonical_hash(records: list[dict[str, Any]]) -> str: + encoded = json.dumps( + records, sort_keys=True, separators=(",", ":"), ensure_ascii=True + ).encode("ascii") + return hashlib.sha256(encoded).hexdigest() + + +def _normalized_records( + archive: dict[str, Any], +) -> tuple[list[dict[str, Any]], list[dict[str, Any]], list[dict[str, Any]]]: + segment_fields = ( + "section_id", + "part_number", + "data_offset", + "aligned_length", + "unaligned_length", + "hash_key_id_or_algorithm_type", + "encryption_key_id_or_algorithm_version", + "iv_hex", + ) + layout_fields = segment_fields[:-1] + hash_fields = ("section_id", "section_type", "section_hash_128_hex") + segments = [ + {field: item[field] for field in segment_fields} + for item in sorted( + archive["segments"], + key=lambda value: ( + value["section_id"], + value["part_number"], + value["data_offset"], + ), + ) + ] + hashes = [ + {field: item[field] for field in hash_fields} + for item in sorted( + archive["section_hashes"], + key=lambda value: (value["section_id"], value["section_type"]), + ) + ] + layout = [{field: item[field] for field in layout_fields} for item in segments] + return segments, hashes, layout + + +def validate_fingerprint_archive(name: str, archive: dict[str, Any]) -> list[str]: + errors: list[str] = [] + if archive.get("classification") != "SIECAF_VALID_STRUCTURE": + errors.append(f"{name}: structure is not valid") + return errors + header = archive.get("header", {}) + if ( + "key_or_unknown_16_hex" in header + or "raw_hex" in header + or header.get("key_or_unknown_16_redacted") is not True + or len(header.get("key_or_unknown_16_sha256", "")) != 64 + ): + errors.append(f"{name}: cryptographic header material is not redacted") + count = header.get("segment_count") + if len(archive.get("segments", [])) != count: + errors.append(f"{name}: segment count mismatch") + if len(archive.get("section_hashes", [])) != count: + errors.append(f"{name}: hash count mismatch") + segments, hashes, layout = _normalized_records(archive) + if _canonical_hash(segments) != archive.get("normalized_segment_table_sha256"): + errors.append(f"{name}: normalized segment hash mismatch") + if _canonical_hash(hashes) != archive.get("normalized_hash_blocks_sha256"): + errors.append(f"{name}: normalized hash-block hash mismatch") + layout_value = [ + { + "unknown_u64": header.get("unknown_u64"), + "version_i32": header.get("version_i32"), + "segment_count": count, + "file_offset": header.get("file_offset"), + "file_size": header.get("file_size"), + }, + *layout, + ] + if _canonical_hash(layout_value) != archive.get("normalized_layout_sha256"): + errors.append(f"{name}: normalized layout hash mismatch") + structural = _canonical_hash( + [ + { + "header_raw_sha256": header.get("raw_sha256"), + "normalized_segment_table_sha256": archive.get( + "normalized_segment_table_sha256" + ), + "normalized_hash_blocks_sha256": archive.get( + "normalized_hash_blocks_sha256" + ), + } + ] + ) + if structural != archive.get("structural_fingerprint_sha256"): + errors.append(f"{name}: structural fingerprint mismatch") + for field in ( + "duplicate_metadata_section_keys", + "duplicate_hash_section_ids", + "overlaps", + "gaps", + "errors", + "warnings", + ): + if archive.get(field): + errors.append(f"{name}: unexpected {field}") + if archive.get("trailing_data_bytes") != 0: + errors.append(f"{name}: trailing data is not zero") + return errors + + +def validate_fingerprints( + root: Path, + value: dict[str, Any], + inspector: ModuleType, + local_path: Path, +) -> list[str]: + errors: list[str] = [] + if value.get("phase") != PHASE or value.get("status") != "HOST_ONLY_EVIDENCE": + errors.append("fingerprint manifest phase/status mismatch") + evidence = value.get("parser_evidence", {}) + if ( + evidence.get("source_commit") != "36d014672bc87577a6e0d750c2cccadc3fae0854" + or evidence.get("header_blob") != "fdbc368353a7797464873ada306f0297257eb95e" + ): + errors.append("public SIECAF source binding changed") + for field in ( + "decryption_attempted", + "content_extraction_attempted", + "ps5_bar_tool_executed", + ): + if evidence.get(field) is not False: + errors.append(f"fingerprint manifest permits {field}") + archives = value.get("archives", {}) + expected_names = { + "local", + "official_y2jb_1_6_4_03", + "community_owendswang_v1_4_autoloader_7_61", + } + if set(archives) != expected_names: + errors.append("fingerprint archive set mismatch") + return errors + for name, archive in archives.items(): + errors.extend(validate_fingerprint_archive(name, archive)) + with zipfile.ZipFile(local_path) as local: + info = local.getinfo(INNER_NAME) + with local.open(info) as stream: + observed = inspector.inspect_siecaf( + stream, info.file_size, source_label="local-mediafire-inner" + ) + if observed != archives["local"]: + errors.append("local SIECAF evidence does not reproduce") + pairs = { + "local_vs_official_y2jb_1_6_4_03": ( + archives["local"], + archives["official_y2jb_1_6_4_03"], + ), + "local_vs_community_owendswang_v1_4_autoloader_7_61": ( + archives["local"], + archives["community_owendswang_v1_4_autoloader_7_61"], + ), + "official_vs_community": ( + archives["official_y2jb_1_6_4_03"], + archives["community_owendswang_v1_4_autoloader_7_61"], + ), + } + comparisons = value.get("comparisons", {}) + for name, (left, right) in pairs.items(): + expected = inspector.compare_structures(left, right) + if comparisons.get(name) != expected: + errors.append(f"{name}: comparison does not reproduce") + if expected["classification"] != "SIECAF_LAYOUT_DIFFERENT": + errors.append(f"{name}: expected fail-closed layout difference") + authorization = value.get("authorization", {}) + for field in AUTHORIZATION_FIELDS: + if authorization.get(field) is not False: + errors.append(f"fingerprints authorize {field}") + return errors + + +def validate_manifest(value: dict[str, Any]) -> list[str]: + errors: list[str] = [] + if value.get("phase") != PHASE or value.get("status") != "HOST_ONLY_COMPLETE": + errors.append("main manifest phase/status mismatch") + if value.get("baseline_commit") != BASELINE: + errors.append("baseline commit mismatch") + if value.get("prior_phase_classification") != "LOCAL_BACKUP_NOT_CORRELATED": + errors.append("prior classification was not preserved") + if value.get("primary_provenance_classification") != PRIMARY_CLASSIFICATION: + errors.append("primary provenance classification mismatch") + for field in ( + "outer_zip_official", + "runtime_deployment_verified", + "current_device_contents_verified", + "phase09f_offline_design_reconsideration_allowed", + ): + if value.get(field) is not False: + errors.append(f"{field} must remain false") + if value.get("runtime_firmware_9_60") != "UNPROVEN": + errors.append("runtime firmware state was overclaimed") + outer = value.get("local_candidate", {}).get("outer", {}) + inner = value.get("local_candidate", {}).get("inner", {}) + if ( + outer.get("name"), + outer.get("size"), + outer.get("sha256"), + ) != (OUTER_NAME, OUTER_SIZE, OUTER_SHA256): + errors.append("local outer identity mismatch") + if ( + inner.get("entry"), + inner.get("size"), + inner.get("sha256"), + ) != (INNER_NAME, INNER_SIZE, INNER_SHA256): + errors.append("local inner identity mismatch") + provenance = value.get("local_download_provenance", {}) + zone = provenance.get("zone_identifier", {}) + if provenance.get("classification") != "MEDIAFIRE_URL_EXACT": + errors.append("local URL provenance classification mismatch") + if zone.get("mediafire_file_key") != "jq3fcutuwbb1mrb": + errors.append("MediaFire key mismatch") + if "" not in zone.get("host_url", {}).get( + "path_redacted", "" + ): + errors.append("signed MediaFire path is not redacted") + browser = provenance.get("browser_history", {}) + if ( + browser.get("matching_records") != 0 + or browser.get("full_history_exported") is not False + or browser.get("original_databases_modified") is not False + ): + errors.append("browser-history boundary mismatch") + mediafire = value.get("mediafire_object", {}) + if mediafire.get("classification") != "MEDIAFIRE_OBJECT_METADATA_BOUND": + errors.append("MediaFire object classification mismatch") + if mediafire.get("redownload_performed") is not False: + errors.append("MediaFire duplicate download was recorded") + official = value.get("official_candidate", {}) + if ( + official.get("asset_id") != 442358421 + or official.get("official_size") != 504395044 + or official.get("official_sha256") + != "b01b4f442327f9eca90ffc4506dfa58249e4ac70cb9d7488c856c9e8dfaf37b4" + or official.get("download_attempts") != 1 + or official.get("resume") is not False + or official.get("automatic_retry") is not False + or official.get("download_valid") is not True + ): + errors.append("official download evidence mismatch") + comparison = official.get("inner_comparison", {}) + observed_class = classify_inner_comparison( + download_valid=official.get("download_valid") is True, + inner_present=True, + left_size=comparison.get("official_inner_size", -1), + right_size=comparison.get("local_inner_size", -1), + left_sha256=comparison.get("official_inner_sha256", ""), + right_sha256=comparison.get("local_inner_sha256", ""), + full_byte_equal=comparison.get("full_byte_equal") is True, + ) + if comparison.get("classification") != observed_class: + errors.append("official inner classification does not reproduce") + families = value.get("community_families", []) + if len(families) != 3: + errors.append("community family count mismatch") + downloaded = [item for item in families if item.get("large_backup_downloaded")] + if len(downloaded) != 1: + errors.append("community large-download count mismatch") + elif ( + downloaded[0].get("downloaded_asset", {}).get("inner_comparison") + != "INNER_ARCHIVE_HASH_MISMATCH" + ): + errors.append("community inner mismatch is not preserved") + siecaf = value.get("siecaf", {}) + for field in ( + "local_vs_official", + "local_vs_community", + "official_vs_community", + ): + if siecaf.get(field) != "SIECAF_LAYOUT_DIFFERENT": + errors.append(f"{field}: structural result overclaimed") + for field in ( + "decryption_attempted", + "content_extraction_attempted", + "ps5_bar_tool_executed", + ): + if siecaf.get(field) is not False: + errors.append(f"main manifest permits {field}") + authorization = value.get("authorization", {}) + for field in AUTHORIZATION_FIELDS: + if authorization.get(field) is not False: + errors.append(f"main manifest authorizes {field}") + actions = value.get("actions", {}) + prohibited_true = ( + "download_resume_used", + "automatic_retry_used", + "downloaded_file_executed", + "downloaded_file_restored", + "ps5_bar_tool_executed", + "ps5_connected", + "ps5_ip_used", + "device_request_performed", + "files_transferred_to_or_from_ps5", + "target_build_performed", + "target_code_created", + "target_artifact_created", + "payload_created", + "device_client_created", + ) + for field in prohibited_true: + if actions.get(field) is not False: + errors.append(f"prohibited action recorded: {field}") + storage = value.get("temporary_research_storage", {}) + if ( + storage.get("cleaned") is not True + or storage.get("cleanup_verified") is not True + or storage.get("large_files_tracked") is not False + or storage.get("browser_database_copies_tracked") is not False + ): + errors.append("temporary-storage final state mismatch") + if value.get("blocking_state", {}).get("payload_manager_backup") != ( + "HARD_BLOCKER_FOR_INSTALLATION" + ): + errors.append("Payload Manager installation blocker changed") + if phase09f_reconsideration_allowed( + inner_source_bound=False, + mediafire_maker_source_bound=False, + auditable_bootstrap_closure=False, + ): + errors.append("Phase 0.9F fail-closed decision failed") + return errors + + +def validate_checksums(root: Path) -> list[str]: + errors: list[str] = [] + path = root / "packaging/phase09er2/SHA256SUMS.txt" + if not path.is_file(): + return ["Phase-0.9E-R2 checksum inventory is missing"] + entries = 0 + for line in path.read_text(encoding="utf-8").splitlines(): + if not line or line.startswith("#"): + continue + match = re.fullmatch(r"([0-9a-f]{64}) (.+)", line) + if not match: + errors.append(f"malformed checksum line: {line}") + continue + expected, relative = match.groups() + target = root / relative + if not target.is_file(): + errors.append(f"checksum target missing: {relative}") + elif sha256_file(target) != expected: + errors.append(f"checksum mismatch: {relative}") + entries += 1 + if entries < 12: + errors.append("checksum inventory is unexpectedly small") + return errors + + +def validate_repository(root: Path) -> list[str]: + errors: list[str] = [] + if git(root, "branch", "--show-current") != BRANCH: + errors.append("current branch mismatch") + ancestors = git(root, "merge-base", "--is-ancestor", BASELINE, "HEAD") + if ancestors: + errors.append("unexpected merge-base output") + for relative, expected in IMMUTABLE_HASHES.items(): + path = root / relative + if not path.is_file() or sha256_file(path) != expected: + errors.append(f"immutable evidence changed: {relative}") + denylist = root / "manifests/artifact-denylist.json" + if sha256_file(denylist) != DENYLIST_SHA256: + errors.append("permanent artifact denylist changed") + for relative in DELIVERABLES: + if not (root / relative).is_file(): + errors.append(f"deliverable missing: {relative}") + tracked = git(root, "ls-files").splitlines() + for relative in tracked: + path = root / relative + if path.is_file() and path.stat().st_size > MAX_TRACKED_FILE_SIZE: + errors.append(f"large tracked file: {relative}") + suffix = Path(relative).suffix.lower() + if relative.startswith("packaging/phase09er2/") and suffix in { + ".zip", + ".rar", + ".7z", + ".dat", + }: + errors.append(f"backup material tracked: {relative}") + changed = git( + root, "diff", "--name-only", "--diff-filter=ACMR", f"{BASELINE}...HEAD" + ).splitlines() + changed.extend(git(root, "diff", "--name-only", "--diff-filter=ACMR").splitlines()) + untracked = git(root, "ls-files", "--others", "--exclude-standard").splitlines() + changed.extend(untracked) + for relative in sorted(set(filter(None, changed))): + normalized = relative.replace("\\", "/") + suffix = Path(normalized).suffix.lower() + if normalized.startswith(FORBIDDEN_PREFIXES) or suffix in FORBIDDEN_SUFFIXES: + errors.append(f"target/binary source forbidden in phase: {normalized}") + path = root / relative + if path.is_file() and path.stat().st_size <= 2 * 1024 * 1024: + try: + text = path.read_text(encoding="utf-8") + except UnicodeDecodeError: + errors.append(f"unexpected binary tracked or staged: {normalized}") + continue + if re.search(r"download2434\.mediafire\.com/[^<\s]+", text): + errors.append(f"unredacted signed MediaFire URL: {normalized}") + return errors + + +def validate_local_candidate(root: Path) -> tuple[Path, list[str]]: + errors: list[str] = [] + path = candidate_archive_path(root) + if not path.is_file(): + return path, [f"local candidate missing: {path}"] + if path.stat().st_size != OUTER_SIZE or sha256_file(path) != OUTER_SHA256: + errors.append("local outer candidate identity changed") + return path, errors + try: + size, digest = sha256_zip_entry(path, INNER_NAME) + except (KeyError, OSError, zipfile.BadZipFile) as error: + errors.append(f"local inner candidate cannot be read: {error}") + else: + if size != INNER_SIZE or digest != INNER_SHA256: + errors.append("local inner candidate identity changed") + return path, errors + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + root = args.root.resolve() + errors: list[str] = [] + local_path, candidate_errors = validate_local_candidate(root) + errors.extend(candidate_errors) + errors.extend(validate_repository(root)) + main_manifest = load_json( + root / "manifests/runtime/phase-0.9e-r2-inner-correlation.json" + ) + fingerprints = load_json( + root / "manifests/runtime/phase-0.9e-r2-siecaf-fingerprints.json" + ) + errors.extend(validate_manifest(main_manifest)) + if local_path.is_file(): + inspector = load_module( + "phase09er2_siecaf_inspector", + root / "tools/inspect_siecaf_header.py", + ) + errors.extend(validate_fingerprints(root, fingerprints, inspector, local_path)) + errors.extend(validate_checksums(root)) + if errors: + for error in errors: + print(f"ERROR: {error}") + return 1 + print( + "Phase-0.9E-R2 validation PASS: " + "LOCAL_BACKUP_UNCORRELATED; no device action authorized" + ) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/validate_phase09er_provenance.py b/tools/validate_phase09er_provenance.py new file mode 100644 index 0000000..5184b45 --- /dev/null +++ b/tools/validate_phase09er_provenance.py @@ -0,0 +1,621 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate the host-only Phase-0.9E-R official Y2JB correlation.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +from pathlib import Path +import re +import subprocess +from typing import Any +import zipfile + + +BASELINE = "3d3151bd12ff0f786c1e1b9af75d7174408e3d2f" +BRANCH = "codex/chimera-gfx-phase09e-r-y2jb-correlation" +PHASE = "PHASE_0_9E_R_OFFICIAL_Y2JB_CORRELATION" +OUTER_NAME = "Y2JB-Autoloader-403-1240.zip" +OUTER_SIZE = 504159435 +OUTER_SHA256 = "805e3f87f0c371223619ffc7d3a7b3c0d41a1fae8a8b1171d9e2f162659e8291" +INNER_NAME = "PS5/EXPORT/BACKUP/202606102126_00/archive.dat" +INNER_SIZE = 504365056 +INNER_SHA256 = "6439834e8856d45b6d6fe699b74c35ca6985a199ea8ecf3e398c018d37be2d55" +DENYLIST_SHA256 = "e9603b0e3792781ad5b511afb22ef61e3d4fd4c5a16bf928f8609193bcd97783" +UPSTREAM_COMMIT = "0dbbf4e7e0203af7e5d101a3256c634edf4e3ba2" +UPSTREAM_TREE = "c4344f43af7c268337437e6419548dba6f6bc211" +UPSTREAM_REMOTE = "https://github.com/Gezine/Y2JB.git" +PORT_9020_CLASSIFICATION = "PORT_9020_REFERENCE_ONLY" +FINAL_CLASSIFICATION = "LOCAL_BACKUP_NOT_CORRELATED" +PROVENANCE_FIELDS = ( + "logical_name", + "artifact_role", + "local_relative_path", + "size", + "sha256", + "file_type", + "source_repository", + "source_commit", + "build_identity", + "version", + "obtained_from", + "evidence_that_it_is_deployed_or_used", + "confidence", + "immutable", + "executable", + "persistent_on_device", + "transferred_per_session", + "required_for_bootstrap", + "required_for_recovery", +) + +AUTHORIZATION_FIELDS = ( + "device_action_authorized", + "target_build_authorized", + "transfer_authorized", + "execution_authorized", + "installation_authorized", + "lifecycle_authorized", + "autoload_authorized", + "device_write_authorized", + "automatic_retry", +) +IMMUTABLE_HASHES = { + "manifests/runtime/phase-0.8-read-only-preflight.json": + "47d7f452f8799979fe99b3e6d56859f03544112725bf7e5b349eba5ed81b3322", + "manifests/runtime/phase-0.8-remediation.json": + "a9dafed8c83722c43709dcf90ea117c21d6f996ff8fe233810ed08aa20cbe071", + "manifests/runtime/phase-0.9-anti-brick-design.json": + "39fd7c70cae998d9d74e7caf1ff3c19f9f76f7de2e5a50bc09baede4ba53e9e9", + "manifests/runtime/phase-0.9b-observer.json": + "104c4a667ad17f9827fc7276852c6faeef96effb8e3561a5ebc19a62c7d51634", + "manifests/runtime/phase-0.9c-feasibility.json": + "84eb737ff3486d0c5d8b5ecf06809f93bc573497da4919595b4c1817fd34247c", + "manifests/runtime/phase-0.9d-existing-stack-readback.json": + "86e5aaf034685dbe058b71ffeec645b682f0a8cc7d249e8ac0397155233991de", + "manifests/runtime/phase-0.9e-bootstrap-provenance.json": + "5dfa9bfe2ae751b2f0ea0e03c60c1a4471a35452389cf471456e6c54eb4601cf", + "manifests/runtime/phase-0.9e-loader-protocol.json": + "a7ca8b4e8072cb60ad4cd4869f6c508e8014059c8d81ad3b94a8d9e8cd0db4cb", +} +UPSTREAM_FILE_HASHES = { + "README.md": (7500, "16bfdaa624b8b04f4a6a4a7d512ca8473ad2db73e80df39a974267403e34f751"), + "payload_sender.py": (1064, "8c87920c41dbdbd66b9f36ca9509f0d6bef9170f351dd97ff831cfb98e642ec6"), + "log_server.py": (929, "463114fd46479a7286706de13beb3f52221f36f3cbe37dc5a27bdb6104787a98"), + "appinfo_editor.py": (2008, "c1bcb453660f597cbc9026dba76519a4929fc3e967c183deec8a1ca56912e2e8"), + "download0/cache/splash_screen/aHR0cHM6Ly93d3cueW91dHViZS5jb20vdHY=/remotejsloader.js": + (7132, "30cc6d1535549b2a49b47a9e0c85a3444cf84177be54398691694b7c6505f38e"), + "download0/cache/splash_screen/aHR0cHM6Ly93d3cueW91dHViZS5jb20vdHY=/elfldr-ps5-1340.elf": + (397000, "30478bcadb6439e1247451c4ac706b6e1385044dd0f12d6486b6d7057929453b"), +} +DELIVERABLES = ( + "docs/runtime/phase-0.9e-r-official-release-correlation.md", + "docs/runtime/phase-0.9e-r-release-source-binding.md", + "docs/runtime/phase-0.9e-r-port9020-source-audit.md", + "docs/runtime/phase-0.9e-r-official-hostsender-audit.md", + "docs/runtime/phase-0.9e-r-provenance-gaps.md", + "docs/approvals/phase-0.9e-r-y2jb-deployed-use-attestation.md", + "manifests/runtime/phase-0.9e-r-release-correlation.json", + "manifests/runtime/phase-0.9e-r-release-correlation.schema.json", + "manifests/runtime/phase-0.9e-r-port9020-audit.json", + "manifests/runtime/phase-0.9e-r-port9020-audit.schema.json", + "tools/validate_phase09er_provenance.py", + "tests/test_phase09er_provenance.py", + "packaging/phase09er/SHA256SUMS.txt", +) +FORBIDDEN_SUFFIXES = { + ".c", ".cc", ".cpp", ".s", ".asm", ".ld", ".elf", ".self", ".sprx", + ".pkg", ".bin", ".wasm", +} +FORBIDDEN_PREFIXES = ("include/", "src/", "adapters/", "samples/") + + +def load_json(path: Path) -> dict[str, Any]: + with path.open("r", encoding="utf-8") as stream: + value = json.load(stream) + if not isinstance(value, dict): + raise ValueError(f"{path} does not contain a JSON object") + return value + + +def sha256_file(path: Path) -> str: + digest = hashlib.sha256() + with path.open("rb") as stream: + for chunk in iter(lambda: stream.read(1024 * 1024), b""): + digest.update(chunk) + return digest.hexdigest() + + +def git(root: Path, *args: str) -> str: + result = subprocess.run( + ["git", *args], + cwd=root, + check=False, + capture_output=True, + text=True, + ) + if result.returncode != 0: + raise RuntimeError(result.stderr.strip() or "git command failed") + return result.stdout.strip() + + +def candidate_archive_path(root: Path) -> Path: + direct = Path.home() / "Downloads" / OUTER_NAME + if direct.is_file(): + return direct + for ancestor in (root, *root.parents): + if ancestor.parent.name.lower() == "users": + return ancestor / "Downloads" / OUTER_NAME + return direct + + +def official_byte_match( + *, + official_source: bool, + local_name: str, + local_size: int, + local_sha256: str, + asset_name: str, + asset_size: int, + asset_sha256: str, +) -> bool: + """Only exact official name, byte count, and hash establish a match.""" + return ( + official_source + and local_name == asset_name + and local_size == asset_size + and local_sha256 == asset_sha256 + ) + + +def source_binding( + *, release_associated: bool, inner_bytes_matched: bool, inner_opaque: bool +) -> str: + if inner_bytes_matched: + return "REPRODUCIBLE_CONTENT_BINDING" + if release_associated and inner_opaque: + return "SOURCE_ONLY_ASSOCIATION" + if release_associated: + return "OFFICIAL_RELEASE_ASSOCIATION" + return "NO_LOCAL_RELEASE_ASSOCIATION" + + +def port_implementation_sufficient(classification: str) -> bool: + return classification in { + "PORT_9020_IMPLEMENTATION_FOUND", + "PORT_9020_IMPLEMENTATION_PARTIAL", + } + + +def phase09f_design_allowed( + *, + correlation: str, + release_commit_known: bool, + upstream_clean: bool, + port_classification: str, + sender_identified: bool, + attestation_available: bool, + all_authorizations_false: bool, +) -> bool: + return all( + ( + correlation == "OFFICIAL_RELEASE_BYTE_MATCH", + release_commit_known, + upstream_clean, + port_implementation_sufficient(port_classification), + sender_identified, + attestation_available, + all_authorizations_false, + ) + ) + + +def sender_duplex(*, sends: bool, receives: bool) -> bool: + return sends and receives + + +def sender_short_send_deficiency(*, uses_sendall: bool, explicit_send_loop: bool) -> bool: + return not (uses_sendall or explicit_send_loop) + + +def attestation_is_runtime_proof(*, attested: bool, hardware_observed: bool) -> bool: + return attested and hardware_observed + + +def _type_matches(value: Any, expected: str | list[str]) -> bool: + if isinstance(expected, list): + return any(_type_matches(value, item) for item in expected) + checks = { + "object": isinstance(value, dict), + "array": isinstance(value, list), + "string": isinstance(value, str), + "boolean": isinstance(value, bool), + "integer": isinstance(value, int) and not isinstance(value, bool), + "null": value is None, + } + return checks.get(expected, True) + + +def validate_schema_instance( + schema: dict[str, Any], instance: Any, path: str = "$" +) -> list[str]: + errors: list[str] = [] + expected_type = schema.get("type") + if expected_type is not None and not _type_matches(instance, expected_type): + return [f"{path}: type mismatch"] + if "const" in schema and instance != schema["const"]: + errors.append(f"{path}: const mismatch") + if "enum" in schema and instance not in schema["enum"]: + errors.append(f"{path}: outside enum") + if isinstance(instance, dict): + for required in schema.get("required", []): + if required not in instance: + errors.append(f"{path}: missing {required}") + properties = schema.get("properties", {}) + for key, value in instance.items(): + if key in properties: + errors.extend( + validate_schema_instance(properties[key], value, f"{path}.{key}") + ) + elif isinstance(schema.get("additionalProperties"), dict): + errors.extend( + validate_schema_instance( + schema["additionalProperties"], value, f"{path}.{key}" + ) + ) + if isinstance(instance, list) and len(instance) < schema.get("minItems", 0): + errors.append(f"{path}: too few items") + return errors + + +def validate_release_manifest(manifest: dict[str, Any]) -> list[str]: + errors: list[str] = [] + expected = { + "phase": PHASE, + "status": "DESIGN_ONLY", + "baseline_commit": BASELINE, + "release_correlation": "OFFICIAL_RELEASE_NO_MATCH", + "final_classification": FINAL_CLASSIFICATION, + "identified_release": None, + "identified_release_asset": None, + "inner_content_binding": "SIECAF_OPAQUE_UNBOUND", + "phase09f_offline_design_allowed": False, + } + for field, value in expected.items(): + if manifest.get(field) != value: + errors.append(f"{field} changed") + for field in AUTHORIZATION_FIELDS: + if manifest.get("authorization", {}).get(field) is not False: + errors.append(f"authorization.{field} must be false") + local = manifest.get("local_candidate", {}) + if ( + local.get("file_name") != OUTER_NAME + or local.get("size") != OUTER_SIZE + or local.get("sha256") != OUTER_SHA256 + ): + errors.append("local candidate identity changed") + inner = manifest.get("inner_archive", {}) + if ( + inner.get("size") != INNER_SIZE + or inner.get("sha256") != INNER_SHA256 + or inner.get("classification") != "OPAQUE_UNBOUND" + or inner.get("further_reverse_engineering_performed") is not False + ): + errors.append("inner SIECAF boundary changed") + assets = manifest.get("assets", []) + if len(assets) != 7: + errors.append("official asset inventory must contain seven assets") + for asset in assets: + if not re.fullmatch(r"[0-9a-f]{64}", str(asset.get("sha256", ""))): + errors.append(f"official asset digest malformed: {asset.get('name')}") + if asset.get("classification") != "NO_MATCH" or asset.get("downloaded"): + errors.append(f"asset was not metadata-excluded: {asset.get('name')}") + if official_byte_match( + official_source=True, + local_name=OUTER_NAME, + local_size=OUTER_SIZE, + local_sha256=OUTER_SHA256, + asset_name=str(asset.get("name")), + asset_size=int(asset.get("size", -1)), + asset_sha256=str(asset.get("sha256")), + ): + errors.append(f"unrecorded official byte match: {asset.get('name')}") + tags = manifest.get("tags", []) + if len(tags) != 5: + errors.append("official tag inventory must contain five tags") + for tag in tags: + if not re.fullmatch(r"[0-9a-f]{40}", str(tag.get("commit", ""))): + errors.append(f"tag commit is not exact: {tag.get('tag')}") + if not re.fullmatch(r"[0-9a-f]{64}", str(tag.get("source_archive_sha256", ""))): + errors.append(f"source archive hash missing: {tag.get('tag')}") + if len(manifest.get("release_inventory", [])) != 6: + errors.append("release inventory must cover versions 1.2 through 1.6") + provenance = manifest.get("artifact_provenance", []) + if len(provenance) < 6: + errors.append("artifact provenance inventory is incomplete") + for artifact in provenance: + missing = [field for field in PROVENANCE_FIELDS if field not in artifact] + if missing: + errors.append( + f"{artifact.get('logical_name')}: missing provenance fields {missing}" + ) + if artifact.get("confidence") == "EXACT_USED": + errors.append( + f"{artifact.get('logical_name')}: deployed-use provenance overclaimed" + ) + if artifact.get("logical_name") == "official_embedded_elfldr_1_6": + if artifact.get("source_commit") is not None: + errors.append("embedded elfldr received an invented source commit") + worktree = manifest.get("upstream_worktree", {}) + if ( + worktree.get("commit") != UPSTREAM_COMMIT + or worktree.get("tree") != UPSTREAM_TREE + or worktree.get("clean") is not True + or worktree.get("submodules") != 0 + or worktree.get("git_lfs_pointers") != 0 + ): + errors.append("official upstream worktree record changed") + sender = manifest.get("official_host_sender", {}) + if ( + sender.get("response_read") is not False + or sender.get("duplex") is not False + or sender.get("deployed_use_attested") is not False + ): + errors.append("official sender was overclaimed") + if manifest.get("operator_attestation", {}).get("attested") is not False: + errors.append("operator attestation must remain empty") + actions = manifest.get("actions", {}) + expected_false_actions = ( + "downloaded_code_executed", + "dependency_installed", + "ps5_connected", + "ps5_ip_used", + "device_request_performed", + "files_transferred_to_or_from_ps5", + "target_build_performed", + "target_code_created", + "target_artifact_created", + "payload_created", + "device_client_created", + ) + for field in expected_false_actions: + if actions.get(field) is not False: + errors.append(f"actions.{field} must be false") + if ( + actions.get("large_release_assets_downloaded") != 0 + or actions.get("large_release_asset_bytes_downloaded") != 0 + ): + errors.append("large release asset download was recorded") + if manifest.get("blocking_state", {}).get("payload_manager_backup") != ( + "HARD_BLOCKER_FOR_INSTALLATION" + ): + errors.append("Payload Manager installation blocker changed") + if phase09f_design_allowed( + correlation=str(manifest.get("release_correlation")), + release_commit_known=False, + upstream_clean=True, + port_classification=PORT_9020_CLASSIFICATION, + sender_identified=True, + attestation_available=True, + all_authorizations_false=True, + ): + errors.append("Phase 0.9F incorrectly passed") + return errors + + +def validate_port_manifest(manifest: dict[str, Any]) -> list[str]: + errors: list[str] = [] + if manifest.get("phase") != PHASE or manifest.get("status") != "DESIGN_ONLY": + errors.append("port manifest phase/status changed") + if manifest.get("release_commit") != UPSTREAM_COMMIT: + errors.append("port manifest release commit changed") + if manifest.get("port_9020_classification") != PORT_9020_CLASSIFICATION: + errors.append("port-9020 implementation was overclaimed") + if ( + manifest.get("port_9020_listener_found") is not False + or manifest.get("port_9020_parser_found") is not False + or manifest.get("port_9020_mapping_found") is not False + ): + errors.append("port-9020 source was invented") + loader = manifest.get("official_remote_js_loader", {}) + if ( + loader.get("desired_dynamic_port") != 50000 + or loader.get("maximum_receive_bytes") != 512000 + or loader.get("framing") != "CONNECTION_EOF_OR_BUFFER_LIMIT" + or loader.get("declared_length") is not False + or loader.get("timeout") is not False + or loader.get("native_elf_mapping") is not False + ): + errors.append("Remote JS Loader contract changed") + if loader.get("automatic_retry_authorized") is not False: + errors.append("automatic retry was authorized") + relation = manifest.get("port_9021_relation", {}) + if relation.get("listener_source_in_y2jb") is not False: + errors.append("embedded 9021 listener source was invented") + if relation.get("source_commit") is not None: + errors.append("embedded elfldr received an invented source commit") + sender = manifest.get("official_sender", {}) + if sender.get("response_read") is not False or sender.get("duplex") is not False: + errors.append("one-way sender was classified duplex") + for field in AUTHORIZATION_FIELDS: + if manifest.get("authorization", {}).get(field) is not False: + errors.append(f"port authorization.{field} must be false") + if manifest.get("phase09f_offline_design_allowed") is not False: + errors.append("Phase 0.9F was authorized") + return errors + + +def changed_paths(root: Path) -> set[str]: + paths = set(git(root, "diff", "--name-only", BASELINE).splitlines()) + for line in git(root, "status", "--porcelain=v1", "--untracked-files=all").splitlines(): + path = line[3:] + if " -> " in path: + path = path.split(" -> ", 1)[1] + paths.add(path.replace("\\", "/")) + return {path for path in paths if path} + + +def phase09er_path_errors(paths: set[str]) -> list[str]: + errors: list[str] = [] + for path in paths: + normalized = path.replace("\\", "/") + lowered = normalized.lower() + if normalized.startswith(FORBIDDEN_PREFIXES): + errors.append(f"target/production path changed: {normalized}") + if Path(normalized).suffix.lower() in FORBIDDEN_SUFFIXES: + errors.append(f"target artifact/source added: {normalized}") + if any( + token in lowered + for token in ( + "rescue.elf", "observer.elf", "readback.elf", "executionpackage", + "installpackage", "transferpackage", + ) + ): + errors.append(f"forbidden Phase-0.9E-R output: {normalized}") + return errors + + +def validate_checksum_file(root: Path) -> list[str]: + checksum_path = root / "packaging/phase09er/SHA256SUMS.txt" + if not checksum_path.is_file(): + return ["missing packaging/phase09er/SHA256SUMS.txt"] + errors: list[str] = [] + for line in checksum_path.read_text(encoding="utf-8").splitlines(): + if not line or line.startswith("#"): + continue + parts = line.split(" ", 1) + if len(parts) != 2 or not re.fullmatch(r"[0-9a-f]{64}", parts[0]): + errors.append(f"invalid checksum line: {line}") + continue + expected, relative = parts + path = root / relative + if not path.is_file() or sha256_file(path) != expected: + errors.append(f"checksum mismatch or missing: {relative}") + return errors + + +def collect_errors(root: Path) -> list[str]: + errors: list[str] = [] + release_path = root / "manifests/runtime/phase-0.9e-r-release-correlation.json" + port_path = root / "manifests/runtime/phase-0.9e-r-port9020-audit.json" + release_schema_path = ( + root / "manifests/runtime/phase-0.9e-r-release-correlation.schema.json" + ) + port_schema_path = ( + root / "manifests/runtime/phase-0.9e-r-port9020-audit.schema.json" + ) + try: + release = load_json(release_path) + port = load_json(port_path) + release_schema = load_json(release_schema_path) + port_schema = load_json(port_schema_path) + except (OSError, ValueError, json.JSONDecodeError) as error: + return [str(error)] + errors.extend(validate_schema_instance(release_schema, release)) + errors.extend(validate_schema_instance(port_schema, port)) + errors.extend(validate_release_manifest(release)) + errors.extend(validate_port_manifest(port)) + + for relative in DELIVERABLES: + if not (root / relative).is_file(): + errors.append(f"missing deliverable: {relative}") + if git(root, "rev-parse", "--abbrev-ref", "HEAD") != BRANCH: + errors.append("current branch is not the Phase-0.9E-R branch") + if git(root, "merge-base", BASELINE, "HEAD") != BASELINE: + errors.append("branch no longer descends from the canonical baseline") + if sha256_file(root / "manifests/artifact-denylist.json") != DENYLIST_SHA256: + errors.append("permanent artifact denylist changed") + for relative, expected in IMMUTABLE_HASHES.items(): + path = root / relative + if not path.is_file() or sha256_file(path) != expected: + errors.append(f"immutable evidence changed: {relative}") + + outer = candidate_archive_path(root) + if not outer.is_file(): + errors.append(f"local Y2JB candidate missing: {outer}") + elif outer.stat().st_size != OUTER_SIZE or sha256_file(outer) != OUTER_SHA256: + errors.append("local Y2JB outer identity changed") + else: + try: + with zipfile.ZipFile(outer) as archive: + info = archive.getinfo(INNER_NAME) + if ( + len(archive.infolist()) != 5 + or archive.comment != b"" + or info.file_size != INNER_SIZE + or info.compress_size != 504158629 + or info.CRC != int("522808c8", 16) + ): + errors.append("local ZIP metadata changed") + digest = hashlib.sha256() + magic = b"" + with archive.open(info) as stream: + for chunk in iter(lambda: stream.read(1024 * 1024), b""): + if not magic: + magic = chunk[:6] + digest.update(chunk) + if magic != b"SIECAF" or digest.hexdigest() != INNER_SHA256: + errors.append("inner SIECAF identity changed") + except (OSError, KeyError, zipfile.BadZipFile) as error: + errors.append(f"local ZIP validation failed: {error}") + + upstream = root / "work/upstream/Y2JB-official" + if not upstream.is_dir(): + errors.append("official immutable upstream worktree missing") + else: + if git(upstream, "rev-parse", "HEAD") != UPSTREAM_COMMIT: + errors.append("official upstream HEAD changed") + if git(upstream, "rev-parse", "HEAD^{tree}") != UPSTREAM_TREE: + errors.append("official upstream tree changed") + if git(upstream, "status", "--porcelain=v1"): + errors.append("official upstream worktree is dirty") + remotes = git(upstream, "remote", "-v") + if UPSTREAM_REMOTE not in remotes: + errors.append("official upstream remote changed") + if git(upstream, "submodule", "status"): + errors.append("unexpected official upstream submodule") + if len(git(upstream, "ls-files").splitlines()) != 19: + errors.append("official upstream tracked file inventory changed") + for relative, (expected_size, expected_hash) in UPSTREAM_FILE_HASHES.items(): + path = upstream / relative + if ( + not path.is_file() + or path.stat().st_size != expected_size + or sha256_file(path) != expected_hash + ): + errors.append(f"official upstream file identity changed: {relative}") + + errors.extend(phase09er_path_errors(changed_paths(root))) + for path in root.rglob("*"): + if ( + path.is_file() + and ".git" not in path.parts + and "work" not in path.parts + and path.stat().st_size > 50 * 1024 * 1024 + ): + errors.append(f"large release-like object present in repository: {path}") + errors.extend(validate_checksum_file(root)) + return errors + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + errors = collect_errors(args.root.resolve()) + if errors: + for error in errors: + print(f"ERROR: {error}") + return 1 + print("Phase-0.9E-R official Y2JB provenance validation: PASS") + print(f"Release correlation: OFFICIAL_RELEASE_NO_MATCH") + print(f"Port 9020: {PORT_9020_CLASSIFICATION}") + print(f"Decision: {FINAL_CLASSIFICATION}") + print("Device actions: NONE") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/validate_retroarch_phase10a.py b/tools/validate_retroarch_phase10a.py new file mode 100644 index 0000000..7d13610 --- /dev/null +++ b/tools/validate_retroarch_phase10a.py @@ -0,0 +1,326 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate the offline Phase-1.0A RetroArch PS5 port evidence.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +from pathlib import Path +import re +import subprocess +import sys +from typing import Any + + +PHASE = "PHASE_1_0A_RETROARCH_PS5_PORT_BOOTSTRAP" +STATUS = "RETROARCH_PS5_SOFTWARE_PORT_BUILT" +BASELINE = "4c086f84ab2e7c53e6750b66b3b3e1f591ff6a06" +BRANCH = "codex/chimera-gfx-phase10a-retroarch-port-bootstrap" +RETROARCH_BRANCH = "codex/ps5-port-bootstrap" +RETROARCH_TAG = "v1.22.2" +RETROARCH_COMMIT = "69a4f0ea1e8aaf442ae4858f2e7f2b31a1776576" +RETROARCH_TREE = "33babf9eb7699b5d571a3063ea21c3e488c159fe" +RETROARCH_ARCHIVE_SHA256 = ( + "245ef18c8fa8fbd9fbb5eb25cf43e17c6aace2f95c1ed99873cbd794012bb232" +) +SDK_COMMIT = "d2e2e585740362976a39fdd5ccf390f199a7bc37" +SDL_COMMIT = "0baf4ac49382b537ba449901b5b6d0d189bb1fbb" +PACBREW_COMMIT = "c2abcfcb60f569128abd0e8e70ad03a67bee5ea7" +DENYLIST_SHA256 = "e9603b0e3792781ad5b511afb22ef61e3d4fd4c5a16bf928f8609193bcd97783" + +OFFLINE_AUTHORIZATION_FIELDS = ( + "offline_source_acquisition_authorized", + "offline_source_modification_authorized", + "offline_target_build_authorized", + "offline_artifact_analysis_authorized", + "private_gitea_push_authorized", +) +DEVICE_AUTHORIZATION_FIELDS = ( + "ps5_connection_authorized", + "device_transfer_authorized", + "device_execution_authorized", + "installation_authorized", + "lifecycle_authorized", + "autoload_authorized", + "device_write_authorized", + "automatic_retry", +) +DELIVERABLES = ( + "docs/retroarch/phase-1.0a-port-plan.md", + "docs/retroarch/phase-1.0a-upstream-analysis.md", + "docs/retroarch/phase-1.0a-ps4-reference-delta.md", + "docs/retroarch/phase-1.0a-pacbrew-sdl-analysis.md", + "docs/retroarch/phase-1.0a-build-results.md", + "docs/retroarch/phase-1.0a-driver-status.md", + "docs/retroarch/phase-1.0a-next-device-smoke-test.md", + "manifests/retroarch/upstreams.json", + "manifests/retroarch/phase-1.0a-build.json", + "manifests/retroarch/phase-1.0a-artifacts.json", + "tests/test_retroarch_phase10a.py", + "tools/validate_retroarch_phase10a.py", + "packaging/retroarch/phase10a/SHA256SUMS.txt", +) +REQUIRED_REAL_SYMBOLS = { + "rarch_main", + "retroarch_main_init", + "runloop_iterate", + "retro_init", + "retro_deinit", + "retro_run", + "frontend_ctx_ps5", +} +DISABLED_FEATURES = { + "networking", + "online_updater", + "dynamic_cores", + "autoload", + "installation", + "gnm", + "opengl", + "vulkan", +} +FORBIDDEN_MARKERS = ( + "chimera_lifecycle_probe", + "prospero-deploy", + "payload_sender", + "sceSystemServiceLoadExec", +) + + +def load_json(path: Path) -> dict[str, Any]: + with path.open("r", encoding="utf-8") as stream: + value = json.load(stream) + if not isinstance(value, dict): + raise ValueError(f"{path} is not a JSON object") + return value + + +def sha256_file(path: Path) -> str: + digest = hashlib.sha256() + with path.open("rb") as stream: + for chunk in iter(lambda: stream.read(1024 * 1024), b""): + digest.update(chunk) + return digest.hexdigest() + + +def artifact_execution_allowed(record: dict[str, Any]) -> bool: + return bool(record.get("execution_eligible", False)) + + +def route_is_forbidden(text: str) -> bool: + lowered = text.lower() + return any(marker.lower() in lowered for marker in FORBIDDEN_MARKERS) + + +def profile_is_closed(profile: dict[str, Any]) -> bool: + disabled = set(profile.get("disabled_features", [])) + return ( + DISABLED_FEATURES.issubset(disabled) + and profile.get("automatic_retry") is False + and profile.get("filesystem_writes_on_default_path") is False + ) + + +def is_real_retroarch_artifact(record: dict[str, Any]) -> bool: + return REQUIRED_REAL_SYMBOLS.issubset(set(record.get("required_symbols", []))) + + +def has_personal_path(strings: list[str]) -> bool: + patterns = ( + re.compile(r"[A-Za-z]:\\Users\\", re.IGNORECASE), + re.compile(r"/(?:mnt/[a-z]/)?Users/[^/]+/", re.IGNORECASE), + re.compile(r"/home/[^/]+/", re.IGNORECASE), + ) + return any(pattern.search(value) for value in strings for pattern in patterns) + + +def has_ip_address(strings: list[str]) -> bool: + pattern = re.compile(r"(? list[str]: + errors: list[str] = [] + name = record.get("name", "") + if artifact_execution_allowed(record): + errors.append(f"{name}: execution_eligible must be false") + if record.get("target_execution_performed") is not False: + errors.append(f"{name}: target execution state is not false") + if record.get("reproducibility") != "BYTE_IDENTICAL_TWO_CLEAN_BUILDS": + errors.append(f"{name}: reproducibility is not byte exact") + if not is_real_retroarch_artifact(record): + errors.append(f"{name}: real RetroArch symbols are incomplete") + if len(record.get("sha256", "")) != 64 or record.get("size", 0) <= 0: + errors.append(f"{name}: invalid size/hash identity") + if not record.get("linker_map_sha256"): + errors.append(f"{name}: linker map identity is missing") + if not isinstance(record.get("undefined_symbols"), list): + errors.append(f"{name}: undefined-symbol inventory is missing") + if record.get("embedded_personal_paths") is not False: + errors.append(f"{name}: personal path audit did not pass") + if not isinstance(record.get("embedded_ip_addresses"), list): + errors.append(f"{name}: IP-address inventory is missing") + if record.get("embedded_device_ip_addresses") is not False: + errors.append(f"{name}: a device/private address is embedded") + if record.get("forbidden_markers_found"): + errors.append(f"{name}: forbidden marker found") + return errors + + +def git(root: Path, *args: str) -> str: + result = subprocess.run( + ["git", *args], + cwd=root, + capture_output=True, + text=True, + check=False, + ) + if result.returncode: + raise RuntimeError(result.stderr.strip() or "git command failed") + return result.stdout.strip() + + +def validate(root: Path, retroarch_root: Path | None = None) -> list[str]: + errors: list[str] = [] + for relative in DELIVERABLES: + if not (root / relative).is_file(): + errors.append(f"missing deliverable: {relative}") + + try: + build = load_json(root / "manifests/retroarch/phase-1.0a-build.json") + artifacts_doc = load_json( + root / "manifests/retroarch/phase-1.0a-artifacts.json" + ) + upstreams = load_json(root / "manifests/retroarch/upstreams.json") + except (OSError, ValueError, json.JSONDecodeError) as error: + return errors + [str(error)] + + if build.get("phase") != PHASE or build.get("status") != STATUS: + errors.append("phase/status mismatch") + if build.get("baseline_commit") != BASELINE: + errors.append("baseline mismatch") + if build.get("branch") != BRANCH: + errors.append("branch mismatch") + for field in OFFLINE_AUTHORIZATION_FIELDS: + if build.get(field) is not True: + errors.append(f"{field} must record the explicit offline grant") + for field in DEVICE_AUTHORIZATION_FIELDS: + if build.get(field) is not False: + errors.append(f"{field} must remain false") + for field in ( + "ps5_connected", + "device_request_performed", + "files_transferred", + "device_write_performed", + "target_execution_performed", + "install_package_created", + "execution_package_created", + ): + if build.get(field) is not False: + errors.append(f"{field} must remain false") + + fork = build.get("chimera_retroarch", {}) + if fork.get("branch") != RETROARCH_BRANCH or len(fork.get("commit", "")) != 40: + errors.append("private fork identity is incomplete") + if fork.get("working_tree_clean") is not True: + errors.append("private fork was not clean at final build") + + profiles = build.get("profiles", {}) + for name in ( + "ps5-headless-smokecore", + "ps5-software-rgui-smokecore", + "host-smokecore-integration", + ): + if name not in profiles or not profile_is_closed(profiles[name]): + errors.append(f"profile is not closed: {name}") + + host = build.get("host_tests", {}) + if ( + host.get("frames") != 600 + or host.get("video_fnv1a64") != "43f920496eb5f435" + or host.get("audio_fnv1a64") != "a48f47dc08c56625" + or host.get("asan_ubsan") != "PASS" + or host.get("input_mapping") != "PASS" + or host.get("clean_shutdown") != "PASS" + ): + errors.append("host smoke-core evidence mismatch") + + expected_upstreams = { + "retroarch": RETROARCH_COMMIT, + "ps5_payload_sdk": SDK_COMMIT, + "ps5_sdl": SDL_COMMIT, + "pacbrew": PACBREW_COMMIT, + } + for name, commit in expected_upstreams.items(): + if upstreams.get("sources", {}).get(name, {}).get("commit") != commit: + errors.append(f"upstream mismatch: {name}") + retroarch = upstreams.get("sources", {}).get("retroarch", {}) + if ( + retroarch.get("tag") != RETROARCH_TAG + or retroarch.get("tree") != RETROARCH_TREE + or retroarch.get("source_archive_sha256") != RETROARCH_ARCHIVE_SHA256 + ): + errors.append("RetroArch release provenance mismatch") + + records = artifacts_doc.get("artifacts", []) + if {item.get("name") for item in records} != { + "retroarch_ps5_headless.elf", + "retroarch_ps5_software.elf", + }: + errors.append("artifact set mismatch") + for record in records: + errors.extend(validate_artifact(record)) + + denylist = root / "manifests/artifact-denylist.json" + if not denylist.is_file() or sha256_file(denylist) != DENYLIST_SHA256: + errors.append("permanent denylist changed") + + tracked = git(root, "ls-files").splitlines() + forbidden_suffixes = (".elf", ".self", ".sprx", ".pkg") + for relative in tracked: + if relative.lower().endswith(forbidden_suffixes): + errors.append(f"tracked target artifact: {relative}") + new_text = "\n".join( + (root / relative).read_text(encoding="utf-8", errors="replace") + for relative in DELIVERABLES + if (root / relative).is_file() + ) + unfinished_template = "{" * 2 + unfinished_label = "".join(("T", "B", "D")) + if unfinished_template in new_text or unfinished_label in new_text: + errors.append("unfinished placeholder in deliverables") + + if retroarch_root is not None: + for record in records: + path = retroarch_root / record["local_relative_path"] + if not path.is_file(): + errors.append(f"local artifact missing: {path}") + continue + if path.stat().st_size != record["size"] or sha256_file(path) != record[ + "sha256" + ]: + errors.append(f"local artifact identity mismatch: {path.name}") + return errors + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + parser.add_argument("--retroarch-root", type=Path) + args = parser.parse_args() + errors = validate( + args.root.resolve(), + args.retroarch_root.resolve() if args.retroarch_root else None, + ) + if errors: + for error in errors: + print(error, file=sys.stderr) + return 1 + print("Phase 1.0A RetroArch evidence validated") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/validate_retroarch_phase10aa.py b/tools/validate_retroarch_phase10aa.py new file mode 100644 index 0000000..81ec097 --- /dev/null +++ b/tools/validate_retroarch_phase10aa.py @@ -0,0 +1,204 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate Phase-1.0AA offline fake-adapter integration evidence.""" + +from __future__ import annotations + +import argparse +import ast +import hashlib +import json +from pathlib import Path +from typing import Any + + +PHASE = "PHASE_1_0AA_OFFLINE_FAKE_ADAPTER_INTEGRATION" +STATUS = "OFFLINE_FAKE_BATCH_INTEGRATION_COMPLETE_LIVE_ADAPTER_BLOCKED" +START_COMMIT = "57ff9a1c5937575b00df05f2cd9897118eab1f2a" +SOURCE_BINDINGS = { + "phase10x_transport_size": 8040, + "phase10x_transport_sha256": "568d7578482ecf2fcd9e29085b2eb9d8705fc699611508acdf22afd30f2ddd23", + "phase10z_contract_size": 10487, + "phase10z_contract_sha256": "0728c2be7f368e0a7f4b68efe86f6e0c5c2f50704a41d0e1992b0bfec19dde06", + "phase10aa_integration_size": 11200, + "phase10aa_integration_sha256": "8e1cac255f85d2cd14baf8fbc27d631c9b607fc7d19fc57c65462089f0574055", + "phase10aa_integration_tests_size": 10667, + "phase10aa_integration_tests_sha256": "18a5470a651fcbe4b23f2a68cba499dd19d3da15a229623c054be0b598025699", +} +SOURCE_FILES = { + "phase10x_transport": "tools/phase10x_inactive_transport.py", + "phase10z_contract": "tools/phase10z_passive_batch_contract.py", + "phase10aa_integration": "tools/phase10aa_offline_fake_batch.py", + "phase10aa_integration_tests": "tests/test_phase10aa_offline_fake_batch.py", +} +AUTHORIZATION_FIELDS = { + "target_build_authorized", "ps5_connection_authorized", + "device_request_authorized", "result_receive_authorized", + "device_transfer_authorized", "device_execution_authorized", + "installation_authorized", "autoload_authorized", + "device_write_authorized", "automatic_retry", "reconnect_authorized", + "resume_authorized", +} +NETWORK_MODULES = { + "socket", "asyncio", "selectors", "urllib", "http", "ftplib", + "requests", "telnetlib", "paramiko", +} + + +def load_json(path: Path) -> dict[str, Any]: + value = json.loads(path.read_text(encoding="utf-8")) + if not isinstance(value, dict): + raise ValueError("Phase-1.0AA manifest is not an object") + return value + + +def exact_file(path: Path, size: int, digest: str) -> bool: + try: payload = path.read_bytes() + except OSError: return False + return len(payload) == size and hashlib.sha256(payload).hexdigest() == digest + + +def _imports(tree: ast.AST) -> set[str]: + values: set[str] = set() + for node in ast.walk(tree): + if isinstance(node, ast.Import): + values.update(alias.name.split(".")[0] for alias in node.names) + elif isinstance(node, ast.ImportFrom) and node.module: + values.add(node.module.split(".")[0]) + return values + + +def validate_record(record: dict[str, Any], root: Path | None = None) -> list[str]: + errors: list[str] = [] + if record.get("phase") != PHASE or record.get("status") != STATUS: + errors.append("phase/status mismatch") + if record.get("start_commit") != START_COMMIT: + errors.append("start commit mismatch") + if record.get("activation") != { + "active": False, "integration_sha256": None, "run_id": None, + "target_address": None, "target_port": None, "window": None, + "deadline_seconds": None, + }: + errors.append("activation is not inert") + if record.get("source_bindings") != SOURCE_BINDINGS: + errors.append("source bindings mismatch") + if record.get("fake_boundary") != { + "exact_builtin_adapter_required": True, + "adapter_subclasses_allowed": False, + "exact_builtin_clock_required": True, + "clock_subclasses_allowed": False, + "exact_fake_evidence_store_required": True, + "live_adapter_protocol_present": False, + "network_import_present": False, + "real_clock_present": False, + "target_present": False, + "cli_present": False, + "maximum_fake_events": 257, + "event_kinds": ["DATA", "HARD_DEADLINE", "REMOTE_EOF", "BLOCKED"], + }: + errors.append("fake boundary mismatch") + if record.get("ordering_contract") != { + "receipt_before_fake_open": True, "fake_open_count": 1, + "complete_batch_send_count": 1, "fake_close_count": 1, + "completion_event": "SYNTHETIC_HARD_DEADLINE_ONLY", + "early_deadline": "INVALID", "remote_eof": "INVALID", + "blocked_receive": "INVALID", "missing_deadline": "INVALID", + "data_at_or_after_deadline": "INVALID", "partial_result": "INVALID", + "incoming_iac": "INVALID", "retry_allowed": False, + "reconnect_allowed": False, "resume_allowed": False, + }: + errors.append("ordering contract mismatch") + evidence = record.get("evidence_contract", {}) + if evidence != { + "exclusive_create": True, "consumed_receipt_retained_on_failure": True, + "failure_output_created": False, "sanitized_output_receipt_bound": True, + "batch_sha256_recorded": True, "batch_size_recorded": True, + "target_retained": False, "raw_transcript_persisted": False, + "logical_event_buffer_discarded": True, + "physical_memory_erasure_proven": False, + "directory_entry_durability_proven": False, + "device_behavior_proven": False, "exact_identity_proven": False, + }: + errors.append("evidence contract mismatch") + authority = record.get("authorizations", {}) + if set(authority) != AUTHORIZATION_FIELDS or any( + authority.get(field) is not False for field in AUTHORIZATION_FIELDS): + errors.append("authorization fields are not exactly false") + if record.get("decision") != { + "offline_fake_batch_integration_complete": True, + "live_adapter_created": False, "live_adapter_allowed": False, + "live_collection_allowed": False, "device_action_allowed": False, + "phase10ab_offline_live_adapter_feasibility_review_allowed": True, + "next_step": "OFFLINE_LIVE_ADAPTER_TIMEOUT_AND_CLEANUP_FEASIBILITY_REVIEW", + }: + errors.append("decision mismatch") + performed = record.get("performed_actions", {}) + if not performed or any(value is not False for value in performed.values()): + errors.append("performed actions are missing or true") + if record.get("tests") != { + "chimera_gfx_ctest": "89_OF_89_PASS", "phase10aa_guardrails": 20, + "phase10aa_integration_tests": 25, "safety_audit": "PASS", + "secret_scan": "PASS", "network_required_by_tests": False, + "hardware_claim_from_host_test": False, + }: + errors.append("test evidence mismatch") + + if root is not None: + for prefix, relative in SOURCE_FILES.items(): + if not exact_file(root / relative, SOURCE_BINDINGS[f"{prefix}_size"], + SOURCE_BINDINGS[f"{prefix}_sha256"]): + errors.append(f"source identity mismatch: {relative}") + source_path = root / SOURCE_FILES["phase10aa_integration"] + try: + source = source_path.read_text(encoding="utf-8") + tree = ast.parse(source) + except (OSError, SyntaxError, UnicodeError): + errors.append("integration source cannot be parsed") + else: + if _imports(tree) & NETWORK_MODULES or "time" in _imports(tree): + errors.append("integration imports network or real-clock support") + names = {node.name for node in ast.walk(tree) + if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef))} + if {"main", "connect", "recv", "open_socket"} & names: + errors.append("integration exposes a live API or CLI") + if "target_address" in source or "target_port" in source: + errors.append("integration contains target fields") + required_shapes = ( + "type(adapter) is not OfflineFakeBatchAdapter", + "type(clock) is not OfflineFakeClock", + "type(evidence) is not OfflineFakeEvidenceStore", + "receipt = evidence.create_fake_consumed_receipt", + "adapter.send_one_batch(batch)", + "accumulator.seal_at_hard_deadline(True)", + ) + if any(shape not in source for shape in required_shapes): + errors.append("exact fake ordering source shape is missing") + approval = (root / "docs/approvals/phase-1.0aa-offline-fake-adapter.md").read_text(encoding="utf-8") + if "active=false" not in approval or "attested=false" not in approval or \ + "ps5_connection_authorized=false" not in approval: + errors.append("approval template is not inert") + for base in (root / "tools", root / "tests", root / "docs", root / "manifests"): + if any(path.is_file() and path.suffix.lower() in {".elf", ".self", ".sprx", ".pkg"} for path in base.rglob("*")): + errors.append("target artifact exists in a Phase-1.0AA output area") + break + return errors + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args(); root = args.root.resolve() + try: + record = load_json(root / "manifests/retroarch/phase-1.0aa-offline-fake-adapter.json") + errors = validate_record(record, root) + except (OSError, ValueError, json.JSONDecodeError) as error: + errors = [f"validation input failed: {error}"] + if errors: + for error in errors: print(f"ERROR: {error}") + return 1 + print("Phase-1.0AA offline fake-adapter validation passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/validate_retroarch_phase10ab.py b/tools/validate_retroarch_phase10ab.py new file mode 100644 index 0000000..18cf428 --- /dev/null +++ b/tools/validate_retroarch_phase10ab.py @@ -0,0 +1,146 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate Phase-1.0AB offline live-adapter feasibility evidence.""" + +from __future__ import annotations + +import argparse +import ast +import hashlib +import json +from pathlib import Path +from typing import Any + + +PHASE = "PHASE_1_0AB_OFFLINE_LIVE_ADAPTER_FEASIBILITY" +STATUS = "PARTIAL_FEASIBILITY_LIVE_IMPLEMENTATION_BLOCKED" +START_COMMIT = "06d2fe831959b71562401722ae36821faa197636" +MODEL_SIZE = 8738 +MODEL_SHA256 = "7d1aa32d49b91b1e5cf3a085dda033767bdf17ab34389ff044f7403f86287959" +MODEL_TEST_SIZE = 7558 +MODEL_TEST_SHA256 = "39597e991b15bcfa9aa28cb2f68c87ed048c38482c6dbf81a28c56ccfceb0a48" +NETWORK_MODULES = {"socket", "selectors", "select", "asyncio", "urllib", "http", "requests", "telnetlib"} +AUTHORIZATION_FIELDS = { + "target_build_authorized", "ps5_connection_authorized", + "device_request_authorized", "result_receive_authorized", + "device_transfer_authorized", "device_execution_authorized", + "installation_authorized", "autoload_authorized", + "device_write_authorized", "automatic_retry", "reconnect_authorized", + "resume_authorized", +} + + +def load_json(path: Path) -> dict[str, Any]: + value = json.loads(path.read_text(encoding="utf-8")) + if not isinstance(value, dict): raise ValueError("Phase-1.0AB manifest is not an object") + return value + + +def exact_file(path: Path, size: int, digest: str) -> bool: + try: payload = path.read_bytes() + except OSError: return False + return len(payload) == size and hashlib.sha256(payload).hexdigest() == digest + + +def _imports(tree: ast.AST) -> set[str]: + values=set() + for node in ast.walk(tree): + if isinstance(node, ast.Import): values.update(alias.name.split(".")[0] for alias in node.names) + elif isinstance(node, ast.ImportFrom) and node.module: values.add(node.module.split(".")[0]) + return values + + +def validate_record(record: dict[str, Any], root: Path | None = None, + python_root: Path | None = None) -> list[str]: + errors=[] + if record.get("phase") != PHASE or record.get("status") != STATUS: errors.append("phase/status mismatch") + if record.get("start_commit") != START_COMMIT: errors.append("start commit mismatch") + if record.get("activation") != {"active": False, "trace_model_sha256": None, "target_address": None, "target_port": None, "run_id": None}: errors.append("activation is not inert") + runtime=record.get("local_runtime", {}) + if runtime != { + "python_version": "3.13.2", "compiler": "MSC_V_1942_64_BIT_AMD64", + "platform": "WINDOWS", "default_selector": "SelectSelector", + "monotonic_implementation": "QueryPerformanceCounter()", "monotonic": True, + "monotonic_adjustable": False, "reported_resolution_seconds": 1e-7, + "socket_py_size": 38741, "socket_py_sha256": "523695ac3383799547b421b4fe18004de1e80181e97181b6d7a10533b47f4c49", + "selectors_py_size": 20060, "selectors_py_sha256": "b3d6cebd4a3a03b4a614f12f171622ce4e4ba3295b9e8b89e2bde051003106eb", + "socket_pyd_size": 84984, "socket_pyd_sha256": "8daefaff53e6956f5aea5279a7c71f17d8c63e2b0d54031c3b9e82fcb0fb84db", + "select_pyd_size": 32248, "select_pyd_sha256": "baee284995b22d495fd12fa8378077e470978db1522c61bfb9af37fb827f33d1", + }: errors.append("local runtime record mismatch") + if record.get("source_bindings") != { + "phase10aa_integration_sha256": "8e1cac255f85d2cd14baf8fbc27d631c9b607fc7d19fc57c65462089f0574055", + "phase10ab_trace_model_size": MODEL_SIZE, "phase10ab_trace_model_sha256": MODEL_SHA256, + "phase10ab_trace_tests_size": MODEL_TEST_SIZE, "phase10ab_trace_tests_sha256": MODEL_TEST_SHA256, + }: errors.append("source bindings mismatch") + if record.get("feasibility") != { + "receipt_before_socket": "FEASIBLE_FROM_EXISTING_HOST_MODEL", + "numeric_address_only": "DESIGN_REQUIRED", + "nonblocking_before_connect": "FEASIBLE_FROM_LOCAL_RUNTIME", + "pending_connect": "PARTIAL", "complete_send_loop": "FEASIBLE_FROM_LOCAL_RUNTIME", + "bounded_receive_memory": "FEASIBLE_FROM_EXISTING_MODEL", + "hard_wall_clock_deadline": "PARTIAL", + "prompt_independent_completion": "FEASIBLE_FROM_Z", + "remote_eof": "FEASIBLE_FAIL_CLOSED", + "local_descriptor_cleanup": "FEASIBLE_BY_DESIGN", + "remote_shell_cleanup": "UNPROVEN", "retry_reconnect_resume": "EXCLUDED", + }: errors.append("feasibility matrix mismatch") + if record.get("trace_model") != { + "synthetic_input_only": True, "maximum_events": 512, + "maximum_batch_bytes": 1035, "maximum_receive_bytes": 65536, + "maximum_deadline_seconds": 10, "network_import_present": False, + "selector_import_present": False, "real_clock_present": False, + "address_present": False, "cli_present": False, "file_output_present": False, + "exact_identity_proven": False, "device_behavior_proven": False, + }: errors.append("trace model record mismatch") + stops=record.get("hard_stops", {}) + if set(stops) != {"live_adapter_implementation","socket_creation","dns","target_retention","connection","request","retry","device_action"} or any(value is not True for value in stops.values()): errors.append("hard stops mismatch") + auth=record.get("authorizations", {}) + if set(auth) != AUTHORIZATION_FIELDS or any(auth.get(field) is not False for field in AUTHORIZATION_FIELDS): errors.append("authorization fields are not exactly false") + if record.get("decision") != { + "overall": STATUS, "live_adapter_created": False, "live_adapter_allowed": False, + "device_action_allowed": False, "phase10ac_offline_dormant_syscall_facade_allowed": True, + "next_step": "OFFLINE_DORMANT_TARGET_FREE_ADAPTER_WITH_FAKE_SYSCALLS", + }: errors.append("decision mismatch") + performed=record.get("performed_actions", {}) + if not performed or any(value is not False for value in performed.values()): errors.append("performed actions are missing or true") + if record.get("tests") != { + "chimera_gfx_ctest": "92_OF_92_PASS", "phase10ab_guardrails": 20, + "phase10ab_trace_tests": 25, "safety_audit": "PASS", "secret_scan": "PASS", + "network_required_by_tests": False, "hardware_claim_from_host_test": False, + }: errors.append("test evidence mismatch") + + if root is not None: + model=root / "tools/phase10ab_nonblocking_trace_model.py"; tests=root / "tests/test_phase10ab_nonblocking_trace_model.py" + if not exact_file(model, MODEL_SIZE, MODEL_SHA256): errors.append("trace model identity mismatch") + if not exact_file(tests, MODEL_TEST_SIZE, MODEL_TEST_SHA256): errors.append("trace tests identity mismatch") + try: source=model.read_text(encoding="utf-8"); tree=ast.parse(source) + except (OSError,SyntaxError,UnicodeError): errors.append("trace model cannot be parsed") + else: + if _imports(tree) & NETWORK_MODULES or "time" in _imports(tree): errors.append("trace model imports live capability") + names={node.name for node in ast.walk(tree) if isinstance(node,(ast.FunctionDef,ast.AsyncFunctionDef))} + if {"main","connect","send","recv"} & names: errors.append("trace model exposes live API") + if "target_address" in source or "target_port" in source: errors.append("trace model contains target fields") + approval=(root / "docs/approvals/phase-1.0ab-live-adapter-feasibility.md").read_text(encoding="utf-8") + if "active=false" not in approval or "ps5_connection_authorized=false" not in approval: errors.append("approval is not inert") + if python_root is not None: + paths={ + "socket_py": python_root / "Lib/socket.py", "selectors_py": python_root / "Lib/selectors.py", + "socket_pyd": python_root / "DLLs/_socket.pyd", "select_pyd": python_root / "DLLs/select.pyd", + } + for prefix,path in paths.items(): + if not exact_file(path, runtime[f"{prefix}_size"], runtime[f"{prefix}_sha256"]): errors.append(f"local runtime identity mismatch: {prefix}") + return errors + + +def main() -> int: + parser=argparse.ArgumentParser();parser.add_argument("--root",type=Path,required=True);parser.add_argument("--python-root",type=Path) + args=parser.parse_args();root=args.root.resolve() + try: record=load_json(root / "manifests/retroarch/phase-1.0ab-live-adapter-feasibility.json");errors=validate_record(record,root,args.python_root) + except (OSError,ValueError,json.JSONDecodeError) as error: errors=[f"validation input failed: {error}"] + if errors: + for error in errors: print(f"ERROR: {error}") + return 1 + print("Phase-1.0AB offline live-adapter feasibility validation passed");return 0 + + +if __name__ == "__main__": raise SystemExit(main()) diff --git a/tools/validate_retroarch_phase10ac.py b/tools/validate_retroarch_phase10ac.py new file mode 100644 index 0000000..ae5e466 --- /dev/null +++ b/tools/validate_retroarch_phase10ac.py @@ -0,0 +1,198 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate the inactive Phase-1.0AC dormant-adapter evidence.""" + +from __future__ import annotations + +import argparse +import ast +import hashlib +import json +from pathlib import Path +import re +from typing import Any + + +PHASE = "PHASE_1_0AC_OFFLINE_DORMANT_ADAPTER" +STATUS = "OFFLINE_DORMANT_FAKE_SYSCALL_ADAPTER_COMPLETE_LIVE_ADAPTER_BLOCKED" +START_COMMIT = "3bc8ac09615dda3c4ee3ad02f19440ca9f4f8f96" +ADAPTER_SIZE = 13282 +ADAPTER_SHA256 = "6f28926b59fd9afa6de1ff36d4fa9b013d7e027c9adc0bffd9445d7c89acf939" +ADAPTER_TEST_SIZE = 11681 +ADAPTER_TEST_SHA256 = "9c8c611dbab5e43df71d523169d9a1bf7579ed1918943df7531bf75ef4b9abfc" +NETWORK_MODULES = { + "socket", "selectors", "select", "asyncio", "urllib", "http", + "requests", "ftplib", "telnetlib", "subprocess", +} +AUTHORIZATION_FIELDS = { + "target_build_authorized", "ps5_connection_authorized", + "device_request_authorized", "result_receive_authorized", + "device_transfer_authorized", "device_execution_authorized", + "installation_authorized", "autoload_authorized", + "device_write_authorized", "automatic_retry", "reconnect_authorized", + "resume_authorized", +} + + +def load_json(path: Path) -> dict[str, Any]: + value = json.loads(path.read_text(encoding="utf-8")) + if not isinstance(value, dict): + raise ValueError("Phase-1.0AC manifest is not an object") + return value + + +def exact_file(path: Path, size: int, digest: str) -> bool: + try: + payload = path.read_bytes() + except OSError: + return False + return len(payload) == size and hashlib.sha256(payload).hexdigest() == digest + + +def _imports(tree: ast.AST) -> set[str]: + values: set[str] = set() + for node in ast.walk(tree): + if isinstance(node, ast.Import): + values.update(alias.name.split(".")[0] for alias in node.names) + elif isinstance(node, ast.ImportFrom) and node.module: + values.add(node.module.split(".")[0]) + return values + + +def validate_record(record: dict[str, Any], root: Path | None = None) -> list[str]: + errors: list[str] = [] + if record.get("phase") != PHASE or record.get("status") != STATUS: + errors.append("phase/status mismatch") + if record.get("start_commit") != START_COMMIT: + errors.append("start commit mismatch") + if record.get("activation") != { + "active": False, "adapter_sha256": None, "target_address": None, + "target_port": None, "run_id": None, + }: + errors.append("activation is not inert") + if record.get("source_bindings") != { + "phase10ab_trace_model_sha256": "7d1aa32d49b91b1e5cf3a085dda033767bdf17ab34389ff044f7403f86287959", + "phase10ac_adapter_size": ADAPTER_SIZE, + "phase10ac_adapter_sha256": ADAPTER_SHA256, + "phase10ac_tests_size": ADAPTER_TEST_SIZE, + "phase10ac_tests_sha256": ADAPTER_TEST_SHA256, + }: + errors.append("source bindings mismatch") + if record.get("adapter") != { + "exact_builtin_fake_facade_required": True, + "exact_builtin_fake_clock_required": True, + "precommitted_receipt_required": True, + "maximum_fake_steps": 1024, "maximum_receive_bytes": 65536, + "maximum_batch_bytes": 1035, "maximum_deadline_seconds": 10, + "network_import_present": False, "selector_import_present": False, + "dns_present": False, "real_clock_present": False, + "address_present": False, "cli_present": False, + "file_output_present": False, + "live_adapter_protocol_present": False, "target_retained": False, + "device_behavior_proven": False, + }: + errors.append("adapter boundary mismatch") + if record.get("lifecycle") != { + "create_count": 1, "nonblocking_before_connect": True, + "pending_connect_requires_write_ready": True, + "pending_connect_requires_zero_so_error": True, + "partial_write_loop": True, "zero_write_rejected": True, + "remote_eof_rejected": True, + "deadline_wins_readiness_race": True, + "deadline_only_completion": True, + "local_close_on_success": True, "local_close_on_failure": True, + "remote_cleanup_proven": False, "automatic_retry": False, + "reconnect": False, "resume": False, + }: + errors.append("lifecycle contract mismatch") + stops = record.get("hard_stops", {}) + if set(stops) != { + "live_adapter_implementation", "socket_creation", "dns", + "target_retention", "connection", "device_request", "retry", + "device_action", + } or any(value is not True for value in stops.values()): + errors.append("hard stops mismatch") + authorizations = record.get("authorizations", {}) + if set(authorizations) != AUTHORIZATION_FIELDS or any( + authorizations.get(field) is not False + for field in AUTHORIZATION_FIELDS): + errors.append("authorization fields are not exactly false") + performed = record.get("performed_actions", {}) + if not performed or any(value is not False for value in performed.values()): + errors.append("performed actions are missing or true") + if record.get("decision") != { + "overall": STATUS, "dormant_fake_adapter_created": True, + "live_adapter_created": False, "live_adapter_allowed": False, + "device_action_allowed": False, + "phase10ad_offline_inactive_activation_design_allowed": True, + "next_step": "OFFLINE_NUMERIC_TARGET_AND_INACTIVE_ACTIVATION_CONTRACT", + }: + errors.append("decision mismatch") + if record.get("tests") != { + "chimera_gfx_ctest": "95_OF_95_PASS", "phase10ac_guardrails": 20, + "phase10ac_adapter_tests": 32, "safety_audit": "PASS", + "secret_scan": "PASS", "network_required_by_tests": False, + "hardware_claim_from_host_test": False, + }: + errors.append("test evidence mismatch") + + if root is not None: + adapter = root / "tools/phase10ac_dormant_adapter.py" + tests = root / "tests/test_phase10ac_dormant_adapter.py" + if not exact_file(adapter, ADAPTER_SIZE, ADAPTER_SHA256): + errors.append("dormant adapter identity mismatch") + if not exact_file(tests, ADAPTER_TEST_SIZE, ADAPTER_TEST_SHA256): + errors.append("dormant adapter tests identity mismatch") + try: + source = adapter.read_text(encoding="utf-8") + tree = ast.parse(source) + except (OSError, SyntaxError, UnicodeError): + errors.append("dormant adapter cannot be parsed") + else: + imports = _imports(tree) + if imports & NETWORK_MODULES or "time" in imports: + errors.append("dormant adapter imports a live capability") + names = { + node.name for node in ast.walk(tree) + if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)) + } + if {"connect", "send", "recv", "main"} & names: + errors.append("dormant adapter exposes a live API") + if re.search(r"(? int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + root = args.root.resolve() + try: + record = load_json( + root / "manifests/retroarch/phase-1.0ac-dormant-adapter.json") + errors = validate_record(record, root) + except (OSError, ValueError, json.JSONDecodeError) as error: + errors = [f"validation input failed: {error}"] + if errors: + for error in errors: + print(f"ERROR: {error}") + return 1 + print("Phase-1.0AC dormant-adapter validation passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/validate_retroarch_phase10ad.py b/tools/validate_retroarch_phase10ad.py new file mode 100644 index 0000000..e029544 --- /dev/null +++ b/tools/validate_retroarch_phase10ad.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate the inactive Phase-1.0AD record and byte bindings.""" + +from __future__ import annotations + +import argparse +import ast +import hashlib +import json +from pathlib import Path + + +def digest(path: Path) -> tuple[int, str]: + data = path.read_bytes() + return len(data), hashlib.sha256(data).hexdigest() + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + root = parser.parse_args().root.resolve() + manifest = json.loads((root / "manifests/retroarch/phase-1.0ad-inactive-activation.json").read_text(encoding="utf-8")) + activation = manifest["activation"] + authorization = manifest["authorizations"] + assert manifest["phase"] == "PHASE_1_0AD_INACTIVE_NUMERIC_TARGET_CONTRACT" + assert activation["active"] is False + assert all(activation[name] is None for name in ( + "target_address", "target_port", "run_id", "not_before", + "expires_at", "launcher_sha256", "payload_sha256", "approval_sha256")) + assert activation["one_shot"] is True + assert activation["automatic_retry"] is False + assert activation["reconnect"] is False + assert activation["resume"] is False + assert not any(authorization.values()) + assert manifest["decision"]["device_action_allowed"] is False + assert manifest["decision"]["bigapp_launcher_implementation_allowed"] is False + + bindings = manifest["source_bindings"] + files = { + "contract": root / "tools/phase10ad_activation_contract.py", + "tests": root / "tests/test_phase10ad_activation_contract.py", + "documentation": root / "docs/retroarch/phase-1.0ad-inactive-activation-contract.md", + } + for name, path in files.items(): + size, sha256 = digest(path) + assert bindings[f"{name}_size"] == size + assert bindings[f"{name}_sha256"] == sha256 + + tree = ast.parse(files["contract"].read_text(encoding="utf-8")) + imports = {alias.name.split(".")[0] for node in ast.walk(tree) + if isinstance(node, ast.Import) for alias in node.names} + imports.update(node.module.split(".")[0] for node in ast.walk(tree) + if isinstance(node, ast.ImportFrom) and node.module) + assert not imports.intersection({"socket", "selectors", "subprocess", "urllib", "http", "requests"}) + calls = {node.func.id for node in ast.walk(tree) if isinstance(node, ast.Call) + and isinstance(node.func, ast.Name)} + assert not calls.intersection({"open", "exec", "eval", "compile", "input"}) + print("Phase-1.0AD inactive activation contract validation passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/validate_retroarch_phase10ae.py b/tools/validate_retroarch_phase10ae.py new file mode 100644 index 0000000..b378cf8 --- /dev/null +++ b/tools/validate_retroarch_phase10ae.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate Phase-1.0AE against exact official shsrv worktrees.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +from pathlib import Path +import subprocess + + +def sha256(path: Path) -> str: + return hashlib.sha256(path.read_bytes()).hexdigest() + + +def git(root: Path, value: str) -> str: + return subprocess.run(["git", "-C", str(root), "rev-parse", value], + check=True, capture_output=True, text=True).stdout.strip() + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + parser.add_argument("--shsrv-root", type=Path, required=True) + parser.add_argument("--shsrv-v07-root", type=Path, required=True) + args = parser.parse_args() + data = json.loads((args.root / "manifests/retroarch/phase-1.0ae-launcher-architecture.json").read_text(encoding="utf-8")) + sources = data["official_sources"] + for label, root in (("v019", args.shsrv_root), ("v07", args.shsrv_v07_root)): + assert git(root, "HEAD") == sources[f"{label}_commit"] + assert git(root, "HEAD^{tree}") == sources[f"{label}_tree"] + assert sha256(args.shsrv_root / "bundles/hbldr/hbldr.c") == sources["v019_hbldr_sha256"] + assert sha256(args.shsrv_root / "elfldr.c") == sources["v019_elfldr_sha256"] + assert sha256(args.shsrv_root / "pt.c") == sources["v019_pt_sha256"] + assert sha256(args.shsrv_v07_root / "bundles/hbldr/main.c") == sources["v07_hbldr_sha256"] + assert sha256(args.shsrv_v07_root / "elfldr.c") == sources["v07_elfldr_sha256"] + assert sha256(args.shsrv_v07_root / "pt.c") == sources["v07_pt_sha256"] + v019 = (args.shsrv_root / "bundles/hbldr/hbldr.c").read_text(encoding="utf-8") + v07 = (args.shsrv_v07_root / "bundles/hbldr/main.c").read_text(encoding="utf-8") + assert "FAKE00000" in v019 and "remount_system_ex" in v019 + assert "PPSA01659" in v07 and "FAKE00000" not in v07 and "nmount" not in v07 + assert data["lineage_decision"]["selected_reference"] == "OFFICIAL_SHSRV_V0_7" + assert data["mandatory_policy"]["kill_existing_bigapp"] is False + assert data["decision"]["target_implementation_allowed"] is False + assert data["decision"]["device_action_allowed"] is False + assert not any(data["authorizations"].values()) + print("Phase-1.0AE launcher architecture validation passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/validate_retroarch_phase10af.py b/tools/validate_retroarch_phase10af.py new file mode 100644 index 0000000..a7165d3 --- /dev/null +++ b/tools/validate_retroarch_phase10af.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate Phase-1.0AF identities and offline-only boundary.""" + +from __future__ import annotations + +import argparse +import ast +import hashlib +import json +from pathlib import Path + + +def identity(path: Path) -> tuple[int, str]: + data = path.read_bytes() + return len(data), hashlib.sha256(data).hexdigest() + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + root = parser.parse_args().root + data = json.loads((root / "manifests/retroarch/phase-1.0af-bigapp-lifecycle-model.json").read_text(encoding="utf-8")) + bindings = data["source_bindings"] + assert hashlib.sha256((root / "manifests/retroarch/phase-1.0ae-launcher-architecture.json").read_bytes()).hexdigest() == bindings["phase10ae_manifest_sha256"] + for prefix, relative in (("model", "tools/phase10af_bigapp_lifecycle_model.py"), + ("tests", "tests/test_phase10af_bigapp_lifecycle_model.py")): + size, digest = identity(root / relative) + assert bindings[f"{prefix}_size"] == size + assert bindings[f"{prefix}_sha256"] == digest + source = (root / "tools/phase10af_bigapp_lifecycle_model.py").read_text(encoding="utf-8") + tree = ast.parse(source) + imports = {alias.name.split(".")[0] for node in ast.walk(tree) + if isinstance(node, ast.Import) for alias in node.names} + imports.update(node.module.split(".")[0] for node in ast.walk(tree) + if isinstance(node, ast.ImportFrom) and node.module) + assert not imports.intersection({"socket", "subprocess", "os", "sys", "ctypes", "time", "pathlib"}) + assert "PPSA01659" in source and "FAKE00000" not in source + assert not any(data["authorizations"].values()) + assert data["decision"]["target_implementation_allowed"] is False + assert data["decision"]["device_action_allowed"] is False + print("Phase-1.0AF offline lifecycle validation passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/validate_retroarch_phase10ag.py b/tools/validate_retroarch_phase10ag.py new file mode 100644 index 0000000..5270d33 --- /dev/null +++ b/tools/validate_retroarch_phase10ag.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate Phase-1.0AG byte identities and closed parser boundary.""" + +from __future__ import annotations + +import argparse +import ast +import hashlib +import json +from pathlib import Path + + +def identity(path: Path) -> tuple[int, str]: + data = path.read_bytes() + return len(data), hashlib.sha256(data).hexdigest() + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + root = parser.parse_args().root + data = json.loads((root / "manifests/retroarch/phase-1.0ag-bounded-elf-contract.json").read_text(encoding="utf-8")) + bindings = data["source_bindings"] + assert hashlib.sha256((root / "manifests/retroarch/phase-1.0af-bigapp-lifecycle-model.json").read_bytes()).hexdigest() == bindings["phase10af_manifest_sha256"] + for prefix, relative in (("validator", "tools/phase10ag_bounded_elf.py"), + ("tests", "tests/test_phase10ag_bounded_elf.py")): + size, digest = identity(root / relative) + assert size == bindings[f"{prefix}_size"] + assert digest == bindings[f"{prefix}_sha256"] + source = (root / "tools/phase10ag_bounded_elf.py").read_text(encoding="utf-8") + tree = ast.parse(source) + imports = {alias.name.split(".")[0] for node in ast.walk(tree) + if isinstance(node, ast.Import) for alias in node.names} + imports.update(node.module.split(".")[0] for node in ast.walk(tree) + if isinstance(node, ast.ImportFrom) and node.module) + assert not imports.intersection({"pathlib", "os", "sys", "subprocess", "socket", "ctypes", "mmap"}) + assert data["historical_reference"]["validated_by_phase10ag"] is False + assert not any(data["authorizations"].values()) + assert data["decision"]["target_implementation_allowed"] is False + assert data["decision"]["device_action_allowed"] is False + print("Phase-1.0AG bounded ELF contract validation passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/validate_retroarch_phase10ah.py b/tools/validate_retroarch_phase10ah.py new file mode 100644 index 0000000..e9df839 --- /dev/null +++ b/tools/validate_retroarch_phase10ah.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate Phase-1.0AH identities and official source bindings.""" + +from __future__ import annotations + +import argparse +import ast +import hashlib +import json +from pathlib import Path +import subprocess + + +def digest(path: Path) -> tuple[int, str]: + data = path.read_bytes() + return len(data), hashlib.sha256(data).hexdigest() + + +def git(root: Path) -> str: + return subprocess.run(["git", "-C", str(root), "rev-parse", "HEAD"], + check=True, capture_output=True, text=True).stdout.strip() + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + parser.add_argument("--shsrv-v07-root", type=Path, required=True) + parser.add_argument("--sdk-root", type=Path, required=True) + args = parser.parse_args() + data = json.loads((args.root / "manifests/retroarch/phase-1.0ah-dynamic-contract.json").read_text(encoding="utf-8")) + bindings = data["source_bindings"] + assert hashlib.sha256((args.root / "manifests/retroarch/phase-1.0ag-bounded-elf-contract.json").read_bytes()).hexdigest() == bindings["phase10ag_manifest_sha256"] + for prefix, relative in (("contract", "tools/phase10ah_dynamic_contract.py"), + ("tests", "tests/test_phase10ah_dynamic_contract.py")): + size, sha256 = digest(args.root / relative) + assert size == bindings[f"{prefix}_size"] and sha256 == bindings[f"{prefix}_sha256"] + assert git(args.shsrv_v07_root) == bindings["official_shsrv_v07_commit"] + assert digest(args.shsrv_v07_root / "elfldr.c")[1] == bindings["official_shsrv_v07_elfldr_sha256"] + assert git(args.sdk_root) == bindings["official_sdk_v041_commit"] + assert digest(args.sdk_root / "crt/rtld_payload.c")[1] == bindings["official_sdk_v041_rtld_payload_sha256"] + tree = ast.parse((args.root / "tools/phase10ah_dynamic_contract.py").read_text(encoding="utf-8")) + imports = {alias.name.split(".")[0] for node in ast.walk(tree) + if isinstance(node, ast.Import) for alias in node.names} + imports.update(node.module.split(".")[0] for node in ast.walk(tree) + if isinstance(node, ast.ImportFrom) and node.module) + assert not imports.intersection({"pathlib", "os", "sys", "subprocess", "socket", "ctypes", "mmap"}) + assert not any(data["authorizations"].values()) + assert data["historical_phase10m_reference"]["validated_by_phase10ah"] is False + assert data["decision"]["target_mapping_allowed"] is False + assert data["decision"]["device_action_allowed"] is False + print("Phase-1.0AH dynamic contract validation passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/validate_retroarch_phase10ai.py b/tools/validate_retroarch_phase10ai.py new file mode 100644 index 0000000..e1a312d --- /dev/null +++ b/tools/validate_retroarch_phase10ai.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate Phase-1.0AI byte bindings and closed model boundary.""" + +from __future__ import annotations + +import argparse +import ast +import hashlib +import json +from pathlib import Path + + +def digest(path: Path) -> tuple[int, str]: + data = path.read_bytes() + return len(data), hashlib.sha256(data).hexdigest() + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + root = parser.parse_args().root + data = json.loads((root / "manifests/retroarch/phase-1.0ai-mapping-model.json").read_text(encoding="utf-8")) + bindings = data["source_bindings"] + assert hashlib.sha256((root / "manifests/retroarch/phase-1.0ah-dynamic-contract.json").read_bytes()).hexdigest() == bindings["phase10ah_manifest_sha256"] + for prefix, relative in (("model", "tools/phase10ai_mapping_model.py"), + ("tests", "tests/test_phase10ai_mapping_model.py")): + size, sha256 = digest(root / relative) + assert size == bindings[f"{prefix}_size"] and sha256 == bindings[f"{prefix}_sha256"] + tree = ast.parse((root / "tools/phase10ai_mapping_model.py").read_text(encoding="utf-8")) + imports = {alias.name.split(".")[0] for node in ast.walk(tree) + if isinstance(node, ast.Import) for alias in node.names} + imports.update(node.module.split(".")[0] for node in ast.walk(tree) + if isinstance(node, ast.ImportFrom) and node.module) + assert not imports.intersection({"os", "sys", "pathlib", "subprocess", "socket", "ctypes", "mmap"}) + transaction = data["transaction"] + assert transaction["partial_mapping_retained_on_failure"] is False + assert transaction["deadline_preempts_operation_atomically"] is True + assert not any(data["authorizations"].values()) + assert data["decision"]["target_mapping_allowed"] is False + assert data["decision"]["device_action_allowed"] is False + print("Phase-1.0AI mapping transaction validation passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/validate_retroarch_phase10aj.py b/tools/validate_retroarch_phase10aj.py new file mode 100644 index 0000000..1d55334 --- /dev/null +++ b/tools/validate_retroarch_phase10aj.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate Phase-1.0AJ external source bindings and closed boundary.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +import subprocess +from pathlib import Path + + +def sha256(path: Path) -> str: + return hashlib.sha256(path.read_bytes()).hexdigest() + + +def commit(root: Path) -> str: + return subprocess.run( + ["git", "rev-parse", "HEAD"], cwd=root, check=True, + capture_output=True, text=True).stdout.strip() + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + parser.add_argument("--shsrv-v07-root", type=Path, required=True) + parser.add_argument("--hardened-elfldr-root", type=Path, required=True) + args = parser.parse_args() + data = json.loads((args.root / "manifests/retroarch/phase-1.0aj-primitive-audit.json").read_text(encoding="utf-8")) + bindings = data["source_bindings"] + assert sha256(args.root / "manifests/retroarch/phase-1.0ai-mapping-model.json") == bindings["phase10ai_manifest_sha256"] + assert commit(args.shsrv_v07_root) == bindings["shsrv_v07_commit"] + assert sha256(args.shsrv_v07_root / "elfldr.c") == bindings["shsrv_v07_elfldr_sha256"] + assert sha256(args.shsrv_v07_root / "pt.c") == bindings["shsrv_v07_pt_sha256"] + assert commit(args.hardened_elfldr_root) == bindings["hardened_elfldr_commit"] + for key, relative in ( + ("controlled_runtime_c_sha256", "controlled_runtime.c"), + ("controlled_runtime_h_sha256", "controlled_runtime.h"), + ("hardened_pt_sha256", "pt.c"), + ("ps5_controlled_sha256", "ps5_controlled.c"), + ): + assert sha256(args.hardened_elfldr_root / relative) == bindings[key] + assert not any(data["authorizations"].values()) + assert data["decision"]["target_implementation_allowed"] is False + assert data["decision"]["device_action_allowed"] is False + print("Phase-1.0AJ primitive source audit validation passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/validate_retroarch_phase10ak.py b/tools/validate_retroarch_phase10ak.py new file mode 100644 index 0000000..b50dad8 --- /dev/null +++ b/tools/validate_retroarch_phase10ak.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate Phase-1.0AK byte bindings and capability-free model.""" + +from __future__ import annotations + +import argparse +import ast +import hashlib +import json +from pathlib import Path + + +def digest(path: Path) -> tuple[int, str]: + data = path.read_bytes() + return len(data), hashlib.sha256(data).hexdigest() + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + root = parser.parse_args().root + data = json.loads((root / "manifests/retroarch/phase-1.0ak-hybrid-composition.json").read_text(encoding="utf-8")) + bindings = data["source_bindings"] + assert hashlib.sha256((root / "manifests/retroarch/phase-1.0aj-primitive-audit.json").read_bytes()).hexdigest() == bindings["phase10aj_manifest_sha256"] + for prefix, relative in (("model", "tools/phase10ak_hybrid_composition.py"), + ("tests", "tests/test_phase10ak_hybrid_composition.py")): + size, sha256 = digest(root / relative) + assert size == bindings[f"{prefix}_size"] + assert sha256 == bindings[f"{prefix}_sha256"] + tree = ast.parse((root / "tools/phase10ak_hybrid_composition.py").read_text(encoding="utf-8")) + imports = {alias.name.split(".")[0] for node in ast.walk(tree) + if isinstance(node, ast.Import) for alias in node.names} + imports.update(node.module.split(".")[0] for node in ast.walk(tree) + if isinstance(node, ast.ImportFrom) and node.module) + assert not imports.intersection({"os", "sys", "pathlib", "subprocess", "socket", "ctypes", "mmap", "time"}) + ownership = data["ownership"] + assert ownership["cleanup_failure_requires_child_termination"] is True + assert ownership["failed_child_termination_is_hard_error"] is True + assert ownership["partial_success_allowed"] is False + assert not any(data["authorizations"].values()) + assert data["decision"]["target_implementation_allowed"] is False + print("Phase-1.0AK hybrid composition validation passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/validate_retroarch_phase10al.py b/tools/validate_retroarch_phase10al.py new file mode 100644 index 0000000..7ec283f --- /dev/null +++ b/tools/validate_retroarch_phase10al.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate Phase-1.0AL against the exact pinned SDK source.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +import subprocess +from pathlib import Path + + +def digest(path: Path) -> tuple[int, str]: + data = path.read_bytes() + return len(data), hashlib.sha256(data).hexdigest() + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + parser.add_argument("--sdk-root", type=Path, required=True) + args = parser.parse_args() + data = json.loads((args.root / "manifests/retroarch/phase-1.0al-mdbg-copy-audit.json").read_text(encoding="utf-8")) + bindings = data["source_bindings"] + assert hashlib.sha256((args.root / "manifests/retroarch/phase-1.0ak-hybrid-composition.json").read_bytes()).hexdigest() == bindings["phase10ak_manifest_sha256"] + sdk_commit = subprocess.run(["git", "rev-parse", "HEAD"], cwd=args.sdk_root, + check=True, capture_output=True, text=True).stdout.strip() + assert sdk_commit == bindings["sdk_commit"] + for prefix, relative in (("mdbg_c", "crt/mdbg.c"), + ("public_header", "include/ps5/mdbg.h")): + size, sha256 = digest(args.sdk_root / relative) + assert size == bindings[f"{prefix}_size"] + assert sha256 == bindings[f"{prefix}_sha256"] + findings = data["findings"] + assert findings["target_may_be_partially_mutated_on_error"] is True + assert findings["return_zero_proves_complete_copy"] is False + assert findings["monotonic_deadline_present"] is False + assert not any(data["authorizations"].values()) + assert data["decision"]["direct_sdk_mdbg_copy_reuse_allowed"] is False + print("Phase-1.0AL mdbg copy audit validation passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/validate_retroarch_phase10am.py b/tools/validate_retroarch_phase10am.py new file mode 100644 index 0000000..47ce9c4 --- /dev/null +++ b/tools/validate_retroarch_phase10am.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate Phase-1.0AM bindings and capability-free boundary.""" + +from __future__ import annotations + +import argparse +import ast +import hashlib +import json +from pathlib import Path + + +def digest(path: Path) -> tuple[int, str]: + data = path.read_bytes() + return len(data), hashlib.sha256(data).hexdigest() + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + root = parser.parse_args().root + data = json.loads((root / "manifests/retroarch/phase-1.0am-bounded-copy-model.json").read_text(encoding="utf-8")) + bindings = data["source_bindings"] + assert hashlib.sha256((root / "manifests/retroarch/phase-1.0al-mdbg-copy-audit.json").read_bytes()).hexdigest() == bindings["phase10al_manifest_sha256"] + for prefix, relative in (("model", "tools/phase10am_bounded_copy_model.py"), + ("tests", "tests/test_phase10am_bounded_copy_model.py")): + size, sha256 = digest(root / relative) + assert size == bindings[f"{prefix}_size"] + assert sha256 == bindings[f"{prefix}_sha256"] + tree = ast.parse((root / "tools/phase10am_bounded_copy_model.py").read_text(encoding="utf-8")) + imports = {alias.name.split(".")[0] for node in ast.walk(tree) + if isinstance(node, ast.Import) for alias in node.names} + imports.update(node.module.split(".")[0] for node in ast.walk(tree) + if isinstance(node, ast.ImportFrom) and node.module) + assert not imports.intersection({"os", "sys", "pathlib", "subprocess", "socket", "ctypes", "mmap", "time"}) + contract = data["contract"] + assert contract["partial_copy_kills_and_reaps_child"] is True + assert contract["restore_failure_kills_child_and_terminates_service"] is True + assert contract["terminal_cleanup_failure_is_hard_error"] is True + assert not any(data["authorizations"].values()) + assert data["decision"]["target_implementation_allowed"] is False + print("Phase-1.0AM bounded copy/restore validation passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/validate_retroarch_phase10an.py b/tools/validate_retroarch_phase10an.py new file mode 100644 index 0000000..d0a3c08 --- /dev/null +++ b/tools/validate_retroarch_phase10an.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate Phase-1.0AN against the hardened elfldr source.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +import subprocess +from pathlib import Path + + +def digest(path: Path) -> tuple[int, str]: + data = path.read_bytes() + return len(data), hashlib.sha256(data).hexdigest() + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + parser.add_argument("--hardened-elfldr-root", type=Path, required=True) + args = parser.parse_args() + data = json.loads((args.root / "manifests/retroarch/phase-1.0an-service-lifecycle-audit.json").read_text(encoding="utf-8")) + bindings = data["source_bindings"] + assert hashlib.sha256((args.root / "manifests/retroarch/phase-1.0am-bounded-copy-model.json").read_bytes()).hexdigest() == bindings["phase10am_manifest_sha256"] + commit = subprocess.run(["git", "rev-parse", "HEAD"], + cwd=args.hardened_elfldr_root, check=True, + capture_output=True, text=True).stdout.strip() + assert commit == bindings["hardened_elfldr_commit"] + for prefix, relative in (("socksrv", "socksrv.c"), ("pt", "pt.c"), + ("main", "main.c"), ("elfldr", "elfldr.c")): + size, sha256 = digest(args.hardened_elfldr_root / relative) + assert size == bindings[f"{prefix}_size"] + assert sha256 == bindings[f"{prefix}_sha256"] + assert data["service_lifecycle"]["request_handler_process_exits_125"] is True + assert data["service_lifecycle"]["service_restart_owner_present"] is False + assert data["copy_path"]["hard_deadline_or_preemption_present"] is False + assert not any(data["authorizations"].values()) + assert data["decision"]["hardened_pt_copyin_reuse_allowed"] is False + print("Phase-1.0AN service lifecycle audit validation passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/validate_retroarch_phase10ao.py b/tools/validate_retroarch_phase10ao.py new file mode 100644 index 0000000..da5a07f --- /dev/null +++ b/tools/validate_retroarch_phase10ao.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate Phase-1.0AO bindings and capability-free boundary.""" + +from __future__ import annotations + +import argparse +import ast +import hashlib +import json +from pathlib import Path + + +def digest(path: Path) -> tuple[int, str]: + data = path.read_bytes() + return len(data), hashlib.sha256(data).hexdigest() + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + root = parser.parse_args().root + data = json.loads((root / "manifests/retroarch/phase-1.0ao-worker-supervisor-model.json").read_text(encoding="utf-8")) + bindings = data["source_bindings"] + assert hashlib.sha256((root / "manifests/retroarch/phase-1.0an-service-lifecycle-audit.json").read_bytes()).hexdigest() == bindings["phase10an_manifest_sha256"] + for prefix, relative in (("model", "tools/phase10ao_worker_supervisor_model.py"), + ("tests", "tests/test_phase10ao_worker_supervisor_model.py")): + size, sha256 = digest(root / relative) + assert size == bindings[f"{prefix}_size"] + assert sha256 == bindings[f"{prefix}_sha256"] + tree = ast.parse((root / "tools/phase10ao_worker_supervisor_model.py").read_text(encoding="utf-8")) + imports = {alias.name.split(".")[0] for node in ast.walk(tree) + if isinstance(node, ast.Import) for alias in node.names} + imports.update(node.module.split(".")[0] for node in ast.walk(tree) + if isinstance(node, ast.ImportFrom) and node.module) + assert not imports.intersection({"os", "sys", "pathlib", "subprocess", "socket", "ctypes", "signal", "time"}) + architecture = data["architecture"] + assert architecture["automatic_restart"] is False + assert architecture["retry"] is False + assert architecture["terminal_cleanup_failure_is_hard_error"] is True + assert not any(data["authorizations"].values()) + assert data["decision"]["target_architecture_feasible"] is False + print("Phase-1.0AO worker supervisor validation passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/validate_retroarch_phase10ap.py b/tools/validate_retroarch_phase10ap.py new file mode 100644 index 0000000..3ae810a --- /dev/null +++ b/tools/validate_retroarch_phase10ap.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate Phase-1.0AP against current official Git objects.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +import subprocess +from pathlib import Path + + +def git(repo: Path, *args: str) -> str: + return subprocess.run(["git", *args], cwd=repo, check=True, + capture_output=True, text=True).stdout.strip() + + +def check_blob(repo: Path, commit: str, path: str, + expected_blob: str, expected_size: int) -> None: + spec = f"{commit}:{path}" + assert git(repo, "rev-parse", spec) == expected_blob + assert int(git(repo, "cat-file", "-s", spec)) == expected_size + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + parser.add_argument("--sdk-root", type=Path, required=True) + parser.add_argument("--shsrv-root", type=Path, required=True) + args = parser.parse_args() + data = json.loads((args.root / "manifests/retroarch/phase-1.0ap-worker-feasibility-audit.json").read_text(encoding="utf-8")) + bindings = data["source_bindings"] + assert hashlib.sha256((args.root / "manifests/retroarch/phase-1.0ao-worker-supervisor-model.json").read_bytes()).hexdigest() == bindings["phase10ao_manifest_sha256"] + check_blob(args.sdk_root, bindings["sdk_commit"], "include/freebsd/unistd.h", + bindings["sdk_unistd_blob"], bindings["sdk_unistd_size"]) + check_blob(args.sdk_root, bindings["sdk_commit"], "crt/syscall.h", + bindings["sdk_syscall_blob"], bindings["sdk_syscall_size"]) + for prefix, path in (("shsrv_builtin", "builtin.c"), + ("shsrv_elfldr", "elfldr.c"), + ("shsrv_shell", "sh.c"), ("shsrv_pt", "pt.c")): + check_blob(args.shsrv_root, bindings["shsrv_commit"], path, + bindings[f"{prefix}_blob"], bindings[f"{prefix}_size"]) + assert data["worker_creation"]["classification"] == "STRONG_SOURCE_CANDIDATE_NOT_RUNTIME_PROOF" + assert data["preemption"]["waitpid_calls_are_bounded"] is False + assert data["result_channel"]["fixed_size_worker_result_record_present"] is False + assert not any(data["authorizations"].values()) + assert data["decision"]["target_worker_architecture_feasible"] is False + print("Phase-1.0AP worker feasibility audit validation passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/validate_retroarch_phase10aq.py b/tools/validate_retroarch_phase10aq.py new file mode 100644 index 0000000..b466154 --- /dev/null +++ b/tools/validate_retroarch_phase10aq.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate Phase-1.0AQ byte bindings and transport-free boundary.""" + +from __future__ import annotations + +import argparse +import ast +import hashlib +import json +from pathlib import Path + + +def digest(path: Path) -> tuple[int, str]: + data = path.read_bytes() + return len(data), hashlib.sha256(data).hexdigest() + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + root = parser.parse_args().root + data = json.loads((root / "manifests/retroarch/phase-1.0aq-worker-result-record.json").read_text(encoding="utf-8")) + bindings = data["source_bindings"] + assert hashlib.sha256((root / "manifests/retroarch/phase-1.0ap-worker-feasibility-audit.json").read_bytes()).hexdigest() == bindings["phase10ap_manifest_sha256"] + for prefix, relative in (("model", "tools/phase10aq_worker_result_record.py"), + ("tests", "tests/test_phase10aq_worker_result_record.py")): + size, sha256 = digest(root / relative) + assert size == bindings[f"{prefix}_size"] + assert sha256 == bindings[f"{prefix}_sha256"] + tree = ast.parse((root / "tools/phase10aq_worker_result_record.py").read_text(encoding="utf-8")) + imports = {alias.name.split(".")[0] for node in ast.walk(tree) + if isinstance(node, ast.Import) for alias in node.names} + imports.update(node.module.split(".")[0] for node in ast.walk(tree) + if isinstance(node, ast.ImportFrom) and node.module) + assert not imports.intersection({"os", "sys", "pathlib", "subprocess", "socket", "ctypes", "secrets", "random", "time"}) + assert data["record"]["size"] == 128 + assert data["identity"]["pid_alone_is_identity"] is False + assert data["result"]["digest_is_authentication"] is False + assert not any(data["authorizations"].values()) + assert data["decision"]["transport_implementation_allowed"] is False + print("Phase-1.0AQ worker result record validation passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/validate_retroarch_phase10ar.py b/tools/validate_retroarch_phase10ar.py new file mode 100644 index 0000000..ecfd478 --- /dev/null +++ b/tools/validate_retroarch_phase10ar.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate Phase-1.0AR bindings and absence of live channel capability.""" + +from __future__ import annotations + +import argparse +import ast +import hashlib +import json +from pathlib import Path + + +def digest(path: Path) -> tuple[int, str]: + data = path.read_bytes() + return len(data), hashlib.sha256(data).hexdigest() + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + root = parser.parse_args().root + data = json.loads((root / "manifests/retroarch/phase-1.0ar-result-channel-model.json").read_text(encoding="utf-8")) + bindings = data["source_bindings"] + assert hashlib.sha256((root / "manifests/retroarch/phase-1.0aq-worker-result-record.json").read_bytes()).hexdigest() == bindings["phase10aq_manifest_sha256"] + for prefix, relative in (("model", "tools/phase10ar_result_channel_model.py"), + ("tests", "tests/test_phase10ar_result_channel_model.py")): + size, sha256 = digest(root / relative) + assert size == bindings[f"{prefix}_size"] + assert sha256 == bindings[f"{prefix}_sha256"] + tree = ast.parse((root / "tools/phase10ar_result_channel_model.py").read_text(encoding="utf-8")) + imports = {alias.name.split(".")[0] for node in ast.walk(tree) + if isinstance(node, ast.Import) for alias in node.names} + imports.update(node.module.split(".")[0] for node in ast.walk(tree) + if isinstance(node, ast.ImportFrom) and node.module) + assert not imports.intersection({"os", "sys", "pathlib", "subprocess", "socket", "selectors", "select", "ctypes", "time"}) + channel = data["channel"] + assert channel["eof_is_success"] is False + assert channel["record_completion_is_success_boundary"] is True + assert channel["live_pipe_present"] is False + assert not any(data["authorizations"].values()) + assert data["decision"]["live_channel_implementation_allowed"] is False + print("Phase-1.0AR result channel validation passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/validate_retroarch_phase10as.py b/tools/validate_retroarch_phase10as.py new file mode 100644 index 0000000..c2eb7c0 --- /dev/null +++ b/tools/validate_retroarch_phase10as.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate Phase-1.0AS against exact current upstream Git objects.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +import subprocess +from pathlib import Path + + +def git(repo: Path, *args: str) -> str: + return subprocess.run(["git", *args], cwd=repo, check=True, + capture_output=True, text=True).stdout.strip() + + +def check(repo: Path, commit: str, path: str, blob: str, size: int) -> None: + spec = f"{commit}:{path}" + assert git(repo, "rev-parse", spec) == blob + assert int(git(repo, "cat-file", "-s", spec)) == size + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + parser.add_argument("--sdk-root", type=Path, required=True) + parser.add_argument("--shsrv-root", type=Path, required=True) + args = parser.parse_args() + data = json.loads((args.root / "manifests/retroarch/phase-1.0as-channel-primitive-audit.json").read_text(encoding="utf-8")) + b = data["source_bindings"] + assert hashlib.sha256((args.root / "manifests/retroarch/phase-1.0ar-result-channel-model.json").read_bytes()).hexdigest() == b["phase10ar_manifest_sha256"] + for prefix, path in (("sdk_sys_unistd", "include/freebsd/sys/unistd.h"), + ("sdk_unistd", "include/freebsd/unistd.h"), + ("sdk_poll", "include/freebsd/sys/poll.h"), + ("sdk_time", "include/freebsd/sys/time.h")): + check(args.sdk_root, b["sdk_commit"], path, + b[f"{prefix}_blob"], b[f"{prefix}_size"]) + for prefix, path in (("shsrv_shell", "sh.c"), + ("shsrv_builtin", "builtin.c")): + check(args.shsrv_root, b["shsrv_commit"], path, + b[f"{prefix}_blob"], b[f"{prefix}_size"]) + assert data["fd_ownership"]["official_shsrv_worker_uses_rfcfdg"] is True + assert data["fd_ownership"]["result_fd_inherited_by_worker"] is False + assert data["deadline_and_cleanup"]["absolute_monotonic_deadline_present"] is False + assert not any(data["authorizations"].values()) + assert data["decision"]["live_channel_architecture_feasible"] is False + print("Phase-1.0AS channel primitive audit validation passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/validate_retroarch_phase10at.py b/tools/validate_retroarch_phase10at.py new file mode 100644 index 0000000..33a475c --- /dev/null +++ b/tools/validate_retroarch_phase10at.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate exact Phase-1.0AT offline model bindings.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +from pathlib import Path + + +def digest(path: Path) -> str: + return hashlib.sha256(path.read_bytes()).hexdigest() + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + root = args.root + data = json.loads((root / "manifests/retroarch/phase-1.0at-fd-deadline-model.json").read_text(encoding="utf-8")) + bindings = data["source_bindings"] + assert digest(root / "manifests/retroarch/phase-1.0as-channel-primitive-audit.json") == bindings["phase10as_manifest_sha256"] + assert digest(root / "tools/phase10at_fd_deadline_model.py") == bindings["model_sha256"] + assert digest(root / "tests/test_phase10at_fd_deadline_model.py") == bindings["failure_tests_sha256"] + assert data["model_boundary"]["fake_facade_only"] is True + assert data["ownership_contract"]["all_acquired_ends_closed_on_failure"] is True + assert data["deadline_contract"]["trailing_read_event_fails"] is True + assert not any(data["authorizations"].values()) + assert data["decision"]["live_channel_architecture_feasible"] is False + print("Phase-1.0AT FD/deadline model validation passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/validate_retroarch_phase10au.py b/tools/validate_retroarch_phase10au.py new file mode 100644 index 0000000..d0085e7 --- /dev/null +++ b/tools/validate_retroarch_phase10au.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate Phase-1.0AU against exact official Git objects.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +import subprocess +from pathlib import Path + + +def git(repo: Path, *args: str) -> str: + return subprocess.run(["git", *args], cwd=repo, check=True, + capture_output=True, text=True).stdout.strip() + + +def check(repo: Path, commit: str, path: str, blob: str, size: int) -> None: + spec = f"{commit}:{path}" + assert git(repo, "rev-parse", spec) == blob + assert int(git(repo, "cat-file", "-s", spec)) == size + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + parser.add_argument("--sdk-root", type=Path, required=True) + parser.add_argument("--shsrv-root", type=Path, required=True) + args = parser.parse_args() + path = args.root / "manifests/retroarch/phase-1.0au-live-channel-feasibility.json" + data = json.loads(path.read_text(encoding="utf-8")) + binding = data["source_bindings"] + at_path = args.root / "manifests/retroarch/phase-1.0at-fd-deadline-model.json" + assert hashlib.sha256(at_path.read_bytes()).hexdigest() == binding["phase10at_manifest_sha256"] + sdk_paths = { + "sdk_sys_unistd": "include/freebsd/sys/unistd.h", + "sdk_unistd": "include/freebsd/unistd.h", + "sdk_sys_fcntl": "include/freebsd/sys/fcntl.h", + "sdk_time": "include/freebsd/time.h", + "sdk_wait": "include/freebsd/sys/wait.h", + "sdk_signal": "include/freebsd/signal.h", + } + for prefix, source in sdk_paths.items(): + check(args.sdk_root, binding["sdk_commit"], source, + binding[f"{prefix}_blob"], binding[f"{prefix}_size"]) + shsrv_paths = {"shsrv_builtin": "builtin.c", "shsrv_shell": "sh.c", + "shsrv_service": "shsrv.c"} + for prefix, source in shsrv_paths.items(): + check(args.shsrv_root, binding["shsrv_commit"], source, + binding[f"{prefix}_blob"], binding[f"{prefix}_size"]) + assert data["public_source_contracts"]["source_level_design_inputs_complete"] is True + assert data["official_composition_audit"]["worker_uses_rffdg"] is False + assert data["official_composition_audit"]["worker_uses_rfcfdg_close_all"] is True + assert not any(data["authorizations"].values()) + assert data["decision"]["live_result_channel_implementation_allowed"] is False + print("Phase-1.0AU live-channel feasibility validation passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/validate_retroarch_phase10av.py b/tools/validate_retroarch_phase10av.py new file mode 100644 index 0000000..127978b --- /dev/null +++ b/tools/validate_retroarch_phase10av.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate exact Phase-1.0AV offline canary bindings.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +from pathlib import Path + + +def digest(path: Path) -> str: + return hashlib.sha256(path.read_bytes()).hexdigest() + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + root = args.root + path = root / "manifests/retroarch/phase-1.0av-launch-context-canary-contract.json" + data = json.loads(path.read_text(encoding="utf-8")) + binding = data["source_bindings"] + assert digest(root / "manifests/retroarch/phase-1.0au-live-channel-feasibility.json") == binding["phase10au_manifest_sha256"] + assert digest(root / "tools/phase10av_launch_context_canary.py") == binding["contract_sha256"] + assert digest(root / "tests/test_phase10av_launch_context_canary.py") == binding["tests_sha256"] + assert data["pair_contract"]["same_payload_sha256_required"] is True + assert data["result_contract"]["distinct_terminal_after_d04_required"] is True + assert data["interpretation_limits"]["submit_zero_means_visible_flip"] is False + assert not any(data["authorizations"].values()) + assert not any(value is not None for key, value in data["tracked_state"].items() + if key.endswith("sha256") or key in {"raw_run_id", "bigapp_run_id", + "target_address", "target_port"}) + assert data["decision"]["causal_hardware_comparison_ready"] is False + print("Phase-1.0AV launch-context canary validation passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/validate_retroarch_phase10aw.py b/tools/validate_retroarch_phase10aw.py new file mode 100644 index 0000000..0d46e2c --- /dev/null +++ b/tools/validate_retroarch_phase10aw.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate Phase-1.0AW against exact RetroArch and shsrv Git objects.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +import subprocess +from pathlib import Path + + +def git(repo: Path, *args: str) -> str: + return subprocess.run(["git", *args], cwd=repo, check=True, + capture_output=True, text=True).stdout.strip() + + +def check(repo: Path, commit: str, path: str, blob: str, size: int) -> None: + spec = f"{commit}:{path}" + assert git(repo, "rev-parse", spec) == blob + assert int(git(repo, "cat-file", "-s", spec)) == size + + +def show(repo: Path, commit: str, path: str) -> str: + return git(repo, "show", f"{commit}:{path}") + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + parser.add_argument("--retroarch-root", type=Path, required=True) + parser.add_argument("--shsrv-root", type=Path, required=True) + args = parser.parse_args() + root = args.root + path = root / "manifests/retroarch/phase-1.0aw-canary-source-delta-audit.json" + data = json.loads(path.read_text(encoding="utf-8")) + b = data["source_bindings"] + av = root / "manifests/retroarch/phase-1.0av-launch-context-canary-contract.json" + assert hashlib.sha256(av.read_bytes()).hexdigest() == b["phase10av_manifest_sha256"] + retroarch_paths = { + "retroarch_c": "retroarch.c", "sdl2_gfx": "gfx/drivers/sdl2_gfx.c", + "platform_smoke_c": "frontend/drivers/platform_ps5_smoke.c", + "platform_smoke_h": "frontend/drivers/platform_ps5_smoke.h", + "diag_c": "pkg/ps5/chimera_ps5_diag.c", + "diag_h": "pkg/ps5/chimera_ps5_diag.h", + "stream_c": "pkg/ps5/chimera_ps5_diag_stream.c", + "stream_h": "pkg/ps5/chimera_ps5_diag_stream.h", + "makefile": "Makefile.ps5", + "sdl_hardening_patch": "pkg/ps5/sdl2-ps5-smoke-hardening.patch", + } + for prefix, source in retroarch_paths.items(): + check(args.retroarch_root, b["retroarch_commit"], source, + b[f"{prefix}_blob"], b[f"{prefix}_size"]) + shsrv_paths = {"v07_hbldr": "bundles/hbldr/main.c", + "v07_elfldr": "elfldr.c", "v07_shell": "sh.c", + "v07_service": "shsrv.c"} + for prefix, source in shsrv_paths.items(): + check(args.shsrv_root, b["shsrv_v07_commit"], source, + b[f"{prefix}_blob"], b[f"{prefix}_size"]) + retroarch_c = show(args.retroarch_root, b["retroarch_commit"], "retroarch.c") + diag_c = show(args.retroarch_root, b["retroarch_commit"], "pkg/ps5/chimera_ps5_diag.c") + hbldr = show(args.shsrv_root, b["shsrv_v07_commit"], "bundles/hbldr/main.c") + elfldr = show(args.shsrv_root, b["shsrv_v07_commit"], "elfldr.c") + assert "main_exit(data);" in retroarch_c and "_Exit(result);" in retroarch_c + assert "CHIMERA_PS5_DIAG_D12" in diag_c and "CHIMERA_PS5_DIAG_FRAME_TERMINAL" in diag_c + assert "elfldr_exec(STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO" in hbldr + assert "stdout_fd = pt_rdup(pid, getpid(), stdout_fd);" in elfldr + assert "while(1)" in hbldr and "sceSystemServiceKillApp" in hbldr + assert data["historical_payload"]["reusable_as_av_canary"] is False + assert data["required_payload_source_delta"]["new_cleanup_failure_counter_required"] is True + assert data["bigapp_result_path"]["live_result_path_proven"] is False + assert not any(data["authorizations"].values()) + assert data["decision"]["target_artifact_build_allowed"] is False + print("Phase-1.0AW canary source-delta audit validation passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/validate_retroarch_phase10ax.py b/tools/validate_retroarch_phase10ax.py new file mode 100644 index 0000000..eea960a --- /dev/null +++ b/tools/validate_retroarch_phase10ax.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate exact Phase-1.0AX host reference bindings.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +from pathlib import Path + + +def digest(path: Path) -> str: + return hashlib.sha256(path.read_bytes()).hexdigest() + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + root = args.root + path = root / "manifests/retroarch/phase-1.0ax-canary-protocol-model.json" + data = json.loads(path.read_text(encoding="utf-8")) + binding = data["source_bindings"] + assert digest(root / "manifests/retroarch/phase-1.0aw-canary-source-delta-audit.json") == binding["phase10aw_manifest_sha256"] + assert digest(root / "tools/phase10ax_canary_protocol_model.py") == binding["model_sha256"] + assert digest(root / "tests/test_phase10ax_canary_protocol_model.py") == binding["tests_sha256"] + assert data["frame_contract"]["only_d14_terminal"] is True + assert data["frame_contract"]["d12_terminal_forbidden"] is True + assert data["cleanup_terminal_contract"]["cleanup_failure_count_zero_required"] is True + assert data["trace_contract"]["host_trace_means_firmware_behavior"] is False + assert not any(data["authorizations"].values()) + assert data["decision"]["target_source_matches_model"] is False + print("Phase-1.0AX canary protocol model validation passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/validate_retroarch_phase10ay.py b/tools/validate_retroarch_phase10ay.py new file mode 100644 index 0000000..49074e3 --- /dev/null +++ b/tools/validate_retroarch_phase10ay.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate the exact Phase-1.0AY private source base selection.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +import subprocess +from pathlib import Path + + +def git(repo: Path, *args: str) -> str: + return subprocess.run(["git", *args], cwd=repo, check=True, + capture_output=True, text=True).stdout.strip() + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + parser.add_argument("--retroarch-root", type=Path, required=True) + args = parser.parse_args() + root = args.root + path = root / "manifests/retroarch/phase-1.0ay-target-source-base.json" + data = json.loads(path.read_text(encoding="utf-8")) + b = data["source_bindings"] + ax = root / "manifests/retroarch/phase-1.0ax-canary-protocol-model.json" + assert hashlib.sha256(ax.read_bytes()).hexdigest() == b["phase10ax_manifest_sha256"] + assert git(args.retroarch_root, "rev-parse", b["selected_remote_ref"]) == b["selected_retroarch_commit"] + assert git(args.retroarch_root, "merge-base", b["phase10m_source_commit"], + b["selected_retroarch_commit"]) == b["phase10m_source_commit"] + paths = { + "retroarch_c": "retroarch.c", "sdl2_gfx": "gfx/drivers/sdl2_gfx.c", + "platform_smoke_c": "frontend/drivers/platform_ps5_smoke.c", + "platform_smoke_h": "frontend/drivers/platform_ps5_smoke.h", + "diag_c": "pkg/ps5/chimera_ps5_diag.c", + "diag_h": "pkg/ps5/chimera_ps5_diag.h", + "stream_c": "pkg/ps5/chimera_ps5_diag_stream.c", + "stream_h": "pkg/ps5/chimera_ps5_diag_stream.h", + "makefile": "Makefile.ps5", + } + for prefix, source in paths.items(): + assert git(args.retroarch_root, "rev-parse", + f'{b["selected_retroarch_commit"]}:{source}') == b[f"{prefix}_blob"] + assert data["base_selection"]["existing_runner_must_remain_inactive"] is True + assert data["permitted_patch_scope"]["target_profile"] is False + assert not any(data["authorizations"].values()) + assert data["decision"]["target_artifact_build_allowed"] is False + print("Phase-1.0AY target-source base validation passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/validate_retroarch_phase10az.py b/tools/validate_retroarch_phase10az.py new file mode 100644 index 0000000..71f4832 --- /dev/null +++ b/tools/validate_retroarch_phase10az.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate the local, host-only Phase-1.0AZ RetroArch source commit.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +import subprocess +from pathlib import Path + + +def git(repo: Path, *args: str) -> str: + return subprocess.run(["git", *args], cwd=repo, check=True, + capture_output=True, text=True).stdout.strip() + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + parser.add_argument("--retroarch-root", type=Path, required=True) + args = parser.parse_args() + path = args.root / "manifests/retroarch/phase-1.0az-host-av-source.json" + data = json.loads(path.read_text(encoding="utf-8")) + binding = data["source_bindings"] + ay = args.root / "manifests/retroarch/phase-1.0ay-target-source-base.json" + assert hashlib.sha256(ay.read_bytes()).hexdigest() == binding["phase10ay_manifest_sha256"] + commit = binding["retroarch_local_commit"] + assert git(args.retroarch_root, "rev-parse", "HEAD") == commit + assert git(args.retroarch_root, "rev-parse", binding["verified_remote_ref"]) == commit + assert git(args.retroarch_root, "rev-parse", f"{commit}^") == binding["retroarch_base_commit"] + expected = { + "Makefile.ps5", "docs/ps5-phase10az-host-av-source.md", + "frontend/drivers/platform_ps5_smoke.c", + "frontend/drivers/platform_ps5_smoke.h", "pkg/ps5/chimera_ps5_diag.c", + "pkg/ps5/chimera_ps5_diag.h", "pkg/ps5/chimera_ps5_diag_stream.c", + "tests/chimera_ps5_av_diag_test.c", + } + assert set(git(args.retroarch_root, "show", "--format=", "--name-only", commit).splitlines()) == expected + blobs = { + "makefile": "Makefile.ps5", + "platform_smoke_c": "frontend/drivers/platform_ps5_smoke.c", + "platform_smoke_h": "frontend/drivers/platform_ps5_smoke.h", + "diag_c": "pkg/ps5/chimera_ps5_diag.c", + "diag_h": "pkg/ps5/chimera_ps5_diag.h", + "stream_c": "pkg/ps5/chimera_ps5_diag_stream.c", + "host_test": "tests/chimera_ps5_av_diag_test.c", + "source_doc": "docs/ps5-phase10az-host-av-source.md", + } + for key, source in blobs.items(): + assert git(args.retroarch_root, "rev-parse", f"{commit}:{source}") == binding[f"{key}_blob"] + makefile = git(args.retroarch_root, "show", f"{commit}:Makefile.ps5") + assert makefile.count("-DCHIMERA_PS5_AV_DIAG=1") == 1 + assert "PS5_PROFILE=av" not in makefile and "retroarch_ps5_av" not in makefile + assert git(args.retroarch_root, "diff", "--quiet", f'{binding["retroarch_base_commit"]}', commit, "--", "retroarch.c") == "" + assert binding["remote_push_verified"] is True + assert not any(data["authorizations"].values()) + assert data["decision"]["target_profile_reassessment_allowed"] is True + assert data["decision"]["target_artifact_build_allowed"] is False + print("Phase-1.0AZ host-only source validation passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/validate_retroarch_phase10b.py b/tools/validate_retroarch_phase10b.py new file mode 100644 index 0000000..59826c0 --- /dev/null +++ b/tools/validate_retroarch_phase10b.py @@ -0,0 +1,558 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate Phase-1.0B bounded RetroArch smoke evidence.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +from pathlib import Path +import struct +import subprocess +import sys +from typing import Any + + +PHASE = "PHASE_1_0B_FIRST_DEVICE_SMOKE_CANDIDATE_HARDENING" +STATUS = "RETROARCH_PS5_DEVICE_SMOKE_CANDIDATE_BUILT_WITH_DECLARED_RISKS" +GFX_BRANCH = "codex/chimera-gfx-phase10b-device-smoke-hardening" +RETROARCH_BRANCH = "codex/ps5-device-smoke-hardening" +RETROARCH_BASE = "ca1b45680577befc743e1c92fa40687e1b1745e7" +RETROARCH_RELEASE = "69a4f0ea1e8aaf442ae4858f2e7f2b31a1776576" +RETROARCH_TREE = "33babf9eb7699b5d571a3063ea21c3e488c159fe" +SDK_COMMIT = "d2e2e585740362976a39fdd5ccf390f199a7bc37" +PACBREW_COMMIT = "c2abcfcb60f569128abd0e8e70ad03a67bee5ea7" +SDL_COMMIT = "0baf4ac49382b537ba449901b5b6d0d189bb1fbb" +DENYLIST_SHA256 = "e9603b0e3792781ad5b511afb22ef61e3d4fd4c5a16bf928f8609193bcd97783" + +DEVICE_AUTHORIZATION_FIELDS = ( + "ps5_connection_authorized", + "device_transfer_authorized", + "device_execution_authorized", + "installation_authorized", + "lifecycle_authorized", + "autoload_authorized", + "device_write_authorized", + "automatic_retry", +) +DEVICE_ACTION_FIELDS = ( + "ps5_connected", + "device_request_performed", + "files_transferred", + "device_write_performed", + "target_execution_performed", + "transfer_package_created", + "execution_package_created", + "installation_package_created", +) +DISABLED_FEATURES = { + "networking", + "online_updater", + "dynamic_cores", + "content_browser_data", + "playlists", + "history", + "screenshots", + "recording", + "achievements", + "shaders", + "databases", + "compressed_content", + "sram", + "savestates", + "config_save", + "remap_save", + "autoconfig_save", + "log_file", + "temporary_files", + "keyboard_ime", + "haptics", + "rumble", + "lightbar", + "gnm", + "retroarch_dynamic_core_loading", + "installation", + "payload_launch", + "autoload", +} +FORBIDDEN_IMPORT_MARKERS = ( + "scenet", + "scehttp", + "scessl", + "scegnm", + "scekernelloadstartmodule", + "scepadsetvibration", + "scepadsetlightbar", + "sceime", + "scekeyboard", + "dlopen", + "dlsym", + "socket", + "connect", + "listen", + "accept", + "send", + "recv", +) +REQUIRED_SYMBOLS = { + "rarch_main", + "retroarch_main_init", + "runloop_iterate", + "retro_init", + "retro_deinit", + "retro_run", + "frontend_ctx_ps5", + "chimera_ps5_smoke_tick", + "chimera_ps5_smoke_block_write", + "__wrap_open", + "__wrap_fopen", + "__wrap_fwrite", +} +DELIVERABLES = ( + "docs/retroarch/phase-1.0b-smoke-candidate-design.md", + "docs/retroarch/phase-1.0b-runtime-and-exit-contract.md", + "docs/retroarch/phase-1.0b-persistent-write-audit.md", + "docs/retroarch/phase-1.0b-linker-and-wx-analysis.md", + "docs/retroarch/phase-1.0b-device-risk-assessment.md", + "docs/retroarch/phase-1.0b-proposed-one-shot-test.md", + "docs/approvals/phase-1.0b-device-smoke-template.md", + "manifests/retroarch/phase-1.0b-build.json", + "manifests/retroarch/phase-1.0b-artifact.json", + "manifests/retroarch/phase-1.0b-runtime-contract.json", + "tests/test_retroarch_phase10b.py", + "tools/validate_retroarch_phase10b.py", + "packaging/retroarch/phase10b/SHA256SUMS.txt", +) + + +def load_json(path: Path) -> dict[str, Any]: + with path.open("r", encoding="utf-8") as stream: + value = json.load(stream) + if not isinstance(value, dict): + raise ValueError(f"{path} is not a JSON object") + return value + + +def sha256_file(path: Path) -> str: + digest = hashlib.sha256() + with path.open("rb") as stream: + for chunk in iter(lambda: stream.read(1024 * 1024), b""): + digest.update(chunk) + return digest.hexdigest() + + +def profile_is_closed(profile: dict[str, Any]) -> bool: + return ( + DISABLED_FEATURES.issubset(set(profile.get("disabled_features", []))) + and profile.get("persistent_writes_allowed") is False + and profile.get("content_required") is False + and profile.get("config_required") is False + and profile.get("networking") is False + and profile.get("autoload") is False + and profile.get("automatic_retry") is False + ) + + +def route_is_read_only(route: dict[str, Any]) -> bool: + return ( + route.get("write") is False + and route.get("create") is False + and route.get("append") is False + and route.get("truncate") is False + and route.get("rename") is False + and route.get("unlink") is False + and route.get("mkdir") is False + and route.get("retry") is False + ) + + +def program_headers_are_wx_closed(headers: list[dict[str, Any]]) -> bool: + loads = [header for header in headers if header.get("type") == "LOAD"] + return ( + len(loads) >= 3 + and any("E" in str(item.get("flags", "")) for item in loads) + and all( + not ( + "W" in str(item.get("flags", "")) + and "E" in str(item.get("flags", "")) + ) + for item in loads + ) + ) + + +def imports_are_closed(imports: list[str]) -> bool: + lowered = [item.lower() for item in imports] + return not any( + marker in item for marker in FORBIDDEN_IMPORT_MARKERS for item in lowered + ) + + +def parse_elf_program_headers(path: Path) -> list[dict[str, Any]]: + with path.open("rb") as stream: + header = stream.read(64) + if len(header) != 64 or header[:5] != b"\x7fELF\x02": + raise ValueError(f"{path} is not ELF64") + byte_order = "<" if header[5] == 1 else ">" + phoff = struct.unpack_from(f"{byte_order}Q", header, 32)[0] + phentsize = struct.unpack_from(f"{byte_order}H", header, 54)[0] + phnum = struct.unpack_from(f"{byte_order}H", header, 56)[0] + if phentsize < 56 or phnum > 64: + raise ValueError("invalid ELF program-header table") + result: list[dict[str, Any]] = [] + stream.seek(phoff) + for _ in range(phnum): + raw = stream.read(phentsize) + if len(raw) != phentsize: + raise ValueError("truncated ELF program-header table") + kind, flags = struct.unpack_from(f"{byte_order}II", raw) + names = {1: "LOAD", 2: "DYNAMIC"} + text = ( + ("R" if flags & 4 else "") + + ("W" if flags & 2 else "") + + ("E" if flags & 1 else "") + ) + result.append( + { + "type": names.get(kind, f"0x{kind:x}"), + "flags": text, + "offset": struct.unpack_from(f"{byte_order}Q", raw, 8)[0], + "virtual_address": struct.unpack_from( + f"{byte_order}Q", raw, 16 + )[0], + "file_size": struct.unpack_from(f"{byte_order}Q", raw, 32)[0], + "memory_size": struct.unpack_from( + f"{byte_order}Q", raw, 40 + )[0], + "alignment": struct.unpack_from(f"{byte_order}Q", raw, 48)[0], + } + ) + return result + + +def parse_elf_sections(path: Path) -> tuple[bytes, list[tuple[int, ...]]]: + data = path.read_bytes() + if len(data) < 64 or data[:6] != b"\x7fELF\x02\x01": + raise ValueError(f"{path} is not little-endian ELF64") + section_offset = struct.unpack_from(" 4096: + raise ValueError("invalid ELF section-header table") + sections: list[tuple[int, ...]] = [] + for index in range(section_count): + offset = section_offset + index * section_size + if offset + 64 > len(data): + raise ValueError("truncated ELF section-header table") + sections.append(struct.unpack_from(" list[str]: + data, sections = parse_elf_sections(path) + undefined: set[str] = set() + for section in sections: + if section[1] != 11: # SHT_DYNSYM + continue + symbol_offset, symbol_size = section[4], section[5] + string_index, symbol_entry_size = section[6], section[9] + if string_index >= len(sections) or symbol_entry_size < 24: + raise ValueError("invalid ELF dynamic-symbol table") + strings_section = sections[string_index] + strings = data[ + strings_section[4] : strings_section[4] + strings_section[5] + ] + for offset in range( + symbol_offset, symbol_offset + symbol_size, symbol_entry_size + ): + if offset + 24 > len(data): + raise ValueError("truncated ELF dynamic-symbol table") + name_offset, _, _, section_index, _, _ = struct.unpack_from( + "= len(strings): + continue + name_end = strings.find(b"\0", name_offset) + if name_end < 0: + raise ValueError("unterminated ELF dynamic-symbol name") + name = strings[name_offset:name_end].decode("utf-8") + if name: + undefined.add(name) + return sorted(undefined) + + +def parse_elf_needed(path: Path) -> list[str]: + data, sections = parse_elf_sections(path) + needed: list[str] = [] + for section in sections: + if section[1] != 6: # SHT_DYNAMIC + continue + dynamic_offset, dynamic_size = section[4], section[5] + string_index, dynamic_entry_size = section[6], section[9] + if string_index >= len(sections) or dynamic_entry_size < 16: + raise ValueError("invalid ELF dynamic table") + strings_section = sections[string_index] + strings = data[ + strings_section[4] : strings_section[4] + strings_section[5] + ] + for offset in range( + dynamic_offset, dynamic_offset + dynamic_size, dynamic_entry_size + ): + if offset + 16 > len(data): + raise ValueError("truncated ELF dynamic table") + tag, value = struct.unpack_from("= len(strings): + raise ValueError("invalid ELF DT_NEEDED string offset") + name_end = strings.find(b"\0", value) + if name_end < 0: + raise ValueError("unterminated ELF DT_NEEDED name") + needed.append(strings[value:name_end].decode("utf-8")) + return needed + + +def parse_elf_relocations(path: Path) -> dict[str, Any]: + data, sections = parse_elf_sections(path) + writable_ranges = [ + ( + header["virtual_address"], + header["virtual_address"] + header["memory_size"], + ) + for header in parse_elf_program_headers(path) + if header["type"] == "LOAD" and "W" in header["flags"] + ] + total = 0 + relative = 0 + outside = 0 + relative_outside = 0 + by_type: dict[str, int] = {} + for section in sections: + if section[1] != 4: # SHT_RELA + continue + relocation_offset, relocation_size = section[4], section[5] + relocation_entry_size = section[9] + if relocation_entry_size < 24: + raise ValueError("invalid ELF relocation table") + for offset in range( + relocation_offset, + relocation_offset + relocation_size, + relocation_entry_size, + ): + if offset + 24 > len(data): + raise ValueError("truncated ELF relocation table") + target, info, _ = struct.unpack_from(" str: + result = subprocess.run( + ["git", *args], + cwd=root, + capture_output=True, + text=True, + check=False, + ) + if result.returncode: + raise RuntimeError(result.stderr.strip() or "git command failed") + return result.stdout.strip() + + +def validate(root: Path, retroarch_root: Path | None = None) -> list[str]: + errors: list[str] = [] + for relative in DELIVERABLES: + if not (root / relative).is_file(): + errors.append(f"missing deliverable: {relative}") + try: + build = load_json(root / "manifests/retroarch/phase-1.0b-build.json") + artifact = load_json(root / "manifests/retroarch/phase-1.0b-artifact.json") + runtime = load_json( + root / "manifests/retroarch/phase-1.0b-runtime-contract.json" + ) + except (OSError, ValueError, json.JSONDecodeError) as error: + return errors + [str(error)] + + if build.get("phase") != PHASE or build.get("status") != STATUS: + errors.append("phase/status mismatch") + if build.get("branch") != GFX_BRANCH: + errors.append("GFX branch mismatch") + for field in DEVICE_AUTHORIZATION_FIELDS: + if build.get(field) is not False: + errors.append(f"{field} must remain false") + for field in DEVICE_ACTION_FIELDS: + if build.get(field) is not False: + errors.append(f"{field} must remain false") + + source = build.get("sources", {}) + expected_sources = { + "retroarch_release_commit": RETROARCH_RELEASE, + "retroarch_release_tree": RETROARCH_TREE, + "retroarch_fork_base_commit": RETROARCH_BASE, + "ps5_payload_sdk_commit": SDK_COMMIT, + "pacbrew_commit": PACBREW_COMMIT, + "sdl_commit": SDL_COMMIT, + } + for field, expected in expected_sources.items(): + if source.get(field) != expected: + errors.append(f"source mismatch: {field}") + if ( + build.get("chimera_retroarch", {}).get("branch") != RETROARCH_BRANCH + or len(build.get("chimera_retroarch", {}).get("source_commit", "")) != 40 + ): + errors.append("RetroArch fork identity is incomplete") + if not profile_is_closed(build.get("profile", {})): + errors.append("smoke profile is not closed") + + record = artifact.get("artifact", {}) + if record.get("name") != "retroarch_ps5_software_smoke.elf": + errors.append("artifact label mismatch") + if record.get("size", 0) <= 0 or len(record.get("sha256", "")) != 64: + errors.append("artifact identity is incomplete") + if len(record.get("linker_map_sha256", "")) != 64: + errors.append("linker-map identity is incomplete") + hashes = record.get("clean_build_sha256", []) + if len(hashes) != 2 or len(set(hashes)) != 1 or hashes[0] != record.get("sha256"): + errors.append("two clean builds are not byte-identical") + if record.get("selected_exit_method") != "PROCESS__EXIT_AFTER_TEARDOWN": + errors.append("exit method mismatch") + if record.get("entrypoint") is None: + errors.append("entrypoint missing") + if not program_headers_are_wx_closed(record.get("program_headers", [])): + errors.append("program headers are not W^X-closed") + if not imports_are_closed(record.get("imports", [])): + errors.append("forbidden import present") + if not REQUIRED_SYMBOLS.issubset(set(record.get("defined_symbols", []))): + errors.append("required real/smoke symbols are incomplete") + if record.get("relative_relocations_outside_rw_load") != 0: + errors.append("relative relocation target outside RW load") + for field in ( + "persistent_writes_allowed", + "content_required", + "config_required", + "networking", + "autoload", + "automatic_retry", + "installation_eligible", + "device_write_eligible", + "transfer_eligible", + "execution_eligible", + "execution_authorized", + ): + if record.get(field) is not False: + errors.append(f"artifact {field} must be false") + + if runtime.get("runtime_limit_ms") != 60000: + errors.append("runtime limit mismatch") + if runtime.get("frame_limit") != 3600: + errors.append("frame limit mismatch") + if runtime.get("shutdown_hold_ms") != 2000: + errors.append("shutdown hold mismatch") + if runtime.get("shutdown_request_max") != 1: + errors.append("shutdown request is not single-shot") + if runtime.get("exit_reachable_call_count") != 1: + errors.append("exit is not exactly once reachable") + if runtime.get("audio_submit_bounded") is not False: + errors.append("blocking AudioOut risk was hidden") + if not all(route_is_read_only(route) for route in runtime.get("write_routes", [])): + errors.append("write route is not fail-closed") + + denylist = root / "manifests/artifact-denylist.json" + if not denylist.is_file() or sha256_file(denylist) != DENYLIST_SHA256: + errors.append("permanent denylist changed") + for relative in git(root, "ls-files").splitlines(): + if relative.lower().endswith((".elf", ".self", ".sprx", ".pkg")): + errors.append(f"tracked target artifact: {relative}") + + approval = (root / "docs/approvals/phase-1.0b-device-smoke-template.md").read_text( + encoding="utf-8" + ) + for value in ( + "authorized: false", + "transfer_authorized: false", + "execution_authorized: false", + "installation_authorized: false", + "automatic_retry: false", + "autoload_authorized: false", + ): + if value not in approval: + errors.append(f"approval template missing {value}") + + if retroarch_root is not None: + path = retroarch_root / record.get("local_relative_path", "") + map_path = retroarch_root / record.get("linker_map_relative_path", "") + if not path.is_file(): + errors.append("local smoke ELF missing") + elif path.stat().st_size != record.get("size") or sha256_file(path) != record.get( + "sha256" + ): + errors.append("local smoke ELF identity mismatch") + else: + try: + actual_headers = parse_elf_program_headers(path) + if not program_headers_are_wx_closed(actual_headers): + errors.append("local smoke ELF has a W+X load segment") + if actual_headers != record.get("program_headers"): + errors.append("local program headers differ from manifest") + actual_needed = parse_elf_needed(path) + if actual_needed != record.get("dt_needed"): + errors.append("local DT_NEEDED differs from manifest") + actual_undefined = parse_elf_dynamic_symbols(path) + if actual_undefined != record.get("undefined_symbols"): + errors.append("local undefined symbols differ from manifest") + if not imports_are_closed(actual_undefined): + errors.append("local smoke ELF has a forbidden import") + actual_relocations = parse_elf_relocations(path) + if actual_relocations != record.get("relocation_audit"): + errors.append("local relocation audit differs from manifest") + except (OSError, ValueError, struct.error) as error: + errors.append(str(error)) + if not map_path.is_file(): + errors.append("local linker map missing") + elif sha256_file(map_path) != record.get("linker_map_sha256"): + errors.append("local linker-map identity mismatch") + if git(retroarch_root, "rev-parse", "HEAD") != build.get( + "chimera_retroarch", {} + ).get("source_commit"): + errors.append("RetroArch HEAD does not match manifest source") + return errors + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + parser.add_argument("--retroarch-root", type=Path) + args = parser.parse_args() + errors = validate( + args.root.resolve(), + args.retroarch_root.resolve() if args.retroarch_root else None, + ) + if errors: + for error in errors: + print(error, file=sys.stderr) + return 1 + print("Phase 1.0B bounded RetroArch smoke evidence validated") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/validate_retroarch_phase10ba.py b/tools/validate_retroarch_phase10ba.py new file mode 100644 index 0000000..3f4ffd0 --- /dev/null +++ b/tools/validate_retroarch_phase10ba.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate the Phase-1.0BA callsite and profile-delta audit.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +import subprocess +from pathlib import Path + + +def git(repo: Path, *args: str) -> str: + return subprocess.run(["git", *args], cwd=repo, check=True, + capture_output=True, text=True).stdout.strip() + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + parser.add_argument("--retroarch-root", type=Path, required=True) + args = parser.parse_args() + path = args.root / "manifests/retroarch/phase-1.0ba-target-profile-callsite-audit.json" + data = json.loads(path.read_text(encoding="utf-8")) + bind = data["source_bindings"] + az = args.root / "manifests/retroarch/phase-1.0az-host-av-source.json" + assert hashlib.sha256(az.read_bytes()).hexdigest() == bind["phase10az_manifest_sha256"] + commit = bind["retroarch_commit"] + paths = {"retroarch_c": "retroarch.c", + "platform_ps5_c": "frontend/drivers/platform_ps5.c", + "makefile": "Makefile.ps5"} + for key, source in paths.items(): + assert git(args.retroarch_root, "rev-parse", f"{commit}:{source}") == bind[f"{key}_blob"] + source = git(args.retroarch_root, "show", f"{commit}:retroarch.c") + sequence = "int result = rarch_main(argc, argv, NULL);" + sequence += "\n#if defined(CHIMERA_PS5_SMOKE_MODE)" + sequence += "\n chimera_ps5_smoke_set_phase(CHIMERA_SMOKE_S15_COMPLETE);\n _Exit(result);" + assert sequence in source + assert "main_exit(data);\n#endif\n\n return 0;" in source + assert not data["authorizations"]["target_build_authorized"] + assert data["decision"]["source_patch_allowed"] + assert not data["decision"]["cross_build_allowed"] + print("Phase-1.0BA callsite/profile audit validation passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/validate_retroarch_phase10bb.py b/tools/validate_retroarch_phase10bb.py new file mode 100644 index 0000000..562be75 --- /dev/null +++ b/tools/validate_retroarch_phase10bb.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate the remote-bound Phase-1.0BB source profile.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +import subprocess +from pathlib import Path + + +def git(repo: Path, *args: str) -> str: + return subprocess.run(["git", *args], cwd=repo, check=True, + capture_output=True, text=True).stdout.strip() + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + parser.add_argument("--retroarch-root", type=Path, required=True) + args = parser.parse_args() + data = json.loads((args.root / "manifests/retroarch/phase-1.0bb-source-only-launch-canary-profile.json").read_text(encoding="utf-8")) + bind = data["source_bindings"] + ba = args.root / "manifests/retroarch/phase-1.0ba-target-profile-callsite-audit.json" + assert hashlib.sha256(ba.read_bytes()).hexdigest() == bind["phase10ba_manifest_sha256"] + commit = bind["retroarch_commit"] + assert git(args.retroarch_root, "rev-parse", "HEAD") == commit + assert git(args.retroarch_root, "rev-parse", bind["remote_ref"]) == commit + assert git(args.retroarch_root, "rev-parse", f"{commit}^") == bind["retroarch_parent_commit"] + paths = {"makefile": "Makefile.ps5", "retroarch_c": "retroarch.c", + "validator": "tools/validate_ps5_phase10bb.py", + "tests": "tests/test_ps5_phase10bb.py", + "source_doc": "docs/ps5-phase10bb-launch-canary-profile.md"} + for key, source in paths.items(): + assert git(args.retroarch_root, "rev-parse", f"{commit}:{source}") == bind[f"{key}_blob"] + assert data["verification"]["launch_canary_artifact_absent"] + assert data["authorizations"]["build_prerequisite_audit_authorized"] + assert not data["authorizations"]["target_build_authorized"] + assert not data["decision"]["cross_build_allowed"] + print("Phase-1.0BB source-profile binding validation passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/validate_retroarch_phase10bd.py b/tools/validate_retroarch_phase10bd.py new file mode 100644 index 0000000..6e9fd21 --- /dev/null +++ b/tools/validate_retroarch_phase10bd.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate the remote-bound Phase-1.0BD policy commit.""" + +import argparse +import hashlib +import json +import subprocess +from pathlib import Path + + +def git(root: Path, *args: str) -> str: + return subprocess.run(["git", *args], cwd=root, check=True, + capture_output=True, text=True).stdout.strip() + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + parser.add_argument("--retroarch-root", type=Path, required=True) + args = parser.parse_args() + data = json.loads((args.root / "manifests/retroarch/phase-1.0bd-dormant-sdl-materializer-policy.json").read_text(encoding="utf-8")) + bind = data["source_bindings"] + parent = args.root / "manifests/retroarch/phase-1.0bc-cross-build-prerequisite-audit.json" + assert hashlib.sha256(parent.read_bytes()).hexdigest() == bind["phase10bc_manifest_sha256"] + commit = bind["retroarch_commit"] + assert git(args.retroarch_root, "rev-parse", "HEAD") == commit + assert git(args.retroarch_root, "rev-parse", bind["remote_ref"]) == commit + assert git(args.retroarch_root, "rev-parse", f"{commit}^") == bind["retroarch_parent_commit"] + paths = {"makefile": "Makefile.ps5", + "policy": "tools/phase10bd_sdl_materializer_policy.py", + "validator": "tools/validate_ps5_phase10bd.py", + "tests": "tests/test_ps5_phase10bd_materializer_policy.py", + "source_doc": "docs/ps5-phase10bd-sdl-materializer-policy.md"} + for key, source in paths.items(): + assert git(args.retroarch_root, "rev-parse", f"{commit}:{source}") == bind[f"{key}_blob"] + assert data["authorizations"]["injected_adapter_source_authorized"] + assert not data["authorizations"]["live_adapter_authorized"] + assert not data["decision"]["materialization_allowed"] + print("Phase-1.0BD dormant policy binding validation passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/validate_retroarch_phase10d.py b/tools/validate_retroarch_phase10d.py new file mode 100644 index 0000000..627cd16 --- /dev/null +++ b/tools/validate_retroarch_phase10d.py @@ -0,0 +1,326 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate the offline Phase-1.0D diagnostic ladder evidence.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +from pathlib import Path +import subprocess +import sys +from typing import Any + + +PHASE = "PHASE_1_0D_LOADER_TO_ENTRY_DIAGNOSIS" +STATUS = "RETROARCH_PS5_ENTRY_DIAGNOSTIC_LADDER_BUILT_OFFLINE" +RETROARCH_BRANCH = "codex/ps5-loader-entry-diagnosis" +RETROARCH_COMMIT = "69b65858ffaee826d70f5c0df61013cd1b0e2048" +DENYLIST_SHA256 = "e9603b0e3792781ad5b511afb22ef61e3d4fd4c5a16bf928f8609193bcd97783" +CANARY_IMPORTS = { + "_Exit", + "memset", + "nanosleep", + "sceKernelSendNotificationRequest", +} +CANARY_NEEDED = {"libSceLibcInternal.sprx", "libkernel_web.sprx"} +REAL_MODEL_ARTIFACTS = { + "retroarch_ps5_software_smoke.elf", + "chimera_ps5_crt_entry_canary.elf", + "retroarch_ps5_early_diag.elf", + "chimera-gfx-lifecycle-probe.elf", +} +AUTHORIZATION_FIELDS = ( + "ps5_connection_authorized", + "device_transfer_authorized", + "device_execution_authorized", + "installation_authorized", + "autoload_authorized", + "device_write_authorized", + "automatic_retry", +) +ACTION_FIELDS = ( + "ps5_connected", + "device_request_performed", + "files_transferred", + "device_write_performed", + "target_execution_performed", +) +DELIVERABLES = ( + "docs/retroarch/phase-1.0d-loader-to-entry-analysis.md", + "docs/retroarch/phase-1.0d-crt-entry-canary.md", + "docs/retroarch/phase-1.0d-early-diagnostic-design.md", + "docs/retroarch/phase-1.0d-startup-import-closure.md", + "docs/retroarch/phase-1.0d-loader-static-model.md", + "docs/retroarch/phase-1.0d-next-device-test-ladder.md", + "manifests/retroarch/phase-1.0d-canary-artifact.json", + "manifests/retroarch/phase-1.0d-early-diag-artifact.json", + "manifests/retroarch/phase-1.0d-loader-model-results.json", + "tools/validate_retroarch_phase10d.py", + "tests/test_retroarch_phase10d.py", + "packaging/retroarch/phase10d/SHA256SUMS.txt", +) + + +def load_json(path: Path) -> dict[str, Any]: + value = json.loads(path.read_text(encoding="utf-8")) + if not isinstance(value, dict): + raise ValueError(f"{path} is not a JSON object") + return value + + +def sha256_file(path: Path) -> str: + digest = hashlib.sha256() + with path.open("rb") as stream: + for chunk in iter(lambda: stream.read(1024 * 1024), b""): + digest.update(chunk) + return digest.hexdigest() + + +def all_false(record: dict[str, Any], fields: tuple[str, ...]) -> bool: + return all(record.get(field) is False for field in fields) + + +def reproducible_artifact(record: dict[str, Any]) -> bool: + artifact = record.get("artifact", {}) + elf_hashes = artifact.get("clean_build_sha256", []) + map_hashes = artifact.get("clean_map_sha256", []) + return ( + artifact.get("size", 0) > 0 + and len(artifact.get("sha256", "")) == 64 + and len(elf_hashes) == 2 + and len(set(elf_hashes)) == 1 + and elf_hashes[0] == artifact.get("sha256") + and len(map_hashes) == 2 + and len(set(map_hashes)) == 1 + and map_hashes[0] == artifact.get("linker_map_sha256") + ) + + +def wx_closed(headers: list[dict[str, Any]]) -> bool: + loads = [item for item in headers if item.get("type") == "LOAD"] + return ( + len(loads) == 3 + and any("E" in str(item.get("flags", "")) for item in loads) + and all( + not ( + "W" in str(item.get("flags", "")) + and "E" in str(item.get("flags", "")) + ) + for item in loads + ) + ) + + +def canary_is_minimal(record: dict[str, Any]) -> bool: + behavior = record.get("behavior", {}) + elf = record.get("elf", {}) + return ( + set(elf.get("undefined_symbols", [])) == CANARY_IMPORTS + and set(elf.get("dt_needed", [])) == CANARY_NEEDED + and record.get("notification_abi", {}).get("maximum_attempts") == 1 + and record.get("notification_abi", {}).get("retry") is False + and behavior.get("sleep_attempts") == 1 + and behavior.get("interrupted_sleep_retry") is False + and all( + behavior.get(field) is False + for field in ( + "retroarch", "sdl", "videoout", "pad", "audioout", + "filesystem", "networking", "threads", "autoload", "installation", + ) + ) + ) + + +def early_ladder_is_closed(record: dict[str, Any]) -> bool: + diagnostic = record.get("diagnostic", {}) + frame = record.get("first_frame", {}) + policy = record.get("profile_policy", {}) + return ( + diagnostic.get("stages") == [f"D{index:02d}" for index in range(13)] + and diagnostic.get("notification_maximum_attempts_per_stage") == 1 + and diagnostic.get("notification_retry") is False + and diagnostic.get("notification_failure_blocks_primary_path") is False + and frame.get("background") == "MAGENTA" + and frame.get("fixed_rectangle") == "WHITE" + and frame.get("embedded_pattern") == "BLACK" + and frame.get("submit_attempts") == 1 + and frame.get("retry") is False + and frame.get("second_buffer_initialization") is False + and policy.get("write_firewall") is True + and all( + policy.get(field) is False + for field in ( + "filesystem_writes_allowed", "networking", "dynamic_cores", + "gnm", "autoload", "installation", "payload_launch", + "automatic_retry", + ) + ) + ) + + +def model_result_set_is_complete(record: dict[str, Any]) -> bool: + artifacts = record.get("real_artifacts", []) + names = {item.get("name") for item in artifacts} + return ( + names == REAL_MODEL_ARTIFACTS + and all( + item.get("classification") + in { + "ACCEPTED_BY_STATIC_MODEL", + "REJECTED_BY_STATIC_MODEL", + "MODEL_INCOMPLETE", + } + for item in artifacts + ) + and all(len(item.get("sha256", "")) == 64 for item in artifacts) + and record.get("model", {}).get("hardware_evidence") is False + ) + + +def sender_trace_is_bounded(trace: dict[str, Any]) -> bool: + return ( + trace.get("connections") == 1 + and trace.get("sendall_calls") == 1 + and trace.get("hash_before_connect") is True + and trace.get("shutdown_called") is False + and trace.get("response_read") is False + and trace.get("retry") is False + and trace.get("reconnect") is False + and trace.get("probe") is False + and "REMOTE_EXECUTION" in trace.get("cannot_prove", []) + ) + + +def git(root: Path, *args: str) -> str: + result = subprocess.run( + ["git", *args], + cwd=root, + capture_output=True, + text=True, + check=False, + ) + if result.returncode: + raise RuntimeError(result.stderr.strip() or "git failed") + return result.stdout.strip() + + +def validate(root: Path, retroarch_root: Path | None = None) -> list[str]: + errors: list[str] = [] + for relative in DELIVERABLES: + if not (root / relative).is_file(): + errors.append(f"missing deliverable: {relative}") + try: + canary = load_json( + root / "manifests/retroarch/phase-1.0d-canary-artifact.json" + ) + early = load_json( + root / "manifests/retroarch/phase-1.0d-early-diag-artifact.json" + ) + model = load_json( + root / "manifests/retroarch/phase-1.0d-loader-model-results.json" + ) + except (OSError, ValueError, json.JSONDecodeError) as error: + return errors + [str(error)] + + for label, record in (("canary", canary), ("early", early), ("model", model)): + if record.get("phase") != PHASE or record.get("status") != STATUS: + errors.append(f"{label} phase/status mismatch") + if not all_false(record.get("authorizations", {}), AUTHORIZATION_FIELDS): + errors.append(f"{label} authorization must remain false") + if not all_false(record.get("actions", {}), ACTION_FIELDS): + errors.append(f"{label} device action must remain false") + + for label, record in (("canary", canary), ("early", early)): + artifact = record.get("artifact", {}) + elf = record.get("elf", {}) + if not reproducible_artifact(record): + errors.append(f"{label} is not reproducible") + if artifact.get("execution_eligible") is not False: + errors.append(f"{label} execution eligibility must be false") + if elf.get("rwx_load_segment_count") != 0: + errors.append(f"{label} RWX count is not zero") + if not wx_closed(elf.get("program_headers", [])): + errors.append(f"{label} headers are not W^X closed") + if elf.get("init_array_size") != 0 or elf.get("fini_array_size") != 0: + errors.append(f"{label} constructor arrays are not empty") + if elf.get("tls") is not False: + errors.append(f"{label} TLS must be absent") + if not canary_is_minimal(canary): + errors.append("canary closure is not minimal") + if not early_ladder_is_closed(early): + errors.append("early diagnostic ladder is not closed") + if not model_result_set_is_complete(model): + errors.append("loader model real-artifact set is incomplete") + if not sender_trace_is_bounded(model.get("sender_trace_contract", {})): + errors.append("sender trace contract is not bounded") + + negative = model.get("negative_host_cases", []) + if len(negative) != 5 or { + item.get("classification") for item in negative + } != { + "ACCEPTED_BY_STATIC_MODEL", + "REJECTED_BY_STATIC_MODEL", + "MODEL_INCOMPLETE", + }: + errors.append("negative loader cases are incomplete") + + denylist = root / "manifests/artifact-denylist.json" + if not denylist.is_file() or sha256_file(denylist) != DENYLIST_SHA256: + errors.append("permanent denylist changed") + tracked = git(root, "ls-files").splitlines() + for relative in tracked: + if relative.lower().endswith((".elf", ".self", ".sprx", ".pkg")): + errors.append(f"tracked target artifact: {relative}") + forbidden_address = "192.168.10." + "105" + for relative in tracked: + path = root / relative + try: + text = path.read_text(encoding="utf-8") + except (OSError, UnicodeDecodeError): + continue + if forbidden_address in text: + errors.append(f"tracked device address in {relative}") + + if retroarch_root is not None: + if git(retroarch_root, "rev-parse", "HEAD") != RETROARCH_COMMIT: + errors.append("chimera-retroarch HEAD mismatch") + if git(retroarch_root, "branch", "--show-current") != RETROARCH_BRANCH: + errors.append("chimera-retroarch branch mismatch") + for label, record in (("canary", canary), ("early", early)): + artifact = record["artifact"] + elf_path = retroarch_root / artifact["local_relative_path"] + map_path = retroarch_root / artifact["linker_map_relative_path"] + if not elf_path.is_file(): + errors.append(f"{label} local ELF missing") + elif ( + elf_path.stat().st_size != artifact["size"] + or sha256_file(elf_path) != artifact["sha256"] + ): + errors.append(f"{label} local ELF identity mismatch") + if not map_path.is_file(): + errors.append(f"{label} local map missing") + elif sha256_file(map_path) != artifact["linker_map_sha256"]: + errors.append(f"{label} local map identity mismatch") + return errors + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + parser.add_argument("--retroarch-root", type=Path) + args = parser.parse_args() + errors = validate( + args.root.resolve(), + args.retroarch_root.resolve() if args.retroarch_root else None, + ) + if errors: + print("\n".join(errors), file=sys.stderr) + return 1 + print("Phase 1.0D offline diagnostic ladder evidence validated") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/validate_retroarch_phase10dc.py b/tools/validate_retroarch_phase10dc.py new file mode 100644 index 0000000..0afa6e3 --- /dev/null +++ b/tools/validate_retroarch_phase10dc.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate Phase-1.0DC identities and inactive policy.""" +from __future__ import annotations +import argparse,ast,hashlib,json +from pathlib import Path + +SOURCE=(6096,"b8e965f343a0c3df5cf7026b68f547a81dad08d4b2e0934e4f9721de8b99010f") +TESTS=(2630,"ecef8f126e2245d707a776b4fd02aad13553e6e79515287e1002d2855a8db2d2") +NETWORK={"socket","select","selectors","subprocess","urllib","http","requests"} +def exact(path,identity): + data=path.read_bytes();return (len(data),hashlib.sha256(data).hexdigest())==identity +def errors(record,root=None): + out=[];activation=record.get("activation",{}) + if record.get("status")!="INACTIVE_DUAL_ARTIFACT_BIGAPP_GATE_COMPLETE_TARGET_IMPLEMENTATION_BLOCKED":out.append("status") + if activation.get("active") is not False or any(value is not None for key,value in activation.items() if key!="active"):out.append("activation") + if any(record.get("authorizations",{}).values()):out.append("authorization") + if any(record.get("implementation_boundary",{}).values()):out.append("implementation") + c=record.get("candidate_contract",{}) + if c.get("exact_payload_sha256")!="8dadce9d9faaef21ea129a3d216c768eea9a3ca9bf8ecb8d852e376b58a9bf95" or c.get("fixed_existing_title")!="PPSA01659" or c.get("sole_cleanup_terminal")!="D14":out.append("candidate") + if not all(record.get("forbidden_effects",{}).values()):out.append("forbidden") + if record.get("decision")!={"host_gate_contract_complete":True,"candidate_can_currently_pass":False,"target_implementation_allowed":False,"device_action_allowed":False,"next_step":"OFFLINE_MINIMAL_LAUNCHER_SOURCE_PREREQUISITE_CLOSURE"}:out.append("decision") + if root: + source=root/"tools/phase10dc_bigapp_gate_contract.py";tests=root/"tests/test_phase10dc_bigapp_gate_contract.py" + if not exact(source,SOURCE):out.append("source identity") + if not exact(tests,TESTS):out.append("test identity") + tree=ast.parse(source.read_text());imports={a.name.split('.')[0] for n in ast.walk(tree) if isinstance(n,ast.Import) for a in n.names}|{(n.module or '').split('.')[0] for n in ast.walk(tree) if isinstance(n,ast.ImportFrom)} + if imports&NETWORK:out.append("network import") + return out +def main(): + p=argparse.ArgumentParser();p.add_argument("--root",type=Path,required=True);root=p.parse_args().root.resolve();record=json.loads((root/"manifests/retroarch/phase-1.0dc-inactive-bigapp-comparison-gate.json").read_text());found=errors(record,root) + for item in found:print("ERROR:",item) + if found:return 1 + print("Phase-1.0DC inactive BigApp gate validation passed");return 0 +if __name__=="__main__":raise SystemExit(main()) diff --git a/tools/validate_retroarch_phase10df.py b/tools/validate_retroarch_phase10df.py new file mode 100644 index 0000000..0cd1c9a --- /dev/null +++ b/tools/validate_retroarch_phase10df.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +from __future__ import annotations +import argparse,ast,hashlib,json +from pathlib import Path +SOURCE=(5298,"2679f03617241abf37ca547141a2a51e3f89b75fec957a1da4f13b904d931f2d");TESTS=(2496,"3760fa60e66857b24bf507f196060ec9e4d8b96ebce3843ab02fc185c0f8c0b8");NETWORK={"socket","select","selectors","subprocess","urllib","http","requests"} +def exact(path,identity):data=path.read_bytes();return (len(data),hashlib.sha256(data).hexdigest())==identity +def errors(record,root=None): + out=[];a=record.get("activation",{}) + if record.get("status")!="HOST_OBSERVER_CONTRACT_COMPLETE_LIVE_REQUEST_UNFORMABLE":out.append("status") + if a.get("active") is not False or any(v is not None for k,v in a.items() if k!="active"):out.append("activation") + if any(record.get("authorizations",{}).values()) or any(record.get("implementation_boundary",{}).values()):out.append("capability") + evidence=record.get("current_evidence",{}) + if any(evidence.values()):out.append("evidence promoted") + contract=record.get("observer_contract",{}) + if contract.get("allowlisted_methods") != ["SOURCE_BOUND_QUERY","EXACT_PATH_METADATA"] or contract.get("error_is_absence") is not False or contract.get("maximum_result_bytes")!=4096:out.append("contract") + if not all(contract.get(k) is True for k in ("shell_forbidden","directory_enumeration_forbidden","title_launch_forbidden","app_termination_forbidden","device_write_forbidden","retry_forbidden","reconnect_forbidden")):out.append("forbidden") + if record.get("decision")!={"host_observer_contract_complete":True,"live_observer_implementation_allowed":False,"live_observation_allowed":False,"device_action_allowed":False,"next_step":"OFFLINE_SOURCE_AUDIT_FOR_ONE_NONMUTATING_TITLE_PRESENCE_PRIMITIVE"}:out.append("decision") + if root: + source=root/"tools/phase10df_title_observer_contract.py";tests=root/"tests/test_phase10df_title_observer_contract.py" + if not exact(source,SOURCE):out.append("source identity") + if not exact(tests,TESTS):out.append("test identity") + tree=ast.parse(source.read_text());imports={x.name.split('.')[0] for n in ast.walk(tree) if isinstance(n,ast.Import) for x in n.names}|{(n.module or '').split('.')[0] for n in ast.walk(tree) if isinstance(n,ast.ImportFrom)} + if imports&NETWORK:out.append("network import") + return out +def main(): + p=argparse.ArgumentParser();p.add_argument("--root",type=Path,required=True);root=p.parse_args().root.resolve();record=json.loads((root/"manifests/retroarch/phase-1.0df-inactive-title-presence-observer.json").read_text());found=errors(record,root) + for item in found:print("ERROR:",item) + if found:return 1 + print("Phase-1.0DF inactive title observer validation passed");return 0 +if __name__=="__main__":raise SystemExit(main()) diff --git a/tools/validate_retroarch_phase10e.py b/tools/validate_retroarch_phase10e.py new file mode 100644 index 0000000..070da27 --- /dev/null +++ b/tools/validate_retroarch_phase10e.py @@ -0,0 +1,382 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate the consumed Phase-1.0E inherited result-channel evidence.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +from pathlib import Path +import subprocess +import sys +from typing import Any + + +PHASE = "PHASE_1_0E_INHERITED_RESULT_CHANNEL" +STATUS = "ONE_SHOT_DEVICE_TEST_COMPLETED_INCOMPLETE_BEFORE_D03" +RETROARCH_BRANCH = "codex/ps5-inherited-result-channel" +RETROARCH_COMMIT = "aed1a6e014d56ed25456b8b095955c7d41f7025d" +OFFLINE_FOLLOWUP_COMMIT = "f1391c3e6717ff4e2b869007fb9627c9edde52e4" +ARTIFACT_SOURCE_COMMIT = "b9fc037304a14199f35f8229edac26fa5c840509" +LOADER_COMMIT = "197623058f509eddde18868dafcb92fdcac66464" +DENYLIST_SHA256 = "e9603b0e3792781ad5b511afb22ef61e3d4fd4c5a16bf928f8609193bcd97783" +ARTIFACT_SHA256 = "1049c78099a60b472a3fb0e2999e3393b6ad76337a28532a7e53872e7772dedf" +ARTIFACT_SIZE = 1844880 +MAP_SHA256 = "ae9739f6f578953bc8dc562bb55967ba587912d161b2d6787438450addec3b44" +TRACE_SHA256 = "4ff27a0eac48283cdc4c7ff964226def2689e808e3adea6594d0e77674a676f0" +TRACE_SIZE = 1795 +FORBIDDEN_TARGET_IMPORTS = {"socket", "connect", "bind", "listen", "accept", "recv"} +AUTHORIZATION_FIELDS = ( + "ps5_connection_authorized", + "device_transfer_authorized", + "device_execution_authorized", + "result_receive_authorized", + "installation_authorized", + "autoload_authorized", + "device_write_authorized", + "automatic_retry", +) +PERFORMED_ACTION_FIELDS = ( + "ps5_connected", + "device_request_performed", + "files_transferred", + "target_execution_performed", + "result_received_from_device", +) +FORBIDDEN_ACTION_FIELDS = ( + "device_write_performed", + "installation_performed", + "autoload_performed", +) +DELIVERABLES = ( + "docs/retroarch/phase-1.0e-device-observations.md", + "docs/retroarch/phase-1.0e-inherited-result-channel.md", + "docs/retroarch/phase-1.0e-next-device-test.md", + "docs/approvals/phase-1.0e-one-shot-result-test.md", + "manifests/retroarch/phase-1.0e-result-channel.json", + "tools/validate_retroarch_phase10e.py", + "tests/test_retroarch_phase10e.py", + "packaging/retroarch/phase10e/SHA256SUMS.txt", +) + + +def load_json(path: Path) -> dict[str, Any]: + value = json.loads(path.read_text(encoding="utf-8")) + if not isinstance(value, dict): + raise ValueError(f"{path} is not a JSON object") + return value + + +def sha256_file(path: Path) -> str: + digest = hashlib.sha256() + with path.open("rb") as stream: + for chunk in iter(lambda: stream.read(1024 * 1024), b""): + digest.update(chunk) + return digest.hexdigest() + + +def git(root: Path, *args: str) -> str: + result = subprocess.run( + ["git", *args], cwd=root, capture_output=True, text=True, check=False + ) + if result.returncode: + raise RuntimeError(result.stderr.strip() or "git failed") + return result.stdout.strip() + + +def all_false(record: dict[str, Any], fields: tuple[str, ...]) -> bool: + return all(record.get(field) is False for field in fields) + + +def reproducible_artifact(record: dict[str, Any]) -> bool: + hashes = record.get("clean_build_sha256", []) + map_hashes = record.get("clean_map_sha256", []) + return ( + record.get("size") == ARTIFACT_SIZE + and record.get("sha256") == ARTIFACT_SHA256 + and hashes == [ARTIFACT_SHA256, ARTIFACT_SHA256] + and record.get("linker_map_sha256") == MAP_SHA256 + and map_hashes == [MAP_SHA256, MAP_SHA256] + and record.get("execution_eligible") is False + and record.get("transfer_eligible") is False + and record.get("installation_eligible") is False + and record.get("device_action_performed") is True + ) + + +def wx_closed(headers: list[dict[str, Any]]) -> bool: + loads = [item for item in headers if item.get("type") == "LOAD"] + return len(loads) == 3 and all( + not ({"W", "E"} <= set(str(item.get("flags", "")))) for item in loads + ) + + +def transport_is_bounded(record: dict[str, Any]) -> bool: + loader = record.get("loader_transport", {}) + protocol = record.get("result_protocol", {}) + host = record.get("host_contract", {}) + return ( + loader.get("raw_elf_exact_length_read") is True + and loader.get("payload_stdout_inherits_connection") is True + and loader.get("controlled_route_supported") is False + and loader.get("new_loader_change") is False + and loader.get("new_target_socket") is False + and loader.get("new_target_connection") is False + and protocol.get("frame_size") == 64 + and protocol.get("target_write_attempts_per_stage") == 1 + and protocol.get("target_write_retry") is False + and protocol.get("short_write_retry") is False + and protocol.get("target_import_added") == "send" + and set(protocol.get("target_forbidden_imports_absent", [])) + == FORBIDDEN_TARGET_IMPORTS + and host.get("hash_before_socket_creation") is True + and host.get("exact_size_before_socket_creation") is True + and host.get("connection_count") == 1 + and host.get("sendall_count") == 1 + and host.get("shutdown_write_count") == 1 + and host.get("receive_limit_bytes") == 65536 + and host.get("retry") is False + and host.get("reconnect") is False + and host.get("resume") is False + and host.get("trace_overwrite") is False + and host.get("partial_result_is_success") is False + ) + + +def observations_are_bounded(record: dict[str, Any]) -> bool: + prior = record.get("prior_device_observations", {}) + run_a = prior.get("run_a", {}) + run_b = prior.get("run_b", {}) + return ( + prior.get("evidence_class") == "OPERATOR_OBSERVED_ARTIFACT_BOUND" + and run_a.get("authorization_consumed") is True + and run_a.get("classification") + == "CRT_MAIN_AND_NOTIFICATION_PROVEN_ON_FW_9_60" + and run_b.get("authorization_consumed") is True + and run_b.get("classification") + == "PAYLOAD_NOTIFICATION_CODE_EXECUTED_STAGE_UNCLASSIFIED" + and run_b.get("console_remained_responsive") is True + ) + + +def device_run_is_exact(record: dict[str, Any]) -> bool: + run = record.get("device_run", {}) + frames = run.get("validated_frames", []) + return ( + run.get("run_id") == "RUN_C" + and run.get("authorization_consumed") is True + and run.get("firmware") == "9.60" + and run.get("trace_tracked") is False + and run.get("trace_size") == TRACE_SIZE + and run.get("trace_sha256") == TRACE_SHA256 + and run.get("connection_count") == 1 + and run.get("sendall_count") == 1 + and run.get("shutdown_write_count") == 1 + and run.get("retry_count") == 0 + and run.get("reconnect_count") == 0 + and run.get("close_called") is True + and run.get("remote_eof_observed") is True + and run.get("timeout_observed") is False + and run.get("parser_errors") == [] + and [frame.get("stage") for frame in frames] == ["D00", "D01", "D02"] + and [frame.get("sequence") for frame in frames] == [1, 2, 3] + and frames[2].get("raw0") == 0 + and all(frame.get("notification_result") == 0 for frame in frames) + and all(frame.get("terminal") is False for frame in frames) + and run.get("terminal_stage") is None + and run.get("last_proven_stage") == "D02" + and run.get("classification") + == "REMOTE_PAYLOAD_OUTPUT_PROVEN_INCOMPLETE_BEFORE_D03" + and "D03_SDL_INIT_BEGIN" in run.get("does_not_prove", []) + and "SAFE_EXIT_OR_LOADER_CLEANUP" in run.get("does_not_prove", []) + ) + + +def offline_followup_is_bounded(record: dict[str, Any]) -> bool: + followup = record.get("offline_followup", {}) + return ( + followup.get("repository") == "chimera-retroarch" + and followup.get("branch") == RETROARCH_BRANCH + and followup.get("commit") == OFFLINE_FOLLOWUP_COMMIT + and followup.get("classification") + == "D02_TO_D03_SOURCE_INTERVAL_BOUNDED_CAUSE_UNPROVEN" + and followup.get("bounded_raw_stream_retention") is True + and followup.get("bounded_ordinary_stdout_retention") is True + and followup.get("encoding") == "base64" + and followup.get("sha256_recorded") is True + and followup.get("maximum_pre_encoding_bytes") == 65536 + and followup.get("exclusive_trace_creation") is True + and followup.get("consumed_manifest_rejected") is True + and followup.get("retry") is False + and followup.get("reconnect") is False + and followup.get("device_action_performed") is False + and followup.get("target_code_changed") is False + and followup.get("target_artifact_created") is False + and followup.get("authorization_changed") is False + ) + + +def validate( + root: Path, + retroarch_root: Path | None = None, + loader_root: Path | None = None, +) -> list[str]: + errors: list[str] = [] + for relative in DELIVERABLES: + if not (root / relative).is_file(): + errors.append(f"missing deliverable: {relative}") + try: + record = load_json( + root / "manifests/retroarch/phase-1.0e-result-channel.json" + ) + except (OSError, ValueError, json.JSONDecodeError) as error: + return errors + [str(error)] + + if record.get("phase") != PHASE or record.get("status") != STATUS: + errors.append("phase/status mismatch") + if record.get("source_commit") != ARTIFACT_SOURCE_COMMIT: + errors.append("source commit mismatch") + if record.get("host_client_commit") != RETROARCH_COMMIT: + errors.append("host client commit mismatch") + authorizations = record.get("authorizations", {}) + if not all_false(authorizations, AUTHORIZATION_FIELDS): + errors.append("consumed Phase-1.0E authorization must be fully false") + actions = record.get("phase_actions", {}) + if not all(actions.get(field) is True for field in PERFORMED_ACTION_FIELDS): + errors.append("performed one-shot actions are missing") + if not all_false(actions, FORBIDDEN_ACTION_FIELDS): + errors.append("a forbidden device action was recorded") + if not observations_are_bounded(record): + errors.append("prior device observations are overclaimed or incomplete") + active = record.get("active_one_shot", {}) + if not ( + active.get("artifact_sha256") == ARTIFACT_SHA256 + and active.get("artifact_size") == ARTIFACT_SIZE + and active.get("firmware") == "9.60" + and active.get("connection_count") == 1 + and active.get("transfer_count") == 1 + and active.get("execution_count") == 1 + and active.get("result_receive_count") == 1 + and active.get("timeout_seconds") == 75 + and active.get("automatic_retry") is False + and active.get("reconnect") is False + and active.get("installation") is False + and active.get("autoload") is False + and active.get("device_write") is False + and active.get("consumed") is True + ): + errors.append("one-shot contract is not exact or not consumed") + if not reproducible_artifact(record.get("artifact", {})): + errors.append("artifact is not exact, reproducible and post-run ineligible") + if not device_run_is_exact(record): + errors.append("RUN C result is missing, malformed or overclaimed") + if not offline_followup_is_bounded(record): + errors.append("offline D02-to-D03 follow-up is missing or unsafe") + + elf = record.get("elf", {}) + imports = set(elf.get("undefined_symbols", [])) + if elf.get("rwx_load_segment_count") != 0 or not wx_closed( + elf.get("program_headers", []) + ): + errors.append("ELF load layout is not W^X closed") + if elf.get("init_array_size") != 0 or elf.get("fini_array_size") != 0: + errors.append("constructor arrays are not empty") + if elf.get("tls") is not False: + errors.append("TLS must be absent") + if "send" not in imports or imports & FORBIDDEN_TARGET_IMPORTS: + errors.append("target import closure is not inherited-output-only") + if elf.get("undefined_symbol_delta_from_phase10d") != ["send"]: + errors.append("Phase-1.0D import delta must be exactly send") + if not transport_is_bounded(record): + errors.append("transport or host contract is not bounded") + + effects = record.get("startup_effects", {}) + if not ( + effects.get("normal_sdk_crt") is True + and effects.get("patch_init_reachable_from_start") is True + and effects.get("kernel_copy_helpers_statically_linked") is True + and effects.get("side_effect_free") is False + ): + errors.append("SDK startup effects are hidden or misclassified") + + denylist = root / "manifests/artifact-denylist.json" + if not denylist.is_file() or sha256_file(denylist) != DENYLIST_SHA256: + errors.append("permanent denylist changed") + tracked = git(root, "ls-files").splitlines() + for relative in tracked: + if relative.lower().endswith((".elf", ".self", ".sprx", ".pkg")): + errors.append(f"tracked target artifact: {relative}") + path = root / relative + try: + text = path.read_text(encoding="utf-8") + except (OSError, UnicodeDecodeError): + continue + if "192.168.10." + "105" in text: + errors.append(f"tracked device address in {relative}") + + if retroarch_root is not None: + ancestor = subprocess.run( + ["git", "merge-base", "--is-ancestor", RETROARCH_COMMIT, "HEAD"], + cwd=retroarch_root, + capture_output=True, + check=False, + ) + if ancestor.returncode: + errors.append("tested chimera-retroarch host client commit is not an ancestor") + followup_ancestor = subprocess.run( + ["git", "merge-base", "--is-ancestor", OFFLINE_FOLLOWUP_COMMIT, "HEAD"], + cwd=retroarch_root, + capture_output=True, + check=False, + ) + if followup_ancestor.returncode: + errors.append("offline stdout-capture commit is not an ancestor") + if git(retroarch_root, "branch", "--show-current") != RETROARCH_BRANCH: + errors.append("chimera-retroarch branch mismatch") + artifact = record["artifact"] + elf_path = retroarch_root / artifact["local_relative_path"] + map_path = retroarch_root / artifact["linker_map_relative_path"] + if not elf_path.is_file() or ( + elf_path.stat().st_size != ARTIFACT_SIZE + or sha256_file(elf_path) != ARTIFACT_SHA256 + ): + errors.append("local result ELF missing or changed") + if not map_path.is_file() or sha256_file(map_path) != MAP_SHA256: + errors.append("local result linker map missing or changed") + run = record["device_run"] + trace_path = retroarch_root / run["trace_relative_path"] + if not trace_path.is_file() or ( + trace_path.stat().st_size != TRACE_SIZE + or sha256_file(trace_path) != TRACE_SHA256 + ): + errors.append("local ignored RUN C trace missing or changed") + if loader_root is not None: + if git(loader_root, "rev-parse", "HEAD") != LOADER_COMMIT: + errors.append("hardened elfldr HEAD mismatch") + if git(loader_root, "status", "--porcelain"): + errors.append("hardened elfldr tree is dirty") + return errors + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + parser.add_argument("--retroarch-root", type=Path) + parser.add_argument("--loader-root", type=Path) + args = parser.parse_args() + errors = validate( + args.root.resolve(), + args.retroarch_root.resolve() if args.retroarch_root else None, + args.loader_root.resolve() if args.loader_root else None, + ) + if errors: + for error in errors: + print(f"ERROR: {error}", file=sys.stderr) + return 1 + print("Phase-1.0E inherited result-channel validation passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/validate_retroarch_phase10f.py b/tools/validate_retroarch_phase10f.py new file mode 100644 index 0000000..ca84c0f --- /dev/null +++ b/tools/validate_retroarch_phase10f.py @@ -0,0 +1,262 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate the offline-only Phase-1.0F startup interval artifact record.""" + +from __future__ import annotations + +import argparse +import json +from pathlib import Path +import subprocess +from typing import Any + + +PHASE = "PHASE_1_0F_STARTUP_INTERVAL_DIAGNOSTIC" +STATUS = "OFFLINE_ARTIFACT_PREPARED_NO_DEVICE_AUTHORIZATION" +SOURCE_COMMIT = "0eaf68d6de4dc9757d85cc7ad5c714b1d151c8f8" +ARTIFACT_SHA256 = "e8bfc01c61bfb14b5814280a6e5442f1a5ad05ace5439d1c09e7e5ee00cd0055" +ARTIFACT_SIZE = 1845152 +MAP_SHA256 = "638642b750b8d5b108cf6c73215a3f1759bcb6da0b29ee0a0ade5c47e8b7b2d5" +MAP_SIZE = 637603 +DISASSEMBLY_SHA256 = "32725415b86a8b3aafbd8b7fa2d089633a3cf90935b111e3c175bdd28dde242a" +AUTHORIZATION_FIELDS = ( + "ps5_connection_authorized", + "device_transfer_authorized", + "device_execution_authorized", + "result_receive_authorized", + "installation_authorized", + "autoload_authorized", + "device_write_authorized", + "automatic_retry", +) +ACTION_FIELDS = ( + "ps5_connected", + "device_request_performed", + "files_transferred", + "device_write_performed", + "target_execution_performed", + "result_received_from_device", + "installation_performed", + "autoload_performed", +) +FORBIDDEN_IMPORTS = { + "socket", "connect", "bind", "listen", "accept", "recv", "recvfrom", + "sendto", "dlopen", "dlsym", "sceGnmSubmitCommandBuffers", + "sceGnmSubmitAndFlipCommandBuffers", +} +DELIVERABLES = ( + "docs/retroarch/phase-1.0f-startup-interval.md", + "docs/approvals/phase-1.0f-device-test-template.md", + "manifests/retroarch/phase-1.0f-startup-interval.json", + "tools/validate_retroarch_phase10f.py", + "tests/test_retroarch_phase10f.py", + "packaging/retroarch/phase10f/SHA256SUMS.txt", +) + + +def load_json(path: Path) -> dict[str, Any]: + value = json.loads(path.read_text(encoding="utf-8")) + if not isinstance(value, dict): + raise ValueError(f"{path} is not a JSON object") + return value + + +def git(root: Path, *args: str) -> str: + result = subprocess.run( + ["git", *args], cwd=root, capture_output=True, text=True, check=False + ) + if result.returncode: + raise RuntimeError(result.stderr.strip() or "git failed") + return result.stdout.strip() + + +def all_false(record: dict[str, Any], fields: tuple[str, ...]) -> bool: + return all(record.get(field) is False for field in fields) + + +def artifact_is_exact(record: dict[str, Any]) -> bool: + return ( + record.get("size") == ARTIFACT_SIZE + and record.get("sha256") == ARTIFACT_SHA256 + and record.get("clean_build_sha256") == [ARTIFACT_SHA256] * 2 + and record.get("linker_map_size") == MAP_SIZE + and record.get("linker_map_sha256") == MAP_SHA256 + and record.get("clean_map_sha256") == [MAP_SHA256] * 2 + and record.get("execution_eligible") is False + and record.get("transfer_eligible") is False + and record.get("installation_eligible") is False + and record.get("device_action_performed") is False + and record.get("tracked") is False + ) + + +def protocol_is_bounded(record: dict[str, Any]) -> bool: + return ( + record.get("magic") == "CHD10F01" + and record.get("version") == 1 + and record.get("frame_size") == 64 + and record.get("interval_stages") + == [f"I{index:02d}" for index in range(15)] + and record.get("interval_notification_calls") == 0 + and record.get("interval_notification_result_sentinel") == -2147483648 + and record.get("target_write_attempts_per_reached_stage") == 1 + and record.get("target_write_retry") is False + and record.get("short_write_retry") is False + and record.get("new_target_imports_from_phase10e") == [] + and set(record.get("forbidden_target_imports_absent", [])) + == {"socket", "connect", "bind", "listen", "accept", "recv"} + ) + + +def interval_callsites_are_complete(items: list[dict[str, Any]]) -> bool: + return ( + len(items) == 15 + and [item.get("stage") for item in items] + == [f"I{index:02d}" for index in range(15)] + and len({item.get("address") for item in items}) == 15 + and all(item.get("source") and item.get("function") for item in items) + ) + + +def elf_is_closed(record: dict[str, Any], phase10e: dict[str, Any]) -> bool: + headers = record.get("program_headers", []) + imports = record.get("undefined_symbols", []) + old_imports = phase10e.get("elf", {}).get("undefined_symbols", []) + return ( + record.get("rwx_load_segment_count") == 0 + and len(headers) == 3 + and all(not ({"W", "E"} <= set(item.get("flags", ""))) for item in headers) + and record.get("preinit_array_size") == 0 + and record.get("init_array_size") == 0 + and record.get("fini_array_size") == 0 + and record.get("tls") is False + and record.get("relocations", {}).get("total") == 1055 + and record.get("undefined_symbol_count") == len(imports) == 142 + and imports == old_imports + and not (set(imports) & FORBIDDEN_IMPORTS) + and "send" in imports + ) + + +def parser_is_offline_only(record: dict[str, Any]) -> bool: + return ( + record.get("offline_interval_parser_available") is True + and record.get("live_cli_defaults_to_consumed_phase10e_protocol") is True + and record.get("live_interval_activation_available") is False + and record.get("manifest_must_be_execution_eligible") is True + and record.get("authorization_must_be_active_and_exact") is True + and record.get("retry") is False + and record.get("reconnect") is False + and record.get("resume") is False + and record.get("trace_overwrite") is False + ) + + +def validate(root: Path, retroarch_root: Path | None = None) -> list[str]: + errors: list[str] = [] + for relative in DELIVERABLES: + if not (root / relative).is_file(): + errors.append(f"missing deliverable: {relative}") + try: + record = load_json( + root / "manifests/retroarch/phase-1.0f-startup-interval.json" + ) + phase10e = load_json( + root / "manifests/retroarch/phase-1.0e-result-channel.json" + ) + except (OSError, ValueError, json.JSONDecodeError) as error: + return errors + [str(error)] + + if record.get("phase") != PHASE or record.get("status") != STATUS: + errors.append("phase/status mismatch") + if record.get("source_commit") != SOURCE_COMMIT: + errors.append("source commit mismatch") + if not all_false(record.get("authorizations", {}), AUTHORIZATION_FIELDS): + errors.append("all Phase-1.0F authorizations must remain false") + if not all_false(record.get("phase_actions", {}), ACTION_FIELDS): + errors.append("Phase-1.0F must record no device action") + prior = record.get("prior_evidence", {}) + if not ( + prior.get("last_proven_stage") == "D02" + and prior.get("d02_raw0") == 0 + and prior.get("authorization_consumed") is True + and prior.get("authority_inherited") is False + ): + errors.append("prior RUN-C evidence is overclaimed or incomplete") + if not artifact_is_exact(record.get("artifact", {})): + errors.append("artifact identity/reproducibility mismatch") + if not protocol_is_bounded(record.get("result_protocol", {})): + errors.append("interval protocol is not bounded") + if not interval_callsites_are_complete(record.get("interval_callsites", [])): + errors.append("interval callsite matrix is incomplete") + reachability = record.get("static_reachability", {}) + if reachability.get("normalized_disassembly_sha256") != [DISASSEMBLY_SHA256] * 2: + errors.append("normalized disassembly is not reproducible") + if not elf_is_closed(record.get("elf", {}), phase10e): + errors.append("ELF/import closure mismatch") + if not parser_is_offline_only(record.get("host_parser", {})): + errors.append("host parser is not offline/fail-closed") + effects = record.get("startup_effects", {}) + if not ( + effects.get("patch_init_reachable_from_start") is True + and effects.get("interval_notification_effect") == "NONE" + and effects.get("side_effect_free") is False + ): + errors.append("startup effects are hidden or overclaimed") + tests = record.get("tests", {}) + if not ( + tests.get("double_clean_build") == "PASS_BYTE_IDENTICAL" + and tests.get("hardware_evidence_from_phase10f") is False + ): + errors.append("offline tests are missing or mislabeled as hardware evidence") + + tracked = git(root, "ls-files").splitlines() + if any(path.lower().endswith((".elf", ".self", ".sprx", ".pkg", ".map")) for path in tracked): + errors.append("target artifact or linker map is tracked") + sums = (root / "packaging/retroarch/phase10f/SHA256SUMS.txt").read_text( + encoding="utf-8" + ) + if ARTIFACT_SHA256 not in sums or MAP_SHA256 not in sums: + errors.append("hash-only record is incomplete") + + if retroarch_root is not None: + try: + git(retroarch_root, "cat-file", "-e", f"{SOURCE_COMMIT}^{{commit}}") + stream = git( + retroarch_root, + "show", + f"{SOURCE_COMMIT}:pkg/ps5/chimera_ps5_diag_stream.c", + ) + interval = git( + retroarch_root, + "show", + f"{SOURCE_COMMIT}:pkg/ps5/chimera_ps5_interval_diag.c", + ) + if ( + "CHIMERA_PS5_INTERVAL_DIAG" not in stream + or "'C', 'H', 'D', '1', '0', 'F', '0', '1'" not in stream + ): + errors.append("source commit lacks Phase-1.0F magic") + if "chimera_ps5_diag_stream_emit" not in interval or "send(" in interval: + errors.append("source commit interval wrapper is not stream-only") + except RuntimeError as error: + errors.append(str(error)) + return errors + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + parser.add_argument("--retroarch-root", type=Path) + args = parser.parse_args() + errors = validate(args.root.resolve(), args.retroarch_root) + if errors: + for error in errors: + print(f"ERROR: {error}") + return 1 + print("Phase-1.0F offline startup interval validation passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/validate_retroarch_phase10g.py b/tools/validate_retroarch_phase10g.py new file mode 100644 index 0000000..e992187 --- /dev/null +++ b/tools/validate_retroarch_phase10g.py @@ -0,0 +1,253 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate the inactive Phase-1.0G host one-shot runner boundary.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +from pathlib import Path +import subprocess +from typing import Any + + +PHASE = "PHASE_1_0G_INTERVAL_ONE_SHOT_RUNNER" +STATUS = "OFFLINE_RUNNER_PREPARED_NO_DEVICE_AUTHORIZATION" +RUNNER_COMMIT = "55e873df12f4dd099c082d4a26b225644b8e0567" +TARGET_SOURCE_COMMIT = "0eaf68d6de4dc9757d85cc7ad5c714b1d151c8f8" +ARTIFACT_NAME = "retroarch_ps5_interval_diag.elf" +ARTIFACT_SIZE = 1845152 +ARTIFACT_SHA256 = "e8bfc01c61bfb14b5814280a6e5442f1a5ad05ace5439d1c09e7e5ee00cd0055" +SOURCE_HASHES = { + "tools/ps5_diag_duplex.py": "df72af4e738ab5969b78c42cf71d376c2a95b43e26b3d445cd7be9d88cadbf19", + "tests/test_ps5_phase10e_tools.py": "1d21072d9bae6d67e38e3908737326bc5b9eceefd90a659ae3d601833f543944", + "pkg/ps5/validate_port.py": "c65373df687328243d20184ddb65e589b1f0d2e6460058062054a9dbc8e3c185", +} +AUTHORIZATION_FIELDS = ( + "ps5_connection_authorized", + "device_transfer_authorized", + "device_execution_authorized", + "result_receive_authorized", + "installation_authorized", + "autoload_authorized", + "device_write_authorized", + "automatic_retry", +) +ACTION_FIELDS = ( + "ps5_connected", + "device_request_performed", + "files_transferred", + "device_write_performed", + "target_execution_performed", + "result_received_from_device", + "installation_performed", + "autoload_performed", +) +APPROVAL_FALSE_FIELDS = ( + "authorized", + "installation_authorized", + "autoload_authorized", + "device_write_authorized", + "automatic_retry", + "reconnect_authorized", + "resume_authorized", + "automatic_reboot_authorized", +) +DELIVERABLES = ( + "docs/retroarch/phase-1.0g-one-shot-runner.md", + "docs/approvals/phase-1.0g-one-shot-runner-template.md", + "manifests/retroarch/phase-1.0g-one-shot-runner.json", + "manifests/retroarch/phase-1.0g-one-shot-approval-template.json", + "tools/validate_retroarch_phase10g.py", + "tests/test_retroarch_phase10g.py", + "packaging/retroarch/phase10g/SHA256SUMS.txt", +) + + +def load_json(path: Path) -> dict[str, Any]: + value = json.loads(path.read_text(encoding="utf-8")) + if not isinstance(value, dict): + raise ValueError(f"{path} is not a JSON object") + return value + + +def git(root: Path, *args: str) -> str: + result = subprocess.run( + ["git", *args], cwd=root, capture_output=True, text=True, check=False + ) + if result.returncode: + raise RuntimeError(result.stderr.strip() or "git failed") + return result.stdout.strip() + + +def sha256_text(value: str) -> str: + return hashlib.sha256(value.encode("utf-8")).hexdigest() + + +def all_false(record: dict[str, Any], fields: tuple[str, ...]) -> bool: + return all(record.get(field) is False for field in fields) + + +def artifact_is_inactive(record: dict[str, Any], phase10f: dict[str, Any]) -> bool: + previous = phase10f.get("artifact", {}) + return ( + record.get("name") == ARTIFACT_NAME + and record.get("size") == ARTIFACT_SIZE == previous.get("size") + and record.get("sha256") == ARTIFACT_SHA256 == previous.get("sha256") + and record.get("profile") == previous.get("profile") == "interval-diag" + and record.get("execution_eligible") is False + and record.get("transfer_eligible") is False + and record.get("installation_eligible") is False + and record.get("device_action_performed") is False + and record.get("tracked") is False + and record.get("changed_from_phase10f") is False + ) + + +def protocol_is_exact(record: dict[str, Any]) -> bool: + return ( + record.get("magic") == "CHD10F01" + and record.get("version") == 1 + and record.get("frame_size") == 64 + and record.get("interval_stages") == [f"I{index:02d}" for index in range(15)] + and record.get("d_stages") == [f"D{index:02d}" for index in range(13)] + ) + + +def runner_is_inactive_and_bounded(record: dict[str, Any]) -> bool: + return ( + record.get("implementation_available") is True + and record.get("protocol_selection") == "MANIFEST_ONLY" + and record.get("protocol_activation_authorized") is False + and record.get("run_id") is None + and record.get("tracked_target") is None + and record.get("tracked_port") is None + and record.get("active_manifest_required") is True + and record.get("local_approval_required") is True + and record.get("actual_artifact_rehashed_before_receipt") is True + and record.get("actual_artifact_rehashed_before_socket") is True + and record.get("attempt_receipt_required") is True + and record.get("attempt_receipt_exclusive_create") is True + and record.get("attempt_receipt_durable_fsync") is True + and record.get("attempt_receipt_written_before_connect") is True + and all(record.get(field) == 1 for field in ( + "maximum_connections", "maximum_transfers", "maximum_executions", + "maximum_result_receives" + )) + and record.get("receive_limit_bytes") == 65536 + and record.get("retry") is False + and record.get("reconnect") is False + and record.get("resume") is False + and record.get("trace_exclusive_create") is True + and record.get("trace_overwrite") is False + ) + + +def approval_template_is_inactive(record: dict[str, Any]) -> bool: + return ( + record.get("phase") == PHASE + and all_false(record, APPROVAL_FALSE_FIELDS) + and record.get("consumed") is False + and record.get("authorization_scope") == "EXACT_ONE_SHOT_PHASE_1_0G" + and record.get("authorized_by") is None + and record.get("approval_reference") is None + and record.get("protocol_magic") == "CHD10F01" + and record.get("protocol_version") == 1 + and record.get("protocol_frame_size") == 64 + and record.get("run_id") is None + and record.get("target") is None + and record.get("port") is None + and record.get("firmware") == "9.60" + and record.get("artifact_name") == ARTIFACT_NAME + and record.get("artifact_size") == ARTIFACT_SIZE + and record.get("artifact_sha256") == ARTIFACT_SHA256 + and record.get("timeout_seconds") == 75 + and all(record.get(field) == 1 for field in ( + "maximum_connections", "maximum_transfers", "maximum_executions", + "maximum_result_receives" + )) + ) + + +def validate(root: Path, retroarch_root: Path | None = None) -> list[str]: + errors: list[str] = [] + for relative in DELIVERABLES: + if not (root / relative).is_file(): + errors.append(f"missing deliverable: {relative}") + try: + record = load_json(root / "manifests/retroarch/phase-1.0g-one-shot-runner.json") + approval = load_json(root / "manifests/retroarch/phase-1.0g-one-shot-approval-template.json") + phase10f = load_json(root / "manifests/retroarch/phase-1.0f-startup-interval.json") + except (OSError, ValueError, json.JSONDecodeError) as error: + return errors + [str(error)] + + if record.get("phase") != PHASE or record.get("status") != STATUS: + errors.append("phase/status mismatch") + if record.get("runner_commit") != RUNNER_COMMIT: + errors.append("runner commit mismatch") + if record.get("target_source_commit") != TARGET_SOURCE_COMMIT: + errors.append("target source commit mismatch") + if not all_false(record.get("authorizations", {}), AUTHORIZATION_FIELDS): + errors.append("all Phase-1.0G authorizations must remain false") + if not all_false(record.get("phase_actions", {}), ACTION_FIELDS): + errors.append("Phase-1.0G must record no device action") + if not artifact_is_inactive(record.get("artifact", {}), phase10f): + errors.append("artifact identity changed or became eligible") + if not protocol_is_exact(record.get("result_protocol", {})): + errors.append("result protocol mismatch") + if not runner_is_inactive_and_bounded(record.get("runner", {})): + errors.append("runner is active or lacks one-shot guardrails") + if not approval_template_is_inactive(approval): + errors.append("tracked approval template grants authority or is incomplete") + activation = record.get("activation_requirements", {}) + if activation.get("current_requirements_satisfied") is not False: + errors.append("activation requirements are incorrectly satisfied") + if record.get("tests", {}).get("hardware_evidence_from_phase10g") is not False: + errors.append("host tests are mislabeled as hardware evidence") + + tracked = git(root, "ls-files").splitlines() + if any(path.lower().endswith((".elf", ".self", ".sprx", ".pkg", ".map")) for path in tracked): + errors.append("target artifact or linker map is tracked") + if any("phase10g" in path.lower() and "execution" in path.lower() for path in tracked): + errors.append("Phase-1.0G execution package is tracked") + sums = (root / "packaging/retroarch/phase10g/SHA256SUMS.txt").read_text(encoding="utf-8") + if ARTIFACT_SHA256 not in sums or not all(value in sums for value in SOURCE_HASHES.values()): + errors.append("hash-only record is incomplete") + + if retroarch_root is not None: + try: + git(retroarch_root, "cat-file", "-e", f"{RUNNER_COMMIT}^{{commit}}") + git(retroarch_root, "cat-file", "-e", f"{TARGET_SOURCE_COMMIT}^{{commit}}") + for relative, expected in SOURCE_HASHES.items(): + content = git(retroarch_root, "show", f"{RUNNER_COMMIT}:{relative}") + if sha256_text(content + "\n") != expected: + errors.append(f"runner source hash mismatch: {relative}") + runner = git(retroarch_root, "show", f"{RUNNER_COMMIT}:tools/ps5_diag_duplex.py") + if "ONE_SHOT_ATTEMPT_CONSUMED_BEFORE_CONNECT" not in runner: + errors.append("runner lacks pre-connect attempt receipt") + if 'open("xb")' not in runner or "os.fsync" not in runner: + errors.append("runner receipt is not exclusive and durable") + if "protocol_selection" not in runner or "MANIFEST_ONLY" not in runner: + errors.append("runner protocol is not manifest-selected") + except RuntimeError as error: + errors.append(str(error)) + return errors + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + parser.add_argument("--retroarch-root", type=Path) + args = parser.parse_args() + errors = validate(args.root.resolve(), args.retroarch_root) + if errors: + for error in errors: + print(f"ERROR: {error}") + return 1 + print("Phase-1.0G offline one-shot runner validation passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/validate_retroarch_phase10g_result.py b/tools/validate_retroarch_phase10g_result.py new file mode 100644 index 0000000..2b1602b --- /dev/null +++ b/tools/validate_retroarch_phase10g_result.py @@ -0,0 +1,154 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate the consumed Phase-1.0G one-shot result record.""" + +from __future__ import annotations + +import argparse +import json +from pathlib import Path +from typing import Any + + +PHASE = "PHASE_1_0G_INTERVAL_ONE_SHOT_DEVICE_RESULT" +STATUS = "ONE_SHOT_AUTHORIZATION_CONSUMED_RESULT_INCOMPLETE" +ARTIFACT_SHA256 = "e8bfc01c61bfb14b5814280a6e5442f1a5ad05ace5439d1c09e7e5ee00cd0055" +TRACE_SHA256 = "eb73611d98e602b89b3cdb3a0e94ec97b9410c5845b0449a07bf2aa6fdec7249" +RECEIPT_SHA256 = "aeb402cb45c16adea8d79d856633a76280895828b8d32822d9bc183fc7251442" +STDOUT_SHA256 = "0b56b42dabf2661233bbb57a63a520d791c13debd5da6839feeb30b534ad315f" +AUTH_FIELDS = ( + "ps5_connection_authorized", "device_transfer_authorized", + "device_execution_authorized", "result_receive_authorized", + "installation_authorized", "autoload_authorized", + "device_write_authorized", "automatic_retry", +) + + +def load(path: Path) -> dict[str, Any]: + value = json.loads(path.read_text(encoding="utf-8")) + if not isinstance(value, dict): + raise ValueError("result manifest is not an object") + return value + + +def all_false(record: dict[str, Any]) -> bool: + return all(record.get(field) is False for field in AUTH_FIELDS) + + +def transport_is_exact(record: dict[str, Any]) -> bool: + return ( + record.get("connect_count") == 1 + and record.get("sendall_count") == 1 + and record.get("bytes_sent") == 1845152 + and record.get("shutdown_write_count") == 1 + and record.get("recv_call_count") == 5 + and record.get("received_byte_count") == 3669 + and record.get("raw_stream_stored_bytes") == 3669 + and record.get("raw_stream_truncated") is False + and record.get("retry_count") == 0 + and record.get("reconnect_count") == 0 + and record.get("close_called") is True + and record.get("remote_eof_observed") is True + and record.get("timeout_observed") is False + ) + + +def protocol_is_exact(record: dict[str, Any]) -> bool: + return ( + record.get("magic") == "CHD10F01" + and record.get("frame_size") == 64 + and record.get("frame_count") == 7 + and record.get("stages") == ["D00", "D01", "D02", "I00", "I01", "I02", "I03"] + and record.get("d02_raw0") == 0 + and record.get("parser_errors") == [] + and record.get("terminal_stage") is None + and record.get("last_proven_stage") == "I03" + and record.get("first_unproven_stage") == "I04" + and record.get("classification") + == "REMOTE_PAYLOAD_OUTPUT_PROVEN_INCOMPLETE_AFTER_I03_BEFORE_I04" + ) + + +def validate(root: Path) -> list[str]: + errors: list[str] = [] + path = root / "manifests/retroarch/phase-1.0g-device-result.json" + doc = root / "docs/retroarch/phase-1.0g-device-result.md" + if not path.is_file() or not doc.is_file(): + return ["Phase-1.0G result deliverables are missing"] + try: + record = load(path) + except (OSError, ValueError, json.JSONDecodeError) as error: + return [str(error)] + if record.get("phase") != PHASE or record.get("status") != STATUS: + errors.append("phase/status mismatch") + artifact = record.get("artifact", {}) + if not ( + artifact.get("size") == 1845152 + and artifact.get("sha256") == ARTIFACT_SHA256 + and artifact.get("execution_eligible") is False + and artifact.get("transfer_eligible") is False + and artifact.get("installation_eligible") is False + and artifact.get("tracked") is False + ): + errors.append("artifact is changed, eligible or tracked") + authorization = record.get("authorization", {}) + if not ( + authorization.get("consumed") is True + and authorization.get("authority_inherited_by_future_action") is False + and authorization.get("attempt_receipt_sha256") == RECEIPT_SHA256 + ): + errors.append("authorization is not permanently consumed") + if not all_false(record.get("current_authorizations", {})): + errors.append("a current authorization remains active") + actions = record.get("performed_actions", {}) + if not all(actions.get(field) is True for field in ( + "ps5_connected", "device_request_performed", "files_transferred", + "target_execution_performed", "result_received_from_device" + )): + errors.append("authorized performed actions are incomplete") + if not all(actions.get(field) is False for field in ( + "device_write_performed", "installation_performed", + "autoload_performed", "reboot_performed" + )): + errors.append("a forbidden action is recorded") + if not transport_is_exact(record.get("transport", {})): + errors.append("transport counters violate the one-shot trace") + if not protocol_is_exact(record.get("protocol_result", {})): + errors.append("frame sequence or bounded classification mismatch") + if record.get("trace", {}).get("sha256") != TRACE_SHA256: + errors.append("trace hash mismatch") + stdout = record.get("ordinary_stdout", {}) + if not ( + stdout.get("byte_count") == 3221 + and stdout.get("sha256") == STDOUT_SHA256 + and stdout.get("key_message") + == "No arguments supplied and no menu built-in, displaying help..." + ): + errors.append("ordinary stdout evidence mismatch") + binding = record.get("source_binding", {}) + if not ( + binding.get("cause") == "DETERMINISTIC_NO_ARGUMENT_NO_MENU_EXIT" + and binding.get("profile_have_menu") is False + and binding.get("sdl_videoout_reached") is False + and binding.get("rendering_reached") is False + and binding.get("terminal_cleanup_proven") is False + ): + errors.append("source cause is missing or runtime progress is overclaimed") + return errors + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + errors = validate(args.root.resolve()) + if errors: + for error in errors: + print(f"ERROR: {error}") + return 1 + print("Phase-1.0G consumed device result validation passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/validate_retroarch_phase10h.py b/tools/validate_retroarch_phase10h.py new file mode 100644 index 0000000..ea37d76 --- /dev/null +++ b/tools/validate_retroarch_phase10h.py @@ -0,0 +1,234 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate the offline-only Phase-1.0H startup-argument artifact record.""" + +from __future__ import annotations + +import argparse +import json +from pathlib import Path +import subprocess +from typing import Any + + +PHASE = "PHASE_1_0H_STARTUP_ARGUMENT_CORRECTION" +STATUS = "OFFLINE_ARTIFACT_PREPARED_NO_DEVICE_AUTHORIZATION" +SOURCE_COMMIT = "c710f85816b5e456dd3a85a46f85eb28883522b3" +RUNNER_COMMIT = "f2dd710f2dff8c840a4c747cbbb3654c24cf23a7" +ARTIFACT_SHA256 = "822f2cf1f4d33a514d2bdd88fde40ad580dda5d85f537362ef6dff2eafcb56b6" +ARTIFACT_SIZE = 1845152 +MAP_SHA256 = "638642b750b8d5b108cf6c73215a3f1759bcb6da0b29ee0a0ade5c47e8b7b2d5" +MAP_SIZE = 637603 +DISASSEMBLY_SHA256 = "d38ccaecb2f52b1529680dab68314b6b014ec446ac66cba645887fbe8f1d8c11" +AUTHORIZATION_FIELDS = ( + "ps5_connection_authorized", "device_transfer_authorized", + "device_execution_authorized", "result_receive_authorized", + "installation_authorized", "autoload_authorized", + "device_write_authorized", "automatic_retry", +) +ACTION_FIELDS = ( + "ps5_connected", "device_request_performed", "files_transferred", + "device_write_performed", "target_execution_performed", + "result_received_from_device", "installation_performed", + "autoload_performed", +) +DELIVERABLES = ( + "docs/retroarch/phase-1.0h-startup-args.md", + "docs/approvals/phase-1.0h-device-test-template.md", + "manifests/retroarch/phase-1.0h-startup-args.json", + "tools/validate_retroarch_phase10h.py", + "tests/test_retroarch_phase10h.py", + "packaging/retroarch/phase10h/SHA256SUMS.txt", +) + + +def load_json(path: Path) -> dict[str, Any]: + value = json.loads(path.read_text(encoding="utf-8")) + if not isinstance(value, dict): + raise ValueError(f"{path} is not a JSON object") + return value + + +def git(root: Path, *args: str) -> str: + result = subprocess.run( + ["git", *args], cwd=root, capture_output=True, text=True, check=False + ) + if result.returncode: + raise RuntimeError(result.stderr.strip() or "git failed") + return result.stdout.strip() + + +def all_false(record: dict[str, Any], fields: tuple[str, ...]) -> bool: + return all(record.get(field) is False for field in fields) + + +def artifact_is_exact(record: dict[str, Any]) -> bool: + return ( + record.get("name") == "retroarch_ps5_startup_args_diag.elf" + and record.get("profile") == "startup-args-diag" + and record.get("size") == ARTIFACT_SIZE + and record.get("sha256") == ARTIFACT_SHA256 + and record.get("clean_build_sha256") == [ARTIFACT_SHA256] * 2 + and record.get("linker_map_size") == MAP_SIZE + and record.get("linker_map_sha256") == MAP_SHA256 + and record.get("clean_map_sha256") == [MAP_SHA256] * 2 + and record.get("changed_from_phase10g") is True + and record.get("execution_eligible") is False + and record.get("transfer_eligible") is False + and record.get("installation_eligible") is False + and record.get("device_action_performed") is False + and record.get("tracked") is False + ) + + +def correction_is_minimal(record: dict[str, Any]) -> bool: + return ( + record.get("profile") == "startup-args-diag" + and record.get("have_menu") is False + and record.get("content_path") is None + and record.get("config_path") is None + and record.get("libretro_path") is None + and record.get("existing_flag_added") == "RARCH_MAIN_WRAP_FLAG_VERBOSE" + and set(record.get("preserved_flags", [])) == { + "RARCH_MAIN_WRAP_FLAG_TOUCHED", "RARCH_MAIN_WRAP_FLAG_NO_CONTENT" + } + and record.get("disassembled_flag_mask") == "0x07" + and record.get("generic_argument_added") == "-v" + and record.get("modeled_argv") == ["retroarch", "-v"] + and record.get("modeled_argc") == 2 + and record.get("expected_next_checkpoint") == "I04" + and record.get("runtime_outcome") == "UNPROVEN" + ) + + +def protocol_is_fail_closed(record: dict[str, Any]) -> bool: + return ( + record.get("magic") == "CHD10H01" + and record.get("version") == 1 + and record.get("frame_size") == 64 + and record.get("d_stages") == [f"D{index:02d}" for index in range(13)] + and record.get("interval_stages") == [f"I{index:02d}" for index in range(15)] + and record.get("target_write_retry") is False + and record.get("short_write_retry") is False + and record.get("live_activation_available") is True + and record.get("live_activation_contract") + == "EXACT_ACTIVE_MANIFEST_AND_SEPARATE_LOCAL_APPROVAL_ONLY" + and record.get("protocol_activation_authorized") is False + and record.get("tracked_target") is None + and record.get("tracked_run_id") is None + and record.get("offline_parser_available") is True + ) + + +def elf_matches_phase10f(record: dict[str, Any], phase10f: dict[str, Any]) -> bool: + old = phase10f.get("elf", {}) + return ( + record.get("type") == "ET_DYN" + and record.get("machine") == "EM_X86_64" + and record.get("rwx_load_segment_count") == 0 + and record.get("tls") is False + and record.get("init_array_size") == 0 + and record.get("fini_array_size") == 0 + and record.get("relocations") == old.get("relocations") + and record.get("dt_needed") == old.get("dt_needed") + and record.get("undefined_symbol_count") == old.get("undefined_symbol_count") == 142 + and record.get("undefined_symbols_match_phase10f") is True + and record.get("inherited_send_present") is True + and record.get("normalized_disassembly_sha256") == [DISASSEMBLY_SHA256] * 2 + and not ({"socket", "connect", "bind", "listen", "accept", "recv"} + - set(record.get("forbidden_imports_absent", []))) + ) + + +def validate(root: Path, retroarch_root: Path | None = None) -> list[str]: + errors: list[str] = [] + for relative in DELIVERABLES: + if not (root / relative).is_file(): + errors.append(f"missing deliverable: {relative}") + try: + record = load_json(root / "manifests/retroarch/phase-1.0h-startup-args.json") + phase10f = load_json(root / "manifests/retroarch/phase-1.0f-startup-interval.json") + phase10g = load_json(root / "manifests/retroarch/phase-1.0g-device-result.json") + except (OSError, ValueError, json.JSONDecodeError) as error: + return errors + [str(error)] + + if record.get("phase") != PHASE or record.get("status") != STATUS: + errors.append("phase/status mismatch") + if record.get("source_commit") != SOURCE_COMMIT: + errors.append("source commit mismatch") + if record.get("runner_commit") != RUNNER_COMMIT: + errors.append("runner commit mismatch") + if not all_false(record.get("authorizations", {}), AUTHORIZATION_FIELDS): + errors.append("all Phase-1.0H authorizations must remain false") + if not all_false(record.get("phase_actions", {}), ACTION_FIELDS): + errors.append("Phase-1.0H must record no device action") + prior = record.get("prior_evidence", {}) + if not ( + prior.get("last_proven_stage") == phase10g.get("protocol_result", {}).get("last_proven_stage") == "I03" + and prior.get("first_unproven_stage") == "I04" + and prior.get("authorization_consumed") is True + and prior.get("authority_inherited") is False + ): + errors.append("consumed Phase-1.0G evidence is overclaimed") + if not artifact_is_exact(record.get("artifact", {})): + errors.append("artifact identity/reproducibility mismatch") + if not correction_is_minimal(record.get("correction", {})): + errors.append("startup correction is broadened or incomplete") + if not protocol_is_fail_closed(record.get("result_protocol", {})): + errors.append("H protocol is active or unbounded") + if not elf_matches_phase10f(record.get("elf", {}), phase10f): + errors.append("ELF closure no longer matches Phase 1.0F") + if record.get("startup_effects", {}).get("side_effect_free") is not False: + errors.append("startup effects are hidden") + if record.get("tests", {}).get("hardware_evidence_from_phase10h") is not False: + errors.append("host evidence is mislabeled as hardware evidence") + + tracked = git(root, "ls-files").splitlines() + if any(path.lower().endswith((".elf", ".self", ".sprx", ".pkg", ".map")) for path in tracked): + errors.append("target artifact or map is tracked") + sums = (root / "packaging/retroarch/phase10h/SHA256SUMS.txt").read_text(encoding="utf-8") + if ARTIFACT_SHA256 not in sums or MAP_SHA256 not in sums: + errors.append("hash-only record is incomplete") + + if retroarch_root is not None: + try: + git(retroarch_root, "cat-file", "-e", f"{SOURCE_COMMIT}^{{commit}}") + git(retroarch_root, "cat-file", "-e", f"{RUNNER_COMMIT}^{{commit}}") + platform = git(retroarch_root, "show", f"{SOURCE_COMMIT}:frontend/drivers/platform_ps5.c") + makefile = git(retroarch_root, "show", f"{SOURCE_COMMIT}:Makefile.ps5") + stream = git(retroarch_root, "show", f"{SOURCE_COMMIT}:pkg/ps5/chimera_ps5_diag_stream.c") + host = git(retroarch_root, "show", f"{RUNNER_COMMIT}:tools/ps5_diag_duplex.py") + if "CHIMERA_PS5_STARTUP_ARGS_DIAG" not in platform or "RARCH_MAIN_WRAP_FLAG_VERBOSE" not in platform: + errors.append("source lacks scoped verbose flag") + if "PS5_PROFILE),startup-args-diag" not in makefile or "CHIMERA_PS5_STARTUP_ARGS_DIAG=1" not in makefile: + errors.append("source lacks startup-args profile") + if "'C', 'H', 'D', '1', '0', 'H', '0', '1'" not in stream: + errors.append("source lacks H magic") + if ( + "STARTUP_ARGS_PROTOCOL" not in host + or "STARTUP_ARGS_ACTIVE_PHASE" not in host + or "EXACT_ONE_SHOT_PHASE_1_0H" not in host + or 'parser.add_argument("--protocol"' in host + ): + errors.append("host H runner is missing or freely selectable") + except RuntimeError as error: + errors.append(str(error)) + return errors + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + parser.add_argument("--retroarch-root", type=Path) + args = parser.parse_args() + errors = validate(args.root.resolve(), args.retroarch_root) + if errors: + for error in errors: + print(f"ERROR: {error}") + return 1 + print("Phase-1.0H offline startup-argument validation passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/validate_retroarch_phase10h_result.py b/tools/validate_retroarch_phase10h_result.py new file mode 100644 index 0000000..db0cc2b --- /dev/null +++ b/tools/validate_retroarch_phase10h_result.py @@ -0,0 +1,161 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate the consumed Phase-1.0H one-shot device result.""" + +from __future__ import annotations + +import argparse +import json +from pathlib import Path +import subprocess +from typing import Any + + +PHASE = "PHASE_1_0H_STARTUP_ARGUMENT_ONE_SHOT_DEVICE_RESULT" +STATUS = "ONE_SHOT_AUTHORIZATION_CONSUMED_FLIP_SUBMIT_FAILED" +ARTIFACT_SHA256 = "822f2cf1f4d33a514d2bdd88fde40ad580dda5d85f537362ef6dff2eafcb56b6" +TRACE_SHA256 = "858a205afcf682a498d8bc11947b67d2446098883a61f6871e31df4d950d0a61" +STAGES = [ + "D00", "D01", "D02", "I00", "I01", "I02", "I03", "D12", + "I04", "I05", "I06", "I07", "I08", "I09", "I10", "D10", + "I11", "I12", "I13", "I14", "D03", "D05", "D06", "D07", "D04", +] +AUTHORIZATION_FIELDS = ( + "ps5_connection_authorized", "device_transfer_authorized", + "device_execution_authorized", "result_receive_authorized", + "installation_authorized", "autoload_authorized", + "device_write_authorized", "automatic_retry", +) +DELIVERABLES = ( + "docs/retroarch/phase-1.0h-device-result.md", + "manifests/retroarch/phase-1.0h-device-result.json", + "tools/validate_retroarch_phase10h_result.py", + "tests/test_retroarch_phase10h_result.py", +) + + +def load_json(path: Path) -> dict[str, Any]: + value = json.loads(path.read_text(encoding="utf-8")) + if not isinstance(value, dict): + raise ValueError("result manifest is not an object") + return value + + +def all_false(record: dict[str, Any], fields: tuple[str, ...]) -> bool: + return all(record.get(field) is False for field in fields) + + +def result_is_exact(record: dict[str, Any]) -> bool: + raw = record.get("raw_results", {}) + return ( + record.get("magic") == "CHD10H01" + and record.get("frame_size") == 64 + and record.get("frame_count") == len(STAGES) == 25 + and record.get("stages") == STAGES + and raw == { + "D02": 0, "D12_shutdown_reason": 6, + "D12_first_error": 118, "D10": 1, "I11": 0, + "D05": 1309671680, "D06": 0, "D07": -1, "D04": -1, + } + and record.get("last_frame") == "D04" + and record.get("runloop_terminal_frame_present") is False + and record.get("classification") + == "STARTUP_ARGUMENT_FIX_PROVEN_VIDEOOUT_BUFFERS_READY_FLIP_SUBMIT_FAILED" + ) + + +def transport_is_one_shot(record: dict[str, Any]) -> bool: + return ( + record.get("connect_count") == 1 + and record.get("sendall_count") == 1 + and record.get("bytes_sent") == 1845152 + and record.get("shutdown_write_count") == 1 + and record.get("received_byte_count") == 3953 + and record.get("raw_stream_stored_bytes") == 3953 + and record.get("raw_stream_truncated") is False + and record.get("retry_count") == 0 + and record.get("reconnect_count") == 0 + and record.get("close_called") is True + and record.get("remote_eof_observed") is True + ) + + +def validate(root: Path) -> list[str]: + errors: list[str] = [] + for relative in DELIVERABLES: + if not (root / relative).is_file(): + errors.append(f"missing deliverable: {relative}") + try: + record = load_json(root / "manifests/retroarch/phase-1.0h-device-result.json") + except (OSError, ValueError, json.JSONDecodeError) as error: + return errors + [str(error)] + if record.get("phase") != PHASE or record.get("status") != STATUS: + errors.append("phase/status mismatch") + artifact = record.get("artifact", {}) + if not ( + artifact.get("sha256") == ARTIFACT_SHA256 + and artifact.get("size") == 1845152 + and artifact.get("execution_eligible") is False + and artifact.get("transfer_eligible") is False + and artifact.get("installation_eligible") is False + and artifact.get("tracked") is False + ): + errors.append("artifact is not exact and consumed") + authorization = record.get("authorization", {}) + if authorization.get("consumed") is not True or authorization.get("authority_inherited_by_future_action") is not False: + errors.append("authorization is reusable") + if not all_false(record.get("current_authorizations", {}), AUTHORIZATION_FIELDS): + errors.append("a current authorization remains active") + actions = record.get("performed_actions", {}) + if not ( + actions.get("ps5_connected") is True + and actions.get("files_transferred") is True + and actions.get("target_execution_performed") is True + and actions.get("result_received_from_device") is True + and actions.get("retry_performed") is False + and actions.get("reconnect_performed") is False + and actions.get("videoout_flip_submit_attempted") is True + and actions.get("videoout_flip_submit_succeeded") is False + ): + errors.append("performed action record is incomplete") + if not transport_is_one_shot(record.get("transport", {})): + errors.append("transport is not exact one-shot") + trace = record.get("trace", {}) + if trace.get("sha256") != TRACE_SHA256 or trace.get("tracked") is not False or trace.get("parser_errors") != []: + errors.append("trace identity/parser result mismatch") + if not result_is_exact(record.get("protocol_result", {})): + errors.append("frame sequence or raw results mismatch") + source = record.get("source_binding", {}) + if not ( + source.get("startup_argument_fix_reached_i04") is True + and source.get("write_firewall_triggered") is True + and source.get("exact_blocked_write_operation") == "UNOBSERVED" + and source.get("first_flip_submit_raw") == -1 + and source.get("sdl_init_raw") == -1 + and source.get("visible_presentation") == "UNPROVEN" + and source.get("complete_cleanup") == "UNPROVEN" + ): + errors.append("source binding overclaims or omits blockers") + tracked = subprocess.run( + ["git", "ls-files"], cwd=root, capture_output=True, text=True, check=True + ).stdout.splitlines() + if any(path.lower().endswith((".elf", ".self", ".sprx", ".pkg", ".map")) for path in tracked): + errors.append("target artifact is tracked") + return errors + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + errors = validate(args.root.resolve()) + if errors: + for error in errors: + print(f"ERROR: {error}") + return 1 + print("Phase-1.0H consumed device result validation passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/validate_retroarch_phase10i.py b/tools/validate_retroarch_phase10i.py new file mode 100644 index 0000000..a7ca17d --- /dev/null +++ b/tools/validate_retroarch_phase10i.py @@ -0,0 +1,191 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate the offline-only Phase-1.0I flip/write postmortem.""" + +from __future__ import annotations + +import argparse +import json +from pathlib import Path +import subprocess +from typing import Any + + +PHASE = "PHASE_1_0I_OFFLINE_FLIP_AND_WRITE_ANALYSIS" +STATUS = "OFFLINE_ANALYSIS_COMPLETE_DEVICE_RETEST_BLOCKED" +ARTIFACT_SHA256 = "822f2cf1f4d33a514d2bdd88fde40ad580dda5d85f537362ef6dff2eafcb56b6" +MAP_SHA256 = "638642b750b8d5b108cf6c73215a3f1759bc6da0b29ee0a0ade5c47e8b7b2d5" +AUTHORIZATION_FIELDS = ( + "ps5_connection_authorized", "device_transfer_authorized", + "device_execution_authorized", "result_receive_authorized", + "target_build_authorized", "installation_authorized", + "autoload_authorized", "device_write_authorized", "automatic_retry", +) +ACTION_FIELDS = ( + "ps5_connected", "device_request_performed", "files_transferred", + "target_execution_performed", "result_received_from_device", + "target_build_performed", "target_artifact_created", + "device_write_performed", "installation_performed", "autoload_performed", + "retry_performed", "reconnect_performed", +) +DELIVERABLES = ( + "docs/retroarch/phase-1.0i-flip-and-write-analysis.md", + "manifests/retroarch/phase-1.0i-flip-and-write-analysis.json", + "tools/validate_retroarch_phase10i.py", + "tests/test_retroarch_phase10i.py", +) + + +def load_json(path: Path) -> dict[str, Any]: + value = json.loads(path.read_text(encoding="utf-8")) + if not isinstance(value, dict): + raise ValueError("Phase-1.0I manifest is not an object") + return value + + +def all_false(record: dict[str, Any], fields: tuple[str, ...]) -> bool: + return all(record.get(field) is False for field in fields) + + +def write_analysis_is_bounded(record: dict[str, Any]) -> bool: + wrappers = record.get("linked_blocking_wrappers", []) + return ( + record.get("observed_after") == "I03" + and record.get("observed_before") == "I04" + and record.get("shutdown_reason") == 6 + and record.get("first_error") == 118 + and record.get("d12_transmitted_fields") == ["shutdown_reason", "first_error"] + and record.get("status_field_not_transmitted") == "first_blocked_write" + and wrappers == [ + {"symbol": "__wrap_open", "address": "0x47580", "operation": "OPEN"}, + {"symbol": "__wrap_fopen", "address": "0x475b0", "operation": "OPEN"}, + {"symbol": "__wrap_fwrite", "address": "0x47600", "operation": "STREAM"}, + ] + and record.get("possible_operations") == ["OPEN", "STREAM"] + and record.get("exact_operation") == "UNPROVEN" + and record.get("exact_path") == "UNPROVEN" + and record.get("write_succeeded") is False + and record.get("shutdown_stopped_later_initialization") is False + ) + + +def flip_analysis_is_exact(record: dict[str, Any]) -> bool: + arguments = record.get("arguments", {}) + disassembly = record.get("disassembly", {}) + return ( + record.get("pattern_destination") == "vbuf[0]" + and record.get("pattern_copied_before_submit") is True + and record.get("submit_symbol") == "sceVideoOutSubmitFlip" + and arguments.get("buffer_index") == 0 + and arguments.get("flip_mode") == 1 + and arguments.get("flip_argument") == 0 + and disassembly == { + "buffer_index_zero": "0xfe6ac", + "flip_mode_one": "0xfe6ae", + "flip_argument_zero": "0xfe6b3", + "submit_call": "0xfe6bc", + } + and record.get("buffer_registration_raw") == 0 + and record.get("submit_raw") == -1 + and record.get("event_wait_attempted") is False + and record.get("original_errno") == "UNPROVEN" + and record.get("errno_captured_before_diagnostic_send") is False + and record.get("visible_presentation") == "UNPROVEN" + ) + + +def decision_is_fail_closed(record: dict[str, Any]) -> bool: + return ( + record.get("offline_analysis_complete") is True + and record.get("root_cause_identified") is False + and record.get("next_device_test_ready") is False + and record.get("next_offline_artifact_design_allowed") is True + and record.get("new_artifact_created_in_phase10i") is False + and record.get("new_device_client_created_in_phase10i") is False + ) + + +def validate(root: Path) -> list[str]: + errors: list[str] = [] + for relative in DELIVERABLES: + if not (root / relative).is_file(): + errors.append(f"missing deliverable: {relative}") + try: + record = load_json(root / "manifests/retroarch/phase-1.0i-flip-and-write-analysis.json") + except (OSError, ValueError, json.JSONDecodeError) as error: + return errors + [str(error)] + if record.get("phase") != PHASE or record.get("status") != STATUS: + errors.append("phase/status mismatch") + artifact = record.get("bound_evidence", {}).get("artifact", {}) + if not ( + artifact.get("sha256") == ARTIFACT_SHA256 + and artifact.get("size") == 1845152 + and artifact.get("tracked") is False + and artifact.get("execution_eligible") is False + and artifact.get("transfer_eligible") is False + and artifact.get("installation_eligible") is False + ): + errors.append("consumed artifact is not exact and ineligible") + linker_map = record.get("bound_evidence", {}).get("linker_map", {}) + if linker_map.get("sha256") != MAP_SHA256 or linker_map.get("tracked") is not False: + errors.append("linker map identity mismatch") + if not all_false(record.get("phase_actions", {}), ACTION_FIELDS): + errors.append("Phase-1.0I records a device/target action") + if not all_false(record.get("current_authorizations", {}), AUTHORIZATION_FIELDS): + errors.append("a current authorization is active") + if not write_analysis_is_bounded(record.get("write_firewall", {})): + errors.append("write analysis overclaims or omits the bounded candidates") + if not flip_analysis_is_exact(record.get("first_flip", {})): + errors.append("flip analysis differs from source/disassembly/runtime evidence") + source = record.get("source_inconsistency", {}) + if not ( + source.get("diagnostic_buffer_index") == 0 + and source.get("patched_normal_initial_frame_id") == 1 + and source.get("public_sdl_initial_frame_id") == 0 + and source.get("classification") == "STRONG_SOURCE_CANDIDATE_NOT_PROVEN_ROOT_CAUSE" + ): + errors.append("source inconsistency is hidden or promoted to root cause") + abi = record.get("abi_evidence", {}) + if not ( + abi.get("sdk_export_stub_present") is True + and abi.get("public_sdk_prototype_present") is False + and abi.get("prototype_source") == "PUBLIC_SDL_FORK" + and abi.get("exact_firmware_semantics") == "UNPROVEN" + ): + errors.append("ABI evidence is overstated") + if not decision_is_fail_closed(record.get("decision", {})): + errors.append("decision opens a device test or claims root cause") + tests = record.get("tests", {}) + if not ( + tests.get("chimera_gfx_ctest") == "46_OF_46_PASS" + and tests.get("retroarch_ps5_host_suite") == "PASS_WITH_ASAN_UBSAN" + and tests.get("phase10i_gfx_guardrails") == 17 + and tests.get("phase10i_retroarch_guardrails") == 6 + and tests.get("safety_audit") == "PASS" + and tests.get("secret_scan") == "PASS" + and tests.get("hardware_evidence_from_phase10i") is False + ): + errors.append("test evidence is incomplete or promoted to hardware evidence") + tracked = subprocess.run( + ["git", "ls-files"], cwd=root, capture_output=True, text=True, check=True + ).stdout.splitlines() + if any(path.lower().endswith((".elf", ".self", ".sprx", ".pkg", ".map")) for path in tracked): + errors.append("target artifact or map is tracked") + return errors + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + errors = validate(args.root.resolve()) + if errors: + for error in errors: + print(f"ERROR: {error}") + return 1 + print("Phase-1.0I offline flip/write analysis validation passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/validate_retroarch_phase10j.py b/tools/validate_retroarch_phase10j.py new file mode 100644 index 0000000..9ed02de --- /dev/null +++ b/tools/validate_retroarch_phase10j.py @@ -0,0 +1,216 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate the offline-only Phase-1.0J diagnostic artifact record.""" + +from __future__ import annotations + +import argparse +import json +from pathlib import Path +import subprocess +from typing import Any + + +PHASE = "PHASE_1_0J_WRITE_FIREWALL_DIAGNOSTIC" +STATUS = "OFFLINE_ARTIFACT_AUDITED_DEVICE_ACTION_BLOCKED" +ARTIFACT_SHA256 = "6ff0f7ea391da5f15ea43512a871078133e896a6900ae9f8f3fa75711abb8009" +MAP_SHA256 = "19f1cf851ad8f99d31d5de3a14591f81faa83589c18e93cd60be96ebef5d7719" +DISASSEMBLY_SHA256 = "45dc4e0233b3770add430aaa6bb23a60210b50744effb76b515fca4b9e6e310f" +AUTHORIZATION_FIELDS = ( + "ps5_connection_authorized", "device_transfer_authorized", + "device_execution_authorized", "result_receive_authorized", + "target_build_authorized", "installation_authorized", + "autoload_authorized", "device_write_authorized", "automatic_retry", +) +DEVICE_ACTION_FIELDS = ( + "ps5_connected", "device_request_performed", "files_transferred", + "target_execution_performed", "result_received_from_device", + "device_write_performed", "installation_performed", "autoload_performed", + "retry_performed", "reconnect_performed", +) +DELIVERABLES = ( + "docs/adr/0012-phase10j-first-frame-identity.md", + "docs/retroarch/phase-1.0j-write-firewall-diagnostic.md", + "manifests/retroarch/phase-1.0j-write-firewall-diagnostic.json", + "packaging/retroarch/phase10j/SHA256SUMS.txt", + "tools/validate_retroarch_phase10j.py", + "tests/test_retroarch_phase10j.py", +) + + +def load_json(path: Path) -> dict[str, Any]: + value = json.loads(path.read_text(encoding="utf-8")) + if not isinstance(value, dict): + raise ValueError("Phase-1.0J manifest is not an object") + return value + + +def all_false(record: dict[str, Any], fields: tuple[str, ...]) -> bool: + return all(record.get(field) is False for field in fields) + + +def artifact_is_exact_and_ineligible(record: dict[str, Any]) -> bool: + return ( + record.get("name") == "retroarch_ps5_write_diag.elf" + and record.get("size") == 1845208 + and record.get("sha256") == ARTIFACT_SHA256 + and record.get("tracked") is False + and record.get("execution_eligible") is False + and record.get("transfer_eligible") is False + and record.get("installation_eligible") is False + ) + + +def protocol_is_bounded(record: dict[str, Any]) -> bool: + return ( + record.get("magic") == "CHD10J01" + and record.get("new_stage") == "D13" + and record.get("historical_stage_values_preserved") is True + and record.get("d13_fields") == ["first_blocked_write", "write_block_count"] + and record.get("d13_immediately_precedes_terminal_d12") is True + and record.get("d13_stream_only") is True + and record.get("target_socket_created") is False + and record.get("live_runner_supports_j") is False + ) + + +def firewall_is_fail_closed(record: dict[str, Any]) -> bool: + wrappers = record.get("surviving_wrappers", []) + return ( + record.get("first_rejection_requests_shutdown") is True + and record.get("stop_check_location") == "IMMEDIATELY_AFTER_RETROARCH_PARSE_INPUT_AND_CONFIG" + and record.get("stop_before") == "I04" + and record.get("stop_before_driver_lookup") is True + and record.get("write_succeeds") is False + and record.get("wrapper_errno") == "EROFS" + and wrappers == [ + {"symbol": "__wrap_open", "address": "0x475a0", "operation": "OPEN"}, + {"symbol": "__wrap_fopen", "address": "0x47610", "operation": "OPEN"}, + {"symbol": "__wrap_fwrite", "address": "0x476a0", "operation": "STREAM"}, + ] + ) + + +def flip_instrumentation_is_bounded(record: dict[str, Any]) -> bool: + return ( + record.get("submit_count_maximum_per_reached_helper") == 1 + and record.get("submit_errno_saved_immediately") is True + and record.get("reporting_before_errno_save") is False + and record.get("d07_fields_under_j") == ["submit_result", "saved_errno"] + and record.get("wait_only_after_submit_success") is True + and record.get("first_frame_source") == "CHIMERA_PS5_FIRST_FRAME_INDEX" + and record.get("first_frame_value") == 0 + and record.get("firmware_semantics") == "UNPROVEN" + and record.get("root_cause_claimed") is False + ) + + +def audit_is_bounded(record: dict[str, Any]) -> bool: + relocations = record.get("relocations", {}) + forbidden_imports = ( + "socket_create_import", "connect_import", "listener_import", + "receive_import", + ) + return ( + record.get("entry_point") == "0xff210" + and record.get("load_segment_permissions") == ["R_E", "R", "RW"] + and record.get("rwx_load_segments") == 0 + and record.get("init_array_size") == 0 + and record.get("fini_array_size") == 0 + and record.get("tls_sections") == 0 + and record.get("undefined_dynamic_symbols") == 142 + and relocations == { + "total": 1055, + "R_X86_64_GLOB_DAT": 142, + "R_X86_64_RELATIVE": 913, + } + and record.get("inherited_send_import_count") == 1 + and all(record.get(field) is False for field in forbidden_imports) + and record.get("gnm_imports") == [] + and record.get("normalized_disassembly_sha256") == DISASSEMBLY_SHA256 + and record.get("string_absence_used_as_reachability_proof") is False + ) + + +def validate(root: Path) -> list[str]: + errors: list[str] = [] + for relative in DELIVERABLES: + if not (root / relative).is_file(): + errors.append(f"missing deliverable: {relative}") + try: + record = load_json(root / "manifests/retroarch/phase-1.0j-write-firewall-diagnostic.json") + except (OSError, ValueError, json.JSONDecodeError) as error: + return errors + [str(error)] + if record.get("phase") != PHASE or record.get("status") != STATUS: + errors.append("phase/status mismatch") + if not artifact_is_exact_and_ineligible(record.get("artifact", {})): + errors.append("artifact is not exact and ineligible") + linker_map = record.get("linker_map", {}) + if not ( + linker_map.get("size") == 637728 + and linker_map.get("sha256") == MAP_SHA256 + and linker_map.get("tracked") is False + ): + errors.append("linker-map identity mismatch") + if not protocol_is_bounded(record.get("protocol", {})): + errors.append("J protocol is widened or incomplete") + if not firewall_is_fail_closed(record.get("write_firewall", {})): + errors.append("write firewall does not stop and report exactly") + if not flip_instrumentation_is_bounded(record.get("flip_diagnostic", {})): + errors.append("flip instrumentation overclaims or widens behavior") + if not audit_is_bounded(record.get("artifact_audit", {})): + errors.append("artifact audit is incomplete or overclaims reachability") + reproducibility = record.get("reproducibility", {}) + if not ( + reproducibility.get("clean_builds") == 2 + and reproducibility.get("elf_byte_identical") is True + and reproducibility.get("map_byte_identical") is True + and reproducibility.get("normalized_disassembly_identical") is True + and reproducibility.get("warnings_as_errors_for_retroarch_target") is True + ): + errors.append("reproducibility evidence is incomplete") + actions = record.get("phase_actions", {}) + if not all_false(actions, DEVICE_ACTION_FIELDS): + errors.append("Phase-1.0J records a device action") + if actions.get("target_build_performed") is not True or actions.get("target_artifact_created") is not True: + errors.append("offline target build is not recorded") + if not all_false(record.get("current_authorizations", {}), AUTHORIZATION_FIELDS): + errors.append("a current authorization is active") + tests = record.get("tests", {}) + if not ( + tests.get("chimera_gfx_ctest") == "49_OF_49_PASS" + and tests.get("retroarch_ps5_host_suite") == "PASS_WITH_ASAN_UBSAN" + and tests.get("phase10j_gfx_guardrails") == 20 + and tests.get("phase10j_retroarch_guardrails") == 4 + and tests.get("safety_audit") == "PASS" + and tests.get("secret_scan") == "PASS" + and tests.get("hardware_evidence_from_phase10j") is False + ): + errors.append("test evidence is incomplete or promoted to hardware evidence") + sums = (root / "packaging/retroarch/phase10j/SHA256SUMS.txt").read_text(encoding="utf-8") + if ARTIFACT_SHA256 not in sums or MAP_SHA256 not in sums: + errors.append("checksum record does not bind both ignored outputs") + tracked = subprocess.run( + ["git", "ls-files"], cwd=root, capture_output=True, text=True, check=True + ).stdout.splitlines() + forbidden_suffixes = (".elf", ".self", ".sprx", ".pkg", ".map") + if any(path.lower().endswith(forbidden_suffixes) for path in tracked): + errors.append("target artifact or map is tracked") + return errors + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + errors = validate(args.root.resolve()) + if errors: + for error in errors: + print(f"ERROR: {error}") + return 1 + print("Phase-1.0J offline diagnostic artifact validation passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/validate_retroarch_phase10k.py b/tools/validate_retroarch_phase10k.py new file mode 100644 index 0000000..580b1ec --- /dev/null +++ b/tools/validate_retroarch_phase10k.py @@ -0,0 +1,242 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate the inactive Phase-1.0K one-shot runner contract.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +from pathlib import Path +import subprocess +from typing import Any + + +PHASE = "PHASE_1_0K_WRITE_DIAG_ONE_SHOT_RUNNER" +STATUS = "OFFLINE_RUNNER_PREPARED_NO_DEVICE_AUTHORIZATION" +ARTIFACT_SHA256 = "6ff0f7ea391da5f15ea43512a871078133e896a6900ae9f8f3fa75711abb8009" +RUNNER_SHA256 = "4ee58f08ff51cff3624cbc072c0e915e8c415eb8cd98185fa0d6a20c02b7c330" +RUNNER_COMMIT = "ee965a0be3cd3e0032330680e7614c766688410a" +WIRE_STAGES = ( + tuple(f"D{index:02d}" for index in range(13)) + + tuple(f"I{index:02d}" for index in range(15)) + + ("C1", "D13") +) +AUTHORIZATION_FIELDS = ( + "ps5_connection_authorized", "device_transfer_authorized", + "device_execution_authorized", "result_receive_authorized", + "installation_authorized", "autoload_authorized", + "device_write_authorized", "automatic_retry", +) +ACTION_FIELDS = ( + "ps5_connected", "device_request_performed", "files_transferred", + "target_execution_performed", "result_received_from_device", + "target_build_performed", "target_artifact_created", + "device_write_performed", "installation_performed", "autoload_performed", + "retry_performed", "reconnect_performed", +) +DELIVERABLES = ( + "docs/retroarch/phase-1.0k-write-diag-one-shot-runner.md", + "docs/approvals/phase-1.0k-write-diag-one-shot-template.md", + "manifests/retroarch/phase-1.0k-write-diag-one-shot-runner.json", + "manifests/retroarch/phase-1.0k-one-shot-approval-template.json", + "packaging/retroarch/phase10k/SHA256SUMS.txt", + "tools/validate_retroarch_phase10k.py", + "tests/test_retroarch_phase10k.py", +) + + +def load_json(path: Path) -> dict[str, Any]: + value = json.loads(path.read_text(encoding="utf-8")) + if not isinstance(value, dict): + raise ValueError(f"{path} is not a JSON object") + return value + + +def all_false(record: dict[str, Any], fields: tuple[str, ...]) -> bool: + return all(record.get(field) is False for field in fields) + + +def artifact_is_ineligible(record: dict[str, Any]) -> bool: + return ( + record.get("name") == "retroarch_ps5_write_diag.elf" + and record.get("profile") == "write-diag" + and record.get("size") == 1845208 + and record.get("sha256") == ARTIFACT_SHA256 + and record.get("source_unchanged_from_phase10j") is True + and record.get("execution_eligible") is False + and record.get("transfer_eligible") is False + and record.get("installation_eligible") is False + and record.get("tracked") is False + ) + + +def protocol_is_exact_and_inactive(record: dict[str, Any]) -> bool: + return ( + record.get("magic") == "CHD10J01" + and record.get("version") == 1 + and record.get("frame_size") == 64 + and record.get("byte_order") == "BIG_ENDIAN" + and record.get("wire_stages") == list(WIRE_STAGES) + and record.get("c1_wire_index") == 28 + and record.get("d13_wire_index") == 29 + and record.get("terminal_stage") == "D12" + and record.get("protocol_activation_authorized") is False + and record.get("tracked_target") is None + and record.get("tracked_port") is None + and record.get("tracked_run_id") is None + ) + + +def runner_is_fail_closed(record: dict[str, Any]) -> bool: + return ( + record.get("implementation_available") is True + and record.get("source_sha256") == RUNNER_SHA256 + and record.get("protocol_selection") == "MANIFEST_ONLY" + and record.get("free_protocol_selector") is False + and record.get("active_manifest_required") is True + and record.get("separate_untracked_approval_required") is True + and record.get("actual_artifact_rehashed_before_socket") is True + and record.get("attempt_receipt_exclusive_create") is True + and record.get("attempt_receipt_durable_fsync") is True + and record.get("attempt_receipt_written_before_connect") is True + and record.get("maximum_connections") == 1 + and record.get("maximum_transfers") == 1 + and record.get("maximum_executions") == 1 + and record.get("maximum_result_receives") == 1 + and record.get("receive_limit_bytes") == 65536 + and record.get("retry") is False + and record.get("reconnect") is False + and record.get("resume") is False + and record.get("trace_exclusive_create") is True + and record.get("trace_overwrite") is False + ) + + +def approval_is_inactive(record: dict[str, Any]) -> bool: + return ( + record.get("phase") == PHASE + and record.get("authorized") is False + and record.get("consumed") is False + and record.get("authorization_scope") == "EXACT_ONE_SHOT_PHASE_1_0K" + and record.get("authorized_by") is None + and record.get("approval_reference") is None + and record.get("protocol_magic") == "CHD10J01" + and record.get("run_id") is None + and record.get("target") is None + and record.get("port") is None + and record.get("artifact_sha256") == ARTIFACT_SHA256 + and record.get("connection_count") == 0 + and record.get("transfer_count") == 0 + and record.get("execution_count") == 0 + and record.get("result_receive_count") == 0 + and all(record.get(field) is False for field in ( + "installation", "autoload", "device_write", "retry", + "reconnect", "resume", "reboot", + )) + ) + + +def git(root: Path, *args: str) -> str: + result = subprocess.run( + ["git", *args], cwd=root, capture_output=True, text=True, check=False + ) + if result.returncode: + raise RuntimeError(result.stderr.strip() or "git failed") + return result.stdout.strip() + + +def git_bytes(root: Path, *args: str) -> bytes: + result = subprocess.run( + ["git", *args], cwd=root, capture_output=True, check=False + ) + if result.returncode: + raise RuntimeError(result.stderr.decode(errors="replace").strip() or "git failed") + return result.stdout + + +def validate(root: Path, retroarch_root: Path | None = None) -> list[str]: + errors: list[str] = [] + for relative in DELIVERABLES: + if not (root / relative).is_file(): + errors.append(f"missing deliverable: {relative}") + try: + record = load_json(root / "manifests/retroarch/phase-1.0k-write-diag-one-shot-runner.json") + approval = load_json(root / "manifests/retroarch/phase-1.0k-one-shot-approval-template.json") + except (OSError, ValueError, json.JSONDecodeError) as error: + return errors + [str(error)] + if record.get("phase") != PHASE or record.get("status") != STATUS: + errors.append("phase/status mismatch") + commits = record.get("source_commits", {}) + if commits.get("retroarch_runner") != RUNNER_COMMIT: + errors.append("runner commit mismatch") + if not all_false(record.get("authorizations", {}), AUTHORIZATION_FIELDS): + errors.append("an authorization is active") + if not all_false(record.get("phase_actions", {}), ACTION_FIELDS): + errors.append("Phase-1.0K records a target or device action") + if not artifact_is_ineligible(record.get("artifact", {})): + errors.append("artifact is not exact and ineligible") + if not protocol_is_exact_and_inactive(record.get("result_protocol", {})): + errors.append("protocol is misindexed or active") + if not runner_is_fail_closed(record.get("runner", {})): + errors.append("runner is widened or incomplete") + if not approval_is_inactive(approval): + errors.append("tracked approval template is active or incomplete") + activation = record.get("activation_requirements", {}) + if activation.get("current_requirements_satisfied") is not False: + errors.append("activation requirements are marked satisfied") + tests = record.get("tests", {}) + if not ( + tests.get("retroarch_duplex_python_cases") == 26 + and tests.get("retroarch_phase10k_guardrails") == 4 + and tests.get("chimera_gfx_ctest") == "51_OF_51_PASS" + and tests.get("chimera_gfx_phase10k_guardrails") == 20 + and tests.get("fake_socket_only") is True + and tests.get("hardware_evidence_from_phase10k") is False + ): + errors.append("test evidence is incomplete or promoted") + sums = (root / "packaging/retroarch/phase10k/SHA256SUMS.txt").read_text(encoding="utf-8") + if ARTIFACT_SHA256 not in sums or RUNNER_SHA256 not in sums: + errors.append("checksum record is incomplete") + tracked = git(root, "ls-files").splitlines() + if any(path.lower().endswith((".elf", ".self", ".sprx", ".pkg", ".map")) for path in tracked): + errors.append("target artifact or map is tracked") + if retroarch_root is not None: + try: + git(retroarch_root, "cat-file", "-e", f"{RUNNER_COMMIT}^{{commit}}") + runner_bytes = git_bytes( + retroarch_root, "show", f"{RUNNER_COMMIT}:tools/ps5_diag_duplex.py" + ) + runner = runner_bytes.decode("utf-8") + if hashlib.sha256(runner_bytes).hexdigest() != RUNNER_SHA256: + errors.append("runner source hash mismatch") + for token in ( + 'WRITE_DIAG_MAGIC = b"CHD10J01"', + 'WRITE_DIAG_STAGES = INTERVAL_STAGES + ("C1", "D13")', + 'expected_scope = "EXACT_ONE_SHOT_PHASE_1_0K"', + ): + if token not in runner: + errors.append(f"runner source omits {token}") + if 'parser.add_argument("--protocol"' in runner: + errors.append("runner source has a free protocol selector") + except RuntimeError as error: + errors.append(str(error)) + return errors + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + parser.add_argument("--retroarch-root", type=Path) + args = parser.parse_args() + errors = validate(args.root.resolve(), args.retroarch_root) + if errors: + for error in errors: + print(f"ERROR: {error}") + return 1 + print("Phase-1.0K inactive one-shot runner validation passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/validate_retroarch_phase10l.py b/tools/validate_retroarch_phase10l.py new file mode 100644 index 0000000..1043211 --- /dev/null +++ b/tools/validate_retroarch_phase10l.py @@ -0,0 +1,236 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate the consumed Phase-1.0K result and Phase-1.0L analysis.""" + +from __future__ import annotations + +import argparse +import json +from pathlib import Path +import subprocess +from typing import Any + + +PHASE = "PHASE_1_0L_WRITE_FIREWALL_DEVICE_RESULT_ANALYSIS" +STATUS = "ONE_SHOT_AUTHORIZATION_CONSUMED_FIRST_WRITE_MKDIR" +SOURCE_COMMIT = "2e385265ab893dbba5898178dde5e108d3dbfa19" +ARTIFACT_SHA256 = "6ff0f7ea391da5f15ea43512a871078133e896a6900ae9f8f3fa75711abb8009" +TRACE_SHA256 = "d06613712df01a51ed4af83b96b844668456589783b6202d669d27b4fd6b915a" +STAGES = ["D00", "D01", "D02", "I00", "I01", "I02", "I03", "D13", "D12"] +AUTHORIZATION_FIELDS = ( + "ps5_connection_authorized", + "device_transfer_authorized", + "device_execution_authorized", + "result_receive_authorized", + "installation_authorized", + "autoload_authorized", + "device_write_authorized", + "automatic_retry", +) +DELIVERABLES = ( + "docs/retroarch/phase-1.0l-write-firewall-result-analysis.md", + "manifests/retroarch/phase-1.0l-write-firewall-result-analysis.json", + "tools/validate_retroarch_phase10l.py", + "tests/test_retroarch_phase10l.py", +) + + +def load_json(path: Path) -> dict[str, Any]: + value = json.loads(path.read_text(encoding="utf-8")) + if not isinstance(value, dict): + raise ValueError("Phase-1.0L manifest is not an object") + return value + + +def all_false(record: dict[str, Any], fields: tuple[str, ...]) -> bool: + return all(record.get(field) is False for field in fields) + + +def result_is_exact(record: dict[str, Any]) -> bool: + return ( + record.get("magic") == "CHD10J01" + and record.get("frame_size") == 64 + and record.get("frame_count") == len(STAGES) == 9 + and record.get("stages") == STAGES + and record.get("raw_results") + == { + "D02": 0, + "D13_first_blocked_write": 6, + "D13_write_block_count": 1, + "D12_shutdown_reason": 6, + "D12_first_error": 118, + } + and record.get("terminal_stage") == "D12" + and record.get("classification") + == "PRE_I04_FAIL_CLOSED_FIRST_WRITE_MKDIR" + ) + + +def transport_is_one_shot(record: dict[str, Any]) -> bool: + return ( + record.get("connect_count") == 1 + and record.get("sendall_count") == 1 + and record.get("bytes_sent") == 1845208 + and record.get("shutdown_write_count") == 1 + and record.get("recv_call_count") == 2 + and record.get("received_byte_count") == 741 + and record.get("raw_stream_stored_bytes") == 741 + and record.get("raw_stream_truncated") is False + and record.get("retry_count") == 0 + and record.get("reconnect_count") == 0 + and record.get("close_called") is True + and record.get("valid_terminal_frame_received") is True + ) + + +def source_binding_is_bounded(record: dict[str, Any]) -> bool: + return ( + record.get("first_blocked_write_operation") == "MKDIR" + and record.get("write_block_count") == 1 + and record.get("shutdown_reason") == "WRITE_FIREWALL" + and record.get("first_error") == "E118_WRITE_BLOCKED" + and record.get("runtime_path") == "UNOBSERVED" + and record.get("source_route") + == "CONFIG_SET_DEFAULTS_BUILTIN_PLAYLIST_DIRECTORY" + and record.get("source_line") == "configuration.c:3357" + and record.get("path_mkdir_cb_relocation") == "0x17c280 -> 0x15ae0" + and record.get("reached_i04") is False + and record.get("sdl_video") == "NOT_REACHED" + and record.get("videoout") == "NOT_REACHED" + and record.get("rendering") == "NOT_REACHED" + and record.get("complete_cleanup") == "UNPROVEN" + and record.get("safe_exit") == "UNPROVEN" + ) + + +def git_show(root: Path, commit: str, path: str) -> str: + return subprocess.run( + ["git", "show", f"{commit}:{path}"], + cwd=root, + capture_output=True, + text=True, + check=True, + ).stdout + + +def source_evidence_is_exact(retroarch_root: Path) -> bool: + configuration = git_show(retroarch_root, SOURCE_COMMIT, "configuration.c") + smoke_c = git_show( + retroarch_root, SOURCE_COMMIT, "frontend/drivers/platform_ps5_smoke.c" + ) + smoke_h = git_show( + retroarch_root, SOURCE_COMMIT, "frontend/drivers/platform_ps5_smoke.h" + ) + retroarch = git_show(retroarch_root, SOURCE_COMMIT, "retroarch.c") + return ( + "void config_load(void *data)" in configuration + and "config_set_defaults(global);" in configuration + and "if (!path_is_directory(new_path))\n path_mkdir(new_path);" + in configuration + and "CHIMERA_REJECT_INT_WRAPPER(mkdir, CHIMERA_SMOKE_WRITE_MKDIR" + in smoke_c + and "CHIMERA_SMOKE_WRITE_MKDIR" in smoke_h + and "CHIMERA_PS5_DIAG_D13" in smoke_c + and "if (chimera_ps5_smoke_get_status()->shutdown_requested)" + in retroarch + ) + + +def validate(root: Path, retroarch_root: Path) -> list[str]: + errors: list[str] = [] + for relative in DELIVERABLES: + if not (root / relative).is_file(): + errors.append(f"missing deliverable: {relative}") + if not (retroarch_root / "docs/ps5-phase10k-device-result.md").is_file(): + errors.append("missing sibling consumed-result document") + try: + record = load_json( + root / "manifests/retroarch/phase-1.0l-write-firewall-result-analysis.json" + ) + except (OSError, ValueError, json.JSONDecodeError) as error: + return errors + [str(error)] + if record.get("phase") != PHASE or record.get("status") != STATUS: + errors.append("phase/status mismatch") + artifact = record.get("artifact", {}) + if not ( + artifact.get("size") == 1845208 + and artifact.get("sha256") == ARTIFACT_SHA256 + and artifact.get("execution_eligible") is False + and artifact.get("transfer_eligible") is False + and artifact.get("installation_eligible") is False + and artifact.get("tracked") is False + ): + errors.append("artifact identity or ineligible state mismatch") + authorization = record.get("authorization", {}) + if not ( + authorization.get("consumed") is True + and authorization.get("authority_inherited_by_future_action") is False + ): + errors.append("authorization is reusable") + if not all_false(record.get("current_authorizations", {}), AUTHORIZATION_FIELDS): + errors.append("a current authorization remains active") + actions = record.get("performed_actions", {}) + if not ( + actions.get("ps5_connected") is True + and actions.get("files_transferred") is True + and actions.get("target_execution_performed") is True + and actions.get("result_received_from_device") is True + and actions.get("device_filesystem_write_performed") is False + and actions.get("normal_sdk_crt_effects") == "INHERITED_PER_ADR_0010" + and actions.get("retry_performed") is False + and actions.get("reconnect_performed") is False + and actions.get("sdl_entered") is False + and actions.get("videoout_entered") is False + ): + errors.append("performed action record is incomplete or overclaims") + if not transport_is_one_shot(record.get("transport", {})): + errors.append("transport is not exact one-shot") + trace = record.get("trace", {}) + if not ( + trace.get("tracked") is False + and trace.get("size") == 5841 + and trace.get("sha256") == TRACE_SHA256 + and trace.get("parser_errors") == [] + ): + errors.append("ignored trace identity/parser result mismatch") + if not result_is_exact(record.get("protocol_result", {})): + errors.append("protocol result mismatch") + if not source_binding_is_bounded(record.get("source_binding", {})): + errors.append("source binding overclaims or omits a boundary") + candidate = record.get("next_offline_candidate", {}) + if not ( + candidate.get("preserve_path_derivation") is True + and candidate.get("preserve_global_write_firewall") is True + and candidate.get("target_build_authorized") is False + and candidate.get("device_action_authorized") is False + ): + errors.append("next offline candidate weakens a safety gate") + try: + if not source_evidence_is_exact(retroarch_root): + errors.append("source evidence is incomplete") + except subprocess.CalledProcessError as error: + errors.append(f"cannot inspect exact artifact source: {error}") + tracked = subprocess.run( + ["git", "ls-files"], cwd=root, capture_output=True, text=True, check=True + ).stdout.splitlines() + if any(path.lower().endswith((".elf", ".self", ".sprx", ".pkg", ".map")) for path in tracked): + errors.append("target artifact is tracked") + return errors + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + parser.add_argument("--retroarch-root", type=Path, required=True) + args = parser.parse_args() + errors = validate(args.root.resolve(), args.retroarch_root.resolve()) + if errors: + for error in errors: + print(f"ERROR: {error}") + return 1 + print("Phase-1.0L consumed write-firewall result validation passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/validate_retroarch_phase10m.py b/tools/validate_retroarch_phase10m.py new file mode 100644 index 0000000..a746172 --- /dev/null +++ b/tools/validate_retroarch_phase10m.py @@ -0,0 +1,259 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate the offline-only Phase-1.0M write-free defaults artifact.""" + +from __future__ import annotations + +import argparse +import json +from pathlib import Path +import subprocess +from typing import Any + + +PHASE = "PHASE_1_0M_WRITE_FREE_DEFAULTS" +STATUS = "OFFLINE_ARTIFACT_AUDITED_DEVICE_ACTION_BLOCKED" +SOURCE_COMMIT = "12cf1d783c41eb303987e49a5a920805a59ef7a4" +ARTIFACT_SHA256 = "c99a0856309a357ad2667d89b4924e4063ad214cae09c8a419457b0732f583cd" +MAP_SHA256 = "6768ffc7267b9b362c3b953571e5dfeed1e70004a3e19f41ef500c3f26272719" +DISASSEMBLY_SHA256 = "141a620484784e5e01ec0cb2a51f77b67712bc484aae6de0f139ba1e28922c87" +AUTHORIZATION_FIELDS = ( + "ps5_connection_authorized", "device_transfer_authorized", + "device_execution_authorized", "result_receive_authorized", + "target_build_authorized", "installation_authorized", + "autoload_authorized", "device_write_authorized", "automatic_retry", +) +DEVICE_ACTION_FIELDS = ( + "ps5_connected", "device_request_performed", "files_transferred", + "target_execution_performed", "result_received_from_device", + "device_write_performed", "installation_performed", "autoload_performed", + "retry_performed", "reconnect_performed", +) +DELIVERABLES = ( + "docs/retroarch/phase-1.0m-write-free-defaults.md", + "manifests/retroarch/phase-1.0m-write-free-defaults.json", + "packaging/retroarch/phase10m/SHA256SUMS.txt", + "tools/validate_retroarch_phase10m.py", + "tests/test_retroarch_phase10m.py", +) + + +def load_json(path: Path) -> dict[str, Any]: + value = json.loads(path.read_text(encoding="utf-8")) + if not isinstance(value, dict): + raise ValueError("Phase-1.0M manifest is not an object") + return value + + +def all_false(record: dict[str, Any], fields: tuple[str, ...]) -> bool: + return all(record.get(field) is False for field in fields) + + +def artifact_is_exact_and_ineligible(record: dict[str, Any]) -> bool: + return ( + record.get("name") == "retroarch_ps5_write_diag.elf" + and record.get("size") == 1845208 + and record.get("sha256") == ARTIFACT_SHA256 + and record.get("tracked") is False + and record.get("execution_eligible") is False + and record.get("transfer_eligible") is False + and record.get("installation_eligible") is False + ) + + +def correction_is_narrow(record: dict[str, Any]) -> bool: + return ( + record.get("guard") == "!defined(CHIMERA_PS5_NO_FILESYSTEM_WRITES)" + and record.get("skipped_under_write_free") + == ["path_is_directory", "path_mkdir"] + and record.get("path_derivation_preserved") is True + and record.get("legacy_migration_compiled") is False + and record.get("have_configfile") is False + and record.get("config_set_defaults_path_mkdir_calls") == 0 + and record.get("generic_path_mkdir_symbol_retained") is True + and record.get("runtime_effect") == "UNPROVEN" + ) + + +def firewall_is_preserved(record: dict[str, Any]) -> bool: + return ( + record.get("linker_wrap_option_count") == 17 + and record.get("all_linker_wrap_options_preserved") is True + and record.get("first_rejection_requests_shutdown") is True + and record.get("stop_before_i04_on_rejection") is True + and record.get("wrapper_errno") == "EROFS" + and record.get("surviving_wrappers") == [ + {"symbol": "__wrap_open", "address": "0x475b0", "operation": "OPEN"}, + {"symbol": "__wrap_fopen", "address": "0x47620", "operation": "OPEN"}, + {"symbol": "__wrap_fwrite", "address": "0x476b0", "operation": "STREAM"}, + ] + ) + + +def audit_is_exact(record: dict[str, Any]) -> bool: + return ( + record.get("entry_point") == "0xff220" + and record.get("load_segment_permissions") == ["R_E", "R", "RW"] + and record.get("rwx_load_segments") == 0 + and record.get("init_array_size") == 0 + and record.get("fini_array_size") == 0 + and record.get("tls_sections") == 0 + and record.get("bss_size") == 697632 + and record.get("undefined_dynamic_symbols") == 142 + and record.get("relocations") == { + "total": 1055, + "R_X86_64_GLOB_DAT": 142, + "R_X86_64_RELATIVE": 913, + } + and record.get("dt_needed") == [ + "libSceAudioOut.sprx", + "libSceLibcInternal.sprx", + "libScePad.sprx", + "libSceSystemService.sprx", + "libSceUserService.sprx", + "libSceVideoOut.sprx", + "libkernel_web.sprx", + ] + and record.get("inherited_send_import_count") == 1 + and all(record.get(field) is False for field in ( + "socket_create_import", "connect_import", "listener_import", + "receive_import", + )) + and record.get("gnm_imports") == [] + and record.get("normalized_disassembly_sha256") == DISASSEMBLY_SHA256 + and record.get("hardware_evidence") is False + ) + + +def source_is_exact(retroarch_root: Path) -> bool: + source = subprocess.run( + ["git", "show", f"{SOURCE_COMMIT}:configuration.c"], + cwd=retroarch_root, capture_output=True, text=True, check=True, + ).stdout + makefile = subprocess.run( + ["git", "show", f"{SOURCE_COMMIT}:Makefile.ps5"], + cwd=retroarch_root, capture_output=True, text=True, check=True, + ).stdout + block = """ fill_pathname_join_special( + new_path, + settings->paths.directory_playlist, + FILE_PATH_BUILTIN, + sizeof(new_path)); + +#if !defined(CHIMERA_PS5_NO_FILESYSTEM_WRITES) + if (!path_is_directory(new_path)) + path_mkdir(new_path); +#endif""" + wraps = ( + "open", "openat", "fopen", "write", "fwrite", "creat", "freopen", + "tmpfile", "mkstemp", "mkdir", "rename", "unlink", "remove", "rmdir", + "chmod", "chown", "ftruncate", + ) + return ( + block in source + and "-DCHIMERA_PS5_NO_FILESYSTEM_WRITES" in makefile + and all(f"--wrap={name}" in makefile for name in wraps) + ) + + +def validate(root: Path, retroarch_root: Path) -> list[str]: + errors: list[str] = [] + for relative in DELIVERABLES: + if not (root / relative).is_file(): + errors.append(f"missing deliverable: {relative}") + if not (retroarch_root / "docs/ps5-phase10m-write-free-defaults.md").is_file(): + errors.append("missing sibling Phase-1.0M audit") + try: + record = load_json( + root / "manifests/retroarch/phase-1.0m-write-free-defaults.json" + ) + except (OSError, ValueError, json.JSONDecodeError) as error: + return errors + [str(error)] + if record.get("phase") != PHASE or record.get("status") != STATUS: + errors.append("phase/status mismatch") + if not artifact_is_exact_and_ineligible(record.get("artifact", {})): + errors.append("artifact identity or eligibility mismatch") + linker_map = record.get("linker_map", {}) + if not ( + linker_map.get("size") == 637728 + and linker_map.get("sha256") == MAP_SHA256 + and linker_map.get("tracked") is False + ): + errors.append("linker-map identity mismatch") + if not correction_is_narrow(record.get("source_correction", {})): + errors.append("source correction is widened or overclaimed") + if not firewall_is_preserved(record.get("write_firewall", {})): + errors.append("global write firewall is incomplete") + if not audit_is_exact(record.get("artifact_audit", {})): + errors.append("artifact audit is incomplete or overclaims") + reproducibility = record.get("reproducibility", {}) + if not ( + reproducibility.get("clean_builds") == 2 + and reproducibility.get("elf_byte_identical") is True + and reproducibility.get("map_byte_identical") is True + and reproducibility.get("normalized_disassembly_identical") is True + and reproducibility.get("warnings_as_errors_for_retroarch_target") is True + ): + errors.append("reproducibility record is incomplete") + runtime = record.get("inherited_runtime_contract", {}) + if not ( + runtime.get("normal_sdk_crt_effects") == "INHERITED_PER_ADR_0010" + and runtime.get("live_runner_authorizes_artifact") is False + and all(runtime.get(field) == "UNPROVEN" for field in ( + "i04", "sdl", "videoout", "visible_flip", "complete_cleanup", + )) + ): + errors.append("runtime contract invents progress or omits CRT effects") + actions = record.get("phase_actions", {}) + if not all_false(actions, DEVICE_ACTION_FIELDS): + errors.append("Phase-1.0M records a device action") + if actions.get("target_build_performed") is not True or actions.get("target_artifact_created") is not True: + errors.append("offline target build is not recorded") + if not all_false(record.get("current_authorizations", {}), AUTHORIZATION_FIELDS): + errors.append("a current authorization is active") + tests = record.get("tests", {}) + if not ( + tests.get("chimera_gfx_ctest") == "54_OF_54_PASS" + and tests.get("retroarch_ps5_host_suite") == "PASS_WITH_ASAN_UBSAN" + and tests.get("phase10m_gfx_guardrails") == 20 + and tests.get("phase10m_retroarch_guardrails") == 6 + and tests.get("safety_audit") == "PASS" + and tests.get("chimera_gfx_secret_scan") == "WHOLE_TREE_PASS" + and tests.get("retroarch_secret_scan") == "PHASE_CHANGED_FILES_PASS" + and tests.get("retroarch_whole_tree_scan") + == "PREEXISTING_UPSTREAM_TEST_KEY_FIXTURES_FLAGGED" + and tests.get("hardware_evidence_from_phase10m") is False + ): + errors.append("test evidence is incomplete or promoted to hardware evidence") + try: + if not source_is_exact(retroarch_root): + errors.append("sibling source guard or write firewall mismatch") + except subprocess.CalledProcessError as error: + errors.append(f"cannot inspect exact sibling source: {error}") + sums = (root / "packaging/retroarch/phase10m/SHA256SUMS.txt").read_text(encoding="utf-8") + if ARTIFACT_SHA256 not in sums or MAP_SHA256 not in sums: + errors.append("checksum record does not bind both ignored outputs") + tracked = subprocess.run( + ["git", "ls-files"], cwd=root, capture_output=True, text=True, check=True + ).stdout.splitlines() + if any(path.lower().endswith((".elf", ".self", ".sprx", ".pkg", ".map")) for path in tracked): + errors.append("target artifact or map is tracked") + return errors + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + parser.add_argument("--retroarch-root", type=Path, required=True) + args = parser.parse_args() + errors = validate(args.root.resolve(), args.retroarch_root.resolve()) + if errors: + for error in errors: + print(f"ERROR: {error}") + return 1 + print("Phase-1.0M offline write-free defaults validation passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/validate_retroarch_phase10n.py b/tools/validate_retroarch_phase10n.py new file mode 100644 index 0000000..a46ebeb --- /dev/null +++ b/tools/validate_retroarch_phase10n.py @@ -0,0 +1,241 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate the inactive Phase-1.0N one-shot runner contract.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +from pathlib import Path +import subprocess +from typing import Any + + +PHASE = "PHASE_1_0N_WRITE_FREE_DEFAULTS_ONE_SHOT_RUNNER" +STATUS = "OFFLINE_RUNNER_PREPARED_NO_DEVICE_AUTHORIZATION" +ARTIFACT_SHA256 = "c99a0856309a357ad2667d89b4924e4063ad214cae09c8a419457b0732f583cd" +RUNNER_SHA256 = "1d46510369349c1e75ef3b5f983a2ef6fa5896fdd4398788bf58708821d0385d" +RUNNER_COMMIT = "606909706f91d7213751c245081333f56c2cce89" +WIRE_STAGES = ( + tuple(f"D{index:02d}" for index in range(13)) + + tuple(f"I{index:02d}" for index in range(15)) + + ("C1", "D13") +) +AUTHORIZATION_FIELDS = ( + "ps5_connection_authorized", "device_transfer_authorized", + "device_execution_authorized", "result_receive_authorized", + "installation_authorized", "autoload_authorized", + "device_write_authorized", "automatic_retry", +) +ACTION_FIELDS = ( + "ps5_connected", "device_request_performed", "files_transferred", + "target_execution_performed", "result_received_from_device", + "target_build_performed", "target_artifact_created", + "device_write_performed", "installation_performed", "autoload_performed", + "retry_performed", "reconnect_performed", +) +DELIVERABLES = ( + "docs/retroarch/phase-1.0n-inactive-one-shot-runner.md", + "docs/approvals/phase-1.0n-write-free-one-shot-template.md", + "manifests/retroarch/phase-1.0n-write-free-one-shot-runner.json", + "manifests/retroarch/phase-1.0n-one-shot-approval-template.json", + "packaging/retroarch/phase10n/SHA256SUMS.txt", + "tools/validate_retroarch_phase10n.py", + "tests/test_retroarch_phase10n.py", +) + + +def load_json(path: Path) -> dict[str, Any]: + value = json.loads(path.read_text(encoding="utf-8")) + if not isinstance(value, dict): + raise ValueError(f"{path} is not a JSON object") + return value + + +def all_false(record: dict[str, Any], fields: tuple[str, ...]) -> bool: + return all(record.get(field) is False for field in fields) + + +def artifact_is_ineligible(record: dict[str, Any]) -> bool: + return ( + record.get("name") == "retroarch_ps5_write_diag.elf" + and record.get("profile") == "write-diag" + and record.get("size") == 1845208 + and record.get("sha256") == ARTIFACT_SHA256 + and record.get("source_unchanged_from_phase10m") is True + and record.get("execution_eligible") is False + and record.get("transfer_eligible") is False + and record.get("installation_eligible") is False + and record.get("tracked") is False + ) + + +def protocol_is_exact_and_inactive(record: dict[str, Any]) -> bool: + return ( + record.get("name") == "PHASE_1_0N_WRITE_FREE_DEFAULTS" + and record.get("magic") == "CHD10J01" + and record.get("version") == 1 + and record.get("frame_size") == 64 + and record.get("byte_order") == "BIG_ENDIAN" + and record.get("wire_stages") == list(WIRE_STAGES) + and record.get("c1_wire_index") == 28 + and record.get("d13_wire_index") == 29 + and record.get("terminal_stage") == "D12" + and record.get("protocol_activation_authorized") is False + and record.get("tracked_target") is None + and record.get("tracked_port") is None + and record.get("tracked_run_id") is None + ) + + +def runner_is_fail_closed(record: dict[str, Any]) -> bool: + return ( + record.get("implementation_available") is True + and record.get("source_size") == 21179 + and record.get("source_sha256") == RUNNER_SHA256 + and record.get("protocol_selection") == "MANIFEST_ONLY" + and record.get("free_protocol_selector") is False + and record.get("active_manifest_required") is True + and record.get("separate_untracked_approval_required") is True + and record.get("actual_artifact_rehashed_before_socket") is True + and record.get("attempt_receipt_required") is True + and record.get("attempt_receipt_exclusive_create") is True + and record.get("attempt_receipt_durable_fsync") is True + and record.get("attempt_receipt_written_before_connect") is True + and all(record.get(field) == 1 for field in ( + "maximum_connections", "maximum_transfers", "maximum_executions", + "maximum_result_receives", + )) + and record.get("receive_limit_bytes") == 65536 + and all(record.get(field) is False for field in ( + "retry", "reconnect", "resume", "trace_overwrite", + )) + and record.get("trace_exclusive_create") is True + ) + + +def approval_is_inactive(record: dict[str, Any]) -> bool: + return ( + record.get("phase") == PHASE + and record.get("authorized") is False + and record.get("consumed") is False + and record.get("authorization_scope") == "EXACT_ONE_SHOT_PHASE_1_0N" + and record.get("authorized_by") is None + and record.get("approval_reference") is None + and record.get("protocol_magic") == "CHD10J01" + and record.get("run_id") is None + and record.get("target") is None + and record.get("port") is None + and record.get("artifact_sha256") == ARTIFACT_SHA256 + and all(record.get(field) == 0 for field in ( + "connection_count", "transfer_count", "execution_count", + "result_receive_count", + )) + and all(record.get(field) is False for field in ( + "installation", "autoload", "device_write", "retry", + "reconnect", "resume", "reboot", + )) + ) + + +def git(root: Path, *args: str) -> str: + result = subprocess.run( + ["git", *args], cwd=root, capture_output=True, text=True, check=False) + if result.returncode: + raise RuntimeError(result.stderr.strip() or "git failed") + return result.stdout.strip() + + +def git_bytes(root: Path, *args: str) -> bytes: + result = subprocess.run(["git", *args], cwd=root, capture_output=True, check=False) + if result.returncode: + raise RuntimeError(result.stderr.decode(errors="replace").strip() or "git failed") + return result.stdout + + +def validate(root: Path, retroarch_root: Path | None = None) -> list[str]: + errors: list[str] = [] + for relative in DELIVERABLES: + if not (root / relative).is_file(): + errors.append(f"missing deliverable: {relative}") + try: + record = load_json(root / "manifests/retroarch/phase-1.0n-write-free-one-shot-runner.json") + approval = load_json(root / "manifests/retroarch/phase-1.0n-one-shot-approval-template.json") + except (OSError, ValueError, json.JSONDecodeError) as error: + return errors + [str(error)] + if record.get("phase") != PHASE or record.get("status") != STATUS: + errors.append("phase/status mismatch") + commits = record.get("source_commits", {}) + if commits.get("retroarch_runner") != RUNNER_COMMIT: + errors.append("runner commit mismatch") + if not all_false(record.get("authorizations", {}), AUTHORIZATION_FIELDS): + errors.append("an authorization is active") + if not all_false(record.get("phase_actions", {}), ACTION_FIELDS): + errors.append("Phase-1.0N records a target or device action") + if not artifact_is_ineligible(record.get("artifact", {})): + errors.append("artifact is not exact and ineligible") + if not protocol_is_exact_and_inactive(record.get("result_protocol", {})): + errors.append("protocol is misindexed, mislabeled or active") + if not runner_is_fail_closed(record.get("runner", {})): + errors.append("runner is widened or incomplete") + if not approval_is_inactive(approval): + errors.append("tracked approval template is active or incomplete") + activation = record.get("activation_requirements", {}) + if (activation.get("current_requirements_satisfied") is not False + or activation.get("phase10k_authority_reusable") is not False): + errors.append("activation or consumed-authority boundary is widened") + tests = record.get("tests", {}) + if not ( + tests.get("retroarch_duplex_python_cases") == 28 + and tests.get("retroarch_phase10n_guardrails") == 5 + and tests.get("chimera_gfx_ctest") == "56_OF_56_PASS" + and tests.get("chimera_gfx_phase10n_guardrails") == 20 + and tests.get("fake_socket_only") is True + and tests.get("hardware_evidence_from_phase10n") is False + ): + errors.append("test evidence is incomplete or promoted") + sums = (root / "packaging/retroarch/phase10n/SHA256SUMS.txt").read_text(encoding="utf-8") + if ARTIFACT_SHA256 not in sums or RUNNER_SHA256 not in sums: + errors.append("checksum record is incomplete") + tracked = git(root, "ls-files").splitlines() + if any(path.lower().endswith((".elf", ".self", ".sprx", ".pkg", ".map")) for path in tracked): + errors.append("target artifact or map is tracked") + if retroarch_root is not None: + try: + git(retroarch_root, "cat-file", "-e", f"{RUNNER_COMMIT}^{{commit}}") + runner_bytes = git_bytes( + retroarch_root, "show", f"{RUNNER_COMMIT}:tools/ps5_diag_duplex.py") + runner = runner_bytes.decode("utf-8") + if len(runner_bytes) != 21179 or hashlib.sha256(runner_bytes).hexdigest() != RUNNER_SHA256: + errors.append("runner source identity mismatch") + for token in ( + 'WRITE_FREE_ACTIVE_PHASE = "PHASE_1_0N_WRITE_FREE_DEFAULTS_ONE_SHOT_RUNNER"', + '"PHASE_1_0N_WRITE_FREE_DEFAULTS", WRITE_DIAG_MAGIC, WRITE_DIAG_STAGES', + 'expected_scope = "EXACT_ONE_SHOT_PHASE_1_0N"', + ): + if token not in runner: + errors.append(f"runner source omits {token}") + if 'parser.add_argument("--protocol"' in runner: + errors.append("runner source has a free protocol selector") + except RuntimeError as error: + errors.append(str(error)) + return errors + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + parser.add_argument("--retroarch-root", type=Path) + args = parser.parse_args() + errors = validate(args.root.resolve(), args.retroarch_root) + if errors: + for error in errors: + print(f"ERROR: {error}") + return 1 + print("Phase-1.0N inactive one-shot runner validation passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/validate_retroarch_phase10o.py b/tools/validate_retroarch_phase10o.py new file mode 100644 index 0000000..3e76062 --- /dev/null +++ b/tools/validate_retroarch_phase10o.py @@ -0,0 +1,240 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate the consumed Phase-1.0O device-result evidence record.""" + +from __future__ import annotations + +import argparse +import json +from pathlib import Path +import subprocess +from typing import Any + + +PHASE = "PHASE_1_0O_WRITE_FREE_DEFAULTS_ONE_SHOT_DEVICE_RESULT" +STATUS = "ONE_SHOT_AUTHORIZATION_CONSUMED_I04_REACHED_FLIP_SUBMIT_FAILED" +SOURCE_COMMIT = "12cf1d783c41eb303987e49a5a920805a59ef7a4" +RUNNER_COMMIT = "606909706f91d7213751c245081333f56c2cce89" +ARTIFACT_SHA256 = "c99a0856309a357ad2667d89b4924e4063ad214cae09c8a419457b0732f583cd" +TRACE_SHA256 = "3d0b8811ae11f5cac2c2d331e252e1789a10588cab0e6045828a6b6af0fe1eb6" +STAGES = ( + "D00", "D01", "D02", "I00", "I01", "I02", "I03", "I04", + "I05", "I06", "I07", "I08", "I09", "I10", "D10", "I11", + "I12", "I13", "I14", "D03", "D05", "D06", "D07", "D12", "D04", +) +AUTHORIZATION_FIELDS = ( + "ps5_connection_authorized", "device_transfer_authorized", + "device_execution_authorized", "result_receive_authorized", + "installation_authorized", "autoload_authorized", + "device_write_authorized", "automatic_retry", +) + + +def load_json(path: Path) -> dict[str, Any]: + value = json.loads(path.read_text(encoding="utf-8")) + if not isinstance(value, dict): + raise ValueError("result manifest is not an object") + return value + + +def all_false(record: dict[str, Any], fields: tuple[str, ...]) -> bool: + return all(record.get(field) is False for field in fields) + + +def artifact_is_consumed_and_ineligible(record: dict[str, Any]) -> bool: + return ( + record.get("name") == "retroarch_ps5_write_diag.elf" + and record.get("size") == 1845208 + and record.get("sha256") == ARTIFACT_SHA256 + and record.get("execution_eligible") is False + and record.get("transfer_eligible") is False + and record.get("installation_eligible") is False + and record.get("tracked") is False + ) + + +def authorization_is_consumed(record: dict[str, Any]) -> bool: + return ( + record.get("run_id") == "phase10o-20260722-write-free-01" + and record.get("active_manifest_sha256") + == "64bb022b2c7de5627c42800406c5db444611a89e98fbfb99ca301e7a90fbfbef" + and record.get("local_approval_sha256") + == "0dc47462e4d5be47def77195aca74c4e320d4c21b45c4decbc319f6d16dbf064" + and record.get("attempt_receipt_sha256") + == "92ba19c3306f99ec73d269f62d7b63c433b83f9a358f6f43faf1c41875613fbb" + and record.get("consumed") is True + and record.get("authority_inherited_by_future_action") is False + ) + + +def transport_is_exact_one_shot(record: dict[str, Any]) -> bool: + return ( + record.get("connect_count") == 1 + and record.get("sendall_count") == 1 + and record.get("bytes_sent") == 1845208 + and record.get("shutdown_write_count") == 1 + and record.get("recv_call_count") == 14 + and record.get("received_byte_count") == 3953 + and record.get("raw_stream_stored_bytes") == 3953 + and record.get("raw_stream_sha256") + == "61941124ea06b22f8e27df795705329bd5b39e76b86770872dbbf4a611d02c1e" + and record.get("raw_stream_truncated") is False + and record.get("remote_eof_observed") is True + and record.get("retry_count") == 0 + and record.get("reconnect_count") == 0 + and record.get("close_called") is True + ) + + +def protocol_result_is_exact(record: dict[str, Any]) -> bool: + raw = record.get("raw_results", {}) + return ( + record.get("name") == "PHASE_1_0N_WRITE_FREE_DEFAULTS" + and record.get("magic") == "CHD10J01" + and record.get("frame_size") == 64 + and record.get("frame_count") == 25 + and record.get("stages") == list(STAGES) + and raw == { + "D02_platform_init": 0, + "D10_core_init": 1, + "I11_core_init": 0, + "D05_videoout_handle": 1309671680, + "D06_buffer_registration": 0, + "D07_flip_submit": -1, + "D07_saved_errno": 0, + "D12_shutdown_reason": 5, + "D12_first_error": 104, + "D04_sdl_init": -1, + } + and record.get("terminal_flag_frame") == "D12_SEQUENCE_24" + and record.get("frame_after_terminal") == "D04_SEQUENCE_25" + and record.get("valid_terminal_frame_received_classification") is False + and record.get("remote_eof_after_frames") is True + ) + + +def source_binding_is_bounded(record: dict[str, Any]) -> bool: + return ( + record.get("phase10m_write_free_correction_passed_i04") is True + and record.get("write_firewall_triggered") is False + and record.get("d13_present") is False + and record.get("sdl_video_entry_reached") is True + and record.get("videoout_open_returned_positive_handle") is True + and record.get("videoout_buffer_registration_raw") == 0 + and record.get("diagnostic_pattern_copied_before_submit") is True + and record.get("first_flip_call") == "sceVideoOutSubmitFlip(handle,0,1,0)" + and record.get("first_flip_submit_raw") == -1 + and record.get("first_flip_saved_errno") == 0 + and record.get("first_flip_wait_called") is False + and record.get("e104_is_generic_framebuffer_fail_label_after_early_flip_failure") is True + and record.get("sdl_init_raw") == -1 + and all(record.get(field) == "UNPROVEN" for field in ( + "visible_presentation", "complete_cleanup", "safe_exit", + )) + ) + + +def git(root: Path, *args: str) -> str: + result = subprocess.run( + ["git", *args], cwd=root, capture_output=True, text=True, check=False) + if result.returncode: + raise RuntimeError(result.stderr.strip() or "git failed") + return result.stdout + + +def validate(root: Path, retroarch_root: Path | None = None) -> list[str]: + errors: list[str] = [] + path = root / "manifests/retroarch/phase-1.0o-write-free-device-result.json" + try: + record = load_json(path) + except (OSError, ValueError, json.JSONDecodeError) as error: + return [str(error)] + if record.get("phase") != PHASE or record.get("status") != STATUS: + errors.append("phase/status mismatch") + if record.get("source_commit") != SOURCE_COMMIT or record.get("runner_commit") != RUNNER_COMMIT: + errors.append("source or runner commit mismatch") + if not artifact_is_consumed_and_ineligible(record.get("artifact", {})): + errors.append("artifact identity or post-run eligibility mismatch") + if not authorization_is_consumed(record.get("authorization", {})): + errors.append("one-shot authorization is not immutably consumed") + if not all_false(record.get("current_authorizations", {}), AUTHORIZATION_FIELDS): + errors.append("a current authorization remains active") + actions = record.get("performed_actions", {}) + if not all(actions.get(field) is True for field in ( + "ps5_connected", "device_request_performed", "files_transferred", + "target_execution_performed", "result_received_from_device", + "display_memory_mutated_by_diagnostic_pattern", + "videoout_flip_submit_attempted", + )) or not all(actions.get(field) is False for field in ( + "persistent_staging_performed", "installation_performed", + "autoload_performed", "retry_performed", "reconnect_performed", + "videoout_flip_submit_succeeded", + )): + errors.append("performed action boundary mismatch") + if not transport_is_exact_one_shot(record.get("transport", {})): + errors.append("transport is not exact one-shot evidence") + trace = record.get("trace", {}) + if not ( + trace.get("tracked") is False and trace.get("size") == 18031 + and trace.get("sha256") == TRACE_SHA256 + and trace.get("parser_errors") == [] + and trace.get("runner_terminal_classification") + == "INCOMPLETE_LAST_FRAME_AFTER_TERMINAL" + and trace.get("individual_frames_usable_as_device_evidence") is True + ): + errors.append("trace boundary is incomplete or promoted") + if not protocol_result_is_exact(record.get("protocol_result", {})): + errors.append("protocol result does not match the immutable trace") + if not source_binding_is_bounded(record.get("source_binding", {})): + errors.append("source inference is broadened or incomplete") + tests = record.get("tests", {}) + if not ( + tests.get("chimera_gfx_ctest") == "58_OF_58_PASS" + and tests.get("phase10o_guardrails") == 20 + and tests.get("device_result_not_generated_by_host_test") is True + ): + errors.append("test evidence is incomplete or promoted") + tracked = git(root, "ls-files").splitlines() + if any(path.lower().endswith((".elf", ".self", ".sprx", ".pkg", ".map")) for path in tracked): + errors.append("target artifact or map is tracked") + if retroarch_root is not None: + try: + patch = git(retroarch_root, "show", f"{SOURCE_COMMIT}:pkg/ps5/sdl2-ps5-early-diag.patch") + platform = git(retroarch_root, "show", f"{SOURCE_COMMIT}:frontend/drivers/platform_ps5_smoke.c") + configuration = git(retroarch_root, "show", f"{SOURCE_COMMIT}:configuration.c") + runner = git(retroarch_root, "show", f"{RUNNER_COMMIT}:tools/ps5_diag_duplex.py") + for token in ( + "CHIMERA_PS5_FIRST_FRAME_INDEX 0u", + "sceVideoOutSubmitFlip(", + "submit_errno = submit_result != 0 ? errno : 0", + "goto framebuffer_fail", + ): + if token not in patch: + errors.append(f"SDL source binding missing: {token}") + if "CHIMERA_SMOKE_E104_FRAMEBUFFER_ALLOC" not in platform: + errors.append("generic E104 source label is missing") + if "#if !defined(CHIMERA_PS5_NO_FILESYSTEM_WRITES)" not in configuration: + errors.append("write-free defaults correction is missing") + if "WRITE_FREE_ACTIVE_PHASE" not in runner or "EXACT_ONE_SHOT_PHASE_1_0N" not in runner: + errors.append("N runner source binding is missing") + except RuntimeError as error: + errors.append(str(error)) + return errors + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + parser.add_argument("--retroarch-root", type=Path) + args = parser.parse_args() + errors = validate(args.root.resolve(), args.retroarch_root) + if errors: + for error in errors: + print(f"ERROR: {error}") + return 1 + print("Phase-1.0O consumed device-result validation passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/validate_retroarch_phase10p.py b/tools/validate_retroarch_phase10p.py new file mode 100644 index 0000000..86f9fe7 --- /dev/null +++ b/tools/validate_retroarch_phase10p.py @@ -0,0 +1,256 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate the offline Phase-1.0P VideoOut submit analysis.""" + +from __future__ import annotations + +import argparse +import json +from pathlib import Path +import subprocess +from typing import Any + + +PHASE = "PHASE_1_0P_VIDEOOUT_SUBMIT_OFFLINE_ANALYSIS" +STATUS = "VIDEOOUT_SUBMIT_FAILURE_SITE_PROVEN_ROOT_CAUSE_UNRESOLVED" +ARTIFACT_SHA256 = "c99a0856309a357ad2667d89b4924e4063ad214cae09c8a419457b0732f583cd" +TRACE_SHA256 = "3d0b8811ae11f5cac2c2d331e252e1789a10588cab0e6045828a6b6af0fe1eb6" +RA_SOURCE = "12cf1d783c41eb303987e49a5a920805a59ef7a4" +RA_RUNNER = "606909706f91d7213751c245081333f56c2cce89" +SDK_COMMIT = "d2e2e585740362976a39fdd5ccf390f199a7bc37" +SDL_COMMIT = "0baf4ac49382b537ba449901b5b6d0d189bb1fbb" +AUTHORIZATION_FIELDS = ( + "ps5_connection_authorized", "device_transfer_authorized", + "device_execution_authorized", "result_receive_authorized", + "installation_authorized", "autoload_authorized", + "device_write_authorized", "automatic_retry", +) + + +def load_json(path: Path) -> dict[str, Any]: + value = json.loads(path.read_text(encoding="utf-8")) + if not isinstance(value, dict): + raise ValueError("analysis manifest is not an object") + return value + + +def all_false(record: dict[str, Any], fields: tuple[str, ...]) -> bool: + return all(record.get(field) is False for field in fields) + + +def bound_inputs_are_exact(record: dict[str, Any]) -> bool: + return ( + record.get("artifact_name") == "retroarch_ps5_write_diag.elf" + and record.get("artifact_size") == 1845208 + and record.get("artifact_sha256") == ARTIFACT_SHA256 + and record.get("trace_size") == 18031 + and record.get("trace_sha256") == TRACE_SHA256 + and record.get("linked_sdl_source_size") == 19642 + and record.get("linked_sdl_source_sha256") + == "9949a280fed40241746788a8c001280455bc629e0566a88f82f16a8634d43025" + and record.get("linker_map_size") == 637728 + and record.get("linker_map_sha256") + == "6768ffc7267b9b362c3b953571e5dfeed1e70004a3e19f41ef500c3f26272719" + ) + + +def exact_call_is_bounded(record: dict[str, Any]) -> bool: + return ( + record.get("symbol") == "sceVideoOutSubmitFlip" + and record.get("tuple") == "(handle,0,1,0)" + and record.get("function_address") == "0xfe560" + and record.get("call_address") == "0xfe7bc" + and record.get("got_address") == "0x1779b0" + and record.get("registers") + == {"edi": "handle", "esi": 0, "edx": 1, "ecx": 0} + and record.get("runtime_handle") == "0x4e100100" + and record.get("runtime_return") == -1 + and record.get("saved_errno") == 0 + and record.get("wait_called_after_failure") is False + ) + + +def abi_claims_fail_closed(record: dict[str, Any]) -> bool: + required_unproven = ( + "flip_mode_semantics", "frame_id_semantics", "buffer_zero_validity", + "buffer_attribute_semantics", "flip_master_or_active_app_requirement", + "return_code_semantics", + ) + return ( + record.get("sdk_exports_symbols_only") is True + and record.get("sdk_public_videoout_headers_present") is False + and record.get("sdl_fork_is_only_local_public_prototype_source") is True + and record.get("submit_signature_independently_corroborated") is False + and all(record.get(field) == "UNPROVEN" for field in required_unproven) + and record.get("errno_is_useful_for_observed_failure") is False + ) + + +def terminal_order_is_not_promoted(record: dict[str, Any]) -> bool: + return ( + record.get("observed") == ["D07", "D12", "D04"] + and record.get("source_deterministic") is True + and record.get("network_reordering_required") is False + and record.get("d12_meaning") == "FAILURE_SHUTDOWN_REQUEST" + and record.get("d12_proves_cleanup_complete") is False + and record.get("current_trace_valid_terminal_classification") is False + and record.get("host_parser_relaxation_allowed") is False + and record.get("future_protocol_requires_new_magic") is True + and record.get("future_terminal_after_d04_and_cleanup_evidence") is True + ) + + +def decision_is_fail_closed(record: dict[str, Any]) -> bool: + return ( + record.get("classification") == STATUS + and record.get("parameter_experiment_allowed") is False + and record.get("new_videoout_export_call_allowed") is False + and record.get("host_parser_promotion_allowed") is False + and record.get("phase10q_target_build_allowed") is False + and record.get("phase10q_device_action_allowed") is False + and record.get("required_next_evidence") + == "ACCEPTABLE_PUBLIC_VIDEOOUT_ABI_AND_RUNTIME_STATE_CONTRACT" + ) + + +def git(root: Path, *args: str) -> str: + result = subprocess.run( + ["git", *args], cwd=root, capture_output=True, text=True, check=False) + if result.returncode: + raise RuntimeError(result.stderr.strip() or "git failed") + return result.stdout + + +def validate_repository(root: Path, retroarch_root: Path, sdk_root: Path, + sdl_root: Path) -> list[str]: + errors: list[str] = [] + record = load_json( + root / "manifests/retroarch/phase-1.0p-videoout-submit-analysis.json") + if record.get("phase") != PHASE or record.get("status") != STATUS: + errors.append("phase/status mismatch") + commits = record.get("source_commits", {}) + expected = { + "chimera_gfx_phase10o": "af283ef93fae907e6c9c376df24d053ada3f7318", + "chimera_retroarch_artifact": RA_SOURCE, + "chimera_retroarch_runner": RA_RUNNER, + "ps5_payload_sdk_v0_41": SDK_COMMIT, + "public_ps5_sdl": SDL_COMMIT, + } + if commits != expected: + errors.append("source commit binding mismatch") + if not bound_inputs_are_exact(record.get("bound_inputs", {})): + errors.append("artifact, trace, source or map binding mismatch") + actions = record.get("offline_actions", {}) + if not all(actions.get(field) is True for field in ( + "source_inspected", "linker_map_inspected", "disassembly_inspected", + "dynamic_symbols_and_relocations_inspected", + )) or not all(actions.get(field) is False for field in ( + "target_source_changed", "target_artifact_created", "ps5_connected", + "device_transfer_performed", "target_execution_performed", + "result_received_from_device", + )): + errors.append("offline-only action boundary mismatch") + if not all_false(record.get("current_authorizations", {}), AUTHORIZATION_FIELDS): + errors.append("authorization remains active") + if not exact_call_is_bounded(record.get("exact_call", {})): + errors.append("exact submit call is not bounded") + registration = record.get("registration", {}) + if not ( + registration.get("function_address") == "0xfdc20" + and registration.get("runtime_return") == 0 + and registration.get("buffer_index_used") == 0 + and registration.get("opaque_layout_semantics") == "UNPROVEN" + ): + errors.append("registration evidence was broadened") + if not abi_claims_fail_closed(record.get("abi_evidence", {})): + errors.append("ABI uncertainty was promoted") + candidates = record.get("candidate_matrix", {}) + if not ( + candidates.get("diagnostic_normal_first_index_mismatch") + == "REJECTED_CURRENT_CAUSE" + and candidates.get("buffer_registration_failure") + == "REJECTED_BY_OBSERVED_RETURN_ZERO" + and candidates.get("errno_specific_failure_identity") == "ABSENT" + and all(candidates.get(field) == "UNPROVEN" for field in ( + "wrong_submit_tuple", "wrong_opaque_buffer_contract", + "missing_flip_master_or_app_state", "visible_flip", + "complete_cleanup", "safe_exit", + )) + ): + errors.append("candidate matrix overclaims a root cause") + if not terminal_order_is_not_promoted(record.get("terminal_ordering", {})): + errors.append("terminal ordering is promoted or incomplete") + if not decision_is_fail_closed(record.get("decision", {})): + errors.append("decision is not fail-closed") + tests = record.get("tests", {}) + if not ( + tests.get("chimera_gfx_ctest") == "60_OF_60_PASS" + and tests.get("phase10p_guardrails") == 20 + and tests.get("hardware_claim_from_host_test") is False + ): + errors.append("test evidence mismatch") + tracked = git(root, "ls-files").splitlines() + if any(path.lower().endswith((".elf", ".self", ".sprx", ".pkg", ".map")) + for path in tracked): + errors.append("target artifact or linker map is tracked") + try: + if git(retroarch_root, "rev-parse", "HEAD").strip() != RA_RUNNER: + errors.append("RetroArch HEAD mismatch") + patch = git(retroarch_root, "show", f"{RA_SOURCE}:pkg/ps5/sdl2-ps5-early-diag.patch") + diag = git(retroarch_root, "show", f"{RA_SOURCE}:pkg/ps5/chimera_ps5_diag.c") + gfx = git(retroarch_root, "show", f"{RA_SOURCE}:gfx/drivers/sdl2_gfx.c") + for token in ( + "CHIMERA_PS5_FIRST_FRAME_INDEX 0u", + "sceVideoOutSubmitFlip(", + "submit_errno = submit_result != 0 ? errno : 0", + "chimera_ps5_smoke_video_error(", + ): + if token not in patch: + errors.append(f"artifact source token missing: {token}") + if "stage == CHIMERA_PS5_DIAG_D12" not in diag: + errors.append("D12 terminal source missing") + if "chimera_ps5_early_diag_sdl_result(sdl_init_result)" not in gfx: + errors.append("D04 caller source missing") + if git(sdk_root, "rev-parse", "HEAD").strip() != SDK_COMMIT: + errors.append("SDK commit mismatch") + if git(sdl_root, "rev-parse", "HEAD").strip() != SDL_COMMIT: + errors.append("SDL commit mismatch") + if git(sdk_root, "status", "--porcelain"): + errors.append("SDK tree is dirty") + if git(sdl_root, "status", "--porcelain"): + errors.append("SDL tree is dirty") + stubs = (sdk_root / "sce_stubs/libSceVideoOut.c").read_text(encoding="utf-8") + header = (sdl_root / "src/video/ps5/SDL_ps5video.h").read_text(encoding="utf-8") + source = (sdl_root / "src/video/ps5/SDL_ps5video.c").read_text(encoding="utf-8") + if "sceVideoOutSubmitFlip" not in stubs: + errors.append("SDK submit export missing") + if "int sceVideoOutSubmitFlip(int, int, uint32_t, int64_t);" not in header: + errors.append("SDL submit prototype missing") + if "static uint32_t frame_id = 0;" not in source or \ + "sceVideoOutSubmitFlip(device_data->handle, idx, 1, frame_id)" not in source: + errors.append("public SDL first-frame source mismatch") + except (OSError, RuntimeError) as error: + errors.append(str(error)) + return errors + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + parser.add_argument("--retroarch-root", type=Path, required=True) + parser.add_argument("--sdk-root", type=Path, required=True) + parser.add_argument("--sdl-root", type=Path, required=True) + args = parser.parse_args() + errors = validate_repository( + args.root.resolve(), args.retroarch_root.resolve(), + args.sdk_root.resolve(), args.sdl_root.resolve()) + if errors: + for error in errors: + print(f"ERROR: {error}") + return 1 + print("Phase-1.0P offline VideoOut analysis validation passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/validate_retroarch_phase10q.py b/tools/validate_retroarch_phase10q.py new file mode 100644 index 0000000..28ec779 --- /dev/null +++ b/tools/validate_retroarch_phase10q.py @@ -0,0 +1,284 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate Phase-1.0Q public VideoOut evidence and fail-closed decision.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +from pathlib import Path +import subprocess +from typing import Any + + +PHASE = "PHASE_1_0Q_PUBLIC_VIDEOOUT_EVIDENCE" +STATUS = "PUBLIC_VIDEOOUT_EVIDENCE_INSUFFICIENT_PARAMETER_CHANGE_BLOCKED" +SDK_COMMIT = "d2e2e585740362976a39fdd5ccf390f199a7bc37" +SDL_COMMIT = "0baf4ac49382b537ba449901b5b6d0d189bb1fbb" +AUTHORIZATION_FIELDS = ( + "target_build_authorized", "ps5_connection_authorized", + "device_transfer_authorized", "device_execution_authorized", + "result_receive_authorized", "installation_authorized", + "autoload_authorized", "device_write_authorized", "automatic_retry", +) + + +def load_json(path: Path) -> dict[str, Any]: + value = json.loads(path.read_text(encoding="utf-8")) + if not isinstance(value, dict): + raise ValueError("evidence manifest is not an object") + return value + + +def sha256(path: Path) -> str: + digest = hashlib.sha256() + with path.open("rb") as stream: + for chunk in iter(lambda: stream.read(1024 * 1024), b""): + digest.update(chunk) + return digest.hexdigest() + + +def all_false(record: dict[str, Any], fields: tuple[str, ...]) -> bool: + return all(record.get(field) is False for field in fields) + + +def network_scope_is_bounded(record: dict[str, Any]) -> bool: + return ( + record.get("github_metadata") is True + and record.get("official_source_archives") is True + and record.get("arbitrary_mirrors") is False + and record.get("downloaded_code_executed") is False + and record.get("ps5_address_used") is False + and record.get("ps5_connected") is False + ) + + +def sdk_evidence_is_symbol_only(record: dict[str, Any]) -> bool: + return ( + record.get("repository") == "https://github.com/ps5-payload-dev/sdk" + and record.get("release") == "v0.41" + and record.get("release_commit") == SDK_COMMIT + and record.get("latest_release_during_audit") is True + and record.get("master_commit_inspected") + == "a0d2bc60bdcc0a5ee9e790fa3b02fe5051a152d0" + and record.get("videoout_stub_size") == 25199 + and record.get("videoout_stub_sha256") + == "da6cff9b3255e9ccb0440790f73e0265696ca47724cf8fe5950b579702940016" + and record.get("exports_only") is True + and record.get("public_videoout_header") is False + and record.get("signatures_or_layouts") is False + and record.get("return_semantics") is False + ) + + +def sdl_lineage_is_single(record: dict[str, Any]) -> bool: + return ( + record.get("repository") == "https://github.com/ps5-payload-dev/SDL" + and record.get("pinned_commit") == SDL_COMMIT + and record.get("initial_ps5_video_commit") + == "2682a5e31e8aee888538b0fb7253d8cadf2797a1" + and record.get("initial_source_size") == 7873 + and record.get("initial_source_sha256") + == "1b9f917cac4e00ba1eccb22093e3ca079238f91c9a44ceefa9e23570ca9abab3" + and record.get("declaration_move_commit") + == "fdfa470a0fa33215c677193982dfe3651ac1321a" + and record.get("uint32_cleanup_commit") + == "14ac2ec2ab5889af47218a957e3456e613b13d3d" + and record.get("pinned_header_size") == 2878 + and record.get("pinned_header_sha256") + == "e60766e0b43c2a7fceba2ada0c030e7f7b05e63885590092f2f8f5b9da34ed75" + and record.get("pinned_source_size") == 12162 + and record.get("pinned_source_sha256") + == "44124546da132ea6e12b1f06a1808d61690c73460a65c3161e8453e8108684b6" + and record.get("independent_abi_source_cited") is False + and record.get("opaque_buffer_member_name") == "junk0" + and record.get("opaque_attribute_member_name") == "junk0" + and record.get("single_lineage_only") is True + ) + + +def archives_do_not_corroborate(records: Any) -> bool: + expected = { + "ps5-payload-dev/libcross2d": ("829b167262d3567a048b6e69416f8e3be399c098", 1838927, "eb952a041396ba01224a890d14d41662b017488f487ddd4c4018ffddd4a59ae2", "SDL_CONSUMER"), + "ps5-payload-dev/pemu": ("4136088e13e825f33c9d0cc43ff9a8c8f749dcb0", 7540449, "9ed14a1ea432638992b614f9946bcbc9770f70b6f18ded5778fd6809a927eaeb", "SDL_CONSUMER"), + "ps5-payload-dev/FBNeo": ("ca4222ca2cd52215a673c565a5eae3589b352ba2", 15318689, "824d6e5b6b61555f5ecc598204052dcb4b1cf16d28be29b9594a44866e726ef3", "NO_DIRECT_IMPLEMENTATION"), + "ps5-payload-dev/LakeSnes": ("a2db690123649c7ffbc68a663af31efb3a41bf3f", 413067, "253a08957a68461a8535a4727ee1242c21b2dc40c8eb8c99bdb85af799ac3ca6", "SDL_CONSUMER"), + "ps5-payload-dev/yquake2": ("9e233b6a601c393be4ff1dcb1c003750346b8bbb", 2914919, "01ef41608b2be0ace119019901a6ea22f304542972bace714d1177cb7f567978", "SDL_CONSUMER"), + "ps5-payload-dev/pacbrew-repo": ("c2abcfcb60f569128abd0e8e70ad03a67bee5ea7", 143496, "17be50219ce41772cfc50ea9274b0a62a2259162556b6eeecfaed4a691822872", "REFERENCES_SAME_SDL_FORK"), + } + if not isinstance(records, list) or len(records) != 6: + return False + if {item.get("repository") for item in records} != set(expected): + return False + for item in records: + repository = item["repository"] + commit, size, digest, relationship = expected[repository] + if item.get("direct_videoout_hits") != 0: + return False + if item.get("relationship") != relationship: + return False + if item.get("commit") != commit or item.get("size") != size: + return False + if item.get("sha256") != digest: + return False + return True + + +def ps4_analogue_is_not_promoted(record: dict[str, Any]) -> bool: + return ( + record.get("repository") + == "https://github.com/OpenOrbis/OpenOrbis-PS4-Toolchain" + and record.get("commit") == "0a1aaf9dd4a92695538bdeb09fb056d06dd11725" + and record.get("four_argument_submit") is True + and record.get("mode_one_documented_as_vsync") is True + and record.get("classification") == "PS4_ANALOG_ONLY" + and record.get("accepted_as_ps5_abi") is False + ) + + +def evidence_matrix_is_fail_closed(record: dict[str, Any]) -> bool: + return record == { + "submit_signature": "PARTIAL_SINGLE_LINEAGE", + "argument_positions": "PARTIAL_SINGLE_LINEAGE", + "mode_one_semantics": "UNPROVEN_ON_PS5", + "frame_zero_validity": "UNPROVEN_ON_PS5", + "video_buffer_layout": "UNPROVEN", + "video_attribute_layout": "UNPROVEN", + "format_constant_semantics": "UNPROVEN", + "register_return_semantics": "PARTIAL_RUNTIME_ONLY", + "submit_error_semantics": "UNPROVEN", + "flip_master_or_active_app_requirement": "UNPROVEN", + "status_query_signatures_and_layouts": "UNPROVEN", + } + + +def decision_is_blocked(record: dict[str, Any]) -> bool: + return ( + record.get("independent_ps5_abi_corroboration_found") is False + and record.get("root_cause_resolved") is False + and record.get("parameter_change_allowed") is False + and record.get("new_videoout_call_allowed") is False + and record.get("phase10r_target_artifact_allowed") is False + and record.get("phase10r_device_action_allowed") is False + and record.get("safe_next_steps") == [ + "OFFLINE_LAUNCH_CONTEXT_COMPARISON", + "HOST_OR_SOFTWARE_ONLY_INTEGRATION", + ] + ) + + +def git(root: Path, *args: str) -> str: + result = subprocess.run( + ["git", *args], cwd=root, capture_output=True, text=True, check=False) + if result.returncode: + raise RuntimeError(result.stderr.strip() or "git failed") + return result.stdout + + +def validate(root: Path, sdk_root: Path, sdl_root: Path) -> list[str]: + errors: list[str] = [] + try: + record = load_json( + root / "manifests/retroarch/phase-1.0q-public-videoout-evidence.json") + except (OSError, ValueError, json.JSONDecodeError) as error: + return [str(error)] + if record.get("phase") != PHASE or record.get("status") != STATUS: + errors.append("phase/status mismatch") + if record.get("start_commit") != "df470c691f375b73768fd0d4a51dc2d35523e71d": + errors.append("start commit mismatch") + if not network_scope_is_bounded(record.get("network_scope", {})): + errors.append("network scope is broadened") + if not all_false(record.get("authorizations", {}), AUTHORIZATION_FIELDS): + errors.append("authorization remains active") + if not sdk_evidence_is_symbol_only(record.get("sdk", {})): + errors.append("SDK evidence was promoted") + if not sdl_lineage_is_single(record.get("sdl_lineage", {})): + errors.append("SDL lineage is incomplete or promoted") + if not archives_do_not_corroborate(record.get("official_project_archives")): + errors.append("official archive inventory mismatch") + storage = record.get("temporary_research_storage", {}) + if storage != { + "archive_count": 6, + "downloaded_bytes": 28169547, + "retained_in_git": False, + "cleaned_after_static_scan": False, + "cleanup_status": "RETAINED_TEMP_LOCAL_DELETE_POLICY_BLOCKED", + }: + errors.append("temporary research storage boundary mismatch") + additional = record.get("additional_ps5_source", {}) + if not ( + additional.get("repository") == "https://github.com/PS5Dev/PS5SDK" + and additional.get("commit") == "a2e03a2a0231a3a3397fa6cd087a01ca6d04f273" + and additional.get("videoout_implementation_found") is False + and additional.get("classification") == "NO_CORROBORATION" + ): + errors.append("additional PS5 source was promoted") + if not ps4_analogue_is_not_promoted(record.get("ps4_analogue", {})): + errors.append("PS4 analogue was promoted to PS5 ABI") + if not evidence_matrix_is_fail_closed(record.get("evidence_matrix", {})): + errors.append("evidence matrix mismatch") + if not decision_is_blocked(record.get("decision", {})): + errors.append("parameter or device path is not blocked") + performed = record.get("performed_actions", {}) + if not all(value is False for value in performed.values()) or set(performed) != { + "target_source_changed", "target_artifact_created", "ps5_connected", + "device_transfer_performed", "target_execution_performed", + "result_received_from_device", + }: + errors.append("performed-action boundary mismatch") + tests = record.get("tests", {}) + if not ( + tests.get("chimera_gfx_ctest") == "62_OF_62_PASS" + and tests.get("phase10q_guardrails") == 20 + and tests.get("network_required_by_tests") is False + and tests.get("hardware_claim_from_host_test") is False + ): + errors.append("test evidence mismatch") + try: + if git(sdk_root, "rev-parse", "HEAD").strip() != SDK_COMMIT: + errors.append("local SDK release commit mismatch") + if git(sdl_root, "rev-parse", "HEAD").strip() != SDL_COMMIT: + errors.append("local SDL commit mismatch") + if git(sdk_root, "status", "--porcelain"): + errors.append("local SDK tree is dirty") + if git(sdl_root, "status", "--porcelain"): + errors.append("local SDL tree is dirty") + stub = sdk_root / "sce_stubs/libSceVideoOut.c" + header = sdl_root / "src/video/ps5/SDL_ps5video.h" + source = sdl_root / "src/video/ps5/SDL_ps5video.c" + if stub.stat().st_size != 25199 or sha256(stub) != record["sdk"]["videoout_stub_sha256"]: + errors.append("local VideoOut stub identity mismatch") + if header.stat().st_size != 2878 or sha256(header) != record["sdl_lineage"]["pinned_header_sha256"]: + errors.append("local SDL header identity mismatch") + if source.stat().st_size != 12162 or sha256(source) != record["sdl_lineage"]["pinned_source_sha256"]: + errors.append("local SDL source identity mismatch") + header_text = header.read_text(encoding="utf-8") + if "uint64_t junk0[3]" not in header_text or "uint8_t junk0[80]" not in header_text: + errors.append("opaque SDL declarations changed") + tracked = git(root, "ls-files").splitlines() + if any(path.lower().endswith((".elf", ".self", ".sprx", ".pkg", ".zip")) + for path in tracked): + errors.append("target or downloaded archive is tracked") + except (OSError, RuntimeError, KeyError) as error: + errors.append(str(error)) + return errors + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + parser.add_argument("--sdk-root", type=Path, required=True) + parser.add_argument("--sdl-root", type=Path, required=True) + args = parser.parse_args() + errors = validate(args.root.resolve(), args.sdk_root.resolve(), args.sdl_root.resolve()) + if errors: + for error in errors: + print(f"ERROR: {error}") + return 1 + print("Phase-1.0Q public VideoOut evidence validation passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/validate_retroarch_phase10r.py b/tools/validate_retroarch_phase10r.py new file mode 100644 index 0000000..7be1769 --- /dev/null +++ b/tools/validate_retroarch_phase10r.py @@ -0,0 +1,319 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate the fail-closed Phase-1.0R launch-context analysis.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +from pathlib import Path +import subprocess +from typing import Any + + +PHASE = "PHASE_1_0R_LAUNCH_CONTEXT_ANALYSIS" +STATUS = "NO_SOURCE_PROVEN_LAUNCH_CONTEXT_FIX_TARGET_CHANGE_BLOCKED" +RA_SOURCE = "12cf1d783c41eb303987e49a5a920805a59ef7a4" +RA_HEAD = "606909706f91d7213751c245081333f56c2cce89" +SDK_COMMIT = "d2e2e585740362976a39fdd5ccf390f199a7bc37" +SDL_COMMIT = "0baf4ac49382b537ba449901b5b6d0d189bb1fbb" +ELFLDR_COMMIT = "197623058f509eddde18868dafcb92fdcac66464" +MANAGER_COMMIT = "e23d94ff91233aa770e2342800c1467875bdef44" +PACBREW_COMMIT = "c2abcfcb60f569128abd0e8e70ad03a67bee5ea7" +AUTHORIZATION_FIELDS = ( + "target_build_authorized", "ps5_connection_authorized", + "device_transfer_authorized", "device_execution_authorized", + "result_receive_authorized", "installation_authorized", + "autoload_authorized", "device_write_authorized", "automatic_retry", +) + + +def load_json(path: Path) -> dict[str, Any]: + value = json.loads(path.read_text(encoding="utf-8")) + if not isinstance(value, dict): + raise ValueError("launch-context manifest is not an object") + return value + + +def sha256(path: Path) -> str: + digest = hashlib.sha256() + with path.open("rb") as stream: + for chunk in iter(lambda: stream.read(1024 * 1024), b""): + digest.update(chunk) + return digest.hexdigest() + + +def git(root: Path, *args: str) -> str: + result = subprocess.run( + ["git", *args], cwd=root, capture_output=True, text=True, check=False) + if result.returncode: + raise RuntimeError(result.stderr.strip() or "git failed") + return result.stdout + + +def all_false(record: dict[str, Any], fields: tuple[str, ...]) -> bool: + return all(record.get(field) is False for field in fields) + + +def source_identities_are_exact(record: dict[str, Any]) -> bool: + return record == { + "retroarch_artifact_source_commit": RA_SOURCE, + "retroarch_inspected_head": RA_HEAD, + "sdk_commit": SDK_COMMIT, + "sdl_commit": SDL_COMMIT, + "hardened_elfldr_commit": ELFLDR_COMMIT, + "controlled_payload_manager_commit": MANAGER_COMMIT, + "pacbrew_commit": PACBREW_COMMIT, + "lakesnes_reference_commit": + "a2db690123649c7ffbc68a663af31efb3a41bf3f", + } + + +def sdl2main_is_lifecycle_only(record: dict[str, Any]) -> bool: + return record == { + "linked_by_exact_retroarch_artifact": False, + "calls_hide_splash_before_sdl_main": True, + "calls_load_exec_exit_after_sdl_main_returns": True, + "adds_application_registration": False, + "adds_title_identity": False, + "adds_lnc_setup": False, + "adds_videoout_ownership_setup": False, + "pre_submit_difference": "EARLIER_HIDE_SPLASH_ONLY", + "post_return_action": "LOAD_EXEC_EXIT", + } + + +def retroarch_path_is_exact(record: dict[str, Any]) -> bool: + return record == { + "sdk_crt1_linked": True, + "sdl_video_backend_linked": True, + "sdl2main_linked": False, + "system_service_hide_splash_imported": True, + "system_service_load_exec_imported": False, + "sdl_video_hides_splash_before_videoout_open": True, + } + + +def launch_routes_are_fail_closed(record: dict[str, Any]) -> bool: + direct = record.get("direct_raw_elf", {}) + manager = record.get("payload_manager_raw_elf", {}) + return ( + direct == {"constructor": "HARDENED_ELFLDR_SPAWN", + "app_registration_found": False} + and manager == {"constructor": "HARDENED_ELFLDR_SPAWN", + "transport": "LOOPBACK_9021", + "app_registration_found": False} + and record.get("direct_and_manager_same_elfldr_spawn_path") is True + and record.get("controlled_route_creates_distinct_app_context") is False + and record.get("port_launcher_contract") == "PARTIAL_UNBOUND" + and record.get("active_app_state_runtime") == "UNPROVEN" + ) + + +def packaging_is_not_launcher_proof(record: dict[str, Any]) -> bool: + return record == { + "pacbrew_is_launcher": False, + "pacbrew_classification": "PACKAGE_METADATA_ONLY", + "homebrew_js_is_app_registration": False, + "lakesnes_links_sdl2main": True, + "lakesnes_launcher_implementation_bound": False, + "official_port_runtime_success_used_as_hardware_evidence": False, + } + + +def runtime_observation_is_not_promoted(record: dict[str, Any]) -> bool: + return record == { + "lnc_log_classification": "OBSERVED_NONUNIQUE_CORRELATION", + "lnc_exact_caller_observed": False, + "lnc_log_accepted_as_root_cause": False, + "first_submit_failure_remains": True, + "visible_output_proven": False, + "cleanup_proven": False, + } + + +def decision_is_blocked(record: dict[str, Any]) -> bool: + return ( + record.get("root_cause_resolved") is False + and record.get("sdl2main_change_allowed") is False + and record.get("lnc_or_system_service_change_allowed") is False + and record.get("submit_parameter_change_allowed") is False + and record.get("new_videoout_call_allowed") is False + and record.get("target_build_allowed") is False + and record.get("device_action_allowed") is False + and record.get("safe_next_steps") == [ + "OFFLINE_EXACT_HBLDR_SHSRV_PROVENANCE_AUDIT", + "HOST_OR_SOFTWARE_ONLY_INTEGRATION", + ] + ) + + +def file_identity_is_exact(path: Path, record: dict[str, Any]) -> bool: + return path.stat().st_size == record.get("size") and \ + sha256(path) == record.get("sha256") + + +def validate( + root: Path, retroarch_root: Path, sdk_root: Path, sdl_root: Path, + elfldr_root: Path, manager_root: Path, pacbrew_root: Path, +) -> list[str]: + errors: list[str] = [] + try: + record = load_json( + root / "manifests/retroarch/phase-1.0r-launch-context-analysis.json") + except (OSError, ValueError, json.JSONDecodeError) as error: + return [str(error)] + if record.get("phase") != PHASE or record.get("status") != STATUS: + errors.append("phase/status mismatch") + if record.get("start_commit") != "dbd2b2658bb12018a691c4a0edfb06c2a103848f": + errors.append("start commit mismatch") + if not all_false(record.get("authorizations", {}), AUTHORIZATION_FIELDS): + errors.append("authorization remains active") + if not source_identities_are_exact(record.get("source_identities", {})): + errors.append("source identity mismatch") + if not sdl2main_is_lifecycle_only(record.get("sdl2main", {})): + errors.append("SDL2main semantics were promoted or changed") + if not retroarch_path_is_exact(record.get("exact_retroarch_path", {})): + errors.append("exact RetroArch linkage mismatch") + if not launch_routes_are_fail_closed(record.get("launch_routes", {})): + errors.append("launch route was promoted") + if not packaging_is_not_launcher_proof(record.get("packaging_and_ports", {})): + errors.append("packaging or descriptor was promoted to launcher proof") + if not runtime_observation_is_not_promoted(record.get("runtime_observation", {})): + errors.append("runtime observation was promoted") + if not decision_is_blocked(record.get("decision", {})): + errors.append("target change or device path is not blocked") + performed = record.get("performed_actions", {}) + if set(performed) != { + "target_source_changed", "target_artifact_created", "ps5_connected", + "device_transfer_performed", "target_execution_performed", + "result_received_from_device", + } or not all(value is False for value in performed.values()): + errors.append("performed-action boundary mismatch") + tests = record.get("tests", {}) + if not ( + tests.get("chimera_gfx_ctest") == "64_OF_64_PASS" + and tests.get("phase10r_guardrails") == 20 + and tests.get("safety_audit") == "PASS" + and tests.get("secret_scan") == "PASS" + and tests.get("network_required_by_tests") is False + and tests.get("hardware_claim_from_host_test") is False + ): + errors.append("test evidence mismatch") + + repositories = ( + (retroarch_root, RA_HEAD, "RetroArch"), + (sdk_root, SDK_COMMIT, "SDK"), + (sdl_root, SDL_COMMIT, "SDL"), + (elfldr_root, ELFLDR_COMMIT, "elfldr"), + (manager_root, MANAGER_COMMIT, "Payload Manager"), + (pacbrew_root, PACBREW_COMMIT, "PacBrew"), + ) + try: + for repository, commit, name in repositories: + if git(repository, "rev-parse", "HEAD").strip() != commit: + errors.append(f"{name} HEAD mismatch") + if git(repository, "status", "--porcelain"): + errors.append(f"{name} tree is dirty") + + exact = record["exact_files"] + paths = { + "sdl2main": sdl_root / "src/main/ps5/SDL_ps5_main.c", + "sdl_ps5_video": sdl_root / "src/video/ps5/SDL_ps5video.c", + "sdk_crt": sdk_root / "crt/crt.c", + "elfldr": elfldr_root / "elfldr.c", + "elfldr_socket_server": elfldr_root / "socksrv.c", + "payload_manager_launcher": manager_root / "src/ps5_launcher.c", + "retroarch_makefile": retroarch_root / "Makefile.ps5", + "artifact": retroarch_root / "build/phase10m/write-diag-a/retroarch_ps5_write_diag.elf", + "linker_map": retroarch_root / "build/phase10m/write-diag-a/retroarch_ps5_write_diag.map", + } + for name, path in paths.items(): + if not file_identity_is_exact(path, exact[name]): + errors.append(f"{name} file identity mismatch") + + wrapper = paths["sdl2main"].read_text(encoding="utf-8") + if not all(token in wrapper for token in ( + "sceSystemServiceHideSplashScreen();", "SDL_main(argc, argv);", + 'sceSystemServiceLoadExec("exit", 0);')): + errors.append("SDL2main lifecycle source mismatch") + if any(token in wrapper for token in ("sceLnc", "sceVideoOut", "AppId", "TitleId")): + errors.append("unexpected registration/VideoOut token in SDL2main") + + video = paths["sdl_ps5_video"].read_text(encoding="utf-8") + video_init = video.find("static int PS5_VideoInit(_THIS)") + hide = video.find("sceSystemServiceHideSplashScreen();", video_init) + open_call = video.find("sceVideoOutOpen(0xff, 0, 0, NULL);", video_init) + if video_init < 0 or hide < 0 or open_call < 0 or hide >= open_call: + errors.append("SDL splash-hide/open ordering mismatch") + + crt = paths["sdk_crt"].read_text(encoding="utf-8") + if "_start(payload_args_t *args)" not in crt or \ + "main(argc, argv, environ)" not in crt: + errors.append("SDK CRT entry contract mismatch") + if any(token in crt for token in ("sceLnc", "sceVideoOut", "AppId")): + errors.append("unexpected application registration in SDK CRT") + + socksrv = paths["elfldr_socket_server"].read_text(encoding="utf-8") + elfldr = paths["elfldr"].read_text(encoding="utf-8") + if "payload_spawn(" not in socksrv or "elfldr_spawn(" not in socksrv: + errors.append("elfldr raw route mismatch") + if "rfork_thread(" not in elfldr or "execve(SceSpZeroConf" not in elfldr: + errors.append("elfldr process constructor mismatch") + + manager = paths["payload_manager_launcher"].read_text(encoding="utf-8") + if "int ps5_launch_elf(" not in manager or \ + 'inet_addr("127.0.0.1")' not in manager: + errors.append("Payload Manager loopback launch mismatch") + + makefile = paths["retroarch_makefile"].read_text(encoding="utf-8") + if "libSDL2.a" not in makefile or "libSDL2main" in makefile: + errors.append("RetroArch SDL linkage mismatch") + link_map = paths["linker_map"].read_text(encoding="utf-8", errors="replace") + if "crt1.o:(.text._start)" not in link_map or \ + "libSDL2.a(SDL_ps5video.c.o)" not in link_map: + errors.append("linker-map source binding missing") + if any(token in link_map for token in ( + "SDL_ps5_main", "libSDL2main", "sceSystemServiceLoadExec")): + errors.append("linker map unexpectedly contains SDL2main") + + recipe = (pacbrew_root / "SDL2/PKGBUILD").read_text(encoding="utf-8") + if "ps5-payload-dev/SDL.git" not in recipe or \ + "sha256sums=('SKIP')" not in recipe: + errors.append("PacBrew SDL recipe mismatch") + + tracked = git(root, "ls-files").splitlines() + if any(path.lower().endswith((".elf", ".self", ".sprx", ".pkg", ".map")) + for path in tracked): + errors.append("target artifact or linker map is tracked") + except (OSError, RuntimeError, KeyError) as error: + errors.append(str(error)) + return errors + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + parser.add_argument("--retroarch-root", type=Path, required=True) + parser.add_argument("--sdk-root", type=Path, required=True) + parser.add_argument("--sdl-root", type=Path, required=True) + parser.add_argument("--elfldr-root", type=Path, required=True) + parser.add_argument("--manager-root", type=Path, required=True) + parser.add_argument("--pacbrew-root", type=Path, required=True) + args = parser.parse_args() + errors = validate( + args.root.resolve(), args.retroarch_root.resolve(), + args.sdk_root.resolve(), args.sdl_root.resolve(), + args.elfldr_root.resolve(), args.manager_root.resolve(), + args.pacbrew_root.resolve()) + if errors: + for error in errors: + print(f"ERROR: {error}") + return 1 + print("Phase-1.0R launch-context analysis validation passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/validate_retroarch_phase10s.py b/tools/validate_retroarch_phase10s.py new file mode 100644 index 0000000..dc70814 --- /dev/null +++ b/tools/validate_retroarch_phase10s.py @@ -0,0 +1,316 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate Phase-1.0S official shsrv/hbldr provenance and safety gates.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +from pathlib import Path +import subprocess +from typing import Any + + +PHASE = "PHASE_1_0S_LAUNCHER_PROVENANCE" +STATUS = "BIGAPP_CONTEXT_SOURCE_PROVEN_DEPLOYED_IDENTITY_UNPROVEN_DEVICE_PATH_BLOCKED" +CURRENT_COMMIT = "6f320637d56d344a0e7797753099e33238bbf146" +CURRENT_TREE = "c26ce02b6c3ca4202993e039b3db7c28c353dee4" +V07_COMMIT = "74287f5db6b20320efd7892d7b29cf438fe7cb98" +V07_TREE = "7184968c702afe038551bf3228cc25f455388bb6" +PACBREW_COMMIT = "c2abcfcb60f569128abd0e8e70ad03a67bee5ea7" +AUTHORIZATION_FIELDS = ( + "target_build_authorized", "ps5_connection_authorized", + "device_request_authorized", "device_transfer_authorized", + "device_execution_authorized", "result_receive_authorized", + "installation_authorized", "autoload_authorized", + "device_write_authorized", "app_termination_authorized", + "system_remount_authorized", "automatic_retry", +) + + +def load_json(path: Path) -> dict[str, Any]: + value = json.loads(path.read_text(encoding="utf-8")) + if not isinstance(value, dict): + raise ValueError("launcher provenance manifest is not an object") + return value + + +def sha256(path: Path) -> str: + digest = hashlib.sha256() + with path.open("rb") as stream: + for chunk in iter(lambda: stream.read(1024 * 1024), b""): + digest.update(chunk) + return digest.hexdigest() + + +def git(root: Path, *args: str) -> str: + result = subprocess.run( + ["git", *args], cwd=root, capture_output=True, text=True, check=False) + if result.returncode: + raise RuntimeError(result.stderr.strip() or "git failed") + return result.stdout + + +def all_false(record: dict[str, Any], fields: tuple[str, ...]) -> bool: + return all(record.get(field) is False for field in fields) + + +def acquisition_is_bounded(record: dict[str, Any]) -> bool: + return record == { + "official_repository": "https://github.com/ps5-payload-dev/shsrv.git", + "official_github_only": True, + "arbitrary_mirrors_used": False, + "dependencies_installed": False, + "downloaded_code_built": False, + "downloaded_code_executed": False, + "target_artifact_downloaded": False, + "ps5_address_used": False, + "ps5_connected": False, + } + + +def deployed_identity_is_unproven(record: dict[str, Any]) -> bool: + return record == { + "local_shsrv_binary_found": False, + "device_version_observed": False, + "device_hash_observed": False, + "package_receipt_found": False, + "classification": "UNPROVEN", + } + + +def context_difference_is_source_only(record: dict[str, Any]) -> bool: + return record == { + "raw_elfldr_process_basis": "SCE_SP_ZERO_CONF", + "hbldr_process_basis": "SYSTEM_SERVICE_BIGAPP", + "different_from_raw_elfldr": True, + "bigapp_launch_source_proven": True, + "foreground_user_context_source_proven": True, + "process_image_replacement_source_proven": True, + "videoout_permission_proven": False, + "firmware_9_60_runtime_proven": False, + "root_cause_classification": + "STRONG_SOURCE_CANDIDATE_NOT_PROVEN_ROOT_CAUSE", + } + + +def effects_block_device_use(record: dict[str, Any]) -> bool: + return record == { + "target_elf_must_exist_on_device": True, + "direct_host_to_memory_target_input": False, + "running_bigapp_may_be_killed": True, + "kernel_or_ptrace_runtime_writes": True, + "v019_system_ex_remount_possible": True, + "v019_persistent_fakeapp_creation_possible": True, + "autoload_change_found": False, + "hard_deadline_present": False, + "automatic_retry_present": False, + "atomic_persistent_write_protocol_present": False, + "rollback_protocol_present": False, + "power_loss_safe": False, + } + + +def decision_is_blocked(record: dict[str, Any]) -> bool: + return ( + record.get("root_cause_resolved") is False + and record.get("existing_hbldr_route_safe_for_device_test") is False + and record.get("existing_hbldr_route_reuse_allowed") is False + and record.get("launcher_code_copy_allowed") is False + and record.get("target_source_change_allowed") is False + and record.get("target_build_allowed") is False + and record.get("device_fact_collection_allowed") is False + and record.get("device_action_allowed") is False + and record.get("safe_next_steps") == [ + "OFFLINE_DEPLOYED_SHSRV_IDENTITY_COLLECTION_DESIGN", + "HOST_OR_SOFTWARE_ONLY_INTEGRATION", + ] + ) + + +def callgraph_is_exact(record: Any) -> bool: + return record == [ + "HOST_TELNET_TO_PORT_2323", + "SHSRV_ACCEPT", + "ELFLDR_SPAWN_EMBEDDED_SHELL", + "SHELL_BUILTIN_HBLDR", + "ELFLDR_SPAWN_EMBEDDED_HBLDR", + "READ_TARGET_ELF_FROM_DEVICE_PATH", + "PREPARE_OR_SELECT_BIGAPP", + "KILL_RUNNING_BIGAPP_IF_PRESENT", + "SYSTEM_SERVICE_LAUNCH_BIGAPP", + "FOLLOW_FORK_AND_EXEC", + "SET_TARGET_ROOT_AND_JAIL", + "REPLACE_BIGAPP_PROCESS_WITH_ELF", + "DETACH_TARGET", + ] + + +def file_identity_is_exact(path: Path, record: dict[str, Any]) -> bool: + return path.stat().st_size == record.get("size") and \ + sha256(path) == record.get("sha256") + + +def validate( + root: Path, shsrv_root: Path, shsrv_v07_root: Path, pacbrew_root: Path, +) -> list[str]: + errors: list[str] = [] + try: + record = load_json( + root / "manifests/retroarch/phase-1.0s-launcher-provenance.json") + except (OSError, ValueError, json.JSONDecodeError) as error: + return [str(error)] + if record.get("phase") != PHASE or record.get("status") != STATUS: + errors.append("phase/status mismatch") + if record.get("start_commit") != "189a4afdf4b6bb6d76b8a5fa7b5ce79cd4f82243": + errors.append("start commit mismatch") + if not all_false(record.get("authorizations", {}), AUTHORIZATION_FIELDS): + errors.append("authorization remains active") + if not acquisition_is_bounded(record.get("source_acquisition", {})): + errors.append("source acquisition scope is broadened") + if not deployed_identity_is_unproven(record.get("deployed_identity", {})): + errors.append("deployed identity was promoted") + if not callgraph_is_exact(record.get("launch_callgraph")): + errors.append("launch callgraph mismatch") + if not context_difference_is_source_only(record.get("launch_context", {})): + errors.append("source context was promoted to runtime proof") + if not effects_block_device_use(record.get("effects", {})): + errors.append("hbldr side effects were relaxed") + if not decision_is_blocked(record.get("decision", {})): + errors.append("target or device decision is not blocked") + performed = record.get("performed_actions", {}) + if set(performed) != { + "target_source_changed", "target_artifact_created", "ps5_connected", + "device_request_performed", "device_transfer_performed", + "target_execution_performed", "device_file_created", + "device_app_terminated", "device_remounted", + } or not all(value is False for value in performed.values()): + errors.append("performed-action boundary mismatch") + if record.get("robustness_findings") != [ + "UNBOUNDED_WHOLE_ELF_ALLOCATION", + "UNBOUNDED_PROCESS_WAITS", + "UNAUTHENTICATED_ALL_INTERFACE_LISTENER", + "SPLITSTRING_POINTER_ALLOCATION_UNDERSIZED", + "UNBOUNDED_PATH_COPY_AND_FORMAT", + "NO_TARGET_HASH_OR_SIZE_POLICY", + "INCOMPLETE_GLOBAL_CLEANUP_PROOF", + ]: + errors.append("robustness inventory mismatch") + tests = record.get("tests", {}) + if not ( + tests.get("chimera_gfx_ctest") == "66_OF_66_PASS" + and tests.get("phase10s_guardrails") == 20 + and tests.get("safety_audit") == "PASS" + and tests.get("secret_scan") == "PASS" + and tests.get("network_required_by_tests") is False + and tests.get("hardware_claim_from_host_test") is False + ): + errors.append("test evidence mismatch") + + try: + if git(shsrv_root, "rev-parse", "HEAD").strip() != CURRENT_COMMIT: + errors.append("current shsrv commit mismatch") + if git(shsrv_root, "rev-parse", "HEAD^{tree}").strip() != CURRENT_TREE: + errors.append("current shsrv tree mismatch") + if git(shsrv_v07_root, "rev-parse", "HEAD").strip() != V07_COMMIT: + errors.append("shsrv v0.7 commit mismatch") + if git(shsrv_v07_root, "rev-parse", "HEAD^{tree}").strip() != V07_TREE: + errors.append("shsrv v0.7 tree mismatch") + if git(pacbrew_root, "rev-parse", "HEAD").strip() != PACBREW_COMMIT: + errors.append("PacBrew commit mismatch") + for repository, name in ( + (shsrv_root, "current shsrv"), + (shsrv_v07_root, "shsrv v0.7"), + (pacbrew_root, "PacBrew")): + if git(repository, "status", "--porcelain"): + errors.append(f"{name} worktree is dirty") + if git(shsrv_root, "remote", "get-url", "origin").strip() != \ + "https://github.com/ps5-payload-dev/shsrv.git": + errors.append("shsrv origin is not official") + + for key, base, files in ( + ("official_current", shsrv_root, record["official_current"]["files"]), + ("historical_reference", shsrv_v07_root, + record["historical_reference"]["files"]), + ): + for relative, identity in files.items(): + if not file_identity_is_exact(base / relative, identity): + errors.append(f"{key} file identity mismatch: {relative}") + + current = (shsrv_root / "bundles/hbldr/hbldr.c").read_text( + encoding="utf-8") + for token in ( + '#define FAKE_PATH "/system_ex/app/FAKE00000"', + "remount_system_ex(void)", "nmount(iov, IOVEC_SIZE(iov), MNT_UPDATE)", + 'sceSystemServiceLaunchApp("FAKE00000", ctx->argv, ctx)', + "sceSystemServiceKillApp(app_id, -1, 0, 0)", + "kernel_set_proc_rootdir(pid, kernel_get_root_vnode())", + "kernel_set_proc_jaildir(pid, 0)", + "char **tokens = calloc(bufsize, sizeof(char))", + 'sprintf(path, "%s/%s", paths[i], name)', + ): + if token not in current: + errors.append(f"current hbldr source token missing: {token}") + + historical = (shsrv_v07_root / "bundles/hbldr/main.c").read_text( + encoding="utf-8") + for token in ( + 'sceSystemServiceLaunchApp("PPSA01659", argv, &ctx)', + "sceSystemServiceKillApp(app_id, -1, 0, 0)", + "kernel_set_proc_rootdir(pid, kernel_get_root_vnode())", + "kernel_set_proc_jaildir(pid, 0)", + ): + if token not in historical: + errors.append(f"v0.7 hbldr source token missing: {token}") + if "FAKE00000" in historical or "remount_system_ex" in historical: + errors.append("v0.7 was incorrectly given the later fake-app path") + + server = (shsrv_root / "shsrv.c").read_text(encoding="utf-8") + shell = (shsrv_root / "sh.c").read_text(encoding="utf-8") + wrapper = (shsrv_root / "bundles/hbldr/main.c").read_text( + encoding="utf-8") + if "int port = 2323" not in server or \ + "server_addr.sin_addr.s_addr = htonl(INADDR_ANY)" not in server: + errors.append("shsrv listener contract mismatch") + if "builtin_cmd_run(argv[0], argc, argv)" not in shell: + errors.append("shell builtin dispatch mismatch") + if 'builtin_cmd_define("hbldr"' not in wrapper or \ + "elfldr_spawn(STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO" \ + not in wrapper: + errors.append("embedded hbldr wrapper mismatch") + + recipe = (pacbrew_root / "shsrv/PKGBUILD").read_text(encoding="utf-8") + if 'source=("git+https://github.com/ps5-payload-dev/shsrv.git")' \ + not in recipe or "sha256sums=('SKIP')" not in recipe: + errors.append("PacBrew shsrv provenance boundary mismatch") + + tracked = git(root, "ls-files").splitlines() + if any(path.lower().endswith((".elf", ".self", ".sprx", ".pkg")) + for path in tracked): + errors.append("target artifact is tracked") + except (OSError, RuntimeError, KeyError) as error: + errors.append(str(error)) + return errors + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + parser.add_argument("--shsrv-root", type=Path, required=True) + parser.add_argument("--shsrv-v07-root", type=Path, required=True) + parser.add_argument("--pacbrew-root", type=Path, required=True) + args = parser.parse_args() + errors = validate( + args.root.resolve(), args.shsrv_root.resolve(), + args.shsrv_v07_root.resolve(), args.pacbrew_root.resolve()) + if errors: + for error in errors: + print(f"ERROR: {error}") + return 1 + print("Phase-1.0S shsrv/hbldr provenance validation passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/validate_retroarch_phase10t.py b/tools/validate_retroarch_phase10t.py new file mode 100644 index 0000000..4ff0b86 --- /dev/null +++ b/tools/validate_retroarch_phase10t.py @@ -0,0 +1,339 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate the inactive Phase-1.0T shsrv identity-collection gate.""" + +from __future__ import annotations + +import argparse +import ast +import hashlib +import json +from pathlib import Path +import subprocess +from typing import Any + + +PHASE = "PHASE_1_0T_INACTIVE_SHSRV_IDENTITY_GATE" +STATUS = "INACTIVE_METADATA_GATE_DESIGNED_EXACT_IDENTITY_UNAVAILABLE" +START_COMMIT = "4c1944ef1412ed8a0bb18ae534244cde92db00ba" +CURRENT_COMMIT = "6f320637d56d344a0e7797753099e33238bbf146" +CURRENT_TREE = "c26ce02b6c3ca4202993e039b3db7c28c353dee4" +V07_COMMIT = "74287f5db6b20320efd7892d7b29cf438fe7cb98" +V07_TREE = "7184968c702afe038551bf3228cc25f455388bb6" +CURRENT_COMMAND_HASH = \ + "f41168292e205590bda1d243cdf727044e0af280a89fb0c070f4c5d6c92f2fd7" +V07_COMMAND_HASH = \ + "40313637116b532f3c7f9bebe2c23c0018fe7d4093840cf463a22ba0314ca021" +AUTHORIZATION_FIELDS = ( + "target_build_authorized", "ps5_connection_authorized", + "device_request_authorized", "result_receive_authorized", + "device_transfer_authorized", "device_execution_authorized", + "installation_authorized", "autoload_authorized", + "device_write_authorized", "app_termination_authorized", + "system_remount_authorized", "automatic_retry", + "reconnect_authorized", +) +SOURCE_IDENTITIES = { + "bundles/core/sum.c": ( + 1965, + "cd03227b0fbad40c342946b3ba93cc3119e94a1dd99a9c8dff04a2a9854bd93b"), + "bundles/core/stat.c": ( + 2183, + "caf25c94f5edaacd0d824187edc47e9e804d9ed51177d89c125fbe959020e638"), + "bundles/core/ps.c": ( + 2761, + "34d21ffef89341d29fbeec04d7b1361a15d3ee1c005681b16bd06c4f7ba6e5dc"), + "builtin.c": ( + 4182, + "4b15b395562f62d547a8c0c2d27da570f9b7776263a0ad5dc23c0b238ba52643"), + "sh.c": ( + 13278, + "3c4b7f76efdd157436ed4b353ee1b550bf3ff9df17c147b4762b767982fc8253"), +} + + +def load_json(path: Path) -> dict[str, Any]: + value = json.loads(path.read_text(encoding="utf-8")) + if not isinstance(value, dict): + raise ValueError("Phase-1.0T manifest is not an object") + return value + + +def sha256(path: Path) -> str: + digest = hashlib.sha256() + with path.open("rb") as stream: + for chunk in iter(lambda: stream.read(1024 * 1024), b""): + digest.update(chunk) + return digest.hexdigest() + + +def git(root: Path, *args: str) -> str: + result = subprocess.run( + ["git", *args], cwd=root, capture_output=True, text=True, check=False) + if result.returncode: + raise RuntimeError(result.stderr.strip() or "git failed") + return result.stdout + + +def all_false(record: dict[str, Any]) -> bool: + return all(record.get(field) is False for field in AUTHORIZATION_FIELDS) + + +def activation_is_inactive(record: dict[str, Any]) -> bool: + return record == { + "active": False, + "target_address": None, + "listener_already_running_attested": False, + "window": None, + "exact_literal_path": None, + "commands": [], + "expires_at": None, + } + + +def connection_effects_are_complete(record: dict[str, Any]) -> bool: + return record == { + "accepted_socket_spawns_shell": True, + "new_session_created": True, + "pipes_created": True, + "thread_created": True, + "heap_allocations": True, + "process_environment_changed": True, + "model_queried": True, + "serial_queried_and_transmitted": True, + "firmware_queried": True, + "temperature_queried_and_transmitted": True, + "cpu_frequency_queried_and_transmitted": True, + "filesystem_write_found": False, + "autoload_change_found": False, + "target_payload_launched": False, + } + + +def identity_is_non_exact(record: dict[str, Any]) -> bool: + return record == { + "greeting_compile_date_time": "METADATA_ONLY", + "help_command_fingerprint": "SOURCE_FAMILY_FINGERPRINT_ONLY", + "stat": "FILE_METADATA_ONLY", + "sum": "BSD_ROTATE_16_WEAK_CHECKSUM_ONLY", + "sha256_command_available": False, + "binary_safe_file_read_available": False, + "exact_deployed_identity_possible": False, + "strongest_possible_classification": "WEAK_FILE_CORRELATION_ONLY", + } + + +def command_policy_is_fail_closed(record: dict[str, Any]) -> bool: + forbidden = record.get("forbidden_mutating_or_launch_commands", []) + return ( + record.get("initial_candidate") == ["help"] + and record.get("exact_path_candidates") == ["stat", "sum"] + and record.get("fakeapp_metadata_candidate") == ["stat"] + and all(command in forbidden for command in ( + "hbldr", "launch", "exec", "rm", "mv", "mount", "touch")) + and all(record.get(field) is False for field in ( + "wildcards_allowed", "relative_paths_allowed", "pipes_allowed", + "redirection_allowed", "multiple_commands_per_line_allowed", + "path_guessing_allowed")) + ) + + +def sanitization_is_strict(record: dict[str, Any]) -> bool: + return record == { + "raw_transcript_persistence_allowed": False, + "serial_value_retained": False, + "model_value_retained": False, + "temperature_values_retained": False, + "cpu_frequency_value_retained": False, + "unknown_paths_retained": False, + "compile_metadata_retained": True, + "firmware_retained": True, + "approved_path_metadata_retained": True, + } + + +def future_windows_are_inactive(record: dict[str, Any]) -> bool: + return set(record) == { + "T1_MANUAL_HOST_FACTS", "T2_GREETING_AND_HELP", + "T3_ONE_EXACT_PATH", "T4_FAKEAPP_METADATA", + } and all(value == "DESIGNED_NOT_ACTIVE" for value in record.values()) + + +def decision_is_inactive(record: dict[str, Any]) -> bool: + return ( + record.get("exact_deployed_identity_obtainable_from_existing_shsrv") + is False + and record.get("metadata_collection_design_complete") is True + and record.get("network_client_created") is False + and record.get("live_collection_allowed") is False + and record.get("target_change_allowed") is False + and record.get("device_action_allowed") is False + and record.get("safe_next_steps") == [ + "OFFLINE_INACTIVE_ONE_SHOT_SANITIZING_CLIENT_DESIGN", + "MANUAL_HOST_ARTIFACT_INVENTORY", + "HOST_OR_SOFTWARE_ONLY_INTEGRATION", + ] + ) + + +def parser_is_offline(path: Path) -> bool: + source = path.read_text(encoding="utf-8") + tree = ast.parse(source) + imports = { + alias.name.split(".", 1)[0] + for node in ast.walk(tree) + if isinstance(node, (ast.Import, ast.ImportFrom)) + for alias in node.names + } + return not imports.intersection({ + "socket", "urllib", "requests", "http", "ftplib", "telnetlib"}) \ + and "sys.stdin.read()" in source \ + and "open(" not in source \ + and "write_text(" not in source \ + and "write_bytes(" not in source + + +def file_identity_is_exact(path: Path, size: int, digest: str) -> bool: + return path.stat().st_size == size and sha256(path) == digest + + +def validate(root: Path, shsrv_root: Path, shsrv_v07_root: Path) -> list[str]: + errors: list[str] = [] + try: + record = load_json( + root / "manifests/retroarch/phase-1.0t-shsrv-identity-gate.json") + if record.get("phase") != PHASE or record.get("status") != STATUS: + errors.append("phase/status mismatch") + if record.get("start_commit") != START_COMMIT: + errors.append("start commit mismatch") + if not all_false(record.get("authorizations", {})): + errors.append("authorization remains active or is missing") + if not activation_is_inactive(record.get("activation", {})): + errors.append("activation record is not inert") + if record.get("source_identities") != { + "official_current_tag": "v0.19", + "official_current_commit": CURRENT_COMMIT, + "historical_tag": "v0.7", + "historical_commit": V07_COMMIT, + }: + errors.append("source identity mismatch") + if not connection_effects_are_complete( + record.get("mandatory_connection_effects", {})): + errors.append("connection effects were hidden or promoted") + if not identity_is_non_exact(record.get("identity_capabilities", {})): + errors.append("identity capability was promoted") + fingerprints = record.get("command_fingerprints", {}) + if fingerprints != { + "v0_7": {"command_count": 44, "sha256": V07_COMMAND_HASH, + "proves_exact_binary": False}, + "v0_19": {"command_count": 50, "sha256": CURRENT_COMMAND_HASH, + "proves_exact_binary": False}, + }: + errors.append("command fingerprint mismatch") + if not command_policy_is_fail_closed(record.get("command_policy", {})): + errors.append("command policy is not fail closed") + if not sanitization_is_strict(record.get("sanitization", {})): + errors.append("sanitization boundary is relaxed") + if not future_windows_are_inactive(record.get("future_windows", {})): + errors.append("a future collection window is active") + if not decision_is_inactive(record.get("decision", {})): + errors.append("decision is not inactive") + performed = record.get("performed_actions", {}) + if not performed or not all(value is False for value in performed.values()): + errors.append("performed action is present") + tests = record.get("tests", {}) + if not ( + tests.get("chimera_gfx_ctest") == "69_OF_69_PASS" + and tests.get("phase10t_guardrails") == 20 + and tests.get("phase10t_transcript_tests") == 12 + and tests.get("safety_audit") == "PASS" + and tests.get("secret_scan") == "PASS" + and tests.get("network_required_by_tests") is False + and tests.get("hardware_claim_from_host_test") is False + ): + errors.append("test evidence mismatch") + + for repository, commit, tree_hash, name in ( + (shsrv_root, CURRENT_COMMIT, CURRENT_TREE, "current shsrv"), + (shsrv_v07_root, V07_COMMIT, V07_TREE, "shsrv v0.7"), + ): + if git(repository, "rev-parse", "HEAD").strip() != commit: + errors.append(f"{name} commit mismatch") + if git(repository, "rev-parse", "HEAD^{tree}").strip() != tree_hash: + errors.append(f"{name} tree mismatch") + if git(repository, "status", "--porcelain"): + errors.append(f"{name} worktree is dirty") + if git(shsrv_root, "remote", "get-url", "origin").strip() != \ + "https://github.com/ps5-payload-dev/shsrv.git": + errors.append("shsrv origin is not official") + for relative, identity in SOURCE_IDENTITIES.items(): + if not file_identity_is_exact( + shsrv_root / relative, identity[0], identity[1]): + errors.append(f"source identity mismatch: {relative}") + + shell = (shsrv_root / "sh.c").read_text(encoding="utf-8") + for token in ( + 'printf("S/N:', 'printf("SoC temp:', 'printf("CPU temp:', + 'printf("CPU freq:', "sh_greet();", "setsid();", + "pthread_create(&trd", "telnet_init(", + ): + if token not in shell: + errors.append(f"mandatory shell effect token missing: {token}") + server = (shsrv_root / "shsrv.c").read_text(encoding="utf-8") + if "accept(srvfd" not in server or \ + "elfldr_spawn(connfd, connfd, -1, sh_elf, argv)" not in server: + errors.append("connection-to-shell contract mismatch") + builtin = (shsrv_root / "builtin.c").read_text(encoding="utf-8") + if "rfork_thread(" not in builtin or "sceKernelSetBudget(0)" not in builtin: + errors.append("forked builtin effects missing") + sum_source = (shsrv_root / "bundles/core/sum.c").read_text( + encoding="utf-8") + if "open(name, O_RDONLY)" not in sum_source or \ + "& 0xffff" not in sum_source or "sha256" in sum_source.lower(): + errors.append("sum read-only weak-checksum contract mismatch") + stat_source = (shsrv_root / "bundles/core/stat.c").read_text( + encoding="utf-8") + if "stat(path, &statbuf)" not in stat_source: + errors.append("stat metadata contract mismatch") + + parser_path = root / "tools/phase10t_shsrv_transcript.py" + if not parser_is_offline(parser_path): + errors.append("transcript parser has network or persistence capability") + approval = (root / "docs/approvals/phase-1.0t-shsrv-metadata-collection.md").read_text( + encoding="utf-8") + for token in ( + "active=false", "ps5_connection_authorized=false", + "device_request_authorized=false", "automatic_retry=false", + "target_address=null", + ): + if token not in approval: + errors.append(f"inactive approval token missing: {token}") + tracked = git(root, "ls-files").splitlines() + if any(path.lower().endswith((".elf", ".self", ".sprx", ".pkg")) + for path in tracked): + errors.append("target artifact is tracked") + except (OSError, RuntimeError, ValueError, KeyError, json.JSONDecodeError, + SyntaxError) as error: + errors.append(str(error)) + return errors + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + parser.add_argument("--shsrv-root", type=Path, required=True) + parser.add_argument("--shsrv-v07-root", type=Path, required=True) + args = parser.parse_args() + errors = validate( + args.root.resolve(), args.shsrv_root.resolve(), + args.shsrv_v07_root.resolve()) + if errors: + for error in errors: + print(f"ERROR: {error}") + return 1 + print("Phase-1.0T inactive shsrv identity gate validation passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/validate_retroarch_phase10u.py b/tools/validate_retroarch_phase10u.py new file mode 100644 index 0000000..d01c38f --- /dev/null +++ b/tools/validate_retroarch_phase10u.py @@ -0,0 +1,243 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate the bounded Phase-1.0U local shsrv inventory.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +from pathlib import Path +import subprocess +from typing import Any + + +PHASE = "PHASE_1_0U_BOUNDED_LOCAL_SHSRV_INVENTORY" +STATUS = "BOUNDED_LOCAL_INVENTORY_COMPLETE_NO_DEPLOYED_CANDIDATE_FOUND" +START_COMMIT = "f5b0ff720dabf3ab745ad05aea4ea8edc9666ac7" +CURRENT_COMMIT = "6f320637d56d344a0e7797753099e33238bbf146" +CURRENT_TREE = "c26ce02b6c3ca4202993e039b3db7c28c353dee4" +V07_COMMIT = "74287f5db6b20320efd7892d7b29cf438fe7cb98" +V07_TREE = "7184968c702afe038551bf3228cc25f455388bb6" +PACBREW_COMMIT = "c2abcfcb60f569128abd0e8e70ad03a67bee5ea7" +AUTHORIZATION_FIELDS = ( + "target_build_authorized", "ps5_connection_authorized", + "device_request_authorized", "result_receive_authorized", + "device_transfer_authorized", "device_execution_authorized", + "installation_authorized", "autoload_authorized", + "device_write_authorized", "automatic_retry", "reconnect_authorized", +) + + +def load_json(path: Path) -> dict[str, Any]: + value = json.loads(path.read_text(encoding="utf-8")) + if not isinstance(value, dict): + raise ValueError("Phase-1.0U manifest is not an object") + return value + + +def sha256(path: Path) -> str: + digest = hashlib.sha256() + with path.open("rb") as stream: + for chunk in iter(lambda: stream.read(1024 * 1024), b""): + digest.update(chunk) + return digest.hexdigest() + + +def git(root: Path, *args: str) -> str: + result = subprocess.run( + ["git", *args], cwd=root, capture_output=True, text=True, check=False) + if result.returncode: + raise RuntimeError(result.stderr.strip() or "git failed") + return result.stdout + + +def all_authorizations_false(record: dict[str, Any]) -> bool: + return all(record.get(field) is False for field in AUTHORIZATION_FIELDS) + + +def scope_is_bounded(record: dict[str, Any]) -> bool: + return record == { + "roots": [ + "CHIMERA_GFX_REPOSITORY", + "KNOWN_CHIMERA_SIBLING_REPOSITORIES", + "SUPPLIED_CODEX_ATTACHMENTS", + "EVIDENCED_USER_DOWNLOAD_DIRECTORY", + ], + "full_computer_scan_performed": False, + "browser_database_scan_performed": False, + "network_share_scan_performed": False, + "internet_access_performed": False, + "ps5_access_performed": False, + } + + +def methods_are_static(record: dict[str, Any]) -> bool: + return record == { + "case_insensitive_filename_search": True, + "zip_entry_name_inventory": True, + "zip_extraction_performed": False, + "non_zip_unrelated_archives_inspected": False, + "downloaded_or_local_code_executed": False, + "host_sender_executed": False, + "target_artifact_executed": False, + "files_modified_by_inventory": False, + } + + +def result_is_scoped_absence(record: dict[str, Any]) -> bool: + expected_false = ( + "local_shsrv_target_candidate_found", "local_shsrv_package_found", + "local_shsrv_package_receipt_found", "local_shsrv_transfer_log_found", + "zip_entry_name_match_found", "operator_supplied_original_binary_found", + "exact_device_path_found", "exact_device_hash_found", + "global_absence_proven", + ) + return all(record.get(field) is False for field in expected_false) and \ + record.get("classification") == "NO_CANDIDATE_IN_SCANNED_SCOPE" + + +def references_are_non_deployed(records: Any) -> bool: + return isinstance(records, list) and len(records) == 4 and all( + item.get("deployed_identity") is False for item in records) + + +def decision_is_blocked(record: dict[str, Any]) -> bool: + return record == { + "exact_deployed_shsrv_identity": "UNPROVEN", + "local_direct_hash_path_available": False, + "phase1_launch_context_experiment_allowed": False, + "phase10v_inactive_collector_design_allowed": True, + "live_collection_allowed": False, + "device_action_allowed": False, + "next_step": "OFFLINE_INACTIVE_ONE_SHOT_SANITIZING_COLLECTOR_DESIGN", + } + + +def exact_file(path: Path, size: int, digest: str) -> bool: + return path.stat().st_size == size and sha256(path) == digest + + +def validate( + root: Path, shsrv_root: Path, shsrv_v07_root: Path, pacbrew_root: Path, +) -> list[str]: + errors: list[str] = [] + try: + record = load_json( + root / "manifests/retroarch/phase-1.0u-local-shsrv-inventory.json") + if record.get("phase") != PHASE or record.get("status") != STATUS: + errors.append("phase/status mismatch") + if record.get("start_commit") != START_COMMIT: + errors.append("start commit mismatch") + if not scope_is_bounded(record.get("scope", {})): + errors.append("inventory scope was broadened") + if not methods_are_static(record.get("methods", {})): + errors.append("inventory method became active or mutating") + if not result_is_scoped_absence(record.get("results", {})): + errors.append("scoped absence was promoted or changed") + references = record.get("reference_objects", []) + if not references_are_non_deployed(references): + errors.append("reference object was promoted to deployed identity") + if not all_authorizations_false(record.get("authorizations", {})): + errors.append("authorization remains active or missing") + if not decision_is_blocked(record.get("decision", {})): + errors.append("device or launch-context decision is not blocked") + performed = record.get("performed_actions", {}) + if not performed or not all(value is False for value in performed.values()): + errors.append("performed action is present") + tests = record.get("tests", {}) + if not ( + tests.get("chimera_gfx_ctest") == "71_OF_71_PASS" + and tests.get("phase10u_guardrails") == 16 + and tests.get("safety_audit") == "PASS" + and tests.get("secret_scan") == "PASS" + and tests.get("network_required_by_tests") is False + and tests.get("hardware_claim_from_host_test") is False + ): + errors.append("test evidence mismatch") + + expected_references = { + "OFFICIAL_SHSRV_CURRENT_SOURCE": ( + "REFERENCE_SOURCE_ONLY", CURRENT_COMMIT, CURRENT_TREE), + "OFFICIAL_SHSRV_V07_SOURCE": ( + "HISTORICAL_REFERENCE_SOURCE_ONLY", V07_COMMIT, V07_TREE), + } + indexed = {item["logical_name"]: item for item in references} + for name, expected in expected_references.items(): + item = indexed[name] + if (item.get("classification"), item.get("commit"), + item.get("tree")) != expected: + errors.append(f"source reference mismatch: {name}") + host = indexed["OFFICIAL_HOST_TELNET_WRAPPER"] + if not exact_file( + shsrv_root / "host/prospero-shsrv-shell", host["size"], + host["sha256"]): + errors.append("host wrapper identity mismatch") + recipe = indexed["PACBREW_SHSRV_RECIPE"] + if not exact_file( + pacbrew_root / "shsrv/PKGBUILD", recipe["size"], + recipe["sha256"]): + errors.append("PacBrew recipe identity mismatch") + if host.get("classification") != "HOST_WRAPPER_NOT_TARGET_BINARY" or \ + host.get("executed") is not False: + errors.append("host wrapper was promoted or executed") + if recipe.get("classification") != \ + "UNPINNED_RECIPE_NOT_PACKAGE_RECEIPT" or \ + recipe.get("executed") is not False: + errors.append("package recipe was promoted or executed") + + for repository, commit, tree_hash, name in ( + (shsrv_root, CURRENT_COMMIT, CURRENT_TREE, "current shsrv"), + (shsrv_v07_root, V07_COMMIT, V07_TREE, "shsrv v0.7"), + ): + if git(repository, "rev-parse", "HEAD").strip() != commit: + errors.append(f"{name} commit mismatch") + if git(repository, "rev-parse", "HEAD^{tree}").strip() != tree_hash: + errors.append(f"{name} tree mismatch") + if git(repository, "status", "--porcelain"): + errors.append(f"{name} worktree is dirty") + if git(pacbrew_root, "rev-parse", "HEAD").strip() != PACBREW_COMMIT: + errors.append("PacBrew commit mismatch") + if git(pacbrew_root, "status", "--porcelain"): + errors.append("PacBrew worktree is dirty") + host_source = (shsrv_root / "host/prospero-shsrv-shell").read_text( + encoding="utf-8") + if "telnet $SHSRV_HOST $SHSRV_PORT" not in host_source: + errors.append("host wrapper behavior mismatch") + recipe_source = (pacbrew_root / "shsrv/PKGBUILD").read_text( + encoding="utf-8") + for token in ( + 'source=("git+https://github.com/ps5-payload-dev/shsrv.git")', + "sha256sums=('SKIP')", "cp shsrv-ps5.elf", + ): + if token not in recipe_source: + errors.append(f"package-recipe boundary missing: {token}") + tracked = git(root, "ls-files").splitlines() + if any(path.lower().endswith((".elf", ".self", ".sprx", ".pkg")) + for path in tracked): + errors.append("target artifact is tracked") + except (OSError, RuntimeError, ValueError, KeyError, json.JSONDecodeError) as error: + errors.append(str(error)) + return errors + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + parser.add_argument("--shsrv-root", type=Path, required=True) + parser.add_argument("--shsrv-v07-root", type=Path, required=True) + parser.add_argument("--pacbrew-root", type=Path, required=True) + args = parser.parse_args() + errors = validate( + args.root.resolve(), args.shsrv_root.resolve(), + args.shsrv_v07_root.resolve(), args.pacbrew_root.resolve()) + if errors: + for error in errors: + print(f"ERROR: {error}") + return 1 + print("Phase-1.0U bounded local shsrv inventory validation passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/validate_retroarch_phase10v.py b/tools/validate_retroarch_phase10v.py new file mode 100644 index 0000000..59d31ea --- /dev/null +++ b/tools/validate_retroarch_phase10v.py @@ -0,0 +1,268 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate the inactive Phase-1.0V one-shot collector model.""" + +from __future__ import annotations + +import argparse +import ast +import hashlib +import json +from pathlib import Path +import subprocess +from typing import Any + + +PHASE = "PHASE_1_0V_INACTIVE_ONE_SHOT_SHSRV_COLLECTOR" +STATUS = "INACTIVE_OFFLINE_COLLECTOR_MODEL_COMPLETE_NO_LIVE_CLIENT" +START_COMMIT = "50e042a96cd75925aa39a579c6242f3613caca03" +MODEL_SIZE = 7429 +MODEL_SHA256 = "f8a306dafee5d135919bec5afda789dd741e57f39803b7683fb8747c186db25c" +PARSER_SHA256 = "4701a057a98b4874e49e1bcf11db9a9a3a105e48f2c25e42796bff10f238f7c2" +AUTHORIZATION_FIELDS = ( + "target_build_authorized", "ps5_connection_authorized", + "device_request_authorized", "result_receive_authorized", + "device_transfer_authorized", "device_execution_authorized", + "installation_authorized", "autoload_authorized", + "device_write_authorized", "automatic_retry", "reconnect_authorized", +) + + +def load_json(path: Path) -> dict[str, Any]: + value = json.loads(path.read_text(encoding="utf-8")) + if not isinstance(value, dict): + raise ValueError("Phase-1.0V manifest is not an object") + return value + + +def sha256(path: Path) -> str: + digest = hashlib.sha256() + with path.open("rb") as stream: + for chunk in iter(lambda: stream.read(1024 * 1024), b""): + digest.update(chunk) + return digest.hexdigest() + + +def git(root: Path, *args: str) -> str: + result = subprocess.run( + ["git", *args], cwd=root, capture_output=True, text=True, check=False) + if result.returncode: + raise RuntimeError(result.stderr.strip() or "git failed") + return result.stdout + + +def all_authorizations_false(record: dict[str, Any]) -> bool: + return all(record.get(field) is False for field in AUTHORIZATION_FIELDS) + + +def activation_is_inactive(record: dict[str, Any]) -> bool: + return record == { + "active": False, + "target_address": None, + "target_port": None, + "window": None, + "exact_literal_path": None, + "commands": [], + "deadline_seconds": None, + "run_id": None, + "expires_at": None, + } + + +def model_is_offline(record: dict[str, Any]) -> bool: + return record == { + "relative_path": "tools/phase10v_shsrv_collector_model.py", + "size": MODEL_SIZE, + "sha256": MODEL_SHA256, + "phase10t_parser_sha256": PARSER_SHA256, + "offline_only": True, + "stdin_only": True, + "network_transport_present": False, + "socket_import_present": False, + "address_argument_present": False, + "port_argument_present": False, + "command_argument_present": False, + "command_send_present": False, + "filesystem_output_present": False, + } + + +def bounds_are_fail_closed(record: dict[str, Any]) -> bool: + return record == { + "max_raw_bytes": 65536, + "max_sanitized_bytes": 65536, + "max_chunks": 256, + "stdin_chunk_bytes": 4096, + "strict_utf8": True, + "one_shot": True, + "automatic_retry": False, + "reconnect": False, + "resume": False, + "fallback": False, + } + + +def telnet_is_offline_partial(record: dict[str, Any]) -> bool: + return record == { + "incremental": True, + "fragmented_negotiation_supported": True, + "subnegotiation_removed": True, + "incomplete_control_rejected": True, + "negotiation_replies_sent": False, + "live_prompt_contract_proven": False, + } + + +def sanitization_is_strict(record: dict[str, Any]) -> bool: + return record == { + "raw_transcript_output": False, + "serial_value_output": False, + "model_value_output": False, + "temperature_value_output": False, + "cpu_frequency_value_output": False, + "unknown_path_output": False, + "approved_literal_path_metadata_output": True, + "exact_identity_output": False, + "logical_buffer_discard": True, + "physical_memory_erasure_proven": False, + } + + +def review_remediation_is_complete(record: dict[str, Any]) -> bool: + return record == { + "telnet_doubled_iac_state_fixed": True, + "empty_chunks_ignored": True, + "expected_paths_absolute_normalized_and_character_allowlisted": True, + "firmware_metadata_gated_to_exact_9_60": True, + "compile_metadata_format_validated": True, + "parser_numeric_failure_normalized": True, + "physical_memory_erasure_proven": False, + } + + +def decision_requires_review(record: dict[str, Any]) -> bool: + return record == { + "offline_collector_model_complete": True, + "exact_deployed_shsrv_identity": "UNPROVEN", + "live_network_client_created": False, + "live_client_implementation_allowed": False, + "live_collection_allowed": False, + "launch_context_experiment_allowed": False, + "device_action_allowed": False, + "next_step": "HUMAN_REVIEW_OF_OFFLINE_COLLECTOR_MODEL", + } + + +def source_has_no_network_capability(path: Path) -> bool: + source = path.read_text(encoding="utf-8") + tree = ast.parse(source) + imports = { + alias.name.split(".", 1)[0] + for node in ast.walk(tree) + if isinstance(node, (ast.Import, ast.ImportFrom)) + for alias in node.names + } + forbidden_args = ( + '"--target"', '"--host"', '"--address"', '"--port"', + '"--command"', '"--connect"', '"--send"', + ) + return ( + not imports.intersection({ + "socket", "telnetlib", "urllib", "requests", "http", "ftplib"}) + and all(token not in source for token in forbidden_args) + and '"--offline-transcript"' in source + and '"--expected-path"' in source + and "sys.stdin.buffer.read(4096)" in source + and "open(" not in source + and "write_text(" not in source + and "write_bytes(" not in source + ) + + +def exact_file(path: Path, size: int, digest: str) -> bool: + return path.stat().st_size == size and sha256(path) == digest + + +def validate(root: Path) -> list[str]: + errors: list[str] = [] + try: + record = load_json( + root / "manifests/retroarch/phase-1.0v-inactive-shsrv-collector.json") + if record.get("phase") != PHASE or record.get("status") != STATUS: + errors.append("phase/status mismatch") + if record.get("start_commit") != START_COMMIT: + errors.append("start commit mismatch") + if not activation_is_inactive(record.get("activation", {})): + errors.append("activation record is not inert") + if not model_is_offline(record.get("model", {})): + errors.append("model identity or offline boundary mismatch") + if not bounds_are_fail_closed(record.get("bounds", {})): + errors.append("collector bounds were relaxed") + if not telnet_is_offline_partial(record.get("telnet_model", {})): + errors.append("Telnet model was promoted to a live contract") + if not sanitization_is_strict(record.get("sanitization", {})): + errors.append("sanitization or memory claim was relaxed") + if not review_remediation_is_complete( + record.get("review_remediation", {})): + errors.append("self-review remediation is incomplete") + if not all_authorizations_false(record.get("authorizations", {})): + errors.append("authorization remains active or missing") + if not decision_requires_review(record.get("decision", {})): + errors.append("live implementation or device decision is enabled") + performed = record.get("performed_actions", {}) + if not performed or not all(value is False for value in performed.values()): + errors.append("performed action is present") + tests = record.get("tests", {}) + if not ( + tests.get("chimera_gfx_ctest") == "77_OF_77_PASS" + and tests.get("phase10v_guardrails") == 18 + and tests.get("phase10v_model_tests") == 21 + and tests.get("safety_audit") == "PASS" + and tests.get("secret_scan") == "PASS" + and tests.get("network_required_by_tests") is False + and tests.get("hardware_claim_from_host_test") is False + ): + errors.append("test evidence mismatch") + + model_path = root / "tools/phase10v_shsrv_collector_model.py" + parser_path = root / "tools/phase10t_shsrv_transcript.py" + if not exact_file(model_path, MODEL_SIZE, MODEL_SHA256): + errors.append("collector model identity mismatch") + if sha256(parser_path) != PARSER_SHA256: + errors.append("Phase-1.0T parser identity mismatch") + if not source_has_no_network_capability(model_path): + errors.append("collector model exposes network, persistence or live arguments") + approval = ( + root / "docs/approvals/phase-1.0v-shsrv-collector.md").read_text( + encoding="utf-8") + for token in ( + "active=false", "ps5_connection_authorized=false", + "device_request_authorized=false", "automatic_retry=false", + "target_address=null", "target_port=null", "commands=[]", + ): + if token not in approval: + errors.append(f"inactive approval token missing: {token}") + tracked = git(root, "ls-files").splitlines() + if any(path.lower().endswith((".elf", ".self", ".sprx", ".pkg")) + for path in tracked): + errors.append("target artifact is tracked") + except (OSError, RuntimeError, ValueError, json.JSONDecodeError, SyntaxError) as error: + errors.append(str(error)) + return errors + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + errors = validate(args.root.resolve()) + if errors: + for error in errors: + print(f"ERROR: {error}") + return 1 + print("Phase-1.0V inactive collector-model validation passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/validate_retroarch_phase10w.py b/tools/validate_retroarch_phase10w.py new file mode 100644 index 0000000..ba083eb --- /dev/null +++ b/tools/validate_retroarch_phase10w.py @@ -0,0 +1,281 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate Phase-1.0W self-review and inactive client architecture.""" + +from __future__ import annotations + +import argparse +import ast +import hashlib +import json +from pathlib import Path +import subprocess +from typing import Any + + +PHASE = "PHASE_1_0W_INACTIVE_SHSRV_CLIENT_ARCHITECTURE" +STATUS = "INACTIVE_CLIENT_ARCHITECTURE_COMPLETE_NETWORK_IMPLEMENTATION_BLOCKED" +START_COMMIT = "665d02995fdb72080a20cd53c92265b1e24afb1c" +PARSER_SHA256 = "4701a057a98b4874e49e1bcf11db9a9a3a105e48f2c25e42796bff10f238f7c2" +COLLECTOR_SIZE = 7429 +COLLECTOR_SHA256 = "f8a306dafee5d135919bec5afda789dd741e57f39803b7683fb8747c186db25c" +POLICY_SIZE = 6997 +POLICY_SHA256 = "747d23c88f2722e8e8846599c3ac1dae3826eb3fca881caaad36b251f30f3592" +FAKE_SIZE = 1870 +FAKE_SHA256 = "e2f074e844102177f45fb0f97254108ee641db00931bb037574dee82111f316f" +AUTHORIZATION_FIELDS = ( + "target_build_authorized", "ps5_connection_authorized", + "device_request_authorized", "result_receive_authorized", + "device_transfer_authorized", "device_execution_authorized", + "installation_authorized", "autoload_authorized", + "device_write_authorized", "automatic_retry", "reconnect_authorized", +) + + +def load_json(path: Path) -> dict[str, Any]: + value = json.loads(path.read_text(encoding="utf-8")) + if not isinstance(value, dict): + raise ValueError("Phase-1.0W manifest is not an object") + return value + + +def sha256(path: Path) -> str: + digest = hashlib.sha256() + with path.open("rb") as stream: + for chunk in iter(lambda: stream.read(1024 * 1024), b""): + digest.update(chunk) + return digest.hexdigest() + + +def git(root: Path, *args: str) -> str: + result = subprocess.run( + ["git", *args], cwd=root, capture_output=True, text=True, check=False) + if result.returncode: + raise RuntimeError(result.stderr.strip() or "git failed") + return result.stdout + + +def all_authorizations_false(record: dict[str, Any]) -> bool: + return all(record.get(field) is False for field in AUTHORIZATION_FIELDS) + + +def activation_is_inactive(record: dict[str, Any]) -> bool: + return record == { + "active": False, + "policy_sha256": None, + "collector_sha256": None, + "run_id": None, + "target_address": None, + "target_port": None, + "window": None, + "exact_literal_path": None, + "commands": [], + "deadline_seconds": None, + "expires_at": None, + } + + +def architecture_has_no_transport(record: dict[str, Any]) -> bool: + return record == { + "policy_returns_data_only": True, + "policy_cli_present": False, + "network_transport_present": False, + "socket_import_present": False, + "dns_lookup_present": False, + "connect_present": False, + "command_formatter_present": False, + "command_sender_present": False, + "receive_present": False, + "filesystem_output_present": False, + "fake_transport_only": True, + } + + +def future_contract_is_bounded(record: dict[str, Any]) -> bool: + return record == { + "source_bound_port": 2323, + "maximum_connections": 1, + "maximum_deadline_seconds": 10, + "maximum_approval_lifetime_minutes": 15, + "allowed_windows": { + "T2_GREETING_AND_HELP": ["help"], + "T3_ONE_EXACT_PATH": ["stat", "sum"], + }, + "dual_record_exact_match_required": True, + "exact_record_shape_required": True, + "policy_hash_dual_match_required": True, + "operator_attestation_required": True, + "listener_attestation_required": True, + "spawned_shell_effects_acceptance_required": True, + "serial_query_acceptance_required": True, + "telemetry_query_acceptance_required": True, + "unproven_memory_erasure_acceptance_required": True, + "automatic_retry": False, + "reconnect": False, + "resume": False, + "fallback": False, + } + + +def live_components_remain_missing(record: dict[str, Any]) -> bool: + return set(record) == { + "consumed_attempt_receipt", "exclusive_sanitized_output", + "monotonic_deadline", "host_clock_acquisition", "network_transport", + "telnet_reply_contract", "prompt_completion_contract", + "bounded_receive_integration", "deterministic_close", + "error_path_redaction_review", + } and all(value is True for value in record.values()) + + +def decision_is_offline_only(record: dict[str, Any]) -> bool: + return record == { + "self_review_complete": True, + "review_findings_remediated": True, + "offline_client_architecture_complete": True, + "exact_deployed_shsrv_identity": "UNPROVEN", + "live_network_client_created": False, + "live_client_implementation_allowed": False, + "phase10x_offline_inactive_transport_implementation_allowed": True, + "live_collection_allowed": False, + "launch_context_experiment_allowed": False, + "device_action_allowed": False, + "next_step": "OFFLINE_INACTIVE_FAKE_SOCKET_TRANSPORT_IMPLEMENTATION", + } + + +def source_has_no_network_import(path: Path) -> bool: + tree = ast.parse(path.read_text(encoding="utf-8")) + imports = { + alias.name.split(".", 1)[0] + for node in ast.walk(tree) + if isinstance(node, (ast.Import, ast.ImportFrom)) + for alias in node.names + } + return not imports.intersection({ + "socket", "telnetlib", "urllib", "requests", "http", "ftplib"}) + + +def exact_file(path: Path, size: int, digest: str) -> bool: + return path.stat().st_size == size and sha256(path) == digest + + +def validate(root: Path) -> list[str]: + errors: list[str] = [] + try: + record = load_json( + root / "manifests/retroarch/phase-1.0w-inactive-client-architecture.json") + if record.get("phase") != PHASE or record.get("status") != STATUS: + errors.append("phase/status mismatch") + if record.get("start_commit") != START_COMMIT: + errors.append("start commit mismatch") + if not activation_is_inactive(record.get("activation", {})): + errors.append("tracked activation is not inert") + if not architecture_has_no_transport(record.get("architecture", {})): + errors.append("network or persistence capability was introduced") + if not future_contract_is_bounded(record.get("future_contract", {})): + errors.append("future contract was relaxed") + if not live_components_remain_missing( + record.get("missing_live_components", {})): + errors.append("missing live component was incorrectly claimed complete") + if not all_authorizations_false(record.get("authorizations", {})): + errors.append("authorization remains active or missing") + if not decision_is_offline_only(record.get("decision", {})): + errors.append("live or device decision is enabled") + performed = record.get("performed_actions", {}) + if not performed or not all(value is False for value in performed.values()): + errors.append("performed action is present") + bindings = record.get("source_bindings", {}) + if bindings != { + "phase10t_parser_sha256": PARSER_SHA256, + "remediated_phase10v_collector_size": COLLECTOR_SIZE, + "remediated_phase10v_collector_sha256": COLLECTOR_SHA256, + "phase10w_policy_size": POLICY_SIZE, + "phase10w_policy_sha256": POLICY_SHA256, + "fake_transport_size": FAKE_SIZE, + "fake_transport_sha256": FAKE_SHA256, + }: + errors.append("source bindings mismatch") + tests = record.get("tests", {}) + if not ( + tests.get("chimera_gfx_ctest") == "77_OF_77_PASS" + and tests.get("phase10w_guardrails") == 18 + and tests.get("phase10w_policy_tests") == 25 + and tests.get("remediated_phase10v_model_tests") == 21 + and tests.get("safety_audit") == "PASS" + and tests.get("secret_scan") == "PASS" + and tests.get("network_required_by_tests") is False + and tests.get("hardware_claim_from_host_test") is False + ): + errors.append("test evidence mismatch") + + identities = ( + (root / "tools/phase10t_shsrv_transcript.py", None, PARSER_SHA256), + (root / "tools/phase10v_shsrv_collector_model.py", + COLLECTOR_SIZE, COLLECTOR_SHA256), + (root / "tools/phase10w_shsrv_client_policy.py", + POLICY_SIZE, POLICY_SHA256), + (root / "tests/phase10w_fake_transport.py", FAKE_SIZE, FAKE_SHA256), + ) + for path, size, digest in identities: + if (size is not None and path.stat().st_size != size) or \ + sha256(path) != digest: + errors.append(f"source identity mismatch: {path.name}") + policy_path = root / "tools/phase10w_shsrv_client_policy.py" + fake_path = root / "tests/phase10w_fake_transport.py" + if not source_has_no_network_import(policy_path) or \ + not source_has_no_network_import(fake_path): + errors.append("policy or fake transport imports networking") + policy_source = policy_path.read_text(encoding="utf-8") + for token in ( + "SOURCE_BOUND_PORT = 2323", "MAX_DEADLINE_SECONDS = 10", + '"T2_GREETING_AND_HELP": ("help",)', + '"T3_ONE_EXACT_PATH": ("stat", "sum")', + "@dataclass(frozen=True)", + ): + if token not in policy_source: + errors.append(f"policy token missing: {token}") + if "def main(" in policy_source or "argparse" in policy_source: + errors.append("policy unexpectedly exposes a CLI") + phase_v = load_json( + root / "manifests/retroarch/phase-1.0v-inactive-shsrv-collector.json") + remediation = phase_v.get("review_remediation", {}) + if not remediation or \ + remediation.get("physical_memory_erasure_proven") is not False or \ + not all(value is True for key, value in remediation.items() + if key != "physical_memory_erasure_proven"): + errors.append("Phase-1.0V remediation evidence mismatch") + approval = ( + root / "docs/approvals/phase-1.0w-shsrv-client.md").read_text( + encoding="utf-8") + for token in ( + "active=false", "target_address=null", "target_port=null", + "commands=[]", "ps5_connection_authorized=false", + "device_request_authorized=false", "automatic_retry=false", + ): + if token not in approval: + errors.append(f"inactive approval token missing: {token}") + tracked = git(root, "ls-files").splitlines() + if any(path.lower().endswith((".elf", ".self", ".sprx", ".pkg")) + for path in tracked): + errors.append("target artifact is tracked") + except (OSError, RuntimeError, ValueError, json.JSONDecodeError, + SyntaxError) as error: + errors.append(str(error)) + return errors + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + errors = validate(args.root.resolve()) + if errors: + for error in errors: + print(f"ERROR: {error}") + return 1 + print("Phase-1.0W inactive client-architecture validation passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/validate_retroarch_phase10x.py b/tools/validate_retroarch_phase10x.py new file mode 100644 index 0000000..28346d2 --- /dev/null +++ b/tools/validate_retroarch_phase10x.py @@ -0,0 +1,291 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate Phase-1.0X inactive injected-transport evidence.""" + +from __future__ import annotations + +import argparse +import ast +import hashlib +import json +from pathlib import Path +import subprocess +from typing import Any + + +PHASE = "PHASE_1_0X_INACTIVE_INJECTED_TRANSPORT" +STATUS = "INACTIVE_INJECTED_TRANSPORT_COMPLETE_LIVE_PROTOCOL_BLOCKED" +START_COMMIT = "45228225e3a0e8811b4a5bcaa79e6f125db3a7f8" +SHSRV_COMMIT = "6f320637d56d344a0e7797753099e33238bbf146" +PARSER_SHA256 = "4701a057a98b4874e49e1bcf11db9a9a3a105e48f2c25e42796bff10f238f7c2" +COLLECTOR_SIZE = 7429 +COLLECTOR_SHA256 = "f8a306dafee5d135919bec5afda789dd741e57f39803b7683fb8747c186db25c" +POLICY_SIZE = 6997 +POLICY_SHA256 = "747d23c88f2722e8e8846599c3ac1dae3826eb3fca881caaad36b251f30f3592" +TRANSPORT_SIZE = 8040 +TRANSPORT_SHA256 = "568d7578482ecf2fcd9e29085b2eb9d8705fc699611508acdf22afd30f2ddd23" +TRANSPORT_TEST_SIZE = 13087 +TRANSPORT_TEST_SHA256 = "19709ed6ab456be428d262b3f0afb4f6f577b34e4db80e66a73ed63d9bf2cd43" +AUTHORIZATION_FIELDS = ( + "target_build_authorized", "ps5_connection_authorized", + "device_request_authorized", "result_receive_authorized", + "device_transfer_authorized", "device_execution_authorized", + "installation_authorized", "autoload_authorized", + "device_write_authorized", "automatic_retry", "reconnect_authorized", +) +NETWORK_MODULES = { + "socket", "telnetlib", "urllib", "requests", "http", "ftplib", + "asyncio", "selectors", +} + + +def load_json(path: Path) -> dict[str, Any]: + value = json.loads(path.read_text(encoding="utf-8")) + if not isinstance(value, dict): + raise ValueError("Phase-1.0X manifest is not an object") + return value + + +def sha256(path: Path) -> str: + digest = hashlib.sha256() + with path.open("rb") as stream: + for chunk in iter(lambda: stream.read(1024 * 1024), b""): + digest.update(chunk) + return digest.hexdigest() + + +def git(root: Path, *args: str) -> str: + result = subprocess.run( + ["git", *args], cwd=root, capture_output=True, text=True, check=False) + if result.returncode: + raise RuntimeError(result.stderr.strip() or "git failed") + return result.stdout.strip() + + +def exact_file(path: Path, size: int, digest: str) -> bool: + return path.stat().st_size == size and sha256(path) == digest + + +def all_authorizations_false(record: dict[str, Any]) -> bool: + return all(record.get(field) is False for field in AUTHORIZATION_FIELDS) + + +def activation_is_inactive(record: dict[str, Any]) -> bool: + return record == { + "active": False, "policy_sha256": None, "collector_sha256": None, + "transport_sha256": None, "run_id": None, "target_address": None, + "target_port": None, "window": None, "exact_literal_path": None, + "commands": [], "deadline_seconds": None, "expires_at": None, + } + + +def architecture_is_injected_only(record: dict[str, Any]) -> bool: + return record == { + "injected_adapter_only": True, + "injected_monotonic_clock_only": True, + "policy_cli_present": False, + "live_cli_present": False, + "network_transport_present": False, + "socket_import_present": False, + "dns_lookup_present": False, + "connect_present": False, + "target_address_present": False, + "command_byte_formatter_present": False, + "telnet_reply_generator_present": False, + "live_prompt_detector_present": False, + "fake_boundary_labels_only": True, + } + + +def evidence_is_bounded(record: dict[str, Any]) -> bool: + required_true = { + "receipt_before_adapter_open", "exclusive_leaf_create", "file_fsync", + "close_and_reopen", "reopened_bytes_verified", + "sanitized_output_receipt_hash_binding", "trusted_caller_owned_root_required", + } + required_false = { + "overwrite_supported", "delete_or_cleanup_supported", + "raw_transcript_persisted", "target_address_persisted", + "partial_file_cleanup", "partial_file_is_valid_evidence", + "directory_fsync", + } + return ( + set(record) == required_true | required_false | + {"directory_entry_durability"} + and all(record.get(field) is True for field in required_true) + and all(record.get(field) is False for field in required_false) + and record.get("directory_entry_durability") == "UNPROVEN" + ) + + +def deadline_is_partial(record: dict[str, Any]) -> bool: + return record == { + "one_absolute_monotonic_deadline": True, + "pre_and_post_boundary_checks": True, + "remaining_budget_passed_to_adapter": True, + "adapter_close_after_open_attempt": True, + "retry_loop_present": False, + "second_open_present": False, + "blocking_adapter_call_preemption": False, + "real_socket_timeout_present": False, + "live_cleanup_proven": False, + } + + +def decision_is_offline_only(record: dict[str, Any]) -> bool: + return record == { + "offline_injected_transport_complete": True, + "offline_local_evidence_complete_with_limitations": True, + "exact_deployed_shsrv_identity": "UNPROVEN", + "live_protocol_framing": "BLOCKED_UNPROVEN", + "live_network_client_created": False, + "live_client_implementation_allowed": False, + "live_collection_allowed": False, + "device_action_allowed": False, + "next_step": "OFFLINE_EXACT_PROMPT_AND_TELNET_FRAMING_AUDIT", + } + + +def source_has_no_network_import(path: Path) -> bool: + tree = ast.parse(path.read_text(encoding="utf-8")) + imports = { + alias.name.split(".", 1)[0] + for node in ast.walk(tree) + if isinstance(node, (ast.Import, ast.ImportFrom)) + for alias in node.names + } + return not imports.intersection(NETWORK_MODULES) + + +def validate(root: Path, shsrv_root: Path) -> list[str]: + errors: list[str] = [] + try: + manifest = load_json( + root / "manifests/retroarch/phase-1.0x-inactive-transport.json") + if manifest.get("phase") != PHASE or manifest.get("status") != STATUS: + errors.append("phase/status mismatch") + if manifest.get("start_commit") != START_COMMIT: + errors.append("start commit mismatch") + if not activation_is_inactive(manifest.get("activation", {})): + errors.append("tracked activation is not inert") + if not all_authorizations_false(manifest.get("authorizations", {})): + errors.append("authorization remains active or missing") + if not architecture_is_injected_only(manifest.get("architecture", {})): + errors.append("architecture contains live transport capability") + if not evidence_is_bounded(manifest.get("local_evidence", {})): + errors.append("local evidence contract mismatch") + if not deadline_is_partial(manifest.get("deadline_and_cleanup", {})): + errors.append("deadline limitations were promoted or changed") + if not decision_is_offline_only(manifest.get("decision", {})): + errors.append("offline-only decision mismatch") + performed = manifest.get("performed_actions", {}) + if not performed or not all(value is False for value in performed.values()): + errors.append("performed device or network action is present") + missing = manifest.get("missing_live_components", {}) + if len(missing) != 8 or not all(value is True for value in missing.values()): + errors.append("missing live component was promoted") + + bindings = manifest.get("source_bindings", {}) + expected_bindings = { + "shsrv_reference_commit": SHSRV_COMMIT, + "phase10t_parser_sha256": PARSER_SHA256, + "phase10v_collector_size": COLLECTOR_SIZE, + "phase10v_collector_sha256": COLLECTOR_SHA256, + "phase10w_policy_size": POLICY_SIZE, + "phase10w_policy_sha256": POLICY_SHA256, + "phase10x_transport_size": TRANSPORT_SIZE, + "phase10x_transport_sha256": TRANSPORT_SHA256, + "phase10x_transport_tests_size": TRANSPORT_TEST_SIZE, + "phase10x_transport_tests_sha256": TRANSPORT_TEST_SHA256, + } + if bindings != expected_bindings: + errors.append("source bindings mismatch") + identities = ( + (root / "tools/phase10t_shsrv_transcript.py", None, PARSER_SHA256), + (root / "tools/phase10v_shsrv_collector_model.py", + COLLECTOR_SIZE, COLLECTOR_SHA256), + (root / "tools/phase10w_shsrv_client_policy.py", + POLICY_SIZE, POLICY_SHA256), + (root / "tools/phase10x_inactive_transport.py", + TRANSPORT_SIZE, TRANSPORT_SHA256), + (root / "tests/test_phase10x_inactive_transport.py", + TRANSPORT_TEST_SIZE, TRANSPORT_TEST_SHA256), + ) + for path, size, digest in identities: + if (size is not None and path.stat().st_size != size) or \ + sha256(path) != digest: + errors.append(f"source identity mismatch: {path.name}") + + transport_path = root / "tools/phase10x_inactive_transport.py" + if not source_has_no_network_import(transport_path): + errors.append("inactive transport imports networking") + source = transport_path.read_text(encoding="utf-8") + for forbidden in ("def main(", "argparse", "target_address", "2323"): + if forbidden in source: + errors.append(f"inactive transport contains forbidden token: {forbidden}") + for required in ( + "os.O_EXCL", "os.fsync", "path.read_bytes()", + "adapter.close_once()", "receipt = evidence.create_consumed_receipt", + ): + if required not in source: + errors.append(f"transport control missing: {required}") + + if git(shsrv_root, "rev-parse", "HEAD") != SHSRV_COMMIT: + errors.append("shsrv reference commit mismatch") + if git(shsrv_root, "status", "--short"): + errors.append("shsrv reference tree is dirty") + sh_source = (shsrv_root / "sh.c").read_text(encoding="utf-8") + for token in ( + "sh_prompt(void)", 'getenv("PWD")', 'setenv("PWD", "/", 0)', + 'fprintf(stdout, "%s$ ", cwd ? cwd : "(null)")', "sh_prompt();", + ): + if token not in sh_source: + errors.append(f"shsrv prompt evidence missing: {token}") + + approval = ( + root / "docs/approvals/phase-1.0x-inactive-transport.md" + ).read_text(encoding="utf-8") + for token in ( + "active=false", "target_address=null", "target_port=null", + "commands=[]", "ps5_connection_authorized=false", + "device_request_authorized=false", "automatic_retry=false", + ): + if token not in approval: + errors.append(f"inactive approval token missing: {token}") + tests = manifest.get("tests", {}) + if not ( + tests.get("chimera_gfx_ctest") == "80_OF_80_PASS" + and tests.get("phase10x_guardrails") == 18 + and tests.get("phase10x_transport_tests") == 18 + and tests.get("safety_audit") == "PASS" + and tests.get("secret_scan") == "PASS" + and tests.get("network_required_by_tests") is False + and tests.get("hardware_claim_from_host_test") is False + ): + errors.append("test evidence mismatch") + tracked = git(root, "ls-files").splitlines() + if any(path.lower().endswith((".elf", ".self", ".sprx", ".pkg")) + for path in tracked): + errors.append("target artifact is tracked") + except (OSError, RuntimeError, ValueError, json.JSONDecodeError, + SyntaxError) as error: + errors.append(str(error)) + return errors + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + parser.add_argument("--shsrv-root", type=Path, required=True) + args = parser.parse_args() + errors = validate(args.root.resolve(), args.shsrv_root.resolve()) + if errors: + for error in errors: + print(f"ERROR: {error}") + return 1 + print("Phase-1.0X inactive injected-transport validation passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/validate_retroarch_phase10y.py b/tools/validate_retroarch_phase10y.py new file mode 100644 index 0000000..d5e077a --- /dev/null +++ b/tools/validate_retroarch_phase10y.py @@ -0,0 +1,336 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate Phase-1.0Y offline shsrv framing evidence.""" + +from __future__ import annotations + +import argparse +import ast +import hashlib +import json +from pathlib import Path +import subprocess +from typing import Any + + +PHASE = "PHASE_1_0Y_OFFLINE_SHSRV_FRAMING_AUDIT" +STATUS = "SOURCE_FAMILY_FRAMING_PARTIAL_PROMPT_COMPLETION_UNPROVEN" +START_COMMIT = "65c274f5ee723680ca6a576b74aa8b3c280978cd" +V07 = "74287f5db6b20320efd7892d7b29cf438fe7cb98" +V08 = "8f76139ee69df4b8cb7c3aee401f05bccb2c2a31" +V09 = "2f2bc5501d40064c18c06f06f7b1f4cab756389b" +V019 = "6f320637d56d344a0e7797753099e33238bbf146" +MODEL_SIZE = 6678 +MODEL_SHA256 = "5081898ec86be52900670be2f9949a20b9abb7781a6b04d5337178a8340775d4" +MODEL_TEST_SIZE = 6485 +MODEL_TEST_SHA256 = "802742450d65b237c0865e5820a8523131391988ca9eadd206766fb51693ca95" +AUTHORIZATION_FIELDS = ( + "target_build_authorized", "ps5_connection_authorized", + "device_request_authorized", "result_receive_authorized", + "device_transfer_authorized", "device_execution_authorized", + "installation_authorized", "autoload_authorized", + "device_write_authorized", "automatic_retry", "reconnect_authorized", +) +NETWORK_MODULES = { + "socket", "telnetlib", "urllib", "requests", "http", "ftplib", + "asyncio", "selectors", +} +LIBTELNET_TAGS = ( + "v0.9", "v0.10", "v0.10.1", "v0.11", "v0.12", "v0.13", + "v0.13.1", "v0.14", "v0.15", "v0.16", "v0.16.1", "v0.16.2", + "v0.17", "v0.18", "v0.18.1", "v0.18.2", "v0.19", +) + + +def load_json(path: Path) -> dict[str, Any]: + value = json.loads(path.read_text(encoding="utf-8")) + if not isinstance(value, dict): + raise ValueError("Phase-1.0Y manifest is not an object") + return value + + +def sha256(path: Path) -> str: + digest = hashlib.sha256() + with path.open("rb") as stream: + for chunk in iter(lambda: stream.read(1024 * 1024), b""): + digest.update(chunk) + return digest.hexdigest() + + +def git(root: Path, *args: str) -> str: + result = subprocess.run( + ["git", *args], cwd=root, capture_output=True, text=True, check=False) + if result.returncode: + raise RuntimeError(result.stderr.strip() or "git failed") + return result.stdout.strip() + + +def exact_file(path: Path, size: int, digest: str) -> bool: + return path.stat().st_size == size and sha256(path) == digest + + +def all_authorizations_false(record: dict[str, Any]) -> bool: + return all(record.get(field) is False for field in AUTHORIZATION_FIELDS) + + +def activation_is_inactive(record: dict[str, Any]) -> bool: + return record == { + "active": False, "framing_model_sha256": None, + "source_family": None, "run_id": None, "target_address": None, + "target_port": None, "commands": [], + } + + +def model_is_offline(record: dict[str, Any]) -> bool: + return record == { + "two_source_families": True, + "maximum_bytes": 65536, + "maximum_chunks": 256, + "network_import_present": False, + "cli_present": False, + "command_formatter_present": False, + "target_present": False, + "file_output_present": False, + "synthetic_negotiation_replies_only": True, + "prompt_candidate_never_exact": True, + "device_behavior_proven": False, + } + + +def prompt_remains_unproven(record: dict[str, Any]) -> bool: + return ( + record.get("prompt_source_shape") == "PWD_PLUS_DOLLAR_SPACE" + and record.get("prompt_has_newline") is False + and record.get("prompt_explicitly_flushed") is True + and record.get("prompt_precedes_each_readline") is True + and record.get("prompt_follows_synchronous_help") is True + and record.get("prompt_follows_waited_stat_sum") is True + and record.get("pwd_forced_overwrite") is False + and record.get("wire_chunk_boundaries_defined") is False + and record.get("short_write_completion_loop") is False + and record.get("external_client_local_echo_defined") is False + and record.get("terminal_shape_candidate_available") is True + and record.get("exact_live_completion_proven") is False + and len(record) == 12 + ) + + +def source_families_are_bound(record: dict[str, Any]) -> bool: + return record == { + "LEGACY_RAW_V07_V08": { + "versions": ["v0.7", "v0.8"], + "incoming_telnet_parser": False, + "outgoing_nvt_translation": False, + "telnet_controls_pass_to_shell": True, + "server_side_echo": False, + "proactive_negotiation": False, + }, + "LIBTELNET_NVT_V09_V019": { + "versions": list(LIBTELNET_TAGS), + "incoming_telnet_parser": True, + "outgoing_nvt_translation": True, + "empty_option_table": True, + "will_reply": "IAC_DONT", + "do_reply": "IAC_WONT", + "initial_wont_dont_reply": "NONE", + "server_side_echo": False, + "proactive_negotiation": False, + }, + } + + +def decision_is_offline_only(record: dict[str, Any]) -> bool: + return record == { + "official_source_families_identified": True, + "offline_framing_model_complete": True, + "exact_deployed_shsrv_identity": "UNPROVEN", + "exact_live_prompt_completion": "UNPROVEN", + "passive_no_negotiation_batch": "OFFLINE_DESIGN_CANDIDATE", + "live_network_client_created": False, + "live_client_implementation_allowed": False, + "phase10z_offline_passive_batch_contract_allowed": True, + "live_collection_allowed": False, + "device_action_allowed": False, + "next_step": "OFFLINE_PASSIVE_SOURCE_FAMILY_TOLERANT_BATCH_CONTRACT", + } + + +def source_has_no_network_import(path: Path) -> bool: + tree = ast.parse(path.read_text(encoding="utf-8")) + imports = { + alias.name.split(".", 1)[0] + for node in ast.walk(tree) + if isinstance(node, (ast.Import, ast.ImportFrom)) + for alias in node.names + } + return not imports.intersection(NETWORK_MODULES) + + +def validate(root: Path, shsrv_root: Path) -> list[str]: + errors: list[str] = [] + try: + manifest = load_json( + root / "manifests/retroarch/phase-1.0y-shsrv-framing.json") + if manifest.get("phase") != PHASE or manifest.get("status") != STATUS: + errors.append("phase/status mismatch") + if manifest.get("start_commit") != START_COMMIT: + errors.append("start commit mismatch") + if not activation_is_inactive(manifest.get("activation", {})): + errors.append("tracked activation is not inert") + if not all_authorizations_false(manifest.get("authorizations", {})): + errors.append("authorization remains active or missing") + if not model_is_offline(manifest.get("offline_model", {})): + errors.append("framing model gained live capability") + if not prompt_remains_unproven(manifest.get("prompt_and_completion", {})): + errors.append("prompt/completion evidence was promoted") + if not source_families_are_bound(manifest.get("source_families", {})): + errors.append("source-family contract mismatch") + if not decision_is_offline_only(manifest.get("decision", {})): + errors.append("offline-only decision mismatch") + performed = manifest.get("performed_actions", {}) + if not performed or not all(value is False for value in performed.values()): + errors.append("performed network or device action is present") + + bindings = manifest.get("source_bindings", {}) + expected_bindings = { + "shsrv_v07_commit": V07, + "shsrv_v08_commit": V08, + "shsrv_v09_commit": V09, + "shsrv_v019_current_commit": V019, + "current_sh_c_size": 13278, + "current_sh_c_sha256": + "3c4b7f76efdd157436ed4b353ee1b550bf3ff9df17c147b4762b767982fc8253", + "current_shsrv_c_size": 5293, + "current_shsrv_c_sha256": + "6ec71b4eb6c2bc1159f21568c1c9834f8aecac8d8881113bf09cf8981be19ee3", + "current_libtelnet_c_size": 45904, + "current_libtelnet_c_sha256": + "64d0b8dc96b128aed30945393d112c2deba000fd00741a9b24b24ae5c596edeb", + "current_libtelnet_h_size": 22302, + "current_libtelnet_h_sha256": + "8d3977ff3993480d18bee8ff91fc14e654b7fb0fdb6581cea91cdfb4120ecca8", + "legacy_v07_sh_c_size": 8449, + "legacy_v07_sh_c_sha256": + "bf97bc6dd3f49345ad8da9a29b28a5d6bcde5e53a6f32c60a538d6e187c7e05a", + "phase10x_transport_sha256": + "568d7578482ecf2fcd9e29085b2eb9d8705fc699611508acdf22afd30f2ddd23", + "phase10y_model_size": MODEL_SIZE, + "phase10y_model_sha256": MODEL_SHA256, + "phase10y_model_tests_size": MODEL_TEST_SIZE, + "phase10y_model_tests_sha256": MODEL_TEST_SHA256, + } + if bindings != expected_bindings: + errors.append("source binding record mismatch") + expected_files = { + "current_sh_c_size": (shsrv_root / "sh.c", 13278, + "3c4b7f76efdd157436ed4b353ee1b550bf3ff9df17c147b4762b767982fc8253"), + "current_shsrv_c_size": (shsrv_root / "shsrv.c", 5293, + "6ec71b4eb6c2bc1159f21568c1c9834f8aecac8d8881113bf09cf8981be19ee3"), + "current_libtelnet_c_size": (shsrv_root / "libtelnet.c", 45904, + "64d0b8dc96b128aed30945393d112c2deba000fd00741a9b24b24ae5c596edeb"), + "current_libtelnet_h_size": (shsrv_root / "libtelnet.h", 22302, + "8d3977ff3993480d18bee8ff91fc14e654b7fb0fdb6581cea91cdfb4120ecca8"), + } + for size_field, (path, size, digest) in expected_files.items(): + digest_field = size_field.replace("_size", "_sha256") + if bindings.get(size_field) != size or \ + bindings.get(digest_field) != digest or \ + not exact_file(path, size, digest): + errors.append(f"source identity mismatch: {path.name}") + model_path = root / "tools/phase10y_shsrv_framing_model.py" + test_path = root / "tests/test_phase10y_shsrv_framing_model.py" + if not exact_file(model_path, MODEL_SIZE, MODEL_SHA256): + errors.append("framing model identity mismatch") + if not exact_file(test_path, MODEL_TEST_SIZE, MODEL_TEST_SHA256): + errors.append("framing model test identity mismatch") + if bindings.get("phase10y_model_size") != MODEL_SIZE or \ + bindings.get("phase10y_model_sha256") != MODEL_SHA256 or \ + bindings.get("phase10y_model_tests_size") != MODEL_TEST_SIZE or \ + bindings.get("phase10y_model_tests_sha256") != MODEL_TEST_SHA256: + errors.append("model manifest binding mismatch") + if bindings.get("phase10x_transport_sha256") != \ + "568d7578482ecf2fcd9e29085b2eb9d8705fc699611508acdf22afd30f2ddd23": + errors.append("Phase-1.0X transport binding mismatch") + + if git(shsrv_root, "status", "--short"): + errors.append("shsrv reference tree is dirty") + if git(shsrv_root, "rev-parse", "HEAD") != V019: + errors.append("current shsrv commit mismatch") + for tag, commit in (("v0.7", V07), ("v0.8", V08), + ("v0.9", V09), ("v0.19", V019)): + if git(shsrv_root, "rev-parse", tag) != commit: + errors.append(f"shsrv {tag} identity mismatch") + for tag in ("v0.7", "v0.8"): + if git(shsrv_root, "ls-tree", tag, "libtelnet.c"): + errors.append(f"legacy {tag} unexpectedly contains libtelnet") + if "libtelnet.c" not in git(shsrv_root, "ls-tree", "v0.9", "libtelnet.c"): + errors.append("v0.9 libtelnet introduction is missing") + for tag in LIBTELNET_TAGS: + tagged_sh = git(shsrv_root, "show", f"{tag}:sh.c") + tagged_tree = git(shsrv_root, "ls-tree", tag, "libtelnet.c") + if not all(token in tagged_sh for token in ( + "static const telnet_telopt_t telopts[]", "{-1, 0, 0}", + "TELNET_FLAG_NVT_EOL", + "telnet_send_text(state.telnet, buf, len)")) or \ + "3d09e53bb2d9fe9498634f0998000146ac6bd214" not in tagged_tree: + errors.append(f"source-family evidence mismatch: {tag}") + + current_sh = (shsrv_root / "sh.c").read_text(encoding="utf-8") + libtelnet = (shsrv_root / "libtelnet.c").read_text(encoding="utf-8") + for token in ( + "static const telnet_telopt_t telopts[]", "{-1, 0, 0}", + "TELNET_FLAG_NVT_EOL", "telnet_recv(state.telnet, buf, len)", + "telnet_send_text(state.telnet, buf, len)", + 'fprintf(stdout, "%s$ ", cwd ? cwd : "(null)")', + ): + if token not in current_sh: + errors.append(f"current shsrv evidence missing: {token}") + for token in ("_send_negotiate(telnet, TELNET_DONT, telopt)", + "_send_negotiate(telnet, TELNET_WONT, telopt)", + "_send(telnet, CRNUL, 2)", "_send(telnet, CRLF, 2)"): + if token not in libtelnet: + errors.append(f"libtelnet evidence missing: {token}") + + if not source_has_no_network_import(model_path): + errors.append("framing model imports networking") + model_source = model_path.read_text(encoding="utf-8") + for forbidden in ("def main(", "argparse", "target_address", "2323"): + if forbidden in model_source: + errors.append(f"framing model contains forbidden token: {forbidden}") + tests = manifest.get("tests", {}) + if not ( + tests.get("chimera_gfx_ctest") == "83_OF_83_PASS" + and tests.get("phase10y_guardrails") == 18 + and tests.get("phase10y_framing_model_tests") == 22 + and tests.get("safety_audit") == "PASS" + and tests.get("secret_scan") == "PASS" + and tests.get("network_required_by_tests") is False + and tests.get("hardware_claim_from_host_test") is False + ): + errors.append("test evidence mismatch") + tracked = git(root, "ls-files").splitlines() + if any(path.lower().endswith((".elf", ".self", ".sprx", ".pkg")) + for path in tracked): + errors.append("target artifact is tracked") + except (OSError, RuntimeError, ValueError, json.JSONDecodeError, + SyntaxError) as error: + errors.append(str(error)) + return errors + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + parser.add_argument("--shsrv-root", type=Path, required=True) + args = parser.parse_args() + errors = validate(args.root.resolve(), args.shsrv_root.resolve()) + if errors: + for error in errors: + print(f"ERROR: {error}") + return 1 + print("Phase-1.0Y offline shsrv-framing validation passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/validate_retroarch_phase10z.py b/tools/validate_retroarch_phase10z.py new file mode 100644 index 0000000..c62c5b0 --- /dev/null +++ b/tools/validate_retroarch_phase10z.py @@ -0,0 +1,230 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate Phase-1.0Z offline passive-batch evidence.""" + +from __future__ import annotations + +import argparse +import ast +import hashlib +import json +from pathlib import Path +from typing import Any + + +PHASE = "PHASE_1_0Z_OFFLINE_PASSIVE_BATCH_CONTRACT" +STATUS = "PASSIVE_LF_BATCH_CONTRACT_COMPLETE_LIVE_TRANSPORT_BLOCKED" +START_COMMIT = "0433b73958f466b50c3b3f301f29e6af4ad9d319" +AUTHORIZATION_FIELDS = { + "target_build_authorized", "ps5_connection_authorized", + "device_request_authorized", "result_receive_authorized", + "device_transfer_authorized", "device_execution_authorized", + "installation_authorized", "autoload_authorized", + "device_write_authorized", "automatic_retry", "reconnect_authorized", + "resume_authorized", +} +SOURCE_BINDINGS = { + "phase10v_collector_size": 7429, + "phase10v_collector_sha256": "f8a306dafee5d135919bec5afda789dd741e57f39803b7683fb8747c186db25c", + "phase10w_policy_size": 6997, + "phase10w_policy_sha256": "747d23c88f2722e8e8846599c3ac1dae3826eb3fca881caaad36b251f30f3592", + "phase10x_transport_size": 8040, + "phase10x_transport_sha256": "568d7578482ecf2fcd9e29085b2eb9d8705fc699611508acdf22afd30f2ddd23", + "phase10y_framing_size": 6678, + "phase10y_framing_sha256": "5081898ec86be52900670be2f9949a20b9abb7781a6b04d5337178a8340775d4", + "phase10z_contract_size": 10487, + "phase10z_contract_sha256": "0728c2be7f368e0a7f4b68efe86f6e0c5c2f50704a41d0e1992b0bfec19dde06", + "phase10z_contract_tests_size": 9792, + "phase10z_contract_tests_sha256": "7706cc212a0fc683eb32acaef26ecaa64cbe5784ea6aeeed7e29ade11612490c", +} +SOURCE_FILES = { + "phase10v_collector": "tools/phase10v_shsrv_collector_model.py", + "phase10w_policy": "tools/phase10w_shsrv_client_policy.py", + "phase10x_transport": "tools/phase10x_inactive_transport.py", + "phase10y_framing": "tools/phase10y_shsrv_framing_model.py", + "phase10z_contract": "tools/phase10z_passive_batch_contract.py", + "phase10z_contract_tests": "tests/test_phase10z_passive_batch_contract.py", +} +NETWORK_MODULES = { + "socket", "asyncio", "selectors", "urllib", "http", "ftplib", + "requests", "telnetlib", "paramiko", +} + + +def load_json(path: Path) -> dict[str, Any]: + value = json.loads(path.read_text(encoding="utf-8")) + if not isinstance(value, dict): + raise ValueError("Phase-1.0Z manifest is not an object") + return value + + +def exact_file(path: Path, size: int, digest: str) -> bool: + try: + payload = path.read_bytes() + except OSError: + return False + return len(payload) == size and hashlib.sha256(payload).hexdigest() == digest + + +def _imports(tree: ast.AST) -> set[str]: + names: set[str] = set() + for node in ast.walk(tree): + if isinstance(node, ast.Import): + names.update(alias.name.split(".")[0] for alias in node.names) + elif isinstance(node, ast.ImportFrom) and node.module: + names.add(node.module.split(".")[0]) + return names + + +def validate_record(record: dict[str, Any], root: Path | None = None) -> list[str]: + errors: list[str] = [] + if record.get("phase") != PHASE or record.get("status") != STATUS: + errors.append("phase/status mismatch") + if record.get("start_commit") != START_COMMIT: + errors.append("start commit mismatch") + if record.get("activation") != { + "active": False, "batch_contract_sha256": None, "run_id": None, + "target_address": None, "target_port": None, "window": None, + "exact_literal_path": None, "deadline_seconds": None, "commands": [], + }: + errors.append("activation is not inert") + if record.get("source_bindings") != SOURCE_BINDINGS: + errors.append("source bindings mismatch") + + contract = record.get("batch_contract", {}) + expected_contract = { + "outbound_batches": 1, "ascii_only": True, "lf_only": True, + "nul_allowed": False, "cr_allowed": False, "iac_allowed": False, + "telnet_negotiation_emitted": False, "server_echo_required": False, + "retry_allowed": False, "reconnect_allowed": False, + "resume_allowed": False, "target_retained": False, + "maximum_literal_path_bytes": 512, "maximum_batch_bytes": 1035, + "windows": { + "T2_GREETING_AND_HELP": { + "commands": ["help"], "exact_payload_hex": "68656c700a", + "exact_payload_bytes": 5, + "completion_requirement": "KNOWN_COMPLETE_HELP_FINGERPRINT", + }, + "T3_ONE_EXACT_PATH": { + "commands": ["stat", "sum"], + "payload_shape": "stat PATH LF sum PATH LF", + "completion_requirement": "STAT_SIZE_AND_WEAK_SUM_FOR_EXACT_PATH", + }, + }, + } + if contract != expected_contract: + errors.append("batch contract mismatch") + + receive = record.get("receive_contract", {}) + expected_receive = { + "input_source": "ALREADY_SUPPLIED_SYNTHETIC_BYTES_ONLY", + "incoming_iac": "FAIL_CLOSED", "prompt_completion_used": False, + "remote_eof_completion_used": False, + "completion_event": "SYNTHETIC_HARD_DEADLINE_ONLY", + "partial_result": "INVALID", "unknown_help_fingerprint": "INVALID", + "source_family_selected": False, "network_transport_present": False, + "clock_present": False, "cli_present": False, + "file_output_present": False, "device_behavior_proven": False, + "exact_identity_proven": False, + } + if receive != expected_receive: + errors.append("receive contract mismatch") + + authority = record.get("authorizations", {}) + if set(authority) != AUTHORIZATION_FIELDS or any( + authority.get(field) is not False for field in AUTHORIZATION_FIELDS): + errors.append("authorization fields are not exactly false") + decision = record.get("decision", {}) + if decision != { + "offline_passive_batch_contract_complete": True, + "exact_deployed_shsrv_identity": "UNPROVEN", + "live_hard_deadline_preemption": "UNPROVEN", + "live_transport_created": False, "live_collection_allowed": False, + "device_action_allowed": False, + "phase10aa_offline_fake_adapter_integration_allowed": True, + "next_step": "OFFLINE_FAKE_ADAPTER_BATCH_AND_DEADLINE_INTEGRATION", + }: + errors.append("decision mismatch") + performed = record.get("performed_actions", {}) + if not performed or any(value is not False for value in performed.values()): + errors.append("a performed action is true or missing") + tests = record.get("tests", {}) + if tests != { + "chimera_gfx_ctest": "86_OF_86_PASS", "phase10z_guardrails": 18, + "phase10z_contract_tests": 25, "safety_audit": "PASS", + "secret_scan": "PASS", "network_required_by_tests": False, + "hardware_claim_from_host_test": False, + }: + errors.append("test evidence mismatch") + + side_effects = record.get("side_effects", {}) + if side_effects.get("offline_model") != "NONE" or \ + side_effects.get("effects_claimed_absent_on_device") is not False or \ + "EXPLICIT_ACCEPTANCE_REQUIRED" not in side_effects.values(): + errors.append("side effects are incomplete or promoted") + + if root is not None: + for prefix, relative in SOURCE_FILES.items(): + size = SOURCE_BINDINGS[f"{prefix}_size"] + digest = SOURCE_BINDINGS[f"{prefix}_sha256"] + if not exact_file(root / relative, size, digest): + errors.append(f"source identity mismatch: {relative}") + model_path = root / SOURCE_FILES["phase10z_contract"] + try: + source = model_path.read_text(encoding="utf-8") + tree = ast.parse(source) + except (OSError, SyntaxError, UnicodeError): + errors.append("contract source cannot be parsed") + else: + if _imports(tree) & NETWORK_MODULES: + errors.append("contract imports a network module") + function_names = { + node.name for node in ast.walk(tree) + if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)) + } + if {"main", "connect", "send", "recv", "seal_at_prompt", + "seal_at_eof"} & function_names: + errors.append("contract exposes a forbidden live or completion API") + if "target_address" in source or "target_port" in source: + errors.append("contract retains target data") + if "argparse" in source or "__main__" in source: + errors.append("contract exposes a CLI") + approval = (root / "docs/approvals/phase-1.0z-passive-batch.md").read_text( + encoding="utf-8") + if "active=false" not in approval or "attested=false" not in approval or \ + "target_address=null" not in approval: + errors.append("approval template is not inert") + phase_output_roots = ( + root / "tools", root / "tests", root / "docs", + root / "manifests", + ) + if any( + path.suffix.lower() in {".elf", ".self", ".sprx", ".pkg"} + for base in phase_output_roots for path in base.rglob("*") + if path.is_file() + ): + errors.append("target artifact exists in a Phase-1.0Z output area") + return errors + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + root = args.root.resolve() + try: + record = load_json( + root / "manifests/retroarch/phase-1.0z-passive-batch.json") + errors = validate_record(record, root) + except (OSError, ValueError, json.JSONDecodeError) as error: + errors = [f"validation input failed: {error}"] + if errors: + for error in errors: + print(f"ERROR: {error}") + return 1 + print("Phase-1.0Z offline passive-batch validation passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/verify_artifact_manifest.py b/tools/verify_artifact_manifest.py new file mode 100644 index 0000000..81b6561 --- /dev/null +++ b/tools/verify_artifact_manifest.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Verify an artifact manifest and, when supplied, its local artifact.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +import re +from pathlib import Path + + +def hash_file(path: Path) -> str: + digest = hashlib.sha256() + with path.open("rb") as stream: + for chunk in iter(lambda: stream.read(1024 * 1024), b""): + digest.update(chunk) + return digest.hexdigest() + + +def fail(message: str) -> None: + raise ValueError(message) + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--manifest", type=Path, required=True) + parser.add_argument("--artifact", type=Path) + args = parser.parse_args() + + document = json.loads(args.manifest.read_text(encoding="utf-8")) + if document.get("schema_version") != 1: + fail("unsupported artifact-manifest schema") + artifact_record = document["artifact"] + if not re.fullmatch(r"[0-9a-f]{64}", artifact_record["sha256"]): + fail("invalid artifact SHA-256") + if not re.fullmatch(r"[0-9a-f]{40}", document["source"]["commit"]): + fail("invalid source commit") + if document["source"].get("dirty") is not False: + fail("artifact source must be clean") + if not isinstance(document["execution"].get("execution_eligible"), bool): + fail("artifact execution eligibility must be an explicit boolean") + if any(document["execution"].get(key) is not False for key in + ("authorized", "transferred", "executed")): + fail("artifact manifest claims execution authority or activity") + if document["safety"].get("direct_gnm_imports") != 0: + fail("artifact manifest claims direct GNM imports") + gate = document["firmware_gate"] + if gate["embedded_identifier"] == "NONE" and gate["allowlisted"] is not False: + fail("NONE firmware gate cannot be allowlisted") + + if args.artifact is not None: + artifact = args.artifact.resolve(strict=True) + if artifact.name != artifact_record["filename"]: + fail("artifact filename mismatch") + if artifact.stat().st_size != artifact_record["size"]: + fail("artifact size mismatch") + if hash_file(artifact) != artifact_record["sha256"]: + fail("artifact digest mismatch") + + print(f"verified artifact manifest: {artifact_record['id']}") + return 0 + + +if __name__ == "__main__": + try: + raise SystemExit(main()) + except (KeyError, TypeError, ValueError, json.JSONDecodeError) as error: + print(f"artifact verification failed: {error}") + raise SystemExit(1) from error diff --git a/tools/verify_manifests.py b/tools/verify_manifests.py new file mode 100644 index 0000000..9175cd8 --- /dev/null +++ b/tools/verify_manifests.py @@ -0,0 +1,338 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Validate locked upstream metadata and the discovery-only manifest.""" + +from __future__ import annotations + +import argparse +import json +import re +from pathlib import Path + + +EXPECTED_SDK_COMMIT = "d2e2e585740362976a39fdd5ccf390f199a7bc37" +EXPECTED_SDK_SHA256 = "ebfb0acb5260511951a80e17db41650c62d20a8caf8659a230b928dc85005984" +EXPECTED_ELFLDR_COMMIT = "699e8bcff03e91e8d6ca6eba281af25c5a58d8c2" +EXPECTED_ELFLDR_SHA256 = "092d16ee0ede0c494947efd38d1a17bbd7cc4b022d3858ea898833c188c703e8" +EXPECTED_PLDMGR_COMMIT = "cfbc70f30f419b09bf2b52283f7409e2d3117ee1" +EXPECTED_PLDMGR_SHA256 = "518740adbacccb9094fadb07dd424c53ee290f38306449ccc9d6957fdf813c0b" + + +def fail(message: str) -> None: + raise ValueError(message) + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--locks", type=Path, required=True) + parser.add_argument("--symbols", type=Path, required=True) + parser.add_argument("--sbom", type=Path, required=True) + parser.add_argument("--artifact-schema", type=Path, required=True) + parser.add_argument("--denylist-schema", type=Path, required=True) + parser.add_argument("--denylist", type=Path, required=True) + parser.add_argument("--runtime-profile-schema", type=Path, required=True) + parser.add_argument("--runtime-profile", type=Path, required=True) + parser.add_argument("--phase06-audit", type=Path, required=True) + parser.add_argument("--phase07-audit", type=Path, required=True) + parser.add_argument("--phase07-proof", type=Path, required=True) + parser.add_argument("--sdk-stub", type=Path) + args = parser.parse_args() + + locks = json.loads(args.locks.read_text(encoding="utf-8")) + manifest = json.loads(args.symbols.read_text(encoding="utf-8")) + sbom = json.loads(args.sbom.read_text(encoding="utf-8")) + artifact_schema = json.loads(args.artifact_schema.read_text(encoding="utf-8")) + denylist_schema = json.loads(args.denylist_schema.read_text(encoding="utf-8")) + denylist = json.loads(args.denylist.read_text(encoding="utf-8")) + runtime_schema = json.loads( + args.runtime_profile_schema.read_text(encoding="utf-8") + ) + runtime_profile = json.loads(args.runtime_profile.read_text(encoding="utf-8")) + phase06_audit = json.loads(args.phase06_audit.read_text(encoding="utf-8")) + phase07_audit = json.loads(args.phase07_audit.read_text(encoding="utf-8")) + phase07_proof = json.loads(args.phase07_proof.read_text(encoding="utf-8")) + + if locks.get("schema_version") != 1: + fail("unsupported lock schema") + if locks.get("verified_at") != "2026-07-17": + fail("upstream verification date is stale") + sdk = locks["sources"]["ps5_payload_sdk"] + if sdk["commit"] != EXPECTED_SDK_COMMIT: + fail("unexpected SDK commit") + if sdk["asset"]["sha256"] != EXPECTED_SDK_SHA256: + fail("unexpected SDK asset digest") + if sdk["asset"]["size"] != 8810966: + fail("unexpected SDK asset size") + elfldr = locks["sources"]["ps5_elfldr_installed"] + if ( + elfldr["commit"] != EXPECTED_ELFLDR_COMMIT + or elfldr["asset"]["sha256"] != EXPECTED_ELFLDR_SHA256 + or elfldr["asset"]["size"] != 397000 + or elfldr["installed_identity"]["release_asset_hash_match"] is not True + ): + fail("exact installed elfldr identity changed") + payload_manager = locks["sources"]["ps5_payload_manager"] + if ( + payload_manager["commit"] != EXPECTED_PLDMGR_COMMIT + or payload_manager["asset"]["sha256"] != EXPECTED_PLDMGR_SHA256 + or payload_manager["asset"]["size"] != 2050320 + or payload_manager["installed_identity"]["release_asset_hash_match"] is not True + ): + fail("exact installed Payload Manager identity changed") + excluded = locks["sources"]["ps5_elfldr_candidate_not_installed"] + if ( + excluded["commit"] != "148b71c2fb9155d2550ef6a14eb03433e23acaeb" + or excluded["installed_identity"]["status"] != "excluded_by_installed_hash" + or excluded["installed_identity"]["release_asset_hash_match"] is not False + ): + fail("excluded elfldr candidate status changed") + + if manifest.get("schema_version") != 1: + fail("unsupported symbol-manifest schema") + if manifest.get("purpose") != "read_only_symbol_discovery": + fail("manifest purpose widened") + if manifest.get("firmware_allowlist") != ["9.60"]: + fail("discovery-only firmware allowlist must be exactly ['9.60']") + + policy = manifest["global_policy"] + expected_false = ("log_symbol_addresses", "submit", "draw", "dispatch", "flip", + "mutate_gpu_memory") + if policy.get("call_resolved_symbols") != "never": + fail("resolved-symbol call policy changed") + if any(policy.get(name) is not False for name in expected_false): + fail("a mutating or address-logging policy was enabled") + + names = [entry["name"] for entry in manifest["symbols"]] + if names != sorted(names): + fail("symbols must remain sorted for deterministic review") + if len(names) != len(set(names)): + fail("duplicate symbol") + for entry in manifest["symbols"]: + if not re.fullmatch(r"sceGnm[A-Za-z0-9_]+", entry["name"]): + fail(f"invalid GNM symbol name: {entry['name']}") + if entry.get("abi_status") != "name_only_unverified": + fail(f"ABI confidence widened: {entry['name']}") + if entry.get("call_policy") != "never": + fail(f"call policy widened: {entry['name']}") + + if args.sdk_stub is not None: + stub = args.sdk_stub.read_text(encoding="utf-8") + missing = [name for name in names if f".global {name}\\n" not in stub] + if missing: + fail(f"symbols missing from pinned SDK stub: {', '.join(missing)}") + + if sbom.get("spdxVersion") != "SPDX-2.3" or sbom.get("dataLicense") != "CC0-1.0": + fail("unsupported SPDX document metadata") + packages = {package["name"]: package for package in sbom["packages"]} + required_packages = { + "chimera-gfx", + "PS5 Payload SDK", + "PS5 Payload Manager", + "PS5 ELF Loader", + "PS5 SDL2 fork", + "RetroArch", + "actions/checkout", + "Ubuntu container image", + } + if set(packages) != required_packages: + fail("SBOM package inventory differs from the reviewed direct inventory") + if packages["chimera-gfx"]["licenseDeclared"] != "GPL-3.0-or-later": + fail("project license differs from the accepted ADR") + if packages["PS5 SDL2 fork"]["licenseDeclared"] != "Zlib": + fail("SDL license inventory is incorrect") + if artifact_schema.get("$schema") != "https://json-schema.org/draft/2020-12/schema": + fail("artifact schema draft changed") + if artifact_schema.get("properties", {}).get("schema_version", {}).get("const") != 1: + fail("artifact schema version changed") + execution_required = artifact_schema["properties"]["execution"]["required"] + if "execution_eligible" not in execution_required: + fail("artifact schema does not require explicit execution eligibility") + if denylist_schema.get("properties", {}).get("fail_closed", {}).get("const") is not True: + fail("denylist schema is not fail-closed") + if denylist.get("schema_version") != 1 or denylist.get("fail_closed") is not True: + fail("artifact denylist metadata changed") + if denylist.get("hash_algorithm") != "sha256": + fail("artifact denylist must use SHA-256") + entries = denylist.get("entries") + if not isinstance(entries, list) or len(entries) != 1: + fail("artifact denylist must retain the one permanent blocked artifact") + entry = entries[0] + if entry.get("sha256") != ( + "4be1c17b4964f2b68c39b5145bc4af4619c32512d60269ecf5c39728b390fa63" + ): + fail("permanently blocked artifact hash changed") + if ( + entry.get("status") != "BLOCKED" + or entry.get("permanent") is not True + or entry.get("execution_eligible") is not False + ): + fail("artifact denylist entry is not permanently blocked") + + if runtime_schema.get("$schema") != "https://json-schema.org/draft/2020-12/schema": + fail("controlled runtime schema draft changed") + if runtime_schema.get("properties", {}).get("schema_version", {}).get("const") != 1: + fail("controlled runtime schema version changed") + runtime_properties = runtime_schema.get("properties", {}) + if runtime_properties.get("profile", {}).get("const") != "controlled-ps5-runtime": + fail("controlled runtime schema profile name changed") + if runtime_properties.get("execution_authorized", {}).get("const") is not False: + fail("controlled runtime schema can grant execution authority") + expected_effects_schema = runtime_properties.get("expected_volatile_effects", {}) + if ( + expected_effects_schema.get("type") != "array" + or expected_effects_schema.get("uniqueItems") is not True + ): + fail("controlled runtime schema weakens expected volatile effects") + required_runtime_fields = set(runtime_schema.get("required", [])) + if { + "artifact", + "budgets", + "decision", + "deployment", + "effects", + "execution", + "execution_authorized", + "expected_volatile_effects", + "firmware", + "hard_blockers", + "loader", + "payload_manager", + "sdk", + } - required_runtime_fields: + fail("controlled runtime schema no longer requires all safety metadata") + + if runtime_profile.get("schema_version") != 1: + fail("unsupported controlled runtime profile") + if runtime_profile.get("profile") != "controlled-ps5-runtime": + fail("controlled runtime profile name changed") + if runtime_profile.get("decision") != "READY_FOR_HARDENED_RUNTIME_DEPLOYMENT": + fail("Phase-0.7 controlled runtime is not deployment-ready") + if runtime_profile.get("execution_authorized") is not False: + fail("Phase-0.7 profile granted execution authority") + if runtime_profile["execution"] != { + "authorized": False, + "executed": False, + "execution_eligible": True, + "transferred": False, + }: + fail("Phase-0.7 execution state is inconsistent") + runtime_artifact = runtime_profile["artifact"] + if runtime_artifact != { + "built": True, + "filename": "chimera-gfx-lifecycle-probe.elf", + "id": "chimera-gfx-lifecycle-phase07-fw960-v1", + "sha256": "bfb4a5cc768e162fe4c2fddf41c3978e152722918a39085277fd172cb95a7182", + "size": 112680, + "source_commit": "fe08300339a13f899fb78ea404ada381a5cba87c", + }: + fail("Phase-0.7 artifact identity changed") + if runtime_profile["firmware"] != { + "device_attested": False, + "evidence": "jens_explicitly_confirmed_exact_9.60", + "exact": "9.60", + }: + fail("Phase-0.7 firmware evidence changed") + if runtime_profile["budgets"] != { + "automatic_retry": False, + "filesystem_write_budget": "controlled_artifact_directory_only", + "maximum_runtime_ms": 2000, + "payload_network_access": "none", + "persistent_write_budget": "controlled_artifact_removable", + }: + fail("Phase-0.7 controlled budgets changed") + if runtime_profile["deployment"] != { + "installed": False, + "ready_for_installation": True, + "rollback_prepared": True, + }: + fail("Phase-0.7 deployment state changed") + if runtime_profile["loader"] != { + "base_commit": "699e8bcff03e91e8d6ca6eba281af25c5a58d8c2", + "hardened_commit": "197623058f509eddde18868dafcb92fdcac66464", + "installed": False, + "release": "v0.23-chimera-phase07", + "reproducible": True, + "sha256": "63e810982471eb40cae3a20aa9df9a0a02892f420e429874fae4e99aa400b561", + "size": 397000, + }: + fail("Phase-0.7 loader identity changed") + if runtime_profile["payload_manager"] != { + "base_commit": "cfbc70f30f419b09bf2b52283f7409e2d3117ee1", + "hardened_commit": "e23d94ff91233aa770e2342800c1467875bdef44", + "installed": False, + "release": "v0.3.1-chimera-controlled-phase07", + "reproducible": True, + "sha256": "8fecf8241a46246eddbd21e8bb4d875f5d76f1f4f4c6a11384df1f131aa5e5b1", + "size": 99560, + }: + fail("Phase-0.7 Payload Manager identity changed") + classifications = { + effect["classification"] for effect in runtime_profile["effects"] + } + if {"PERSISTENT_WRITE", "UNBOUNDED_OR_UNKNOWN"} & classifications: + fail("Phase-0.7 retains a hard effect classification") + if runtime_profile["hard_blockers"]: + fail("Phase-0.7 hard blockers are present") + expected_volatile = sorted( + effect["id"] + for effect in runtime_profile["effects"] + if effect["classification"] == "EXPECTED_VOLATILE_RUNTIME_EFFECT" + ) + if sorted(runtime_profile["expected_volatile_effects"]) != expected_volatile: + fail("Phase-0.7 expected volatile effects are inconsistent") + + if phase06_audit.get("decision") != "BLOCKED_VERSION_OR_UNBOUNDED_EFFECT": + fail("Phase-0.6 machine audit decision changed") + if phase06_audit["artifact"] != { + "built": False, + "execution_eligible": False, + "filename": None, + "sha256": None, + "size": None, + }: + fail("Phase-0.6 machine audit unexpectedly contains an artifact") + if any(phase06_audit["no_console_actions"].values()): + fail("Phase-0.6 machine audit claims a forbidden console action") + binary = phase06_audit["binary_evidence"] + if binary["dt_needed"] != [ + "libSceLibcInternal.sprx", + "libSceNet.sprx", + "libkernel_web.sprx", + ]: + fail("exact elfldr DT_NEEDED evidence changed") + if binary["relocations"] != {"relative": 140, "total": 164}: + fail("exact elfldr relocation evidence changed") + if binary["tls_present"] is not False: + fail("exact elfldr TLS evidence changed") + + phase07_decision = "READY_FOR_HARDENED_RUNTIME_DEPLOYMENT" + if phase07_audit.get("decision") != phase07_decision: + fail("Phase-0.7 offline-audit decision changed") + if any(phase07_audit["ps5_actions"].values()): + fail("Phase-0.7 audit claims a forbidden PS5 action") + if phase07_proof.get("decision") != phase07_decision: + fail("Phase-0.7 proof-matrix decision changed") + if phase07_proof.get("kernelwrite_free_claim") is not False: + fail("Phase-0.7 proof matrix incorrectly claims kernelwrite-free") + phase07_reviews = { + item["component"]: item["status"] for item in phase07_proof["reviews"] + } + if phase07_reviews.get("sdk_patch_init") != "UNSAFE": + fail("Phase-0.7 proof matrix hides normal CRT startup writes") + if phase07_reviews.get("firmware_9_60_runtime_behavior") != "UNPROVEN": + fail("Phase-0.7 proof matrix claims hardware evidence") + + print( + f"validated {len(names)} discovery-only symbols, upstream locks, " + f"{len(packages)} SBOM packages, runtime policy, Phase-0.7 audit, " + "and permanent denylist" + ) + return 0 + + +if __name__ == "__main__": + try: + raise SystemExit(main()) + except (KeyError, TypeError, ValueError, json.JSONDecodeError) as error: + print(f"manifest verification failed: {error}") + raise SystemExit(1) from error