Minifilter hangs on unload : 0

It’s for sure got something to do with the way I manage contexts , struggling to spot it

I call FltAllocateContext in post create and if successful , save it in a custom list using the following function

{
	AutoLock<Mutex> locker(ContextsListMutex);
	PContextToDelete New = (PContextToDelete)ExAllocatePoolWithTag(NonPagedPool, sizeof(ContextToDelete), TAG);
	if (!New)
		return false;
	New->Context = (DWORD64)context;
	New->Next = nullptr;
	if (!ContextsToDelete)
	{
		ContextsToDelete = New;
		return true;
	}
	PContextToDelete current = ContextsToDelete;
	while (current->Next != nullptr)
	{
		current = (PContextToDelete)current->Next;
	}
	current->Next = (LIST_ENTRY*)New;

	return true;
} ```

On unload I call the following

{

	AutoLock<Mutex> locker(ContextsListMutex);
	if (ContextsToDelete)
	{
		PContextToDelete current = ContextsToDelete;
		PContextToDelete temp = nullptr;
		while (current != nullptr)
		{
			temp = current;
			current = (PContextToDelete)current->Next;
			ExFreePoolWithTag(temp, TAG);
		}
	}

} ``` 

and for every call the increments the reference count (FltGetStreamHandleContext in my case) I construct the following object :
AutoContext AutoHandleContx(HandleContx);

{
public:
	AutoContext(PFLT_CONTEXT Context) : m_context_ptr(Context) {};
	~AutoContext() { FltReleaseContext(m_context_ptr); };
private:
	PFLT_CONTEXT m_context_ptr;
}; ``` 

ideally initial reference by FltAllocateContext is dereferenced in pre / post close , for example

PreClose(
	_Inout_ PFLT_CALLBACK_DATA Data,
	_In_ PCFLT_RELATED_OBJECTS FltObjects,
	_Flt_CompletionContext_Outptr_ PVOID* CompletionContext
)
{
	pHandleContext HandleContx = nullptr;
	NTSTATUS status = FltGetStreamHandleContext(FltObjects->Instance, FltObjects->FileObject, reinterpret_cast<PFLT_CONTEXT*>(&HandleContx));
	if (!NT_SUCCESS(status))
		return FLT_PREOP_SUCCESS_NO_CALLBACK;
	
	// auto release get reference 
	AutoContext AutoHandleContxGet(HandleContx);

	// no write occured , no need to evaluate 
	if (HandleContx->FirstPreWrite)
	{
		contexts::RemoveContextFromList(HandleContx);
		ExFreePoolWithTag(HandleContx->FileName.Buffer, TAG);
		FltReleaseContext(HandleContx);
		return FLT_PREOP_SUCCESS_NO_CALLBACK;
	}
	
	// pass handle context pointer to post close 
	*CompletionContext = HandleContx;
	
	return FLT_PREOP_SUCCESS_WITH_CALLBACK;
}

but in case the minifilter unloads before close has taken place the context is released through the list of contexts , anyone sees something I dont ?
usually it unloads cleanly but every once in a while it hangs and requires restart

And you have tested with verifier? That will tell you what you are leaking.

2 Likes

Typical way to work with it is like this:

  1. Allocate context by calling FltAllocateContext() - ref count is 1.
  2. Call FltSetStreamHandleContext() - ref count is 2. And if successful you immediately call FltReleaseContext() to decrement ref count from step 1. Right now the ref count is 1 and the only one holding the reference is the System because you called FltSetStreamHandleContext().

At this point you don't need to do anything else, if you want, in PreCleanup just call FltDeleteStreamHandleContext but that is optional, the System will delete it at some point for you if file is closed.

Whenever you want to ask for the context, just get it and then release it. I usually use scoped objects. But I never needed to release any context in unload by myself.

So I suspect that in your case the problem is that you keep the ref count for the context allocation for the entire life, which is wrong, you should rely on the System which is keeping the context for you.

1 Like

I don’t keep the contexts for the entire lifetime of the driver, I release it on close.
on unload i release only contexts for files that haven’t been closed yet at the time of unloading , so it won’t hang

You are over thinking this. You really do not need to keep lists of contexts the OS will do it all for you. Just make sure that every allocate and every get (sets too under certain circumstances) is matched by a release.

That's it.

BTW: It's not just context leaks which cause hangs. Did you try running with verifier?

1 Like

Did you enable Driver verifier with I/O verification option which does validates minifilter driver usage of FltMgr provided functions?

yep thats better , should the context be accessible in post close ? or by that point filter manager already released the initial reference on the context?

I would imagine so. The FSD will tear down all its stuff and that will include stream, streamhandle and file contexts.

Any reason why you cannot grab it in pre and pass it across to post….

what im doing now is passing a pointer to it through the completion context to post close, I was wondering why does that work if the FSD releases the context allocation during close

Filter manager holds a reference on it , the file system may be done with it, but it wont go away until you call fltreleasecontext…

1 Like

well , I understand the correct way to do it is to get in pre, pass to post and release ref in post.
but the driver was calling get in pre , used a scoped object to release the ref in pre and pass to post the pointer(see pre close above) , yet touching it in post worked and im not sure why

It's a use after free, could go unnoticed for a long time as long as the memory doesn't get recycled.

Enable Driver Verifier on your driver and FltMgr.sys and you should hopefully see the problem much sooner.

2 Likes

I want to give other example with FltSetStreamContext. Considering that you have released any reference that you had and the only one left is the one from FltSetStreamContext, you can close the file and the stream context will still live. How long? It depends on the file activity pressure. And at some point the OS will decide to free the context. That's the beauty of it, later when you open the same file, even if your code doesn't have any ref to that context, you can still find it by calling FltGetStreamContext. Like I said, no guarantee, but you can even use this mechanism like some kind of cache.

In the case of stream handle context, I don't know how long the context can live because after handle is closed it doesn't make sense for the OS to keep a context for a specific handle opened and closed already. But still could be deleted with a delay and maybe that would explain why you touched.

The rule should be: if your code is holding a pointer to the context, you should ensure the reference count was incremented (above what FltSetStreamContext did) and keep it like this until you guarantee you don't use the pointer anymore.

1 Like

Just a clarification: A Stream Handle Context is really a File Object Context, so it lives for as long as the File Object does and is not actually tied to a specific HANDLE.

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.