[Spice-devel] [PATCH spice-gtk 11/16] channel-main: Don't close the file_stream if callbacks are pending

Christophe Fergeau cfergeau at redhat.com
Tue Apr 9 03:18:24 PDT 2013


ACK..

Christophe

On Fri, Mar 29, 2013 at 12:40:23PM +0100, Hans de Goede wrote:
> If file_xfer_completed gets called while callbacks are pending we should
> not call g_input_stream_close_async, because:
> 1) Doing so while another operation is pending on the file_stream will fail
> 2) Doing so while a non file_stream callback is pending may result in the
> task being freed before the callback runs, resulting in a use after free.
> 
> This patch fixes this by setting a pending flag when any callbacks are
> scheduled, and clearing it when it runs + checking for task->error
> from all callbacks and if that is set call file_xfer_completed again.
> 
> Signed-off-by: Hans de Goede <hdegoede at redhat.com>
> ---
>  gtk/channel-main.c | 39 ++++++++++++++++++++++++++++++---------
>  1 file changed, 30 insertions(+), 9 deletions(-)
> 
> diff --git a/gtk/channel-main.c b/gtk/channel-main.c
> index b0d60eb..3222591 100644
> --- a/gtk/channel-main.c
> +++ b/gtk/channel-main.c
> @@ -55,6 +55,7 @@ typedef struct spice_migrate spice_migrate;
>  #define FILE_XFER_CHUNK_SIZE (VD_AGENT_MAX_DATA_SIZE * 32)
>  typedef struct SpiceFileXferTask {
>      uint32_t                       id;
> +    gboolean                       pending;
>      GFile                          *file;
>      SpiceMainChannel               *channel;
>      GFileInputStream               *file_stream;
> @@ -1600,9 +1601,9 @@ static void file_xfer_data_flushed_cb(GObject *source_object,
>      SpiceMainChannel *channel = (SpiceMainChannel *)source_object;
>      GError *error = NULL;
>  
> +    task->pending = FALSE;
>      file_xfer_flush_finish(channel, res, &error);
> -
> -    if (error != NULL) {
> +    if (error || task->error) {
>          file_xfer_completed(task, error);
>          return;
>      }
> @@ -1638,13 +1639,21 @@ static void file_xfer_read_cb(GObject *source_object,
>      gssize count;
>      GError *error = NULL;
>  
> +    task->pending = FALSE;
>      count = g_input_stream_read_finish(G_INPUT_STREAM(task->file_stream),
>                                         res, &error);
> +    /* Check for pending earlier errors */
> +    if (task->error) {
> +        file_xfer_completed(task, error);
> +        return;
> +    }
> +
>      if (count > 0) {
>          task->read_bytes += count;
>          file_xfer_queue(task, count);
>          file_xfer_flush_async(channel, task->cancellable,
>                                file_xfer_data_flushed_cb, task);
> +        task->pending = TRUE;
>      } else if (error) {
>          VDAgentFileXferStatusMessage msg = {
>              .id = task->id,
> @@ -1668,6 +1677,7 @@ static void file_xfer_continue_read(SpiceFileXferTask *task)
>                                task->cancellable,
>                                file_xfer_read_cb,
>                                task);
> +    task->pending = TRUE;
>  }
>  
>  /* coroutine context */
> @@ -2568,16 +2578,24 @@ void spice_main_set_display_enabled(SpiceMainChannel *channel, int id, gboolean
>  
>  static void file_xfer_completed(SpiceFileXferTask *task, GError *error)
>  {
> +    /* In case of multiple errors we only report the first error */
> +    if (task->error)
> +        g_clear_error(&error);
>      if (error) {
>          SPICE_DEBUG("File %s xfer failed: %s",
>                      g_file_get_path(task->file), error->message);
>          task->error = error;
>      }
> +
> +    if (task->pending)
> +        return;
> +
>      g_input_stream_close_async(G_INPUT_STREAM(task->file_stream),
>                                 G_PRIORITY_DEFAULT,
>                                 task->cancellable,
>                                 file_xfer_close_cb,
>                                 task);
> +    task->pending = TRUE;
>  }
>  
>  static void file_xfer_info_async_cb(GObject *obj, GAsyncResult *res, gpointer data)
> @@ -2591,10 +2609,10 @@ static void file_xfer_info_async_cb(GObject *obj, GAsyncResult *res, gpointer da
>      gsize /*msg_size*/ data_len;
>      gchar *string;
>      SpiceFileXferTask *task = (SpiceFileXferTask *)data;
> -    SpiceMainChannelPrivate *c = task->channel->priv;
>  
> +    task->pending = FALSE;
>      info = g_file_query_info_finish(file, res, &error);
> -    if (error)
> +    if (error || task->error)
>          goto failed;
>  
>      task->file_size =
> @@ -2616,9 +2634,6 @@ static void file_xfer_info_async_cb(GObject *obj, GAsyncResult *res, gpointer da
>          goto failed;
>  
>      /* Create file-xfer start message */
> -    CHANNEL_DEBUG(task->channel, "Insert a xfer task:%d to task list", task->id);
> -    c->file_xfer_task_list = g_list_append(c->file_xfer_task_list, task);
> -
>      msg.id = task->id;
>      agent_msg_queue_many(task->channel, VD_AGENT_FILE_XFER_START,
>                           &msg, sizeof(msg),
> @@ -2637,8 +2652,9 @@ static void file_xfer_read_async_cb(GObject *obj, GAsyncResult *res, gpointer da
>      SpiceFileXferTask *task = (SpiceFileXferTask *)data;
>      GError *error = NULL;
>  
> +    task->pending = FALSE;
>      task->file_stream = g_file_read_finish(file, res, &error);
> -    if (error) {
> +    if (error || task->error) {
>          file_xfer_completed(task, error);
>          return;
>      }
> @@ -2650,6 +2666,7 @@ static void file_xfer_read_async_cb(GObject *obj, GAsyncResult *res, gpointer da
>                              task->cancellable,
>                              file_xfer_info_async_cb,
>                              task);
> +    task->pending = TRUE;
>  }
>  
>  static void file_xfer_send_start_msg_async(SpiceMainChannel *channel,
> @@ -2661,6 +2678,7 @@ static void file_xfer_send_start_msg_async(SpiceMainChannel *channel,
>                                             GAsyncReadyCallback callback,
>                                             gpointer user_data)
>  {
> +    SpiceMainChannelPrivate *c = channel->priv;
>      SpiceFileXferTask *task;
>      static uint32_t xfer_id;    /* Used to identify task id */
>  
> @@ -2675,12 +2693,15 @@ static void file_xfer_send_start_msg_async(SpiceMainChannel *channel,
>      task->callback = callback;
>      task->user_data = user_data;
>  
> +    CHANNEL_DEBUG(task->channel, "Insert a xfer task:%d to task list", task->id);
> +    c->file_xfer_task_list = g_list_append(c->file_xfer_task_list, task);
> +
>      g_file_read_async(file,
>                        G_PRIORITY_DEFAULT,
>                        cancellable,
>                        file_xfer_read_async_cb,
>                        task);
> -
> +    task->pending = TRUE;
>  }
>  
>  /**
> -- 
> 1.8.1.4
> 
> _______________________________________________
> Spice-devel mailing list
> Spice-devel at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/spice-devel
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 198 bytes
Desc: not available
URL: <http://lists.freedesktop.org/archives/spice-devel/attachments/20130409/bc31cb27/attachment.pgp>


More information about the Spice-devel mailing list