python / python/cpython

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

Open
#142,665 0 comments 0 reactions 1 assignee View on GitHub

@picnixz is already working on this.

Since Jan 1, 2026.

interpreter-core type-crash
Dominant language
Python
Stars
77.2k
Forks
35.9k
PR merge metrics
PR metrics pending

Description

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

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.