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

194 lines
6.7 KiB
Python

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