remote-desktop: Check pipe fd before assuming existing read() operation

Currently, if g-r-d closes the read end of the pipe for a
SelectionRead() operation, due to realizing that the application, that
should provide the mime type content, does not provide any content,
mutter won't notice that and still assumes that the read() operation
on the pipe in g-r-d is still happening, as mutter never writes to the
pipe in that situation and therefore cannot realize that the pipe is
already closed.
The effect of this is, that if g-r-d aborts a read() operation and
requests a new read() operation via SelectionRead(), mutter will deny
the request since it assumes that the previous read() operation is
still ongoing.

Fix this behaviour by also checking the pipe fd in mutter before
denying a SelectionRead() request.

https://gitlab.gnome.org/GNOME/gnome-remote-desktop/-/issues/60

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1874>
This commit is contained in:
Pascal Nowack 2021-05-25 11:07:40 +02:00 committed by Marge Bot
parent 6c19d39d8b
commit 20db6af4e6

View File

@ -1467,6 +1467,47 @@ handle_selection_write_done (MetaDBusRemoteDesktopSession *skeleton,
return TRUE; return TRUE;
} }
static gboolean
is_pipe_broken (int fd)
{
GPollFD poll_fd = {0};
int poll_ret;
int errsv;
poll_fd.fd = fd;
poll_fd.events = G_IO_OUT;
do
{
poll_ret = g_poll (&poll_fd, 1, 0);
errsv = errno;
}
while (poll_ret == -1 && errsv == EINTR);
if (poll_ret < 0)
return FALSE;
return !!(poll_fd.revents & G_IO_ERR);
}
static gboolean
has_pending_read_operation (SelectionReadData *read_data)
{
int fd;
if (!read_data)
return FALSE;
fd = g_unix_output_stream_get_fd (G_UNIX_OUTPUT_STREAM (read_data->stream));
if (is_pipe_broken (fd))
{
cancel_selection_read (read_data->session);
return FALSE;
}
return TRUE;
}
static void static void
transfer_cb (MetaSelection *selection, transfer_cb (MetaSelection *selection,
GAsyncResult *res, GAsyncResult *res,
@ -1544,7 +1585,7 @@ handle_selection_read (MetaDBusRemoteDesktopSession *skeleton,
return TRUE; return TRUE;
} }
if (session->read_data) if (has_pending_read_operation (session->read_data))
{ {
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
G_DBUS_ERROR_LIMITS_EXCEEDED, G_DBUS_ERROR_LIMITS_EXCEEDED,