FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavutil/buffer.c
Date: 2026-10-30 17:37:53
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 6133689 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 6133689 AVBufferRef *ref = NULL;
35
36 6133689 buf->data = data;
37 6133689 buf->size = size;
38
2/2
✓ Branch 0 taken 6123970 times.
✓ Branch 1 taken 9719 times.
6133689 buf->free = free ? free : av_buffer_default_free;
39 6133689 buf->opaque = opaque;
40
41 6133689 atomic_init(&buf->refcount, 1);
42
43 6133689 buf->flags = flags;
44
45 6133689 ref = av_mallocz(sizeof(*ref));
46
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6133689 times.
6133689 if (!ref)
47 ✗ return NULL;
48
49 6133689 ref->buffer = buf;
50 6133689 ref->data = data;
51 6133689 ref->size = size;
52
53 6133689 return ref;
54 }
55
56 4287947 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 4287947 AVBuffer *buf = av_mallocz(sizeof(*buf));
62
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4287947 times.
4287947 if (!buf)
63 ✗ return NULL;
64
65 4287947 ret = buffer_create(buf, data, size, free, opaque, flags);
66
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4287947 times.
4287947 if (!ret) {
67 ✗ av_free(buf);
68 ✗ return NULL;
69 }
70 4287947 return ret;
71 }
72
73 2608695 void av_buffer_default_free(void *opaque, uint8_t *data)
74 {
75 2608695 av_free(data);
76 2608695 }
77
78 1053665 AVBufferRef *av_buffer_alloc(size_t size)
79 {
80 1053665 AVBufferRef *ret = NULL;
81 1053665 uint8_t *data = NULL;
82
83 1053665 data = av_malloc(size);
84
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 1053658 times.
1053665 if (!data)
85 7 return NULL;
86
87 1053658 ret = av_buffer_create(data, size, av_buffer_default_free, NULL, 0);
88
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1053658 times.
1053658 if (!ret)
89 ✗ av_freep(&data);
90
91 1053658 return ret;
92 }
93
94 23823 AVBufferRef *av_buffer_allocz(size_t size)
95 {
96 23823 AVBufferRef *ret = av_buffer_alloc(size);
97
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 23822 times.
23823 if (!ret)
98 1 return NULL;
99
100 23822 memset(ret->data, 0, size);
101 23822 return ret;
102 }
103
104 5570807 AVBufferRef *av_buffer_ref(const AVBufferRef *buf)
105 {
106 5570807 AVBufferRef *ret = av_mallocz(sizeof(*ret));
107
108
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 5570806 times.
5570807 if (!ret)
109 1 return NULL;
110
111 5570806 *ret = *buf;
112
113 5570806 atomic_fetch_add_explicit(&buf->buffer->refcount, 1, memory_order_relaxed);
114
115 5570806 return ret;
116 }
117
118 11704495 static void buffer_replace(AVBufferRef **dst, AVBufferRef **src)
119 {
120 AVBuffer *b;
121
122 11704495 b = (*dst)->buffer;
123
124
2/2
✓ Branch 0 taken 5359 times.
✓ Branch 1 taken 11699136 times.
11704495 if (src) {
125 5359 **dst = **src;
126 5359 av_freep(src);
127 } else
128 11699136 av_freep(dst);
129
130
2/2
✓ Branch 0 taken 6133689 times.
✓ Branch 1 taken 5570806 times.
11704495 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 6133689 int free_avbuffer = !(b->flags_internal & BUFFER_FLAG_NO_FREE);
134 6133689 b->free(b->opaque, b->data);
135
2/2
✓ Branch 0 taken 4287947 times.
✓ Branch 1 taken 1845742 times.
6133689 if (free_avbuffer)
136 4287947 av_free(b);
137 }
138 11704495 }
139
140 116557560 void av_buffer_unref(AVBufferRef **buf)
141 {
142
3/4
✓ Branch 0 taken 116557560 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 104858424 times.
✓ Branch 3 taken 11699136 times.
116557560 if (!buf || !*buf)
143 104858424 return;
144
145 11699136 buffer_replace(buf, NULL);
146 }
147
148 2827166 int av_buffer_is_writable(const AVBufferRef *buf)
149 {
150
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2827165 times.
2827166 if (buf->buffer->flags & AV_BUFFER_FLAG_READONLY)
151 1 return 0;
152
153 2827165 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 146147 int av_buffer_get_ref_count(const AVBufferRef *buf)
162 {
163 146147 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 1554067 int av_buffer_realloc(AVBufferRef **pbuf, size_t size)
185 {
186 1554067 AVBufferRef *buf = *pbuf;
187 uint8_t *tmp;
188 int ret;
189
190
2/2
✓ Branch 0 taken 1545274 times.
✓ Branch 1 taken 8793 times.
1554067 if (!buf) {
191 /* allocate a new buffer with av_realloc(), so it will be reallocatable
192 * later */
193 1545274 uint8_t *data = av_realloc(NULL, size);
194
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1545273 times.
1545274 if (!data)
195 1 return AVERROR(ENOMEM);
196
197 1545273 buf = av_buffer_create(data, size, av_buffer_default_free, NULL, 0);
198
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1545273 times.
1545273 if (!buf) {
199 ✗ av_freep(&data);
200 ✗ return AVERROR(ENOMEM);
201 }
202
203 1545273 buf->buffer->flags_internal |= BUFFER_FLAG_REALLOCATABLE;
204 1545273 *pbuf = buf;
205
206 1545273 return 0;
207
2/2
✓ Branch 0 taken 43 times.
✓ Branch 1 taken 8750 times.
8793 } else if (buf->size == size)
208 43 return 0;
209
210
4/4
✓ Branch 0 taken 3724 times.
✓ Branch 1 taken 5026 times.
✓ Branch 2 taken 3586 times.
✓ Branch 3 taken 138 times.
12474 if (!(buf->buffer->flags_internal & BUFFER_FLAG_REALLOCATABLE) ||
211
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3586 times.
7310 !av_buffer_is_writable(buf) || buf->data != buf->buffer->data) {
212 /* cannot realloc, allocate a new reallocable buffer and copy data */
213 5164 AVBufferRef *new = NULL;
214
215 5164 ret = av_buffer_realloc(&new, size);
216
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 5163 times.
5164 if (ret < 0)
217 1 return ret;
218
219 5163 memcpy(new->data, buf->data, FFMIN(size, buf->size));
220
221 5163 buffer_replace(pbuf, &new);
222 5163 return 0;
223 }
224
225 3586 tmp = av_realloc(buf->buffer->data, size);
226
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3586 times.
3586 if (!tmp)
227 ✗ return AVERROR(ENOMEM);
228
229 3586 buf->buffer->data = buf->data = tmp;
230 3586 buf->buffer->size = buf->size = size;
231 3586 return 0;
232 }
233
234 3662676 int av_buffer_replace(AVBufferRef **pdst, const AVBufferRef *src)
235 {
236 3662676 AVBufferRef *dst = *pdst;
237 AVBufferRef *tmp;
238
239
2/2
✓ Branch 0 taken 586604 times.
✓ Branch 1 taken 3076072 times.
3662676 if (!src) {
240 586604 av_buffer_unref(pdst);
241 586604 return 0;
242 }
243
244
4/4
✓ Branch 0 taken 12781 times.
✓ Branch 1 taken 3063291 times.
✓ Branch 2 taken 76 times.
✓ Branch 3 taken 12705 times.
3076072 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 3075996 tmp = av_buffer_ref(src);
252
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3075996 times.
3075996 if (!tmp)
253 ✗ return AVERROR(ENOMEM);
254
255 3075996 av_buffer_unref(pdst);
256 3075996 *pdst = tmp;
257 3075996 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 315414 AVBufferPool *av_buffer_pool_init(size_t size, AVBufferRef* (*alloc)(size_t size))
285 {
286 315414 AVBufferPool *pool = av_mallocz(sizeof(*pool));
287
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 315413 times.
315414 if (!pool)
288 1 return NULL;
289
290
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 315413 times.
315413 if (ff_mutex_init(&pool->mutex, NULL)) {
291 ✗ av_free(pool);
292 ✗ return NULL;
293 }
294
295 315413 pool->size = size;
296
2/2
✓ Branch 0 taken 14515 times.
✓ Branch 1 taken 300898 times.
315413 pool->alloc = alloc ? alloc : av_buffer_alloc;
297
298 315413 atomic_init(&pool->refcount, 1);
299
300 315413 return pool;
301 }
302
303 630828 static void buffer_pool_flush(AVBufferPool *pool)
304 {
305
2/2
✓ Branch 0 taken 295588 times.
✓ Branch 1 taken 630828 times.
926416 while (pool->pool) {
306 295588 BufferPoolEntry *buf = pool->pool;
307 295588 pool->pool = buf->next;
308
309 295588 if (buf->free == av_buffer_default_free)
310 FF_ASAN_UNPOISON(buf->data, pool->size);
311 295588 buf->free(buf->opaque, buf->data);
312 295588 av_freep(&buf);
313 }
314 630828 }
315
316 /*
317 * This function gets called when the pool has been uninited and
318 * all the buffers returned to it.
319 */
320 315414 static void buffer_pool_free(AVBufferPool *pool)
321 {
322 315414 buffer_pool_flush(pool);
323 315414 ff_mutex_destroy(&pool->mutex);
324
325
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 315413 times.
315414 if (pool->pool_free)
326 1 pool->pool_free(pool->opaque);
327
328 315414 av_freep(&pool);
329 315414 }
330
331 1623215 void av_buffer_pool_uninit(AVBufferPool **ppool)
332 {
333 AVBufferPool *pool;
334
335
3/4
✓ Branch 0 taken 1623215 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1307801 times.
✓ Branch 3 taken 315414 times.
1623215 if (!ppool || !*ppool)
336 1307801 return;
337 315414 pool = *ppool;
338 315414 *ppool = NULL;
339
340 315414 ff_mutex_lock(&pool->mutex);
341 315414 buffer_pool_flush(pool);
342 315414 ff_mutex_unlock(&pool->mutex);
343
344
2/2
✓ Branch 0 taken 285504 times.
✓ Branch 1 taken 29910 times.
315414 if (atomic_fetch_sub_explicit(&pool->refcount, 1, memory_order_acq_rel) == 1)
345 285504 buffer_pool_free(pool);
346 }
347
348 2141330 static void pool_release_buffer(void *opaque, uint8_t *data)
349 {
350 2141330 BufferPoolEntry *buf = opaque;
351 2141330 AVBufferPool *pool = buf->pool;
352
353 2141330 if (buf->free == av_buffer_default_free)
354 FF_ASAN_POISON(buf->data, pool->size);
355
356 2141330 ff_mutex_lock(&pool->mutex);
357 2141330 buf->next = pool->pool;
358 2141330 pool->pool = buf;
359 2141330 ff_mutex_unlock(&pool->mutex);
360
361
2/2
✓ Branch 0 taken 29910 times.
✓ Branch 1 taken 2111420 times.
2141330 if (atomic_fetch_sub_explicit(&pool->refcount, 1, memory_order_acq_rel) == 1)
362 29910 buffer_pool_free(pool);
363 2141330 }
364
365 /* allocate a new buffer and override its free() callback so that
366 * it is returned to the pool on free */
367 295588 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 295588 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
295588 av_assert0(pool->alloc || pool->alloc2);
373
374
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 295587 times.
295588 ret = pool->alloc2 ? pool->alloc2(pool->opaque, pool->size) :
375 295587 pool->alloc(pool->size);
376
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 295588 times.
295588 if (!ret)
377 ✗ return NULL;
378
379 295588 buf = av_mallocz(sizeof(*buf));
380
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 295588 times.
295588 if (!buf) {
381 ✗ av_buffer_unref(&ret);
382 ✗ return NULL;
383 }
384
385 295588 buf->data = ret->buffer->data;
386 295588 buf->opaque = ret->buffer->opaque;
387 295588 buf->free = ret->buffer->free;
388 295588 buf->pool = pool;
389
390 295588 ret->buffer->opaque = buf;
391 295588 ret->buffer->free = pool_release_buffer;
392
393 295588 return ret;
394 }
395
396 2141330 AVBufferRef *av_buffer_pool_get(AVBufferPool *pool)
397 {
398 AVBufferRef *ret;
399 BufferPoolEntry *buf;
400
401 2141330 ff_mutex_lock(&pool->mutex);
402 2141330 buf = pool->pool;
403
2/2
✓ Branch 0 taken 1845742 times.
✓ Branch 1 taken 295588 times.
2141330 if (buf) {
404 1845742 memset(&buf->buffer, 0, sizeof(buf->buffer));
405 1845742 ret = buffer_create(&buf->buffer, buf->data, pool->size,
406 pool_release_buffer, buf, 0);
407
1/2
✓ Branch 0 taken 1845742 times.
✗ Branch 1 not taken.
1845742 if (ret) {
408 1845742 pool->pool = buf->next;
409 1845742 buf->next = NULL;
410 1845742 buf->buffer.flags_internal |= BUFFER_FLAG_NO_FREE;
411
1/2
✓ Branch 0 taken 1845742 times.
✗ Branch 1 not taken.
1845742 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 1845742 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1558826 times.
✓ Branch 3 taken 286916 times.
1845742 if (!pool->alloc2 && pool->alloc == av_buffer_alloc)
417 1558826 FF_MEM_UNDEFINED(buf->data, pool->size);
418 }
419 }
420 } else {
421 295588 ret = pool_alloc_buffer(pool);
422 }
423 2141330 ff_mutex_unlock(&pool->mutex);
424
425
1/2
✓ Branch 0 taken 2141330 times.
✗ Branch 1 not taken.
2141330 if (ret)
426 2141330 atomic_fetch_add_explicit(&pool->refcount, 1, memory_order_relaxed);
427
428 2141330 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