FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavutil/buffer.c
Date: 2026-09-26 14:26:21
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 5358291 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 5358291 AVBufferRef *ref = NULL;
35
36 5358291 buf->data = data;
37 5358291 buf->size = size;
38
2/2
✓ Branch 0 taken 5348586 times.
✓ Branch 1 taken 9705 times.
5358291 buf->free = free ? free : av_buffer_default_free;
39 5358291 buf->opaque = opaque;
40
41 5358291 atomic_init(&buf->refcount, 1);
42
43 5358291 buf->flags = flags;
44
45 5358291 ref = av_mallocz(sizeof(*ref));
46
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5358291 times.
5358291 if (!ref)
47 ✗ return NULL;
48
49 5358291 ref->buffer = buf;
50 5358291 ref->data = data;
51 5358291 ref->size = size;
52
53 5358291 return ref;
54 }
55
56 3885805 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 3885805 AVBuffer *buf = av_mallocz(sizeof(*buf));
62
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3885805 times.
3885805 if (!buf)
63 ✗ return NULL;
64
65 3885805 ret = buffer_create(buf, data, size, free, opaque, flags);
66
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3885805 times.
3885805 if (!ret) {
67 ✗ av_free(buf);
68 ✗ return NULL;
69 }
70 3885805 return ret;
71 }
72
73 2227062 void av_buffer_default_free(void *opaque, uint8_t *data)
74 {
75 2227062 av_free(data);
76 2227062 }
77
78 686421 AVBufferRef *av_buffer_alloc(size_t size)
79 {
80 686421 AVBufferRef *ret = NULL;
81 686421 uint8_t *data = NULL;
82
83 686421 data = av_malloc(size);
84
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 686414 times.
686421 if (!data)
85 7 return NULL;
86
87 686414 ret = av_buffer_create(data, size, av_buffer_default_free, NULL, 0);
88
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 686414 times.
686414 if (!ret)
89 ✗ av_freep(&data);
90
91 686414 return ret;
92 }
93
94 23206 AVBufferRef *av_buffer_allocz(size_t size)
95 {
96 23206 AVBufferRef *ret = av_buffer_alloc(size);
97
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 23205 times.
23206 if (!ret)
98 1 return NULL;
99
100 23205 memset(ret->data, 0, size);
101 23205 return ret;
102 }
103
104 5527387 AVBufferRef *av_buffer_ref(const AVBufferRef *buf)
105 {
106 5527387 AVBufferRef *ret = av_mallocz(sizeof(*ret));
107
108
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 5527386 times.
5527387 if (!ret)
109 1 return NULL;
110
111 5527386 *ret = *buf;
112
113 5527386 atomic_fetch_add_explicit(&buf->buffer->refcount, 1, memory_order_relaxed);
114
115 5527386 return ret;
116 }
117
118 10885677 static void buffer_replace(AVBufferRef **dst, AVBufferRef **src)
119 {
120 AVBuffer *b;
121
122 10885677 b = (*dst)->buffer;
123
124
2/2
✓ Branch 0 taken 4641 times.
✓ Branch 1 taken 10881036 times.
10885677 if (src) {
125 4641 **dst = **src;
126 4641 av_freep(src);
127 } else
128 10881036 av_freep(dst);
129
130
2/2
✓ Branch 0 taken 5358291 times.
✓ Branch 1 taken 5527386 times.
10885677 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 5358291 int free_avbuffer = !(b->flags_internal & BUFFER_FLAG_NO_FREE);
134 5358291 b->free(b->opaque, b->data);
135
2/2
✓ Branch 0 taken 3885805 times.
✓ Branch 1 taken 1472486 times.
5358291 if (free_avbuffer)
136 3885805 av_free(b);
137 }
138 10885677 }
139
140 110213540 void av_buffer_unref(AVBufferRef **buf)
141 {
142
3/4
✓ Branch 0 taken 110213540 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 99332504 times.
✓ Branch 3 taken 10881036 times.
110213540 if (!buf || !*buf)
143 99332504 return;
144
145 10881036 buffer_replace(buf, NULL);
146 }
147
148 2803252 int av_buffer_is_writable(const AVBufferRef *buf)
149 {
150
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2803251 times.
2803252 if (buf->buffer->flags & AV_BUFFER_FLAG_READONLY)
151 1 return 0;
152
153 2803251 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 26 times.
✓ Branch 2 taken 197 times.
223 if (av_buffer_is_writable(buf))
171 26 return 0;
172
173 197 newbuf = av_buffer_alloc(buf->size);
174
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 197 times.
197 if (!newbuf)
175 ✗ return AVERROR(ENOMEM);
176
177 197 memcpy(newbuf->data, buf->data, buf->size);
178
179 197 buffer_replace(pbuf, &newbuf);
180
181 197 return 0;
182 }
183
184 1539153 int av_buffer_realloc(AVBufferRef **pbuf, size_t size)
185 {
186 1539153 AVBufferRef *buf = *pbuf;
187 uint8_t *tmp;
188 int ret;
189
190
2/2
✓ Branch 0 taken 1530899 times.
✓ Branch 1 taken 8254 times.
1539153 if (!buf) {
191 /* allocate a new buffer with av_realloc(), so it will be reallocatable
192 * later */
193 1530899 uint8_t *data = av_realloc(NULL, size);
194
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1530898 times.
1530899 if (!data)
195 1 return AVERROR(ENOMEM);
196
197 1530898 buf = av_buffer_create(data, size, av_buffer_default_free, NULL, 0);
198
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1530898 times.
1530898 if (!buf) {
199 ✗ av_freep(&data);
200 ✗ return AVERROR(ENOMEM);
201 }
202
203 1530898 buf->buffer->flags_internal |= BUFFER_FLAG_REALLOCATABLE;
204 1530898 *pbuf = buf;
205
206 1530898 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 3373768 int av_buffer_replace(AVBufferRef **pdst, const AVBufferRef *src)
235 {
236 3373768 AVBufferRef *dst = *pdst;
237 AVBufferRef *tmp;
238
239
2/2
✓ Branch 0 taken 324945 times.
✓ Branch 1 taken 3048823 times.
3373768 if (!src) {
240 324945 av_buffer_unref(pdst);
241 324945 return 0;
242 }
243
244
4/4
✓ Branch 0 taken 12705 times.
✓ Branch 1 taken 3036118 times.
✓ Branch 2 taken 76 times.
✓ Branch 3 taken 12629 times.
3048823 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 3048747 tmp = av_buffer_ref(src);
252
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3048747 times.
3048747 if (!tmp)
253 ✗ return AVERROR(ENOMEM);
254
255 3048747 av_buffer_unref(pdst);
256 3048747 *pdst = tmp;
257 3048747 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 155236 AVBufferPool *av_buffer_pool_init(size_t size, AVBufferRef* (*alloc)(size_t size))
285 {
286 155236 AVBufferPool *pool = av_mallocz(sizeof(*pool));
287
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 155235 times.
155236 if (!pool)
288 1 return NULL;
289
290
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 155235 times.
155235 if (ff_mutex_init(&pool->mutex, NULL)) {
291 ✗ av_free(pool);
292 ✗ return NULL;
293 }
294
295 155235 pool->size = size;
296
2/2
✓ Branch 0 taken 14013 times.
✓ Branch 1 taken 141222 times.
155235 pool->alloc = alloc ? alloc : av_buffer_alloc;
297
298 155235 atomic_init(&pool->refcount, 1);
299
300 155235 return pool;
301 }
302
303 310472 static void buffer_pool_flush(AVBufferPool *pool)
304 {
305
2/2
✓ Branch 0 taken 224873 times.
✓ Branch 1 taken 310472 times.
535345 while (pool->pool) {
306 224873 BufferPoolEntry *buf = pool->pool;
307 224873 pool->pool = buf->next;
308
309 224873 if (buf->free == av_buffer_default_free)
310 FF_ASAN_UNPOISON(buf->data, pool->size);
311 224873 buf->free(buf->opaque, buf->data);
312 224873 av_freep(&buf);
313 }
314 310472 }
315
316 /*
317 * This function gets called when the pool has been uninited and
318 * all the buffers returned to it.
319 */
320 155236 static void buffer_pool_free(AVBufferPool *pool)
321 {
322 155236 buffer_pool_flush(pool);
323 155236 ff_mutex_destroy(&pool->mutex);
324
325
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 155235 times.
155236 if (pool->pool_free)
326 1 pool->pool_free(pool->opaque);
327
328 155236 av_freep(&pool);
329 155236 }
330
331 888395 void av_buffer_pool_uninit(AVBufferPool **ppool)
332 {
333 AVBufferPool *pool;
334
335
3/4
✓ Branch 0 taken 888395 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 733159 times.
✓ Branch 3 taken 155236 times.
888395 if (!ppool || !*ppool)
336 733159 return;
337 155236 pool = *ppool;
338 155236 *ppool = NULL;
339
340 155236 ff_mutex_lock(&pool->mutex);
341 155236 buffer_pool_flush(pool);
342 155236 ff_mutex_unlock(&pool->mutex);
343
344
2/2
✓ Branch 0 taken 125003 times.
✓ Branch 1 taken 30233 times.
155236 if (atomic_fetch_sub_explicit(&pool->refcount, 1, memory_order_acq_rel) == 1)
345 125003 buffer_pool_free(pool);
346 }
347
348 1697359 static void pool_release_buffer(void *opaque, uint8_t *data)
349 {
350 1697359 BufferPoolEntry *buf = opaque;
351 1697359 AVBufferPool *pool = buf->pool;
352
353 1697359 if (buf->free == av_buffer_default_free)
354 FF_ASAN_POISON(buf->data, pool->size);
355
356 1697359 ff_mutex_lock(&pool->mutex);
357 1697359 buf->next = pool->pool;
358 1697359 pool->pool = buf;
359 1697359 ff_mutex_unlock(&pool->mutex);
360
361
2/2
✓ Branch 0 taken 30233 times.
✓ Branch 1 taken 1667126 times.
1697359 if (atomic_fetch_sub_explicit(&pool->refcount, 1, memory_order_acq_rel) == 1)
362 30233 buffer_pool_free(pool);
363 1697359 }
364
365 /* allocate a new buffer and override its free() callback so that
366 * it is returned to the pool on free */
367 224873 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 224873 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
224873 av_assert0(pool->alloc || pool->alloc2);
373
374
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 224872 times.
224873 ret = pool->alloc2 ? pool->alloc2(pool->opaque, pool->size) :
375 224872 pool->alloc(pool->size);
376
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 224873 times.
224873 if (!ret)
377 ✗ return NULL;
378
379 224873 buf = av_mallocz(sizeof(*buf));
380
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 224873 times.
224873 if (!buf) {
381 ✗ av_buffer_unref(&ret);
382 ✗ return NULL;
383 }
384
385 224873 buf->data = ret->buffer->data;
386 224873 buf->opaque = ret->buffer->opaque;
387 224873 buf->free = ret->buffer->free;
388 224873 buf->pool = pool;
389
390 224873 ret->buffer->opaque = buf;
391 224873 ret->buffer->free = pool_release_buffer;
392
393 224873 return ret;
394 }
395
396 1697359 AVBufferRef *av_buffer_pool_get(AVBufferPool *pool)
397 {
398 AVBufferRef *ret;
399 BufferPoolEntry *buf;
400
401 1697359 ff_mutex_lock(&pool->mutex);
402 1697359 buf = pool->pool;
403
2/2
✓ Branch 0 taken 1472486 times.
✓ Branch 1 taken 224873 times.
1697359 if (buf) {
404 1472486 memset(&buf->buffer, 0, sizeof(buf->buffer));
405 1472486 ret = buffer_create(&buf->buffer, buf->data, pool->size,
406 pool_release_buffer, buf, 0);
407
1/2
✓ Branch 0 taken 1472486 times.
✗ Branch 1 not taken.
1472486 if (ret) {
408 1472486 pool->pool = buf->next;
409 1472486 buf->next = NULL;
410 1472486 buf->buffer.flags_internal |= BUFFER_FLAG_NO_FREE;
411
1/2
✓ Branch 0 taken 1472486 times.
✗ Branch 1 not taken.
1472486 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 1472486 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1192298 times.
✓ Branch 3 taken 280188 times.
1472486 if (!pool->alloc2 && pool->alloc == av_buffer_alloc)
417 1192298 FF_MEM_UNDEFINED(buf->data, pool->size);
418 }
419 }
420 } else {
421 224873 ret = pool_alloc_buffer(pool);
422 }
423 1697359 ff_mutex_unlock(&pool->mutex);
424
425
1/2
✓ Branch 0 taken 1697359 times.
✗ Branch 1 not taken.
1697359 if (ret)
426 1697359 atomic_fetch_add_explicit(&pool->refcount, 1, memory_order_relaxed);
427
428 1697359 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