-
-
Notifications
You must be signed in to change notification settings - Fork 33.9k
Open
Labels
interpreter-core(Objects, Python, Grammar, and Parser dirs)(Objects, Python, Grammar, and Parser dirs)type-crashA hard crash of the interpreter, possibly with a core dumpA hard crash of the interpreter, possibly with a core dump
Description
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
Metadata
Metadata
Assignees
Labels
interpreter-core(Objects, Python, Grammar, and Parser dirs)(Objects, Python, Grammar, and Parser dirs)type-crashA hard crash of the interpreter, possibly with a core dumpA hard crash of the interpreter, possibly with a core dump