Minifilter: Directly changing IRP_MJ_WRITE buffers

Hi,

As shown by minifilter code snippet below, I am incrementing the IRP_MJ_WRITE buffers directly in PreOpWrite and decrementing them in PostOpWrite.

  1. My first query is whether I can manipulate buffers directly (no file size/attributes changed though) or should I use “swap buffers” as in swapbuffer WDK sample.

I am testing it in Wordpad (small 4-5kb file). On debug printing the Write buffer I found out that:
* I am receiving 2 WRITE IRPrequests for same file save. First one is without MdlAddress and Second one is with MdlAdress.
* PreOpWrite for 2nd IRP has buffer bytes already incremented, even though I am decrementing it in PostOpWrite for 1st IRP. As a result final buffer in 2nd IRP PreOpWrite is incremented twice.

However, after closing and opening the file again, the contents were correct i.e. the characters in file were only the one increment of original buffers.

  1. Is this a bug I need to correct, how? Is there a way to check if I have already manipulated the buffer before and not do it again.

Note the if I comment out the PostOpWrite the file (after saving, closing and then opening) shows that the buffers bytes were incremented twice).

//CODE
FLT_PREOP_CALLBACK_STATUS
PreOpWrite (
__inout PFLT_CALLBACK_DATA Data,
__in PCFLT_RELATED_OBJECTS FltObjects,
__deref_out PVOID *CompletionContext
)
{
FLT_PREOP_CALLBACK_STATUS returnStatus = FLT_PREOP_SUCCESS_NO_CALLBACK; //assume we are NOT going to call our completion routine
UNICODE_STRING uStrPath;
NTSTATUS nStatus;

if (PASSIVE_LEVEL == KeGetCurrentIrql() &&
RP_MJ_WRITE == Data->Iopb->MajorFunction &&
SUCCESS == getFileNameWithCompletePath(&uStrPath, Data, FltObjects->FileObject) &&
TRUE == isFileToBeChanged(uStrPath))
{
char *ioBuffer = NULL;
PMDL MdlAddress = Data->Iopb->Parameters.Write.MdlAddress;
ULONG Length = (ULONG)Data->Iopb->Parameters.Write.Length;

if (NULL != MdlAddress)
{
// Don’t expect chained MDLs this high up the stack
ASSERT(MdlAddress->Next == NULL);
ioBuffer = (char*)MmGetSystemAddressForMdlSafe(MdlAddress, NormalPagePriority);
}
else
{
ioBuffer = (char*)Data->Iopb->Parameters.Write.WriteBuffer;
}
if (NULL != ioBuffer)
{
ULONG ii;
FltSetCallbackDataDirty(Data);
for(ii = 0; ii < Length; ii++) ioBuffer[ii]++;
}
else
{
Data->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
Data->IoStatus.Information = 0;
}
ExFreePoolWithTag(uStrPath.Buffer,‘2fNP’);
uStrPath.Buffer = NULL;
returnStatus = FLT_PREOP_SUCCESS_WITH_CALLBACK;

}
return returnStatus;
}

FLT_POSTOP_CALLBACK_STATUS
PostOpWrite (
__inout PFLT_CALLBACK_DATA Data,
__in PCFLT_RELATED_OBJECTS FltObjects,
__in PVOID CompletionContext,
__in FLT_POST_OPERATION_FLAGS Flags
)
{
UNICODE_STRING uStrPath;
NTSTATUS nStatus;

if (PASSIVE_LEVEL == KeGetCurrentIrql() &&
RP_MJ_WRITE == Data->Iopb->MajorFunction &&
SUCCESS == getFileNameWithCompletePath(&uStrPath, Data, FltObjects->FileObject) &&
TRUE == isFileToBeChanged(uStrPath))
{
char *ioBuffer = NULL;
PMDL MdlAddress = Data->Iopb->Parameters.Write.MdlAddress;
ULONG Length = (ULONG)Data->Iopb->Parameters.Write.Length;

if (NULL != MdlAddress)
{
// Don’t expect chained MDLs this high up the stack
ASSERT(MdlAddress->Next == NULL);
ioBuffer = (char*)MmGetSystemAddressForMdlSafe(MdlAddress, NormalPagePriority);
}
else
{
ioBuffer = (char*)Data->Iopb->Parameters.Write.WriteBuffer;
}
if (NULL != ioBuffer)
{
ULONG ii;
if (NT_SUCCESS(Data->IoStatus.Status)) Length = (ULONG)Data->IoStatus.Information;
for(ii = 0; ii < Length; ii++) ioBuffer[ii]–;
}
else
{
Data->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
Data->IoStatus.Information = 0;
}
ExFreePoolWithTag(uStrPath.Buffer,‘2fNP’);
uStrPath.Buffer = NULL;

}
return FLT_POSTOP_FINISHED_PROCESSING;
}

Further, I would like to add that after rebooting the system the content in the file were found to be incremented twice. Any help in fixing it would be appreciated

Hi Aneel
At the risk of pointing out the obvious are you sure that this is being decremented in the PostWrite callback? PostWrite is frequently called at DISPATCH_LEVEL which would bypass your logic completely. So too would a failure from getFileNameWithCompletePath or isFileToBeChanged.

Regards

Mark

It would appear you are not differentiating paging writes from ‘normal’,
top level writes. If you are going to manipulate the content of buffers
then, in general, you would do this for any ‘non-cached’ IO which would
include paging writes. The reason is that you would only want to modify
the content which is on disk, not the content which is in the system cache.

The other point to note is that by manipulating the content directly in
the write buffer then you are modifying the buffers which describe the
file in the system cache, in the case of paging writes. Thus if there
happens to be a read of the system cache via memory mapped IO, that read
could result in retrieving corrupted data. Instead, for writes, you
should make a copy of the data and perform your data manipulations on
this buffer which is written to disk, like the swap buffers example.

