kernel: Fix unbalanced release of sync object in FD select race.

When a file descriptor is closed between being selected and adding the
select info to its IO context, the select info needs to be cleaned up.
This is done by deselect_select_infos() which unconditionally also put
the select_sync associated with the infos. In this special case we do
not yet hold a reference to the select_sync however, so avoid putting
the corresponding sync object.

Fixes #11098, #10763 and #10230.
This commit is contained in:
Michael Lotz 2014-10-25 12:23:37 +02:00
parent a88f2beb0f
commit 831abecd6a

View File

@ -41,7 +41,7 @@ static struct file_descriptor* get_fd_locked(struct io_context* context,
int fd);
static struct file_descriptor* remove_fd(struct io_context* context, int fd);
static void deselect_select_infos(file_descriptor* descriptor,
select_info* infos);
select_info* infos, bool putSyncObjects);
struct FDGetterLocking {
@ -367,7 +367,7 @@ remove_fd(struct io_context* context, int fd)
mutex_unlock(&context->io_mutex);
if (selectInfos != NULL)
deselect_select_infos(descriptor, selectInfos);
deselect_select_infos(descriptor, selectInfos, true);
return disconnected ? NULL : descriptor;
}
@ -457,7 +457,7 @@ dup2_fd(int oldfd, int newfd, bool kernel)
// Say bye bye to the evicted fd
if (evicted) {
deselect_select_infos(evicted, selectInfos);
deselect_select_infos(evicted, selectInfos, true);
close_fd(evicted);
put_fd(evicted);
}
@ -525,7 +525,8 @@ fd_ioctl(bool kernelFD, int fd, uint32 op, void* buffer, size_t length)
static void
deselect_select_infos(file_descriptor* descriptor, select_info* infos)
deselect_select_infos(file_descriptor* descriptor, select_info* infos,
bool putSyncObjects)
{
TRACE(("deselect_select_infos(%p, %p)\n", descriptor, infos));
@ -546,7 +547,9 @@ deselect_select_infos(file_descriptor* descriptor, select_info* infos)
notify_select_events(info, B_EVENT_INVALID);
info = info->next;
put_select_sync(sync);
if (putSyncObjects)
put_select_sync(sync);
}
}
@ -601,7 +604,7 @@ select_fd(int32 fd, struct select_info* info, bool kernel)
// Someone close()d the index in the meantime. deselect() all
// events.
info->next = NULL;
deselect_select_infos(descriptor, info);
deselect_select_infos(descriptor, info, false);
// Release our open reference of the descriptor.
close_fd(descriptor);