From b8772426371b17cef96770afbbfdb570cf8c03c6 Mon Sep 17 00:00:00 2001 From: Clinton Thomas <1033162+KernelClint@users.noreply.github.com> Date: Tue, 1 Sep 2026 13:29:01 -0400 Subject: [PATCH] automaton: drop stopped children from the spawn listener AI-Assisted: yes (GPT-5.6-Cyber) --- scapy/automaton.py | 3 ++- test/scapy/automaton.uts | 46 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/scapy/automaton.py b/scapy/automaton.py index 5f3ffcc12cd..f26370b102f 100644 --- a/scapy/automaton.py +++ b/scapy/automaton.py @@ -1042,9 +1042,10 @@ def _run() -> None: # start atmt atmt_server.runbg() # housekeeping - for atmt, clientsocket in clients: + for atmt, clientsocket in clients[:]: if not atmt.isrunning(): atmt.destroy() + clients.remove((atmt, clientsocket)) except KeyboardInterrupt: print("X Exiting.") ssock.shutdown(socket.SHUT_RDWR) diff --git a/test/scapy/automaton.uts b/test/scapy/automaton.uts index ab107b8bc74..c53018333c8 100644 --- a/test/scapy/automaton.uts +++ b/test/scapy/automaton.uts @@ -352,6 +352,51 @@ r = x r assert r == "Venus" += Spawn removes stopped connection children during housekeeping + +def stopped_spawn_children_are_removed(): + import socket + import time + class QuickConnection: + socketcls = None + pkt_cls = None + created = 0 + destroy_calls = 0 + def __init__(self, sock, **kwargs): + type(self).created += 1 + self.running = True + def runbg(self): + self.running = False + def isrunning(self): + return self.running + def destroy(self): + type(self).destroy_calls += 1 + def forcestop(self, wait=False): + self.running = False + QuickConnection.spawn = classmethod(Automaton.spawn.__func__) + listener = QuickConnection.spawn( + 0, + local_ip="127.0.0.1", + bg=True, + verb=False, + ) + try: + for expected in range(1, 5): + client = socket.create_connection(listener.getsockname(), timeout=1) + client.close() + deadline = time.monotonic() + 1 + while QuickConnection.created < expected and time.monotonic() < deadline: + time.sleep(0.001) + return QuickConnection.destroy_calls == QuickConnection.created + finally: + try: + listener.shutdown(socket.SHUT_RDWR) + except OSError: + pass + listener.close() + +assert stopped_spawn_children_are_removed() + = Automaton timer function ~ run timers @@ -519,4 +564,3 @@ if LINUX: if LINUX: # Remove the iptables rule assert os.system(IPTABLE_RULE % ('D', SECDEV_IP4)) == 0 -