IRP_MJ_CLEANUP and FltGetFileNameInformation in minifilter driver

Hello everyone,

I would like to request your help on this one, since I am relatively new to
driver development, and more specifically to minifilter.

I have developed a driver based on the WDK’s minispy code and so far it
works flawlessly, until very recently. The problem is that when I perform a
certain number of operations in a given order, I get a IRP_MJ_CLEANUP in the
PreCallback which is calling FltGetFileNameInformation to get the associated
filename, and the OS gets an instant BSOD (0x0000007f, GP fault).

Actually I was using FLT_FILE_NAME_QUERY_ALWAYS_ALLOW_CACHE_LOOKUP but after
changing to FLT_FILE_NAME_QUERY_DEFAULT nothing different happened. I first
thought I had introduced some bug in the original minispy code I based my
driver on, but later noticed that minispy itself also suffers from the same
problem.

Here’s the faulting code (latest WDK, AKA 7600.16385.0):

FLT_PREOP_CALLBACK_STATUS
SpyPreOperationCallback (
__inout PFLT_CALLBACK_DATA Data,
__in PCFLT_RELATED_OBJECTS FltObjects,
__deref_out_opt PVOID *CompletionContext
)


if (FltObjects->FileObject != NULL) {

status = FltGetFileNameInformation( Data,
FLT_FILE_NAME_NORMALIZED |

MiniSpyData.NameQueryMethod,
&nameInfo );

} else {


I even added a IRQL <= APC_LEVEL check in the beginning of the function, but
the BSOD still happens.

Do you see any problem calling this piece of code in the PreOp callback? I
have read here and there, and most people seem to advise that it would be
better to get the filename when a IRP_MJ_CREATE is received and then keeping
that information for later use. How would you think it would be safest to do
this one?

Ok, so here’s the simple procedure I use to reproduce this problem:

1 - Build the minispy driver (src\filesys\minifilter\minispy) and install
on a XP machine by copying the .sys and .exe files that were compiled, and the .inf (already included in the same folder). Select “Install” from the .inf’s right-click menu. I tested on a SP2 and on a SP3. On a Vista it may work too but
not through this procedure because process creation is different by default.
2 - Start minispy driver (by issuing “nc start minispy” on a cmd.exe console).
3 - Run minispy.exe application, press ENTER to go to command mode, type “/a
C:” to attach to the C drive, and type “go” to start getting the log of
operations on the filesystem.
4 - In a debugger of your choice, open and execute a new cmd.exe process.
When the cmd.exe’s window opens, pause the process and set a breakpoint on
the ntdll’s NtCreateThread function. Resume the process’s execution and
execute some executable file from within the debugged cmd.exe (e.g. type:
net.exe).
5 - When the breakpoint is fired, simply force process termination from
inside the debugger.

You should get a STOP?0x0000007F BSOD due to a GP fault.

I am attaching a crash dump summary below (I have the full crash dump just
in case):

kd> !analyze -v *******************************************************************************
*
*
* Bugcheck
Analysis *
*
* *******************************************************************************

UNEXPECTED_KERNEL_MODE_TRAP (7f)
This means a trap occurred in kernel mode, and it’s a trap of a kind
that the kernel isn’t allowed to have/catch (bound trap) or that
is always instant death (double fault). The first number in the
bugcheck params is the number of the trap (8 = double fault, etc)
Consult an Intel x86 family manual to learn more about what these
traps are. Here is a *portion* of those codes:
If kv shows a taskGate
use .tss on the part before the colon, then kv.
Else if kv shows a trapframe
use .trap on that value
Else
.trap on the appropriate frame will show where the trap was taken
(on x86, this will be the ebp that goes with the procedure KiTrap)
Endif
kb will then show the corrected stack.
Arguments:
Arg1: 0000000d, EXCEPTION_GP_FAULT
Arg2: 00000000
Arg3: 00000000
Arg4: 00000000

Debugging Details:

PEB is paged out (Peb.Ldr = 7ffd600c). Type “.hh dbgerr001” for details
PEB is paged out (Peb.Ldr = 7ffd600c). Type “.hh dbgerr001” for details

BUGCHECK_STR: 0x7f_d

DEFAULT_BUCKET_ID: DRIVER_FAULT

PROCESS_NAME: cmd.exe

LAST_CONTROL_TRANSFER: from 80544d0b to 80543e5b

STACK_TEXT:
b2381584 80544d0b badb0d00 81af9910 00000000 nt!KiSystemFatalException+0xf
b23815f4 804f84db 81af9910 823c6830 00000000 nt!KiSwapProcess+0x8b
b2381618 805badc9 b2381630 b238166c b23816e8 nt!KeUnstackDetachProcess+0x103
b238164c 805baed1 0000055c 00000000 00000000 nt!ObpCloseHandle+0xb7
b2381660 8054060c 8000055c b2381718 804ff2a5 nt!NtClose+0x1d
b2381660 804ff2a5 8000055c b2381718 804ff2a5 nt!KiFastCallEntry+0xfc
b23816dc f84db0f3 8000055c 81ca0708 00000033 nt!ZwClose+0x11
b2381718 f84db32b 81ca06b8 b2381750 f84db864
fltMgr!FltpNormalizeNameComponent+0xab
b2381724 f84db864 81ca06b8 00000000 81ca06b8
fltMgr!FltpCallNormalizeNameComponentHandler+0x41
b2381750 f84dc590 00000033 00000000 81ca06b8
fltMgr!FltpExpandShortNames+0x110
b238176c f84dcba1 000006b8 00000000 000000fe
fltMgr!FltpGetNormalizedFileNameWorker+0x90
b2381784 f84da75e 81ca06b8 00000000 81ca06b8
fltMgr!FltpGetNormalizedFileName+0x19
b238179c f84da7a9 80553000 81ca06b8 b23817d8
fltMgr!FltpCreateFileNameInformation+0x84
b23817ac f84cbbfb 81ca06b8 81fdad12 81f5fef8
fltMgr!CreateTemporaryFileNameInformation+0xf
b23817d8 f84cc142 81ca06b8 81bcddfc 81bcdb08
fltMgr!FltpGetFileNameInformation+0xdf
b23817fc b22f6534 81fdadd4 00000401 b238182c
fltMgr!FltGetFileNameInformation+0x106
b23819d4 f84c7944 81fdadd4 b23819f4 b2381a24
minispy!SpyPreOperationCallback+0x64
[c:\winddk\7600.16385.0\src\filesys\minifilter\minispy\filter\minispy.c
@ 655]
b2381a34 f84c9352 00381a7c 82207198 b2381a7c
fltMgr!FltpPerformPreCallbacks+0x2d4
b2381a48 f84c9c15 b2381a7c 00000000 81f6b9b8
fltMgr!FltpPassThroughInternal+0x32
b2381a64 f84c9ffb b2381a7c 81f5fef8 8232c508 fltMgr!FltpPassThrough+0x1df
b2381a94 804eeeb1 81f5b830 82207008 82207008 fltMgr!FltpDispatch+0xf3
b2381aa4 80582617 81f5fee0 823e9e70 00000001 nt!IopfCallDriver+0x31
b2381ad4 805bb3a9 81af9910 81f5b830 00100020 nt!IopCloseFile+0x26b
b2381b04 805bacfb 81af9910 01f5fee0 823e9e70
nt!ObpDecrementHandleCount+0x11b
b2381b2c 805c125b e16be390 81f5fef8 0000000c
nt!ObpCloseHandleTableEntry+0x14d
b2381b4c 8060c18f e1208018 0000000c b2381b8c nt!ObpCloseHandleProcedure+0x1f
b2381b6c 805c1354 e16be390 805c123c b2381b8c nt!ExSweepHandleTable+0x3b
b2381b98 805d05f9 81af9910 00000000 81af98f8 nt!ObKillProcess+0x5c
b2381bc8 805b9e2d 81af9910 00000000 81af98f8 nt!PspProcessDelete+0xf9
b2381be4 805257b8 81af9910 00000000 00000740 nt!ObpRemoveObjectRoutine+0xdf
b2381bfc 805bad03 00000030 00000740 e11f9e80 nt!ObfDereferenceObject+0x4c
b2381c14 805c125b e1e95f60 81af9910 00000740
nt!ObpCloseHandleTableEntry+0x155
b2381c34 8060c18f e11f9e80 00000740 b2381c74 nt!ObpCloseHandleProcedure+0x1f
b2381c54 805c1354 e1e95f60 805c123c b2381c74 nt!ExSweepHandleTable+0x3b
b2381c80 805d0e40 82232b88 8223f2c8 8223f510 nt!ObKillProcess+0x5c
b2381d08 805d109a c000013a 8223f2c8 00000000 nt!PspExitThread+0x58a
b2381d28 805d1275 8223f2c8 c000013a b2381d64
nt!PspTerminateThreadByPointer+0x52
b2381d54 8054060c 00000000 c000013a 00d8ff04 nt!NtTerminateProcess+0x105
b2381d54 7c90eb94 00000000 c000013a 00d8ff04 nt!KiFastCallEntry+0xfc
WARNING: Frame IP not in any known module. Following frames may be wrong.
00d8ff04 00000000 00000000 00000000 00000000 0x7c90eb94

STACK_COMMAND: kb

FOLLOWUP_IP:
minispy!SpyPreOperationCallback+64
[c:\winddk\7600.16385.0\src\filesys\minifilter\minispy\filter\minispy.c
@ 655]
b22f6534 898560feffff mov dword ptr [ebp-1A0h],eax

FAULTING_SOURCE_CODE:
651:
652: status = FltGetFileNameInformation( Data,
653:
FLT_FILE_NAME_NORMALIZED |
654:
MiniSpyData.NameQueryMethod,

655: &nameInfo );
656:
657: } else {
658:
659: //
660: // Can’t get a name when there’s no file object

SYMBOL_STACK_INDEX: 10

SYMBOL_NAME: minispy!SpyPreOperationCallback+64

FOLLOWUP_NAME: MachineOwner

MODULE_NAME: minispy

IMAGE_NAME: minispy.sys

DEBUG_FLR_IMAGE_TIMESTAMP: 4a975e6c

FAILURE_BUCKET_ID: 0x7f_d_minispy!SpyPreOperationCallback+64

BUCKET_ID: 0x7f_d_minispy!SpyPreOperationCallback+64

Followup: MachineOwner

I noticed that when minispy!SpyPreOperationCallback+64 is executed,
SpyPreOperationCallback seems to be called in the context of the process
(!?!) that is being terminated (in this case net.exe, which has no threads!
because we forced its parent’s terminated before net.exe’s first thread was
created). I suppose I am messing some concepts here and there but, if this is
to be true, then I would like to ask you how could something be executed in
the context of net.exe’s process if it has no threads itself?!

Could you guys help me clarify here the reason for this crash. I would like
to know better what’s going on “behind the scenes”.
Finally, how do you think it would be better to change the code to perform
what it is already doing but preventing this problem from happening.

Please let me know if I was unclear about something!

Thank you very much guys!

Hello,

Can you repro this without the debugger in the mix ? Adding a debugger changes things a great deal and with a debugger attached you can crash a system in any number of ways.

For example, when a debugger is present the image in memory can be different (a debugger might add breakpoints by inserting an INT 3, thus changing the code). Also, a debugger can create threads in the process being debugged… And, finally, I’m not sure how the debugger kills the process, but I’m afraid it might be different from what the system does.

So please try to reproduce this crash without the debugger in the mix.

Regards,
Alex.
This posting is provided “AS IS” with no warranties, and confers no rights.

Hello Alex, first of all, thank you very much for your reply!

In fact, when I first found this problem it was with a real program, not a debugger.

Compile the following code which hooks the program itself (nothing else) to ntdll’s NtCreateThread which will call Terminate during CreateProcessW of calc.exe (NtCreateProcessEx, set process parameters, set stack, etc., NtCreateThread, Terminate!). The code should crash your system if you are running an XP machine with the minispy code from the latest build like I described in the previous post.

Please let me know if you have any problem with the code.

#include <windows.h>
#include <stdio.h>

void Terminate ()
{
TerminateProcess(GetCurrentProcess(), (UINT)-1);
}

int wmain( int argc, TCHAR *argv )
{
PROCESS_INFORMATION pi;
STARTUPINFO si;
HMODULE h_NTDLL = GetModuleHandleW(L"ntdll.dll");
PVOID NtCreateThread, TerminateAddr;
BYTE TerminateCode[6];
DWORD oldProtect, oldProtect2;

if (h_NTDLL == NULL ||
(NtCreateThread = GetProcAddress(h_NTDLL, “NtCreateThread”)) == NULL)

{
printf(“Error: NTDLL\n”);
return -1;
}

TerminateAddr = Terminate;
TerminateCode[0] = 0xFF;
TerminateCode[1] = 0x15;
memcpy(&TerminateCode[2], &TerminateAddr, sizeof(PVOID));

if (VirtualProtect(NtCreateThread, sizeof(TerminateCode), PAGE_EXECUTE_READWRITE, &oldProtect) == FALSE)
{
printf(“Error: VirtualProtect\n”);
return -1;
}

memcpy(NtCreateThread, &TerminateCode, sizeof(TerminateCode));

if (FlushInstructionCache(GetCurrentProcess(), NtCreateThread, sizeof(TerminateCode)) == FALSE)
{
printf(“Error: FlushInstructionCache\n”);
return -1;
}

if (VirtualProtect(NtCreateThread, sizeof(TerminateCode), oldProtect, &oldProtect2) == FALSE)
{
printf(“Error: VirtualProtect\n”);
return -1;
}

memset( &si, 0, sizeof(si) );
si.cb = sizeof(si);
si.dwFlags = STARTF_USESHOWWINDOW;

if (!CreateProcessW(
L"C:\windows\system32\calc.exe", NULL,
NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi))
{
printf(“Error: CreateProcessW\n”);
return -1;
}
}</stdio.h></windows.h>

Hello crunch,

I’m still not convinced this is a real problem. The user mode code you have provided is still doing some unsupported things which might be responsible for the issue you are seeing. For example, you are not letting NtCreateThread complete its initialization which I imagine might have something to do with the way it is cleaning up after itself…

So please try to reproduce this using only documented features. Also, please try in Win7 and Vista.

Regards,
Alex.
This posting is provided “AS IS” with no warranties, and confers no rights.

Hello Alex,

Thanks for your reply.

I don’t see any problem with my code except the fact that it is doing something that you definitely would NOT like to do in a real environment.

But think about this:

  • The code has no problems if a minispy-like driver is not running.
  • There’s nothing wrong IMHO with not letting NtCreateThread complete its initialization. Suppose process A wants to create process B and has just called NtCreateThread to create B’s first thread, while another process C did a TerminateProcess on process A before B’s first thread was created.
  • The code is performing a set of operations from unprivileged user mode, which, are not so undocumented as said: all functions in the code I provided are very well documented at MSDN and the “hooking” thing I wrote is a quick and dirty way to do what Microsoft does with Microsoft Detours ( http://research.microsoft.com/en-us/projects/detours/ ).

About Vista and Win7, I have not tested because mainly CreateProcess has changed internally and is calling a different syscall which already creates the first thread. I suppose that due to backwards compatibility if I call the piece of code which is included in XP’s kernel32.dll in Vista/7 and do some changes here or there, I will still get a BSOD because I am convinced there’s some bug in the driver.