PR #25503 opened by Marti2203
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25503
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25503.patch
With a video height of 32768 or more, showwaves writes outside the frame:
```
ffmpeg -f lavfi -i "aevalsrc=if(lt(mod(n\,96)\,48)\,-1\,0):s=48000:d=0.1" \
-filter_complex "[0:a]showwaves=s=64x32768:mode=p2p[out]" -map "[out]"
-f null -
```
crashes (SIGSEGV in a release build; ASan reports a wild write in
`draw_sample_p2p_rgba_scale`, avf_showwaves.c:291, and in
`draw_sample_p2p_gray` / `draw_sample_p2p_rgba_full` with `format=gray` /
`draw=full`).
**First commit.** The previous sample's row is stored in an `int16_t`
(`buf_idy`), and the line code swaps `start`/`end` through `int16_t`. Rows
above 32767 wrap to negative values, so p2p draws from a negative row, below
the start of the frame. The row and the swaps now use `int`. At these heights a
full-scale negative sample also maps to row `height + 1` (rounding in
`get_lin_h()`), and p2p then drew down to row `height`; the start of the p2p
line is limited to `[-1, height]`, the range it already has for smaller heights.
**Second commit** (found while testing the first). `mode=cline` has a similar
off-by-one from 16384 rows: `get_lin_h2()` returns `height + 1` for a sample of
-32768, and the centered line extends one row past the frame (ASan
heap-buffer-overflow). The line length is limited to the height.
With both commits the command above and its `draw=full` / `format=gray`
variants, and `mode=cline` at 64x16384, run clean under ASan. For normal
heights nothing changes: at 64x240 the framemd5 output of p2p and cline, each
with `draw=scale`, `draw=full` and `format=gray`, is identical before and after.
This issue was found by an LLM-Enhanced Static Analysis Engine and verified
manually.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
From 409b4a5c454f82fefe74c5f613b19a21169cf5f8 Mon Sep 17 00:00:00 2001
From: Martin Mirchev <[email protected]>
Date: Tue, 27 Oct 2026 09:59:22 +0800
Subject: [PATCH 1/2] avfilter/avf_showwaves: keep the previous y coordinate in
an int
The previous sample's row was stored in an int16_t, and the line drawing
code swapped start and end through int16_t. With a video height of 32768
or more, rows above 32767 wrapped to negative values, so mode=p2p drew
from a negative row and wrote below the start of the frame (the line
modes have the same problem once height/2 exceeds 32767). Use int for
the stored row and for the swaps.
At these heights a full-scale negative sample also maps to row
height + 1, and p2p then drew down to row height, one past the last
row. Limit the start of the p2p line to [-1, height], the range it has
for smaller heights, so the drawn rows stay within the frame.
Signed-off-by: Martin Mirchev <[email protected]>
---
libavfilter/avf_showwaves.c | 48 ++++++++++++++++++-------------------
1 file changed, 24 insertions(+), 24 deletions(-)
diff --git a/libavfilter/avf_showwaves.c b/libavfilter/avf_showwaves.c
index dda15c53065..347564dfd4c 100644
--- a/libavfilter/avf_showwaves.c
+++ b/libavfilter/avf_showwaves.c
@@ -77,7 +77,7 @@ typedef struct ShowWavesContext {
AVRational rate;
char *colors;
int buf_idx;
- int16_t *buf_idy; /* y coordinate of previous sample for each channel */
+ int *buf_idy; /* y coordinate of previous sample for each channel */
int16_t *history;
int history_nb_samples;
int history_index;
@@ -93,7 +93,7 @@ typedef struct ShowWavesContext {
int (*get_h)(int16_t sample, int height);
void (*draw_sample)(uint8_t *buf, int height, int linesize,
- int16_t *prev_y, const uint8_t color[4], int h);
+ int *prev_y, const uint8_t color[4], int h);
/* single picture */
int single_pic;
@@ -218,7 +218,7 @@ static int get_cbrt_h2(int16_t sample, int height)
}
static void draw_sample_point_rgba_scale(uint8_t *buf, int height, int
linesize,
- int16_t *prev_y,
+ int *prev_y,
const uint8_t color[4], int h)
{
if (h >= 0 && h < height) {
@@ -230,7 +230,7 @@ static void draw_sample_point_rgba_scale(uint8_t *buf, int
height, int linesize,
}
static void draw_sample_point_rgba_full(uint8_t *buf, int height, int linesize,
- int16_t *prev_y,
+ int *prev_y,
const uint8_t color[4], int h)
{
uint32_t clr = AV_RN32(color);
@@ -239,14 +239,14 @@ static void draw_sample_point_rgba_full(uint8_t *buf, int
height, int linesize,
}
static void draw_sample_line_rgba_scale(uint8_t *buf, int height, int linesize,
- int16_t *prev_y,
+ int *prev_y,
const uint8_t color[4], int h)
{
int start = height/2;
int end = av_clip(h, 0, height-1);
uint8_t *bufk;
if (start > end)
- FFSWAP(int16_t, start, end);
+ FFSWAP(int, start, end);
bufk = buf + start * linesize;
for (int k = start; k < end; k++, bufk += linesize) {
bufk[0] += color[0];
@@ -257,7 +257,7 @@ static void draw_sample_line_rgba_scale(uint8_t *buf, int
height, int linesize,
}
static void draw_sample_line_rgba_full(uint8_t *buf, int height, int linesize,
- int16_t *prev_y,
+ int *prev_y,
const uint8_t color[4], int h)
{
int start = height/2;
@@ -265,14 +265,14 @@ static void draw_sample_line_rgba_full(uint8_t *buf, int
height, int linesize,
uint32_t clr = AV_RN32(color);
uint8_t *bufk;
if (start > end)
- FFSWAP(int16_t, start, end);
+ FFSWAP(int, start, end);
bufk = buf + start * linesize;
for (int k = start; k < end; k++, bufk += linesize)
AV_WN32(bufk, clr);
}
static void draw_sample_p2p_rgba_scale(uint8_t *buf, int height, int linesize,
- int16_t *prev_y,
+ int *prev_y,
const uint8_t color[4], int h)
{
if (h >= 0 && h < height) {
@@ -281,11 +281,11 @@ static void draw_sample_p2p_rgba_scale(uint8_t *buf, int
height, int linesize,
buf[h * linesize + 2] += color[2];
buf[h * linesize + 3] += color[3];
if (*prev_y && h != *prev_y) {
- int start = *prev_y;
+ int start = av_clip(*prev_y, -1, height);
uint8_t *bufk;
int end = av_clip(h, 0, height-1);
if (start > end)
- FFSWAP(int16_t, start, end);
+ FFSWAP(int, start, end);
bufk = buf + (start + 1) * linesize;
for (int k = start + 1; k < end; k++, bufk += linesize) {
bufk[0] += color[0];
@@ -299,18 +299,18 @@ static void draw_sample_p2p_rgba_scale(uint8_t *buf, int
height, int linesize,
}
static void draw_sample_p2p_rgba_full(uint8_t *buf, int height, int linesize,
- int16_t *prev_y,
+ int *prev_y,
const uint8_t color[4], int h)
{
uint32_t clr = AV_RN32(color);
if (h >= 0 && h < height) {
AV_WN32(buf + h * linesize, clr);
if (*prev_y && h != *prev_y) {
- int start = *prev_y;
+ int start = av_clip(*prev_y, -1, height);
uint8_t *bufk;
int end = av_clip(h, 0, height-1);
if (start > end)
- FFSWAP(int16_t, start, end);
+ FFSWAP(int, start, end);
bufk = buf + (start + 1) * linesize;
for (int k = start + 1; k < end; k++, bufk += linesize)
AV_WN32(bufk, clr);
@@ -320,7 +320,7 @@ static void draw_sample_p2p_rgba_full(uint8_t *buf, int
height, int linesize,
}
static void draw_sample_cline_rgba_scale(uint8_t *buf, int height, int
linesize,
- int16_t *prev_y,
+ int *prev_y,
const uint8_t color[4], int h)
{
const int start = (height - h) / 2;
@@ -335,7 +335,7 @@ static void draw_sample_cline_rgba_scale(uint8_t *buf, int
height, int linesize,
}
static void draw_sample_cline_rgba_full(uint8_t *buf, int height, int linesize,
- int16_t *prev_y,
+ int *prev_y,
const uint8_t color[4], int h)
{
uint32_t clr = AV_RN32(color);
@@ -347,7 +347,7 @@ static void draw_sample_cline_rgba_full(uint8_t *buf, int
height, int linesize,
}
static void draw_sample_point_gray(uint8_t *buf, int height, int linesize,
- int16_t *prev_y,
+ int *prev_y,
const uint8_t color[4], int h)
{
if (h >= 0 && h < height)
@@ -355,30 +355,30 @@ static void draw_sample_point_gray(uint8_t *buf, int
height, int linesize,
}
static void draw_sample_line_gray(uint8_t *buf, int height, int linesize,
- int16_t *prev_y,
+ int *prev_y,
const uint8_t color[4], int h)
{
int k;
int start = height/2;
int end = av_clip(h, 0, height-1);
if (start > end)
- FFSWAP(int16_t, start, end);
+ FFSWAP(int, start, end);
for (k = start; k < end; k++)
buf[k * linesize] += color[0];
}
static void draw_sample_p2p_gray(uint8_t *buf, int height, int linesize,
- int16_t *prev_y,
+ int *prev_y,
const uint8_t color[4], int h)
{
int k;
if (h >= 0 && h < height) {
buf[h * linesize] += color[0];
if (*prev_y && h != *prev_y) {
- int start = *prev_y;
+ int start = av_clip(*prev_y, -1, height);
int end = av_clip(h, 0, height-1);
if (start > end)
- FFSWAP(int16_t, start, end);
+ FFSWAP(int, start, end);
for (k = start + 1; k < end; k++)
buf[k * linesize] += color[0];
}
@@ -387,7 +387,7 @@ static void draw_sample_p2p_gray(uint8_t *buf, int height,
int linesize,
}
static void draw_sample_cline_gray(uint8_t *buf, int height, int linesize,
- int16_t *prev_y,
+ int *prev_y,
const uint8_t color[4], int h)
{
int k;
@@ -705,7 +705,7 @@ static int showwaves_filter_frame(AVFilterLink *inlink,
AVFrame *insamples)
const AVRational i_n = av_inv_q(showwaves->n);
const AVRational u_q = av_make_q(1, 1);
const AVRational z_q = av_make_q(0, 1);
- int16_t *buf_idy = showwaves->buf_idy;
+ int *buf_idy = showwaves->buf_idy;
int idx = showwaves->history_index;
int buf_idx = showwaves->buf_idx;
const uint8_t *fg = showwaves->fg;
--
2.54.0
From ecaf95a1ddb4a6442f5a0b0e4230e25919857b16 Mon Sep 17 00:00:00 2001
From: Martin Mirchev <[email protected]>
Date: Tue, 27 Oct 2026 10:05:16 +0800
Subject: [PATCH 2/2] avfilter/avf_showwaves: keep the centered line within the
frame
For a sample of -32768 the cline scaling functions return more than the
height (get_lin_h2() returns height + 1 for heights of 16384 and more),
and the line then extends one row past the end of the frame. Limit the
line length to the height.
Signed-off-by: Martin Mirchev <[email protected]>
---
libavfilter/avf_showwaves.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/libavfilter/avf_showwaves.c b/libavfilter/avf_showwaves.c
index 347564dfd4c..d5e93c13ece 100644
--- a/libavfilter/avf_showwaves.c
+++ b/libavfilter/avf_showwaves.c
@@ -323,8 +323,8 @@ static void draw_sample_cline_rgba_scale(uint8_t *buf, int
height, int linesize,
int *prev_y,
const uint8_t color[4], int h)
{
- const int start = (height - h) / 2;
- const int end = start + h;
+ const int start = (height - FFMIN(h, height)) / 2;
+ const int end = start + FFMIN(h, height);
uint8_t *bufk = buf + start * linesize;
for (int k = start; k < end; k++, bufk += linesize) {
bufk[0] += color[0];
@@ -339,8 +339,8 @@ static void draw_sample_cline_rgba_full(uint8_t *buf, int
height, int linesize,
const uint8_t color[4], int h)
{
uint32_t clr = AV_RN32(color);
- const int start = (height - h) / 2;
- const int end = start + h;
+ const int start = (height - FFMIN(h, height)) / 2;
+ const int end = start + FFMIN(h, height);
uint8_t *bufk = buf + start * linesize;
for (int k = start; k < end; k++, bufk += linesize)
AV_WN32(bufk, clr);
@@ -391,8 +391,8 @@ static void draw_sample_cline_gray(uint8_t *buf, int
height, int linesize,
const uint8_t color[4], int h)
{
int k;
- const int start = (height - h) / 2;
- const int end = start + h;
+ const int start = (height - FFMIN(h, height)) / 2;
+ const int end = start + FFMIN(h, height);
for (k = start; k < end; k++)
buf[k * linesize] += color[0];
}
--
2.54.0
_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]