clock: Keep weak reference to underlying clock

Fixes potential segmentation fault when using a GstClockID that
is referencing an already freed GstClock

Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/issues/187
This commit is contained in:
Thomas Bluemel 2016-09-08 08:49:54 -06:00 committed by Sebastian Dröge
parent 8abe052590
commit 36ab067905
3 changed files with 116 additions and 9 deletions

View file

@ -249,7 +249,10 @@ gst_clock_entry_new (GstClock * clock, GstClockTime time,
"created entry %p, time %" GST_TIME_FORMAT, entry, GST_TIME_ARGS (time)); "created entry %p, time %" GST_TIME_FORMAT, entry, GST_TIME_ARGS (time));
entry->refcount = 1; entry->refcount = 1;
#ifndef GST_DISABLE_DEPRECATED
entry->clock = clock; entry->clock = clock;
#endif
g_weak_ref_init (&entry->ABI.clock, clock);
entry->type = type; entry->type = type;
entry->time = time; entry->time = time;
entry->interval = interval; entry->interval = interval;
@ -270,7 +273,8 @@ gst_clock_entry_reinit (GstClock * clock, GstClockEntry * entry,
GstClockTime time, GstClockTime interval, GstClockEntryType type) GstClockTime time, GstClockTime interval, GstClockEntryType type)
{ {
g_return_val_if_fail (entry->status != GST_CLOCK_BUSY, FALSE); g_return_val_if_fail (entry->status != GST_CLOCK_BUSY, FALSE);
g_return_val_if_fail (entry->clock == clock, FALSE); g_return_val_if_fail (gst_clock_id_uses_clock ((GstClockID) entry, clock),
FALSE);
entry->type = type; entry->type = type;
entry->time = time; entry->time = time;
@ -354,6 +358,8 @@ _gst_clock_id_free (GstClockID id)
if (entry->destroy_data) if (entry->destroy_data)
entry->destroy_data (entry->user_data); entry->destroy_data (entry->user_data);
g_weak_ref_clear (&entry->ABI.clock);
/* FIXME: add tracer hook for struct allocations such as clock entries */ /* FIXME: add tracer hook for struct allocations such as clock entries */
g_slice_free (GstClockEntry, id); g_slice_free (GstClockEntry, id);
@ -526,7 +532,9 @@ gst_clock_id_wait (GstClockID id, GstClockTimeDiff * jitter)
entry = (GstClockEntry *) id; entry = (GstClockEntry *) id;
requested = GST_CLOCK_ENTRY_TIME (entry); requested = GST_CLOCK_ENTRY_TIME (entry);
clock = GST_CLOCK_ENTRY_CLOCK (entry); clock = g_weak_ref_get (&entry->ABI.clock);
if (G_UNLIKELY (clock == NULL))
goto invalid_entry;
/* can't sync on invalid times */ /* can't sync on invalid times */
if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (requested))) if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (requested)))
@ -549,6 +557,7 @@ gst_clock_id_wait (GstClockID id, GstClockTimeDiff * jitter)
if (entry->type == GST_CLOCK_ENTRY_PERIODIC) if (entry->type == GST_CLOCK_ENTRY_PERIODIC)
entry->time = requested + entry->interval; entry->time = requested + entry->interval;
gst_object_unref (clock);
return res; return res;
/* ERRORS */ /* ERRORS */
@ -556,13 +565,20 @@ invalid_time:
{ {
GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock,
"invalid time requested, returning _BADTIME"); "invalid time requested, returning _BADTIME");
gst_object_unref (clock);
return GST_CLOCK_BADTIME; return GST_CLOCK_BADTIME;
} }
not_supported: not_supported:
{ {
GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "clock wait is not supported"); GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "clock wait is not supported");
gst_object_unref (clock);
return GST_CLOCK_UNSUPPORTED; return GST_CLOCK_UNSUPPORTED;
} }
invalid_entry:
{
GST_CAT_DEBUG (GST_CAT_CLOCK, "clock entry %p lost its clock", id);
return GST_CLOCK_ERROR;
}
} }
/** /**
@ -600,7 +616,9 @@ gst_clock_id_wait_async (GstClockID id,
entry = (GstClockEntry *) id; entry = (GstClockEntry *) id;
requested = GST_CLOCK_ENTRY_TIME (entry); requested = GST_CLOCK_ENTRY_TIME (entry);
clock = GST_CLOCK_ENTRY_CLOCK (entry); clock = g_weak_ref_get (&entry->ABI.clock);
if (G_UNLIKELY (clock == NULL))
goto invalid_entry;
/* can't sync on invalid times */ /* can't sync on invalid times */
if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (requested))) if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (requested)))
@ -617,6 +635,7 @@ gst_clock_id_wait_async (GstClockID id,
res = cclass->wait_async (clock, entry); res = cclass->wait_async (clock, entry);
gst_object_unref (clock);
return res; return res;
/* ERRORS */ /* ERRORS */
@ -625,13 +644,20 @@ invalid_time:
(func) (clock, GST_CLOCK_TIME_NONE, id, user_data); (func) (clock, GST_CLOCK_TIME_NONE, id, user_data);
GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock,
"invalid time requested, returning _BADTIME"); "invalid time requested, returning _BADTIME");
gst_object_unref (clock);
return GST_CLOCK_BADTIME; return GST_CLOCK_BADTIME;
} }
not_supported: not_supported:
{ {
GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "clock wait is not supported"); GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "clock wait is not supported");
gst_object_unref (clock);
return GST_CLOCK_UNSUPPORTED; return GST_CLOCK_UNSUPPORTED;
} }
invalid_entry:
{
GST_CAT_DEBUG (GST_CAT_CLOCK, "clock entry %p lost its clock", id);
return GST_CLOCK_ERROR;
}
} }
/** /**
@ -655,12 +681,23 @@ gst_clock_id_unschedule (GstClockID id)
g_return_if_fail (id != NULL); g_return_if_fail (id != NULL);
entry = (GstClockEntry *) id; entry = (GstClockEntry *) id;
clock = entry->clock; clock = g_weak_ref_get (&entry->ABI.clock);
if (G_UNLIKELY (clock == NULL))
goto invalid_entry;
cclass = GST_CLOCK_GET_CLASS (clock); cclass = GST_CLOCK_GET_CLASS (clock);
if (G_LIKELY (cclass->unschedule)) if (G_LIKELY (cclass->unschedule))
cclass->unschedule (clock, entry); cclass->unschedule (clock, entry);
gst_object_unref (clock);
return;
invalid_entry:
{
GST_CAT_DEBUG (GST_CAT_CLOCK, "clock entry %p lost its clock", id);
return;
}
} }
@ -1338,6 +1375,63 @@ gst_clock_get_master (GstClock * clock)
return result; return result;
} }
/**
* gst_clock_id_get_clock:
* @id: a #GstClockID
*
* This function returns the underlying clock.
*
* Returns: (transfer full) (nullable): a #GstClock or %NULL when the
* underlying clock has been freed. Unref after usage.
*
* MT safe.
*/
GstClock *
gst_clock_id_get_clock (GstClockID id)
{
GstClockEntry *entry;
g_return_val_if_fail (id != NULL, NULL);
entry = (GstClockEntry *) id;
return g_weak_ref_get (&entry->ABI.clock);
}
/**
* gst_clock_id_uses_clock:
* @id: a #GstClockID to check
* @clock: a #GstClock to compare against
*
* This function returns whether @id uses @clock as the underlying clock.
* @clock can be NULL, in which case the return value indicates whether
* the underlying clock has been freed. If this is the case, the @id is
* no longer usable and should be freed.
*
* Returns: whether the clock @id uses the same underlying #GstClock @clock.
*
* MT safe.
*/
gboolean
gst_clock_id_uses_clock (GstClockID id, GstClock * clock)
{
GstClockEntry *entry;
GstClock *entry_clock;
gboolean ret = FALSE;
g_return_val_if_fail (id != NULL, FALSE);
entry = (GstClockEntry *) id;
entry_clock = g_weak_ref_get (&entry->ABI.clock);
if (entry_clock == clock)
ret = TRUE;
if (G_LIKELY (entry_clock != NULL))
gst_object_unref (entry_clock);
return ret;
}
/** /**
* gst_clock_add_observation: * gst_clock_add_observation:
* @clock: a #GstClock * @clock: a #GstClock

View file

@ -339,13 +339,18 @@ typedef enum {
* Cast to a clock entry * Cast to a clock entry
*/ */
#define GST_CLOCK_ENTRY(entry) ((GstClockEntry *)(entry)) #define GST_CLOCK_ENTRY(entry) ((GstClockEntry *)(entry))
#ifndef GST_DISABLE_DEPRECATED
/** /**
* GST_CLOCK_ENTRY_CLOCK: * GST_CLOCK_ENTRY_CLOCK:
* @entry: the entry to query * @entry: the entry to query
* *
* Get the owner clock of the entry * Get the owner clock of the entry
*
* Deprecated: Use gst_clock_id_get_clock() instead.
*/ */
#define GST_CLOCK_ENTRY_CLOCK(entry) ((entry)->clock) #define GST_CLOCK_ENTRY_CLOCK(entry) ((entry)->clock)
#endif
/** /**
* GST_CLOCK_ENTRY_TYPE: * GST_CLOCK_ENTRY_TYPE:
* @entry: the entry to query * @entry: the entry to query
@ -387,7 +392,9 @@ typedef enum {
struct _GstClockEntry { struct _GstClockEntry {
gint refcount; gint refcount;
/*< protected >*/ /*< protected >*/
#ifndef GST_DISABLE_DEPRECATED
GstClock *clock; GstClock *clock;
#endif
GstClockEntryType type; GstClockEntryType type;
GstClockTime time; GstClockTime time;
GstClockTime interval; GstClockTime interval;
@ -399,7 +406,10 @@ struct _GstClockEntry {
gboolean woken_up; gboolean woken_up;
/*< private >*/ /*< private >*/
union {
gpointer _gst_reserved[GST_PADDING]; gpointer _gst_reserved[GST_PADDING];
GWeakRef clock;
} ABI;
}; };
#include <gst/gstobject.h> #include <gst/gstobject.h>
@ -600,6 +610,12 @@ void gst_clock_id_unref (GstClockID id);
GST_API GST_API
gint gst_clock_id_compare_func (gconstpointer id1, gconstpointer id2); gint gst_clock_id_compare_func (gconstpointer id1, gconstpointer id2);
GST_API
GstClock * gst_clock_id_get_clock (GstClockID id);
GST_API
gboolean gst_clock_id_uses_clock (GstClockID id, GstClock * clock);
GST_API GST_API
GstClockTime gst_clock_id_get_time (GstClockID id); GstClockTime gst_clock_id_get_time (GstClockID id);

View file

@ -2286,11 +2286,8 @@ gst_base_sink_wait_clock (GstBaseSink * sink, GstClockTime time,
time += base_time; time += base_time;
/* Re-use existing clockid if available */ /* Re-use existing clockid if available */
/* FIXME: Casting to GstClockEntry only works because the types
* are the same */
if (G_LIKELY (sink->priv->cached_clock_id != NULL if (G_LIKELY (sink->priv->cached_clock_id != NULL
&& GST_CLOCK_ENTRY_CLOCK ((GstClockEntry *) sink-> && gst_clock_id_uses_clock (sink->priv->cached_clock_id, clock))) {
priv->cached_clock_id) == clock)) {
if (!gst_clock_single_shot_id_reinit (clock, sink->priv->cached_clock_id, if (!gst_clock_single_shot_id_reinit (clock, sink->priv->cached_clock_id,
time)) { time)) {
gst_clock_id_unref (sink->priv->cached_clock_id); gst_clock_id_unref (sink->priv->cached_clock_id);