summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVictor Stinner <vstinner@python.org>2022-02-07 13:53:15 (GMT)
committerGitHub <noreply@github.com>2022-02-07 13:53:15 (GMT)
commit4cce1352bb47babaeefb68fcfcc48ffa073745c3 (patch)
tree0b920e214dc1ddbb8d796de50cf93ba99ae3bc3f
parent8e98175a03fe03d62822d96007a74e5273013764 (diff)
downloadcpython-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__.py30
-rw-r--r--Lib/ctypes/test/test_callbacks.py7
-rw-r--r--Misc/NEWS.d/next/Library/2022-02-07-13-27-59.bpo-46323.7UENAj.rst3
-rw-r--r--Modules/_ctypes/_ctypes.c10
-rw-r--r--Modules/_ctypes/callproc.c8
-rw-r--r--Modules/_ctypes/ctypes.h9
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);