diff mbox series

[FFmpeg-devel] tools: Add target_sws_fuzzer.c

Message ID 20240217234851.23208-1-michael@niedermayer.cc
State New
Headers show
Series [FFmpeg-devel] tools: Add target_sws_fuzzer.c | expand

Checks

Context Check Description
yinshiyou/make_loongarch64 success Make finished
yinshiyou/make_fate_loongarch64 success Make fate finished
andriy/make_x86 success Make finished
andriy/make_fate_x86 success Make fate finished

Commit Message

Michael Niedermayer Feb. 17, 2024, 11:48 p.m. UTC
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
---
 Makefile                  |   3 +
 tools/Makefile            |   3 +
 tools/target_sws_fuzzer.c | 168 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 174 insertions(+)
 create mode 100644 tools/target_sws_fuzzer.c

Comments

Sean McGovern Feb. 18, 2024, 12:04 a.m. UTC | #1
On Sat, Feb 17, 2024, 18:49 Michael Niedermayer <michael@niedermayer.cc>
wrote:

> Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
> ---
>  Makefile                  |   3 +
>  tools/Makefile            |   3 +
>  tools/target_sws_fuzzer.c | 168 ++++++++++++++++++++++++++++++++++++++
>  3 files changed, 174 insertions(+)
>  create mode 100644 tools/target_sws_fuzzer.c
>
> diff --git a/Makefile b/Makefile
> index dbc930270b3..b309dbc4db9 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -64,6 +64,9 @@ tools/target_dem_fuzzer$(EXESUF):
> tools/target_dem_fuzzer.o $(FF_DEP_LIBS)
>  tools/target_io_dem_fuzzer$(EXESUF): tools/target_io_dem_fuzzer.o
> $(FF_DEP_LIBS)
>         $(LD) $(LDFLAGS) $(LDEXEFLAGS) $(LD_O) $^ $(ELIBS) $(FF_EXTRALIBS)
> $(LIBFUZZER_PATH)
>
> +tools/target_sws_fuzzer$(EXESUF): tools/target_sws_fuzzer.o $(FF_DEP_LIBS)
> +       $(LD) $(LDFLAGS) $(LDEXEFLAGS) $(LD_O) $^ $(ELIBS) $(FF_EXTRALIBS)
> $(LIBFUZZER_PATH)
> +
>
>  tools/enum_options$(EXESUF): ELIBS = $(FF_EXTRALIBS)
>  tools/enum_options$(EXESUF): $(FF_DEP_LIBS)
> diff --git a/tools/Makefile b/tools/Makefile
> index dee6a416688..72e8e709a8d 100644
> --- a/tools/Makefile
> +++ b/tools/Makefile
> @@ -17,6 +17,9 @@ tools/target_dem_fuzzer.o: tools/target_dem_fuzzer.c
>  tools/target_io_dem_fuzzer.o: tools/target_dem_fuzzer.c
>         $(COMPILE_C) -DIO_FLAT=0
>
> +tools/target_sws_fuzzer.o: tools/target_sws_fuzzer.c
> +       $(COMPILE_C)
> +
>  tools/enc_recon_frame_test$(EXESUF): tools/decode_simple.o
>  tools/venc_data_dump$(EXESUF): tools/decode_simple.o
>  tools/scale_slice_test$(EXESUF): tools/decode_simple.o
> diff --git a/tools/target_sws_fuzzer.c b/tools/target_sws_fuzzer.c
> new file mode 100644
> index 00000000000..babb6e81629
> --- /dev/null
> +++ b/tools/target_sws_fuzzer.c
> @@ -0,0 +1,168 @@
> +/*
> + * Copyright (c) 2024 Michael Niedermayer <michael-ffmpeg@niedermayer.cc>
> + *
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * FFmpeg is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with FFmpeg; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
> 02110-1301 USA
> + */
> +
> +#include "config.h"
> +#include "libavutil/avassert.h"
> +#include "libavutil/avstring.h"
> +#include "libavutil/cpu.h"
> +#include "libavutil/imgutils.h"
> +#include "libavutil/intreadwrite.h"
> +#include "libavutil/opt.h"
> +
> +#include "libavcodec/bytestream.h"
> +
> +#include "libswscale/swscale.h"
> +
> +
> +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
> +
> +static void error(const char *err)
> +{
> +    fprintf(stderr, "%s", err);
> +    exit(1);
> +}
> +
> +static int alloc_plane(uint8_t *data[AV_VIDEO_MAX_PLANES], int
> stride[AV_VIDEO_MAX_PLANES], int w, int h, int format, int *hshift, int
> *vshift)
> +{
> +    int ret = av_image_fill_linesizes(stride, format, w);
> +    if (ret < 0)
> +        return -1;
> +
> +    av_pix_fmt_get_chroma_sub_sample(format, hshift, vshift);
> +
> +    for(int p=0; p<AV_VIDEO_MAX_PLANES; p++) {
> +        if (stride[p]) {
> +            stride[p] = FFALIGN(stride[p], 32);
> +            int ph = AV_CEIL_RSHIFT(h, (p == 1 || p == 2) ? *vshift : 0);
> +            av_log(0,0, "P:%d St %d ph %d\n", p, stride[p], ph);
> +            data[p] = av_mallocz(stride[p] * ph + 32);
> +            if (!data[p])
> +                return -1;
> +        }
> +    }
> +    if (format == AV_PIX_FMT_PAL8) {
> +        data[1] = av_mallocz(256*4);
> +        if (!data[1])
> +            return -1;
> +    }
> +    return 0;
> +}
> +
> +static void free_plane(uint8_t *data[AV_VIDEO_MAX_PLANES])
> +{
> +    for(int p=0; p<AV_VIDEO_MAX_PLANES; p++)
> +        av_freep(&data[p]);
> +}
> +
> +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
> +    int srcW= 48, srcH = 48;
> +    int dstW= 48, dstH = 48;
> +    int srcHShift, srcVShift;
> +    int dstHShift, dstVShift;
> +    unsigned flags = 1;
> +    int srcStride[AV_VIDEO_MAX_PLANES] = {0};
> +    int dstStride[AV_VIDEO_MAX_PLANES] = {0};
> +    int ret;
> +    const uint8_t *end = data + size;
> +    enum AVPixelFormat srcFormat = AV_PIX_FMT_YUV420P;
> +    enum AVPixelFormat dstFormat = AV_PIX_FMT_YUV420P;
> +    uint8_t *src[4] = { 0 };
> +    uint8_t *dst[4] = { 0 };
> +    struct SwsContext *sws = NULL;
> +    const AVPixFmtDescriptor *desc_src, *desc_dst;
> +
> +    if (size > 128) {
> +        GetByteContext gbc;
> +        int64_t flags64;
> +
> +        size -= 128;
> +        bytestream2_init(&gbc, data + size, 128);
> +        srcW = bytestream2_get_le32(&gbc) % 256;
> +        srcH = bytestream2_get_le32(&gbc) % 256;
> +        dstW = bytestream2_get_le32(&gbc) % 256;
> +        dstH = bytestream2_get_le32(&gbc) % 256;
> +        flags = bytestream2_get_le32(&gbc);
> +
> +        srcFormat = bytestream2_get_le32(&gbc) % AV_PIX_FMT_NB;
> +        dstFormat = bytestream2_get_le32(&gbc) % AV_PIX_FMT_NB;
> +
> +        flags64 = bytestream2_get_le64(&gbc);
> +        if (flags64 & 0x10)
> +            av_force_cpu_flags(0);
> +
> +        if (av_image_check_size(srcW, srcH, srcFormat, NULL))
> +            srcW = srcH = 123;
> +        if (av_image_check_size(dstW, dstH, dstFormat, NULL))
> +            dstW = dstH = 123;
> +        //TODO alphablend
> +    }
> +
> +    desc_src = av_pix_fmt_desc_get(srcFormat);
> +    desc_dst = av_pix_fmt_desc_get(dstFormat);
> +
> +    ret = alloc_plane(src, srcStride, srcW, srcH, srcFormat, &srcHShift,
> &srcVShift);
> +    if (ret < 0)
> +        goto end;
> +
> +    ret = alloc_plane(dst, dstStride, dstW, dstH, dstFormat, &dstHShift,
> &dstVShift);
> +    if (ret < 0)
> +        goto end;
> +
> +
> +    for(int p=0; p<AV_VIDEO_MAX_PLANES; p++) {
> +        int psize = srcStride[p] * AV_CEIL_RSHIFT(srcH, (p == 1 || p ==
> 2) ? srcVShift : 0);
> +        if (psize > size)
> +            psize = size;
> +        if (psize) {
> +            memcpy(src[p], data, psize);
> +            data += psize;
> +            size -= psize;
> +        }
> +    }
> +
> +    sws = sws_alloc_context();
> +    if (!sws)
> +        error("Failed sws allocation");
> +
> +    av_opt_set_int(sws, "sws_flags",  flags, 0);
> +    av_opt_set_int(sws, "srcw",       srcW, 0);
> +    av_opt_set_int(sws, "srch",       srcH, 0);
> +    av_opt_set_int(sws, "dstw",       dstW, 0);
> +    av_opt_set_int(sws, "dsth",       dstH, 0);
> +    av_opt_set_int(sws, "src_format", srcFormat, 0);
> +    av_opt_set_int(sws, "dst_format", dstFormat, 0);
> +    av_opt_set(sws, "alphablend", "none", 0);
> +
> +    ret = sws_init_context(sws, NULL, NULL);
> +    if (ret < 0)
> +        goto end;
> +
> +    fprintf(stderr, "%d x %d %s -> %d x %d %s\n", srcW, srcH,
> desc_src->name, dstW, dstH, desc_dst->name);
> +    //TODO Slices
> +    sws_scale(sws, (const uint8_t * const*)src, srcStride, 0, srcH, dst,
> dstStride);
> +
> +end:
> +    sws_freeContext(sws);
> +
> +    free_plane(src);
> +    free_plane(dst);
> +
> +    return 0;
> +}
> --
> 2.17.1
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
>

