From 73caf7ace23578a2bfce5f49dcb72c2a55309a3d Mon Sep 17 00:00:00 2001 From: Alistair Leslie-Hughes Date: Tue, 17 Dec 2019 08:19:06 +1100 Subject: [PATCH] Added ntoskrnl-safedisc-2 patchset --- ...urn-driver-dispatch-result-to-caller.patch | 119 +++++++++++++++ ...ays-copy-the-buffer-for-non-METHOD_B.patch | 85 +++++++++++ ...r-Delay-completing-a-synchronous-IRP.patch | 139 ++++++++++++++++++ ...-the-driver-s-Information-from-ioctl.patch | 56 +++++++ patches/ntoskrnl-safedisc-2/definition | 1 + patches/patchinstall.sh | 28 +++- 6 files changed, 427 insertions(+), 1 deletion(-) create mode 100644 patches/ntoskrnl-safedisc-2/0001-ntoskrnl.exe-Return-driver-dispatch-result-to-caller.patch create mode 100644 patches/ntoskrnl-safedisc-2/0002-ntoskrnl.exe-Always-copy-the-buffer-for-non-METHOD_B.patch create mode 100644 patches/ntoskrnl-safedisc-2/0003-server-Delay-completing-a-synchronous-IRP.patch create mode 100644 patches/ntoskrnl-safedisc-2/0004-server-Return-the-driver-s-Information-from-ioctl.patch create mode 100644 patches/ntoskrnl-safedisc-2/definition diff --git a/patches/ntoskrnl-safedisc-2/0001-ntoskrnl.exe-Return-driver-dispatch-result-to-caller.patch b/patches/ntoskrnl-safedisc-2/0001-ntoskrnl.exe-Return-driver-dispatch-result-to-caller.patch new file mode 100644 index 00000000..9edd5f08 --- /dev/null +++ b/patches/ntoskrnl-safedisc-2/0001-ntoskrnl.exe-Return-driver-dispatch-result-to-caller.patch @@ -0,0 +1,119 @@ +From b2b7bc84befea8100d1996262ef1b36091df7ebc Mon Sep 17 00:00:00 2001 +From: Chip Davis +Date: Tue, 18 Jun 2019 00:54:35 -0500 +Subject: [PATCH 1/4] ntoskrnl.exe: Return driver dispatch result to caller. + +As native does. + +Signed-off-by: Chip Davis +--- + dlls/ntoskrnl.exe/ntoskrnl.c | 31 ++++++++++++------------------- + 1 file changed, 12 insertions(+), 19 deletions(-) + +diff --git a/dlls/ntoskrnl.exe/ntoskrnl.c b/dlls/ntoskrnl.exe/ntoskrnl.c +index 93c9720e96..9b1efed6de 100644 +--- a/dlls/ntoskrnl.exe/ntoskrnl.c ++++ b/dlls/ntoskrnl.exe/ntoskrnl.c +@@ -479,9 +479,10 @@ struct dispatch_context + void *in_buff; + }; + +-static void dispatch_irp( DEVICE_OBJECT *device, IRP *irp, struct dispatch_context *context ) ++static NTSTATUS dispatch_irp( DEVICE_OBJECT *device, IRP *irp, struct dispatch_context *context ) + { + LARGE_INTEGER count; ++ NTSTATUS status; + + IoSetCompletionRoutine( irp, dispatch_irp_completion, context->handle, TRUE, TRUE, TRUE ); + context->handle = 0; +@@ -491,9 +492,10 @@ static void dispatch_irp( DEVICE_OBJECT *device, IRP *irp, struct dispatch_conte + context->irp = irp; + device->CurrentIrp = irp; + KeEnterCriticalRegion(); +- IoCallDriver( device, irp ); ++ status = IoCallDriver( device, irp ); + KeLeaveCriticalRegion(); + device->CurrentIrp = NULL; ++ return status; + } + + /* process a create request for a given file */ +@@ -536,9 +538,7 @@ static NTSTATUS dispatch_create( struct dispatch_context *context ) + irp->UserEvent = NULL; + + irp->Flags |= IRP_CREATE_OPERATION; +- dispatch_irp( device, irp, context ); +- +- return STATUS_SUCCESS; ++ return dispatch_irp( device, irp, context ); + } + + /* process a close request for a given file */ +@@ -574,9 +574,7 @@ static NTSTATUS dispatch_close( struct dispatch_context *context ) + irp->UserEvent = NULL; + + irp->Flags |= IRP_CLOSE_OPERATION; +- dispatch_irp( device, irp, context ); +- +- return STATUS_SUCCESS; ++ return dispatch_irp( device, irp, context ); + } + + /* process a read request for a given device */ +@@ -616,9 +614,7 @@ static NTSTATUS dispatch_read( struct dispatch_context *context ) + + irp->Flags |= IRP_READ_OPERATION; + irp->Flags |= IRP_DEALLOCATE_BUFFER; /* deallocate out_buff */ +- dispatch_irp( device, irp, context ); +- +- return STATUS_SUCCESS; ++ return dispatch_irp( device, irp, context ); + } + + /* process a write request for a given device */ +@@ -652,9 +648,7 @@ static NTSTATUS dispatch_write( struct dispatch_context *context ) + + irp->Flags |= IRP_WRITE_OPERATION; + irp->Flags |= IRP_DEALLOCATE_BUFFER; /* deallocate in_buff */ +- dispatch_irp( device, irp, context ); +- +- return STATUS_SUCCESS; ++ return dispatch_irp( device, irp, context ); + } + + /* process a flush request for a given device */ +@@ -681,9 +675,7 @@ static NTSTATUS dispatch_flush( struct dispatch_context *context ) + irpsp = IoGetNextIrpStackLocation( irp ); + irpsp->FileObject = file; + +- dispatch_irp( device, irp, context ); +- +- return STATUS_SUCCESS; ++ return dispatch_irp( device, irp, context ); + } + + /* process an ioctl request for a given device */ +@@ -696,6 +688,7 @@ static NTSTATUS dispatch_ioctl( struct dispatch_context *context ) + DEVICE_OBJECT *device; + FILE_OBJECT *file = wine_server_get_ptr( context->params.ioctl.file ); + ULONG out_size = context->params.ioctl.out_size; ++ NTSTATUS status; + + if (!file) return STATUS_INVALID_HANDLE; + +@@ -744,10 +737,10 @@ static NTSTATUS dispatch_ioctl( struct dispatch_context *context ) + context->in_buff = NULL; + + irp->Flags |= IRP_DEALLOCATE_BUFFER; /* deallocate in_buff */ +- dispatch_irp( device, irp, context ); ++ status = dispatch_irp( device, irp, context ); + + HeapFree( GetProcessHeap(), 0, to_free ); +- return STATUS_SUCCESS; ++ return status; + } + + static NTSTATUS dispatch_free( struct dispatch_context *context ) +-- +2.17.1 + diff --git a/patches/ntoskrnl-safedisc-2/0002-ntoskrnl.exe-Always-copy-the-buffer-for-non-METHOD_B.patch b/patches/ntoskrnl-safedisc-2/0002-ntoskrnl.exe-Always-copy-the-buffer-for-non-METHOD_B.patch new file mode 100644 index 00000000..4f340959 --- /dev/null +++ b/patches/ntoskrnl-safedisc-2/0002-ntoskrnl.exe-Always-copy-the-buffer-for-non-METHOD_B.patch @@ -0,0 +1,85 @@ +From 3d09e621ab0ed20a4e7fd6f120cd2251d15b5e43 Mon Sep 17 00:00:00 2001 +From: Chip Davis +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 +--- + 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 + diff --git a/patches/ntoskrnl-safedisc-2/0003-server-Delay-completing-a-synchronous-IRP.patch b/patches/ntoskrnl-safedisc-2/0003-server-Delay-completing-a-synchronous-IRP.patch new file mode 100644 index 00000000..239d163d --- /dev/null +++ b/patches/ntoskrnl-safedisc-2/0003-server-Delay-completing-a-synchronous-IRP.patch @@ -0,0 +1,139 @@ +From 3ad0085d6003a4521dfd7089f34c431f105835df Mon Sep 17 00:00:00 2001 +From: Chip Davis +Date: Wed, 10 Jul 2019 19:14:45 -0500 +Subject: [PATCH 3/4] server: Delay completing a synchronous IRP. + +Wait until the client fetches the result. Save any buffer that the +driver provided so it can be copied back to the client. + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=30155 +Signed-off-by: Chip Davis +--- + dlls/ntoskrnl.exe/tests/ntoskrnl.c | 4 +-- + server/device.c | 50 ++++++++++++++++++++---------- + 2 files changed, 36 insertions(+), 18 deletions(-) + +diff --git a/dlls/ntoskrnl.exe/tests/ntoskrnl.c b/dlls/ntoskrnl.exe/tests/ntoskrnl.c +index 2535ed903e..1765ae7ddf 100644 +--- a/dlls/ntoskrnl.exe/tests/ntoskrnl.c ++++ b/dlls/ntoskrnl.exe/tests/ntoskrnl.c +@@ -216,8 +216,8 @@ static void test_mismatched_status_ioctl(void) + + res = DeviceIoControl(device, IOCTL_WINETEST_MISMATCHED_STATUS, NULL, 0, buf, + sizeof(buf), &written, NULL); +- todo_wine ok(res, "DeviceIoControl failed: %u\n", GetLastError()); +- todo_wine ok(!strcmp(buf, teststr), "got '%s'\n", buf); ++ ok(res, "DeviceIoControl failed: %u\n", GetLastError()); ++ ok(!strcmp(buf, teststr), "got '%s'\n", buf); + } + + static void test_overlapped(void) +diff --git a/server/device.c b/server/device.c +index d3e2a84c1e..99492f8202 100644 +--- a/server/device.c ++++ b/server/device.c +@@ -52,6 +52,7 @@ struct irp_call + struct async *async; /* pending async op */ + irp_params_t params; /* irp parameters */ + struct iosb *iosb; /* I/O status block */ ++ int dispatched; /* the call's dispatch returned */ + int canceled; /* the call was canceled */ + client_ptr_t user_ptr; /* client side pointer */ + }; +@@ -349,13 +350,14 @@ static struct irp_call *create_irp( struct device_file *file, const irp_params_t + + if ((irp = alloc_object( &irp_call_ops ))) + { +- irp->file = file ? (struct device_file *)grab_object( file ) : NULL; +- irp->thread = NULL; +- irp->async = NULL; +- irp->params = *params; +- irp->iosb = NULL; +- irp->canceled = 0; +- irp->user_ptr = 0; ++ irp->file = file ? (struct device_file *)grab_object( file ) : NULL; ++ irp->thread = NULL; ++ irp->async = NULL; ++ irp->params = *params; ++ irp->iosb = NULL; ++ irp->dispatched = 0; ++ irp->canceled = 0; ++ irp->user_ptr = 0; + + if (async) irp->iosb = async_get_iosb( async ); + if (!irp->iosb && !(irp->iosb = create_iosb( NULL, 0, 0 ))) +@@ -367,6 +369,19 @@ static struct irp_call *create_irp( struct device_file *file, const irp_params_t + return irp; + } + ++static void set_irp_result_buffer( struct irp_call *irp, const void *out_data, ++ data_size_t out_size, data_size_t result ) ++{ ++ struct iosb *iosb = irp->iosb; ++ ++ if (!irp->file) return; /* already finished */ ++ ++ iosb->result = result; ++ iosb->out_size = min( iosb->out_size, out_size ); ++ if (iosb->out_size && !(iosb->out_data = memdup( out_data, iosb->out_size ))) ++ iosb->out_size = 0; ++} ++ + static void set_irp_result( struct irp_call *irp, unsigned int status, + const void *out_data, data_size_t out_size, data_size_t result ) + { +@@ -377,17 +392,15 @@ static void set_irp_result( struct irp_call *irp, unsigned int status, + + /* FIXME: handle the STATUS_PENDING case */ + iosb->status = status; +- iosb->result = result; +- iosb->out_size = min( iosb->out_size, out_size ); +- if (iosb->out_size && !(iosb->out_data = memdup( out_data, iosb->out_size ))) +- iosb->out_size = 0; ++ if (!iosb->out_data || irp->dispatched) ++ set_irp_result_buffer( irp, out_data, out_size, result ); + + /* remove it from the device queue */ + list_remove( &irp->dev_entry ); + irp->file = NULL; + if (irp->async) + { +- if (result) status = STATUS_ALERTED; ++ if (iosb->out_size || result) status = STATUS_ALERTED; + async_terminate( irp->async, status ); + release_object( irp->async ); + irp->async = NULL; +@@ -933,13 +946,15 @@ DECL_HANDLER(get_next_device_request) + irp = manager->current_call; + irp->user_ptr = req->user_ptr; + +- if (req->status) +- set_irp_result( irp, req->status, NULL, 0, 0 ); ++ if (req->status != STATUS_PENDING) ++ set_irp_result( irp, req->status, NULL, 0, irp->iosb->result ); ++ else ++ irp->dispatched = 1; + if (irp->canceled) + /* if it was canceled during dispatch, we couldn't queue cancel call without client pointer, + * so we need to do it now */ + cancel_irp_call( irp ); +- else if (irp->async) ++ else if (irp->async && req->status == STATUS_PENDING) + set_async_pending( irp->async, irp->file && is_fd_overlapped( irp->file->fd ) ); + + free_irp_params( irp ); +@@ -991,7 +1006,10 @@ DECL_HANDLER(set_irp_result) + + if ((irp = (struct irp_call *)get_handle_obj( current->process, req->handle, 0, &irp_call_ops ))) + { +- if (!irp->canceled) ++ if (!irp->canceled && !irp->dispatched && irp->file && !is_fd_overlapped( irp->file->fd )) ++ /* Don't complete the IRP right away, but save the buffer. */ ++ set_irp_result_buffer( irp, get_req_data(), get_req_data_size(), req->size ); ++ else if (!irp->canceled) + set_irp_result( irp, req->status, get_req_data(), get_req_data_size(), req->size ); + else if(irp->user_ptr) /* cancel already queued */ + set_error( STATUS_MORE_PROCESSING_REQUIRED ); +-- +2.17.1 + diff --git a/patches/ntoskrnl-safedisc-2/0004-server-Return-the-driver-s-Information-from-ioctl.patch b/patches/ntoskrnl-safedisc-2/0004-server-Return-the-driver-s-Information-from-ioctl.patch new file mode 100644 index 00000000..62e9bcc6 --- /dev/null +++ b/patches/ntoskrnl-safedisc-2/0004-server-Return-the-driver-s-Information-from-ioctl.patch @@ -0,0 +1,56 @@ +From d60f2de8b419618674457e5539b081421c110f1e Mon Sep 17 00:00:00 2001 +From: Chip Davis +Date: Sat, 14 Dec 2019 01:58:26 -0600 +Subject: [PATCH 4/4] server: Return the driver's 'Information' from ioctl. + +This actually shouldn't matter for driver modules, since those always +call out to another process, but it seems more correct. + +Signed-off-by: Chip Davis +--- + dlls/ntdll/file.c | 2 +- + server/fd.c | 2 +- + server/protocol.def | 1 + + 3 files changed, 3 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/file.c b/dlls/ntdll/file.c +index 26acc066cd..087a92cda2 100644 +--- a/dlls/ntdll/file.c ++++ b/dlls/ntdll/file.c +@@ -1536,7 +1536,7 @@ static NTSTATUS server_ioctl_file( HANDLE handle, HANDLE event, + if (wait_handle && status != STATUS_PENDING) + { + io->u.Status = status; +- io->Information = wine_server_reply_size( reply ); ++ io->Information = reply->size; + } + } + SERVER_END_REQ; +diff --git a/server/fd.c b/server/fd.c +index 5d80e218b9..dbfb65abd0 100644 +--- a/server/fd.c ++++ b/server/fd.c +@@ -2579,7 +2579,7 @@ DECL_HANDLER(ioctl) + + if ((async = create_request_async( fd, fd->comp_flags, &req->async ))) + { +- reply->wait = async_handoff( async, fd->fd_ops->ioctl( fd, req->code, async ), NULL, 0 ); ++ reply->wait = async_handoff( async, fd->fd_ops->ioctl( fd, req->code, async ), &reply->size, 0 ); + reply->options = fd->options; + release_object( async ); + } +diff --git a/server/protocol.def b/server/protocol.def +index 6c44b2b43f..f1f694bf96 100644 +--- a/server/protocol.def ++++ b/server/protocol.def +@@ -2484,6 +2484,7 @@ enum message_type + @REPLY + obj_handle_t wait; /* handle to wait on for blocking ioctl */ + unsigned int options; /* device open options */ ++ data_size_t size; /* result size */ + VARARG(out_data,bytes); /* ioctl output data */ + @END + +-- +2.17.1 + diff --git a/patches/ntoskrnl-safedisc-2/definition b/patches/ntoskrnl-safedisc-2/definition new file mode 100644 index 00000000..23a28ec1 --- /dev/null +++ b/patches/ntoskrnl-safedisc-2/definition @@ -0,0 +1 @@ +Fixes: [30155] Improve support for SafeDisc v2.05.030 diff --git a/patches/patchinstall.sh b/patches/patchinstall.sh index dc3ccf1f..65f13e12 100755 --- a/patches/patchinstall.sh +++ b/patches/patchinstall.sh @@ -52,7 +52,7 @@ usage() # Get the upstream commit sha upstream_commit() { - echo "ce7e10868a1279573acc5be5a9659d254e936b27" + echo "6e986bbd810890339569f82aca39273e41427f24" } # Show version information @@ -231,6 +231,7 @@ patch_enable_all () enable_ntdll_set_full_cpu_context="$1" enable_ntdll_x86_64_SegDs="$1" enable_ntoskrnl_Stubs="$1" + enable_ntoskrnl_safedisc_2="$1" enable_nvapi_Stub_DLL="$1" enable_nvcuda_CUDA_Support="$1" enable_nvcuvid_CUDA_Video_Support="$1" @@ -817,6 +818,9 @@ patch_enable () ntoskrnl-Stubs) enable_ntoskrnl_Stubs="$2" ;; + ntoskrnl-safedisc-2) + enable_ntoskrnl_safedisc_2="$2" + ;; nvapi-Stub_DLL) enable_nvapi_Stub_DLL="$2" ;; @@ -5231,6 +5235,28 @@ if test "$enable_ntoskrnl_Stubs" -eq 1; then ) >> "$patchlist" fi +# Patchset ntoskrnl-safedisc-2 +# | +# | This patchset fixes the following Wine bugs: +# | * [#30155] Improve support for SafeDisc v2.05.030 +# | +# | Modified files: +# | * dlls/ntdll/file.c, dlls/ntoskrnl.exe/ntoskrnl.c, dlls/ntoskrnl.exe/tests/ntoskrnl.c, server/device.c, server/fd.c, +# | server/protocol.def +# | +if test "$enable_ntoskrnl_safedisc_2" -eq 1; then + patch_apply ntoskrnl-safedisc-2/0001-ntoskrnl.exe-Return-driver-dispatch-result-to-caller.patch + patch_apply ntoskrnl-safedisc-2/0002-ntoskrnl.exe-Always-copy-the-buffer-for-non-METHOD_B.patch + patch_apply ntoskrnl-safedisc-2/0003-server-Delay-completing-a-synchronous-IRP.patch + patch_apply ntoskrnl-safedisc-2/0004-server-Return-the-driver-s-Information-from-ioctl.patch + ( + printf '%s\n' '+ { "Chip Davis", "ntoskrnl.exe: Return driver dispatch result to caller.", 1 },'; + printf '%s\n' '+ { "Chip Davis", "ntoskrnl.exe: Always copy the buffer for non-METHOD_BUFFERED ioctls.", 1 },'; + printf '%s\n' '+ { "Chip Davis", "server: Delay completing a synchronous IRP.", 1 },'; + printf '%s\n' '+ { "Chip Davis", "server: Return the driver'\''s '\''Information'\'' from ioctl.", 1 },'; + ) >> "$patchlist" +fi + # Patchset nvcuvid-CUDA_Video_Support # | # | This patchset has the following (direct or indirect) dependencies: