From 80fa75131ed387ce1bb729e6aedd0be83ddaa52b Mon Sep 17 00:00:00 2001 From: Ravi Jotwani Date: Tue, 14 Jul 2020 16:49:49 -0700 Subject: [PATCH] [opus] Add new fuzzer (#4118) * added new opus fuzzer, build working * added missing license header, cleaned up Dockerfile * fixed build process * changed build process depending on C or C++ fuzzer * converted opus_multi_fuzzer from C++ to C, reverted build script to only accept C files --- projects/opus/Dockerfile | 1 + projects/opus/build.sh | 5 +- projects/opus/opus_multi_fuzzer.c | 99 +++++++++++++++++++++++++++++++ 3 files changed, 103 insertions(+), 2 deletions(-) create mode 100644 projects/opus/opus_multi_fuzzer.c diff --git a/projects/opus/Dockerfile b/projects/opus/Dockerfile index ccdd01937..717b88270 100644 --- a/projects/opus/Dockerfile +++ b/projects/opus/Dockerfile @@ -21,3 +21,4 @@ RUN git clone https://gitlab.xiph.org/xiph/opus.git RUN wget https://opus-codec.org/static/testvectors/opus_testvectors.tar.gz WORKDIR opus COPY build.sh $SRC/ +COPY *.c tests/ diff --git a/projects/opus/build.sh b/projects/opus/build.sh index 0cd91bf76..850007151 100644 --- a/projects/opus/build.sh +++ b/projects/opus/build.sh @@ -16,7 +16,7 @@ ############################################################################## set -eu -FUZZERS="opus_decode_fuzzer" +FUZZERS="opus_decode_fuzzer opus_multi_fuzzer" BUILDS=(floating fixed) tar xvf $SRC/opus_testvectors.tar.gz @@ -53,7 +53,8 @@ for build in "${BUILDS[@]}"; do # Setup the .options and test corpus zip files using the corresponding # fuzzer's name - cp tests/$fuzzer.options $OUT/${fuzzer}_${build}.options + [ -f tests/$fuzzer.options ] \ + && cp tests/$fuzzer.options $OUT/${fuzzer}_${build}.options zip -r $OUT/${fuzzer}_${build}_seed_corpus.zip opus_testvectors/ done done diff --git a/projects/opus/opus_multi_fuzzer.c b/projects/opus/opus_multi_fuzzer.c new file mode 100644 index 000000000..de63bfb3e --- /dev/null +++ b/projects/opus/opus_multi_fuzzer.c @@ -0,0 +1,99 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include "opus.h" +#include "opus_multistream.h" + +struct TocInfo { + opus_int32 frequency; // in [Hz*1000] + int channels; // number of channels; either 1 or 2 + int frame_len_x2; // in [ms*2]. x2 is to avoid float value of 2.5 ms +}; + +void extractTocInfo(const uint8_t toc, struct TocInfo *const info) { + const int frame_lengths_x2[3][4] = { + {20, 40, 80, 120}, + {20, 40, 20, 40}, + {5, 10, 20, 40} + }; + + info->channels = toc & 4 ? 2 : 1; + + const uint8_t config = toc >> 3; + + int len_index; + if (config < 12) { + len_index = 0; + } else if (config < 16) { + len_index = 1; + } else { + len_index = 2; + } + info->frame_len_x2 = frame_lengths_x2[len_index][config & 3]; + + switch (config >> 2) { + case 0: info->frequency = 8; break; + case 1: info->frequency = 12; break; + case 2: info->frequency = 16; break; + case 3: info->frequency = (config < 14) ? 24 : 48; break; + case 4: info->frequency = 8; break; + case 5: info->frequency = 16; break; + case 6: info->frequency = 24; break; + default: info->frequency = 48; break; + } +} + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + if (size < 3 || size > 1000000) return 0; + + // Using last byte as a number of streams (instead of rand_r). Each stream + // should be at least 3 bytes long hence divmod. + int streams = 1 + data[size - 1] % (size / 3); + if (streams > 255) streams = 255; + unsigned char *mapping = (unsigned char*) malloc(sizeof(unsigned char)*streams); + if (!mapping) return 0; + + for (int i = 0; i < streams; ++i) { + mapping[i] = i; + } + + struct TocInfo info; + extractTocInfo(*data, &info); + + int error = 0; + OpusMSDecoder *const decoder = opus_multistream_decoder_create( + info.frequency * 1000, streams, streams, 0, mapping, &error); + + if (!decoder || error) return 0; + + const int frame_size = (info.frequency * info.frame_len_x2) / 2; + opus_int16 *pcm = (opus_int16*) malloc(sizeof(opus_int16)*frame_size*streams); + if (!pcm) goto exit; + + // opus_decode wants us to use its return value, but we don't really care. + const int foo = + opus_multistream_decode(decoder, data, size, pcm, frame_size, 0); + (void)foo; + + opus_multistream_decoder_destroy(decoder); + + free(pcm); + +exit: + free(mapping); + return 0; +}