From c567883ff5915da6fc8f215b0f1625ba470c7fa1 Mon Sep 17 00:00:00 2001 From: Nils Weiss Date: Tue, 20 Oct 2026 08:42:18 +0200 Subject: [PATCH] automaton: raise on Windows WAIT_FAILED ctypes may return WAIT_FAILED as -1, which select_objects() treats as an input index and can report the last object ready. Raise the Windows error and close temporary Winsock events on failed waits. AI-Assisted: yes (GPT-6) --- scapy/automaton.py | 13 ++++++++++++- test/windows.uts | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/scapy/automaton.py b/scapy/automaton.py index 303db73a38d..9ef4a0aa325 100644 --- a/scapy/automaton.py +++ b/scapy/automaton.py @@ -139,7 +139,13 @@ def select_objects(inputs, remain): False, remainms ) - if res != 0xFFFFFFFF and res != 0x00000102: # Failed or Timeout + # ctypes' default signed c_int restype returns WAIT_FAILED as -1. + if res == -1 or res == 0xFFFFFFFF: + error = ctypes.WinError() + for evt in created: + ctypes.windll.ws2_32.WSACloseEvent(evt) + raise error + if res != 0x00000102: # Timeout results.add(inputs[res]) if len(events) > 1: # Now poll the others, if any @@ -148,6 +154,11 @@ def select_objects(inputs, remain): ctypes.c_void_p(evt), 0 # poll: don't wait ) + if res == -1 or res == 0xFFFFFFFF: + error = ctypes.WinError() + for created_evt in created: + ctypes.windll.ws2_32.WSACloseEvent(created_evt) + raise error if res == 0: results.add(inputs[i]) # Cleanup created events, if any diff --git a/test/windows.uts b/test/windows.uts index 394376be0c2..56e2f6f1eba 100644 --- a/test/windows.uts +++ b/test/windows.uts @@ -5,6 +5,7 @@ + Configuration = Imports +~ automaton from unittest import mock @@ -21,6 +22,51 @@ a = ObjectPipe() a.send("test") assert select_objects([a], 0) == [a] += Automaton - WaitFor* errors are raised +~ automaton + +import ctypes as _ctypes +import scapy.automaton as _automaton + +for failure, input_count, use_native_socket in [ + (-1, 1, True), + (0xFFFFFFFF, 2, False), + (0, 2, True)]: + inputs = [mock.Mock() for _ in range(input_count)] + for index, obj in enumerate(inputs): + obj.fileno.return_value = index + 10 + obj.__selectable_force_select__ = False + if use_native_socket: + inputs[0].__selectable_force_select__ = True + kernel32 = mock.Mock() + if input_count == 1: + kernel32.WaitForSingleObject.return_value = failure + elif use_native_socket: + kernel32.WaitForMultipleObjects.return_value = 0 + kernel32.WaitForSingleObject.return_value = -1 + else: + kernel32.WaitForMultipleObjects.return_value = failure + ws2_32 = mock.Mock() + ws2_32.WSACreateEvent.return_value = 100 + ws2_32.WSAEventSelect.return_value = 0 + windll = mock.Mock(kernel32=kernel32, ws2_32=ws2_32) + with mock.patch.object(_automaton, "WINDOWS", True): + with mock.patch.object(_automaton, "ctypes", _ctypes, create=True): + with mock.patch.object( + _ctypes, "windll", windll, create=True): + with mock.patch.object( + _ctypes, "WinError", + return_value=OSError("wait failed"), create=True + ) as win_error: + try: + select_objects(inputs, 0) + except OSError: + pass + else: + assert False, "WAIT_FAILED must raise an OSError" + win_error.assert_called_once_with() + if use_native_socket: + assert ws2_32.WSACloseEvent.call_count == 1 ############ ############ + Windows arch unit tests