diff --git a/.github/workflows/cifuzz.yml b/.github/workflows/cifuzz.yml new file mode 100644 index 00000000000..1246c2f073b --- /dev/null +++ b/.github/workflows/cifuzz.yml @@ -0,0 +1,67 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# +# 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. + +# CIFuzz – runs OSS-Fuzz fuzz targets on every pull request. +# +# On each PR this action: +# 1. Checks out the repository (with the PR changes). +# 2. Builds the affected fuzz targets inside the OSS-Fuzz Docker image. +# 3. Runs each fuzzer for a short duration (600 s by default). +# 4. Uploads any crashes found as a workflow artifact. +# +# Reference: https://google.github.io/oss-fuzz/getting-started/continuous-integration/ + +name: CIFuzz + +on: + push: + branches: + - main + pull_request: + branches: + - main + +permissions: + contents: read # to fetch code (actions/checkout) + +jobs: + fuzzing: + name: Run fuzz targets + runs-on: ubuntu-latest + steps: + - name: Build fuzz targets + uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master + with: + oss-fuzz-project-name: folly + # Build the version of the code in this PR, not the OSS-Fuzz HEAD pin. + dry-run: false + language: c++ + sanitizer: address + + - name: Run fuzz targets + uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master + with: + oss-fuzz-project-name: folly + fuzz-seconds: 600 + dry-run: false + language: c++ + sanitizer: address + output-sarif: true + + - name: Upload crash artifacts + if: failure() + uses: actions/upload-artifact@v4 + with: + name: cifuzz-artifacts + path: ./out/artifacts diff --git a/CMakeLists.txt b/CMakeLists.txt index 49dd96451f0..b1bc73cb52a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -368,6 +368,8 @@ option(BUILD_BENCHMARKS "If enabled, compile the benchmarks." OFF) option(BUILD_BROKEN_TESTS "If enabled, compile tests that are known to be broken." OFF) option(BUILD_HANGING_TESTS "If enabled, compile tests that are known to hang." OFF) option(BUILD_SLOW_TESTS "If enabled, compile tests that take a while to run in debug mode." OFF) +option(BUILD_FUZZ_TARGETS + "If enabled, compile the libFuzzer-based fuzz targets under folly/fuzz/." OFF) if (BUILD_TESTS OR BUILD_BENCHMARKS) option(USE_CMAKE_GOOGLE_TEST_INTEGRATION "If enabled, use the google test integration included in CMake." ON) find_package(GMock MODULE REQUIRED) @@ -1077,3 +1079,11 @@ if (BUILD_TESTS OR BUILD_BENCHMARKS) ) endif() endif() + +# --------------------------------------------------------------------------- +# Fuzz targets (OSS-Fuzz integration + local developer fuzzing) +# Enable with -DBUILD_FUZZ_TARGETS=ON. Requires a Clang toolchain. +# --------------------------------------------------------------------------- +if (BUILD_FUZZ_TARGETS) + add_subdirectory(${FOLLY_DIR}/fuzz ${CMAKE_CURRENT_BINARY_DIR}/folly/fuzz) +endif() diff --git a/folly/fuzz/CMakeLists.txt b/folly/fuzz/CMakeLists.txt new file mode 100644 index 00000000000..58184cdedc8 --- /dev/null +++ b/folly/fuzz/CMakeLists.txt @@ -0,0 +1,85 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# +# 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. + +# CMake build file for Folly fuzz targets. +# +# To build locally (requires a Clang toolchain): +# +# cmake -S . -B _build \ +# -DCMAKE_C_COMPILER=clang \ +# -DCMAKE_CXX_COMPILER=clang++ \ +# -DBUILD_FUZZ_TARGETS=ON \ +# -DLIB_FUZZING_ENGINE="-fsanitize=fuzzer" \ +# -DCMAKE_CXX_FLAGS="-fsanitize=address,undefined -fno-omit-frame-pointer" +# cmake --build _build --target json_fuzzer uri_fuzzer ipaddress_fuzzer conv_fuzzer +# +# For OSS-Fuzz the build is driven by projects/folly/build.sh, which sets +# LIB_FUZZING_ENGINE to the engine library provided by the OSS-Fuzz harness +# (e.g. -fsanitize=fuzzer, or a path to a static driver library). + +# Default to the libFuzzer engine when building without the OSS-Fuzz harness. +if(NOT DEFINED LIB_FUZZING_ENGINE) + set(LIB_FUZZING_ENGINE + "-fsanitize=fuzzer" + CACHE STRING + "Fuzzing-engine flags or library path passed to the compiler and linker. \ +Common values: '-fsanitize=fuzzer' (libFuzzer) or a path like \ +'/usr/lib/libFuzzingEngine.a' (AFL++ driver).") +endif() + +# --------------------------------------------------------------------------- +# Helper to register a single fuzz target. +# --------------------------------------------------------------------------- +function(folly_fuzz_target name) + add_executable(${name} ${name}.cc) + + # Folly requires C++20. + target_compile_features(${name} PRIVATE cxx_std_20) + + # Link against the monolithic folly library. + target_link_libraries(${name} PRIVATE folly) + + # Apply the same compiler flags as the rest of folly (warnings, defines …). + apply_folly_compile_options_to_target(${name}) + + # Wire in the fuzzing engine. + # LIB_FUZZING_ENGINE may be: + # a) a compiler/linker flag (e.g. -fsanitize=fuzzer) — must be passed as a + # compile option AND a link option so that clang instruments the binary. + # b) a path to a static library (e.g. /usr/lib/libFuzzingEngine.a) — link + # as a plain library. + string(STRIP "${LIB_FUZZING_ENGINE}" _lfe_stripped) + if(_lfe_stripped MATCHES "^-") + target_compile_options(${name} PRIVATE ${_lfe_stripped}) + target_link_options(${name} PRIVATE ${_lfe_stripped}) + else() + target_link_libraries(${name} PRIVATE ${_lfe_stripped}) + endif() +endfunction() + +# --------------------------------------------------------------------------- +# Fuzz targets +# --------------------------------------------------------------------------- + +# JSON parser / serializer (folly::parseJson, folly::toJson) +folly_fuzz_target(json_fuzzer) + +# URI parser (folly::Uri) +folly_fuzz_target(uri_fuzzer) + +# IP address parser (folly::IPAddress, CIDR notation) +folly_fuzz_target(ipaddress_fuzzer) + +# String-to-scalar conversions (folly::tryTo) +folly_fuzz_target(conv_fuzzer) diff --git a/folly/fuzz/README.md b/folly/fuzz/README.md new file mode 100644 index 00000000000..a1e084ed67f --- /dev/null +++ b/folly/fuzz/README.md @@ -0,0 +1,44 @@ +# Folly Fuzz Targets + +Fuzz targets for [Folly](https://github.com/facebook/folly), integrated with +[OSS-Fuzz](https://github.com/google/oss-fuzz/tree/master/projects/folly). + +## What is fuzzed + +| Target | API | +|--------|-----| +| `json_fuzzer` | `folly::parseJson()`, `folly::toJson()` — parse + round-trip invariant | +| `uri_fuzzer` | `folly::Uri::tryFromString()` — all URI field accessors | +| `ipaddress_fuzzer` | `folly::IPAddress::tryFromString()`, `tryCreateNetwork()` — all predicates | +| `conv_fuzzer` | `folly::tryTo()` — all signed/unsigned integer, float, and bool conversions | + +## Building + +Requires Clang with AddressSanitizer and libFuzzer support (Clang ≥ 12). + +```sh +cmake -S . -B _build \ + -DCMAKE_C_COMPILER=clang \ + -DCMAKE_CXX_COMPILER=clang++ \ + -DBUILD_FUZZ_TARGETS=ON \ + -DLIB_FUZZING_ENGINE="-fsanitize=fuzzer" \ + -DCMAKE_CXX_FLAGS="-fsanitize=address,undefined -fno-omit-frame-pointer" + +cmake --build _build --target json_fuzzer uri_fuzzer ipaddress_fuzzer conv_fuzzer +``` + +## Running + +```sh +./_build/folly/fuzz/json_fuzzer folly/fuzz/corpus/json_fuzzer/ +./_build/folly/fuzz/uri_fuzzer folly/fuzz/corpus/uri_fuzzer/ +./_build/folly/fuzz/ipaddress_fuzzer folly/fuzz/corpus/ipaddress_fuzzer/ +./_build/folly/fuzz/conv_fuzzer folly/fuzz/corpus/conv_fuzzer/ +``` + +Pass `-max_total_time=600` to limit each run to 10 minutes. + +## OSS-Fuzz + +These targets are built and exercised continuously by OSS-Fuzz. Crashes are +triaged automatically and filed as security bugs against the Folly project. diff --git a/folly/fuzz/conv_fuzzer.cc b/folly/fuzz/conv_fuzzer.cc new file mode 100644 index 00000000000..f5ea182f7a2 --- /dev/null +++ b/folly/fuzz/conv_fuzzer.cc @@ -0,0 +1,81 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * 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. + */ + +/** + * Fuzz target for folly::tryTo() string-to-scalar conversions (Conv.h). + * + * Exercises: + * - tryTo() for all signed/unsigned integer types and bool + * - tryTo() and tryTo() + * + * All conversions use the non-throwing tryTo<> variant to avoid false-positive + * "crashes" on expected parse failures. + * + * Security relevance: Integer and floating-point parsers are prone to + * off-by-one errors, sign-extension bugs, and silent overflow/underflow. + * UBSan is particularly effective at detecting these classes of bugs. + * + * OSS-Fuzz integration: https://github.com/google/oss-fuzz/tree/master/projects/folly + */ + +#include +#include +#include + +#include +#include + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + if (size > 4096) { + return 0; + } + + const folly::StringPiece sp( + reinterpret_cast(data), size); + + // Test the raw input against every scalar type. + (void)folly::tryTo(sp); + (void)folly::tryTo(sp); + (void)folly::tryTo(sp); + (void)folly::tryTo(sp); + (void)folly::tryTo(sp); + (void)folly::tryTo(sp); + (void)folly::tryTo(sp); + (void)folly::tryTo(sp); + (void)folly::tryTo(sp); + (void)folly::tryTo(sp); + (void)folly::tryTo(sp); + + // Negate the input to exercise negative-number and sign-handling paths. + if (size > 0) { + const std::string negated = + "-" + std::string(reinterpret_cast(data), size); + const folly::StringPiece neg(negated); + (void)folly::tryTo(neg); + (void)folly::tryTo(neg); + } + + // Prepend "0x" to hit hexadecimal parsing branches. + if (size > 0) { + const std::string hexed = + "0x" + std::string(reinterpret_cast(data), size); + const folly::StringPiece hex(hexed); + (void)folly::tryTo(hex); + (void)folly::tryTo(hex); + } + + return 0; +} diff --git a/folly/fuzz/corpus/conv_fuzzer/bool.txt b/folly/fuzz/corpus/conv_fuzzer/bool.txt new file mode 100644 index 00000000000..f32a5804e29 --- /dev/null +++ b/folly/fuzz/corpus/conv_fuzzer/bool.txt @@ -0,0 +1 @@ +true \ No newline at end of file diff --git a/folly/fuzz/corpus/conv_fuzzer/float.txt b/folly/fuzz/corpus/conv_fuzzer/float.txt new file mode 100644 index 00000000000..0b169da80b7 --- /dev/null +++ b/folly/fuzz/corpus/conv_fuzzer/float.txt @@ -0,0 +1 @@ +3.14159265358979323846 \ No newline at end of file diff --git a/folly/fuzz/corpus/conv_fuzzer/int.txt b/folly/fuzz/corpus/conv_fuzzer/int.txt new file mode 100644 index 00000000000..f70d7bba4ae --- /dev/null +++ b/folly/fuzz/corpus/conv_fuzzer/int.txt @@ -0,0 +1 @@ +42 \ No newline at end of file diff --git a/folly/fuzz/corpus/conv_fuzzer/int64_min.txt b/folly/fuzz/corpus/conv_fuzzer/int64_min.txt new file mode 100644 index 00000000000..9af921bf678 --- /dev/null +++ b/folly/fuzz/corpus/conv_fuzzer/int64_min.txt @@ -0,0 +1 @@ +-9223372036854775808 \ No newline at end of file diff --git a/folly/fuzz/corpus/ipaddress_fuzzer/ipv4.txt b/folly/fuzz/corpus/ipaddress_fuzzer/ipv4.txt new file mode 100644 index 00000000000..3fc5c1775c1 --- /dev/null +++ b/folly/fuzz/corpus/ipaddress_fuzzer/ipv4.txt @@ -0,0 +1 @@ +192.168.1.1 \ No newline at end of file diff --git a/folly/fuzz/corpus/ipaddress_fuzzer/ipv4_cidr.txt b/folly/fuzz/corpus/ipaddress_fuzzer/ipv4_cidr.txt new file mode 100644 index 00000000000..814f80c29f7 --- /dev/null +++ b/folly/fuzz/corpus/ipaddress_fuzzer/ipv4_cidr.txt @@ -0,0 +1 @@ +10.0.0.0/8 \ No newline at end of file diff --git a/folly/fuzz/corpus/ipaddress_fuzzer/ipv6.txt b/folly/fuzz/corpus/ipaddress_fuzzer/ipv6.txt new file mode 100644 index 00000000000..3eb3c742efc --- /dev/null +++ b/folly/fuzz/corpus/ipaddress_fuzzer/ipv6.txt @@ -0,0 +1 @@ +2001:db8::1 \ No newline at end of file diff --git a/folly/fuzz/corpus/ipaddress_fuzzer/ipv6_cidr.txt b/folly/fuzz/corpus/ipaddress_fuzzer/ipv6_cidr.txt new file mode 100644 index 00000000000..9cbba473346 --- /dev/null +++ b/folly/fuzz/corpus/ipaddress_fuzzer/ipv6_cidr.txt @@ -0,0 +1 @@ +2001:db8::/32 \ No newline at end of file diff --git a/folly/fuzz/corpus/ipaddress_fuzzer/loopback.txt b/folly/fuzz/corpus/ipaddress_fuzzer/loopback.txt new file mode 100644 index 00000000000..e56ea71e3bc --- /dev/null +++ b/folly/fuzz/corpus/ipaddress_fuzzer/loopback.txt @@ -0,0 +1 @@ +127.0.0.1 \ No newline at end of file diff --git a/folly/fuzz/corpus/json_fuzzer/empty_object.json b/folly/fuzz/corpus/json_fuzzer/empty_object.json new file mode 100644 index 00000000000..9e26dfeeb6e --- /dev/null +++ b/folly/fuzz/corpus/json_fuzzer/empty_object.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/folly/fuzz/corpus/json_fuzzer/unicode_and_escapes.json b/folly/fuzz/corpus/json_fuzzer/unicode_and_escapes.json new file mode 100644 index 00000000000..e17fa9dfd37 --- /dev/null +++ b/folly/fuzz/corpus/json_fuzzer/unicode_and_escapes.json @@ -0,0 +1 @@ +{"unicode":"\u0048\u0065\u006C\u006C\u006F","escape":"line1\nline2\ttab","empty_str":""} \ No newline at end of file diff --git a/folly/fuzz/corpus/json_fuzzer/valid_array.json b/folly/fuzz/corpus/json_fuzzer/valid_array.json new file mode 100644 index 00000000000..d5e7166a45b --- /dev/null +++ b/folly/fuzz/corpus/json_fuzzer/valid_array.json @@ -0,0 +1 @@ +[1,2,3,"hello",false,null,{"nested":{"deep":{"value":0}}}] \ No newline at end of file diff --git a/folly/fuzz/corpus/json_fuzzer/valid_object.json b/folly/fuzz/corpus/json_fuzzer/valid_object.json new file mode 100644 index 00000000000..2fdef1077f8 --- /dev/null +++ b/folly/fuzz/corpus/json_fuzzer/valid_object.json @@ -0,0 +1 @@ +{"name":"folly","version":42,"active":true,"ratio":3.14,"tags":["fast","safe"],"meta":null} \ No newline at end of file diff --git a/folly/fuzz/corpus/uri_fuzzer/ftp.txt b/folly/fuzz/corpus/uri_fuzzer/ftp.txt new file mode 100644 index 00000000000..9a567777846 --- /dev/null +++ b/folly/fuzz/corpus/uri_fuzzer/ftp.txt @@ -0,0 +1 @@ +ftp://files.example.org/pub/file.tar.gz \ No newline at end of file diff --git a/folly/fuzz/corpus/uri_fuzzer/full_http.txt b/folly/fuzz/corpus/uri_fuzzer/full_http.txt new file mode 100644 index 00000000000..d8d3990d4ce --- /dev/null +++ b/folly/fuzz/corpus/uri_fuzzer/full_http.txt @@ -0,0 +1 @@ +http://user:pass@example.com:8080/path/to/res?key=val&flag=1#section \ No newline at end of file diff --git a/folly/fuzz/corpus/uri_fuzzer/simple_https.txt b/folly/fuzz/corpus/uri_fuzzer/simple_https.txt new file mode 100644 index 00000000000..a364175574b --- /dev/null +++ b/folly/fuzz/corpus/uri_fuzzer/simple_https.txt @@ -0,0 +1 @@ +https://example.com/ \ No newline at end of file diff --git a/folly/fuzz/ipaddress_fuzzer.cc b/folly/fuzz/ipaddress_fuzzer.cc new file mode 100644 index 00000000000..9f7d11e9083 --- /dev/null +++ b/folly/fuzz/ipaddress_fuzzer.cc @@ -0,0 +1,81 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * 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. + */ + +/** + * Fuzz target for folly::IPAddress, folly::IPAddressV4, and + * folly::IPAddressV6. + * + * Exercises: + * - folly::IPAddress::tryFromString() – non-throwing constructor + * - All classification predicates: isV4, isV6, isLoopback, isPrivate, + * isMulticast, isLinkLocal, isNonroutable, isZero + * - Serialization: str(), toFullyQualified() + * - CIDR network parsing via tryCreateNetwork() + * + * Security relevance: Malformed IP strings can trigger integer overflows + * or out-of-bounds reads in address-family detection, CIDR prefix parsing, + * and IPv4-in-IPv6 embedding logic. UBSan is especially effective here. + * + * OSS-Fuzz integration: https://github.com/google/oss-fuzz/tree/master/projects/folly + */ + +#include +#include +#include + +#include +#include + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + if (size > 4096) { + return 0; + } + + const folly::StringPiece input( + reinterpret_cast(data), size); + + // --- Plain address parsing (non-throwing) --- + auto addrResult = folly::IPAddress::tryFromString(input); + if (addrResult.hasValue()) { + const auto& addr = addrResult.value(); + (void)addr.str(); + (void)addr.toFullyQualified(); + (void)addr.isV4(); + (void)addr.isV6(); + (void)addr.isLoopback(); + (void)addr.isPrivate(); + (void)addr.isMulticast(); + (void)addr.isLinkLocal(); + (void)addr.isNonroutable(); + (void)addr.isZero(); + } + + // --- CIDR network parsing (non-throwing) e.g. "192.168.0.0/24" --- + try { + auto netResult = folly::IPAddress::tryCreateNetwork( + input, /*defaultCidr=*/-1, /*mask=*/false); + if (netResult.hasValue()) { + const auto& network = netResult.value(); + (void)network.first.str(); + (void)network.second; + (void)folly::IPAddress::networkToString(network); + } + } catch (...) { + abort(); + } + + return 0; +} diff --git a/folly/fuzz/json_fuzzer.cc b/folly/fuzz/json_fuzzer.cc new file mode 100644 index 00000000000..24d75d1b3a4 --- /dev/null +++ b/folly/fuzz/json_fuzzer.cc @@ -0,0 +1,63 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * 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. + */ + +/** + * Fuzz target for the Folly JSON parser and serializer. + * + * Exercises: + * - folly::parseJson() – arbitrary byte sequence → dynamic object + * - folly::toJson() – round-trip serialization back to a string + * - Re-parse invariant – serialized output must always be valid JSON + * + * Security relevance: JSON parsers are a common attack surface for + * integer overflows, out-of-bounds reads, stack overflows from deeply + * nested structures, and denial-of-service via large/malformed inputs. + * + * OSS-Fuzz integration: https://github.com/google/oss-fuzz/tree/master/projects/folly + */ + +#include +#include +#include +#include + +#include +#include +#include + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + if (size > 4096) { + return 0; + } + + const folly::StringPiece input( + reinterpret_cast(data), size); + + folly::json::serialization_opts opts; + opts.recursion_limit = 100; + + try { + auto parsed = folly::parseJson(input, opts); + const std::string serialized = folly::toJson(parsed); + (void)folly::parseJson(serialized, opts); + } catch (const folly::json::parse_error&) { + return 0; + } catch (...) { + abort(); + } + + return 0; +} diff --git a/folly/fuzz/uri_fuzzer.cc b/folly/fuzz/uri_fuzzer.cc new file mode 100644 index 00000000000..7060addd0fe --- /dev/null +++ b/folly/fuzz/uri_fuzzer.cc @@ -0,0 +1,73 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * 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. + */ + +/** + * Fuzz target for folly::Uri. + * + * Exercises: + * - folly::Uri::tryFromString() – non-throwing parse of arbitrary bytes + * - All field accessors: scheme, username, password, host, hostname, + * port, path, query, fragment, authority + * - str() serialization + * - getQueryParams() – key/value decomposition of the query string + * + * Security relevance: URI parsers are frequently exploited for + * path-traversal, SSRF, credential leakage via crafted authority + * components, and integer overflow in port-number parsing. + * + * OSS-Fuzz integration: https://github.com/google/oss-fuzz/tree/master/projects/folly + */ + +#include +#include +#include + +#include +#include + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + if (size > 4096) { + return 0; + } + + const folly::StringPiece input( + reinterpret_cast(data), size); + + auto result = folly::Uri::tryFromString(input); + if (!result.hasValue()) { + return 0; + } + + try { + auto& uri = result.value(); + (void)uri.scheme(); + (void)uri.username(); + (void)uri.password(); + (void)uri.host(); + (void)uri.hostname(); + (void)uri.port(); + (void)uri.path(); + (void)uri.query(); + (void)uri.fragment(); + (void)uri.authority(); + (void)uri.str(); + (void)uri.getQueryParams(); + } catch (...) { + abort(); + } + + return 0; +}