Bug 981869 - Blacklist crashy flump3dec gstreamer plugin - r=kinetik
--- 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);
@@ -1143,17 +1187,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);
@@ -1180,19 +1224,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
@@ -302,16 +302,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);