FFmpeg coverage


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