I don't see anything intrinsically wrong with this other than:

- is this compiler-backend specific? Does it work without LLVM?
- would it ever be useful to redirect the output of error() to another fd?
In which case maybe a convenience method that defaults to stderr would be
appropriate?

-- Sean McGovern

>
James Almer Feb. 18, 2024, 12:13 a.m. UTC | #2
On 2/17/2024 8:48 PM, Michael Niedermayer wrote:
> Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
> ---
>   Makefile                  |   3 +
>   tools/Makefile            |   3 +
>   tools/target_sws_fuzzer.c | 168 ++++++++++++++++++++++++++++++++++++++
>   3 files changed, 174 insertions(+)
>   create mode 100644 tools/target_sws_fuzzer.c
> 
> diff --git a/Makefile b/Makefile
> index dbc930270b3..b309dbc4db9 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -64,6 +64,9 @@ tools/target_dem_fuzzer$(EXESUF): tools/target_dem_fuzzer.o $(FF_DEP_LIBS)
>   tools/target_io_dem_fuzzer$(EXESUF): tools/target_io_dem_fuzzer.o $(FF_DEP_LIBS)
>   	$(LD) $(LDFLAGS) $(LDEXEFLAGS) $(LD_O) $^ $(ELIBS) $(FF_EXTRALIBS) $(LIBFUZZER_PATH)
>   
> +tools/target_sws_fuzzer$(EXESUF): tools/target_sws_fuzzer.o $(FF_DEP_LIBS)
> +	$(LD) $(LDFLAGS) $(LDEXEFLAGS) $(LD_O) $^ $(ELIBS) $(FF_EXTRALIBS) $(LIBFUZZER_PATH)
> +
>   
>   tools/enum_options$(EXESUF): ELIBS = $(FF_EXTRALIBS)
>   tools/enum_options$(EXESUF): $(FF_DEP_LIBS)
> diff --git a/tools/Makefile b/tools/Makefile
> index dee6a416688..72e8e709a8d 100644
> --- a/tools/Makefile
> +++ b/tools/Makefile
> @@ -17,6 +17,9 @@ tools/target_dem_fuzzer.o: tools/target_dem_fuzzer.c
>   tools/target_io_dem_fuzzer.o: tools/target_dem_fuzzer.c
>   	$(COMPILE_C) -DIO_FLAT=0
>   
> +tools/target_sws_fuzzer.o: tools/target_sws_fuzzer.c
> +	$(COMPILE_C)
> +
>   tools/enc_recon_frame_test$(EXESUF): tools/decode_simple.o
>   tools/venc_data_dump$(EXESUF): tools/decode_simple.o
>   tools/scale_slice_test$(EXESUF): tools/decode_simple.o
> diff --git a/tools/target_sws_fuzzer.c b/tools/target_sws_fuzzer.c
> new file mode 100644
> index 00000000000..babb6e81629
> --- /dev/null
> +++ b/tools/target_sws_fuzzer.c
> @@ -0,0 +1,168 @@
> +/*
> + * Copyright (c) 2024 Michael Niedermayer <michael-ffmpeg@niedermayer.cc>
> + *
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * FFmpeg is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with FFmpeg; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
> + */
> +
> +#include "config.h"
> +#include "libavutil/avassert.h"
> +#include "libavutil/avstring.h"
> +#include "libavutil/cpu.h"
> +#include "libavutil/imgutils.h"
> +#include "libavutil/intreadwrite.h"
> +#include "libavutil/opt.h"
> +
> +#include "libavcodec/bytestream.h"
> +
> +#include "libswscale/swscale.h"
> +
> +
> +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
> +
> +static void error(const char *err)
> +{
> +    fprintf(stderr, "%s", err);
> +    exit(1);
> +}
> +
> +static int alloc_plane(uint8_t *data[AV_VIDEO_MAX_PLANES], int stride[AV_VIDEO_MAX_PLANES], int w, int h, int format, int *hshift, int *vshift)
> +{
> +    int ret = av_image_fill_linesizes(stride, format, w);
> +    if (ret < 0)
> +        return -1;
> +
> +    av_pix_fmt_get_chroma_sub_sample(format, hshift, vshift);
> +
> +    for(int p=0; p<AV_VIDEO_MAX_PLANES; p++) {
> +        if (stride[p]) {
> +            stride[p] = FFALIGN(stride[p], 32);
> +            int ph = AV_CEIL_RSHIFT(h, (p == 1 || p == 2) ? *vshift : 0);
> +            av_log(0,0, "P:%d St %d ph %d\n", p, stride[p], ph);
> +            data[p] = av_mallocz(stride[p] * ph + 32);
> +            if (!data[p])
> +                return -1;
> +        }
> +    }
> +    if (format == AV_PIX_FMT_PAL8) {
> +        data[1] = av_mallocz(256*4);
> +        if (!data[1])
> +            return -1;
> +    }
> +    return 0;

av_image_alloc()? Would be better to actually test sws with buffers 
created by our own public helpers.

> +}
> +
> +static void free_plane(uint8_t *data[AV_VIDEO_MAX_PLANES])
> +{
> +    for(int p=0; p<AV_VIDEO_MAX_PLANES; p++)
> +        av_freep(&data[p]);
> +}
> +
> +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
> +    int srcW= 48, srcH = 48;
> +    int dstW= 48, dstH = 48;
> +    int srcHShift, srcVShift;
> +    int dstHShift, dstVShift;
> +    unsigned flags = 1;
> +    int srcStride[AV_VIDEO_MAX_PLANES] = {0};
> +    int dstStride[AV_VIDEO_MAX_PLANES] = {0};
> +    int ret;
> +    const uint8_t *end = data + size;
> +    enum AVPixelFormat srcFormat = AV_PIX_FMT_YUV420P;
> +    enum AVPixelFormat dstFormat = AV_PIX_FMT_YUV420P;
> +    uint8_t *src[4] = { 0 };
> +    uint8_t *dst[4] = { 0 };

