Skip to content

sniff: reap the tcpdump prefilter subprocess in offline mode#5046

Open
eugen-goebel wants to merge 1 commit into
secdev:masterfrom
eugen-goebel:fix-tcpdump-zombie-getproc
Open

sniff: reap the tcpdump prefilter subprocess in offline mode#5046
eugen-goebel wants to merge 1 commit into
secdev:masterfrom
eugen-goebel:fix-tcpdump-zombie-getproc

Conversation

@eugen-goebel

Copy link
Copy Markdown
Contributor

Problem

sniff(offline="file.pcap", filter="tcp") prefilters packets through a tcpdump subprocess. The offline path built PcapReader(tcpdump(..., getfd=True)), which returns only the stdout pipe, so the Popen object was discarded and never wait()ed. The process was left as a zombie, and Python 3 reports:

ResourceWarning: subprocess NNNNN is still running

Reproduction on current master:

from scapy.all import sniff, wrpcap, IP, TCP
wrpcap("/tmp/t.pcap", [IP()/TCP()] * 5)
sniff(offline="/tmp/t.pcap", filter="tcp")   # leaves a <defunct> tcpdump child

Fix

As discussed in the issue, the reader now takes the process via getproc=True and owns its lifetime. RawPcapReader.close() closes the read pipe, sends a still-running tcpdump a SIGTERM, and wait()s for it, so nothing is left behind. This also covers stopping early (count=, timeout=), where tcpdump has not exited on its own.

Thanks to @AbhishekPandey1998 for scoping the getproc approach in the thread.

Test

Added a regression test in test/regression.uts that reads a filtered offline capture (a full read and an early count= stop) and asserts no subprocess ... still running ResourceWarning is emitted.

Fixes #4512

sniff(offline=..., filter=...) prefilters packets through a tcpdump
subprocess, but the code kept only its stdout pipe and discarded the
Popen. The process was never waited on, so it lingered as a zombie and
Python emitted "ResourceWarning: subprocess N is still running".

Get the process with getproc=True, attach it to the PcapReader, and reap
it in close(): the read pipe is closed first, a still-running tcpdump then
gets a SIGTERM, and wait() collects it. This also handles early stops
(count=, timeout=), where tcpdump has not finished on its own.

Fixes secdev#4512

AI-Assisted: yes (Claude Opus 4.8)
@codecov

codecov Bot commented Jul 17, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 92.85714% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 80.22%. Comparing base (de33992) to head (6cd745a).

Files with missing lines Patch % Lines
scapy/sendrecv.py 88.88% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #5046      +/-   ##
==========================================
+ Coverage   80.13%   80.22%   +0.08%     
==========================================
  Files         388      388              
  Lines       96467    96479      +12     
==========================================
+ Hits        77308    77397      +89     
+ Misses      19159    19082      -77     
Files with missing lines Coverage Δ
scapy/utils.py 72.30% <100.00%> (+0.11%) ⬆️
scapy/sendrecv.py 87.26% <88.88%> (+1.22%) ⬆️

... and 11 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Zombie tcpdump Process After Using sniff() on pcap with Filter

1 participant