diff options
Diffstat (limited to 'fuzzer')
-rw-r--r-- | fuzzer/Android.bp | 47 | ||||
-rw-r--r-- | fuzzer/README.md | 62 | ||||
-rw-r--r-- | fuzzer/opus_dec_fuzzer.cpp | 124 |
3 files changed, 233 insertions, 0 deletions
diff --git a/fuzzer/Android.bp b/fuzzer/Android.bp new file mode 100644 index 00000000..711ed34b --- /dev/null +++ b/fuzzer/Android.bp @@ -0,0 +1,47 @@ +/****************************************************************************** + * + * Copyright (C) 2020 The Android Open Source Project + * + * 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. + * + ***************************************************************************** + * Originally developed and contributed by Ittiam Systems Pvt. Ltd, Bangalore + */ + +cc_fuzz { + name: "opus_dec_fuzzer", + host_supported:true, + + static_libs: [ + "libopus", + ], + + srcs: [ + "opus_dec_fuzzer.cpp", + ], +} + +cc_fuzz { + name: "opus_multistream_dec_fuzzer", + host_supported:true, + + static_libs: [ + "libopus", + ], + + srcs: [ + "opus_dec_fuzzer.cpp", + ], + + cflags: ["-DMULTISTREAM"], +} diff --git a/fuzzer/README.md b/fuzzer/README.md new file mode 100644 index 00000000..cd6a680c --- /dev/null +++ b/fuzzer/README.md @@ -0,0 +1,62 @@ +# Fuzzer for libopus decoder + +## Plugin Design Considerations +The fuzzer plugin for opus decoder is designed based on the understanding of the +codec and tries to achieve the following: + +##### Maximize code coverage + +This fuzzer provides support for both single stream and multi stream inputs, +thus enabling fuzzing for API's provided for single stream as well as multi +stream. + +Following arguments are passed to OPUS_DEC_CREATE_API: + +1. Sampling frequency (parameter name: `Fs`) +2. Number of channels (parameter name: `channels`) + +| Parameter| Valid Values| Configured Value| +|------------- |-------------| ----- | +| `Fs` | `8000 ` `12000 ` `16000 ` `24000 ` `48000 ` | Derived from Byte-9 of input stream| +| `channels` | `1 ` `2 ` | Derived from Byte-9 of input stream | + +##### Maximize utilization of input data +The plugin feeds the entire input data to the codec. Frame sizes are determined only +after the call to extractor, so in absence of call to extractor, +we feed the entire data to the decoder. +This ensures that the plugin tolerates any kind of input (empty, huge, +malformed, etc) and doesnt `exit()` on any input and thereby increasing the +chance of identifying vulnerabilities. + +## Build + +This describes steps to build opus_dec_fuzzer and opus_multistream_dec_fuzzer binary. + +## Android + +### Steps to build +Build the fuzzer +``` + $ mm -j$(nproc) opus_dec_fuzzer + $ mm -j$(nproc) opus_multistream_dec_fuzzer +``` + +### Steps to run +Create a directory CORPUS_DIR and copy some opus files to that folder. +Push this directory to device. + +To run on device +``` + $ adb sync data + $ adb shell /data/fuzz/arm64/opus_dec_fuzzer/opus_dec_fuzzer CORPUS_DIR + $ adb shell /data/fuzz/arm64/opus_multistream_dec_fuzzer/opus_multistream_dec_fuzzer CORPUS_DIR +``` +To run on host +``` + $ $ANDROID_HOST_OUT/fuzz/x86_64/opus_dec_fuzzer/opus_dec_fuzzer CORPUS_DIR + $ $ANDROID_HOST_OUT/fuzz/x86_64/opus_multistream_dec_fuzzer/opus_multistream_dec_fuzzer CORPUS_DIR +``` + +## References: + * http://llvm.org/docs/LibFuzzer.html + * https://github.com/google/oss-fuzz diff --git a/fuzzer/opus_dec_fuzzer.cpp b/fuzzer/opus_dec_fuzzer.cpp new file mode 100644 index 00000000..23bf69e4 --- /dev/null +++ b/fuzzer/opus_dec_fuzzer.cpp @@ -0,0 +1,124 @@ +/****************************************************************************** + * + * Copyright (C) 2020 The Android Open Source Project + * + * 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. + * + ***************************************************************************** + * Originally developed and contributed by Ittiam Systems Pvt. Ltd, Bangalore + */ + +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +#include <opus.h> + +/* 4 bytes: packet length, 4 bytes: encoder final range */ +constexpr int kSetupByteOffset = 8; +constexpr int kMaxFrameSample = 5760; +const int kSamplingRates[] = {8000, 12000, 16000, 24000, 48000}; +constexpr int kNumberSamplingRates = sizeof(kSamplingRates) / sizeof(kSamplingRates[0]); + +#ifdef MULTISTREAM +#include "opus_multistream.h" +#define OPUS_DEC_DATA_TYPE OpusMSDecoder +#define OPUS_DEC_DECODE_API opus_multistream_decode +#define OPUS_DEC_CREATE_API ms_opus_decoder_create +#define OPUS_DEC_DESTROY_API opus_multistream_decoder_destroy +static OpusMSDecoder *ms_opus_decoder_create(opus_int32 Fs, int channels, int *error) { + int streams = 1; + int coupledStreams = channels == 2; + unsigned char mapping[256] = {0, 1}; + return opus_multistream_decoder_create(Fs, channels, streams, coupledStreams, mapping, error); +} +#else +#define OPUS_DEC_DATA_TYPE OpusDecoder +#define OPUS_DEC_DECODE_API opus_decode +#define OPUS_DEC_CREATE_API opus_decoder_create +#define OPUS_DEC_DESTROY_API opus_decoder_destroy +#endif + +class Codec { + public: + Codec() = default; + ~Codec() { deInitDecoder(); } + bool initDecoder(const uint8_t *data); + void decodeFrames(const uint8_t *data, size_t size); + void deInitDecoder(); + + private: + int mSamplingRate; + int mNoOfChannels; + OPUS_DEC_DATA_TYPE *mDec = nullptr; + opus_int16 *mPcm = nullptr; +}; + +bool Codec::initDecoder(const uint8_t *data) { + const uint8_t *tocPtr = &data[kSetupByteOffset]; + const int bandwidth = opus_packet_get_bandwidth(tocPtr); + int samplingRateIndex = bandwidth - OPUS_BANDWIDTH_NARROWBAND; + + /*bounds check on samplingRateIndex*/ + if ((samplingRateIndex >= 0) && (samplingRateIndex < kNumberSamplingRates)) { + mSamplingRate = kSamplingRates[samplingRateIndex]; + } else { + mSamplingRate = 8000; // set to a default value + } + + mNoOfChannels = opus_packet_get_nb_channels(tocPtr); + if ((mNoOfChannels != 1) && (mNoOfChannels != 2)) { + mNoOfChannels = 1; + } + + int err; + mDec = OPUS_DEC_CREATE_API(mSamplingRate, mNoOfChannels, &err); + if (!mDec || err != OPUS_OK) { + return false; + } + size_t sizePcm = sizeof(*mPcm) * kMaxFrameSample * mNoOfChannels; + mPcm = static_cast<opus_int16 *>(malloc(sizePcm)); + if (!mPcm) { + return false; + } + memset(mPcm, 0x0, sizePcm); + return true; +} + +void Codec::deInitDecoder() { + OPUS_DEC_DESTROY_API(mDec); + mDec = nullptr; + if (mPcm) { + free(mPcm); + } + mPcm = nullptr; +} + +void Codec::decodeFrames(const uint8_t *data, size_t size) { + (void)OPUS_DEC_DECODE_API(mDec, data, size, mPcm, kMaxFrameSample, 0 /*fec*/); +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + if (size < kSetupByteOffset + 1) { + return 0; + } + Codec *codec = new Codec(); + if (!codec) { + return 0; + } + if (codec->initDecoder(data)) { + codec->decodeFrames(data, size); + } + delete codec; + return 0; +} |