Skip to content

Commit 1c4dcaf

Browse files
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 29a1384) Co-authored-by: Sean Gillespie <[email protected]>
1 parent a93bf82 commit 1c4dcaf

File tree

3 files changed

+31
-0
lines changed

3 files changed

+31
-0
lines changed

Lib/ctypes/test/test_callbacks.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,21 @@ def callback(check, s):
287287
self.assertEqual(s.second, check.second)
288288
self.assertEqual(s.third, check.third)
289289

290+
def test_callback_too_many_args(self):
291+
def func(*args):
292+
return len(args)
293+
294+
CTYPES_MAX_ARGCOUNT = 1024
295+
proto = CFUNCTYPE(c_int, *(c_int,) * CTYPES_MAX_ARGCOUNT)
296+
cb = proto(func)
297+
args1 = (1,) * CTYPES_MAX_ARGCOUNT
298+
self.assertEqual(cb(*args1), CTYPES_MAX_ARGCOUNT)
299+
300+
args2 = (1,) * (CTYPES_MAX_ARGCOUNT + 1)
301+
with self.assertRaises(ArgumentError):
302+
cb(*args2)
303+
304+
290305
################################################################
291306

292307
if __name__ == '__main__':
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
``ctypes`` now raises an ``ArgumentError`` when a callback is invoked with more than 1024 arguments.

Modules/_ctypes/callproc.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1060,6 +1060,14 @@ GetComError(HRESULT errcode, GUID *riid, IUnknown *pIunk)
10601060
#define IS_PASS_BY_REF(x) (x > 8 || !POW2(x))
10611061
#endif
10621062

1063+
/*
1064+
* bpo-13097: Max number of arguments _ctypes_callproc will accept.
1065+
*
1066+
* This limit is enforced for the `alloca()` call in `_ctypes_callproc`,
1067+
* to avoid allocating a massive buffer on the stack.
1068+
*/
1069+
#define CTYPES_MAX_ARGCOUNT 1024
1070+
10631071
/*
10641072
* Requirements, must be ensured by the caller:
10651073
* - argtuple is tuple of arguments
@@ -1095,6 +1103,13 @@ PyObject *_ctypes_callproc(PPROC pProc,
10951103
++argcount;
10961104
#endif
10971105

1106+
if (argcount > CTYPES_MAX_ARGCOUNT)
1107+
{
1108+
PyErr_Format(PyExc_ArgError, "too many arguments (%zi), maximum is %i",
1109+
argcount, CTYPES_MAX_ARGCOUNT);
1110+
return NULL;
1111+
}
1112+
10981113
args = (struct argument *)alloca(sizeof(struct argument) * argcount);
10991114
if (!args) {
11001115
PyErr_NoMemory();

0 commit comments

Comments
 (0)