GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — gh-139540: Fix executor deallocation crash when JIT compilation fails by ashm-dev · Pull Request #139952 · python/cpython

Conversation

@ashm-dev

@ashm-dev ashm-dev commented Oct 11, 2025 •
edited by bedevere-app Bot
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

There is a memory leak in make_executor_from_uops (Python/optimizer.c) when JIT compilation fails. The executor object is created via _PyObject_GC_NewVar but _PyObject_GC_TRACK is called after the JIT compilation attempt. If _PyJIT_Compile fails and returns an error, the code calls Py_DECREF(executor) before the object has been tracked by the garbage collector.

  • Issue: JIT executors are not properly freed #139540

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@ashm-dev
Track executor object with GC
3a2d34b
@ashm-dev
ashm-dev requested a review from markshannon as a code owner October 11, 2025 12:11
@bedevere-app bedevere-app Bot added the awaiting review label Oct 11, 2025
@bedevere-app bedevere-app Bot mentioned this pull request Oct 11, 2025
JIT executors are not properly freed #139540
Closed
@picnixz

picnixz commented Oct 11, 2025

Copy link
Copy Markdown
Member

Note that this wouldn't solve the cold executor issue. It would only prevent an assertion failure in case we branch here (but if we branch here, we're already in a very bad state)

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@picnixz picnixz changed the title gh-139540: Fix memory leak in make_executor_from_uops when JIT compilation fails gh-139540: Fix executor deallocation crash when JIT compilation fails Oct 11, 2025
@ashm-dev

ashm-dev commented Oct 11, 2025

Copy link
Copy Markdown
Contributor Author

@picnixz Thanks for the context regarding the "cold executor issue".
Just to clarify the scope of this PR: my understanding is that this is a larger, separate problem to be handled in a follow-up. Is that correct, or should I look into addressing it here as well?

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@picnixz

picnixz commented Oct 11, 2025 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member

It's a larger and more complex issue. The fact that we have an immortal cold executor seems to make normal executors leak. I don't know if it's because we reset executors in exit sides to a cold executor that then makes that specific executor not garbage-collectable (this is concerning). Your PR would still be fine but I don't know if it won't be eventually rechanged because of the fix we need to do on the other side.

ashm-dev reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ashm-dev
Create 2025-10-11-16-14-36.gh-issue-139549.9BGTLP.rst
963bcf0
@ashm-dev
ashm-dev force-pushed the gh-139540 branch from 3e1c20a to 963bcf0 Compare October 11, 2025 13:17
@Fidget-Spinner

Fidget-Spinner commented Oct 11, 2025 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member

Sorry but I have the same PR up here https://github.com/python/cpython/pull/137016/files, since July of this year

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ashm-dev
Fix memory leak in JIT executor on compilation failure
db344b2
The executor object wasn't decremented if Tier 2 code returned with an
exception set but not a fatal error.

This change moves the Py_DECREF call inside the TIER1_TO_TIER2 macro.
This ensures the executor's reference count is always decremented.
@ashm-dev
ashm-dev requested review from ZeroIntensity and ericsnowcurrently as code owners October 11, 2025 18:27
@picnixz

picnixz commented Oct 11, 2025

Copy link
Copy Markdown
Member

Considering this is a duplicate of gh-137016, I'm closing this PR. Sorry!

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@picnixz picnixz closed this Oct 11, 2025
@picnixz

picnixz commented Oct 11, 2025 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member

For the other leaks, please open a separate PR. please wait for Mark and other JIT experts to give their opinion because we can't jump on the task without understanding how to properly solve it.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

picnixz
picnixz reviewed Oct 11, 2025
View reviewed changes
Comment thread Python/optimizer.c
if (cold == NULL) {
Py_FatalError("Cannot allocate core JIT code");
}
_PyObject_GC_TRACK(cold);

@picnixz picnixz Oct 11, 2025 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member

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.

This is incorrect. The executor is immortal for now.

No this was correct, my bad. I didn't read the function correctly. The problem is that we're setting it immortal afterwards and I'm not sure it will be happy.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@ashm-dev ashm-dev Oct 11, 2025

Copy link
Copy Markdown
Contributor Author

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.

Good point, thanks for the correction.

The intent was to track it for consistency immediately after allocation. My assumption is the GC correctly handles a tracked object that later becomes immortal.

Is that a safe assumption, or should we explicitly _PyObject_GC_UNTRACK it just before immortalization?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@picnixz picnixz Oct 11, 2025

Copy link
Copy Markdown
Member

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.

That I don't know. The problem here is whether we want an immortal cold executor or not. That's why I wanted to wait for Mark's output here, and didn't write a PR already.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@picnixz

picnixz commented Oct 11, 2025

Copy link
Copy Markdown
Member

@Fidget-Spinner Do you mind incorporating some observations from this PR in yours? I don't know whether the change is correct here when changing current_executor etc.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ashm-dev

ashm-dev commented Oct 11, 2025

Copy link
Copy Markdown
Contributor Author

Hi @Fidget-Spinner ,

Thanks for pointing that out and linking your PR. I've taken a close look at both.

While our changes overlap in optimizer.c, they actually address two different underlying issues. Your PR #137016 correctly fixes a GC assertion failure by moving _PyObject_GC_TRACK to an earlier point. It's a great catch!

