[prev in list] [next in list] [prev in thread] [next in thread] 

List:       wine-devel
Subject:    Re: [PATCH 3/3] server: Hold a reference to the file in delete_file().
From:       Zebediah Figura <z.figura12 () gmail ! com>
Date:       2020-02-28 16:33:19
Message-ID: 4bc00dc5-2162-e476-85ee-c853b5256ef9 () gmail ! com
[Download RAW message or body]

On 2/14/20 12:10 PM, Zebediah Figura wrote:
> From: Michael Müller <michael@fds-team.de>
> 
> Otherwise, we may attempt to access freed memory trawling the device list.
> This can occur if a device driver crashes during an IRP_CALL_CLOSE request.
> 
> Signed-off-by: Zebediah Figura <z.figura12@gmail.com>
> ---
>   server/device.c | 6 ++++++
>   1 file changed, 6 insertions(+)
> 
> diff --git a/server/device.c b/server/device.c
> index b02d965e33..12208ec8a2 100644
> --- a/server/device.c
> +++ b/server/device.c
> @@ -729,12 +729,18 @@ static void delete_file( struct device_file *file )
>   {
>       struct irp_call *irp, *next;
>   
> +    /* The pending requests may be the only thing holding a reference to the
> +     * file. */
> +    grab_object( file );
> +
>       /* terminate all pending requests */
>       LIST_FOR_EACH_ENTRY_SAFE( irp, next, &file->requests, struct irp_call, dev_entry )
>       {
>           list_remove( &irp->mgr_entry );
>           set_irp_result( irp, STATUS_FILE_DELETED, NULL, 0, 0 );
>       }
> +
> +    release_object( file );
>   }
>   
>   static void delete_device( struct device *device )
> 

I'm attaching a diff here which reproduces this issue. The test case is 
added to httpapi/tests, and intentionally causes a crash in http.sys' 
IRP_MJ_CLOSE handler (which is artificial, but as I understand it should 
not be allowed to crash the server). It essentially opens a file on the 
device, makes one overlapped ioctl which is never completed, and then 
closes the file. I've also added traces to server/device.c to clearly 
demonstrate the crash.

The server accesses freed memory for me without both of these patches. 
(In the case of delete_file(), my understanding is that 
LIST_FOR_EACH_ENTRY_SAFE still dereferences the list on every iteration 
to determine whether there are more elements.) This doesn't always 
manifest as a crash, but usually does for me.

["server_device_refcount.diff" (text/x-patch)]

diff --git a/dlls/http.sys/http.c b/dlls/http.sys/http.c
index 4259ca618f..ad7dc33d1c 100644
--- a/dlls/http.sys/http.c
+++ b/dlls/http.sys/http.c
@@ -1373,6 +1373,8 @@ static NTSTATUS WINAPI dispatch_close(DEVICE_OBJECT *device, \
IRP *irp)  IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(irp);
     struct request_queue *queue = stack->FileObject->FsContext;
 
+    *(int *)0 = 0;
+
     TRACE("Closing queue %p.\n", queue);
     close_queue(queue);
 
diff --git a/dlls/httpapi/tests/httpapi.c b/dlls/httpapi/tests/httpapi.c
index a52a3f6c34..bbf5c580f1 100644
--- a/dlls/httpapi/tests/httpapi.c
+++ b/dlls/httpapi/tests/httpapi.c
@@ -1449,6 +1449,29 @@ START_TEST(httpapi)
     ret = HttpInitialize(version, HTTP_INITIALIZE_SERVER, NULL);
     ok(!ret, "Failed to initialize library, ret %u.\n", ret);
 
+    {
+        char DECLSPEC_ALIGN(8) req[2000];
+        unsigned short port;
+        OVERLAPPED ovl;
+        HANDLE queue;
+
+        ret = HttpCreateHttpHandle(&queue, 0);
+        ok(!ret, "Got error %u.\n", ret);
+
+        port = add_url_v1(queue);
+
+        ovl.hEvent = CreateEventA(NULL, TRUE, FALSE, NULL);
+        ret = HttpReceiveHttpRequest(queue, HTTP_NULL_ID, 0, (HTTP_REQUEST *)req, \
sizeof(req), NULL, &ovl); +        ok(ret == ERROR_IO_PENDING, "Got error %u.\n", \
ret); +
+        ret = remove_url_v1(queue, port);
+        ok(!ret, "Got error %u.\n", ret);
+
+        ret = CloseHandle(queue);
+        ok(ret, "Got error %u.\n", GetLastError());
+        return;
+    }
+
     test_v1_server();
     test_v1_completion_port();
     test_v1_multiple_requests();
diff --git a/server/device.c b/server/device.c
index d3e2a84c1e..f32daeb64b 100644
--- a/server/device.c
+++ b/server/device.c
@@ -734,6 +734,10 @@ static void delete_file( struct device_file *file )
         list_remove( &irp->mgr_entry );
         set_irp_result( irp, STATUS_FILE_DELETED, NULL, 0, 0 );
     }
+
+fprintf(stderr, "Dumping file %p:\n", file);
+    file->obj.ops->dump( &file->obj, 1 );
+fprintf(stderr, "Dumped file %p.\n", file);
 }
 
 static void delete_device( struct device *device )
@@ -745,6 +749,10 @@ static void delete_device( struct device *device )
     LIST_FOR_EACH_ENTRY_SAFE( file, next, &device->files, struct device_file, entry \
)  delete_file( file );
 
+fprintf(stderr, "Dumping device %p:\n", device);
+    device->obj.ops->dump( &file->obj, 1 );
+fprintf(stderr, "Dumped device %p.\n", device);
+
     unlink_named_object( &device->obj );
     list_remove( &device->entry );
     device->manager = NULL;



[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic