Skip to content

Type confusion in SimpleNamespace.__replace__ via re-entrant __new__ #143636

Closed

Description

@jackfromeast

What happened?

SimpleNamespace.__replace__ constructs the replacement object with Py_TYPE(self) and immediately treats the result as a namespace. A subclass can override __new__ to return a non-namespace object with a crafted layout so ns_dict points near NULL. The subsequent PyDict_Update passes that bogus pointer to dict_merge, which reads through _Py_TYPE and crashes.

Proof of Concept:

from types import SimpleNamespace

class Trigger(SimpleNamespace):
    armed = False

    def __new__(cls, *args, **kwargs):
        if cls.armed:
            # Return an object with a non-null length field so ns_dict points at 0x1
            return [0]
        cls.armed = True
        return super().__new__(cls)

Trigger().__replace__()

Vulnerable Code Snippet:

Click to expand
/* Buggy Re-entrant Path */
static PyObject *
namespace_replace(PyObject *self, PyObject *args, PyObject *kwargs)
{
    PyObject *result = PyObject_CallNoArgs((PyObject *)Py_TYPE(self));  /* Reentrant call site */
    if (!result) {
        return NULL;
    }
    if (PyDict_Update(((_PyNamespaceObject*)result)->ns_dict,  /* crashing pointer derived */
                      ((_PyNamespaceObject*)self)->ns_dict) < 0)
    {
        Py_DECREF(result);
        return NULL;
    }
    ...
}

static int
dict_merge(PyInterpreterState *interp, PyObject *a, PyObject *b, int override)
{
    if (a == NULL || !PyDict_Check(a) || b == NULL) {  /* Crash site */
        PyErr_BadInternalCall();
        return -1;
    }
    ...
}

/* Clobbering Path */
static PyObject *
type_call(PyObject *self, PyObject *args, PyObject *kwds)
{
    PyTypeObject *type = PyTypeObject_CAST(self);
    PyObject *obj;
    /* ... */
    obj = type->tp_new(type, args, kwds);  /* Reentrant call site */
    /* If the returned object is not an instance of type, it won't be initialized. */
    if (!PyObject_TypeCheck(obj, type))
        return obj;  /* state mutate site */
    /* ... */
    return obj;
}

Sanitizer Output:

Click to expand
AddressSanitizer:DEADLYSIGNAL
=================================================================
==438252==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000009 (pc 0x57f864c2bde4 bp 0x7ffdbf4514e0 sp 0x7ffdbf4513d0 T0)
==438252==The signal is caused by a READ memory access.
==438252==Hint: address points to the zero page.
    #0 0x57f864c2bde4 in _Py_TYPE Include/object.h:277
    #1 0x57f864c2bde4 in dict_merge Objects/dictobject.c:3964
    #2 0x57f864c71488 in namespace_replace Objects/namespaceobject.c:237
    #3 0x57f864b83371 in method_vectorcall_VARARGS_KEYWORDS Objects/descrobject.c:359
    #4 0x57f864b573e7 in _PyObject_VectorcallTstate Include/internal/pycore_call.h:169
    #5 0x57f864b573e7 in PyObject_Vectorcall Objects/call.c:327
    #6 0x57f864a0b5a2 in _PyEval_EvalFrameDefault Python/generated_cases.c.h:1620
    #7 0x57f864ed5ad6 in _PyEval_EvalFrame Include/internal/pycore_ceval.h:121
    #8 0x57f864ed5ad6 in _PyEval_Vector Python/ceval.c:2001
    #9 0x57f864ed5ad6 in PyEval_EvalCode Python/ceval.c:884
    #10 0x57f86501b16e in run_eval_code_obj Python/pythonrun.c:1365
    #11 0x57f86501b16e in run_mod Python/pythonrun.c:1459
    #12 0x57f86501fe17 in pyrun_file Python/pythonrun.c:1293
    #13 0x57f86501fe17 in _PyRun_SimpleFileObject Python/pythonrun.c:521
    #14 0x57f86502093c in _PyRun_AnyFileObject Python/pythonrun.c:81
    #15 0x57f865093e3c in pymain_run_file_obj Modules/main.c:410
    #16 0x57f865093e3c in pymain_run_file Modules/main.c:429
    #17 0x57f865093e3c in pymain_run_python Modules/main.c:691
    #18 0x57f86509571e in Py_RunMain Modules/main.c:772
    #19 0x57f86509571e in pymain_main Modules/main.c:802
    #20 0x57f86509571e in Py_BytesMain Modules/main.c:826
    #21 0x7ce0f822a1c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
    #22 0x7ce0f822a28a in __libc_start_main_impl ../csu/libc-start.c:360
    #23 0x57f864a2f634 in _start (/home/jackfromeast/Desktop/entropy/targets/grammar-afl++-latest/targets/cpython/python+0x206634) (BuildId: 4d105290d0ad566a4d6f4f7b2f05fbc9e317b533)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV Include/object.h:277 in _Py_TYPE
==438252==ABORTING

CPython versions tested on:

Details
Python Version Status Exit Code
Python 3.9.24+ (heads/3.9:111bbc15b26, Oct 28 2025, 16:51:20) Exception 1
Python 3.10.19+ (heads/3.10:014261980b1, Oct 28 2025, 16:52:08) [Clang 18.1.3 (1ubuntu1)] Exception 1
Python 3.11.14+ (heads/3.11:88f3f5b5f11, Oct 28 2025, 16:53:08) [Clang 18.1.3 (1ubuntu1)] Exception 1
Python 3.12.12+ (heads/3.12:8cb2092bd8c, Oct 28 2025, 16:54:14) [Clang 18.1.3 (1ubuntu1)] Exception 1
Python 3.13.9+ (heads/3.13:9c8eade20c6, Oct 28 2025, 16:55:18) [Clang 18.1.3 (1ubuntu1)] ASAN 1
Python 3.14.0+ (heads/3.14:2e216728038, Oct 28 2025, 16:56:16) [Clang 18.1.3 (1ubuntu1)] ASAN 1
Python 3.15.0a1+ (heads/main:f5394c257ce, Oct 28 2025, 19:29:54) [GCC 13.3.0] ASAN 1

Output from running 'python -VV' on the command line:

Python 3.15.0a1+ (heads/main:f5394c257ce, Oct 28 2025, 19:29:54) [GCC 13.3.0]

Linked PRs

Activity

  1. added
    interpreter-core(Objects, Python, Grammar, and Parser dirs)
    type-crashA hard crash of the interpreter, possibly with a core dump
    on Jan 10, 2026
  2. self-assigned this
    on Jan 10, 2026
  3. added a commit that references this issue on Jan 10, 2026
  4. added a commit that references this issue on Mar 14, 2026
  5. added a commit that references this issue on Mar 14, 2026
  6. picnixz commented on Mar 14, 2026

    @picnixz
    Member

    Thanks for the report!

  7. added 2 commits that reference this issue on Mar 14, 2026
  8. added a commit that references this issue on Apr 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

interpreter-core(Objects, Python, Grammar, and Parser dirs)type-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