[rhythmbox] podcast: reference count RBPodcastChannel structures



commit 7d383c89a28ac214721ce18d9e2b69441364761e
Author: Jonathan Matthew <jonathan d14n org>
Date:   Tue Jan 26 18:34:33 2021 +1000

    podcast: reference count RBPodcastChannel structures
    
    Search results can be deleted from the podcast add dialog while still being
    parsed, which presents the opportunity for a use after free.  Rather than
    making the main thread wait for parsing to hit a cancellation point before
    freeing the data structure, it's easier to reference count it.
    
    Closes: #1846

 podcast/rb-podcast-add-dialog.c    | 15 ++++++++-------
 podcast/rb-podcast-manager.c       |  2 +-
 podcast/rb-podcast-parse.c         | 30 +++++++++++++++++++++++++-----
 podcast/rb-podcast-parse.h         |  6 +++++-
 podcast/rb-podcast-search-itunes.c |  2 +-
 5 files changed, 40 insertions(+), 15 deletions(-)
---
diff --git a/podcast/rb-podcast-add-dialog.c b/podcast/rb-podcast-add-dialog.c
index 0180b00b9..c46807410 100644
--- a/podcast/rb-podcast-add-dialog.c
+++ b/podcast/rb-podcast-add-dialog.c
@@ -125,7 +125,7 @@ remove_all_feeds_cb (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter,
 {
        RBPodcastChannel *channel;
        gtk_tree_model_get (model, iter, FEED_COLUMN_PARSED_FEED, &channel, -1);
-       rb_podcast_parse_channel_free (channel);
+       rb_podcast_parse_channel_unref (channel);
        return FALSE;
 }
 
@@ -304,7 +304,7 @@ parse_cb (RBPodcastChannel *channel, GError *error, gpointer user_data)
 
        if (data->reset_count != data->dialog->priv->reset_count) {
                rb_debug ("dialog reset while parsing");
-               rb_podcast_parse_channel_free (data->channel);
+               rb_podcast_parse_channel_unref (data->channel);
                g_object_unref (data->dialog);
                g_clear_error (&data->error);
                g_free (data);
@@ -327,14 +327,14 @@ parse_cb (RBPodcastChannel *channel, GError *error, gpointer user_data)
                        RBPodcastItem *item;
 
                        item = l->data;
-                       channel = g_new0 (RBPodcastChannel, 1);
+                       channel = rb_podcast_parse_channel_new ();
                        channel->url = g_strdup (item->url);
                        channel->title = g_strdup (item->title);
                        /* none of the other fields get populated anyway */
                        insert_search_result (data->dialog, channel, FALSE);
                }
                update_feed_status (data->dialog);
-               rb_podcast_parse_channel_free (data->channel);
+               rb_podcast_parse_channel_unref (data->channel);
        } else if (data->existing) {
                GtkTreeIter iter;
                gboolean found = FALSE;
@@ -355,7 +355,7 @@ parse_cb (RBPodcastChannel *channel, GError *error, gpointer user_data)
 
                /* if the row is selected, create entries for the channel contents */
                if (found == FALSE) {
-                       rb_podcast_parse_channel_free (data->channel);
+                       rb_podcast_parse_channel_unref (data->channel);
                } else if (data->dialog->priv->have_selection) {
                        GtkTreePath *a;
                        GtkTreePath *b;
@@ -375,6 +375,7 @@ parse_cb (RBPodcastChannel *channel, GError *error, gpointer user_data)
                update_feed_status (data->dialog);
        }
 
+       rb_podcast_parse_channel_unref (data->channel);
        g_object_unref (data->dialog);
        g_clear_error (&data->error);
        g_free (data);
@@ -386,7 +387,7 @@ parse_search_text (RBPodcastAddDialog *dialog, const char *text)
        ParseData *data;
        RBPodcastChannel *channel;
 
-       channel = g_new0 (RBPodcastChannel, 1);
+       channel = rb_podcast_parse_channel_new ();
        channel->url = g_strdup (text);
 
        data = g_new0 (ParseData, 1);
@@ -406,7 +407,7 @@ parse_search_result (RBPodcastAddDialog *dialog, RBPodcastChannel *channel)
 
        data = g_new0 (ParseData, 1);
        data->dialog = g_object_ref (dialog);
-       data->channel = channel;
+       data->channel = rb_podcast_parse_channel_ref (channel);
        data->existing = TRUE;
        data->single = FALSE;
        data->reset_count = dialog->priv->reset_count;
diff --git a/podcast/rb-podcast-manager.c b/podcast/rb-podcast-manager.c
index a34b2aaae..59c95a111 100644
--- a/podcast/rb-podcast-manager.c
+++ b/podcast/rb-podcast-manager.c
@@ -670,7 +670,7 @@ podcast_update_free (RBPodcastUpdate *update)
        g_object_unref (pd);
 
        g_clear_error (&update->error);
-       rb_podcast_parse_channel_free (update->channel);
+       rb_podcast_parse_channel_unref (update->channel);
        g_free (update);
 }
 