AV_VIDEO_MAX_PLANES.

> +    struct SwsContext *sws = NULL;
> +    const AVPixFmtDescriptor *desc_src, *desc_dst;
> +
> +    if (size > 128) {
> +        GetByteContext gbc;
> +        int64_t flags64;
> +
> +        size -= 128;
> +        bytestream2_init(&gbc, data + size, 128);
> +        srcW = bytestream2_get_le32(&gbc) % 256;
> +        srcH = bytestream2_get_le32(&gbc) % 256;
> +        dstW = bytestream2_get_le32(&gbc) % 256;
> +        dstH = bytestream2_get_le32(&gbc) % 256;
> +        flags = bytestream2_get_le32(&gbc);
> +
> +        srcFormat = bytestream2_get_le32(&gbc) % AV_PIX_FMT_NB;
> +        dstFormat = bytestream2_get_le32(&gbc) % AV_PIX_FMT_NB;

nit: Maybe sanitize the choices with sws_isSupportedInput() and 
sws_isSupportedOutput()? Unless having sws_init_context() fail with 
invalid arguments is also intended.

> +
> +        flags64 = bytestream2_get_le64(&gbc);
> +        if (flags64 & 0x10)
> +            av_force_cpu_flags(0);
> +
> +        if (av_image_check_size(srcW, srcH, srcFormat, NULL))
> +            srcW = srcH = 123;
> +        if (av_image_check_size(dstW, dstH, dstFormat, NULL))
> +            dstW = dstH = 123;

Is there a format where this could fail, knowing the dimensions are at 
most 255x255?

> +        //TODO alphablend
> +    }
> +
> +    desc_src = av_pix_fmt_desc_get(srcFormat);
> +    desc_dst = av_pix_fmt_desc_get(dstFormat);
> +
> +    ret = alloc_plane(src, srcStride, srcW, srcH, srcFormat, &srcHShift, &srcVShift);
> +    if (ret < 0)
> +        goto end;
> +
> +    ret = alloc_plane(dst, dstStride, dstW, dstH, dstFormat, &dstHShift, &dstVShift);
> +    if (ret < 0)
> +        goto end;
> +
> +
> +    for(int p=0; p<AV_VIDEO_MAX_PLANES; p++) {
> +        int psize = srcStride[p] * AV_CEIL_RSHIFT(srcH, (p == 1 || p == 2) ? srcVShift : 0);
> +        if (psize > size)
> +            psize = size;
> +        if (psize) {
> +            memcpy(src[p], data, psize);
> +            data += psize;
> +            size -= psize;
> +        }
> +    }

av_image_copy(). Or av_image_copy_plane() in a loop if you prefer.

> +
> +    sws = sws_alloc_context();
> +    if (!sws)
> +        error("Failed sws allocation");
> +
> +    av_opt_set_int(sws, "sws_flags",  flags, 0);
> +    av_opt_set_int(sws, "srcw",       srcW, 0);
> +    av_opt_set_int(sws, "srch",       srcH, 0);
> +    av_opt_set_int(sws, "dstw",       dstW, 0);
> +    av_opt_set_int(sws, "dsth",       dstH, 0);
> +    av_opt_set_int(sws, "src_format", srcFormat, 0);
> +    av_opt_set_int(sws, "dst_format", dstFormat, 0);
> +    av_opt_set(sws, "alphablend", "none", 0);
> +
> +    ret = sws_init_context(sws, NULL, NULL);
> +    if (ret < 0)
> +        goto end;
> +
> +    fprintf(stderr, "%d x %d %s -> %d x %d %s\n", srcW, srcH, desc_src->name, dstW, dstH, desc_dst->name);
> +    //TODO Slices
> +    sws_scale(sws, (const uint8_t * const*)src, srcStride, 0, srcH, dst, dstStride);
> +
> +end:
> +    sws_freeContext(sws);
> +
> +    free_plane(src);
> +    free_plane(dst);
> +
> +    return 0;
> +}
Michael Niedermayer Feb. 18, 2024, 1:03 a.m. UTC | #3
On Sat, Feb 17, 2024 at 09:13:21PM -0300, James Almer wrote:
> 
> 
> On 2/17/2024 8:48 PM, Michael Niedermayer wrote:
> > Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
> > ---
> >   Makefile                  |   3 +
> >   tools/Makefile            |   3 +
> >   tools/target_sws_fuzzer.c | 168 ++++++++++++++++++++++++++++++++++++++
> >   3 files changed, 174 insertions(+)
> >   create mode 100644 tools/target_sws_fuzzer.c
> > 
> > diff --git a/Makefile b/Makefile
> > index dbc930270b3..b309dbc4db9 100644
> > --- a/Makefile
> > +++ b/Makefile
> > @@ -64,6 +64,9 @@ tools/target_dem_fuzzer$(EXESUF): tools/target_dem_fuzzer.o $(FF_DEP_LIBS)
> >   tools/target_io_dem_fuzzer$(EXESUF): tools/target_io_dem_fuzzer.o $(FF_DEP_LIBS)
> >   	$(LD) $(LDFLAGS) $(LDEXEFLAGS) $(LD_O) $^ $(ELIBS) $(FF_EXTRALIBS) $(LIBFUZZER_PATH)
> > +tools/target_sws_fuzzer$(EXESUF): tools/target_sws_fuzzer.o $(FF_DEP_LIBS)
> > +	$(LD) $(LDFLAGS) $(LDEXEFLAGS) $(LD_O) $^ $(ELIBS) $(FF_EXTRALIBS) $(LIBFUZZER_PATH)
> > +
> >   tools/enum_options$(EXESUF): ELIBS = $(FF_EXTRALIBS)
> >   tools/enum_options$(EXESUF): $(FF_DEP_LIBS)
> > diff --git a/tools/Makefile b/tools/Makefile
> > index dee6a416688..72e8e709a8d 100644
> > --- a/tools/Makefile
> > +++ b/tools/Makefile
> > @@ -17,6 +17,9 @@ tools/target_dem_fuzzer.o: tools/target_dem_fuzzer.c
> >   tools/target_io_dem_fuzzer.o: tools/target_dem_fuzzer.c
> >   	$(COMPILE_C) -DIO_FLAT=0
> > +tools/target_sws_fuzzer.o: tools/target_sws_fuzzer.c
> > +	$(COMPILE_C)
> > +
> >   tools/enc_recon_frame_test$(EXESUF): tools/decode_simple.o
> >   tools/venc_data_dump$(EXESUF): tools/decode_simple.o
> >   tools/scale_slice_test$(EXESUF): tools/decode_simple.o
> > diff --git a/tools/target_sws_fuzzer.c b/tools/target_sws_fuzzer.c
> > new file mode 100644
> > index 00000000000..babb6e81629
> > --- /dev/null
> > +++ b/tools/target_sws_fuzzer.c
> > @@ -0,0 +1,168 @@
> > +/*
> > + * Copyright (c) 2024 Michael Niedermayer <michael-ffmpeg@niedermayer.cc>
> > + *
> > + * This file is part of FFmpeg.
> > + *
> > + * FFmpeg is free software; you can redistribute it and/or
> > + * modify it under the terms of the GNU Lesser General Public
> > + * License as published by the Free Software Foundation; either
> > + * version 2.1 of the License, or (at your option) any later version.
> > + *
> > + * FFmpeg is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> > + * Lesser General Public License for more details.
> > + *
> > + * You should have received a copy of the GNU Lesser General Public
> > + * License along with FFmpeg; if not, write to the Free Software
> > + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
> > + */
> > +
> > +#include "config.h"
> > +#include "libavutil/avassert.h"
> > +#include "libavutil/avstring.h"
> > +#include "libavutil/cpu.h"
> > +#include "libavutil/imgutils.h"
> > +#include "libavutil/intreadwrite.h"
> > +#include "libavutil/opt.h"
> > +
> > +#include "libavcodec/bytestream.h"
> > +
> > +#include "libswscale/swscale.h"
> > +
> > +
> > +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
> > +
> > +static void error(const char *err)
> > +{
> > +    fprintf(stderr, "%s", err);
> > +    exit(1);
> > +}
> > +
> > +static int alloc_plane(uint8_t *data[AV_VIDEO_MAX_PLANES], int stride[AV_VIDEO_MAX_PLANES], int w, int h, int format, int *hshift, int *vshift)
> > +{
> > +    int ret = av_image_fill_linesizes(stride, format, w);
> > +    if (ret < 0)
> > +        return -1;
> > +
> > +    av_pix_fmt_get_chroma_sub_sample(format, hshift, vshift);
> > +
> > +    for(int p=0; p<AV_VIDEO_MAX_PLANES; p++) {
> > +        if (stride[p]) {
> > +            stride[p] = FFALIGN(stride[p], 32);
> > +            int ph = AV_CEIL_RSHIFT(h, (p == 1 || p == 2) ? *vshift : 0);
> > +            av_log(0,0, "P:%d St %d ph %d\n", p, stride[p], ph);
> > +            data[p] = av_mallocz(stride[p] * ph + 32);
> > +            if (!data[p])
> > +                return -1;
> > +        }
> > +    }
> > +    if (format == AV_PIX_FMT_PAL8) {
> > +        data[1] = av_mallocz(256*4);
> > +        if (!data[1])
> > +            return -1;
> > +    }
> > +    return 0;
> 
> av_image_alloc()? Would be better to actually test sws with buffers created
> by our own public helpers.

av_image_alloc() allocates the planes in one continous piece
so teh fuzzer would not be able to detect accesses over the end of the first
or accesses prior the 2nd.

So this is not possible


> 
> > +}
> > +
> > +static void free_plane(uint8_t *data[AV_VIDEO_MAX_PLANES])
> > +{
> > +    for(int p=0; p<AV_VIDEO_MAX_PLANES; p++)
> > +        av_freep(&data[p]);
> > +}
> > +
> > +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
> > +    int srcW= 48, srcH = 48;
> > +    int dstW= 48, dstH = 48;
> > +    int srcHShift, srcVShift;
> > +    int dstHShift, dstVShift;
> > +    unsigned flags = 1;
> > +    int srcStride[AV_VIDEO_MAX_PLANES] = {0};
> > +    int dstStride[AV_VIDEO_MAX_PLANES] = {0};
> > +    int ret;
> > +    const uint8_t *end = data + size;
> > +    enum AVPixelFormat srcFormat = AV_PIX_FMT_YUV420P;
> > +    enum AVPixelFormat dstFormat = AV_PIX_FMT_YUV420P;
> > +    uint8_t *src[4] = { 0 };
> > +    uint8_t *dst[4] = { 0 };
> 
> AV_VIDEO_MAX_PLANES.

will change


> 
> > +    struct SwsContext *sws = NULL;
> > +    const AVPixFmtDescriptor *desc_src, *desc_dst;
> > +
> > +    if (size > 128) {
> > +        GetByteContext gbc;
> > +        int64_t flags64;
> > +
> > +        size -= 128;
> > +        bytestream2_init(&gbc, data + size, 128);
> > +        srcW = bytestream2_get_le32(&gbc) % 256;
> > +        srcH = bytestream2_get_le32(&gbc) % 256;
> > +        dstW = bytestream2_get_le32(&gbc) % 256;
> > +        dstH = bytestream2_get_le32(&gbc) % 256;
> > +        flags = bytestream2_get_le32(&gbc);
> > +
> > +        srcFormat = bytestream2_get_le32(&gbc) % AV_PIX_FMT_NB;
> > +        dstFormat = bytestream2_get_le32(&gbc) % AV_PIX_FMT_NB;
> 
> nit: Maybe sanitize the choices with sws_isSupportedInput() and
> sws_isSupportedOutput()? Unless having sws_init_context() fail with invalid
> arguments is also intended.

Honestly i do not know which way is best


> 
> > +
> > +        flags64 = bytestream2_get_le64(&gbc);
> > +        if (flags64 & 0x10)
> > +            av_force_cpu_flags(0);
> > +
> > +        if (av_image_check_size(srcW, srcH, srcFormat, NULL))
> > +            srcW = srcH = 123;
> > +        if (av_image_check_size(dstW, dstH, dstFormat, NULL))
> > +            dstW = dstH = 123;
> 
> Is there a format where this could fail, knowing the dimensions are at most
> 255x255?

The 255 is temporary, a less restrictive size should be choosen as there may
be bugs with huge sizes. Its just that these really slow it down


> 
> > +        //TODO alphablend
> > +    }
> > +
> > +    desc_src = av_pix_fmt_desc_get(srcFormat);
> > +    desc_dst = av_pix_fmt_desc_get(dstFormat);
> > +
> > +    ret = alloc_plane(src, srcStride, srcW, srcH, srcFormat, &srcHShift, &srcVShift);
> > +    if (ret < 0)
> > +        goto end;
> > +
> > +    ret = alloc_plane(dst, dstStride, dstW, dstH, dstFormat, &dstHShift, &dstVShift);
> > +    if (ret < 0)
> > +        goto end;
> > +
> > +
> > +    for(int p=0; p<AV_VIDEO_MAX_PLANES; p++) {
> > +        int psize = srcStride[p] * AV_CEIL_RSHIFT(srcH, (p == 1 || p == 2) ? srcVShift : 0);
> > +        if (psize > size)
> > +            psize = size;
> > +        if (psize) {
> > +            memcpy(src[p], data, psize);
> > +            data += psize;
> > +            size -= psize;
> > +        }
> > +    }
> 
> av_image_copy(). Or av_image_copy_plane() in a loop if you prefer.

these dont seem to have a input size so ill leave it for now

thx

