FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavutil/buffer.c
Date: 2026-09-26 20:14:34
Exec Total Coverage
Lines: 211 229 92.1%
Functions: 23 23 100.0%
Branches: 80 104 76.9%

Line Branch Exec 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 5358499 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 {
34 5358499 AVBufferRef *ref = NULL;
35
36 5358499 buf->data = data;
37 5358499 buf->size = size;
38
2/2
✓ Branch 0 taken 5348794 times.
✓ Branch 1 taken 9705 times.
5358499 buf->free = free ? free : av_buffer_default_free;
39 5358499 buf->opaque = opaque;
40
41 5358499 atomic_init(&buf->refcount, 1);
42
43 5358499 buf->flags = flags;
44
45 5358499 ref = av_mallocz(sizeof(*ref));
46
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5358499 times.
5358499 if (!ref)
47 ✗ return NULL;
48
49 5358499 ref->buffer = buf;
50 5358499 ref->data = data;
51 5358499 ref->size = size;
52
53 5358499 return ref;
54 }
55
56 3885600 AVBufferRef *av_buffer_create(uint8_t *data, size_t size,
57 void (*free)(void *opaque, uint8_t *data),
58 void *opaque, int flags)
59 {
60 AVBufferRef *ret;
61 3885600 AVBuffer *buf = av_mallocz(sizeof(*buf));
62
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3885600 times.
3885600 if (!buf)
63 ✗ return NULL;
64
65 3885600 ret = buffer_create(buf, data, size, free, opaque, flags);
66
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3885600 times.
3885600 if (!ret) {
67 ✗ av_free(buf);
68 ✗ return NULL;
69 }
70 3885600 return ret;
71 }
72
73 2226410 void av_buffer_default_free(void *opaque, uint8_t *data)
74 {
75 2226410 av_free(data);
76 2226410 }
77
78 685784 AVBufferRef *av_buffer_alloc(size_t size)
79 {
80 685784 AVBufferRef *ret = NULL;
81 685784 uint8_t *data = NULL;
82
83 685784 data = av_malloc(size);
84
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 685777 times.
685784 if (!data)
85 7 return NULL;
86
87 685777 ret = av_buffer_create(data, size, av_buffer_default_free, NULL, 0);
88
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 685777 times.
685777 if (!ret)
89 ✗ av_freep(&data);
90
91 685777 return ret;
92 }
93
94 22972 AVBufferRef *av_buffer_allocz(size_t size)
95 {
96 22972 AVBufferRef *ret = av_buffer_alloc(size);
97
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 22971 times.
22972 if (!ret)
98 1 return NULL;
99
100 22971 memset(ret->data, 0, size);
101 22971 return ret;
102 }
103
104 5527317 AVBufferRef *av_buffer_ref(const AVBufferRef *buf)
105 {
106 5527317 AVBufferRef *ret = av_mallocz(sizeof(*ret));
107
108
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 5527316 times.
5527317 if (!ret)
109 1 return NULL;
110
111 5527316 *ret = *buf;
112
113 5527316 atomic_fetch_add_explicit(&buf->buffer->refcount, 1, memory_order_relaxed);
114
115 5527316 return ret;
116 }
117
118 10885815 static void buffer_replace(AVBufferRef **dst, AVBufferRef **src)
119 {
120 AVBuffer *b;
121
122 10885815 b = (*dst)->buffer;
123
124
2/2
✓ Branch 0 taken 4640 times.
✓ Branch 1 taken 10881175 times.
10885815 if (src) {
125 4640 **dst = **src;
126 4640 av_freep(src);
127 } else
128 10881175 av_freep(dst);
129
130
2/2
✓ Branch 0 taken 5358499 times.
✓ Branch 1 taken 5527316 times.
10885815 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 5358499 int free_avbuffer = !(b->flags_internal & BUFFER_FLAG_NO_FREE);
134 5358499 b->free(b->opaque, b->data);
135
2/2
✓ Branch 0 taken 3885600 times.
✓ Branch 1 taken 1472899 times.
5358499 if (free_avbuffer)
136 3885600 av_free(b);
137 }
138 10885815 }
139
140 110212104 void av_buffer_unref(AVBufferRef **buf)
141 {
142
3/4
✓ Branch 0 taken 110212104 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 99330929 times.
✓ Branch 3 taken 10881175 times.
110212104 if (!buf || !*buf)
143 99330929 return;
144
145 10881175 buffer_replace(buf, NULL);
146 }
147
148 2803212 int av_buffer_is_writable(const AVBufferRef *buf)
149 {
150
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2803211 times.
2803212 if (buf->buffer->flags & AV_BUFFER_FLAG_READONLY)
151 1 return 0;
152
153 2803211 return atomic_load(&buf->buffer->refcount) == 1;
154 }
155
156 1 void *av_buffer_get_opaque(const AVBufferRef *buf)
157 {
158 1 return buf->buffer->opaque;
159 }
160
161 145680 int av_buffer_get_ref_count(const AVBufferRef *buf)
162 {
163 145680 return atomic_load(&buf->buffer->refcount);
164 }
165
166 223 int av_buffer_make_writable(AVBufferRef **pbuf)
167 {
168 223 AVBufferRef *newbuf, *buf = *pbuf;
169
170
2/2
✓ Branch 1 taken 27 times.
✓ Branch 2 taken 196 times.
223 if (av_buffer_is_writable(buf))
171 27 return 0;
172
173 196 newbuf = av_buffer_alloc(buf->size);
174
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 196 times.
196 if (!newbuf)
175 ✗ return AVERROR(ENOMEM);
176
177 196 memcpy(newbuf->data, buf->data, buf->size);
178
179 196 buffer_replace(pbuf, &newbuf);
180
181 196 return 0;
182 }
183
184 1539138 int av_buffer_realloc(AVBufferRef **pbuf, size_t size)
185 {
186 1539138 AVBufferRef *buf = *pbuf;
187 uint8_t *tmp;
188 int ret;
189
190
2/2
✓ Branch 0 taken 1530884 times.
✓ Branch 1 taken 8254 times.
1539138 if (!buf) {
191 /* allocate a new buffer with av_realloc(), so it will be reallocatable
192 * later */
193 1530884 uint8_t *data = av_realloc(NULL, size);
194
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1530883 times.
1530884 if (!data)
195 1 return AVERROR(ENOMEM);
196
197 1530883 buf = av_buffer_create(data, size, av_buffer_default_free, NULL, 0);
198
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1530883 times.
1530883 if (!buf) {
199 ✗ av_freep(&data);
200 ✗ return AVERROR(ENOMEM);
201 }
202
203 1530883 buf->buffer->flags_internal |= BUFFER_FLAG_REALLOCATABLE;
204 1530883 *pbuf = buf;
205
206 1530883 return 0;
207
2/2
✓ Branch 0 taken 41 times.
✓ Branch 1 taken 8213 times.
8254 } else if (buf->size == size)
208 41 return 0;
209
210
4/4
✓ Branch 0 taken 3906 times.
✓ Branch 1 taken 4307 times.
✓ Branch 2 taken 3768 times.
✓ Branch 3 taken 138 times.
12119 if (!(buf->buffer->flags_internal & BUFFER_FLAG_REALLOCATABLE) ||
211
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3768 times.
7674 !av_buffer_is_writable(buf) || buf->data != buf->buffer->data) {
212 /* cannot realloc, allocate a new reallocable buffer and copy data */
213 4445 AVBufferRef *new = NULL;
214
215 4445 ret = av_buffer_realloc(&new, size);
216
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 4444 times.
4445 if (ret < 0)
217 1 return ret;
218
219 4444 memcpy(new->data, buf->data, FFMIN(size, buf->size));
220
221 4444 buffer_replace(pbuf, &new);
222 4444 return 0;
223 }
224
225 3768 tmp = av_realloc(buf->buffer->data, size);
226
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3768 times.
3768 if (!tmp)
227 ✗ return AVERROR(ENOMEM);
228
229 3768 buf->buffer->data = buf->data = tmp;
230 3768 buf->buffer->size = buf->size = size;
231 3768 return 0;
232 }
233
234 3373722 int av_buffer_replace(AVBufferRef **pdst, const AVBufferRef *src)
235 {
236 3373722 AVBufferRef *dst = *pdst;
237 AVBufferRef *tmp;
238
239
2/2
✓ Branch 0 taken 324966 times.
✓ Branch 1 taken 3048756 times.
3373722 if (!src) {
240 324966 av_buffer_unref(pdst);
241 324966 return 0;
242 }
243
244
4/4
✓ Branch 0 taken 12772 times.
✓ Branch 1 taken 3035984 times.
✓ Branch 2 taken 76 times.
✓ Branch 3 taken 12696 times.
3048756 if (dst && dst->buffer == src->buffer) {
245 /* make sure the data pointers match */
246 76 dst->data = src->data;
247 76 dst->size = src->size;
248 76 return 0;
249 }
250
251 3048680 tmp = av_buffer_ref(src);
252
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3048680 times.
3048680 if (!tmp)
253 ✗ return AVERROR(ENOMEM);
254
255 3048680 av_buffer_unref(pdst);
256 3048680 *pdst = tmp;
257 3048680 return 0;
258 }
259
260 1 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 {
264 1 AVBufferPool *pool = av_mallocz(sizeof(*pool));
265
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!pool)
266 ✗ return NULL;
267
268
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (ff_mutex_init(&pool->mutex, NULL)) {
269 ✗ av_free(pool);
270 ✗ return NULL;
271 }
272
273 1 pool->size = size;
274 1 pool->opaque = opaque;
275 1 pool->alloc2 = alloc;
276 1 pool->alloc = av_buffer_alloc; // fallback
277 1 pool->pool_free = pool_free;
278
279 1 atomic_init(&pool->refcount, 1);
280
281 1 return pool;
282 }
283
284 155233 AVBufferPool *av_buffer_pool_init(size_t size, AVBufferRef* (*alloc)(size_t size))
285 {
286 155233 AVBufferPool *pool = av_mallocz(sizeof(*pool));
287
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 155232 times.
155233 if (!pool)
288 1 return NULL;
289
290
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 155232 times.
155232 if (ff_mutex_init(&pool->mutex, NULL)) {
291 ✗ av_free(pool);
292 ✗ return NULL;
293 }
294
295 155232 pool->size = size;
296
2/2
✓ Branch 0 taken 14011 times.
✓ Branch 1 taken 141221 times.
155232 pool->alloc = alloc ? alloc : av_buffer_alloc;
297
298 155232 atomic_init(&pool->refcount, 1);
299
300 155232 return pool;
301 }
302
303 310466 static void buffer_pool_flush(AVBufferPool *pool)
304 {
305
2/2
✓ Branch 0 taken 224236 times.
✓ Branch 1 taken 310466 times.
534702 while (pool->pool) {
306 224236 BufferPoolEntry *buf = pool->pool;
307 224236 pool->pool = buf->next;
308
309 224236 if (buf->free == av_buffer_default_free)
310 FF_ASAN_UNPOISON(buf->data, pool->size);
311 224236 buf->free(buf->opaque, buf->data);
312 224236 av_freep(&buf);
313 }
314 310466 }
315
316 /*
317 * This function gets called when the pool has been uninited and
318 * all the buffers returned to it.
319 */
320 155233 static void buffer_pool_free(AVBufferPool *pool)
321 {
322 155233 buffer_pool_flush(pool);
323 155233 ff_mutex_destroy(&pool->mutex);
324
325
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 155232 times.
155233 if (pool->pool_free)
326 1 pool->pool_free(pool->opaque);
327
328 155233 av_freep(&pool);
329 155233 }
330
331 888375 void av_buffer_pool_uninit(AVBufferPool **ppool)
332 {
333 AVBufferPool *pool;
334
335
3/4
✓ Branch 0 taken 888375 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 733142 times.
✓ Branch 3 taken 155233 times.
888375 if (!ppool || !*ppool)
336 733142 return;
337 155233 pool = *ppool;
338 155233 *ppool = NULL;
339
340 155233 ff_mutex_lock(&pool->mutex);
341 155233 buffer_pool_flush(pool);
342 155233 ff_mutex_unlock(&pool->mutex);
343
344
2/2
✓ Branch 0 taken 125687 times.
✓ Branch 1 taken 29546 times.
155233 if (atomic_fetch_sub_explicit(&pool->refcount, 1, memory_order_acq_rel) == 1)
345 125687 buffer_pool_free(pool);
346 }
347
348 1697135 static void pool_release_buffer(void *opaque, uint8_t *data)
349 {
350 1697135 BufferPoolEntry *buf = opaque;
351 1697135 AVBufferPool *pool = buf->pool;
352
353 1697135 if (buf->free == av_buffer_default_free)
354 FF_ASAN_POISON(buf->data, pool->size);
355
356 1697135 ff_mutex_lock(&pool->mutex);
357 1697135 buf->next = pool->pool;
358 1697135 pool->pool = buf;
359 1697135 ff_mutex_unlock(&pool->mutex);
360
361
2/2
✓ Branch 0 taken 29546 times.
✓ Branch 1 taken 1667589 times.
1697135 if (atomic_fetch_sub_explicit(&pool->refcount, 1, memory_order_acq_rel) == 1)
362 29546 buffer_pool_free(pool);
363 1697135 }
364
365 /* allocate a new buffer and override its free() callback so that
366 * it is returned to the pool on free */
367 224236 static AVBufferRef *pool_alloc_buffer(AVBufferPool *pool)
368 {
369 BufferPoolEntry *buf;
370 AVBufferRef *ret;
371
372
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 224236 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
224236 av_assert0(pool->alloc || pool->alloc2);
373
374
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 224235 times.
224236 ret = pool->alloc2 ? pool->alloc2(pool->opaque, pool->size) :
375 224235 pool->alloc(pool->size);
376
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 224236 times.
224236 if (!ret)
377 ✗ return NULL;
378
379 224236 buf = av_mallocz(sizeof(*buf));
380
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 224236 times.
224236 if (!buf) {
381 ✗ av_buffer_unref(&ret);
382 ✗ return NULL;
383 }
384
385 224236 buf->data = ret->buffer->data;
386 224236 buf->opaque = ret->buffer->opaque;
387 224236 buf->free = ret->buffer->free;
388 224236 buf->pool = pool;
389
390 224236 ret->buffer->opaque = buf;
391 224236 ret->buffer->free = pool_release_buffer;
392
393 224236 return ret;
394 }
395
396 1697135 AVBufferRef *av_buffer_pool_get(AVBufferPool *pool)
397 {
398 AVBufferRef *ret;
399 BufferPoolEntry *buf;
400
401 1697135 ff_mutex_lock(&pool->mutex);
402 1697135 buf = pool->pool;
403
2/2
✓ Branch 0 taken 1472899 times.
✓ Branch 1 taken 224236 times.
1697135 if (buf) {
404 1472899 memset(&buf->buffer, 0, sizeof(buf->buffer));
405 1472899 ret = buffer_create(&buf->buffer, buf->data, pool->size,
406 pool_release_buffer, buf, 0);
407
1/2
✓ Branch 0 taken 1472899 times.
✗ Branch 1 not taken.
1472899 if (ret) {
408 1472899 pool->pool = buf->next;
409 1472899 buf->next = NULL;
410 1472899 buf->buffer.flags_internal |= BUFFER_FLAG_NO_FREE;
411
1/2
✓ Branch 0 taken 1472899 times.
✗ Branch 1 not taken.
1472899 if (buf->free == av_buffer_default_free) {
412 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
3/4
✓ Branch 0 taken 1472899 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1192424 times.
✓ Branch 3 taken 280475 times.
1472899 if (!pool->alloc2 && pool->alloc == av_buffer_alloc)
417 1192424 FF_MEM_UNDEFINED(buf->data, pool->size);
418 }
419 }
420 } else {
421 224236 ret = pool_alloc_buffer(pool);
422 }
423 1697135 ff_mutex_unlock(&pool->mutex);
424
425
1/2
✓ Branch 0 taken 1697135 times.
✗ Branch 1 not taken.
1697135 if (ret)
426 1697135 atomic_fetch_add_explicit(&pool->refcount, 1, memory_order_relaxed);
427
428 1697135 return ret;
429 }
430
431 1 void *av_buffer_pool_buffer_get_opaque(const AVBufferRef *ref)
432 {
433 1 BufferPoolEntry *buf = ref->buffer->opaque;
434
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 av_assert0(buf);
435 1 return buf->opaque;
436 }
437