From 943e5341b06b05129e2f909b48e446141a6d976d Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Sun, 29 Jul 2018 16:50:08 -0500
Subject: [PATCH 78/83] ntdll, server: Initialize the shared memory portion on
 the server side.

Simply using a CS only prevents this race within one process.
---
 dlls/ntdll/esync.c  | 100 ++++----------------------------
 server/esync.c      | 135 +++++++++++++++++++++++++++++---------------
 server/protocol.def |   1 +
 3 files changed, 101 insertions(+), 135 deletions(-)

diff --git a/dlls/ntdll/esync.c b/dlls/ntdll/esync.c
index 4f418b211..b84b7b83c 100644
--- a/dlls/ntdll/esync.c
+++ b/dlls/ntdll/esync.c
@@ -109,7 +109,7 @@ static int shm_addrs_size;  /* length of the allocated shm_addrs array */
 static long pagesize;
 
 static NTSTATUS create_esync( enum esync_type type, HANDLE *handle,
-    ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr, int initval );
+    ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr, int initval, int max );
 
 void esync_init(void)
 {
@@ -121,7 +121,7 @@ void esync_init(void)
         HANDLE handle;
         NTSTATUS ret;
 
-        ret = create_esync( 0, &handle, 0, NULL, 0 );
+        ret = create_esync( 0, &handle, 0, NULL, 0, 0 );
         if (ret != STATUS_NOT_IMPLEMENTED)
         {
             ERR("Server is running with WINEESYNC but this process is not, please enable WINEESYNC or restart wineserver.\n");
@@ -320,7 +320,7 @@ NTSTATUS esync_close( HANDLE handle )
 }
 
 static NTSTATUS create_esync( enum esync_type type, HANDLE *handle,
-    ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr, int initval )
+    ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr, int initval, int max )
 {
     NTSTATUS ret;
     data_size_t len;
@@ -340,6 +340,7 @@ static NTSTATUS create_esync( enum esync_type type, HANDLE *handle,
         req->access  = access;
         req->initval = initval;
         req->type    = type;
+        req->max     = max;
         wine_server_add_data( req, objattr, len );
         ret = wine_server_call( req );
         if (!ret || ret == STATUS_OBJECT_NAME_EXISTS)
@@ -404,60 +405,21 @@ static NTSTATUS open_esync( enum esync_type type, HANDLE *handle,
     return ret;
 }
 
-RTL_CRITICAL_SECTION shm_init_section;
-static RTL_CRITICAL_SECTION_DEBUG critsect_debug =
-{
-    0, 0, &shm_init_section,
-    { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
-      0, 0, { (DWORD_PTR)(__FILE__ ": shm_init_section") }
-};
-RTL_CRITICAL_SECTION shm_init_section = { &critsect_debug, -1, 0, 0, 0, 0 };
-
 NTSTATUS esync_create_semaphore(HANDLE *handle, ACCESS_MASK access,
     const OBJECT_ATTRIBUTES *attr, LONG initial, LONG max)
 {
-    NTSTATUS ret;
-
     TRACE("name %s, initial %d, max %d.\n",
         attr ? debugstr_us(attr->ObjectName) : "<no name>", initial, max);
 
-    /* We need this lock to protect against a potential (though unlikely) race:
-     * if a different process tries to open a named object and manages to use
-     * it between the time we get back from the server and the time we
-     * initialize the shared memory, it'll have uninitialized values for the
-     * object's state. That requires us to be REALLY slow, but we're not taking
-     * any chances. Synchronize on the CS here so that we're sure to be ready
-     * before anyone else can open the object. */
-    RtlEnterCriticalSection( &shm_init_section );
-
-    ret = create_esync( ESYNC_SEMAPHORE, handle, access, attr, initial );
-    if (!ret)
-    {
-        /* Initialize the shared memory portion.
-         * Note we store max here (even though we don't need to) just to keep
-         * it the same size as the mutex's shm portion. */
-        struct esync *obj = get_cached_object( *handle );
-        struct semaphore *semaphore = obj->shm;
-        semaphore->max = max;
-        semaphore->count = initial;
-    }
-
-    RtlLeaveCriticalSection( &shm_init_section );
-
-    return ret;
+    return create_esync( ESYNC_SEMAPHORE, handle, access, attr, initial, max );
 }
 
 NTSTATUS esync_open_semaphore( HANDLE *handle, ACCESS_MASK access,
     const OBJECT_ATTRIBUTES *attr )
 {
-    NTSTATUS ret;
-
     TRACE("name %s.\n", debugstr_us(attr->ObjectName));
 
-    RtlEnterCriticalSection( &shm_init_section );
-    ret = open_esync( ESYNC_SEMAPHORE, handle, access, attr );
-    RtlLeaveCriticalSection( &shm_init_section );
-    return ret;
+    return open_esync( ESYNC_SEMAPHORE, handle, access, attr );
 }
 
 NTSTATUS esync_release_semaphore( HANDLE handle, ULONG count, ULONG *prev )
@@ -523,41 +485,20 @@ NTSTATUS esync_create_event( HANDLE *handle, ACCESS_MASK access,
     const OBJECT_ATTRIBUTES *attr, EVENT_TYPE event_type, BOOLEAN initial )
 {
     enum esync_type type = (event_type == SynchronizationEvent ? ESYNC_AUTO_EVENT : ESYNC_MANUAL_EVENT);
-    NTSTATUS ret;
 
     TRACE("name %s, %s-reset, initial %d.\n",
         attr ? debugstr_us(attr->ObjectName) : "<no name>",
         event_type == NotificationEvent ? "manual" : "auto", initial);
 
-    RtlEnterCriticalSection( &shm_init_section );
-
-    ret = create_esync( type, handle, access, attr, initial );
-
-    if (!ret)
-    {
-        /* Initialize the shared memory portion. */
-        struct esync *obj = get_cached_object( *handle );
-        struct event *event = obj->shm;
-        event->signaled = initial;
-        event->locked = 0;
-    }
-
-    RtlLeaveCriticalSection( &shm_init_section );
-
-    return ret;
+    return create_esync( type, handle, access, attr, initial, 0 );
 }
 
 NTSTATUS esync_open_event( HANDLE *handle, ACCESS_MASK access,
     const OBJECT_ATTRIBUTES *attr )
 {
-    NTSTATUS ret;
-
     TRACE("name %s.\n", debugstr_us(attr->ObjectName));
 
-    RtlEnterCriticalSection( &shm_init_section );
-    ret = open_esync( ESYNC_AUTO_EVENT, handle, access, attr ); /* doesn't matter which */
-    RtlLeaveCriticalSection( &shm_init_section );
-    return ret;
+    return open_esync( ESYNC_AUTO_EVENT, handle, access, attr ); /* doesn't matter which */
 }
 
 static inline void small_pause(void)
@@ -740,39 +681,18 @@ NTSTATUS esync_query_event( HANDLE handle, EVENT_INFORMATION_CLASS class,
 NTSTATUS esync_create_mutex( HANDLE *handle, ACCESS_MASK access,
     const OBJECT_ATTRIBUTES *attr, BOOLEAN initial )
 {
-    NTSTATUS ret;
-
     TRACE("name %s, initial %d.\n",
         attr ? debugstr_us(attr->ObjectName) : "<no name>", initial);
 
-    RtlEnterCriticalSection( &shm_init_section );
-
-    ret = create_esync( ESYNC_MUTEX, handle, access, attr, initial ? 0 : 1 );
-    if (!ret)
-    {
-        /* Initialize the shared memory portion. */
-        struct esync *obj = get_cached_object( *handle );
-        struct mutex *mutex = obj->shm;
-        mutex->tid = initial ? GetCurrentThreadId() : 0;
-        mutex->count = initial ? 1 : 0;
-    }
-
-    RtlLeaveCriticalSection( &shm_init_section );
-
-    return ret;
+    return create_esync( ESYNC_MUTEX, handle, access, attr, initial ? 0 : 1, 0 );
 }
 
 NTSTATUS esync_open_mutex( HANDLE *handle, ACCESS_MASK access,
     const OBJECT_ATTRIBUTES *attr )
 {
-    NTSTATUS ret;
-
     TRACE("name %s.\n", debugstr_us(attr->ObjectName));
 
-    RtlEnterCriticalSection( &shm_init_section );
-    ret = open_esync( ESYNC_MUTEX, handle, access, attr );
-    RtlLeaveCriticalSection( &shm_init_section );
-    return ret;
+    return open_esync( ESYNC_MUTEX, handle, access, attr );
 }
 
 NTSTATUS esync_release_mutex( HANDLE *handle, LONG *prev )
diff --git a/server/esync.c b/server/esync.c
index f5556e91c..19e614a89 100644
--- a/server/esync.c
+++ b/server/esync.c
@@ -179,8 +179,63 @@ static int type_matches( enum esync_type type1, enum esync_type type2 )
             (type2 == ESYNC_AUTO_EVENT || type2 == ESYNC_MANUAL_EVENT));
 }
 
+static void *get_shm( unsigned int idx )
+{
+    int entry  = (idx * 8) / pagesize;
+    int offset = (idx * 8) % pagesize;
+
+    if (entry >= shm_addrs_size)
+    {
+        if (!(shm_addrs = realloc( shm_addrs, (entry + 1) * sizeof(shm_addrs[0]) )))
+            fprintf( stderr, "esync: couldn't expand shm_addrs array to size %d\n", entry + 1 );
+
+        memset( &shm_addrs[shm_addrs_size], 0, (entry + 1 - shm_addrs_size) * sizeof(shm_addrs[0]) );
+
+        shm_addrs_size = entry + 1;
+    }
+
+    if (!shm_addrs[entry])
+    {
+        void *addr = mmap( NULL, pagesize, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, entry * pagesize );
+        if (addr == (void *)-1)
+        {
+            fprintf( stderr, "esync: failed to map page %d (offset %#lx): ", entry, entry * pagesize );
+            perror( "mmap" );
+        }
+
+        if (debug_level)
+            fprintf( stderr, "esync: Mapping page %d at %p.\n", entry, addr );
+
+        if (InterlockedCompareExchangePointer( &shm_addrs[entry], addr, 0 ))
+            munmap( addr, pagesize ); /* someone beat us to it */
+    }
+
+    return (void *)((unsigned long)shm_addrs[entry] + offset);
+}
+
+struct semaphore
+{
+    int max;
+    int count;
+};
+C_ASSERT(sizeof(struct semaphore) == 8);
+
+struct mutex
+{
+    DWORD tid;
+    int count;    /* recursion count */
+};
+C_ASSERT(sizeof(struct mutex) == 8);
+
+struct event
+{
+    int signaled;
+    int locked;
+};
+C_ASSERT(sizeof(struct event) == 8);
+
 static struct esync *create_esync( struct object *root, const struct unicode_str *name,
-    unsigned int attr, int initval, enum esync_type type,
+    unsigned int attr, int initval, int max, enum esync_type type,
     const struct security_descriptor *sd )
 {
 #ifdef HAVE_SYS_EVENTFD_H
@@ -220,6 +275,38 @@ static struct esync *create_esync( struct object *root, const struct unicode_str
                     perror( "ftruncate" );
                 }
             }
+
+            /* Initialize the shared memory portion. We want to do this on the
+             * server side to avoid a potential though unlikely race whereby
+             * the same object is opened and used between the time it's created
+             * and the time its shared memory portion is initialized. */
+            switch (type)
+            {
+            case ESYNC_SEMAPHORE:
+            {
+                struct semaphore *semaphore = get_shm( esync->shm_idx );
+                semaphore->max = max;
+                semaphore->count = initval;
+                break;
+            }
+            case ESYNC_AUTO_EVENT:
+            case ESYNC_MANUAL_EVENT:
+            {
+                struct event *event = get_shm( esync->shm_idx );
+                event->signaled = initval ? 1 : 0;
+                event->locked = 0;
+                break;
+            }
+            case ESYNC_MUTEX:
+            {
+                struct mutex *mutex = get_shm( esync->shm_idx );
+                mutex->tid = initval ? 0 : current->id;
+                mutex->count = initval ? 0 : 1;
+                break;
+            }
+            default:
+                assert( 0 );
+            }
         }
         else
         {
@@ -288,49 +375,6 @@ void esync_clear( int fd )
     read( fd, &value, sizeof(value) );
 }
 
-/* Sadly, we need all of this infrastructure to keep the shm state in sync. */
-
-static void *get_shm( unsigned int idx )
-{
-    int entry  = (idx * 8) / pagesize;
-    int offset = (idx * 8) % pagesize;
-
-    if (entry >= shm_addrs_size)
-    {
-        if (!(shm_addrs = realloc( shm_addrs, (entry + 1) * sizeof(shm_addrs[0]) )))
-            fprintf( stderr, "esync: couldn't expand shm_addrs array to size %d\n", entry + 1 );
-
-        memset( &shm_addrs[shm_addrs_size], 0, (entry + 1 - shm_addrs_size) * sizeof(shm_addrs[0]) );
-
-        shm_addrs_size = entry + 1;
-    }
-
-    if (!shm_addrs[entry])
-    {
-        void *addr = mmap( NULL, pagesize, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, entry * pagesize );
-        if (addr == (void *)-1)
-        {
-            fprintf( stderr, "esync: failed to map page %d (offset %#lx): ", entry, entry * pagesize );
-            perror( "mmap" );
-        }
-
-        if (debug_level)
-            fprintf( stderr, "esync: Mapping page %d at %p.\n", entry, addr );
-
-        if (InterlockedCompareExchangePointer( &shm_addrs[entry], addr, 0 ))
-            munmap( addr, pagesize ); /* someone beat us to it */
-    }
-
-    return (void *)((unsigned long)shm_addrs[entry] + offset);
-}
-
-struct event
-{
-    int signaled;
-    int locked;
-};
-C_ASSERT(sizeof(struct event) == 8);
-
 static inline void small_pause(void)
 {
 #ifdef __i386__
@@ -414,7 +458,8 @@ DECL_HANDLER(create_esync)
 
     if (!objattr) return;
 
-    if ((esync = create_esync( root, &name, objattr->attributes, req->initval, req->type, sd )))
+    if ((esync = create_esync( root, &name, objattr->attributes, req->initval,
+        req->max, req->type, sd )))
     {
         if (get_error() == STATUS_OBJECT_NAME_EXISTS)
             reply->handle = alloc_handle( current->process, esync, req->access, objattr->attributes );
diff --git a/server/protocol.def b/server/protocol.def
index 220c7c407..dbb8770df 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -4047,6 +4047,7 @@ struct handle_info
     unsigned int access;        /* wanted access rights */
     int          initval;       /* initial value */
     int          type;          /* type of esync object (see below) */
+    int          max;           /* maximum count on a semaphore */
     VARARG(objattr,object_attributes); /* object attributes */
 @REPLY
     obj_handle_t handle;        /* handle to the object */
-- 
2.20.1

