NX dependency on PAE
Hardware supported NX is dependent on PAE (Windows Internals chapter 9. Memory). But why would that be?
The AMD64 Architecture Programmer's Manual (Volume 2: System Programming) mentions this:
No Execute (NX) Bit. Bit 63. This bit is present in the translation-table entries defined for PAE paging, with the exception that the legacy-mode PDPE does not contain this bit. This bit is not supported by non-PAE paging.
Again from Windows Internals we know that PTEs with PAE enabled are 64 bits long; without PAE they are only 32 bits long. There simply is no bit 63 for non-PAE paging. We can see it in the debugger.
Here is a call stack from the non-PAE crash dump:
0: kd> k
ChildEBP RetAddr
8292dc0c 968a1160 nt!KeBugCheckEx+0x1e
8292dc3c 968a1768 i8042prt!I8xProcessCrashDump+0x251
8292dc88 82840d4d i8042prt!I8042KeyboardInterruptService+0x2ce
8292dc88 8286449a nt!KiInterruptDispatch+0x6d
8292dd24 00000000 nt!KiIdleLoop+0x1a
If we dump out the the ChildEBP for the 2nd frame:
0: kd> !pte 8292dc3c
VA 8292dc3c
PDE at C0300828 PTE at C020A4B4
contains 001BF063 contains 0292D963
pfn 1bf ---DA--KWEV pfn 292d -G-DA—KWEV
You see that the PTE contains a 32 bit value (0292D963).
The same exercise on a PAE enabled system gives us this call stack:
1: kd> k
ChildEBP RetAddr
807e1c0c 928bd160 nt!KeBugCheckEx+0x1e
807e1c3c 928bd768 i8042prt!I8xProcessCrashDump+0x251
807e1c88 828587cd i8042prt!I8042KeyboardInterruptService+0x2ce
807e1c88 8288101a nt!KiInterruptDispatch+0x6d
807e1d24 00000000 nt!KiIdleLoop+0x1a
The PTE here is 64 bit:
1: kd> !pte 807e1c3c
VA 807e1c3c
PDE at C0602018 PTE at C0403F08
contains 000000007A986863 contains 800000002D21F963
pfn 7a986 ---DA--KWEV pfn 2d21f -G-DA--KW-V
We would expect bit 63 on the ChildEBP to be set (EBP is on the stack and we would not want to execute any code from the stack: NX should be 1).
1: kd> .formats 800000002D21F963
Evaluate expression:
Hex: 80000000`2d21f963
Decimal: -9223372036097574557
Octal: 1000000000005510374543
Binary: 10000000 00000000 00000000 00000000 00101101 00100001 11111001 01100011
…
How about the return address of that same frame?
1: kd> !pte 928bd768
VA 928bd768
PDE at C06024A0 PTE at C04945E8
contains 0000000023615863 contains 000000007B9CD121
pfn 23615 ---DA--KWEV pfn 7b9cd -G--A—KREV
There the bit is not set:
1: kd> .formats 000000007B9CD121
Evaluate expression:
Hex: 7b9cd121
Decimal: 2073874721
Octal: 17347150441
Binary: 01111011 10011100 11010001 00100001
…
Note that .formats truncates the preceding 0s.