Skip to content

Segfaults in _cyptes during _build_callargs #138008

@magnified103

Description

@magnified103

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:

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:

#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]

Metadata

Metadata

Assignees

No one assigned

    Labels

    extension-modulesC modules in the Modules dirtopic-ctypestype-crashA hard crash of the interpreter, possibly with a core dump

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions