I had an interesting crash on a system running Process Monitor from Sysinternals that implicated the Process Monitor driver in a NULL pointer dereference:
Here we see what appears to be an image load callback (as indicated by the call to PsCallImageNotifyRoutines on the stack) that is dereferencing a NULL pointer value contained in EDI.
Zooming into the assembly of the routine in question, we can see that the bad pointer value of EDI is retrieved a couple of lines up from EBP+8. Since we also can see that this is a standard EBP frame in the prolog, we can assume at this point that the NULL pointer deref occurred due to a failure to check the first parameter for NULL:
At this point I went back to the PsSetLoadImageNotifyRoutine documentation to check the prototype of the callback function, hoping to found out what the first parameter of this routine was and if it was documented to be NULL. According to the documentation, the following is the prototype of the function:
VOID
(*PLOAD_IMAGE_NOTIFY_ROUTINE) (
IN PUNICODE_STRING FullImageName,
IN HANDLE ProcessId, // where image is mapped
IN PIMAGE_INFO ImageInfo
);
Note that the first parameter is indicated to be a pointer to a UNICODE_STRING structure. This fits with the assembly lines listed above:
mov edi, dword ptr [ebp+8]
xor eax, eax
cmp word ptr [edi], ax
Since that assembly listing would match the following C code due to the fact that the first field of a UNICODE_STRING structure is an USHORT Length value:
if (FullImageName->Length == 0)
Thus, this routine assumes that if there is no image name available for the module being loaded, the first parameter to this routine will be a pointer to an empty UNICODE_STRING. Clearly in my case the first parameter passed in was NULL, thus I had to dig further back to determine if I was dealing with a corruption or if Process Monitor’s image load callback had a genuine bug.
Moving up a couple of frames, I located the code that gathers the name for the image being loaded and calls the dispatcher routine. Below is the highlighted assembly path that is of interest to us:
The highlighted sections correspond to the following four steps that ultimately lead to the crash:
1) Set the value of EBX to be zero, or NULL
2) Call an internal memory manager routine, MmGetFileNameForSection. While I’m not familiar with this routine, I suspect that it tries to get the file name of a section
Upon return, compare the return value of the routine (EAX) with EBX, which we know to be zero.
3) Based on the compare, we will “jump if less than zero.” This assembly pattern basically checks a value to determine if it is less than zero and, if it is, jumps to a new location. Failure codes in the kernel are negative values, thus this is a fairly common pattern that corresponds to the following C statement:
status = SomeFunction();
if (!NT_SUCCESS(status)) {
}
Thus, if MmGetFileNameForSection returns a negative value (i.e. a failure code) we will jump.
4) The final highlighted section is the failure path that we take if MmGetFileNameForSection returns a failure code. In that case, a push of EBX is performed. This means that we will pass NULL as the first parameter to PsCallImageNotifyRoutines and not a pointer to an empty UNICODE_STRING structure.
So, after all of that digging, the result is that this is a legitimate bug in the Process Monitor image load callback. The system that this crash occurred on had several other third party drivers present including two security products, thus I suspect that someone failed the attempt to get the image name for some reason or another. This triggered an untested path in the Process Monitor driver and led to the crash.
Note: If you try to follow the above assembly on your own system you might find different routines in play here as this code was rewritten for Vista and later. However, the bug is still real as the first parameter to the image load callback may be NULL on those platforms as well.



I’ve been nailed by this little gem. We always check for NULL here, but some other drivers don’t. The best part is the WDK documentation doesn’t say it can ever be NULL. Yay!
Nice, eh? I also would have guessed a zero length UNICODE_STRING instead of a NULL pointer, so can’t say I blame them. Of course, that makes Process Monitor entirely useless on that particular system (though the bug that I was tracking down wouldn’t have been caught by Process Monitor anyway, so maybe this was a blessing in disguise).
I filed a bug to get the docs fixed, so at least future souls will be saved…
-scott
What I found out was that MmGetFileNameForSection can fail depending on any filter/file-system in the same stack that the section object was opened on. If I remember correctly the call translates to IRP_MJ_QUERY_INFORMATION->FileNameInformation.
-Jeff
Yup, that’s it. MmGetFileNameForSection is just a wrapper around a call to ObQueryNameString for the file object backing the section. And for file objects that’s going to ultimately result in a query IRP for FileNameInformation.
-scott
Nice one!