Chip Davis : ntoskrnl: Always copy the output buffer for non-buffered ioctls.

Alexandre Julliard julliard at winehq.org
Tue Sep 14 16:00:13 CDT 2021


Module: wine
Branch: master
Commit: 7c0f6420059974ab127ae944b55d11dd71df3b85
URL:    https://source.winehq.org/git/wine.git/?a=commit;h=7c0f6420059974ab127ae944b55d11dd71df3b85

Author: Chip Davis <cdavis at codeweavers.com>
Date:   Tue Sep 14 01:02:45 2021 -0500

ntoskrnl: Always copy the output buffer for non-buffered ioctls.

Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=30155
Signed-off-by: Zebediah Figura <zfigura at codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard at winehq.org>

---

 dlls/ntoskrnl.exe/ntoskrnl.c       | 51 +++++++++++++++++++++++++++-----------
 dlls/ntoskrnl.exe/tests/ntoskrnl.c | 21 ++++++----------
 2 files changed, 44 insertions(+), 28 deletions(-)

diff --git a/dlls/ntoskrnl.exe/ntoskrnl.c b/dlls/ntoskrnl.exe/ntoskrnl.c
index 1bd5d6fb351..f2fb0a6d66e 100644
--- a/dlls/ntoskrnl.exe/ntoskrnl.c
+++ b/dlls/ntoskrnl.exe/ntoskrnl.c
@@ -440,15 +440,43 @@ static void free_dispatch_irp( struct irp_data *irp_data )
     free( irp_data );
 }
 
+static ULONG get_irp_output_size( IRP *irp )
+{
+    IO_STACK_LOCATION *stack = IoGetNextIrpStackLocation( irp );
+
+    if (!irp->UserBuffer || (irp->Flags & IRP_WRITE_OPERATION))
+        return 0;
+
+    /* For IRPs not using buffered I/O, the driver is supposed to have direct
+     * access to the user's output buffer, either via an MDL (direct I/O) or
+     * with the raw user VA (neither). We can't fully support this, but we
+     * should at least copy the entire buffer back to the caller. */
+    switch (stack->MajorFunction)
+    {
+        case IRP_MJ_FILE_SYSTEM_CONTROL:
+        case IRP_MJ_DEVICE_CONTROL:
+        case IRP_MJ_INTERNAL_DEVICE_CONTROL:
+            if ((stack->Parameters.DeviceIoControl.IoControlCode & 3) != METHOD_BUFFERED)
+                return stack->Parameters.DeviceIoControl.OutputBufferLength;
+            break;
+
+        case IRP_MJ_READ:
+            /* FIXME: Handle non-buffered reads. */
+        default:
+            break;
+    }
+
+    if (NT_ERROR(irp->IoStatus.u.Status))
+        return 0;
+    return irp->IoStatus.Information;
+}
+
 /* transfer result of IRP back to wineserver */
 static NTSTATUS WINAPI dispatch_irp_completion( DEVICE_OBJECT *device, IRP *irp, void *context )
 {
     struct irp_data *irp_data = context;
-    void *out_buff = irp->UserBuffer;
     NTSTATUS status;
-
-    if (irp->Flags & IRP_WRITE_OPERATION)
-        out_buff = NULL;  /* do not transfer back input buffer */
+    ULONG out_size;
 
     EnterCriticalSection( &irp_completion_cs );
 
@@ -460,15 +488,14 @@ static NTSTATUS WINAPI dispatch_irp_completion( DEVICE_OBJECT *device, IRP *irp,
         return STATUS_MORE_PROCESSING_REQUIRED;
     }
 
+    out_size = get_irp_output_size( irp );
+
     SERVER_START_REQ( set_irp_result )
     {
         req->handle   = wine_server_obj_handle( irp_data->handle );
         req->status   = irp->IoStatus.u.Status;
         req->size     = irp->IoStatus.Information;
-        if (!NT_ERROR(irp->IoStatus.u.Status))
-        {
-            if (out_buff) wine_server_add_data( req, out_buff, irp->IoStatus.Information );
-        }
+        if (out_size) wine_server_add_data( req, irp->UserBuffer, out_size );
         status = wine_server_call( req );
     }
     SERVER_END_REQ;
@@ -943,17 +970,13 @@ NTSTATUS CDECL wine_ntoskrnl_main_loop( HANDLE stop_event )
                 if (context.irp_data->complete)
                 {
                     /* IRP completed even before we got here; we can report completion now */
-                    void *out_buff = irp->UserBuffer;
-
-                    if (irp->Flags & IRP_WRITE_OPERATION)
-                        out_buff = NULL;  /* do not transfer back input buffer */
+                    unsigned int out_size = get_irp_output_size( irp );
 
                     req->prev        = wine_server_obj_handle( context.irp_data->handle );
                     req->pending     = irp->PendingReturned;
                     req->iosb_status = irp->IoStatus.u.Status;
                     req->result      = irp->IoStatus.Information;
-                    if (!NT_ERROR(irp->IoStatus.u.Status) && out_buff)
-                        wine_server_add_data( req, out_buff, irp->IoStatus.Information );
+                    if (out_size) wine_server_add_data( req, irp->UserBuffer, out_size );
                 }
                 else
                 {
diff --git a/dlls/ntoskrnl.exe/tests/ntoskrnl.c b/dlls/ntoskrnl.exe/tests/ntoskrnl.c
index edc4a607225..f9fd9aa23e8 100644
--- a/dlls/ntoskrnl.exe/tests/ntoskrnl.c
+++ b/dlls/ntoskrnl.exe/tests/ntoskrnl.c
@@ -679,8 +679,7 @@ static void do_return_status(ULONG ioctl, struct return_status_params *params)
     else if (!NT_ERROR(params->iosb_status))
         todo_wine_if (params->iosb_status == STATUS_PENDING) ok(size == 3, "got size %u\n", size);
     /* size is garbage if !NT_ERROR(expect_status) && NT_ERROR(iosb_status) */
-    todo_wine_if (ioctl != IOCTL_WINETEST_RETURN_STATUS_BUFFERED)
-        ok(!strcmp(buffer, expect_buffer), "got buffer %s\n", buffer);
+    ok(!strcmp(buffer, expect_buffer), "got buffer %s\n", buffer);
 
     strcpy(buffer, "abcdef");
     io.Status = 0xdeadf00d;
@@ -701,8 +700,7 @@ static void do_return_status(ULONG ioctl, struct return_status_params *params)
             ok(io.Information == 3, "got size %Iu\n", io.Information);
         }
     }
-    todo_wine_if (ioctl != IOCTL_WINETEST_RETURN_STATUS_BUFFERED)
-        ok(!strcmp(buffer, expect_buffer), "got buffer %s\n", buffer);
+    ok(!strcmp(buffer, expect_buffer), "got buffer %s\n", buffer);
 
     /* Test the overlapped case. */
 
@@ -741,8 +739,7 @@ static void do_return_status(ULONG ioctl, struct return_status_params *params)
         ret = WaitForSingleObject(event, 0);
         ok(!ret, "got %d\n", ret);
     }
-    todo_wine_if (ioctl != IOCTL_WINETEST_RETURN_STATUS_BUFFERED)
-        ok(!strcmp(buffer, expect_buffer), "got buffer %s\n", buffer);
+    ok(!strcmp(buffer, expect_buffer), "got buffer %s\n", buffer);
 
     ret = WaitForSingleObject(file, 0);
     ok(ret == WAIT_TIMEOUT, "got %d\n", ret);
@@ -793,8 +790,7 @@ static void do_return_status(ULONG ioctl, struct return_status_params *params)
         ret = WaitForSingleObject(event, 0);
         ok(!ret, "got %d\n", ret);
     }
-    todo_wine_if (ioctl != IOCTL_WINETEST_RETURN_STATUS_BUFFERED)
-        ok(!strcmp(buffer, expect_buffer), "got buffer %s\n", buffer);
+    ok(!strcmp(buffer, expect_buffer), "got buffer %s\n", buffer);
 
     /* As above, but use the file handle instead of an event. */
     ret = WaitForSingleObject(file, 0);
@@ -825,8 +821,7 @@ static void do_return_status(ULONG ioctl, struct return_status_params *params)
         ret = WaitForSingleObject(file, 0);
         ok(!ret, "got %d\n", ret);
     }
-    todo_wine_if (ioctl != IOCTL_WINETEST_RETURN_STATUS_BUFFERED)
-        ok(!strcmp(buffer, expect_buffer), "got buffer %s\n", buffer);
+    ok(!strcmp(buffer, expect_buffer), "got buffer %s\n", buffer);
 
     /* Test FILE_SKIP_COMPLETION_PORT_ON_SUCCESS. */
 
@@ -861,8 +856,7 @@ static void do_return_status(ULONG ioctl, struct return_status_params *params)
             ret = WaitForSingleObject(event, 0);
             ok(!ret, "got %d\n", ret);
         }
-        todo_wine_if (ioctl != IOCTL_WINETEST_RETURN_STATUS_BUFFERED)
-            ok(!strcmp(buffer, expect_buffer), "got buffer %s\n", buffer);
+        ok(!strcmp(buffer, expect_buffer), "got buffer %s\n", buffer);
 
         key = 0xdeadf00d;
         value = 0xdeadf00d;
@@ -926,8 +920,7 @@ static void do_return_status(ULONG ioctl, struct return_status_params *params)
             ok(io.Information == 3, "got size %Iu\n", io.Information);
         }
     }
-    todo_wine_if (ioctl != IOCTL_WINETEST_RETURN_STATUS_BUFFERED)
-        ok(!strcmp(buffer, expect_buffer), "got buffer %s\n", buffer);
+    ok(!strcmp(buffer, expect_buffer), "got buffer %s\n", buffer);
 
     ret = SleepEx(0, TRUE);
     if (!params->pending && NT_ERROR(params->iosb_status))




More information about the wine-cvs mailing list