fix(fuzz): build a minimal libpcap for oss-fuzz targets instead of linking the system one (#1092) - #1117
Merged
Merged
Conversation
…nking the system one (#1092) Found by the actual OSS-Fuzz build matrix, not locally: every target across every engine/sanitizer combination failed at startup with error while loading shared libraries: libpcap.so.0.8: cannot open shared object file: No such file or directory The build itself succeeded - the OSS-Fuzz *builder* image has libpcap.so, since that's what apt installed it there for. The separate *runner* image, where the built binaries actually execute, does not: it installs libcap2 only (per upstream infra/base-images/base-runner/install_deps.sh), not libpcap. A build that reports success but produces nothing that can run is worse than one that fails outright. Statically linking the system libpcap.a doesn't fix this, it only moves the missing library: Debian/Ubuntu's libpcap is built with D-Bus, Bluetooth and USB-sniffing support enabled, so the static archive itself pulls in libdbus-1 and libsystemd (which in turn needs libcap's cap_* symbols) - verified locally by trying exactly that and watching the missing-library chase move one library at a time. None of that capture-backend code is reachable from reading a pcap file, which is all these targets ever do, so it's built out entirely: build.sh now clones and builds its own libpcap from source with -DDISABLE_DBUS/DISABLE_BLUETOOTH/DISABLE_LINUX_USBMON/DISABLE_RDMA, matching what the actual libpcap OSS-Fuzz project's own build.sh already does for this same reason. The result depends on nothing beyond libc/libstdc++/libm, verified with ldd against all three targets. The Dockerfile (submitted separately to google/oss-fuzz, not tracked in this repo per test/fuzz/README.md) gains a second git clone of tcpdump-group's libpcap plus cmake/flex/bison to build it; libpcap-dev stays for tcpreplay's own normal build, which still uses the system package as it always has - only the fuzz targets' *link* step changes. Verified end-to-end against a full simulated OSS-Fuzz environment (a fake $SRC with both tcpreplay and a fresh libpcap clone, exactly what the Dockerfile produces): all three targets build, `ldd` shows only libc/libstdc++/libm/libgcc_s/libresolv, and each passes OSS-Fuzz's own bad_build_check invocation (-rss_limit_mb=2560 -timeout=25 -seed=1337 -runs=4 < /dev/null), which is the exact check that was failing.
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.
Fixing this ahead of a resubmission to OSS-Fuzz - found by the actual OSS-Fuzz build matrix (google/oss-fuzz#15924), not locally.
The failure
Every target across every engine/sanitizer combination OSS-Fuzz tried failed at startup:
The build itself succeeded. OSS-Fuzz's separate builder and runner images are the reason: the builder has
libpcap.sobecause that's whatapt-get install libpcap-devput there, but the runner - where the built binaries actually execute duringcheck_build/fuzzing - installslibcap2only (per upstreaminfra/base-images/base-runner/install_deps.sh), notlibpcap. A build that reports success but produces nothing that can run is worse than one that fails outright.Why simply linking libpcap statically doesn't work
I tried that first. It doesn't fix anything - it just moves the missing library. Debian/Ubuntu's
libpcapis built with D-Bus, Bluetooth, and USB-sniffing support enabled, so the static archive pulls inlibdbus-1andlibsystemd(which itself needslibcap'scap_*symbols). Verified locally by trying exactly that and watching the missing-shared-library error move fromlibpcap.sotolibdbus-1.soto acap_get_proclink failure, one hop at a time.The actual fix
None of that capture-backend code (D-Bus, Bluetooth, USB) is reachable from reading a pcap file, which is all these targets ever do.
build.shnow clones and builds its own libpcap from source with-DDISABLE_DBUS=ON -DDISABLE_BLUETOOTH=ON -DDISABLE_LINUX_USBMON=ON -DDISABLE_RDMA=ON- matching what the actuallibpcapOSS-Fuzz project's ownbuild.shalready does, for this same reason. The result depends on nothing beyond libc/libstdc++/libm.libpcap-devstays in the Dockerfile for tcpreplay's own normal build (which still links the system package, unchanged) - only the fuzz targets' link step now uses the self-built copy.Verification
Built a fake
$SRClocally with both a fresh tcpreplay checkout and a fresh libpcap clone - exactly what the Dockerfile produces - and ran the actual updatedbuild.shagainst it with$CC=clang/$CXX=clang++/ASan+UBSan+libFuzzer flags:lddon each shows onlylibstdc++/libm/libresolv/libgcc_s/libc/ld-linux- nolibpcap.so,libdbus-1, orlibsystemd.bad_build_checkinvocation exactly:-rss_limit_mb=2560 -timeout=25 -seed=1337 -runs=4 < /dev/null→ exit 0. That's the precise check that was failing in the real CI run.🤖 Generated with Claude Code
https://claude.ai/code/session_01NBmWiWg46r8BLbdwozKo6v