[FFmpeg-devel] [PATCH] avutil/buffer: Avoid allocation of AVBuffer when using buffer pool

James Almer jamrial at gmail.com
Fri Sep 17 20:02:32 EEST 2021


On 9/14/2021 7:16 AM, Andreas Rheinhardt wrote:
> Do this by putting an AVBuffer structure into BufferPoolEntry and
> reuse it for all subsequent uses of said BufferPoolEntry.
> 
> Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt at outlook.com>
> ---
>   libavutil/buffer.c          | 44 +++++++++++++++++++++++++------------
>   libavutil/buffer_internal.h | 11 ++++++++++
>   2 files changed, 41 insertions(+), 14 deletions(-)
> 
> diff --git a/libavutil/buffer.c b/libavutil/buffer.c
> index b13eeadffb..4d9ccf74b7 100644
> --- a/libavutil/buffer.c
> +++ b/libavutil/buffer.c
> @@ -26,16 +26,11 @@
>   #include "mem.h"
>   #include "thread.h"
>   
> -AVBufferRef *av_buffer_create(uint8_t *data, size_t size,
> -                              void (*free)(void *opaque, uint8_t *data),
> -                              void *opaque, int flags)
> +static AVBufferRef *buffer_create(AVBuffer *buf, uint8_t *data, size_t size,
> +                                  void (*free)(void *opaque, uint8_t *data),
> +                                  void *opaque, int flags)
>   {
>       AVBufferRef *ref = NULL;
> -    AVBuffer    *buf = NULL;
> -
> -    buf = av_mallocz(sizeof(*buf));
> -    if (!buf)
> -        return NULL;
>   
>       buf->data     = data;
>       buf->size     = size;
> @@ -47,10 +42,8 @@ AVBufferRef *av_buffer_create(uint8_t *data, size_t size,
>       buf->flags = flags;
>   
>       ref = av_mallocz(sizeof(*ref));
> -    if (!ref) {
> -        av_freep(&buf);
> +    if (!ref)
>           return NULL;
> -    }
>   
>       ref->buffer = buf;
>       ref->data   = data;
> @@ -59,6 +52,23 @@ AVBufferRef *av_buffer_create(uint8_t *data, size_t size,
>       return ref;
>   }
>   
> +AVBufferRef *av_buffer_create(uint8_t *data, size_t size,
> +                              void (*free)(void *opaque, uint8_t *data),
> +                              void *opaque, int flags)
> +{
> +    AVBufferRef *ret;
> +    AVBuffer *buf = av_mallocz(sizeof(*buf));
> +    if (!buf)
> +        return NULL;
> +
> +    ret = buffer_create(buf, data, size, free, opaque, flags);
> +    if (!ret) {
> +        av_free(buf);
> +        return NULL;
> +    }
> +    return ret;
> +}
> +
>   void av_buffer_default_free(void *opaque, uint8_t *data)
>   {
>       av_free(data);
> @@ -117,8 +127,12 @@ static void buffer_replace(AVBufferRef **dst, AVBufferRef **src)
>           av_freep(dst);
>   
>       if (atomic_fetch_sub_explicit(&b->refcount, 1, memory_order_acq_rel) == 1) {
> +        /* b->free below might already free the structure containing *b,
> +         * so we have to read the flag now to avoid use-after-free. */
> +        int free_avbuffer = !(b->flags_internal & BUFFER_FLAG_NO_FREE);
>           b->free(b->opaque, b->data);
> -        av_freep(&b);
> +        if (free_avbuffer)
> +            av_free(b);
>       }
>   }
>   
> @@ -378,11 +392,13 @@ AVBufferRef *av_buffer_pool_get(AVBufferPool *pool)
>       ff_mutex_lock(&pool->mutex);
>       buf = pool->pool;
>       if (buf) {
> -        ret = av_buffer_create(buf->data, pool->size, pool_release_buffer,
> -                               buf, 0);
> +        memset(&buf->buffer, 0, sizeof(buf->buffer));
> +        ret = buffer_create(&buf->buffer, buf->data, pool->size,
> +                            pool_release_buffer, buf, 0);
>           if (ret) {
>               pool->pool = buf->next;
>               buf->next = NULL;
> +            buf->buffer.flags_internal |= BUFFER_FLAG_NO_FREE;
>           }
>       } else {
>           ret = pool_alloc_buffer(pool);
> diff --git a/libavutil/buffer_internal.h b/libavutil/buffer_internal.h
> index 839dc05f8f..bdff1b5b32 100644
> --- a/libavutil/buffer_internal.h
> +++ b/libavutil/buffer_internal.h
> @@ -30,6 +30,11 @@
>    * The buffer was av_realloc()ed, so it is reallocatable.
>    */
>   #define BUFFER_FLAG_REALLOCATABLE (1 << 0)
> +/**
> + * The AVBuffer structure is part of a larger structure
> + * and should not be freed.
> + */
> +#define BUFFER_FLAG_NO_FREE       (1 << 1)

How about calling it something more generic BUFFER_FLAG_POOL or 
BUFFER_FLAG_FROM_POOL, to signal that the buffer was allocated by the 
buffer pool API? It can be reused, like in 
av_buffer_pool_buffer_get_opaque() to ensure there will be no crashes if 
the user passes it an AVBufferRef that was not created by the buffer 
pool API, and instead return NULL.

>   
>   struct AVBuffer {
>       uint8_t *data; /**< data described by this buffer */
> @@ -73,6 +78,12 @@ typedef struct BufferPoolEntry {
>   
>       AVBufferPool *pool;
>       struct BufferPoolEntry *next;
> +
> +    /*
> +     * An AVBuffer structure to (re)use as AVBuffer for subsequent uses
> +     * of this BufferPoolEntry.
> +     */
> +    AVBuffer buffer;
>   } BufferPoolEntry;
>   
>   struct AVBufferPool {
> 



More information about the ffmpeg-devel mailing list