diff options
author | Mark Thompson <sw@jkqxz.net> | 2018-01-09 23:56:41 +0100 |
---|---|---|
committer | Mark Thompson <sw@jkqxz.net> | 2018-01-21 00:37:35 +0000 |
commit | a0c624e299730c8c5800375c2f5f3c6c200053ff (patch) | |
tree | 6a7b9312d0b25d9a65d160d62165675d4985bdfb /libavcodec/v4l2_buffers.c | |
parent | 6c1c6c6c71fc776c6dd25d13861b036dad2cdc1b (diff) |
avcodec: v4l2_m2m: fix races around freeing data on close
Refcount all of the context information. This also fixes a potential
segmentation fault when accessing freed memory (buffer returned after
the codec has been closed).
Tested-by: Jorge Ramirez-Ortiz <jorge.ramirez.ortiz@gmail.com>
Diffstat (limited to 'libavcodec/v4l2_buffers.c')
-rw-r--r-- | libavcodec/v4l2_buffers.c | 32 |
1 files changed, 20 insertions, 12 deletions
diff --git a/libavcodec/v4l2_buffers.c b/libavcodec/v4l2_buffers.c index ba70c5d14b..4e68f901b3 100644 --- a/libavcodec/v4l2_buffers.c +++ b/libavcodec/v4l2_buffers.c @@ -207,20 +207,17 @@ static void v4l2_free_buffer(void *opaque, uint8_t *unused) V4L2Buffer* avbuf = opaque; V4L2m2mContext *s = buf_to_m2mctx(avbuf); - atomic_fetch_sub_explicit(&s->refcount, 1, memory_order_acq_rel); - if (s->reinit) { - if (!atomic_load(&s->refcount)) - sem_post(&s->refsync); - return; - } + if (atomic_fetch_sub(&avbuf->context_refcount, 1) == 1) { + atomic_fetch_sub_explicit(&s->refcount, 1, memory_order_acq_rel); - if (avbuf->context->streamon) { - ff_v4l2_buffer_enqueue(avbuf); - return; - } + if (s->reinit) { + if (!atomic_load(&s->refcount)) + sem_post(&s->refsync); + } else if (avbuf->context->streamon) + ff_v4l2_buffer_enqueue(avbuf); - if (!atomic_load(&s->refcount)) - ff_v4l2_m2m_codec_end(s->avctx); + av_buffer_unref(&avbuf->context_ref); + } } static int v4l2_buf_to_bufref(V4L2Buffer *in, int plane, AVBufferRef **buf) @@ -236,6 +233,17 @@ static int v4l2_buf_to_bufref(V4L2Buffer *in, int plane, AVBufferRef **buf) if (!*buf) return AVERROR(ENOMEM); + if (in->context_ref) + atomic_fetch_add(&in->context_refcount, 1); + else { + in->context_ref = av_buffer_ref(s->self_ref); + if (!in->context_ref) { + av_buffer_unref(buf); + return AVERROR(ENOMEM); + } + in->context_refcount = 1; + } + in->status = V4L2BUF_RET_USER; atomic_fetch_add_explicit(&s->refcount, 1, memory_order_relaxed); |