Package: libvisual-0.4-plugins
Version: 1:0.4.2+dfsg-6
Severity: normal
Tags: patch upstream

JESS can crash when rendering an 8-bit surface through its non-MMX path.
In render_blur(), bmax is initialized only inside the MMX branch, but
is also used as the loop bound in the scalar branch. The resulting
undefined behavior can make the loop run beyond the pixel buffer.

I reproduced the SIGSEGV with a headless libvisual client rendering a
320x240 8-bit surface with synthetic stereo audio, on native amd64 and
sh4 under QEMU. GDB stops in render_blur() at renderer.c:421, called by
renderer(), act_jess_render() and visual_actor_run(). The preserved
amd64 -6 binary also reproduces the crash; this is a pre-existing bug.
The unchanged sh4 plugin completes the same test with a 32-bit surface.

Moving the existing bmax assignment before the MMX/non-MMX branch fixes
all 16 test frames on both native amd64 and sh4/QEMU. I submitted the
runtime fix upstream:
https://github.com/Libvisual/libvisual/pull/433

The Debian patch and regression test are committed on Salsa:
https://salsa.debian.org/debian/libvisual-plugins/-/commit/de745fb

The attached regression test explicitly disables MMX and exercises the
8-bit path. With libvisual-0.4-dev, a C compiler and pkgconf installed:

   cc -O2 -Wall -Wextra jess-8bit.c -o jess-8bit \
     $(pkg-config --cflags --libs libvisual-0.4)
   ./jess-8bit

The updated autopkgtest fails with exit 139 against the -6 package
currently in unstable and passes with the local patched -7 candidate.
The complete amd64 sbuild and Lintian source/binary checks also pass.
Salsa CI pipeline #1203422 passed all 11 required jobs, including
amd64/i386 builds, Lintian and the updated autopkgtest:
https://salsa.debian.org/debian/libvisual-plugins/-/pipelines/1203422
The candidate remains UNRELEASED and has not been uploaded.

Best regards,
Federico

Attachment: 030_fix_jess_8bit_blur.patch
Description: Binary data

/* Exercise JESS's scalar 8-bit rendering path with synthetic audio. */
#include <math.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <libvisual/libvisual.h>

static void require(int ok, const char *operation)
{
    if (!ok) {
        fprintf(stderr, "JESS 8-bit test failed: %s\n", operation);
        exit(1);
    }
}

int main(int argc, char **argv)
{
    require(visual_init(&argc, &argv) == 0, "initialize libvisual");
    visual_cpu_set_mmx(0);
    require(visual_cpu_get_mmx() == 0, "disable MMX");

    VisActor *actor = visual_actor_new("jess");
    require(actor != NULL, "load JESS");
    require(visual_actor_realize(actor) == 0, "initialize JESS");
    VisVideo *video = visual_video_new_with_buffer(320, 240,
                                                  VISUAL_VIDEO_DEPTH_8BIT);
    VisAudio *audio = visual_audio_new();
    require(video != NULL && audio != NULL, "allocate video and audio");
    require(visual_actor_set_video(actor, video) == 0, "set video");
    require(visual_actor_video_negotiate(actor, 0, 0, 0) == 0,
            "negotiate video");

    int16_t pcm[1024];
    VisBuffer *buffer = visual_buffer_new_with_buffer(pcm, sizeof(pcm), NULL);
    require(buffer != NULL, "allocate audio buffer");
    unsigned int nonzero = 0;
    for (int frame = 0; frame < 16; frame++) {
        for (int i = 0; i < 1024; i++)
            pcm[i] = (int16_t)(16000.0 * sin((i + frame * 1024) * 0.05));
        require(visual_audio_samplepool_input(audio->samplepool, buffer,
                    VISUAL_AUDIO_SAMPLE_RATE_44100,
                    VISUAL_AUDIO_SAMPLE_FORMAT_S16,
                    VISUAL_AUDIO_SAMPLE_CHANNEL_STEREO) == 0,
                "feed audio");
        require(visual_actor_run(actor, audio) == 0, "render frame");
        const unsigned char *pixels = visual_video_get_pixels(video);
        require(pixels != NULL, "get rendered pixels");
        for (int i = 0; i < visual_video_get_size(video); i++)
            nonzero += pixels[i] != 0;
    }
    require(nonzero > 0, "produce rendered output");
    printf("JESS scalar 8-bit: 16 frames PASS (%u nonzero pixels)\n", nonzero);

    visual_object_unref(VISUAL_OBJECT(buffer));
    visual_object_unref(VISUAL_OBJECT(actor));
    visual_object_unref(VISUAL_OBJECT(video));
    visual_object_unref(VISUAL_OBJECT(audio));
    visual_quit();
    return 0;
}

Reply via email to