About freeing stream contexts

Hello

I got a minifilter using Stream handle contexts, and an issue of memory leak that happen sometimes. I think this is due to a misunderstanding of the execution flow of contexts.

From what I know, contexts must be allocated / used / freed like this:

---- Creation
FltAllocateContext () => Increment +1 if succeed
Doing some internal allocations , in my example I need a UNICODE_STRING on the pool
FltSetStreamHandleContext() => Increment +1 if succeed
FltReleaseContext () => decrement -1

At this time, the context is allocated, and assigned. Its count is +1 so that we keep it alive

---- Retrieve (happen many times)
FltGetStreamHandleContext () => Increment +1 if succeed
FltReleaseContext () => decrement -1

At this time, we are still at +1

---- Cleanup
FltGetStreamHandleContext () => Increment +1 if succeed
FltReleaseContext () => decrement -1

FltReleaseContext () => decrement -1
=> This one is censed to decrease the count to 0, so that the cleanup routine is called.


The problem occurs as soon as I trigger the last FltReleaseContext in the cleanup callback. I got a immediate BSOD. see below for stack trace.

If I do not call this one, no BSOD but the strange thing is that most of the time the cleanup routine is called (why? my count is never of 0) but sometimes, as this should happen normally, the cleanup routine is not called and I got a memory leak, growing with time (seen with !verifier 0x7 in Windbg)

Someone got an idea of what I’m doing / understand wrong and why this cleanup routine is called even with no count at 0? When I unload the driver, all the objects which are “leaked” are freed at this time. Don’t know why also…

Forgot the stack trace :slight_smile:
Seems to happen when the Delete context is called

Debugging Details:

EXCEPTION_CODE: (NTSTATUS) 0xc0000005 - L’instruction “0x%08lx” emploie l’adresse m moire “0x%08lx”. La m moire ne peut pas tre “%s”.
(translation : The instruction “” uses the memory address “”. The memory cannot be “”)

FAULTING_IP:
fltMgr!FltpDeleteContextList+50
bae4ec46 0fb7400c movzx eax,word ptr [eax+0Ch]

TRAP_FRAME: f6817b40 – (.trap 0xfffffffff6817b40)
ErrCode = 00000000
eax=00000000 ebx=f6817c00 ecx=00000000 edx=f6810000 esi=e1db29c8 edi=00000000
eip=bae4ec46 esp=f6817bb4 ebp=f6817bc0 iopl=0 nv up ei ng nz na po nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00010282
fltMgr!FltpDeleteContextList+0x50:
bae4ec46 0fb7400c movzx eax,word ptr [eax+0Ch] ds:0023:0000000c=???
Resetting default scope

DEFAULT_BUCKET_ID: INTEL_CPU_MICROCODE_ZERO

BUGCHECK_STR: 0x8E

PROCESS_NAME: svchost.exe

LAST_CONTROL_TRANSFER: from bae4ecce to bae4ec46

STACK_TEXT:
f6817bc0 bae4ecce 81b3b4fc 81b3b4c0 81b0d500 fltMgr!FltpDeleteContextList+0x50
f6817bd8 bae59d30 81b0d500 81b3b498 81b3b498 fltMgr!FltpRemoveAllStreamHandleContextsForFileObject+0x1a
f6817bec bae45b19 81cb3008 81b0d500 81e021f8 fltMgr!FltpCleanupStreamListCtrlForFileObjectClose+0x26
f6817c08 bae46059 f6817c20 81b0d500 81f63280 fltMgr!FltpPassThrough+0x93
f6817c38 804ee119 81e021f8 82786e48 806d12a4 fltMgr!FltpDispatch+0x10d
f6817c48 8064d628 82786e58 82786e48 81b0d500 nt!IopfCallDriver+0x31
f6817c6c 80578eea 81b0d4e8 81b0d4d8 00000000 nt!IovCallDriver+0xa0
f6817ca4 805b0a8a 00b0d500 81b0d4e8 00000000 nt!IopDeleteFile+0x132
f6817cc0 80522bc1 81b0d500 00000000 00000db8 nt!ObpRemoveObjectRoutine+0xe0
f6817ce4 805b1a89 81cc0a58 e1757eb0 81b0eda8 nt!ObfDereferenceObject+0x5f
f6817cfc 805b1b1f e1757eb0 81b0d500 00000db8 nt!ObpCloseHandleTableEntry+0x155
f6817d44 805b1c57 00000db8 00000001 00000000 nt!ObpCloseHandle+0x87
f6817d58 8053d638 00000db8 03f5fd88 7c91e4f4 nt!NtClose+0x1d
f6817d58 7c91e4f4 00000db8 03f5fd88 7c91e4f4 nt!KiFastCallEntry+0xf8
WARNING: Frame IP not in any known module. Following frames may be wrong.
03f5fd88 00000000 00000000 00000000 00000000 0x7c91e4f4