[...]
Michael Niedermayer Feb. 18, 2024, 1:20 a.m. UTC | #4
On Sat, Feb 17, 2024 at 07:04:08PM -0500, Sean McGovern wrote:
> On Sat, Feb 17, 2024, 18:49 Michael Niedermayer <michael@niedermayer.cc>
> wrote:
> 
> > Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
> > ---
> >  Makefile                  |   3 +
> >  tools/Makefile            |   3 +
> >  tools/target_sws_fuzzer.c | 168 ++++++++++++++++++++++++++++++++++++++
> >  3 files changed, 174 insertions(+)
> >  create mode 100644 tools/target_sws_fuzzer.c
> >
> > diff --git a/Makefile b/Makefile
> > index dbc930270b3..b309dbc4db9 100644
> > --- a/Makefile
> > +++ b/Makefile
> > @@ -64,6 +64,9 @@ tools/target_dem_fuzzer$(EXESUF):
> > tools/target_dem_fuzzer.o $(FF_DEP_LIBS)
> >  tools/target_io_dem_fuzzer$(EXESUF): tools/target_io_dem_fuzzer.o
> > $(FF_DEP_LIBS)
> >         $(LD) $(LDFLAGS) $(LDEXEFLAGS) $(LD_O) $^ $(ELIBS) $(FF_EXTRALIBS)
> > $(LIBFUZZER_PATH)
> >
> > +tools/target_sws_fuzzer$(EXESUF): tools/target_sws_fuzzer.o $(FF_DEP_LIBS)
> > +       $(LD) $(LDFLAGS) $(LDEXEFLAGS) $(LD_O) $^ $(ELIBS) $(FF_EXTRALIBS)
> > $(LIBFUZZER_PATH)
> > +
> >
> >  tools/enum_options$(EXESUF): ELIBS = $(FF_EXTRALIBS)
> >  tools/enum_options$(EXESUF): $(FF_DEP_LIBS)
> > diff --git a/tools/Makefile b/tools/Makefile
> > index dee6a416688..72e8e709a8d 100644
> > --- a/tools/Makefile
> > +++ b/tools/Makefile
> > @@ -17,6 +17,9 @@ tools/target_dem_fuzzer.o: tools/target_dem_fuzzer.c
> >  tools/target_io_dem_fuzzer.o: tools/target_dem_fuzzer.c
> >         $(COMPILE_C) -DIO_FLAT=0
> >
> > +tools/target_sws_fuzzer.o: tools/target_sws_fuzzer.c
> > +       $(COMPILE_C)
> > +
> >  tools/enc_recon_frame_test$(EXESUF): tools/decode_simple.o
> >  tools/venc_data_dump$(EXESUF): tools/decode_simple.o
> >  tools/scale_slice_test$(EXESUF): tools/decode_simple.o
> > diff --git a/tools/target_sws_fuzzer.c b/tools/target_sws_fuzzer.c
> > new file mode 100644
> > index 00000000000..babb6e81629
> > --- /dev/null
> > +++ b/tools/target_sws_fuzzer.c
> > @@ -0,0 +1,168 @@
> > +/*
> > + * Copyright (c) 2024 Michael Niedermayer <michael-ffmpeg@niedermayer.cc>
> > + *
> > + * This file is part of FFmpeg.
> > + *
> > + * FFmpeg is free software; you can redistribute it and/or
> > + * modify it under the terms of the GNU Lesser General Public
> > + * License as published by the Free Software Foundation; either
> > + * version 2.1 of the License, or (at your option) any later version.
> > + *
> > + * FFmpeg is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> > + * Lesser General Public License for more details.
> > + *
> > + * You should have received a copy of the GNU Lesser General Public
> > + * License along with FFmpeg; if not, write to the Free Software
> > + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
> > 02110-1301 USA
> > + */
> > +
> > +#include "config.h"
> > +#include "libavutil/avassert.h"
> > +#include "libavutil/avstring.h"
> > +#include "libavutil/cpu.h"
> > +#include "libavutil/imgutils.h"
> > +#include "libavutil/intreadwrite.h"
> > +#include "libavutil/opt.h"
> > +
> > +#include "libavcodec/bytestream.h"
> > +
> > +#include "libswscale/swscale.h"
> > +
> > +
> > +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
> > +
> > +static void error(const char *err)
> > +{
> > +    fprintf(stderr, "%s", err);
> > +    exit(1);
> > +}
> > +
> > +static int alloc_plane(uint8_t *data[AV_VIDEO_MAX_PLANES], int
> > stride[AV_VIDEO_MAX_PLANES], int w, int h, int format, int *hshift, int
> > *vshift)
> > +{
> > +    int ret = av_image_fill_linesizes(stride, format, w);
> > +    if (ret < 0)
> > +        return -1;
> > +
> > +    av_pix_fmt_get_chroma_sub_sample(format, hshift, vshift);
> > +
> > +    for(int p=0; p<AV_VIDEO_MAX_PLANES; p++) {
> > +        if (stride[p]) {
> > +            stride[p] = FFALIGN(stride[p], 32);
> > +            int ph = AV_CEIL_RSHIFT(h, (p == 1 || p == 2) ? *vshift : 0);
> > +            av_log(0,0, "P:%d St %d ph %d\n", p, stride[p], ph);
> > +            data[p] = av_mallocz(stride[p] * ph + 32);
> > +            if (!data[p])
> > +                return -1;
> > +        }
> > +    }
> > +    if (format == AV_PIX_FMT_PAL8) {
> > +        data[1] = av_mallocz(256*4);
> > +        if (!data[1])
> > +            return -1;
> > +    }
> > +    return 0;
> > +}
> > +
> > +static void free_plane(uint8_t *data[AV_VIDEO_MAX_PLANES])
> > +{
> > +    for(int p=0; p<AV_VIDEO_MAX_PLANES; p++)
> > +        av_freep(&data[p]);
> > +}
> > +
> > +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
> > +    int srcW= 48, srcH = 48;
> > +    int dstW= 48, dstH = 48;
> > +    int srcHShift, srcVShift;
> > +    int dstHShift, dstVShift;
> > +    unsigned flags = 1;
> > +    int srcStride[AV_VIDEO_MAX_PLANES] = {0};
> > +    int dstStride[AV_VIDEO_MAX_PLANES] = {0};
> > +    int ret;
> > +    const uint8_t *end = data + size;
> > +    enum AVPixelFormat srcFormat = AV_PIX_FMT_YUV420P;
> > +    enum AVPixelFormat dstFormat = AV_PIX_FMT_YUV420P;
> > +    uint8_t *src[4] = { 0 };
> > +    uint8_t *dst[4] = { 0 };
> > +    struct SwsContext *sws = NULL;
> > +    const AVPixFmtDescriptor *desc_src, *desc_dst;
> > +
> > +    if (size > 128) {
> > +        GetByteContext gbc;
> > +        int64_t flags64;
> > +
> > +        size -= 128;
> > +        bytestream2_init(&gbc, data + size, 128);
> > +        srcW = bytestream2_get_le32(&gbc) % 256;
> > +        srcH = bytestream2_get_le32(&gbc) % 256;
> > +        dstW = bytestream2_get_le32(&gbc) % 256;
> > +        dstH = bytestream2_get_le32(&gbc) % 256;
> > +        flags = bytestream2_get_le32(&gbc);
> > +
> > +        srcFormat = bytestream2_get_le32(&gbc) % AV_PIX_FMT_NB;
> > +        dstFormat = bytestream2_get_le32(&gbc) % AV_PIX_FMT_NB;
> > +
> > +        flags64 = bytestream2_get_le64(&gbc);
> > +        if (flags64 & 0x10)
> > +            av_force_cpu_flags(0);
> > +
> > +        if (av_image_check_size(srcW, srcH, srcFormat, NULL))
> > +            srcW = srcH = 123;
> > +        if (av_image_check_size(dstW, dstH, dstFormat, NULL))
> > +            dstW = dstH = 123;
> > +        //TODO alphablend
> > +    }
> > +
> > +    desc_src = av_pix_fmt_desc_get(srcFormat);
> > +    desc_dst = av_pix_fmt_desc_get(dstFormat);
> > +
> > +    ret = alloc_plane(src, srcStride, srcW, srcH, srcFormat, &srcHShift,
> > &srcVShift);
> > +    if (ret < 0)
> > +        goto end;
> > +
> > +    ret = alloc_plane(dst, dstStride, dstW, dstH, dstFormat, &dstHShift,
> > &dstVShift);
> > +    if (ret < 0)
> > +        goto end;
> > +
> > +
> > +    for(int p=0; p<AV_VIDEO_MAX_PLANES; p++) {
> > +        int psize = srcStride[p] * AV_CEIL_RSHIFT(srcH, (p == 1 || p ==
> > 2) ? srcVShift : 0);
> > +        if (psize > size)
> > +            psize = size;
> > +        if (psize) {
> > +            memcpy(src[p], data, psize);
> > +            data += psize;
> > +            size -= psize;
> > +        }
> > +    }
> > +
> > +    sws = sws_alloc_context();
> > +    if (!sws)
> > +        error("Failed sws allocation");
> > +
> > +    av_opt_set_int(sws, "sws_flags",  flags, 0);
> > +    av_opt_set_int(sws, "srcw",       srcW, 0);
> > +    av_opt_set_int(sws, "srch",       srcH, 0);
> > +    av_opt_set_int(sws, "dstw",       dstW, 0);
> > +    av_opt_set_int(sws, "dsth",       dstH, 0);
> > +    av_opt_set_int(sws, "src_format", srcFormat, 0);
> > +    av_opt_set_int(sws, "dst_format", dstFormat, 0);
> > +    av_opt_set(sws, "alphablend", "none", 0);
> > +
> > +    ret = sws_init_context(sws, NULL, NULL);
> > +    if (ret < 0)
> > +        goto end;
> > +
> > +    fprintf(stderr, "%d x %d %s -> %d x %d %s\n", srcW, srcH,
> > desc_src->name, dstW, dstH, desc_dst->name);
> > +    //TODO Slices
> > +    sws_scale(sws, (const uint8_t * const*)src, srcStride, 0, srcH, dst,
> > dstStride);
> > +
> > +end:
> > +    sws_freeContext(sws);
> > +
> > +    free_plane(src);
> > +    free_plane(dst);
> > +
> > +    return 0;
> > +}
> > --
> > 2.17.1
> >
> > _______________________________________________
> > ffmpeg-devel mailing list
> > ffmpeg-devel@ffmpeg.org
> > https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> >
> > To unsubscribe, visit link above, or email
> > ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
> >
> 
> I don't see anything intrinsically wrong with this other than:
> 
> - is this compiler-backend specific? Does it work without LLVM?

