Read data on paged write path

Hello.

I have a minifilter driver.
Sometimes I need to be able on PreWrite path read data from the place where it will be written (same file, same offset, same length). Also I need to get size of file here. In situation when it is not paging IO (no flag IRP_PAGING_IO) all is OK. But when it is paging IO, I have a lot of troubles. It is well-reproduced when I am writing data to file via mapped pointer. IoGetTopLevelIrp is not NULL.

  1. I can’t get my stream handle context
  2. I can’t get name, FltGetFileNameInformation returns error status, but I saw that operations to my file via FileMon.
  3. FltQueryInformation file causes BSOD, if I use FltObjects that arrived to me.

As I know, FileMon uses own hash-table based on FileObjects or FsContext, I don’t know exactly. I added to filter hash-table based on FsContext.
And now I do following: when I see paged write, I search by FsContext previously saved FltObjects, and I perform FltQueryInformation and FltReadFile via FltObjects that I found in my hash table. I have no BSOD here and in such way I can read size and data, but my system freeze eventually. In fact there are a lot of posts saying that it is very dangerous to perform file operations on paged IO path or IoGetTopLevelIrp is not NULL.
I tried move this actions to separate work item, with KeWaitForSingleObject in main thread – but it is bad solution. Also I tried to save\set to NULL\restore TopLevelIrp – it is bad solution too. If I do it , I have BSOD inside ntfs or freezing.
Is there some hope to solve this problem or I need to redesign all ?
Can I read size and data directly from cache manager maybe (assume it was read before) ?

>the place where it will be written (same file, same offset, same length)

So if you are reading data from the same file (same file object) in paging write path.

  1. You can be called at APC_LEVEL
  2. You must not cause neither data not code page fault. Imagine case when there is lack of physical memory in system and you are called because system is releasing physical pages. Page-fault causes deadlock in MM.
  3. You must roll your own IRP. FltQueryInformation and FltWriteFile are restricted to PASSIVE_LEVEL.
  4. Paging write can be sent (and is very offten) after cleanup. After cleanup only paging operations are allowed. It means you can only send paging read operation, which is not recommended. I am not sure if you can issue paging IRP_MJ_QUERY_INFO.
  5. Little bit hidden restriction is that you must not use cached operations, because of possible deadlock in CC.

If you read from different file point 4) is not applicable.

-bg

Thanks for your answer, Bronislav. I?ve done some new tries / experiments and I can say that:

  • FltReadFile without any other operations does not caused troubles (I mean that I have not seen neither BSOD nor deadlocks, but I cannot guarantee that it will never cause a problems).
  • Using own IRP with IRP_MJ_QUERY_INFORMATION code to get file size (FileStandardInformation) causes deadlock.
  • I succeeded to get FILE_STANDARD_INFORMATION via pDeviceObject->DriverObject->FastIoDispatch->FastIoQueryStandardInfo. It never failed and I haven?t see deadlock.

And now I want to ask: is there way to verify that this calls will be always safe? A lot of time that I saw it in a stable state, as I suppose, cannot guarantee stability and the situation I saw maybe just my luck.

FltCreateFile very dangerous and it is not acceptable to call it here (my observation based on a lot of experiments).
Also now I realized that I need to perform write to another file on this paging write path. The only way I found is to create file with FILE_NO_INTERMEDIATE_BUFFERING flag (do it before paging write) and to call FltWriteFile with flags FLTFL_IO_OPERATION_NON_CACHED | FLTFL_IO_OPERATION_PAGING. Otherwise it returns STATUS_FILE_LOCK_CONFLICT.
So the second question: is this way correct and safe? Is there danger of occurring troubles in future because I am performing this operations on paged write path ?

You really may not want to do any operations in the paging path whatsoever. The paging path is mostly invokf by the vmm - modified page writer - and cache manager - lazy writer - to flush dirty pages to disk. Check the top level irp for looking this info up. I am not sure if you can pend paging path operations but for sure this is extra overhead. My advice: try to see if you really need to make i/o on the paging path knowing what it stands for

>- Using own IRP with IRP_MJ_QUERY_INFORMATION code to get file size

(FileStandardInformation) causes deadlock.
Synchronous operations are synchronized by means of FILE_OBJECT lock. Because you use application’s FILE_OBJECT you cannot affect if it is sync or not. It is determined by FILE_SYNCHRONOUS_IO_ALERT or FILE_SYNCHRONOUS_IO_NONALERT flag during create which I cannot recommend you somehow modify. Check FO.Busy for determining if FO is locked.

And now I want to ask: is there way to verify that this calls will be always
safe? A lot of time that I saw it in a stable state, as I suppose, cannot
guarantee stability and the situation I saw maybe just my luck.

You should own file object before sending of this fast IO. You can be called by MPW (MM) or lazy writer(CC) with paging write. They both acquire fcb resources by (FastIO/cache) callback. I think you should use FsRtlGetFileSize() routine instead direct call of FastIO. Doc doesn’t state allowed IRQL, but it should work at APC_LEVEL. This will not be synchronized with sync operations on the same file.

FltCreateFile very dangerous and it is not acceptable to call it here (my
observation based on a lot of experiments).
It’s too late to open file in paging write path. You must do it in advance. In post-write/read detect if caching was not initialized and if yes open the file for later usage. (see CcIsFileCached())

The only way I found is to create file with
FILE_NO_INTERMEDIATE_BUFFERING flag (do it before paging write) and to
call FltWriteFile with flags FLTFL_IO_OPERATION_NON_CACHED |
FLTFL_IO_OPERATION_PAGING.

I am not sure how FltWriteFile is implemented internaly, but according doc it is limited to PASSIVE_LEVEL from which I guess it is wrapper over Zw API. I would roll my own IRP for write in such a situation.
-bg

>You should own file object before sending of this fast IO.
Please ignore this statement in my previous post as it is not true.
-bg

Hello. Thanks again.
" Check FO.Busy for determining if FO is locked."

  • Do you mean to check FILE_OBJECT::Lock with KeReadStateEvent and if it is in not signaled state, it means that FILE_OBJECT is locked and it is the reason to do not do anything with it?

I ment just check it during debugging session in debugger.
-bg