From 8c51282f7fd7e712628aba1677a9733d903eb219 Mon Sep 17 00:00:00 2001 From: aleloi Date: Tue, 20 Jun 2017 05:26:55 -0700 Subject: [PATCH] Added new AudioProcessing fuzzer This is a high-level fuzzer that creates an AudioProcessing instance. All possible combinations of publicly visible components are fuzzed. Input and output sample rate, call order and use of the float/fix interface is fuzzed. Sample rate may change between calls. To fuzz floating point numbers, raw data is converted to floats, and filtered for special values like 'inf', 'nan' and very large values. Note that the default use case of APM is to only allow values between +/- 2^15. BUG=webrtc:7820 Review-Url: https://codereview.webrtc.org/2876793002 Cr-Commit-Position: refs/heads/master@{#18678} --- webrtc/test/fuzzers/BUILD.gn | 13 ++ .../test/fuzzers/audio_processing_fuzzer.cc | 157 ++++++++++++++++++ webrtc/test/fuzzers/audio_processing_fuzzer.h | 27 +++ .../audio_processing_fuzzer_configs.cc | 70 ++++++++ 4 files changed, 267 insertions(+) create mode 100644 webrtc/test/fuzzers/audio_processing_fuzzer.cc create mode 100644 webrtc/test/fuzzers/audio_processing_fuzzer.h create mode 100644 webrtc/test/fuzzers/audio_processing_fuzzer_configs.cc diff --git a/webrtc/test/fuzzers/BUILD.gn b/webrtc/test/fuzzers/BUILD.gn index 0590f1c6c6..3e68470749 100644 --- a/webrtc/test/fuzzers/BUILD.gn +++ b/webrtc/test/fuzzers/BUILD.gn @@ -358,3 +358,16 @@ webrtc_fuzzer_test("transport_feedback_packet_loss_tracker_fuzzer") { "../../voice_engine", ] } + +webrtc_fuzzer_test("audio_processing_fuzzer") { + sources = [ + "audio_processing_fuzzer.cc", + "audio_processing_fuzzer.h", + "audio_processing_fuzzer_configs.cc", + ] + deps = [ + "../../base:rtc_base_approved", + "../../modules:module_api", + "../../modules/audio_processing", + ] +} diff --git a/webrtc/test/fuzzers/audio_processing_fuzzer.cc b/webrtc/test/fuzzers/audio_processing_fuzzer.cc new file mode 100644 index 0000000000..d5a3bea0a1 --- /dev/null +++ b/webrtc/test/fuzzers/audio_processing_fuzzer.cc @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2017 The WebRTC 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. + */ + +#include "webrtc/test/fuzzers/audio_processing_fuzzer.h" + +#include +#include +#include + +#include "webrtc/base/checks.h" +#include "webrtc/modules/audio_processing/include/audio_processing.h" +#include "webrtc/modules/include/module_common_types.h" + +namespace webrtc { +namespace { +size_t ByteToNativeRate(uint8_t data) { + using Rate = AudioProcessing::NativeRate; + switch (data % 4) { + case 0: + // Breaks AEC3. + // return static_cast(Rate::kSampleRate8kHz); + case 1: + return static_cast(Rate::kSampleRate16kHz); + case 2: + return static_cast(Rate::kSampleRate32kHz); + default: + return static_cast(Rate::kSampleRate48kHz); + } +} + +template +bool ParseSequence(size_t size, + const uint8_t** data, + size_t* remaining_size, + T* result_data) { + const size_t data_size_bytes = sizeof(T) * size; + if (data_size_bytes > *remaining_size) { + return false; + } + + std::copy(*data, *data + data_size_bytes, + reinterpret_cast(result_data)); + + *data += data_size_bytes; + *remaining_size -= data_size_bytes; + return true; +} + +void FuzzAudioProcessing(const uint8_t* data, + size_t size, + bool is_float, + AudioProcessing* apm) { + AudioFrame fixed_frame; + std::array float_frame; + float* const first_channel = &float_frame[0]; + + while (size > 0) { + // Decide input/output rate for this iteration. + const auto input_rate_byte = ParseByte(&data, &size); + const auto output_rate_byte = ParseByte(&data, &size); + if (!input_rate_byte || !output_rate_byte) { + return; + } + const auto input_rate_hz = ByteToNativeRate(*input_rate_byte); + const auto output_rate_hz = ByteToNativeRate(*output_rate_byte); + + const size_t samples_per_input_channel = + rtc::CheckedDivExact(input_rate_hz, 100ul); + fixed_frame.samples_per_channel_ = samples_per_input_channel; + fixed_frame.sample_rate_hz_ = input_rate_hz; + + // Two channels breaks AEC3. + fixed_frame.num_channels_ = 1; + + // Fill the arrays with audio samples from the data. + if (is_float) { + if (!ParseSequence(samples_per_input_channel, &data, &size, + &float_frame[0])) { + return; + } + } else if (!ParseSequence(samples_per_input_channel, &data, &size, + fixed_frame.mutable_data())) { + return; + } + + // Filter obviously wrong values like inf/nan and values that will + // lead to inf/nan in calculations. 1e6 leads to DCHECKS failing. + for (auto& x : float_frame) { + if (!std::isnormal(x) || std::abs(x) > 1e5) { + x = 0; + } + } + + // Make the APM call depending on capture/render mode and float / + // fix interface. + const auto is_capture = ParseBool(&data, &size); + if (!is_capture) { + return; + } + if (*is_capture) { + auto apm_return_code = + is_float ? (apm->ProcessStream( + &first_channel, StreamConfig(input_rate_hz, 1), + StreamConfig(output_rate_hz, 1), &first_channel)) + : (apm->ProcessStream(&fixed_frame)); + RTC_DCHECK_NE(apm_return_code, AudioProcessing::kBadDataLengthError); + } else { + auto apm_return_code = + is_float ? (apm->ProcessReverseStream( + &first_channel, StreamConfig(input_rate_hz, 1), + StreamConfig(output_rate_hz, 1), &first_channel)) + : (apm->ProcessReverseStream(&fixed_frame)); + RTC_DCHECK_NE(apm_return_code, AudioProcessing::kBadDataLengthError); + } + } +} + +} // namespace + +rtc::Optional ParseBool(const uint8_t** data, size_t* remaining_size) { + if (1 > *remaining_size) { + return rtc::Optional(); + } + auto res = rtc::Optional((**data) % 2); + *data += 1; + *remaining_size -= 1; + return res; +} + +rtc::Optional ParseByte(const uint8_t** data, size_t* remaining_size) { + if (1 > *remaining_size) { + return rtc::Optional(); + } + auto res = rtc::Optional((**data)); + *data += 1; + *remaining_size -= 1; + return res; +} + +void FuzzAudioProcessing(const uint8_t* data, + size_t size, + std::unique_ptr apm) { + const auto is_float = ParseBool(&data, &size); + if (!is_float) { + return; + } + + FuzzAudioProcessing(data, size, *is_float, apm.get()); +} +} // namespace webrtc diff --git a/webrtc/test/fuzzers/audio_processing_fuzzer.h b/webrtc/test/fuzzers/audio_processing_fuzzer.h new file mode 100644 index 0000000000..4a54d6936a --- /dev/null +++ b/webrtc/test/fuzzers/audio_processing_fuzzer.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2017 The WebRTC 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. + */ + +#ifndef WEBRTC_TEST_FUZZERS_AUDIO_PROCESSING_FUZZER_H_ +#define WEBRTC_TEST_FUZZERS_AUDIO_PROCESSING_FUZZER_H_ + +#include + +#include "webrtc/modules/audio_processing/include/audio_processing.h" +namespace webrtc { + +rtc::Optional ParseBool(const uint8_t** data, size_t* remaining_size); +rtc::Optional ParseByte(const uint8_t** data, size_t* remaining_size); + +void FuzzAudioProcessing(const uint8_t* data, + size_t size, + std::unique_ptr apm); +} // namespace webrtc + +#endif // WEBRTC_TEST_FUZZERS_AUDIO_PROCESSING_FUZZER_H_ diff --git a/webrtc/test/fuzzers/audio_processing_fuzzer_configs.cc b/webrtc/test/fuzzers/audio_processing_fuzzer_configs.cc new file mode 100644 index 0000000000..9878da98b3 --- /dev/null +++ b/webrtc/test/fuzzers/audio_processing_fuzzer_configs.cc @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2017 The WebRTC 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. + */ + +#include "webrtc/modules/audio_processing/include/audio_processing.h" +#include "webrtc/test/fuzzers/audio_processing_fuzzer.h" + +#include "webrtc/base/optional.h" + +namespace webrtc { + +std::unique_ptr CreateAPM(const uint8_t** data, + size_t* remaining_size) { + // Parse boolean values for optionally enabling different + // configurable public components of APM. + auto exp_agc = ParseBool(data, remaining_size); + auto exp_ns = ParseBool(data, remaining_size); + auto bf = ParseBool(data, remaining_size); + auto ef = ParseBool(data, remaining_size); + auto raf = ParseBool(data, remaining_size); + auto da = ParseBool(data, remaining_size); + auto ie = ParseBool(data, remaining_size); + auto red = ParseBool(data, remaining_size); + auto lc = ParseBool(data, remaining_size); + auto hpf = ParseBool(data, remaining_size); + auto aec3 = ParseBool(data, remaining_size); + + if (!(exp_agc && exp_ns && bf && ef && raf && da && ie && red && lc && hpf && + aec3)) { + return nullptr; + } + + // Components can be enabled through webrtc::Config and + // webrtc::AudioProcessingConfig. + Config config; + + config.Set(new ExperimentalAgc(*exp_agc)); + config.Set(new ExperimentalNs(*exp_ns)); + if (*bf) { + config.Set(new Beamforming()); + } + config.Set(new ExtendedFilter(*ef)); + config.Set(new RefinedAdaptiveFilter(*raf)); + config.Set(new DelayAgnostic(*da)); + config.Set(new Intelligibility(*ie)); + + std::unique_ptr apm(AudioProcessing::Create(config)); + + webrtc::AudioProcessing::Config apm_config; + apm_config.residual_echo_detector.enabled = *red; + apm_config.level_controller.enabled = *lc; + apm_config.high_pass_filter.enabled = *hpf; + apm_config.echo_canceller3.enabled = *aec3; + + apm->ApplyConfig(apm_config); + + return apm; +} + +void FuzzOneInput(const uint8_t* data, size_t size) { + auto apm = CreateAPM(&data, &size); + FuzzAudioProcessing(data, size, std::move(apm)); +} +} // namespace webrtc