Skip to content

Rewrite mmap emulation as page-granular region allocator (WARP-69) - #126

Merged
Arshia001 merged 1 commit into
mainfrom
warp69-page-granular-mmap
Jul 3, 2026
Merged

Rewrite mmap emulation as page-granular region allocator (WARP-69)#126
Arshia001 merged 1 commit into
mainfrom
warp69-page-granular-mmap

Conversation

@Arshia001

Copy link
Copy Markdown

Summary

Replaces the malloc-veneer mmap emulation in libc-bottom-half/mman/mman.c with a side-table region allocator that serves all mappings in whole 64 KiB wasm pages drawn directly from sbrk (memory.grow).

Design and rationale: WARP-69. Root fix for ECO-263.

Key change: anonymous mappings are no longer memset. Pages fresh from memory.grow are guaranteed zero by the wasm spec and are handed out untouched, so the host commits them lazily on first write instead of all-upfront. Only pages recycled from a previous munmap are zeroed on reuse.

Conformance fixes over the old shim

  • mmap returns page-aligned addresses (side table instead of an inline header before the mapping)
  • Partial munmap: any whole-page subrange can be unmapped, including splitting a mapping in the middle; freed pages coalesce and are recycled for future mappings
  • msync no longer rejects writable mappings (the PROT_WRITE check was inverted, so MAP_SHARED file writeback silently never happened — a data-loss bug); partial unmap writes back just the unmapped subrange with correct (fd, offset) on surviving fragments
  • Writes past EOF within the last page are not written back, and the tail past EOF reads as zero, per POSIX
  • MAP_NORESERVE accepted (commit is lazy anyway); failure paths return MAP_FAILED with a meaningful errno instead of NULL

Still unsupported without an MMU (unchanged): PROT_NONE/PROT_EXEC, MAP_FIXED, CoW private file mappings, continuous writeback.

Measurements

Under wasmer 7.2.0-rc.1, a 128 MiB anonymous mapping (the PHP opcache SHM pattern):

old emulation this PR
host RSS cost of the mapping ~128 MiB (fully committed at mmap time) ~0 until pages are actually written

End-to-end on PHPIX (PHP 8.1 ZTS server): idle RSS 906 → 781 MiB (−125 MiB); additionally, partial-munmap support un-blocks re-enabling Zend MM on WASIX, which measures 2.9× faster on allocation-heavy requests vs the tracked-malloc fallback.

Tests

Integration tests live in the wasmer repo (branch warp69-mman-tests, PR to follow after a prerelease of this change is pinned there): a new libc/mmap-page-granular test covering alignment, fresh/recycled zero-fill, partial munmap (including the over-allocate-and-trim pattern used by Zend MM), coalescing, file writeback via msync/munmap, and error semantics — plus the seven previously-ignored msync-*/munmap-sync-*/read-after-munmap tests, which this PR fixes (they were ignored due to the inverted msync check; four of them also used non-page-aligned file offsets, which POSIX rejects and this implementation now correctly rejects, and were fixed accordingly).

Full wasmer wasm_tests sweep against a sysroot built from this branch: no regressions (all failures identical under the old pinned sysroot and attributable to a missing local wasm32-wasip1 rustup target).

🤖 Generated with Claude Code

Replace the malloc-veneer mmap emulation with a side-table region
allocator that serves all mappings in whole 64 KiB wasm pages drawn
directly from sbrk (memory.grow).

Key change: anonymous mappings are no longer memset. Pages fresh from
memory.grow are guaranteed zero by the wasm spec and are handed out
untouched, so the host commits them lazily on first write instead of
all-upfront. A 128 MiB anonymous mapping now costs ~0 host RSS until
actually used (previously: full 128 MiB committed at mmap time). Pages
recycled from a previous munmap are zeroed on reuse to preserve
MAP_ANON semantics.

Conformance fixes over the old shim:
- mmap returns page-aligned addresses (side table instead of an inline
  header before the mapping)
- partial munmap: any whole-page subrange can be unmapped, including
  splitting a mapping in the middle; freed pages coalesce and are
  recycled for future mappings
- msync no longer rejects writable mappings (the PROT_WRITE check was
  inverted), so MAP_SHARED file writeback actually happens on msync and
  munmap; partial unmap writes back just the unmapped subrange
- writes past EOF within the last page are not written back, and the
  tail past EOF reads as zero, per POSIX
- MAP_NORESERVE is accepted (commit is lazy anyway); dup/pread failure
  paths return MAP_FAILED with a meaningful errno instead of NULL

Still unsupported without an MMU: PROT_NONE/PROT_EXEC, MAP_FIXED, CoW
private file mappings, continuous writeback.

Tested by lib/wasix/tests/wasm_tests/libc/mmap-page-granular in the
wasmer repository, which covers alignment, zero-fill of fresh and
recycled pages, partial munmap (including the over-allocate-and-trim
pattern used by allocators like PHP's Zend MM), free-run coalescing,
shared file writeback via msync/munmap, fragment writeback offsets
after splits, and error semantics.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Arshia001
Arshia001 merged commit 09503b2 into main Jul 3, 2026
5 checks passed
@Arshia001
Arshia001 deleted the warp69-page-granular-mmap branch July 3, 2026 11:55
Arshia001 added a commit to wasmerio/wasmer that referenced this pull request Jul 3, 2026
Pulls in the page-granular mmap emulation rewrite (WARP-69,
wasix-org/wasix-libc#126), required by the mmap tests in the previous
commit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Arshia001 added a commit to wasmerio/wasmer that referenced this pull request Jul 3, 2026
Pulls in the page-granular mmap emulation rewrite (WARP-69,
wasix-org/wasix-libc#126), required by the mmap tests in the previous
commit.

Co-Authored-By: Claude Fable 5 <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