Always remove, invalidate and free watches before closing watched sockets

This should mean we don't get invalid fds in the main loop.

The BSD (kqueue) and Windows code paths are untested, but follow the same
patterns as the tested Linux/generic Unix versions.

DBusTransportSocket was already OK (it called free_watches() before
_dbus_close_socket, and that did the remove, invalidate, unref dance).

Bug: https://bugs.freedesktop.org/show_bug.cgi?id=33336
Reviewed-by: Will Thompson <will.thompson@collabora.co.uk>
Reviewed-by: Thiago Macieira <thiago@kde.org>
This commit is contained in:
Simon McVittie 2011-01-21 18:51:27 +00:00
parent a2e330980d
commit 58f968a2cc
8 changed files with 124 additions and 78 deletions

View file

@ -204,16 +204,18 @@ _shutdown_inotify (void *data)
_set_watched_dirs_internal (&empty);
close (inotify_fd);
inotify_fd = -1;
if (watch != NULL)
{
_dbus_loop_remove_watch (loop, watch, _inotify_watch_callback, NULL);
_dbus_watch_invalidate (watch);
_dbus_watch_unref (watch);
_dbus_loop_unref (loop);
}
watch = NULL;
loop = NULL;
close (inotify_fd);
inotify_fd = -1;
}
static int

View file

@ -81,6 +81,7 @@ _handle_kqueue_watch (DBusWatch *watch, unsigned int flags, void *data)
if (watch != NULL)
{
_dbus_loop_remove_watch (loop, watch, _kqueue_watch_callback, NULL);
_dbus_watch_invalidate (watch);
_dbus_watch_unref (watch);
watch = NULL;
}
@ -124,10 +125,11 @@ _init_kqueue (BusContext *context)
NULL, NULL))
{
_dbus_warn ("Unable to add reload watch to main loop");
close (kq);
kq = -1;
_dbus_watch_invalidate (watch);
_dbus_watch_unref (watch);
watch = NULL;
close (kq);
kq = -1;
goto out;
}
}

View file

@ -43,7 +43,8 @@ static int reload_pipe[2];
#define RELOAD_READ_END 0
#define RELOAD_WRITE_END 1
static void close_reload_pipe (void);
static void close_reload_pipe (DBusWatch **);
static void close_reload_pipe_write (void);
static void
signal_handler (int sig)
@ -64,7 +65,7 @@ signal_handler (int sig)
!_dbus_write_socket (reload_pipe[RELOAD_WRITE_END], &str, 0, 1))
{
_dbus_warn ("Unable to write to reload pipe.\n");
close_reload_pipe ();
close_reload_pipe_write ();
}
}
break;
@ -178,7 +179,7 @@ handle_reload_watch (DBusWatch *watch,
_dbus_read_socket (reload_pipe[RELOAD_READ_END], &str, 1) != 1)
{
_dbus_warn ("Couldn't read from reload pipe.\n");
close_reload_pipe ();
close_reload_pipe (&watch);
return TRUE;
}
_dbus_string_free (&str);
@ -249,11 +250,24 @@ setup_reload_pipe (DBusLoop *loop)
}
static void
close_reload_pipe (void)
close_reload_pipe (DBusWatch **watch)
{
_dbus_loop_remove_watch (bus_context_get_loop (context),
*watch, reload_watch_callback, NULL);
_dbus_watch_invalidate (*watch);
_dbus_watch_unref (*watch);
*watch = NULL;
_dbus_close_socket (reload_pipe[RELOAD_READ_END], NULL);
reload_pipe[RELOAD_READ_END] = -1;
close_reload_pipe_write ();
}
/* this is the only bit that's safe to do from an async signal handler */
static void
close_reload_pipe_write (void)
{
_dbus_close_socket (reload_pipe[RELOAD_WRITE_END], NULL);
reload_pipe[RELOAD_WRITE_END] = -1;
}

View file

