Skip to content

segfault when deleting an object after returning it #133371

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

Closed
graingert opened this issue May 4, 2025 · 8 comments
Closed

segfault when deleting an object after returning it #133371

graingert opened this issue May 4, 2025 · 8 comments
Labels
3.14 bugs and security fixes interpreter-core (Objects, Python, Grammar, and Parser dirs) release-blocker type-crash A hard crash of the interpreter, possibly with a core dump

Comments

@graingert
Copy link
Contributor

graingert commented May 4, 2025

a slightly minimized segfault:

import asyncio


async def asyncfn():
    pass

def create_task(loop, coro, **kwargs):
    task = asyncio.Task(coro, loop=loop, **kwargs)
    try:
        return task
    finally:
        # gh-128552: prevent a refcycle of
        # task.exception().__traceback__->BaseEventLoop.create_task->task
        del task

async def main():
    t = create_task(asyncio.get_running_loop(), asyncfn(), eager_start=True, name="example")
    assert t.done()
    await t

asyncio.run(main(), loop_factory=asyncio.EventLoop)

Originally posted by @graingert in #128306 (comment)

Linked PRs

@graingert graingert added the 3.14 bugs and security fixes label May 4, 2025
@picnixz picnixz added type-bug An unexpected behavior, bug, or error topic-asyncio extension-modules C modules in the Modules dir labels May 4, 2025
@github-project-automation github-project-automation bot moved this to Todo in asyncio May 4, 2025
@efimov-mikhail
Copy link
Contributor

I also have a segfault on this source code snippet.

Moreover, very simplified example like this

def create_obj():
    obj = [42]
    try:
        return obj
    finally:
        del obj

o = create_obj()
print(o)

also have a problem (assertion, not crash):

python: ./Include/internal/pycore_gc.h:167: _PyGCHead_SET_NEXT: Assertion `(unext & ~_PyGC_PREV_MASK) == 0' failed.
[1]    667990 IOT instruction  ./python asyncio_segfault.py

Is it in general correct to delete returned object in finally section?

@graingert graingert changed the title asyncio segfault when deleting an eager asyncio.Task segfault when deleting an object after returning it May 4, 2025
@graingert
Copy link
Contributor Author

Is it in general correct to delete returned object in finally section?

Yes, this is correct

@ZeroIntensity ZeroIntensity added type-crash A hard crash of the interpreter, possibly with a core dump and removed type-bug An unexpected behavior, bug, or error labels May 4, 2025
@ZeroIntensity
Copy link
Member

The returned object is being borrowed, so the del releases the caller's strong reference, not its own.

cc @mpage

@efimov-mikhail
Copy link
Contributor

efimov-mikhail commented May 4, 2025

The returned object is being borrowed, so the del releases the caller's strong reference, not its own.

cc @mpage

Yes, this problem appears after this PR was being merged:
#130708

I've checked it with git bisect.

Another important detail I've noticed is that sys.getrefcount result have changed after this PR.
Maybe this also should be fixed.
Or at least docstring for this method should be adopted.

@ZeroIntensity
Copy link
Member

Another important detail I've noticed is that sys.getrefcount result have changed after this PR.

Yeah, there's less strong references being formed, so the reference count isn't as high. Is there anything to document?

@efimov-mikhail
Copy link
Contributor

efimov-mikhail commented May 4, 2025

I've checked that original code snippet with asyncio.Task also works well (no crash) on the last commit before PR #130708.

Yeah, there's less strong references being formed, so the reference count isn't as high. Is there anything to document?

Return the reference count of the object. The count returned is generally one higher than you might expect, because it includes the (temporary) reference as an argument to getrefcount().

Text "one higher than you might expect" is not appropriate for this situation.

@ZeroIntensity ZeroIntensity added interpreter-core (Objects, Python, Grammar, and Parser dirs) and removed extension-modules C modules in the Modules dir labels May 4, 2025
mpage added a commit to mpage/cpython that referenced this issue May 5, 2025
mpage added a commit that referenced this issue May 5, 2025
…led by `DELETE_FAST` (#133383)

In certain cases it's possible for locals loaded by `LOAD_FAST` instructions
to be on the stack when the local is killed by `DEL_FAST`. These `LOAD_FAST`
instructions should not be optimized into `LOAD_FAST_BORROW` as the strong
reference in the frame is killed while there is still a reference on the stack.
@hugovk
Copy link
Member

hugovk commented May 5, 2025

#133383 is merged, can this be closed now?

@graingert
Copy link
Contributor Author

yes, I believe it was a 3.14 only issue

@github-project-automation github-project-automation bot moved this from Todo to Done in asyncio May 5, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
3.14 bugs and security fixes interpreter-core (Objects, Python, Grammar, and Parser dirs) release-blocker type-crash A hard crash of the interpreter, possibly with a core dump
Projects
Status: Done
Development

No branches or pull requests

6 participants