libpcap: fix timeval on multiple platforms leading to a crash - #5209
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #5209 +/- ##
==========================================
- Coverage 81.16% 81.14% -0.02%
==========================================
Files 393 393
Lines 98120 98316 +196
==========================================
+ Hits 79637 79779 +142
- Misses 18483 18537 +54 🚀 New features to boost your workflow:
|
scapy can manually trigger Fedora builds on various architectures including 32-bit i386 and big-endian s390x machines. That being said it seems it started failing when 59f9a0e was merged (https://download.copr.fedorainfracloud.org/results/packit/evverx-scapy-2/fedora-rawhide-x86_64/11055396-scapy/builder-live.log.gz). I'll try to take a look tomorrow. I'm far from the keyboard where I can look into that properly. |
62cc2bf to
ed0df76
Compare
|
Pushed an update:
The IPv6ExtHdrSegmentRouting failure in every job comes from #5207 on master, not from this PR. @evverx thanks, I didn't know about the Fedora builds. A 32-bit i386 run would exercise the new test. |
The timestamp at the start of struct pcap_pkthdr was declared as two C longs. Where time_t is 64-bit but long is 32-bit (NetBSD, musl, 64-bit-time glibc), caplen and len were read from the wrong offsets, so reading caplen copied up to 999,999 bytes per packet. OpenBSD's libpcap uses its own struct bpf_timeval of two u_int32_t instead. AI-Assisted: yes
ed0df76 to
3beafd2
Compare
|
Not a huge fan of the code, so I made an alternative (much simpler) version: #5211 |
|
Tested #5211 and replied there: #5211 (comment). I'll close this one once it's merged. |
|
You're right, my code just doesn't work.. I missed the musl case which kind of makes my approach wrong. I've removed the parts that were supposed to fix this issue to just keep the cleanups, let's keep this open. |
| if WINDOWS: | ||
| _time_t = _timeval_usec_t = c_long | ||
| elif OPENBSD: | ||
| _time_t = _timeval_usec_t = c_uint32 |
There was a problem hiding this comment.
It isn't obvious from the commit message but this change makes scapy actually work in libpcap mode on 64-bit OpenBSD machines.
I'm still trying to wrap my head around the code but in the meantime I tested it on 32/64-bit NetBSD, 32/64-bit FreeBSD, 64-bit OpenBSD and it seems to work (I'm not sure what happens on illumos yet though). I also ran the testsuite with this commit included in libpcap mode with netaccess enabled on Fedora on i386, s390x,aarch64, ppc64le and arm64 and it passed there (apart from some failures caused by the firewall blocking whois things and things like that on Packit).
There was a problem hiding this comment.
illumos comes with time_t and long so it should work there too (I tested it on OmniOS). It would be cool if it was possible to detect possible time_t python/libpcap mismatches (other than segfaults at runtime) but I can't come up with anything.
AI-Assisted: no
34e42ee to
db38571
Compare
AI-Assisted: no
402e07a to
32e60df
Compare
|
/packit build |
Description
Fixes the segfault @evverx reported in #5122 on 32-bit NetBSD after the GHSA-c547-xwrv-q9jm fix.
struct pcap_pkthdrstarts with astruct timeval, and Scapy declared it as two Clongs (scapy/libs/winpcapy.py:63on master). Wheretime_tis 64-bit butlongis 32-bit, the real timestamp is bigger than that. Socaplenandlen, which come right after it, were read from the wrong place:lenwas the realcaplen, and itscaplenwas the timestamp's microseconds. Before the GHSA fix the packet bytes happened to be right. After it, each packet is copied with a length between 0 and 999,999, which crashessniff.This PR declares the timestamp with the platform's real sizes (
scapy/libs/winpcapy.py:65-96):ctypes.c_time_ton Python 3.12+. On older Pythons,time.gmtime(2 ** 31)tells us whether this build'stime_tis 64-bit;inton NetBSD and macOS, as wide as the seconds on Linux, andlongelsewhere;longs, and OpenBSD uses twou_int32_t: its libpcap declares its ownstruct bpf_timeval(lib/libpcap/pcap.h:93,sys/net/bpf.h:143).This assumes Python and libpcap use the same
time_t, as a distribution's packages do.Test
Native libpcap capture reads the header as libpcap lays it out(test/regression.uts:315) reads a pcap file through libpcap itself, so a wrong layout shows up as a wrongcaplen. The existing test above it builds the header with Scapy's own struct, so it passes whatever the layout is. The new test uses a 1 µs timestamp so a misread fails cleanly rather than crashing. It is taggedlibpcaponly, so the BSD configs run it too.Checked with real libpcap, before and after this PR:
longis 32 bits there)On Python 3.7 to 3.13 (official
pythonimages, glibc and musl, amd64, i386 and armv7), the fallback andc_time_tpicked the right size in all 35 combinations.The new test fails on master on the NetBSD, musl and armhf targets above, and passes on all of them with this PR. Scapy's CI is 64-bit only, so it cannot fail there.
Not changed here
On macOS, Apple's
pcap_pkthdrhas an extrachar comment[256]afterlen(280 bytes, where Scapy's is 24). Capture reads the header throughpcap_next_ex, which is unaffected, and nothing in Scapy callspcap_nextorpcap_dump, the two functions where it would matter. I've left it out so this stays a small fix; happy to add it.