From 8e0b576fc82cc28ae89dccd45521d5dc15e3cb31 Mon Sep 17 00:00:00 2001 From: pacbypass Date: Tue, 15 Apr 2025 20:31:19 -0700 Subject: [PATCH] OSS-Fuzz: Fuzzer improvements (#3786) * fuzzer changes * fix number_type error * rename fuzz_util back to fuzz_phone --------- Co-authored-by: mandlil <138015259+mandlil@users.noreply.github.com> --- .../phonenumbers/fuzz_asyoutypeformatter.cc | 64 ++++++ cpp/test/phonenumbers/fuzz_matcher.cc | 85 ++++++++ cpp/test/phonenumbers/fuzz_phone.cc | 189 ++++++++++++++++-- cpp/test/phonenumbers/fuzz_shortnumberinfo.cc | 79 ++++++++ 4 files changed, 403 insertions(+), 14 deletions(-) create mode 100644 cpp/test/phonenumbers/fuzz_asyoutypeformatter.cc create mode 100644 cpp/test/phonenumbers/fuzz_matcher.cc create mode 100644 cpp/test/phonenumbers/fuzz_shortnumberinfo.cc diff --git a/cpp/test/phonenumbers/fuzz_asyoutypeformatter.cc b/cpp/test/phonenumbers/fuzz_asyoutypeformatter.cc new file mode 100644 index 000000000..5089ae68c --- /dev/null +++ b/cpp/test/phonenumbers/fuzz_asyoutypeformatter.cc @@ -0,0 +1,64 @@ +/* Copyright 2025 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +#include "phonenumbers/phonenumbermatcher.h" +#include +#include +#include +#include + +#include "phonenumbers/base/basictypes.h" +#include "phonenumbers/base/memory/scoped_ptr.h" +#include "phonenumbers/base/memory/singleton.h" +#include "phonenumbers/default_logger.h" +#include "phonenumbers/phonenumber.h" +#include "phonenumbers/phonenumbermatch.h" +#include "phonenumbers/phonenumberutil.h" +#include "phonenumbers/stringutil.h" +#include "phonenumbers/asyoutypeformatter.h" +#include "phonenumbers/shortnumberinfo.h" +#include + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + // initial setup of all the structs we need + FuzzedDataProvider fuzzed_data(data, size); + i18n::phonenumbers::PhoneNumberUtil* phone_util = + i18n::phonenumbers::PhoneNumberUtil::GetInstance(); + bool region_is_2_bytes = fuzzed_data.ConsumeBool(); + std::string region = fuzzed_data.ConsumeBytesAsString(region_is_2_bytes ? 2 : 3); + std::unique_ptr formatter( + phone_util->GetAsYouTypeFormatter(region)); + + // setup the data passed to the target methods + const int iterations = fuzzed_data.ConsumeIntegralInRange(0, 32); + std::string result; + + // Random amount of iterations + for (int i = 0; i < iterations; ++i) { + const char32_t next_char = fuzzed_data.ConsumeIntegral(); + const bool remember = fuzzed_data.ConsumeBool(); + + // Randomly trigger the remember method + if (remember) { + formatter->InputDigitAndRememberPosition(next_char, &result); + } else { + formatter->InputDigit(next_char, &result); + } + + // get the remembered position whether we remembered it or not + formatter->GetRememberedPosition(); + } + + return 0; +} \ No newline at end of file diff --git a/cpp/test/phonenumbers/fuzz_matcher.cc b/cpp/test/phonenumbers/fuzz_matcher.cc new file mode 100644 index 000000000..aaacc57a1 --- /dev/null +++ b/cpp/test/phonenumbers/fuzz_matcher.cc @@ -0,0 +1,85 @@ +/* Copyright 2025 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +#include "phonenumbers/phonenumbermatcher.h" +#include +#include +#include +#include + +#include "phonenumbers/base/basictypes.h" +#include "phonenumbers/base/memory/scoped_ptr.h" +#include "phonenumbers/base/memory/singleton.h" +#include "phonenumbers/default_logger.h" +#include "phonenumbers/phonenumber.h" +#include "phonenumbers/phonenumbermatch.h" +#include "phonenumbers/regexp_adapter_icu.h" +#include "phonenumbers/phonenumberutil.h" +#include "phonenumbers/stringutil.h" +#include "phonenumbers/asyoutypeformatter.h" +#include "phonenumbers/shortnumberinfo.h" +#include + +// returns a leniency level based on the data we got from libfuzzer +i18n::phonenumbers::PhoneNumberMatcher::Leniency ConsumeLeniency( + FuzzedDataProvider& fuzzed_data) { + switch (fuzzed_data.ConsumeIntegralInRange(0, 3)) { + case 0: + return i18n::phonenumbers::PhoneNumberMatcher::Leniency::POSSIBLE; + case 1: + return i18n::phonenumbers::PhoneNumberMatcher::Leniency::VALID; + case 2: + return i18n::phonenumbers::PhoneNumberMatcher::Leniency::STRICT_GROUPING; + default: + return i18n::phonenumbers::PhoneNumberMatcher::Leniency::EXACT_GROUPING; + } +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + // Setup the data provider and util + FuzzedDataProvider fuzzed_data(data, size); + i18n::phonenumbers::PhoneNumberUtil* phone_util = + i18n::phonenumbers::PhoneNumberUtil::GetInstance(); + + // this should be enought to get at least 2 matches + std::string text = fuzzed_data.ConsumeBytesAsString(128); + + // the region is either 2 or 3 characters long + bool region_is_2_bytes = fuzzed_data.ConsumeBool(); + std::string region = fuzzed_data.ConsumeBytesAsString(region_is_2_bytes ? 2 : 3); + + // setup fuzzed data for matchers + i18n::phonenumbers::PhoneNumberMatcher::Leniency leniency = + ConsumeLeniency(fuzzed_data); + int max_tries = fuzzed_data.ConsumeIntegralInRange(0, 500); + bool full_match = fuzzed_data.ConsumeBool(); + std::string regexp_string = fuzzed_data.ConsumeRandomLengthString(32); + + + // initialize and fuzz the built-in matcher + i18n::phonenumbers::PhoneNumberMatcher matcher(*phone_util, text, region, + leniency, max_tries); + while (matcher.HasNext()) { + i18n::phonenumbers::PhoneNumberMatch match; + matcher.Next(&match); + } + + // fuzz the matching with the icu adapter + std::string matched_string; + i18n::phonenumbers::ICURegExpFactory factory; + i18n::phonenumbers::RegExp* regexp = factory.CreateRegExp(regexp_string); + regexp->Match(text, full_match, &matched_string); + + return 0; +} \ No newline at end of file diff --git a/cpp/test/phonenumbers/fuzz_phone.cc b/cpp/test/phonenumbers/fuzz_phone.cc index a63d747a9..d9cc5312f 100644 --- a/cpp/test/phonenumbers/fuzz_phone.cc +++ b/cpp/test/phonenumbers/fuzz_phone.cc @@ -15,7 +15,7 @@ limitations under the License. #include "phonenumbers/phonenumbermatcher.h" #include #include - +#include #include #include "phonenumbers/base/basictypes.h" @@ -23,26 +23,187 @@ limitations under the License. #include "phonenumbers/base/memory/singleton.h" #include "phonenumbers/default_logger.h" #include "phonenumbers/phonenumber.h" -#include "phonenumbers/phonenumber.pb.h" #include "phonenumbers/phonenumbermatch.h" #include "phonenumbers/phonenumberutil.h" #include "phonenumbers/stringutil.h" - +#include "phonenumbers/asyoutypeformatter.h" +#include "phonenumbers/shortnumberinfo.h" #include -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) -{ - FuzzedDataProvider fuzzed_data(data, size); +using google::protobuf::RepeatedPtrField; + +// consume PhoneNumberUtil::PhoneNumberType from libfuzzer data +i18n::phonenumbers::PhoneNumberUtil::PhoneNumberType ConsumePhoneNumberType( + FuzzedDataProvider& fuzzed_data) { + switch (fuzzed_data.ConsumeIntegralInRange(0, 11)) { + case 0: + return i18n::phonenumbers::PhoneNumberUtil::FIXED_LINE; + case 1: + return i18n::phonenumbers::PhoneNumberUtil::MOBILE; + case 2: + return i18n::phonenumbers::PhoneNumberUtil::FIXED_LINE_OR_MOBILE; + case 3: + return i18n::phonenumbers::PhoneNumberUtil::TOLL_FREE; + case 4: + return i18n::phonenumbers::PhoneNumberUtil::PREMIUM_RATE; + case 5: + return i18n::phonenumbers::PhoneNumberUtil::SHARED_COST; + case 6: + return i18n::phonenumbers::PhoneNumberUtil::VOIP; + case 7: + return i18n::phonenumbers::PhoneNumberUtil::PERSONAL_NUMBER; + case 8: + return i18n::phonenumbers::PhoneNumberUtil::PAGER; + case 9: + return i18n::phonenumbers::PhoneNumberUtil::UAN; + case 10: + return i18n::phonenumbers::PhoneNumberUtil::VOICEMAIL; + default: + return i18n::phonenumbers::PhoneNumberUtil::UNKNOWN; + } +} + +// consume PhoneNumberUtil::PhoneNumberFormat from libfuzzer data +i18n::phonenumbers::PhoneNumberUtil::PhoneNumberFormat ConsumePhoneNumberFormat( + FuzzedDataProvider& fuzzed_data) { + switch (fuzzed_data.ConsumeIntegralInRange(0, 3)) { + case 0: + return i18n::phonenumbers::PhoneNumberUtil::E164; + case 1: + return i18n::phonenumbers::PhoneNumberUtil::INTERNATIONAL; + case 2: + return i18n::phonenumbers::PhoneNumberUtil::NATIONAL; + default: + return i18n::phonenumbers::PhoneNumberUtil::RFC3966; + } +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + // initialize the phone util + i18n::phonenumbers::PhoneNumberUtil* phone_util = + i18n::phonenumbers::PhoneNumberUtil::GetInstance(); + FuzzedDataProvider fuzzed_data(data, size); + + // initialize the first phone number, region and country calling code + i18n::phonenumbers::PhoneNumber phone_number; + bool region_is_2_bytes = fuzzed_data.ConsumeBool(); + std::string region = fuzzed_data.ConsumeBytesAsString(region_is_2_bytes ? 2 : 3); + std::string number = fuzzed_data.ConsumeRandomLengthString(32); + int country_calling_code = fuzzed_data.ConsumeIntegral(); + + // trigger either one of the public parse methods + if (fuzzed_data.ConsumeBool()) { + phone_util->ParseAndKeepRawInput(number, region, &phone_number); + } else { + phone_util->Parse(number, region, &phone_number); + } + + // initialize the second phone number, this is used only for the + // isNumberMatch* methods + i18n::phonenumbers::PhoneNumber phone_number2; + std::string number2 = fuzzed_data.ConsumeRandomLengthString(32); + if (fuzzed_data.ConsumeBool()) { + phone_util->ParseAndKeepRawInput(number2, region, &phone_number2); + } else { + phone_util->Parse(number2, region, &phone_number2); + } + + // randomly trigger the truncate method, this may affect state of the input + // for the method calls that follow it + if (fuzzed_data.ConsumeIntegralInRange(0, 10) == 5) { + phone_util->TruncateTooLongNumber(&phone_number); + } + + // fuzz public methods + phone_util->IsAlphaNumber(number); + phone_util->IsPossibleNumber(phone_number); + phone_util->IsNumberMatch(phone_number, phone_number2); + phone_util->IsNumberMatchWithOneString(phone_number, number2); + phone_util->IsNumberMatchWithTwoStrings(number, number2); + phone_util->CanBeInternationallyDialled(phone_number); + phone_util->GetNumberType(phone_number); + phone_util->GetLengthOfGeographicalAreaCode(phone_number); + phone_util->GetLengthOfNationalDestinationCode(phone_number); + phone_util->IsNANPACountry(region); + phone_util->GetCountryCodeForRegion(region); + phone_util->IsPossibleNumberForString(number, region); + phone_util->IsNumberGeographical(phone_number); + i18n::phonenumbers::PhoneNumberUtil::PhoneNumberType number_type = + ConsumePhoneNumberType(fuzzed_data); + phone_util->IsNumberGeographical(number_type, country_calling_code); + phone_util->IsPossibleNumberForType(phone_number, number_type); + + i18n::phonenumbers::PhoneNumber example_number; + phone_util->GetExampleNumberForType(region, number_type, &example_number); + + i18n::phonenumbers::PhoneNumber example_number_2; + phone_util->GetExampleNumberForType(number_type, &example_number_2); + + i18n::phonenumbers::PhoneNumber invalid_number; + phone_util->GetInvalidExampleNumber(region, &invalid_number); + + i18n::phonenumbers::PhoneNumber non_geo_number; + phone_util->GetExampleNumberForNonGeoEntity(country_calling_code, &non_geo_number); + + std::string output; + phone_util->GetCountryMobileToken(country_calling_code, &output); + output.clear(); + + phone_util->GetRegionCodeForNumber(phone_number, &output); + output.clear(); + + phone_util->GetNddPrefixForRegion(region, fuzzed_data.ConsumeBool(), &output); + output.clear(); + + // Fuzz the methods which affect the input string, but not the PhoneNumber object + std::string input = fuzzed_data.ConsumeRandomLengthString(32); + phone_util->ConvertAlphaCharactersInNumber(&input); + input.clear(); + + input = fuzzed_data.ConsumeRandomLengthString(32); + phone_util->NormalizeDigitsOnly(&input); + input.clear(); + + input = fuzzed_data.ConsumeRandomLengthString(32); + phone_util->NormalizeDiallableCharsOnly(&input); + input.clear(); + + // Fuzz the formatting methods + i18n::phonenumbers::PhoneNumberUtil::PhoneNumberFormat format = ConsumePhoneNumberFormat(fuzzed_data); + + std::string formatted; + phone_util->Format(phone_number, format, &formatted); + formatted.clear(); + + phone_util->FormatInOriginalFormat(phone_number, region, &formatted); + formatted.clear(); + + phone_util->FormatNumberForMobileDialing(phone_number, region, + fuzzed_data.ConsumeBool(), &formatted); + formatted.clear(); + + phone_util->FormatNationalNumberWithPreferredCarrierCode(phone_number, region, &formatted); + formatted.clear(); + + phone_util->FormatOutOfCountryKeepingAlphaChars(phone_number, region, &formatted); + formatted.clear(); - std::string input = fuzzed_data.ConsumeRandomLengthString(); - std::string input2 = fuzzed_data.ConsumeRandomLengthString(); + std::string carrier = fuzzed_data.ConsumeRandomLengthString(8); + phone_util->FormatNationalNumberWithCarrierCode(phone_number, carrier, &formatted); + formatted.clear(); - i18n::phonenumbers::PhoneNumberUtil *phone_util = i18n::phonenumbers::PhoneNumberUtil::GetInstance(); - i18n::phonenumbers::PhoneNumber parsed; + // setup the parameters for FormatByPattern + i18n::phonenumbers::PhoneNumberUtil::PhoneNumberFormat number_format = ConsumePhoneNumberFormat(fuzzed_data); + RepeatedPtrField number_formats; + i18n::phonenumbers::NumberFormat* temp_number_format = number_formats.Add(); + std::string pattern = fuzzed_data.ConsumeRandomLengthString(16); + std::string format_string = fuzzed_data.ConsumeRandomLengthString(16); + temp_number_format->set_pattern(pattern); + temp_number_format->set_format(format_string); - phone_util->Parse(input, input2, &parsed); - phone_util->IsValidNumber(parsed); - phone_util->GetCountryCodeForRegion(input); + // fuzz FormatByPattern + phone_util->FormatByPattern(phone_number, number_format, number_formats, &formatted); + formatted.clear(); - return 0; + return 0; } diff --git a/cpp/test/phonenumbers/fuzz_shortnumberinfo.cc b/cpp/test/phonenumbers/fuzz_shortnumberinfo.cc new file mode 100644 index 000000000..7dbcff8f1 --- /dev/null +++ b/cpp/test/phonenumbers/fuzz_shortnumberinfo.cc @@ -0,0 +1,79 @@ +/* Copyright 2025 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +#include "phonenumbers/phonenumbermatcher.h" +#include +#include +#include +#include + +#include "phonenumbers/base/basictypes.h" +#include "phonenumbers/base/memory/scoped_ptr.h" +#include "phonenumbers/base/memory/singleton.h" +#include "phonenumbers/default_logger.h" +#include "phonenumbers/phonenumber.h" +#include "phonenumbers/phonenumbermatch.h" +#include "phonenumbers/phonenumberutil.h" +#include "phonenumbers/stringutil.h" +#include "phonenumbers/asyoutypeformatter.h" +#include "phonenumbers/shortnumberinfo.h" +#include + +// returns a short number cost based on the data we got from libfuzzer +i18n::phonenumbers::ShortNumberInfo::ShortNumberCost ConsumeShortNumberCost( + FuzzedDataProvider& fuzzed_data) { + switch (fuzzed_data.ConsumeIntegralInRange(0, 4)) { + case 0: return i18n::phonenumbers::ShortNumberInfo::TOLL_FREE; + case 1: return i18n::phonenumbers::ShortNumberInfo::STANDARD_RATE; + case 2: return i18n::phonenumbers::ShortNumberInfo::PREMIUM_RATE; + default: return i18n::phonenumbers::ShortNumberInfo::UNKNOWN_COST; + } +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + // setup the data provider and util + FuzzedDataProvider fuzzed_data(data, size); + i18n::phonenumbers::PhoneNumberUtil* phone_util = + i18n::phonenumbers::PhoneNumberUtil::GetInstance(); + + // setup all the data we need to pass to the target methods + i18n::phonenumbers::PhoneNumber phone_number; + std::string number = fuzzed_data.ConsumeRandomLengthString(32); + bool region_is_2_bytes = fuzzed_data.ConsumeBool(); + std::string region = fuzzed_data.ConsumeBytesAsString(region_is_2_bytes ? 2 : 3); + if (fuzzed_data.ConsumeBool()) { + phone_util->ParseAndKeepRawInput(number, region, &phone_number); + } else { + phone_util->Parse(number, region, &phone_number); + } + + // fuzz the public methods + i18n::phonenumbers::ShortNumberInfo short_info; + short_info.IsPossibleShortNumberForRegion(phone_number, region); + short_info.IsPossibleShortNumber(phone_number); + short_info.IsValidShortNumber(phone_number); + short_info.GetExpectedCostForRegion(phone_number, region); + short_info.GetExpectedCost(phone_number); + short_info.GetExampleShortNumber(region); + i18n::phonenumbers::ShortNumberInfo::ShortNumberCost cost = + ConsumeShortNumberCost(fuzzed_data); + short_info.GetExampleShortNumberForCost(region, cost); + short_info.ConnectsToEmergencyNumber(number, region); + short_info.IsEmergencyNumber(number, region); + short_info.IsCarrierSpecific(phone_number); + short_info.IsCarrierSpecificForRegion(phone_number, region); + short_info.IsSmsServiceForRegion(phone_number, region); + + return 0; +} \ No newline at end of file