Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions .github/workflows/cifuzz.yml
Original file line number Diff line number Diff line change
@@ -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
10 changes: 10 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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()
85 changes: 85 additions & 0 deletions folly/fuzz/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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<T>)
folly_fuzz_target(conv_fuzzer)
44 changes: 44 additions & 0 deletions folly/fuzz/README.md
Original file line number Diff line number Diff line change
@@ -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<T>()` — 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.
81 changes: 81 additions & 0 deletions folly/fuzz/conv_fuzzer.cc
Original file line number Diff line number Diff line change
@@ -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<T>() string-to-scalar conversions (Conv.h).
*
* Exercises:
* - tryTo<T>() for all signed/unsigned integer types and bool
* - tryTo<float>() and tryTo<double>()
*
* 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 <cstddef>
#include <cstdint>
#include <string>

#include <folly/Conv.h>
#include <folly/Range.h>

extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
if (size > 4096) {
return 0;
}

const folly::StringPiece sp(
reinterpret_cast<const char*>(data), size);

// Test the raw input against every scalar type.
(void)folly::tryTo<int8_t>(sp);
(void)folly::tryTo<int16_t>(sp);
(void)folly::tryTo<int32_t>(sp);
(void)folly::tryTo<int64_t>(sp);
(void)folly::tryTo<uint8_t>(sp);
(void)folly::tryTo<uint16_t>(sp);
(void)folly::tryTo<uint32_t>(sp);
(void)folly::tryTo<uint64_t>(sp);
(void)folly::tryTo<float>(sp);
(void)folly::tryTo<double>(sp);
(void)folly::tryTo<bool>(sp);

// Negate the input to exercise negative-number and sign-handling paths.
if (size > 0) {
const std::string negated =
"-" + std::string(reinterpret_cast<const char*>(data), size);
const folly::StringPiece neg(negated);
(void)folly::tryTo<int64_t>(neg);
(void)folly::tryTo<double>(neg);
}

// Prepend "0x" to hit hexadecimal parsing branches.
if (size > 0) {
const std::string hexed =
"0x" + std::string(reinterpret_cast<const char*>(data), size);
const folly::StringPiece hex(hexed);
(void)folly::tryTo<uint64_t>(hex);
(void)folly::tryTo<int64_t>(hex);
}

return 0;
}
1 change: 1 addition & 0 deletions folly/fuzz/corpus/conv_fuzzer/bool.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
true
1 change: 1 addition & 0 deletions folly/fuzz/corpus/conv_fuzzer/float.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.14159265358979323846
1 change: 1 addition & 0 deletions folly/fuzz/corpus/conv_fuzzer/int.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
42
1 change: 1 addition & 0 deletions folly/fuzz/corpus/conv_fuzzer/int64_min.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
-9223372036854775808
1 change: 1 addition & 0 deletions folly/fuzz/corpus/ipaddress_fuzzer/ipv4.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
192.168.1.1
1 change: 1 addition & 0 deletions folly/fuzz/corpus/ipaddress_fuzzer/ipv4_cidr.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
10.0.0.0/8
1 change: 1 addition & 0 deletions folly/fuzz/corpus/ipaddress_fuzzer/ipv6.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
2001:db8::1
1 change: 1 addition & 0 deletions folly/fuzz/corpus/ipaddress_fuzzer/ipv6_cidr.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
2001:db8::/32
1 change: 1 addition & 0 deletions folly/fuzz/corpus/ipaddress_fuzzer/loopback.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
127.0.0.1
1 change: 1 addition & 0 deletions folly/fuzz/corpus/json_fuzzer/empty_object.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
1 change: 1 addition & 0 deletions folly/fuzz/corpus/json_fuzzer/unicode_and_escapes.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"unicode":"\u0048\u0065\u006C\u006C\u006F","escape":"line1\nline2\ttab","empty_str":""}
1 change: 1 addition & 0 deletions folly/fuzz/corpus/json_fuzzer/valid_array.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[1,2,3,"hello",false,null,{"nested":{"deep":{"value":0}}}]
1 change: 1 addition & 0 deletions folly/fuzz/corpus/json_fuzzer/valid_object.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"name":"folly","version":42,"active":true,"ratio":3.14,"tags":["fast","safe"],"meta":null}
1 change: 1 addition & 0 deletions folly/fuzz/corpus/uri_fuzzer/ftp.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ftp://files.example.org/pub/file.tar.gz
1 change: 1 addition & 0 deletions folly/fuzz/corpus/uri_fuzzer/full_http.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
http://user:pass@example.com:8080/path/to/res?key=val&flag=1#section
1 change: 1 addition & 0 deletions folly/fuzz/corpus/uri_fuzzer/simple_https.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
https://example.com/
Loading