1 /*
2 * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11 #include <bitset>
12 #include <string>
13
14 #include "absl/memory/memory.h"
15 #include "api/audio/echo_canceller3_factory.h"
16 #include "api/audio/echo_detector_creator.h"
17 #include "api/task_queue/default_task_queue_factory.h"
18 #include "modules/audio_processing/aec_dump/aec_dump_factory.h"
19 #include "modules/audio_processing/include/audio_processing.h"
20 #include "modules/audio_processing/test/audio_processing_builder_for_testing.h"
21 #include "rtc_base/arraysize.h"
22 #include "rtc_base/numerics/safe_minmax.h"
23 #include "rtc_base/task_queue.h"
24 #include "system_wrappers/include/field_trial.h"
25 #include "test/fuzzers/audio_processing_fuzzer_helper.h"
26 #include "test/fuzzers/fuzz_data_helper.h"
27
28 namespace webrtc {
29 namespace {
30
31 const std::string kFieldTrialNames[] = {
32 "WebRTC-Audio-Agc2ForceExtraSaturationMargin",
33 "WebRTC-Audio-Agc2ForceInitialSaturationMargin",
34 "WebRTC-Aec3MinErleDuringOnsetsKillSwitch",
35 "WebRTC-Aec3ShortHeadroomKillSwitch",
36 };
37
CreateApm(test::FuzzDataHelper * fuzz_data,std::string * field_trial_string,rtc::TaskQueue * worker_queue)38 rtc::scoped_refptr<AudioProcessing> CreateApm(test::FuzzDataHelper* fuzz_data,
39 std::string* field_trial_string,
40 rtc::TaskQueue* worker_queue) {
41 // Parse boolean values for optionally enabling different
42 // configurable public components of APM.
43 bool use_ts = fuzz_data->ReadOrDefaultValue(true);
44 bool use_red = fuzz_data->ReadOrDefaultValue(true);
45 bool use_hpf = fuzz_data->ReadOrDefaultValue(true);
46 bool use_aec3 = fuzz_data->ReadOrDefaultValue(true);
47 bool use_aec = fuzz_data->ReadOrDefaultValue(true);
48 bool use_aecm = fuzz_data->ReadOrDefaultValue(true);
49 bool use_agc = fuzz_data->ReadOrDefaultValue(true);
50 bool use_ns = fuzz_data->ReadOrDefaultValue(true);
51 bool use_agc_limiter = fuzz_data->ReadOrDefaultValue(true);
52 bool use_agc2 = fuzz_data->ReadOrDefaultValue(true);
53 bool use_agc2_adaptive_digital = fuzz_data->ReadOrDefaultValue(true);
54
55 // Read a gain value supported by GainController2::Validate().
56 const float gain_controller2_gain_db =
57 fuzz_data->ReadOrDefaultValue<uint8_t>(0) % 50;
58
59 constexpr size_t kNumFieldTrials = arraysize(kFieldTrialNames);
60 // Verify that the read data type has enough bits to fuzz the field trials.
61 using FieldTrialBitmaskType = uint64_t;
62 static_assert(kNumFieldTrials <= sizeof(FieldTrialBitmaskType) * 8,
63 "FieldTrialBitmaskType is not large enough.");
64 std::bitset<kNumFieldTrials> field_trial_bitmask(
65 fuzz_data->ReadOrDefaultValue<FieldTrialBitmaskType>(0));
66 for (size_t i = 0; i < kNumFieldTrials; ++i) {
67 if (field_trial_bitmask[i]) {
68 *field_trial_string += kFieldTrialNames[i] + "/Enabled/";
69 }
70 }
71 field_trial::InitFieldTrialsFromString(field_trial_string->c_str());
72
73 // Ignore a few bytes. Bytes from this segment will be used for
74 // future config flag changes. We assume 40 bytes is enough for
75 // configuring the APM.
76 constexpr size_t kSizeOfConfigSegment = 40;
77 RTC_DCHECK(kSizeOfConfigSegment >= fuzz_data->BytesRead());
78 static_cast<void>(
79 fuzz_data->ReadByteArray(kSizeOfConfigSegment - fuzz_data->BytesRead()));
80
81 // Filter out incompatible settings that lead to CHECK failures.
82 if ((use_aecm && use_aec) || // These settings cause CHECK failure.
83 (use_aecm && use_aec3 && use_ns) // These settings trigger webrtc:9489.
84 ) {
85 return nullptr;
86 }
87
88 std::unique_ptr<EchoControlFactory> echo_control_factory;
89 if (use_aec3) {
90 echo_control_factory.reset(new EchoCanceller3Factory());
91 }
92
93 webrtc::AudioProcessing::Config apm_config;
94 apm_config.pipeline.multi_channel_render = true;
95 apm_config.pipeline.multi_channel_capture = true;
96 apm_config.echo_canceller.enabled = use_aec || use_aecm;
97 apm_config.echo_canceller.mobile_mode = use_aecm;
98 apm_config.high_pass_filter.enabled = use_hpf;
99 apm_config.gain_controller1.enabled = use_agc;
100 apm_config.gain_controller1.enable_limiter = use_agc_limiter;
101 apm_config.gain_controller2.enabled = use_agc2;
102 apm_config.gain_controller2.fixed_digital.gain_db = gain_controller2_gain_db;
103 apm_config.gain_controller2.adaptive_digital.enabled =
104 use_agc2_adaptive_digital;
105 apm_config.noise_suppression.enabled = use_ns;
106 apm_config.transient_suppression.enabled = use_ts;
107
108 rtc::scoped_refptr<AudioProcessing> apm =
109 AudioProcessingBuilderForTesting()
110 .SetEchoControlFactory(std::move(echo_control_factory))
111 .SetEchoDetector(use_red ? CreateEchoDetector() : nullptr)
112 .SetConfig(apm_config)
113 .Create();
114
115 #ifdef WEBRTC_LINUX
116 apm->AttachAecDump(AecDumpFactory::Create("/dev/null", -1, worker_queue));
117 #endif
118
119 return apm;
120 }
121
GetTaskQueueFactory()122 TaskQueueFactory* GetTaskQueueFactory() {
123 static TaskQueueFactory* const factory =
124 CreateDefaultTaskQueueFactory().release();
125 return factory;
126 }
127
128 } // namespace
129
FuzzOneInput(const uint8_t * data,size_t size)130 void FuzzOneInput(const uint8_t* data, size_t size) {
131 if (size > 400000) {
132 return;
133 }
134 test::FuzzDataHelper fuzz_data(rtc::ArrayView<const uint8_t>(data, size));
135 // This string must be in scope during execution, according to documentation
136 // for field_trial.h. Hence it's created here and not in CreateApm.
137 std::string field_trial_string = "";
138
139 rtc::TaskQueue worker_queue(GetTaskQueueFactory()->CreateTaskQueue(
140 "rtc-low-prio", rtc::TaskQueue::Priority::LOW));
141 auto apm = CreateApm(&fuzz_data, &field_trial_string, &worker_queue);
142
143 if (apm) {
144 FuzzAudioProcessing(&fuzz_data, std::move(apm));
145 }
146 }
147 } // namespace webrtc
148