Skip to content

Commit

Permalink
fsevents: fix race on simultaneous init+close
Browse files Browse the repository at this point in the history
When `uv_fsevents_t` handle is closed immediately after initializing,
there is a possibility that the `CFRunLoop`'s thread will process both
of these events at the same time. `uv__is_active(handle)` will return
`0`, and the `uv_close()` semaphore will be unblocked, leading
to the use after free in node.js.

See: nodejs/node#4091
  • Loading branch information
indutny committed Dec 2, 2015
1 parent f5796d2 commit 6b059f5
Showing 1 changed file with 15 additions and 8 deletions.
23 changes: 15 additions & 8 deletions src/unix/fsevents.c
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ typedef struct uv__cf_loop_state_s uv__cf_loop_state_t;
struct uv__cf_loop_signal_s {
QUEUE member;
uv_fs_event_t* handle;
unsigned int closing:1;
};

struct uv__fsevents_event_s {
Expand All @@ -98,7 +99,9 @@ struct uv__cf_loop_state_s {
/* Forward declarations */
static void uv__cf_loop_cb(void* arg);
static void* uv__cf_loop_runner(void* arg);
static int uv__cf_loop_signal(uv_loop_t* loop, uv_fs_event_t* handle);
static int uv__cf_loop_signal(uv_loop_t* loop,
uv_fs_event_t* handle,
unsigned int closing);

/* Lazy-loaded by uv__fsevents_global_init(). */
static CFArrayRef (*pCFArrayCreate)(CFAllocatorRef,
Expand Down Expand Up @@ -383,7 +386,8 @@ static void uv__fsevents_destroy_stream(uv_loop_t* loop) {


/* Runs in CF thread, when there're new fsevent handles to add to stream */
static void uv__fsevents_reschedule(uv_fs_event_t* handle) {
static void uv__fsevents_reschedule(uv_fs_event_t* handle,
unsigned int closing) {
uv__cf_loop_state_t* state;
QUEUE* q;
uv_fs_event_t* curr;
Expand Down Expand Up @@ -482,7 +486,7 @@ static void uv__fsevents_reschedule(uv_fs_event_t* handle) {
*
* NOTE: This is coupled with `uv_sem_wait()` in `uv__fsevents_close`
*/
if (!uv__is_active(handle))
if (closing)
uv_sem_post(&state->fsevent_sem);
}

Expand Down Expand Up @@ -672,7 +676,7 @@ void uv__fsevents_loop_delete(uv_loop_t* loop) {
if (loop->cf_state == NULL)
return;

if (uv__cf_loop_signal(loop, NULL) != 0)
if (uv__cf_loop_signal(loop, NULL, 0) != 0)
abort();

uv_thread_join(&loop->cf_thread);
Expand Down Expand Up @@ -746,15 +750,17 @@ static void uv__cf_loop_cb(void* arg) {
if (s->handle == NULL)
pCFRunLoopStop(state->loop);
else
uv__fsevents_reschedule(s->handle);
uv__fsevents_reschedule(s->handle, s->closing);

uv__free(s);
}
}


/* Runs in UV loop to notify CF thread */
int uv__cf_loop_signal(uv_loop_t* loop, uv_fs_event_t* handle) {
int uv__cf_loop_signal(uv_loop_t* loop,
uv_fs_event_t* handle,
unsigned int closing) {
uv__cf_loop_signal_t* item;
uv__cf_loop_state_t* state;

Expand All @@ -763,6 +769,7 @@ int uv__cf_loop_signal(uv_loop_t* loop, uv_fs_event_t* handle) {
return -ENOMEM;

item->handle = handle;
item->closing = closing;

uv_mutex_lock(&loop->cf_mutex);
QUEUE_INSERT_TAIL(&loop->cf_signals, &item->member);
Expand Down Expand Up @@ -825,7 +832,7 @@ int uv__fsevents_init(uv_fs_event_t* handle) {

/* Reschedule FSEventStream */
assert(handle != NULL);
err = uv__cf_loop_signal(handle->loop, handle);
err = uv__cf_loop_signal(handle->loop, handle, 0);
if (err)
goto fail_loop_signal;

Expand Down Expand Up @@ -865,7 +872,7 @@ int uv__fsevents_close(uv_fs_event_t* handle) {

/* Reschedule FSEventStream */
assert(handle != NULL);
err = uv__cf_loop_signal(handle->loop, handle);
err = uv__cf_loop_signal(handle->loop, handle, 1);
if (err)
return -err;

Expand Down

0 comments on commit 6b059f5

Please sign in to comment.