Is MmIsAddressValid trigger Page Faults exceptions if a memory is paged out ?

Hi there

Summary

I’ve met some ‘strange’ problem when access a memory gaining information, sometime target memory contains data I want and sometime it’s not.

And it may be caused by pagefile.sys by my investigation.

Proves & Logs

  • Sometime the address is valid and have content I want :

OK STATUS IMAGE

(NOTE: If you can not see the picture, it’s here: https://ibb.co/hWPXB50)

As you can see, target memory 0xffff80013141dfc0 is a valid memory.

  • But sometime same address (I have a virtual machine snapshot) is not valid, I thought this memory is paged out to disk:

FAIL STATUS IMAGE

(NOTE: If you can not see the picture, it’s here: https://ibb.co/StYh0Y9)

As you can see, target memory 0xffff80013141dfc0 is NOT a valid memory and the !pte output showed a pagefile keyword.

Questions

  1. I have a check statements before accessing this memory, is MmIsAddressValid causing Page Faults exceptions (I need the “memory” actually exists in memory) ?
if (Node->Name.Buffer && MmIsAddressValid(Node->Name.Buffer))
  1. How can I ensure this “memory” is not paged out when I access it ?

How can I ensure this “memory” is not paged out when I access it ?

If you are at PASSIVE_LEVEL, then it doesn’t matter. When you access it, it will be paged in.

If you are at DISPATCH_LEVEL, then you can’t safely access it. You need to use MmProbeAndLockPages, which can only be done at PASSIVE_LEVEL. You simply cannot play with paged memory at a raised IRQL.

If you are at PASSIVE_LEVEL, then it doesn’t matter. When you access it, it will be paged in.

I’m in PASSIVE_LEVEL, but this logic is an universal logic, so I dont know whether this memory is a illegale memory or it’s a paged out memory. Any solutions to detect it ?

If you are at DISPATCH_LEVEL, then you can’t safely access it. You need to use MmProbeAndLockPages, which can only be done at PASSIVE_LEVEL. You simply cannot play with paged memory at a raised IRQL.

So, below code:

_OBJECT_TYPE *Node = some_exists_object;

if (Node->Name.Buffer && MmIsAddressValid(Node->Name.Buffer))
{
    // Position 1
}

Where Position 1 code will not be execute if Node->Name.Buffer paged out, because MmIsAddressValid(Node->Name.Buffer) will not trigger page in action ?

According to your solution, I tried lock it, problem seems solved:

PMDL Mdl = NULL;
if (Node->Name.Buffer && !MmIsAddressValid(Node->Name.Buffer))
{
    Mdl = IoAllocateMdl(Node->Name.Buffer, Node->Name.Length, FALSE, FALSE, NULL);
    if (Mdl) MmProbeAndLockPages(Mdl, KernelMode, IoReadAccess);
    // For debug reason, 0xffff80013141dfc0 is fixed
    if (0xffff80013141dfc0 == Node->Name.Buffer)
        DbgPrint("Buffer Invalid: %I64X Mdl: %I64X\r\n", Node->Name.Buffer, Mdl);
}
if (Node->Name.Buffer && MmIsAddressValid(Node->Name.Buffer))
{
	// Do the logic
}
if (Mdl)
{
    MmUnlockPages(Mdl);
    IoFreeMdl(Mdl);
}

As was pointed out to me by the owner if the Memory Manager many years ago, the function MmIsAddressValid is effectively useless in most scenarios. Even if the memory is paged in when it checks, by the time you return the memory could have been paged out. Hence, the functions SHOULD have been called MmWasAddressValidWhenIChecked… which isn’t that very useful, right?

Peter

Repeating myself, if you are at PASSIVE_LEVEL, which you said you are, then paging is totally irrelevant. The behavior is exactly the same as in user-mode. If you touch a page that is paged out, it will be brought in for you, silently. You don’t need to worry about it, and you don’t need to call ProbeAndLock.

@“Peter_Viscarola_(OSR)” said:
As was pointed out to me by the owner if the Memory Manager many years ago, the function MmIsAddressValid is effectively useless in most scenarios. Even if the memory is paged in when it checks, by the time you return the memory could have been paged out. Hence, the functions SHOULD have been called MmWasAddressValidWhenIChecked… which isn’t that very useful, right?

Peter

I’ve tested, when the memory is paged out, MmIsAddressValid will return FALSE, and this function do not trigger PAGE FAULT exception, so the memory will NOT paged back.

@Tim_Roberts said:
Repeating myself, if you are at PASSIVE_LEVEL, which you said you are, then paging is totally irrelevant. The behavior is exactly the same as in user-mode. If you touch a page that is paged out, it will be brought in for you, silently. You don’t need to worry about it, and you don’t need to call ProbeAndLock.

Well, I’m sure I’m in PASSIVE_LEVEL, but this is not the point.

The point is, I have to check an arbitrary kernel space memory, so before I read data from this arbitrary memory, I have to ensure read operation will not cause BSOD. (I know interrupts comes anytime, memory can be released after check in interrupts, this is not my concern)

As I said above, MmIsAddressValid do the memory check job, but this function can not trigger PAGE FAULT exception, and it returns FALSE if a memory is paged out.

The solution, as above described, call MmProbeAndLockPages if a target address seems like an effective kernel space address and MmIsAddressValid returns FALSE in case this memory is a paged out memory.

I’m sure this thread could be marked as CLOSED (or SOLVED), but I cant find how to do it …

clearly we have somewhat of a language barrier, but let me see if I can help

Peter’s point is that MmIsAddressValid is a useless function. It does not do what you want, and what it does do is completely useless.

Tim’s point is that if you are at passive level, then you don’t need to know if memory is physically resident. If it is not, when you access the memory, a page fault will happen and it will become physically resident. It is only when you are running at IRQL levels that prevent page faults from being handled that you need to ensure memory is physically resident. And that’s when you probe and lock it before raising the IRQL from passive to something higher. Probing and locking provides a guarantee that those pages remain resident until unlocked sometime later

The last part is the most concerning. You cannot safely read data from arbitrary KM memory. It has to be memory that you own in some way. If not, then the real owner can do stuff with it that will cause you to BSOD. If you do own it, then the rules are easy to follow. If not, then nothing you do can be made to be completely safe

Peter’s point is that MmIsAddressValid is a useless function. It does not do what you want, and what it does do is completely useless.

Emmm, I’ve use this function solved numbers of BSOD problems, so it‘s not that useless LOL ~

The last part is the most concerning. You cannot safely read data from arbitrary KM memory

This is an ARK module, BSOD is acceptable(I have to access un-documented structures, event if it’s not exists in PDB file), what I have to do is try do not trigger BSOD as much as possible.

Any way, thanks for your summarize~

MmIsAddressValid returns false if dereferencing the address would cause a page fault. That’s it. And, given that you don’t own the virtual memory tables, you have the TOCTU problem. So, sure, by calling this you avoid crashes caused by dereferencing kernel virtual addresses that are invalid both in hardware and software (I.e. unallocated virtual addresses). But you also avoid dereferencing kernel virtual addresses that are invalid in hardware but logically valid (I.e. a paged out virtual address). That’s by design and why the API is, generally speaking, worthless. Use MmCopyMemory instead. I’ve never personally used it but it’s there for what you’re trying to do. (Ps: I have no idea what an ARK module is. I did a significant amount of due diligence by asking ChatGPT and it doesn’t know either)

MmCopyMemory was and perhaps still is used by hardware platforms that do memory mirroring for fault tolerance.

> @Mark_Roddy said: > MmCopyMemory was and perhaps still is used by hardware platforms that do memory mirroring for fault tolerance. MmCopyMemory or MmDuplicateMemory?

Yeah, my mistake - that’s mmduplicatememory.