However, this PR primarily addresses a critical reference counting leak that was detectable with ASan. The root cause was that the executor's refcount was not decremented when Tier 2 code exited with a Python exception set (but didn't fatally error out by returning NULL). The main fix here is adding Py_DECREF(EXECUTOR) inside the TIER1_TO_TIER2 macro in ceval_macros.h to ensure the reference is always released.

My PR also includes the same _PyObject_GC_TRACK move that you proposed, so it appears to fix both the memory leak from issue #139540 and the GC assertion issue that your PR addresses.

I believe this PR provides a more complete solution for the stability issues in this code path.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ashm-dev

ashm-dev commented Oct 11, 2025

Copy link
Copy Markdown
Contributor Author

I don't know whether the change is correct here when changing current_executor etc

These changes are correct and necessary. tstate->current_executor acts as a temporary channel to pass the executor object from the _ENTER_EXECUTOR instruction to the new Tier 2 frame, which is set up in _PyEval_EvalFrameDefault.

The intended lifecycle is:

  1. _ENTER_EXECUTOR (in bytecodes.c) sets tstate->current_executor.
  2. _PyEval_EvalFrameDefault consumes it at the very start of the new frame, creates a PyStackRef for it, and immediately sets tstate->current_executor back to NULL.

The other NULL/CLEAR additions in ceval.c and pystate.c are for robustness, ensuring we don't leak the executor if a frame or thread is torn down unexpectedly. This appears to be the established design for this handover.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@picnixz

picnixz commented Oct 11, 2025

Copy link
Copy Markdown
Member

The root cause was that the executor's refcount was not decremented when Tier 2 code exited with a Python exception set (but didn't fatally error out by returning NULL). The main fix here is adding Py_DECREF(EXECUTOR) inside the TIER1_TO_TIER2 macro in ceval_macros.h to ensure the reference is always released.

Does it fix the leak as in the issue? that is, does it fix the refcounting? I can't check this now, but please check whether the reproducer (without ASAN) is fixed when you run ./python -X showrefcount repro.py.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@picnixz picnixz reopened this Oct 11, 2025
picnixz
picnixz reviewed Oct 11, 2025
View reviewed changes
Comment thread Python/ceval_macros.h
#define TIER1_TO_TIER2(EXECUTOR) \
#define TIER1_TO_TIER2(EXECUTOR) \
do { \
OPT_STAT_INC(traces_executed); \

@picnixz picnixz Oct 11, 2025

Copy link
Copy Markdown
Member

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.

Why was this changed? please revert.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread Python/ceval_macros.h
/* Tier-switching macros. */

#define TIER1_TO_TIER2(EXECUTOR) \
#define TIER1_TO_TIER2(EXECUTOR) \

@picnixz picnixz Oct 11, 2025

Copy link
Copy Markdown
Member

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.

Why was this changed? please revert.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread Python/generated_cases.c.h
Comment on lines +7671 to +7673
tstate->current_executor = (PyObject *)executor;
_PyFrame_SetStackPointer(frame, stack_pointer);
stack_pointer = _PyFrame_GetStackPointer(frame);

@picnixz picnixz Oct 11, 2025

Copy link
Copy Markdown
Member

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.

This seems to be redundant.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@picnixz picnixz Oct 11, 2025

Copy link
Copy Markdown
Member

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.

We are setting the stack pointer and requerying it while doing nothing inbetween.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread Python/ceval_macros.h
next_instr = _Py_jit_entry((EXECUTOR), frame, stack_pointer, tstate); \
frame = tstate->current_frame; \
stack_pointer = _PyFrame_GetStackPointer(frame); \
Py_DECREF(EXECUTOR); \

@picnixz picnixz Oct 11, 2025

Copy link
Copy Markdown
Member

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.

Why should we decref it here?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread Python/bytecodes.c
assert(tstate->current_executor == NULL);
assert(executor != tstate->interp->cold_executor);
tstate->jit_exit = NULL;
tstate->current_executor = (PyObject *)executor;

@picnixz picnixz Oct 11, 2025

Copy link
Copy Markdown
Member

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.

Is it just to hold a reference to the executor?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@ashm-dev
ashm-dev marked this pull request as draft October 11, 2025 19:22
@bedevere-app bedevere-app Bot removed the awaiting review label Oct 11, 2025
@ashm-dev ashm-dev closed this Oct 13, 2025
@ashm-dev
ashm-dev deleted the gh-139540 branch October 19, 2025 20:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@picnixz picnixz picnixz left review comments
@markshannon markshannon Awaiting requested review from markshannon markshannon is a code owner
@ericsnowcurrently ericsnowcurrently Awaiting requested review from ericsnowcurrently ericsnowcurrently is a code owner
@ZeroIntensity ZeroIntensity Awaiting requested review from ZeroIntensity ZeroIntensity is a code owner
@FFY00 FFY00 Awaiting requested review from FFY00 FFY00 will be requested when the pull request is marked ready for review FFY00 is a code owner
@Fidget-Spinner Fidget-Spinner Awaiting requested review from Fidget-Spinner Fidget-Spinner will be requested when the pull request is marked ready for review Fidget-Spinner is a code owner

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

3 participants

@ashm-dev @picnixz @Fidget-Spinner
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.