1
0
mirror of https://github.com/winfsp/winfsp.git synced 2026-03-06 23:59:26 -06:00

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.
This commit is contained in:
Bill Zissimopoulos
2026-02-17 15:21:35 +02:00
parent aed8bb745f
commit ae40f0edb1

View File

@@ -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",