Add tcpreplay project - #15924
Conversation
tcpreplay replays and rewrites captured network traffic (pcap files). 15
security advisories were published against it in 2026, every one of them
found by an outside researcher rather than the project itself - the bug
classes were exactly what a fuzzer finds quickly (off-by-one writes,
unchecked attacker-supplied lengths, sign confusion, unbounded copies), and
there was no fuzzing infrastructure in the repository at all.
Three libFuzzer harnesses, covering the surfaces behind most of that history:
fuzz_pcap the libpcap read path + shared header parsers, DLT chosen
by the input so one target reaches the whole plugin matrix
fuzz_fragroute the fragroute rules-file parser, and then runs a packet
through the module chain it builds - several of the past
advisories are in the modules' apply paths, not the parser
fuzz_services tcpprep's --services parser
The harnesses are also built as a plain standalone corpus-replay program
(no libFuzzer/clang needed) and run under `make check` on every build of the
project, so a crash OSS-Fuzz finds and gets committed to the corpus becomes
a permanent regression test for everyone, not just people with a fuzzing
toolchain installed.
Branch pinned to 4.6.1-beta1 rather than the default branch: the fuzz
targets haven't reached the project's master yet (see the comment in
project.yaml). Verified build.sh manually against a fresh checkout with
$CC=clang $CXX=clang++ and the ASan+UBSan+libFuzzer flags this environment
would supply - all three targets build, run cleanly against the checked-in
corpus, and ran several million iterations apiece in a few seconds with no
crashes. `infra/presubmit.py` passes clean.
|
Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA). View this failed invocation of the CLA check for more information. For the most up to date status, view the checks section at the bottom of the pull request. |
|
fklassen is integrating a new project: |
DavidKorczynski
left a comment
There was a problem hiding this comment.
Better late than never :D #5546
Project looks good, can you sign the CLA?
| main_repo: "https://github.com/appneta/tcpreplay.git" | ||
| language: c | ||
| primary_contact: "tcpreplay.dev@gmail.com" | ||
| file_github_issue: true |
There was a problem hiding this comment.
this means a public github issue will be created once an issue is found, potentially disclosing security vulns. Are you sure you want this?
There was a problem hiding this comment.
If you word it that way, maybe this isn’t what I want. Let me discuss this with others.
There was a problem hiding this comment.
Good catch, fixed - set to false. Findings will route to primary_contact by email instead. Thanks for the review!
The initial submission's build succeeded, but every target failed at
startup in the runner image:
error while loading shared libraries: libpcap.so.0.8: cannot open
shared object file: No such file or directory
The base-builder image has libpcap.so (apt installed it there); the
runner image, where check_build/fuzzing actually execute the binary,
installs libcap2 only, not libpcap.
Statically linking the system libpcap.a doesn't fix it, it just moves the
missing library one hop: Debian/Ubuntu's package is built with D-Bus,
Bluetooth and USB-sniffing support, so the static archive itself pulls in
libdbus-1 and libsystemd (which needs libcap's cap_* symbols in turn) -
verified by trying exactly that.
None of that capture-backend code is reachable from reading a pcap file,
which is all these targets do, so it's built out entirely: build.sh now
clones and builds its own libpcap with
-DDISABLE_DBUS/DISABLE_BLUETOOTH/DISABLE_LINUX_USBMON/DISABLE_RDMA - the
same thing the libpcap project's own OSS-Fuzz integration already does,
for this same reason. The result depends on nothing beyond
libc/libstdc++/libm - verified with ldd against all three targets, and
each passes check_build's exact invocation
(-rss_limit_mb=2560 -timeout=25 -seed=1337 -runs=4 < /dev/null).
libpcap-dev/cmake/flex/bison added to the Dockerfile (cmake/flex/bison to
build libpcap from source; libpcap-dev unchanged, still used by
tcpreplay's own normal build).
Flagged in review by David Korczynski: file_github_issue: true means a public GitHub issue gets filed on appneta/tcpreplay the instant a crash is found, before anyone there has had a chance to look at it privately. For a project whose whole reason for adding fuzzing is 15 externally reported CVEs in one year, that's the wrong default. Findings now route to primary_contact by email instead.
appneta#1092) Flagged in review on the submission PR (google/oss-fuzz#15924) by a project maintainer: file_github_issue: true means OSS-Fuzz auto-files a *public* GitHub issue on this repo the instant it finds a crash. For a project whose entire reason for adding fuzzing is 15 externally reported CVEs in one year, that's backwards - it discloses a vulnerability, potentially with a reproducer attached, before anyone here has had a chance to look at it privately. Set to false. Findings now route to primary_contact by email instead, which is what OSS-Fuzz's own responsible-disclosure timeline (private first, then public after a fix ships or 90 days pass) actually assumes.
tcpreplay replays and rewrites captured network traffic (pcap files): https://github.com/appneta/tcpreplay
Motivation
15 security advisories were published against it in 2026, every one found by an outside researcher rather than the project itself. The bug classes are what a fuzzer finds quickly - off-by-one writes, unchecked attacker-supplied lengths, sign confusion, unbounded copies - and there was no fuzzing infrastructure in the repository at all.
The three targets
fuzz_pcapfuzz_fragroutefuzz_servicestcpprep's--servicesparserThey're also built as a plain standalone corpus-replay program (no libFuzzer/clang needed) and run under
make checkon every build of the project, so a crash this finds and gets committed to the corpus becomes a permanent regression test for everyone, not just people with a fuzzing toolchain installed.One thing worth flagging
The Dockerfile pins
git clone -b 4.6.1-beta1, not the default branch - the fuzz targets haven't reached the project'smasteryet (they landed this cycle). Noted inproject.yaml's own comment for whoever revisits it: drop the branch pin once that release merges tomaster.Verification
No Docker available in the environment I built this in, so I couldn't run
infra/helper.py build_fuzzers/check_builddirectly. Instead:build.shmanually against a fresh checkout with$CC=clang $CXX=clang++and$CFLAGS/$CXXFLAGS/$LIB_FUZZING_ENGINEset to what this environment supplies (ASan+UBSan,-fsanitize=fuzzer). All three targets built and linked cleanly.python3 infra/presubmit.py(license, project.yaml schema,$LIB_FUZZING_ENGINEusage, seed-corpus, apt-update-before-install) passes clean.Happy to iterate based on your own CI if the actual OSS-Fuzz build environment surfaces anything this couldn't catch.