Skip to content

Commit 8e0b576

Browse files
pacbypassmandlil
andauthored
OSS-Fuzz: Fuzzer improvements (#3786)
* fuzzer changes * fix number_type error * rename fuzz_util back to fuzz_phone --------- Co-authored-by: mandlil <[email protected]>
1 parent 0f4f3e7 commit 8e0b576

File tree

4 files changed

+403
-14
lines changed

4 files changed

+403
-14
lines changed
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/* Copyright 2025 Google Inc.
2+
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
7+
http://www.apache.org/licenses/LICENSE-2.0
8+
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License.
14+
*/
15+
#include "phonenumbers/phonenumbermatcher.h"
16+
#include <string>
17+
#include <vector>
18+
#include <limits>
19+
#include <unicode/unistr.h>
20+
21+
#include "phonenumbers/base/basictypes.h"
22+
#include "phonenumbers/base/memory/scoped_ptr.h"
23+
#include "phonenumbers/base/memory/singleton.h"
24+
#include "phonenumbers/default_logger.h"
25+
#include "phonenumbers/phonenumber.h"
26+
#include "phonenumbers/phonenumbermatch.h"
27+
#include "phonenumbers/phonenumberutil.h"
28+
#include "phonenumbers/stringutil.h"
29+
#include "phonenumbers/asyoutypeformatter.h"
30+
#include "phonenumbers/shortnumberinfo.h"
31+
#include <fuzzer/FuzzedDataProvider.h>
32+
33+
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
34+
// initial setup of all the structs we need
35+
FuzzedDataProvider fuzzed_data(data, size);
36+
i18n::phonenumbers::PhoneNumberUtil* phone_util =
37+
i18n::phonenumbers::PhoneNumberUtil::GetInstance();
38+
bool region_is_2_bytes = fuzzed_data.ConsumeBool();
39+
std::string region = fuzzed_data.ConsumeBytesAsString(region_is_2_bytes ? 2 : 3);
40+
std::unique_ptr<i18n::phonenumbers::AsYouTypeFormatter> formatter(
41+
phone_util->GetAsYouTypeFormatter(region));
42+
43+
// setup the data passed to the target methods
44+
const int iterations = fuzzed_data.ConsumeIntegralInRange(0, 32);
45+
std::string result;
46+
47+
// Random amount of iterations
48+
for (int i = 0; i < iterations; ++i) {
49+
const char32_t next_char = fuzzed_data.ConsumeIntegral<char32_t>();
50+
const bool remember = fuzzed_data.ConsumeBool();
51+
52+
// Randomly trigger the remember method
53+
if (remember) {
54+
formatter->InputDigitAndRememberPosition(next_char, &result);
55+
} else {
56+
formatter->InputDigit(next_char, &result);
57+
}
58+
59+
// get the remembered position whether we remembered it or not
60+
formatter->GetRememberedPosition();
61+
}
62+
63+
return 0;
64+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/* Copyright 2025 Google Inc.
2+
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
7+
http://www.apache.org/licenses/LICENSE-2.0
8+
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License.
14+
*/
15+
#include "phonenumbers/phonenumbermatcher.h"
16+
#include <string>
17+
#include <vector>
18+
#include <limits>
19+
#include <unicode/unistr.h>
20+
21+
#include "phonenumbers/base/basictypes.h"
22+
#include "phonenumbers/base/memory/scoped_ptr.h"
23+
#include "phonenumbers/base/memory/singleton.h"
24+
#include "phonenumbers/default_logger.h"
25+
#include "phonenumbers/phonenumber.h"
26+
#include "phonenumbers/phonenumbermatch.h"
27+
#include "phonenumbers/regexp_adapter_icu.h"
28+
#include "phonenumbers/phonenumberutil.h"
29+
#include "phonenumbers/stringutil.h"
30+
#include "phonenumbers/asyoutypeformatter.h"
31+
#include "phonenumbers/shortnumberinfo.h"
32+
#include <fuzzer/FuzzedDataProvider.h>
33+
34+
// returns a leniency level based on the data we got from libfuzzer
35+
i18n::phonenumbers::PhoneNumberMatcher::Leniency ConsumeLeniency(
36+
FuzzedDataProvider& fuzzed_data) {
37+
switch (fuzzed_data.ConsumeIntegralInRange(0, 3)) {
38+
case 0:
39+
return i18n::phonenumbers::PhoneNumberMatcher::Leniency::POSSIBLE;
40+
case 1:
41+
return i18n::phonenumbers::PhoneNumberMatcher::Leniency::VALID;
42+
case 2:
43+
return i18n::phonenumbers::PhoneNumberMatcher::Leniency::STRICT_GROUPING;
44+
default:
45+
return i18n::phonenumbers::PhoneNumberMatcher::Leniency::EXACT_GROUPING;
46+
}
47+
}
48+
49+
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
50+
// Setup the data provider and util
51+
FuzzedDataProvider fuzzed_data(data, size);
52+
i18n::phonenumbers::PhoneNumberUtil* phone_util =
53+
i18n::phonenumbers::PhoneNumberUtil::GetInstance();
54+
55+
// this should be enought to get at least 2 matches
56+
std::string text = fuzzed_data.ConsumeBytesAsString(128);
57+
58+
// the region is either 2 or 3 characters long
59+
bool region_is_2_bytes = fuzzed_data.ConsumeBool();
60+
std::string region = fuzzed_data.ConsumeBytesAsString(region_is_2_bytes ? 2 : 3);
61+
62+
// setup fuzzed data for matchers
63+
i18n::phonenumbers::PhoneNumberMatcher::Leniency leniency =
64+
ConsumeLeniency(fuzzed_data);
65+
int max_tries = fuzzed_data.ConsumeIntegralInRange(0, 500);
66+
bool full_match = fuzzed_data.ConsumeBool();
67+
std::string regexp_string = fuzzed_data.ConsumeRandomLengthString(32);
68+
69+
70+
// initialize and fuzz the built-in matcher
71+
i18n::phonenumbers::PhoneNumberMatcher matcher(*phone_util, text, region,
72+
leniency, max_tries);
73+
while (matcher.HasNext()) {
74+
i18n::phonenumbers::PhoneNumberMatch match;
75+
matcher.Next(&match);
76+
}
77+
78+
// fuzz the matching with the icu adapter
79+
std::string matched_string;
80+
i18n::phonenumbers::ICURegExpFactory factory;
81+
i18n::phonenumbers::RegExp* regexp = factory.CreateRegExp(regexp_string);
82+
regexp->Match(text, full_match, &matched_string);
83+
84+
return 0;
85+
}

cpp/test/phonenumbers/fuzz_phone.cc

Lines changed: 175 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,34 +15,195 @@ limitations under the License.
1515
#include "phonenumbers/phonenumbermatcher.h"
1616
#include <string>
1717
#include <vector>
18-
18+
#include <limits>
1919
#include <unicode/unistr.h>
2020

2121
#include "phonenumbers/base/basictypes.h"
2222
#include "phonenumbers/base/memory/scoped_ptr.h"
2323
#include "phonenumbers/base/memory/singleton.h"
2424
#include "phonenumbers/default_logger.h"
2525
#include "phonenumbers/phonenumber.h"
26-
#include "phonenumbers/phonenumber.pb.h"
2726
#include "phonenumbers/phonenumbermatch.h"
2827
#include "phonenumbers/phonenumberutil.h"
2928
#include "phonenumbers/stringutil.h"
30-
29+
#include "phonenumbers/asyoutypeformatter.h"
30+
#include "phonenumbers/shortnumberinfo.h"
3131
#include <fuzzer/FuzzedDataProvider.h>
3232

33-
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
34-
{
35-
FuzzedDataProvider fuzzed_data(data, size);
33+
using google::protobuf::RepeatedPtrField;
34+
35+
// consume PhoneNumberUtil::PhoneNumberType from libfuzzer data
36+
i18n::phonenumbers::PhoneNumberUtil::PhoneNumberType ConsumePhoneNumberType(
37+
FuzzedDataProvider& fuzzed_data) {
38+
switch (fuzzed_data.ConsumeIntegralInRange(0, 11)) {
39+
case 0:
40+
return i18n::phonenumbers::PhoneNumberUtil::FIXED_LINE;
41+
case 1:
42+
return i18n::phonenumbers::PhoneNumberUtil::MOBILE;
43+
case 2:
44+
return i18n::phonenumbers::PhoneNumberUtil::FIXED_LINE_OR_MOBILE;
45+
case 3:
46+
return i18n::phonenumbers::PhoneNumberUtil::TOLL_FREE;
47+
case 4:
48+
return i18n::phonenumbers::PhoneNumberUtil::PREMIUM_RATE;
49+
case 5:
50+
return i18n::phonenumbers::PhoneNumberUtil::SHARED_COST;
51+
case 6:
52+
return i18n::phonenumbers::PhoneNumberUtil::VOIP;
53+
case 7:
54+
return i18n::phonenumbers::PhoneNumberUtil::PERSONAL_NUMBER;
55+
case 8:
56+
return i18n::phonenumbers::PhoneNumberUtil::PAGER;
57+
case 9:
58+
return i18n::phonenumbers::PhoneNumberUtil::UAN;
59+
case 10:
60+
return i18n::phonenumbers::PhoneNumberUtil::VOICEMAIL;
61+
default:
62+
return i18n::phonenumbers::PhoneNumberUtil::UNKNOWN;
63+
}
64+
}
65+
66+
// consume PhoneNumberUtil::PhoneNumberFormat from libfuzzer data
67+
i18n::phonenumbers::PhoneNumberUtil::PhoneNumberFormat ConsumePhoneNumberFormat(
68+
FuzzedDataProvider& fuzzed_data) {
69+
switch (fuzzed_data.ConsumeIntegralInRange(0, 3)) {
70+
case 0:
71+
return i18n::phonenumbers::PhoneNumberUtil::E164;
72+
case 1:
73+
return i18n::phonenumbers::PhoneNumberUtil::INTERNATIONAL;
74+
case 2:
75+
return i18n::phonenumbers::PhoneNumberUtil::NATIONAL;
76+
default:
77+
return i18n::phonenumbers::PhoneNumberUtil::RFC3966;
78+
}
79+
}
80+
81+
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
82+
// initialize the phone util
83+
i18n::phonenumbers::PhoneNumberUtil* phone_util =
84+
i18n::phonenumbers::PhoneNumberUtil::GetInstance();
85+
FuzzedDataProvider fuzzed_data(data, size);
86+
87+
// initialize the first phone number, region and country calling code
88+
i18n::phonenumbers::PhoneNumber phone_number;
89+
bool region_is_2_bytes = fuzzed_data.ConsumeBool();
90+
std::string region = fuzzed_data.ConsumeBytesAsString(region_is_2_bytes ? 2 : 3);
91+
std::string number = fuzzed_data.ConsumeRandomLengthString(32);
92+
int country_calling_code = fuzzed_data.ConsumeIntegral<int>();
93+
94+
// trigger either one of the public parse methods
95+
if (fuzzed_data.ConsumeBool()) {
96+
phone_util->ParseAndKeepRawInput(number, region, &phone_number);
97+
} else {
98+
phone_util->Parse(number, region, &phone_number);
99+
}
100+
101+
// initialize the second phone number, this is used only for the
102+
// isNumberMatch* methods
103+
i18n::phonenumbers::PhoneNumber phone_number2;
104+
std::string number2 = fuzzed_data.ConsumeRandomLengthString(32);
105+
if (fuzzed_data.ConsumeBool()) {
106+
phone_util->ParseAndKeepRawInput(number2, region, &phone_number2);
107+
} else {
108+
phone_util->Parse(number2, region, &phone_number2);
109+
}
110+
111+
// randomly trigger the truncate method, this may affect state of the input
112+
// for the method calls that follow it
113+
if (fuzzed_data.ConsumeIntegralInRange(0, 10) == 5) {
114+
phone_util->TruncateTooLongNumber(&phone_number);
115+
}
116+
117+
// fuzz public methods
118+
phone_util->IsAlphaNumber(number);
119+
phone_util->IsPossibleNumber(phone_number);
120+
phone_util->IsNumberMatch(phone_number, phone_number2);
121+
phone_util->IsNumberMatchWithOneString(phone_number, number2);
122+
phone_util->IsNumberMatchWithTwoStrings(number, number2);
123+
phone_util->CanBeInternationallyDialled(phone_number);
124+
phone_util->GetNumberType(phone_number);
125+
phone_util->GetLengthOfGeographicalAreaCode(phone_number);
126+
phone_util->GetLengthOfNationalDestinationCode(phone_number);
127+
phone_util->IsNANPACountry(region);
128+
phone_util->GetCountryCodeForRegion(region);
129+
phone_util->IsPossibleNumberForString(number, region);
130+
phone_util->IsNumberGeographical(phone_number);
131+
i18n::phonenumbers::PhoneNumberUtil::PhoneNumberType number_type =
132+
ConsumePhoneNumberType(fuzzed_data);
133+
phone_util->IsNumberGeographical(number_type, country_calling_code);
134+
phone_util->IsPossibleNumberForType(phone_number, number_type);
135+
136+
i18n::phonenumbers::PhoneNumber example_number;
137+
phone_util->GetExampleNumberForType(region, number_type, &example_number);
138+
139+
i18n::phonenumbers::PhoneNumber example_number_2;
140+
phone_util->GetExampleNumberForType(number_type, &example_number_2);
141+
142+
i18n::phonenumbers::PhoneNumber invalid_number;
143+
phone_util->GetInvalidExampleNumber(region, &invalid_number);
144+
145+
i18n::phonenumbers::PhoneNumber non_geo_number;
146+
phone_util->GetExampleNumberForNonGeoEntity(country_calling_code, &non_geo_number);
147+
148+
std::string output;
149+
phone_util->GetCountryMobileToken(country_calling_code, &output);
150+
output.clear();
151+
152+
phone_util->GetRegionCodeForNumber(phone_number, &output);
153+
output.clear();
154+
155+
phone_util->GetNddPrefixForRegion(region, fuzzed_data.ConsumeBool(), &output);
156+
output.clear();
157+
158+
// Fuzz the methods which affect the input string, but not the PhoneNumber object
159+
std::string input = fuzzed_data.ConsumeRandomLengthString(32);
160+
phone_util->ConvertAlphaCharactersInNumber(&input);
161+
input.clear();
162+
163+
input = fuzzed_data.ConsumeRandomLengthString(32);
164+
phone_util->NormalizeDigitsOnly(&input);
165+
input.clear();
166+
167+
input = fuzzed_data.ConsumeRandomLengthString(32);
168+
phone_util->NormalizeDiallableCharsOnly(&input);
169+
input.clear();
170+
171+
// Fuzz the formatting methods
172+
i18n::phonenumbers::PhoneNumberUtil::PhoneNumberFormat format = ConsumePhoneNumberFormat(fuzzed_data);
173+
174+
std::string formatted;
175+
phone_util->Format(phone_number, format, &formatted);
176+
formatted.clear();
177+
178+
phone_util->FormatInOriginalFormat(phone_number, region, &formatted);
179+
formatted.clear();
180+
181+
phone_util->FormatNumberForMobileDialing(phone_number, region,
182+
fuzzed_data.ConsumeBool(), &formatted);
183+
formatted.clear();
184+
185+
phone_util->FormatNationalNumberWithPreferredCarrierCode(phone_number, region, &formatted);
186+
formatted.clear();
187+
188+
phone_util->FormatOutOfCountryKeepingAlphaChars(phone_number, region, &formatted);
189+
formatted.clear();
36190

37-
std::string input = fuzzed_data.ConsumeRandomLengthString();
38-
std::string input2 = fuzzed_data.ConsumeRandomLengthString();
191+
std::string carrier = fuzzed_data.ConsumeRandomLengthString(8);
192+
phone_util->FormatNationalNumberWithCarrierCode(phone_number, carrier, &formatted);
193+
formatted.clear();
39194

40-
i18n::phonenumbers::PhoneNumberUtil *phone_util = i18n::phonenumbers::PhoneNumberUtil::GetInstance();
41-
i18n::phonenumbers::PhoneNumber parsed;
195+
// setup the parameters for FormatByPattern
196+
i18n::phonenumbers::PhoneNumberUtil::PhoneNumberFormat number_format = ConsumePhoneNumberFormat(fuzzed_data);
197+
RepeatedPtrField<i18n::phonenumbers::NumberFormat> number_formats;
198+
i18n::phonenumbers::NumberFormat* temp_number_format = number_formats.Add();
199+
std::string pattern = fuzzed_data.ConsumeRandomLengthString(16);
200+
std::string format_string = fuzzed_data.ConsumeRandomLengthString(16);
201+
temp_number_format->set_pattern(pattern);
202+
temp_number_format->set_format(format_string);
42203

43-
phone_util->Parse(input, input2, &parsed);
44-
phone_util->IsValidNumber(parsed);
45-
phone_util->GetCountryCodeForRegion(input);
204+
// fuzz FormatByPattern
205+
phone_util->FormatByPattern(phone_number, number_format, number_formats, &formatted);
206+
formatted.clear();
46207

47-
return 0;
208+
return 0;
48209
}

0 commit comments

Comments
 (0)