Byte Locks in Isolation Filter

I’m trying to implement byte locks in my isolation filter but haven’t seemed to get it quite right yet. I’ve tried a couple different approaches but none work properly. To test byte locks I’m using the multi-threaded code found at http://msdn.microsoft.com/en-us/library/windows/desktop/aa365204(v=vs.85).aspx

So first, if I don’t handle IRP_MJ_LOCK_CONTROL explicitly and just allow my pass through function to handle it, at some point in the programs execution it will just hang. No errors or anything but the test program will just lock up. So that leads me to believe something is wrong in my read or write paths.

If I do attempt to handle it, I’ve run into a few problems. First, I’ve tried creating a single FILE_LOCK for a FILE_OBJECT and store it in my STREAM_CONTEXT. When I allocate the file locks, I am passing NULL for both of the callbacks. In my PreCallback for the LOCK message I call FltProcessFileLock and return the PREOP STATUS. When a single thread grabs and releases the FILE_LOCK it works but as soon as another thread does, FLT_PREOP_PENDING is returned. This is where the threads then each get stuck and I’m not sure where I am supposed to call FltCompletePendedPreOperation. Would I queue this to some list and then attempt to call it during CLEAN_UP or CLOSE? Is this even the right approach?

Another way I tried to get this working was to have two separate FILE_LOCKs (one each for the upper and lower file). Then in the PreCallback, I use FltProcessFileLock with the original CallbackData on the upper lock but then modify the CallbackData (taking into account positions and lengths based on some data transformations) and call FltProcessFileLock again with the lower lock using the modified data. What happens here though is a double free bugcheck at a ExFreePool somewhere down the callstack not in my code. I haven’t figured out what it is being freed twice but the !pool info shows it’s some system structure based on the tag.

My other thought was to do something similar above but instead of called FltProcessFileLock again, I would just pass the modified parameters down to the FSD. That too does not work properly. Once I get a chance, I’ll post some WinDbg output to show more information.

> So first, if I don’t handle IRP_MJ_LOCK_CONTROL explicitly and just allow

my pass through function to handle it, at some point in the programs
execution it will just hang. No errors or anything but the test program
will just lock up. So that leads me to believe something is wrong in
my read or write paths.

Yea, well that’s never going to work. You are passing the lock function
down but fulfilling the reads and writes yourself. If you are doing that
then you have to do locking, and caching, and cache callbacks and oplocks
and pretty much every thing that an FSD does.

First, I’ve tried creating a single FILE_LOCK for a FILE_OBJECT and store
it in my STREAM_CONTEXT.

Right. Just like FAT.

In my PreCallback for the LOCK message I call FltProcessFileLock and
return the PREOP STATUS.

Fine.

When a single thread grabs and releases the FILE_LOCK it works but as soon
as another thread does, FLT_PREOP_PENDING is returned.
As expected.

This is where the threads then each get stuck and I’m not sure where I am
supposed to call FltCompletePendedPreOperation.

You don’t. You gave up control of the CBD when you called
FltProcessFileLock. It isn’t your any more.

You *have* wired in the calls in the read and write path
(FltCheckLockForXXXXAccess)?

Hi Rod. So I do handle caching and cache callbacks. I have not implemented oplocks yet though. I am calling FltCheckForXXXAccess in both read and writes just like FAT. i guess Ill add oplock support and see what happens. thanks.

I’ve run into a little issue with oplocks. FltInitializeOplock does not seem to be initializing anything as the returned pointer is always NULL. I’m basically using FastFat verbatim…

//Declaration in my stream context
OPLOCK Oplock;

//In Pre-Create when the stream context is created
/* From MSDN: Caller-supplied pointer variable that receives the initialized opaque oplock pointer. This variable must be initialized to NULL before the initial call to FltInitializeOplock. */
sCtx->Oplock = NULL;
FltInitializeOplock(&sCtx->Oplock);

With no return value, I’m not sure what the issue may be and because the OPLOCK structure is “opaque” I can’t just allocate the memory myself. There is a similar thread (http://www.osronline.com/showthread.cfm?link=240075) but with no solution. Is there any reason this call will fail to initialize the pointer?

Which OS? ISRT that in later versions the creation is deferred until the
oplock is really needed.

Win 7 x64.

That seems to be correct. When it was crashing on first access I assumed it was because it wasn’t initialized properly but then I also missed the note in the docs that say you can only call FltCheckOplock with an IRP based call so once I put that check in it didn’t crash.

Now with oplocks “working” without causing any crashes, I am back to my original problem of the the program freezing which obviously means I’m missing something else. So based on my debug output, when a thread successfully gets the file lock it gets stuck when it attempts to write. FltCheckLockForWriteAccess fails so like FastFat I return STATUS_FILE_LOCK_CONFLICT and FLT_PREOP_COMPLETE but this is a call that shouldn’t fail because the same thread currently has the lock and is within the byte range that the lock was issued for. And if it does fail, I would think that an unlock call should follow from that thread but it just hangs.

Here is a portion of the IRP_MJ_LOCK handler.

KeEnterCriticalRegion();

if (!ExAcquireResourceSharedLite(ufcb->Resource, TRUE)) {
KeLeaveCriticalRegion();
FltReleaseContext(sCtx);
Data->IoStatus.Status = STATUS_UNSUCCESSFUL;
Data->IoStatus.Information = 0;
return FLT_PREOP_COMPLETE;
}

__try {
ret = FltCheckOplock(&sCtx->Oplock, Data, NULL, NULL, NULL);
if (Data->IoStatus.Status != STATUS_SUCCESS) {
__leave;
}

ret = FltProcessFileLock(sCtx->UpperFileLock, Data, NULL);
} __finally {
ExReleaseResourceLite(ufcb->Resource);
KeLeaveCriticalRegion();
}

return ret;

So two questions that may help me figure this out:
-What is the appropriate FLT_PREOP_CALLBACK_STATUS to return if FltCheckLockForXXXAccess fails? Pending or complete?
-Would disallowing FastIO throughout the filter (FLT_PREOP_DISALLOW_FASTIO) possibly be a reason behind this?

> What is the appropriate FLT_PREOP_CALLBACK_STATUS to return if

FltCheckLockForXXXAccess fails? Pending or complete?

Complete, you are not holding on to the request to complete it later.

From reading FAT you set iosb.Status to STATUS_FILE_LOCK_CONFLICT and
complete the CBD.

-Would disallowing FastIO throughout the filter
(FLT_PREOP_DISALLOW_FASTIO) possibly be a reason behind this?

It will certainly change flows.

So like most issues, it came down to my own carelessness. I had modified some CBD parameters earlier in the path before calling FltCheckLockForXXXAccess. Once I swapped them back to the original, it began to work. I appreciate the help though.