@ -832,11 +832,14 @@ _dbus_loop_iterate (DBusLoop *loop,
if (_DBUS_UNLIKELY (fds[i].revents & _DBUS_POLLNVAL))
{
DBusWatch *watch = _dbus_watch_ref (wcb->watch);
_dbus_warn ("invalid request, socket fd %d not open\n",
fds[i].fd);
_dbus_watch_invalidate (wcb->watch);
_dbus_loop_remove_watch (loop, wcb->watch, wcb->function,
_dbus_loop_remove_watch (loop, watch, wcb->function,
((Callback *)wcb)->data);
_dbus_watch_invalidate (watch);
_dbus_watch_unref (watch);
}
}

View file

@ -236,6 +236,7 @@ socket_disconnect (DBusServer *server)
{
_dbus_server_remove_watch (server,
socket_server->watch[i]);
_dbus_watch_invalidate (socket_server->watch[i]);
_dbus_watch_unref (socket_server->watch[i]);
socket_server->watch[i] = NULL;
}

View file

@ -154,6 +154,27 @@ _dbus_babysitter_ref (DBusBabysitter *sitter)
return sitter;
}
static void
close_socket_to_babysitter (DBusBabysitter *sitter)
{
_dbus_verbose ("Closing babysitter\n");
if (sitter->sitter_watch != NULL)
{
_dbus_assert (sitter->watches != NULL);
_dbus_watch_list_remove_watch (sitter->watches, sitter->sitter_watch);
_dbus_watch_invalidate (sitter->sitter_watch);
_dbus_watch_unref (sitter->sitter_watch);
sitter->sitter_watch = NULL;
}
if (sitter->socket_to_babysitter != -1)
{
_dbus_close_socket (sitter->socket_to_babysitter, NULL);
sitter->socket_to_babysitter = -1;
}
}
/**
* Decrement the reference count on the babysitter object.
*
@ -172,11 +193,7 @@ _dbus_babysitter_unref (DBusBabysitter *sitter)
if (sitter->refcount == 0)
{
if (sitter->socket_to_babysitter != -1)
{
_dbus_close_socket (sitter->socket_to_babysitter, NULL);
sitter->socket_to_babysitter = -1;
}
close_socket_to_babysitter (sitter);
if (sitter->socket_to_main != -1)
{
@ -372,9 +389,8 @@ handle_watch (DBusWatch *watch,
*/
PING();
_dbus_close_socket (sitter->socket_to_babysitter, NULL);
close_socket_to_babysitter (sitter);
PING();
sitter->socket_to_babysitter = -1;
return TRUE;
}
@ -668,6 +684,12 @@ _dbus_spawn_async_with_babysitter (DBusBabysitter **sitter_p,
PING();
if (!_dbus_watch_list_add_watch (sitter->watches, sitter->sitter_watch))
{
/* we need to free it early so the destructor won't try to remove it
* without it having been added, which DBusLoop doesn't allow */
_dbus_watch_invalidate (sitter->sitter_watch);
_dbus_watch_unref (sitter->sitter_watch);
sitter->sitter_watch = NULL;
_DBUS_SET_OOM (error);
goto out0;
}

View file

@ -257,6 +257,9 @@ _dbus_babysitter_ref (DBusBabysitter *sitter)
return sitter;
}
static void close_socket_to_babysitter (DBusBabysitter *sitter);
static void close_error_pipe_from_child (DBusBabysitter *sitter);
/**
* Decrement the reference count on the babysitter object.
* When the reference count of the babysitter object reaches
@ -273,25 +276,17 @@ _dbus_babysitter_unref (DBusBabysitter *sitter)
sitter->refcount -= 1;
if (sitter->refcount == 0)
{
if (sitter->socket_to_babysitter >= 0)
{
/* If we haven't forked other babysitters
* since this babysitter and socket were
* created then this close will cause the
* babysitter to wake up from poll with
* a hangup and then the babysitter will
* quit itself.
*/
_dbus_close_socket (sitter->socket_to_babysitter, NULL);
sitter->socket_to_babysitter = -1;
}
{
/* If we haven't forked other babysitters
* since this babysitter and socket were
* created then this close will cause the
* babysitter to wake up from poll with
* a hangup and then the babysitter will
* quit itself.
*/
close_socket_to_babysitter (sitter);
if (sitter->error_pipe_from_child >= 0)
{
_dbus_close_socket (sitter->error_pipe_from_child, NULL);
sitter->error_pipe_from_child = -1;
}
close_error_pipe_from_child (sitter);
if (sitter->sitter_pid > 0)
{
@ -341,21 +336,7 @@ _dbus_babysitter_unref (DBusBabysitter *sitter)
sitter->sitter_pid = -1;
}
if (sitter->error_watch)
{
_dbus_watch_invalidate (sitter->error_watch);
_dbus_watch_unref (sitter->error_watch);
sitter->error_watch = NULL;
}
if (sitter->sitter_watch)
{
_dbus_watch_invalidate (sitter->sitter_watch);
_dbus_watch_unref (sitter->sitter_watch);
sitter->sitter_watch = NULL;
}
if (sitter->watches)
_dbus_watch_list_free (sitter->watches);
@ -476,16 +457,42 @@ static void
close_socket_to_babysitter (DBusBabysitter *sitter)
{
_dbus_verbose ("Closing babysitter\n");
_dbus_close_socket (sitter->socket_to_babysitter, NULL);
sitter->socket_to_babysitter = -1;
if (sitter->sitter_watch != NULL)
{
_dbus_assert (sitter->watches != NULL);
_dbus_watch_list_remove_watch (sitter->watches, sitter->sitter_watch);
_dbus_watch_invalidate (sitter->sitter_watch);
_dbus_watch_unref (sitter->sitter_watch);
sitter->sitter_watch = NULL;
}
if (sitter->socket_to_babysitter >= 0)
{
_dbus_close_socket (sitter->socket_to_babysitter, NULL);
sitter->socket_to_babysitter = -1;
}
}
static void
close_error_pipe_from_child (DBusBabysitter *sitter)
{
_dbus_verbose ("Closing child error\n");
_dbus_close_socket (sitter->error_pipe_from_child, NULL);
sitter->error_pipe_from_child = -1;
if (sitter->error_watch != NULL)
{
_dbus_assert (sitter->watches != NULL);
_dbus_watch_list_remove_watch (sitter->watches, sitter->error_watch);
_dbus_watch_invalidate (sitter->error_watch);
_dbus_watch_unref (sitter->error_watch);
sitter->error_watch = NULL;
}
if (sitter->error_pipe_from_child >= 0)
{
_dbus_close_socket (sitter->error_pipe_from_child, NULL);
sitter->error_pipe_from_child = -1;
}
}
static void
@ -752,7 +759,7 @@ handle_watch (DBusWatch *watch,
unsigned int condition,
void *data)
{
DBusBabysitter *sitter = data;
DBusBabysitter *sitter = _dbus_babysitter_ref (data);
int revents;
int fd;
@ -775,31 +782,12 @@ handle_watch (DBusWatch *watch,
babysitter_iteration (sitter, FALSE))
;
/* Those might have closed the sockets we're watching. Before returning
* to the main loop, we must sort that out. */
if (sitter->error_watch != NULL && sitter->error_pipe_from_child == -1)
{
_dbus_watch_invalidate (sitter->error_watch);
if (sitter->watches != NULL)
_dbus_watch_list_remove_watch (sitter->watches, sitter->error_watch);
_dbus_watch_unref (sitter->error_watch);
sitter->error_watch = NULL;
}
if (sitter->sitter_watch != NULL && sitter->socket_to_babysitter == -1)
{
_dbus_watch_invalidate (sitter->sitter_watch);
if (sitter->watches != NULL)
_dbus_watch_list_remove_watch (sitter->watches, sitter->sitter_watch);
_dbus_watch_unref (sitter->sitter_watch);
sitter->sitter_watch = NULL;
}
/* fd.o #32992: if the handle_* methods closed their sockets, they previously
* didn't always remove the watches. Check that we don't regress. */
_dbus_assert (sitter->socket_to_babysitter != -1 || sitter->sitter_watch == NULL);
_dbus_assert (sitter->error_pipe_from_child != -1 || sitter->error_watch == NULL);
_dbus_babysitter_unref (sitter);
return TRUE;
}
@ -1189,6 +1177,12 @@ _dbus_spawn_async_with_babysitter (DBusBabysitter **sitter_p,
if (!_dbus_watch_list_add_watch (sitter->watches, sitter->error_watch))
{
/* we need to free it early so the destructor won't try to remove it
* without it having been added, which DBusLoop doesn't allow */
_dbus_watch_invalidate (sitter->error_watch);
_dbus_watch_unref (sitter->error_watch);
sitter->error_watch = NULL;
dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
goto cleanup_and_fail;
}
@ -1204,6 +1198,12 @@ _dbus_spawn_async_with_babysitter (DBusBabysitter **sitter_p,
if (!_dbus_watch_list_add_watch (sitter->watches, sitter->sitter_watch))
{
/* we need to free it early so the destructor won't try to remove it
* without it having been added, which DBusLoop doesn't allow */
_dbus_watch_invalidate (sitter->sitter_watch);
_dbus_watch_unref (sitter->sitter_watch);
sitter->sitter_watch = NULL;
dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
goto cleanup_and_fail;
}

View file

@ -1278,8 +1278,10 @@ _dbus_transport_new_for_socket (int fd,
return (DBusTransport*) socket_transport;
failed_4:
_dbus_watch_invalidate (socket_transport->read_watch);
_dbus_watch_unref (socket_transport->read_watch);
failed_3:
_dbus_watch_invalidate (socket_transport->write_watch);
_dbus_watch_unref (socket_transport->write_watch);
failed_2:
_dbus_string_free (&socket_transport->encoded_incoming);