Menu string ownership and shared duplication — 2026-09-22
Pass-5 #5 asks for one guest string-duplication implementation. The current
tree already routes CRT, DirectPlay, scalable fonts, DDE, atoms, registered
clipboard formats, ICM, open-dialog paths, ListView and TreeView copies
through guest_strdup. Two menu paths still repeated its allocation/copy
sequence: dynamic_menu_take_text and the ANSI branch of
dynamic_menu_item_info_set. They now use the helper too.
The wide branch retains its UTF-16-to-ANSI conversion. Replacement still allocates before releasing old text, and non-string type data is unchanged. Counted WinHelp slices, relative-path construction, title truncation and encoding conversions are not interchangeable with NUL-terminated ANSI duplication and were not mechanically converted.
Safety defect found in the shared helper
guest_strlen caps its scan at 65,536 bytes, while the old guest_strdup
allocated that capped length plus one and then used unbounded guest_strcpy.
A longer valid input therefore overran its allocation. The helper now
continues the scan beyond the cap before allocating and bounds its copy to
the measured length plus NUL. The bounded behavior of guest_strlen itself
is unchanged. An enormous length that cannot fit the signed copy count
returns NULL; this is an emulator safety limit, not a measured Win98 limit.
test-crt-strdup.js checks lengths 65,535, 65,536, 65,537 and 70,000,
allocation capacity, complete bytes/terminator and independence from a later
allocation. A test-only compiler transform restoring the HEAD helper failed
the 70,000-byte allocation-capacity assertion. The fixed helper passes.
That negative control changed no production files or build artifacts.
The menu ownership suite also needed its free-chain inspection updated for the existing small-block allocator bins. It still checks actual release; no assertion was removed to hide a lifetime failure. New cases verify AppendMenuA copies caller storage and empty ANSI replacements are separately owned, terminated and retire the old label.
Verification passed: test-crt-strdup.js, test-menu-item-info-wide.js,
test-dynamic-menu-bar.js, test-crt-mb-cur-max-handler.js, fragment balance,
logical-operand checks, handler stack cleanup, tier discovery and whitespace.
These are shared-worktree tests, not a clean release build.
This change does not finish the entire string-copy or COM audit. No claim is made about invalid-pointer probing, concurrent mutation of caller strings, OOM fault injection, full Unicode menu fidelity, browser output or performance.