Skip to content

rtspsrc: Don't invoke close when stopping if we've started cleanup

Nirbheek Chauhan requested to merge nirbheek/gstreamer:rtspsrc-segfault into main

When we're doing a state change from PLAYING to NULL, first we invoke gst_rtspsrc_loop_send_cmd_and_wait (..., CMD_CLOSE, ...) during PAUSED_TO_READY which will schedule a TEARDOWN to happen async on the task thread.

The task thread will call gst_rtspsrc_close(), which will send the TEARDOWN and once it's complete, it will call gst_rtspsrc_cleanup() without taking any locks, which frees src->streams.

At the same time however, the state change in the app thread will progress further and in READY_TO_NULL it will call gst_rtspsrc_stop() which calls gst_rtspsrc_close() a second time, which accesses src->streams (without a lock again), which leads to simultaneous access of src->streams, and a segfault.

So the state change and the cleanup are racing, but they almost always complete sequentially. Either the cleanup sets src->streams to NULL or _stop() completes first. Very rarely, _stop() can start while src->streams is being freed in a for loop. That causes the segfault.

This is unlocked access is unfixable with more locking, it just leads to deadlocks. This pattern has been observed in rtspsrc a lot: state changes and cleanup in the element are unfixably racy, and that foundational issue is being addressed separately via a rewrite.

The bandage fix here is to prevent gst_rtspsrc_stop() from accessing src->streams after it has already been freed by setting src->state to INVALID.

Merge request reports