Skip to content

Commit 788d7bf

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 <sean@swgillespie.me>
1 parent 43fccb1 commit 788d7bf

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

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

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

0 commit comments

Comments
 (0)