ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
hil_convergence_safety_process_mutations.py
Go to the documentation of this file.
1# SPDX-License-Identifier: MIT
2# Copyright (c) 2026 Brighton Sikarskie
3"""Process-authority mutation catalog for HIL convergence selftests."""
4
5from __future__ import annotations
6
7import hil_convergence_safety_process_source_fixtures as process_source_fixtures
8import hil_convergence_safety_runtime_fixtures as runtime_fixtures
9import hil_convergence_safety_source_fixtures as source_fixtures
10
11Mutation = tuple[str, str, str, str]
12
13_BOUND_EXIT_MUTATION_LABELS = frozenset(
14 {
15 "descriptor-bound wrong entry refusal removed",
16 "descriptor-bound canonical entry proof removed",
17 "parent-death watchdog selftest removed",
18 "live-supervisor watchdog deadline selftest removed",
19 "closed death descriptor selftest removed",
20 "bound-exit supervisor failure cases removed",
21 "stall fixture descendant marker removed",
22 "hardlink publication group proof removed",
23 "missing payload entry proof removed",
24 "nested phase parent identity binding removed",
25 "nested phase root receipt binding removed",
26 "supervisor failure payload regressed to full entry",
27 "supervisor launcher interpreter changed",
28 "supervisor launcher no-bytecode flag removed",
29 "supervisor launcher isolation flag removed",
30 "supervisor launcher site-import refusal removed",
31 "supervisor launcher bound program removed",
32 "supervisor launcher bound entry environment removed",
33 "supervisor launcher process descriptor option removed",
34 "supervisor launcher process descriptor changed",
35 "supervisor launcher cases descriptor option removed",
36 "supervisor launcher cases descriptor changed",
37 }
38)
39
40
41def _process_authority_mutations_1() -> tuple[Mutation, ...]:
42 """Return one bounded group of process-authority mutations."""
43 return (
44 (
45 "case signal dispatch image lock load removed",
46 "devcontainer_image",
47 "\n load_image_lock_selftest\n selftest_case_signal_child ",
48 "\n selftest_case_signal_child ",
49 ),
50 (
51 "image lock jobs-table lookup bypassed",
52 "devcontainer_image_selftest",
53 " done < <(jobs -r -l)",
54 " done < <(printf '[1] 1 Running\\n')",
55 ),
56 (
57 "descriptor-bound wrong entry refusal removed",
58 "devcontainer_image_selftest",
59 ' if output="$(RA8_SELFTEST_BOUND_ENTRY="$wrong_entry" \\\n',
60 ' if output="$(RA8_SELFTEST_BOUND_ENTRY="" \\\n',
61 ),
62 (
63 "descriptor-bound canonical entry proof removed",
64 "devcontainer_image_selftest",
65 ' output="$(RA8_SELFTEST_BOUND_ENTRY="$SCRIPT_DIR/devcontainer_image.sh" \\\n',
66 ' output="$(RA8_SELFTEST_BOUND_ENTRY="" \\\n',
67 ),
68 (
69 "group selection explicit success removed",
70 "devcontainer_image_selftest",
71 (
72 ' select_selftest_group_id "$tmp" "$rejected" >/dev/null 2>&1 && return 1\n'
73 " done\n return 0"
74 ),
75 (' select_selftest_group_id "$tmp" "$rejected" >/dev/null 2>&1 && return 1\n done'),
76 ),
77 )
78
79
80def _process_authority_mutations_1b() -> tuple[Mutation, ...]:
81 """Return the second bounded group of process-authority mutations."""
82 return (
83 (
84 "image lock verified PID signal reverted to stale jobspec",
85 "devcontainer_image_selftest",
86 ' builtin kill -"$signal" "$child"',
87 ' builtin kill -"$signal" "$job_spec"',
88 ),
89 (
90 "signal controller PID local renamed to controller",
91 "devcontainer_image_signal_selftest",
92 (
93 ' local launcher_mode="${5:-}" controller_pid pending '
94 'ready="$3/controller-launcher.ready"'
95 ),
96 (
97 ' local launcher_mode="${5:-}" controller pending '
98 'ready="$3/controller-launcher.ready"'
99 ),
100 ),
101 (
102 "image lock controller group signal reduced to direct PID",
103 "devcontainer_image_signal_selftest",
104 ' signal_owned_controller_group "$signal" "$controller" ||',
105 ' signal_owned_live_child "$signal" "$controller" ||',
106 ),
107 (
108 "image lock controller group authorization removed",
109 "devcontainer_image_signal_selftest",
110 ' controller_group_signal_is_authorized "$controller" || return 1',
111 " true",
112 ),
113 (
114 "image lock controller group target reduced to PID",
115 "devcontainer_image_signal_selftest",
116 ' builtin kill -"$signal" -- "-$controller"',
117 ' builtin kill -"$signal" -- "$controller"',
118 ),
119 (
120 "allocation KILL direct-child guard removed",
121 "devcontainer_image_selftest_cases",
122 ' signal_owned_live_child KILL "$child" ||',
123 " true ||",
124 ),
125 (
126 "suite-root nested allocation selection removed",
127 "devcontainer_image_selftest",
128 (
129 ' if [[ -n "$SELFTEST_SUITE_ROOT" ]]; then\n selftest_suite_'
130 "root_is_safe || return 1"
131 ),
132 " if false; then\n true",
133 ),
134 )
135
136
137def _process_authority_mutations_2() -> tuple[Mutation, ...]:
138 """Return one bounded group of process-authority mutations."""
139 return (
140 (
141 "suite-root parent grammar reverted to ten characters",
142 "devcontainer_image_selftest",
143 ' "$suffix" =~ ^[0-9a-f]{32}$ && ! -L "$canonical" && -d "$canonical" &&',
144 ' "$suffix" =~ ^[[:alnum:]]{10}$ && ! -L "$canonical" && -d "$canonical" &&',
145 ),
146 (
147 "portable Bash 3 shell identity replaced by BASHPID",
148 "devcontainer_image_selftest",
149 ' local destination="$1" value="$$:${BASH_SUBSHELL:-0}"',
150 ' local destination="$1" value="$BASHPID"',
151 ),
152 (
153 "fresh allocation signal process bypassed",
154 "devcontainer_image_selftest_cases",
155 (
156 ' if /bin/bash -p -- "$SCRIPT_DIR/devcontainer_image.sh" \\\n '
157 ' --selftest-allocation-checkpoint-child "$phase" "$receipt" \\\n'
158 ),
159 " if (\n",
160 ),
161 (
162 "fresh case signal process bypassed",
163 "devcontainer_image_signal_selftest",
164 (
165 ' if /bin/bash -p -- "$SCRIPT_DIR/devcontainer_image.sh" \\\n '
166 ' --selftest-case-signal-child "$signal" "$tmp" "$SELFTEST_TMP_'
167 'IDENTITY" \\\n'
168 ),
169 " if (\n",
170 ),
171 )
172
173
174def _process_authority_mutations_3() -> tuple[Mutation, ...]:
175 """Return one bounded group of process-authority mutations."""
176 return (
177 (
178 "selftest atomic directory allocation replaced by mktemp create",
179 "devcontainer_image_selftest",
180 ' if (umask 077 && mkdir -m 0700 -- "$candidate"); then',
181 (' if SELFTEST_TMP_DIR="$(mktemp -d "$SELFTEST_TMP_ROOT/unsafe.XXXXXXXXXX")"; then'),
182 ),
183 (
184 "selftest suite-root binding removed",
185 "devcontainer_image_selftest_cases",
186 ' establish_selftest_suite_root || die "selftest: could not bind its suite root"',
187 " true",
188 ),
189 (
190 "selftest suite-root completion proof removed",
191 "devcontainer_image_selftest_cases",
192 ' clear_selftest_suite_root || die "selftest: suite-root cleanup did not complete"',
193 " true",
194 ),
195 (
196 "worker group direct-child authority removed",
197 "devcontainer_image_lock_selftest",
198 (
199 ' worker_group_is_safe && [[ "$SELFTEST_WORKER_PGID" == "$SELF'
200 'TEST_WORKER_PID" ]] &&\n shell_owns_live_child "$SELFTEST_WO'
201 'RKER_PID"'
202 ),
203 " worker_group_is_safe",
204 ),
205 )
206
207
208def _process_authority_mutations_4() -> tuple[Mutation, ...]:
209 """Return one bounded group of process-authority mutations."""
210 return (
211 (
212 "shared worker process-group binding removed",
213 "devcontainer_image_lock_selftest",
214 ' [[ "$pgid" == "$PPID" && "$pgid" != "$pid" ]] ||',
215 ' [[ "$pgid" == "$pid" ]] ||',
216 ),
217 (
218 "isolated worker process-group binding removed",
219 "devcontainer_image_lock_selftest",
220 (
221 ' [[ "$pgid" == "$pid" ]] || die "selftest isolated worker i'
222 's not its group leader"'
223 ),
224 " true",
225 ),
226 (
227 "worker leader TERM resistance removed",
228 "devcontainer_image_lock_selftest",
229 (
230 " pre-ready-hang | signal-controller | post-ready-build-hang"
231 ") trap '' HUP INT TERM ;;"
232 ),
233 " pre-ready-hang | signal-controller | post-ready-build-hang) true ;;",
234 ),
235 (
236 "process-enumeration failure scenario removed",
237 "devcontainer_image_selftest_cases",
238 ' selftest_ps_failure_cleanup "$tmp"',
239 " true",
240 ),
241 )
242
243
244def _process_authority_mutations_5() -> tuple[Mutation, ...]:
245 """Return one bounded group of process-authority mutations."""
246 return (
247 (
248 "rebound process-group refusal removed",
249 "devcontainer_image_lock_selftest",
250 (
251 ' ! worker_group_signal_is_authorized ||\n die "selftest: re'
252 'bound numeric process group gained signal authority"'
253 ),
254 " true",
255 ),
256 (
257 "process-enumeration descendant proof removed",
258 "devcontainer_image_lock_selftest",
259 (
260 ' assert_no_surviving_descendants ||\n die "selftest: repeat'
261 'ed ps failure left a signal-ignoring descendant"'
262 ),
263 " true",
264 ),
265 (
266 "bound-exit supervisor pre-spawn signal block removed",
267 "devcontainer_image_selftest_supervisor",
268 "old_mask = signal.pthread_sigmask(signal.SIG_BLOCK, MANAGED_SIGNALS)",
269 "old_mask = set()",
270 ),
271 (
272 "bound-exit payload signal reset removed",
273 "devcontainer_image_selftest_supervisor",
274 (
275 " _reset_managed_signals()\n try:\n os.e"
276 "xecl( # noqa: S606 -- fixed Bash and descriptor-bound entry"
277 ),
278 (
279 " try:\n os.execl( # noqa: S606 -- fixed prot"
280 "ected Bash and descriptor-bound entry"
281 ),
282 ),
283 )
284
285
286def _process_authority_mutations_6() -> tuple[Mutation, ...]:
287 """Return one bounded group of process-authority mutations."""
288 return (
289 (
290 "bound-exit parent-death pipe removed",
291 "devcontainer_image_selftest_process",
292 "self.death_read, self.death_write = os.pipe2(os.O_CLOEXEC)",
293 "self.death_read, self.death_write = (None, None)",
294 ),
295 (
296 "bound-exit parent-death descriptor propagation removed",
297 "devcontainer_image_selftest_process",
298 "inherited = [source_descriptor, self.death_read, root_descriptor]",
299 "inherited = [source_descriptor, root_descriptor]",
300 ),
301 (
302 "bound-exit controller interpreter changed",
303 "devcontainer_image_selftest_process",
304 " argv = (\n sys.executable,",
305 ' argv = (\n os.environ["PYTHON"],',
306 ),
307 (
308 "bound-exit controller no-bytecode isolation removed",
309 "devcontainer_image_selftest_process",
310 ' argv = (\n sys.executable,\n "-B",',
311 ' argv = (\n sys.executable,\n "--version",',
312 ),
313 )
314
315
316def _process_authority_mutations_7() -> tuple[Mutation, ...]:
317 """Return one bounded group of process-authority mutations."""
318 return (
319 (
320 "bound-exit controller isolated-mode flag removed",
321 "devcontainer_image_selftest_process",
322 ' argv = (\n sys.executable,\n "-B",\n "-I",',
323 " argv = (\n sys.executable,\n"
324 ' "-B",\n "--version",',
325 ),
326 (
327 "bound-exit controller site-import refusal removed",
328 "devcontainer_image_selftest_process",
329 ' argv = (\n sys.executable,\n "-B",\n'
330 ' "-I",\n "-S",',
331 ' argv = (\n sys.executable,\n "-B",\n'
332 ' "-I",\n "--version",',
333 ),
334 (
335 "bound-exit controller immutable helper path removed",
336 "devcontainer_image_selftest_process",
337 ' argv = (\n sys.executable,\n "-B",\n'
338 ' "-I",\n "-S",\n SUPERVISOR_PROGRAM,\n'
339 ' "--controller",',
340 ' argv = (\n sys.executable,\n "-B",\n'
341 ' "-I",\n "-S",\n'
342 ' "scripts/ci/devcontainer_image_selftest_supervisor.py",\n'
343 ' "--controller",',
344 ),
345 (
346 "bound-exit payload fixed Bash path removed",
347 "devcontainer_image_selftest_supervisor",
348 ' "/bin/bash",',
349 ' "/usr/bin/env",',
350 ),
351 )
352
353
354def _process_authority_mutations_8() -> tuple[Mutation, ...]:
355 """Return one bounded group of process-authority mutations."""
356 return (
357 (
358 "bound-exit payload protected Bash mode removed",
359 "devcontainer_image_selftest_supervisor",
360 ' "-p",',
361 ' "-c",',
362 ),
363 (
364 "bound-exit parent-death polling removed",
365 "devcontainer_image_selftest_supervisor",
366 "ready, _, _ = select.select((death_descriptor,), (), (), POLL_SECONDS)",
367 "ready = ()",
368 ),
369 (
370 "bound-exit controller group cleanup reduced to controller PID",
371 "devcontainer_image_selftest_supervisor",
372 "os.killpg(os.getpgrp(), signal.SIGKILL)",
373 "os.kill(os.getpid(), signal.SIGKILL)",
374 ),
375 (
376 "bound-exit terminal payload is polled after reap",
377 "devcontainer_image_selftest_supervisor",
378 " if not published:\n child_status = _poll_payload(child)",
379 " if True:\n child_status = _poll_payload(child)",
380 ),
381 )
382
383
384def _process_authority_mutations_9() -> tuple[Mutation, ...]:
385 """Return one bounded group of process-authority mutations."""
386 return (
387 (
388 "bound-exit controller liveness proof removed",
389 "devcontainer_image_selftest_supervisor",
390 ' supervisor.require_running("after publishing status")',
391 " pass",
392 ),
393 (
394 "bound receipt hardlink refusal removed",
395 "devcontainer_image_selftest_supervisor",
396 " or metadata.st_nlink != 1",
397 " or False",
398 ),
399 (
400 "bound receipt owner binding removed",
401 "devcontainer_image_selftest_supervisor",
402 " or metadata.st_uid != os.getuid()",
403 " or False",
404 ),
405 (
406 "bound receipt mode binding removed",
407 "devcontainer_image_selftest_supervisor",
408 " or stat.S_IMODE(metadata.st_mode) != RECEIPT_MODE",
409 " or False",
410 ),
411 )
412
413
414def _process_authority_mutations_10() -> tuple[Mutation, ...]:
415 """Return one bounded group of process-authority mutations."""
416 return (
417 (
418 "bound receipt truncation removed",
419 "devcontainer_image_selftest_supervisor",
420 " os.ftruncate(descriptor, 0)",
421 " pass",
422 ),
423 (
424 "status receipt no-follow descriptor removed",
425 "devcontainer_image_selftest_supervisor",
426 " descriptor = os.open(path, os.O_RDONLY | os.O_NOFOLLOW)",
427 " descriptor = os.open(path, os.O_RDONLY)",
428 ),
429 (
430 "process stat bytes parser replaced with text decoding",
431 "devcontainer_image_selftest_process",
432 '(entry / "stat").read_bytes()',
433 '(entry / "stat").read_text(encoding="ascii").encode("ascii")',
434 ),
435 (
436 "emergency cleanup process identity check removed",
437 "devcontainer_image_selftest_supervisor_cases",
438 " if not _identity_is_current(authority):\n return False",
439 " if False:\n return False",
440 ),
441 )
442
443
444def _process_authority_mutations_11() -> tuple[Mutation, ...]:
445 """Return one bounded group of process-authority mutations."""
446 return (
447 (
448 "parent-death watchdog selftest removed",
449 "devcontainer_image_selftest",
450 (
451 ' run_bound_exit_supervisor --selftest-parent-death "$stall_entr'
452 'y" "$tmp" \\\n "$SELFTEST_TMP_IDENTITY" || {'
453 ),
454 " true || {",
455 ),
456 (
457 "live-supervisor watchdog deadline selftest removed",
458 "devcontainer_image_selftest",
459 (
460 ' run_bound_exit_supervisor --selftest-watchdog-expiry "$stall_'
461 'entry" "$tmp" \\\n "$SELFTEST_TMP_IDENTITY" || {'
462 ),
463 " true || {",
464 ),
465 (
466 "closed death descriptor selftest removed",
467 "devcontainer_image_selftest",
468 (
469 ' run_bound_exit_supervisor --selftest-closed-death-fd "$stall_'
470 'entry" "$tmp" \\\n "$SELFTEST_TMP_IDENTITY" || {'
471 ),
472 " true || {",
473 ),
474 (
475 "controller close failure bypasses group KILL",
476 "devcontainer_image_selftest_supervisor",
477 (
478 " for private_descriptor in (death_descriptor, root_descriptor):\n"
479 " with suppress(OSError):\n"
480 " os.close(private_descriptor)\n"
481 " os.killpg(os.getpgrp(), signal.SIGKILL)"
482 ),
483 (
484 " for private_descriptor in (death_descriptor, root_descriptor):\n"
485 " os.close(private_descriptor)\n"
486 " os.killpg(os.getpgrp(), signal.SIGKILL)"
487 ),
488 ),
489 )
490
491
492def _process_authority_mutations_12() -> tuple[Mutation, ...]:
493 """Return one bounded group of process-authority mutations."""
494 return (
495 (
496 "bound-exit group cleanup reduced to leader PID",
497 "devcontainer_image_selftest_process",
498 "os.killpg(leader, signal.SIGKILL)",
499 "os.kill(leader, signal.SIGKILL)",
500 ),
501 (
502 "bound-exit cleanup signal block moved after authority checks",
503 "devcontainer_image_selftest_process",
504 (
505 " signal.pthread_sigmask(signal.SIG_BLOCK, MANAGED_SIGNA"
506 "LS)\n if self.authority_lost:"
507 ),
508 (
509 " if self.authority_lost:\n signal"
510 ".pthread_sigmask(signal.SIG_BLOCK, MANAGED_SIGNALS)"
511 ),
512 ),
513 (
514 "bound-exit status atomic publication removed",
515 "devcontainer_image_selftest_supervisor",
516 "os.link(temporary, path, follow_symlinks=False)",
517 'path.write_text(value, encoding="ascii")',
518 ),
519 (
520 "bound-exit supervisor failure cases removed",
521 "devcontainer_image_selftest",
522 "selftest_bound_exit_supervisor_failures() {",
523 "selftest_bound_exit_supervisor_failures_disabled() {",
524 ),
525 )
526
527
528def _process_authority_mutations_13() -> tuple[Mutation, ...]:
529 """Return the hidden supervisor runtime-proof mutations."""
530 return (
531 (
532 "closed death descriptor source fd propagation removed",
533 "devcontainer_image_selftest_supervisor_cases",
534 " inherited = [source_descriptor, root_descriptor]",
535 " inherited = [root_descriptor]",
536 ),
537 (
538 "closed death descriptor expected KILL status weakened",
539 "devcontainer_image_selftest_supervisor_cases",
540 "return 0 if observed and cleaned and child.returncode == -signal.SIGKILL else 1",
541 "return 0 if observed and cleaned and child.returncode is not None else 1",
542 ),
543 (
544 "watchdog expiry pre-release proof removed",
545 "devcontainer_image_selftest_supervisor_cases",
546 (
547 " pre_release_proven = killed_receipt and members is not None\n"
548 " pre_release_proven = pre_release_proven and members <= {authority.pid}"
549 ),
550 " pre_release_proven = True",
551 ),
552 (
553 "watchdog expiry test deadline extended",
554 "devcontainer_image_selftest_supervisor_cases",
555 "launch = ControllerLaunch(entry, status, SELFTEST_WATCHDOG_TIMEOUT_SECONDS)",
556 "launch = ControllerLaunch(entry, status, WATCHDOG_TIMEOUT_SECONDS)",
557 ),
558 (
559 "hardlink publication preservation proof removed",
560 "devcontainer_image_selftest_supervisor_cases",
561 (
562 'preserved = victim.read_bytes() == b"preserve\\n" and '
563 "victim.stat().st_nlink == HARDLINK_COUNT"
564 ),
565 "preserved = True",
566 ),
567 (
568 "missing payload exec status weakened",
569 "devcontainer_image_selftest_supervisor",
570 " except OSError:\n os._exit(127)",
571 " except OSError:\n os._exit(1)",
572 ),
573 )
574
575
576def _process_authority_mutations_15() -> tuple[Mutation, ...]:
577 """Return public Bash dispatch mutations after the supervisor split."""
578 return (
579 (
580 "hardlink publication group proof removed",
581 "devcontainer_image_selftest",
582 (
583 ' run_bound_exit_supervisor --selftest-hardlink-bound "$stall_'
584 'entry" "$tmp" \\\n "$SELFTEST_TMP_IDENTITY" || {'
585 ),
586 " true || {",
587 ),
588 (
589 "missing payload entry proof removed",
590 "devcontainer_image_selftest",
591 (
592 ' run_bound_exit_supervisor --selftest-missing-entry "$tmp" '
593 '"$SELFTEST_TMP_IDENTITY" || {'
594 ),
595 " true || {",
596 ),
597 )
598
599
600def _process_authority_mutations_14() -> tuple[Mutation, ...]:
601 """Return the watchdog observation and runner-lifetime mutations."""
602 return (
603 (
604 "watchdog exact KILL receipt weakened",
605 "devcontainer_image_selftest_supervisor_cases",
606 "killed = result.si_code == os.CLD_KILLED and result.si_status == signal.SIGKILL",
607 "killed = result is not None",
608 ),
609 (
610 "watchdog runner liveness proof removed",
611 "devcontainer_image_selftest_supervisor_cases",
612 "if pre_release_proven and runner_is_live:",
613 "if pre_release_proven:",
614 ),
615 (
616 "watchdog post-release group proof removed",
617 "devcontainer_image_selftest_supervisor_cases",
618 "watchdog_succeeded = expected and _wait_group_gone(authority.group)",
619 "watchdog_succeeded = expected",
620 ),
621 (
622 "watchdog post-reap PID guard conflated with expected status",
623 "devcontainer_image_selftest_supervisor_cases",
624 (
625 " runner_status = _wait_direct_child_status(runner)\n"
626 " runner_reaped = runner_status is not None\n"
627 " expected = runner_status == STALL_STATUS"
628 ),
629 (
630 " runner_status = _wait_direct_child_status(runner)\n"
631 " runner_reaped = runner_status == STALL_STATUS\n"
632 " expected = runner_status == STALL_STATUS"
633 ),
634 ),
635 (
636 "hardlink runner post-reap signal guard removed",
637 "devcontainer_image_selftest_supervisor_cases",
638 (
639 " if not hardlink_runner_reaped:\n"
640 " with suppress(ProcessLookupError):"
641 ),
642 " if True:\n with suppress(ProcessLookupError):",
643 ),
644 (
645 "stall fixture descendant marker removed",
646 "devcontainer_image_selftest",
647 ' "exec -a \\"\\$0\\" /bin/sleep 30" >"$destination") || return 1',
648 ' "/bin/sleep 30" >"$destination") || return 1',
649 ),
650 )
651
652
653def _main_descriptor_mutations() -> tuple[Mutation, ...]:
654 """Return mutations for descriptor-bound main-script path authority."""
655 return (
656 (
657 "main descriptor basename predicate removed",
658 "devcontainer_image",
659 ' "$ra8_bound_entry" == /*/devcontainer_image.sh &&\n',
660 " true &&\n",
661 ),
662 (
663 "main descriptor file and link predicate removed",
664 "devcontainer_image",
665 ' -f "$ra8_bound_entry" && ! -L "$ra8_bound_entry" &&\n',
666 " true &&\n",
667 ),
668 (
669 "main descriptor canonical path predicate removed",
670 "devcontainer_image",
671 ' [[ "$ra8_bound_entry" == "$SCRIPT_DIR/devcontainer_image.sh" ]] || {\n',
672 " false || {\n",
673 ),
674 )
675
676
677def _helper_parent_mutations() -> tuple[Mutation, ...]:
678 """Return mutations for canonical helper-parent path bindings."""
679 specifications = (
680 ("lifecycle", "devcontainer_image_selftest", "SELFTEST_HELPER_PARENT_DIR"),
681 ("cases", "devcontainer_image_selftest_cases", "SELFTEST_CASES_PARENT_DIR"),
682 ("signal", "devcontainer_image_signal_selftest", "SELFTEST_SIGNAL_PARENT_DIR"),
683 ("lock", "devcontainer_image_lock_selftest", "SELFTEST_LOCK_HELPER_PARENT_DIR"),
684 )
685 return tuple(
686 (
687 f"{label} helper canonical parent proof removed",
688 key,
689 (
690 ' "${DEVCONTAINER_SELFTEST_PARENT:-}" == '
691 f'"${variable}/devcontainer_image.sh" &&\n'
692 ),
693 (f' "${{DEVCONTAINER_SELFTEST_PARENT:-}}" == "${variable}/not-main.sh" &&\n'),
694 )
695 for label, key, variable in specifications
696 )
697
698
699def _entry_descriptor_mutations() -> tuple[Mutation, ...]:
700 """Return mutations for the reserved descriptor execution namespace."""
701 key = "devcontainer_image_selftest_process"
702 return (
703 (
704 "entry descriptor floor lowered into helper range",
705 key,
706 "ENTRY_EXEC_DESCRIPTOR_MINIMUM = 64",
707 "ENTRY_EXEC_DESCRIPTOR_MINIMUM = 8",
708 ),
709 (
710 "entry descriptor reservation call removed",
711 key,
712 " descriptor = _reserve_entry_descriptor(descriptor)",
713 " descriptor = descriptor",
714 ),
715 (
716 "entry descriptor propagation removed",
717 key,
718 " inherited.append(self.entry_descriptor)",
719 " pass",
720 ),
721 (
722 "entry descriptor high-FD duplication removed",
723 key,
724 (
725 " reserved = fcntl.fcntl(\n"
726 " descriptor,\n"
727 " fcntl.F_DUPFD_CLOEXEC,\n"
728 " ENTRY_EXEC_DESCRIPTOR_MINIMUM,\n"
729 " )"
730 ),
731 " reserved = os.dup(descriptor)",
732 ),
733 (
734 "entry descriptor original close removed",
735 key,
736 " os.close(descriptor)",
737 " pass",
738 ),
739 (
740 "entry descriptor CLOEXEC readback removed",
741 key,
742 " descriptor_flags = fcntl.fcntl(reserved, fcntl.F_GETFD)",
743 " descriptor_flags = fcntl.FD_CLOEXEC",
744 ),
745 )
746
747
748def _entry_descriptor_predicate_mutations() -> tuple[Mutation, ...]:
749 """Return the two independent reserved-descriptor predicate mutations."""
750 key = "devcontainer_image_selftest_process"
751 return (
752 (
753 "entry descriptor reservation bound removed",
754 key,
755 " if reserved < ENTRY_EXEC_DESCRIPTOR_MINIMUM or not (",
756 " if not (",
757 ),
758 (
759 "entry descriptor CLOEXEC predicate removed",
760 key,
761 (
762 " if reserved < ENTRY_EXEC_DESCRIPTOR_MINIMUM or not "
763 "(descriptor_flags & fcntl.FD_CLOEXEC):"
764 ),
765 (" if reserved < ENTRY_EXEC_DESCRIPTOR_MINIMUM or not (descriptor_flags >= 0):"),
766 ),
767 )
768
769
770def _tmp_root_mutations() -> tuple[Mutation, ...]:
771 """Return mutations for canonical and nested allocation-parent proofs."""
772 key = "devcontainer_image_selftest_cases"
773 return (
774 (
775 "allocation parent proof call removed",
776 key,
777 ' selftest_temp_root_is_safe || die "selftest: allocation parent authority is unsafe"',
778 " true",
779 ),
780 (
781 "canonical tmp special-mode proof removed",
782 key,
783 ' "$(file_special_mode "$SELFTEST_TMP_ROOT")" == "1777" ]]',
784 ' -d "$SELFTEST_TMP_ROOT" ]]',
785 ),
786 (
787 "nested suite-root safety proof removed",
788 key,
789 (
790 ' "$SELFTEST_TMP_ROOT_IDENTITY" == "$SELFTEST_SUITE_ROOT_IDENTITY" ]] &&\n'
791 " selftest_suite_root_is_safe"
792 ),
793 (
794 ' "$SELFTEST_TMP_ROOT_IDENTITY" == "$SELFTEST_SUITE_ROOT_IDENTITY" ]] &&\n'
795 " true"
796 ),
797 ),
798 )
799
800
801def _suite_anchor_validation_mutations() -> tuple[Mutation, ...]:
802 """Bind suite-anchor path, identity, owner, mode, and depth validation."""
803 lifecycle = "devcontainer_image_selftest"
804 return (
805 (
806 "suite anchor canonical path binding removed",
807 lifecycle,
808 ' "$anchor" == "$canonical/ra8-devcontainer-image-selftest.$suffix" &&',
809 " true &&",
810 ),
811 (
812 "suite anchor identity binding removed",
813 lifecycle,
814 ' "$(file_identity "$anchor")" == "$SELFTEST_SUITE_ANCHOR_IDENTITY" &&',
815 " true &&",
816 ),
817 (
818 "suite anchor owner binding removed",
819 lifecycle,
820 ' "$(file_owner_id "$anchor")" == "$SELFTEST_SUITE_ANCHOR_OWNER_UID" &&',
821 " true &&",
822 ),
823 (
824 "suite anchor mode binding removed",
825 lifecycle,
826 ' "$(file_mode "$anchor")" == "700" ]]',
827 " true ]]",
828 ),
829 (
830 "suite anchor child depth binding removed",
831 lifecycle,
832 ' [[ "$suite" == "$SELFTEST_SUITE_ANCHOR/ra8-devcontainer-image-selftest.$suffix" &&',
833 " [[ true &&",
834 ),
835 )
836
837
838def _suite_anchor_flow_mutations() -> tuple[Mutation, ...]:
839 """Bind suite-anchor production, dispatch, and receipt authority."""
840 lifecycle = "devcontainer_image_selftest"
841 cases = "devcontainer_image_selftest_cases"
842 return (
843 (
844 "suite anchor producer path removed",
845 lifecycle,
846 ' SELFTEST_SUITE_ANCHOR="$SELFTEST_TMP_DIR"',
847 ' SELFTEST_SUITE_ANCHOR=""',
848 ),
849 (
850 "suite anchor producer identity removed",
851 lifecycle,
852 ' SELFTEST_SUITE_ANCHOR_IDENTITY="$SELFTEST_TMP_IDENTITY"',
853 ' SELFTEST_SUITE_ANCHOR_IDENTITY=""',
854 ),
855 (
856 "suite anchor producer owner removed",
857 lifecycle,
858 ' SELFTEST_SUITE_ANCHOR_OWNER_UID="$SELFTEST_TMP_OWNER_UID"',
859 ' SELFTEST_SUITE_ANCHOR_OWNER_UID=""',
860 ),
861 (
862 "suite anchor receiver path removed",
863 lifecycle,
864 ' SELFTEST_SUITE_ANCHOR="$3"',
865 ' SELFTEST_SUITE_ANCHOR=""',
866 ),
867 (
868 "suite anchor receiver identity removed",
869 lifecycle,
870 ' SELFTEST_SUITE_ANCHOR_IDENTITY="$4"',
871 ' SELFTEST_SUITE_ANCHOR_IDENTITY=""',
872 ),
873 (
874 "suite anchor receiver owner removed",
875 lifecycle,
876 ' SELFTEST_SUITE_ANCHOR_OWNER_UID="$5"',
877 ' SELFTEST_SUITE_ANCHOR_OWNER_UID=""',
878 ),
879 (
880 "suite anchor dispatcher propagation removed",
881 cases,
882 ' "$tmp" "$SELFTEST_TMP_IDENTITY" "$SELFTEST_SUITE_ANCHOR" \\\n'
883 ' "$SELFTEST_SUITE_ANCHOR_IDENTITY" "$SELFTEST_SUITE_ANCHOR_OWNER_UID"; then',
884 ' "$tmp" "$SELFTEST_TMP_IDENTITY" "" "" ""; then',
885 ),
886 )
887
888
889def _process_authority_mutations() -> tuple[Mutation, ...]:
890 """Return the unassigned image-lock process-authority mutation catalog."""
891 return (
892 *_process_authority_mutations_1(),
893 *_process_authority_mutations_1b(),
894 *_process_authority_mutations_2(),
895 *_process_authority_mutations_3(),
896 *_process_authority_mutations_4(),
897 *_process_authority_mutations_5(),
898 *_process_authority_mutations_6(),
899 *_process_authority_mutations_7(),
900 *_process_authority_mutations_8(),
901 *_process_authority_mutations_9(),
902 *_process_authority_mutations_10(),
903 *_process_authority_mutations_11(),
904 *_process_authority_mutations_12(),
905 *_process_authority_mutations_13(),
906 *_process_authority_mutations_14(),
907 *_process_authority_mutations_15(),
908 *_main_descriptor_mutations(),
909 *_helper_parent_mutations(),
910 *_entry_descriptor_mutations(),
911 *_entry_descriptor_predicate_mutations(),
912 *_tmp_root_mutations(),
913 *_suite_anchor_validation_mutations(),
914 *_suite_anchor_flow_mutations(),
915 *runtime_fixtures.process_authority_mutations(),
916 *source_fixtures.process_authority_mutations(),
917 *process_source_fixtures.process_authority_mutations(),
918 )
919
920
921def process_authority_mutations() -> tuple[Mutation, ...]:
922 """Assign every mutation to the source module that owns its target bytes."""
923 return tuple(
924 (
925 label,
926 "devcontainer_image_bound_exit_selftest"
927 if label in _BOUND_EXIT_MUTATION_LABELS
928 else key,
929 old,
930 new,
931 )
932 for label, key, old, new in _process_authority_mutations()
933 )