Merge changes Ia8bc8706,Icef891eb am: a224da5c41 am: 19b1a8e0b7
Change-Id: I219dca5d3111b87b5ee10a87636bcda0690a66c5
diff --git a/media/libstagefright/codecs/mp3dec/Android.bp b/media/libstagefright/codecs/mp3dec/Android.bp
index b630524..75b32bd 100644
--- a/media/libstagefright/codecs/mp3dec/Android.bp
+++ b/media/libstagefright/codecs/mp3dec/Android.bp
@@ -2,6 +2,7 @@
name: "libstagefright_mp3dec",
vendor_available: true,
+ host_supported:true,
srcs: [
"src/pvmp3_normalize.cpp",
"src/pvmp3_alias_reduction.cpp",
@@ -72,6 +73,12 @@
"-DOSCL_UNUSED_ARG(x)=(void)(x)",
"-Werror",
],
+
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
}
//###############################################################################
diff --git a/media/libstagefright/codecs/mp3dec/fuzzer/Android.bp b/media/libstagefright/codecs/mp3dec/fuzzer/Android.bp
new file mode 100644
index 0000000..2f0eda7
--- /dev/null
+++ b/media/libstagefright/codecs/mp3dec/fuzzer/Android.bp
@@ -0,0 +1,32 @@
+/******************************************************************************
+ *
+ * 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: "mp3_dec_fuzzer",
+ host_supported: true,
+
+ static_libs: [
+ "libstagefright_mp3dec",
+ ],
+
+ srcs: [
+ "mp3_dec_fuzzer.cpp",
+ ],
+}
diff --git a/media/libstagefright/codecs/mp3dec/fuzzer/README.md b/media/libstagefright/codecs/mp3dec/fuzzer/README.md
new file mode 100644
index 0000000..09dd5c3
--- /dev/null
+++ b/media/libstagefright/codecs/mp3dec/fuzzer/README.md
@@ -0,0 +1,56 @@
+# Fuzzer for libstagefright_mp3dec decoder
+
+## Plugin Design Considerations
+The fuzzer plugin for mp3 decoder is designed based on the understanding of the
+codec and tries to achieve the following:
+
+##### Maximize code coverage
+
+This fuzzer makes use of the following config parameters:
+1. Equalizer type (parameter name: `equalizerType`)
+
+| Parameter| Valid Values| Configured Value|
+|------------- |-------------| ----- |
+| `equalizerType` | 0. `flat ` 1. `bass_boost ` 2. `rock ` 3. `pop ` 4. `jazz ` 5. `classical ` 6. `talk ` 7. `flat_ ` | Bits 0, 1 and 2 of first byte of input stream |
+| `crcEnabled` | 0. `false ` 1. `true `| Bit 0 of second byte of input stream |
+
+##### Maximize utilization of input data
+The plugin feeds the entire input data to the codec using a loop.
+ * If the decode operation was successful, the input is advanced by the number
+ of bytes used by the decoder.
+ * If the decode operation was un-successful, the input is advanced by 1 byte
+ till it reaches a valid frame or end of stream.
+
+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 mp3_dec_fuzzer binary.
+
+### Android
+
+#### Steps to build
+Build the fuzzer
+```
+ $ mm -j$(nproc) mp3_dec_fuzzer
+```
+
+#### Steps to run
+Create a directory CORPUS_DIR and copy some mp3 files to that folder.
+Push this directory to device.
+
+To run on device
+```
+ $ adb sync data
+ $ adb shell /data/fuzz/arm64/mp3_dec_fuzzer/mp3_dec_fuzzer CORPUS_DIR
+```
+To run on host
+```
+ $ $ANDROID_HOST_OUT/fuzz/x86_64/mp3_dec_fuzzer/mp3_dec_fuzzer CORPUS_DIR
+```
+
+## References:
+ * http://llvm.org/docs/LibFuzzer.html
+ * https://github.com/google/oss-fuzz
diff --git a/media/libstagefright/codecs/mp3dec/fuzzer/mp3_dec_fuzzer.cpp b/media/libstagefright/codecs/mp3dec/fuzzer/mp3_dec_fuzzer.cpp
new file mode 100644
index 0000000..847c8c4
--- /dev/null
+++ b/media/libstagefright/codecs/mp3dec/fuzzer/mp3_dec_fuzzer.cpp
@@ -0,0 +1,237 @@
+/******************************************************************************
+ *
+ * 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 <stdlib.h>
+#include <algorithm>
+
+#include <pvmp3decoder_api.h>
+
+constexpr int kMaxFrameSamples = 4608;
+constexpr int kMaxChannels = 2;
+constexpr e_equalization kEqualizerTypes[] = {flat, bass_boost, rock, pop,
+ jazz, classical, talk, flat_};
+
+static bool parseMp3Header(uint32_t header, size_t *frame_size,
+ uint32_t *out_sampling_rate = nullptr, uint32_t *out_channels = nullptr,
+ uint32_t *out_bitrate = nullptr, uint32_t *out_num_samples = nullptr) {
+ *frame_size = 0;
+ if (out_sampling_rate) *out_sampling_rate = 0;
+ if (out_channels) *out_channels = 0;
+ if (out_bitrate) *out_bitrate = 0;
+ if (out_num_samples) *out_num_samples = 0;
+
+ if ((header & 0xffe00000) != 0xffe00000) {
+ return false;
+ }
+ unsigned version = (header >> 19) & 3;
+ if (version == 0x01) {
+ return false;
+ }
+ unsigned layer = (header >> 17) & 3;
+ if (layer == 0x00) {
+ return false;
+ }
+ unsigned bitrate_index = (header >> 12) & 0x0f;
+ if (bitrate_index == 0 || bitrate_index == 0x0f) {
+ return false;
+ }
+ unsigned sampling_rate_index = (header >> 10) & 3;
+ if (sampling_rate_index == 3) {
+ return false;
+ }
+ static const int kSamplingRateV1[] = {44100, 48000, 32000};
+ int sampling_rate = kSamplingRateV1[sampling_rate_index];
+ if (version == 2 /* V2 */) {
+ sampling_rate /= 2;
+ } else if (version == 0 /* V2.5 */) {
+ sampling_rate /= 4;
+ }
+
+ unsigned padding = (header >> 9) & 1;
+
+ if (layer == 3) { // layer I
+ static const int kBitrateV1[] = {32, 64, 96, 128, 160, 192, 224,
+ 256, 288, 320, 352, 384, 416, 448};
+ static const int kBitrateV2[] = {32, 48, 56, 64, 80, 96, 112,
+ 128, 144, 160, 176, 192, 224, 256};
+
+ int bitrate =
+ (version == 3 /* V1 */) ? kBitrateV1[bitrate_index - 1] : kBitrateV2[bitrate_index - 1];
+
+ if (out_bitrate) {
+ *out_bitrate = bitrate;
+ }
+ *frame_size = (12000 * bitrate / sampling_rate + padding) * 4;
+ if (out_num_samples) {
+ *out_num_samples = 384;
+ }
+ } else { // layer II or III
+ static const int kBitrateV1L2[] = {32, 48, 56, 64, 80, 96, 112,
+ 128, 160, 192, 224, 256, 320, 384};
+ static const int kBitrateV1L3[] = {32, 40, 48, 56, 64, 80, 96,
+ 112, 128, 160, 192, 224, 256, 320};
+ static const int kBitrateV2[] = {8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160};
+ int bitrate;
+ if (version == 3 /* V1 */) {
+ bitrate =
+ (layer == 2 /* L2 */) ? kBitrateV1L2[bitrate_index - 1] : kBitrateV1L3[bitrate_index - 1];
+
+ if (out_num_samples) {
+ *out_num_samples = 1152;
+ }
+ } else { // V2 (or 2.5)
+ bitrate = kBitrateV2[bitrate_index - 1];
+ if (out_num_samples) {
+ *out_num_samples = (layer == 1 /* L3 */) ? 576 : 1152;
+ }
+ }
+
+ if (out_bitrate) {
+ *out_bitrate = bitrate;
+ }
+
+ if (version == 3 /* V1 */) {
+ *frame_size = 144000 * bitrate / sampling_rate + padding;
+ } else { // V2 or V2.5
+ size_t tmp = (layer == 1 /* L3 */) ? 72000 : 144000;
+ *frame_size = tmp * bitrate / sampling_rate + padding;
+ }
+ }
+
+ if (out_sampling_rate) {
+ *out_sampling_rate = sampling_rate;
+ }
+
+ if (out_channels) {
+ int channel_mode = (header >> 6) & 3;
+ *out_channels = (channel_mode == 3) ? 1 : 2;
+ }
+
+ return true;
+}
+
+static uint32_t U32_AT(const uint8_t *ptr) {
+ return ptr[0] << 24 | ptr[1] << 16 | ptr[2] << 8 | ptr[3];
+}
+
+static bool checkHeader(uint8 *header, size_t inSize) {
+ size_t frameSize;
+ size_t totalInSize = 0;
+ bool isValidBuffer = false;
+
+ while (totalInSize + 4 < inSize) {
+ isValidBuffer = true;
+ uint32_t val = U32_AT(header + totalInSize);
+ if (!parseMp3Header(val, &frameSize, nullptr, nullptr, nullptr, nullptr)) {
+ return false;
+ }
+ totalInSize += frameSize;
+ }
+
+ return (isValidBuffer);
+}
+
+class Codec {
+ public:
+ Codec() = default;
+ ~Codec() { deInitDecoder(); }
+
+ bool initDecoder();
+ void decodeFrames(uint8_t *data, size_t size);
+ void deInitDecoder();
+
+ private:
+ tPVMP3DecoderExternal *mConfig = nullptr;
+ void *mDecoderBuf = nullptr;
+};
+
+bool Codec::initDecoder() {
+ mConfig = new tPVMP3DecoderExternal{};
+ if (!mConfig) {
+ return false;
+ }
+ size_t decoderBufSize = pvmp3_decoderMemRequirements();
+ mDecoderBuf = malloc(decoderBufSize);
+ if (!mDecoderBuf) {
+ return false;
+ }
+ memset(mDecoderBuf, 0x0, decoderBufSize);
+ pvmp3_InitDecoder(mConfig, mDecoderBuf);
+ return true;
+}
+
+void Codec::decodeFrames(uint8_t *data, size_t size) {
+ uint8_t equalizerTypeValue = (data[0] & 0x7);
+ mConfig->equalizerType = kEqualizerTypes[equalizerTypeValue];
+ mConfig->crcEnabled = data[1] & 0x1;
+
+ while (size > 0) {
+ bool status = checkHeader(data, size);
+ if (!status) {
+ size--;
+ data++;
+ continue;
+ }
+ size_t outBufSize = kMaxFrameSamples * kMaxChannels;
+ size_t usedBytes = 0;
+ int16_t outputBuf[outBufSize];
+ mConfig->inputBufferCurrentLength = size;
+ mConfig->inputBufferUsedLength = 0;
+ mConfig->inputBufferMaxLength = 0;
+ mConfig->pInputBuffer = data;
+ mConfig->pOutputBuffer = outputBuf;
+ mConfig->outputFrameSize = outBufSize / sizeof(int16_t);
+
+ ERROR_CODE decoderErr;
+ decoderErr = pvmp3_framedecoder(mConfig, mDecoderBuf);
+ if (decoderErr != NO_DECODING_ERROR) {
+ size--;
+ data++;
+ } else {
+ usedBytes = std::min((int32_t)size, mConfig->inputBufferUsedLength);
+ size -= usedBytes;
+ data += usedBytes;
+ }
+ }
+}
+
+void Codec::deInitDecoder() {
+ if (mDecoderBuf) {
+ free(mDecoderBuf);
+ mDecoderBuf = nullptr;
+ }
+ delete mConfig;
+ mConfig = nullptr;
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ if (size < 4) {
+ return 0;
+ }
+ Codec *codec = new Codec();
+ if (!codec) {
+ return 0;
+ }
+ if (codec->initDecoder()) {
+ codec->decodeFrames(const_cast<uint8_t *>(data), size);
+ }
+ delete codec;
+ return 0;
+}