Signed-off-by: Chip Davis cdavis@codeweavers.com --- 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 a810dee573f..3a3c543be83 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 )
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 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 ); }
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 cdavis@codeweavers.com --- This patch is an absolute kluge. I hate it. ---
server/device.c | 54 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 39 insertions(+), 15 deletions(-)
diff --git a/server/device.c b/server/device.c index 117dd99261a..c1464a66a5e 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,27 +369,44 @@ 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 (debug_level) fprintf( stderr, "%04x: set_irp_result_buffer(%p, %p, %u, %u)\n", current->id, irp, out_data, out_size, result ); + + 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 ) { struct device_file *file = irp->file; struct iosb *iosb = irp->iosb;
+ if (debug_level) fprintf( stderr, "%04x: set_irp_result(%p, %08x, %p, %u, %u)\n", current->id, irp, status, out_data, out_size, result ); + if (!file) return; /* already finished */
+ if (debug_level) fprintf( stderr, "%04x: completing IRP\n", current->id ); + /* 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 (status != STATUS_SUCCESS || !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) status = STATUS_ALERTED; async_terminate( irp->async, status ); release_object( irp->async ); irp->async = NULL; @@ -933,13 +952,15 @@ DECL_HANDLER(get_next_device_request) irp = manager->current_call; irp->user_ptr = req->user_ptr;
- if (req->status) + if (req->status != STATUS_PENDING) set_irp_result( irp, req->status, NULL, 0, 0 ); + 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 +1012,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 );
Signed-off-by: Chip Davis cdavis@codeweavers.com --- dlls/ntoskrnl.exe/tests/driver.c | 30 ++++++++++++++++++++++++++++++ dlls/ntoskrnl.exe/tests/driver.h | 1 + dlls/ntoskrnl.exe/tests/ntoskrnl.c | 14 ++++++++++++++ 3 files changed, 45 insertions(+)
diff --git a/dlls/ntoskrnl.exe/tests/driver.c b/dlls/ntoskrnl.exe/tests/driver.c index a0f63ade791..6827b74a3c9 100644 --- a/dlls/ntoskrnl.exe/tests/driver.c +++ b/dlls/ntoskrnl.exe/tests/driver.c @@ -1855,6 +1855,34 @@ static NTSTATUS test_load_driver_ioctl(IRP *irp, IO_STACK_LOCATION *stack, ULONG return ZwUnloadDriver(&name); }
+static NTSTATUS test_mismatched_status_ioctl(IRP *irp, IO_STACK_LOCATION *stack, ULONG_PTR *info) +{ + ULONG length = stack->Parameters.DeviceIoControl.OutputBufferLength; + char *buffer = irp->UserBuffer; + + if (!buffer) + { + irp->IoStatus.Status = STATUS_ACCESS_VIOLATION; + IoCompleteRequest(irp, IO_NO_INCREMENT); + return STATUS_ACCESS_VIOLATION; + } + + if (length < sizeof(teststr)) + { + irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL; + IoCompleteRequest(irp, IO_NO_INCREMENT); + return STATUS_BUFFER_TOO_SMALL; + } + + memcpy(buffer, teststr, sizeof(teststr)); + + /* This is deliberate; some broken drivers do this */ + *info = 0; + irp->IoStatus.Status = STATUS_UNSUCCESSFUL; + IoCompleteRequest(irp, IO_NO_INCREMENT); + return STATUS_SUCCESS; +} + static NTSTATUS WINAPI driver_Create(DEVICE_OBJECT *device, IRP *irp) { IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp ); @@ -1916,6 +1944,8 @@ static NTSTATUS WINAPI driver_IoControl(DEVICE_OBJECT *device, IRP *irp) IoDetachDevice(lower_device); status = STATUS_SUCCESS; break; + case IOCTL_WINETEST_MISMATCHED_STATUS: + return test_mismatched_status_ioctl(irp, stack, &irp->IoStatus.Information); default: break; } diff --git a/dlls/ntoskrnl.exe/tests/driver.h b/dlls/ntoskrnl.exe/tests/driver.h index 313d554a37b..aa9de674e2f 100644 --- a/dlls/ntoskrnl.exe/tests/driver.h +++ b/dlls/ntoskrnl.exe/tests/driver.h @@ -33,6 +33,7 @@ #define IOCTL_WINETEST_GET_CLOSE_COUNT CTL_CODE(FILE_DEVICE_UNKNOWN, 0x808, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IOCTL_WINETEST_GET_FSCONTEXT CTL_CODE(FILE_DEVICE_UNKNOWN, 0x809, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IOCTL_WINETEST_RETURN_STATUS CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80a, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_WINETEST_MISMATCHED_STATUS CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80b, METHOD_NEITHER, FILE_ANY_ACCESS)
static const char teststr[] = "Wine is not an emulator";
diff --git a/dlls/ntoskrnl.exe/tests/ntoskrnl.c b/dlls/ntoskrnl.exe/tests/ntoskrnl.c index 76bcf0d0ce6..0667458db8b 100644 --- a/dlls/ntoskrnl.exe/tests/ntoskrnl.c +++ b/dlls/ntoskrnl.exe/tests/ntoskrnl.c @@ -208,6 +208,19 @@ static void test_basic_ioctl(void) ok(!strcmp(buf, "Wine is no"), "got '%s'\n", buf); }
+static void test_mismatched_status_ioctl(void) +{ + DWORD written; + char buf[32]; + BOOL res; + + res = DeviceIoControl(device, IOCTL_WINETEST_MISMATCHED_STATUS, NULL, 0, buf, + sizeof(buf), &written, NULL); + ok(res, "DeviceIoControl failed: %u\n", GetLastError()); + ok(written == 0, "got size %d\n", written); + ok(!strcmp(buf, teststr), "got '%s'\n", buf); +} + static void test_overlapped(void) { OVERLAPPED overlapped, overlapped2, *o; @@ -514,6 +527,7 @@ START_TEST(ntoskrnl) ok(device != INVALID_HANDLE_VALUE, "failed to open device: %u\n", GetLastError());
test_basic_ioctl(); + test_mismatched_status_ioctl(); main_test(); test_overlapped(); test_load_driver(service2);
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=60780
Your paranoid android.
=== wxppro (32 bit report) ===
ntoskrnl.exe: ntoskrnl.c:220: Test failed: got size 2291032
=== w2003std (32 bit report) ===
ntoskrnl.exe: ntoskrnl.c:220: Test failed: got size 4227914
=== w7u (32 bit report) ===
ntoskrnl.exe: ntoskrnl.c:220: Test failed: got size 2292123
=== w8 (32 bit report) ===
ntoskrnl.exe: ntoskrnl.c:220: Test failed: got size 2357659
=== debian10 (32 bit report) ===
ntoskrnl.exe: ntoskrnl.c:437: Test failed: got buffer abcdef ntoskrnl.c:438: Test failed: got size 0 ntoskrnl.c:447: Test failed: got buffer abcdef ntoskrnl.c:448: Test failed: got size 0 ntoskrnl.c:457: Test failed: got buffer abcdef ntoskrnl.c:458: Test failed: got size 0 ntoskrnl.c:467: Test failed: got buffer abcdef ntoskrnl.c:468: Test failed: got size 0 ntoskrnl.c:478: Test failed: got size 0
=== debian10 (32 bit French report) ===
ntoskrnl.exe: ntoskrnl.c:437: Test failed: got buffer abcdef ntoskrnl.c:438: Test failed: got size 0 ntoskrnl.c:447: Test failed: got buffer abcdef ntoskrnl.c:448: Test failed: got size 0 ntoskrnl.c:457: Test failed: got buffer abcdef ntoskrnl.c:458: Test failed: got size 0 ntoskrnl.c:467: Test failed: got buffer abcdef ntoskrnl.c:468: Test failed: got size 0 ntoskrnl.c:478: Test failed: got size 0
=== debian10 (32 bit Japanese:Japan report) ===
ntoskrnl.exe: ntoskrnl.c:437: Test failed: got buffer abcdef ntoskrnl.c:438: Test failed: got size 0 ntoskrnl.c:447: Test failed: got buffer abcdef ntoskrnl.c:448: Test failed: got size 0 ntoskrnl.c:457: Test failed: got buffer abcdef ntoskrnl.c:458: Test failed: got size 0 ntoskrnl.c:467: Test failed: got buffer abcdef ntoskrnl.c:468: Test failed: got size 0 ntoskrnl.c:478: Test failed: got size 0
=== debian10 (32 bit Chinese:China report) ===
ntoskrnl.exe: ntoskrnl.c:437: Test failed: got buffer abcdef ntoskrnl.c:438: Test failed: got size 0 ntoskrnl.c:447: Test failed: got buffer abcdef ntoskrnl.c:448: Test failed: got size 0 ntoskrnl.c:457: Test failed: got buffer abcdef ntoskrnl.c:458: Test failed: got size 0 ntoskrnl.c:467: Test failed: got buffer abcdef ntoskrnl.c:468: Test failed: got size 0 ntoskrnl.c:478: Test failed: got size 0
=== debian10 (32 bit WoW report) ===
ntoskrnl.exe: ntoskrnl.c:437: Test failed: got buffer abcdef ntoskrnl.c:438: Test failed: got size 0 ntoskrnl.c:447: Test failed: got buffer abcdef ntoskrnl.c:448: Test failed: got size 0 ntoskrnl.c:457: Test failed: got buffer abcdef ntoskrnl.c:458: Test failed: got size 0 ntoskrnl.c:467: Test failed: got buffer abcdef ntoskrnl.c:468: Test failed: got size 0 ntoskrnl.c:478: Test failed: got size 0
=== debian10 (64 bit WoW report) ===
ntoskrnl.exe: ntoskrnl.c:437: Test failed: got buffer abcdef ntoskrnl.c:438: Test failed: got size 0 ntoskrnl.c:447: Test failed: got buffer abcdef ntoskrnl.c:448: Test failed: got size 0 ntoskrnl.c:457: Test failed: got buffer abcdef ntoskrnl.c:458: Test failed: got size 0 ntoskrnl.c:467: Test failed: got buffer abcdef ntoskrnl.c:468: Test failed: got size 0 ntoskrnl.c:478: Test failed: got size 0
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=60779
Your paranoid android.
=== debian10 (32 bit report) ===
ntoskrnl.exe: ntoskrnl.c:424: Test failed: got buffer abcdef ntoskrnl.c:425: Test failed: got size 0 ntoskrnl.c:434: Test failed: got buffer abcdef ntoskrnl.c:435: Test failed: got size 0 ntoskrnl.c:444: Test failed: got buffer abcdef ntoskrnl.c:445: Test failed: got size 0 ntoskrnl.c:454: Test failed: got buffer abcdef ntoskrnl.c:455: Test failed: got size 0 ntoskrnl.c:465: Test failed: got size 0
=== debian10 (32 bit Chinese:China report) ===
ntoskrnl.exe: ntoskrnl.c:424: Test failed: got buffer abcdef ntoskrnl.c:425: Test failed: got size 0 ntoskrnl.c:434: Test failed: got buffer abcdef ntoskrnl.c:435: Test failed: got size 0 ntoskrnl.c:444: Test failed: got buffer abcdef ntoskrnl.c:445: Test failed: got size 0 ntoskrnl.c:454: Test failed: got buffer abcdef ntoskrnl.c:455: Test failed: got size 0 ntoskrnl.c:465: Test failed: got size 0
=== debian10 (32 bit WoW report) ===
ntoskrnl.exe: ntoskrnl.c:424: Test failed: got buffer abcdef ntoskrnl.c:425: Test failed: got size 0 ntoskrnl.c:434: Test failed: got buffer abcdef ntoskrnl.c:435: Test failed: got size 0 ntoskrnl.c:444: Test failed: got buffer abcdef ntoskrnl.c:445: Test failed: got size 0 ntoskrnl.c:454: Test failed: got buffer abcdef ntoskrnl.c:455: Test failed: got size 0 ntoskrnl.c:465: Test failed: got size 0
=== debian10 (64 bit WoW report) ===
ntoskrnl.exe: ntoskrnl.c:424: Test failed: got buffer abcdef ntoskrnl.c:425: Test failed: got size 0 ntoskrnl.c:434: Test failed: got buffer abcdef ntoskrnl.c:435: Test failed: got size 0 ntoskrnl.c:444: Test failed: got buffer abcdef ntoskrnl.c:445: Test failed: got size 0 ntoskrnl.c:454: Test failed: got buffer abcdef ntoskrnl.c:455: Test failed: got size 0 ntoskrnl.c:465: Test failed: got size 0
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=60778
Your paranoid android.
=== debian10 (build log) ===
Task errors: The task timed out
=== debian10 (build log) ===
Task errors: The task timed out
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=60777
Your paranoid android.
=== debian10 (build log) ===
Task errors: The task timed out
=== debian10 (build log) ===
Task errors: The task timed out