Skip to content

vms-d00: fix 64-bit width overflow in MMK.EXE early path (objtree tree head) - #463

Merged
baron-3dl merged 1 commit into
mainfrom
vms-d00-mmk-overflow-fix
Aug 13, 2026
Merged

baron-3dl merged 1 commit into
mainfrom
vms-d00-mmk-overflow-fix

Conversation

@baron-3dl

Copy link
Copy Markdown
Contributor

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 reached sp_open cleanly). 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.c declared the LIB$*_TREE root cell as static unsigned int objtree4 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_tree take the head by reference and dereference it as a full 8-byte pointer, so they:

  • over-READ 8 bytes of the 4-byte global (the adjacent global's 4 bytes become the high half of a bogus root pointer), and
  • on insert, over-WRITE it, 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.

Real backtrace (the deliverable prior work lacked)

Pinned with 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)

This lands exactly where the item said the crash was — post-parse, pre-drive, in Read_Description's object-tree build.

Fix

Declare objtree pointer-width (void *), matching symbols.c's apply_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.c is stock MadGoat freeware; this is an OVMX 64-bit portability fix, tagged inline. No cross-image symbols touched → no .vec changes.

Proof

Check Result
ASan before fix (exact guest input) global-buffer-overflow at lib_tree.c:108 (above)
ASan after fix (exact guest input) clean; MMK proceeds through the object tree and reaches the drive (sp_open)
toolchain-mmk-parse host ctest PASS — no host regression
build-static (musl) mmk_native builds clean (this is the exact static ELF staged into the guest)

Why 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/vms status and next blocker

MMK now reaches sp_open (the drive). The live guest QEMU exercise belongs to vms-b23's capstone (test_syssvc_mmk_drive, deferred at 0be3591c): that test staged this same static MMK.EXE and 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 reaches sp_open; the next blocker is vms-95c.

Fixes the crash tracked by vms-d00. Do not merge without CI review.

🤖 Generated with Claude Code

…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
baron-3dl merged commit 6045b68 into main Aug 13, 2026
61 checks passed
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant