From 626b3f3c81a9539c1f49c29c2a15373a78127bdb Mon Sep 17 00:00:00 2001 From: Sushant Poudel Date: Tue, 15 Sep 2026 20:53:44 +0545 Subject: [PATCH] fuzz(asyoutypeformatter): select a valid region and feed phone-relevant characters The existing harness constructs AsYouTypeFormatter with a 2-3 byte random region string, which is essentially never a valid ISO-3166 alpha-2 code. AsYouTypeFormatter::GetMetadataForRegion then falls back to the empty metadata instance (no NumberFormat entries), so possible_formats_ stays empty and the formatting state machine is never exercised. Additionally, each input character is a uniformly random char32_t, which is essentially never a digit, so InputDigit sets able_to_format_ = false on the first character and the formatter only echoes the raw input. Together these leave asyoutypeformatter.cc at 0% line/function coverage in OSS-Fuzz (0/532 lines, 0/29 functions in the 2025-02-02 report). Select the region from a fixed set of real region codes and feed characters from a phone-relevant alphabet, raising local coverage from 23.5% to 93.1% of lines and 31.0% to 93.1% of functions in a 20k-run empty-corpus comparison. --- .../phonenumbers/fuzz_asyoutypeformatter.cc | 45 ++++++++++++++----- 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/cpp/test/phonenumbers/fuzz_asyoutypeformatter.cc b/cpp/test/phonenumbers/fuzz_asyoutypeformatter.cc index 5089ae68c1..0251a11856 100644 --- a/cpp/test/phonenumbers/fuzz_asyoutypeformatter.cc +++ b/cpp/test/phonenumbers/fuzz_asyoutypeformatter.cc @@ -30,35 +30,56 @@ limitations under the License. #include "phonenumbers/shortnumberinfo.h" #include +namespace { + +// A fixed set of valid ISO-3166-1 alpha-2 region codes spanning different +// numbering plans (NANP, variable-length, leading-zero, etc.). Selecting from +// this set guarantees that AsYouTypeFormatter is constructed with real +// metadata instead of the empty metadata instance, so the formatting state +// machine is actually exercised. +const char* const kRegions[] = { + "US", "CA", "GB", "DE", "FR", "IN", "BR", "RU", "JP", "CN", + "AU", "MX", "IT", "ES", "NL", "KR", +}; + +// A phone-relevant alphabet. Feeding characters from this set (rather than +// arbitrary char32_t code points) keeps InputDigit on the formatting path +// instead of immediately setting able_to_format_ = false on the first +// non-digit, while still letting the fuzzer control the exact sequence, +// length, and position of '+' / '*' / '#' / '(' / ')' / '-' / ' '. +const char kPhoneChars[] = "0123456789+*#() -"; + +} // namespace + 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* 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); + + // Pick a valid region from the fixed set. + const char* region = kRegions[fuzzed_data.ConsumeIntegralInRange( + 0, (sizeof(kRegions) / sizeof(kRegions[0])) - 1)]; std::unique_ptr formatter( phone_util->GetAsYouTypeFormatter(region)); - // setup the data passed to the target methods - const int iterations = fuzzed_data.ConsumeIntegralInRange(0, 32); + const int iterations = fuzzed_data.ConsumeIntegralInRange(0, 64); std::string result; - // Random amount of iterations for (int i = 0; i < iterations; ++i) { - const char32_t next_char = fuzzed_data.ConsumeIntegral(); + // Feed a character from the phone alphabet so the digit state machine is + // actually exercised. + const char32_t next_char = kPhoneChars[fuzzed_data.ConsumeIntegralInRange( + 0, sizeof(kPhoneChars) - 2)]; 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 +}