Skip to content

Commit a285af7

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 75635c6 commit a285af7

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
@@ -1073,6 +1073,14 @@ GetComError(HRESULT errcode, GUID *riid, IUnknown *pIunk)
10731073
#define IS_PASS_BY_REF(x) (x > 8 || !POW2(x))
10741074
#endif
10751075

1076+
/*
1077+
* bpo-13097: Max number of arguments _ctypes_callproc will accept.
1078+
*
1079+
* This limit is enforced for the `alloca()` call in `_ctypes_callproc`,
1080+
* to avoid allocating a massive buffer on the stack.
1081+
*/
1082+
#define CTYPES_MAX_ARGCOUNT 1024
1083+
10761084
/*
10771085
* Requirements, must be ensured by the caller:
10781086
* - argtuple is tuple of arguments
@@ -1108,6 +1116,13 @@ PyObject *_ctypes_callproc(PPROC pProc,
11081116
++argcount;
11091117
#endif
11101118

1119+
if (argcount > CTYPES_MAX_ARGCOUNT)
1120+
{
1121+
PyErr_Format(PyExc_ArgError, "too many arguments (%zi), maximum is %i",
1122+
argcount, CTYPES_MAX_ARGCOUNT);
1123+
return NULL;
1124+
}
1125+
11111126
args = (struct argument *)alloca(sizeof(struct argument) * argcount);
11121127
if (!args) {
11131128
PyErr_NoMemory();

0 commit comments

Comments
 (0)