From 3d09e621ab0ed20a4e7fd6f120cd2251d15b5e43 Mon Sep 17 00:00:00 2001
From: Chip Davis <cdavis@codeweavers.com>
Date: Wed, 10 Jul 2019 19:12:06 -0500
Subject: [PATCH 2/4] ntoskrnl.exe: Always copy the buffer for
 non-METHOD_BUFFERED ioctls.

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 <cdavis@codeweavers.com>
---
 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 9b1efed6de..368bfcfd20 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.17.1

