Commit bf8982b
committed
fix(global_cache): Py_IsFinalizing guards on dict-watcher + clear()
ARM64 pydebug shutdown SEGV root-cause: GlobalCacheKey holds raw
PyDictObject* pointers to globals/builtins. When globals is freed
during Py_FinalizeEx (interpreter_clear → PyDict_Clear), the cache
entry remains in map_ + watch_map_[builtins]. Subsequent
PyDict_EVENT_CLEARED on builtins triggers updateCache, which derefs
cache.key().globals → use-after-free. Pydebug poison fill (0xdd...)
makes the UAF a hard SEGV; x86_64 release reads stale-but-readable
type pointer and silently passes the JIT_CHECK.
Two surgical guards address the FINALIZE-SPECIFIC manifestation:
Part A — Python/jit_support/phoenix_init.cpp
phoenix_dict_watcher early-returns when Py_IsFinalizing(). Cache
state is about to be destroyed in phoenix_free; processing events
on tearing-down dicts is moot. Pattern matches pyjit.cpp:1642
funcDestroyed precedent.
Part B — Python/jit/global_cache.cpp
GlobalCacheManager::clear() short-circuits when Py_IsFinalizing().
Avoids two failure modes:
1. Ci_Watchers_UnwatchDict (PyDict_Unwatch) reads dict internals
on freed memory → UAF under pydebug
2. ci_watcher_state_fini ran first in phoenix_free → watcher_id
is -1 → JIT_CHECK 'Invalid dict watcher ID -1' abort
(observed in earlier gate logs, regression historically)
SCOPE HONESTY: this is FINALIZE-SPECIFIC protection, NOT full
root-cause for the raw-pointer-aliasing class. Mid-execution module
unload (refcount→0 on globals while cache holds raw pointer) remains
unaddressed. Filed as W27 GlobalCacheKey raw-pointer lifecycle
re-architecture (docs/w27-globalcachekey-lifecycle.md, Tier 6+ scope).
The 314d0f2 'weak reference semantics' comment was FALSIFIED — no
mechanism actually provided weak-reference semantics; comments updated
to name the actual mechanisms (Py_IsFinalizing guard, finalize-only
protection).
Diagnostic evidence (per Debug-First protocol):
lldb backtrace on ARM64 pydebug push 59 falsifier:
PyType_HasFeature(type=0xdddddddddddddddd) object.h:966 <-- SEGV
hasOnlyUnicodeKeys() dict.h:52
GlobalCacheManager::updateCache() global_cache.cpp:325
GlobalCacheManager::notifyDictClear() global_cache.cpp:144
phoenix_dict_watcher() phoenix_init.cpp:58
PyDict_Clear → interpreter_clear → Py_FinalizeEx
Test plan post-commit:
testkeeper ARM64 pydebug --clean rebuild + falsifier rerun
(test_phoenix_jit_inline_except_closure under timeout+redirect)
+ push 58 baseline regression check (8b7b935 still passes).
testkeeper x86_64 compile-only PASS verified pre-commit at
binary timestamp 1776874355 (chat L1872 build verification).
Authorization chain: supervisor option (i) at chat L1862, theologian
LEVEL-1 ratification at chat L1861. Pythia python#75 python#1 (trigger isolation)
gates downstream emitCallExceptionHandler queue, NOT this fix.1 parent b44f075 commit bf8982b
3 files changed
Lines changed: 137 additions & 6 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
3 | 3 | | |
4 | 4 | | |
5 | 5 | | |
| 6 | + | |
6 | 7 | | |
7 | 8 | | |
8 | 9 | | |
| |||
181 | 182 | | |
182 | 183 | | |
183 | 184 | | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
184 | 207 | | |
185 | 208 | | |
186 | 209 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
15 | 15 | | |
16 | 16 | | |
17 | 17 | | |
| 18 | + | |
18 | 19 | | |
19 | 20 | | |
20 | 21 | | |
| |||
30 | 31 | | |
31 | 32 | | |
32 | 33 | | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
33 | 53 | | |
34 | 54 | | |
35 | 55 | | |
| |||
59 | 79 | | |
60 | 80 | | |
61 | 81 | | |
62 | | - | |
63 | | - | |
64 | | - | |
65 | | - | |
66 | | - | |
67 | | - | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
68 | 89 | | |
69 | 90 | | |
70 | 91 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
0 commit comments