Add sanitizer and fuzzing support with CI coverage#144
Merged
Conversation
Adds two CMake options, both off by default so the existing build and CI matrix are unaffected: - NETCODE_SANITIZE: builds everything with AddressSanitizer + UndefinedBehaviorSanitizer. The vendored sodium subset is exempted from UBSan (third-party SIMD crypto uses intentional type punning / unaligned access) but still gets ASan. - NETCODE_FUZZ: builds the fuzz/ harnesses. Where the compiler ships libFuzzer (LLVM clang) they are libFuzzer targets; elsewhere they build as a standalone file replayer so they compile everywhere and crashes reproduce anywhere. Fuzz harnesses cover netcode's untrusted-input surface, each with write/read round-trip invariants: - fuzz_read_packet: netcode_read_packet (the socket packet reader), both raw input and encrypted round-trip paths including the connect token request. - fuzz_connect_token: the public and private connect token readers. - fuzz_parse_address: netcode_parse_address plus a to_string round-trip. CI gains a Linux ASan+UBSan leg (builds all, runs ctest) and a bounded smoke-fuzz leg (60s per target, uploads any crash inputs as an artifact). BUILDING.md and fuzz/README.md document usage. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
netcode_replay_protection_already_received computed "sequence + NETCODE_REPLAY_PROTECTION_BUFFER_SIZE <= most_recent_sequence", which wraps for sequence values in the top 256 of the uint64 space and falsely rejects legitimate packets as replays. Not reachable in practice (sequences start at 0 / 1<<63 and increment per packet), but wrong, and in security-relevant code. Found by fuzz_read_packet's round-trip invariant within the first minute of the first CI smoke-fuzz run, on input sequence 0xFFFFFFFFFFFFFF00. The check is now written subtraction-side so it cannot overflow, and test_replay_protection covers the top of the sequence space, including that replays up there are still caught. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.
Summary
Hardens netcode's hostile-input surface with sanitizers and fuzzing, wired into CI. Both CMake options are off by default, so the existing 6-cell build matrix is unchanged.
CMake options
NETCODE_SANITIZE— builds everything with AddressSanitizer + UndefinedBehaviorSanitizer. The vendored sodium subset is exempted from UBSan (third-party SIMD crypto uses intentional type punning / unaligned access that UBSan flags but isn't netcode's to fix) but still gets ASan.NETCODE_FUZZ— builds thefuzz/harnesses. Where the compiler ships libFuzzer (LLVM clang) they are libFuzzer targets; elsewhere (AppleClang, GCC, MSVC) they build as a standalone file replayer, so the harnesses compile everywhere and a crashing input reproduces anywhere.Fuzz harnesses (
fuzz/)Cover the code paths that parse untrusted bytes, each with a write/read round-trip invariant:
fuzz_read_packet—netcode_read_packet, the reader every client/server runs on received datagrams; both raw-input and encrypted round-trip paths, including the connect-token request path (token encrypted with the real private key so the full decrypt+read is exercised).fuzz_connect_token— the public and private connect token readers, plus a private-token round-trip.fuzz_parse_address—netcode_parse_addressplus ato_string→parseround-trip.CI
Two new jobs on
ubuntu-24.04with clang:-DNETCODE_SANITIZE=ON, build all, run ctest withhalt_on_error.crash-*/oom-*/timeout-*inputs as an artifact on failure.Test plan
NETCODE_SANITIZE=ON: builds clean, all tests pass under ASan+UBSan locally (Apple Silicon)NETCODE_FUZZ=ON: all three harnesses build (standalone fallback on AppleClang) and run clean over ~200 random inputs under ASan/UBSan🤖 Generated with Claude Code