ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
hil_convergence_safety_runtime_loader.py
Go to the documentation of this file.
1# SPDX-License-Identifier: MIT
2# Copyright (c) 2026 Brighton Sikarskie
3"""Source-only loader proofs for image-supervisor runtime mutations."""
4
5from __future__ import annotations
6
7import os
8import subprocess
9import sys
10from pathlib import Path
11
12import hil_convergence_safety_runtime_loader_harness as loader_harness
13import hil_convergence_safety_runtime_mutations as runtime_mutations
14from hil_convergence_safety_runtime_mutations import (
15 RuntimeMutationError,
16 SourceBundle,
17 _create_root,
18 _identity_text,
19 _no_residue,
20 _owned_root_scope,
21 _remove_root,
22 _run_supervisor,
23 _write_sources,
24)
25
26
27def _one_shot_result(
28 main_path: Path, process_path: Path, cases_path: Path
29) -> subprocess.CompletedProcess[bytes]:
30 """Load each private API, then require the cases globals to reject re-exec."""
31 return loader_harness.run(
32 main_path, process_path, cases_path, runtime_mutations.RESIDUE_TIMEOUT_SECONDS
33 )
34
35
36def _one_shot_case(root: Path, sources: SourceBundle) -> tuple[bool, frozenset[str]]:
37 """Require base refusal and prove deleting consumption makes the negative fire."""
38 supervisor, process_source, cases = sources
39 main_path, process_path, cases_path = _write_sources(root, supervisor, process_source, cases)
40 baseline = frozenset(path.name for path in root.iterdir())
41 base = _one_shot_result(main_path, process_path, cases_path)
42 source_guard = 'globals().get("_RA8_SUPERVISOR_CASES_VERSION")'
43 if cases.count(source_guard) != 1:
44 message = "supervisor cases one-shot source guard is not unique"
45 raise RuntimeMutationError(message)
46 mutant_main, mutant_process, mutant_cases = _write_sources(
47 root,
48 supervisor,
49 process_source,
50 cases.replace(source_guard, "CASES_LOAD_VERSION"),
51 )
52 mutation = _one_shot_result(mutant_main, mutant_process, mutant_cases)
53 exact = (
54 base.returncode == 0
55 and base.stdout == base.stderr == b""
56 and mutation.returncode == runtime_mutations.ONE_SHOT_MUTATION_STATUS
57 and mutation.stdout == mutation.stderr == b""
58 )
59 return exact, baseline
60
61
62def _source_only_cases(sources: SourceBundle) -> list[tuple[str, bool]]:
63 """Prove the exact source-only diagnostic and loader sentinel."""
64 supervisor, process_source, cases = sources
65 root, identity = _create_root()
66 try:
67 _main_path, _process_path, cases_path = _write_sources(
68 root, supervisor, process_source, cases
69 )
70 descriptor = os.open(cases_path, os.O_RDONLY | os.O_NOFOLLOW)
71 try:
72 direct = subprocess.run( # noqa: S603 -- current interpreter and bound source FD
73 (sys.executable, "-B", "-I", "-S", f"/proc/self/fd/{descriptor}"),
74 pass_fds=(descriptor,),
75 capture_output=True,
76 timeout=runtime_mutations.RESIDUE_TIMEOUT_SECONDS,
77 check=False,
78 )
79 finally:
80 os.close(descriptor)
81 one_shot, baseline = _one_shot_case(root, sources)
82 source_exact = (
83 direct.returncode == 1
84 and direct.stderr.count(b"RuntimeError: supervisor cases module is source-only") == 1
85 )
86 sentinel = ' "_RA8_SUPERVISOR_CASES_VERSION": 1,\n'
87 if supervisor.count(sentinel) != 1:
88 message = "supervisor cases load sentinel is not unique"
89 raise RuntimeMutationError(message)
90 mutant_main, mutant_process, mutant_cases = _write_sources(
91 root, supervisor.replace(sentinel, ""), process_source, cases
92 )
93 names = {path.name for path in root.iterdir()}
94 status, clean, _stderr = _run_supervisor(
95 mutant_main,
96 mutant_process,
97 mutant_cases,
98 ("--selftest-missing-entry", str(root), _identity_text(root)),
99 )
100 sentinel_refused = (
101 status == runtime_mutations.PUBLIC_REFUSAL_STATUS
102 and clean
103 and {path.name for path in root.iterdir()} == names
104 )
105 return [
106 ("supervisor cases source-only diagnostic is exact", source_exact),
107 (
108 "supervisor cases grant is consumed before exact re-exec refusal",
109 one_shot and frozenset(path.name for path in root.iterdir()) == baseline,
110 ),
111 ("supervisor cases load sentinel removal refuses before effects", sentinel_refused),
112 ("source-only runtime leaves no process or descriptor residue", _no_residue((root,))),
113 ]
114 finally:
115 _remove_root(root, identity)
116
117
118def cases(inputs: dict[str, str]) -> list[tuple[str, bool]]:
119 """Run source-only, loader collision, removal, and repeat-load proofs."""
120 sources = (
121 inputs["devcontainer_image_selftest_supervisor"],
122 inputs["devcontainer_image_selftest_process"],
123 inputs["devcontainer_image_selftest_supervisor_cases"],
124 )
125 with _owned_root_scope():
126 return _source_only_cases(sources)