After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 777146 - vaapisink: segfault caused by race condition with OverlayInterface expose method
vaapisink: segfault caused by race condition with OverlayInterface expose method
Status: RESOLVED FIXED
Product: GStreamer
Classification: Platform
Component: gstreamer-vaapi
git master
Other Linux
: Normal normal
: 1.10.3
Assigned To: GStreamer Maintainers
GStreamer Maintainers
Depends on:
Blocks:
 
 
Reported: 2017-01-11 16:27 UTC by Matt Staples
Modified: 2017-01-20 15:24 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
Add mutex protection around video_buffer member variable (2.75 KB, patch)
2017-01-11 16:36 UTC, Matt Staples
reviewed Details | Review
vaapisink: don't use member variable outside lock (1.42 KB, patch)
2017-01-12 19:14 UTC, Víctor Manuel Jáquez Leal
none Details | Review
vaapisink: don't use member variable outside lock (2.41 KB, patch)
2017-01-13 20:49 UTC, Víctor Manuel Jáquez Leal
committed Details | Review

Description Matt Staples 2017-01-11 16:27:42 UTC
gst_vaapisink_overlay_expose calls gst_vaapisink_show_frame, passing a pointer to sink->video_buffer, but that variable is accessed outside any lock.  Meanwhile, gst_vaapisink_show_frame, which is also called from the main data-flow thread, explicitly clears that variable outside the lock.

The result is that application calls to the expose method can occasionally cause a segfault during playback.
Comment 1 Matt Staples 2017-01-11 16:36:09 UTC
Created attachment 343315 [details] [review]
Add mutex protection around video_buffer member variable
Comment 2 Víctor Manuel Jáquez Leal 2017-01-11 17:59:24 UTC
Review of attachment 343315 [details] [review]:

Good catch! thanks!

Just a couple comments.

::: gst/vaapi/gstvaapisink.c
@@ +578,3 @@
   GstVaapiSink *const sink = GST_VAAPISINK (overlay);
 
+  gst_vaapisink_reconfigure_window (sink);

I wonder if this would launch unneeded window configures.

What do you think about this approach?

gst_vaapi_display_lock (GST_VAAPI_PLUGIN_BASE_DISPLAY (sink));
if (sink->video_buffer)
  buffer = gst_buffer_ref (sink->video_buffer);
gst_vaapi_display_unlock (GST_VAAPI_PLUGIN_BASE_DISPLAY (sink));

if (buffer) {
  gst_vaapisink_reconfigure_window (sink);
  gst_vaapisink_show_frame (GST_VIDEO_SINK_CAST (sink), buffer);
}

@@ +1463,3 @@
+  if (oldBuffer != NULL) {
+    gst_buffer_unref (oldBuffer);
+  }

Is this truly required?
Comment 3 Víctor Manuel Jáquez Leal 2017-01-11 17:59:25 UTC
Review of attachment 343315 [details] [review]:

Good catch! thanks!

Just a couple comments.

::: gst/vaapi/gstvaapisink.c
@@ +578,3 @@
   GstVaapiSink *const sink = GST_VAAPISINK (overlay);
 
+  gst_vaapisink_reconfigure_window (sink);

I wonder if this would launch unneeded window configures.

What do you think about this approach?

gst_vaapi_display_lock (GST_VAAPI_PLUGIN_BASE_DISPLAY (sink));
if (sink->video_buffer)
  buffer = gst_buffer_ref (sink->video_buffer);
gst_vaapi_display_unlock (GST_VAAPI_PLUGIN_BASE_DISPLAY (sink));

if (buffer) {
  gst_vaapisink_reconfigure_window (sink);
  gst_vaapisink_show_frame (GST_VIDEO_SINK_CAST (sink), buffer);
}

@@ +1463,3 @@
+  if (oldBuffer != NULL) {
+    gst_buffer_unref (oldBuffer);
+  }

Is this truly required?
Comment 4 Matt Staples 2017-01-11 19:33:35 UTC
Review of attachment 343315 [details] [review]:

::: gst/vaapi/gstvaapisink.c
@@ +578,3 @@
   GstVaapiSink *const sink = GST_VAAPISINK (overlay);
 
+  gst_vaapisink_reconfigure_window (sink);

I thought about that, but the reason I moved the call to reconfigure_window outside of the check on sink->video_buffer, is that its implementation doesn't seem to have anything to do with the buffer.  It only works on the caps, which could get set/changed independent of the buffer (or even before the first buffer arrives).

Also, by reffing sink->video_buffer inside the lock, then releasing the lock, and then calling show_frame, there's the possibility of a new frame arriving in the interim.  I think that would cause this method to re-show the previous frame out of sequence.

@@ +1463,3 @@
+  if (oldBuffer != NULL) {
+    gst_buffer_unref (oldBuffer);
+  }

Do you mean the check for whether oldBuffer is null?  I didn't know whether gst_buffer_unref was robust to null pointers, so maybe that's not necessary.

