From 6cb8d8a2b1be08b51f65bf38ce1e699698315138 Mon Sep 17 00:00:00 2001 From: Harish Mahendrakar Date: Fri, 9 Nov 2018 14:12:07 -0800 Subject: [PATCH] Added libFuzzer plugin to test decoders vpx_dec_fuzzer.cc can be built with clang++ to generate fuzzer binary Build instructions are part of the file Change-Id: I19ba0bd49b236e27b27e81a83f6de59f15bdc994 --- examples/vpx_dec_fuzzer.cc | 174 +++++++++++++++++++++++++++++++++++++ 1 file changed, 174 insertions(+) create mode 100644 examples/vpx_dec_fuzzer.cc diff --git a/examples/vpx_dec_fuzzer.cc b/examples/vpx_dec_fuzzer.cc new file mode 100644 index 000000000..1bb2e9c32 --- /dev/null +++ b/examples/vpx_dec_fuzzer.cc @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2018 The WebM project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +/* + * Fuzzer for libvpx decoders + * ========================== + * Requirements + * -------------- + * Requires Clang 6.0 or above as -fsanitize=fuzzer is used as a linker + * option. + + * Steps to build + * -------------- + * Clone libvpx repository + $git clone https://chromium.googlesource.com/webm/libvpx + + * Create a directory in parallel to libvpx and change directory + $mkdir vpx_dec_fuzzer + $cd vpx_dec_fuzzer/ + + * Enable sanitizers (Supported: address integer memory thread undefined) + $source ../libvpx/tools/set_analyzer_env.sh address + + * Configure libvpx. + * Note --size-limit and VPX_MAX_ALLOCABLE_MEMORY are defined to avoid + * Out of memory errors when running generated fuzzer binary + $../libvpx/configure --disable-unit-tests --size-limit=12288x12288 \ + --extra-cflags="-DVPX_MAX_ALLOCABLE_MEMORY=1073741824" \ + --disable-webm-io --enable-debug + + * Build libvpx + $make -j32 + + * Build vp9 threaded fuzzer + $ $CXX $CXXFLAGS -std=c++11 -DDECODE_MODE_threaded -DDECODER=vp9 \ + -fsanitize=fuzzer -I../libvpx -I. -Wl,--start-group \ + ../libvpx/examples/vpx_dec_fuzzer.cc -o ./vpx_dec_fuzzer_threaded_vp9 \ + ./libvpx.a ./tools_common.c.o -Wl,--end-group + + * DECODER should be defined as vp9 or vp8 to enable vp9/vp8 + * DECODE_MODE_threaded or DECODE_MODE_serial needs to be defined to test + * multi-threaded or single core implementation + * + * create a corpus directory and copy some ivf files there. + * Based on which codec (vp8/vp9) is being tested, it is recommended to + * have corresponding ivf files in corpus directory + * Empty corpus directoy also is acceptable, though not recommended + $mkdir CORPUS && cp some-files CORPUS + + * Run fuzzing: + $./vpx_dec_fuzzer_threaded_vp9 CORPUS + + * References: + * http://llvm.org/docs/LibFuzzer.html + * https://github.com/google/oss-fuzz + */ + +#include +#include +#include +#include +#if defined(DECODE_MODE_threaded) +#include +#endif +#include + +#include "./tools_common.h" +#include "vpx/vp8dx.h" +#include "vpx/vpx_decoder.h" +#include "vpx_ports/mem_ops.h" + +#define VPX_TOSTRING(str) #str +#define VPX_STRINGIFY(str) VPX_TOSTRING(str) + +static void CloseFile(FILE *file) { fclose(file); } + +/* ReadFrame is derived from ivf_read_frame in ivfdec.c + * This function doesn't call warn(), but instead ignores those errors. + * This is done to minimize the prints on console when running fuzzer + * Also if fread fails to read frame_size number of bytes, instead of + * returning an error, this returns with partial frames. + * This is done to ensure that partial frames are sent to decoder. + */ +static int ReadFrame(FILE *infile, uint8_t **buffer, size_t *bytes_read, + size_t *buffer_size) { + char raw_header[IVF_FRAME_HDR_SZ] = { 0 }; + size_t frame_size = 0; + + if (fread(raw_header, IVF_FRAME_HDR_SZ, 1, infile) == 1) { + frame_size = mem_get_le32(raw_header); + + if (frame_size > 256 * 1024 * 1024) { + frame_size = 0; + } + + if (frame_size > *buffer_size) { + uint8_t *new_buffer = (uint8_t *)realloc(*buffer, 2 * frame_size); + + if (new_buffer) { + *buffer = new_buffer; + *buffer_size = 2 * frame_size; + } else { + frame_size = 0; + } + } + } + + if (!feof(infile)) { + *bytes_read = fread(*buffer, 1, frame_size, infile); + return 0; + } + + return 1; +} + +extern "C" void usage_exit(void) { exit(EXIT_FAILURE); } + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + std::unique_ptr file( + fmemopen((void *)data, size, "rb"), &CloseFile); + if (file == nullptr) { + return 0; + } + // Ensure input contains at least one file header and one frame header + if (size < IVF_FILE_HDR_SZ + IVF_FRAME_HDR_SZ) { + return 0; + } + char header[IVF_FILE_HDR_SZ]; + if (fread(header, 1, IVF_FILE_HDR_SZ, file.get()) != IVF_FILE_HDR_SZ) { + return 0; + } + const VpxInterface *decoder = get_vpx_decoder_by_name(VPX_STRINGIFY(DECODER)); + if (decoder == nullptr) { + return 0; + } + + vpx_codec_ctx_t codec; +#if defined(DECODE_MODE_serial) + const unsigned int threads = 1; +#elif defined(DECODE_MODE_threaded) + // Set thread count in the range [2, 64]. + const unsigned int threads = std::max((data[IVF_FILE_HDR_SZ] & 0x3f) + 1, 2); +#else +#error define one of DECODE_MODE_(serial|threaded) +#endif + vpx_codec_dec_cfg_t cfg = { threads, 0, 0 }; + if (vpx_codec_dec_init(&codec, decoder->codec_interface(), &cfg, 0)) { + return 0; + } + + uint8_t *buffer = nullptr; + size_t buffer_size = 0; + size_t frame_size = 0; + + while (!ReadFrame(file.get(), &buffer, &frame_size, &buffer_size)) { + const vpx_codec_err_t err = + vpx_codec_decode(&codec, buffer, frame_size, nullptr, 0); + static_cast(err); + vpx_codec_iter_t iter = nullptr; + vpx_image_t *img = nullptr; + while ((img = vpx_codec_get_frame(&codec, &iter)) != nullptr) { + } + } + vpx_codec_destroy(&codec); + free(buffer); + return 0; +} -- 2.40.0