From 9981c81f8b726e3a028ec93e556dbe42e92ba528 Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Tue, 29 Apr 2008 23:54:35 +0000 Subject: [PATCH] Fixed a race condition between select_fd() and close(). Since select_fd() first added the select info to the IO context and then called select() on the descriptor, a close() called at the same time could already deselect the events and close the descriptor before select_fd() was done with select(). select_fd() does now keep an own open reference while selecting the events and add the select info afterwards to the IO context (if the FD is still current). git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@25266 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/system/kernel/fs/fd.cpp | 44 ++++++++++++++++++++++++++++--------- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/src/system/kernel/fs/fd.cpp b/src/system/kernel/fs/fd.cpp index ce960e184b..fe50bc2827 100644 --- a/src/system/kernel/fs/fd.cpp +++ b/src/system/kernel/fs/fd.cpp @@ -505,13 +505,9 @@ select_fd(int32 fd, struct select_info* info, bool kernel) return notify_select_events(info, info->selected_events); } - // add the info to the IO context - info->next = context->select_infos[fd]; - context->select_infos[fd] = info; - - // as long as the info is in the list, we keep a reference to the sync - // object - atomic_add(&info->sync->ref_count, 1); + // We need the FD to stay open while we're doing this, so no select()/ + // deselect() will be called on it after it is closed. + atomic_add(&descriptor->open_count, 1); locker.Unlock(); @@ -527,9 +523,37 @@ select_fd(int32 fd, struct select_info* info, bool kernel) } info->selected_events = selectedEvents; - // if nothing has been selected, we deselect immediately - if (selectedEvents == 0) - deselect_fd(fd, info, kernel); + // If any event has been selected, add the info to the IO context. + if (selectedEvents != 0) { + locker.Lock(); + if (context->fds[fd] != descriptor) { + // Someone close()d the index in the meantime. deselect() all + // events. + info->next = NULL; + deselect_select_infos(descriptor, info); + + // Release our open reference of the descriptor. + close_fd(descriptor); + return B_FILE_ERROR; + } + + // The FD index hasn't changed, so we add the select info to the table. + + info->next = context->select_infos[fd]; + context->select_infos[fd] = info; + + // As long as the info is in the list, we keep a reference to the sync + // object. + atomic_add(&info->sync->ref_count, 1); + + // Finally release our open reference. It is safe just to decrement, + // since as long as the descriptor is associated with the slot, + // someone else still has it open. + atomic_add(&descriptor->open_count, -1); + } else { + // Release our open reference. + close_fd(descriptor); + } return B_OK; }