>From 471126a98c92102046cd7bd6e5ec832f1c2ae907 Mon Sep 17 00:00:00 2001
From: Zebediah Figura <zfigura@codeweavers.com>
Date: Mon, 3 Jun 2019 13:25:03 -0500
Subject: [PATCH 5/5] ntdll: Handle objects that are closed while waited on.

Signed-off-by: Zebediah Figura <zfigura@codeweavers.com>
---
 dlls/ntdll/esync.c | 85 +++++++++++++++++++++++++++++++++++-----------
 1 file changed, 65 insertions(+), 20 deletions(-)

diff --git a/dlls/ntdll/esync.c b/dlls/ntdll/esync.c
index 7e22bd956ea..836017c9ac4 100644
--- a/dlls/ntdll/esync.c
+++ b/dlls/ntdll/esync.c
@@ -75,6 +75,7 @@ struct esync
 #else
     int readfd, writefd;
 #endif
+    int refcount;
     void *shm;              /* pointer to shm section */
 };
 
@@ -133,6 +134,20 @@ static void efd_close(struct esync *esync)
 #endif
 }
 
+static void grab_object( struct esync *obj )
+{
+    interlocked_xchg_add( &obj->refcount, 1 );
+}
+
+static void release_object( struct esync *obj )
+{
+    if (interlocked_xchg_add( &obj->refcount, -1 ) == 1)
+    {
+        efd_close( obj );
+        RtlFreeHeap( GetProcessHeap(), 0, obj );
+    }
+}
+
 /* Careful how this is used—we can't reliably completely clear an object with it. */
 static int get_read_fd( struct esync *obj )
 {
@@ -343,6 +358,7 @@ static struct esync *add_to_list( HANDLE handle, enum esync_type type, int fd, v
     obj->readfd = fd;
     obj->writefd = writefd;
 #endif
+    obj->refcount = 1;
     obj->shm = shm;
 
     assert(!interlocked_xchg_ptr((void **)&esync_list[entry][idx], obj));
@@ -431,8 +447,7 @@ NTSTATUS esync_close( HANDLE handle )
     {
         if ((obj = interlocked_xchg_ptr((void **)&esync_list[entry][idx], 0)))
         {
-            efd_close( obj );
-            RtlFreeHeap( GetProcessHeap(), 0, obj );
+            release_object( obj );
             return STATUS_SUCCESS;
         }
     }
@@ -1009,6 +1024,7 @@ static NTSTATUS __esync_wait_objects( DWORD count, const HANDLE *handles,
     BOOL msgwait = FALSE;
     LONGLONG timeleft;
     LARGE_INTEGER now;
+    NTSTATUS status;
     DWORD pollcount;
     ULONGLONG end;
     int i, j;
@@ -1100,6 +1116,12 @@ static NTSTATUS __esync_wait_objects( DWORD count, const HANDLE *handles,
         }
     }
 
+    for (i = 0; i < count; i++)
+    {
+        if (objs[i])
+            grab_object( objs[i] );
+    }
+
     if (wait_any || count == 1)
     {
         /* Try to check objects now, so we can obviate poll() at least. */
@@ -1119,7 +1141,8 @@ static NTSTATUS __esync_wait_objects( DWORD count, const HANDLE *handles,
                     {
                         TRACE("Woken up by handle %p [%d].\n", handles[i], i);
                         mutex->count++;
-                        return i;
+                        status = i;
+                        goto done;
                     }
                     else if (!mutex->count)
                     {
@@ -1134,7 +1157,8 @@ static NTSTATUS __esync_wait_objects( DWORD count, const HANDLE *handles,
                                 TRACE("Woken up by handle %p [%d].\n", handles[i], i);
                             mutex->tid = GetCurrentThreadId();
                             mutex->count++;
-                            return i;
+                            status = i;
+                            goto done;
                         }
                     }
                     break;
@@ -1149,7 +1173,8 @@ static NTSTATUS __esync_wait_objects( DWORD count, const HANDLE *handles,
                         {
                             TRACE("Woken up by handle %p [%d].\n", handles[i], i);
                             interlocked_xchg_add( &semaphore->count, -1 );
-                            return i;
+                            status = i;
+                            goto done;
                         }
                     }
                     break;
@@ -1164,7 +1189,8 @@ static NTSTATUS __esync_wait_objects( DWORD count, const HANDLE *handles,
                         {
                             TRACE("Woken up by handle %p [%d].\n", handles[i], i);
                             event->signaled = 0;
-                            return i;
+                            status = i;
+                            goto done;
                         }
                     }
                     break;
@@ -1176,7 +1202,8 @@ static NTSTATUS __esync_wait_objects( DWORD count, const HANDLE *handles,
                     if (event->signaled)
                     {
                         TRACE("Woken up by handle %p [%d].\n", handles[i], i);
-                        return i;
+                        status = i;
+                        goto done;
                     }
                     break;
                 }
@@ -1228,7 +1255,8 @@ static NTSTATUS __esync_wait_objects( DWORD count, const HANDLE *handles,
                     if (fds[i].revents & (POLLERR | POLLHUP | POLLNVAL))
                     {
                         ERR("Polling on fd %d returned %#x.\n", fds[i].fd, fds[i].revents);
-                        return STATUS_INVALID_HANDLE;
+                        status = STATUS_INVALID_HANDLE;
+                        goto done;
                     }
 
                     if (obj)
@@ -1239,7 +1267,8 @@ static NTSTATUS __esync_wait_objects( DWORD count, const HANDLE *handles,
                             if (fds[i].revents & POLLIN)
                             {
                                 TRACE("Woken up by handle %p [%d].\n", handles[i], i);
-                                return i;
+                                status = i;
+                                goto done;
                             }
                         }
                         else
@@ -1249,9 +1278,10 @@ static NTSTATUS __esync_wait_objects( DWORD count, const HANDLE *handles,
                             {
                                 /* We found our object. */
                                 TRACE("Woken up by handle %p [%d].\n", handles[i], i);
+                                status = i;
                                 if (update_grabbed_object( obj ))
-                                    return STATUS_ABANDONED_WAIT_0 + i;
-                                return i;
+                                    status += STATUS_ABANDONED_WAIT_0;
+                                goto done;
                             }
                         }
                     }
@@ -1262,7 +1292,8 @@ static NTSTATUS __esync_wait_objects( DWORD count, const HANDLE *handles,
                     if (fds[i++].revents & POLLIN)
                     {
                         TRACE("Woken up by driver events.\n");
-                        return count - 1;
+                        status = count - 1;
+                        goto done;
                     }
                 }
 
@@ -1334,7 +1365,8 @@ tryagain:
                 if (fds[0].revents & (POLLHUP | POLLERR | POLLNVAL))
                 {
                     ERR("Polling on fd %d returned %#x.\n", fds[0].fd, fds[0].revents);
-                    return STATUS_INVALID_HANDLE;
+                    status = STATUS_INVALID_HANDLE;
+                    goto done;
                 }
             }
 
@@ -1393,7 +1425,10 @@ tryagain:
                             for (j = i; j >= 0; j--)
                             {
                                 if (efd_write( obj, 1 ) == -1 && errno != EAGAIN)
-                                    return FILE_GetNtStatus();
+                                {
+                                    status = FILE_GetNtStatus();
+                                    goto done;
+                                }
                             }
 
                             goto tryagain;  /* break out of two loops and a switch */
@@ -1418,7 +1453,8 @@ tryagain:
                     return STATUS_ABANDONED;
                 }
                 TRACE("Wait successful.\n");
-                return STATUS_SUCCESS;
+                status = STATUS_SUCCESS;
+                goto done;
             }
 
             /* If we got here, ppoll() returned less than all of our objects.
@@ -1432,12 +1468,14 @@ err:
     if (ret == 0)
     {
         TRACE("Wait timed out.\n");
-        return STATUS_TIMEOUT;
+        status = STATUS_TIMEOUT;
+        goto done;
     }
     else
     {
         ERR("ppoll failed: %s\n", strerror(errno));
-        return FILE_GetNtStatus();
+        status = FILE_GetNtStatus();
+        goto done;
     }
 
 userapc:
@@ -1445,13 +1483,20 @@ userapc:
 
     /* We have to make a server call anyway to get the APC to execute, so just
      * delegate down to server_select(). */
-    ret = server_select( NULL, 0, SELECT_INTERRUPTIBLE | SELECT_ALERTABLE, &zero );
+    status = server_select( NULL, 0, SELECT_INTERRUPTIBLE | SELECT_ALERTABLE, &zero );
 
     /* This can happen if we received a system APC, and the APC fd was woken up
      * before we got SIGUSR1. poll() doesn't return EINTR in that case. The
      * right thing to do seems to be to return STATUS_USER_APC anyway. */
-    if (ret == STATUS_TIMEOUT) ret = STATUS_USER_APC;
-    return ret;
+    if (status == STATUS_TIMEOUT) status = STATUS_USER_APC;
+
+done:
+    for (i = 0; i < count; i++)
+    {
+        if (objs[i])
+            release_object( objs[i] );
+    }
+    return status;
 }
 
 /* We need to let the server know when we are doing a message wait, and when we
-- 
2.25.1

