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]

Reply via email to