Bug
Tuple elements are cast to spy_message_object* and only checked for NULL. Valid Python objects of unrelated types pass validation, so their object memory is treated as an icsSpyMessage, including its length and pointer fields.
Reviewed commit: 45167e09de6b886cd451a4245f842c8221349711 (current master on 2026-09-15).
Source: src/methods.cpp:1792
Reproduction
This reproducer uses a hardware-free DLL. Save this as mock.c:
#define API __declspec(dllexport)
typedef unsigned long long size_t;
static int calls;
API int icsneoTxMessages(void* h, void* m, int net, int count) { ++calls; return 1; }
API int review_calls(void) { return calls; }
Build with LLVM on Windows x64:
clang-cl /nologo /c /GS- /Zl mock.c /Fomock.obj
lld-link /dll /noentry /nodefaultlib /out:mock.dll mock.obj
Then run:
import ics
import ctypes
from pathlib import Path
ics.override_library_name(str(Path('mock.dll').resolve()))
d = ics.PyNeoDeviceEx()
d._auto_handle_close = False
dll = ctypes.CDLL(str(Path('mock.dll').resolve()))
for value in (None, 42, object()):
before = dll.review_calls()
ics.transmit_messages(d, value)
print(type(value).__name__, dll.review_calls() - before)
Observed / expected
All three invalid values are accepted and each produces one DLL call. The wrapper itself reads NetworkID/NetworkID2 outside the actual object layout; a real DLL can also follow bogus payload pointers. Expected a Python type error before any native call. Closed #111 concerns oversized CAN FD payloads, a different trigger/root cause.
Suggested fix and regression coverage
Validate every element with the supported SpyMessage/SpyMessageJ1850 type checks before taking a native pointer. Validate the entire batch before transmitting any element. Test invalid scalar arguments and invalid elements inside otherwise valid tuples.
Validation environment
Windows x64, CPython 3.14.5, extension rebuilt from the commit above. Hardware-dependent entry points below were exercised with a mock DLL, not a connected device. The existing 30 tests pass despite these findings.
Bug
Tuple elements are cast to
spy_message_object*and only checked for NULL. Valid Python objects of unrelated types pass validation, so their object memory is treated as anicsSpyMessage, including its length and pointer fields.Reviewed commit:
45167e09de6b886cd451a4245f842c8221349711(current master on 2026-09-15).Source: src/methods.cpp:1792
Reproduction
This reproducer uses a hardware-free DLL. Save this as
mock.c:Build with LLVM on Windows x64:
Then run:
Observed / expected
All three invalid values are accepted and each produces one DLL call. The wrapper itself reads NetworkID/NetworkID2 outside the actual object layout; a real DLL can also follow bogus payload pointers. Expected a Python type error before any native call. Closed #111 concerns oversized CAN FD payloads, a different trigger/root cause.
Suggested fix and regression coverage
Validate every element with the supported SpyMessage/SpyMessageJ1850 type checks before taking a native pointer. Validate the entire batch before transmitting any element. Test invalid scalar arguments and invalid elements inside otherwise valid tuples.
Validation environment
Windows x64, CPython 3.14.5, extension rebuilt from the commit above. Hardware-dependent entry points below were exercised with a mock DLL, not a connected device. The existing 30 tests pass despite these findings.