Otherwise, yes, it's necessary to keep the assignment of the sink->video_buffer variable inside the mutex lock in order to avoid a race with calls to the expose method on separate threads.  The actual deadlock possibility, for which we need to leave the lock, appears to only be related to the gst_buffer_unref operation.
Comment 5 Víctor Manuel Jáquez Leal 2017-01-12 19:14:42 UTC
Created attachment 343388 [details] [review]
vaapisink: don't use member variable outside lock

Thus a race condition segfault is avoided.
Comment 6 Víctor Manuel Jáquez Leal 2017-01-12 19:15:51 UTC
(In reply to Víctor Manuel Jáquez Leal from comment #5)
> Created attachment 343388 [details] [review] [review]
> vaapisink: don't use member variable outside lock
> 
> Thus a race condition segfault is avoided.

@Matt, 

What do you think about this version of the patch? I think it is simpler. But I haven't tested it.
Comment 7 Matt Staples 2017-01-13 20:18:15 UTC
I like the general approach.  It certainly simplifies the overlay_expose method.  
However, the race condition is still there as long as the call, "gst_buffer_replace(&sink->videoBuffer, buffer);" is made outside the lock.
Comment 8 Víctor Manuel Jáquez Leal 2017-01-13 20:35:32 UTC
(In reply to Matt Staples from comment #7)
> I like the general approach.  It certainly simplifies the overlay_expose
> method.  
> However, the race condition is still there as long as the call,
> "gst_buffer_replace(&sink->videoBuffer, buffer);" is made outside the lock.

Got it! I'll update the patch. Thanks!
Comment 9 Víctor Manuel Jáquez Leal 2017-01-13 20:49:38 UTC
Created attachment 343453 [details] [review]
vaapisink: don't use member variable outside lock

Thus a race condition segfault is avoided.

Original-patch-by: Matt Staples <staples255@gmail.com>
Comment 10 Matt Staples 2017-01-15 01:15:53 UTC
Review of attachment 343453 [details] [review]:

::: gst/vaapi/gstvaapisink.c
@@ +1461,2 @@
   gst_vaapi_display_unlock (GST_VAAPI_PLUGIN_BASE_DISPLAY (sink));
+  gst_buffer_replace (&old_buf, NULL);

I think this changes the behavior.  Previously, sink->video_buffer was set to NULL at the end of this function, but now it's being set to a new reference to buffer.  Was that intentional?
If not, I think sink->video_buffer could just be assigned NULL on line 1458.
Comment 11 Víctor Manuel Jáquez Leal 2017-01-18 12:48:04 UTC
(In reply to Matt Staples from comment #10)
> Review of attachment 343453 [details] [review] [review]:
> 
> ::: gst/vaapi/gstvaapisink.c
> @@ +1461,2 @@
>    gst_vaapi_display_unlock (GST_VAAPI_PLUGIN_BASE_DISPLAY (sink));
> +  gst_buffer_replace (&old_buf, NULL);
> 
> I think this changes the behavior.  Previously, sink->video_buffer was set
> to NULL at the end of this function, but now it's being set to a new
> reference to buffer.  Was that intentional?
> If not, I think sink->video_buffer could just be assigned NULL on line 1458.

sink->video_buffer wasn't never set to NULL inside gst_vaapisink_show_frame_unlocked(), since buffer always has a value, it always has have a reference to this buffer.

(In reply to Matt Staples from comment #7)
> I like the general approach.  It certainly simplifies the overlay_expose
> method.  
> However, the race condition is still there as long as the call,
> "gst_buffer_replace(&sink->videoBuffer, buffer);" is made outside the lock.

Giving a second thought, the race condition is not there anymore, IIUC, since 
sink->videoBuffer now is not read/write outside of gst_vaapisink_show_frame_unlocked(), so we can call gst_buffer_replace (sink->video_buffer, buffer) with the display unlocked.

Nonetheless, I think we'll do it as you propose since it is clearer, IMO.

Do you agree?
Comment 12 Matt Staples 2017-01-18 20:54:54 UTC
Sorry, my mistake - I see now that sink->video_buffer had previously been replaced with buffer, not NULL.  So please ignore my previous comment.  I think your last attachment looks correct as-is.
Comment 13 Víctor Manuel Jáquez Leal 2017-01-20 15:00:03 UTC
Attachment 343453 [details] pushed as f536028 - vaapisink: don't use member variable outside lock
Comment 14 Víctor Manuel Jáquez Leal 2017-01-20 15:24:44 UTC
Also pushed in branch 1.10

commit a7d536301be72f6f83663740169d885048f4c175
Author: Víctor Manuel Jáquez Leal <victorx.jaquez@intel.com>
Date:   Thu Jan 12 19:54:41 2017 +0100

    vaapisink: don't use member variable outside lock

    Thus a race condition segfault is avoided.

    Original-patch-by: Matt Staples <staples255@gmail.com>

    https://bugzilla.gnome.org/show_bug.cgi?id=777146