vms-d00: fix 64-bit width overflow in MMK.EXE early path (objtree tree head) - #463
Merged
Merged
Conversation
…ee head must be pointer-width 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: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
baron-3dl
added a commit
that referenced
this pull request
Aug 13, 2026
…ve (#472) Bump OVMX_PRODUCT_VERSION V0.4-2 → V0.4-3. 15 PRs since V0.4-2. Headline: the self-host toolchain now BUILDS — MMK.EXE drives real compile+link inside OVMX against a live executive. SELF-HOST #4 COMPLETE MMK.EXE genuinely drives compile+link builds vs real /dev/vms (#464 capstone). Full exec-drive substrate: async AST delivery + interruptible $HIBER (#457), IO$M_NOW (#458), DCL-over-mailbox (#460), + crash fixes #463 (32→64 ptr-width) / #464 (IO$M_NOW func-code mask). Freeze-join fix (#459). Component build host-proven (#470). UX FIDELITY SHOW CPU (#465), file protection SET/display (#467), RECALL readline-independent (#468), DCL scripting $STATUS/%X + CALL/SUBROUTINE + DECK/EOD (#469), DIRECTORY wildcards/ellipsis (#461). + swept other threads' merged work Self-host spine #5/#6 (MMK-drives-a-real-component IN QEMU) in flight. 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
MMK.EXE SIGSEGV'd in the QEMU guest before
sp_open(post-parse / pre-drive), nondeterministically, and only with a real executive (on the host the same path reachedsp_opencleanly). This blocked spine #4's capstone drive (vms-b23). Prior work (vms-b23 #462) characterized it via a SIGSEGV handler but lacked a real backtrace — this PR gets one and fixes the root cause.Root cause — a 64-bit pointer-width bug (not the stack/foreign-command overflow the SIGSEGV handler suggested)
tests/corpus/tier3-mmk/objects.cdeclared theLIB$*_TREEroot cell asstatic unsigned int objtree— 4 bytes. On the VAX a longword is a pointer, so stock MadGoat MMK is correct; on a 64-bit OVMX target that cell is too small.lib$insert_tree/lib$lookup_treetake the head by reference and dereference it as a full 8-byte pointer, so they: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.Real backtrace (the deliverable prior work lacked)
Pinned with ASan on the host
mmk_nativeELF, run with the identical input the guest capstone uses (VMS_FOREIGN_CMD="/DESCRIPTION=OVMXB23.MMS OVMXB23.OUT"):This lands exactly where the item said the crash was — post-parse, pre-drive, in
Read_Description's object-tree build.Fix
Declare
objtreepointer-width (void *), matchingsymbols.c'sapply_sort()void *tree(already correct) and the LIB$ manual's quadword tree head on 64-bit architectures. One-line change + an inline OVMX tag. Node links were already pointer-width (struct OBJECT *flink/blink), so only the head cell was wrong.Clean-room (Rule 8):
objects.cis stock MadGoat freeware; this is an OVMX 64-bit portability fix, tagged inline. No cross-image symbols touched → no.vecchanges.Proof
global-buffer-overflowatlib_tree.c:108(above)sp_open)toolchain-mmk-parsehost ctestbuild-static(musl)mmk_nativeWhy ASan is the right proof for this bug class: the fault was nondeterministic precisely because it's deterministic memory corruption whose fault depends on address layout. ASan's redzone detects the corruption on every run regardless of whether it would fault, so before/after ASan on the identical guest input is a stronger verdict than N QEMU boots that could each pass by luck with the bug still present.
Guest /
/dev/vmsstatus and next blockerMMK now reaches
sp_open(the drive). The live guest QEMU exercise belongs to vms-b23's capstone (test_syssvc_mmk_drive, deferred at0be3591c): that test staged this same staticMMK.EXEand drove the full build. This fix unblocks it past this crash — but the full drive then hits the separate completion-hang blocker vms-95c, so the capstone can only go green once vms-95c is also fixed. Answering the item's question directly: MMK reachessp_open; the next blocker is vms-95c.Fixes the crash tracked by vms-d00. Do not merge without CI review.
🤖 Generated with Claude Code