It uses the same entry points as the existing decode/demxuer fuzzer
I have no oppinon on using "something else"
but "something else" maybe will not work with "ossfuzz"


> - would it ever be useful to redirect the output of error() to another fd?
> In which case maybe a convenience method that defaults to stderr would be
> appropriate?

I dont know. Noone seems to have asked this question before in relation
to the existing decoder fuzzer code

thx

[...]
James Almer Feb. 18, 2024, 1:47 a.m. UTC | #5
On 2/17/2024 10:03 PM, Michael Niedermayer wrote:
> On Sat, Feb 17, 2024 at 09:13:21PM -0300, James Almer wrote:
>>
>>
>> On 2/17/2024 8:48 PM, Michael Niedermayer wrote:
>>> Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
>>> ---
>>>    Makefile                  |   3 +
>>>    tools/Makefile            |   3 +
>>>    tools/target_sws_fuzzer.c | 168 ++++++++++++++++++++++++++++++++++++++
>>>    3 files changed, 174 insertions(+)
>>>    create mode 100644 tools/target_sws_fuzzer.c
>>>
>>> diff --git a/Makefile b/Makefile
>>> index dbc930270b3..b309dbc4db9 100644
>>> --- a/Makefile
>>> +++ b/Makefile
>>> @@ -64,6 +64,9 @@ tools/target_dem_fuzzer$(EXESUF): tools/target_dem_fuzzer.o $(FF_DEP_LIBS)
>>>    tools/target_io_dem_fuzzer$(EXESUF): tools/target_io_dem_fuzzer.o $(FF_DEP_LIBS)
>>>    	$(LD) $(LDFLAGS) $(LDEXEFLAGS) $(LD_O) $^ $(ELIBS) $(FF_EXTRALIBS) $(LIBFUZZER_PATH)
>>> +tools/target_sws_fuzzer$(EXESUF): tools/target_sws_fuzzer.o $(FF_DEP_LIBS)
>>> +	$(LD) $(LDFLAGS) $(LDEXEFLAGS) $(LD_O) $^ $(ELIBS) $(FF_EXTRALIBS) $(LIBFUZZER_PATH)
>>> +
>>>    tools/enum_options$(EXESUF): ELIBS = $(FF_EXTRALIBS)
>>>    tools/enum_options$(EXESUF): $(FF_DEP_LIBS)
>>> diff --git a/tools/Makefile b/tools/Makefile
>>> index dee6a416688..72e8e709a8d 100644
>>> --- a/tools/Makefile
>>> +++ b/tools/Makefile
>>> @@ -17,6 +17,9 @@ tools/target_dem_fuzzer.o: tools/target_dem_fuzzer.c
>>>    tools/target_io_dem_fuzzer.o: tools/target_dem_fuzzer.c
>>>    	$(COMPILE_C) -DIO_FLAT=0
>>> +tools/target_sws_fuzzer.o: tools/target_sws_fuzzer.c
>>> +	$(COMPILE_C)
>>> +
>>>    tools/enc_recon_frame_test$(EXESUF): tools/decode_simple.o
>>>    tools/venc_data_dump$(EXESUF): tools/decode_simple.o
>>>    tools/scale_slice_test$(EXESUF): tools/decode_simple.o
>>> diff --git a/tools/target_sws_fuzzer.c b/tools/target_sws_fuzzer.c
>>> new file mode 100644
>>> index 00000000000..babb6e81629
>>> --- /dev/null
>>> +++ b/tools/target_sws_fuzzer.c
>>> @@ -0,0 +1,168 @@
>>> +/*
>>> + * Copyright (c) 2024 Michael Niedermayer <michael-ffmpeg@niedermayer.cc>
>>> + *
>>> + * This file is part of FFmpeg.
>>> + *
>>> + * FFmpeg is free software; you can redistribute it and/or
>>> + * modify it under the terms of the GNU Lesser General Public
>>> + * License as published by the Free Software Foundation; either
>>> + * version 2.1 of the License, or (at your option) any later version.
>>> + *
>>> + * FFmpeg is distributed in the hope that it will be useful,
>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>>> + * Lesser General Public License for more details.
>>> + *
>>> + * You should have received a copy of the GNU Lesser General Public
>>> + * License along with FFmpeg; if not, write to the Free Software
>>> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
>>> + */
>>> +
>>> +#include "config.h"
>>> +#include "libavutil/avassert.h"
>>> +#include "libavutil/avstring.h"
>>> +#include "libavutil/cpu.h"
>>> +#include "libavutil/imgutils.h"
>>> +#include "libavutil/intreadwrite.h"
>>> +#include "libavutil/opt.h"
>>> +
>>> +#include "libavcodec/bytestream.h"
>>> +
>>> +#include "libswscale/swscale.h"
>>> +
>>> +
>>> +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
>>> +
>>> +static void error(const char *err)
>>> +{
>>> +    fprintf(stderr, "%s", err);
>>> +    exit(1);
>>> +}
>>> +
>>> +static int alloc_plane(uint8_t *data[AV_VIDEO_MAX_PLANES], int stride[AV_VIDEO_MAX_PLANES], int w, int h, int format, int *hshift, int *vshift)
>>> +{
>>> +    int ret = av_image_fill_linesizes(stride, format, w);
>>> +    if (ret < 0)
>>> +        return -1;
>>> +
>>> +    av_pix_fmt_get_chroma_sub_sample(format, hshift, vshift);
>>> +
>>> +    for(int p=0; p<AV_VIDEO_MAX_PLANES; p++) {
>>> +        if (stride[p]) {
>>> +            stride[p] = FFALIGN(stride[p], 32);
>>> +            int ph = AV_CEIL_RSHIFT(h, (p == 1 || p == 2) ? *vshift : 0);
>>> +            av_log(0,0, "P:%d St %d ph %d\n", p, stride[p], ph);
>>> +            data[p] = av_mallocz(stride[p] * ph + 32);
>>> +            if (!data[p])
>>> +                return -1;
>>> +        }
>>> +    }
>>> +    if (format == AV_PIX_FMT_PAL8) {
>>> +        data[1] = av_mallocz(256*4);
>>> +        if (!data[1])
>>> +            return -1;
>>> +    }
>>> +    return 0;
>>
>> av_image_alloc()? Would be better to actually test sws with buffers created
>> by our own public helpers.
> 
> av_image_alloc() allocates the planes in one continous piece
> so teh fuzzer would not be able to detect accesses over the end of the first
> or accesses prior the 2nd.
> 
> So this is not possible

Then use av_image_fill_plane_sizes() after av_image_fill_linesizes() and 
then allocate the buffers with its output. See fuzz_video_get_buffer() 
in test_dec_fuzzer.c
It's best if we don't rewrite basic buffer allocation and size 
calculation functions every time we add new code.

> 
> 
>>
>>> +}
>>> +
>>> +static void free_plane(uint8_t *data[AV_VIDEO_MAX_PLANES])
>>> +{
>>> +    for(int p=0; p<AV_VIDEO_MAX_PLANES; p++)
>>> +        av_freep(&data[p]);
>>> +}
>>> +
>>> +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
>>> +    int srcW= 48, srcH = 48;
>>> +    int dstW= 48, dstH = 48;
>>> +    int srcHShift, srcVShift;
>>> +    int dstHShift, dstVShift;
>>> +    unsigned flags = 1;
>>> +    int srcStride[AV_VIDEO_MAX_PLANES] = {0};
>>> +    int dstStride[AV_VIDEO_MAX_PLANES] = {0};
>>> +    int ret;
>>> +    const uint8_t *end = data + size;
>>> +    enum AVPixelFormat srcFormat = AV_PIX_FMT_YUV420P;
>>> +    enum AVPixelFormat dstFormat = AV_PIX_FMT_YUV420P;
>>> +    uint8_t *src[4] = { 0 };
>>> +    uint8_t *dst[4] = { 0 };
>>
>> AV_VIDEO_MAX_PLANES.
> 
> will change
> 
> 
>>
>>> +    struct SwsContext *sws = NULL;
>>> +    const AVPixFmtDescriptor *desc_src, *desc_dst;
>>> +
>>> +    if (size > 128) {
>>> +        GetByteContext gbc;
>>> +        int64_t flags64;
>>> +
>>> +        size -= 128;
>>> +        bytestream2_init(&gbc, data + size, 128);
>>> +        srcW = bytestream2_get_le32(&gbc) % 256;
>>> +        srcH = bytestream2_get_le32(&gbc) % 256;
>>> +        dstW = bytestream2_get_le32(&gbc) % 256;
>>> +        dstH = bytestream2_get_le32(&gbc) % 256;
>>> +        flags = bytestream2_get_le32(&gbc);
>>> +
>>> +        srcFormat = bytestream2_get_le32(&gbc) % AV_PIX_FMT_NB;
>>> +        dstFormat = bytestream2_get_le32(&gbc) % AV_PIX_FMT_NB;
>>
>> nit: Maybe sanitize the choices with sws_isSupportedInput() and
>> sws_isSupportedOutput()? Unless having sws_init_context() fail with invalid
>> arguments is also intended.
> 
> Honestly i do not know which way is best

Leave it as is, it's not important.

> 
> 
>>
>>> +
>>> +        flags64 = bytestream2_get_le64(&gbc);
>>> +        if (flags64 & 0x10)
>>> +            av_force_cpu_flags(0);
>>> +
>>> +        if (av_image_check_size(srcW, srcH, srcFormat, NULL))
>>> +            srcW = srcH = 123;
>>> +        if (av_image_check_size(dstW, dstH, dstFormat, NULL))
>>> +            dstW = dstH = 123;
>>
>> Is there a format where this could fail, knowing the dimensions are at most
>> 255x255?
> 
> The 255 is temporary, a less restrictive size should be choosen as there may
> be bugs with huge sizes. Its just that these really slow it down
> 
> 
>>
>>> +        //TODO alphablend
>>> +    }
>>> +
>>> +    desc_src = av_pix_fmt_desc_get(srcFormat);
>>> +    desc_dst = av_pix_fmt_desc_get(dstFormat);
>>> +
>>> +    ret = alloc_plane(src, srcStride, srcW, srcH, srcFormat, &srcHShift, &srcVShift);
>>> +    if (ret < 0)
>>> +        goto end;
>>> +
>>> +    ret = alloc_plane(dst, dstStride, dstW, dstH, dstFormat, &dstHShift, &dstVShift);
>>> +    if (ret < 0)
>>> +        goto end;
>>> +
>>> +
>>> +    for(int p=0; p<AV_VIDEO_MAX_PLANES; p++) {
>>> +        int psize = srcStride[p] * AV_CEIL_RSHIFT(srcH, (p == 1 || p == 2) ? srcVShift : 0);
>>> +        if (psize > size)
>>> +            psize = size;
>>> +        if (psize) {
>>> +            memcpy(src[p], data, psize);
>>> +            data += psize;
>>> +            size -= psize;
>>> +        }
>>> +    }
>>
>> av_image_copy(). Or av_image_copy_plane() in a loop if you prefer.
> 
> these dont seem to have a input size so ill leave it for now

