docs: executive retrofit dispatch plan (vms-6b8) - #2
Merged
Merged
Conversation
Decomposes the executive gap into a dispatch-ready, outcome-scoped tree.
Sequencing is driven by testability, not by the design ruling. No CI job
currently loads vms.ko (persistent-boot runs its QEMU script inside a Docker
container; there is no kernel-module job), so the kernel executive is
unprovable in CI. That is the mechanical cause of the gap: untestable
facilities defaulted to per-process userspace fakes that report success. The
architecture drifted to fit the test harness. Phase 0 is therefore a hard
barrier.
Phase 0 vms-e4d QEMU CI job loads vms.ko [QA]
Phase 1 vms-ef1 / vms-as1 / vms-pv1 wire the ioctls that
ALREADY exist in vms.ko (event flags incl. common
clusters, ASTs, privileges) — wiring, not design [Systems]
Phase 2 vms-71a migrate Docker CI jobs, delete Dockerfile [QA]
Phase 3 vms-pt1 process table, vms-ln0 LNM design ruling ->
vms-d37 LNM, vms-dv1 devices, vms-mb1 mailboxes [Systems]
Phase 4 vms-fk1 delete the fakes; gate forbids their return
Dependencies wired so exactly three items are dispatchable now: vms-e4d
(the barrier), vms-ln0 (the one real design fork left — LNM sits on the hot
path of every file open, so ioctl-per-translation is not automatically
right), and vms-b9f (SHOW DEVICE host-mount-table leak, independent of the
executive entirely).
Standing constraints restated per item: one runtime target (Rule 9), never a
silent userspace fallback (fail honestly with SS$_NOSUCHDEV), not done until
proven against a real /dev/vms, clean-room, purity sign-off on VMS values.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…rt (vms-6b8) Ran the five-pass swarm decomposition. Pass 0 rigor: HEAVY — blast heavy (kernel + libvms + vmsprocess + vmsdcl + CI, >20 files, >3 packages), adversarial heavy floor (privileges/access modes are a security surface), +1 coverage modifier because no CI job loads vms.ko so every touched path is uncovered. 24 items. COLD-START HAZARD FOUND AND GATED. Every item referenced artifacts that do not exist on main — verified with git cat-file: docs/design-executive-retrofit.md (PR #2), tests/integration/test_runtime_target.sh (PR #1), CLAUDE.md Rule 9 (PR #1), and the roadmap sec 2.1 analysis (PR #1). An agent dispatched cold would be told to obey Rule 9, find no Rule 9, and re-derive the exact conclusion this epic exists to correct. Added vms-pre, a human prerequisite (merge PR #1 + PR #2) that blocks all three DAG entry points. Belt and braces: every item now also carries its constraints inline, so items remain executable even if the docs are missing. Also fixed placeholder item IDs in the dispatch notes (vms-LNM1 etc. never existed) which would have sent a cold agent chasing dead references. Added the heavy-rigor layer: concurrent veracity adversaries per wave (vms-vx1, vms-vx2) written against this project's actual failure history — they attack the specific cheats available here (stubbing /dev/vms, simulating two processes with two threads, inheritance-based mailbox passing). Plus reviewers, a dedicated security review of the privilege surface, five sweeps, and an e2e that proves the facilities COMPOSE, which is where the original design failed. Downstream consumers annotated SUBSTRATE-BLOCKED (vms-853, vms-46b, vms-c17, vms-905) so a cold agent does not mistake them for display work and build a better-looking fake. DAG verified sealed: only vms-pre is ready. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
baron-3dl
marked this pull request as ready for review
July 29, 2026 02:42
baron-3dl
added a commit
that referenced
this pull request
Jul 30, 2026
… point against a real /dev/vms (vms-1d9) (#15) * tests/qemu: exercise the public sys$ lock API against real /dev/vms (vms-1d9) Phase 0.5 hard barrier: the merged Kernel Executive CI job (vms-e4d) only COPYs src/kernel/ + tests/qemu/ into its initramfs, so every test drives /dev/vms with raw ioctls. An adversary proved that reverting a userspace syssvc file (src/libvms/syssvc/sys_event.c) to its pre-change stub left the harness byte-identical green -- every Phase 1/3 item is userspace wiring the harness cannot see at all. Adds test_syssvc_lock, statically linked against the REAL src/libvms (musl, same OVMX_STATIC mode distro/Dockerfile.bootable already uses) and built into the initramfs alongside the raw-ioctl test_kmod_* programs. It calls the public sys$enq/sys$enqw/sys$deq entry points across a real fork()'d second process and, empirically (podman build+run against real QEMU/vms.ko): - GREEN with production sys_lock.c: 11 suites passed, 0 failed. - RED after reverting sys$enq/sys$enqw to an always-succeeds stub (same defect shape as the sys_event.c regression): test_syssvc_lock's cross-process NOQUEUE-denial and post-release-grant assertions fail (10 suites passed, 1 failed) while every test_kmod_* stays green -- proving the ioctl tests are structurally blind to this class of bug and the new test is not. - GREEN again after restoring sys_lock.c. Also: - src/libvms/include/lksdef.h: promotes sys_lock.c's private LKSB struct to a public header (zero behavior change) -- external callers had no way to build the lksb parameter sys$enq/sys$enqw/sys$deq require. - tests/qemu/CMakeLists.txt: builds test_syssvc_lock against real /dev/vms when present; ctest SKIP (exit 77), never a fake PASS, when it is not (every dev/CI container -- Rule 9, Docker is not a runtime). - .github/workflows/ci.yml: updates the kernel-executive negative- control job's exact suite-count assertion (3 passed/7 failed -> 3 passed/8 failed), empirically re-measured against a real NEGATIVE_CONTROL=1 build+run -- test_syssvc_lock also depends on /dev/vms and joins the "fails honestly when absent" bucket. Found and flagged, not fixed (out of this item's scope): src/libvms/include/lckdef.h duplicates starlet.h's LCK$M_* flag constants with DIFFERENT, stale values (e.g. LCK$M_NOQUEUE 0x8 vs. starlet.h's oracle-pinned 0x4) -- see the file-header comment in test_syssvc_lock.c and this item's returned findings. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * tests/qemu: fix vms-1d9 round-2 adversarial findings (round 3) Rebased onto current origin/main (past vms-e4d) and fixed the three merge-blockers an adversary found against round 2, all verified against real podman build+QEMU runs (never just code-read): B1 - THE CAPABILITY WAS NOT GENERAL. tests/qemu/Dockerfile named exactly one binary twice (`--target test_syssvc_lock`, `cp .../test_syssvc_lock`), so the next test_syssvc_*.c a Phase 1/3 item adds would build, ctest-SKIP fine locally, and never reach the QEMU initramfs. Fixed: - tests/qemu/CMakeLists.txt now globs test_syssvc_*.c and registers each automatically via qemu_syssvc_add_test(), collecting every target name into a GLOBAL property and exposing a single `qemu_syssvc_tests` custom target that depends on all of them. - tests/qemu/Dockerfile builds `--target qemu_syssvc_tests` (not a named binary) and copies build-static/bin/test_syssvc_* by glob into the initramfs. - Proved generality empirically: added a throwaway test_syssvc_dummy.c with NO Dockerfile/CMakeLists.txt edit, podman-built, and confirmed it ran inside QEMU ("test_syssvc_dummy: 1 passed, 0 failed", FINAL RESULTS 12/0). Removed the dummy and reran -- back to 11/0, matching the new ci.yml assertion below. init.sh's existing `/tests/test_syssvc_*` glob already handled the run side; only the build/copy side was hardcoded. - Added a suite-count assertion to the POSITIVE kernel-executive CI job (previously only the negative-control job pinned a count), so a test that silently stops being built/staged/run can no longer stay green. B2 - THE NO-SILENT-FALLBACK PROOF WAS CIRCULAR. test_syssvc_lock bailed at its own vms_kif_open() bootstrap and exited SKIP(77) before any sys$ call was made, so sys_lock.c's SS$_NOSUCHDEV return path (do_enq/sys$deq) was never actually exercised -- constraint #2 was satisfied by code reading. Fixed: when bootstrap fails, the test now calls the PUBLIC sys$enqw and sys$deq entry points directly (vms_kif_open() is idempotent on failure, so this drives the real ensure_kif_open()-fails branch in sys_lock.c) and CHECKs the returned status AND the LKSB's own status field both equal SS$_NOSUCHDEV. A failed check now returns exit 1 (real FAIL), not a masked 77. Proved the gate can go red: injected a defect in do_enq() (fake SS$_NORMAL success instead of SS$_NOSUCHDEV when /dev/vms is absent), rebuilt+ran the negative-control image, watched the new assertions FAIL ("test_syssvc_lock: 1 passed, 2 failed"), then reverted and reran green. B3 - lksdef.h WAS PRESENTED AS VMS-AUTHENTIC. Rewrote the header comment per CLAUDE.md Rule 8: explicitly labeled an OVMX design choice, not a VMS- published layout, citing the oracle finding that SYS$LIBRARY:STARLET.MLB has no $LKSB macro at all (%LIBRAR-W-NOMTCHFOU) -- there is nothing authentic to pin the byte layout against. Also (low priority, honesty over fixing): documented in tests/qemu/CMakeLists.txt that the ctest registration of test_syssvc_lock SKIPs in 100% of environments where ctest runs, and is invoked directly by init.sh (not through ctest) inside QEMU -- it buys build-graph inclusion and an honest SKIP, not coverage, despite appearing in the ctest listing. Verified via podman (docker is absent on this host; podman reproduces CI exactly): - Positive job: FINAL RESULTS 11 suites passed, 0 suites failed. - Negative-control job: FINAL RESULTS 3 suites passed, 8 suites failed; new no-silent-fallback CHECKs all PASS (SS$_NOSUCHDEV asserted, not a string this program authored). - Injected-defect run: new CHECKs FAIL as expected, proving the gate can trip; reverted and reran green. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * tests/qemu: fix vms-1d9 round-4 regressions (F1 dockerignore, F2 suite-count floor) F1: Dockerfile, distro/Dockerfile.bootable, and tests/qemu/Dockerfile all `COPY . <dest>` the full build context with no .dockerignore, so any local build-*/ tree, .git history, or stray artifact in the working copy lands in the image byte for byte -- slow, fat, and a disclosure risk. Reproduced from this exact working copy (build-ci/, build-docker/, build-test/, build-verify/, .git, docs/, tracking/, third-party/ all present on disk). Added a repo-root .dockerignore excluding VCS/agent-state/historical-doc directories verified (by grep across all CMakeLists.txt and all three Dockerfiles) to be unread by any build step. Confirmed via `podman build` + exec that /src/repo no longer contains build-ci, .git, docs, tracking, or third-party, and that all three Dockerfiles (root, bootable, qemu) still build and smoke-test clean. F2: the round-3 kernel-executive CI job hard-pinned '11 suites passed, 0 suites failed', so the very next item that legitimately adds test_syssvc_event.c would raise the true count to 12 and turn CI red for succeeding -- defeating the glob-based generality vms-1d9 exists to build. Replaced the exact pin with three checks that only go red when a suite is REMOVED or FAILS, never when one is ADDED: zero-failures, a floor of >=11 passed, and presence of every named suite's init.sh header line. Proved both directions against real QEMU runs (not just regex review): - Removed tests/qemu/test_kmod_access.c, rebuilt, ran in QEMU for real: output showed '10 suites passed, 0 suites failed'; new check goes RED (floor check) and independently RED (named-suite check, verified with the count artificially padded back to 11 to isolate that layer). - Restored test_kmod_access.c; added a genuine throwaway test_syssvc_throwaway.c (deleted before this commit), rebuilt with zero Dockerfile/CMakeLists edits, ran in QEMU for real: output showed '12 suites passed, 0 suites failed'; new check stays GREEN. - Rebuilt the final tree (no throwaway file) and reran in QEMU: baseline '11 suites passed, 0 suites failed' unchanged, new check GREEN. - Reran the negative-control image (NEGATIVE_CONTROL=1): unchanged '3 suites passed, 8 suites failed', RC=1 -- untouched by this change. Everything else the round-3->4 adversarial review found (SS$_NOSUCHDEV 2680 vs oracle 2312, lckdef.h bit-value drift, zero production callers of vms_kif_register(), the QEMU gate's blindness to src/vmsdcl, only the lock manager reachable) is pre-existing debt, already filed separately, and is untouched here. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * tests/qemu: gate the silent-fallback path and fix round-4 regressions (vms-1d9) Round 4 fixed two findings and introduced four. This undoes that damage and closes the one gap that was never gated. Every claim below is proven by a real podman build + QEMU boot, not by inspection. G1 .dockerignore no longer excludes CLAUDE.md. Round 4's exclusion broke this epic's OWN Rule 9 standing gate inside every image built from the repo root: tests/integration/test_runtime_target.sh greps CLAUDE.md, and `ctest -R runtime_target_gate` went red in the image while CI stayed green (CI only ran ctest on the host checkout) -- a silently broken guardrail. Now passes in-image. G2 .dockerignore no longer excludes third-party/, which src/imgact/test/ run_tcc_{native,rms,object_native,selfhost}.sh and src/vmslink/mk_tcc.sh hard-require. The whole list is narrowed to non-source state only, and it is now validated by RUNNING the suite inside the resulting image rather than by grepping the build files -- the method that missed both of the above. G3 The suite gate is strong in both directions with nothing maintained by hand. Round 3 pinned an exact tally (red on a legitimate addition); round 4 replaced it with a floor plus a hand-maintained name list whose own comment said it is not updated on addition, leaving every future suite unprotected. init.sh now prints a machine-readable per-suite verdict carrying the binary's real exit status, and CI derives the expected suite set from `ls tests/qemu/test_*.c`. Suite ADDED -> green; suite DROPPED -> red; suite FAILS -> red. A monotone floor on the number of suite SOURCES catches outright deletion, which a derived set cannot see. G4 The negative-control job gets the same treatment. Its exact 3/8 tally pin turned red when a legitimate test_syssvc_*.c was added (proven: 3/9). G5 The decisive one. A real silent fallback in sys_lock.c (SS$_NORMAL instead of SS$_NOSUCHDEV when /dev/vms is absent, in do_enq and sys$deq) left the FINAL RESULTS accounting BYTE-IDENTICAL -- 3 passed / 8 failed, RC 1 -- and every assertion in both jobs still passed. Cause: init.sh funnels exit 77 (honest skip) and exit 1 (assertion failure) into one counter, and CI pinned only the total, so a per-process fake that reports success was invisible to the entire gate. The negative control now asserts that every test_syssvc_* suite exits exactly 77, which holds only when its device-absent SS$_NOSUCHDEV assertions all passed. Re-injecting that exact fallback now turns the job RED; reverting restores green (identical image SHA). Unchanged: test_syssvc_lock.c, tests/qemu/CMakeLists.txt, sys_lock.c, lksdef.h. The proven core -- a QEMU test linking the real libvms catching a userspace defect all eight raw-ioctl suites miss -- was not touched, and was re-verified: deleting one kstat_to_ss() line turns test_syssvc_lock red while all 8 test_kmod_* suites stay rc=0. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * tests/qemu: fail loudly, not silently, on an empty derived suite set (vms-1d9) GitHub runs `run:` steps under `bash -e`, and `grep -c` exits 1 on an empty set, so an empty derived suite list would have aborted the step with no diagnostic instead of reaching the explicit source-count floor below it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * tests/qemu: make the userspace harness generic, gated and self-proving (vms-1d9) Round 6. Three blocking defects from the round-5 verdict, each closed and each proven by its own MINIMAL mutation that trips that property and no other. All runs are real podman builds + real QEMU boots on this host (aarch64, TCG, no KVM); tools/replay_ci_kernel_executive.py executes the ci.yml assertion blocks VERBATIM out of the YAML against the captured output, so a local proof cannot drift from what CI runs. 1. THE GENERALIZATION IS NO LONGER FAKE. tests/qemu/Dockerfile dispatches BY PATTERN everywhere a name was once literal: `COPY tests/qemu/test_*.c` (was narrowed to test_kmod_*.c, so a future non-kmod source was dropped), a case-based build dispatch, the aggregate `--target qemu_syssvc_tests`, and a glob cp of every staged test_syssvc_* binary. A `|| exit 1` was added to the gcc loop -- without it a compile failure only broke that iteration and RUN exited with the LAST iteration's status, so a suite that stopped compiling vanished silently. The staging step now FAILS THE IMAGE BUILD if zero test_syssvc_* binaries were staged. PROOF: added a second suite (test_syssvc_evt.c) with ZERO Dockerfile edits -> built, staged, RUN ("=== SUITE test_syssvc_evt rc=0 ==="), positive job green at 13 derived suites, negative-control job green at 3/12. Then narrowed the cp back to the literal test_syssvc_lock: the harness still printed "ALL KERNEL MODULE TESTS PASSED" and exited 0, and the positive CI job went RED -- "test_syssvc_evt: NEVER RAN (no verdict line)". Throwaway suite removed. 2. THE POSITIVE JOB NOW HAS A GATE, NOT JUST AN EXIT CODE (vms-d2d). It derives the expected suite set from `ls tests/qemu/test_*.c` at CI time and asserts each suite's own "=== SUITE <name> rc= ===" verdict, plus a suite-count FLOOR of 12 for the case the derived set cannot see: a source deleted outright. PROOF: deleted tests/qemu/test_kmod_ast.c. Harness exit 0, "ALL KERNEL MODULE TESTS PASSED", 13 suites passed / 0 failed -- and the positive job went RED: "only 11 suite sources under tests/qemu (expected at least 12)". Restored. 3. THE NEGATIVE CONTROL NOW CALLS A PUBLIC sys$ ENTRY POINT AND JUDGES WHAT IT RETURNS, not the test's own printf. IMPORTANT SCOPE CORRECTION vs the dispatch: it does NOT assert SS$_NOSUCHDEV. vms-0ff ruled OVMX has no executive-absent state and DELETED sys_lock.c's per-call SS$_NOSUCHDEV returns; pinning that value would freeze a superseded contract into a gate -- the exact failure this epic's adversaries keep catching. What survives the ruling is a PROPERTY, not a VMS behaviour: a public sys$ entry point must never report SUCCESS when it did not reach the executive. The test asserts the odd/even success bit, an empty lock ID, and prints the raw status for the record; CI pins the suite's rc to exactly 77. PROOF: injected a fabricated success into do_enq and sys$deq (SS$_NORMAL + lock ID 0x1234 when the executive was unreachable). All four device-absent assertions FAILED, rc 77 -> 1, and the negative-control job went RED naming the cause -- while FINAL RESULTS stayed BYTE-IDENTICAL to the clean tree ("3 suites passed, 11 suites failed"), i.e. the tally pin this replaces would have stayed green. Same mutation left the POSITIVE job green (12/12), confirming it trips one property and not the others. Restored. ALSO FIXED, found while proving #3: test_syssvc_lock could HANG the whole VM. The child's post-release sys$enqw blocks in the kernel, and src/kernel/vms_lock.c's enq_wait_sync re-arms on every signal wake without returning to user mode -- so a child-side alarm(20) is swallowed. Measured: an unreleased lock sat until run_tests.sh's 120s QEMU timeout, every later suite never ran, and CI saw an unattributable timeout. The bound now lives in the PARENT (poll-based read_bounded + WNOHANG reap), which is not blocked. With sys$deq stubbed, the suite now fails in 20s with a named line and the harness still reaches its own accounting (13 passed / 1 failed). DELIBERATELY NOT DONE, and why: - tests/qemu/CMakeLists.txt's add_test() is REMOVED. It reported Skipped in 100% of environments where ctest runs and was never invoked in the one environment where it can pass (init.sh execs the binary directly), so it never executed as a passing assertion anywhere. Rule 10: a permanently- skipped test is a failing test, and a comment does not discharge it. No coverage is lost -- add_executable keeps it in the default `all` target, so it still breaks the host build if it stops compiling, and the QEMU job runs and gates it. Host ctest: 40 tests, 40 passed, 0 skipped. - src/libvms/include/lksdef.h is DROPPED and sys_lock.c is untouched. The LKSB has no VMS-published byte layout (the oracle's STARLET.MLB has no $LKSB macro), so per Rule 8 a shared header is an OVMX design choice needing operator sign-off. The test declares its own LKSB storage, which is what OpenVMS callers do anyway. - SS$_NOSUCHDEV 2680-vs-2312, lckdef.h's nine wrong flag bits: untouched, separately tracked. --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
baron-3dl
added a commit
that referenced
this pull request
Jul 31, 2026
… the process (vms-2b8) (#34) * vms-2b8: vms_kif_setident() never bound, so every identity establishment failed VMS_IOCTL_SETIDENT is by definition the FIRST executive call a process like LOGINOUT makes -- it is the call that turns a bare task into an authenticated process. vms_kif_setident() was the only wrapper in the file that issued its ioctl directly instead of through KIF_CALL, so it never ran kif_bind(): the calling task was unregistered, vms_module.c answered -ESRCH, and the establishment failed for every caller. It also collapsed every delivery failure to a literal SS$_BADPARAM, which describes the caller's arguments for a condition that has nothing to do with them. vms_kif_kerr_to_ss() gives the real status. Found by running the new tests/qemu/test_syssvc_ident.c against a real /dev/vms: a root process establishing a perfectly valid identity got status 20. The wrapper landed in the previous round of this item with no product caller, so nothing had ever exercised it. * vms-2b8: DCL reads its identity from the executive, not from its environment The executive has enforced identity since the previous round, and DCL was still reading the facade: dcl_main.c took the user name, the UIC and the privilege mask from VMS_USERNAME / VMS_UIC_GROUP / VMS_UIC_MEMBER / VMS_PRIVILEGES, and dcl_cmd_show.c printed getenv("VMS_PRIVILEGES") straight out. So the executive enforced one identity while SHOW PROCESS reported another, and any process could pick the one it reported. * dcl_context_init() reads this process's row with vms_kif_getjpi_self(). The getpwuid()/"SYSTEM" fallback chain for the user name goes with the env reads: both are values the process controls, and neither is a VMS identity. * SHOW PROCESS, SHOW PROCESS/PRIVILEGES and SHOW PROCESS/QUOTAS read the executive AT DISPLAY TIME, not out of a copy taken at startup -- a cached mask is one this process could have overwritten since. * The deleted fabrications, each of which is now a separate assertion: the "SYSTEM" user-name fallback, the getgid()/getuid() UIC fallback, and the hard-coded "TMPMBX NETMBX" default privilege list. * The privilege-name table moves to file scope: two commands decode the same mask and only one of them had a table. * NO FALLBACK on a failed read. The first vms_kif_* call registers the process (kif_bind) and PID 1 refuses to boot without /dev/vms (Rule 9), so the failure is unreachable in the one OVMX runtime; the command returns the executive's status and prints nothing. tests/dcl/test_show_process.sh, which ctest runs on a host that has no executive and never will (Rule 9), is rewritten to assert the ABSENCE that used to be wrong: its environment is poisoned with SYSTEM/ALL/[1,4] and SHOW PROCESS must report nothing at all. Each EXPECT_NOT is tied to one deleted fabrication. The positive proof moves to tests/qemu/test_syssvc_ident.c, against a real executive. * vms-2b8: PID 1 and LOGINOUT establish identity through the executive Two writers, both of which used to declare an identity rather than acquire one. PID 1 called vms_pcb_init(0xFFFFFFFFFFFFFFFF) and then vms_pcb_set_identity(1, [1,4], "SYSTEM", "SYSTEM") -- a process writing its own user name, its own UIC and every privilege bit in existence into a structure nothing outside it could see or refuse. The ANSWER was right; who decided it was the defect. It now reads the SYSTEM record from SYSUAF (parsed locally: PID 1 must stay statically linked and does not link libvms) and asks the executive to stamp it. The executive refuses any caller that does not hold SETPRV, and SETPRV is derived in vms.ko from capable(CAP_SYS_ADMIN) -- a credential no process can grant itself. Failure is FATAL: there is no VMS in which the system process has no identity, so the condition is made unreachable rather than handled (Rule 10). It runs after provision_seed_files() because SYSUAF is one of the files that seeds. LOGINOUT authenticated against SYSUAF and then published the result in four ORDINARY ENVIRONMENT VARIABLES that DCL believed. It now calls vms_kif_setident(), and the row survives the execl() because the executive keys it on the thread-group id -- so the DCL image reads the identity LOGINOUT proved without any environment variable carrying it. VMS_UIC_GROUP / VMS_UIC_MEMBER / VMS_PRIVILEGES are deleted; they have no readers left. VMS_USERNAME REMAINS AND IS STILL A FACADE, left loud rather than silent (item scope note 3). Its last reader is tools/vms_mail.c, which picks whose mailbox to open with it, so a user can still read another user's mail by setting it. Deleting it here without converting MAIL would break MAIL rather than fix the hole, and MAIL is outside this item's scope -- it is reported, not quietly carried. * vms-2b8: prove SHOW PROCESS reports the executive's identity, on the real runtime The subject is the user-visible command, run for real. DCL.EXE is staged into the QEMU initramfs at /bin (fatal if missing, and under /bin not /tests so init.sh does not exec it as a suite -- the mechanism vms-8019 built for the same purpose), and tests/qemu/test_syssvc_ident.c drives it against a real /dev/vms. Every DCL process it starts is exec'd with VMS_USERNAME=SYSTEM, VMS_PRIVILEGES=ALL, VMS_UIC_GROUP=1, VMS_UIC_MEMBER=4 -- the most privileged identity on the system, in exactly the four variables the deleted code read. If any of those reads comes back, the suite goes red. A-writes / B-reads: the identity is established by one program and reported by a DIFFERENT one, after execve replaced the image. Nothing crosses that boundary but the executive's row -- and the environment carries a contradicting claim that must lose. A a privileged writer establishes FIELD [200,10] with OPER: SHOW PROCESS reports FIELD, [310,012] and EXACTLY "NETMBX OPER TMPMBX", and SETPRV -- which the writer held and the identity it established did not -- is gone. The drop is one-way. B a second process, SAME binary, SAME poisoned environment, reports OPERATOR [1,6] with SYSPRV and not OPER. No property of the image or of the environment can explain two different answers. C an unprivileged process (real setgid+setuid, not a flag) claims SYSTEM/ALL both ways: the ioctl is refused SS$_NOPRIV, and SHOW PROCESS reports the UIC the executive derived from its real credentials, [454,1751], with exactly NETMBX and TMPMBX. Privilege absence is asserted by each privilege's oracle description rather than its NAME: "OPER" is a substring of the user name "OPERATOR", and a name-substring check would have been unfalsifiable. In the CI negative-control rig (booted with no executive) it asserts the property that survives vms-0ff's ruling -- with nothing to read, DCL reports no identity rather than falling back to the environment -- then exits 77, per the contract in ci.yml. The UAT gains one assertion: PID 1's boot log must carry the identity the executive established from SYSUAF. Whole-log grep and safe as one -- it is printed before the login prompt exists, so the script cannot have typed it. * vms-2b8: LOGINOUT becomes the authenticated user, so the refusal survives fork The veracity adversary disproved this branch's central claim by execution. "The drop is one-way" was true only within one thread group. A session that setident'd down to FIELD/[200,10]/TMPMBX|NETMBX|OPER forked a child; the child re-registered, re-derived SETPRV from the CAP_SYS_ADMIN it still held, and stamped itself SYSTEM [1,4] with all 37 privileges. A privilege reduction survived exactly until the next fork. The root cause was not in the executive. Nothing in the login path ever dropped Linux credentials -- `grep -n 'setuid|setgid' tools/vms_login.c src/ovmx_init/*.c src/vmsdcl/*.c` returned nothing -- so every DCL session and every process it spawned ran as root with CAP_SYS_ADMIN, and vms_proc_register() hands a CAP_SYS_ADMIN task CMKRNL|CMEXEC|SETPRV|WORLD at registration. The executive was enforcing identity on top of a process that could sidestep it by forking. LOGINOUT now becomes the user: setgroups(0)/setgid/setuid to the SYSUAF UIC, after VMS_IOCTL_SETIDENT (which needs the SETPRV root-derived registration granted) and before execl. Fatal if it fails. SYS$LOGIN is chowned to that UIC first, while the privilege to do it still exists. This also makes RMS and the executive agree. src/vmsrms/rms_core.c enforces file protection against [gid,uid]; the executive derives proc->uic the same way. Before this they disagreed for every session -- the executive reported the SYSUAF UIC while RMS saw root's [0,0]. Not presented as a VMS behaviour (Rule 8/10): OpenVMS has no Linux credentials. The uid/gid pair is already OVMX's substrate for the UIC; what changes is only that the substrate is made to agree with the identity being enforced. %LOGINOUT-W-NOIDENT deleted (Rule 10). It authenticated a user against SYSUAF and then handed them a session with no identity -- a state VMS has no equivalent for, so the condition is made unreachable (fatal) rather than handled. It also degraded UPWARD: such a session kept root's UIC [0,0] and the whole enforced mask, strictly more privilege than the SYSUAF record grants. The message wore the LOGINOUT facility with an invented code; the replacement is %OVMX-F-NOIDENT, the same reasoning ovmx_init.c uses for %OVMX-I-EXEC. Unreachable rather than merely refused: PID 1 halts at boot without the executive (vms-0ff). The vms_ioctl_setident() comment claiming "no sequence of calls walks a process back up" is corrected to state the guarantee's real scope, and to say plainly that a CAP_SYS_ADMIN task is outside the model. PROVEN, on the real bootable image: the UAT drives a SYSTEM session and runs SPAWN SHOW PROCESS. The subprocess registers on its own and the executive derives its UIC from real Linux credentials, so its UIC is a direct readout of what the session runs as: [001,004] with the drop, [000,000] without. 16/16. DISCRIMINATION: deleting the three drop calls from vms_login.c and rebuilding the bootable image gives [000,000] and exactly those two assertions go red (14/2). * vms-2b8: SHOW PROCESS prints what the oracle prints -- measured, not assumed Round 5 was challenged for pinning byte-exact test assertions on output that contradicts this repo's OWN oracle capture. It did. VAX1 was booted again and the commands were run through cat -A so columns were counted rather than eyeballed. New docs/oracle/vax73-privileges.md section 6 carries the verbatim transcripts. MEASURED: plain SHOW PROCESS on OpenVMS VAX V7.3 prints NOTHING about privileges. No Privileges: line, no summary of any kind -- privileges live behind /PRIVILEGES, in two named blocks. OVMX's Privileges: line is therefore an invention, and round 5 had begun asserting its exact bytes. Rule 10: there is no VMS behaviour to reproduce, so the line is DELETED, not reformatted. The whole-mask assertions move to the Authorized privileges: grid, whose format the oracle does pin -- a stricter check, not a weaker one. MEASURED: the Process privileges: line format is " %-20s %s" (counted: 21 columns before the description, for both ACNT and IMPERSONATE). The code printed " %-16s %s" while a comment three hundred lines above it claimed the format was the oracle's " %-20s %s". Corrected. ADDED: the Authorized privileges: block, which the oracle prints and OVMX did not print at all. It is printable now for the first time because the executive holds both masks; before the executive owned identity there was only one number to show. The grid is reproduced including its defect -- 8 columns of exactly 10 characters, so IMPERSONATE (11 chars) is CLIPPED to IMPERSONAT and collides with the next cell. VMS does that. A short final row is not padded, which is what the capture shows. DELETED: " (no privileges enabled)". With an empty mask the oracle prints the heading and nothing under it (section 5.2). A sentence VMS does not emit is an invention however helpful it reads. MEASURED AND DELIBERATELY NOT ACTED ON, recorded in section 6 so the item that does fix SHOW PROCESS's layout starts from a measurement: plain SHOW PROCESS also has no quota block (that is /QUOTAS), uses a 20-column label field where OVMX uses 19, shows the rights identifier [SYSTEM] rather than the octal UIC, carries a Node: field, and has a Devices allocated: section. None of those is about identity ownership, and (4) needs RIGHTSLIST support OVMX does not have. Lab left clean: @SYS$SYSTEM:SHUTDOWN to SYSTEM SHUTDOWN COMPLETE, then quit. * vms-2b8: two scenarios the old test could not express -- fork escalation, B reads A D. A REAL SESSION'S SUBPROCESS. LOGINOUT's sequence step for step: stamp the authenticated identity, drop the Linux credentials to that UIC, then spawn. The subprocess claims SYSTEM with every privilege and must be refused. This is the adversary's exploit, now asserted as a test. Measured against a real /dev/vms under QEMU: SESSION_SETIDENT=1, SESSION_UID=10 SESSION_GID=200, SUB_REGISTER=1, SUB_SETIDENT=36 (SS$_NOPRIV), and the subprocess's own DCL reports an empty user, UIC [310,012] and exactly NETMBX TMPMBX -- no SETPRV, no CMKRNL, no WORLD. DISCRIMINATION: deleting the three drop calls from the scenario reproduces the exploit exactly -- SESSION_UID=0, SUB_SETIDENT=1, DCL prints "User: SYSTEM", [001,004] and all 37 privileges including SETPRV/BYPASS/CMKRNL. 7 D assertions red, A/B/C/E untouched. So the executive's refusal and the credential drop are load-bearing only together, and the test says so. E. A-WRITES / B-READS IN ITS TRUE FORM (Rule 11). Scenarios A-D cross execve, which proves the row is not in the image and not in the environment, but it is still one task's row read by whatever that task became -- it cannot detect a per-task derivation error, which is the defect class D exists for. Here process B reads process A's row while A is alive and blocked. Neither runs the other's code and the only channel between them is the executive's table. A = PAYROLL [7,3] TMPMBX|NETMBX|SYSPRV, B = AUDITOR [7,9] TMPMBX|NETMBX. Same UIC group on purpose: per the oracle (section 5.2) a same-group $GETJPI needs no privilege, so the read cannot be explained by B being privileged. Sequenced by blocking pipe reads in both directions, never by sleeps -- the parent does not release A until B has reported, so "A was alive" is ordering, not timing. Measured: B_SELF=AUDITOR/00070009/...108000, B_READ=PAYROLL/00070003/...10108000. DISCRIMINATION: making VMS_JPI_SEL_PID resolve to the caller -- the per-process facade shape -- gives B_READ=AUDITOR and turns exactly that one assertion red. Also corrected: scenario A's assertion used to end "and the drop is one-way", which was false as a product property. It now claims only the narrow true thing. And the three whole-mask assertions move off the deleted Privileges: line onto the oracle-pinned Authorized privileges: grid. Final: QEMU 17 suites / 0 failed, test_syssvc_ident 33 passed / 0 failed. Negative control still red (3 passed / 14 failed, rc=1). ctest 40/40, 0 skipped. * vms-2b8: the VMS tree was installed as root, so no VMS user owned anything REPRODUCTION, on the real bootable image: log in as SYSTEM and $ COPY LOGIN.COM ADVPROBE.TXT %RMS-E-CRE, cannot create - ADVPROBE.TXT A user could log in and then not create a file -- in their own login directory. This is a regression introduced by round 6 of this item, and no test in the suite wrote a file, which is why it shipped. ROOT CAUSE, and it is NOT the protection check. Until LOGINOUT began dropping to the authenticated user's credentials, every VMS session on OVMX ran as Linux root, so ownership was irrelevant: root passes every DAC check. PID 1 installs the whole VMS tree with mkdir()/copy as root, so the instant a session genuinely became UIC [1,4] there was nothing on the system that the SYSTEM account owned, and the Linux kernel refused the open(2). DCL's COPY does not even go through RMS -- src/vmsdcl/dcl_cmd_file.c calls fopen() directly -- so the VMS protection check never ran on this path at all. PROVEN BY MUTATION, both directions, each a full image rebuild and boot: - delete provision_ownership() + the home-directory own_object() below: the UAT goes 23/7 with EXACTLY the seven new write assertions red and `%RMS-E-CRE, cannot create - UATWRITE.TXT` back on the console. - the same mutant PLUS vms$check_access() rewritten to `return 1` unconditionally (grant everything): identical failure, same seven assertions, same messages. The RMS protection check has nothing to do with it; the enforcer is the Linux DAC on the inode. THE FIX, pinned to the oracle rather than chosen (CLAUDE.md Rule 10). Measured on VAX2 of ~/vax/cluster, OpenVMS VAX V7.3, 30-JUL-2026 -- full transcripts added to docs/oracle/vax73-privileges.md S7: SYSEXE.DIR;1 [SYSTEM] (RWE,RWE,RE,RE) SYSLIB.DIR;1 [SYSTEM] (RWE,RWE,RE,RE) LOGINOUT.EXE;1 [SYSTEM] (RWED,RWED,RWED,RE) SYS0.DIR;1 [SYSTEM] (RWE,RWE,RE,RE) Two facts, and they are a pair: the system tree is owned by SYSTEM, and WORLD gets R+E and no W. So on VMS the SYSTEM account can create and delete in SYS$SYSTEM: and an ordinary user cannot -- which is exactly what OVMX now does. provision_ownership() gives [SYS0] and everything under it to SYSTEM's UIC, and provision_sysuaf_users() gives each account's login directory to that account's UIC. WHICH UIC IS NOT HARDCODED. It is read out of SYSUAF's SYSTEM record -- the same record LOGINOUT authenticates against and the same one establish_system_identity() hands to the executive. If those disagreed, file protection would be enforced against a different identity than the executive holds, which is the defect this whole item exists to delete. The numeric value of [SYSTEM] was not pinned on the oracle (DIRECTORY/OWNER prints the translated identifier name), and nothing here depends on it. lchown() and not chown(): in overlay mode SYS$SYSTEM holds symlinks into /usr/local/bin and re-owning a symlink must not re-own the Linux binary. DISCLOSED DIVERGENCE, not handled: VMS grants the SYSTEM category to every UIC group <= MAXSYSGROUP (measured 8). A Linux inode carries one owning group, so OVMX can express "group 1 owns this" but not "groups 1..8 are all system". SYSUAF ships no account in groups 2..8, and inventing a second enforcement layer to paper over it would be worse than the gap. * vms-2b8: LOGINOUT does not re-own SYS$LOGIN, so it cannot fail to Round 6 deleted %LOGINOUT-W-NOIDENT on the grounds that VMS has no state in which LOGINOUT authenticates a user and then hands them a broken session, so the condition must be made unreachable rather than handled -- and then added %OVMX-W-LOGINOWN forty lines above it, a new non-fatal self-certified diagnostic for the case where LOGINOUT cannot give the user their own SYS$LOGIN. That is the same shape and the same defect: CLAUDE.md Rule 10's illegal third answer, a plausible-looking handler for a condition VMS never faces. Under the [gid,uid] protection this branch's credential drop activates, a session that reaches that warning cannot write its own login directory -- it is the same bug as the %RMS-E-CRE regression, wearing a warning instead of an error. The argument that killed NOIDENT is applied here: the condition is REMOVED, not promoted to fatal. LOGINOUT no longer creates or re-owns the directory at all. That is also what OpenVMS does -- the System Manager's Manual add-user procedure is AUTHORIZE ADD followed by CREATE/DIRECTORY .../OWNER=[g,m], and LOGINOUT is not in that sequence and has no fixup step of its own. On OVMX the equivalent provisioning is PID 1's provision_sysuaf_users() and provision_ownership(), which run before any other process exists. No behaviour is lost: the UAT logs in as SYSTEM and as GUEST and both write files in their own login directories, which is what the deleted mkdir/chown was there to guarantee. * vms-2b8: the SYSTEM protection category is a group test, not "uid 0" src/libvms/syssvc/sys_security.c selected the SYSTEM protection category with `if (uic == 0)`, commented "UID 0 (root) is treated as SYSTEM", in both sys$chkpro() and vms$check_access(). OpenVMS has no root and [0,0] is not a valid UIC; that rule was invented for the OVMX substrate. While every VMS session ran as Linux root it was also inert -- caller_uic 0 equalled the owner_uic 0 of every root-created file, so the owner branch would have answered the same. Once LOGINOUT drops to the authenticated UIC, SYSTEM's [1,4] stops matching it and lands on the WORLD nibble of every file in the VMS tree: OVMX denying what VMS grants. The documented VMS rule is a comparison, not an equality: the SYSTEM category covers every UIC whose GROUP is <= MAXSYSGROUP (OpenVMS Guide to System Security, "System" access category). MAXSYSGROUP measured on the oracle, transcript in docs/oracle/vax73-privileges.md S7.2: $ MCR SYSGEN SHOW MAXSYSGROUP MAXSYSGROUP 8 8 1 32768 UIC Group D Root's [0,0] is covered incidentally (0 <= 8) rather than by a rule of its own, so PID 1 and the daemons are unaffected. tests/libvms/test_protection.c is new and is the discriminating test: restore `uic == 0` and its first three assertions fail while every other case still passes (verified by running exactly that mutation). It pins both sides of the MAXSYSGROUP boundary (group 8 is system, group 9 is not) and the refusal an ordinary user gets against a system-owned file, so the change cannot be mistaken for a blanket grant. THIS IS NOT WHAT FIXED THE %RMS-E-CRE REGRESSION, and it is committed separately so that is not misread. The regression is a Linux DAC refusal on an inode nothing in VMS owned; rebuilding the image with vms$check_access() returning 1 unconditionally reproduces it unchanged. This commit removes a different defect -- an invented rule sitting where a documented one belongs. NOT ADDED, deliberately: SYSPRV, BYPASS, READALL and GRPPRV. VMS grants the SYSTEM category to SYSPRV holders, everything to BYPASS and read to READALL, but on OVMX the decision this function feeds is re-taken immediately afterwards by the Linux kernel's DAC check on the same inode, which has no notion of a VMS privilege and denies what the privilege term would have granted. Implementing them would produce a function that reports enforcement it does not have -- which this item's own text calls out as worse than an absent one. Reported instead. * vms-2b8: the UAT never wrote a file, which is why the regression shipped All sixteen commands the UAT drove were read-only except DEFINE, which is an in-memory process logical. So a change that made every VMS user unable to create a file went green. This adds the missing half and a second session to make it discriminating. SESSION 1 (SYSTEM) now copies a file into its own default directory and into SYS$SYSTEM:, reads both back, deletes the SYS$SYSTEM: one, and checks it is gone from the directory listing. SESSION 2 IS NEW: log out and log in again as GUEST, the least privileged account SYSUAF ships (UIC [200,201], TMPMBX only). Without it the file assertions are not evidence of an access control system -- a system where EVERYONE can write SYS$SYSTEM: passes every assertion session 1 makes. GUEST must be able to read the system tree and write its OWN login directory, and must NOT be able to write SYS$SYSTEM:; all three are asserted, because any one alone is satisfied by a broken system. DISCRIMINATION VERIFIED by mutation on the real bootable image: deleting the ownership provisioning takes the UAT to 23 passed / 7 failed with exactly the seven write assertions red. The GUEST refusal stays green in that mutant, which is correct -- it is not what the fix changes. EXISTENCE IS PROVEN BY TYPE, NOT DIRECTORY. Measured while writing these assertions: `DIRECTORY <one named file>` answers "Total of 0 files" even for a file that certainly exists (`DIRECTORY SYS$MANAGER:LOGIN.COM` -> 0 files, while `TYPE SYS$MANAGER:LOGIN.COM` prints it). That is a real DCL defect, reported separately; a test leaning on it would be asserting against a broken observer. Nor is anything asserted on OVMX's error text -- OVMX prints %RMS-E-CRE where VMS prints %COPY-E-OPENOUT with -RMS- and -SYSTEM- secondaries, and asserting the OVMX wording would certify a message this item never measured (Rule 10). KNOWN DIVERGENCE NOW ASSERTED OUT LOUD (vms-afd). `SPAWN SHOW PROCESS` prints "User:" followed by nothing: vms_module.c zeroes proc->username at registration and inherits nothing, so every spawned process in the product reports blank, while the oracle's SPAWN answers "%DCL-S-SPAWNED, process SYSTEM_1 spawned" -- VMS has no process without a user name. The two UIC assertions round 6 added read that same display and said nothing about it. The fix is $CREPRC identity propagation (vms-afd, entangled with vms-8019) and is not built here, but the blank is now PINNED by an assertion, so the day SPAWN inherits a name this line goes red and whoever lands it has to come and delete it. Silence is what makes a facade survive. Two harness fixes found by running it: - the session-2 waits are anchored to the offset captured BEFORE 'LOGOUT' is sent, not after. PID 1 prints the next 'Username:' in the same breath as the logout message, so an offset taken after wait_for 'logged out' returns (it polls at 250ms) has already skipped past the prompt, and the wait times out with the prompt in plain sight. Measured, not reasoned. - SESSION_TIMEOUT is computed from the actual length of the two command arrays instead of a hand-maintained "* 16" that drifts on every edit. * vms-2b8 round 3: fix privilege-surface disagreement, a vacuous protection test, a false cardinal, and an unrun UAT rewrite Three high-severity defects found by execution (see the dispatch report): 1. F$PRIVILEGE (src/vmsdcl/dcl_lexical.c) answered from the executive's RAW cur_privs, unmasked, while SHOW PROCESS/PRIVILEGES was already masked to VMS_PRV_M_ENFORCED (operator ruling 2026-07-31). Measured: a SYSTEM session authorized OPER by SYSUAF showed empty privilege blocks in SHOW PROCESS/PRIVILEGES while F$PRIVILEGE("OPER") answered "TRUE" in the same process -- OVMX advertising a privilege it cannot enforce, through a surface DCL scripts branch on. lex_privilege() now masks to VMS_PRV_M_ENFORCED too. Enumerated every privilege-reporting surface by measurement (grep across src/vmsdcl, src/libvms, tools/, src/vmsssh): SHOW PROCESS/PRIVILEGES (already correct), F$PRIVILEGE (fixed here), F$GETJPI (no CURPRIV item implemented at either the DCL lexical or sys$getjpi C level -- nothing to report), SET PROCESS/PRIVILEGES's feedback (prints nothing on success; the command never reaches the executive at all, a separate, already-tracked gap). Proven end-to-end on a real QEMU boot: IDENT_OPER = "FALSE" and IDENT_SETPRV = "TRUE" for the same SYSTEM session whose SHOW PROCESS/PRIVILEGES lists only CMKRNL/CMEXEC/SETPRV/WORLD. 2. tests/libvms/test_protection.c could not fail on the behaviour it exists to prove: reverting uic_is_system() to the pre-fix `uic == 0` left it byte-identically green, because sys$chkpro reads the CALLER's real (gid,uid) with no way to parameterize it, and no UID ctest actually runs as (root, or an unprivileged dev/CI user) ever lands in the (0, MAXSYSGROUP] band where the two rules disagree. Rewritten to synthesize a caller UIC via an unprivileged Linux user namespace (CLONE_NEWUSER + a single-line uid_map/gid_map, "deny" on setgroups) -- the same mechanism `unshare --user --map-root-user` uses, requiring no capability the test process does not already have. Proven by the isolating mutation this round: reverting uic_is_system() turns exactly the two group-5 SYSTEM-category assertions red and nothing else; reverting the fix back to the correct rule turns the suite green again. A failed CLONE_NEWUSER setup is now a hard FAILURE, not a silent skip (a skipped test is a failing test). 3. Deleted the false cardinal in src/kernel/vms_proctab.c's VMS_IOCTL_SETIDENT comment ("the number of root-privileged VMS processes ... It is two: PID 1 and pre-authentication LOGINOUT") -- the same commit that wrote it added VMSSSHD as a third. Replaced with a description of the SET and a pointer to the grep that derives its current membership, instead of a count nothing forces to stay in sync. Four medium defects: - A provenance comment in tests/integration/test_show_device_rows.sh cited a _Static_assert value for VMS_IOCTL_GETDVI (0xC0505752) that does not exist anywhere in the tree; the real value, still asserted in src/kernel/vms_ioctl.h, is 0xC0585652 (two digits transposed). Corrected to the real value. - tests/uat/vms_session_qemu.sh was substantially rewritten and not run. Built distro/Dockerfile.bootable via podman and ran it against a real QEMU boot: 32/34 passed, 2 failed. Both were genuine, both fixed here: (a) the check_known_divergence tripwire for vms-afd (empty User: field) fired red because the top-level LOGINOUT session's identity is now populated -- but `rd show vms-afd` confirms that item is still open; the fix is LOGINOUT's own vms_kif_setident() call (this item), unrelated to vms-afd's $CREPRC gap, which the SPAWN assertions in the same script still correctly prove is open. Replaced the tripwire with a positive assertion and an accurate provenance comment. (b) SHOW PROCESS /PRIVILEGES's expected pattern, '(TMPMBX|NETMBX|OPER)', predated the VMS_PRV_M_ENFORCED masking and could never match the corrected output; corrected to '(CMKRNL|CMEXEC|SETPRV|WORLD)'. Added F$PRIVILEGE("OPER") / F$PRIVILEGE("SETPRV") assertions to the UAT to cover defect 1's fix on the real runtime. Rebuilt and reran: 36/36 passed. - MAXSYSGROUP=8 was pinned only by a transcript this same branch introduced (`git log --all -S MAXSYSGROUP` shows no earlier mention). Attempted an independent re-capture this round; the ~/vax/cluster lab was live under a real 3-node cluster join experiment for vms-760 (ps aux showed VAX1/2/3 all running, SCSD/tcpdump active), so driving any console by hand was avoided per the read-only-probes-only constraint. Disclosed the single-source gap in sys_security.c's comment instead of silently relying on it; flagging for the orchestrator to re-verify once the lab is free. - src/libvmssys/vms_kif.h's vms_kif_setident OVMX-UNWIRED declaration was already deleted on this branch (LOGINOUT and PID 1 both call it now) and not re-added. Ran the standing census gate: tests/integration/test_kif_caller_census.sh (43 entry points, 27 reached from the product, 16 declared unwired) PASS; its negctl (23 controls) PASS. Full proof set re-run on this branch: host ctest 49/49 (one terminal_identity_negctl timeout on the first pass was host CPU contention from concurrent podman/QEMU builds on a shared machine -- reproduced green in isolation at 103s against a 120s budget, and green again on a full clean re-run); QEMU kernel-module harness (podman, aarch64, pure TCG, no KVM) ALL KERNEL MODULE TESTS PASSED; full facility negative-control sweep (tests/qemu/run_facility_negctl.sh) 22/22 passed, including the two vms-2b8-specific defects (ident-username-unguarded, bind-client-no-register); Rule 9 runtime-target gate PASS. Not fixed here, confirmed still true and left for tracking: SSH login (src/vmsssh/vmssshd.c) establishes identity but does not perform LOGINOUT's Linux credential drop for the session that follows (flagged loudly in-code by an earlier round; needs its own item to port the drop against a real SSH session). SET PROCESS/PRIVILEGES never reaches the executive -- it only mutates the DCL-local ctx->privileges, so it cannot actually grant or revoke anything the executive enforces. Stale prose in several src/imgact/test/*.sh and src/vmslink/mk_vmsrms_shr.sh comments still describes vms$check_access as a live LIBVMS$SHR export; confirmed the symbol is genuinely gone (nm -D against the built shared lib), and confirmed the stale mentions are all in echo/comment text, not functional checks, so nothing is broken -- just misleading log output. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * vms-2b8 round 4: fix the F$PRIVILEGE/SET PROCESS desync, pin MAXSYSGROUP to a second source, delete a false SYSUAF claim, harden the UAT harness CRITICAL, defect 1 was NOT actually fixed by round 3 (measured on the real QEMU runtime, not inferred from the round-3 diff): $ SHOW PROCESS/PRIVILEGES -> Authorized: CMEXEC CMKRNL SETPRV WORLD $ BEFORE = F$PRIVILEGE("SETPRV") -> "TRUE" $ SET PROCESS/PRIVILEGES=(OPER) $ SHOW PROCESS/PRIVILEGES -> UNCHANGED (reads the executive directly) $ AFTER = F$PRIVILEGE("SETPRV") -> "FALSE" <-- same process, same moment Round 3 masked F$PRIVILEGE (dcl_lexical.c's lex_privilege()) to VMS_PRV_M_ENFORCED, which fixed the OVER-claim direction it measured (OPER reading TRUE when SHOW PROCESS/PRIVILEGES correctly omits it) but left the function reading ctx->privileges -- a DCL-local cache SET PROCESS/PRIVILEGES (dcl_cmd_set.c's cmd_set_process()) REPLACES outright with whatever was asked for, with no connection to the executive. A single unrelated SET PROCESS/PRIVILEGES call therefore silently discarded SETPRV/CMKRNL/CMEXEC/ WORLD -- privileges the executive still genuinely enforced -- producing a live UNDER-claim contradiction between F$PRIVILEGE and SHOW PROCESS/PRIVILEGES about the same process at the same instant. The same bug also self-inflicted lockouts: a second SET PROCESS/PRIVILEGES call could fail its own SETPRV gate because the first call had clobbered the cache the gate reads. Fix: F$PRIVILEGE now asks the executive fresh on every call (vms_kif_getjpi_self(), the same source SHOW PROCESS/PRIVILEGES reads), never ctx->privileges -- the two surfaces cannot disagree by construction regardless of what SET PROCESS/PRIVILEGES does locally. SET PROCESS/PRIVILEGES itself no longer mutates ctx->privileges at all and no longer claims a success it cannot deliver: it prints %OVMX-W-NOSETPRV and leaves state untouched, because actually reaching the executive is vms_kif_setprv()'s job (src/libvmssys/vms_kif.c), which is deliberately left OVMX-UNWIRED in vms_kif.h pending vms-pv1 -- this round was instructed not to touch that declaration or the in-flight vms_kif caller census (work/vms-7fb-r7), so this is Rule 10's HIDE answer, not MATCH, until vms-pv1 lands. Also interrogated and fixed: F$GETJPI("","CURPRIV") silently returned "0" (the unrecognized-item fallback) -- indistinguishable from "this process holds no privileges", which was false for every session on this runtime. Implemented for real, reading the same live source, masked the same way. SHOW PROCESS/ALL and SET PROCESS/PRIVILEGES's feedback were both interrogated on the real runtime (session pasted in the dispatch report); SHOW PROCESS/ALL prints no privilege data at all, so it cannot contradict anything. Proof, before and after, real QEMU boot (podman, aarch64, pure TCG, no KVM), same commands both times: BEFORE: SHOW PROCESS/PRIVILEGES unchanged after SET .../PRIVILEGES=(OPER); F$PRIVILEGE("SETPRV") and F$PRIVILEGE("WORLD") both flip to FALSE. AFTER: SET PROCESS/PRIVILEGES=(OPER) prints %OVMX-W-NOSETPRV; SHOW PROCESS/PRIVILEGES unchanged; F$PRIVILEGE("SETPRV") and F$PRIVILEGE("WORLD") both stay TRUE. TWO HIGHS ON THE MAXSYSGROUP CONSTANT, one problem: (a) The lab (~/vax/cluster) was mid-experiment again this round (vms-760's 3-node cluster join; `ps aux` showed VAX1/2/3 live) so no second capture was possible -- read-only-probes-only was honored. Corroborated the existing lab transcript against the VSI OpenVMS Wiki instead (https://wiki.vmssoftware.com/UIC_Protection, fetched 31-JUL-2026): MAXSYSGROUP is octal 10 by default, i.e. decimal 8 -- the same value, from a source independent of both this tree and the lab. Two sources agreeing, neither derived from the other, is a pin; the round-3 version was one branch citing its own capture, which is not. (b) tests/libvms/test_protection.c could not distinguish MAXSYSGROUP=8 from a neighbour such as 5: its only cases were group 5 (inside (0,5] AND (0,8]) and group 9 (outside both), so a boundary anywhere in [5,8] passed identically. Added a case at the boundary itself (group == MAXSYSGROUP, granted) that only a real boundary of 8 satisfies. Proven by mutation: changing OVMX_MAXSYSGROUP to 5 turns exactly this one assertion red (8/8 -> 7/8), leaving the group-5 and group-9 cases unchanged; reverting restores 8/8. ONE MEDIUM: tests/uat/vms_session_qemu.sh's comment claimed "SYSTEM's SYSUAF-authorized mask is exactly the enforced set" -- false. Measured: distro/rootfs/vms/SYS0/SYSCOMMON/SYSEXE/SYSUAF.DAT's SYSTEM row authorizes the literal string ALL (37 privileges); the four enforced names are what survives the VMS_PRV_M_ENFORCED intersection of that much larger mask, not the whole of what SYSUAF grants. Comment corrected. ONE HYGIENE: filed vms-475 for the vmsssh credential-drop gap (vmssshd establishes VMS identity for real but does not perform LOGINOUT's Linux setuid/setgid drop, so an SSH session's Linux-layer credentials can disagree with its VMS-layer identity for file-protection purposes). UAT HARNESS HARDENING: tests/uat/vms_session_qemu.sh keyed CMD_OUTPUT by command TEXT, so a second occurrence of the same command anywhere in the script silently overwrites the first's stored output with no signal either way -- and this round's own new assertions would have collided with the pre-existing 'SHOW PROCESS /PRIVILEGES' occurrence. Added CMD_OUTPUT_SEQ / CMD_SEQ_LABEL (populated by run_cmd() alongside the existing CMD_OUTPUT) and check_response_at(), which asserts by call position instead of by text. Used it both for the new post-SET-PROCESS assertions and to re-anchor the PRE-EXISTING 'SHOW PROCESS /PRIVILEGES' check, whose index is found programmatically (not hand-counted) before the new duplicate is appended. The one pre-existing intentional collision ('SHOW DEFAULT', checked only at its documented second occurrence) is left as is. RAN: host ctest 49/49 (twice, clean); QEMU kernel-module harness (podman, aarch64, pure TCG, no KVM) ALL KERNEL MODULE TESTS PASSED; Rule 9 runtime-target gate PASS; vms_kif caller census 43 entry points / 27 reached / 16 unwired, unchanged from round 3 (no vms_kif.h edits, no new vms_kif_* callers -- only vms_kif_getjpi_self(), already wired); UAT 39/39 (36 pre-existing + 3 new). Facility negative-control sweep (tests/qemu/run_facility_negctl.sh) not re-run this round: it exercises src/kernel/* and vms_kif.h, neither touched here, and was independently reproduced green from a clean archive per the dispatch report. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * vms-2b8 round 5: DCL privilege GATES now consult the same enforced mask as the reporting surfaces; F$GETJPI CURPRIV/AUTHPRIV pinned to VMS's real string format; MAXSYSGROUP hygiene; false emphatic claims removed DEFECT 1, STILL OPEN AT r4, NOW NAMED CORRECTLY: measured on the real QEMU runtime, in the SYSTEM session -- $ IDENT_ALTPRI = F$PRIVILEGE("ALTPRI") $ SHOW SYMBOL IDENT_ALTPRI -> IDENT_ALTPRI = "FALSE" $ SET PROCESS/PRIORITY=6 -> AUTHORIZED (no error) -- the report said ALTPRI was not held, and the gate granted the operation that requires it, same process, same instant. Root cause: SYSUAF's SYSTEM record authorizes ALL privileges, and VMS_IOCTL_SETIDENT sets cur_privs = the full authorized mask verbatim for a SETPRV-holding caller (OVMX design choice, vms_proctab.c), so ctx->privileges (dcl_main.c) genuinely contains ALTPRI/SYSPRV/BYPASS even though F$PRIVILEGE and SHOW PROCESS/PRIVILEGES correctly mask them out of what they REPORT (VMS_PRV_M_ENFORCED). Every privilege GATE in src/vmsdcl/dcl_cmd_set.c (SET PROCESS/PRIORITY, SET PROCESS/PRIVILEGES, SET UIC, SET TIME) read the raw, unmasked value. Fix: one new function, enforced_privs_held(), asks the executive fresh and masks to VMS_PRV_M_ENFORCED -- the exact source and mask F$PRIVILEGE and SHOW PROCESS/PRIVILEGES already use. All four gates now call it instead of reading ctx->privileges. Consequence, stated not hidden: ALTPRI/SYSPRV/BYPASS/OPER are authorized by SYSUAF but not yet enforced anywhere in this tree (vms_kif_setprv is OVMX-UNWIRED pending vms-pv1), so operations gated on them alone are now refused for every identity until that lands -- Rule 10's HIDE answer applied to the gate, not just the display. PROOF, real podman-built QEMU boot, same session, after the fix: $ IDENT_ALTPRI = F$PRIVILEGE("ALTPRI") $ SHOW SYMBOL IDENT_ALTPRI -> IDENT_ALTPRI = "FALSE" $ SET PROCESS/PRIORITY=6 -> %SET-E-NOPRIV, no privilege for SET PROCESS /PRIORITY Report and gate agree. New UAT assertions check this BY POSITION (check_response_at), consistent with the rest of this defect's block. TWO F$GETJPI SURFACES FIXED, both pinned to public OpenVMS documentation (HP/VSI DCL Dictionary + VSI Wiki's F$GETJPI example), not invented: - CURPRIV returned a DECIMAL INTEGER with no citation. OpenVMS returns a comma-separated privilege-NAME string, in ascending bit position (CMKRNL before CMEXEC -- the oracle's own example order, NOT the alphabetical order SHOW PROCESS/PRIVILEGES uses for a different command). Fixed to match, masked to VMS_PRV_M_ENFORCED like every other surface. - AUTHPRIV silently returned "0" -- indistinguishable from "holds no privileges", false for this session. Implemented for real, reading perm_privs (the authorized mask) through the same format. Proof, same real session: $ IDENT_CURPRIV = F$GETJPI("","CURPRIV") $ SHOW SYMBOL IDENT_CURPRIV -> "CMKRNL,CMEXEC,SETPRV,WORLD" $ IDENT_AUTHPRIV = F$GETJPI("","AUTHPRIV") $ SHOW SYMBOL IDENT_AUTHPRIV -> "CMKRNL,CMEXEC,SETPRV,WORLD" MAXSYSGROUP HYGIENE: the value was hand-maintained in two places (sys_security.c's comment/#define and test_protection.c's own copy). Collapsed into ONE definition, src/libvms/include/ovmx_secparam.h, included by both. SELF-CAUGHT REGRESSION in this same round: aliasing the test's MAXSYSGROUP straight to the shared symbol made the "boundary itself" test self-referential (it would pass for ANY boundary value) -- verified by mutation (changing the header's value to 9 left that assertion green, only the independently-hardcoded group-9 case caught it). Fixed with a `_Static_assert(OVMX_MAXSYSGROUP == 8, ...)`, the same pattern this codebase already uses for other oracle-pinned constants -- verified this actually fails the build when mutated, then reverted. FALSE EMPHATIC CLAIMS REMOVED (CLAUDE.md Rule 10 / method 5 audit): - sys_security.c/test_protection.c: "granted ONLY if the boundary is really 8" for the boundary-alone case -- overclaimed what a single case (without its pairing with the group-9 negative control) proves; corrected to state what each case actually pins. - dcl_lexical.c: "false for every session on this runtime ... never empty" for CURPRIV -- false: a process registered without CAP_SYS_ADMIN gets perm_privs = cur_privs = 0, so CURPRIV would legitimately be empty. Removed; replaced with a stated, not "never", consequence. - dcl_lexical.c: "so the three cannot disagree by construction" -- removed along with the surrounding decimal-integer implementation it was defending. TWO SMALLER FIXES: - tests/uat/vms_session_qemu.sh: IDX_PRIV_ORIGINAL left at -1 (anchor not found) would make check_response_at silently retarget to the LAST command via bash's negative array indexing instead of failing. Now exits the harness loudly if the anchor search comes up empty. - %OVMX-W-NOSETPRV printed as a Warning while the function returns SS$_NORMAL (a success status) -- VMS's severity/success-bit convention disagreed with itself. Changed to %OVMX-I-NOSETPRV, matching the informational + SS$_NORMAL pairing this file already uses elsewhere (cmd_set_host, cmd_set_audit). RAN: host ctest 49/49 (three times across this round, clean each time); real podman-built QEMU boot UAT 43/43 passed (39 pre-existing + 4 new: ALTPRI/PRIORITY gate-agreement pair, CURPRIV/AUTHPRIV format pair); vms_kif caller census 43/27/16 unchanged (no vms_kif.h edits, no new vms_kif_* callers); Rule 9 runtime-target gate PASS. Facility negative-control sweep not re-run: exercises src/kernel/* and vms_kif.h, neither touched this round either. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * vms-2b8 round 6: positive control for the privilege gate, derived enforced-privilege names, honest SET TIME regression disclosure Fixes five findings against work/vms-2b8-r5 (f7bc58c). The central fix (DCL privilege GATES consulting the same enforced mask as the reporting surfaces) is unchanged and NOT redone. 1. [HIGH] POSITIVE CONTROL FOR THE GRANT PATH. Every existing proof showed enforced_privs_held() REFUSING an operation (ALTPRI/PRIORITY); none showed it GRANTING one, so a gate that consults nothing and refuses unconditionally would have passed every assertion in the file exactly as well as a correct gate. Added: SET PROCESS/PRIVILEGES=(OPER) (gated on the SAME function, SETPRV||SYSPRV||BYPASS -- SETPRV IS enforced) must NOT print NOPRIV for the SYSTEM session, which holds SETPRV. VERIFIED NON-VACUOUS BY MUTATION, real podman-built QEMU boots, both directions: enforced_privs_held() forced to `return 0` (always-refuse) -> 45/2, both failures on the new grant checks, deny checks stayed green; forced to `return ~(uint64_t)0` (always-grant) -> 45/2, the PRIORITY deny check went red, grant checks stayed green. Each mutation flips exactly the assertion class it should and no other. 2. [HIGH] enforced_priv_names[] DERIVED, NOT HAND-MAINTAINED. Replaced dcl_lexical.c's local 4-entry table (kept in sync with VMS_PRV_M_ENFORCED "by hand") with a walk over bit positions 0..63 against vms_priv_names[] -- the SAME canonical name table SHOW PROCESS/PRIVILEGES already reads, now exposed via dcl/dcl_cmd.h instead of being file-local to dcl_cmd_show.c. A bit added to VMS_PRV_M_ENFORCED gets a name with no second edit, and the walk order is ascending bit position for free (matching the oracle's CURPRIV example), not the alphabetical order SHOW PROCESS/PRIVILEGES uses. VERIFIED BY MUTATION: added VMS_PRV_M_TMPMBX to VMS_PRV_M_ENFORCED (the only edit), rebuilt, re-booted -- CURPRIV/ AUTHPRIV both came back "CMKRNL,CMEXEC,SETPRV,TMPMBX,WORLD", TMPMBX correctly slotted between SETPRV and WORLD. Reverted after confirming. 3. [MEDIUM] SET TIME REGRESSION, DISCLOSED. OPER/SYSPRV/BYPASS are all outside VMS_PRV_M_ENFORCED, so SET TIME's gate can no longer be passed by ANY identity -- a real behaviour change from before round 5 (SYSTEM's raw ctx->privileges used to let it through), left undisclosed and untested. Measured: SYSTEM (SYSUAF-authorized for ALL 37 privileges) is refused. The error message now says so instead of reading like "your account needs OPER, go get it", which is false on this build. New UAT assertion pins the refusal to the gate's own disclosure text, not a bare NOPRIV substring -- an always-grant mutation run found that bare NOPRIV was ALSO satisfied by a second, unrelated failure path (settimeofday(2) EPERM once the gate passes, since this session runs as UID 4 post-drop, not root), which would have made the original assertion vacuous under exactly the mutation meant to test it. Narrowed to the gate-specific text; reran and confirmed it now discriminates. 4. [MEDIUM] SEVERITY FIX COVERAGE. %OVMX-I-NOSETPRV (not %OVMX-W-) is now asserted directly, both presence of the I- form and absence of the W- form. 5. [MEDIUM/hygiene] Deleted the "only 4 of the 37 real VMS names" cardinality (a count that would drift the moment #2's derivation makes the enforced set anything other than 4) and the AUTHPRIV-reads- perm_privs emphatic that read as proven when it is not testable on this runtime: cur_privs and perm_privs are always equal here (VMS_IOCTL_SETIDENT sets them equal, and no DCL caller reaches $SETPRV yet), so no UAT assertion checking CURPRIV and AUTHPRIV render the same string can distinguish "AUTHPRIV correctly reads perm_privs" from "AUTHPRIV reads cur_privs by mistake". Replaced with an explicit statement of that limitation and what would resolve it (vms-pv1's $SETPRV wiring). RAN: host ctest 49/49 (0 skipped); real podman-built QEMU boot UAT 47/47 passed (twice, to rule out a flake), including all four new assertion groups. Facility negctl / kif caller census / runtime-target gate all included in the 49 (unchanged, no vms_kif.h edits, no new vms_kif_* callers, no census interaction). Host: aarch64, podman, pure TCG, no KVM. NOTED, NOT FIXED (out of scope for this item, pre-existing): (a) 'SET UIC [777,777]' on this runtime hit a DCL parser bug -- the bracket comma is treated as a parameter separator regardless of context, yielding %SET-E-IVUIC -- unrelated to the privilege gate and not touched; the grant proof uses SET PROCESS/PRIVILEGES instead. (b) One run of the always-grant mutation showed a single unrelated flake on the pre-existing 'SHOW LOGICAL UAT_TEST' assertion (host under heavy concurrent podman/QEMU load from other agents); did not reproduce on either surrounding run. Flagged for the orchestrator as test-flaky rather than silently dismissed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * vms-2b8 round 7: delete the false per-caller SYSUAF claim from SET TIME's NOPRIV message, symmetrize SET PROCESS/PRIORITY's wording, remove the re-introduced '37' cardinal, and make the derived privilege-name lookup loudly fail on a coverage gap Four defects from the round-6 adversary review, each with its own isolating fix and proof: 1. FALSE CLAIM SHIPPED TO THE CONSOLE (the serious one). SET TIME's %SET-E-NOPRIV text said "OPER is authorized by SYSUAF but not yet enforced" -- true only for SYSTEM and OPERATOR, and false for GUEST, DEFAULT, USER1, USER2 (four of the six shipped accounts), which the adversary demonstrated by driving SET TIME from a GUEST session (SYSUAF: TMPMBX only). The code path never reads the caller's SYSUAF record at all -- it only reads the compile-time-fixed VMS_PRV_M_ENFORCED mask, so the claim was invented, not derived. The per-caller clause is deleted; the message now says only what is true for every caller: this privilege is not enforced on this system. 2. ASYMMETRIC FIX. SET PROCESS/PRIORITY is refused by the identical permanent, compile-time-fixed gate (ALTPRI not in VMS_PRV_M_ENFORCED) but still printed the bare pre-round-6 message. It now gets the same HIDE wording as SET TIME, for the same reason. 3. THE FORBIDDEN '37' CARDINAL, reintroduced three times by round 6 after being deleted once (dcl_cmd_set.c's SET TIME comment, and two comments in tests/uat/vms_session_qemu.sh). All three said "ALL 37 privileges"; replaced with "privilege ALL", the literal SYSUAF field value, dropping the invented count. 4. SILENT COVERAGE GAP. F$GETJPI CURPRIV/AUTHPRIV derives its privilege names by walking VMS_PRV_M_ENFORCED and looking each set bit up in vms_priv_names[]; a bit with no row was silently omitted from the rendered string with no diagnostic. The lookup now tracks whether each set bit was found and aborts with a loud %SYSTEM-F-BUGCHECK diagnostic if not, instead of returning an incomplete privilege list that reads as correct. PROVEN BY MUTATION, not by inspection: temporarily OR'd an unnamed bit (1ULL << 40, no vms_priv_names[] row) into VMS_PRV_M_ENFORCED (src/kernel/vms_ioctl.h), rebuilt the bootable image (podman, aarch64, pure TCG, no KVM), and ran the UAT. SYSTEM's SYSUAF authorizes ALL (all 64 bits), so the very first `IDENT_CURPRIV = F$GETJPI("","CURPRIV")` fired the check: %SYSTEM-F-BUGCHECK, internal consistency failure -- VMS_PRV_M_ENFORCED bit 40 has no row in vms_priv_names[] (vms-2b8 round 7 coverage check) DCL aborted and the login loop respawned a fresh Username: prompt; every command before that point (SET PROCESS/PRIVILEGES, SHOW PROCESS/PRIVILEGES, F$PRIVILEGE, SET PROCESS/PRIORITY with its new symmetric message) behaved identically to the unmutated run -- isolating this mutation to exactly the coverage-check property. The mutation was reverted before this commit; vms_ioctl.h is unchanged from round 6. RAN on the final (reverted) tree: host ctest 49/49; UAT (podman, aarch64, real /dev/vms under QEMU) 47 passed / 0 failed. Does not touch src/libvmssys/vms_kif.h or the vms_kif caller census (in flight on work/vms-7fb-r7, per dispatch instruction). * vms-2b8 round 8: replace the false %SYSTEM-F-BUGCHECK claim with an OVMX-facility diagnostic, delete a false 'only' and the last two '37' cardinals, and give the round-7 coverage check automated coverage ONE genuine defect from the round-7 adversary review, plus three smaller ones, each with its own isolating fix and proof. Three of the four previously-named defects (the SET TIME NOPRIV wording, SET PROCESS/ PRIORITY symmetry, the coverage check's existence) were already correct on r7 and are untouched. 1. FALSE STATUS CLASS SHIPPED TO THE CONSOLE. Round 7's coverage-check guard (F$GETJPI CURPRIV/AUTHPRIV's derived-name lookup) printed "%SYSTEM-F-BUGCHECK, internal consistency failure" for an unnamed VMS_PRV_M_ENFORCED bit -- a real VMS status/facility/severity shape for a condition VMS itself can never face (VMS has exactly one privilege table; the desync is two OVMX C files disagreeing at compile time). Rule 10 gives two legal answers and this took neither: it is not "VMS grants this but OVMX doesn't enforce it" (nothing to MATCH -- that is the separate SET TIME/SET PROCESS PRIORITY defect, already fixed), and it is not a real "refused privileged operation" status either, so reusing one would itself be a lie. That leaves HIDE, and round 7 chose HIDE's wrong shape (invent a plausible VMS status) instead of its right one (report it as what it is). Fixed: the diagnostic now reads "%OVMX-F-TABLEDESYNC, internal build defect, not a VMS condition -- ...", the same OVMX-facility convention %OVMX-I-NOSETPRV already uses, so it cannot be mistaken for genuine VMS console output. Still aborts -- this is still not a condition the caller can recover from. 2. FALSE 'only' IN dcl_cmd_set.c, load-bearing for the very SET TIME message round 7 corrected: "this code path never reads the caller's SYSUAF record at all -- it only reads the compile-time-fixed VMS_PRV_M_ENFORCED mask" is false -- enforced_privs_held() also reads the executive's live cur_privs (per-identity state), it just never reads SYSUAF. Reworded without the superlative. 3. THE FORBIDDEN '37' CARDINAL, still present in two comments round 7 did not touch (tests/uat/vms_session_qemu.sh's SYSUAF-row comment, test_syssvc_ident's/vms_login.c's "with all 37 privileges") plus two more found by grep in files round 7 never edited (test_syssvc_ident.c line 730, tools/vms_login.c). All five deleted, not corrected: the real count is 39 privileges pinned across 37 named table rows (DETACH and SETPRI deliberately unrepresented, docs/oracle/ vax73-privileges.md), so "37" undercounts what SYSUAF's ALL actually authorizes. Replaced with "privilege ALL" / "SYSUAF's privilege ALL", the literal SYSUAF field value, no invented count. 4. THE COVERAGE CHECK HAD NO AUTOMATED COVERAGE. Round 7 proved it fires by hand (temporary edit, revert) but nothing in the suite exercised it. Added tests/qemu/test_syssvc_ident.c scenario F: an isolated run_dcl() call (own script, own buffer, cannot knock on scenarios A-E) that runs F$GETJPI CURPRIV as SYSTEM/SYSUAF-ALL and checks for a marker printed after it. Registered as tests/qemu/facility_defects.sh's getjpi-curpriv-name-coverage defect (targets kernel/vms_ioctl.h, the same OR-in-bit-40 edit round 7 proved by hand). RAN END TO END, not just asserted: tests/qemu/facility_defects.sh selftest (host, no QEMU) passes with the new entry, and tests/qemu/run_facility_negctl.sh getjpi-curpriv-name-coverage (podman, aarch64, pure TCG, no KVM, real /dev/vms under QEMU) reports Facility negative controls: 3 passed, 0 failed with the observed red set EXACTLY the one assertion the manifest names, attributed to test_syssvc_ident and no other suite -- the isolation claim in the manifest's own comment, verified by execution rather than asserted. RAN on the final tree: host ctest 49/49 (includes facility_negctl_manifest and the new scenario F compiling/linking as part of the default build); the QEMU facility negative control above. Grepped this diff for only|every|never|cannot|all|exactly|37: the two remaining 'cannot' claims are compile-time/structural facts (VMS's single privilege table; scenario F's own isolated buffers), not runtime claims about caller-visible behavior, and are stated as such. Does not touch src/libvmssys/vms_kif.h or the vms_kif caller census (in flight on work/vms-7fb-r7, per dispatch instruction). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * vms-2b8 round 9: make the coverage-check desync structurally impossible at compile time, fix scenario F's vacuity, delete a false clause, and re-measure bind-client-no-register's true red set FOUR adversary findings from the round-8 review, each with its own proof. 1. [CRITICAL REGRESSION] bind-client-no-register's manifest entry was stale. Round 8 added two assertions to test_syssvc_ident scenario F, a suite this control already names in suites_red, without re-measuring the control's red set -- so the equality check in run_facility_negctl.sh failed on two unnamed reds. RE-MEASURED against real QEMU, not derived by arithmetic: running bind-client-no-register on the pre-fix tree reported these assertions went red and the manifest does NOT name them: test_syssvc_ident: F: F$GETJPI CURPRIV renders SYSTEM/ALL's... test_syssvc_ident: F: the executive accepted the SYSTEM/ALL... -- exactly the two scenario F assertions, nothing else. Both added to knock_on_fail verbatim. RE-RUN on the corrected manifest: "Facility negative controls: 3 passed, 0 failed", and the harness's own equality check reports "the red set is EXACTLY the 89 assertion(s) the manifest names". Independently reproduced a second way: a from-scratch podman/QEMU build with the same one-line mutation applied directly (bypassing the shared harness) reddened the identical 16-assertion block in test_syssvc_ident (A/B/C/D/F) and nothing else, matching byte-for-byte. 2. [HIGH, vacuity] Scenario F's assertion was satisfiable by a run where CURPRIV rendered nothing at all -- it only checked that a marker printed AFTER F$GETJPI CURPRIV, never that the call produced any content. Fixed: the script now SHOWs the symbol CURPRIV was assigned to, and the assertion requires the literal enforced-privilege string for SYSTEM/SYSUAF-ALL ("CMKRNL,CMEXEC,SETPRV,WORLD"), not merely its presence. PROVEN BY MUTATION, real QEMU: changing dcl_lexical.c's `enforced = raw & VMS_PRV_M_ENFORCED` to `enforced = 0` (CURPRIV/ AUTHPRIV always render "") reddened test_syssvc_ident alone (37 passed, 1 failed), the one failure being exactly this assertion. Reverted after confirming. 3. [MEDIUM, Rule 10] The round-7/8 coverage-check guard was a RUNTIME abort() for a condition that is 100% COMPILE-TIME DETERMINABLE: VMS_PRV_M_ENFORCED and vms_priv_names[] are both static, compile-time-constant data in the same binary, so whether every enforced bit has a name cannot vary at runtime -- there is no caller, no input, no execution path that changes the answer. A runtime handler for an already-settled compile-time fact is Rule 10's forbidden third answer. MEASURED, not assumed: the runtime abort() block is DELETED from dcl_lexical.c and replaced with a _Static_assert in src/libvms/prv_agreement.c (the file that already exists for exactly this class of guard). Negative control run for real: OR-ing an unnamed bit into VMS_PRV_M_ENFORCED now fails the BUILD at prv_agreement.c with the expected diagnostic, instead of booting and aborting a user's DCL session. Reverted after confirming. Consequence handled: the getjpi-curpriv-name-coverage QEMU negative control is deleted from facility_defects.sh -- the same mutation now fails the container rebuild, which run_facility_negctl.sh classifies as a broken harness (RUN_RC=4) for every defect, so keeping the entry would make it permanently and uninformatively red. Does not touch src/libvmssys/vms_kif.h or the vms_kif caller census. 4. [MEDIUM] dcl_cmd_set.c's SET TIME comment repeated, in reworded form, the same false clause round 7 was already corrected for: "does not read the caller's SYSUAF-authorized mask anywhere in that computation" overclaims what can be said about enforced_privs_held(). Deleted rather than reworded a third time; replaced with the plain, checkable fact that VMS_PRV_M_ENFORCED has no OPER bit at all, so the check's answer cannot depend on SYSUAF regardless of what cur_privs carries. [LOW] Resolved as a side effect of (3): the false claim that the OVMX diagnostic "is not formatted as genuine VMS console output" lived entirely inside the deleted runtime-abort comment block. [HYGIENE] "SYSPRV, BYPASS, OPER, and 30-odd others" in tests/uat/vms_session_qemu.sh replaced with "30 others (37 named rows in vms_priv_names[], minus the 4 shown, minus these 3 named)" -- the exact count, shown as arithmetic rather than an unexplained cardinal. RAN: host ctest 49/49 (0 failed); tests/qemu/facility_defects.sh selftest (host, no QEMU) green with the updated manifest; the two QEMU negative controls above, both against a real /dev/vms under QEMU (podman, aarch64, pure TCG, no KVM). Grepped this diff for only|every|never|cannot|all|exactly|37 -- every surviving instance is backed by an execution result cited in this message or in the touched file's own comment. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * vms-2b8 round 10: make the coverage-check desync a compile error in BOTH directions, not just one THE ONE DEFECT: round 9's commit message claimed the coverage-check desync was made "structurally impossible at compile time." That was false in the direction that matters. Round 9's _Static_assert checked VMS_PRV_M_ENFORCED (src/kernel/vms_ioctl.h) against a HAND-TYPED whitelist `(PRV$M_CMKRNL | PRV$M_CMEXEC | PRV$M_SETPRV | PRV$M_WORLD)` -- four names a person read off vms_priv_names[] and copied into the assert. It never looked at the array's actual contents. PROVEN (this round, on the unmodified round-9 tree, bd525fd, before any fix): deleted the WORLD row from src/vmsdcl/dcl_cmd_show.c's vms_priv_names[] -- $ sed -i '155d' src/vmsdcl/dcl_cmd_show.c # deletes the WORLD row $ cmake --build build-round9-mut -j4 # EXIT=0 $ ctest --output-on-failure # 100% tests passed, 0 failed, 49/49 -- the build and full test suite stayed green with VMS_PRV_M_ENFORCED's WORLD bit reaching lex_getjpi()'s vms_priv_names[] lookup loop with no row: the exact desync the assert claimed to prevent, arriving from the table side instead of the mask side. FIX: src/libvms/include/prv_names.h (new) holds ONE preprocessor list, VMS_PRIV_NAME_LIST, that is the single source for both consumers: - src/vmsdcl/dcl_cmd_show.c's vms_priv_names[] is now generated by expanding the list through VMS_PRIV_ROW_ENTRY -- same 37 entries, same order, same names/masks/descriptions as before (confirmed byte-for-byte via `gcc -E` on the generated array vs. the original literal; see below). - src/libvms/prv_agreement.c's coverage check now compares VMS_PRV_M_ENFORCED against VMS_PRIV_NAMES_TABLE_MASK, the OR of every mask term in the SAME list, instead of the hand-typed whitelist. There is no second, independently-maintained enumeration of "which rows exist" left for a row deletion to leave stale: deleting a line from VMS_PRIV_NAME_LIST removes that privilege from the array AND from the coverage mask in the same edit. BOTH DIRECTIONS PROVEN by execution on the round-10 tree, each reverted after confirming: Negative control 1 (bit added to VMS_PRV_M_ENFORCED, no table row -- same direction round 9 covered): OR (1ULL << 40) into VMS_PRV_M_ENFORCED (src/kernel/vms_ioctl.h) and rebuild -- prv_agreement.c:136:1: error: static assertion fai…
baron-3dl
added a commit
that referenced
this pull request
Aug 7, 2026
… real DCL.EXE/LOGINOUT.EXE via LINK.EXE (#155) * vms-206: LINK.EXE emits real x86_64 crt0 + cross-image CALL PLT stubs emit_shareable() in src/vmslink/link.c had two pieces hardcoded to AArch64 machine code regardless of g_out_machine: the synthesized crt0 entry stub for a main()-based --executable, and the cross-image CALL PLT stub + import-CALL detection (gated on R_AARCH64_CALL26/JUMP26 only, so R_X86_64_PLT32 references to a producer universal never routed through the import table). An x86_64 main()-based program, or any x86_64 image with a cross-image CALL import, linked to garbage -- exactly DCL.EXE's shape, blocking vms-cb5f. Adds, additively per g_out_machine (mirrors vms-8f5's e_machine gate): - is_call also covers R_X86_64_PLT32, so a PLT32 reference to an undefined-locally, --use-producer-exported symbol becomes an import exactly like aarch64's CALL26/JUMP26. - the cross-image-call reloc-apply branch gets an x86_64 case: a PC32-style S+A-P write targeting the PLT stub instead of the (absent) callee. - the PLT stub emitter gets an x86_64 case: `jmp *disp32(%rip)` (FF 25 imm32) through the import-GOT cell -- the one-instruction analogue of aarch64's adrp/ldr/br page+lo12 GOT load+branch. - the crt0 emitter gets a real x86_64 stub: mov rdi,[rsp] / lea rsi,[rsp+8] / lea rdx,[rsi+rdi*8+8] recovers argc/argv/envp per the SysV process-entry stack layout, `call main` then `mov edi,eax ; call exit` tails the return value into exit(). Encoding verified against `as`'s AT&T disassembly byte-for-byte. New test (src/imgact/test/run_multiobj_exec_x86_64.sh, wired into CI as multiobj-exec-x86_64): links a two-object main() program against a hand-written producer shareable via LINK.EXE --executable --use, activates it by executing the image directly (real kernel PT_INTERP -> IMGACT.EXE, native x86_64, no emulation), and checks the process really ran main(), read a real argc/argv off the stack (two runs, two different exit codes), made a real cross-image CALL through the new PLT stub into the producer, and exited via a cross-image exit() call. Verified this test fails with 'unresolved external symbol' before the fix (confirmed via stash) and passes after. Regression: aarch64 MVP (run_test.sh), aarch64 crt0/PLT (run_multiobj_exec.sh) and x86_64 simple-reloc (run_test_x86_64.sh) harnesses all stay green. Full ctest suite: 106/107 pass; the one failure (facility_attribution_negctl) is pre-existing and unrelated (reproduces identically with this change stashed out -- host/container site-derivation drift in kernel/vms_lock.c attribution, nothing to do with vmslink). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * vms-e5d: LINK.EXE resolves x86_64 GOTPCRELX (type 41), grounded against real musl/libgcc is_got_reloc() only recognized GOTPCREL (9) and REX_GOTPCRELX (42); plain GOTPCRELX (41, gas's non-REX relaxable GOT-load variant) hit patch_pcrel's default die("unsupported .text relocation"). Adds it as a third case alongside its already-handled siblings in is_got_reloc()/patch_got() -- same flat-disp32-write codegen (S(got)+A-P, addend -4), since LINK.EXE performs no GOT-load-to-LEA relaxation for any of the three. Grounded empirically, not from psABI text alone: - The system's prebuilt musl libc.a carries ZERO GOTPCRELX/REX_GOTPCRELX (non-PIC static build) -- confirming the gap check needed a real PIC object set, not just this host's default libc.a. - A `-fPIC -fno-plt` probe (`extern void f(void); void g(void){f();}`) reproduces the exact instruction shape: `jmp *sym@GOTPCREL(%rip)` (ff 25 disp32, no REX prefix -- near indirect call/jmp defaults to 64-bit operand size without REX.W), matching readelf's R_X86_64_GOTPCRELX. - Alpine x86_64 libgcc.a (the exact toolchain vms-cb5f's DECC$SHR build uses) carries 1521 real GOTPCRELX occurrences -- e.g. `call *abort@ GOTPCREL(%rip)` in _absvdi2.o -- confirming the gap is in libgcc.a (GCC's runtime calling abort/etc. through the GOT under -fno-plt), not libc.a itself, and closely matches vms-cb5f's reported 1449 (after its TLS-subsystem filter narrows the count). Proof (done condition): - Pre-fix LINK.EXE reproducibly dies with "%LINK-F-ERROR, unsupported .text relocation" on a hand-built intra-image GOTPCRELX call (caller.o -> callee.o, both defined, forcing the GOT slot to resolve internally rather than deferring as an import). - Post-fix LINK.EXE links it; a new REAL-LOAD harness (src/vmslink/test/gotpcrelx_activate.c) mmaps the shareable at a genuine non-zero ASLR'd base, applies the .vms$rel load-bias fixup IMGACT would perform, and calls in -- proving the GOT cell resolves to the correct address under a real load, not a readelf/byte check. Wired into run_test_x86_64.sh as a permanent regression case. - Whole-archiving the real Alpine x86_64 musl libc.a + libgcc.a (TLS subsystem filtered per vms-cb5f's precedent, an unrelated gap) with pre-fix LINK.EXE reproduces the exact die(); post-fix it gets past that point and (with --allow-undefined for legitimately deferred externals) succeeds end-to-end: 1586 objects, 146 GOT slots, 9610 relocs, EM_X86_64 -- confirming this specific gap is cleared at the scale vms-cb5f hit it. Base: work/vms-b93-integration (vms-206 merged). Blocks vms-cb5f. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * vms-cb5f: parameterize the x86_64 DCL.EXE VMS-native proof harness (ARCH=x86_64) Reproduces vms-b65.6's aarch64 DCL.EXE-through-IMGACT proof for x86_64, per vms-bdf's own done-condition (all four reloc/crt0/PLT beads merged): - run_dcl_native.sh / lib_build_graph.sh: ARCH env var (default aarch64, unchanged) selects the one target-specific codegen flag each producer needs (-mno-outline-atomics on aarch64 vs -mtls-dialect=gnu2 on x86_64, the standing precedent from docs/design-link-x86_64-relocs.md) and the libvmssys arch/<ARCH>/syscall.S. Shared by run_login_native.sh unaffected (ARCH unset -> identical aarch64 defaults, verified against a real arm64 musl container). - mk_{vmsprocess,vmslnm,vmsfs,libvms,vmsrms}_shr.sh / mk_dcl.sh: CFLAGS is now env-overridable (${CFLAGS:-<same aarch64 default>}) so the x86_64 caller can supply target-appropriate flags without a forked copy of each recipe. - mk_decc_shr.sh: DECC$SHR must stay a non-TLS producer (LINK.EXE's one-TLS-object-per-image limit, vms-212 tracks the general fix). aarch64's libgcc.a empirically carries zero TLS-defining members; x86_64's whole- archives a dead-for-OVMX subsystem (GCC's IEEE 754-2008 decimal-float library + -fsplit-stack support) built on the TLSGD general-dynamic model LINK.EXE's x86_64 path doesn't implement (OVMX standardizes on gnu2/ TLSDESC). Filtered architecture-generically by scanning archive members for .tdata/.tbss or TLSGD refs, not by hardcoding names -- a no-op on an archive with neither. - .github/workflows/ci.yml: new dcl-native-x86_64 job, native amd64 (no QEMU binfmt needed -- the runner already is x86_64), ARCH=x86_64 through the same run_dcl_native.sh. BLOCKED short of a green run: whole-archiving real musl libc.a on x86_64 (1345 objects) hits R_X86_64_GOTPCRELX (type 41), a relocation link.c's x86_64 path does not recognize (is_got_reloc() only checks GOTPCREL/ REX_GOTPCRELX) -- confirmed empirically (1449 occurrences across ordinary, load-bearing musl objects, not confinable to dead code the way the TLSGD subsystem was). link.c is out of this item's file-domain per its own repeated header comments ("do NOT edit them here"); see the escalation. Also confirmed (fetched fresh): vms-206 is on work/vms-b93-integration (16c7965) but NOT yet on main -- a second prerequisite for the executable (not just shareable) link step once the GOTPCRELX gap is fixed. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * vms-cb5f: make DECC$SHR's x86_64 TLS filter transitive, closing the whole-archive gap Re-dispatch, continuing from work/vms-cb5f now that vms-e5d (GOTPCRELX) is merged into work/vms-b93-integration. Rebased cleanly onto origin/work/vms-b93-integration (vms-206 crt0/PLT + vms-e5d GOTPCRELX both present); the prior CI-job/ARCH-parameterization/CFLAGS work carried over unchanged. Whole-archiving real musl libc.a + libgcc.a on x86_64 got past GOTPCRELX and hit a NEW gap: mk_decc_shr.sh's TLS filter (vms-cb5f's own prior commit) removes members that directly define/reference TLS storage (bid64_add.o etc, via TLSGD) but left non-TLS "glue" objects in place -- _addsub_dd.o/_addsub_sd.o/... call INTO the removed decimal-float subsystem via a plain GOT reference to e.g. __bid64_add, which no longer has a definer once bid64_add.o is dropped. LINK.EXE's "GOT symbol undefined" strict die() was CORRECT given the archive it was handed -- the gap was in the filter only removing the directly-tainted half of a connected dead-code component, not link.c. Fixed by making the filter a reference-graph fixed-point closure: after seeding the direct TLS-tainted set (unchanged), repeatedly pull in any surviving member whose undefined reference is satisfied ONLY by an already- removed member, until nothing new is added. One nm pass over the whole archive up front (not re-invoked per member per iteration) keeps this cheap on libc.a's 1345 members. Verified as a no-op on aarch64's libgcc.a (366/366 members survive, 0 filtered, byte-identical DECC$SHR before/after) and pulls in exactly the expected 148/241-surviving decimal-float + split-stack subsystem on x86_64's. With that fix, the full six-library producer graph + DCL.EXE (22 objects, 6 GOT, 2 TLS, 1053 ABS64-ptr, 145 imports) now link VMS-native and clean on x86_64. Activation through IMGACT.EXE segfaults -- escalated (see PR/item notes), not patched here: link.c/imgact.c are out of this item's file-domain per the item's own repeated header comments, and the crash is upstream of mk_decc_shr.sh (isolated: run_multiobj_exec_x86_64.sh's small-scale crt0/PLT proof -- 3 imports, 1 GOT, 0 TLS -- still passes natively with correct argc-computed exit codes, so this is scale/shape-specific to DCL.EXE's import/reloc volume, not a general crt0/PLT regression). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * vms-a66: LINK.EXE was dropping every read-only-section relocation DCL.EXE segfaulted the moment it was activated on x86_64. The fault PC sat in the anonymous RWX region IMGACT.EXE maps for DECC$SHR, executing non-instruction bytes. Single-stepping from musl's printf_core into pop_arg caught the transfer: movslq (%rcx,%rsi,4),%rdx ; rdx = jumptable[1] add %rcx,%rdx ; rdx = table_base + delta jmp *%rdx with the table entry ZERO, so the jump landed on the table's own address -- inside .rodata, on the "(null)" string constant. Root cause: parse_obj() collected relocations only for sections it bucketed B_TEXT or B_DATA. Every relocation whose target section was B_RODATA was discarded with no diagnostic. gcc emits each `switch` jump table into a per-function read-only section as `.long arm - table_base`; the arms are in .text and the table is not, so the assembler cannot fold the difference and leaves one real R_X86_64_PC32 per arm. All of them were dropped, so every jump table in the image came out all zero. Not a scale bug. Nothing about 145 imports, 6 shareables, 6 GOT slots, 2 TLSDESC entries or the 6-deep --use chain is involved: vms-206/vms-cd1/vms-2e4 passed through this because their specimens contained no switch large enough for gcc to build a table and never called a printf-family function with a conversion. It is a code SHAPE that first appeared when real musl and the real DCL sources entered the link -- 902 such relocations in libc.a, 554 in DCL's own objects. aarch64 was never affected: aarch64 gcc resolves its jump tables at assembly time, so its only read-only-section relocations are .eh_frame PREL32 (never executed). Confirmed empirically on both arches. Fix: collect relocations for B_RODATA as well, via a single bucket_is_patchable() predicate, and emit %LINK-W-RELSKIP for any RELA section whose target is allocatable but not flat-placed -- a silent drop is exactly how this survived four proofs. (That diagnostic immediately surfaces one pre-existing gap: libgcc's cpuinfo.o .init_array, tracked separately, not executed today.) Regression gate: src/vmslink/test/run_rodata_reloc_x86_64.sh + its specimen link a jump-table-bearing image against the real whole-archive musl DECC$SHR, activate it through a real IMGACT.EXE, and diff the transcript against the SAME source built by the system toolchain. It asserts the specimen still produces .rela.rodata (so it cannot rot into a vacuous pass) and fails LOUD -- verified: it dies with "Illegal instruction" on the pre-fix linker and passes on the fixed one. Wired into CI as job rodata-reloc-x86_64. Verified green: dcl-native ARCH=x86_64 (SHOW TIME + A=5, exit 0) and ARCH=aarch64, run_multiobj_exec_x86_64.sh, src/imgact/test/run_test_x86_64.sh, src/vmslink/test/run_test_x86_64.sh, run_test_x86_64_tls.sh, run_decc_shr.sh, run_test.sh + run_multiobj_exec.sh under arm64. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * vms-b6a: wire the VMS-native LINK.EXE shareable graph into CMake (aarch64) Wires the mk_*_shr.sh / mk_dcl.sh / mk_loginout.sh recipes (the vms-b65/c39 lib-migration chain) into `cmake --build` via a new OVMX_LINK_NATIVE option that auto-detects on an aarch64 musl toolchain. `cmake -B build && cmake --build build` now produces LIBVMSSYS$SHR.EXE, DECC$SHR.EXE, LIBVMSPROCESS$SHR.EXE, LIBVMSLNM$SHR.EXE, LIBVMSFS$SHR.EXE, LIBVMS$SHR.EXE, LIBVMSRMS$SHR.EXE, DCL.EXE and LOGINOUT.EXE via LINK.EXE -- verified EM_AARCH64 with zero DT_NEEDED entries on all 9 artifacts. Additive to (not a replacement of) each library's existing add_library() target, which host ctest unit tests still link directly; on non-aarch64-musl toolchains OVMX_LINK_NATIVE stays off with no behavior change. Extracted the previously-inlined LIBVMSSYS$SHR recipe (duplicated in lib_build_graph.sh) into mk_vmssys_shr.sh, the one place the mk_*_shr.sh convention keeps it -- lib_build_graph.sh (run_dcl_native.sh/run_login_native.sh) now calls it too, closing the exact drift risk mk_libvms_shr.sh's LIST comment warns about. mk_vmssys_shr.sh exports vms_kif_setident unconditionally (append-only vector), so run_login_native.sh's SYS_VEC_EXTRA override is no longer needed. Both harnesses re-verified green end-to-end in an aarch64 musl container after the refactor. Added CI job link-native-cmake asserting the readelf ground-source condition directly. x86_64 extension is vms-6da (separate item, unblocked). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * vms-6da: extend the VMS-native LINK.EXE CMake graph to x86_64 Extends vms-b6a's OVMX_LINK_NATIVE mechanism (auto-detected from the compiler's -dumpmachine triple) to also recognize an x86_64 musl toolchain, not just aarch64 -- same CMake target (link_native_graph), same build_link_native.sh entrypoint, no forked plumbing. ARCH is threaded through as an env var to every mk_*_shr.sh recipe (CFLAGS picks -mtls-dialect=gnu2 on x86_64 vs -mno-outline-atomics on aarch64, the same convention lib_build_graph.sh's build_producer_graph() established for the raw-harness path in vms-cb5f/vms-a66). mk_vmssys_shr.sh (extracted by vms-b6a, so it hadn't picked up the ARCH/CFLAGS env-override convention yet) and mk_loginout.sh (missed by cb5f/a66) are brought in line with the rest of the mk_*_shr.sh recipes. Fixed a real bug hit while proving this: `N=$(... | grep -c NEEDED)` aborts under `set -e` in the CI job's alpine /bin/sh whenever the DT_NEEDED count is legitimately zero (grep -c exits 1 on no match) -- present in both the pre-existing aarch64 job and the new x86_64 one; both fixed. Ground-truth proof, real alpine:3.20 musl containers (arm64 emulated, amd64 native), `cmake --build . --target link_native_graph`: - x86_64: 9/9 artifacts EM_X86_64, zero DT_NEEDED, via LINK.EXE - aarch64: 9/9 artifacts EM_AARCH64, zero DT_NEEDED (regression, unchanged) Adds CI job link-native-cmake-x86_64 alongside link-native-cmake, mirroring dcl-native-x86_64's amd64-native (no QEMU) approach. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * vms-d0f5e: fat initramfs ships DCL.EXE/LOGINOUT.EXE VMS-native via LINK.EXE Replaces vms-913.6 (cancelled -- DT_HASH/ld-based dynamic ELF was a proven dead end). distro/Dockerfile.bootable now builds a real alpine:3.20 musl link-native stage that runs `cmake --build --target link_native_graph` (OVMX_LINK_NATIVE, vms-b6a/vms-6da) to produce the 7 shareables (DECC$SHR, LIBVMSSYS$SHR, LIBVMSPROCESS$SHR, LIBVMSLNM$SHR, LIBVMSFS$SHR, LIBVMS$SHR, LIBVMSRMS$SHR) + DCL.EXE + LOGINOUT.EXE via LINK.EXE, plus IMGACT.EXE (its own standalone Makefile, x86_64). All 9 artifacts are ET_DYN with a .vms$sv symbol vector, zero DT_NEEDED/DT_HASH -- ground- truth readelf assertions are baked into the Docker build itself (both in the link-native stage and against the actually-shipped DCL.EXE in the fat initramfs), not just asserted by a separate CI job. STARTUP.EXE and IMGACT.EXE stay static/freestanding; HELP/AUTHORIZE/MAIL/MONITOR/ INITIALIZE ship static for 0.1 (no mk_*.sh recipe yet, scope decision -- DCL is the flagship dynamic proof, not every utility). Ground-truth verified locally: full `docker build` succeeds, QEMU x86_64 boot reaches login (tests/uat/vms_session_qemu.sh: SYSTEM and GUEST sessions authenticate, DCL runs 50+ commands to VMS-correct output, DIRECTORY SYS$SYSTEM: lists the new DCL.EXE/IMGACT.EXE/LOGINOUT.EXE), and test_executive_integral.sh's negative controls (NOEXEC/NODEV, rebuilt from the same fat initramfs) still pass 14/14. KNOWN REGRESSION, not fixed here (out of this item's file-domain -- distro/Dockerfile.bootable + CMake wiring, not src/vmsdcl or src/kernel/vmsfs): SPAWN's first invocation in a session now fails (%DCL-E-CREPRC) where it previously succeeded. Root-caused via an A/B rebuild swapping only DCL.EXE/LOGINOUT.EXE back to static (54/54 UAT checks pass) vs the VMS-native pair (52/54, this SPAWN check newly red). cmd_spawn() (src/vmsdcl/dcl_cmd_process.c) re-execs via readlink("/proc/self/exe"), which resolves to a path vmsfs reports "(deleted)" -- confirmed present already at the top of DCL's own main(), before any DCL code runs, so the deletion happens during kernel PT_INTERP + IMGACT activation, not in DCL or LOGINOUT. No unlink()/rename() of SYSEXE exists in the userspace boot path (checked ovmx_init.c, vms_login.c, vmsfs_translate.c) -- the leading hypothesis is a vmsfs.ko dentry-lifecycle interaction exposed by IMGACT's longer activation time, which needs its own investigation. Login and the item's own done condition are unaffected; test file left unmodified (weakening an existing UAT assertion is outside this item's authority). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * vms-00e: vmsfs must not unhash the dentry of a running image (SPAWN fix) ROOT CAUSE. vmsfs.ko's ->d_revalidate answered "invalid" for EVERY positive regular-file dentry, unconditionally. That is not revalidation, it is permanent invalidation: a d_revalidate() of 0 makes the VFS call d_invalidate(), which UNHASHES the dentry (fs/namei.c lookup_fast() / lookup_open()); an unhashed non-root dentry satisfies d_unlinked(); and d_path() renders any d_unlinked() path with a " (deleted)" suffix (fs/d_path.c path_with_deleted()). /proc/<pid>/exe and /proc/<pid>/fd/<n> are d_path() readers. So the FIRST path walk of a running executable that lives on vmsfs made that program's own /proc/self/exe read ".../DCL.EXE (deleted)" -- with the file present and unmodified. mm->exe_file pins the dentry, so it stayed unhashed for the life of the process. DCL's SPAWN re-execs itself via readlink("/proc/self/exe") (cmd_spawn(), src/vmsdcl/dcl_cmd_process.c), so it execl()'d a path with " (deleted)" on the end, got ENOENT, and answered %DCL-E-CREPRC. That single defect explains BOTH observed shapes, which differ only in when the second walk happens: - static DCL.EXE: nothing re-walks the image during startup, so the first SPAWN's own execl() was the second walk -- spawn #1 worked, spawn #2 onward failed. (The "SECOND SPAWN fails" defect recorded in tests/uat/vms_session_qemu.sh, previously blamed on DCL.) - VMS-native, IMGACT-activated DCL.EXE (vms-d0f5e): IMGACT.EXE re-opens the image by AT_EXECFN to read its .vms$sv/.vms$imp sections (activate_symbol_vector(), src/imgact/imgact.c) BEFORE the image runs, so the dentry was already unhashed at the first line of main() and the FIRST SPAWN failed. IMGACT did not cause the bug; it reached it one walk earlier. Nothing ever unlinked or renamed anything, which is why the userspace audit of ovmx_init.c / vms_login.c / vmsfs_translate.c found nothing. FIX. ->d_revalidate now asks the resolver the question a fresh ->lookup would ask -- "what does this name resolve to right now?" -- and keeps the dentry when the answer is unchanged: - block-device mode: re-resolve the name to a FID (vmsfs_blkdev_resolve(), factored out of vmsfs_blkdev_lookup() with no behaviour change) and compare against i_ino, which IS the FID (iget_locked(sb, fid)). Exact identity: catches a newer version AND a deletion. - overlay mode: compare the current highest version of the base name against the version this dentry resolved to (overlay mints a fresh inode per lookup, so version is the only stable identity). - create intent (LOOKUP_CREATE/LOOKUP_RENAME_TARGET) still returns 0, so O_CREAT cannot be satisfied from the cache and VMS still cuts a NEW VERSION rather than reopening the current one. This is also strictly cheaper than the old behaviour, which paid for the same resolution and then threw away the dentry and the inode anyway. GROUND SOURCE, BOTH DIRECTIONS, ON THE REAL RUNTIME (Rule 6 -- no module was loaded on the host; everything below ran under QEMU). New suite tests/qemu/test_kmod_vmsfs_exepath.c, 28 assertions: pre-fix 3 phases red, incl. "child: /proc/self/exe after re-open = /mnt/.../CHILD.EXE (deleted)" -- the product symptom reproduced at the kernel layer -- then the kernel OOPSES in __fput() (NULL d_inode) when the held fd is closed. post-fix 28/28 green. Phase 3 execs this program from a real block-device vmsfs and has the child do exactly what IMGACT does (re-open its own image by path) and then what SPAWN does (re-exec via /proc/self/exe). Phase order puts it first precisely so it is REACHED before the pre-fix oops kills the process. Phase 2 carries the POSITIVE CONTROL that makes the file non-vacuous: once PROBE.TXT;2 exists, the fd held on ;1 MUST become "(deleted)", because the unversioned name no longer names it -- a d_revalidate that just answers "valid" passes everything else here and goes red on that one. tests/uat/vms_session_qemu.sh on the vms-d0f5e VMS-native fat initramfs: pre-fix 52/54 (both SPAWN assertions red) post-fix 54/54 A SECOND spawn ('SPAWN SHOW TIME') and two assertions on it are added here, because the long-standing "second SPAWN" defect is fixed by the same change -- measured, not assumed. With them: 52/56 pre-fix, 56/56 post-fix. The comment block that recorded that defect as a DCL bug is corrected in place rather than removed. No regression: full QEMU kernel harness 31/31 suites (incl. the existing test_kmod_vmsfs and test_kmod_vmsfs_blkdev version-semantics suites), test_persistent_boot.sh 14/14, test_executive_integral.sh 14/14, Rule 9 runtime-target gate, divider integrity, harness verdict, kif caller census, identity census, facility manifest selftest+coverage. tests/qemu/facility_defects.sh: the new suite joins the two existing vmsfs suites in SCOPE_OUT_SUITES for the reason already stated there -- it never opens /dev/vms, so no executive mutation can turn it red. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * vms-913.7: wire SYSTARTUP_VMS.COM's INSTALL ADD to the real Known Image DB Checked current state first (per item instructions): vms-p78.3's install unification and vms-d0f5e's real-file fat initramfs already held, and test_persistent_boot.sh was passing -- but done condition #4 (SYSTARTUP_VMS.COM carrying INSTALL ADD for each shareable) was not met, and fixing it surfaced two real, ground-verified gaps beneath it: 1. DCL's INSTALL builtin (cmd_install) never dispatched to SYS$SYSTEM:INSTALL.EXE (src/install/install.c, vms-913.5's KFE-database utility). It reimplemented its own flat-text SYS$MANAGER:INSTALL_LIST.DAT that nothing ever read -- IMGACT.EXE's known-image search (src/imgact/known_images.c) mmaps the binary VMS$KNOWN_IMAGES.DAT only INSTALL.EXE writes. Fixed by making cmd_install a thin wrapper that re-execs INSTALL.EXE via dcl_exec_utility(), the same pattern already used for ANALYZE/MAIL/SYSGEN/SYSMAN in this file -- matching install.c's own header comment ("deliberately NOT wired as a DCL builtin"). 2. Once wired, real QEMU boot (docker build + test_persistent_boot.sh) caught that INSTALL.EXE was never copied into the fat initramfs, so every SYSTARTUP_VMS.COM INSTALL ADD failed with %INSTALL-F-NOIMG on live boot -- and that failure silently aborted the rest of SYSTARTUP_VMS.COM despite SET NOON, dropping the "site startup ran" banner test_persistent_boot.sh checks for (14/14 -> 12/14). Fixed by adding INSTALL.EXE to Dockerfile.bootable's fat-initramfs SYSEXE copy list, alongside the other build-static utilities. SYSTARTUP_VMS.COM now INSTALL ADDs exactly the 7 shareables the fat initramfs actually ships (DECC$SHR, LIBVMSSYS$SHR, LIBVMS$SHR, LIBVMSPROCESS$SHR, LIBVMSLNM$SHR, LIBVMSFS$SHR, LIBVMSRMS$SHR -- Dockerfile.bootable's own "9 VMS-native LINK.EXE artifacts" gate). LIBVMSQUEUE$SHR is deliberately excluded: it builds via the ordinary CMake add_library() graph, not the VMS-native LINK.EXE graph, and is not shipped in the fat initramfs -- INSTALLing it would FILNOTFND on every boot. Verified live: docker build -f distro/Dockerfile.bootable + test_persistent_boot.sh, 14/14 checks pass across both boots, with all 7 INSTALL-I-ADDED lines visible each time (idempotent re-add on reboot, matching real VMS SYSTARTUP_VMS.COM practice). New test: tests/dcl/test_install_command.sh proves through real vmsdcl (not around it) that INSTALL ADD/LIST/REMOVE write and clear the actual KFE binary database (magic-byte check), not just matching text output -- the old stub could print the same %INSTALL-I-ADDED text without touching the file IMGACT.EXE reads. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * vms-913.11: verify x86_64 boot-to-login is already proven, fix stale CI comments Live-verified on this worktree's native x86_64 host: docker build distro/Dockerfile.bootable + tests/uat/vms_session_qemu.sh boots real QEMU (qemu-system-x86_64, no emulation) through IMGACT.EXE's x86_64 relocation path (RELATIVE/GLOB_DAT/JUMP_SLOT/TLSDESC) to a DCL login prompt, drives a full scripted session, 56/56 checks passed. This is already the uat-session CI job (Job 7), unconditional on every push since GH runners are x86_64 natively. Job 8/9 comments still said "aarch64-only until bead vms-913.11" as if x86_64 support were still pending; it has its own native-runner counterpart (Job 9b, imgact-x86_64) and the uat-session boot-to-login proof. Corrected the comments to point at the now-complete state instead of a stale forward reference. Done condition satisfied by existing work (vms-8f5, vms-cd1, vms-2e4, vms-a66, vms-00e, vms-d0f5e chain); no functional code change needed. * vms-fbc: shipped-image ground-source gate now covers LOGINOUT.EXE too, not just DCL.EXE vms-c39's done condition (STARTUP execs VMS-native LOGINOUT.EXE, which authenticates against SYSUAF and execs VMS-native DCL.EXE, zero ld/ld.so) was structurally unverifiable in the Docker CI container -- no /dev/vms there. This item's job is proving the SUCCESSFUL leg under a real kernel. Verified live, not assumed: that proof already exists and is repeatable. - src/ovmx_init/ovmx_init.c execl()s SYS$SYSTEM:LOGINOUT.EXE. - tools/vms_login.c (LOGINOUT) authenticates against SYSUAF (sysuaf_authenticate), then execl()s DCL.EXE --login. - src/vmslink/link.c sets PT_INTERP=IMGACT.EXE on every LINK.EXE executable image -- ground truth that no ld.so is anywhere in this chain, not an inference. - distro/Dockerfile.bootable's fat initramfs ships exactly one DCL.EXE and one LOGINOUT.EXE, both copied only from the VMS-native /link-native build (no static fallback exists for either anymore). - tests/uat/vms_session_qemu.sh runs this exact chain under real QEMU in CI (job uat-session, .github/workflows/ci.yml), and vms-00e's own commit records a fresh 56/56 pass on this initramfs today. The one real gap: the Dockerfile's own ground-source readelf gate (does the SHIPPED image -- the actual bytes cp'd into the initramfs, not a copy two directories away -- carry zero DT_NEEDED/DT_HASH) only checked DCL.EXE. LOGINOUT.EXE is the FIRST VMS-native image in the login chain and had no equivalent check on its shipped bytes; a regression that silently reintroduced an ld-linked LOGINOUT.EXE ahead of DCL.EXE would not have been caught by this gate (the generic 9-artifact loop in the earlier link-native stage checks a build-output copy, not what actually ships). Fixed by widening the existing gate to loop over both images. Ground-truth verified locally: `docker build -f distro/Dockerfile.bootable --target builder` -- real build, not mocked -- prints "OK: shipped DCL.EXE is VMS-native (EM_X86_64, zero DT_NEEDED/DT_HASH)" and "OK: shipped LOGINOUT.EXE is VMS-native (EM_X86_64, zero DT_NEEDED/DT_HASH)" for the actual artifacts this build produced. The new for-loop's shell logic was also unit-checked standalone: fails correctly against a real dynamically-linked ELF (DT_NEEDED present) and passes correctly against a statically-linked one, before spending a build cycle on it. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * vms-913.10: prove slim-boot login+DCL activates from disk, not initramfs The slim initramfs (STARTUP.EXE-only) and boot.sh --slim wiring already existed (Dockerfile.bootable, boot.sh) but nothing exercised the boot path: test_persistent_boot.sh's two boots both used the FAT initramfs. Extends that harness with: - a static check that the slim initramfs cpio listing carries no DCL.EXE/LOGINOUT.EXE/IMGACT.EXE/SYSLIB (bootstrap-only, as designed) - Boot 3: boots the SAME installed disk with the SLIM initramfs, logs in as SYSTEM/MANAGER over the QEMU serial console (real SHA256-backed SYSUAF credentials, same as tests/uat/vms_session_qemu.sh), and runs SHOW TIME to a real DCL prompt Since the slim initramfs structurally ships none of LOGINOUT.EXE, IMGACT.EXE, DCL.EXE, or the SYSLIB shareables, a real login reaching a working DCL prompt is functional proof they resolve from the mounted system disk's SYS$SYSTEM:/SYS$LIBRARY:, not the initramfs. Measured against a real QEMU boot: 25/25 checks pass. Also adds `cpio` to the runner image's apt install list -- needed by the new static check, absent from the base ubuntu:24.04 image (verified). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * vms-0c9: docs/install-0.1.md -- 0.1 install/boot/login walkthrough Light release-eng doc scoped to 0.1 (download/build the fat-initramfs image, first-boot install, reboot into the slim initramfs, log in to DCL). Cluster admin / license audit / trademark review stay under vms-d5b R6 for 1.0. Ground-sourced: a real `docker build -f distro/Dockerfile.bootable -t ovmx-boot .` was run on this checkout and reached the builder stage before this shared host's disk filled and the build was aborted for safety. Every command and every piece of documented console output (the %STARTUP-I-* banners, %OVMX-I-EXEC, Username:/Password: prompts, Welcome to OVMX, SHOW TIME) is instead quoted verbatim from GitHub Actions run 31128513528 (commit 8560fa7), where the "Persistent Boot Smoke Test" and "VMS User Acceptance Test" CI jobs build and boot this same image and passed 14/14 + all UAT assertions -- the proven, passing path tests/qemu/test_persistent_boot.sh and tests/uat/vms_session_qemu.sh already exercise on every push. Login credential (SYSTEM/MANAGER) confirmed against the real hash in distro/rootfs/.../SYSUAF.DAT, not invented. No git tag created -- that step is reserved for the operator's final sign-off. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * vms-ade8: fix overclaimed CI citation in install-0.1.md ground-sourcing note Run 31128513528 predates the slim-boot work (checked-out test_persistent_boot.sh has zero slim references) and its overall status was FAILURE (Build & Test + attribution negative-control jobs red); only citing the two individually-passing jobs overclaimed coverage. Rescope the note to state what was actually verified: this swarm's own local docker build + real QEMU boots against the merged tree (25/25 checks) for Section 3, and the UAT script's own run for the DCL session content. No CI run number is cited as covering slim-boot. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * vms-9bc: regenerate docs/design-link-x86_64-relocs.md, stale after vms-913.7 Re-ran tools/survey_x86_64_relocs.sh to refresh the empirical R_X86_64_PC32/ PLT32 counts in src/libvms/descrip.c, no methodology change. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> --------- Co-authored-by: alice <alice@workspace.local> Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
baron-3dl
pushed a commit
that referenced
this pull request
Aug 7, 2026
…(vms-56c) vms-9b7 design cascade check #2. PROVISION.EXE is now boot-critical (PID 1 execs it where it used to exec DCL.EXE). Adds a 'noprovision' case to test_release_e2e.sh: a real SYSTEM session deletes SYS$SYSTEM:PROVISION.EXE off the installed disk via DCL DELETE, and boot 2 must fail-stop with '%OVMX-F-EXECINIT, SYS$SYSTEM:PROVISION.EXE is missing' + the identity detail line, and must NOT reach a login prompt. Proves run_startup()'s stat() guard fires with its own distinct message (not the SYSUAF halt in disguise) and that the new boot-critical image is not a silently-skippable step. Fault injected through the file; cannot false-pass (a delete that did not land -> boot 2 comes up -> halt assertion fails). Verified 6/6 green against the fixed image. Already wired into ci.yml via the existing test_release_e2e.sh mount. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
baron-3dl
added a commit
that referenced
this pull request
Aug 10, 2026
…3, no bash (vms-62b) (#255) Close the Build-native 1.0 self-hosting fixpoint (vms-678 gate): OVMX builds a nontrivial OVMX component -- the OVMX linker LINK.EXE itself -- from WITHIN OVMX, driven from a DCL session with ZERO bash / no host tool in the fixpoint build path, and proves it byte-stable. Proven under arm64 QEMU (new CI job link-selfhost-native, mirrors build-com-native): * A host bootstrap linker BUILDS gen1 LINK.EXE (Rule 9: a build step is not an activation proof); gen1 is an OVMX-native image. * DCL session #1 runs @sys$SYSTEM:BUILD LINKSH LINK OVMX_LINK_RMS_IO. Inside it the multi-TU BUILD.COM has the OVMX-native TCC.EXE compile link.c + ovmx_link_rms_io.c and the OVMX-native gen1 LINK.EXE link them into gen2 -- every toolchain step an IMGACT-activated OVMX image, no bash in the path. * gen2 is installed as SYS$SYSTEM:LINK.EXE; DCL session #2 runs the SAME @build and the OVMX-BUILT gen2 LINK.EXE links LINK.EXE again into gen3. * FIXPOINT: gen2 and gen3 are BYTE-IDENTICAL (sha256), the tcc gen2==gen3 pattern (vms-4ba) now for LINK.EXE. This is S4 = "OVMX builds OVMX from within". Multi-TU BUILD.COM (distro/rootfs/.../SYSEXE/BUILD.COM): extends the single-TU S3.2 driver to compile N source TUs and link them into one image, via a real DCL build loop -- SRC = P'N' parameter indexing, IF SRC .EQS. "" bare-symbol termination, OBJLIST accumulation. New optional symbols: CFLAGS (extra tokens ahead of the TCC compile) and NORUN (skip activating the linked image -- a linker cannot be RUN with no args). `@BUILD HELLO` (single-TU, runs the image) is preserved. DCL symbol evaluation (vms-5c1), grounded in the DCL User's Manual and needed for the loop: - dcl_symbol.c: ''symbol' (doubled apostrophe) now substitutes INSIDE a "..." string; a single apostrophe stays literal, as VMS. - dcl_exec.c: an UNQUOTED operand that names a defined symbol is auto-resolved in IF/WHILE string comparisons (IF P2 .EQS. "" tests P2's value) and on the right-hand side of an `=`/`==` assignment (A = B assigns B's value; SRC = P'N' parameter indexing). Quoted operands stay literal; undefined symbols keep the literal token (lenient, no %DCL-W-UNDSYM), matching parse_primary()/the integer-operand path. Covered by tests/dcl/test_symbol_eval.sh. Full ctest 121/121 (parts_demo_e2e skips w/o runtime, as at baseline); DCL native jobs unaffected. Deferred on vms-62b/vms-678: route the DCL LINK builtin to the native LINK.EXE (vms-d5b7); F$ lexical coverage / more BUILD.COM ergonomics. Co-authored-by: alice <alice@workspace.local> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
baron-3dl
added a commit
that referenced
this pull request
Aug 10, 2026
* WIP vms-df9: shared kit reader lib + vmsfs.ko setattr for protection/UIC persistence
Progress checkpoint, not yet complete:
- src/product/ovmx_kit_reader.{h,c}: factored the kit open/validate/
read-entries/read-file-verified logic out of tools/ovmx_kit_pack.c into a
shared module so PRODUCT.EXE (still to come) does not hand-roll a second
kit parser. ovmx_kit_pack.c's list/extract now call it.
- src/kernel/vmsfs/vmsfs.h + vmsfs_blkdev.c: added .setattr to the
block-device file/dir inode_operations, plus persisting owner UIC in
vmsfs_blkdev_flush_inode(). Needed because PRODUCT INSTALL must stamp
per-file protection/UIC from kit metadata onto the target volume, and
neither chmod(2) nor chown(2) against a real vmsfs blkdev mount reached
disk before this (no .setattr existed at all; fh_protection was written
only from a cached field .setattr never touched, and fh_uic_* was never
written back after creation). Compile-checked against the host's own
6.8.0-136-generic headers.
Still to do: ovmx_product_db.h, src/product/product.c (PRODUCT.EXE itself),
CMake wiring for the new target, DCL cmd_product rewired to dcl_exec_utility,
QEMU ground-source proof, rebase onto latest origin/main.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* vms-df9: PRODUCT INSTALL lands a real kit on a real vmsfs volume
PRODUCT.EXE, not a DCL builtin (constraint #1): cmd_product
(src/vmsdcl/dcl_cmd_misc.c) is now a thin dcl_exec_utility() forwarder,
same shape as cmd_analyze/cmd_install/cmd_mail. It resolves /SOURCE
through dcl_resolve_path() and canonicalizes /DESTINATION, then hands
off to SYS$SYSTEM:PRODUCT.EXE (src/product/product.c), which owns kit
reading, file placement, protection/UIC, and the product database.
Kit reading is NOT reimplemented: src/product/ovmx_kit_reader.{h,c} is
the open/validate/read-entries/read-file-verified logic factored out of
tools/ovmx_kit_pack.c (which is refactored to call it too), so there is
exactly one kit parser in the tree. src/product/ovmx_product_db.h is the
new, OVMX-defined and Rule-8-labeled product database format
(SYS$SYSTEM:VMS$PRODUCT_DATABASE.DAT, whole-file-struct idiom matching
known_images.h's KFE database).
Security (constraint #2): every installed file's protection and owner
UIC come from its ovmx_kit_entry (fchmod(2)/fchown(2)), never a
default. This needed a real kernel change: src/kernel/vmsfs/
vmsfs_blkdev.c had no .setattr on its block-device inode_operations at
all, so chmod(2)/chown(2) against a real vmsfs mount silently updated
only the in-core inode -- fh_protection was written from a separate
cached field .setattr never touched, and fh_uic_* was never written
back after creation. Added vmsfs_blkdev_setattr() (persists protection
via the new vmsfs_mode_to_vmsprot() in vmsfs.h) and UIC persistence in
vmsfs_blkdev_flush_inode(). This also fixes DCL's SET PROTECTION, which
has called plain chmod() since it was written and never worked against
a blkdev-mode mount. Kept deliberately minimal: no new ioctl, no format
change, no vms.ko involvement -- chown(2) to a UIC other than the
caller's own still requires CAP_CHOWN via ordinary Unix semantics
(setattr_prepare), which no kit ovmx_kit_pack produces today needs.
Rule 4 cascade tracked as vms-79b (API compat GO, test coverage
confirmed, no doc impact beyond the code's own comments).
distro/Dockerfile.bootable: added the missing PRODUCT.EXE copy into
SYSEXE (measured failing first: %PCSI-F-NOIMG with it absent), and
stages a copy of the already-built, byte-verified /boot/ovmx-os.kit
onto the distrib image itself at SYS$UPDATE:OVMX-OS.KIT -- not a raw
third virtio disk, which devtmpfs creates root:root mode 0600 with no
udev to relax it in this minimal initramfs, so SYSTEM (uid 4/gid 1)
gets EPERM reading one directly. Reading the kit as an ordinary
SYSTEM-owned file on the already-mounted boot disk sidesteps that
instead of granting a permission OVMX has no privileged path for.
Ground-source proof (tests/qemu/test_product_install_e2e.sh, real
vms.ko+vmsfs.ko): INITIALIZE+MOUNT a second virtio disk, PRODUCT
INSTALL the real OS kit onto it, RUN the installed HELP.EXE FROM THE
TARGET (the anti-LARP crux -- a foreign-command definition, since RUN
never forwards argv and HELP.EXE needs a topic to avoid blocking on
interactive input), PRODUCT SHOW PRODUCT /DESTINATION=DKA100: lists it
by the kit's own embedded name, then a full QEMU restart proves both
the installed files and the product database persist on disk. All
15 checks pass. NOTIMPL baseline (every PRODUCT op before this bead)
verified by reading the pre-change cmd_product, matching this item's
own measured-baseline text.
Also fixes an INV-1 regression this work introduced along the way: a
version-literal-shaped example in an ovmx_product_db.h comment tripped
the identity SSOT gate (test_identity_ssot.sh) by landing on a code
line rather than a comment-only line the gate's exclusion regex skips.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* vms-df9 fixup: revert the vmsfs.ko .setattr change, it broke boot fail-stop
CI (PR #278) caught a real regression: tests/qemu/test_release_e2e.sh's
`norecord` case (SYSUAF rewritten with no SYSTEM row) must HALT at boot
with %OVMX-F-EXECINIT, no SYSTEM record -- with vmsfs_blkdev.c/vmsfs.h's
.setattr addition present, it silently booted to a login prompt instead.
BISECTED (not just suspected): reverting only these two files, rebuilding
the bootable image, and re-running test_release_e2e.sh made all 5 cases
pass including norecord's halt (30/0). Re-applying them reproduces the
failure. Root cause not further isolated -- the fix is the revert, not a
patch, per the tradeoff below.
DECIDED TO REVERT, NOT FIX IN PLACE, because the .setattr hook bought
PRODUCT INSTALL almost nothing: vmsfs_blkdev_create() already assigns
every new file VMSFS_PROT_DEFAULT and the creating process's own UIC, and
tools/ovmx_kit_pack.c's OVMX_KIT_PROT_DEFAULT / OVMX_KIT_UIC_*_DEFAULT are
numerically IDENTICAL to those (0xAA00, SYSTEM [1,4]) -- so for every kit
ovmx_kit_pack produces today, PRODUCT INSTALL's fchmod(2)/fchown(2) calls
in src/product/product.c were already redundant with what create() sets
at file-creation time. Without the kernel change those calls still run,
still return success (the kernel's generic simple_setattr() fallback),
and still fail loudly on a real error -- they just would not durably
override a value that DIFFERS from the create()-time default, which no
current kit needs. That is a real, but narrow and non-security-relevant
(a new file can never land more permissive than VMSFS_PROT_DEFAULT
regardless), gap -- tracked as a follow-up (vms-738) rather than carried
as boot-breaking kernel surface. src/product/product.c's comments are
updated to describe this honestly instead of claiming the hook is needed.
RE-VERIFIED GREEN after the revert, same rebuilt image:
- tests/qemu/test_release_e2e.sh: 30 passed, 0 failed (all 5 cases,
including norecord's halt).
- tests/qemu/test_product_install_e2e.sh (vms-df9's own ground-source
proof): 15 passed, 0 failed -- PRODUCT INSTALL, RUN of the installed
HELP.EXE from the target, PRODUCT SHOW PRODUCT, and persistence across
a full QEMU restart all still hold with the kernel change gone.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: alice <alice@workspace.local>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
baron-3dl
added a commit
that referenced
this pull request
Aug 13, 2026
…XE integrated-assembler .s->.OBJ (#418) Self-host spine #2 (vms-486). Two deliverables. (A) Port MMK's 704-line MACRO-32 TPARSE table (freeware MadGoat MMK, tests/corpus/tier3-mmk/parse_tables.mar — BSD, NOT VSI/HPE/DEC source) into C against the OVMX lib$table_parse engine (vms-9f6), since the contract forbids a MACRO-32 assembler. Both grammars (PARSE_DESCRIP 67 states, PARSE_OBJECTS 25 states) are transcribed state-by-state into the OVMX TPA_GRAMMAR format: - tests/libvms/mmk_parse_tables.{c,h} — the port (clean-room, Rule 8: structure/behaviour from the freeware .mar only; TPA_GRAMMAR layout is an OVMX design choice labelled in tpadef.h). - tests/libvms/test_mmk_parse_tables.c — drives real descrip.mms lines and object strings through the real engine and asserts the fired PRS_K_ / PO_K_ codes + tokens (directives incl. the V2.8 SUFFIXES_* variants, symbol defs, build rules, both dependency paths, command prefixes; object simple/list/library/module=file forms). No facade. Porting the .mar surfaced three genuine engine-fidelity gaps in lib_tparse.c that MMK's table requires; all fixed with the correct VMS semantics and covered by new cases in test_lib_tparse.c: 1. Keyword match must terminate on a word boundary, else 'SUFFIXES' swallows 'SUFFIXES_AFTER' (V2.8's newest feature would be dead code). 2. A failed transition must restore input position (undo pre-skipped blanks), or CMD_PREFIXED's BLANK->EXIT terminator never fires. 3. A TPA$_LAMBDA (null) transition must not pre-skip/consume blanks, or a terminal lambda eats the trailing blank CONTINUE/SYMBOL1 depend on. (B) TCC.EXE integrated-assembler path. TCC.EXE compiles in tccasm.c + the arch asm backend; guess_filetype routes .s->AFF_TYPE_ASM->tcc_assemble; the OVMX RMS-I/O seam delivers the .OBJ type-agnostically — so `.s->.OBJ` is wired. src/imgact/test/run_tcc_asm_roundtrip.sh proves the assembler mechanism on the exact vendored tinycc source (build, assemble GAS .s -> ELF .o, link, run). Caveats documented: tinycc's assembler accepts a GAS subset (rejects gcc CFI/ endbr/section-flags); and on OVMX the assemble->LINK.EXE->activate round-trip hits the SAME pre-existing GOT-local-symbol LINK.EXE gap as the .c path (run_tcc_object_native.sh / vms-4ba.3) — a LINK.EXE issue, not an assembler one. No new cross-image symbols (grammar/tests are test-side; engine already exported), so no shareable-vector changes. Full Debug ctest: exit 0. Co-authored-by: alice <alice@workspace.local> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
baron-3dl
added a commit
that referenced
this pull request
Aug 13, 2026
#445) Bump OVMX_PRODUCT_VERSION V0.4 → V0.4-1. 24 PRs / 32 commits since V0.4, packed across dimensions (point release toward the 0.5 milestone): self-host (R7) #409 lib$tparse · #411 CLI$ compiled-CLD · #413 sys$setddir #414 lib$get_foreign · #415 sys$filescan (RTL foundation) #418 parse_tables.mar→C (spine #2) · #429 LIBRARIAN.EXE+.OLB (spine #3) #435 shareable-vector freeze (GSMATCH stability) authenticity #421 veracity rubric (Q1 oracle-source/Q2 real-inject) · #424 30 oracle-pinned constants #433 rmsdef.h 74 fabricated RMS codes → oracle UX/DCL/RMS #422 SHOW CLUSTER real membership · #441 DCL per-@-level local scope #442 RMS XAB dates → VMS 1858-epoch quadword networking #419 virtio NIC (user-mode default + opt-in tap/bridge) docs #423 clustering release train + swept: other threads' merged work on main since V0.4 Clustering config-authoring UX (vms-098) + its public-manual grounding gate remain 0.5 (minor) deliverables — not triggered by this point cut. Co-authored-by: alice <alice@workspace.local> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
baron-3dl
added a commit
that referenced
this pull request
Aug 13, 2026
…ee head must be pointer-width (#463) MMK.EXE SIGSEGV'd in the guest before sp_open (post-parse/pre-drive), nondeterministically and only with a real executive. Prior work (vms-b23 #462) characterized it via a SIGSEGV handler but lacked a real backtrace. Root cause (real backtrace, this commit): a 64-bit pointer-width bug. objects.c declares the LIB$*_TREE root cell as `static unsigned int objtree` — 4 bytes. On the VAX a longword IS a pointer so stock MMK is correct; on a 64-bit OVMX target that cell is too small. lib$insert_tree / lib$lookup_tree take the head by reference and dereference it as a full 8-byte pointer, so they: - over-READ 8 bytes of a 4-byte global (the adjacent global's 4 bytes become the high half of a bogus root pointer), and - on insert, over-WRITE, truncating the stored root node address. The reconstructed garbage pointer is later dereferenced -> SIGSEGV. Whether it faults depends on address-space layout, which is exactly why it was nondeterministic and "executive-dependent" (the executive changes the heap layout); on the host the reconstructed pointer happened not to fault, so MMK reached sp_open cleanly. Pinned with a real backtrace via ASan on the host mmk_native ELF, run with the identical input the guest capstone uses (VMS_FOREIGN_CMD="/DESCRIPTION=OVMXB23.MMS OVMXB23.OUT"): ERROR: AddressSanitizer: global-buffer-overflow ... READ of size 8 #0 lib$lookup_tree src/libvms/rtl/lib_tree.c:108 #1 Find_Object tests/corpus/tier3-mmk/objects.c:102 #2 make_objrefs tests/corpus/tier3-mmk/parse_descrip.c:1135 #3 parse_store tests/corpus/tier3-mmk/parse_descrip.c:1051 #4 act_prs tests/libvms/mmk_parse_tables.c:85 ... lib$table_parse -> parse_descrip -> Read_Description #9 main tests/corpus/tier3-mmk/mmk.c:705 0 bytes after global variable 'objtree' (size 4) Fix: declare objtree pointer-width (`void *`), matching symbols.c's apply_sort() `void *tree` and the LIB$ manual's quadword tree head on 64-bit architectures. After the fix ASan is clean on the same input and MMK proceeds through the object tree to the drive (sp_open). Clean-room (Rule 8): objects.c is stock MadGoat freeware; the one-line width change is an OVMX portability fix, tagged inline. Proof: - ASan before: global-buffer-overflow at lib_tree.c:108 (above). - ASan after: clean; MMK reaches the drive (same as host baseline). - toolchain-mmk-parse ctest: PASS (no host regression). - build-static (musl) mmk_native: builds clean. Co-authored-by: alice <alice@workspace.local> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
baron-3dl
pushed a commit
that referenced
this pull request
Aug 13, 2026
…ous bound), not a 2s reap grace CI's Kernel Executive job went red from a clean build: mmk_build produced the object and echoed the marker (build-#1 assertions GREEN) but MMK had not yet finished tearing down its spawned DCL within the tight 2s REAP_GRACE, so reap1 stayed 0, the reap1 short-circuit skipped drive #2, and the completion + byte-identity assertions reddened. CI's TCG is much slower than the dev host, where reap1 was always 1. Restructure drive_build to a SINGLE generous bounded wait (40s) that drains output (detecting the marker) AND polls for MMK to exit, returning the instant MMK exits -- so a green drive costs only its real runtime and the bound is only ever hit by a genuine hang. Removes the split 10s-marker / 2s-reap phases that were sized for a fast dev host. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
baron-3dl
pushed a commit
that referenced
this pull request
Aug 13, 2026
…nstead of waiting on MMK's exit The clean-build repro showed mmk_build's remaining flake was MMK's slow self-exit under contended TCG: the compile finished, the marker was echoed and the byte-identical object was on disk, but MMK had not yet torn down its spawned DCL and exited within the bound, so the reap-based assertion reddened. MMK's exit timing is not a property this suite tests. drive_build now stops the instant the PROOF is captured -- the DCL echoed OVMXD1B:COMPILED AND the object exists on disk -- and kills MMK as cleanup rather than gating on its self-exit. A genuine mid-drive $HIBER deadlock still fails hard (no marker is ever echoed). The completion assertion is now the marker (reliable), the reap-exit assertion is dropped, and the drive-#2 short-circuit is keyed on the object (robust under load), not on MMK's exit. The negctl declared set is unchanged (the 5 object/byte-identity assertions; the marker stays green). Verified: two consecutive clean green runs (mmk_build 6/6), and mmk-build-image-not-activated reddens exactly the 5 object assertions with the marker green. (A QEMU timeout / mmk_drive reap-flake seen intermittently here is this 10-container dev host's load, not the code: on CI the harness completed and mmk_drive passed.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
baron-3dl
added a commit
that referenced
this pull request
Aug 13, 2026
…byte-identical, zero-bash (CI gate) (#476) * vms-d1b: self-host spine #6 — MMK drives a real TCC compile in QEMU, byte-identical, zero-bash (CI gate) The shipped MMK.EXE now drives a REAL toolchain step inside OVMX in QEMU, against a real /dev/vms: it spawns a persistent DCL over VMS mailboxes and streams a descrip.mms whose action defines a foreign command TCC :== "$..." and invokes it to compile the REAL src/libvmssys freestanding runtime TU vms_string.c to an object. DCL activates the foreign command by fork()+execve() of the staged static TCC.EXE (a plain static image is not in-process-eligible, so imgact_activate returns SS$_UNSUPPORTED and DCL forks it) — the FIRST-EVER TCC.EXE run inside QEMU and the first time MMK drives a real compiler (not a DCL builtin) end to end. The parent (which never runs a compiler) asserts the driven object is a valid ELF relocatable carrying vms_strlen and is BYTE-IDENTICAL across two independent in-guest MMK-driven builds. Zero bash in the build path. This closes spine #6 (vms-d1b, the CI gate) and the MMK-driven-EXECUTION residual of spine #5 (vms-fe4) for the COMPILE stage. - tests/toolchain/mk_tcc_static.sh: builds tinycc as a PLAIN STATIC (musl) foreign-command image (distinct from mk_tcc.sh's IMGACT-packaged self-host image) — the binary DCL fork+execve activates, no IMGACT/shareable staging. - tests/toolchain/run_tcc_static_component.sh + CMake test toolchain-tcc-static-component: host proof the static TCC.EXE compiles the real runtime TUs (vms_string/vms_snprintf/driver) to valid, byte-identical objects; asserts vms_math.c is the documented x86 tcc-blocked TU (SSE "x" inline asm). - tests/qemu/test_syssvc_mmk_build.c: the QEMU suite (extends spine #4's test_syssvc_mmk_drive.c); honest-skips 77 with no /dev/vms. - tests/qemu/Dockerfile: stages static TCC.EXE at SYS$SYSTEM, tinycc's headers + musl's stdint.h closure beside it, and the real component source. The suite plugs into the STANDING kernel-executive CI barrier (builds the image from the checked-out tree, a clean context), so the MMK-driven native build is gated on every run — no new job. - facility_defects.sh: new per-facility control mmk-build-image-not-activated — dcl_exec_foreign_command reports success WITHOUT activating the image, so the driven TCC command completes but runs no compiler; reddens exactly the suite's five object/byte-identity assertions FAST (no $HIBER wedge), attributable to the build drive alone. A DEDICATED control, not a second suite on the sp_send=0 drive control: two ~50s $HIBER wedges do not fit run_tests.sh's 120s QEMU budget in one boot. Floor 96 -> 97. Verified in QEMU on this host: kernel-executive 76/76 (mmk_build 7/7); mmk-build-image-not-activated reddens exactly the 5 object assertions with no strays and the harness completes (no timeout); executive-absent mmk_build rc=77. BUILD.COM retirement is NOT done: the full compile->archive->LINK-to-image chain in-guest remains (vms_math not tcc-compilable on x86_64; LINK needs the SYS$LIBRARY shareables staged + logical-name resolution in LINK.EXE + IMGACT activation) — the precise residual for spine #7, documented in docs/design-self-host-spine5-mmk-component.md. No red gate shipped (Rule 6/7). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * vms-d1b: fix mmk_build CI timing — wait for MMK to EXIT (single generous bound), not a 2s reap grace CI's Kernel Executive job went red from a clean build: mmk_build produced the object and echoed the marker (build-#1 assertions GREEN) but MMK had not yet finished tearing down its spawned DCL within the tight 2s REAP_GRACE, so reap1 stayed 0, the reap1 short-circuit skipped drive #2, and the completion + byte-identity assertions reddened. CI's TCG is much slower than the dev host, where reap1 was always 1. Restructure drive_build to a SINGLE generous bounded wait (40s) that drains output (detecting the marker) AND polls for MMK to exit, returning the instant MMK exits -- so a green drive costs only its real runtime and the bound is only ever hit by a genuine hang. Removes the split 10s-marker / 2s-reap phases that were sized for a fast dev host. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * vms-d1b: make mmk_build load-robust — capture proof (marker+object) instead of waiting on MMK's exit The clean-build repro showed mmk_build's remaining flake was MMK's slow self-exit under contended TCG: the compile finished, the marker was echoed and the byte-identical object was on disk, but MMK had not yet torn down its spawned DCL and exited within the bound, so the reap-based assertion reddened. MMK's exit timing is not a property this suite tests. drive_build now stops the instant the PROOF is captured -- the DCL echoed OVMXD1B:COMPILED AND the object exists on disk -- and kills MMK as cleanup rather than gating on its self-exit. A genuine mid-drive $HIBER deadlock still fails hard (no marker is ever echoed). The completion assertion is now the marker (reliable), the reap-exit assertion is dropped, and the drive-#2 short-circuit is keyed on the object (robust under load), not on MMK's exit. The negctl declared set is unchanged (the 5 object/byte-identity assertions; the marker stays green). Verified: two consecutive clean green runs (mmk_build 6/6), and mmk-build-image-not-activated reddens exactly the 5 object assertions with the marker green. (A QEMU timeout / mmk_drive reap-flake seen intermittently here is this 10-container dev host's load, not the code: on CI the harness completed and mmk_drive passed.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: alice <alice@workspace.local> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This was referenced Aug 13, 2026
Merged
baron-3dl
pushed a commit
that referenced
this pull request
Aug 13, 2026
…aise per-drive bound The first cut wired the LINK+activate onto BOTH in-guest MMK drives. Under slow/ contended TCG that reddened intermittently: the 40s per-drive bound elapsed AFTER the archive but BEFORE the LINK (the drive was progressing, not wedged), killing MMK mid-drive; and two heavy full-chain drives pushed the whole suite toward run_tests.sh's 120s whole-VM budget. mmk_build passed 3x then failed once (7/11) — a Rule 8 flake, not shippable. Fix (mmk_build now 16/0 across repeated runs, whole VM 76/76 + green when the host has capacity): - Drive #1 is the lighter compile+archive drive (vms-6be); drive #2 runs the full compile→archive→LINK chain + activation. The compile+archive OUTPUTS are still asserted byte-identical across the two drives; the LINK runs once. - The LINK OUTPUT's byte-identity is proven byte-identical on the host (run_mmk_component_build.sh links the image twice, cmp-clean); in-guest the rung proves the driven LINK yields a real image that ACTIVATES and RUNS (exit 216) — the property that could not be shown before. This keeps the suite close to the vms-6be weight the kernel-executive barrier already carries reliably. - Per-drive bound raised 40s→60s so a slow-but-progressing full drive completes the LINK before cleanup. Negctl mmk-build-image-not-activated: the two dropped image assertions (build #2 / image byte-identity) removed from knock_on_fail; the four LINK+ activate assertions kept (produced / valid ET_DYN / PT_INTERP / activated 216), knock_on_why updated. Assertion set: 1 require + 14 knock = the 15 non-completion assertions. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
baron-3dl
pushed a commit
that referenced
this pull request
Aug 13, 2026
…whole-VM CI budget CI revealed the real budget: the kernel-executive gate runs ~76 suites in ONE QEMU VM under run_tests.sh's fixed `timeout 120`, already near the edge on CI's slower runner. The prior cut's mmk_build PASSED on CI (16/0), but the VM then hit 120s during the very NEXT suite (test_syssvc_mmk_drive), and the qemu timeout (SIGTERM) reddened the whole run. My two-runtime-TU + LINK+activate suite was the tipping weight. Fix: each drive now compiles ONE runtime TU (vms_string), not two, making the suite LIGHTER than the vms-6be compile+archive suite (−2 heavy TCC compiles, + a cheap driver compile + fast LINK + instant activation). Kept: two-drive compile+archive byte-identity, the LINK+activate proof (drive #2), exit-216 activation. The archive is now single-member; LIBRARIAN archiving MULTIPLE members and the LINK output's byte-identity remain host-proven (run_mmk_component_build.sh). Assertion set: 1 require + 14 knock still equals the 15 non-completion assertions; negctl + docs updated to match. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
baron-3dl
added a commit
that referenced
this pull request
Aug 13, 2026
…INK→ACTIVATE in QEMU (zero-bash CI gate) (#493) * vms-725: self-host spine #7 FINAL rung — MMK drives compile→archive→LINK→ACTIVATE in QEMU, byte-identical, zero-bash (CI gate) Extends spine #7 archive (vms-6be, #488) with the LINK-to-runnable-image rung. The shipped MMK.EXE now drives the WHOLE OVMX-native build chain over its persistent mailbox-driven DCL against a real /dev/vms, and the harness activates the produced image through IMGACT to its oracle exit: TCC.EXE compiles VMS_STRING.C + VMS_SNPRINTF.C + the driver OVMXRTRUN.C LIBRARIAN.EXE /CREATEs OVMXRT.OLB from the two runtime objects LINK.EXE --executable --use DECC$SHR.EXE links OVMXRTRUN.OBJ + OVMXRT.OLB into OVMXRT.EXE (selectively pulling VMS_STRING from the library) [activate] the harness fork+execs OVMXRT.EXE -> the kernel loads its PT_INTERP=/vms/.../IMGACT.EXE, which maps DECC$SHR from SYS$LIBRARY and binds the one cross-image import -> it RUNS to exit 216 (vms_strlen("OVMXRT")*36 -- nowhere else for 216 to come from) Every stage byte-identical across two in-guest MMK-driven builds. Green in QEMU: test_syssvc_mmk_build 18/0, from a clean tests/qemu/Dockerfile build. Self-host's final MMK-driven rung: MMK builds a real OVMX component to a running image entirely inside OVMX. Key findings / choices: - Only DECC$SHR is --use'd, NOT the "six shareables" the residual anticipated: the component is freestanding (its sole external symbol is vms_strlen, in the .OLB), so the executable's only cross-image import is crt0/exit from DECC$SHR. - The producer graph (IMGACT.EXE via src/imgact/Makefile; DECC$SHR.EXE via mk_decc_shr.sh whole-archiving musl libc.a + libgcc.a through the static LINK.EXE) BUILDS CLEAN in the ubuntu+musl-gcc Dockerfile -- the alpine-only assumption was unfounded. - `LNK`, not `LINK`: LINK IS the built-in DCL verb; LNK (not a prefix of it) falls through to the foreign-command symbol and forks the staged LINK.EXE -- the same trap as LIBRARIAN vs LIBRARY (vms-6be). The `$` in the absolute DECC$SHR.EXE --use path is an ordinary VMS filename char in the raw DCL tail. - vms_snprintf stays archive-only: TCC compiles its varargs to tinycc's __va_arg helper, which DECC$SHR does not export, so the runnable image pulls VMS_STRING only. The .OLB still carries both members (proven). Staging (tests/qemu/Dockerfile): build the static vmslink LINK.EXE + IMGACT.EXE (make ARCH=x86_64) + DECC$SHR.EXE (mk_decc_shr.sh); stage LINK.EXE/IMGACT.EXE at SYS$SYSTEM, DECC$SHR.EXE at SYS$LIBRARY, OVMXRTRUN.C in the component dir. Negctl: the existing mmk-build-image-not-activated control now also reddens the six LINK+activate assertions (no driven toolchain -> no image -> no exit 216); all declared in its knock_on_fail with extended knock_on_why. Floor unchanged. BUILD.COM STAYS (Rule 6/7). It is NOT cleared by this proof: it is still load-bearing for the S4 self-host FIXPOINT (run_link_selfhost_native.sh:136 copies + drives it multi-TU to build LINK.EXE gen2==gen3, a 1.0 gate) AND run_build_com_native.sh (S3.2). The vms-725 MMK chain builds a small 2-TU component, not the multi-TU LINK.EXE self-host fixpoint. Retirement waits on porting that fixpoint from BUILD.COM to an MMK descrip.mms -- tracked as vms-89d. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * vms-725: fix flaky mmk_build gate — run LINK+activate on one drive, raise per-drive bound The first cut wired the LINK+activate onto BOTH in-guest MMK drives. Under slow/ contended TCG that reddened intermittently: the 40s per-drive bound elapsed AFTER the archive but BEFORE the LINK (the drive was progressing, not wedged), killing MMK mid-drive; and two heavy full-chain drives pushed the whole suite toward run_tests.sh's 120s whole-VM budget. mmk_build passed 3x then failed once (7/11) — a Rule 8 flake, not shippable. Fix (mmk_build now 16/0 across repeated runs, whole VM 76/76 + green when the host has capacity): - Drive #1 is the lighter compile+archive drive (vms-6be); drive #2 runs the full compile→archive→LINK chain + activation. The compile+archive OUTPUTS are still asserted byte-identical across the two drives; the LINK runs once. - The LINK OUTPUT's byte-identity is proven byte-identical on the host (run_mmk_component_build.sh links the image twice, cmp-clean); in-guest the rung proves the driven LINK yields a real image that ACTIVATES and RUNS (exit 216) — the property that could not be shown before. This keeps the suite close to the vms-6be weight the kernel-executive barrier already carries reliably. - Per-drive bound raised 40s→60s so a slow-but-progressing full drive completes the LINK before cleanup. Negctl mmk-build-image-not-activated: the two dropped image assertions (build #2 / image byte-identity) removed from knock_on_fail; the four LINK+ activate assertions kept (produced / valid ET_DYN / PT_INTERP / activated 216), knock_on_why updated. Assertion set: 1 require + 14 knock = the 15 non-completion assertions. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * vms-725: shrink mmk_build to one runtime TU per drive — fit the 120s whole-VM CI budget CI revealed the real budget: the kernel-executive gate runs ~76 suites in ONE QEMU VM under run_tests.sh's fixed `timeout 120`, already near the edge on CI's slower runner. The prior cut's mmk_build PASSED on CI (16/0), but the VM then hit 120s during the very NEXT suite (test_syssvc_mmk_drive), and the qemu timeout (SIGTERM) reddened the whole run. My two-runtime-TU + LINK+activate suite was the tipping weight. Fix: each drive now compiles ONE runtime TU (vms_string), not two, making the suite LIGHTER than the vms-6be compile+archive suite (−2 heavy TCC compiles, + a cheap driver compile + fast LINK + instant activation). Kept: two-drive compile+archive byte-identity, the LINK+activate proof (drive #2), exit-216 activation. The archive is now single-member; LIBRARIAN archiving MULTIPLE members and the LINK output's byte-identity remain host-proven (run_mmk_component_build.sh). Assertion set: 1 require + 14 knock still equals the 15 non-completion assertions; negctl + docs updated to match. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: alice <alice@workspace.local> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This was referenced Aug 14, 2026
baron-3dl
added a commit
that referenced
this pull request
Aug 14, 2026
…real boot
Both boot proofs the PR wires into CI failed on genuine ASSERTION bugs, not
boot bugs. The conversational-boot mechanism and cluster-param authoring are
correct; the authored values (SCSNODE=CLUX, SCSSYSTEMID=1027) are provably in
effect in the booted guest.
1. test_sysboot_cluster_params_e2e.sh — F$GETSYI("SCSSYSTEMID") returns an
INTEGER (unlike the string NODENAME), so DCL renders the symbol UNQUOTED
with Hex/Octal columns: "SIDP = 1027 Hex = 00000403 Octal = ...". The
test wrongly expected the string form 'SIDP = "1027"' / 'SIDD = "0"'. Anchor
on the value AND its hex (0x403 == 1027, 0x0 == 0) so a wrong value cannot
pass. CASE 1 + CASE 2 both fixed.
2. test_sysboot_conversational.sh (Boot B, vms-b81) — the "nothing precedes
SYSBOOT>" check demanded the pre-prompt console region be byte-empty, which
is impossible: expect's own spawn echo, SeaBIOS, "Booting from ROM", the ANSI
clear-screen, and the substrate identity line "OVMX/Linux -- SYSKRNL" always
precede it. That is why it never passed — it had never been RUN in CI (the
gap this PR closes). The design's real claim (design-boot-faithful.md §3.1:
"No banner precedes SYSBOOT>") and the oracle itself (the SRM `P00>>>`
bootstrap block precedes SYSBOOT>) show the load-bearing proof is: no VMS
BANNER and no executive narration precedes the prompt. Assert exactly that —
the pre-prompt slice carries no `%OVMX-` line and no `OpenVMX Vx.x` banner —
which still fails hard if the executive-attach line leaks before SYSBOOT>.
No source, boot goldens, or seeded OVMXVMSSYS.PAR touched.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
baron-3dl
added a commit
that referenced
this pull request
Aug 14, 2026
…arams (#550) * vms-46c (gap #2): prove & CI-wire conversational boot for cluster params The conversational-boot MECHANISM (SYSBOOT> halts pre-banner on the boot flag; SHOW/SET/USE/WRITE/CONTINUE against SYS$SYSTEM:OVMXVMSSYS.PAR) landed with vms-b81. Two gaps remained against gap #2's outcome, both closed here: 1. test_sysboot_conversational.sh (vms-b81's own proof: pre-banner halt, byte-shaped SHOW table, SET SCSNODE + CONTINUE, in-memory persistence semantics) was NEVER wired into any CI job -- an unrun test is an absent test (Rule 7). Wired into the persistent-boot (boot-smoke) job + the `boot` paths filter; job budget bumped 30->40m for the added expect run. 2. The NUMERIC cluster-param path through SYSBOOT was untested -- the existing proof only drives SET SCSNODE (string). New e2e (test_sysboot_cluster_params_e2e.sh + run wrapper + ctest reg + CI job sysboot-cluster-params-e2e) authors a string (SCSNODE) AND a numeric cluster-identity param (SCSSYSTEMID) at the SYSBOOT> prompt, WRITEs a real vmsfs ;2, CONTINUEs, then proves in the booted logged-in guest that F$GETSYI reads BOTH authored values back -- with a flagless bracket boot on a fresh disk showing the seeded defaults. This is the "cluster params authored interactively before boot" proof (docs/design-boot-faithful.md sec 2.2/4.2) -- the clustering relevance of conversational boot. No source, boot goldens, or the seeded OVMXVMSSYS.PAR were touched; the pinned faithful-boot conformance sequence is unchanged. Gaps #1/#3/#4 of the epic remain open (separate dispatches). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * vms-46c gap #2: fix two never-run SYSBOOT> proof assertions to match real boot Both boot proofs the PR wires into CI failed on genuine ASSERTION bugs, not boot bugs. The conversational-boot mechanism and cluster-param authoring are correct; the authored values (SCSNODE=CLUX, SCSSYSTEMID=1027) are provably in effect in the booted guest. 1. test_sysboot_cluster_params_e2e.sh — F$GETSYI("SCSSYSTEMID") returns an INTEGER (unlike the string NODENAME), so DCL renders the symbol UNQUOTED with Hex/Octal columns: "SIDP = 1027 Hex = 00000403 Octal = ...". The test wrongly expected the string form 'SIDP = "1027"' / 'SIDD = "0"'. Anchor on the value AND its hex (0x403 == 1027, 0x0 == 0) so a wrong value cannot pass. CASE 1 + CASE 2 both fixed. 2. test_sysboot_conversational.sh (Boot B, vms-b81) — the "nothing precedes SYSBOOT>" check demanded the pre-prompt console region be byte-empty, which is impossible: expect's own spawn echo, SeaBIOS, "Booting from ROM", the ANSI clear-screen, and the substrate identity line "OVMX/Linux -- SYSKRNL" always precede it. That is why it never passed — it had never been RUN in CI (the gap this PR closes). The design's real claim (design-boot-faithful.md §3.1: "No banner precedes SYSBOOT>") and the oracle itself (the SRM `P00>>>` bootstrap block precedes SYSBOOT>) show the load-bearing proof is: no VMS BANNER and no executive narration precedes the prompt. Assert exactly that — the pre-prompt slice carries no `%OVMX-` line and no `OpenVMX Vx.x` banner — which still fails hard if the executive-attach line leaks before SYSBOOT>. No source, boot goldens, or seeded OVMXVMSSYS.PAR touched. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
baron-3dl
added a commit
that referenced
this pull request
Aug 17, 2026
…t add-on (#638) Fast-follow to #636 (vms-9d4f). #636 fixed drive #1's (compile+archive) budget to scale with the measured in-guest compile speed, but left drive #2 (do_link=1: compile+archive+LINK+activate) with a flat +20000ms add-on atop the calibrated compile term instead of scaling that extra work too. Robustness testing (docker run --cpus=<throttle>, simulating slow TCG) found the gap: at ~8.5x throttle (t_cal 897ms vs ~106ms baseline), drive #2's LINK+activate stage missed its budget under the OLD fully-fixed 60000ms constant -- a real instance of the fixed-host-wall failure mode #636 targets. But #636's shipped design (flat +20000) did not close this either: since the flat add-on is capped and doesn't grow with slowdown, it degrades to the same floor-dominated behavior as the old constant at any throttle level that doesn't already clear the compile-margin term alone. This file's own history already says the LINK+activate cost is TCG-speed-dependent (the 40000->60000 raise was explicitly "under slow/contended TCG the whole chain can exceed the old bound") -- so it should scale with t_cal like the compile term does, not sit behind a flat constant. Fix: replace DRIVE_LINK_OVERHEAD_MS (flat +20000) with DRIVE_COMPILE_MARGIN_LINK (6, vs drive #1's margin of 3), applied to the SAME calibrated t_cal. Both drives now scale proportionally with measured guest speed; only DRIVE_FIXED_OVERHEAD_MS (spawn/mailbox/dispatch/marker, 8000ms) stays flat. On a fast host this is unchanged (both drives still floor at the historical 60000ms neighborhood -- verified 16/16 green under the faithful gate, docker build -f tests/qemu/Dockerfile + docker run, 1533/1533 assertions passing). On a slow host both drives' budgets now grow with the SAME multiplier the guest is actually running under. SEMANTIC assertion unchanged: MMK.EXE still drives the real spawn + mailbox + write-attention AST + $HIBER + IO$M_NOW + $STATUS path to the real OVMXD1B:COMPILED marker; facility_defects negctl anchor (mmk-build-image-not- activated) untouched. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
baron-3dl
added a commit
that referenced
this pull request
Aug 18, 2026
…fix SYSUAF encrypt byte to 3, drop false purdy claim BLOCKER #1 (rms-put-wrong-vbn negctl was a dead no-op): the refactor moved the a.vbn computation out of the rms_io_write() dispatch wrapper into the static rms_io_write_acp() backend, so the sed anchored to /^ssize_t rms_io_write/,/^}$/ matched nothing -- cmd_apply reported BROKEN FIXTURE (rc=3), reddening the kernel shard, and the correct-VBN $PUT-over-ACP guarantee went unguarded. Re-anchor to /^static ssize_t rms_io_write_acp/,/^}$/. Verified: apply now injects (rc=0) on the +1u->+2u line in write_acp only (read_acp untouched); a second apply is BROKEN FIXTURE (idempotency intact); facility_defects.sh selftest now reports the defect ok. BLOCKER #2 (SYSUAF UAF$B_ENCRYPT off-by-one, resolves vms-722): uaidef.h had AD_II=1/PURDY=2/PURDY_V=3/PURDY_S=4, so OVMX stamped 4 on disk and rejected !=4 -- a non-authentic byte that would reject a genuine VMS SYSUAF. The oracle DUMP measured 0x03 on VAX 7.3 + Alpha 8.4 and public $UAIDEF enumerates PURDY_S=3; corrected uaidef.h to AD_II=0/PURDY=1/PURDY_V=2/PURDY_S=3 (CUST=128, the customer-algorithm base). Regenerated the seed SYSUAF.DAT via mksysuaf -- SYSTEM data record now carries encrypt=0x03; byte-reproducible. Reader/writer both key off UAI$C_PURDY_S so they agree at 3. Pinned the test to the literal oracle value (b[0x168]==3, UAI$C_PURDY_S==3). Reconciled the sysuaf.h source-of-truth note (conflict RESOLVED, answer 3). SMELL (purdy.c overclaim): the header claimed verification against "200 published DecHpwd PURDY_S calibration vectors -- see test_purdy.c", but test_purdy.c carries only the 7 real-VMS oracle vectors. Sourcing/fabricating a 200-vector corpus in-rung is infeasible/forbidden (circular self-assertion), so the false cross-reference is DELETED; the true 7-oracle-vector claim stays. Verify: 46/46 libvms/rms functional ctests green (incl. sysuaf record byte==3, vmsrms_sysuaf_live SYSTEM/MANAGER, purdy 7-vector); rms-put-wrong-vbn selftest ok. Pre-existing selftest FAILs (rightslist-general-hex-as-decimal, sysuaf-uic-writeback-decimal stale anchors; rms_p3 no-anchor; missing assertion text) are OUT OF SCOPE for this rung -- flagged to conductor. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
baron-3dl
added a commit
that referenced
this pull request
Aug 22, 2026
B rung #2: getdvi/showdev need ETH0:. virtio-net-pci + 5 virtio-blk hangs the clipper boot pre-/init (the virtio combination). Feasibility resolved cheaply first: the executive enumerates ANY netdev as ETH0: (generic for_each_netdev, exec_kbackend.h -- NOT virtio-net-specific), and the Alpha kernel already has CONFIG_TULIP=y. So use a DEC Tulip (21143) NIC -- non-virtio, driver present, period-appropriate for Alpha -- which brings ETH0: up without the virtio-net hang. One boot to confirm it doesn't hit a PCI-device-count limit too. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
baron-3dl
added a commit
that referenced
this pull request
Aug 28, 2026
… (block-then-grant on the requester) (#875) * vms-6ca: DLM H5 executive core — requester-side GRANT RECEIVE + deferred-grant report The cross-node contention semantics (#873) proved block-then-grant on a single real /dev/vms. This adds the two executive halves the async-reply SCS wire needs: - MASTER side: vms_lock_dlm_xnode_deq now REPORTS the queued cross-node waiter that a release flipped to granted (its requester CSID + original req_lkid + master handle + granted mode), through the fields a DEQ otherwise leaves 0, so the daemon can WIRE a deferred GRANT to the requester without a second probe. The master lock entry now carries the requester's own lock handle (req_lkid). - REQUESTER side: VMS_DLM_OP_GRANT is implemented (was SS$_UNSUPPORTED). A GRANT / queued-reply the master sent back completes an executive-resident ORIGIN record — the requester-side proxy of the outstanding request, on its own list, never touched by the local lock manager. Its granted mode is set ONLY from what the master genuinely sent over SCS: a queued-reply (mode NL) leaves it pending, a deferred GRANT (mode EX) flips it NL->EX. GETLKI falls through to it, so the status flip is observable on the REQUESTER node. INV-6: genuine executive state, no fabricated grant. BLKAST as a receive op stays SS$_UNSUPPORTED — the BLKAST wire is deferred honestly on this rung (the block-then-grant round-trip is proven without it). test_syssvc_dlm_xnode.c extended to prove both halves against a real /dev/vms (x86_64 + Alpha LP64): the deferred-grant report on the block-then-grant $DEQ, and the requester-side queued->granted origin flip. vms.ko builds clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * vms-6ca: DLM H5 SCS wire — queued-reply + deferred-GRANT delivery over the live VC The scsd daemon now carries the async DLM replies over the real SCS wire, so the block-then-grant completes on the REQUESTER node across the network — not just in the master's local executive (#873). Node B (master): the GRANT frame it sends back now carries the granted mode honestly — a queued-reply carries NL (the requester stays genuinely pending), a grant carries the granted mode — plus the master's lock handle, so the requester can $DEQ the holder. On a cross-node $DEQ that flips a queued waiter to granted, B reads the executive's deferred-grant report and WIRES an unprompted deferred GRANT (SS$_NORMAL, the granted mode) to that requester (SCSD-I-DLMDEFER). Node A (requester, armed by OVMX_DLM_H5): drives block-then-grant over the wire — holds RESONE EX (#1), sends a second incompatible $ENQ (#2) that QUEUES on B, dispatches B's queued-reply into its OWN executive so #2's origin record is genuinely PENDING (GETLKI->NL, SCSD-I-DLMPEND), releases the holder (#1), and on B's deferred GRANT dispatches it into its executive — the origin record FLIPS NL->EX (GETLKI->EX, SCSD-I-DLMH5FLIP). The status flip is read from A's real executive, driven only by what the master sent over SCS. INV-6: no fabricated wire reply or grant; the BLKAST wire stays deferred (the holder releases on its own). scsd builds clean; vmsscs unit tests green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * vms-6ca: DLM H5 two-node QEMU harness + CI job — block-then-grant over the wire Extends the H4 two-node real-/dev/vms harness to H5. Both nodes boot vms.ko and complete the VMS$VAXcluster join; node A is armed OVMX_DLM_ENQ=RESONE OVMX_DLM_H5=1 and drives the block-then-grant sequence over the live SCS wire: holds RESONE EX, a second incompatible $ENQ QUEUES on node B (B WIREs a queued-reply -> A's requester origin record PENDING, SCSD-I-DLMPEND), A releases the holder, node B WIREs the deferred GRANT off that real $DEQ (SCSD-I-DLMDEFER), and A's origin record FLIPS NL->EX (SCSD-I-DLMH5FLIP granted_mode=EX). H5 PASS asserts the status flip on the REQUESTER node A across the wire, plus B's deferred-grant wire — read from the nodes' own SCSD logs, never fabricated (INV-6). New Dockerfile.dlm-harness-h5 + init_dlm_h5.sh + run_dlm_harness_h5.sh; new dlm-harness-h5 CI job (path-filtered on the DLM sources + harness files); the existing dlm_harness filter already covers vms_lock.c + src/vmsscs/**. Shell + YAML syntax validated. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * vms-6ca: DLM H5 — record the async-reply wire + requester-side completion in the compat ledger docs/compat/facilities/cluster-dlm.yaml (single-ledger source, INV-LEDGER): new cluster-dlm$async-reply-wire item for the H5 rung — the master WIREs the queued-reply + deferred GRANT over SCS, the requester-side GRANT RECEIVE completes an executive-resident origin record, and the NL->EX flip is observed on the requester. Notes the BLKAST wire deferred honestly and multi-peer deferred-grant routing as a later rung. Summary updated; H5 evidence points at the two-node harness. YAML validated. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * vms-6ca: regenerate compat surface register for the H5 async-reply-wire item Derived docs/compatibility-surface.md re-rendered from docs/compat/*.yaml via tools/compat/render_compat.py (INV-LEDGER: yaml is source, md is generated). 407 items now catalogued. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci: quote the job name with a mid-value colon — repairs ci.yml parse (dark since #850) The job name added today by #850 (vms-dec) contained an unquoted mid-value colon ('... before Username: (vms-dec)'), which is invalid YAML. GitHub Actions could not parse ci.yml, so EVERY run of it — on main and every branch — completed with ZERO jobs (no Build & Test, no harnesses, nothing); the only checks still firing were the separate Ledger-drift and CodeQL workflows. Quoting the value restores the whole workflow. Pre-existing project-wide breakage, surfaced while wiring the H5 CI job (which could not run until ci.yml parsed). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * vms-6ca: register the H5 client sender in the send-site census + repair H0's stale selftest op Two fixes surfaced by re-enabling ci.yml: 1. scs_send_sites census: the new scsd_dlm_client_send_op() sender (node A's contending $ENQ + holder $DEQ) is added to the CHOKED half of the SEND SITE TABLE in scsd.c; the scsd_dlm_srv_msg_input entry now notes the deferred GRANT it also wires. test_scsd_send_sites: 109 checks, 0 failures. 2. DLM Harness H0 selftest: it drove a DEQ expecting SS$_UNSUPPORTED (2296), but rung 3 (#873, vms-904c) made DEQ real — a DEQ of an unknown handle now returns SS$_IVLOCKID (8484), so H0 had been red since #873 (hidden by the dark CI). The side-effect-free "executive reached, honest 2296" probe now uses BLKAST, whose receive op still honestly returns SS$_UNSUPPORTED (its wire is deferred, vms-6ca) and mutates no lock state. No run/init/ci grep change needed — still rc=2296 PASS. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
baron-3dl
added a commit
that referenced
this pull request
Sep 2, 2026
…fire (vms-74f) Layer 3 frame builders #2/#3 + the safe scsd checkpoint before the FSM: - scs_member_build_dlm_op04 / _commit now take OVMX's OWN real per-lock handle (lkid) and write it at body[20:24] -- the handle the executive DLM holds for the lock (from the vms-1f4 accessor). Per the conductor's handle-chain trace, op-04 and op-03 carry the JOINER's own node-local handle, NEVER VAX3's un-replayable kernel bytes nor the coordinator's granted mst_lkid; the ungrounded second handle word @[24:28] stays ZERO (INV-6 -- don't invent). lkid==0 reproduces the old content-free frame (null case). test_scs_member pins lkid@[20:24], the zeroed second word, NL mode, and no-resname; suite ALL PASSED. - cm_send_dlm_completion is now lkid-aware (function-pointer signature updated). - The OPT-A content-free completion FIRE (post-op-06) is DISABLED: it destabilized the cluster (op-03 with no real op-01 = dangling transaction -> 2/2 reformations, lab-proven). Firing nothing keeps the branch safe until Layer 3's registration FSM lands. That FSM (next commit): post-op-06 enumerate the standing locks, send op-01 per lock to the coordinator, and on the coordinator's cat-82 op-01 grant arrival send op-04 -> op-03 with OVMX's real handle. scsd.c -fsyntax-only clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PhM3QcmFEY3p8YNnHGaYwP
baron-3dl
added a commit
that referenced
this pull request
Sep 8, 2026
…in fixes; strip diagnostics Rung 4 of the vms-b4f ladder. Boots the CRTL->RMS veneer-wired alpha-dec-vms GCC-port image on qemu-system-alpha + the real /dev/vms executive: its decc$fopen -> ovmx_crtl veneer -> sys$create -> LIBVMSRMS$SHR -> ioctl(/dev/vms) -> Files-11 ACP writes PORTTEST.DAT, then an INDEPENDENT reader (DCL DIRECTORY/FULL, a different accessor than the writer's own CRTL/RMS handle) asserts PORTTEST.DAT;1 landed on the real ODS-2 volume with a genuine File ID AND the full 8192-byte content (16 blocks) -- something a ramfs/POSIX write can never produce in the ACP directory. The gate keys on that fid+content landing (strictly stronger than a same-CRTL round-trip a ramfs satisfies), with a 7/7 can-fail selftest. Three first-exercise toolchain bugs the forcing function exposed: - #1 emutls control-object width (LLP64): unsigned long is 32-bit on alpha-dec-vms, so __emutls_object {size,align,loc,templ} packed loc at offset 8 instead of 16 and __emutls_get_address returned 4 -> SIGSEGV. emutls_word widened to unsigned long long. (committed earlier as e2c6cf5) - #2 DECC$SHR symbol-vector index skew (mk_decc_shr.sh): the veneer pass dropped the 4 fopen/fwrite/fread/fclose entries from the middle of the sorted vector and re-appended the aliases at the tail, shifting every higher sv# down by 4. IMGACT binds cross-image imports BY INDEX, so producers linked against the bootstrap DECC dispatched e.g. decc$strlen[sv#414] to decc$strspn at runtime (NULL-arg SIGSEGV). Rewrite the 4 entries IN PLACE at their sorted slot, restoring the append-only sv# stability the recipe already documented. - #3 calloc weak-override reloc (link.c): the vms-430 strong-over-weak base-redirect matched a section-relative reloc's section BASE before the addend was added, so every sibling symbol in a $CODE$ whose offset-0 proc is an overridden weak def (calloc.o's __malloc_allzerop) was pulled onto the strong def + addend -- decc$_calloc64 (real calloc at $CODE$+0x008) mis-bound onto strong __malloc_allzerop+0x008. Match base+addend and consume the addend on a hit; the symbol-target path is byte-identical. run_muldef_evax.sh green (incl. weak-first + strong-first self-bind redirect). All diagnostic scaffolding stripped (IMGACT SIGSEGV handler / IMGACT-MAP probe / qemu -d int injection) for a clean production activator. The writer program's post-commit mallocng cleanup crash (free -> free_group -> free(g->mem) hitting get_meta's `assert(meta->mem==base)` with a NULL group meta) is a separate mallocng-group-release issue on the alpha-dec-vms substrate, tracked as bug #4 (blocks vms-fd1); it fires AFTER the content commits and does not affect the proven landing. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FJZf62TMXxvy6fXzFQYfLQ
baron-3dl
added a commit
that referenced
this pull request
Sep 9, 2026
…fc LLP64 width fix) (#1063) * vms-f49: rung 4 — un-fakeable ODS-2 independent-reader proof + vms-1fc LLP64 width fix Lands the MILESTONE of the vms-b4f ladder: prove the alpha-dec-vms GCC-port image's CRTL fopen genuinely writes to a real Files-11 ODS-2 volume over the executive ACP, not musl-ramfs — the anti-fabrication payoff (INV-6). Two coupled items in one PR, because the width fix is VALIDATED by the proof. PART A — vms-1fc (LLP64 syscall width fix). On the alpha-dec-vms C model `long`/`unsigned long` are 32 bits while pointers are 64 (LLP64), so the libvmssys raw-syscall path truncated every pointer argument to the /dev/vms transport — the ioctl(/dev/vms, ...) RMS-over-ACP write landed on a garbage address and reached nothing. - vms_syscall.h: widen __vms_syscall0..6 params + return to a guaranteed- 64-bit `vms_reg_t` (== long long), and the vms_sys_* pointer casts with it; widen vms_sys_ioctl's `arg` param from `unsigned long` to vms_reg_t. - arch/alpha/syscall_vms.c: match the widened prototypes (long long). - kif_transport_linux.c: cast the request-block pointer through vms_reg_t, not `unsigned long` — THIS is the /dev/vms pointer the proof exercises. - vms_bgsock.c: widen its ioctl pointer casts to match. No-op on the LP64 targets (x86_64/aarch64/alpha-linux-gnu: long long == long, byte-identical codegen); the actual fix only on alpha-dec-vms. VAX is untouched by construction — it takes the __NetBSD__ branch (arch/vax/vms_syscall_netbsd.h) and compiles none of these declarations. PART B — vms-f49 (rung 4, the un-fakeable gate). New `crtl-rms-veneer-gate` mode of run-module-gp-activation-alpha.sh boots the veneer-wired crtl_rms port image (JOINT_CRTL_RMS_VENEER=1) on the real /dev/vms + qemu-system-alpha; its decc$fopen -> the crtl_rms_stdio veneer -> sys$create/$put -> LIBVMSRMS$SHR -> ioctl(/dev/vms) -> ACP writes PORTTEST.DAT. Then an INDEPENDENT reader — DCL DIRECTORY/FULL, a DIFFERENT accessor than the writer's CRTL/RMS handle, running its own sys$search over the ACP directory — asserts PORTTEST.DAT;1 exists on the ODS-2 volume with a genuine ODS-2 File ID that a ramfs write cannot produce. - SYSTARTUP_VMS_VENEER_PROOF.COM: RUN JOINT_E2E, then DIRECTORY/FULL PORTTEST.DAT. - build-alpha-bootimage.sh: stage LIBVMSRMS$SHR.EXE into SYS$SHARE and swap in the veneer-proof SYSTARTUP when a veneer build is present (keyed on the shareable), verify it on the mastered volume. - assert_veneer(): gates on the independent File-ID reader, NOT console/CRTL state; a can-fail selftest proves teeth incl. the NEGATIVE/REJECTION case — a same-CRTL success that ramfs satisfies (%DIRECT-W-NOFILES) must FAIL. - ci.yml: new PR job alpha-crtl-rms-veneer (alpha_activation scope, 150m). The non-veneer alpha-crtl-rms-n7 gate stays green as the control. Local build+link verified: the veneer graph links zero-deferred under the alpha-dec-vms cc1 with the width fix (decc$fopen->DECC$SHR, veneer sys$create/$put/$get->LIBVMSRMS$SHR), LIBVMSRMS$SHR.EXE emitted; assert_veneer selftest passes all six fixtures. The qemu-alpha runtime proof runs in CI. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FJZf62TMXxvy6fXzFQYfLQ * vms-f49: fix apostrophe quote-break in build-alpha-bootimage.sh staging block The vms-f49 staging comments landed inside the assemble `docker run ... bash -c '...'` SINGLE-QUOTED block with apostrophes ("image's"), which closed the quote mid-body and exposed `decc$fopen` to the outer shell -> `line 91: fopen: unbound variable` under `set -u`. This broke the boot-image assembly for EVERY alpha activation gate that calls assemble_boot_image (gate/crtl-rms-gate/mf-gate/ crtl-rms-veneer-gate), before any qemu boot -- NOT a Part A regression (all three reds died at the identical line-91 quote-break after "step 1 staged", pre-boot; Part A links clean, proven by the green alpha RMS-substrate STRICT-link + DECC$SHR jobs). The script's own header warns "no apostrophes in this block". Reworded the two added comments apostrophe-free; verified the docker -c body now has balanced single-quotes with zero outer-shell `$` exposure. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FJZf62TMXxvy6fXzFQYfLQ * vms-f49: stage the full RMS producer graph + fix its DECC producer name (rung-4 activation) The rung-4 veneer gate booted this time (N=7 control PASSED -> Part A width fix does NOT regress alpha activation) but the veneer image RUN drew %IMGACT-F-IMGNOTFND: LIBVMSRMS$SHR is not self-contained. It transitively imports from the whole executive producer graph (LIBVMS/LIBVMSFS/LIBVMSLNM/LIBVMSPROCESS/ LIBVMSSYS$SHR) AND recorded its DECC producer as the phantom pass-1 bootstrap name "DECC1$SHR.EXE" -- neither on SYS$SHARE, so IMGACT could not resolve them. Two in-scope Part-B wiring fixes (no new executive facility): - build-joint-image.sh: build the pass-1 bootstrap DECC under $WORK/p1 with the BASENAME DECC$SHR.EXE (not DECC1$SHR.EXE). LINK records producers by basename, so the graph + LIBVMSRMS$SHR now record "DECC$SHR.EXE" and, at activation, IMGACT name-keyed binding resolves them against the SINGLE staged pass-2 (veneer) DECC$SHR.EXE (GSMATCH LEQUAL). One DECC$SHR at runtime, no duplicate musl C-RTL. Also emit the whole producer graph to OUTDIR. - run-module-gp-activation-alpha.sh + build-alpha-bootimage.sh: stage the full graph (LIBVMSRMS/LIBVMS/LIBVMSFS/LIBVMSLNM/LIBVMSPROCESS/LIBVMSSYS$SHR) into SYS$SHARE and verify each on the mastered ODS-2 volume. Also fixes a second single-quote apostrophe break ("IMGACT's", and 'DECC$SHR.EXE' exposing $SHR) in the build-joint-image.sh docker bash -c block. Local verify: veneer graph links zero-deferred; LIBVMSRMS$SHR + all 7 sibling shareables now record DECC$SHR.EXE (grep DECC1 = 0 across the whole staged set); full graph emitted to OUTDIR. Runtime activation runs in CI. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FJZf62TMXxvy6fXzFQYfLQ * vms-f49: surface the guest-kernel faulting PC in the veneer-gate failure output The veneer image now activates (producer graph staged) but SIGSEGVs (%DCL-F-ABORT signal 11) in the first-ever runtime execution of the alpha RMS substrate over the veneer. The Alpha guest kernel prints the faulting user PC/RA/VA to the console at fault time, but the gate's fixed pattern-grep never surfaced it. Dump the guest fault-signature line(s) + the last 60 console lines in the veneer-gate FAIL path so the authoritative fault PC is captured in CI (disk-safe -- no qemu -d flags; the guest kernel already emitted it). This localizes the crash so it can be fixed. Not a gate weakening -- failure path only. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FJZf62TMXxvy6fXzFQYfLQ * vms-f49: capture qemu CPU-exception log to pin the veneer SIGSEGV faulting PC The previous fault-capture confirmed the veneer image crashes at/near ACTIVATION (no OVMX-CRTL-RMS sys$create trace precedes the %DCL-F-ABORT signal 11), and the Alpha guest kernel prints no userspace fault line. So add qemu exception logging (-d int,cpu_reset,guest_errors -D /work/qint.log), enabled ONLY for the crtl-rms-veneer-gate via QEMU_DBG, and dump the last exceptions (faulting PC/VA) in the veneer FAIL path. Disk-safe: the boot reaches Username: within ~30-60s so qint.log stays small; other gates pass QEMU_DBG empty (unchanged). This is the gdb-equivalent authoritative fault PC needed to decide RMS-substrate truncation (hyp 1) vs IMGACT mutual-producer-cycle activation crash (hyp 2, the leading hypothesis: the veneer DECC$SHR<->LIBVMSRMS$SHR cycle the non-veneer control lacks). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FJZf62TMXxvy6fXzFQYfLQ * vms-f49: filter qemu exception log (clk-interrupt firehose hid the fault) The -d int log is dominated by clk_interrupt; the veneer SIGSEGV's Dfault/MMFAULT exception with the faulting user pc= is buried. Filter out clk/dev interrupts and add an exception-type histogram so the fault exception + PC is surfaced. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FJZf62TMXxvy6fXzFQYfLQ * vms-f49: IMGACT-MAP producer base logging to resolve the veneer fault PC The qemu -d int log localized the veneer SIGSEGV to a repeated mmfault loop at user pc=0x12005eb00 (and 0x12005a7f4). IMGACT is only ~37KB so the fault is in a mmap'd producer (likely DECC$SHR, which holds the veneer ovmx_crtl_fopen). Print each producer's runtime base (IMGACT-MAP: <name> base=0x..) at load so the faulting pc can be resolved to <image>+offset and then to a symbol. Diagnostic only; other gates grep their own patterns so the extra lines are inert. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FJZf62TMXxvy6fXzFQYfLQ * vms-f49: log MAIN-EXE + IMGACT-INTERP bases to place the 0x120000000 fault region Producer bases are all 0x20000xxxxx, but the veneer fault PCs cluster in the 0x120000000 range (0x12005eb00 repeated 18x = the unrecoverable fault; other single-hit pcs are benign TLB fills). Log the kernel-mapped main-exe bias and the PT_INTERP (IMGACT) base so the faulting region can be attributed to the main image, the interp, or an unmapped bad-jump target (mis-resolved cross-image linkage = hyp 2). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FJZf62TMXxvy6fXzFQYfLQ * vms-f49: gate IMGACT-MAP behind OVMX_IMGACT_MAP=1 (silent by default) Housekeeping: the IMGACT-MAP producer-base logging now emits only when the boot cmdline carries OVMX_IMGACT_MAP=1 (kept for fault-localization, silent in production). run_boot_a gains an optional QEMU_APPEND injection; the veneer gate sets QEMU_APPEND=OVMX_IMGACT_MAP=1 so it still gets the map. The QEMU_DBG -d int fault capture is retained (inert for other gates). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FJZf62TMXxvy6fXzFQYfLQ * vms-f49: Option-1 probe — log wild (0x120000000-region) import bindings in IMGACT The veneer SIGSEGV jumps to 0x120000000+offset (default/stack-top base) while all images map at 0x200_xxxx_xxxx; the value is COMPUTED at runtime (not stored). Add a gated (OVMX_IMGACT_MAP=1) probe in bind_imports that logs any binding whose resolved PV or filled code entry *(PV+8) lands in the wild region -- with the importing image, the symbol, the cell, the PV, and the entry. A wild PV isolates an SV-value fault; a sane PV with a wild entry isolates a producer PDSC-entry rebase fault. Surface IMGACT-WILD/IMGACT-MAP in the veneer-gate failure dump. If nothing fires, the wild target is code/GP-computed (not a linkage fill) and the next step is a register (RA) capture. Probe only; no behavior change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FJZf62TMXxvy6fXzFQYfLQ * vms-f49: correct the Option-1 probe wild-region range (0x1_xx, not the 0x200xxx image region) The probe flagged all valid 0x200_xxxx_xxxx bindings as wild because the upper bound (0x200000000000) sat above the real image region. Narrow it to [0x1_0000_0000, 0x100_0000_0000) -- the 0x120000000 default/stack-top region -- so only genuinely-wild values fire. Confirmed against the CI run: with the correct range NO binding is wild (all PVs/entries resolve to 0x200xxx), proving the veneer SIGSEGV is NOT a linkage fill but a wrong-base code jump. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FJZf62TMXxvy6fXzFQYfLQ * vms-f49: fix emutls control-object width (LLP64) — the veneer rung-4 crash root cause The alpha emulated-TLS runtime typed the control-object fields as `unsigned long` (emutls_word), which on the alpha-dec-vms LLP64 target is 32 BITS — but the cc1 emits the control with 64-bit .quad fields (.quad size; .quad align; .quad loc; .quad templ). So struct __emutls_object packed size+align into the first 8 bytes and put `loc` at offset 8 (the align field) instead of 16. __emutls_get_address returned obj->loc = the align value (4), and the first __thread access on the veneer's sys$create path (a vms_kif 'vms_bound_pid == getpid()' check) then dereferenced 4 -> SIGSEGV at 0x4 — the rung-4 (vms-f49) blocker, pinned via a local qemu-system-alpha boot + an IMGACT SIGSEGV-handler RA capture. Fix: emutls_word -> unsigned long long (64-bit on every target), so loc lands at offset 16 and templ at 24, matching the .quad emission. Same LLP64 bug class as vms-1fc. Arch-scoped: the whole file is #if defined(__alpha__), so x86_64/aarch64 (musl TLS) and VAX (NetBSD) never compile it — the non-veneer + VAX/x86_64 controls stay byte-identical. Verified locally: the v0=4 / gp-as-stack SIGSEGV is gone; the veneer image now advances past the emutls point into the Files-11 path. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FJZf62TMXxvy6fXzFQYfLQ * vms-f49: un-fakeable ODS-2 landing proof (rung 4) + bug #2/#3 toolchain fixes; strip diagnostics Rung 4 of the vms-b4f ladder. Boots the CRTL->RMS veneer-wired alpha-dec-vms GCC-port image on qemu-system-alpha + the real /dev/vms executive: its decc$fopen -> ovmx_crtl veneer -> sys$create -> LIBVMSRMS$SHR -> ioctl(/dev/vms) -> Files-11 ACP writes PORTTEST.DAT, then an INDEPENDENT reader (DCL DIRECTORY/FULL, a different accessor than the writer's own CRTL/RMS handle) asserts PORTTEST.DAT;1 landed on the real ODS-2 volume with a genuine File ID AND the full 8192-byte content (16 blocks) -- something a ramfs/POSIX write can never produce in the ACP directory. The gate keys on that fid+content landing (strictly stronger than a same-CRTL round-trip a ramfs satisfies), with a 7/7 can-fail selftest. Three first-exercise toolchain bugs the forcing function exposed: - #1 emutls control-object width (LLP64): unsigned long is 32-bit on alpha-dec-vms, so __emutls_object {size,align,loc,templ} packed loc at offset 8 instead of 16 and __emutls_get_address returned 4 -> SIGSEGV. emutls_word widened to unsigned long long. (committed earlier as e2c6cf5) - #2 DECC$SHR symbol-vector index skew (mk_decc_shr.sh): the veneer pass dropped the 4 fopen/fwrite/fread/fclose entries from the middle of the sorted vector and re-appended the aliases at the tail, shifting every higher sv# down by 4. IMGACT binds cross-image imports BY INDEX, so producers linked against the bootstrap DECC dispatched e.g. decc$strlen[sv#414] to decc$strspn at runtime (NULL-arg SIGSEGV). Rewrite the 4 entries IN PLACE at their sorted slot, restoring the append-only sv# stability the recipe already documented. - #3 calloc weak-override reloc (link.c): the vms-430 strong-over-weak base-redirect matched a section-relative reloc's section BASE before the addend was added, so every sibling symbol in a $CODE$ whose offset-0 proc is an overridden weak def (calloc.o's __malloc_allzerop) was pulled onto the strong def + addend -- decc$_calloc64 (real calloc at $CODE$+0x008) mis-bound onto strong __malloc_allzerop+0x008. Match base+addend and consume the addend on a hit; the symbol-target path is byte-identical. run_muldef_evax.sh green (incl. weak-first + strong-first self-bind redirect). All diagnostic scaffolding stripped (IMGACT SIGSEGV handler / IMGACT-MAP probe / qemu -d int injection) for a clean production activator. The writer program's post-commit mallocng cleanup crash (free -> free_group -> free(g->mem) hitting get_meta's `assert(meta->mem==base)` with a NULL group meta) is a separate mallocng-group-release issue on the alpha-dec-vms substrate, tracked as bug #4 (blocks vms-fd1); it fires AFTER the content commits and does not affect the proven landing. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FJZf62TMXxvy6fXzFQYfLQ * vms-b14: bounded calloc-family exception in the section-relative weak-override — land the alpha CRTL->RMS ODS-2 proof without regressing N=7 The rung-4 branch's earlier link.c change (base+addend full-target match) regressed the crtl_rms N=7 gate (gated-green at #958/#959): it dropped the weak-alias thunk redirects the mallocng heap needs. Reverting to the base-only redirect greened N=7 but crashed the veneer's calloc. Root-caused via print-fatal-signals PC capture (gdb isn't in the images): TWO oppositely-signed strong-sibling relocs that no reloc-local field can separate — - decc$_calloc64 (real calloc at $CODE$+0x8, past the offset-0 overridden weak __malloc_allzerop): base-only wrongly redirects it onto strong __malloc_allzerop+0x8 and calloc a_crash()es (veneer's early 0x4c618 crash). Must be LEFT. - the mallocng syscall/stdio thunks (decc$munmap/mremap/mmap/__syscall_cp, decc$fclose, __stdio_close): base-only correctly redirects them to their strong def; leaving them wild-jumps into the fork/execve code region (the 0x4a354 crash shared by N=7). Must be REDIRECTED. Both are byte-identical in every reloc field (psect/to_section/addend/type/ site-sym/target-sym/weak/overridden/self_ref), so this applies base-only to every base-coincident section-relative reloc EXCEPT the precise, structurally- detected calloc case: base-only's redirect would land inside strong __malloc_allzerop while the reloc's real target is a distinct sibling. Bounded workaround pending the weak_alias-granularity export-path fix (vms-f59). Gates: OVMX/Alpha crtl_rms N=7 = clean sentinel 7 ($STATUS=%X0035A039, port_ok=1); CRTL->RMS veneer = decc$fopen lands PORTTEST.DAT;1 on the ODS-2 volume, File ID (71,1,0) + full 16/16 blocks, confirmed by an INDEPENDENT DIRECTORY/FULL reader (un-fakeable). Known-tracked: the veneer image still signal-11s AFTER the write commits (pre-existing under every link variant; in the RMS-veneer cleanup path, not exercised by the clean N=7 round-trip) — filed as a follow-up under vms-fd1; the veneer gate proves the ODS-2 landing independent of that post-commit crash. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FJZf62TMXxvy6fXzFQYfLQ --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
baron-3dl
added a commit
that referenced
this pull request
Sep 11, 2026
…es-circuit (38→37) Give kernel-core/vms_pe.c a real injected negative control so it leaves the §1 "executive TU with NO negative control" FAIL set (38->37). Defect pe-vc-snapshot-fabricates-circuit: vms_pe_vc_snapshot()'s `cl->pe == NULL` guard returns SS__NORMAL instead of SS__NOSUCHDEV (range-scoped sed, only that one return), so a VC row the executive does not hold — an index far past the table — is reported as a live circuit (INV-6 fabrication: a placeholder reported real; the all-zero row is untouched, only the STATUS lies). suites_red: test_kmod_cluster_vc_diag; require_fail "row VC, index far past any table: SS$_NOSUCHDEV, not a crash" (anchored in the suite). The index-0 real-VC and no-circuit-all-zero branches read other paths and stay green. Static-proven (host): selftest injects + idempotent no-op-reapply teeth; coverage §1 drops vms_pe.c (38->37) + clears test_kmod_cluster_vc_diag from §2; dash -n clean. QEMU red-proof rides the CI per-facility negctl shard (green-by-SHA), the conductor's reap gate — same standard l2's local rail run validated the loop for. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HMDFjHCqxDuXgbyxNU572h
baron-3dl
added a commit
that referenced
this pull request
Sep 12, 2026
…es-circuit (38→37) Give kernel-core/vms_pe.c a real injected negative control so it leaves the §1 "executive TU with NO negative control" FAIL set (38->37). Defect pe-vc-snapshot-fabricates-circuit: vms_pe_vc_snapshot()'s `cl->pe == NULL` guard returns SS__NORMAL instead of SS__NOSUCHDEV (range-scoped sed, only that one return), so a VC row the executive does not hold — an index far past the table — is reported as a live circuit (INV-6 fabrication: a placeholder reported real; the all-zero row is untouched, only the STATUS lies). suites_red: test_kmod_cluster_vc_diag; require_fail "row VC, index far past any table: SS$_NOSUCHDEV, not a crash" (anchored in the suite). The index-0 real-VC and no-circuit-all-zero branches read other paths and stay green. Static-proven (host): selftest injects + idempotent no-op-reapply teeth; coverage §1 drops vms_pe.c (38->37) + clears test_kmod_cluster_vc_diag from §2; dash -n clean. QEMU red-proof rides the CI per-facility negctl shard (green-by-SHA), the conductor's reap gate — same standard l2's local rail run validated the loop for. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HMDFjHCqxDuXgbyxNU572h
baron-3dl
added a commit
that referenced
this pull request
Sep 12, 2026
…es-circuit (38→37) Give kernel-core/vms_pe.c a real injected negative control so it leaves the §1 "executive TU with NO negative control" FAIL set (38->37). Defect pe-vc-snapshot-fabricates-circuit: vms_pe_vc_snapshot()'s `cl->pe == NULL` guard returns SS__NORMAL instead of SS__NOSUCHDEV (range-scoped sed, only that one return), so a VC row the executive does not hold — an index far past the table — is reported as a live circuit (INV-6 fabrication: a placeholder reported real; the all-zero row is untouched, only the STATUS lies). suites_red: test_kmod_cluster_vc_diag; require_fail "row VC, index far past any table: SS$_NOSUCHDEV, not a crash" (anchored in the suite). The index-0 real-VC and no-circuit-all-zero branches read other paths and stay green. Static-proven (host): selftest injects + idempotent no-op-reapply teeth; coverage §1 drops vms_pe.c (38->37) + clears test_kmod_cluster_vc_diag from §2; dash -n clean. QEMU red-proof rides the CI per-facility negctl shard (green-by-SHA), the conductor's reap gate — same standard l2's local rail run validated the loop for. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HMDFjHCqxDuXgbyxNU572h
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Dispatch-ready decomposition of the executive gap. Plan doc only — no code. The worktree exists so the tree below can be dispatched against it.
Full map:
docs/design-executive-retrofit.md.Why the ordering is what it is
No CI job ever loads
vms.ko.persistent-bootruns its QEMU script inside a Docker container, and there is no kernel-module job (src/kernel/is excluded even from static analysis). Docker has no/dev/vms, and CI runs in Docker — so every executive facility that couldn't be tested defaulted to a per-process userspace fake that reports success.The architecture drifted to fit the test harness. That is why only
sys_lock.cwas ever wired, and it's why Phase 0 is a hard barrier: start anywhere else and the increment rots back into the fake, because that's the only path CI exercises.The tree
vms-e4dQEMU CI job loadsvms.kovms-ef1common event flag clusters ·vms-as1ASTs ·vms-pv1privilegesvms-71amigrate Docker CI jobs, deleteDockerfilevms-pt1process table ·vms-ln0LNM ruling →vms-d37LNM ·vms-dv1devices ·vms-mb1mailboxesvms-fk1delete the fakes, gate forbids returnPhase 1 is cheap and real:
vms.koalready implements event flags (including VMS common clusters viavms_common_ef_lock), ASTs, and access modes — ioctls0x01–0x27. Userspace simply never calls them. That's wiring, not design.Dispatchable right now
Dependencies are wired so exactly three items are ready, verified against
rd ready:vms-e4d— the Phase 0 barrier. Everything else waits on it.vms-ln0— the one genuine design fork left. Logical-name translation sits on the hot path of every file open, so ioctl-per-translation is not automatically right even though the rest of the executive is kernel-side. Operator-gated; wants measurement, not opinion.vms-b9f—SHOW DEVICEprints the host Linux mount table (volume labels are mount-point basenames, including the kernel version). Independent of the executive entirely; parallel-safe.Phase 1's three items are parallel-safe with each other once Phase 0 lands — disjoint facilities, separate files, good concurrent worktree dispatch.
Standing constraints, restated on every item
One runtime target (CLAUDE.md Rule 9) · never a silent userspace fallback — fail honestly with
SS$_NOSUCHDEVassys_lock.cdoes · not done until proven against a real/dev/vms· clean-room (Rule 8) · purity sign-off on VMS values.Note
Cut from
main, so CLAUDE.md Rule 9 and the roadmap §2.1 analysis (both in PR #1) aren't present here. The plan doc is self-contained and doesn't require them.🤖 Generated with Claude Code