diff options
author | Victor Stinner <vstinner@python.org> | 2022-02-07 13:53:15 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-02-07 13:53:15 (GMT) |
commit | 4cce1352bb47babaeefb68fcfcc48ffa073745c3 (patch) | |
tree | 0b920e214dc1ddbb8d796de50cf93ba99ae3bc3f | |
parent | 8e98175a03fe03d62822d96007a74e5273013764 (diff) | |
download | cpython-4cce1352bb47babaeefb68fcfcc48ffa073745c3.zip cpython-4cce1352bb47babaeefb68fcfcc48ffa073745c3.tar.gz cpython-4cce1352bb47babaeefb68fcfcc48ffa073745c3.tar.bz2 |
bpo-46323: _ctypes.CFuncPtr fails if _argtypes_ is too long (GH-31188)
ctypes.CFUNCTYPE() and ctypes.WINFUNCTYPE() now fail to create the
type if its "_argtypes_" member contains too many arguments.
Previously, the error was only raised when calling a function.
Change also how CFUNCTYPE() and WINFUNCTYPE() handle KeyError to
prevent creating a chain of exceptions if ctypes.CFuncPtr raises an
error.
-rw-r--r-- | Lib/ctypes/__init__.py | 30 | ||||
-rw-r--r-- | Lib/ctypes/test/test_callbacks.py | 7 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Library/2022-02-07-13-27-59.bpo-46323.7UENAj.rst | 3 | ||||
-rw-r--r-- | Modules/_ctypes/_ctypes.c | 10 | ||||
-rw-r--r-- | Modules/_ctypes/callproc.c | 8 | ||||
-rw-r--r-- | Modules/_ctypes/ctypes.h | 9 |
6 files changed, 45 insertions, 22 deletions
diff --git a/Lib/ctypes/__init__.py b/Lib/ctypes/__init__.py index b08629e..ab4d31b 100644 --- a/Lib/ctypes/__init__.py +++ b/Lib/ctypes/__init__.py @@ -92,15 +92,18 @@ def CFUNCTYPE(restype, *argtypes, **kw): flags |= _FUNCFLAG_USE_LASTERROR if kw: raise ValueError("unexpected keyword argument(s) %s" % kw.keys()) + try: return _c_functype_cache[(restype, argtypes, flags)] except KeyError: - class CFunctionType(_CFuncPtr): - _argtypes_ = argtypes - _restype_ = restype - _flags_ = flags - _c_functype_cache[(restype, argtypes, flags)] = CFunctionType - return CFunctionType + pass + + class CFunctionType(_CFuncPtr): + _argtypes_ = argtypes + _restype_ = restype + _flags_ = flags + _c_functype_cache[(restype, argtypes, flags)] = CFunctionType + return CFunctionType if _os.name == "nt": from _ctypes import LoadLibrary as _dlopen @@ -116,15 +119,18 @@ if _os.name == "nt": flags |= _FUNCFLAG_USE_LASTERROR if kw: raise ValueError("unexpected keyword argument(s) %s" % kw.keys()) + try: return _win_functype_cache[(restype, argtypes, flags)] except KeyError: - class WinFunctionType(_CFuncPtr): - _argtypes_ = argtypes - _restype_ = restype - _flags_ = flags - _win_functype_cache[(restype, argtypes, flags)] = WinFunctionType - return WinFunctionType + pass + + class WinFunctionType(_CFuncPtr): + _argtypes_ = argtypes + _restype_ = restype + _flags_ = flags + _win_functype_cache[(restype, argtypes, flags)] = WinFunctionType + return WinFunctionType if WINFUNCTYPE.__doc__: WINFUNCTYPE.__doc__ = CFUNCTYPE.__doc__.replace("CFUNCTYPE", "WINFUNCTYPE") diff --git a/Lib/ctypes/test/test_callbacks.py b/Lib/ctypes/test/test_callbacks.py index d8e9c5a..5561ffe 100644 --- a/Lib/ctypes/test/test_callbacks.py +++ b/Lib/ctypes/test/test_callbacks.py @@ -294,15 +294,22 @@ class SampleCallbacksTestCase(unittest.TestCase): return len(args) CTYPES_MAX_ARGCOUNT = 1024 + + # valid call with nargs <= CTYPES_MAX_ARGCOUNT proto = CFUNCTYPE(c_int, *(c_int,) * CTYPES_MAX_ARGCOUNT) cb = proto(func) args1 = (1,) * CTYPES_MAX_ARGCOUNT self.assertEqual(cb(*args1), CTYPES_MAX_ARGCOUNT) + # invalid call with nargs > CTYPES_MAX_ARGCOUNT args2 = (1,) * (CTYPES_MAX_ARGCOUNT + 1) with self.assertRaises(ArgumentError): cb(*args2) + # error when creating the type with too many arguments + with self.assertRaises(ArgumentError): + CFUNCTYPE(c_int, *(c_int,) * (CTYPES_MAX_ARGCOUNT + 1)) + def test_convert_result_error(self): def func(): return ("tuple",) diff --git a/Misc/NEWS.d/next/Library/2022-02-07-13-27-59.bpo-46323.7UENAj.rst b/Misc/NEWS.d/next/Library/2022-02-07-13-27-59.bpo-46323.7UENAj.rst new file mode 100644 index 0000000..e144450 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-02-07-13-27-59.bpo-46323.7UENAj.rst @@ -0,0 +1,3 @@ +``ctypes.CFUNCTYPE()`` and ``ctypes.WINFUNCTYPE()`` now fail to create the type +if its ``_argtypes_`` member contains too many arguments. Previously, the error +was only raised when calling a function. Patch by Victor Stinner. diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 96078c7..da9dd09 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -2382,7 +2382,6 @@ converters_from_argtypes(PyObject *ob) _Py_IDENTIFIER(from_param); PyObject *converters; Py_ssize_t i; - Py_ssize_t nArgs; ob = PySequence_Tuple(ob); /* new reference */ if (!ob) { @@ -2391,7 +2390,14 @@ converters_from_argtypes(PyObject *ob) return NULL; } - nArgs = PyTuple_GET_SIZE(ob); + Py_ssize_t nArgs = PyTuple_GET_SIZE(ob); + if (nArgs > CTYPES_MAX_ARGCOUNT) { + PyErr_Format(PyExc_ArgError, + "_argtypes_ has too many arguments (%zi), maximum is %i", + nArgs, CTYPES_MAX_ARGCOUNT); + return NULL; + } + converters = PyTuple_New(nArgs); if (!converters) { Py_DECREF(ob); diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c index 928737e..da29567 100644 --- a/Modules/_ctypes/callproc.c +++ b/Modules/_ctypes/callproc.c @@ -1119,14 +1119,6 @@ GetComError(HRESULT errcode, GUID *riid, IUnknown *pIunk) #endif /* - * bpo-13097: Max number of arguments _ctypes_callproc will accept. - * - * This limit is enforced for the `alloca()` call in `_ctypes_callproc`, - * to avoid allocating a massive buffer on the stack. - */ -#define CTYPES_MAX_ARGCOUNT 1024 - -/* * Requirements, must be ensured by the caller: * - argtuple is tuple of arguments * - argtypes is either NULL, or a tuple of the same size as argtuple diff --git a/Modules/_ctypes/ctypes.h b/Modules/_ctypes/ctypes.h index 9e82ce8..0badb48 100644 --- a/Modules/_ctypes/ctypes.h +++ b/Modules/_ctypes/ctypes.h @@ -11,6 +11,15 @@ #define PARAMFLAG_FLCID 0x4 #endif +/* + * bpo-13097: Max number of arguments CFuncPtr._argtypes_ and + * _ctypes_callproc() will accept. + * + * This limit is enforced for the `alloca()` call in `_ctypes_callproc`, + * to avoid allocating a massive buffer on the stack. + */ +#define CTYPES_MAX_ARGCOUNT 1024 + typedef struct tagPyCArgObject PyCArgObject; typedef struct tagCDataObject CDataObject; typedef PyObject *(* GETFUNC)(void *, Py_ssize_t size); |