ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
devcontainer_image_selftest_supervisor_cases.py
Go to the documentation of this file.
1# SPDX-License-Identifier: MIT
2# Copyright (c) 2026 Brighton Sikarskie
3"""Authenticated entry and descriptor regressions for the image supervisor."""
4
5from __future__ import annotations
6
7import errno
8import os
9import select
10import signal
11import stat
12import subprocess
13import sys
14import time
15from contextlib import suppress
16from pathlib import Path
17
18import __main__
19
20HIDDEN_ARG_COUNT = 5
21ROOT_ARG_COUNT = 4
22CASES_LOAD_VERSION = 1
23CLOSED_DESCRIPTOR = 99
24PRIVATE_MODE = 0o700
25SUITE_ROOT_SUFFIX_LENGTH = 32
26STAT_SELFTEST_ARG_COUNT = 2
27TEST_PROCESS_GROUP = 42
28USAGE_STATUS = 64
29CANONICAL_TMP = Path(
30 "/tmp" # noqa: S108 -- fixed physical parent; random mode-0700 inode-bound direct child
31)
32if globals().get("_RA8_SUPERVISOR_CASES_VERSION") != CASES_LOAD_VERSION:
33 message = "supervisor cases module is source-only"
34 raise RuntimeError(message)
35MAIN_API = vars(__main__)
36DEADLINE_SECONDS = MAIN_API["DEADLINE_SECONDS"]
37ENTRY_MAX_BYTES = MAIN_API["ENTRY_MAX_BYTES"]
38HARDLINK_COUNT = MAIN_API["HARDLINK_COUNT"]
39INTEGRITY_REFUSAL_STATUS = MAIN_API["INTEGRITY_REFUSAL_STATUS"]
40POLL_SECONDS = MAIN_API["POLL_SECONDS"]
41PUBLIC_REFUSAL_STATUS = MAIN_API["PUBLIC_REFUSAL_STATUS"]
42RECEIPT_MODE = MAIN_API["RECEIPT_MODE"]
43RECEIPT_MAX_BYTES = MAIN_API["RECEIPT_MAX_BYTES"]
44SELFTEST_WATCHDOG_TIMEOUT_SECONDS = MAIN_API["SELFTEST_WATCHDOG_TIMEOUT_SECONDS"]
45STALL_STATUS = MAIN_API["STALL_STATUS"]
46BoundGroup = MAIN_API["BoundGroup"]
47ControllerLaunch = MAIN_API["ControllerLaunch"]
48ProcessIdentity = MAIN_API["ProcessIdentity"]
49SupervisionControls = MAIN_API["SupervisionControls"]
50SupervisorRequest = MAIN_API["SupervisorRequest"]
51_bind_process = MAIN_API["_bind_process"]
52_close_suite_root_authority = MAIN_API["_close_suite_root_authority"]
53_controller = MAIN_API["_controller"]
54_group_members = MAIN_API["_group_members"]
55_stat_group = MAIN_API["_stat_group"]
56_wait_group_populated = MAIN_API["_wait_group_populated"]
57_wait_group_gone = MAIN_API["_wait_group_gone"]
58_census_cleanup_retry_selftest = MAIN_API["_census_cleanup_retry_selftest"]
59_open_entry_authority = MAIN_API["_open_entry_authority"]
60_open_suite_root_authority = MAIN_API["_open_suite_root_authority"]
61_supervise = MAIN_API["_supervise"]
62_write_exclusive = MAIN_API["_write_exclusive"]
63_write_exact = MAIN_API["_write_exact"]
64_anchored_root_path = MAIN_API["_anchored_root_path"]
65SUPERVISOR_PROGRAM = __main__.__file__
66
67
68def _bind_group_leader(pid: int) -> ProcessIdentity | None:
69 """Bind one live session/group leader before an emergency fallback."""
70 authority = _bind_process(pid)
71 if authority is None or authority.group != pid or authority.session != pid:
72 return None
73 return authority
74
75
76def _wait_direct_child_status(child: int) -> int | None:
77 """Wait boundedly and reap one exact direct child without releasing its PID early."""
78 deadline = time.monotonic() + DEADLINE_SECONDS
79 while time.monotonic() < deadline:
80 result = os.waitid(os.P_PID, child, os.WEXITED | os.WNOHANG | os.WNOWAIT)
81 if result is not None:
82 _, raw_status = os.waitpid(child, 0)
83 return os.waitstatus_to_exitcode(raw_status)
84 time.sleep(POLL_SECONDS)
85 return None
86
87
88def _identity_is_current(authority: ProcessIdentity) -> bool:
89 """Revalidate one unreaped leader immediately before emergency cleanup."""
90 return _bind_process(authority.pid) == authority
91
92
93def _emergency_group_cleanup(authority: ProcessIdentity) -> bool:
94 """Remove a failed watchdog group only while its exact leader stays bound."""
95 if _wait_group_gone(authority.group):
96 return True
97 if not _identity_is_current(authority):
98 return False
99 with suppress(ProcessLookupError):
100 os.killpg(authority.group, signal.SIGKILL)
101 return _wait_group_gone(authority.group)
102
103
104def _receive_controller_identity(descriptor: int) -> ProcessIdentity | None:
105 """Bind the controller named by one private runner pipe."""
106 ready, _, _ = select.select((descriptor,), (), (), 2)
107 if not ready:
108 return None
109 value = os.read(descriptor, 32).strip()
110 if not value.isdigit():
111 return None
112 return _bind_group_leader(int(value))
113
114
115def _inject_observation_failure() -> None:
116 """Raise the controlled observation error used by the cleanup direction."""
117 message = "injected controller observation failure"
118 raise OSError(message)
119
120
121def _suite_root_is_safe(root: Path, expected_identity: str) -> bool:
122 """Bind one direct canonical-/tmp suite root before any case side effect."""
123 try:
124 canonical = CANONICAL_TMP.resolve(strict=True)
125 metadata = root.lstat()
126 suffix = root.name.removeprefix("ra8-devcontainer-image-selftest.")
127 identity = f"{metadata.st_dev}:{metadata.st_ino}"
128 resolved = root.resolve(strict=True)
129 except OSError:
130 return False
131 return (
132 root.is_absolute()
133 and root.parent == canonical
134 and resolved == root
135 and len(suffix) == SUITE_ROOT_SUFFIX_LENGTH
136 and all(character in "0123456789abcdef" for character in suffix)
137 and stat.S_ISDIR(metadata.st_mode)
138 and metadata.st_uid == os.getuid()
139 and metadata.st_gid == os.getgid()
140 and stat.S_IMODE(metadata.st_mode) == PRIVATE_MODE
141 and identity == expected_identity
142 )
143
144
145def _entry_belongs_to_root(entry: Path, root: Path) -> bool:
146 """Require one private mode-0700 regular payload directly under its suite."""
147 try:
148 metadata = entry.lstat()
149 except OSError:
150 return False
151 return (
152 entry.is_absolute()
153 and entry.parent == root
154 and stat.S_ISREG(metadata.st_mode)
155 and metadata.st_nlink == 1
156 and metadata.st_uid == os.getuid()
157 and metadata.st_gid == os.getgid()
158 and stat.S_IMODE(metadata.st_mode) == PRIVATE_MODE
159 )
160
161
162def _bound_phase_matches(bound: Path, authority: ProcessIdentity, phase: str) -> bool:
163 """Prove the requested publication boundary before killing the runner."""
164 deadline = time.monotonic() + 2
165 expected = b"" if phase == "pre-bound" else f"{authority.pid}\n".encode("ascii")
166 while time.monotonic() < deadline:
167 try:
168 value = bound.read_bytes()
169 except OSError:
170 return False
171 if value == expected:
172 return True
173 if phase == "pre-bound" or value:
174 return False
175 time.sleep(POLL_SECONDS)
176 return False
177
178
179def _supervisor_sigkill_case(entry: str, root: Path, phase: str) -> bool:
180 """Prove pipe EOF removes a controller group after supervisor SIGKILL."""
181 bound = root / f"supervisor-sigkill-{phase}.bound"
182 outer = root / f"supervisor-sigkill-{phase}.outer"
183 status = root / f"supervisor-sigkill-{phase}.status"
184 descriptor = os.open(
185 bound,
186 os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW,
187 RECEIPT_MODE,
188 )
189 os.close(descriptor)
190 read_descriptor, write_descriptor = os.pipe2(os.O_CLOEXEC)
191 runner = os.fork()
192 if runner == 0:
193 os.close(read_descriptor)
194 request = SupervisorRequest(entry, bound, outer, status, "normal")
195 controls = SupervisionControls(
196 test_descriptor=write_descriptor,
197 pause_before_bound=phase == "pre-bound",
198 )
199 os._exit(_supervise(request, controls))
200 os.close(write_descriptor)
201 authority: ProcessIdentity | None = None
202 runner_reaped = False
203 watchdog_succeeded = False
204 try:
205 authority = _receive_controller_identity(read_descriptor)
206 if authority is not None and _bound_phase_matches(bound, authority, phase):
207 os.kill(runner, signal.SIGKILL)
208 os.waitpid(runner, 0)
209 runner_reaped = True
210 watchdog_succeeded = _wait_group_gone(authority.group)
211 finally:
212 os.close(read_descriptor)
213 if not runner_reaped:
214 with suppress(ProcessLookupError):
215 os.kill(runner, signal.SIGKILL)
216 with suppress(ChildProcessError):
217 os.waitpid(runner, 0)
218 if authority is not None and not watchdog_succeeded:
219 _emergency_group_cleanup(authority)
220 return watchdog_succeeded
221
222
223def _supervisor_sigkill_selftest(entry: str, root: Path) -> int:
224 """Exercise the parent-death watchdog before and after public binding."""
225 phases = ("pre-bound", "post-bound")
226 return 0 if all(_supervisor_sigkill_case(entry, root, phase) for phase in phases) else 1
227
228
229def _hardlink_bound_selftest(entry: str, root: Path) -> int:
230 """Prove hardlink refusal preserves data and leaves no controller group."""
231 victim = root / "supervisor-hardlink-bound.victim"
232 bound = root / "supervisor-hardlink-bound.bound"
233 outer = root / "supervisor-hardlink-bound.outer"
234 status = root / "supervisor-hardlink-bound.status"
235 _write_exclusive(victim, "preserve\n")
236 os.link(victim, bound, follow_symlinks=False)
237 read_descriptor, write_descriptor = os.pipe2(os.O_CLOEXEC)
238 runner = os.fork()
239 if runner == 0:
240 os.close(read_descriptor)
241 request = SupervisorRequest(entry, bound, outer, status, "normal")
242 controls = SupervisionControls(test_descriptor=write_descriptor)
243 os._exit(_supervise(request, controls))
244 os.close(write_descriptor)
245 authority: ProcessIdentity | None = None
246 group_absent = False
247 runner_status: int | None = None
248 hardlink_runner_reaped = False
249 try:
250 authority = _receive_controller_identity(read_descriptor)
251 if authority is not None:
252 group_absent = _wait_group_gone(authority.group)
253 runner_status = _wait_direct_child_status(runner)
254 hardlink_runner_reaped = runner_status is not None
255 finally:
256 os.close(read_descriptor)
257 if not hardlink_runner_reaped:
258 with suppress(ProcessLookupError):
259 os.kill(runner, signal.SIGKILL)
260 with suppress(ChildProcessError):
261 os.waitpid(runner, 0)
262 if authority is not None and not group_absent:
263 _emergency_group_cleanup(authority)
264 preserved = victim.read_bytes() == b"preserve\n" and victim.stat().st_nlink == HARDLINK_COUNT
265 return 0 if group_absent and runner_status == PUBLIC_REFUSAL_STATUS and preserved else 1
266
267
268def _watchdog_expiry_runner(
269 entry: str,
270 status: Path,
271 identity_descriptor: int,
272 proof_descriptor: int,
273 release_descriptor: int,
274) -> int:
275 """Hold supervisor ownership until the observing parent releases this runner."""
276 supervisor = BoundGroup()
277 source_descriptor = int(Path(SUPERVISOR_PROGRAM).name)
278 try:
279 if not supervisor.enable_subreaper():
280 return INTEGRITY_REFUSAL_STATUS
281 launch = ControllerLaunch(entry, status, SELFTEST_WATCHDOG_TIMEOUT_SECONDS)
282 supervisor.spawn(source_descriptor, launch)
283 if supervisor.pid is None:
284 return INTEGRITY_REFUSAL_STATUS
285 _write_exact(
286 identity_descriptor,
287 f"{supervisor.pid}\n".encode("ascii"),
288 RECEIPT_MAX_BYTES,
289 )
290 deadline = time.monotonic() + DEADLINE_SECONDS
291 killed = False
292 while time.monotonic() < deadline:
293 result = os.waitid(os.P_PID, supervisor.pid, os.WEXITED | os.WNOHANG | os.WNOWAIT)
294 if result is not None:
295 killed = result.si_code == os.CLD_KILLED and result.si_status == signal.SIGKILL
296 break
297 time.sleep(POLL_SECONDS)
298 contained = supervisor.contain()
299 _write_exact(
300 proof_descriptor,
301 b"K\n" if killed and contained else b"F\n",
302 RECEIPT_MAX_BYTES,
303 )
304 ready, _, _ = select.select((release_descriptor,), (), (), DEADLINE_SECONDS * 2)
305 return STALL_STATUS if ready else INTEGRITY_REFUSAL_STATUS
306 finally:
307 os.close(identity_descriptor)
308 os.close(proof_descriptor)
309 os.close(release_descriptor)
310 supervisor.contain()
311
312
313def _watchdog_expiry_selftest(entry: str, root: Path) -> int:
314 """Prove a live but stalled supervisor cannot retain its controller group."""
315 status = root / "supervisor-watchdog-expiry.status"
316 read_descriptor, write_descriptor = os.pipe2(os.O_CLOEXEC)
317 proof_read, proof_write = os.pipe2(os.O_CLOEXEC)
318 release_read, release_write = os.pipe2(os.O_CLOEXEC)
319 runner = os.fork()
320 if runner == 0:
321 os.close(read_descriptor)
322 os.close(proof_read)
323 os.close(release_write)
324 result = _watchdog_expiry_runner(entry, status, write_descriptor, proof_write, release_read)
325 os._exit(result)
326 os.close(write_descriptor)
327 os.close(proof_write)
328 os.close(release_read)
329 authority: ProcessIdentity | None = None
330 watchdog_succeeded = False
331 runner_reaped = False
332 try:
333 authority = _receive_controller_identity(read_descriptor)
334 if authority is not None:
335 ready, _, _ = select.select((proof_read,), (), (), DEADLINE_SECONDS)
336 killed_receipt = ready and os.read(proof_read, 2) == b"K\n"
337 members = _group_members(authority.group)
338 runner_is_live = (
339 os.waitid(os.P_PID, runner, os.WEXITED | os.WNOHANG | os.WNOWAIT) is None
340 )
341 pre_release_proven = killed_receipt and members is not None
342 pre_release_proven = pre_release_proven and members <= {authority.pid}
343 if pre_release_proven and runner_is_live:
344 os.close(release_write)
345 release_write = -1
346 runner_status = _wait_direct_child_status(runner)
347 runner_reaped = runner_status is not None
348 expected = runner_status == STALL_STATUS
349 watchdog_succeeded = expected and _wait_group_gone(authority.group)
350 finally:
351 os.close(read_descriptor)
352 os.close(proof_read)
353 if release_write >= 0:
354 os.close(release_write)
355 if not runner_reaped:
356 with suppress(ProcessLookupError):
357 os.kill(runner, signal.SIGKILL)
358 with suppress(ChildProcessError):
359 os.waitpid(runner, 0)
360 if authority is not None and not watchdog_succeeded:
361 _emergency_group_cleanup(authority)
362 return 0 if watchdog_succeeded else 1
363
364
365def _missing_entry_selftest(root: Path) -> int:
366 """Prove payload exec failure is published and its group is removed."""
367 bound = root / "supervisor-missing-entry.bound"
368 outer = root / "supervisor-missing-entry.outer"
369 status = root / "supervisor-missing-entry.status"
370 descriptor = os.open(bound, os.O_WRONLY | os.O_CREAT | os.O_EXCL, RECEIPT_MODE)
371 os.close(descriptor)
372 request = SupervisorRequest(str(root / "absent-entry"), bound, outer, status, "normal")
373 controls = SupervisionControls(missing_entry_selftest=True)
374 result = _supervise(request, controls)
375 bound_value = bound.read_bytes().strip()
376 if not bound_value.isdigit():
377 return 1
378 group = int(bound_value)
379 exec_failure_proven = result == PUBLIC_REFUSAL_STATUS and status.read_bytes() == b"127\n"
380 return 0 if exec_failure_proven and _wait_group_gone(group) else 1
381
382
383def _write_entry_fixture(path: Path, body: str) -> None:
384 """Publish one private executable used only by entry-authority tests."""
385 _write_exclusive(path, body)
386 path.chmod(0o700)
387
388
389def _entry_mutation_case(root: Path, mode: str) -> bool:
390 """Prove replacement uses the opened inode and in-place change is detected."""
391 entry = root / f"entry-{mode}.sh"
392 saved = root / f"entry-{mode}.saved"
393 marker = root / f"entry-{mode}.forged"
394 bound = root / f"entry-{mode}.bound"
395 outer = root / f"entry-{mode}.outer"
396 status = root / f"entry-{mode}.status"
397 _write_entry_fixture(entry, "#!/bin/bash\nexit 1\n")
398 _write_exclusive(bound, "")
399 marker_path = marker.resolve(strict=False)
400 forged = f"#!/bin/bash\nprintf forged >{marker_path}\nexit 0\n"
401
402 def mutate() -> None:
403 if mode in ("replace", "pre-open-replace"):
404 entry.rename(saved)
405 _write_entry_fixture(entry, forged)
406 elif mode == "grow-in-place":
407 os.truncate(entry, ENTRY_MAX_BYTES + 1)
408 else:
409 entry.write_text(forged, encoding="ascii")
410 entry.chmod(0o700)
411
412 request = SupervisorRequest(str(entry), bound, outer, status, "normal")
413 controls = (
414 SupervisionControls(pre_open_mutator=mutate)
415 if mode == "pre-open-replace"
416 else SupervisionControls(entry_mutator=mutate)
417 )
418 result = _supervise(request, controls)
419 if mode == "pre-open-replace":
420 return result == PUBLIC_REFUSAL_STATUS and not marker.exists()
421 if mode in ("replace", "grow-in-place"):
422 return result == INTEGRITY_REFUSAL_STATUS and not marker.exists()
423 return result == INTEGRITY_REFUSAL_STATUS and marker.read_bytes() == b"forged"
424
425
426def _entry_refusal_case(root: Path, mode: str) -> bool:
427 """Prove unsafe path and metadata shapes fail before controller spawn."""
428 entry = root / f"entry-refusal-{mode}.sh"
429 target = root / f"entry-refusal-{mode}.target"
430 bound = root / f"entry-refusal-{mode}.bound"
431 _write_entry_fixture(target, "#!/bin/bash\nexit 1\n")
432 if mode == "symlink":
433 entry.symlink_to(target.name)
434 elif mode == "hardlink":
435 os.link(target, entry, follow_symlinks=False)
436 else:
437 target.rename(entry)
438 if mode == "mode":
439 entry.chmod(0o600)
440 elif mode == "owner":
441 os.chown(entry, 1, os.getgid())
442 elif mode == "group":
443 os.chown(entry, os.getuid(), 1)
444 elif mode == "zero":
445 entry.write_bytes(b"")
446 elif mode == "oversize":
447 os.truncate(entry, ENTRY_MAX_BYTES + 1)
448 _write_exclusive(bound, "")
449 request = SupervisorRequest(
450 str(entry),
451 bound,
452 root / f"entry-refusal-{mode}.outer",
453 root / f"entry-refusal-{mode}.status",
454 "normal",
455 )
456 return _supervise(request) == PUBLIC_REFUSAL_STATUS and bound.read_bytes() == b""
457
458
459def _root_argument_directions(root: Path, identity: str) -> bool:
460 """Exercise safe root refusal directions without changing root metadata."""
461 return (
462 _suite_root_is_safe(root, identity)
463 and not _suite_root_is_safe(Path("/"), identity)
464 and not _suite_root_is_safe(Path(root.name), identity)
465 and not _suite_root_is_safe(root, "0:0")
466 )
467
468
469def _entry_binding_selftest(root: Path, original_root: Path, identity: str) -> int:
470 """Exercise both live entry mutations and every rejected metadata shape."""
471 mutation_modes = ("pre-open-replace", "replace", "in-place", "grow-in-place")
472 mutations = all(_entry_mutation_case(root, mode) for mode in mutation_modes)
473 refusal_modes = ["symlink", "hardlink", "mode", "zero", "oversize"]
474 if os.getuid() == 0:
475 refusal_modes.extend(("owner", "group"))
476 refusals = all(_entry_refusal_case(root, mode) for mode in refusal_modes)
477 roots = _root_argument_directions(original_root, identity)
478 return 0 if roots and mutations and refusals else 1
479
480
481def _controller_kill_observed(child: subprocess.Popen[bytes]) -> bool:
482 """Observe one controller terminal while its leader remains unreaped."""
483 deadline = time.monotonic() + DEADLINE_SECONDS
484 while time.monotonic() < deadline:
485 result = os.waitid(os.P_PID, child.pid, os.WEXITED | os.WNOHANG | os.WNOWAIT)
486 if result is not None:
487 return result.si_code == os.CLD_KILLED and result.si_status == signal.SIGKILL
488 time.sleep(POLL_SECONDS)
489 return False
490
491
492def _wait_forked_group_terminal(process: int) -> bool:
493 """Retain one forked group leader unreaped through its absence proof."""
494 deadline = time.monotonic() + DEADLINE_SECONDS
495 while time.monotonic() < deadline:
496 result = os.waitid(os.P_PID, process, os.WEXITED | os.WNOHANG | os.WNOWAIT)
497 if result is not None:
498 members = _group_members(process)
499 return members is not None and members <= {process}
500 time.sleep(POLL_SECONDS)
501 return False
502
503
504def _controller_isolation_selftest(root: Path, identity: str) -> int:
505 """Prove a raw non-session-leader controller refuses before any effect."""
506 status = root / "supervisor-controller-isolation.status"
507 identity_read, identity_write = os.pipe2(os.O_CLOEXEC)
508 result_read, result_write = os.pipe2(os.O_CLOEXEC)
509 broker = os.fork()
510 if broker == 0:
511 os.close(identity_read)
512 os.close(result_read)
513 os.setsid()
514 _write_exact(
515 identity_write,
516 f"{os.getpid()}\n".encode("ascii"),
517 RECEIPT_MAX_BYTES,
518 )
519 contender = os.fork()
520 if contender == 0:
521 root_authority = (int(root.name), identity)
522 os._exit(_controller("99", status, CLOSED_DESCRIPTOR, root_authority, 1.0))
523 _, raw_status = os.waitpid(contender, 0)
524 _write_exact(
525 result_write,
526 f"{os.waitstatus_to_exitcode(raw_status)}\n".encode("ascii"),
527 RECEIPT_MAX_BYTES,
528 )
529 os._exit(0)
530 os.close(identity_write)
531 os.close(result_write)
532 authority: ProcessIdentity | None = None
533 broker_reaped = False
534 terminal = False
535 refusal = b""
536 try:
537 authority = _receive_controller_identity(identity_read)
538 ready, _, _ = select.select((result_read,), (), (), DEADLINE_SECONDS)
539 if ready:
540 refusal = os.read(result_read, 16).strip()
541 terminal = _wait_forked_group_terminal(broker)
542 if terminal:
543 os.waitpid(broker, 0)
544 broker_reaped = True
545 safe = (
546 authority is not None
547 and refusal == b"64"
548 and terminal
549 and broker_reaped
550 and not status.exists()
551 )
552 return 0 if safe else 1
553 finally:
554 os.close(identity_read)
555 os.close(result_read)
556 if not broker_reaped:
557 if authority is not None and _identity_is_current(authority):
558 with suppress(ProcessLookupError):
559 os.killpg(authority.group, signal.SIGKILL)
560 _wait_forked_group_terminal(broker)
561 with suppress(ChildProcessError):
562 os.waitpid(broker, 0)
563
564
565def _refused_controller_launch(root_descriptor: int, identity: str, status: Path) -> bool:
566 """Run one isolated raw controller expected to refuse before forking."""
567 source_descriptor = int(Path(SUPERVISOR_PROGRAM).name)
568 child = subprocess.Popen( # noqa: S603 -- pinned interpreter and supervisor FD
569 (
570 sys.executable,
571 "-B",
572 "-I",
573 "-S",
574 SUPERVISOR_PROGRAM,
575 "--controller",
576 "99",
577 str(status),
578 str(CLOSED_DESCRIPTOR),
579 str(root_descriptor),
580 identity,
581 str(SELFTEST_WATCHDOG_TIMEOUT_SECONDS),
582 ),
583 pass_fds=(source_descriptor, root_descriptor),
584 start_new_session=True,
585 )
586 try:
587 result = child.wait(timeout=DEADLINE_SECONDS)
588 except subprocess.TimeoutExpired:
589 os.killpg(child.pid, signal.SIGKILL)
590 child.wait(timeout=DEADLINE_SECONDS)
591 return False
592 return result == USAGE_STATUS and not status.exists()
593
594
595def _controller_root_refusal_selftest(root: Path) -> bool:
596 """Refuse wrong identity and non-suite directory FDs before effects."""
597 root_descriptor = int(root.name)
598 wrong = _refused_controller_launch(
599 root_descriptor, "0:0", root / "controller-wrong-identity.status"
600 )
601 sibling = root / "controller-sibling"
602 sibling.mkdir(mode=PRIVATE_MODE)
603 descriptor = os.open(sibling, os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW)
604 try:
605 metadata = os.fstat(descriptor)
606 sibling_identity = f"{metadata.st_dev}:{metadata.st_ino}"
607 sibling_root = _anchored_root_path(descriptor)
608 refused = _refused_controller_launch(
609 descriptor, sibling_identity, sibling_root / "controller-sibling.status"
610 )
611 finally:
612 os.close(descriptor)
613 sibling.rmdir()
614 return wrong and refused
615
616
617def _reap_cleanup_retry_selftest(entry: str, root: Path) -> bool:
618 """Retain a terminal leader when its first exact reap fails, then retry."""
619 supervisor = BoundGroup()
620 original_reap = vars(BoundGroup)["_reap"]
621 retained = False
622 cleaned = False
623
624 def fail_reap(_authority: BoundGroup) -> bool:
625 return False
626
627 try:
628 if not supervisor.enable_subreaper():
629 return False
630 launch = ControllerLaunch(
631 entry, root / "supervisor-reap-retry.status", SELFTEST_WATCHDOG_TIMEOUT_SECONDS
632 )
633 supervisor.spawn(int(Path(SUPERVISOR_PROGRAM).name), launch)
634 if supervisor.pid is not None and _wait_group_populated(supervisor.pid):
635 type.__setattr__(BoundGroup, "_reap", fail_reap)
636 first_cleanup = supervisor.cleanup()
637 retained = (
638 not first_cleanup
639 and not supervisor.reaped
640 and not supervisor.cleaning
641 and supervisor.entry_descriptor is not None
642 )
643 finally:
644 type.__setattr__(BoundGroup, "_reap", original_reap)
645 cleaned = supervisor.contain()
646 group_absent = supervisor.pid is None or _wait_group_gone(supervisor.pid)
647 return retained and cleaned and supervisor.entry_descriptor is None and group_absent
648
649
650def _cleanup_retry_selftest(entry: str, root: Path) -> int:
651 """Exercise independent census and reap retry directions."""
652 census = _census_cleanup_retry_selftest(entry, root)
653 reaping = _reap_cleanup_retry_selftest(entry, root)
654 writes = _receipt_write_selftest()
655 permanent = _permanent_containment_failure_selftest()
656 return 0 if census and reaping and writes and permanent else 1
657
658
659def _closed_controller_command(
660 entry_authority: str,
661 status: Path,
662 death_descriptor: int,
663 root_descriptor: int,
664 root_identity: str,
665) -> tuple[str, ...]:
666 """Build the fixed controller command used by closed-descriptor cases."""
667 return (
668 sys.executable,
669 "-B",
670 "-I",
671 "-S",
672 SUPERVISOR_PROGRAM,
673 "--controller",
674 entry_authority,
675 str(status),
676 str(death_descriptor),
677 str(root_descriptor),
678 root_identity,
679 str(SELFTEST_WATCHDOG_TIMEOUT_SECONDS),
680 )
681
682
683def _closed_controller_descriptor_selftest(entry: str, root: Path, mode: str) -> int:
684 """Prove invalid entry/death descriptors cannot bypass group cleanup."""
685 status = root / f"supervisor-closed-{mode}.status"
686 source_descriptor = int(Path(SUPERVISOR_PROGRAM).name)
687 root_descriptor = int(root.name)
688 root_metadata = os.fstat(root_descriptor)
689 root_identity = f"{root_metadata.st_dev}:{root_metadata.st_ino}"
690 inherited = [source_descriptor, root_descriptor]
691 supervisor = BoundGroup()
692 death_descriptor: int | None = CLOSED_DESCRIPTOR
693 death_write: int | None = None
694 entry_authority = "98"
695 observation_injected = False
696 try:
697 if not supervisor.enable_subreaper():
698 return 1
699 if mode in ("death", "observation"):
700 descriptor, identity, digest = _open_entry_authority(entry)
701 supervisor.entry_descriptor = descriptor
702 supervisor.entry_path = entry
703 supervisor.entry_identity = identity
704 supervisor.entry_digest = digest
705 inherited.append(descriptor)
706 entry_authority = str(descriptor)
707 if mode != "death":
708 death_descriptor, death_write = os.pipe2(os.O_CLOEXEC)
709 inherited.append(death_descriptor)
710 command = _closed_controller_command(
711 entry_authority, status, death_descriptor, root_descriptor, root_identity
712 )
713 child = subprocess.Popen( # noqa: S603 -- pinned interpreter and supervisor FD
714 command,
715 pass_fds=tuple(inherited),
716 start_new_session=True,
717 )
718 supervisor.bind_spawned_child(child)
719 if mode == "observation":
720 observation_injected = True
721 _inject_observation_failure()
722 if death_write is not None:
723 os.close(death_write)
724 os.close(death_descriptor)
725 death_write, death_descriptor = None, None
726 observed = _controller_kill_observed(child)
727 cleaned = supervisor.cleanup()
728 except OSError:
729 succeeded = observation_injected and supervisor.pid is not None
730 return 0 if succeeded and supervisor.contain() else 1
731 else:
732 if mode == "observation":
733 return 1
734 return 0 if observed and cleaned and child.returncode == -signal.SIGKILL else 1
735 finally:
736 for descriptor in (death_write, death_descriptor):
737 if descriptor is not None and descriptor != CLOSED_DESCRIPTOR:
738 with suppress(OSError):
739 os.close(descriptor)
740 supervisor.contain()
741
742
743def _validated_case_paths(
744 argv: list[str], entry_modes: set[str], root_modes: set[str]
745) -> tuple[Path | None, Path | None, int | None]:
746 """Validate one case root and optional entry before any side effect."""
747 mode = argv[1] if len(argv) > 1 else ""
748 entry: Path | None = None
749 root: Path | None = None
750 error_status: int | None = None
751 if mode in entry_modes:
752 if len(argv) != HIDDEN_ARG_COUNT:
753 error_status = 64
754 else:
755 entry, root = Path(argv[2]), Path(argv[3])
756 if not _suite_root_is_safe(root, argv[4]) or not _entry_belongs_to_root(entry, root):
757 error_status = 64
758 elif mode in root_modes:
759 if len(argv) != ROOT_ARG_COUNT:
760 error_status = 64
761 else:
762 root = Path(argv[2])
763 if not _suite_root_is_safe(root, argv[3]):
764 error_status = 64
765 return entry, root, error_status
766
767
768def _run_supervisor_case(
769 mode: str,
770 entry: Path | None,
771 root: Path,
772 original_root: Path,
773 root_identity: str,
774) -> int | None:
775 """Run one already validated supervisor regression."""
776 if (
777 mode
778 in {
779 "--selftest-parent-death",
780 "--selftest-watchdog-expiry",
781 "--selftest-hardlink-bound",
782 "--selftest-closed-death-fd",
783 "--selftest-closed-entry-fd",
784 "--selftest-observation-failure",
785 "--selftest-cleanup-retry",
786 }
787 and entry is None
788 ):
789 return 64
790 status = None
791 if mode == "--selftest-parent-death":
792 status = _supervisor_sigkill_selftest(str(entry), root)
793 elif mode == "--selftest-watchdog-expiry":
794 status = _watchdog_expiry_selftest(str(entry), root)
795 elif mode == "--selftest-hardlink-bound":
796 status = _hardlink_bound_selftest(str(entry), root)
797 elif mode == "--selftest-closed-death-fd":
798 status = _closed_controller_descriptor_selftest(str(entry), root, "death")
799 elif mode == "--selftest-closed-entry-fd":
800 status = _closed_controller_descriptor_selftest(str(entry), root, "entry")
801 elif mode == "--selftest-observation-failure":
802 status = _closed_controller_descriptor_selftest(str(entry), root, "observation")
803 elif mode == "--selftest-cleanup-retry":
804 status = _cleanup_retry_selftest(str(entry), root)
805 elif mode == "--selftest-missing-entry":
806 status = _missing_entry_selftest(root)
807 elif mode == "--selftest-entry-binding":
808 status = _entry_binding_selftest(root, original_root, root_identity)
809 elif mode == "--selftest-controller-isolation":
810 isolated = _controller_isolation_selftest(root, root_identity) == 0
811 root_refused = _controller_root_refusal_selftest(root)
812 status = 0 if isolated and root_refused else 1
813 return status
814
815
816def _open_validated_root(root: Path, expected_identity: str) -> tuple[int, tuple[int, int]] | None:
817 """Open the exact caller-bound root or refuse before case effects."""
818 try:
819 descriptor, opened_identity = _open_suite_root_authority(root)
820 except (OSError, RuntimeError):
821 return None
822 if f"{opened_identity[0]}:{opened_identity[1]}" != expected_identity:
823 _close_suite_root_authority(descriptor, root, opened_identity)
824 return None
825 return descriptor, opened_identity
826
827
828def _nonroot_case_status(argv: list[str], mode: str) -> int | None:
829 """Run the authenticated stat-parser case that requires no suite root."""
830 if len(argv) != STAT_SELFTEST_ARG_COUNT or mode != "--selftest-stat-parser":
831 return None
832 raw = b"1 (non-ascii-\xff) S 0 42 42 " + (b"0 " * 15) + b"99"
833 return 0 if _stat_group(raw) == TEST_PROCESS_GROUP else 1
834
835
836def dispatch_supervisor_cases(argv: list[str]) -> int | None:
837 """Dispatch one authenticated entry or descriptor regression."""
838 entry_modes = {
839 "--selftest-parent-death",
840 "--selftest-watchdog-expiry",
841 "--selftest-hardlink-bound",
842 "--selftest-closed-death-fd",
843 "--selftest-closed-entry-fd",
844 "--selftest-observation-failure",
845 "--selftest-cleanup-retry",
846 }
847 root_modes = {
848 "--selftest-missing-entry",
849 "--selftest-entry-binding",
850 "--selftest-controller-isolation",
851 }
852 mode = argv[1] if len(argv) > 1 else ""
853 entry, root, error_status = _validated_case_paths(argv, entry_modes, root_modes)
854 if error_status is not None:
855 return error_status
856 if mode in entry_modes and (entry is None or root is None):
857 return 64
858 if mode in root_modes and root is None:
859 return 64
860 if root is None:
861 return _nonroot_case_status(argv, mode)
862 expected_identity = argv[4] if mode in entry_modes else argv[3]
863 root_authority = _open_validated_root(root, expected_identity)
864 if root_authority is None:
865 return 64
866 descriptor, opened_identity = root_authority
867 anchored_root = _anchored_root_path(descriptor)
868 anchored_entry = anchored_root / entry.name if entry is not None else None
869 case_failed = False
870 try:
871 try:
872 status = _run_supervisor_case(
873 mode, anchored_entry, anchored_root, root, expected_identity
874 )
875 except (OSError, RuntimeError, TimeoutError, subprocess.SubprocessError):
876 case_failed = True
877 status = PUBLIC_REFUSAL_STATUS
878 finally:
879 root_integrity = _close_suite_root_authority(descriptor, root, opened_identity)
880 if not root_integrity:
881 status = INTEGRITY_REFUSAL_STATUS
882 elif case_failed:
883 status = PUBLIC_REFUSAL_STATUS
884 return status
885
886
887def _receipt_write_selftest() -> bool:
888 """Prove exact receipt writes complete and reject impossible progress."""
889 original_write = os.write
890
891 def scripted(actions: tuple[str, ...], payload: bytes, expect_success: bool) -> bool:
892 read_descriptor, write_descriptor = os.pipe2(os.O_CLOEXEC)
893 action_index = 0
894
895 def fake_write(descriptor: int, value: bytes) -> int:
896 nonlocal action_index
897 action = actions[min(action_index, len(actions) - 1)]
898 action_index += 1
899 if action == "eintr":
900 raise InterruptedError(errno.EINTR, "injected interruption")
901 if action == "zero":
902 return 0
903 if action == "partial":
904 return original_write(descriptor, value[:1])
905 return original_write(descriptor, value)
906
907 os.write = fake_write
908 succeeded = False
909 try:
910 try:
911 _write_exact(write_descriptor, payload, RECEIPT_MAX_BYTES)
912 succeeded = True
913 except (OSError, ValueError):
914 succeeded = False
915 if succeeded != expect_success:
916 return False
917 if succeeded:
918 return os.read(read_descriptor, len(payload)) == payload
919 return True
920 finally:
921 os.write = original_write
922 os.close(read_descriptor)
923 os.close(write_descriptor)
924
925 payload = b"1234\n"
926 partial = scripted(("partial",), payload, expect_success=True)
927 interrupted = scripted(("eintr", "partial", "eintr", "partial"), payload, expect_success=True)
928 zero = scripted(("zero",), payload, expect_success=False)
929 excess_eintr = scripted(("eintr",), payload, expect_success=False)
930 return partial and interrupted and zero and excess_eintr
931
932
933def _permanent_containment_observer(mode: str, terminate_early: bool = False) -> bool:
934 """Prove contain remains fail-stopped under permanent census or reap failure."""
935 process_globals = BoundGroup.cleanup.__globals__
936 observer = os.fork()
937 if observer == 0:
938 if terminate_early:
939 time.sleep(POLL_SECONDS * 10)
940 os._exit(0)
941 supervisor = BoundGroup()
942 supervisor.children_contained = False
943 if mode == "census":
944 supervisor.subreaper = True
945 process_globals["_direct_children"] = lambda: None
946 else:
947 supervisor.pid = 999999
948 supervisor.child = object()
949 supervisor.leader_terminal = True
950 type.__setattr__(BoundGroup, "_reap", lambda _authority: False)
951 supervisor.contain()
952 os._exit(1)
953 deadline = time.monotonic() + 0.25
954 stayed_live = True
955 try:
956 while time.monotonic() < deadline:
957 waited, _status = os.waitpid(observer, os.WNOHANG)
958 if waited != 0:
959 stayed_live = False
960 break
961 time.sleep(POLL_SECONDS)
962 finally:
963 if stayed_live:
964 with suppress(ProcessLookupError):
965 os.kill(observer, signal.SIGKILL)
966 with suppress(ChildProcessError):
967 os.waitpid(observer, 0)
968 return stayed_live
969
970
971def _permanent_containment_failure_selftest() -> bool:
972 """Keep permanent cleanup uncertainty fail-stopped until external containment."""
973 census = _permanent_containment_observer("census")
974 reaping = _permanent_containment_observer("reap")
975 early_terminal = not _permanent_containment_observer("census", terminate_early=True)
976 return census and reaping and early_terminal
#define min(x, y)
Untyped minimum shim used by the SOUP's buffer clamping.
Definition xz_config.h:157