Coverage Report

Created: 2026-10-28 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ffmpeg/libavutil/buffer.c
Line
Count
Source
1
/*
2
 * This file is part of FFmpeg.
3
 *
4
 * FFmpeg is free software; you can redistribute it and/or
5
 * modify it under the terms of the GNU Lesser General Public
6
 * License as published by the Free Software Foundation; either
7
 * version 2.1 of the License, or (at your option) any later version.
8
 *
9
 * FFmpeg is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
 * Lesser General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU Lesser General Public
15
 * License along with FFmpeg; if not, write to the Free Software
16
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
 */
18
19
#include <stdatomic.h>
20
#include <stdint.h>
21
#include <string.h>
22
23
#include "avassert.h"
24
#include "buffer_internal.h"
25
#include "common.h"
26
#include "mem.h"
27
#include "sanitizer.h"
28
#include "thread.h"
29
30
static AVBufferRef *buffer_create(AVBuffer *buf, uint8_t *data, size_t size,
31
                                  void (*free)(void *opaque, uint8_t *data),
32
                                  void *opaque, int flags)
33
0
{
34
0
    AVBufferRef *ref = NULL;
35
36
0
    buf->data     = data;
37
0
    buf->size     = size;
38
0
    buf->free     = free ? free : av_buffer_default_free;
39
0
    buf->opaque   = opaque;
40
41
0
    atomic_init(&buf->refcount, 1);
42
43
0
    buf->flags = flags;
44
45
0
    ref = av_mallocz(sizeof(*ref));
46
0
    if (!ref)
47
0
        return NULL;
48
49
0
    ref->buffer = buf;
50
0
    ref->data   = data;
51
0
    ref->size   = size;
52
53
0
    return ref;
54
0
}
55
56
AVBufferRef *av_buffer_create(uint8_t *data, size_t size,
57
                              void (*free)(void *opaque, uint8_t *data),
58
                              void *opaque, int flags)
