From: Chip Davis Subject: [PATCH 2/4] ntoskrnl.exe: Always copy the buffer for non-METHOD_BUFFERED ioctls. Message-Id: <20191125015235.49004-2-cdavis@codeweavers.com> Date: Sun, 24 Nov 2019 19:52:33 -0600 In-Reply-To: <20191125015235.49004-1-cdavis@codeweavers.com> References: <20191125015235.49004-1-cdavis@codeweavers.com> In these cases, the driver expects to have direct access to some memory within the user-mode client, either via an MDL (as in the METHOD_IN_DIRECT/METHOD_OUT_DIRECT case) or via a raw address (as in the METHOD_NEITHER case). Because of this, we have to copy the buffer back, no matter what status is returned. Signed-off-by: Chip Davis --- dlls/ntoskrnl.exe/ntoskrnl.c | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/dlls/ntoskrnl.exe/ntoskrnl.c b/dlls/ntoskrnl.exe/ntoskrnl.c index 3a3c543be83..4c196bc1c98 100644 --- a/dlls/ntoskrnl.exe/ntoskrnl.c +++ b/dlls/ntoskrnl.exe/ntoskrnl.c @@ -424,16 +424,48 @@ static void WINAPI cancel_completed_irp( DEVICE_OBJECT *device, IRP *irp ) IoCompleteRequest(irp, IO_NO_INCREMENT); } +static ULONG get_irp_out_size( IRP *irp, BOOLEAN *need_copy ) +{ + IO_STACK_LOCATION *irpsp = IoGetNextIrpStackLocation(irp); + switch (irpsp->MajorFunction) + { + case IRP_MJ_FILE_SYSTEM_CONTROL: + case IRP_MJ_DEVICE_CONTROL: + case IRP_MJ_INTERNAL_DEVICE_CONTROL: + /* For an ioctl not using METHOD_BUFFERED, the driver is supposed to have + * direct access to userland's output buffer, either via an MDL (as in METHOD_OUT_DIRECT) + * or with the raw user VA (as in METHOD_NEITHER). In these cases, we need + * to copy the entire buffer back to the caller, whether or not Information + * is non-zero and whether or not the call succeeded. */ + switch (irpsp->Parameters.DeviceIoControl.IoControlCode & 3) + { + case METHOD_BUFFERED: + break; + default: + *need_copy = TRUE; + return irpsp->Parameters.DeviceIoControl.OutputBufferLength; + } + break; + default: + break; + } + return irp->IoStatus.Information; +} + /* transfer result of IRP back to wineserver */ static NTSTATUS WINAPI dispatch_irp_completion( DEVICE_OBJECT *device, IRP *irp, void *context ) { HANDLE irp_handle = context; void *out_buff = irp->UserBuffer; NTSTATUS status; + ULONG out_size; + BOOLEAN need_copy = FALSE; if (irp->Flags & IRP_WRITE_OPERATION) out_buff = NULL; /* do not transfer back input buffer */ + out_size = get_irp_out_size( irp, &need_copy ); + EnterCriticalSection( &irp_completion_cs ); SERVER_START_REQ( set_irp_result ) @@ -441,9 +473,9 @@ static NTSTATUS WINAPI dispatch_irp_completion( DEVICE_OBJECT *device, IRP *irp, req->handle = wine_server_obj_handle( irp_handle ); req->status = irp->IoStatus.u.Status; req->size = irp->IoStatus.Information; - if (!NT_ERROR(irp->IoStatus.u.Status)) + if (!NT_ERROR(irp->IoStatus.u.Status) || need_copy) { - if (out_buff) wine_server_add_data( req, out_buff, irp->IoStatus.Information ); + if (out_buff) wine_server_add_data( req, out_buff, out_size ); } status = wine_server_call( req ); } -- 2.21.0