mirror of https://github.com/python/cpython
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.
This commit is contained in:
parent
8e98175a03
commit
4cce1352bb
|
@ -92,15 +92,18 @@ def CFUNCTYPE(restype, *argtypes, **kw):
|
||||||
flags |= _FUNCFLAG_USE_LASTERROR
|
flags |= _FUNCFLAG_USE_LASTERROR
|
||||||
if kw:
|
if kw:
|
||||||
raise ValueError("unexpected keyword argument(s) %s" % kw.keys())
|
raise ValueError("unexpected keyword argument(s) %s" % kw.keys())
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return _c_functype_cache[(restype, argtypes, flags)]
|
return _c_functype_cache[(restype, argtypes, flags)]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
class CFunctionType(_CFuncPtr):
|
pass
|
||||||
_argtypes_ = argtypes
|
|
||||||
_restype_ = restype
|
class CFunctionType(_CFuncPtr):
|
||||||
_flags_ = flags
|
_argtypes_ = argtypes
|
||||||
_c_functype_cache[(restype, argtypes, flags)] = CFunctionType
|
_restype_ = restype
|
||||||
return CFunctionType
|
_flags_ = flags
|
||||||
|
_c_functype_cache[(restype, argtypes, flags)] = CFunctionType
|
||||||
|
return CFunctionType
|
||||||
|
|
||||||
if _os.name == "nt":
|
if _os.name == "nt":
|
||||||
from _ctypes import LoadLibrary as _dlopen
|
from _ctypes import LoadLibrary as _dlopen
|
||||||
|
@ -116,15 +119,18 @@ if _os.name == "nt":
|
||||||
flags |= _FUNCFLAG_USE_LASTERROR
|
flags |= _FUNCFLAG_USE_LASTERROR
|
||||||
if kw:
|
if kw:
|
||||||
raise ValueError("unexpected keyword argument(s) %s" % kw.keys())
|
raise ValueError("unexpected keyword argument(s) %s" % kw.keys())
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return _win_functype_cache[(restype, argtypes, flags)]
|
return _win_functype_cache[(restype, argtypes, flags)]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
class WinFunctionType(_CFuncPtr):
|
pass
|
||||||
_argtypes_ = argtypes
|
|
||||||
_restype_ = restype
|
class WinFunctionType(_CFuncPtr):
|
||||||
_flags_ = flags
|
_argtypes_ = argtypes
|
||||||
_win_functype_cache[(restype, argtypes, flags)] = WinFunctionType
|
_restype_ = restype
|
||||||
return WinFunctionType
|
_flags_ = flags
|
||||||
|
_win_functype_cache[(restype, argtypes, flags)] = WinFunctionType
|
||||||
|
return WinFunctionType
|
||||||
if WINFUNCTYPE.__doc__:
|
if WINFUNCTYPE.__doc__:
|
||||||
WINFUNCTYPE.__doc__ = CFUNCTYPE.__doc__.replace("CFUNCTYPE", "WINFUNCTYPE")
|
WINFUNCTYPE.__doc__ = CFUNCTYPE.__doc__.replace("CFUNCTYPE", "WINFUNCTYPE")
|
||||||
|
|
||||||
|
|
|
@ -294,15 +294,22 @@ class SampleCallbacksTestCase(unittest.TestCase):
|
||||||
return len(args)
|
return len(args)
|
||||||
|
|
||||||
CTYPES_MAX_ARGCOUNT = 1024
|
CTYPES_MAX_ARGCOUNT = 1024
|
||||||
|
|
||||||
|
# valid call with nargs <= CTYPES_MAX_ARGCOUNT
|
||||||
proto = CFUNCTYPE(c_int, *(c_int,) * CTYPES_MAX_ARGCOUNT)
|
proto = CFUNCTYPE(c_int, *(c_int,) * CTYPES_MAX_ARGCOUNT)
|
||||||
cb = proto(func)
|
cb = proto(func)
|
||||||
args1 = (1,) * CTYPES_MAX_ARGCOUNT
|
args1 = (1,) * CTYPES_MAX_ARGCOUNT
|
||||||
self.assertEqual(cb(*args1), CTYPES_MAX_ARGCOUNT)
|
self.assertEqual(cb(*args1), CTYPES_MAX_ARGCOUNT)
|
||||||
|
|
||||||
|
# invalid call with nargs > CTYPES_MAX_ARGCOUNT
|
||||||
args2 = (1,) * (CTYPES_MAX_ARGCOUNT + 1)
|
args2 = (1,) * (CTYPES_MAX_ARGCOUNT + 1)
|
||||||
with self.assertRaises(ArgumentError):
|
with self.assertRaises(ArgumentError):
|
||||||
cb(*args2)
|
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 test_convert_result_error(self):
|
||||||
def func():
|
def func():
|
||||||
return ("tuple",)
|
return ("tuple",)
|
||||||
|
|
|
@ -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.
|
|
@ -2382,7 +2382,6 @@ converters_from_argtypes(PyObject *ob)
|
||||||
_Py_IDENTIFIER(from_param);
|
_Py_IDENTIFIER(from_param);
|
||||||
PyObject *converters;
|
PyObject *converters;
|
||||||
Py_ssize_t i;
|
Py_ssize_t i;
|
||||||
Py_ssize_t nArgs;
|
|
||||||
|
|
||||||
ob = PySequence_Tuple(ob); /* new reference */
|
ob = PySequence_Tuple(ob); /* new reference */
|
||||||
if (!ob) {
|
if (!ob) {
|
||||||
|
@ -2391,7 +2390,14 @@ converters_from_argtypes(PyObject *ob)
|
||||||
return NULL;
|
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);
|
converters = PyTuple_New(nArgs);
|
||||||
if (!converters) {
|
if (!converters) {
|
||||||
Py_DECREF(ob);
|
Py_DECREF(ob);
|
||||||
|
|
|
@ -1118,14 +1118,6 @@ GetComError(HRESULT errcode, GUID *riid, IUnknown *pIunk)
|
||||||
#define IS_PASS_BY_REF(x) (x > 8 || !POW2(x))
|
#define IS_PASS_BY_REF(x) (x > 8 || !POW2(x))
|
||||||
#endif
|
#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:
|
* Requirements, must be ensured by the caller:
|
||||||
* - argtuple is tuple of arguments
|
* - argtuple is tuple of arguments
|
||||||
|
|
|
@ -11,6 +11,15 @@
|
||||||
#define PARAMFLAG_FLCID 0x4
|
#define PARAMFLAG_FLCID 0x4
|
||||||
#endif
|
#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 tagPyCArgObject PyCArgObject;
|
||||||
typedef struct tagCDataObject CDataObject;
|
typedef struct tagCDataObject CDataObject;
|
||||||
typedef PyObject *(* GETFUNC)(void *, Py_ssize_t size);
|
typedef PyObject *(* GETFUNC)(void *, Py_ssize_t size);
|
||||||
|
|
Loading…
Reference in New Issue