Kernel Page Fault handling; NTE

We have a number of drivers running in the kernel, and one of them
has decided to overwrite 8 bytes the HAL code after several hours
of a stress test. Since this is a dual CPU, and a distributed across
two PCs also, using an ICE would be problematic.

Using the 4 breakpoint registers sounded like a good idea, but
it seems like these only cover 4 words, and the stepping seems to
be randomly spread over a page.

So we figured out how to modify the page directory for the first kernel
4MB page, and broke it into 1024 4K pages, then we set the write
protect bit on the 4th page, which is where the stepping is.

With a test program, we were able to make it blue screen when that
page is written to, which is what we want. Other times, however,
it just hangs. It does work fine with the page table changes without the
write protect bit being set.

I know I have to verify that there are no legitimate writes to
0x80003000-0x80003fff (we built this HAL ourselves from the HDK),
but I’m wondering if the kernel
page fault handler may decide sometimes that a page fault
in this address range must be spurrious, and just returns.
Anyone familiar with what the page fault handler will do
on a write page fault here?

-DH

PS. Yes, we invalidate the page table cache, because the
“G” bit was set, and yes we do this
for each process (but this is a real HACK => we don’t
know how to find all processes and then their page tables,
so instead we search memory for the very distictive 512 byte
pattern at offset 0x800 in a page. For random data patterns,
the probability of a false positive is about (2**15 pages)/(2**4096 patterns/page)
each time we run this).

Before you did all this complicated work, did you try enabling driver
verifier on the HAL ?

This will make, amound other things, the code to the HAL read-only (by
doing things similar to what you were trying to do).

It’s just a lot simpler.

-Andre

> Anyone familiar with what the page fault handler will do

on a write page fault here?

On >= DISPATCH_LEVEL faults in kseg0 (the directly-mapped 512MB region
starting from 0x80000000. The 512MB size varies with the OS versions,
packagings and the /3GB parameter):

  • if kseg0 is built by 4MB pages - then MmAccessFault just returns zero,
    regardless of whether this is a write or not!
  • else if the PTE is valid and a write was attempted - then
  • if this is copy-on-write - MmAccessFault returns 0xd0000006
    (STATUS_??? who knows?)
  • if the PTE is dirty - return 0
  • else set the dirty bit in the PTE (on SMP only) - and return 0

On < DISPATCH_LEVEL faults in kseg0:

  • if there is a valid PDE for the address and kseg0 is built by 4MB pages -
    then return 0.
  • if the PTE and the PDE for the address are both valid - then set the dirty
    bit if the PTE is not dirty and this is a write (on SMP only) - then return

So, MmAccessFault will return 0 (STATUS_SUCCESS) for majority of the kseg0
faults. Setting the PTE as read/only will not protect you - IIRC the RO PTE
bit (as defined by the hardware) is used as the “dirty bit” and is
automatically set to 1 on first write fault to this PTE. The read RO flag in
somewhere in not hardware-defined part of the PTE, it is NOT checked for
kseg0 faults.

You can patch MmAccessFault if you want - for sure :slight_smile:

I can send you the rev.enged code of MmAccessFault and friends (from vanilla
NT4 SMP without any patches).

Max

Our symptoms are inline with your description of how the code works.
The “hang” we see is the page fault retrying.
However, we also found that in this state
we can break in Windbg and get a stack trace, which is all
we wanted, so we don’t need to go any further down this path.

-DH
----- Original Message -----
From: “Maxim S. Shatskih”
To: “NT Developers Interest List”
Sent: Thursday, October 05, 2000 6:23 PM
Subject: [ntdev] Re: Kernel Page Fault handling; NTE

> Anyone familiar with what the page fault handler will do
> on a write page fault here?

On >= DISPATCH_LEVEL faults in kseg0 (the directly-mapped 512MB region
starting from 0x80000000. The 512MB size varies with the OS versions,
packagings and the /3GB parameter):

- if kseg0 is built by 4MB pages - then MmAccessFault just returns zero,
regardless of whether this is a write or not!
- else if the PTE is valid and a write was attempted - then
- if this is copy-on-write - MmAccessFault returns 0xd0000006
(STATUS_??? who knows?)
- if the PTE is dirty - return 0
- else set the dirty bit in the PTE (on SMP only) - and return 0

On < DISPATCH_LEVEL faults in kseg0:

- if there is a valid PDE for the address and kseg0 is built by 4MB pages -
then return 0.
- if the PTE and the PDE for the address are both valid - then set the dirty
bit if the PTE is not dirty and this is a write (on SMP only) - then return
0.

So, MmAccessFault will return 0 (STATUS_SUCCESS) for majority of the kseg0
faults. Setting the PTE as read/only will not protect you - IIRC the RO PTE
bit (as defined by the hardware) is used as the “dirty bit” and is
automatically set to 1 on first write fault to this PTE. The read RO flag in
somewhere in not hardware-defined part of the PTE, it is NOT checked for
kseg0 faults.

You can patch MmAccessFault if you want - for sure :slight_smile:

I can send you the rev.enged code of MmAccessFault and friends (from vanilla
NT4 SMP without any patches).

Max


You are currently subscribed to ntdev as: xxxxx@syssoftsol.com
To unsubscribe send a blank email to $subst(‘Email.Unsub’)

It was my impression that the Driver Verifier was Win2K
only, and not available on NT4/NTE. Can you really run it
on the *HAL* in Win2k? Or do you mean that if I’m running it
on some other driver, it will write protect the HAL and other
kernel code?

In any case, our theory is that some driver is using an uninitialized
pointer on the stack, and that any major code movement, etc.,
will cause the bug to migrate somewhere else.

-DH
----- Original Message -----
From:
To: “NT Developers Interest List”
Sent: Thursday, October 05, 2000 1:28 PM
Subject: [ntdev] Re: Kernel Page Fault handling; NTE

Before you did all this complicated work, did you try enabling driver
verifier on the HAL ?

This will make, amound other things, the code to the HAL read-only (by
doing things similar to what you were trying to do).

It’s just a lot simpler.

-Andre


You are currently subscribed to ntdev as: xxxxx@syssoftsol.com
To unsubscribe send a blank email to $subst(‘Email.Unsub’)

This turned out to be related to some hardware errata
with some newer version of the Intel MCH chip and system
management interrupts. We actually moved the HAL’s
physical address, and left the virtual unchanged, and it
all ran. We found that something was still corrupting
the low physical pages that were no longer used by the Hal,
even if none of our drivers were loaded.

-DH

----- Original Message -----
From: “Maxim S. Shatskih”
To: “NT Developers Interest List”
Sent: Thursday, October 05, 2000 6:23 PM
Subject: [ntdev] Re: Kernel Page Fault handling; NTE

> Anyone familiar with what the page fault handler will do
> on a write page fault here?

On >= DISPATCH_LEVEL faults in kseg0 (the directly-mapped 512MB region
starting from 0x80000000. The 512MB size varies with the OS versions,
packagings and the /3GB parameter):

- if kseg0 is built by 4MB pages - then MmAccessFault just returns zero,
regardless of whether this is a write or not!
- else if the PTE is valid and a write was attempted - then
- if this is copy-on-write - MmAccessFault returns 0xd0000006
(STATUS_??? who knows?)
- if the PTE is dirty - return 0
- else set the dirty bit in the PTE (on SMP only) - and return 0

On < DISPATCH_LEVEL faults in kseg0:

- if there is a valid PDE for the address and kseg0 is built by 4MB pages -
then return 0.
- if the PTE and the PDE for the address are both valid - then set the dirty
bit if the PTE is not dirty and this is a write (on SMP only) - then return
0.

So, MmAccessFault will return 0 (STATUS_SUCCESS) for majority of the kseg0
faults. Setting the PTE as read/only will not protect you - IIRC the RO PTE
bit (as defined by the hardware) is used as the “dirty bit” and is
automatically set to 1 on first write fault to this PTE. The read RO flag in
somewhere in not hardware-defined part of the PTE, it is NOT checked for
kseg0 faults.

You can patch MmAccessFault if you want - for sure :slight_smile:

I can send you the rev.enged code of MmAccessFault and friends (from vanilla
NT4 SMP without any patches).

Max


You are currently subscribed to ntdev as: xxxxx@syssoftsol.com
To unsubscribe send a blank email to $subst(‘Email.Unsub’)