dcl: MOUNT/DISMOUNT really mount(2)/umount(2) vmsfs volumes (vms-651) - #277
Merged
Merged
Conversation
KILLED the cmd_mount/cmd_dismount facade (src/vmsdcl/dcl_cmd_misc.c:1597): it never called mount(2), wrote a per-process userspace device table (struct vms_device / vms_device_table[], src/vmsdcl/dcl_builtin.c -- deleted, its only two readers), used getcwd() as the "mount path", and printed %MOUNT-I-MOUNTED unconditionally. MOUNT DKA100: now: checks PRV$M_MOUNT through the executive (vms_kif_chkpriv, not getuid() -- PRV$M_MOUNT is a new privilege bit, oracle-pinned public $PRVDEF position 17, wired into VMS_PRV_M_ENFORCED); resolves the unit's backing block device through the executive (vms_kif_disk_resolve, vms-3e8); claims the unit in the executive's device table (vms_kif_alloc, wired here for the first time, so a second process cannot mount the same unit concurrently); and mount(2)s "/dev/<backing>" as vmsfs at the unit's mount point. DISMOUNT is the mirror image with umount(2). "Already mounted" is answered from the kernel's own /proc/mounts, not a per-process field. THE PRIVILEGE-DROP WRINKLE, measured rather than assumed: LOGINOUT setuid()/setgid()'s every VMS session onto its SYSUAF UIC (tools/ vms_login.c), so by the time MOUNT runs the calling process holds no Linux capability regardless of PRV$M_MOUNT -- mount(2)/umount(2) require CAP_SYS_ADMIN unconditionally. A kernel-mediated mount (a vms.ko ioctl calling the mount-syscall internals with elevated creds) was tried and does NOT work on this platform: fs/namespace.c's do_mount()/path_mount()/ do_umount()/path_umount() are absent from this kernel's exported-symbol table entirely (confirmed against the real Module.symvers, not guessed), and the public fs_context replacement API has no exported way to graft a mount onto a path in a process's namespace. The settled design is the classic Unix shape instead: tools/vms_mount_helper.c, a small setuid-root binary that does only mount(2)/umount(2) and re-derives ITS OWN authorization from the executive (vms_kif_register_continue() +chkpriv) before doing anything privileged, so it never trusts its caller -- direct invocation registers as a fresh, unauthenticated process and is refused. cmd_mount/cmd_dismount fork, continue this session's identity onto the child, and exec the helper. Mount points are pre-created as root by src/ovmx_init/ovmx_init.c's new provision_disk_mount_points() (enumerating DC$_DISK units via the SAME vms_kif_devscan() table MOUNT reads) before any session drops privilege -- mkdir(2) is a DAC check a de-privileged session can't satisfy against root-owned /mnt either. Ground-source proof (tests/qemu/test_mount_e2e.sh + run_mount_e2e.sh, real vms.ko+vmsfs.ko, opt-in OVMX_QEMU_FULL_E2E=1): MOUNT DKA100:, COPY a real file onto it, DISMOUNT, re-MOUNT -> file still there; /proc/mounts shows the vmsfs mount while mounted and not after DISMOUNT; a full QEMU process restart against the same disk file -> the file survives, byte-identical (proves the disk, not a tmpfs); MOUNT of a nonexistent unit (DKA200:) fails honestly with no %MOUNT-I-MOUNTED. 17/17 assertions pass. tests/dcl/ test_mount.sh and test_show_device.sh (ctest, no /dev/vms) are rewritten to assert the honest-failure path instead of the deleted facade's success message -- MOUNT's own liveness anchor changed from "the facade always succeeds" to a bare WRITE, since MOUNT now asks the executive too and fails the same way SHOW DEVICE does with no executive present. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
VMS_PRV_M_ENFORCED gained PRV$M_MOUNT (bit 17, right after WORLD's bit 16) in the previous commit -- cmd_mount/cmd_dismount really gate on it now, so it belongs in the enforced set F$GETJPI CURPRIV/AUTHPRIV render (they walk VMS_PRV_M_ENFORCED bit by bit, ascending). SYSTEM/ALL's rendered string gains ",MOUNT" at the end. Measured against a real CI run (Kernel Executive + VMS User Acceptance Test jobs both failed on this exact string before the fix): three hardcoded expectations updated to match -- tests/qemu/test_syssvc_ident.c scenario F, its facility_defects.sh manifest line, and tests/uat/vms_session_qemu.sh's CURPRIV/AUTHPRIV checks (whose own comment already documented the update rule this follows). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… fixup) The previous commit inserted an 8-line explanatory comment BETWEEN test_syssvc_ident.c's `/* negctl-knockon: bind-client-no-register */` marker and the CHECK() it anchors -- every other marker in this file (and the anchoring reader in facility_defects.sh) assumes the marker sits DIRECTLY above the statement it names, with nothing in between. Moved the explanatory comment above the marker instead, restoring that adjacency. Measured against a real CI run (Kernel Executive — Per-Facility Negative Controls, shard 0/6): it reported "test_syssvc_ident.c:1851 anchors 'bind-client-no-register' but the statement under it names none of that defect's require_fail/knock_on_fail texts" -- the reader had walked into the explanatory comment instead of the CHECK() call two names anchors away. The same run's "derived suite(s) NAMED BY NO defect's suites_red" report (unrelated-looking suites: test_kmod_resdir, test_syssvc_rightslist, test_syssvc_sysuaf_uic_base) is suspected fallout from the same desync -- this fix's own next run tells us whether that suspicion holds. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
baron-3dl
force-pushed
the
worktree-agent-a5187fa021a23a823
branch
from
August 10, 2026 11:32
5b608bf to
f8e5063
Compare
This was referenced Aug 10, 2026
baron-3dl
added a commit
that referenced
this pull request
Aug 12, 2026
… self-emitted tokens (#375) A family of tests in tests/dcl/ traced to the self-emitted-token framework (f7a0169) asserted only tokens the test script itself always printed, or grepped the product's own hardcoded banners — so they could never fail no matter how broken or faked the underlying behavior was. Each is re-authored to assert a real, product-independent property with a genuine failure mode, proven red-on-defect and green-on-correct. - test_no_unix_leaks.sh (priority): the real guard (EXPECT_NOT on UNIX_LEAK_DETECTED) was written but commented out as FUTURE_EXPECT_NOT, and a detected leak was downgraded to a printed WARNING instead of a failure. Restored: any leak now sets UNIX_LEAK_DETECTED and the script exits non-zero. - test_vms_messages.sh: absence of "%" output used to read as OK. Now each of 11 scenarios (IVVERB, NOKEYW, DIRECT, RMS FNF/RNF, NOIFBLK x2, NOLAB, NOGOSUB, IVKEYW) asserts the SPECIFIC ident VMS raises for that condition, grounded in the source that raises it. This surfaced a real bug: RENAME of a nonexistent file segfaulted (dcl_cmd_file.c never included dcl/vms_messages.h, so vms_strerror() had no prototype and its pointer return value was truncated under implicit int-return assumption) — fixed with the missing include. - test_help_content.sh: HELP's listing does come from the live verb table, but the test never checked HELP actually dispatches on its argument. Added differential checks: HELP SHOW must produce the SHOW-specific Subcommands: block (not the generic listing), and an unknown topic must produce the real %DCL-W-NOHELP. - test_show_memory.sh: cross-checks SHOW MEMORY's page count (converted back to KB) against /proc/meminfo, read independently by the script, 2% tolerance. - test_sysgen.sh: removed the self-fulfilling fallback that ECHOED the exact literals ("MAXPROCESSCNT ... 64 ... 64") its own assertions checked for when SYSGEN.EXE wasn't found. Real check now: SET changes the value, SHOW reflects it (round-trip through the live working set), and an out-of-range SET is rejected with the real ident and leaves the value unchanged. Honest SKIP (SYSGEN_SKIPPED, not a fabricated pass) if the binary is genuinely absent. - test_tcpip_show_version.sh: was a regex matching the command's own printf template. Now cross-checks the version against SHOW SYSTEM, an independently-coded call site that reads the same SSOT — catches drift between the two. - test_mount.sh (vms-3bb): already re-armed by vms-651 (#277) with a real $STATUS-based check; confirmed it still fails on an injected facade success and needed no further change. All 152 ctest tests pass (9 e2e intentionally skipped, unrelated to this work); the dcl-integration suite (121 test_*.sh files) passes clean. Co-authored-by: alice <alice@workspace.local> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
Summary
cmd_mount/cmd_dismountfacade (src/vmsdcl/dcl_cmd_misc.c, plus the process-localvms_device_table[]indcl_builtin.c) that never calledmount(2), faked its device table, and usedgetcwd()as the "mount path".MOUNT DKA100:now checksPRV$M_MOUNTthrough the executive (new oracle-pinned privilege bit, wired intoVMS_PRV_M_ENFORCED), resolves the unit's backing block device viavms_kif_disk_resolve, claims the unit in the executive's device table (vms_kif_alloc, wired for the first time), andmount(2)s the backing device as vmsfs.DISMOUNTmirrors this withumount(2). "Already mounted" is answered from/proc/mounts, not a per-process struct.mount(2)/umount(2)(both requireCAP_SYS_ADMIN) regardless of VMS privilege. A kernel-mediated mount was attempted first and does not work on this platform —do_mount/path_mount/do_umount/path_umountare absent from the kernel's exported-symbol table entirely (verified against the realModule.symvers). Settled on the classic Unix shape instead:tools/vms_mount_helper.c, a small setuid-root binary that re-derives its ownPRV$M_MOUNTauthorization from the executive before doing anything privileged, so it never trusts its caller.provision_disk_mount_points()inovmx_init.c, before any session drops privilege.Test plan
tests/dcl/test_mount.sh/test_show_device.shrewritten for the honest-failure path (ctest, no/dev/vms) — both pass.ctestfull suite: 124/125 pass (the one pre-existing failure,env_identity_census_negctl, is an unrelated timeout on this host, unchanged by this PR).kif_caller_census,terminal_identity_gate,show_device_rows_gate/negctlall green (privilege bit + new wrapper wiring correctly detected).tests/qemu/test_mount_e2e.sh, opt-inOVMX_QEMU_FULL_E2E=1, realvms.ko+vmsfs.ko): 17/17 assertions pass — MOUNT/COPY/DISMOUNT/re-MOUNT round-trip,/proc/mountsreflects mount state correctly, a full QEMU process restart against the same disk file proves the file persisted to disk (not tmpfs), and a nonexistent-unit negative control fails honestly with no%MOUNT-I-MOUNTED.distro/Dockerfile.bootable, LINK.EXE graph) builds clean with the new symbol-vector entries.🤖 Generated with Claude Code
Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com