Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletionsLib/test/test_array.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,6 +8,7 @@
from test.support import import_helper
from test.support import os_helper
from test.support import _2G
from test.support import subTests
import weakref
import pickle
import operator
Expand DownExpand Up@@ -1680,6 +1681,45 @@ def test_gh_128961(self):
it.__setstate__(0)
self.assertRaises(StopIteration, next, it)

# Tests for NULL pointer dereference in array.__setitem__
# when the index conversion mutates the array.
# See: https://github.com/python/cpython/issues/142555.

@subTests("dtype", ["b", "B", "h", "H", "i", "l", "q", "I", "L", "Q"])
def test_setitem_use_after_clear_with_int_data(self, dtype):
victim = array.array(dtype, list(range(64)))

class Index:
def __index__(self):
victim.clear()
return 0

self.assertRaises(IndexError, victim.__setitem__, 1, Index())
self.assertEqual(len(victim), 0)

def test_setitem_use_after_shrink_with_int_data(self):
victim = array.array('b', [1, 2, 3])

class Index:
def __index__(self):
victim.pop()
victim.pop()
return 0

self.assertRaises(IndexError, victim.__setitem__, 1, Index())

@subTests("dtype", ["f", "d"])
def test_setitem_use_after_clear_with_float_data(self, dtype):
victim = array.array(dtype, [1.0, 2.0, 3.0])

class Float:
def __float__(self):
victim.clear()
return 0.0

self.assertRaises(IndexError, victim.__setitem__, 1, Float())
self.assertEqual(len(victim), 0)


if __name__ == "__main__":
unittest.main()
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
:mod:`array`: fix a crash in ``a[i] = v`` when converting *i* to
an index via :meth:`i.__index__ <object.__index__>` or :meth:`i.__float__
<object.__float__>` mutates the array.
65 changes: 64 additions & 1 deletionModules/arraymodule.c
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -205,6 +205,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)
{
Expand All@@ -221,7 +248,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;
Expand DownExpand Up@@ -250,6 +280,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;
Expand DownExpand Up@@ -342,6 +375,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;
Expand DownExpand Up@@ -371,6 +407,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;
Expand All@@ -389,6 +428,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;
Expand DownExpand Up@@ -429,6 +471,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;

Expand All@@ -450,6 +495,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;
Expand DownExpand Up@@ -481,6 +529,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;

Expand All@@ -502,6 +553,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;
Expand DownExpand Up@@ -534,6 +588,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;

Expand All@@ -555,6 +612,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;
Expand All@@ -572,6 +632,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;
Expand Down
Loading

[8]ページ先頭

©2009-2026 Movatter.jp