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
- gh-144766: Fix a crash in fork child process when perf support is enabled. #144795
- [3.14] gh-144766: Fix a crash in fork child process when perf support is enabled. (GH-144795) #144816
- [3.13] gh-144766: Fix a crash in fork child process when perf support is enabled. (GH-144795) #144818
- gh-144766: Fix flaky test_trampoline_works_with_forks #148056