Skip to content

perf_trampoline crash in the fork child process #144766

Closed

Description

@yilei

Crash report

What happened?

After upgrading from 3.13.11 to 3.13.12, we saw a consistent SIGSEGV crash in perf_trampoline from the fork child process when perf support is enabled. Here is a minimal example:

import gc
import os
import sys


def main():
    if not sys.is_stack_trampoline_active():
        try:
            sys.activate_stack_trampoline("perf")
        except ValueError:
            print("Perf trampoline not supported on this platform")
            return

    # Execute functions to create trampolined code objects so
    # trampoline_refcount > 1 at fork time. Keep references alive.
    funcs = []
    ns = {}
    for i in range(50):
        exec(compile(f"def _f{i}(): return {i}", f"<gen-{i}>", "exec"), ns)
        f = ns[f"_f{i}"]
        f()  # Execute to create trampoline
        funcs.append(f)

    pid = os.fork()

    if pid == 0:
        # Child: _PyPerfTrampoline_AfterFork_Child has run, leaving two
        # active code watchers. We need to create NEW code objects that get
        # trampolines at the NEW extra_code_index, so both watchers see
        # them and double-decrement trampoline_refcount.
        ns2 = {}
        for i in range(50):
            exec(compile(f"def _g{i}(): return {i}", f"<child-{i}>", "exec"), ns2)
            ns2[f"_g{i}"]()  # Execute to create trampoline (increments refcount)

        # Destroy them — both watchers fire, double-decrement -> SIGSEGV
        del ns2
        gc.collect()
        os._exit(0)
    else:
        _, status = os.waitpid(pid, 0)
        if os.WIFSIGNALED(status):
            sig = os.WTERMSIG(status)
            print(f"FAIL: Child killed by signal {sig} (SIGSEGV=11)")
            sys.exit(1)
        else:
            exit_code = os.WEXITSTATUS(status)
            if exit_code == 0:
                print("OK: Child exited normally (bug not triggered)")
            else:
                print(f"FAIL: Child exited with code {exit_code}")
                sys.exit(1)


if __name__ == "__main__":
    main()

Compiling CPython 3.13.12 with:

./configure CFLAGS="-g -O0" && make -j $(nproc)

Running in gdb shows the crash is here:

❯ PYTHONPERFSUPPORT=1 gdb --args ./python ~/perf_trampoline_crash.py
...
(gdb) set follow-fork-mode child
(gdb) run
...

Thread 2.1 "python" received signal SIGSEGV, Segmentation fault.
[Switching to process 753038]
compile_trampoline () at Python/perf_trampoline.c:411
411	    size_t total_code_size = round_up(perf_code_arena->code_size + trampoline_api.code_padding, 16);

After some digging, I believe this is a regression from the bugfix for #143228. Reverting de34f6d makes the crash go away.

This crash is also reproducible at main branch.

CPython versions tested on:

3.13, CPython main branch

Operating systems tested on:

Linux

Output from running 'python -VV' on the command line:

No response

Linked PRs

Activity

  1. added
    type-crashA hard crash of the interpreter, possibly with a core dump
    on Feb 12, 2026
  2. yilei commented on Feb 12, 2026

    @yilei
    ContributorAuthor

    I think we need to call perf_trampoline_reset_state() before init here:

    _PyPerfTrampoline_Init(1);
    I'll put up a PR.

  3. added a commit that references this issue on Feb 13, 2026
  4. self-assigned this
    on Feb 13, 2026
  5. added a commit that references this issue on Feb 14, 2026
  6. added a commit that references this issue on Feb 14, 2026
  7. added 3 commits that reference this issue on Feb 14, 2026
  8. pablogsal commented on Feb 15, 2026

    @pablogsal
    Member

    Thanks a lot @yilei for the fantastic investigation and the fix! Your rock 🚀

  9. added a commit that references this issue on Feb 28, 2026
  10. added 2 commits that reference this issue on Apr 3, 2026
  11. added a commit that references this issue on Apr 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

interpreter-core(Objects, Python, Grammar, and Parser dirs)type-crashA hard crash of the interpreter, possibly with a core dump

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions