diff --git a/test/fuzzers/BUILD.gn b/test/fuzzers/BUILD.gn index 1a400c8c1b..43df61899a 100644 --- a/test/fuzzers/BUILD.gn +++ b/test/fuzzers/BUILD.gn @@ -421,14 +421,13 @@ webrtc_fuzzer_test("transport_feedback_packet_loss_tracker_fuzzer") { "../../voice_engine", ] } - -webrtc_fuzzer_test("audio_processing_fuzzer") { +rtc_static_library("audio_processing_fuzzer_helper") { sources = [ - "audio_processing_fuzzer.cc", - "audio_processing_fuzzer.h", - "audio_processing_fuzzer_configs.cc", + "audio_processing_fuzzer_helper.cc", + "audio_processing_fuzzer_helper.h", ] deps = [ + ":fuzz_data_helper", "../../api:optional", "../../modules:module_api", "../../modules/audio_processing", @@ -437,6 +436,17 @@ webrtc_fuzzer_test("audio_processing_fuzzer") { ] } +webrtc_fuzzer_test("audio_processing_fuzzer") { + sources = [ + "audio_processing_configs_fuzzer.cc", + ] + deps = [ + ":audio_processing_fuzzer_helper", + ":fuzz_data_helper", + "../../modules/audio_processing", + ] +} + webrtc_fuzzer_test("comfort_noise_decoder_fuzzer") { sources = [ "comfort_noise_decoder_fuzzer.cc", diff --git a/test/fuzzers/audio_processing_configs_fuzzer.cc b/test/fuzzers/audio_processing_configs_fuzzer.cc new file mode 100644 index 0000000000..81baab1ac6 --- /dev/null +++ b/test/fuzzers/audio_processing_configs_fuzzer.cc @@ -0,0 +1,96 @@ +/* + * 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 "modules/audio_processing/include/audio_processing.h" +#include "test/fuzzers/audio_processing_fuzzer_helper.h" +#include "test/fuzzers/fuzz_data_helper.h" + +namespace webrtc { +namespace { +std::unique_ptr CreateApm(test::FuzzDataHelper* fuzz_data) { + // Parse boolean values for optionally enabling different + // configurable public components of APM. + bool exp_agc = fuzz_data->ReadOrDefaultValue(true); + bool exp_ns = fuzz_data->ReadOrDefaultValue(true); + bool bf = fuzz_data->ReadOrDefaultValue(true); + bool ef = fuzz_data->ReadOrDefaultValue(true); + bool raf = fuzz_data->ReadOrDefaultValue(true); + bool da = fuzz_data->ReadOrDefaultValue(true); + bool ie = fuzz_data->ReadOrDefaultValue(true); + bool red = fuzz_data->ReadOrDefaultValue(true); + bool lc = fuzz_data->ReadOrDefaultValue(true); + bool hpf = fuzz_data->ReadOrDefaultValue(true); + bool aec3 = fuzz_data->ReadOrDefaultValue(true); + + bool use_aec = fuzz_data->ReadOrDefaultValue(true); + bool use_aecm = fuzz_data->ReadOrDefaultValue(true); + bool use_agc = fuzz_data->ReadOrDefaultValue(true); + bool use_ns = fuzz_data->ReadOrDefaultValue(true); + bool use_le = fuzz_data->ReadOrDefaultValue(true); + bool use_vad = fuzz_data->ReadOrDefaultValue(true); + bool use_agc_limiter = fuzz_data->ReadOrDefaultValue(true); + + // Filter out incompatible settings that lead to CHECK failures. + if (use_aecm && use_aec) { + return nullptr; + } + + // Components can be enabled through webrtc::Config and + // webrtc::AudioProcessingConfig. + Config config; + + std::unique_ptr echo_control_factory; + if (aec3) { + echo_control_factory.reset(new EchoCanceller3Factory()); + } + + 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( + AudioProcessingBuilder() + .SetEchoControlFactory(std::move(echo_control_factory)) + .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->ApplyConfig(apm_config); + + apm->echo_cancellation()->Enable(use_aec); + apm->echo_control_mobile()->Enable(use_aecm); + apm->gain_control()->Enable(use_agc); + apm->noise_suppression()->Enable(use_ns); + apm->level_estimator()->Enable(use_le); + apm->voice_detection()->Enable(use_vad); + apm->gain_control()->enable_limiter(use_agc_limiter); + + return apm; +} +} // namespace + +void FuzzOneInput(const uint8_t* data, size_t size) { + test::FuzzDataHelper fuzz_data(rtc::ArrayView(data, size)); + auto apm = CreateApm(&fuzz_data); + + if (apm) { + FuzzAudioProcessing(&fuzz_data, std::move(apm)); + } +} +} // namespace webrtc diff --git a/test/fuzzers/audio_processing_fuzzer.cc b/test/fuzzers/audio_processing_fuzzer.cc deleted file mode 100644 index a44d446096..0000000000 --- a/test/fuzzers/audio_processing_fuzzer.cc +++ /dev/null @@ -1,156 +0,0 @@ -/* - * 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 "test/fuzzers/audio_processing_fuzzer.h" - -#include -#include -#include - -#include "modules/audio_processing/include/audio_processing.h" -#include "modules/include/module_common_types.h" -#include "rtc_base/checks.h" - -namespace webrtc { -namespace { -size_t ByteToNativeRate(uint8_t data) { - using Rate = AudioProcessing::NativeRate; - switch (data % 4) { - case 0: - 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::nullopt; - } - auto res = (**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::nullopt; - } - auto res = **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/test/fuzzers/audio_processing_fuzzer_configs.cc b/test/fuzzers/audio_processing_fuzzer_configs.cc deleted file mode 100644 index 2e0a5402fb..0000000000 --- a/test/fuzzers/audio_processing_fuzzer_configs.cc +++ /dev/null @@ -1,101 +0,0 @@ -/* - * 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 "modules/audio_processing/include/audio_processing.h" -#include "test/fuzzers/audio_processing_fuzzer.h" - -#include "api/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); - - auto use_aec = ParseBool(data, remaining_size); - auto use_aecm = ParseBool(data, remaining_size); - auto use_agc = ParseBool(data, remaining_size); - auto use_ns = ParseBool(data, remaining_size); - auto use_le = ParseBool(data, remaining_size); - auto use_vad = ParseBool(data, remaining_size); - auto use_agc_limiter = ParseBool(data, remaining_size); - - if (!(exp_agc && exp_ns && bf && ef && raf && da && ie && red && lc && hpf && - aec3 && use_aec && use_aecm && use_agc && use_ns && use_le && use_vad && - use_agc_limiter)) { - return nullptr; - } - - // Filter out incompatible settings that lead to CHECK failures. - if (*use_aecm && *use_aec) { - return nullptr; - } - - // Components can be enabled through webrtc::Config and - // webrtc::AudioProcessingConfig. - Config config; - - std::unique_ptr echo_control_factory; - if (*aec3) { - echo_control_factory.reset(new EchoCanceller3Factory()); - } - - 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( - AudioProcessingBuilder() - .SetEchoControlFactory(std::move(echo_control_factory)) - .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->ApplyConfig(apm_config); - - apm->echo_cancellation()->Enable(*use_aec); - apm->echo_control_mobile()->Enable(*use_aecm); - apm->gain_control()->Enable(*use_agc); - apm->noise_suppression()->Enable(*use_ns); - apm->level_estimator()->Enable(*use_le); - apm->voice_detection()->Enable(*use_vad); - apm->gain_control()->enable_limiter(*use_agc_limiter); - - return apm; -} - -void FuzzOneInput(const uint8_t* data, size_t size) { - auto apm = CreateAPM(&data, &size); - if (apm) { - FuzzAudioProcessing(data, size, std::move(apm)); - } -} -} // namespace webrtc diff --git a/test/fuzzers/audio_processing_fuzzer_helper.cc b/test/fuzzers/audio_processing_fuzzer_helper.cc new file mode 100644 index 0000000000..30a66ed848 --- /dev/null +++ b/test/fuzzers/audio_processing_fuzzer_helper.cc @@ -0,0 +1,120 @@ +/* + * 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 "test/fuzzers/audio_processing_fuzzer_helper.h" + +#include +#include +#include +#include + +#include "modules/audio_processing/include/audio_processing.h" +#include "modules/include/module_common_types.h" +#include "rtc_base/checks.h" + +namespace webrtc { +namespace { +void GenerateFloatFrame(test::FuzzDataHelper* fuzz_data, + size_t input_rate, + size_t num_channels, + float* const* float_frames) { + const size_t samples_per_input_channel = + rtc::CheckedDivExact(input_rate, 100ul); + RTC_DCHECK_LE(samples_per_input_channel, 480); + for (size_t i = 0; i < num_channels; ++i) { + for (size_t j = 0; j < samples_per_input_channel; ++j) { + float_frames[i][j] = + static_cast(fuzz_data->ReadOrDefaultValue(0)) / + static_cast(std::numeric_limits::max()); + } + } +} + +void GenerateFixedFrame(test::FuzzDataHelper* fuzz_data, + size_t input_rate, + size_t num_channels, + AudioFrame* fixed_frame) { + const size_t samples_per_input_channel = + rtc::CheckedDivExact(input_rate, 100ul); + fixed_frame->samples_per_channel_ = samples_per_input_channel; + fixed_frame->sample_rate_hz_ = input_rate; + fixed_frame->num_channels_ = num_channels; + + RTC_DCHECK_LE(samples_per_input_channel * num_channels, + AudioFrame::kMaxDataSizeSamples); + for (size_t i = 0; i < samples_per_input_channel * num_channels; ++i) { + fixed_frame->mutable_data()[i] = fuzz_data->ReadOrDefaultValue(0); + } +} +} // namespace + +void FuzzAudioProcessing(test::FuzzDataHelper* fuzz_data, + std::unique_ptr apm) { + AudioFrame fixed_frame; + std::array float_frame1; + std::array float_frame2; + std::array float_frame_ptrs = { + &float_frame1[0], &float_frame2[0], + }; + float* const* ptr_to_float_frames = &float_frame_ptrs[0]; + + using Rate = AudioProcessing::NativeRate; + const Rate rate_kinds[] = {Rate::kSampleRate8kHz, Rate::kSampleRate16kHz, + Rate::kSampleRate32kHz, Rate::kSampleRate48kHz}; + + // We may run out of fuzz data in the middle of a loop iteration. In + // that case, default values will be used for the rest of that + // iteration. + while (fuzz_data->CanReadBytes(1)) { + const bool is_float = fuzz_data->ReadOrDefaultValue(true); + // Decide input/output rate for this iteration. + const auto input_rate = + static_cast(fuzz_data->SelectOneOf(rate_kinds)); + const auto output_rate = + static_cast(fuzz_data->SelectOneOf(rate_kinds)); + + const bool num_channels = fuzz_data->ReadOrDefaultValue(true) ? 2 : 1; + const uint8_t stream_delay = fuzz_data->ReadOrDefaultValue(0); + + // API call needed for AEC-2 and AEC-m to run. + apm->set_stream_delay_ms(stream_delay); + + // Make the APM call depending on capture/render mode and float / + // fix interface. + const bool is_capture = fuzz_data->ReadOrDefaultValue(true); + + // Fill the arrays with audio samples from the data. + int apm_return_code = AudioProcessing::Error::kNoError; + if (is_float) { + GenerateFloatFrame(fuzz_data, input_rate, num_channels, + ptr_to_float_frames); + if (is_capture) { + apm_return_code = apm->ProcessStream( + ptr_to_float_frames, StreamConfig(input_rate, num_channels), + StreamConfig(output_rate, num_channels), ptr_to_float_frames); + } else { + apm_return_code = apm->ProcessReverseStream( + ptr_to_float_frames, StreamConfig(input_rate, 1), + StreamConfig(output_rate, 1), ptr_to_float_frames); + } + } else { + GenerateFixedFrame(fuzz_data, input_rate, num_channels, &fixed_frame); + + if (is_capture) { + apm_return_code = apm->ProcessStream(&fixed_frame); + } else { + apm_return_code = apm->ProcessReverseStream(&fixed_frame); + } + } + + RTC_DCHECK_NE(apm_return_code, AudioProcessing::kBadDataLengthError); + } +} +} // namespace webrtc diff --git a/test/fuzzers/audio_processing_fuzzer.h b/test/fuzzers/audio_processing_fuzzer_helper.h similarity index 60% rename from test/fuzzers/audio_processing_fuzzer.h rename to test/fuzzers/audio_processing_fuzzer_helper.h index 337d9b215d..697ed8db61 100644 --- a/test/fuzzers/audio_processing_fuzzer.h +++ b/test/fuzzers/audio_processing_fuzzer_helper.h @@ -8,20 +8,18 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef TEST_FUZZERS_AUDIO_PROCESSING_FUZZER_H_ -#define TEST_FUZZERS_AUDIO_PROCESSING_FUZZER_H_ +#ifndef TEST_FUZZERS_AUDIO_PROCESSING_FUZZER_HELPER_H_ +#define TEST_FUZZERS_AUDIO_PROCESSING_FUZZER_HELPER_H_ #include #include "modules/audio_processing/include/audio_processing.h" +#include "test/fuzzers/fuzz_data_helper.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, +void FuzzAudioProcessing(test::FuzzDataHelper* fuzz_data, std::unique_ptr apm); + } // namespace webrtc -#endif // TEST_FUZZERS_AUDIO_PROCESSING_FUZZER_H_ +#endif // TEST_FUZZERS_AUDIO_PROCESSING_FUZZER_HELPER_H_