For reads, you can modify the data directly within the paging read buffer.

Of course, the above assumes that you are trying to implement some sort
of data manipulation/modification which is ‘hidden’ from the caller;
i.e. the caller will always see the correct, unmodified data whereas the
data on disk contains the modified data.

Pete

On 1/17/2013 4:46 AM, xxxxx@gmail.com wrote:

Hi,

As shown by minifilter code snippet below, I am incrementing the IRP_MJ_WRITE buffers directly in PreOpWrite and decrementing them in PostOpWrite.

  1. My first query is whether I can manipulate buffers directly (no file size/attributes changed though) or should I use “swap buffers” as in swapbuffer WDK sample.

I am testing it in Wordpad (small 4-5kb file). On debug printing the Write buffer I found out that:
* I am receiving 2 WRITE IRPrequests for same file save. First one is without MdlAddress and Second one is with MdlAdress.
* PreOpWrite for 2nd IRP has buffer bytes already incremented, even though I am decrementing it in PostOpWrite for 1st IRP. As a result final buffer in 2nd IRP PreOpWrite is incremented twice.

However, after closing and opening the file again, the contents were correct i.e. the characters in file were only the one increment of original buffers.

  1. Is this a bug I need to correct, how? Is there a way to check if I have already manipulated the buffer before and not do it again.

Note the if I comment out the PostOpWrite the file (after saving, closing and then opening) shows that the buffers bytes were incremented twice).

//CODE
FLT_PREOP_CALLBACK_STATUS
PreOpWrite (
__inout PFLT_CALLBACK_DATA Data,
__in PCFLT_RELATED_OBJECTS FltObjects,
__deref_out PVOID *CompletionContext
)
{
FLT_PREOP_CALLBACK_STATUS returnStatus = FLT_PREOP_SUCCESS_NO_CALLBACK; //assume we are NOT going to call our completion routine
UNICODE_STRING uStrPath;
NTSTATUS nStatus;

if (PASSIVE_LEVEL == KeGetCurrentIrql()&&
RP_MJ_WRITE == Data->Iopb->MajorFunction&&
SUCCESS == getFileNameWithCompletePath(&uStrPath, Data, FltObjects->FileObject)&&
TRUE == isFileToBeChanged(uStrPath))
{
char *ioBuffer = NULL;
PMDL MdlAddress = Data->Iopb->Parameters.Write.MdlAddress;
ULONG Length = (ULONG)Data->Iopb->Parameters.Write.Length;

if (NULL != MdlAddress)
{
// Don’t expect chained MDLs this high up the stack
ASSERT(MdlAddress->Next == NULL);
ioBuffer = (char*)MmGetSystemAddressForMdlSafe(MdlAddress, NormalPagePriority);
}
else
{
ioBuffer = (char*)Data->Iopb->Parameters.Write.WriteBuffer;
}
if (NULL != ioBuffer)
{
ULONG ii;
FltSetCallbackDataDirty(Data);
for(ii = 0; ii< Length; ii++) ioBuffer[ii]++;
}
else
{
Data->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
Data->IoStatus.Information = 0;
}
ExFreePoolWithTag(uStrPath.Buffer,‘2fNP’);
uStrPath.Buffer = NULL;
returnStatus = FLT_PREOP_SUCCESS_WITH_CALLBACK;

}
return returnStatus;
}

FLT_POSTOP_CALLBACK_STATUS
PostOpWrite (
__inout PFLT_CALLBACK_DATA Data,
__in PCFLT_RELATED_OBJECTS FltObjects,
__in PVOID CompletionContext,
__in FLT_POST_OPERATION_FLAGS Flags
)
{
UNICODE_STRING uStrPath;
NTSTATUS nStatus;

if (PASSIVE_LEVEL == KeGetCurrentIrql()&&
RP_MJ_WRITE == Data->Iopb->MajorFunction&&
SUCCESS == getFileNameWithCompletePath(&uStrPath, Data, FltObjects->FileObject)&&
TRUE == isFileToBeChanged(uStrPath))
{
char *ioBuffer = NULL;
PMDL MdlAddress = Data->Iopb->Parameters.Write.MdlAddress;
ULONG Length = (ULONG)Data->Iopb->Parameters.Write.Length;

if (NULL != MdlAddress)
{
// Don’t expect chained MDLs this high up the stack
ASSERT(MdlAddress->Next == NULL);
ioBuffer = (char*)MmGetSystemAddressForMdlSafe(MdlAddress, NormalPagePriority);
}
else
{
ioBuffer = (char*)Data->Iopb->Parameters.Write.WriteBuffer;
}
if (NULL != ioBuffer)
{
ULONG ii;
if (NT_SUCCESS(Data->IoStatus.Status)) Length = (ULONG)Data->IoStatus.Information;
for(ii = 0; ii< Length; ii++) ioBuffer[ii]–;
}
else
{
Data->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
Data->IoStatus.Information = 0;
}
ExFreePoolWithTag(uStrPath.Buffer,‘2fNP’);
uStrPath.Buffer = NULL;

}
return FLT_POSTOP_FINISHED_PROCESSING;
}


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


Kernel Drivers
Windows File System and Device Driver Consulting
www.KernelDrivers.com
866.263.9295

> 1) My first query is whether I can manipulate buffers directly (no file size/attributes changed though)

Surely you cannot, you are updating the app’s data without the app being aware of this.

The app will see that WRITE have altered to data buffer.


Maxim S. Shatskih
Windows DDK MVP
xxxxx@storagecraft.com
http://www.storagecraft.com