Bug 981869 - Blacklist crashy flump3dec gstreamer plugin. r=kinetik, a=lmandel
authorEdwin Flores <eflores@mozilla.com>
Mon, 15 Jul 2013 19:39:00 +1200
changeset 250320 fc7893265f9d
parent 250319 73a7e99cfd2a
child 250321 2c20766b4493
push id4545
push userryanvm@gmail.com
push date2015-03-09 20:28 +0000
treeherdermozilla-beta@2c20766b4493 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskinetik, lmandel
bugs981869
milestone37.0
Bug 981869 - Blacklist crashy flump3dec gstreamer plugin. r=kinetik, a=lmandel
dom/media/gstreamer/GStreamerFormatHelper.cpp
dom/media/gstreamer/GStreamerFormatHelper.h
dom/media/gstreamer/GStreamerFunctionList.h
dom/media/gstreamer/GStreamerReader.cpp
dom/media/gstreamer/GStreamerReader.h
modules/libpref/init/all.js
--- a/dom/media/gstreamer/GStreamerFormatHelper.cpp
+++ b/dom/media/gstreamer/GStreamerFormatHelper.cpp
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "GStreamerFormatHelper.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "nsString.h"
 #include "GStreamerLoader.h"
+#include "mozilla/Preferences.h"
 
 #define ENTRY_FORMAT(entry) entry[0]
 #define ENTRY_CAPS(entry) entry[1]
 
 namespace mozilla {
 
 GStreamerFormatHelper* GStreamerFormatHelper::gInstance = nullptr;
 bool GStreamerFormatHelper::sLoadOK = false;
@@ -59,16 +60,20 @@ static char const * const sDefaultCodecC
   {"video/mp4", "video/x-h264"},
   {"video/quicktime", "video/x-h264"},
   {"audio/mp4", "audio/mpeg, mpegversion=(int)4"},
   {"audio/x-m4a", "audio/mpeg, mpegversion=(int)4"},
   {"audio/mp3", "audio/mpeg, layer=(int)3"},
   {"audio/mpeg", "audio/mpeg, layer=(int)3"}
 };
 
+static char const * const sPluginBlacklist[] = {
+  "flump3dec",
+};
+
 GStreamerFormatHelper::GStreamerFormatHelper()
   : mFactories(nullptr),
     mCookie(static_cast<uint32_t>(-1))
 {
   if (!sLoadOK) {
     return;
   }
 
@@ -198,31 +203,68 @@ GstCaps* GStreamerFormatHelper::ConvertF
     GstCaps* tmp = gst_caps_from_string(capsString);
     /* appends and frees tmp */
     gst_caps_append(caps, tmp);
   }
 
   return caps;
 }
 
