Skip to content

Commit bf1bcb2

Browse files
authored
Add a patch for scapy to fix fd leak issue in AsyncSniffer (#20415)
Why I did it This PR is to add a patch to fix potential fd leak issue in AsyncSniffer in scapy python library. There are two fd leak scenarios. When starting worker thread _run, if an interface is down, an OSError is thrown, and the sockets that have been created will be leaked as it never got a chance to be closed. When stopping the worker thread, same error can happen when calling close. The sockets not closed will be leaked. How I did it Catch OSError when creating sockets, and catch any exception when closing socket to ensure all sockets are closed. How to verify it Verified by the testing code above. No fd leak happened.
1 parent 6c05500 commit bf1bcb2

File tree

2 files changed

+48
-0
lines changed

2 files changed

+48
-0
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
diff --git a/scapy/sendrecv.py b/scapy/sendrecv.py
2+
index f97fc415..dbcc372f 100644
3+
--- a/scapy/sendrecv.py
4+
+++ b/scapy/sendrecv.py
5+
@@ -1111,17 +1111,19 @@ class AsyncSniffer(object):
6+
# The _RL2 function resolves the L2socket of an iface
7+
_RL2 = lambda i: L2socket or resolve_iface(i).l2listen() # type: Callable[[_GlobInterfaceType], Callable[..., SuperSocket]] # noqa: E501
8+
if isinstance(iface, list):
9+
- sniff_sockets.update(
10+
- (_RL2(ifname)(type=ETH_P_ALL, iface=ifname, **karg),
11+
- ifname)
12+
- for ifname in iface
13+
- )
14+
+ for ifname in iface:
15+
+ try:
16+
+ sniff_sockets.update({_RL2(ifname)(type=ETH_P_ALL, iface=ifname, **karg): ifname})
17+
+ except OSError:
18+
+ # Ignore OSError when opening the socket
19+
+ # The error can happen when the port goes down during the creation of the socket
20+
+ pass
21+
elif isinstance(iface, dict):
22+
- sniff_sockets.update(
23+
- (_RL2(ifname)(type=ETH_P_ALL, iface=ifname, **karg),
24+
- iflabel)
25+
- for ifname, iflabel in six.iteritems(iface)
26+
- )
27+
+ for ifname, iflabel in six.iteritems(iface):
28+
+ try:
29+
+ sniff_sockets.update({_RL2(ifname)(type=ETH_P_ALL, iface=ifname, **karg): iflabel})
30+
+ except OSError:
31+
+ pass
32+
else:
33+
iface = iface or conf.iface
34+
sniff_sockets[_RL2(iface)(type=ETH_P_ALL, iface=iface,
35+
@@ -1221,7 +1223,11 @@ class AsyncSniffer(object):
36+
self.running = False
37+
if opened_socket is None:
38+
for s in sniff_sockets:
39+
- s.close()
40+
+ try:
41+
+ s.close()
42+
+ except Exception:
43+
+ # Ignore exceptions to ensure all sockets are closed
44+
+ pass
45+
elif close_pipe:
46+
close_pipe.close()
47+
self.results = session.toPacketList()

src/scapy.patch/series

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
0001-Fix-version-string-generation-when-scapy-is-a-submod.patch
22
0002-Check-if-the-network-interface-still-exists.patch
33
0003-Do-not-resolve-the-interface-name-globally.patch
4+
0004-Fix-fd-leak-in-worker-thread.patch

0 commit comments

Comments
 (0)