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