diff --git a/podcast/rb-podcast-parse.c b/podcast/rb-podcast-parse.c
index 25307d5bf..19b9f865a 100644
--- a/podcast/rb-podcast-parse.c
+++ b/podcast/rb-podcast-parse.c
@@ -37,6 +37,7 @@
 #include <glib/gprintf.h>
 
 #include "rb-debug.h"
+#include "rb-util.h"
 #include "rb-podcast-parse.h"
 #include "rb-file-helpers.h"
 
@@ -219,11 +220,19 @@ rb_podcast_parse_load_feed (RBPodcastChannel *channel,
        totem_pl_parser_parse_async (plparser, channel->url, FALSE, cancellable, parse_cb, data);
 }
 
+RBPodcastChannel *
+rb_podcast_parse_channel_new (void)
+{
+       RBPodcastChannel *data;
+       data = g_new0 (RBPodcastChannel, 1);
+       data->refcount = 1;
+       return data;
+}
+
 RBPodcastChannel *
 rb_podcast_parse_channel_copy (RBPodcastChannel *data)
 {
-       RBPodcastChannel *copy;
-       copy = g_new0 (RBPodcastChannel, 1);
+       RBPodcastChannel *copy = rb_podcast_parse_channel_new ();
        copy->url = g_strdup (data->url);
        copy->title = g_strdup (data->title);
        copy->lang = g_strdup (data->lang);
@@ -250,10 +259,22 @@ rb_podcast_parse_channel_copy (RBPodcastChannel *data)
        return copy;
 }
 
+RBPodcastChannel *
+rb_podcast_parse_channel_ref (RBPodcastChannel *data)
+{
+       data->refcount++;
+       return data;
+}
+
 void
-rb_podcast_parse_channel_free (RBPodcastChannel *data)
+rb_podcast_parse_channel_unref (RBPodcastChannel *data)
 {
        g_return_if_fail (data != NULL);
+       g_assert (rb_is_main_thread ());
+
+       if (--data->refcount > 0) {
+               return;
+       }
 
        g_list_foreach (data->posts, (GFunc) rb_podcast_parse_item_free, NULL);
        g_list_free (data->posts);
@@ -269,7 +290,6 @@ rb_podcast_parse_channel_free (RBPodcastChannel *data)
        g_free (data->copyright);
 
        g_free (data);
-       data = NULL;
 }
 
 RBPodcastItem *
@@ -307,7 +327,7 @@ rb_podcast_channel_get_type (void)
        if (G_UNLIKELY (type == 0)) {
                type = g_boxed_type_register_static ("RBPodcastChannel",
                                                     (GBoxedCopyFunc)rb_podcast_parse_channel_copy,
-                                                    (GBoxedFreeFunc)rb_podcast_parse_channel_free);
+                                                    (GBoxedFreeFunc)rb_podcast_parse_channel_unref);
        }
        return type;
 }
diff --git a/podcast/rb-podcast-parse.h b/podcast/rb-podcast-parse.h
index c2acc9613..31b564d4c 100644
--- a/podcast/rb-podcast-parse.h
+++ b/podcast/rb-podcast-parse.h
@@ -55,6 +55,8 @@ typedef struct
 
 typedef struct
 {
+       int refcount;
+
        char* url;
        char* title;
        char* lang;
@@ -83,9 +85,11 @@ void rb_podcast_parse_load_feed (RBPodcastChannel *data,
                                    RBPodcastParseCallback callback,
                                    gpointer user_data);
 
+RBPodcastChannel *rb_podcast_parse_channel_new (void);
 RBPodcastChannel *rb_podcast_parse_channel_copy (RBPodcastChannel *data);
 RBPodcastItem *rb_podcast_parse_item_copy (RBPodcastItem *data);
-void rb_podcast_parse_channel_free     (RBPodcastChannel *data);
+RBPodcastChannel *rb_podcast_parse_channel_ref (RBPodcastChannel *data);
+void rb_podcast_parse_channel_unref    (RBPodcastChannel *data);
 void rb_podcast_parse_item_free        (RBPodcastItem *data);
 
 #endif /* RB_PODCAST_PARSE_H */
diff --git a/podcast/rb-podcast-search-itunes.c b/podcast/rb-podcast-search-itunes.c
index bfae276c6..f66859a31 100644
--- a/podcast/rb-podcast-search-itunes.c
+++ b/podcast/rb-podcast-search-itunes.c
@@ -93,7 +93,7 @@ process_results (RBPodcastSearchITunes *search, JsonParser *parser)
 
                rb_debug ("got result %s (%s)", channel->title, channel->url);
                rb_podcast_search_result (RB_PODCAST_SEARCH (search), channel);
-               rb_podcast_parse_channel_free (channel);
+               rb_podcast_parse_channel_unref (channel);
        }
 }
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]