Share via

FLT_CALLBACK_DATA::RequestorMode is UserMode but FILE_RENAME_INFORMATION::RootDirectory is kernel handle

Benedikt Lazar 0 Reputation points
2026-05-13T14:53:03.87+00:00

I develop a file system minifilter driver and have observed the following issue:

When intercepting a IRP_MJ_SET_INFORMATION in a PreOperationCallback, I could see that FLT_CALLBACK_DATA::RequestorMode was UserMode but the handle in FILE_RENAME_INFORMATION::RootDirectory was a kernel handle.

Is this to be expected?

A bit more detail on my concrete situation:

I could observe this case when installing an starting the Claude Desktop app on my machine.

I could trace this observatioin back to Claude Desktop calling MoveFileW with these arguments:

lpExistingFileName = C:\Users<UserName>\AddData\Roaming\Claude\Local Storage\leveldb/000001.dbtmp

lpNewFileName = C:\Users<UserName>\AddData\Roaming\Claude\Local Storage\leveldb/CURRENT

Letting sit Claude at a break point before MoveFileW and looking into the file system I saw even C:\Users<UserName>\AddData\Roaming\Claude didn’t exist.

Letting Claude execute MoveFileW and looking at ProcMon I saw that the first Create went to C:\Users<UserName>\AppData\Local\Packages\Claude_pzs8sxrjxfjjc\LocalCache\Roaming\Claude\Local Storage\leveldb/000001.dbtmp

What was particularly interesting was, that C:\Users<UserName>\AppData\Local\Packages\Claude_pzs8sxrjxfjjc\LocalCache\ basically mirrored AppData (having subdirs Local, LocalLow and Roaming).

I did some research and I found that apparently the Windows AppModel Runtime redirects some paths in the file system to …\AppData\Local\Packages<AppName>_<ID>...

Since Claude Desktop is a MSIX app I suspect this is what I saw here.

Looking at ProcMon even further I could see multiple calls originating from bindflt.sys within the MoveFileW. I suspect that it is bindflt.sys that injects the kernel handle into the FILE_RENAME_INFORMATION buffer.

If so, shouldn't it also change the RequestorMode?

If bindflt.sys behaves as specified, that means I cannot rely on FLT_CALLBACK_DATA::RequestorMode to tell me what kind of handle FILE_RENAME_INFORMATION::RootDirectory is.

Is there a official/supported way of finding out if a handle is a user mode or a kernel mode handle?

I am happy to provide more information or traces if necessary.

Best regards

Benedikt

Windows development | Windows Driver Kit (WDK)
0 comments No comments

2 answers

