Keep live pages when filling in lazy memory

MA
4d ago
+490-83

guest_materialize_lazy fills in the part of a MAP_NORESERVE region that lies in the faulting 2 MiB block. Three things it did wrong:

It zeroed every page of that part, including pages already mapped. A mapping placed next to one the guest has written joins the same region, so the first touch of the new mapping wiped the old one.

It zeroed after publishing the descriptors, so a sibling vCPU could store through a new descriptor before the zeroing landed and lose the store.

It mapped a PROT_NONE region readable, so touching a PROT_NONE reservation read zeros instead of raising SIGSEGV.

Zero only the pages that had no mapping, before any descriptor is published, refuse a region with no access bits, and treat a fault on a page another vCPU has already filled in as a stale translation.


Summary by cubic

Fixes guest_materialize_lazy so the first touch of MAP_NORESERVE memory no longer wipes neighbor data, loses concurrent stores, grants access to PROT_NONE reservations, or lets a sibling vCPU reach a hole in a partly covered block.

  • Zeroes only pages without an existing mapping, before any descriptor is published.
  • Returns an error for regions with no access bits instead of making them readable.
  • Treats a fault on a page another vCPU already filled in as a stale translation, with an I-cache invalidation hint for executable regions, and reports it as SEGV_ACCERR.
  • Fills the L3 table of a partly covered block before the L2 slot points at it, so no vCPU sees a hole or guard during the trim.
  • Adds test-noreserve-materialize, a stress test that races a writer against the fill, checks PROT_NONE behavior, and verifies a hole past the mapping still faults.