59
0
{
60
0
    AVBufferRef *ret;
61
0
    AVBuffer *buf = av_mallocz(sizeof(*buf));
62
0
    if (!buf)
63
0
        return NULL;
64
65
0
    ret = buffer_create(buf, data, size, free, opaque, flags);
66
0
    if (!ret) {
67
0
        av_free(buf);
68
0
        return NULL;
69
0
    }
70
0
    return ret;
71
0
}
72
73
void av_buffer_default_free(void *opaque, uint8_t *data)
74
0
{
75
0
    av_free(data);
76
0
}
77
78
AVBufferRef *av_buffer_alloc(size_t size)
79
0
{
80
0
    AVBufferRef *ret = NULL;
81
0
    uint8_t    *data = NULL;
82
83
0
    data = av_malloc(size);
84
0
    if (!data)
85
0
        return NULL;
86
87
0
    ret = av_buffer_create(data, size, av_buffer_default_free, NULL, 0);
88
0
    if (!ret)
89
0
        av_freep(&data);
90
91
0
    return ret;
92
0
}
93
94
AVBufferRef *av_buffer_allocz(size_t size)
95
0
{
96
0
    AVBufferRef *ret = av_buffer_alloc(size);
97
0
    if (!ret)
98
0
        return NULL;
99
100
0
    memset(ret->data, 0, size);
101
0
    return ret;
102
0
}
103
104
AVBufferRef *av_buffer_ref(const AVBufferRef *buf)
105
0
{
106
0
    AVBufferRef *ret = av_mallocz(sizeof(*ret));
107
108
0
    if (!ret)
109
0
        return NULL;
110
111
0
    *ret = *buf;
112
113
0
    atomic_fetch_add_explicit(&buf->buffer->refcount, 1, memory_order_relaxed);
114
115
0
    return ret;
116
0
}
117
118
static void buffer_replace(AVBufferRef **dst, AVBufferRef **src)
119
0
{
120
0
    AVBuffer *b;
121
122
0
    b = (*dst)->buffer;
123
124
0
    if (src) {
125
0
        **dst = **src;
126
0
        av_freep(src);
127
0
    } else
128
0
        av_freep(dst);
129
130
0
    if (atomic_fetch_sub_explicit(&b->refcount, 1, memory_order_acq_rel) == 1) {
131
        /* b->free below might already free the structure containing *b,
132
         * so we have to read the flag now to avoid use-after-free. */
133
0
        int free_avbuffer = !(b->flags_internal & BUFFER_FLAG_NO_FREE);
134
0
        b->free(b->opaque, b->data);
135
0
        if (free_avbuffer)
136
0
            av_free(b);
137
0
    }
138
0
}
139
140
void av_buffer_unref(AVBufferRef **buf)
141
5.74k
{
142
5.74k
    if (!buf || !*buf)
143
5.74k
        return;
144
145
0
    buffer_replace(buf, NULL);
146
0
}
147
148
int av_buffer_is_writable(const AVBufferRef *buf)
149
0
{
150
0
    if (buf->buffer->flags & AV_BUFFER_FLAG_READONLY)
151
0
        return 0;
152
153
0
    return atomic_load(&buf->buffer->refcount) == 1;
154
0
}
155
156
void *av_buffer_get_opaque(const AVBufferRef *buf)
157
0
{
158
0
    return buf->buffer->opaque;
159
0
}
160
161
int av_buffer_get_ref_count(const AVBufferRef *buf)
162
0
{
163
0
    return atomic_load(&buf->buffer->refcount);
164
0
}
165
166
int av_buffer_make_writable(AVBufferRef **pbuf)
167
0
{
168
0
    AVBufferRef *newbuf, *buf = *pbuf;
169
170
0
    if (av_buffer_is_writable(buf))
171
0
        return 0;
172
173
0
    newbuf = av_buffer_alloc(buf->size);
174
0
    if (!newbuf)
175
0
        return AVERROR(ENOMEM);
176
177
0
    memcpy(newbuf->data, buf->data, buf->size);
178
179
0
    buffer_replace(pbuf, &newbuf);
180
181
0
    return 0;
182
0
}
183
184
int av_buffer_realloc(AVBufferRef **pbuf, size_t size)
185
0
{
186
0
    AVBufferRef *buf = *pbuf;
187
0
    uint8_t *tmp;
188
0
    int ret;
189
190
0
    if (!buf) {
191
        /* allocate a new buffer with av_realloc(), so it will be reallocatable
192
         * later */
193
0
        uint8_t *data = av_realloc(NULL, size);
194
0
        if (!data)
195
0
            return AVERROR(ENOMEM);
196
197
0
        buf = av_buffer_create(data, size, av_buffer_default_free, NULL, 0);
198
0
        if (!buf) {
199
0
            av_freep(&data);
200
0
            return AVERROR(ENOMEM);
201
0
        }
202
203
0
        buf->buffer->flags_internal |= BUFFER_FLAG_REALLOCATABLE;
204
0
        *pbuf = buf;
205
206
0
        return 0;
207
0
    } else if (buf->size == size)
208
0
        return 0;
209
210
0
    if (!(buf->buffer->flags_internal & BUFFER_FLAG_REALLOCATABLE) ||
211
0
        !av_buffer_is_writable(buf) || buf->data != buf->buffer->data) {
212
        /* cannot realloc, allocate a new reallocable buffer and copy data */
213
0
        AVBufferRef *new = NULL;
214
215
0
        ret = av_buffer_realloc(&new, size);
216
0
        if (ret < 0)
217
0
            return ret;
218
219
0
        memcpy(new->data, buf->data, FFMIN(size, buf->size));
220
221
0
        buffer_replace(pbuf, &new);
222
0
        return 0;
223
0
    }
224
225
0
    tmp = av_realloc(buf->buffer->data, size);
226
0
    if (!tmp)
227
0
        return AVERROR(ENOMEM);
228
229
0
    buf->buffer->data = buf->data = tmp;
230
0
    buf->buffer->size = buf->size = size;
231
0
    return 0;
232
0
}
233
234
int av_buffer_replace(AVBufferRef **pdst, const AVBufferRef *src)
235
0
{
236
0
    AVBufferRef *dst = *pdst;
237
0
    AVBufferRef *tmp;
238
239
0
    if (!src) {
240
0
        av_buffer_unref(pdst);
241
0
        return 0;
242
0
    }
243
244
0
    if (dst && dst->buffer == src->buffer) {
245
        /* make sure the data pointers match */
246
0
        dst->data = src->data;
247
0
        dst->size = src->size;
248
0
        return 0;
249
0
    }
250
251
0
    tmp = av_buffer_ref(src);
252
0
    if (!tmp)
253
0
        return AVERROR(ENOMEM);
254
255
0
    av_buffer_unref(pdst);
256
0
    *pdst = tmp;
257
0
    return 0;
258
0
}
259
260
AVBufferPool *av_buffer_pool_init2(size_t size, void *opaque,
261
                                   AVBufferRef* (*alloc)(void *opaque, size_t size),
262
                                   void (*pool_free)(void *opaque))
