static PyObject *
memory_richcompare(PyObject *v, PyObject *w, int op)
{
PyObject *res;
Py_buffer wbuf, *vv;
Py_buffer *ww = NULL;
struct unpacker *unpack_v = NULL;
struct unpacker *unpack_w = NULL;
char vfmt, wfmt;
int equal = MV_COMPARE_NOT_IMPL;
...
else if (vv->ndim == 1) {
equal = cmp_base(vv->buf, ww->buf, vv->shape,
vv->strides, vv->suboffsets,
ww->strides, ww->suboffsets,
vfmt, unpack_v, unpack_w);
}
}
static int
cmp_base(const char *p, const char *q, const Py_ssize_t *shape,
const Py_ssize_t *pstrides, const Py_ssize_t *psuboffsets,
const Py_ssize_t *qstrides, const Py_ssize_t *qsuboffsets,
char fmt, struct unpacker *unpack_p, struct unpacker *unpack_q)
{
Py_ssize_t i;
int equal;
for (i = 0; i < shape[0]; p+=pstrides[0], q+=qstrides[0], i++) {
const char *xp = ADJUST_PTR(p, psuboffsets, 0);
const char *xq = ADJUST_PTR(q, qsuboffsets, 0);
equal = unpack_cmp(xp, xq, fmt, unpack_p, unpack_q);
if (equal <= 0)
return equal;
}
return 1;
}
/* unpack a single item */
static PyObject *
struct_unpack_single(const char *ptr, struct unpacker *x)
{
PyObject *v;
// Bug: The second time comparsion will trigger the crash as the first unpack free the old buffer
memcpy(x->item, ptr, x->itemsize);
// Override Struct.unpack_from has been called on each element.
v = PyObject_CallOneArg(x->unpack_from, x->mview);
if (v == NULL)
return NULL;
if (PyTuple_GET_SIZE(v) == 1) {
PyObject *res = Py_NewRef(PyTuple_GET_ITEM(v, 0));
Py_DECREF(v);
return res;
}
return v;
}
What happened?
Comparing two 1-D
memoryviews iterates element-by-element. For each element,struct_unpack_single()firstmemcpys from the left view’s saved pointer, then calls the type’sunpack_from. A subclassedunpack_fromcanmv.release()and resize the exportingarray('d'), reallocating and freeing the original buffer. The loop then advances to the next element using that stale pointer, so the nextmemcpydereferences freed memory, causing a heap use-after-free.Proof of Concept:
Affected Versions:
Details
Python 3.9.24+ (heads/3.9:9c4638d, Oct 17 2025, 11:19:30)Python 3.10.19+ (heads/3.10:0142619, Oct 17 2025, 11:20:05) [GCC 13.3.0]Python 3.11.14+ (heads/3.11:88f3f5b, Oct 17 2025, 11:20:44) [GCC 13.3.0]Python 3.12.12+ (heads/3.12:8cb2092, Oct 17 2025, 11:21:35) [GCC 13.3.0]Python 3.13.9+ (heads/3.13:0760a57, Oct 17 2025, 11:22:25) [GCC 13.3.0]Python 3.14.0+ (heads/3.14:889e918, Oct 17 2025, 11:23:02) [GCC 13.3.0]Python 3.15.0a1+ (heads/main:fbf0843, Oct 17 2025, 11:23:37) [GCC 13.3.0]Related Code Snippet
Details
Sanitizer Output
Details
Linked PRs