-
-
Notifications
You must be signed in to change notification settings - Fork 30.7k
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 our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
GH-59313: Do not allow incomplete tuples, or tuples with NULLs. #117747
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you please restrict changes to the bare minimum fix for gh-59313, to ease backport? It would be better to move other changes (ex: reject NULL) in a separated PR.
Lib/test/test_tuple.py
Outdated
for x in range(10): | ||
yield x # SystemError when the tuple needs to be resized | ||
|
||
with self.assertRaises(IndexError): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be possible to rewrite the test to return somehow [x for x in gc.get_referrers(TAG) if isinstance(x, tuple)]
, and make then check that it's empty?
Example:
def my_iter():
yield TAG # 'tag' gets stored in the result tuple
yield [x for x in gc.get_referrers(TAG) if isinstance(x, tuple)]
for x in range(10):
yield x
result = tuple(my_iter())
self.assertEqual(result, (TAG, [], *range(10)))
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
Objects/tupleobject.c
Outdated
@@ -391,11 +385,8 @@ _PyTuple_FromArray(PyObject *const *src, Py_ssize_t n) | |||
} | |||
|
|||
PyObject * | |||
_PyTuple_FromArraySteal(PyObject *const *src, Py_ssize_t n) | |||
_PyTuple_FromNonEmptyArraySteal(PyObject *const *src, Py_ssize_t n) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see how this change is related to the fix. And I don't see the benefits to reject n=0.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've reverted it.
_PyTuple_FromArraySteal
is only called from the interpreter so we know that n != 0
. I can change it in another PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should also update _PyTuple_Resize():
/* Zero out items added by growing */
if (newsize > oldsize)
memset(&sv->ob_item[oldsize], 0,
sizeof(*sv->ob_item) * (newsize - oldsize));
@@ -30,6 +30,7 @@ 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(value != NULL); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You modified PyTuple_SET_ITEM() but not PyTuple_SetItem(), is it on purpose? I suggest to modify both, since PyTuple_SetItem() doesn't call PyTuple_SET_ITEM().
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
Doc/whatsnew/3.13.rst
Outdated
@@ -1872,6 +1872,10 @@ Porting to Python 3.13 | |||
platforms, the ``HAVE_STDDEF_H`` macro is only defined on Windows. | |||
(Contributed by Victor Stinner in :gh:`108765`.) | |||
|
|||
* The :c:func:`PyTuple_SET_ITEM` inline function may not be passed ``NULL``. | |||
This has always been the documented behavior, but was not enforced for |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
https://docs.python.org/dev/c-api/tuple.html#c.PyTuple_SET_ITEM and https://docs.python.org/dev/c-api/tuple.html#c.PyTuple_SetItem don't say that the second argument must not be NULL. I suggest to modify the doc to be extra explicit about it.
Objects/abstract.c
Outdated
@@ -2040,8 +2040,6 @@ PySequence_Tuple(PyObject *v) | |||
{ | |||
PyObject *it; /* iter(v) */ | |||
Py_ssize_t n; /* guess for result tuple size */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a warning: unused variable ‘n’ [-Wunused-variable]
Overall, the change looks good to me, but I made a second review with more suggestions. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some nits
@@ -87,7 +87,7 @@ Tuple Objects | |||
.. c:function:: void PyTuple_SET_ITEM(PyObject *p, Py_ssize_t pos, PyObject *o) | |||
|
|||
Like :c:func:`PyTuple_SetItem`, but does no error checking, and should *only* be | |||
used to fill in brand new tuples. | |||
used to fill in brand new tuples. Both ``p`` and ``o`` must be non-``NULL``. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you move this addition to PyTuple_SetItem(), since PyTuple_SET_ITEM() is "like PyTuple_SetItem()"?
Or just copy/paste the sentence in PyTuple_SetItem() doc?
This change seems to break weakrefs. Closing until I have time to investigate and fix it. |
It is possible that changing from |
Prevents
gc.get_referrers
from returning invalid tuples, and should make the cycle GC a bit more robust.Not using
NULL
allows some efficiency improvements, like changingXDECREF
toDECREF
.I've also streamlined
tuple_alloc
, since I was fiddling with tuple creation and destruction anyway.PySequence_Tuple
takes 0.02% of the runtime of the benchmark suite, so any change to its performance will be undetectable.