bpo-13097: ctypes: limit callback to 1024 arguments (GH-19914)

ctypes now raises an ArgumentError when a callback
is invoked with more than 1024 arguments.

The ctypes module allocates arguments on the stack in
ctypes_callproc() using alloca(), which is problematic
when large numbers of arguments are passed. Instead
of a stack overflow, this commit raises an ArgumentError
if more than 1024 parameters are passed.
This commit is contained in:
Sean Gillespie 2020-05-27 08:22:07 -07:00 committed by GitHub
parent fe2978b3b9
commit 29a1384c04
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 31 additions and 0 deletions

View File

@ -287,6 +287,21 @@ class SampleCallbacksTestCase(unittest.TestCase):
self.assertEqual(s.second, check.second)
self.assertEqual(s.third, check.third)
def test_callback_too_many_args(self):
def func(*args):
return len(args)
CTYPES_MAX_ARGCOUNT = 1024
proto = CFUNCTYPE(c_int, *(c_int,) * CTYPES_MAX_ARGCOUNT)
cb = proto(func)
args1 = (1,) * CTYPES_MAX_ARGCOUNT
self.assertEqual(cb(*args1), CTYPES_MAX_ARGCOUNT)
args2 = (1,) * (CTYPES_MAX_ARGCOUNT + 1)
with self.assertRaises(ArgumentError):
cb(*args2)
################################################################
if __name__ == '__main__':

View File

@ -0,0 +1 @@
``ctypes`` now raises an ``ArgumentError`` when a callback is invoked with more than 1024 arguments.

View File

@ -1072,6 +1072,14 @@ 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
@ -1107,6 +1115,13 @@ PyObject *_ctypes_callproc(PPROC pProc,
++argcount;
#endif
if (argcount > CTYPES_MAX_ARGCOUNT)
{
PyErr_Format(PyExc_ArgError, "too many arguments (%zi), maximum is %i",
argcount, CTYPES_MAX_ARGCOUNT);
return NULL;
}
args = (struct argument *)alloca(sizeof(struct argument) * argcount);
if (!args) {
PyErr_NoMemory();