Skip to content

Commit

Permalink
Add a patch for scapy to fix fd leak issue in AsyncSniffer (#20415)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
bingwang-ms authored Oct 30, 2024
1 parent 6c05500 commit bf1bcb2
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 0 deletions.
47 changes: 47 additions & 0 deletions src/scapy.patch/0004-Fix-fd-leak-in-worker-thread.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
diff --git a/scapy/sendrecv.py b/scapy/sendrecv.py
index f97fc415..dbcc372f 100644
--- a/scapy/sendrecv.py
+++ b/scapy/sendrecv.py
@@ -1111,17 +1111,19 @@ class AsyncSniffer(object):
# The _RL2 function resolves the L2socket of an iface
_RL2 = lambda i: L2socket or resolve_iface(i).l2listen() # type: Callable[[_GlobInterfaceType], Callable[..., SuperSocket]] # noqa: E501
if isinstance(iface, list):
- sniff_sockets.update(
- (_RL2(ifname)(type=ETH_P_ALL, iface=ifname, **karg),
- ifname)
- for ifname in iface
- )
+ for ifname in iface:
+ try:
+ sniff_sockets.update({_RL2(ifname)(type=ETH_P_ALL, iface=ifname, **karg): ifname})
+ except OSError:
+ # Ignore OSError when opening the socket
+ # The error can happen when the port goes down during the creation of the socket
+ pass
elif isinstance(iface, dict):
- sniff_sockets.update(
- (_RL2(ifname)(type=ETH_P_ALL, iface=ifname, **karg),
- iflabel)
- for ifname, iflabel in six.iteritems(iface)
- )
+ for ifname, iflabel in six.iteritems(iface):
+ try:
+ sniff_sockets.update({_RL2(ifname)(type=ETH_P_ALL, iface=ifname, **karg): iflabel})
+ except OSError:
+ pass
else:
iface = iface or conf.iface
sniff_sockets[_RL2(iface)(type=ETH_P_ALL, iface=iface,
@@ -1221,7 +1223,11 @@ class AsyncSniffer(object):
self.running = False
if opened_socket is None:
for s in sniff_sockets:
- s.close()
+ try:
+ s.close()
+ except Exception:
+ # Ignore exceptions to ensure all sockets are closed
+ pass
elif close_pipe:
close_pipe.close()
self.results = session.toPacketList()
1 change: 1 addition & 0 deletions src/scapy.patch/series
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
0001-Fix-version-string-generation-when-scapy-is-a-submod.patch
0002-Check-if-the-network-interface-still-exists.patch
0003-Do-not-resolve-the-interface-name-globally.patch
0004-Fix-fd-leak-in-worker-thread.patch

0 comments on commit bf1bcb2

Please sign in to comment.