-
-
Notifications
You must be signed in to change notification settings - Fork 32.7k
Description
Crash report
What happened?
The following code crashes the interpreter due to missing checks in _ctypes
:
import ctypes
libc = ctypes.CDLL(None)
PRINTF_PROTO = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_char_p)
c_printf = PRINTF_PROTO(("printf", libc), ((1,),))
c_printf.argtypes = (ctypes.c_char_p, ctypes.c_int)
c_printf(b"Hello\n")
According to the docs, paramflags must be a tuple of the same length as argtypes. This is enforced in _validate_paramflags
:
cpython/Modules/_ctypes/_ctypes.c
Lines 3926 to 3932 in 7dc42b6
len = PyTuple_GET_SIZE(paramflags); | |
if (len != PyTuple_GET_SIZE(info->argtypes)) { | |
PyErr_SetString(PyExc_ValueError, | |
"paramflags must have the same length as argtypes"); | |
return 0; | |
} |
However, _validate_paramflags
is only invoked during prototype construction (PyCFuncPtr_FromDll
), not during function calls (PyCFuncPtr_call
-> _build_callargs
). If argtypes is modified after the prototype is created (as in the PoC), _build_callargs
may access paramflags
out of bounds:
cpython/Modules/_ctypes/_ctypes.c
Lines 4383 to 4388 in 7dc42b6
#endif | |
for (i = 0; i < len; ++i) { | |
PyObject *item = PyTuple_GET_ITEM(paramflags, i); | |
PyObject *ob; | |
unsigned int flag; | |
PyObject *name = NULL; |
Here, len
refers to the length of argtypes
.
Although I tested this PoC only on Python 3.11, 3.12, and 3.13, it affects all versions of Python in my opinion.
Backtrace:
pwndbg> bt
#0 PyTuple_GET_SIZE (op=op@entry=<unknown at remote 0xfdfdfdfdfdfdfdfd>) at ../Include/cpython/tupleobject.h:22
#1 0x00007ffff7b0fa2d in _build_callargs (st=st@entry=0x7ffff7c198c0, self=self@entry=0x7ffff7c1ac20,
argtypes=(<_ctypes.PyCSimpleType(__module__='ctypes', __firstlineno__=241, _type_='z', __repr__=<function at remote 0x7ffff7618950>, __static_attributes__=(), __dict__=<getset_descriptor at remote 0x7ffff763bbf0>, __weakref__=<getset_descriptor at remote 0x7ffff763bc50>, __doc__=None, from_param=<classmethod_descriptor at remote 0x7ffff763bd10>) at remote 0xc67640>, <_ctypes.PyCSimpleType(__module__='ctypes', __firstlineno__=187, _type_='i', __static_attributes__=(...), __dict__=<getset_descriptor at remote 0x7ffff763a4b0>, __weakref__=<getset_descriptor at remote 0x7ffff763a510>, __doc__=None, __ctype_be__=<_ctypes.PyCSimpleType(__module__='ctypes', __firstlineno__=187, _type_='i', __static_attributes__=(...), __dict__=<getset_descriptor at remote 0x7ffff763a630>, __weakref__=<getset_descriptor at remote 0x7ffff763a690>, __doc__=None, __ctype_le__=<...>, __ctype_be__=<...>) at remote 0xc61ef0>, __ctype_le__=<...>) at remote 0xc61700>), inargs=inargs@entry=(b'Hello\n',), kwds=kwds@entry=0x0,
poutmask=poutmask@entry=0x7fffffffda00, pinoutmask=0x7fffffffd9fc, pnumretvals=0x7fffffffda04) at ../Modules/_ctypes/_ctypes.c:4098
#2 0x00007ffff7b0ff54 in PyCFuncPtr_call (self=self@entry=0x7ffff7c1ac20, inargs=inargs@entry=(b'Hello\n',), kwds=kwds@entry=0x0) at ../Modules/_ctypes/_ctypes.c:4346
#3 0x00000000004b9438 in _PyObject_MakeTpCall (tstate=tstate@entry=0xafbe80 <_PyRuntime+299040>, callable=callable@entry=<CFunctionType() at remote 0x7ffff7c1ac20>,
args=args@entry=0x7ffff7fb5078, nargs=<optimized out>, keywords=keywords@entry=0x0) at ../Objects/call.c:242
#4 0x00000000004b964b in _PyObject_VectorcallTstate (tstate=0xafbe80 <_PyRuntime+299040>, callable=callable@entry=<CFunctionType() at remote 0x7ffff7c1ac20>,
args=args@entry=0x7ffff7fb5078, nargsf=<optimized out>, kwnames=kwnames@entry=0x0) at ../Include/internal/pycore_call.h:166
#5 0x00000000004b96a1 in PyObject_Vectorcall (callable=callable@entry=<CFunctionType() at remote 0x7ffff7c1ac20>, args=args@entry=0x7ffff7fb5078,
nargsf=<optimized out>, kwnames=kwnames@entry=0x0) at ../Objects/call.c:327
#6 0x00000000005d24a5 in _PyEval_EvalFrameDefault (tstate=0xafbe80 <_PyRuntime+299040>, frame=0x7ffff7fb5020, throwflag=0) at ../Python/generated_cases.c.h:813
#7 0x00000000005e245e in _PyEval_EvalFrame (tstate=tstate@entry=0xafbe80 <_PyRuntime+299040>, frame=<optimized out>, throwflag=throwflag@entry=0)
at ../Include/internal/pycore_ceval.h:119
#8 0x00000000005e2594 in _PyEval_Vector (tstate=tstate@entry=0xafbe80 <_PyRuntime+299040>, func=func@entry=0x7ffff776a510,
locals=locals@entry={'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <SourceFileLoader(name='__main__', path='/home/kila/stuff/python-poc.py') at remote 0x7ffff77b2760>, '__spec__': None, '__annotations__': {}, '__builtins__': <module at remote 0x7ffff7c36990>, '__file__': '/home/kila/stuff/python-poc.py', '__cached__': None, 'ctypes': <module at remote 0x7ffff7616c30>, 'libc': <CDLL(_name=None, _FuncPtr=<_ctypes.PyCFuncPtrType(__module__='ctypes', __firstlineno__=384, _flags_=1, _restype_=<_ctypes.PyCSimpleType(__module__='ctypes', __firstlineno__=187, _type_='i', __static_attributes__=(), __dict__=<getset_descriptor at remote 0x7ffff763a4b0>, __weakref__=<getset_descriptor at remote 0x7ffff763a510>, __doc__=None, __ctype_be__=<_ctypes.PyCSimpleType(__module__='ctypes', __firstlineno__=187, _type_='i', __static_attributes__=(...), __dict__=<getset_descriptor at remote 0x7ffff763a630>, __weakref__=<getset_descriptor at remote 0x7ffff763a690>, __doc__=None, __ctype_le__=<...>, __ctype_be__=<...>) at re...(truncated), args=args@entry=0x0, argcount=argcount@entry=0, kwnames=kwnames@entry=0x0) at ../Python/ceval.c:1816
#9 0x00000000005e2648 in PyEval_EvalCode (co=co@entry=<code at remote 0x7ffff7bc9e00>,
globals=globals@entry={'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <SourceFileLoader(name='__main__', path='/home/kila/stuff/python-poc.py') at remote 0x7ffff77b2760>, '__spec__': None, '__annotations__': {}, '__builtins__': <module at remote 0x7ffff7c36990>, '__file__': '/home/kila/stuff/python-poc.py', '__cached__': None, 'ctypes': <module at remote 0x7ffff7616c30>, 'libc': <CDLL(_name=None, _FuncPtr=<_ctypes.PyCFuncPtrType(__module__='ctypes', __firstlineno__=384, _flags_=1, _restype_=<_ctypes.PyCSimpleType(__module__='ctypes', __firstlineno__=187, _type_='i', __static_attributes__=(), __dict__=<getset_descriptor at remote 0x7ffff763a4b0>, __weakref__=<getset_descriptor at remote 0x7ffff763a510>, __doc__=None, __ctype_be__=<_ctypes.PyCSimpleType(__module__='ctypes', __firstlineno__=187, _type_='i', __static_attributes__=(...), __dict__=<getset_descriptor at remote 0x7ffff763a630>, __weakref__=<getset_descriptor at remote 0x7ffff763a690>, __doc__=None, __ctype_le__=<...>, __ctype_be__=<...>) at re...(truncated),
locals=locals@entry={'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <SourceFileLoader(name='__main__', path='/home/kila/stuff/python-poc.py') at remote 0x7ffff77b2760>, '__spec__': None, '__annotations__': {}, '__builtins__': <module at remote 0x7ffff7c36990>, '__file__': '/home/kila/stuff/python-poc.py', '__cached__': None, 'ctypes': <module at remote 0x7ffff7616c30>, 'libc': <CDLL(_name=None, _FuncPtr=<_ctypes.PyCFuncPtrType(__module__='ctypes', __firstlineno__=384, _flags_=1, _restype_=<_ctypes.PyCSimpleType(__module__='ctypes', __firstlineno__=187, _type_='i', __static_attributes__=(), __dict__=<getset_descriptor at remote 0x7ffff763a4b0>, __weakref__=<getset_descriptor at remote 0x7ffff763a510>, __doc__=None, __ctype_be__=<_ctypes.PyCSimpleType(__module__='ctypes', __firstlineno__=187, _type_='i', __static_attributes__=(...), __dict__=<getset_descriptor at remote 0x7ffff763a630>, __weakref__=<getset_descriptor at remote 0x7ffff763a690>, __doc__=None, __ctype_le__=<...>, __ctype_be__=<...>) at re...(truncated)) at ../Python/ceval.c:604
#10 0x0000000000647f82 in run_eval_code_obj (tstate=tstate@entry=0xafbe80 <_PyRuntime+299040>, co=co@entry=0x7ffff7bc9e00,
globals=globals@entry={'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <SourceFileLoader(name='__main__', path='/home/kila/stuff/python-poc.py') at remote 0x7ffff77b2760>, '__spec__': None, '__annotations__': {}, '__builtins__': <module at remote 0x7ffff7c36990>, '__file__': '/home/kila/stuff/python-poc.py', '__cached__': None, 'ctypes': <module at remote 0x7ffff7616c30>, 'libc': <CDLL(_name=None, _FuncPtr=<_ctypes.PyCFuncPtrType(__module__='ctypes', __firstlineno__=384, _flags_=1, _restype_=<_ctypes.PyCSimpleType(__module__='ctypes', __firstlineno__=187, _type_='i', __static_attributes__=(), __dict__=<getset_descriptor at remote 0x7ffff763a4b0>, __weakref__=<getset_descriptor at remote 0x7ffff763a510>, __doc__=None, __ctype_be__=<_ctypes.PyCSimpleType(__module__='ctypes', __firstlineno__=187, _type_='i', __static_attributes__=(...), __dict__=<getset_descriptor at remote 0x7ffff763a630>, __weakref__=<getset_descriptor at remote 0x7ffff763a690>, __doc__=None, __ctype_le__=<...>, __ctype_be__=<...>) at re...(truncated),
locals=locals@entry={'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <SourceFileLoader(name='__main__', path='/home/kila/stuff/python-poc.py') at remote 0x7ffff77b2760>, '__spec__': None, '__annotations__': {}, '__builtins__': <module at remote 0x7ffff7c36990>, '__file__': '/home/kila/stuff/python-poc.py', '__cached__': None, 'ctypes': <module at remote 0x7ffff7616c30>, 'libc': <CDLL(_name=None, _FuncPtr=<_ctypes.PyCFuncPtrType(__module__='ctypes', __firstlineno__=384, _flags_=1, _restype_=<_ctypes.PyCSimpleType(__module__='ctypes', __firstlineno__=187, _type_='i', __static_attributes__=(), __dict__=<getset_descriptor at remote 0x7ffff763a4b0>, __weakref__=<getset_descriptor at remote 0x7ffff763a510>, __doc__=None, __ctype_be__=<_ctypes.PyCSimpleType(__module__='ctypes', __firstlineno__=187, _type_='i', __static_attributes__=(...), __dict__=<getset_descriptor at remote 0x7ffff763a630>, __weakref__=<getset_descriptor at remote 0x7ffff763a690>, __doc__=None, __ctype_le__=<...>, __ctype_be__=<...>) at re...(truncated)) at ../Python/pythonrun.c:1381
#11 0x0000000000648130 in run_mod (mod=mod@entry=0xc56278, filename=filename@entry='/home/kila/stuff/python-poc.py',
globals=globals@entry={'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <SourceFileLoader(name='__main__', path='/home/kila/stuff/python-poc.py') at remote 0x7ffff77b2760>, '__spec__': None, '__annotations__': {}, '__builtins__': <module at remote 0x7ffff7c36990>, '__file__': '/home/kila/stuff/python-poc.py', '__cached__': None, 'ctypes': <module at remote 0x7ffff7616c30>, 'libc': <CDLL(_name=None, _FuncPtr=<_ctypes.PyCFuncPtrType(__module__='ctypes', __firstlineno__=384, _flags_=1, _restype_=<_ctypes.PyCSimpleType(__module__='ctypes', __firstlineno__=187, _type_='i', __static_attributes__=(), __dict__=<getset_descriptor at remote 0x7ffff763a4b0>, __weakref__=<getset_descriptor at remote 0x7ffff763a510>, __doc__=None, __ctype_be__=<_ctypes.PyCSimpleType(__module__='ctypes', __firstlineno__=187, _type_='i', __static_attributes__=(...), __dict__=<getset_descriptor at remote 0x7ffff763a630>, __weakref__=<getset_descriptor at remote 0x7ffff763a690>, __doc__=None, __ctype_le__=<...>, __ctype_be__=<...>) at re...(truncated),
locals=locals@entry={'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <SourceFileLoader(name='__main__', path='/home/kila/stuff/python-poc.py') at remote 0x7ffff77b2760>, '__spec__': None, '__annotations__': {}, '__builtins__': <module at remote 0x7ffff7c36990>, '__file__': '/home/kila/stuff/python-poc.py', '__cached__': None, 'ctypes': <module at remote 0x7ffff7616c30>, 'libc': <CDLL(_name=None, _FuncPtr=<_ctypes.PyCFuncPtrType(__module__='ctypes', __firstlineno__=384, _flags_=1, _restype_=<_ctypes.PyCSimpleType(__module__='ctypes', __firstlineno__=187, _type_='i', __static_attributes__=(), __dict__=<getset_descriptor at remote 0x7ffff763a4b0>, __weakref__=<getset_descriptor at remote 0x7ffff763a510>, __doc__=None, __ctype_be__=<_ctypes.PyCSimpleType(__module__='ctypes', __firstlineno__=187, _type_='i', __static_attributes__=(...), __dict__=<getset_descriptor at remote 0x7ffff763a630>, __weakref__=<getset_descriptor at remote 0x7ffff763a690>, __doc__=None, __ctype_le__=<...>, __ctype_be__=<...>) at re...(truncated), flags=flags@entry=0x7fffffffdf08, arena=arena@entry=0x7ffff76168c0, interactive_src=0x0, generate_new_source=0)
at ../Python/pythonrun.c:1466
#12 0x0000000000648980 in pyrun_file (fp=fp@entry=0xb98e80, filename=filename@entry='/home/kila/stuff/python-poc.py', start=start@entry=257,
globals=globals@entry={'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <SourceFileLoader(name='__main__', path='/home/kila/stuff/python-poc.py') at remote 0x7ffff77b2760>, '__spec__': None, '__annotations__': {}, '__builtins__': <module at remote 0x7ffff7c36990>, '__file__': '/home/kila/stuff/python-poc.py', '__cached__': None, 'ctypes': <module at remote 0x7ffff7616c30>, 'libc': <CDLL(_name=None, _FuncPtr=<_ctypes.PyCFuncPtrType(__module__='ctypes', __firstlineno__=384, _flags_=1, _restype_=<_ctypes.PyCSimpleType(__module__='ctypes', __firstlineno__=187, _type_='i', __static_attributes__=(), __dict__=<getset_descriptor at remote 0x7ffff763a4b0>, __weakref__=<getset_descriptor at remote 0x7ffff763a510>, __doc__=None, __ctype_be__=<_ctypes.PyCSimpleType(__module__='ctypes', __firstlineno__=187, _type_='i', __static_attributes__=(...), __dict__=<getset_descriptor at remote 0x7ffff763a630>, __weakref__=<getset_descriptor at remote 0x7ffff763a690>, __doc__=None, __ctype_le__=<...>, __ctype_be__=<...>) at re...(truncated),
locals=locals@entry={'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <SourceFileLoader(name='__main__', path='/home/kila/stuff/python-poc.py') at remote 0x7ffff77b2760>, '__spec__': None, '__annotations__': {}, '__builtins__': <module at remote 0x7ffff7c36990>, '__file__': '/home/kila/stuff/python-poc.py', '__cached__': None, 'ctypes': <module at remote 0x7ffff7616c30>, 'libc': <CDLL(_name=None, _FuncPtr=<_ctypes.PyCFuncPtrType(__module__='ctypes', __firstlineno__=384, _flags_=1, _restype_=<_ctypes.PyCSimpleType(__module__='ctypes', __firstlineno__=187, _type_='i', __static_attributes__=(), __dict__=<getset_descriptor at remote 0x7ffff763a4b0>, __weakref__=<getset_descriptor at remote 0x7ffff763a510>, __doc__=None, __ctype_be__=<_ctypes.PyCSimpleType(__module__='ctypes', __firstlineno__=187, _type_='i', __static_attributes__=(...), __dict__=<getset_descriptor at remote 0x7ffff763a630>, __weakref__=<getset_descriptor at remote 0x7ffff763a690>, __doc__=None, __ctype_le__=<...>, __ctype_be__=<...>) at re...(truncated), closeit=closeit@entry=1, flags=0x7fffffffdf08) at ../Python/pythonrun.c:1295
#13 0x000000000064a094 in _PyRun_SimpleFileObject (fp=fp@entry=0xb98e80, filename=filename@entry='/home/kila/stuff/python-poc.py', closeit=closeit@entry=1,
flags=flags@entry=0x7fffffffdf08) at ../Python/pythonrun.c:517
#14 0x000000000064a28c in _PyRun_AnyFileObject (fp=fp@entry=0xb98e80, filename=filename@entry='/home/kila/stuff/python-poc.py', closeit=closeit@entry=1,
flags=flags@entry=0x7fffffffdf08) at ../Python/pythonrun.c:77
#15 0x000000000066dc27 in pymain_run_file_obj (program_name=program_name@entry='/usr/bin/python3-dbg', filename=filename@entry='/home/kila/stuff/python-poc.py',
skip_source_first_line=0) at ../Modules/main.c:410
#16 0x000000000066dd33 in pymain_run_file (config=config@entry=0xace568 <_PyRuntime+112392>) at ../Modules/main.c:429
#17 0x000000000066e749 in pymain_run_python (exitcode=exitcode@entry=0x7fffffffe06c) at ../Modules/main.c:697
#18 0x000000000066e9b7 in Py_RunMain () at ../Modules/main.c:776
#19 0x000000000066ea0b in pymain_main (args=args@entry=0x7fffffffe0b0) at ../Modules/main.c:806
#20 0x000000000066ea93 in Py_BytesMain (argc=argc@entry=2, argv=argv@entry=0x7fffffffe1f8) at ../Modules/main.c:830
#21 0x00000000004220f3 in main (argc=argc@entry=2, argv=argv@entry=0x7fffffffe1f8) at ../Programs/python.c:15
#22 0x00007ffff7cb8dba in __libc_start_call_main (main=main@entry=0x4220e6 <main>, argc=argc@entry=2, argv=argv@entry=0x7fffffffe1f8)
at ../sysdeps/nptl/libc_start_call_main.h:58
#23 0x00007ffff7cb8e75 in __libc_start_main_impl (main=0x4220e6 <main>, argc=2, argv=0x7fffffffe1f8, init=<optimized out>, fini=<optimized out>,
rtld_fini=<optimized out>, stack_end=0x7fffffffe1e8) at ../csu/libc-start.c:360
#24 0x0000000000422021 in _start ()
CPython versions tested on:
3.13, 3.12, 3.11
Operating systems tested on:
Linux
Output from running 'python -VV' on the command line:
Python 3.13.5 (main, Jun 25 2025, 18:55:22) [GCC 14.2.0]