Skip to content

Commit

Permalink
pythongh-106168: PyTuple_SET_ITEM() now checks the index
Browse files Browse the repository at this point in the history
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.

* PyStructSequence_GET_ITEM() and PyStructSequence_SET_ITEM() are now
  aliases to PyStructSequence_GetItem() and
  PyStructSequence_SetItem()
* PyStructSequence_GetItem() and PyStructSequence_SetItem() now check
  the index argument: must be lesser than REAL_SIZE(op).
  • Loading branch information
vstinner committed Jun 28, 2023
1 parent 6b5166f commit 8b53e45
Show file tree
Hide file tree
Showing 8 changed files with 42 additions and 16 deletions.
6 changes: 6 additions & 0 deletions Doc/whatsnew/3.13.rst
Expand Up @@ -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
----------------------

Expand Down
2 changes: 2 additions & 0 deletions Include/cpython/listobject.h
Expand Up @@ -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) \
Expand Down
2 changes: 2 additions & 0 deletions Include/cpython/tupleobject.h
Expand Up @@ -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) \
Expand Down
2 changes: 1 addition & 1 deletion Include/internal/pycore_list.h
Expand Up @@ -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);
Expand Down
13 changes: 5 additions & 8 deletions Include/structseq.h
Expand Up @@ -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
Expand Down
@@ -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.
5 changes: 3 additions & 2 deletions Objects/listobject.c
Expand Up @@ -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)
Expand Down
23 changes: 18 additions & 5 deletions Objects/structseq.c
Expand Up @@ -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);
}


Expand Down Expand Up @@ -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) {
Expand Down

0 comments on commit 8b53e45

Please sign in to comment.