Skip to content

gh-112564: Fix UAF for gc debug print - #160662

Merged
nascheme merged 10 commits into
python:mainfrom
johng:gh-112564-gc-debug-uaf
Nov 12, 2026
Merged

nascheme merged 10 commits into
python:mainfrom
johng:gh-112564-gc-debug-uaf

Conversation

@johng

@johng johng commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

In both uncollectable and collectables we would
iterate through gc items using GC_NEXT(gc).
However in the case the current item was cleaned
during iteration this would reuslt in UAF when
fetching the next step in the loop.

The fix is to move each item to a temporary seen list
before printing it, and always take the next
item from the head of the original list, so the
loop never reads from a freed item. This takes
a similar approach to finalize_garbage().

In both uncollectable and collectables we would
iterate through gc items using `GC_NEXT(gc)`.
However in the case the current item was cleaned
during iteration this would reuslt in UAF when
fetching the next step in the loop.

The fix is to move each item to a temporary `seen`
list before printing it, and always take the next
item from the head of the original list, so the
loop never reads from a freed item. This takes
a similar approach to `finalize_garbage()`.
@johng

johng commented Oct 25, 2026

Copy link
Copy Markdown
Contributor Author

Wonder if we just call PyUnstable_GC_VisitObjects to avoid maintaining two copies of the traversing code here

@sergey-miryanov
sergey-miryanov self-requested a review October 25, 2026 16:49

@sergey-miryanov sergey-miryanov left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Same notes about tests and list traversal.

Comment thread Python/gc.c Resolved
@bedevere-app

bedevere-app Bot commented Oct 25, 2026

Copy link
Copy Markdown

A Python core developer has requested some changes be made to your pull request before we can consider merging it. If you could please address their requests along with any other requests in other reviews from core developers that would be appreciated.

Once you have made the requested changes, please leave a comment on this pull request containing the phrase I have made the requested changes; please review again. I will then notify any core developers who have left a review that you're ready for them to take another look at this pull request.

@johng
johng requested a review from sergey-miryanov October 26, 2026 08:38
@johng

This comment was marked as resolved.

@sergey-miryanov

Copy link
Copy Markdown
Contributor

@johng Could you please fix a cast warning in debug_cycles?

@sergey-miryanov sergey-miryanov left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks! LGTM.

@sergey-miryanov

Copy link
Copy Markdown
Contributor

@nascheme Could you please take a look?

@nascheme

nascheme commented Oct 30, 2026 •

Copy link
Copy Markdown
Member

As mentioned on the other issue, my inclination would be to have a way to output debug info without the GIL drop. Likely we can't be using sys.stderr and have to write directly to the stderr file descriptor. It would be nicer if we could output to whatever sys.stderr is bound to, more user friendly, but I'm afraid that could make the GC implementation a fair bit more complex.

OTOH, if we look what's done for resource warnings, they do get written to sys.stderr. PyErr_ResourceWarning takes extra trouble to try to write to it.

Another possible solution: write the debugging output to a buffer while GC is in the middle of its work. Then, write that buffer to sys.stderr at a safe place. Buffering the output should be fine.

@johng

johng commented Oct 30, 2026

Copy link
Copy Markdown
Contributor Author

Happy to explore those suggestions, thanks @nascheme !

@johng

johng commented Oct 30, 2026

Copy link
Copy Markdown
Contributor Author

I think the buffer is the cleanest approach, it does result in iterating twice through the loop since we need to know the length ahead of time for buffer allocations unless we have something quite a bit more complex (I believe)

Comment thread Python/gc.c Resolved
@johng
johng requested a review from sergey-miryanov October 30, 2026 22:05
Comment thread Python/gc.c Resolved
@nascheme

nascheme commented Nov 4, 2026

Copy link
Copy Markdown
Member

Thanks for trying the buffered approach! At quick glance, this seems the right solution to me. PR still needs review though.

@nascheme

nascheme commented Nov 4, 2026

Copy link
Copy Markdown
Member

