How can I reserve a range of address space and receive notifications when the program first reads or writes a page in the range?

A customer wanted to reserve a range of address space and be notified when the program first reads or writes a page in the range.

It's not clear what the customer's goal is, but if it's true that all they want is to be notified of the access, without affecting the underlying memory, then it's not so hard.

In the simplest case, you can mark the page as PAGE_GUARD. This will raise a guard page violation the first time the program reads from or writes to the memory. You can log whatever you need and then indicate that you handled the exception and want execution to continue as if no exception had occurred. The guard page violation is raised only once per page. After it's done, the memory behaves normally, either as a normal read-only page or a normal read-write page, depending on how you allocated the memory.

In the more complicated case where you want to detect reads and writes separately, you can mark the page as PAGE_NO­ACCESS. If that's all you do, then this will raise an access violation every time the program reads from or writes to the memory. But what you can do is to inspect the exception reason, and if it's "read", then change the protection from PAGE_NO­ACCESS PAGE_READ­ONLY to upgrade the page from no-access to read-only. If it's "write", then upgrade all the way to PAGE_READ­WRITE. Log the information, change the page protections, and indicate that execution should continue.

Watch out for the multithreaded case, if two threads take access violations simultaneously on the same page.

If you want this fancy memory management only for the duration of a function call, then you can install a structured exception handler around the code whose access is being monitored. If you need this beyond the scope of a single function, then you can use a vectored exception handler.

A variation of this is where you want to commit empty pages on demand. In that case, you the same technique that the Format­Message function used to use: Reserve a bunch of memory, and then install an exception handler that creates the memory on demand in response to an accss violation on one of the pages you're managing.

There is a gotcha here: Your custom page fault handler will be called only for page faults incurred by user mode. If the program passes a buffer to kernel mode (say as the source of a Write­File or the destination of a Read­File), then kernel mode will complain that the buffer is invalid because not all the pages are committed with appropriate access. To work around this, you'll have to manually fault in the pages with the appropriate protections before using then as source or destination buffers in kernel calls.

Okay, so this works in the case where the program merely wants to be notified of the access, or if it wants to swoop in and allocate blank pages. Next time, we'll look at a more complicated scenario.

Comments (4)
  1. M says:

    “It’s not clear what the customer’s goal is,”…The first thing that comes to mind is a data breakpoint on a range of addresses

  2. Peter Doubleday says:

    What a bizarre request. The last time I needed to reserve a memory range was when I dealt with dual-ported memory between a Z8530 board and some sort of M68000 mini-computer.

    Shared memory? Use the API given to you by the OS. In fact, just rely on the OS. Any OS. This sort of question suggests that the customer has never heard of virtual memory and associated software support going way back into the 1990s.

    (I’m wide open to being proven wrong in cases I haven’t considered. But, really. It’s a waste of your time.)

  3. Brian says:

    One of the 1990s era Object-Oriented databases did funky things like this with virtual memory. It made for some interesting behavior when things when you started using it – there all sorts of unexpected side-effects.

  4. Tanveer Badar says:

    There are too many typos in this post. :(

Comments are closed.

Skip to main content