Ok.

> 
> thx
> 
> [...]
> 
> 
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> 
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
diff mbox series

Patch

diff --git a/Makefile b/Makefile
index dbc930270b3..b309dbc4db9 100644
--- a/Makefile
+++ b/Makefile
@@ -64,6 +64,9 @@  tools/target_dem_fuzzer$(EXESUF): tools/target_dem_fuzzer.o $(FF_DEP_LIBS)
 tools/target_io_dem_fuzzer$(EXESUF): tools/target_io_dem_fuzzer.o $(FF_DEP_LIBS)
 	$(LD) $(LDFLAGS) $(LDEXEFLAGS) $(LD_O) $^ $(ELIBS) $(FF_EXTRALIBS) $(LIBFUZZER_PATH)
 
+tools/target_sws_fuzzer$(EXESUF): tools/target_sws_fuzzer.o $(FF_DEP_LIBS)
+	$(LD) $(LDFLAGS) $(LDEXEFLAGS) $(LD_O) $^ $(ELIBS) $(FF_EXTRALIBS) $(LIBFUZZER_PATH)
+
 
 tools/enum_options$(EXESUF): ELIBS = $(FF_EXTRALIBS)
 tools/enum_options$(EXESUF): $(FF_DEP_LIBS)
diff --git a/tools/Makefile b/tools/Makefile
index dee6a416688..72e8e709a8d 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -17,6 +17,9 @@  tools/target_dem_fuzzer.o: tools/target_dem_fuzzer.c
 tools/target_io_dem_fuzzer.o: tools/target_dem_fuzzer.c
 	$(COMPILE_C) -DIO_FLAT=0
 
