From 1c4dcafd0b025e771f4dbd7197d0b5f263c9cb54 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 27 May 2020 08:51:23 -0700 Subject: [PATCH] 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. (cherry picked from commit 29a1384c040d39659e7d01f1fd7b6eb71ef2634e) Co-authored-by: Sean Gillespie --- Lib/ctypes/test/test_callbacks.py | 15 +++++++++++++++ .../2020-05-06-02-01-25.bpo-13097.Wh5xSK.rst | 1 + Modules/_ctypes/callproc.c | 15 +++++++++++++++ 3 files changed, 31 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2020-05-06-02-01-25.bpo-13097.Wh5xSK.rst diff --git a/Lib/ctypes/test/test_callbacks.py b/Lib/ctypes/test/test_callbacks.py index f622093df61da5..937a06d981b003 100644 --- a/Lib/ctypes/test/test_callbacks.py +++ b/Lib/ctypes/test/test_callbacks.py @@ -287,6 +287,21 @@ def callback(check, s): 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__': diff --git a/Misc/NEWS.d/next/Library/2020-05-06-02-01-25.bpo-13097.Wh5xSK.rst b/Misc/NEWS.d/next/Library/2020-05-06-02-01-25.bpo-13097.Wh5xSK.rst new file mode 100644 index 00000000000000..a7f5f588289174 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-05-06-02-01-25.bpo-13097.Wh5xSK.rst @@ -0,0 +1 @@ +``ctypes`` now raises an ``ArgumentError`` when a callback is invoked with more than 1024 arguments. \ No newline at end of file diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c index 5861042398d403..73413531bdbf0b 100644 --- a/Modules/_ctypes/callproc.c +++ b/Modules/_ctypes/callproc.c @@ -1060,6 +1060,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 @@ -1095,6 +1103,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();