python / python/cpython

Use-after-free in `memoryview` slicing via re-entrant `__index__`

Abierto
#142,665 0 comentarios 0 reacciones 1 asignado Ver en GitHub

@picnixz ya está trabajando en esto.

Desde el 1/1/2026.

interpreter-core type-crash
Lenguaje dominante
Python
Estrellas
77.2k
Forks
35.9k
Métricas de merge de PR
Métricas de PR pendientes

Descripción

What happened?

memory_subscript() creates a new view (mbuf_add_view) before parsing slice indices. If a slice bound’s __index__ releases/truncates the underlying buffer (e.g., mv.release(); ftruncate()), the newly created subview keeps a dangling pointer. Subsequent access (e.g., sub[0]) dereferences freed/invalid memory, crashing in unpack_single.

Proof of Concept:

import mmap, os, tempfile

fd, path = tempfile.mkstemp()
os.write(fd, b"A" * 4096)
mm = mmap.mmap(fd, 4096, access=mmap.ACCESS_WRITE)
mv = memoryview(mm)

class Trigger:
    def __index__(self):
        mv.release()
        os.ftruncate(fd, 0)
        return 0

try:
    sub = mv[slice(Trigger(), None, None)]
    sub[0] # Trigger the slicing to call __index__
finally:
    mm.close()
    os.close(fd)
    os.unlink(path)

Related Code Snippet

static PyObject *
memory_subscript(PyObject *_self, PyObject *key)
{
    PyMemoryViewObject *self = (PyMemoryViewObject *)_self;
    Py_buffer *view;
    view = &(self->view);

    CHECK_RELEASED(self);

    if (view->ndim == 0) {
        if (PyTuple_Check(key) && PyTuple_GET_SIZE(key) == 0) {
            const char *fmt = adjust_fmt(view);
            if (fmt == NULL)
                return NULL;
            return unpack_single(self, view->buf, fmt);
        }
        else if (key == Py_Ellipsis) {
            return Py_NewRef(self);
        }
        else {
            PyErr_SetString(PyExc_TypeError,
                "invalid indexing of 0-dim memory");
            return NULL;
        }
    }

    if (_PyIndex_Check(key)) {
        Py_ssize_t index;
        index = PyNumber_AsSsize_t(key, PyExc_IndexError);
        if (index == -1 && PyErr_Occurred())
            return NULL;
        return memory_item((PyObject *)self, index);
    }
    else if (PySlice_Check(key)) {
        CHECK_RESTRICTED(self);
        PyMemoryViewObject *sliced;

        sliced = (PyMemoryViewObject *)mbuf_add_view(self->mbuf, view);
        if (sliced == NULL)
            return NULL;
				
		// Call __index__ method which close the mmap memory while sliced still holds its pointer
        if (init_slice(&sliced->view, key, 0) < 0) {
            Py_DECREF(sliced);
            return NULL;
        }
        init_len(&sliced->view);
        init_flags(sliced);

        return (PyObject *)sliced;
    }
    else if (is_multiindex(key)) {
        return memory_item_multi(self, key);
    }
    else if (is_multislice(key)) {
        PyErr_SetString(PyExc_NotImplementedError,
            "multi-dimensional slicing is not implemented");
        return NULL;
    }

    PyErr_SetString(PyExc_TypeError, "memoryview: invalid slice key");
    return NULL;
}

Affected Versions:

Python Version Status Exit Code
Python 3.9.24+ (heads/3.9:9c4638d, Oct 17 2025, 11:19:30) ASAN 1
Python 3.10.19+ (heads/3.10:0142619, Oct 17 2025, 11:20:05) [GCC 13.3.0] ASAN 1
Python 3.11.14+ (heads/3.11:88f3f5b, Oct 17 2025, 11:20:44) [GCC 13.3.0] ASAN 1
Python 3.12.12+ (heads/3.12:8cb2092, Oct 17 2025, 11:21:35) [GCC 13.3.0] ASAN 1
Python 3.13.9+ (heads/3.13:0760a57, Oct 17 2025, 11:22:25) [GCC 13.3.0] ASAN 1
Python 3.14.0+ (heads/3.14:889e918, Oct 17 2025, 11:23:02) [GCC 13.3.0] ASAN 1
Python 3.15.0a1+ (heads/main:fbf0843, Oct 17 2025, 11:23:37) [GCC 13.3.0] ASAN 1

Sanitizer Output

=================================================================
==1532703==ERROR: AddressSanitizer: BUS on unknown address (pc 0x5ae17c7bd8ce bp 0x7fffb3a81570 sp 0x7fffb3a814b0 T0)
==1532703==The signal is caused by a READ memory access.
==1532703==Hint: this fault was caused by a dereference of a high value address (see register values below).  Disassemble the provided pc to learn which register was used.
    #0 0x5ae17c7bd8ce in unpack_single Objects/memoryobject.c:1842
    #1 0x5ae17c7be2c2 in memory_item Objects/memoryobject.c:2474
    #2 0x5ae17c7c31d6 in memory_subscript Objects/memoryobject.c:2610
    #3 0x5ae17c6e5393 in PyObject_GetItem Objects/abstract.c:163
    #4 0x5ae17c98a8f8 in _PyEval_EvalFrameDefault Python/generated_cases.c.h:62
    #5 0x5ae17c9d7e54 in _PyEval_EvalFrame Include/internal/pycore_ceval.h:121
    #6 0x5ae17c9d8148 in _PyEval_Vector Python/ceval.c:2001
    #7 0x5ae17c9d83f8 in PyEval_EvalCode Python/ceval.c:884
    #8 0x5ae17cacf507 in run_eval_code_obj Python/pythonrun.c:1365
    #9 0x5ae17cacf723 in run_mod Python/pythonrun.c:1459
    #10 0x5ae17cad057a in pyrun_file Python/pythonrun.c:1293
    #11 0x5ae17cad3220 in _PyRun_SimpleFileObject Python/pythonrun.c:521
    #12 0x5ae17cad34f6 in _PyRun_AnyFileObject Python/pythonrun.c:81
    #13 0x5ae17cb2474d in pymain_run_file_obj Modules/main.c:410
    #14 0x5ae17cb249b4 in pymain_run_file Modules/main.c:429
    #15 0x5ae17cb261b2 in pymain_run_python Modules/main.c:691
    #16 0x5ae17cb26842 in Py_RunMain Modules/main.c:772
    #17 0x5ae17cb26a2e in pymain_main Modules/main.c:802
    #18 0x5ae17cb26db3 in Py_BytesMain Modules/main.c:826
    #19 0x5ae17c5aa645 in main Programs/python.c:15
    #20 0x715a34c2a1c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
    #21 0x715a34c2a28a in __libc_start_main_impl ../csu/libc-start.c:360
    #22 0x5ae17c5aa574 in _start (/home/jackfromeast/Desktop/entropy/tasks/grammar-afl++-latest/targets/cpython/python+0x2dd574) (BuildId: ff3dc40ea460bd4beb2c3a72283cca525b319bf0)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: BUS Objects/memoryobject.c:1842 in unpack_single
==1532703==ABORTING
Linked PRs
  • gh-143324

Guía de contribución

Abrir la guía de contribución

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Evaluación

Este issue todavía no se ha evaluado.

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.