bufferlist: Prevent gst_buffer_list_foreach() from modifying non-writeable lists

Previously gst_buffer_list_foreach() could modify (drop or replace)
buffers in non-writable lists, which could cause all kinds of problems
if other code also has a reference to the list and assumes that it stays
the same.

https://bugzilla.gnome.org/show_bug.cgi?id=796692
This commit is contained in:
Sebastian Dröge 2018-06-29 07:16:28 +02:00
parent 111faa58c0
commit cb51bd6b31
2 changed files with 49 additions and 2 deletions

View file

@ -247,7 +247,7 @@ gst_buffer_list_foreach (GstBufferList * list, GstBufferListFunc func,
{
guint i, len;
gboolean ret = TRUE;
gboolean list_was_writable;
gboolean list_was_writable, first_warning = TRUE;
g_return_val_if_fail (GST_IS_BUFFER_LIST (list), FALSE);
g_return_val_if_fail (func != NULL, FALSE);
@ -281,7 +281,22 @@ gst_buffer_list_foreach (GstBufferList * list, GstBufferListFunc func,
/* Check if the function changed the buffer */
if (buf != buf_ret) {
if (buf_ret == NULL) {
/* If the list was not writable but the callback was actually changing
* our buffer, then it wouldn't have been allowed to do so.
*
* Fortunately we still have a reference to the old buffer in that case
* and just not modify the list, unref the new buffer (if any) and warn
* about this */
if (!list_was_writable) {
if (first_warning) {
g_critical
("gst_buffer_list_foreach: non-writable list %p was changed from callback",
list);
first_warning = FALSE;
}
if (buf_ret)
gst_buffer_unref (buf_ret);
} else if (buf_ret == NULL) {
gst_buffer_list_remove_range_internal (list, i, 1, !was_writable);
--len;
} else {

View file

@ -508,6 +508,37 @@ GST_START_TEST (test_multiple_mutable_buffer_references)
GST_END_TEST;
static gboolean
foreach_replace_buffer (GstBuffer ** buffer, guint idx, gpointer user_data)
{
gst_buffer_unref (*buffer);
*buffer = gst_buffer_new ();
return TRUE;
}
GST_START_TEST (test_foreach_modify_non_writeable_list)
{
GstBufferList *b = gst_buffer_list_new_sized (1);
GstBuffer *buf;
buf = gst_buffer_new ();
gst_buffer_list_add (b, gst_buffer_ref (buf));
gst_buffer_list_ref (b);
fail_if (gst_buffer_list_is_writable (b));
ASSERT_CRITICAL (gst_buffer_list_foreach (b, foreach_replace_buffer, NULL));
fail_unless (gst_buffer_list_get (b, 0) == buf);
gst_buffer_list_unref (b);
gst_buffer_list_unref (b);
gst_buffer_unref (buf);
}
GST_END_TEST;
static Suite *
gst_buffer_list_suite (void)
{
@ -527,6 +558,7 @@ gst_buffer_list_suite (void)
tcase_add_test (tc_chain, test_calc_size);
tcase_add_test (tc_chain, test_new_sized_0);
tcase_add_test (tc_chain, test_multiple_mutable_buffer_references);
tcase_add_test (tc_chain, test_foreach_modify_non_writeable_list);
return s;
}