From ae40f0edb167f8782ef592ee600ddf3e0ec11777 Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Tue, 17 Feb 2026 15:21:35 +0200 Subject: [PATCH] sys: FspFastIoDeviceControl: fix exploit reported by Tay Kiat Loong The WinFsp "transact" protocol is used by user mode file systems to interface with the FSD. This protocol works via the DeviceIoControl API and uses the FSP_IOCTL_TRANSACT control code. The FSP_IOCTL_TRANSACT code is marked as METHOD_BUFFERED. When the DeviceIoControl call is forwarded as an IRP, the METHOD_BUFFERED flag instructs the kernel to copy user mode buffers to kernel mode buffers (and vice-versa). However when the DeviceIoControl call is forwarded via the FastIO mechanism the METHOD_BUFFERED flag is ignored. This means that when WinFsp added support for DeviceIoControl FastIO, the FSD started accessing user mode buffers directly. This means that a malicious file system could attempt exploits like changing or freeing a buffer while the FSD is reading it. Tay Kiat Loong developed a POC exploit which demonstrated this vulnerability. This commit fixes the problem by patching FspFastIoDeviceControl to add the missing METHOD_BUFFERED handling. --- src/sys/devctl.c | 43 +++++++++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/src/sys/devctl.c b/src/sys/devctl.c index a14036df..394a1550 100644 --- a/src/sys/devctl.c +++ b/src/sys/devctl.c @@ -72,15 +72,24 @@ BOOLEAN FspFastIoDeviceControl( if (!Result) FSP_RETURN(); -#if 0 - PDEVICE_OBJECT FsctlDeviceObject = DeviceObject; - if (!FspDeviceReference(FsctlDeviceObject)) + PVOID SystemBuffer = 0; + if (0 != InputBufferLength || 0 != OutputBufferLength) { - IoStatus->Status = STATUS_CANCELLED; - IoStatus->Information = 0; - FSP_RETURN(); + SystemBuffer = FspAllocNonPaged( + InputBufferLength > OutputBufferLength ? InputBufferLength : OutputBufferLength); + if (0 == SystemBuffer) + FSP_RETURN(Result = FALSE); + if (0 != InputBuffer) + try + { + RtlCopyMemory(SystemBuffer, InputBuffer, InputBufferLength); + } + except (EXCEPTION_EXECUTE_HANDLER) + { + FspFree(SystemBuffer); + FSP_RETURN(Result = FALSE); + } } -#endif ASSERT(0 == IoGetTopLevelIrp()); IoSetTopLevelIrp((PIRP)FSRTL_FAST_IO_TOP_LEVEL_IRP); @@ -88,18 +97,28 @@ BOOLEAN FspFastIoDeviceControl( IoStatus->Status = FspVolumeFastTransact( FileObject->FsContext2, IoControlCode, - InputBuffer, + SystemBuffer, InputBufferLength, - OutputBuffer, + 0 != OutputBufferLength ? SystemBuffer : 0, OutputBufferLength, IoStatus, (PIRP)FSRTL_FAST_IO_TOP_LEVEL_IRP); IoSetTopLevelIrp(0); -#if 0 - FspDeviceDereference(FsctlDeviceObject); -#endif + if (0 != SystemBuffer) + { + if (0 != OutputBuffer) + try + { + RtlCopyMemory(OutputBuffer, SystemBuffer, OutputBufferLength); + } + except (EXCEPTION_EXECUTE_HANDLER) + { + IoStatus->Status = GetExceptionCode(); + } + FspFree(SystemBuffer); + } FSP_LEAVE_BOOL( "%s, FileObject=%p",