Skip to content

mpk: restore protection keys after mmap'ing memory images - #14076

Draft
jlb6740 wants to merge 2 commits into
bytecodealliance:mainfrom
jlb6740:mpk-issue-13982-fix
Draft

mpk: restore protection keys after mmap'ing memory images#14076
jlb6740 wants to merge 2 commits into
bytecodealliance:mainfrom
jlb6740:mpk-issue-13982-fix

Conversation

@jlb6740

@jlb6740 jlb6740 commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

A fresh mmap associates the pages it replaces with the default protection key 0, and key 0 is accessible from every stripe (host code needs it). MemoryImageSlot maps over pkey-colored pool slots in three places, so any module with a (data ...) segment silently lost its key. Because MPK striping deliberately shrinks the guard regions between slots, a neighboring instance could then read and write that memory for real. Note that mprotect preserves the key, so only mmap sites are affected.

Fix this by re-applying the key with pkey_mprotect after each mmap: add ProtectionKey::reprotect, give MemoryImageSlot the key its stripe was colored with, and call the new reapply_pkey helper after map_at, remap_as_zeros_at, and erase_existing_mapping.

Tables, stacks, and GC heaps are never pkey-colored, and decommit uses madvise(MADV_DONTNEED) which preserves VMA flags, so MemoryImageSlot was the only exposure.

Cost: one extra syscall per mmap, and instantiate only mmaps when a slot is handed a different image than it already holds. Measured over 1000 instantiations, a module repeatedly instantiated into its affine slot adds 8 calls total (one per slot, at first use) and is in the noise end-to-end. A pool thrashing between more modules than it has slots takes 2 extra calls per instantiation, ~+43% on instantiation. With MPK disabled ProtectionKey is uninhabited and this all compiles away.

Fixes #13982
Fixes #7942

jlb6740 added 2 commits August 3, 2026 17:36
A fresh `mmap` associates the pages it replaces with the default
protection key 0, and key 0 is accessible from every stripe (host code
needs it). `MemoryImageSlot` maps over pkey-colored pool slots in three
places, so any module with a `(data ...)` segment silently lost its
key. Because MPK striping deliberately shrinks the guard regions between
slots, a neighboring instance could then read and write that memory for
real. Note that `mprotect` preserves the key, so only `mmap` sites are
affected.

Fix this by re-applying the key with `pkey_mprotect` after each `mmap`:
add `ProtectionKey::reprotect`, give `MemoryImageSlot` the key its
stripe was colored with, and call the new `reapply_pkey` helper after
`map_at`, `remap_as_zeros_at`, and `erase_existing_mapping`.

Tables, stacks, and GC heaps are never pkey-colored, and decommit uses
`madvise(MADV_DONTNEED)` which preserves VMA flags, so `MemoryImageSlot`
was the only exposure.

Cost: one extra syscall per `mmap`, and `instantiate` only `mmap`s when
a slot is handed a different image than it already holds. Measured over
1000 instantiations, a module repeatedly instantiated into its affine
slot adds 8 calls total (one per slot, at first use) and is in the noise
end-to-end. A pool thrashing between more modules than it has slots
takes 2 extra calls per instantiation, ~+43% on instantiation. With MPK
disabled `ProtectionKey` is uninhabited and this all compiles away.

Fixes bytecodealliance#13982
Fixes bytecodealliance#7942
@jlb6740
jlb6740 force-pushed the mpk-issue-13982-fix branch from d74f556 to a5810db Compare August 4, 2026 01:10
@github-actions github-actions Bot added the wasmtime:api Related to the API of the `wasmtime` crate itself label Aug 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

wasmtime:api Related to the API of the `wasmtime` crate itself

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MPK does not reset protection keys after fresh mmaps mpk: memory image slots protection undoes any MPK protection

1 participant