diff options
author | Victor Stinner <vstinner@python.org> | 2023-06-28 01:45:57 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-06-28 01:45:57 (GMT) |
commit | 3f8483cad2f3b94600c3ecf3f0bb220bb1e61d7d (patch) | |
tree | d2a68be122484d443ab9260b2b542a9c509defe1 | |
parent | 161012fc25910a47423bae8012398bf519a88140 (diff) | |
download | cpython-3f8483cad2f3b94600c3ecf3f0bb220bb1e61d7d.zip cpython-3f8483cad2f3b94600c3ecf3f0bb220bb1e61d7d.tar.gz cpython-3f8483cad2f3b94600c3ecf3f0bb220bb1e61d7d.tar.bz2 |
gh-106168: PyTuple_SET_ITEM() now checks the index (#106164)
PyTuple_SET_ITEM() and PyList_SET_ITEM() now check the index argument
with an assertion if Python is built in debug mode or is built with
assertions.
* list_extend() and _PyList_AppendTakeRef() now set the list size
before calling PyList_SET_ITEM().
* PyStructSequence_GetItem() and PyStructSequence_SetItem() now check
the index argument: must be lesser than REAL_SIZE(op).
* PyStructSequence_GET_ITEM() and PyStructSequence_SET_ITEM() are now
aliases to PyStructSequence_GetItem() and
PyStructSequence_SetItem().
-rw-r--r-- | Doc/c-api/list.rst | 4 | ||||
-rw-r--r-- | Doc/c-api/tuple.rst | 23 | ||||
-rw-r--r-- | Doc/whatsnew/3.13.rst | 6 | ||||
-rw-r--r-- | Include/cpython/listobject.h | 2 | ||||
-rw-r--r-- | Include/cpython/tupleobject.h | 2 | ||||
-rw-r--r-- | Include/internal/pycore_list.h | 2 | ||||
-rw-r--r-- | Include/structseq.h | 13 | ||||
-rw-r--r-- | Misc/NEWS.d/next/C API/2023-06-28-02-30-50.gh-issue-106168.NFOZPv.rst | 5 | ||||
-rw-r--r-- | Objects/listobject.c | 5 | ||||
-rw-r--r-- | Objects/structseq.c | 23 |
10 files changed, 62 insertions, 23 deletions
diff --git a/Doc/c-api/list.rst b/Doc/c-api/list.rst index dbf3561..c15cecd 100644 --- a/Doc/c-api/list.rst +++ b/Doc/c-api/list.rst @@ -86,6 +86,10 @@ List Objects Macro form of :c:func:`PyList_SetItem` without error checking. This is normally only used to fill in new lists where there is no previous content. + Bounds checking is performed as an assertion if Python is built in + :ref:`debug mode <debug-build>` or :option:`with assertions + <--with-assertions>`. + .. note:: This macro "steals" a reference to *item*, and, unlike diff --git a/Doc/c-api/tuple.rst b/Doc/c-api/tuple.rst index ac62058..3fe1062 100644 --- a/Doc/c-api/tuple.rst +++ b/Doc/c-api/tuple.rst @@ -89,6 +89,9 @@ Tuple Objects Like :c:func:`PyTuple_SetItem`, but does no error checking, and should *only* be used to fill in brand new tuples. + Bounds checking is performed as an assertion if Python is built in + :ref:`debug mode <debug-build>` or :option:`with assertions <--with-assertions>`. + .. note:: This function "steals" a reference to *o*, and, unlike @@ -194,12 +197,17 @@ type. .. c:function:: PyObject* PyStructSequence_GetItem(PyObject *p, Py_ssize_t pos) Return the object at position *pos* in the struct sequence pointed to by *p*. - No bounds checking is performed. + + Bounds checking is performed as an assertion if Python is built in + :ref:`debug mode <debug-build>` or :option:`with assertions <--with-assertions>`. .. c:function:: PyObject* PyStructSequence_GET_ITEM(PyObject *p, Py_ssize_t pos) - Macro equivalent of :c:func:`PyStructSequence_GetItem`. + Alias to :c:func:`PyStructSequence_GetItem`. + + .. versionchanged:: 3.13 + Now implemented as an alias to :c:func:`PyStructSequence_GetItem`. .. c:function:: void PyStructSequence_SetItem(PyObject *p, Py_ssize_t pos, PyObject *o) @@ -208,6 +216,9 @@ type. :c:func:`PyTuple_SET_ITEM`, this should only be used to fill in brand new instances. + Bounds checking is performed as an assertion if Python is built in + :ref:`debug mode <debug-build>` or :option:`with assertions <--with-assertions>`. + .. note:: This function "steals" a reference to *o*. @@ -215,9 +226,7 @@ type. .. c:function:: void PyStructSequence_SET_ITEM(PyObject *p, Py_ssize_t *pos, PyObject *o) - Similar to :c:func:`PyStructSequence_SetItem`, but implemented as a static - inlined function. + Alias to :c:func:`PyStructSequence_SetItem`. - .. note:: - - This function "steals" a reference to *o*. + .. versionchanged:: 3.13 + Now implemented as an alias to :c:func:`PyStructSequence_SetItem`. diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index f3460be..c0e9e92 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -441,6 +441,12 @@ New Features ``NULL`` if the referent is no longer live. (Contributed by Victor Stinner in :gh:`105927`.) +* If Python is built in :ref:`debug mode <debug-build>` or :option:`with + assertions <--with-assertions>`, :c:func:`PyTuple_SET_ITEM` and + :c:func:`PyList_SET_ITEM` now check the index argument with an assertion. + If the assertion fails, make sure that the size is set before. + (Contributed by Victor Stinner in :gh:`106168`.) + Porting to Python 3.13 ---------------------- diff --git a/Include/cpython/listobject.h b/Include/cpython/listobject.h index 8fa8212..b3b2398 100644 --- a/Include/cpython/listobject.h +++ b/Include/cpython/listobject.h @@ -41,6 +41,8 @@ static inline Py_ssize_t PyList_GET_SIZE(PyObject *op) { static inline void PyList_SET_ITEM(PyObject *op, Py_ssize_t index, PyObject *value) { PyListObject *list = _PyList_CAST(op); + assert(0 <= index); + assert(index < Py_SIZE(list)); list->ob_item[index] = value; } #define PyList_SET_ITEM(op, index, value) \ diff --git a/Include/cpython/tupleobject.h b/Include/cpython/tupleobject.h index f6a1f07..370da16 100644 --- a/Include/cpython/tupleobject.h +++ b/Include/cpython/tupleobject.h @@ -31,6 +31,8 @@ static inline Py_ssize_t PyTuple_GET_SIZE(PyObject *op) { static inline void PyTuple_SET_ITEM(PyObject *op, Py_ssize_t index, PyObject *value) { PyTupleObject *tuple = _PyTuple_CAST(op); + assert(0 <= index); + assert(index < Py_SIZE(tuple)); tuple->ob_item[index] = value; } #define PyTuple_SET_ITEM(op, index, value) \ diff --git a/Include/internal/pycore_list.h b/Include/internal/pycore_list.h index 2fcbe12..b2e503c 100644 --- a/Include/internal/pycore_list.h +++ b/Include/internal/pycore_list.h @@ -49,8 +49,8 @@ _PyList_AppendTakeRef(PyListObject *self, PyObject *newitem) Py_ssize_t allocated = self->allocated; assert((size_t)len + 1 < PY_SSIZE_T_MAX); if (allocated > len) { - PyList_SET_ITEM(self, len, newitem); Py_SET_SIZE(self, len + 1); + PyList_SET_ITEM(self, len, newitem); return 0; } return _PyList_AppendTakeRefListResize(self, newitem); diff --git a/Include/structseq.h b/Include/structseq.h index 9687115..29e24fe 100644 --- a/Include/structseq.h +++ b/Include/structseq.h @@ -31,18 +31,15 @@ PyAPI_FUNC(PyTypeObject*) PyStructSequence_NewType(PyStructSequence_Desc *desc); PyAPI_FUNC(PyObject *) PyStructSequence_New(PyTypeObject* type); +PyAPI_FUNC(void) PyStructSequence_SetItem(PyObject*, Py_ssize_t, PyObject*); +PyAPI_FUNC(PyObject*) PyStructSequence_GetItem(PyObject*, Py_ssize_t); + #ifndef Py_LIMITED_API typedef PyTupleObject PyStructSequence; - -/* Macro, *only* to be used to fill in brand new objects */ -#define PyStructSequence_SET_ITEM(op, i, v) PyTuple_SET_ITEM((op), (i), (v)) - -#define PyStructSequence_GET_ITEM(op, i) PyTuple_GET_ITEM((op), (i)) +#define PyStructSequence_SET_ITEM PyStructSequence_SetItem +#define PyStructSequence_GET_ITEM PyStructSequence_GetItem #endif -PyAPI_FUNC(void) PyStructSequence_SetItem(PyObject*, Py_ssize_t, PyObject*); -PyAPI_FUNC(PyObject*) PyStructSequence_GetItem(PyObject*, Py_ssize_t); - #ifdef __cplusplus } #endif diff --git a/Misc/NEWS.d/next/C API/2023-06-28-02-30-50.gh-issue-106168.NFOZPv.rst b/Misc/NEWS.d/next/C API/2023-06-28-02-30-50.gh-issue-106168.NFOZPv.rst new file mode 100644 index 0000000..741d709 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2023-06-28-02-30-50.gh-issue-106168.NFOZPv.rst @@ -0,0 +1,5 @@ +If Python is built in :ref:`debug mode <debug-build>` or :option:`with +assertions <--with-assertions>`, :c:func:`PyTuple_SET_ITEM` and +:c:func:`PyList_SET_ITEM` now check the index argument with an assertion. If +the assertion fails, make sure that the size is set before. Patch by Victor +Stinner. diff --git a/Objects/listobject.c b/Objects/listobject.c index f1edfb3..f1f324f 100644 --- a/Objects/listobject.c +++ b/Objects/listobject.c @@ -953,8 +953,9 @@ list_extend(PyListObject *self, PyObject *iterable) } if (Py_SIZE(self) < self->allocated) { /* steals ref */ - PyList_SET_ITEM(self, Py_SIZE(self), item); - Py_SET_SIZE(self, Py_SIZE(self) + 1); + Py_ssize_t len = Py_SIZE(self); + Py_SET_SIZE(self, len + 1); + PyList_SET_ITEM(self, len, item); } else { if (_PyList_AppendTakeRef(self, item) < 0) diff --git a/Objects/structseq.c b/Objects/structseq.c index 8b18959..4901113 100644 --- a/Objects/structseq.c +++ b/Objects/structseq.c @@ -74,15 +74,28 @@ PyStructSequence_New(PyTypeObject *type) } void -PyStructSequence_SetItem(PyObject* op, Py_ssize_t i, PyObject* v) +PyStructSequence_SetItem(PyObject *op, Py_ssize_t index, PyObject *value) { - PyStructSequence_SET_ITEM(op, i, v); + PyTupleObject *tuple = _PyTuple_CAST(op); + assert(0 <= index); +#ifndef NDEBUG + Py_ssize_t n_fields = REAL_SIZE(op); + assert(n_fields >= 0); + assert(index < n_fields); +#endif + tuple->ob_item[index] = value; } PyObject* -PyStructSequence_GetItem(PyObject* op, Py_ssize_t i) +PyStructSequence_GetItem(PyObject *op, Py_ssize_t index) { - return PyStructSequence_GET_ITEM(op, i); + assert(0 <= index); +#ifndef NDEBUG + Py_ssize_t n_fields = REAL_SIZE(op); + assert(n_fields >= 0); + assert(index < n_fields); +#endif + return PyTuple_GET_ITEM(op, index); } @@ -287,7 +300,7 @@ structseq_repr(PyStructSequence *obj) goto error; } - PyObject *value = PyStructSequence_GET_ITEM(obj, i); + PyObject *value = PyStructSequence_GetItem((PyObject*)obj, i); assert(value != NULL); PyObject *repr = PyObject_Repr(value); if (repr == NULL) { |