STACK_COMMAND: kb

FOLLOWUP_IP:
fltMgr!FltpDeleteContextList+50
bae4ec46 0fb7400c movzx eax,word ptr [eax+0Ch]

SYMBOL_STACK_INDEX: 0

SYMBOL_NAME: fltMgr!FltpDeleteContextList+50

FOLLOWUP_NAME: MachineOwner

MODULE_NAME: fltMgr

IMAGE_NAME: fltMgr.sys

DEBUG_FLR_IMAGE_TIMESTAMP: 480251da

FAILURE_BUCKET_ID: 0x8E_fltMgr!FltpDeleteContextList+50

BUCKET_ID: 0x8E_fltMgr!FltpDeleteContextList+50

Followup: MachineOwner

On Feb 11, 2013 10:23 AM, wrote:

> ---- Cleanup
> FltGetStreamHandleContext () => Increment +1 if succeed
> FltReleaseContext () => decrement -1
>
> FltReleaseContext () => decrement -1
> => This one is censed to decrease the count to 0, so that the cleanup
routine is called.
I think that here you have problem, you cannot release reference if you
haven’t acquired it.
I suggest following code:
if ( FltGetStreamHandleContext success) {

FltDeleteContext () ;
FltReleaseContext () ; // Release AFTER Delete!
}

Thanks, you’re right that makes sense.
However, when I read MSDN of FltDeleteContext, I see this:

"Because contexts are reference-counted, it is not usually necessary for a minifilter driver to call a routine, such as FltDeleteContext, to explicitly delete a context. "

So, I understand the sentence as “when a driver is well coded, context must be marked for delete only by playing with reference count”. Am I wrong?

What can be wrong in calling FltReleaseContext twice? Should it not be BSOD proof?
It would be easy to check whether the pointer passed is valid or not in the context list

Moreover, I see this

"FltDeleteContext removes the context from the internal filter manager structures. Then, further calls to functions that retrieve contexts, such as FltGetContexts and FltGetInstanceContext, cannot locate that context. However, the context memory is not released until the reference count for the context goes to 0. "

It means that the context is only marked for deletion, but wait for the last handle to be closed.
So we return at our initial problem :slight_smile:

You don’t have to do anything. Fltmgr will delete the context when the flie
object is closed. The close will decrement the reference count to 0 and then
delete the context.

Bill Wandel

-----Original Message-----
From: xxxxx@lists.osr.com
[mailto:xxxxx@lists.osr.com] On Behalf Of xxxxx@hotmail.fr
Sent: Monday, February 11, 2013 8:12 AM
To: Windows File Systems Devs Interest List
Subject: RE:[ntfsd] About freeing stream contexts

Moreover, I see this

"FltDeleteContext removes the context from the internal filter manager
structures. Then, further calls to functions that retrieve contexts, such as
FltGetContexts and FltGetInstanceContext, cannot locate that context.
However, the context memory is not released until the reference count for
the context goes to 0. "

It means that the context is only marked for deletion, but wait for the last
handle to be closed.
So we return at our initial problem :slight_smile:


NTFSD is sponsored by OSR

OSR is hiring!! Info at http://www.osr.com/careers

