webrtcdatachannel: Bind to parent webrtcbin using a weak reference
authorPhilippe Normand <philn@igalia.com>
Sun, 12 Mar 2023 14:55:22 +0000 (14:55 +0000)
committerTim-Philipp Müller <tim@centricular.com>
Tue, 9 May 2023 07:54:52 +0000 (08:54 +0100)
The previous approach of using a simple pointer could lead to a use-after-free
in case a data-channel was created and its parent webrtcbin was disposed soon
after.

Fixes #2103

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4580>

subprojects/gst-plugins-bad/ext/webrtc/gstwebrtcbin.c
subprojects/gst-plugins-bad/ext/webrtc/webrtcdatachannel.c
subprojects/gst-plugins-bad/ext/webrtc/webrtcdatachannel.h

index 8929fef..d230a44 100644 (file)
@@ -2543,7 +2543,7 @@ _on_sctpdec_pad_added (GstElement * sctpdec, GstPad * pad,
   if (!channel) {
     channel = g_object_new (WEBRTC_TYPE_DATA_CHANNEL, NULL);
     channel->parent.id = stream_id;
-    channel->webrtcbin = webrtc;
+    webrtc_data_channel_set_webrtcbin (channel, webrtc);
 
     g_signal_emit (webrtc, gst_webrtc_bin_signals[PREPARE_DATA_CHANNEL_SIGNAL],
         0, channel, FALSE);
@@ -7166,7 +7166,7 @@ gst_webrtc_bin_create_data_channel (GstWebRTCBin * webrtc, const gchar * label,
   gst_element_sync_state_with_parent (ret->sink_bin);
 
   ret = gst_object_ref (ret);
-  ret->webrtcbin = webrtc;
+  webrtc_data_channel_set_webrtcbin (ret, webrtc);
   g_ptr_array_add (webrtc->priv->data_channels, ret);
   DC_UNLOCK (webrtc);
 
index 1ca7868..85e8d23 100644 (file)
@@ -51,6 +51,7 @@ typedef void (*ChannelTask) (GstWebRTCDataChannel * channel,
 
 struct task
 {
+  GstWebRTCBin *webrtcbin;
   GstWebRTCDataChannel *channel;
   ChannelTask func;
   gpointer user_data;
@@ -69,6 +70,7 @@ _execute_task (GstWebRTCBin * webrtc, struct task *task)
 static void
 _free_task (struct task *task)
 {
+  g_object_unref (task->webrtcbin);
   gst_object_unref (task->channel);
 
   if (task->notify)
@@ -80,14 +82,22 @@ static void
 _channel_enqueue_task (WebRTCDataChannel * channel, ChannelTask func,
     gpointer user_data, GDestroyNotify notify)
 {
-  struct task *task = g_new0 (struct task, 1);
+  GstWebRTCBin *webrtcbin = NULL;
+  struct task *task = NULL;
 
+  webrtcbin = g_weak_ref_get (&channel->webrtcbin_weak);
+  if (!webrtcbin)
+    return;
+
+  task = g_new0 (struct task, 1);
+
+  task->webrtcbin = webrtcbin;
   task->channel = gst_object_ref (channel);
   task->func = func;
   task->user_data = user_data;
   task->notify = notify;
 
-  gst_webrtc_bin_enqueue_task (channel->webrtcbin,
+  gst_webrtc_bin_enqueue_task (task->webrtcbin,
       (GstWebRTCBinFunc) _execute_task, task, (GDestroyNotify) _free_task,
       NULL);
 }
@@ -1130,6 +1140,8 @@ gst_webrtc_data_channel_finalize (GObject * object)
   g_clear_object (&channel->appsrc);
   g_clear_object (&channel->appsink);
 
+  g_weak_ref_clear (&channel->webrtcbin_weak);
+
   G_OBJECT_CLASS (parent_class)->finalize (object);
 }
 
@@ -1155,6 +1167,8 @@ webrtc_data_channel_init (WebRTCDataChannel * channel)
   G_LOCK (outstanding_channels_lock);
   outstanding_channels = g_list_prepend (outstanding_channels, channel);
   G_UNLOCK (outstanding_channels_lock);
+
+  g_weak_ref_init (&channel->webrtcbin_weak, NULL);
 }
 
 static void
@@ -1204,3 +1218,10 @@ webrtc_data_channel_link_to_sctp (WebRTCDataChannel * channel,
     }
   }
 }
+
+void
+webrtc_data_channel_set_webrtcbin (WebRTCDataChannel * channel,
+    GstWebRTCBin * webrtcbin)
+{
+  g_weak_ref_set (&channel->webrtcbin_weak, webrtcbin);
+}
index dd65a66..919949f 100644 (file)
@@ -51,7 +51,7 @@ struct _WebRTCDataChannel
   GstElement                       *sink_bin;
   GstElement                       *appsink;
 
-  GstWebRTCBin                     *webrtcbin;
+  GWeakRef                          webrtcbin_weak;
   gboolean                          opened;
   gulong                            src_probe;
   GError                           *stored_error;
@@ -72,6 +72,10 @@ G_GNUC_INTERNAL
 void    webrtc_data_channel_link_to_sctp (WebRTCDataChannel                 *channel,
                                           WebRTCSCTPTransport               *sctp_transport);
 
+G_GNUC_INTERNAL
+void    webrtc_data_channel_set_webrtcbin (WebRTCDataChannel                *channel,
+                                           GstWebRTCBin                     *webrtcbin);
+
 G_DECLARE_FINAL_TYPE (WebRTCErrorIgnoreBin, webrtc_error_ignore_bin, WEBRTC, ERROR_IGNORE_BIN, GstBin);
 
 G_END_DECLS