Phase 7d: fuzz harnesses + fix fuzzer-found CBOR length overflow - #19
Merged
Merged
Conversation
libFuzzer harnesses for the untrusted-byte parsers — JSON decode, CBOR decode, URI percent-decode/encode, and full generated-server request dispatch. Each builds two ways: a deterministic-driver cc_test that runs in every CI job (including ASan/UBSan, no fuzzer runtime needed) and a real libFuzzer binary under --config=fuzz that the new `fuzz` CI job runs 30s per harness on every PR. The CBOR harness immediately earned its keep: DecodeChunkedString checked bounds with `pos_ + length > size_`, but length is an attacker-controlled CBOR argument up to 2^64-1, so the addition overflowed size_t and wrapped past the guard — then string::assign(ptr, length) threw std::length_error (an unhandled crash, and an OOB read on inputs that don't happen to throw). Fixed to the overflow-safe `length > size_ - pos_` (pos_ <= size_ is invariant) on both the definite and indefinite-chunk paths, with a direct cbor_test regression pinning huge text/byte/indefinite lengths. docs/fuzzing.md documents the harness pattern and invariants. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WjaNFwBZxoHdqagvq8ycQf
--config=fuzz //fuzz:all also builds the *_fuzz_smoke cc_tests, which link the deterministic driver's main(); --config=fuzz injects libFuzzer's own main(), so they collide with a duplicate-symbol link error. Build each //fuzz:<name>_fuzz binary explicitly instead (the smoke tests keep running in the plain bazel matrix). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WjaNFwBZxoHdqagvq8ycQf
__builtin_trap() is a GCC/Clang builtin MSVC doesn't provide, so the server_dispatch and uri smoke-test builds failed on windows-msvc. std::abort() (<cstdlib>) is standard and does the same job: fail loud on an invariant violation for libFuzzer/ASan to catch. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WjaNFwBZxoHdqagvq8ycQf
…UTF-8
The server_dispatch fuzzer found that smithy::json::Encode terminated
the process on any string containing invalid UTF-8. nlohmann's dump()
defaults to error_handler_t::strict, which throws type_error.316 on
malformed UTF-8; nothing catches it, so std::terminate aborts the
server. Raw bytes reach Encode routinely — @httpLabel segments
(especially greedy {path+}), header values, and blobs get echoed into
response fields — so a single crafted request (e.g. GET /reports/...
with a 0xe1 byte) is a remote denial of service against any restJson1
server.
Encode now dumps with error_handler_t::replace: invalid sequences
become U+FFFD, output stays valid JSON, and the process survives.
Valid UTF-8 (including multibyte) is unaffected. Direct json_test
regression plus the fuzz corpus confirm it.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WjaNFwBZxoHdqagvq8ycQf
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
First Phase 7d slice: libFuzzer harnesses for the parsers that face untrusted bytes — and a real memory-safety bug the CBOR harness found on its first run.
The bug (fixed here)
Decoder::DecodeChunkedStringbounds-checked withpos_ + length > size_, butlengthis a CBOR-supplied argument up to 2^64−1. On a 64-bit host the addition overflowssize_tand wraps to a small value, sailing past the guard — thenstd::string::assign(ptr, length)throwsstd::length_error(an unhandled-exception crash) or, for lengths that don't happen to throw, reads far out of bounds. Any peer sending a malformed CBOR string header could crash an rpcv2Cbor server.Fixed to the overflow-safe
length > size_ - pos_(thepos_ <= size_invariant is maintained by every read path) on both the definite-length and indefinite-chunk branches, with a directcbor_testregression (7bffffffffffffffffand friends) so it stays caught without the fuzzer.Harnesses
fuzz/covers JSON decode, CBOR decode, URI percent-decode/encode, and full generated-server request dispatch. Each is written once and built two ways:*_fuzz_smokecc_tests): ~20k seeded pseudo-random inputs through the same entry point, running in every CI job including the ASan/UBSan matrix — reproducible, no fuzzer runtime.*_fuzzbinaries,--config=fuzz, clang-fsanitize=fuzzer,address): the newfuzzCI job runs each 30s per PR.Invariants enforced: decoders never crash/throw and round-trip whatever they accept;
PercentDecoderound-trips and the encoders accept arbitrary bytes; server dispatch always returns a valid HTTP status for any request. Local soak runs (35s each) after the fix are clean across all four (380k–610k execs).Docs
docs/fuzzing.md— the harness pattern, how to soak/reproduce locally, and the invariants. Seed corpus + OSS-Fuzz remain post-0.1.0.Validation
bazel test //...— 51/51 green, plus ASan+UBSan on the CBOR + fuzz targets🤖 Generated with Claude Code
https://claude.ai/code/session_01WjaNFwBZxoHdqagvq8ycQf
Generated by Claude Code