ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
hil_convergence_safety_image_process_analysis.py
Go to the documentation of this file.
1# SPDX-License-Identifier: MIT
2# Copyright (c) 2026 Brighton Sikarskie
3"""Source analysis for the devcontainer image process-authority policy."""
4
5from __future__ import annotations
6
7import ast
8import re
9
10import hil_convergence_safety_image_lock_receipts as image_lock_receipts
11import hil_convergence_safety_image_process_policy as catalog
12import hil_convergence_safety_image_subreaper_policy as subreaper_policy
13
14CROSS_LANGUAGE_SCOPED_TOKENS = catalog.CROSS_LANGUAGE_SCOPED_TOKENS
15PROCESS_MODULE_SEMANTIC_TOKENS = catalog.PROCESS_MODULE_SEMANTIC_TOKENS
16SUPERVISOR_CASES_SCOPED_PROCESS_TOKENS = catalog.SUPERVISOR_CASES_SCOPED_PROCESS_TOKENS
17SUPERVISOR_CASES_SEMANTIC_PROCESS_TOKENS = catalog.SUPERVISOR_CASES_SEMANTIC_PROCESS_TOKENS
18SUPERVISOR_SCOPED_LOADER_TOKENS = catalog.SUPERVISOR_SCOPED_LOADER_TOKENS
19SUPERVISOR_SEMANTIC_PROCESS_TOKENS = catalog.SUPERVISOR_SEMANTIC_PROCESS_TOKENS
20TRIPWIRE_LABELS = catalog.TRIPWIRE_LABELS
21TRIPWIRE_PATTERN = catalog.TRIPWIRE_PATTERN
22
23
24def _cross_language_finding(key: str, owner: str, token: str) -> str:
25 """Return one exact cross-language source diagnostic."""
26 return f"devcontainer image source policy: {key}:{owner} token is not unique: {token}"
27
28
29def _scoped_finding(authority: str, function: str, kind: str, token: str) -> str:
30 """Return one exact function-scoped semantic diagnostic."""
31 return f"{authority}: {function} {kind} token is not unique: {token}"
32
33
34def _precomputed_semantic_findings(label: str) -> tuple[str, ...] | None:
35 """Return findings owned by split or non-Python process policies."""
36 if (subreaper := subreaper_policy.semantic_findings(label)) is not None:
37 return subreaper
38 if label in TRIPWIRE_LABELS:
39 return ("devcontainer image source policy: build tripwire count drifted",)
40 return image_lock_receipts.semantic_process_findings(label)
41
42
43def semantic_process_findings(label: str) -> tuple[str, ...] | None:
44 """Return the exact focused diagnostic for one process mutation label."""
45 if (precomputed := _precomputed_semantic_findings(label)) is not None:
46 return precomputed
47 if (token := PROCESS_MODULE_SEMANTIC_TOKENS.get(label)) is not None:
48 return (
49 "devcontainer image process helper: required process-authority token "
50 f"is not unique: {token}",
51 )
52 for candidate, key, owner, token in CROSS_LANGUAGE_SCOPED_TOKENS:
53 if label == candidate:
54 return (_cross_language_finding(key, owner, token),)
55 scoped = (
56 (SUPERVISOR_CASES_SCOPED_PROCESS_TOKENS, "devcontainer image supervisor cases"),
57 (SUPERVISOR_SCOPED_LOADER_TOKENS, "devcontainer image supervisor"),
58 )
59 for specifications, authority in scoped:
60 if (spec := specifications.get(label)) is not None:
61 return (_scoped_finding(authority, *spec),)
62 token = SUPERVISOR_SEMANTIC_PROCESS_TOKENS.get(label)
63 authority = "devcontainer image supervisor"
64 if token is None:
65 token = SUPERVISOR_CASES_SEMANTIC_PROCESS_TOKENS.get(label)
66 authority = "devcontainer image supervisor cases"
67 if token is None:
68 return None
69 findings = (f"{authority}: required process-authority token is not unique: {token}",)
70 if label == "bound-exit supervisor pre-spawn signal block removed":
71 findings += ("devcontainer image supervisor: subreaper cleanup order drifted",)
72 return findings
73
74
75def _exact_tokens(source: str, label: str, tokens: tuple[str, ...]) -> list[str]:
76 """Require every Python process-authority token exactly once."""
77 return [
78 f"{label}: required process-authority token is not unique: {token}"
79 for token in tokens
80 if source.count(token) != 1
81 ]
82
83
84def _source_ordered(source: str, anchors: tuple[str, ...]) -> bool:
85 """Find repeated anchors only after the preceding authority."""
86 position = -1
87 for anchor in anchors:
88 position = source.find(anchor, position + 1)
89 if position < 0:
90 return False
91 return True
92
93
94def _function_source(source: str, name: str) -> str | None:
95 """Return one complete top-level Python function from parsed source."""
96 try:
97 module = ast.parse(source)
98 except SyntaxError:
99 return None
100 scope = module.body
101 target = name
102 if "." in name:
103 class_name, target = name.split(".", 1)
104 classes = [
105 node
106 for node in module.body
107 if isinstance(node, ast.ClassDef) and node.name == class_name
108 ]
109 if len(classes) != 1:
110 return None
111 scope = classes[0].body
112 matches = [
113 node
114 for node in scope
115 if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)) and node.name == target
116 ]
117 if len(matches) != 1:
118 return None
119 function = matches[0]
120 if function.end_lineno is None:
121 return None
122 lines = source.splitlines(keepends=True)
123 return "".join(lines[function.lineno - 1 : function.end_lineno])
124
125
126def _bash_function_source(source: str, name: str) -> str | None:
127 """Return one complete top-level Bash function body."""
128 opening = re.compile(rf"(?m)^{re.escape(name)}\‍(\‍) \{{\n")
129 matches = list(opening.finditer(source))
130 if len(matches) != 1:
131 return None
132 start = matches[0].start()
133 match = re.search(r"(?m)^}\s*$", source[matches[0].end() :])
134 if match is None:
135 return None
136 end = matches[0].end() + match.end()
137 return source[start:end]
138
139
140def _owner_source(source: str, key: str, owner: str) -> str | None:
141 """Resolve one Python or Bash semantic owner without widening its scope."""
142 if owner == "<module>":
143 return source
144 if key in (
145 "devcontainer_image_selftest_process",
146 "devcontainer_image_selftest_supervisor",
147 "devcontainer_image_selftest_supervisor_cases",
148 ):
149 return _function_source(source, owner)
150 body = _bash_function_source(source, owner)
151 if body is not None and owner == "run_bound_exit_supervisor":
152 lines = body.splitlines()
153 return "\n".join(
154 line
155 for index, line in enumerate(lines)
156 if line.rstrip().endswith("|| status=$?")
157 or (
158 line.rstrip().endswith("\\")
159 and index + 1 < len(lines)
160 and lines[index + 1].rstrip().endswith("|| status=$?")
161 )
162 )
163 return body
164
165
166def source_errors(inputs: dict[str, str]) -> list[str]:
167 """Bind fixed launchers, roots, entry FDs, traps, and tripwires by owner."""
168 errors = _exact_tokens(
169 inputs["devcontainer_image_selftest_process"],
170 "devcontainer image process helper",
171 tuple(PROCESS_MODULE_SEMANTIC_TOKENS.values()),
172 )
173 grouped: dict[tuple[str, str], list[str]] = {}
174 for _label, key, owner, token in CROSS_LANGUAGE_SCOPED_TOKENS:
175 grouped.setdefault((key, owner), []).append(token)
176 for (key, owner), tokens in grouped.items():
177 body = _owner_source(inputs[key], key, owner)
178 if body is None:
179 finding = f"devcontainer image source policy: {key}:{owner} owner is missing, "
180 finding += "ambiguous, or unparseable"
181 errors.append(finding)
182 continue
183 missing = [
184 _cross_language_finding(key, owner, token) for token in tokens if body.count(token) != 1
185 ]
186 errors.extend(missing)
187 positions = [body.find(token) for token in tokens]
188 if not missing and positions != sorted(positions):
189 errors.append(f"devcontainer image source policy: {key}:{owner} order drifted")
190 tripwire_body = _bash_function_source(
191 inputs["devcontainer_image_selftest_cases"], "selftest_managed_discovery_and_open"
192 )
193 if tripwire_body is None or tripwire_body.count(TRIPWIRE_PATTERN) != len(TRIPWIRE_LABELS):
194 errors.append("devcontainer image source policy: build tripwire count drifted")
195 return errors + image_lock_receipts.process_source_errors(inputs)
196
197
198def _scoped_token_errors(
199 source: str,
200 authority: str,
201 specifications: dict[str, tuple[str, str, str]],
202) -> list[str]:
203 """Bind each semantic token only inside its owning function."""
204 errors = []
205 functions: dict[str, list[tuple[str, str]]] = {}
206 for function, kind, token in specifications.values():
207 functions.setdefault(function, []).append((kind, token))
208 for function, scoped in functions.items():
209 body = _function_source(source, function)
210 if body is None:
211 finding = f"{authority}: {function} scoped owner is missing, ambiguous, or unparseable"
212 errors.append(finding)
213 continue
214 missing = [
215 _scoped_finding(authority, function, kind, token)
216 for kind, token in scoped
217 if body.count(token) != 1
218 ]
219 errors.extend(missing)
220 positions = [body.find(token) for _, token in scoped]
221 if not missing and positions != sorted(positions):
222 errors.append(f"{authority}: {function} scoped proof order drifted")
223 return errors
224
225
226def _supervisor_token_errors(supervisor: str) -> list[str]:
227 """Bind the supervisor to retained roots, entries, and child groups."""
228 return _exact_tokens(
229 supervisor,
230 "devcontainer image supervisor",
231 (
232 'CASES_ARG = "--cases-fd"',
233 "def _open_suite_root_authority(",
234 "os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW",
235 "def _close_suite_root_authority(",
236 "def _anchored_root_descriptor(path: Path) -> int:",
237 "def _open_entry_authority(",
238 "def _census_cleanup_retry_selftest(",
239 *dict.fromkeys(
240 token
241 for label, token in SUPERVISOR_SEMANTIC_PROCESS_TOKENS.items()
242 if label not in subreaper_policy.MOVED_PROCESS_TOKENS
243 ),
244 "process != os.getpgrp()",
245 "process != os.getsid(0)",
246 "_suite_root_path_is_safe(resolved, metadata)",
247 "identity == expected_identity",
248 "_spawn_payload(entry_authority, (death_descriptor, root_descriptor))",
249 "for private_descriptor in (death_descriptor, root_descriptor):",
250 "def _load_cases_dispatch(descriptor: int)",
251 "cases_dispatch = _load_cases_dispatch(cases_descriptor)",
252 "hidden_status = _dispatch_controller(request_argv)",
253 'f"{identity[0]}:{identity[1]}" != request_argv[6]',
254 "anchored_request = SupervisorRequest(",
255 "root_integrity = _close_suite_root_authority(descriptor, root, identity)",
256 ),
257 )
258
259
260def _supervisor_cases_token_errors(cases_source: str) -> list[str]:
261 """Bind authenticated cases to their adversarial cleanup directions."""
262 return _exact_tokens(
263 cases_source,
264 "devcontainer image supervisor cases",
265 (
266 *SUPERVISOR_CASES_SEMANTIC_PROCESS_TOKENS.values(),
267 "!= CASES_LOAD_VERSION:",
268 'message = "supervisor cases module is source-only"',
269 'identity = f"{metadata.st_dev}:{metadata.st_ino}"\n resolved = root.resolve',
270 "def _controller_isolation_selftest(",
271 'root_descriptor, "0:0", root / "controller-wrong-identity.status"',
272 'sibling = root / "controller-sibling"',
273 "def _reap_cleanup_retry_selftest(",
274 'original_reap = vars(BoundGroup)["_reap"]',
275 'type.__setattr__(BoundGroup, "_reap", fail_reap)',
276 "and not supervisor.cleaning\n"
277 " and supervisor.entry_descriptor is not None",
278 "observation_injected = True\n _inject_observation_failure()",
279 "succeeded = observation_injected and supervisor.pid is not None",
280 "def _open_validated_root(",
281 'f"{opened_identity[0]}:{opened_identity[1]}" != expected_identity',
282 "anchored_root = _anchored_root_path(descriptor)",
283 "root_integrity = _close_suite_root_authority(descriptor, root, opened_identity)",
284 "status = INTEGRITY_REFUSAL_STATUS",
285 ),
286 )
287
288
289def _supervisor_order_errors(supervisor: str) -> list[str]:
290 """Require root, group, and controller proofs in fail-closed order."""
291 orders = (
292 (
293 "def _install_interruption_handlers(supervisor: BoundGroup) -> None:",
294 "def _supervise(",
295 "old_mask = signal.pthread_sigmask(signal.SIG_BLOCK, MANAGED_SIGNALS)",
296 "_install_interruption_handlers(supervisor)",
297 "supervisor.spawn(source_descriptor, launch)",
298 ),
299 (
300 "process = os.getpid()",
301 "process != os.getpgrp()",
302 "for managed in MANAGED_SIGNALS:",
303 "child = _spawn_payload(",
304 "select.select((death_descriptor,), (), (), POLL_SECONDS)",
305 "for private_descriptor in (death_descriptor, root_descriptor):",
306 "os.killpg(os.getpgrp(), signal.SIGKILL)",
307 ),
308 (
309 "descriptor, identity = _open_suite_root_authority(root)",
310 'f"{identity[0]}:{identity[1]}" != request_argv[6]',
311 "anchored_request = SupervisorRequest(",
312 "result = _supervise(anchored_request)",
313 "root_integrity = _close_suite_root_authority(descriptor, root, identity)",
314 ),
315 )
316 if any(not _source_ordered(supervisor, order) for order in orders):
317 return ["devcontainer image supervisor: cleanup proof order drifted"]
318 return []
319
320
321def supervisor_errors(supervisor: str, cases_source: str, process_source: str) -> list[str]:
322 """Return retained-root and child-process findings for Python helpers."""
323 return (
324 _supervisor_token_errors(supervisor)
325 + _supervisor_cases_token_errors(cases_source)
326 + _scoped_token_errors(
327 supervisor, "devcontainer image supervisor", SUPERVISOR_SCOPED_LOADER_TOKENS
328 )
329 + _scoped_token_errors(
330 cases_source,
331 "devcontainer image supervisor cases",
332 SUPERVISOR_CASES_SCOPED_PROCESS_TOKENS,
333 )
334 + _supervisor_order_errors(supervisor)
335 + subreaper_policy.errors(supervisor, process_source)
336 )