Skip to content

Segfaults in _ctypes during _build_callargs #138008

Closed

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]

Linked PRs

Activity

  1. added
    type-crashA hard crash of the interpreter, possibly with a core dump
    on Aug 20, 2025
  2. ZeroIntensity commented on Aug 21, 2025

    @ZeroIntensity
    Member

    Sounds like a bug at first glance. Would you like to submit a PR?

  3. added a commit that references this issue on Aug 26, 2025
  4. changed the title [-]Segfaults in _cyptes during _build_callargs[/-] [+]Segfaults in _ctypes during _build_callargs[/+] on Aug 31, 2025
  5. magnified103 commented on Aug 31, 2025

    @magnified103
    ContributorAuthor

    Sounds like a bug at first glance. Would you like to submit a PR?

    I opened a PR with the fix.

  6. added a commit that references this issue on Sep 10, 2025
  7. added a commit that references this issue on Sep 10, 2025
  8. added a commit that references this issue on Sep 10, 2025
  9. added a commit that references this issue on Sep 18, 2025
  10. added a commit that references this issue on Oct 7, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions