Thursday, January 26, 2012

More on Asynchronous Handles

Since we're on the subject of asynchronous handles I'd like to spend some time do discuss the create options that deal with synchronization and what their effects are. The documentation for ZwCreateFile() mentions three of them: SYNCHRONIZE, FILE_SYNCHRONOUS_IO_ALERT and FILE_SYNCHRONOUS_IO_NONALERT. The documentation describes briefly how they might be used but doesn't go into enough detail (in my opinion). So let's talk about each of the options.

  • SYNCHRONIZE - this option is not specific to files. As you probably already know, the NT kernel makes use of objects that have a common subset of features. For example most objects can be used for signaling and thus can be waited on. Each object is signaled under specific circumstances (threads are signaled when they are terminated, files are signaled when IO has completed and so on). If an object is to be used for synchronization then the user must have SYNCHRONIZE access to it (the user generally has a handle to an object and so the handle must allow for SYNCHRONIZE access). In other words, if you open a handle to an object and don't specify SYNCHRONIZE, you can't wait on it. As I mentioned before, a FILE_OBJECT might be is signaled when an IRP has completed. The FILE_OBJECT will be signaled during IRP completion in most cases (there are some exceptions here) but only handles that were opened with SYNCHRONIZE can wait on it. Just specifying this for ZwCreateFile doesn't make the handle synchronous, but does allow a caller to issue requests that might be completed asynchronously (the caller gets STATUS_PENDING) and then to wait on the handle to know when the IO has completed. Of course, any IRP will signal the FILE_OBJECT so in a heavily asynchronous environment waiting on the handle is not particularly useful and an event should be used instead (the IO manager will signal the event instead of the FILE_OBJECT when the caller provides one). The MSDN page on Synchronous and Asynchronous I/O is an interesting read as well.
  • FILE_SYNCHRONOUS_IO_ALERT - this means that he handle is synchronous. There is at most one request issued by the IO manager on that handle at any given time. Of course, other components in the system that roll their own IRPs can issue additional IRPs on the same FILE_OBJECT even if it's synchronous and if they don't use an event when doing so IRP completion might signal the FILE_OBJECT (I would call this a bug in the driver that does this). So calling a handle "synchronous" only means it's synchronous at a top level (Nt API level). When the user performs any operation on the user handle, the function won't return until the request is complete. It is therefore impossible for a user to queue more than one request on a handle using only one thread anyway. However, if a handle is synchronous and a user queues multiple requests on a synchronous handle by using multiple threads then the IO manager will wait for each request to complete before sending the next one down. In case of the FILE_SYNCHRONOUS_IO_ALERT flag specifically, all the waits will be alertable (if any APCs are queued they can be processed; a common case for this is when the thread is killed and the wait is abandoned).
  • FILE_SYNCHRONOUS_IO_NON_ALERT - this is the same as above (the handle is synchronous and so on) but the difference from FILE_SYNCHRONOUS_IO_ALERT is that waits are not alertable, i.e. if any waits happen along the way they won't return until the objects they're waiting for are signaled (and APCs queued to such threads won't be processed).

Now, in terms of the FILE_OBJECT associated with the create, the SYNCHRONIZE option has no effect (the object itself can always be signaled, it's just that some handles might not be able to wait on it). However, if either the FILE_SYNCHRONOUS_IO_ALERT or the FILE_SYNCHRONOUS_IO_NON_ALERT flags are set then the FILE_OBJECT will have a special flag set, the FO_SYNCHRONOUS_IO. If the FILE_SYNCHRONOUS_IO_ALERT is specified then the FILE_OBJECT will also have the FO_ALERTABLE_IO (obviously if FILE_SYNCHRONOUS_IO_NON_ALERT is specified then only FO_SYNCHRONOUS_IO is set). Also, it's not possible to call create with FILE_SYNCHRONOUS_IO_ALERT or FILE_SYNCHRONOUS_IO_NON_ALERT without specifying SYNCHRONIZE, since it would be impossible for the IO manager to synchronize with the FILE_OBJECT.

When a handle is opened for asynchronous IO (FO_SYNCHRONOUS_IO is not set) and an Nt API is called that is synchronous (like NtQueryInformationFile() from my previous post) then the function will allocate an EVENT and pass it in the IRP to be signaled when the request has completed. If the FILE_OBJECT does have FO_SYNCHRONOUS_IO set then it will simply wait on the file handle.

One more thing to note is that it is possible for the synchronous mode to change during the lifetime of a FILE_OBJECT. Calling NtSetInformationFile() with the FileModeInformation information class allows the caller to change this parameter as well so filters must be careful not to assume that once the create is done that this can't change anymore (for example by caching this information or the result of some decision based on this information in a StreamHandleContext).

So now I'd like to talk a bit about what this might mean for filters and what sort of things a filter must be aware:

  • Filters can detect whether a certain operation is synchronous by calling IoIsOperationSynchronous() or the FltMgr counterpart, FltIsOperationSynchronous(). Please note the documentation passage that states: "If the operation is an asynchronous paging I/O operation, the operation is asynchronous, even if one of the other conditions in this list is true". This means that if a file is used for asynchronous paging IO, whether the file has the FO_SYNCHRONOUS_IO flag is irrelevant, the request is still asynchronous. This information can be used as a hint about whether the filter can afford to hold the caller's IRP and perform whatever operations it requires inline or it should post the request to a worker queue or a different thread and return STATUS_PENDING to the caller. If the request is synchronous then it generally is easier to do things inline.
  • If a filter is performing operations in response to a user request (where the filter stops the request for some time and does some work before releasing the operation) they must try to follow the user's instructions and if any wait is required it should be of the right type (if the user wants alertable waits then the filter should try to accommodate them).
  • If a filter wants to use a FILE_OBJECT that it didn't open itself to perform IO then it must take great care to not do something that will signal the file handle. For minifilters this is fairly easy since FltMgr provides a large list of FltXxx APIs and the FltPerformAsynchronousIo() and FltPerformSynchronousIo() functions that help with that and take care of the complexity of issuing IO on a FILE_OBJECT that they don't own.
  • Of course any minifilter that holds on to a user's request for a while should use a cancel-safe queue irrespective of whether the operation is synchronous or not. Using a cancel-safe queue is shown in the cancelSafe minifilter sample in the WDK and legacy filters have a similar API set (IoCsqXxx..) and if I remember correctly there was a sample for that as well.

Thursday, January 19, 2012

Application Compatibility: Asynchronous Handles and Minifilters

Investigating application compatibility (AppCompat) issues with file system filters is the bread and butter of file system developers. There is simply an infinite number of ways in which filters can misbehave so I'm not going to even try to address them here. However, there are some that are worth discussing either because they are fairly common or because they are not the filter's fault but rather the system's.
The one such issue I'd like to talk about in this post is one that can be pretty hard to identify but not that hard to fix (or work around). My hope is that if developers are more aware of the problem they might have an easier time identifying it. The problem is that some applications (mostly user mode but it could happen for kernel drivers as well) occasionally open handles for asynchronous IO and then forget about this and perform IO on the handle as if it is synchronous (or maybe the developers aren't certain how to use a handle that was opened for asynchronous IO ). Anyway, what happens is that the application looks something like this:
 HANDLE hFile;

hFile = CreateFile(szFileName,
                      GENERIC_READ,
                      0,
                      NULL,
                      OPEN_EXISTING,
                      FILE_FLAG_NORMAL | FILE_FLAG_OVERLAPPED,
                      NULL);

...
// use the handle for some asynchronous IO
…
// perform some operation on the handle
If (GetFileInformationByHandle( hFile, ..)) {
 // do stuff with the information 
}
Please note that I'm using GetFileInformationByHandle() here just as an example, any function that works on a file handle could be used instead. Anyway, the problem in some cases is that the function might not be working as expected for asynchronous handles. For our example, GetFileInformationByHandle() is simply a thin wrapper over NtQueryInformationFile and it simply calls it and if the status it gets is NT_SUCCESS(status) then it thinks the IO was successful. However, if the handle has been opened for asynchronous IO it is possible that NtQueryInformationFile will return STATUS_PENDING, which is a success status (so NT_SUCCESS(STATUS_PENDING) == TRUE) and so the caller of GetFileInformationByHandle() might think the operation has completed successfully even though it hasn't. The result of this in our case is the fact that the application might be working an uninitialized buffer.
Of course, most such bugs will be caught during testing but (as always) there is a twist. There are some operations for which the file system never returns STATUS_PENDING and instead it just completes the operation in-line. In fact the file system is generally pretty conservative about returning STATUS_PENDING because doing it introduces additional overhead (there must be another thread that will be used to complete the request) and so the scenario where some operations are completed inline is rather common. This means that during testing the application will never get STATUS_PENDING and so the code that calls GetFileInformationByHandle() always works well and so the potential problem is never found. Driver Verifier has a mode in which it will return STATUS_PENDING for most IO but then not many application developers run with Driver Verifier enabled. I'm not sure whether Application Verifier offers something like this, but it's worth looking into.
This is where file system filters enter the picture. File system filters might return STATUS_PENDING in cases where the file system won't do that. In fact, there is one file system in particular that marks IRPs pending all the time: FltMgr. Filter Manager has simplified the IO model for minifilters quite a lot. Pretty much all the complexity of passing IRPs down and dealing with the completion routine is hidden from minifilters, but there is a cost. In FltMgr's model a filter can decide that it needs to pend a request during a postOp callback. In the NT model however this is not possible, the IRP must have been pended already in order for a completion callback to be able to return STATUS_MORE_PROCESSING_REQUIRED. Since FltMgr doesn't make the filter decide during the preOp whether it will pend the request during the postOp callback (in fact FltMgr allows a filter to not even have a preOp callback), it proactively marks the IRP pending, in case the minifilter will want to return FLT_POSTOP_MORE_PROCESSING_REQUIRED.
If the explanation above seems convoluted, the short version is this: FltMgr returns STATUS_PENDING a lot more than the file system. So this is where the problem shows up. Someone installs an innocent looking minifilter and suddenly some application starts failing in a strange way (if you're lucky; the unlucky case is the one where application starts slowly corrupting data…). The investigation shows that the presence of the filter clearly has something to do with the problem, but it's impossible to pin down what the filter is doing wrong. In some cases even disabling all filter functionality still somehow manages to reproduce the problem (and of course the reason is that the bug is in the application and the mere presence of the filter makes FltMgr return STATUS_PENDING and expose the bug).
So how can this be fixed ? In most cases there is a very simple fix, the filter must replace FLT_PREOP_SUCCESS_WITH_CALLBACK with FLT_PREOP_SYNCHRONIZE for the operation that exhibits the problem (for our example, IRP_MJ_QUERY_INFORMATION). This has the effect of making the operation synchronous at FltMgr's level and so NtQueryInformationFile() won't return STATUS_PENDING anymore. Of course, from a filter perspective the problem is identifying the operation that is causing the problem, but that can be done by replacing FLT_PREOP_SUCCESS_WITH_CALLBACK with FLT_PREOP_SYNCHRONIZE one at a time until the problem no longer occurs. Please note that it's possible that there is more than one operation that needs this so there may be more than one replacement necessary for the app to start working. In my experience it's easiest to start by replacing all the return statuses and then reverting them one at a time. Of course, this is a hack around the real fix (which is fixing the application) but since in most cases the filter developer doesn't have the luxury to tell the app developer to fix their problem this approach can be quite handy.
Now, this problem has largely been addressed by now in most apps since FltMgr has been around for a while and since Vista there are some minifilters running by default on the system and so application developers have mostly fixed their stuff, but it's possible that one still runs into this issue.
Finally I'd like to show you how this might look in practice. This thread shows how this problem might appear to a developer. Also, see KB article 939767 and KB article 2009604 and KB article 970878, all of which are examples of applications doing this sort of thing.

Update:

So I was a bit uncomfortable about not being able to remember if there are any specific Nt APIs that would leak STATUS_PENDING so I spent some time looking at some of the APIs. In most cases (including in the NtQueryInformationFile() function from my example) if the handle was opened for async IO and if the IO manager returns STATUS_PENDING the function will wait for the API to complete by waiting on a event. So the Nt APIs are well behaved, but applications might still try to perform reads and writes on a async handle treating it like a synchronous one. For example KB 970878 that I mentioned above states that the problem comes from using BackupRead() and BackupSeek() on an asynchronous handle (and the documentation for BackupWrite() states that it also requires a synchronous handle so it likely would exhibit the same behavior). KB article 939767 also explains how this could happen for a kernel driver (and in fact it's more likely to happen for a driver if they roll their own IRPs (or the minifilter equivalent of using the FLT_CALLBACKDATAs) instead of going through the Nt/Zw APIs since as we've seen those are careful about this sort of thing).

Another thing i've been meaning to say but I forgot until Jeho pointed it out (thanks Jeho!) was that the easiest way I found to check for this scenario is to use the Passthrough sample. It filters all APIs and it has completion routines for all of them, so FltMgr will be doing a lot of pending. Moreover, it's easy to just hand it with the source and everything to the app developer (where possible) and tell them how to reproduce the problem and since it's also coming from Microsoft it carries potentially more weight. However, when using this approach make sure to change the altitude of the sample because if the problem exists in a filter in the file system stack that is lower than the sample it won't reproduce the problem.

Thursday, January 12, 2012

Debugging Minifilters: using !fltkd.filter - Part III

In this post I finally get to look at context information about a filter and how to get to it. The !fltkd extension has split the information about contexts into information about which types of contexts the filter has registered and information about the contexts that the filter is using (they are either referenced by the filter or attached to an object). I'll try to follow the same scheme.
So first we'll look to see what information about the contexts we can get from the _FLT_FILTER structure:
0: kd> dt 94bb2da0 fltmgr!_FLT_FILTER
   +0x000 Base             : _FLT_OBJECT
   +0x014 Frame            : 0x94b58cc0 _FLTP_FRAME
   +0x018 Name             : _UNICODE_STRING "FileInfo"
   +0x020 DefaultAltitude  : _UNICODE_STRING "45000"
   +0x028 Flags            : 2 ( FLTFL_FILTERING_INITIATED )
   +0x02c DriverObject     : 0x9750f598 _DRIVER_OBJECT
   +0x030 InstanceList     : _FLT_RESOURCE_LIST_HEAD
   +0x074 VerifierExtension : 0x94bdcdd0 _FLT_VERIFIER_EXTENSION
   +0x078 VerifiedFiltersLink : _LIST_ENTRY [ 0x926904bc - 0xa0c92b38 ]
   +0x080 FilterUnload     : 0x926b8b6a     long  +ffffffff926b8b6a
   +0x084 InstanceSetup    : (null) 
   +0x088 InstanceQueryTeardown : (null) 
   +0x08c InstanceTeardownStart : (null) 
   +0x090 InstanceTeardownComplete : (null) 
   +0x094 SupportedContextsListHead : 0x94bbee70 _ALLOCATE_CONTEXT_HEADER
   +0x098 SupportedContexts : [6] (null) 
   +0x0b0 PreVolumeMount   : (null) 
   +0x0b4 PostVolumeMount  : (null) 
   +0x0b8 GenerateFileName : (null) 
   +0x0bc NormalizeNameComponent : (null) 
   +0x0c0 NormalizeNameComponentEx : (null) 
   +0x0c4 NormalizeContextCleanup : (null) 
   +0x0c8 KtmNotification  : (null) 
   +0x0cc Operations       : 0x94bb2efc _FLT_OPERATION_REGISTRATION
   +0x0d0 OldDriverUnload  : (null) 
   +0x0d4 ActiveOpens      : _FLT_MUTEX_LIST_HEAD
   +0x100 ConnectionList   : _FLT_MUTEX_LIST_HEAD
   +0x12c PortList         : _FLT_MUTEX_LIST_HEAD
   +0x158 PortLock         : _EX_PUSH_LOCK
0: kd> dp 94bb2da0+0x98 L6
94bb2e38  00000000 94bbee70 00000000 94bbef38
94bb2e48  00000000 00000000
As we can see there are two members that seem to be related to contexts, "SupportedContextsListHead" and "SupportedContexts". Interestingly enough SupportedContexts is an array of fixed size, embedded into the _FLT_FILTER structure. This is the same number of contexts that FltMgr supports in Win7. According to the documentation there seems to be a new context type coming in Win8 (FLT_SECTION_CONTEXT). Anyway, each entry in the array is of type _ALLOCATE_CONTEXT_HEADER, just like the "SupportedContextsListHead" variable. However, the _ALLOCATE_CONTEXT_HEADER doesn't start with a LIST_ENTRY (though it does contain one) so I'm not sure what SupportedContextsListHead actually does (since it's clearly not a list head). Anyway, let's ignore it for now and look at the entries in the array:
0: kd> dt 0x94bbee70 _ALLOCATE_CONTEXT_HEADER
fltmgr!_ALLOCATE_CONTEXT_HEADER
   +0x000 Filter           : 0x94bb2da0 _FLT_FILTER
   +0x004 ContextCleanupCallback : 0x926b7880     void  fileinfo!FIVolumeCleanup+0
   +0x008 Next             : (null) 
   +0x00c ContextType      : 2
   +0x00e Flags            : 0x1 ''
   +0x00f AllocationType   : 0x1 ''
0: kd> dt 94bbef38 _ALLOCATE_CONTEXT_HEADER
fltmgr!_ALLOCATE_CONTEXT_HEADER
   +0x000 Filter           : 0x94bb2da0 _FLT_FILTER
   +0x004 ContextCleanupCallback : 0x926b7cc4     void  fileinfo!FIStreamCleanup+0
   +0x008 Next             : (null) 
   +0x00c ContextType      : 8
   +0x00e Flags            : 0x1 ''
   +0x00f AllocationType   : 0x1 ''
The one odd bit here is the fact that the context type for the second entry in the array is 8, but we know there are only 6 context types.. Looking at the position of the entries in the array we can conclude that these are bit fields and they most likely map to the values in the FLT_CONTEXT_TYPE define. This also helps us identify the members of the array (first entry is for Volume contexts, the second for Instance contexts and so on). This is still a bit confusing since it would seems that the first entry is for an instance context but it's callback is named fileinfo!FIVolumeCleanup. Most likely what's happened is that FileInfo was using volume contexts and then they switched to using instance contexts (most likely for performance reasons) but didn't change the function name. A quick look at luafv shows it uses the same context type but the callback this time indicates it's an instance context:
0: kd> dt a0c72e70 _ALLOCATE_CONTEXT_HEADER
fltmgr!_ALLOCATE_CONTEXT_HEADER
   +0x000 Filter           : 0xa0c92ac0 _FLT_FILTER
   +0x004 ContextCleanupCallback : 0xa29b8a1f     void  luafv!LuafvInstanceCleanup+0
   +0x008 Next             : (null) 
   +0x00c ContextType      : 2
   +0x00e Flags            : 0x1 ''
   +0x00f AllocationType   : 0x1 ''
Another thing worth mentioning is that when allocating contexts of fixed size FltMgr uses a lookaside list. As such we expect that there is a lookaside list related to this context somewhere, and since the name of the structure is "_ALLOCATE_CONTEXT_HEADER" it's easy to guess that the data containing the lookaside list is right after the header. This is especially obvious considering that the memory that was allocated for one context is 0xc8 bytes, much larger than the 0x10 bytes that the _ALLOCATE_CONTEXT_HEADER takes:
0: kd> !pool 0x94bbee70 
Pool page 94bbee70 region is Special pool
*94bbe000 size:  190 data: 94bbee70 (NonPaged) *FMcr  <- we have 0x190 bytes
  Pooltag FMcr : Context registration structures, Binary : fltmgr.sys
0: kd> ? 0x94bbef38 - 0x94bbee70    
Evaluate expression: 200 = 000000c8 <- the second context starts at 0xc8 after the first
0: kd> ? 0xc8 * 2
Evaluate expression: 400 = 00000190 <- 0xc8 * 2 (for two contexts) is exactly 0x190 , the size of our block.. 
We could spend some time to try to reverse-engineer the meaning of the AllocationType and Flags members but it'd take too long.. So let's just see how we can get the contexts that are used by the filter. FltMgr keeps track of all the context it has allocated because it needs to be able to free all of them when the instance is detached, but they are stored inside the actual FLT_INSTANCE structure and while that's an interesting topic in itself, I'd rather not go into it in this post. However, information about all the contexts used by a filter is available when using Driver Verifier. As you might remember from the previous post, the _FLT_VERIFIER_EXTENSION structure has a tree of all the contexts. Unfortunately, the tree for the allocated objects is filled with other stuff as well so it's hard to find just the contexts (file name cache entries are also stored in the verifier tree and when a filter is using FltGetFileNameInformation() most of the verifier entries will be name cache entries (at least that has been my experience so far)).

Thursday, January 5, 2012

Debugging Minifilters: using !fltkd.filter - Part II

Happy New Year everyone! As I promised in my previous post I'll go over some of the flags !fltkd.filter has and show a couple more tricks about debugging filters. So let's start by listing the flags that are available for !fltkd.filter:
0: kd> !fltkd.filter
   filter [addr] [flags] [detail]            Dump FLT_FILTER
      where [flags] specify what information to dump:
         0x00000001 Basic filter information
         0x00000002 Filter's context registration information
         0x00000004 Filter's context usage information
         0x00000008 Filter's object usage/reference information
         0x00000010 Filter's Verifier information
         0x00000020 Filter's Port information
Let's go over each flag and discuss its output. When it comes to output please note that I have a different setup than last time (I've enabled driver verifier) and so the addresses will be different from the ones in my previous post.
  • 0x00000001 Basic filter information - this is actually the default flag so the output is the usual output that is displayed when !fltkd.filter is called for a filter.
  • 0x00000002 Filter's context registration information - this doesn't seem to show anything in addition to what the basic information shows. However, as I mentioned in my previous post, I get an error about how the PVOID type is not defined and as it happens I get that error right when displaying the field "SupportedContexts" and so possibly the extension would do something for contexts but it can't because of the error.
  • 0x00000004 Filter's context usage information - I'm not sure what this is supposed to show but it's only showing a list of the instances for the filter:
    0: kd> !fltkd.filter a0c92ac0 4
    
    FLT_FILTER: a0c92ac0 "luafv" "135000"
       InstanceList             : (a0c92af0)
          FLT_INSTANCE: a0cd0a50 "luafv" "135000"
    
  • 0x00000008 Filter's object usage/reference information - this requires verifier to be enabled and when verification is enabled for the filter it shows this (we'll get into more detail later):
    0: kd> !fltkd.filter a0c92ac0 0x8
    
    FLT_FILTER: a0c92ac0 "luafv" "135000"
       InstanceList             : (a0c92af0)
          FLT_INSTANCE: a0cd0a50 "luafv" "135000"
       Object usage/reference information: 
          References to FLT_CONTEXT                : 0 
          Allocations of FLT_CALLBACK_DATA         : 1 
          Allocations of FLT_DEFERRED_IO_WORKITEM  : 0 
          Allocations of FLT_GENERIC_WORKITEM      : 0 
          References to FLT_FILE_NAME_INFORMATION  : 0 
          Open files                               : 1 
          References to FLT_OBJECT                 : 0 
    
  • 0x00000010 Filter's Verifier information - this shows a long list of what seem to be operations but I think it's broken because they're all NULL.
  • 0x00000020 Filter's Port information - this appears to work but unfortunately I don't have a minifilter that uses communication ports handy so all I can see is this:
    0: kd> !fltkd.filter a0c92ac0 0x20
    
    FLT_FILTER: a0c92ac0 "luafv" "135000"
       InstanceList             : (a0c92af0)
          FLT_INSTANCE: a0cd0a50 "luafv" "135000"
       Client Port List         : Mutex (a0c92bec) List [a0c92c0c-a0c92c0c] mCount=0 
    
So as we can see the flags don't really add much information to this extension. In particular two things seem to be missing, more details about the VerifierExtension and the contexts. So let's take a look at what we can find out without the extension. We'll start with VerifierExtension and we'll look at the symbols that FltMgr publishes:
0: kd> dt fltmgr!*VERIFIER*
          fltmgr!_FLT_VERIFIER_EXTENSION
          fltmgr!_FLT_VERIFIER_EXTENSION_FLAGS
          fltmgr!FLT_VERIFIER_OPERATIONS
          fltmgr!_FLT_VERIFIER_OBJECT
Clearly we need to see what's in fltmgr!_FLT_VERIFIER_EXTENSION and the address we need to look at is shown in !fltkd.filter under "VerifierExtension":
0: kd> !fltkd.filter a0c92ac0

FLT_FILTER: a0c92ac0 "luafv" "135000"
   FLT_OBJECT: a0c92ac0  [02000000] Filter
….
   Client Port List         : (a0c92bec)  mCount=0 
   VerifierExtension        : a0c3add0 
…
0: kd> dt fltmgr!_FLT_VERIFIER_EXTENSION a0c3add0
   +0x000 Flags            : 0 (No matching name)
   +0x004 FilterUnload     : (null) 
   +0x008 InstanceSetup    : 0xa29b562b     long  luafv!LuafvInstanceSetup+0
   +0x00c InstanceQueryTeardown : (null) 
   +0x010 InstanceTeardownStart : (null) 
   +0x014 InstanceTeardownComplete : (null) 
   +0x018 Operations       : [50] FLT_VERIFIER_OPERATIONS
   +0x1a8 Name             : 0xa0c30ff8  "luafv"
   +0x1b0 FltVerifierObjectsLookasideList : _NPAGED_LOOKASIDE_LIST
   +0x200 List             : _TREE_ROOT
   +0x204 Lock             : 0
   +0x208 Count            : [7] 0
   +0x224 FltVerifierObjectsAllocFailures : 0n0
   +0x228 FltVerifierObjectsUnlinkFailures : 0n0
Now, we expect that there are a lot of callbacks pointers (in fact all the callbacks) because when verifier is enabled FltMgr replaces all the callbacks that minifilter can register with stubs so that it can control exactly how the filter's callback behaves. As such it needs a place to store the original function pointers for the filter and here they are, in the VerifierExtension. Incidentally, if you follow the steps outlined in my previous post on finding the callbacks for a minifilter you'll see that for a verified filter all the preOp callbacks are fltmgr!FltvPreOperation and all the postOp callbacks are fltmgr!FltvPostOperation… So know you know where the real callbacks are even for verified filters.
Once we're done with the callbacks there are few interesting things left:
  • Name - this is the name of the filter again but it's not a unicode string, it's an proper ASCII string complete with a NULL.
  • FltVerifierObjectsLookasideList - clearly a lookaside list of some kind, for some objects that are only allocated when verification is enabled.
  • List - this is probably the most interesting one.. When verification is enabled FltMgr tracks certain things (references to contexts, allocations of certain structures and so on) and they're all stored in this list (which is actually a tree, as we're about to see).
  • Lock - obviously some lock protecting the list.
  • Count - not as obvious but this seems to be where the counts for the "Filter's object usage/reference information" flag are coming from..
The rest of the fields don't seem to be interesting and I think they are meant for FltMgr internal tracking. Anyway, let's look at the List and Count in more detail:
0: kd> dt fltmgr!_FLT_VERIFIER_EXTENSION a0c3add0 List.
   +0x200 List  : 
      +0x000 Tree  : 0xcbc78fd8 _RTL_SPLAY_LINKS
0: kd> !tree 0xcbc78fd8 

TREE_ROOT: cbc78fd8 
TREE_NODE: a0ccefd8 (k1=974957b8, k2=00000000) [00010000] InTree
TREE_NODE: cbc78fd8 (k1=cbc62e40, k2=00000000) [00010000] InTree
0: kd> !pool a0ccefd8 2
Pool page a0ccefd8 region is Special pool
*a0cce000 size:   28 data: a0ccefd8 (NonPaged) *FMvj
  Pooltag FMvj : FLT_VERIFIER_OBJECT structure, Binary : fltmgr.sys
0: kd> !pool cbc78fd8 2
Pool page cbc78fd8 region is Special pool
*cbc78000 size:   28 data: cbc78fd8 (NonPaged) *FMvj
  Pooltag FMvj : FLT_VERIFIER_OBJECT structure, Binary : fltmgr.sys
So the tree is a tree of FLT_VERIFIER_OBJECT (which probably come from the FltVerifierObjectsLookasideList) and there are two things in the tree:
0: kd> !pool a0ccefd8 2
Pool page a0ccefd8 region is Special pool
*a0cce000 size:   28 data: a0ccefd8 (NonPaged) *FMvj
  Pooltag FMvj : FLT_VERIFIER_OBJECT structure, Binary : fltmgr.sys
0: kd> !pool cbc78fd8 2
Pool page cbc78fd8 region is Special pool
*cbc78000 size:   28 data: cbc78fd8 (NonPaged) *FMvj
  Pooltag FMvj : FLT_VERIFIER_OBJECT structure, Binary : fltmgr.sys
0: kd> dt a0ccefd8 fltmgr!_FLT_VERIFIER_OBJECT
   +0x000 TreeLink         : _TREE_NODE
   +0x01c Type             : 5
   +0x020 Object           : 0x974957b8 Void
   +0x024 RefCount         : 0n1
0: kd> !pool 0x974957b8 2
Pool page 974957b8 region is Nonpaged pool
*97495780 size:   b8 previous size:   30  (Allocated) *File (Protected)
  Pooltag File : File objects
0: kd> dt cbc78fd8 fltmgr!_FLT_VERIFIER_OBJECT
   +0x000 TreeLink         : _TREE_NODE
   +0x01c Type             : 1
   +0x020 Object           : 0xcbc62e40 Void
   +0x024 RefCount         : 0n1
0: kd> !pool 0xcbc62e40 2
Pool page cbc62e40 region is Special pool
*cbc62000 size:  220 data: cbc62de0 (NonPaged) *FMic
  Pooltag FMic : IRP_CTRL structure, Binary : fltmgr.sys
