Files
chimera-gfx-Public/tools/validate_phase09c_feasibility.py
Chimera GFX release export a6037502d7
phase0-ci / build-and-audit (push) Successful in 2m14s
Publish Chimera GFX source
2026-09-03 03:27:14 +02:00

688 lines
25 KiB
Python

#!/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())