]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-106168: PyTuple_SET_ITEM() now checks the index (#106164)
authorVictor Stinner <vstinner@python.org>
Wed, 28 Jun 2023 01:45:57 +0000 (03:45 +0200)
committerGitHub <noreply@github.com>
Wed, 28 Jun 2023 01:45:57 +0000 (03:45 +0200)
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().

Doc/c-api/list.rst
Doc/c-api/tuple.rst
Doc/whatsnew/3.13.rst
Include/cpython/listobject.h
Include/cpython/tupleobject.h
Include/internal/pycore_list.h
Include/structseq.h
Misc/NEWS.d/next/C API/2023-06-28-02-30-50.gh-issue-106168.NFOZPv.rst [new file with mode: 0644]
Objects/listobject.c
Objects/structseq.c

index dbf35611eccd3e74ec6e67bb5c36989da90dd6b6..c15cecd41b89d17b67590937b1de1eb84e5f5442 100644 (file)
@@ -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
index ac62058676eeeb6a020e77096e6c5b44dd64af01..3fe1062aa8539a91bc14cabb1a3699d658aa10a1 100644 (file)
@@ -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`.
index f3460beeb16be678352db98c84891462652533a6..c0e9e924c8e82f852cc5badbb28b1078774e6ed5 100644 (file)
@@ -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
 ----------------------
 
index 8fa82122d8d248b5bd75455bc473b4c2d0ad724b..b3b23985de7a669b7c879d3d08d7ac3c6dec144b 100644 (file)
@@ -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) \
index f6a1f076e033305426972f251584681f258d6543..370da1612a61ed4cc61bf59232b23d812bcc8fb8 100644 (file)
@@ -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) \
index 2fcbe12cd6559eb357c88828ab223db4b742a1fa..b2e503c87542bf3eaabab8b0a5fdc9b356c92737 100644 (file)
@@ -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);
index 968711556119581dbd4141fbe771625e3ff18fb8..29e24fee54e6135ee93be1f3faf4d4b7d914c8f3 100644 (file)
@@ -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 (file)
index 0000000..741d709
--- /dev/null
@@ -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.
index f1edfb3a9a039d62d0dd540dc9c43aa16fb3d51b..f1f324f7439b43529d124277bb09338c8cc3f245 100644 (file)
@@ -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)
index 8b1895957101a48919b72f9e79a454154902b1f3..49011139b665345b05c6b6e77bf0a6e960d3c490 100644 (file)
@@ -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) {