Ah, btw, the gc_free_threading.c file will likely need the same kind of fix because it looks like it has the same issue.

@johng

johng commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor Author

When I checked this with free threading it was quite a different error case, instead we seem to hit a deadlock instead likely cause by Stop the World rather than anything being wrong with the debug loop.

This seems like a new issue given it would have a very different bug and fix (which I'm happy to attempt to address, I should raise an issue too).

@nascheme nascheme left a comment

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.

This looks good, I had two minor suggestions (NEWS update, comment revision). Could be merged as is. Note that we should still fix the FT GC, IMHO.

Comment thread Python/gc.c Outdated

/* Format a line for each object in `list`, then print them.
* sys.stderr.write(). Stores into buffers so can iterate
* to avoid calling into Python whilst we iterate the objects

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.

Suggested wording:
"Format a line for each object in list before printing any of them, since sys.stderr.write() can run Python code that frees objects in the list."

Comment thread Python/gc.c
if (line == NULL) {
return NULL;
}
/* Not %p: its output is platform-specific (no "0x" prefix on Windows). */

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.

You should mention in NEWS that formatting on some platforms like Windows has changed (lowercase, now always with 0x prefix). Likely no one should care but mention.

@johng

johng commented Nov 11, 2026 •

Copy link
Copy Markdown
Contributor Author

Thanks for the review!

Note I fixed the above mentioned deadlock for FT in #161444

Per above I did not come across a similar class of bug for this, just the deadlock

@nascheme
nascheme merged commit 8b85375 into python:main Nov 12, 2026
56 checks passed
@nascheme nascheme added needs backport to 3.14 bugs and security fixes needs backport to 3.15 bugs and security fixes labels Nov 12, 2026
@miss-islington-app

Copy link
Copy Markdown

Thanks @johng for the PR, and @nascheme for merging it 🌮🎉.. I'm working now to backport this PR to: 3.14.
🐍🍒⛏🤖

@miss-islington-app

Copy link
Copy Markdown

Thanks @johng for the PR, and @nascheme for merging it 🌮🎉.. I'm working now to backport this PR to: 3.15.
🐍🍒⛏🤖

@miss-islington-app

Copy link
Copy Markdown

Sorry, @johng and @nascheme, I could not cleanly backport this to 3.14 due to a conflict.

Please backport manually with cherry_picker, see the devguide for more information.

cherry_picker 8b85375e9e72f6de9f1cad498a99658c3a2ad91c 3.14

@miss-islington-app

Copy link
Copy Markdown

Sorry, @johng and @nascheme, I could not cleanly backport this to 3.15 due to a conflict.

Please backport manually with cherry_picker, see the devguide for more information.

cherry_picker 8b85375e9e72f6de9f1cad498a99658c3a2ad91c 3.15

@bedevere-app

bedevere-app Bot commented Nov 12, 2026

Copy link
Copy Markdown

GH-161895 is a backport of this pull request to the 3.14 branch.

@bedevere-app bedevere-app Bot removed the needs backport to 3.14 bugs and security fixes label Nov 12, 2026
@bedevere-app

bedevere-app Bot commented Nov 12, 2026

Copy link
Copy Markdown

GH-161896 is a backport of this pull request to the 3.15 branch.

@bedevere-app bedevere-app Bot removed the needs backport to 3.15 bugs and security fixes label Nov 12, 2026
nascheme pushed a commit that referenced this pull request Nov 12, 2026
Fix a crash in the garbage collector when gc.DEBUG_COLLECTABLE or
gc.DEBUG_UNCOLLECTABLE is set and writing the debug output to
sys.stderr frees one of the objects being reported.

(cherry picked from commit 8b85375)
nascheme pushed a commit that referenced this pull request Nov 12, 2026
Fix a crash in the garbage collector when gc.DEBUG_COLLECTABLE or
gc.DEBUG_UNCOLLECTABLE is set and writing the debug output to
sys.stderr frees one of the objects being reported.

(cherry picked from commit 8b85375)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants