loader: keep the page table region off a large page boundary - #4382
loader: keep the page table region off a large page boundary#4382Ben Hillis (benhillis) wants to merge 1 commit into
Conversation
The page table region sits immediately after the relocation region and is excluded from it, but the boot identity map uses large pages. A relocating loader only fixes up a leaf entry when the region it maps overlaps the relocation region, so when the page table region starts exactly on a large page boundary its leaf stays identity mapped at the pre-relocation address. The relocated cr3 is then unmapped and the first page table access triple faults VTL2 with no IDT loaded. Latent; only trips when unrelated image growth lands the region on the boundary. Pad by a page so it always shares a large page with the relocation region. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🟡 Changes recommended
The new padding is applied unconditionally but is only necessary when relocation is enabled, and changing layout in non-relocation configurations can waste memory or risk NotEnoughMemory in tighter setups.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
This PR fixes a latent relocation failure in the paravisor loader where the page-table region could start exactly on a large-page boundary, causing the identity-map leaf covering the page tables to remain unrelocated and leading to a VTL2 triple fault after cr3 relocation.
Changes:
- Add a 4KB pad when the end of the relocation region lands exactly on a large-page boundary (x64: 2MB, arm64: 2MB) so the page-table region shares a large page with the relocation region.
- Apply the same layout fix for both x64 (
offset) and arm64 (next_addr) loader paths.
File summaries
| File | Description |
|---|---|
| vm/loader/src/paravisor.rs | Adds a one-page padding rule to avoid large-page-boundary placement of the page-table region, preventing relocation/identity-map mismatches. |
Review details
Suppressed comments (1)
vm/loader/src/paravisor.rs:1256
- This padding is only relevant when relocation is enabled (it’s meant to ensure the identity-map leaf covering the page-table region overlaps the relocation region). Applying it unconditionally changes the layout and
used_endfor non-relocation boots and could waste memory. Gate it onwith_relocationto keep non-relocation layouts unchanged.
// Keep the page table region sharing a large page with the relocation
// region, so the identity map entry covering it is relocated with it.
if next_addr.is_multiple_of(u64::from(Arm64PageSize::Large)) {
next_addr += HV_PAGE_SIZE;
}
- Files reviewed: 1/1 changed files
- Comments generated: 1
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| // Relocating loaders only fix up identity map entries that overlap the | ||
| // relocation region, so keep the page table region sharing a large page | ||
| // with it. Otherwise the relocated cr3 is unmapped and VTL2 triple faults. | ||
| if offset.is_multiple_of(X64_LARGE_PAGE_SIZE) { | ||
| offset += HV_PAGE_SIZE; | ||
| } |
The paravisor loader places the page table region immediately after the relocation region and excludes it from that region, but the boot identity map uses large pages.
A relocating loader only fixes up an identity map leaf entry when the range it maps overlaps the relocation region. When the page table region starts exactly on a large page boundary there is no overlap, so its leaf stays identity mapped at the pre-relocation address.
cr3is relocated and the page table pages are physically moved, so the relocatedcr3has no mapping and the first page table access triple faults VTL2 with no IDT loaded:This is latent. The region start is just the running total of everything loaded before it, so unrelated image growth decides whether it lands on the boundary — roughly 1 in 512.
mainis currently at0xcb20000and passes by luck;release/1.8.2607landed on0xca00000and everyhyperv_openhcl_*test fails.Pad by a page so the region always shares a large page with the relocation region. Verified on 1.8: 713/713 tests pass with the region at
0xca01000.