This commit is contained in:
@@ -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
|
||||
Reference in New Issue
Block a user