summaryrefslogtreecommitdiff
path: root/libavcodec/v4l2_buffers.c
diff options
context:
space:
mode:
authorMark Thompson <sw@jkqxz.net>2018-01-09 23:56:41 +0100
committerMark Thompson <sw@jkqxz.net>2018-01-21 00:37:35 +0000
commita0c624e299730c8c5800375c2f5f3c6c200053ff (patch)
tree6a7b9312d0b25d9a65d160d62165675d4985bdfb /libavcodec/v4l2_buffers.c
parent6c1c6c6c71fc776c6dd25d13861b036dad2cdc1b (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.c32
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);