]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-149156: Fix perf trampoline crash after fork (#150347)
authorPablo Galindo Salgado <Pablogsal@gmail.com>
Mon, 25 May 2026 12:45:58 +0000 (13:45 +0100)
committerGitHub <noreply@github.com>
Mon, 25 May 2026 12:45:58 +0000 (13:45 +0100)
Misc/NEWS.d/next/Core_and_Builtins/2026-05-24-14-45-00.gh-issue-149156.NP73rB.rst [new file with mode: 0644]
Python/perf_trampoline.c

diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-05-24-14-45-00.gh-issue-149156.NP73rB.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-24-14-45-00.gh-issue-149156.NP73rB.rst
new file mode 100644 (file)
index 0000000..2cb091e
--- /dev/null
@@ -0,0 +1,3 @@
+Fix an intermittent crash after :func:`os.fork` when perf trampoline
+profiling is enabled and the child returns through trampoline frames
+inherited from the parent process.
index 58c61e64bfc4e9959d8f9bfc84d23f857e6444ad..d90b789c2b571261b8a0ef36288357f7b821b62e 100644 (file)
@@ -210,9 +210,8 @@ enum perf_trampoline_type {
 static void free_code_arenas(void);
 
 static void
-perf_trampoline_reset_state(void)
+perf_trampoline_clear_code_watcher(void)
 {
-    free_code_arenas();
     if (code_watcher_id >= 0) {
         PyCode_ClearWatcher(code_watcher_id);
         code_watcher_id = -1;
@@ -220,6 +219,13 @@ perf_trampoline_reset_state(void)
     extra_code_index = -1;
 }
 
+static void
+perf_trampoline_reset_state(void)
+{
+    free_code_arenas();
+    perf_trampoline_clear_code_watcher();
+}
+
 static int
 perf_trampoline_code_watcher(PyCodeEvent event, PyCodeObject *co)
 {
@@ -621,9 +627,10 @@ _PyPerfTrampoline_AfterFork_Child(void)
             // After fork, Fini may leave the old code watcher registered
             // if trampolined code objects from the parent still exist
             // (trampoline_refcount > 0). Clear it unconditionally before
-            // Init registers a new one, to prevent two watchers sharing
-            // the same globals and double-decrementing trampoline_refcount.
-            perf_trampoline_reset_state();
+            // Init registers a new one, but keep the old arenas mapped: the
+            // child may still need to return through trampoline frames that
+            // were on the C stack at fork().
+            perf_trampoline_clear_code_watcher();
             _PyPerfTrampoline_Init(1);
         }
     }