CVE-2024-31585 (C, published 2024-04-17T19:15Z)
Description: value: FFmpeg version n5.1 to n6.1 was discovered to contain an Off-by-one Error vulnerability in libavfilter/avf_showspectrum.c. This vulnerability allows attackers to cause a Denial of Service (DoS) via a crafted input.
Severity: nan (CVSS v3 base score: )
Weakness: NVD-CWE-noinfo \u2014 Insufficient Information
There is insufficient information about the issue to classify it; details are unkown or unspecified.
Repository: https://github.com/FFmpeg/FFmpeg
Files changed:
libavfilter/avf_showspectrum.c
Commit message: avfilter/avf_showspectrum: stop using AVAudioFifo to keep samples

Fixes limitation of input duration that showspectrumpic can process.


Vulnerable code:
#include "libavutil/audio_fifo.h"
    AVAudioFifo *fifo;
    av_audio_fifo_free(s->fifo);
            s->fft_data[ch][n].re = p[n] * window_func_lut[n];
            s->fft_in[ch][n].re = p[n] * window_func_lut[n];
static int draw_legend(AVFilterContext *ctx, int samples)
    av_audio_fifo_free(s->fifo);
    s->fifo = av_audio_fifo_alloc(inlink->format, inlink->channels, s->win_size);
    if (!s->fifo)
        outpicref->pts = av_rescale_q(insamples->pts, inlink->time_base, outlink->time_base);
    int ret;
    if (av_audio_fifo_size(s->fifo) < s->win_size) {
        AVFrame *frame = NULL;
        ret = ff_inlink_consume_frame(inlink, &frame);
            s->pts = frame->pts;
            s->consumed = 0;
            av_audio_fifo_write(s->fifo, (void **)frame->extended_data, frame->nb_samples);
            av_frame_free(&frame);
        }
    }
    if (s->outpicref && (av_audio_fifo_size(s->fifo) >= s->win_size ||
        ff_outlink_get_status(inlink))) {
        AVFrame *fin = ff_get_audio_buffer(inlink, s->win_size);
        if (!fin)
            return AVERROR(ENOMEM);
        fin->pts = s->pts + s->consumed;
        s->consumed += s->hop_size;
        ret = av_audio_fifo_peek(s->fifo, (void **)fin->extended_data,
                                 FFMIN(s->win_size, av_audio_fifo_size(s->fifo)));
        if (ret < 0) {
            return ret;

        av_assert0(fin->nb_samples == s->win_size);

        ff_filter_execute(ctx, run_channel_fft, fin, NULL, s->nb_display_channels);

        if (s->data == D_MAGNITUDE)
            ff_filter_execute(ctx, calc_channel_magnitudes, NULL, NULL, s->nb_display_channels);

        if (s->data == D_PHASE)
            ff_filter_execute(ctx, calc_channel_phases, NULL, NULL, s->nb_display_channels);

        if (s->data == D_UPHASE)
            ff_filter_execute(ctx, calc_channel_uphases, NULL, NULL, s->nb_display_channels);

        ret = plot_spectrum_column(inlink, fin);

        av_frame_free(&fin);
        av_audio_fifo_drain(s->fifo, s->hop_size);
        if (ret <= 0 && !ff_outlink_get_status(inlink))
            return ret;
        int64_t pts;
    FF_FILTER_FORWARD_STATUS(inlink, outlink);
    if (av_audio_fifo_size(s->fifo) >= s->win_size ||
        ff_inlink_queued_frames(inlink) > 0 ||
        ff_outlink_get_status(inlink) == AVERROR_EOF) {
    if (ff_outlink_frame_wanted(outlink) && av_audio_fifo_size(s->fifo) < s->win_size &&
        ff_inlink_queued_frames(inlink) == 0 &&
        ff_outlink_get_status(inlink) != AVERROR_EOF) {
    int ret, samples;
    samples = av_audio_fifo_size(s->fifo);
    if (ret == AVERROR_EOF && s->outpicref && samples > 0) {
        spf = s->win_size * (samples / ((s->win_size * sz) * ceil(samples / (float)(s->win_size * sz))));
        spb = (samples / (spf * sz)) * spf;
        fin = ff_get_audio_buffer(inlink, s->win_size);
            ret = av_audio_fifo_peek(s->fifo, (void **)fin->extended_data, s->win_size);
            if (ret < 0) {
                av_frame_free(&fin);
                return ret;
            }

            av_audio_fifo_drain(s->fifo, spf);
            if (ret < s->win_size) {
                for (ch = 0; ch < s->nb_display_channels; ch++) {
                    memset(fin->extended_data[ch] + ret * sizeof(float), 0,
                           (s->win_size - ret) * sizeof(float));
            draw_legend(ctx, samples);
    int ret;
    ret = av_audio_fifo_write(s->fifo, (void **)insamples->extended_data, insamples->nb_samples);
    av_frame_free(&insamples);
    return ret;

Fixed code:
#define DEFAULT_LENGTH 300

    AVFrame *in_frame;
    uint64_t samples;

    AVFrame **frames;
    unsigned int nb_frames;
    unsigned int frames_size;
    av_frame_free(&s->in_frame);

    while (s->nb_frames > 0) {
        av_frame_free(&s->frames[s->nb_frames - 1]);
        s->nb_frames--;
    }

    av_freep(&s->frames);
    float *in_frame = (float *)s->in_frame->extended_data[ch];

    memmove(in_frame, in_frame + s->hop_size, (s->fft_size - s->hop_size) * sizeof(float));
    memcpy(in_frame + s->fft_size - s->hop_size, p, fin->nb_samples * sizeof(float));

    for (int i = fin->nb_samples; i < s->hop_size; i++)
        in_frame[i + s->fft_size - s->hop_size] = 0.f;
            s->fft_data[ch][n].re = in_frame[n] * window_func_lut[n];
            s->fft_in[ch][n].re = in_frame[n] * window_func_lut[n];
static int draw_legend(AVFilterContext *ctx, uint64_t samples)
    s->in_frame = ff_get_audio_buffer(inlink, s->win_size);
    if (!s->in_frame)
        return AVERROR(ENOMEM);

    s->frames = av_fast_realloc(NULL, &s->frames_size,
                                DEFAULT_LENGTH * sizeof(*(s->frames)));
    if (!s->frames)

        s->pts = outpicref->pts = av_rescale_q(insamples->pts, inlink->time_base, outlink->time_base);
    int ret, status;
    int64_t pts;
    if (s->outpicref) {
        AVFrame *fin;
        ret = ff_inlink_consume_samples(inlink, s->hop_size, s->hop_size, &fin);
            s->consumed += fin->nb_samples;
            ff_filter_execute(ctx, run_channel_fft, fin, NULL, s->nb_display_channels);
            if (s->data == D_MAGNITUDE)
                ff_filter_execute(ctx, calc_channel_magnitudes, NULL, NULL, s->nb_display_channels);
            if (s->data == D_PHASE)
                ff_filter_execute(ctx, calc_channel_phases, NULL, NULL, s->nb_display_channels);
            if (s->data == D_UPHASE)
                ff_filter_execute(ctx, calc_channel_uphases, NULL, NULL, s->nb_display_channels);

            ret = plot_spectrum_column(inlink, fin);
            if (ret <= 0)
                return ret;
    if (ff_inlink_acknowledge_status(inlink, &status, &pts)) {
        if (status == AVERROR_EOF) {
            ff_outlink_set_status(outlink, status, s->pts);
            return 0;
        }
    }

    if (ff_inlink_queued_samples(inlink) >= s->hop_size) {
    if (ff_outlink_frame_wanted(outlink)) {
    int ret;
    if (ret == AVERROR_EOF && s->outpicref && s->samples > 0) {
        unsigned int nb_frame = 0;
        int src_offset = 0;
        spf = s->win_size * (s->samples / ((s->win_size * sz) * ceil(s->samples / (float)(s->win_size * sz))));
        spb = (s->samples / (spf * sz)) * spf;
        fin = ff_get_audio_buffer(inlink, spf);
            int acc_samples = 0;
            int dst_offset = 0;

            while (nb_frame <= s->nb_frames) {
                AVFrame *cur_frame = s->frames[nb_frame];
                int cur_frame_samples = cur_frame->nb_samples;
                int nb_samples = 0;

                if (acc_samples < spf) {
                    nb_samples = FFMIN(spf - acc_samples, cur_frame_samples - src_offset);
                    acc_samples += nb_samples;
                    av_samples_copy(fin->extended_data, cur_frame->extended_data,
                                    dst_offset, src_offset, nb_samples,
                                    cur_frame->channels, AV_SAMPLE_FMT_FLTP);
                }
                src_offset += nb_samples;
                dst_offset += nb_samples;
                if (cur_frame_samples <= src_offset) {
                    av_frame_free(&s->frames[nb_frame]);
                    nb_frame++;
                    src_offset = 0;

                if (acc_samples == spf)
                    break;
            draw_legend(ctx, s->samples);
    void *ptr;
    if (s->nb_frames + 1ULL > s->frames_size / sizeof(*(s->frames))) {
        ptr = av_fast_realloc(s->frames, &s->frames_size, s->frames_size * 2);
        if (!ptr)
            return AVERROR(ENOMEM);
        s->frames = ptr;
    }

    s->frames[s->nb_frames] = insamples;
    s->samples += insamples->nb_samples;
    s->nb_frames++;

    return 0;