+/* static */ bool
+GStreamerFormatHelper::IsBlacklistEnabled()
+{
+  static bool sBlacklistEnabled;
+  static bool sBlacklistEnabledCached = false;
+
+  if (!sBlacklistEnabledCached) {
+    Preferences::AddBoolVarCache(&sBlacklistEnabled,
+                                 "media.gstreamer.enable-blacklist", true);
+    sBlacklistEnabledCached = true;
+  }
+
+  return sBlacklistEnabled;
+}
+
+/* static */ bool
+GStreamerFormatHelper::IsPluginFeatureBlacklisted(GstPluginFeature *aFeature,
+                                                  FactoryType aTypes)
+{
+  if (!IsBlacklistEnabled()) {
+    return false;
+  }
+
+  const gchar *className =
+    gst_element_factory_get_klass(GST_ELEMENT_FACTORY_CAST(aFeature));
+
+  const gchar *factoryName =
+    gst_plugin_feature_get_name(aFeature);
+
+  if ((!(aTypes & FactoryTypeDecoder) && strstr(className, "Decoder")) ||
+      (!(aTypes & FactoryTypeDemuxer) && strstr(className, "Demuxer")) ||
+      (!(aTypes & FactoryTypeParser) && strstr(className, "Parser"))) {
+    return false;
+  }
+
+  for (unsigned int i = 0; i < G_N_ELEMENTS(sPluginBlacklist); i++) {
+    if (!strcmp(factoryName, sPluginBlacklist[i])) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
 static gboolean FactoryFilter(GstPluginFeature *aFeature, gpointer)
 {
   if (!GST_IS_ELEMENT_FACTORY(aFeature)) {
     return FALSE;
   }
 
-  // TODO _get_klass doesn't exist in 1.0
-  const gchar *className =
-    gst_element_factory_get_klass(GST_ELEMENT_FACTORY_CAST(aFeature));
-
-  if (!strstr(className, "Decoder") && !strstr(className, "Demux")) {
-    return FALSE;
-  }
-
-  return gst_plugin_feature_get_rank(aFeature) >= GST_RANK_MARGINAL;
+  return !GStreamerFormatHelper::IsPluginFeatureBlacklisted(aFeature,
+                                                            (FactoryType)(FactoryTypeDecoder|FactoryTypeDemuxer));
 }
 
 /**
  * Returns true if any |aFactory| caps intersect with |aCaps|
  */
 static bool SupportsCaps(GstElementFactory *aFactory, GstCaps *aCaps)
 {
   for (const GList *iter = gst_element_factory_get_static_pad_templates(aFactory); iter; iter = iter->next) {
--- a/dom/media/gstreamer/GStreamerFormatHelper.h
+++ b/dom/media/gstreamer/GStreamerFormatHelper.h
@@ -8,32 +8,43 @@
 #define GStreamerFormatHelper_h_
 
 #include <gst/gst.h>
 #include <mozilla/Types.h>
 #include "nsXPCOMStrings.h"
 
 namespace mozilla {
 
+enum FactoryType {
+  FactoryTypeDecoder  = 1 << 0,
+  FactoryTypeDemuxer  = 1 << 1,
+  FactoryTypeParser   = 1 << 2,
+  FactoryTypeAll      = FactoryTypeDecoder|FactoryTypeDemuxer|FactoryTypeParser
+};
+
 class GStreamerFormatHelper {
   /* This class can be used to query the GStreamer registry for the required
    * demuxers/decoders from nsHTMLMediaElement::CanPlayType.
    * It implements looking at the GstRegistry to check if elements to
    * demux/decode the formats passed to CanPlayType() are actually installed.
    */
   public:
     static GStreamerFormatHelper* Instance();
     ~GStreamerFormatHelper();
 
     bool CanHandleMediaType(const nsACString& aMIMEType,
                             const nsAString* aCodecs);
 
     bool CanHandleContainerCaps(GstCaps* aCaps);
     bool CanHandleCodecCaps(GstCaps* aCaps);
 
+    static bool IsBlacklistEnabled();
+    static bool IsPluginFeatureBlacklisted(GstPluginFeature *aFeature,
+                                           FactoryType aTypes = FactoryTypeAll);
+
     static GstCaps* ConvertFormatsToCaps(const char* aMIMEType,
                                          const nsAString* aCodecs);
 
     static void Shutdown();
 
   private:
     GStreamerFormatHelper();
     char* const *CodecListFromCaps(GstCaps* aCaps);
--- a/dom/media/gstreamer/GStreamerFunctionList.h
+++ b/dom/media/gstreamer/GStreamerFunctionList.h
@@ -57,16 +57,17 @@ GST_FUNC(LIBGSTREAMER, gst_object_get_na
 GST_FUNC(LIBGSTREAMER, gst_object_get_parent)
 GST_FUNC(LIBGSTREAMER, gst_object_unref)
 GST_FUNC(LIBGSTREAMER, gst_pad_get_element_private)
 GST_FUNC(LIBGSTREAMER, gst_pad_set_element_private)
 GST_FUNC(LIBGSTREAMER, gst_parse_bin_from_description)
 GST_FUNC(LIBGSTREAMER, gst_pipeline_get_bus)
 GST_FUNC(LIBGSTREAMER, gst_pipeline_get_type)
 GST_FUNC(LIBGSTREAMER, gst_plugin_feature_get_rank)
+GST_FUNC(LIBGSTREAMER, gst_plugin_feature_get_type)
 GST_FUNC(LIBGSTREAMER, gst_registry_feature_filter)
 GST_FUNC(LIBGSTREAMER, gst_registry_get_feature_list_cookie)
 GST_FUNC(LIBGSTREAMER, gst_segment_init)
 GST_FUNC(LIBGSTREAMER, gst_segment_to_stream_time)
 GST_FUNC(LIBGSTREAMER, gst_static_caps_get)
 GST_FUNC(LIBGSTREAMER, gst_structure_copy)
 GST_FUNC(LIBGSTREAMER, gst_structure_get_fraction)
 GST_FUNC(LIBGSTREAMER, gst_structure_get_int)
@@ -83,16 +84,17 @@ GST_FUNC(LIBGSTREAMER, gst_element_facto
 GST_FUNC(LIBGSTREAMER, gst_element_get_pad)
 GST_FUNC(LIBGSTREAMER, gst_event_parse_new_segment)
 GST_FUNC(LIBGSTREAMER, gst_mini_object_get_type)
 GST_FUNC(LIBGSTREAMER, gst_mini_object_new)
 GST_FUNC(LIBGSTREAMER, gst_pad_add_event_probe)
 GST_FUNC(LIBGSTREAMER, gst_pad_alloc_buffer)
 GST_FUNC(LIBGSTREAMER, gst_pad_get_negotiated_caps)
 GST_FUNC(LIBGSTREAMER, gst_pad_set_bufferalloc_function)
+GST_FUNC(LIBGSTREAMER, gst_plugin_feature_get_name)
 GST_FUNC(LIBGSTREAMER, gst_registry_get_default)
 GST_FUNC(LIBGSTREAMER, gst_segment_set_newsegment)
 GST_FUNC(LIBGSTVIDEO, gst_video_format_get_component_height)
 GST_FUNC(LIBGSTVIDEO, gst_video_format_get_component_offset)
 GST_FUNC(LIBGSTVIDEO, gst_video_format_get_component_width)
 GST_FUNC(LIBGSTVIDEO, gst_video_format_get_pixel_stride)
 GST_FUNC(LIBGSTVIDEO, gst_video_format_get_row_stride)
 GST_FUNC(LIBGSTVIDEO, gst_video_format_parse_caps)
--- a/dom/media/gstreamer/GStreamerReader.cpp
+++ b/dom/media/gstreamer/GStreamerReader.cpp
@@ -190,16 +190,19 @@ nsresult GStreamerReader::Init(MediaDeco
                "audio-sink", mAudioSink,
                nullptr);
 
   g_signal_connect(G_OBJECT(mPlayBin), "notify::source",
                    G_CALLBACK(GStreamerReader::PlayBinSourceSetupCb), this);
   g_signal_connect(G_OBJECT(mPlayBin), "element-added",
                    G_CALLBACK(GStreamerReader::PlayElementAddedCb), this);
 
+  g_signal_connect(G_OBJECT(mPlayBin), "element-added",
+                   G_CALLBACK(GStreamerReader::ElementAddedCb), this);
+
   return NS_OK;
 }
 
 GstBusSyncReply
 GStreamerReader::ErrorCb(GstBus *aBus, GstMessage *aMessage, gpointer aUserData)
 {
   return static_cast<GStreamerReader*>(aUserData)->Error(aBus, aMessage);
 }
@@ -209,16 +212,57 @@ GStreamerReader::Error(GstBus *aBus, Gst
 {
   if (GST_MESSAGE_TYPE(aMessage) == GST_MESSAGE_ERROR) {
     Eos();
   }
 
   return GST_BUS_PASS;
 }
 
+void GStreamerReader::ElementAddedCb(GstBin *aPlayBin,
+                                     GstElement *aElement,
+                                     gpointer aUserData)
+{
+  const gchar *name =
+    gst_plugin_feature_get_name(GST_PLUGIN_FEATURE(gst_element_get_factory(aElement)));
+
+  if (!strcmp(name, "uridecodebin")) {
+    g_signal_connect(G_OBJECT(aElement), "autoplug-sort",
+                     G_CALLBACK(GStreamerReader::ElementFilterCb), aUserData);
+  }
+}
+
+GValueArray *GStreamerReader::ElementFilterCb(GstURIDecodeBin *aBin,
+                                              GstPad *aPad,
+                                              GstCaps *aCaps,
+                                              GValueArray *aFactories,
+                                              gpointer aUserData)
+{
+  return ((GStreamerReader*)aUserData)->ElementFilter(aBin, aPad, aCaps, aFactories);
+}
+
+GValueArray *GStreamerReader::ElementFilter(GstURIDecodeBin *aBin,
+                                            GstPad *aPad,
+                                            GstCaps *aCaps,
+                                            GValueArray *aFactories)
+{
+  GValueArray *filtered = g_value_array_new(aFactories->n_values);
+
+  for (unsigned int i = 0; i < aFactories->n_values; i++) {
+    GValue *value = &aFactories->values[i];
+    GstPluginFeature *factory = GST_PLUGIN_FEATURE(g_value_peek_pointer(value));
+
+    if (!GStreamerFormatHelper::IsPluginFeatureBlacklisted(factory)) {
+      g_value_array_append(filtered, value);
+    }
+  }
+
+  return filtered;
+}
+
 void GStreamerReader::PlayBinSourceSetupCb(GstElement* aPlayBin,
                                            GParamSpec* pspec,
                                            gpointer aUserData)
 {
   GstElement *source;
   GStreamerReader* reader = reinterpret_cast<GStreamerReader*>(aUserData);
 
   g_object_get(aPlayBin, "source", &source, nullptr);
@@ -1144,17 +1188,17 @@ void GStreamerReader::Eos(GstAppSink* aS
     mon.NotifyAll();
   }
 }
 
 /**
  * This callback is called while the pipeline is automatically built, after a
  * new element has been added to the pipeline. We use it to find the
  * uridecodebin instance used by playbin and connect to it to apply our
- * whitelist.
+ * blacklist.
  */
 void
 GStreamerReader::PlayElementAddedCb(GstBin *aBin, GstElement *aElement,
                                     gpointer *aUserData)
 {
   const static char sUriDecodeBinPrefix[] = "uridecodebin";
   gchar *name = gst_element_get_name(aElement);
 
@@ -1181,19 +1225,18 @@ GStreamerReader::ShouldAutoplugFactory(G
     autoplug = true;
   }
 
   return autoplug;
 }
 
 /**
  * This is called by uridecodebin (running inside playbin), after it has found
- * candidate factories to continue decoding the stream. We apply the whitelist
- * here, allowing only demuxers and decoders that output the formats we want to
- * support.
+ * candidate factories to continue decoding the stream. We apply the blacklist
+ * here, disallowing known-crashy plugins.
  */
 GValueArray*
 GStreamerReader::AutoplugSortCb(GstElement* aElement, GstPad* aPad,
                                 GstCaps* aCaps, GValueArray* aFactories)
 {
   if (!aFactories->n_values) {
     return nullptr;
   }
--- a/dom/media/gstreamer/GStreamerReader.h
+++ b/dom/media/gstreamer/GStreamerReader.h
@@ -20,16 +20,18 @@
 #include <gst/video/video.h>
 #pragma GCC diagnostic pop
 
 #include "MediaDecoderReader.h"
 #include "MP3FrameParser.h"
 #include "ImageContainer.h"
 #include "nsRect.h"
 
+struct GstURIDecodeBin;
+
 namespace mozilla {
 
 namespace dom {
 class TimeRanges;
 }
 
 class AbstractMediaDecoder;
 
@@ -85,16 +87,40 @@ private:
    */
   nsresult CheckSupportedFormats();
 
   /* Gst callbacks */
 
   static GstBusSyncReply ErrorCb(GstBus *aBus, GstMessage *aMessage, gpointer aUserData);
   GstBusSyncReply Error(GstBus *aBus, GstMessage *aMessage);
 
+  /*
+   * We attach this callback to playbin so that when uridecodebin is
+   * constructed, we can then list for its autoplug-sort signal to blacklist
+   * the elements it can construct.
+   */
+  static void ElementAddedCb(GstBin *aPlayBin,
+                             GstElement *aElement,
+                             gpointer aUserData);
+
+  /*
+   * Called on the autoplug-sort signal emitted by uridecodebin for filtering
+   * the elements it uses.
+   */
+  static GValueArray *ElementFilterCb(GstURIDecodeBin *aBin,
+                                      GstPad *aPad,
+                                      GstCaps *aCaps,
+                                      GValueArray *aFactories,
+                                      gpointer aUserData);
+
+  GValueArray *ElementFilter(GstURIDecodeBin *aBin,
+                             GstPad *aPad,
+                             GstCaps *aCaps,
+                             GValueArray *aFactories);
+
   /* Called on the source-setup signal emitted by playbin. Used to
    * configure appsrc .
    */
   static void PlayBinSourceSetupCb(GstElement* aPlayBin,
                                    GParamSpec* pspec,
                                    gpointer aUserData);
   void PlayBinSourceSetup(GstAppSrc* aSource);
 
@@ -164,17 +190,17 @@ private:
   static void PlayElementAddedCb(GstBin *aBin, GstElement *aElement,
                                  gpointer *aUserData);
 
   /* Called during decoding, to decide whether a (sub)stream should be decoded or
    * ignored */
   static bool ShouldAutoplugFactory(GstElementFactory* aFactory, GstCaps* aCaps);
 
   /* Called by decodebin during autoplugging. We use it to apply our
-   * container/codec whitelist.
+   * container/codec blacklist.
    */
   static GValueArray* AutoplugSortCb(GstElement* aElement,
                                      GstPad* aPad, GstCaps* aCaps,
                                      GValueArray* aFactories);
 
   // Try to find MP3 headers in this stream using our MP3 frame parser.
   nsresult ParseMP3Headers();
 
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -298,16 +298,17 @@ pref("media.wave.enabled", true);
 #ifdef MOZ_WEBM
 pref("media.webm.enabled", true);
 #if defined(MOZ_FMP4) && defined(MOZ_WMF)
 pref("media.webm.intel_decoder.enabled", false);
 #endif
 #endif
 #ifdef MOZ_GSTREAMER
 pref("media.gstreamer.enabled", true);
+pref("media.gstreamer.enable-blacklist", true);
 #endif
 #ifdef MOZ_APPLEMEDIA
 pref("media.apple.mp3.enabled", true);
 pref("media.apple.mp4.enabled", true);
 #endif
 #ifdef MOZ_WEBRTC
 pref("media.navigator.enabled", true);
 pref("media.navigator.video.enabled", true);