Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 18 additions & 12 deletions Lib/ctypes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -116,15 +119,18 @@ def WINFUNCTYPE(restype, *argtypes, **kw):
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")

Expand Down
7 changes: 7 additions & 0 deletions Lib/ctypes/test/test_callbacks.py
Original file line number Diff line number Diff line change
Expand Up @@ -294,15 +294,22 @@ def func(*args):
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",)
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
10 changes: 8 additions & 2 deletions Modules/_ctypes/_ctypes.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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);
Expand Down
8 changes: 0 additions & 8 deletions Modules/_ctypes/callproc.c
Original file line number Diff line number Diff line change
Expand Up @@ -1118,14 +1118,6 @@ GetComError(HRESULT errcode, GUID *riid, IUnknown *pIunk)
#define IS_PASS_BY_REF(x) (x > 8 || !POW2(x))
#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
Expand Down
9 changes: 9 additions & 0 deletions Modules/_ctypes/ctypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down