+tools/target_sws_fuzzer.o: tools/target_sws_fuzzer.c
+	$(COMPILE_C)
+
 tools/enc_recon_frame_test$(EXESUF): tools/decode_simple.o
 tools/venc_data_dump$(EXESUF): tools/decode_simple.o
 tools/scale_slice_test$(EXESUF): tools/decode_simple.o
diff --git a/tools/target_sws_fuzzer.c b/tools/target_sws_fuzzer.c
new file mode 100644
index 00000000000..babb6e81629
--- /dev/null
+++ b/tools/target_sws_fuzzer.c
@@ -0,0 +1,168 @@ 
+/*
+ * Copyright (c) 2024 Michael Niedermayer <michael-ffmpeg@niedermayer.cc>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "config.h"
+#include "libavutil/avassert.h"
+#include "libavutil/avstring.h"
+#include "libavutil/cpu.h"
+#include "libavutil/imgutils.h"
+#include "libavutil/intreadwrite.h"
+#include "libavutil/opt.h"
+
+#include "libavcodec/bytestream.h"
+
+#include "libswscale/swscale.h"
+
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
+
+static void error(const char *err)
+{
+    fprintf(stderr, "%s", err);
+    exit(1);
+}
+
+static int alloc_plane(uint8_t *data[AV_VIDEO_MAX_PLANES], int stride[AV_VIDEO_MAX_PLANES], int w, int h, int format, int *hshift, int *vshift)
+{
+    int ret = av_image_fill_linesizes(stride, format, w);
+    if (ret < 0)
+        return -1;
+
+    av_pix_fmt_get_chroma_sub_sample(format, hshift, vshift);
+
+    for(int p=0; p<AV_VIDEO_MAX_PLANES; p++) {
+        if (stride[p]) {
+            stride[p] = FFALIGN(stride[p], 32);
+            int ph = AV_CEIL_RSHIFT(h, (p == 1 || p == 2) ? *vshift : 0);
+            av_log(0,0, "P:%d St %d ph %d\n", p, stride[p], ph);
+            data[p] = av_mallocz(stride[p] * ph + 32);
+            if (!data[p])
+                return -1;
+        }
+    }
+    if (format == AV_PIX_FMT_PAL8) {
+        data[1] = av_mallocz(256*4);
+        if (!data[1])
+            return -1;
+    }
+    return 0;
+}
+
+static void free_plane(uint8_t *data[AV_VIDEO_MAX_PLANES])
+{
+    for(int p=0; p<AV_VIDEO_MAX_PLANES; p++)
+        av_freep(&data[p]);
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+    int srcW= 48, srcH = 48;
+    int dstW= 48, dstH = 48;
+    int srcHShift, srcVShift;
+    int dstHShift, dstVShift;
+    unsigned flags = 1;
+    int srcStride[AV_VIDEO_MAX_PLANES] = {0};
+    int dstStride[AV_VIDEO_MAX_PLANES] = {0};
+    int ret;
+    const uint8_t *end = data + size;
+    enum AVPixelFormat srcFormat = AV_PIX_FMT_YUV420P;
+    enum AVPixelFormat dstFormat = AV_PIX_FMT_YUV420P;
+    uint8_t *src[4] = { 0 };
+    uint8_t *dst[4] = { 0 };
+    struct SwsContext *sws = NULL;
+    const AVPixFmtDescriptor *desc_src, *desc_dst;
+
+    if (size > 128) {
+        GetByteContext gbc;
+        int64_t flags64;
+
+        size -= 128;
+        bytestream2_init(&gbc, data + size, 128);
+        srcW = bytestream2_get_le32(&gbc) % 256;
+        srcH = bytestream2_get_le32(&gbc) % 256;
+        dstW = bytestream2_get_le32(&gbc) % 256;
+        dstH = bytestream2_get_le32(&gbc) % 256;
+        flags = bytestream2_get_le32(&gbc);
+
+        srcFormat = bytestream2_get_le32(&gbc) % AV_PIX_FMT_NB;
+        dstFormat = bytestream2_get_le32(&gbc) % AV_PIX_FMT_NB;
+
+        flags64 = bytestream2_get_le64(&gbc);
+        if (flags64 & 0x10)
+            av_force_cpu_flags(0);
+
+        if (av_image_check_size(srcW, srcH, srcFormat, NULL))
+            srcW = srcH = 123;
+        if (av_image_check_size(dstW, dstH, dstFormat, NULL))
+            dstW = dstH = 123;
+        //TODO alphablend
+    }
+
+    desc_src = av_pix_fmt_desc_get(srcFormat);
+    desc_dst = av_pix_fmt_desc_get(dstFormat);
+
+    ret = alloc_plane(src, srcStride, srcW, srcH, srcFormat, &srcHShift, &srcVShift);
+    if (ret < 0)
+        goto end;
+
+    ret = alloc_plane(dst, dstStride, dstW, dstH, dstFormat, &dstHShift, &dstVShift);
+    if (ret < 0)
+        goto end;
+
+
+    for(int p=0; p<AV_VIDEO_MAX_PLANES; p++) {
+        int psize = srcStride[p] * AV_CEIL_RSHIFT(srcH, (p == 1 || p == 2) ? srcVShift : 0);
+        if (psize > size)
+            psize = size;
+        if (psize) {
+            memcpy(src[p], data, psize);
+            data += psize;
+            size -= psize;
+        }
+    }
+
+    sws = sws_alloc_context();
+    if (!sws)
+        error("Failed sws allocation");
+
+    av_opt_set_int(sws, "sws_flags",  flags, 0);
+    av_opt_set_int(sws, "srcw",       srcW, 0);
+    av_opt_set_int(sws, "srch",       srcH, 0);
+    av_opt_set_int(sws, "dstw",       dstW, 0);
+    av_opt_set_int(sws, "dsth",       dstH, 0);
+    av_opt_set_int(sws, "src_format", srcFormat, 0);
+    av_opt_set_int(sws, "dst_format", dstFormat, 0);
+    av_opt_set(sws, "alphablend", "none", 0);
+
+    ret = sws_init_context(sws, NULL, NULL);
+    if (ret < 0)
+        goto end;
+
+    fprintf(stderr, "%d x %d %s -> %d x %d %s\n", srcW, srcH, desc_src->name, dstW, dstH, desc_dst->name);
+    //TODO Slices
+    sws_scale(sws, (const uint8_t * const*)src, srcStride, 0, srcH, dst, dstStride);
+
+end:
+    sws_freeContext(sws);
+
+    free_plane(src);
+    free_plane(dst);
+
+    return 0;
+}