263
0
{
264
0
    AVBufferPool *pool = av_mallocz(sizeof(*pool));
265
0
    if (!pool)
266
0
        return NULL;
267
268
0
    if (ff_mutex_init(&pool->mutex, NULL)) {
269
0
        av_free(pool);
270
0
        return NULL;
271
0
    }
272
273
0
    pool->size      = size;
274
0
    pool->opaque    = opaque;
275
0
    pool->alloc2    = alloc;
276
0
    pool->alloc     = av_buffer_alloc; // fallback
277
0
    pool->pool_free = pool_free;
278
279
0
    atomic_init(&pool->refcount, 1);
280
281
0
    return pool;
282
0
}
283
284
AVBufferPool *av_buffer_pool_init(size_t size, AVBufferRef* (*alloc)(size_t size))
285
0
{
286
0
    AVBufferPool *pool = av_mallocz(sizeof(*pool));
287
0
    if (!pool)
288
0
        return NULL;
289
290
0
    if (ff_mutex_init(&pool->mutex, NULL)) {
291
0
        av_free(pool);
292
0
        return NULL;
293
0
    }
294
295
0
    pool->size     = size;
296
0
    pool->alloc    = alloc ? alloc : av_buffer_alloc;
297
298
0
    atomic_init(&pool->refcount, 1);
299
300
0
    return pool;
301
0
}
302
303
static void buffer_pool_flush(AVBufferPool *pool)
304
0
{
305
0
    while (pool->pool) {
306
0
        BufferPoolEntry *buf = pool->pool;
307
0
        pool->pool = buf->next;
308
309
0
        if (buf->free == av_buffer_default_free)
310
0
            FF_ASAN_UNPOISON(buf->data, pool->size);
311
0
        buf->free(buf->opaque, buf->data);
312
0
        av_freep(&buf);
313
0
    }
314
0
}
315
316
/*
317
 * This function gets called when the pool has been uninited and
318
 * all the buffers returned to it.
319
 */