For our schedule of debugging and file system seminars visit:
http://www.osr.com/seminars

To unsubscribe, visit the List Server section of OSR Online at
http://www.osronline.com/page.cfm?name=ListServer

Are you sure?
I filter IRP_MJ_CLEANUP, which is sensed to occur once the last handle closed. I though I was responsible for mark the context for deletion.

You don’t have to delete it, but you can. If you for example allocate an
additional memory and store pointer to it in context, then it might be
reason to “delete” it (mark for deletion/detach from FO), when you know
that you won’t need it any more. Note that you should free additional
memory in PFLT_CONTEXT_CLEANUP_CALLBACK (not after deleting and/or
releasing context!)
As I read on this list, time between IRP_MJ_CLEANUP (last handle closed)
and IRP_MJ_CLOSE (FO deleted) could be in some cases very long (counted
even in hours/days).

And about “calling FltReleaseContext twice”/“Should it not be BSOD proof”
IMO such checks could make code messy/slower/etc. It’s similar to
double-free memory. Driver Verifier might catch such bugs.

Best regards,
Krystian Bigaj

On 11 February 2013 15:08, wrote:

> Are you sure?
> I filter IRP_MJ_CLEANUP, which is sensed to occur once the last handle
> closed. I though I was responsible for mark the context for deletion.

Although the context strucure is undocumented you can see the reference count as a -ve offset from the start of the context. On Windows 7 for example it is -8 bytes from the start of the context. I used an assert on the following expression to track my context refcount issues down.

DWORD refcount = *(DWORD*)((char*)(pcontext)-8);

It goes without saying that you would never include this in production code but it may or may not help when debugging.

Regards

Mark

Thanks, that will be useful indeed.
Do you know the offset under XP?

I got another question regarding the contexts.
Let’s assume there’s already another minifilter in the kernel, which is also working with stream handle contexts.

When I try to find a context, how can we know that the context is a one we have allocated, or the one from another minifilter? How can be sure that our context will not be overwritten by another minifilter?

Context is set per mini filter instance (see for example docs for Instance
parameter of FltSetStreamHandleContext or Remarks section)

On Feb 13, 2013 8:55 AM, wrote:
>
> I got another question regarding the contexts.
> Let’s assume there’s already another minifilter in the kernel, which is
also working with stream handle contexts.
>
> When I try to find a context, how can we know that the context is a one
we have allocated, or the one from another minifilter? How can be sure that
our context will not be overwritten by another minifilter?

Ok, so no conflict :slight_smile:
That’s good!

The problem I got on Seven, is as I still got contexts not freed, I can’t unload my minifilter.
Tried to delete the context before release it, and got a Bsod ( I believe this is the same issue as trying to release twice).

Trying to get a minidump, but each time my VM won’t reboot :confused:
nobody used the contexts before?

Got it.

PAGE_FAULT_IN_NONPAGED_AREA (50)
Invalid system memory was referenced. This cannot be protected by try-except,
it must be protected by a Probe. Typically the address is just plain bad or it
is pointing at freed memory.
Arguments:
Arg1: fffffff8, memory referenced.
Arg2: 00000001, value 0 = read operation, 1 = write operation.
Arg3: 865a378d, If non-zero, the instruction address which referenced the bad memory
address.
Arg4: 00000000, (reserved)

Debugging Details:

Could not read faulting driver name

WRITE_ADDRESS: GetPointerFromAddress: unable to read from 827ad718
Unable to read MiSystemVaType memory at 8278d160
fffffff8

FAULTING_IP:
fltmgr!DoReleaseContext+f
865a378d f00fc108 lock xadd dword ptr [eax],ecx

MM_INTERNAL_CODE: 0

CUSTOMER_CRASH_COUNT: 1

DEFAULT_BUCKET_ID: INTEL_CPU_MICROCODE_ZERO

BUGCHECK_STR: 0x50

PROCESS_NAME: svchost.exe

CURRENT_IRQL: 0

LAST_CONTROL_TRANSFER: from 865a3841 to 865a378d

