Skip to content

GH-155728: Untrack tuples while unmarshalling - #156423

Open
sergey-miryanov wants to merge 4 commits into
python:mainfrom
sergey-miryanov:bug/155728-untrack-tuples-from-marshal
Open

GH-155728: Untrack tuples while unmarshalling #156423
sergey-miryanov wants to merge 4 commits into
python:mainfrom
sergey-miryanov:bug/155728-untrack-tuples-from-marshal

Conversation

@sergey-miryanov

@sergey-miryanov sergey-miryanov commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Comment thread Python/marshal.c Outdated
}
PyTuple_SET_ITEM(v, i, v2);
}
_PyTuple_MaybeUntrack(v);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we allocate this untracked, then conditionally track it after construction. That way we're not exposing partially constructed tuples to the GC.

Also, it would be a bit more efficient to record whether tracking is needed in the loop, so we don't need to loop over the tuple again

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, got it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

@sergey-miryanov sergey-miryanov added needs backport to 3.14 bugs and security fixes needs backport to 3.15 pre-release feature fixes, bugs and security fixes labels Aug 26, 2026
@maurycy

maurycy commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

@sergey-miryanov

I'm wondering if it makes sense to reuse this set of payloads for testing gc.is_tracked() for each under this PR:

def test_loads_abnormal_reference_loops(self):

My hunch is to check for recursion, self-referentials etc.

cc @serhiy-storchaka

Comment thread Python/marshal.c
break;
}
PyTuple_SET_ITEM(v, i, v2);
if (!track_tuple && PyObject_GC_IsTracked(v2)) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not _PyObject_GC_MAY_BE_TRACKED, as in _PyObject_GC_TRACK?
Is there a significant difference between these functions?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PyObject_GC_IsTracked wider than _PyObject_GC_MAY_BE_TRACKED, I'm not sure we should narrow our checks only to tuples as _PyObject_GC_MAY_BE_TRACKED do.

Comment thread Lib/test/test_marshal.py
self._not_tracked_instantly((1, 2) * 5)

self._not_tracked(((1, x), y, (2, 3)))
self._not_tracked((1, 2, (True, False, ())))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe, something like this?

t = (True, False, ())
self._not_tracked((1, 2, t))

Because for now self._not_tracked_instantly((1, 2, (True, False, ()))) is also correct.

Comment thread Lib/test/test_marshal.py
@@ -1,3 +1,5 @@
import gc

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should move this import below?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

awaiting core review needs backport to 3.14 bugs and security fixes needs backport to 3.15 pre-release feature fixes, bugs and security fixes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants