Commit 1871bd1
committed
record/drivers/record_ffmpeg: fix OOM bugs in audio/video buffer init, resample, and flush
Six OOM bugs across record_ffmpeg.c. Most have the same
underlying pattern: av_realloc is used as 'av_realloc(x, ...)'
with the result self-assigned to x, which on OOM both leaks
the old buffer AND leaves an associated *_frames / size
counter claiming the grown size while the allocation is the
old size (or NULL). On the next call the 'size > counter'
growth-guard is skipped, and a stale NULL is passed to the
downstream memcpy / fifo_read / convert call.
=== planarize_audio: planar_buf realloc-assign-self ===
handle->audio.planar_buf = av_realloc(handle->audio.planar_buf,
...);
if (!handle->audio.planar_buf)
return;
handle->audio.planar_buf_frames = handle->audio.frames_in_buffer;
On OOM: planar_buf goes NULL (old buffer leaked), function
returns without bumping planar_buf_frames. Next call with
frames_in_buffer <= planar_buf_frames would skip the realloc
branch and fall through to planarize_float/s16 with a NULL
planar_buf.
Fix: realloc-to-tmp.
=== ffmpeg_audio_resample: three stacked realloc-assign-selfs ===
handle->audio.float_conv = av_realloc(handle->audio.float_conv, ...);
if (!handle->audio.float_conv) return;
handle->audio.float_conv_frames = aud->frames;
/* ... */
handle->audio.resample_out_frames = aud->frames * ratio + 16;
handle->audio.resample_out = av_realloc(handle->audio.resample_out, ...);
if (!handle->audio.resample_out) return;
/* ... */
handle->audio.fixed_conv_frames = MAX(...);
handle->audio.fixed_conv = av_realloc(handle->audio.fixed_conv, ...);
Same pattern, three times in one 'if (aud->frames > float_conv_frames)'
block. Worst case: first realloc succeeds and bumps
float_conv_frames to aud->frames; second realloc fails;
function returns with float_conv grown, resample_out still
old-size (or NULL), float_conv_frames claiming new size.
Next call with aud->frames == float_conv_frames skips the
entire block and passes the stale resample_out to
convert_s16_to_float / convert_float_to_s16 downstream.
Fix: realloc each into a local tmp (new_float_conv,
new_resample_out, new_fixed_conv), commit each pointer
only after its own alloc succeeds, and defer the
*_frames counter bumps until after their corresponding
allocation has succeeded.
=== ffmpeg_init_video: video->outbuf + conv_frame_buf unchecked ===
video->outbuf_size = 1 << 23;
video->outbuf = (uint8_t*)av_malloc(video->outbuf_size);
video->frame_drop_ratio = params->frame_drop_ratio;
...
video->conv_frame_buf = (uint8_t*)av_malloc(size);
memset(video->conv_frame_buf, 0, size);
Both unchecked. conv_frame_buf is immediately NULL-deref'd
by the memset, and av_image_fill_arrays a few lines down
writes frame->data pointers based on it. outbuf is used
later by the encoder's avcodec_encode_video calls.
Fix: NULL-check both, return false. The caller
(ffmpeg_new) already has 'if (!ffmpeg_init_video(handle))
goto error' at line 1073, and the error label calls
ffmpeg_free which handles partial-init state via av_freep
guards.
=== ffmpeg_init_muxer_pre: ctx + ctx->url unchecked ===
ctx = avformat_alloc_context();
handle->muxer.ctx = ctx;
...
ctx->url = (char*)av_malloc(_len);
strlcpy(ctx->url, handle->params.filename, _len);
avformat_alloc_context can return NULL on OOM; the immediate
'ctx->url = ...' NULL-derefs. av_malloc for ctx->url is also
unchecked; strlcpy NULL-derefs on OOM.
Fix: two NULL-checks, return false. ffmpeg_free cleans up
handle->muxer.ctx via avformat_free_context which NULL-
tolerates and frees ctx->url as part of its teardown.
=== ffmpeg_flush_buffers: audio_buf + video_buf unchecked ===
void *video_buf = av_malloc(...);
...
if (audio_buf_size)
audio_buf = av_malloc(audio_buf_size);
do {
if (handle->config.audio_enable)
if (FIFO_READ_AVAIL(...) >= audio_buf_size)
fifo_read(handle->audio_fifo, audio_buf, audio_buf_size); /* NULL-deref */
if (FIFO_READ_AVAIL(handle->attr_fifo) >= sizeof(attr_buf))
fifo_read(..., video_buf, ...); /* NULL-deref */
} while (did_work);
Both av_malloc sites unchecked. fifo_read does memcpy into
the destination pointer (see libretro-common/queues/
fifo_queue.c) which NULL-derefs on a NULL destination.
Fix: gate both fifo drain branches on 'buf != NULL'. Also
gate the final ffmpeg_flush_audio call on audio_buf != NULL
since its internal fifo_read path would NULL-deref the same
way.
The pattern of NULL-gating is already in place in the sibling
ffmpeg_thread function (lines 1791, 1804 pre-patch) which got
it right; this patch just brings ffmpeg_flush_buffers in line
with that.
=== Not a bug, verified clean ===
Other av_malloc / av_realloc sites audited this pass:
* record_ffmpeg.c:379, 388 (ffmpeg_init_audio audio->buffer /
outbuf) - NULL-checked and symmetric with a goto-error-free
teardown via ffmpeg_free.
* record_ffmpeg.c:1036 (ffmpeg_new handle calloc) - NULL-
checked.
* record_ffmpeg.c:1744, 1748 (ffmpeg_thread video_buf /
audio_buf) - unchecked allocs but already NULL-gated at
every subsequent use site (lines 1791, 1804).
Other subdirs swept this pass:
* wifi/drivers/ (connmanctl.c, nmcli.c) - zero raw alloc
sites, both drivers use shell-exec pattern.
* record/drivers/record_wav.c - zero raw alloc sites.
* location/drivers/android.c:40 - the single
androidlocation_t calloc is NULL-checked with a 'dealloc'
goto label that properly unwinds.
=== Thread-safety ===
planarize_audio and ffmpeg_audio_resample run on the record
worker thread (ffmpeg_thread) or the main thread during
finalize. ffmpeg_init_video and ffmpeg_init_muxer_pre run
during driver init on the main thread before the worker
thread is spawned. ffmpeg_flush_buffers runs during finalize
after deinit_thread has joined the worker.
The *_frames counters are single-writer from whichever thread
is in the convert/resample path (worker during recording,
main during finalize) so no lock changes are needed; the
out-of-sync bug was purely about the single writer leaving
its own state in an inconsistent window.
=== Reachability ===
Every bug here fires during active video/audio recording:
* ffmpeg_init_video / ffmpeg_init_muxer_pre: once per
record-session start.
* planarize_audio: every audio frame when the codec wants
planar input (most modern codecs).
* ffmpeg_audio_resample: every audio frame when sample rate
conversion is needed.
* ffmpeg_flush_buffers: once per record-session stop/finalize.
Memory pressure during recording is realistic - ffmpeg buffers
can be large (the video outbuf is 8MB fixed at line 525) and
the encoder itself holds multi-MB internal state.1 parent 5d1272a commit 1871bd1
1 file changed
Lines changed: 84 additions & 19 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
524 | 524 | | |
525 | 525 | | |
526 | 526 | | |
| 527 | + | |
| 528 | + | |
| 529 | + | |
| 530 | + | |
| 531 | + | |
527 | 532 | | |
528 | 533 | | |
529 | 534 | | |
530 | 535 | | |
531 | 536 | | |
532 | 537 | | |
| 538 | + | |
| 539 | + | |
| 540 | + | |
| 541 | + | |
| 542 | + | |
| 543 | + | |
533 | 544 | | |
534 | 545 | | |
535 | 546 | | |
| |||
844 | 855 | | |
845 | 856 | | |
846 | 857 | | |
| 858 | + | |
| 859 | + | |
| 860 | + | |
| 861 | + | |
| 862 | + | |
847 | 863 | | |
848 | 864 | | |
849 | 865 | | |
| 866 | + | |
| 867 | + | |
| 868 | + | |
| 869 | + | |
| 870 | + | |
850 | 871 | | |
851 | 872 | | |
852 | 873 | | |
| |||
1349 | 1370 | | |
1350 | 1371 | | |
1351 | 1372 | | |
1352 | | - | |
| 1373 | + | |
| 1374 | + | |
| 1375 | + | |
| 1376 | + | |
| 1377 | + | |
| 1378 | + | |
| 1379 | + | |
| 1380 | + | |
| 1381 | + | |
| 1382 | + | |
1353 | 1383 | | |
1354 | 1384 | | |
1355 | | - | |
| 1385 | + | |
1356 | 1386 | | |
| 1387 | + | |
1357 | 1388 | | |
1358 | 1389 | | |
1359 | 1390 | | |
| |||
1478 | 1509 | | |
1479 | 1510 | | |
1480 | 1511 | | |
1481 | | - | |
| 1512 | + | |
| 1513 | + | |
| 1514 | + | |
| 1515 | + | |
| 1516 | + | |
| 1517 | + | |
| 1518 | + | |
| 1519 | + | |
| 1520 | + | |
| 1521 | + | |
| 1522 | + | |
| 1523 | + | |
| 1524 | + | |
| 1525 | + | |
| 1526 | + | |
| 1527 | + | |
| 1528 | + | |
| 1529 | + | |
| 1530 | + | |
| 1531 | + | |
| 1532 | + | |
| 1533 | + | |
1482 | 1534 | | |
1483 | | - | |
| 1535 | + | |
1484 | 1536 | | |
| 1537 | + | |
| 1538 | + | |
1485 | 1539 | | |
1486 | | - | |
1487 | 1540 | | |
1488 | | - | |
1489 | | - | |
1490 | | - | |
| 1541 | + | |
| 1542 | + | |
1491 | 1543 | | |
1492 | | - | |
| 1544 | + | |
1493 | 1545 | | |
1494 | | - | |
| 1546 | + | |
1495 | 1547 | | |
| 1548 | + | |
| 1549 | + | |
1496 | 1550 | | |
1497 | | - | |
| 1551 | + | |
1498 | 1552 | | |
1499 | 1553 | | |
1500 | | - | |
| 1554 | + | |
1501 | 1555 | | |
1502 | | - | |
| 1556 | + | |
1503 | 1557 | | |
1504 | | - | |
1505 | | - | |
| 1558 | + | |
1506 | 1559 | | |
| 1560 | + | |
| 1561 | + | |
1507 | 1562 | | |
1508 | 1563 | | |
1509 | 1564 | | |
| |||
1622 | 1677 | | |
1623 | 1678 | | |
1624 | 1679 | | |
1625 | | - | |
| 1680 | + | |
| 1681 | + | |
| 1682 | + | |
| 1683 | + | |
| 1684 | + | |
| 1685 | + | |
| 1686 | + | |
1626 | 1687 | | |
1627 | 1688 | | |
1628 | 1689 | | |
| |||
1637 | 1698 | | |
1638 | 1699 | | |
1639 | 1700 | | |
1640 | | - | |
| 1701 | + | |
| 1702 | + | |
| 1703 | + | |
1641 | 1704 | | |
1642 | 1705 | | |
1643 | 1706 | | |
| |||
1649 | 1712 | | |
1650 | 1713 | | |
1651 | 1714 | | |
1652 | | - | |
1653 | | - | |
| 1715 | + | |
| 1716 | + | |
| 1717 | + | |
| 1718 | + | |
1654 | 1719 | | |
1655 | 1720 | | |
1656 | 1721 | | |
| |||
0 commit comments