diff --git a/include/memmodel.h b/include/memmodel.h index 983efa8..53f409f 100644 --- a/include/memmodel.h +++ b/include/memmodel.h @@ -125,7 +125,11 @@ typedef struct { * x86-64 has no read bit: a present page is readable, so VR_R is always set on a * returned region. Write/execute/user are the EFFECTIVE rights along the whole * page-table path (RW & US are AND-ed across levels, NX is OR-ed), not just the - * leaf entry, so they reflect what the guest CPU actually enforces. */ + * leaf entry, so they reflect what the guest CPU actually enforces. One + * exception keeps VR_X honest under KVA shadow (KPTI): the kernel CR3 marks + * user-half PML4Es NX as hardening, but ring 3 executes those pages via the + * user/shadow CR3 (same shared lower tables, NX-clear PML4E), so the user-half + * top-level NX bit is ignored when deriving VR_X. */ #ifndef VMIE_VREGION_DEFINED #define VMIE_VREGION_DEFINED #define VR_R 0x1u /* readable (present => always set) */ diff --git a/src/engine/gva.c b/src/engine/gva.c index d1241a1..73d3524 100644 --- a/src/engine/gva.c +++ b/src/engine/gva.c @@ -158,7 +158,16 @@ int gva_regions(vmie_mem* m, uintptr_t cr3, uint64_t lo, uint64_t hi, if (!(e4 & PG_P)) continue; const uint64_t b4 = VA_CANON((uint64_t)i4 << 39); if (!rgn_hit(b4, 1ull << 39, lo, hi)) continue; - const int rw4 = (e4 >> 1) & 1, us4 = (e4 >> 2) & 1, nx4 = (int)(e4 >> 63) & 1; + const int rw4 = (e4 >> 1) & 1, us4 = (e4 >> 2) & 1; + /* On a KVA-shadow (KPTI) Windows the kernel CR3 marks user-half PML4Es + * NX as hardening: the kernel must not execute user pages through its own + * mapping. Ring 3 runs under the user/shadow CR3, which reaches the SAME + * lower tables (PDPT/PD/PT are shared) through a PML4E with NX clear, so + * that top-level NX is not what executes user code. Drop it on the user + * half; sub-PML4E NX stays authoritative (it is shared between the two + * CR3s). On a non-KPTI guest a code-covering user PML4E already has NX + * clear, so this only ever discards a zero. i4 < 256 == canonical user. */ + const int nx4 = (i4 < 256) ? 0 : ((int)(e4 >> 63) & 1); const uint64_t* t3 = gpa_ptr(m, e4 & PFN_MASK, 4096); if (!t3) continue;