From 0458f3282bd33720923dec97eb36a5615c85e032 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Wed, 21 Oct 2026 21:29:33 -0700 Subject: [PATCH] gh-159739: Detect recursive untyped ctypes arguments _as_parameter_ chains were recursion-checked for typed arguments but not for foreign calls without argtypes. A cyclic chain could exhaust the C stack and crash before the function was called. Apply the same recursion guard to untyped conversion. Backport to 3.15 and 3.14. --- Lib/test/test_ctypes/test_as_parameter.py | 4 ++++ .../2026-10-21-21-26-10.gh-issue-159739.b4ueJ7.rst | 1 + Modules/_ctypes/callproc.c | 9 +++++++-- 3 files changed, 12 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-10-21-21-26-10.gh-issue-159739.b4ueJ7.rst diff --git a/Lib/test/test_ctypes/test_as_parameter.py b/Lib/test/test_ctypes/test_as_parameter.py index 8634c3f03a02ec1..dc383a12ce8bb35 100644 --- a/Lib/test/test_ctypes/test_as_parameter.py +++ b/Lib/test/test_ctypes/test_as_parameter.py @@ -214,6 +214,10 @@ class A: with self.assertRaises(RecursionError): c_type.from_param(a) + function = CDLL(_ctypes_test.__file__)._testfunc_p_p + with self.assertRaisesRegex(ArgumentError, 'RecursionError'): + function(a) + class AsParamWrapper: def __init__(self, param): diff --git a/Misc/NEWS.d/next/Library/2026-10-21-21-26-10.gh-issue-159739.b4ueJ7.rst b/Misc/NEWS.d/next/Library/2026-10-21-21-26-10.gh-issue-159739.b4ueJ7.rst new file mode 100644 index 000000000000000..6230801bbc09278 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-10-21-21-26-10.gh-issue-159739.b4ueJ7.rst @@ -0,0 +1 @@ +Detect recursive ``_as_parameter_`` chains for :mod:`ctypes` calls without argument types. diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c index 84aa8c1443fdf58..c8a54b0cec83a77 100644 --- a/Modules/_ctypes/callproc.c +++ b/Modules/_ctypes/callproc.c @@ -91,6 +91,7 @@ module _ctypes #define DONT_USE_SEH #endif +#include "pycore_ceval.h" // _Py_EnterRecursiveCall() #include "pycore_runtime.h" // _PyRuntime #include "pycore_global_objects.h"// _Py_ID() #include "pycore_traceback.h" // _PyTraceback_Add() @@ -795,8 +796,12 @@ static int ConvParam(ctypes_state *st, attribute) */ if (arg) { - int result; - result = ConvParam(st, arg, index, pa); + if (_Py_EnterRecursiveCall(" while processing _as_parameter_")) { + Py_DECREF(arg); + return -1; + } + int result = ConvParam(st, arg, index, pa); + _Py_LeaveRecursiveCall(); Py_DECREF(arg); return result; }