[3.13] gh-142555: Fix null pointer dereference in array.__setitem__ via re-entrant __index__ (GH-142713) (#144397)

gh-142555: Fix null pointer dereference in array.__setitem__ via re-entrant __index__ (GH-142713)
(cherry picked from commit 39f16a93ef)

Co-authored-by: AN Long <aisk@users.noreply.github.com>
This commit is contained in:
Miss Islington (bot) 2026-02-02 21:06:59 +01:00 committed by GitHub
parent bb1247d83c
commit b74e3a4c05
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 107 additions and 1 deletions

View file

@ -202,6 +202,33 @@ Note that the basic Get and Set functions do NOT check that the index is
in bounds; that's the responsibility of the caller.
****************************************************************************/
/* Macro to check array buffer validity and bounds after calling
user-defined methods (like __index__ or __float__) that might modify
the array during the call.
*/
#define CHECK_ARRAY_BOUNDS(OP, IDX) \
do { \
if ((IDX) >= 0 && ((OP)->ob_item == NULL || \
(IDX) >= Py_SIZE((OP)))) { \
PyErr_SetString(PyExc_IndexError, \
"array assignment index out of range"); \
return -1; \
} \
} while (0)
#define CHECK_ARRAY_BOUNDS_WITH_CLEANUP(OP, IDX, VAL, CLEANUP) \
do { \
if ((IDX) >= 0 && ((OP)->ob_item == NULL || \
(IDX) >= Py_SIZE((OP)))) { \
PyErr_SetString(PyExc_IndexError, \
"array assignment index out of range"); \
if (CLEANUP) { \
Py_DECREF(VAL); \
} \
return -1; \
} \
} while (0)
static PyObject *
b_getitem(arrayobject *ap, Py_ssize_t i)
{
@ -218,7 +245,10 @@ b_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v)
the overflow checking */
if (!PyArg_Parse(v, "h;array item must be integer", &x))
return -1;
else if (x < -128) {
CHECK_ARRAY_BOUNDS(ap, i);
if (x < -128) {
PyErr_SetString(PyExc_OverflowError,
"signed char is less than minimum");
return -1;
@ -247,6 +277,9 @@ BB_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v)
/* 'B' == unsigned char, maps to PyArg_Parse's 'b' formatter */
if (!PyArg_Parse(v, "b;array item must be integer", &x))
return -1;
CHECK_ARRAY_BOUNDS(ap, i);
if (i >= 0)
((unsigned char *)ap->ob_item)[i] = x;
return 0;
@ -323,6 +356,9 @@ h_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v)
/* 'h' == signed short, maps to PyArg_Parse's 'h' formatter */
if (!PyArg_Parse(v, "h;array item must be integer", &x))
return -1;
CHECK_ARRAY_BOUNDS(ap, i);
if (i >= 0)
((short *)ap->ob_item)[i] = x;
return 0;
@ -352,6 +388,9 @@ HH_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v)
"unsigned short is greater than maximum");
return -1;
}
CHECK_ARRAY_BOUNDS(ap, i);
if (i >= 0)
((short *)ap->ob_item)[i] = (short)x;
return 0;
@ -370,6 +409,9 @@ i_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v)
/* 'i' == signed int, maps to PyArg_Parse's 'i' formatter */
if (!PyArg_Parse(v, "i;array item must be integer", &x))
return -1;
CHECK_ARRAY_BOUNDS(ap, i);
if (i >= 0)
((int *)ap->ob_item)[i] = x;
return 0;
@ -410,6 +452,9 @@ II_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v)
}
return -1;
}
CHECK_ARRAY_BOUNDS_WITH_CLEANUP(ap, i, v, do_decref);
if (i >= 0)
((unsigned int *)ap->ob_item)[i] = (unsigned int)x;
@ -431,6 +476,9 @@ l_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v)
long x;
if (!PyArg_Parse(v, "l;array item must be integer", &x))
return -1;
CHECK_ARRAY_BOUNDS(ap, i);
if (i >= 0)
((long *)ap->ob_item)[i] = x;
return 0;
@ -462,6 +510,9 @@ LL_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v)
}
return -1;
}
CHECK_ARRAY_BOUNDS_WITH_CLEANUP(ap, i, v, do_decref);
if (i >= 0)
((unsigned long *)ap->ob_item)[i] = x;
@ -483,6 +534,9 @@ q_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v)
long long x;
if (!PyArg_Parse(v, "L;array item must be integer", &x))
return -1;
CHECK_ARRAY_BOUNDS(ap, i);
if (i >= 0)
((long long *)ap->ob_item)[i] = x;
return 0;
@ -515,6 +569,9 @@ QQ_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v)
}
return -1;
}
CHECK_ARRAY_BOUNDS_WITH_CLEANUP(ap, i, v, do_decref);
if (i >= 0)
((unsigned long long *)ap->ob_item)[i] = x;
@ -536,6 +593,9 @@ f_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v)
float x;
if (!PyArg_Parse(v, "f;array item must be float", &x))
return -1;
CHECK_ARRAY_BOUNDS(ap, i);
if (i >= 0)
((float *)ap->ob_item)[i] = x;
return 0;
@ -553,6 +613,9 @@ d_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v)
double x;
if (!PyArg_Parse(v, "d;array item must be float", &x))
return -1;
CHECK_ARRAY_BOUNDS(ap, i);
if (i >= 0)
((double *)ap->ob_item)[i] = x;
return 0;