Skip to content

Commit ca6b0ae

Browse files
zfiguraGuy1524
authored andcommitted
ntdll, server: Initialize the shared memory portion on the server side.
Simply using a CS only prevents this race within one process.
1 parent 8dfcad1 commit ca6b0ae

File tree

6 files changed

+107
-137
lines changed

6 files changed

+107
-137
lines changed

dlls/ntdll/esync.c

Lines changed: 10 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ static int shm_addrs_size; /* length of the allocated shm_addrs array */
109109
static long pagesize;
110110

111111
static NTSTATUS create_esync( enum esync_type type, HANDLE *handle,
112-
ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr, int initval );
112+
ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr, int initval, int max );
113113

114114
void esync_init(void)
115115
{
@@ -121,7 +121,7 @@ void esync_init(void)
121121
HANDLE handle;
122122
NTSTATUS ret;
123123

124-
ret = create_esync( 0, &handle, 0, NULL, 0 );
124+
ret = create_esync( 0, &handle, 0, NULL, 0, 0 );
125125
if (ret != STATUS_NOT_IMPLEMENTED)
126126
{
127127
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 )
320320
}
321321

322322
static NTSTATUS create_esync( enum esync_type type, HANDLE *handle,
323-
ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr, int initval )
323+
ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr, int initval, int max )
324324
{
325325
NTSTATUS ret;
326326
data_size_t len;
@@ -340,6 +340,7 @@ static NTSTATUS create_esync( enum esync_type type, HANDLE *handle,
340340
req->access = access;
341341
req->initval = initval;
342342
req->type = type;
343+
req->max = max;
343344
wine_server_add_data( req, objattr, len );
344345
ret = wine_server_call( req );
345346
if (!ret || ret == STATUS_OBJECT_NAME_EXISTS)
@@ -404,60 +405,21 @@ static NTSTATUS open_esync( enum esync_type type, HANDLE *handle,
404405
return ret;
405406
}
406407

407-
RTL_CRITICAL_SECTION shm_init_section;
408-
static RTL_CRITICAL_SECTION_DEBUG critsect_debug =
409-
{
410-
0, 0, &shm_init_section,
411-
{ &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
412-
0, 0, { (DWORD_PTR)(__FILE__ ": shm_init_section") }
413-
};
414-
RTL_CRITICAL_SECTION shm_init_section = { &critsect_debug, -1, 0, 0, 0, 0 };
415-
416408
NTSTATUS esync_create_semaphore(HANDLE *handle, ACCESS_MASK access,
417409
const OBJECT_ATTRIBUTES *attr, LONG initial, LONG max)
418410
{
419-
NTSTATUS ret;
420-
421411
TRACE("name %s, initial %d, max %d.\n",
422412
attr ? debugstr_us(attr->ObjectName) : "<no name>", initial, max);
423413

424-
/* We need this lock to protect against a potential (though unlikely) race:
425-
* if a different process tries to open a named object and manages to use
426-
* it between the time we get back from the server and the time we
427-
* initialize the shared memory, it'll have uninitialized values for the
428-
* object's state. That requires us to be REALLY slow, but we're not taking
429-
* any chances. Synchronize on the CS here so that we're sure to be ready
430-
* before anyone else can open the object. */
431-
RtlEnterCriticalSection( &shm_init_section );
432-
433-
ret = create_esync( ESYNC_SEMAPHORE, handle, access, attr, initial );
434-
if (!ret)
435-
{
436-
/* Initialize the shared memory portion.
437-
* Note we store max here (even though we don't need to) just to keep
438-
* it the same size as the mutex's shm portion. */
439-
struct esync *obj = get_cached_object( *handle );
440-
struct semaphore *semaphore = obj->shm;
441-
semaphore->max = max;
442-
semaphore->count = initial;
443-
}
444-
445-
RtlLeaveCriticalSection( &shm_init_section );
446-
447-
return ret;
414+
return create_esync( ESYNC_SEMAPHORE, handle, access, attr, initial, max );
448415
}
449416

450417
NTSTATUS esync_open_semaphore( HANDLE *handle, ACCESS_MASK access,
451418
const OBJECT_ATTRIBUTES *attr )
452419
{
453-
NTSTATUS ret;
454-
455420
TRACE("name %s.\n", debugstr_us(attr->ObjectName));
456421

457-
RtlEnterCriticalSection( &shm_init_section );
458-
ret = open_esync( ESYNC_SEMAPHORE, handle, access, attr );
459-
RtlLeaveCriticalSection( &shm_init_section );
460-
return ret;
422+
return open_esync( ESYNC_SEMAPHORE, handle, access, attr );
461423
}
462424

463425
NTSTATUS esync_release_semaphore( HANDLE handle, ULONG count, ULONG *prev )
@@ -523,41 +485,20 @@ NTSTATUS esync_create_event( HANDLE *handle, ACCESS_MASK access,
523485
const OBJECT_ATTRIBUTES *attr, EVENT_TYPE event_type, BOOLEAN initial )
524486
{
525487
enum esync_type type = (event_type == SynchronizationEvent ? ESYNC_AUTO_EVENT : ESYNC_MANUAL_EVENT);
526-
NTSTATUS ret;
527488

528489
TRACE("name %s, %s-reset, initial %d.\n",
529490
attr ? debugstr_us(attr->ObjectName) : "<no name>",
530491
event_type == NotificationEvent ? "manual" : "auto", initial);
531492

532-
RtlEnterCriticalSection( &shm_init_section );
533-
534-
ret = create_esync( type, handle, access, attr, initial );
535-
536-
if (!ret)
537-
{
538-
/* Initialize the shared memory portion. */
539-
struct esync *obj = get_cached_object( *handle );
540-
struct event *event = obj->shm;
541-
event->signaled = initial;
542-
event->locked = 0;
543-
}
544-
545-
RtlLeaveCriticalSection( &shm_init_section );
546-
547-
return ret;
493+
return create_esync( type, handle, access, attr, initial, 0 );
548494
}
549495

550496
NTSTATUS esync_open_event( HANDLE *handle, ACCESS_MASK access,
551497
const OBJECT_ATTRIBUTES *attr )
552498
{
553-
NTSTATUS ret;
554-
555499
TRACE("name %s.\n", debugstr_us(attr->ObjectName));
556500

557-
RtlEnterCriticalSection( &shm_init_section );
558-
ret = open_esync( ESYNC_AUTO_EVENT, handle, access, attr ); /* doesn't matter which */
559-
RtlLeaveCriticalSection( &shm_init_section );
560-
return ret;
501+
return open_esync( ESYNC_AUTO_EVENT, handle, access, attr ); /* doesn't matter which */
561502
}
562503

563504
static inline void small_pause(void)
@@ -740,39 +681,18 @@ NTSTATUS esync_query_event( HANDLE handle, EVENT_INFORMATION_CLASS class,
740681
NTSTATUS esync_create_mutex( HANDLE *handle, ACCESS_MASK access,
741682
const OBJECT_ATTRIBUTES *attr, BOOLEAN initial )
742683
{
743-
NTSTATUS ret;
744-
745684
TRACE("name %s, initial %d.\n",
746685
attr ? debugstr_us(attr->ObjectName) : "<no name>", initial);
747686

748-
RtlEnterCriticalSection( &shm_init_section );
749-
750-
ret = create_esync( ESYNC_MUTEX, handle, access, attr, initial ? 0 : 1 );
751-
if (!ret)
752-
{
753-
/* Initialize the shared memory portion. */
754-
struct esync *obj = get_cached_object( *handle );
755-
struct mutex *mutex = obj->shm;
756-
mutex->tid = initial ? GetCurrentThreadId() : 0;
757-
mutex->count = initial ? 1 : 0;
758-
}
759-
760-
RtlLeaveCriticalSection( &shm_init_section );
761-
762-
return ret;
687+
return create_esync( ESYNC_MUTEX, handle, access, attr, initial ? 0 : 1, 0 );
763688
}
764689

765690
NTSTATUS esync_open_mutex( HANDLE *handle, ACCESS_MASK access,
766691
const OBJECT_ATTRIBUTES *attr )
767692
{
768-
NTSTATUS ret;
769-
770693
TRACE("name %s.\n", debugstr_us(attr->ObjectName));
771694

772-
RtlEnterCriticalSection( &shm_init_section );
773-
ret = open_esync( ESYNC_MUTEX, handle, access, attr );
774-
RtlLeaveCriticalSection( &shm_init_section );
775-
return ret;
695+
return open_esync( ESYNC_MUTEX, handle, access, attr );
776696
}
777697

778698
NTSTATUS esync_release_mutex( HANDLE *handle, LONG *prev )

include/wine/server_protocol.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5944,7 +5944,9 @@ struct create_esync_request
59445944
unsigned int access;
59455945
int initval;
59465946
int type;
5947+
int max;
59475948
/* VARARG(objattr,object_attributes); */
5949+
char __pad_28[4];
59485950
};
59495951
struct create_esync_reply
59505952
{
@@ -6959,7 +6961,7 @@ union generic_reply
69596961

69606962
/* ### protocol_version begin ### */
69616963

6962-
#define SERVER_PROTOCOL_VERSION 625
6964+
#define SERVER_PROTOCOL_VERSION 626
69636965

69646966
/* ### protocol_version end ### */
69656967

server/esync.c

Lines changed: 90 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -178,8 +178,63 @@ static int type_matches( enum esync_type type1, enum esync_type type2 )
178178
(type2 == ESYNC_AUTO_EVENT || type2 == ESYNC_MANUAL_EVENT));
179179
}
180180

181+
static void *get_shm( unsigned int idx )
182+
{
183+
int entry = (idx * 8) / pagesize;
184+
int offset = (idx * 8) % pagesize;
185+
186+
if (entry >= shm_addrs_size)
187+
{
188+
if (!(shm_addrs = realloc( shm_addrs, (entry + 1) * sizeof(shm_addrs[0]) )))
189+
fprintf( stderr, "esync: couldn't expand shm_addrs array to size %d\n", entry + 1 );
190+
191+
memset( &shm_addrs[shm_addrs_size], 0, (entry + 1 - shm_addrs_size) * sizeof(shm_addrs[0]) );
192+
193+
shm_addrs_size = entry + 1;
194+
}
195+
196+
if (!shm_addrs[entry])
197+
{
198+
void *addr = mmap( NULL, pagesize, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, entry * pagesize );
199+
if (addr == (void *)-1)
200+
{
201+
fprintf( stderr, "esync: failed to map page %d (offset %#lx): ", entry, entry * pagesize );
202+
perror( "mmap" );
203+
}
204+
205+
if (debug_level)
206+
fprintf( stderr, "esync: Mapping page %d at %p.\n", entry, addr );
207+
208+
if (interlocked_cmpxchg_ptr( &shm_addrs[entry], addr, 0 ))
209+
munmap( addr, pagesize ); /* someone beat us to it */
210+
}
211+
212+
return (void *)((unsigned long)shm_addrs[entry] + offset);
213+
}
214+
215+
struct semaphore
216+
{
217+
int max;
218+
int count;
219+
};
220+
C_ASSERT(sizeof(struct semaphore) == 8);
221+
222+
struct mutex
223+
{
224+
DWORD tid;
225+
int count; /* recursion count */
226+
};
227+
C_ASSERT(sizeof(struct mutex) == 8);
228+
229+
struct event
230+
{
231+
int signaled;
232+
int locked;
233+
};
234+
C_ASSERT(sizeof(struct event) == 8);
235+
181236
static struct esync *create_esync( struct object *root, const struct unicode_str *name,
182-
unsigned int attr, int initval, enum esync_type type,
237+
unsigned int attr, int initval, int max, enum esync_type type,
183238
const struct security_descriptor *sd )
184239
{
185240
#ifdef HAVE_SYS_EVENTFD_H
@@ -219,6 +274,38 @@ static struct esync *create_esync( struct object *root, const struct unicode_str
219274
perror( "ftruncate" );
220275
}
221276
}
277+
278+
/* Initialize the shared memory portion. We want to do this on the
279+
* server side to avoid a potential though unlikely race whereby
280+
* the same object is opened and used between the time it's created
281+
* and the time its shared memory portion is initialized. */
282+
switch (type)
283+
{
284+
case ESYNC_SEMAPHORE:
285+
{
286+
struct semaphore *semaphore = get_shm( esync->shm_idx );
287+
semaphore->max = max;
288+
semaphore->count = initval;
289+
break;
290+
}
291+
case ESYNC_AUTO_EVENT:
292+
case ESYNC_MANUAL_EVENT:
293+
{
294+
struct event *event = get_shm( esync->shm_idx );
295+
event->signaled = initval ? 1 : 0;
296+
event->locked = 0;
297+
break;
298+
}
299+
case ESYNC_MUTEX:
300+
{
301+
struct mutex *mutex = get_shm( esync->shm_idx );
302+
mutex->tid = initval ? 0 : current->id;
303+
mutex->count = initval ? 0 : 1;
304+
break;
305+
}
306+
default:
307+
assert( 0 );
308+
}
222309
}
223310
else
224311
{
@@ -287,49 +374,6 @@ void esync_clear( int fd )
287374
read( fd, &value, sizeof(value) );
288375
}
289376

290-
/* Sadly, we need all of this infrastructure to keep the shm state in sync. */
291-
292-
static void *get_shm( unsigned int idx )
293-
{
294-
int entry = (idx * 8) / pagesize;
295-
int offset = (idx * 8) % pagesize;
296-
297-
if (entry >= shm_addrs_size)
298-
{
299-
if (!(shm_addrs = realloc( shm_addrs, (entry + 1) * sizeof(shm_addrs[0]) )))
300-
fprintf( stderr, "esync: couldn't expand shm_addrs array to size %d\n", entry + 1 );
301-
302-
memset( &shm_addrs[shm_addrs_size], 0, (entry + 1 - shm_addrs_size) * sizeof(shm_addrs[0]) );
303-
304-
shm_addrs_size = entry + 1;
305-
}
306-
307-
if (!shm_addrs[entry])
308-
{
309-
void *addr = mmap( NULL, pagesize, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, entry * pagesize );
310-
if (addr == (void *)-1)
311-
{
312-
fprintf( stderr, "esync: failed to map page %d (offset %#lx): ", entry, entry * pagesize );
313-
perror( "mmap" );
314-
}
315-
316-
if (debug_level)
317-
fprintf( stderr, "esync: Mapping page %d at %p.\n", entry, addr );
318-
319-
if (interlocked_cmpxchg_ptr( &shm_addrs[entry], addr, 0 ))
320-
munmap( addr, pagesize ); /* someone beat us to it */
321-
}
322-
323-
return (void *)((unsigned long)shm_addrs[entry] + offset);
324-
}
325-
326-
struct event
327-
{
328-
int signaled;
329-
int locked;
330-
};
331-
C_ASSERT(sizeof(struct event) == 8);
332-
333377
static inline void small_pause(void)
334378
{
335379
#ifdef __i386__
@@ -413,7 +457,8 @@ DECL_HANDLER(create_esync)
413457

414458
if (!objattr) return;
415459

416-
if ((esync = create_esync( root, &name, objattr->attributes, req->initval, req->type, sd )))
460+
if ((esync = create_esync( root, &name, objattr->attributes, req->initval,
461+
req->max, req->type, sd )))
417462
{
418463
if (get_error() == STATUS_OBJECT_NAME_EXISTS)
419464
reply->handle = alloc_handle( current->process, esync, req->access, objattr->attributes );

server/protocol.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4046,6 +4046,7 @@ struct handle_info
40464046
unsigned int access; /* wanted access rights */
40474047
int initval; /* initial value */
40484048
int type; /* type of esync object (see below) */
4049+
int max; /* maximum count on a semaphore */
40494050
VARARG(objattr,object_attributes); /* object attributes */
40504051
@REPLY
40514052
obj_handle_t handle; /* handle to the object */

server/request.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2513,7 +2513,8 @@ C_ASSERT( sizeof(struct get_system_info_reply) == 24 );
25132513
C_ASSERT( FIELD_OFFSET(struct create_esync_request, access) == 12 );
25142514
C_ASSERT( FIELD_OFFSET(struct create_esync_request, initval) == 16 );
25152515
C_ASSERT( FIELD_OFFSET(struct create_esync_request, type) == 20 );
2516-
C_ASSERT( sizeof(struct create_esync_request) == 24 );
2516+
C_ASSERT( FIELD_OFFSET(struct create_esync_request, max) == 24 );
2517+
C_ASSERT( sizeof(struct create_esync_request) == 32 );
25172518
C_ASSERT( FIELD_OFFSET(struct create_esync_reply, handle) == 8 );
25182519
C_ASSERT( FIELD_OFFSET(struct create_esync_reply, type) == 12 );
25192520
C_ASSERT( FIELD_OFFSET(struct create_esync_reply, shm_idx) == 16 );

server/trace.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4699,6 +4699,7 @@ static void dump_create_esync_request( const struct create_esync_request *req )
46994699
fprintf( stderr, " access=%08x", req->access );
47004700
fprintf( stderr, ", initval=%d", req->initval );
47014701
fprintf( stderr, ", type=%d", req->type );
4702+
fprintf( stderr, ", max=%d", req->max );
47024703
dump_varargs_object_attributes( ", objattr=", cur_size );
47034704
}
47044705

0 commit comments

Comments
 (0)