STACK_TEXT:
8af656e8 865a3841 ffffffd0 8af65b18 94218dbf fltmgr!DoReleaseContext+0xf
8af656f4 94218dbf 00000000 84adb068 83dce540 fltmgr!FltReleaseContext+0x11
8af65b18 865a2aeb 84adb068 8af65b38 8af65b64 rkrtflt!PreCleanupOperationCallback+0xd3 [c:\tools\mydriver\drivers\minifilter\filemon.c @ 790]
8af65b84 865a59f0 8af65bd8 83c6a368 83c6a51c fltmgr!FltpPerformPreCallbacks+0x34d
8af65b9c 865a5f01 8af65bd8 00000000 848e66b8 fltmgr!FltpPassThroughInternal+0x40
8af65bc0 865a63ba 12f65bd8 848e66b8 00000000 fltmgr!FltpPassThrough+0x203
8af65bf0 826814bc 848e66b8 83c6a368 83c5e748 fltmgr!FltpDispatch+0xb4
8af65c08 828a0ea2 83b98870 83c5e730 00000001 nt!IofCallDriver+0x63
8af65c48 82867c0a 8527dc30 83c5e748 00000001 nt!IopCloseFile+0x2f3
8af65c94 8288a772 8527dc30 8dde27d0 853dd640 nt!ObpDecrementHandleCount+0x139
8af65cdc 8288bf72 8dde27d0 87928a78 8527dc30 nt!ObpCloseHandleTableEntry+0x203
8af65d0c 8288c0ea 8527dc30 853dd601 0105ec04 nt!ObpCloseHandle+0x7f
8af65d28 8268842a 0000053c 0105ec08 774764f4 nt!NtClose+0x4e
8af65d28 774764f4 0000053c 0105ec08 774764f4 nt!KiFastCallEntry+0x12a
WARNING: Frame IP not in any known module. Following frames may be wrong.
0105ec08 00000000 00000000 00000000 00000000 0x774764f4

STACK_COMMAND: kb

FOLLOWUP_IP:
rkrtflt!PreCleanupOperationCallback+d3 [c:\tools\roguekillerrt\drivers\minifilter\filemon.c @ 790]
94218dbf eb41 jmp rkrtflt!PreCleanupOperationCallback+0x116 (94218e02)

SYMBOL_STACK_INDEX: 2

SYMBOL_NAME: rkrtflt!PreCleanupOperationCallback+d3

FOLLOWUP_NAME: MachineOwner

MODULE_NAME: rkrtflt

IMAGE_NAME: rkrtflt.sys

DEBUG_FLR_IMAGE_TIMESTAMP: 511c9503

FAILURE_BUCKET_ID: 0x50_rkrtflt!PreCleanupOperationCallback+d3

BUCKET_ID: 0x50_rkrtflt!PreCleanupOperationCallback+d3

Followup: MachineOwner

Ok, it seems you are right, even the contexts not released currently are kept until we try to unload the driver.

I can see a lot of free when I unload it.
Unfortunately, it hangs at the end of frees, and it doesn’t unload.

How can I see in !verifier which memory is still allocated at unload?

If you enable driver verifier for FltMgr and for your filter it will print a list of leaked resources (if any) when you try to unload. Also, try the latest OS since driver verifier is improved with every release and it will find problems previous releases won’t.

Thanks,
Alex.
On Feb 14, 2013, at 1:50 AM, xxxxx@hotmail.fr wrote:

Ok, it seems you are right, even the contexts not released currently are kept until we try to unload the driver.

I can see a lot of free when I unload it.
Unfortunately, it hangs at the end of frees, and it doesn’t unload.

How can I see in !verifier which memory is still allocated at unload?


NTFSD is sponsored by OSR

OSR is hiring!! Info at http://www.osr.com/careers

For our schedule of debugging and file system seminars visit:
http://www.osr.com/seminars

To unsubscribe, visit the List Server section of OSR Online at http://www.osronline.com/page.cfm?name=ListServer