Sort by: Most helpful
  1. Taki Ly (WICLOUD CORPORATION) 1,830 Reputation points Microsoft External Staff Moderator
    2026-05-14T08:37:36.2633333+00:00

    Hello @Benedikt Lazar ,

    According to the Understanding how packaged desktop apps (MSIX) run on Windows documentation, operations in the user's AppData are dynamically redirected to a private LocalCache to prevent system rot. bindflt.sys achieves this by injecting its own kernel handle. It intentionally leaves RequestorMode as UserMode for security. As explicitly noted in the ObReferenceObjectByHandle documentation, UserMode forces the system to check the requested access against the object's granted access. If bindflt.sys elevated it to KernelMode, it would bypass Access Control List (ACL) checks, potentially allowing a restricted app to escalate privileges.

    While Windows doesn't expose a public API for this, in the NT architecture, kernel handles typically have their most significant bit (MSB) set. You can see architectural evidence of this in structures like OB_PRE_OPERATION_INFORMATION, which explicitly dedicates a KernelHandle bit. Because of this MSB behavior, kernel handles evaluate to a negative number. I usually use a macro like this to perform a quick check:

    #define IS_KERNEL_HANDLE(h) (((ULONG_PTR)(h) != (ULONG_PTR)-1) && ((LONG_PTR)(h) < 0))
    

    If you strictly need to call ObReferenceObjectByHandle, the safe workaround is a try/fallback: call it with UserMode first. If it returns STATUS_INVALID_HANDLE and the macro above is true, you can safely retry with KernelMode.

    If your ultimate goal is simply to construct the destination path, I suggest not manually parsing this handle. Resolving handles injected by bindflt.sys in a PreOperationCallback is notoriously prone to triggering BSODs.

    Instead, the recommended alternative is to pass the handle directly into the FltGetDestinationFileNameInformation API:

    NTSTATUS status;
    PFLT_FILE_NAME_INFORMATION nameInfo = NULL;
    status = FltGetDestinationFileNameInformation(
        FltObjects->Instance,
        FltObjects->FileObject,
        RenameInfo->RootDirectory, // Pass the handle here directly, FltMgr will handle it
        RenameInfo->FileName,
        RenameInfo->FileNameLength,
        FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT,
        &nameInfo
    );
    
    if (NT_SUCCESS(status)) {
        // FltMgr safely resolves the virtualized root directory and combines it with the file name.
        FltReleaseFileNameInformation(nameInfo);
    }
    

    Hope this gives you a direction. If you found my response helpful or informative, I would greatly appreciate it if you could provide feedback by following this guide.

    Thank you.

    Was this answer helpful?


  2. AI answer

    2026-05-13T14:53:55.3066667+00:00

    Yes, this is expected, and it means a minifilter cannot rely on FLT_CALLBACK_DATA::RequestorMode to determine whether a handle in FILE_RENAME_INFORMATION::RootDirectory is a user‑mode or kernel‑mode handle.

    Key points from the documented model:

    1. RequestorMode reflects the original caller, not the handle type
      • RequestorMode is effectively a copy of the thread’s PreviousMode at the time the I/O was issued.
      • As documented, PreviousMode/RequestorMode is used by the OS to decide whether to treat parameters as coming from user mode or kernel mode (for example, whether to probe buffers or interpret a handle as a user handle):
        • If PreviousMode = UserMode, Nt* routines probe buffers and look up handles in the user handle table.
        • If PreviousMode = KernelMode, they treat parameters as trusted and can use kernel handles.
      • However, nothing guarantees that every handle embedded in an IRP buffer (like FILE_RENAME_INFORMATION::RootDirectory) is a user handle just because RequestorMode == UserMode. Kernel components (file systems, filters, redirection layers like bindflt, etc.) can and do inject kernel handles while still servicing a user‑mode request.
    2. Drivers must not assume handle type from RequestorMode
      • The documented guidance for secure handle usage in kernel drivers is:
        • Convert user‑supplied handles to object pointers using ObReferenceObjectByHandle, with AccessMode taken from Irp->RequestorMode and the correct ObjectType (for example, IoFileObjectType).
        • For top‑level drivers, the static analysis rule C28126 explicitly warns that AccessMode should be Irp->RequestorMode, not hard‑coded UserMode or KernelMode.
      • This guidance exists precisely because:
        • A kernel driver may receive IRPs whose RequestorMode is UserMode, but which contain kernel handles (for example, supplied by another kernel component higher in the stack).
        • Intermediate drivers are expected to pass kernel handles safely by setting AccessMode = KernelMode when they themselves call ObReferenceObjectByHandle, while the top‑level driver uses Irp->RequestorMode.
      • Therefore, RequestorMode is about how the OS should validate parameters, not a guarantee about the origin or “kind” of every handle embedded in those parameters.
    3. Why a user‑mode request can carry a kernel handle
      • The handle management documentation explicitly calls out that kernel drivers should prefer creating their own handles with OBJ_KERNEL_HANDLE so they are valid in arbitrary process context and only accessible from kernel mode.
      • File systems and filters often perform proxy operations on behalf of a user request (for example, path redirection, virtualization, cloud sync, or MSIX/AppModel redirection). In doing so, they may:
        • Open files using kernel handles.
        • Populate or modify IRP parameters (including structures like FILE_RENAME_INFORMATION) with those kernel handles.
      • The proxy‑operations guidance stresses that filters must ensure they do not accidentally perform operations with more privilege than the original user, but it does not forbid them from using kernel handles internally. Instead, it recommends using mechanisms like IoCreateFile with IO_FORCE_ACCESS_CHECK to enforce security checks with the correct credentials.
      • In such scenarios, the IRP still represents a user‑mode request (RequestorMode = UserMode), but some of the state (like RootDirectory) may now be a kernel handle introduced by a kernel component.
    4. Official/supported way to distinguish user vs kernel handles
      • The documented, supported pattern is not to “inspect” a handle to see if it is user or kernel, but to:
        1. Use ObReferenceObjectByHandle with the correct AccessMode and ObjectType.
        2. For top‑level drivers, pass Irp->RequestorMode as AccessMode.
        3. For lower‑level drivers, pass KernelMode and rely on the top‑level driver to have done the access check.
      • The handle‑management guidance explicitly states:
        • “The best practice is to convert the handle to an object pointer by calling ObReferenceObjectByHandle, specifying the correct AccessMode (usually from Irp->RequestorMode), DesiredAccess, and ObjectType…”
      • There is no documented API that, given an arbitrary handle value, returns “this is a user handle” vs “this is a kernel handle”. Instead, the contract is:
        • Use ObReferenceObjectByHandle with the appropriate AccessMode for the layer of the stack being implemented.
        • Structure the driver stack so that the top‑level driver enforces access using RequestorMode, and lower drivers treat handles as kernel‑mode (trusted) and do not redo access checks.
    5. Implications for a minifilter
      • A minifilter is typically not the top‑level creator of the IRP; it sits in the stack below user‑mode and above the file system.
      • According to the C28126 guidance:
        • The top‑level driver should use Irp->RequestorMode.
        • All other drivers in the stack should specify KernelMode when calling ObReferenceObjectByHandle.
      • For a minifilter, this means:
        • Do not assume that RequestorMode == UserMode implies FILE_RENAME_INFORMATION::RootDirectory is a user handle.
        • When dereferencing handles that come from IRP parameters, use KernelMode as AccessMode (because the top‑level component should already have enforced access), unless the minifilter is explicitly acting as the top‑level consumer of a user handle.
        • Continue to use RequestorMode for decisions about how to treat user buffers and for security policy (for example, deciding whether to allow an operation based on whether it originated from user vs kernel), but not as a classifier for handle type.

    In summary:

    • FLT_CALLBACK_DATA::RequestorMode being UserMode while FILE_RENAME_INFORMATION::RootDirectory is a kernel handle is consistent with the documented driver model.
    • There is no supported API to “test” whether a handle is user or kernel; the supported pattern is to use ObReferenceObjectByHandle with the correct AccessMode and to follow the top‑level vs lower‑level driver responsibilities described in the handle‑management and C28126 documentation.

    References:

    AI-generated content may be incorrect. Read our transparency notes for more information.

    Was this answer helpful?

Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.