Message ID | 20240220024922.22779-1-michael@niedermayer.cc |
---|---|
State | New |
Headers | show |
Series | [FFmpeg-devel,1/3] tools: Add target_sws_fuzzer.c | expand |
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 |
On 2/19/2024 11:49 PM, Michael Niedermayer wrote: > +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[AV_VIDEO_MAX_PLANES] = { 0 }; > + uint8_t *dst[AV_VIDEO_MAX_PLANES] = { 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) % 16384; > + srcH = bytestream2_get_le32(&gbc) % 16384; > + dstW = bytestream2_get_le32(&gbc) % 16384; > + dstH = bytestream2_get_le32(&gbc) % 16384; Might as well use bytestream2_get_le16 to save bytes from the input buffer. > + > + if (srcW * (uint64_t)srcH > 16384 || dstW * (uint64_t)dstH > 16384) > + return 0; // we avoid high res as its very slow This will abort in a lot of cases. Would reading only xW then setting xH to 16384 / xW make sense? You can remove these checks if so.
On Tue, Feb 20, 2024 at 12:41:57AM -0300, James Almer wrote: > On 2/19/2024 11:49 PM, Michael Niedermayer wrote: > > +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[AV_VIDEO_MAX_PLANES] = { 0 }; > > + uint8_t *dst[AV_VIDEO_MAX_PLANES] = { 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) % 16384; > > + srcH = bytestream2_get_le32(&gbc) % 16384; > > + dstW = bytestream2_get_le32(&gbc) % 16384; > > + dstH = bytestream2_get_le32(&gbc) % 16384; > > Might as well use bytestream2_get_le16 to save bytes from the input buffer. > > > + > > + if (srcW * (uint64_t)srcH > 16384 || dstW * (uint64_t)dstH > 16384) > > + return 0; // we avoid high res as its very slow > > This will abort in a lot of cases. Would reading only xW then setting xH to > 16384 / xW make sense? You can remove these checks if so. no that would reduce the number of tested resolutions to one for each width What we are trying to do here, is to map some flat random data into a resolution constraint to X > 0 Y > 0 X*Y <= 16484 and at the same time have each pair occur approximately as frequent as any other that can be achieved in a few ways, ill use the following static void mapres(unsigned *r0, unsigned *r1) { double d = (double)(*r0*10ll - 9ll*UINT32_MAX) / UINT32_MAX; double a = exp(d) * 16384 / exp(1) ; int ai = (int)round(a); uint64_t maxb = 16384 / ai; *r0 = ai; *r1 = 1 + (*r1 * maxb) / UINT32_MAX; } this avoids all the aborts and is flat enough statically for this purpose will apply with that, but if you prefer the previous simpler code, dont hesitate to replace it. Iam not sure this helps the fuzzer at all thx [...]
On 2/20/2024 10:17 PM, Michael Niedermayer wrote: > On Tue, Feb 20, 2024 at 12:41:57AM -0300, James Almer wrote: >> On 2/19/2024 11:49 PM, Michael Niedermayer wrote: >>> +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[AV_VIDEO_MAX_PLANES] = { 0 }; >>> + uint8_t *dst[AV_VIDEO_MAX_PLANES] = { 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) % 16384; >>> + srcH = bytestream2_get_le32(&gbc) % 16384; >>> + dstW = bytestream2_get_le32(&gbc) % 16384; >>> + dstH = bytestream2_get_le32(&gbc) % 16384; >> >> Might as well use bytestream2_get_le16 to save bytes from the input buffer. >> >>> + >>> + if (srcW * (uint64_t)srcH > 16384 || dstW * (uint64_t)dstH > 16384) >>> + return 0; // we avoid high res as its very slow >> >> This will abort in a lot of cases. Would reading only xW then setting xH to >> 16384 / xW make sense? You can remove these checks if so. > > no that would reduce the number of tested resolutions to one for each width > > What we are trying to do here, is to map some flat random data into > a resolution constraint to > X > 0 > Y > 0 > X*Y <= 16484 > and at the same time have each pair occur approximately as frequent as any other > > that can be achieved in a few ways, ill use the following > > static void mapres(unsigned *r0, unsigned *r1) { > double d = (double)(*r0*10ll - 9ll*UINT32_MAX) / UINT32_MAX; > double a = exp(d) * 16384 / exp(1) ; > int ai = (int)round(a); > uint64_t maxb = 16384 / ai; > *r0 = ai; > *r1 = 1 + (*r1 * maxb) / UINT32_MAX; > } > > this avoids all the aborts and is flat enough statically for this purpose > > will apply with that, but if you prefer the previous simpler code, dont hesitate > to replace it. Iam not sure this helps the fuzzer at all No, you know this better than i do.
diff --git a/Makefile b/Makefile index dbc930270b..b309dbc4db 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 dee6a41668..72e8e709a8 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 0000000000..a118368ce0 --- /dev/null +++ b/tools/target_sws_fuzzer.c @@ -0,0 +1,193 @@ +/* + * 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) +{ + size_t size[AV_VIDEO_MAX_PLANES]; + ptrdiff_t ptrdiff_stride[AV_VIDEO_MAX_PLANES]; + int ret = av_image_fill_linesizes(stride, format, w); + if (ret < 0) + return -1; + + av_assert0(AV_VIDEO_MAX_PLANES == 4); // Some of the libavutil API has 4 hardcoded so this has undefined behaviour if its not 4 + + av_pix_fmt_get_chroma_sub_sample(format, hshift, vshift); + + for(int p=0; p<AV_VIDEO_MAX_PLANES; p++) { + stride[p] = + ptrdiff_stride[p] = FFALIGN(stride[p], 32); + } + ret = av_image_fill_plane_sizes(size, format, h, ptrdiff_stride); + if (ret < 0) + return ret; + + for(int p=0; p<AV_VIDEO_MAX_PLANES; p++) { + if (size[p]) { + data[p] = av_mallocz(size[p] + 32); + if (!data[p]) + return -1; + } else + data[p] = NULL; + } + 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[AV_VIDEO_MAX_PLANES] = { 0 }; + uint8_t *dst[AV_VIDEO_MAX_PLANES] = { 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) % 16384; + srcH = bytestream2_get_le32(&gbc) % 16384; + dstW = bytestream2_get_le32(&gbc) % 16384; + dstH = bytestream2_get_le32(&gbc) % 16384; + + if (srcW * (uint64_t)srcH > 16384 || dstW * (uint64_t)dstH > 16384) + return 0; // we avoid high res as its very slow + + flags = bytestream2_get_le32(&gbc); + + unsigned mask = flags & (SWS_POINT | + SWS_AREA | + SWS_BILINEAR | + SWS_FAST_BILINEAR | + SWS_BICUBIC | + SWS_X | + SWS_GAUSS | + SWS_LANCZOS | + SWS_SINC | + SWS_SPLINE | + SWS_BICUBLIN); + mask &= flags; + if (mask && (mask & (mask -1))) + return 0; // multiple scalers are set, not possible + + 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) < 0) + srcW = srcH = 23; + if (av_image_check_size(dstW, dstH, dstFormat, NULL) < 0) + dstW = dstH = 23; + //TODO alphablend + } + + desc_src = av_pix_fmt_desc_get(srcFormat); + desc_dst = av_pix_fmt_desc_get(dstFormat); + + fprintf(stderr, "%d x %d %s -> %d x %d %s\n", srcW, srcH, desc_src->name, dstW, dstH, desc_dst->name); + + 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; + + //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; +}
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc> --- Makefile | 3 + tools/Makefile | 3 + tools/target_sws_fuzzer.c | 193 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 199 insertions(+) create mode 100644 tools/target_sws_fuzzer.c