So as we can see the tree holds all structures tracked by FltMgr. It wasn't immediately obvious but when looking at the numbers returned with the "Filter's object usage/reference information" flag set it seems that the order in which those counts are displayed corresponds to the number that is the "Type" member of the fltmgr!_FLT_VERIFIER_OBJECT structure and that the count of each type are stored in the "Count" array in the of fltmgr!_FLT_VERIFIER_EXTENSION. So type 1 is IRP_CTRL and type 5 is FILE_OBJECT. Since I'm running on Win7 I have FileInfo running and it has more interesting stuff (like a lot of contexts and file names ):
0: kd> !fltkd.filter 94bb2da0 8

FLT_FILTER: 94bb2da0 "FileInfo" "45000"
   InstanceList             : (94bb2dd0)
      FLT_INSTANCE: 94a34dc8 "FileInfo" "45000"
      FLT_INSTANCE: 98952dc8 "FileInfo" "45000"
      FLT_INSTANCE: 99f00dc8 "FileInfo" "45000"
      FLT_INSTANCE: 9afdedc8 "FileInfo" "45000"
      FLT_INSTANCE: 9aeb0dc8 "FileInfo" "45000"
   Object usage/reference information: 
      References to FLT_CONTEXT                : 4229 
      Allocations of FLT_CALLBACK_DATA         : 0 
      Allocations of FLT_DEFERRED_IO_WORKITEM  : 0 
      Allocations of FLT_GENERIC_WORKITEM      : 0 
      References to FLT_FILE_NAME_INFORMATION  : 4224 
      Open files                               : 0 
      References to FLT_OBJECT                 : 0 
I was going to take a look at the context information that we can dig from the FLT_FILTER structure but I see this post has gotten quite long so I'll leave that for next time.