320
static void buffer_pool_free(AVBufferPool *pool)
321
0
{
322
0
    buffer_pool_flush(pool);
323
0
    ff_mutex_destroy(&pool->mutex);
324
325
0
    if (pool->pool_free)
326
0
        pool->pool_free(pool->opaque);
327
328
0
    av_freep(&pool);
329
0
}
330
331
void av_buffer_pool_uninit(AVBufferPool **ppool)
332
0
{
333
0
    AVBufferPool *pool;
334
335
0
    if (!ppool || !*ppool)
336
0
        return;
337
0
    pool   = *ppool;
338
0
    *ppool = NULL;
339
340
0
    ff_mutex_lock(&pool->mutex);
341
0
    buffer_pool_flush(pool);
342
0
    ff_mutex_unlock(&pool->mutex);
343
344
0
    if (atomic_fetch_sub_explicit(&pool->refcount, 1, memory_order_acq_rel) == 1)
345
0
        buffer_pool_free(pool);
346
0
}
347
348
static void pool_release_buffer(void *opaque, uint8_t *data)
349
0
{
350
0
    BufferPoolEntry *buf = opaque;
351
0
    AVBufferPool *pool = buf->pool;
352
353
0
    if (buf->free == av_buffer_default_free)
354
0
        FF_ASAN_POISON(buf->data, pool->size);
355
356
0
    ff_mutex_lock(&pool->mutex);
357
0
    buf->next = pool->pool;
358
0
    pool->pool = buf;
359
0
    ff_mutex_unlock(&pool->mutex);
360
361
0
    if (atomic_fetch_sub_explicit(&pool->refcount, 1, memory_order_acq_rel) == 1)
362
0
        buffer_pool_free(pool);
363
0
}
364
365
/* allocate a new buffer and override its free() callback so that
366
 * it is returned to the pool on free */
367
static AVBufferRef *pool_alloc_buffer(AVBufferPool *pool)
368
0
{
369
0
    BufferPoolEntry *buf;
370
0
    AVBufferRef     *ret;
371
372
0
    av_assert0(pool->alloc || pool->alloc2);
373
374
0
    ret = pool->alloc2 ? pool->alloc2(pool->opaque, pool->size) :
375
0
                         pool->alloc(pool->size);
376
0
    if (!ret)
377
0
        return NULL;
378
379
0
    buf = av_mallocz(sizeof(*buf));
380
0
    if (!buf) {
381
0
        av_buffer_unref(&ret);
382
0
        return NULL;
383
0
    }
384
385
0
    buf->data   = ret->buffer->data;
386
0
    buf->opaque = ret->buffer->opaque;
387
0
    buf->free   = ret->buffer->free;
388
0
    buf->pool   = pool;
389
390
0
    ret->buffer->opaque = buf;
391
0
    ret->buffer->free   = pool_release_buffer;
392
393
0
    return ret;
394
0
}
395
396
AVBufferRef *av_buffer_pool_get(AVBufferPool *pool)
397
0
{
398
0
    AVBufferRef *ret;
399
0
    BufferPoolEntry *buf;
400
401
0
    ff_mutex_lock(&pool->mutex);
402
0
    buf = pool->pool;
403
0
    if (buf) {
404
0
        memset(&buf->buffer, 0, sizeof(buf->buffer));
405
0
        ret = buffer_create(&buf->buffer, buf->data, pool->size,
406
0
                            pool_release_buffer, buf, 0);
407
0
        if (ret) {
408
0
            pool->pool = buf->next;
409
0
            buf->next = NULL;
410
0
            buf->buffer.flags_internal |= BUFFER_FLAG_NO_FREE;
411
0
            if (buf->free == av_buffer_default_free) {
412
0
                FF_ASAN_UNPOISON(buf->data, pool->size);
413
                /* A pool that zeroes its buffers may be relied upon for never
414
                 * written regions staying zero, so only mark a reused buffer
415
                 * undefined when nothing was ever promised about its contents. */
416
0
                if (!pool->alloc2 && pool->alloc == av_buffer_alloc)
417
0
                    FF_MEM_UNDEFINED(buf->data, pool->size);
418
0
            }
419
0
        }
420
0
    } else {
421
0
        ret = pool_alloc_buffer(pool);
422
0
    }
423
0
    ff_mutex_unlock(&pool->mutex);
424
425
0
    if (ret)
426
0
        atomic_fetch_add_explicit(&pool->refcount, 1, memory_order_relaxed);
427
428
0
    return ret;
429
0
}
430
431
void *av_buffer_pool_buffer_get_opaque(const AVBufferRef *ref)
432
0
{
433
0
    BufferPoolEntry *buf = ref->buffer->opaque;
434
0
    av_assert0(buf);
435
0
    return buf->opaque;
436
0
}