You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(Session): skip Reset() in destructor to prevent V8 JIT corruption
On Alpine/musl, calling Napi::ObjectReference::Reset() during GC
finalization corrupts V8's JIT page allocations, causing SIGTRAP/SIGSEGV.
The ObjectReference destructor is designed to be GC-safe, but explicitly
calling Reset() during GC is not. This fix adds an in_destructor_ flag
to Session so that Delete() skips the explicit Reset() call when called
from the destructor, letting the ObjectReference destructor handle
cleanup naturally instead.
Also updates CI matrix: Node 23 -> 25 (current dev branch).
Copy file name to clipboardExpand all lines: doc/todo/P02-investigate-flaky-native-crashes.md
+24-37Lines changed: 24 additions & 37 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -77,7 +77,7 @@ Affected tests: Various, shifting between runs due to Jest worker assignment
77
77
78
78
**Commit**: `e1a3fcb`
79
79
80
-
### Bug 2e: V8 JIT Corruption on Alpine During Jest Cleanup (FINAL FIX)
80
+
### Bug 2e: V8 JIT Corruption on Alpine During Jest Cleanup
81
81
82
82
**The bug**: When Sessions are GC'd during Jest cleanup (process exit), their `Napi::ObjectReference` destructors call `Reset()`. On Alpine/musl, this corrupts V8's JIT page allocations.
83
83
@@ -89,47 +89,33 @@ Affected tests: Various, shifting between runs due to Jest worker assignment
89
89
4. V8 JIT corruption: `Check failed: it != jit_page_->allocations_.end()`
90
90
5.**SIGSEGV** or **SIGABRT**
91
91
92
-
**Why it only happened on Alpine/musl**:
92
+
**Partial Fix**: Hold strong references to Session JS objects during `DeleteAllSessions()` cleanup, preventing GC from destroying them while we iterate.
93
93
94
-
- Different GC timing during process exit
95
-
- musl's allocator behavior differs from glibc
96
-
- V8's JIT page management is more sensitive in this environment
94
+
**Commit**: `413c93f`
97
95
98
-
**Key Insight**: The crash happens during Jest cleanup, NOT during normal test execution. Tests pass with `--forceExit` (which skips cleanup).
**The Fix**: Hold strong references to Session JS objects during `DeleteAllSessions()`cleanup, preventing GC from destroying them while we iterate. Then explicitly clear `database_ref_` for all sessions. When the temporary references are released, Sessions can be GC'd with already-empty `database_ref_`.
98
+
**The bug**: Even with the `DeleteAllSessions()`fix, crashes still occurred when Sessions were GC'd without an explicit `db.close()` call. The root cause: `Session::Delete()` was calling `database_ref_.Reset()` during GC finalization, which corrupts V8's JIT on Alpine/musl.
**Key Insight**: The `Napi::ObjectReference` destructor is designed to be GC-safe. But **explicitly** calling `Reset()` during GC finalization is NOT safe on Alpine/musl.
110
101
111
-
// Hold strong references to prevent GC during iteration
**The Fix**: Add an `in_destructor_` flag to Session. When `Delete()` is called from the destructor, skip the explicit `Reset()` call and let the ObjectReference destructor handle cleanup naturally.
117
103
118
-
// Pass 1: Clear SQLite sessions
119
-
for (auto *session : sessions_copy) {
120
-
if (session->GetSession()) {
121
-
sqlite3session_delete(session->GetSession());
122
-
session->session_ = nullptr;
123
-
}
124
-
}
104
+
```cpp
105
+
Session::~Session() {
106
+
in_destructor_ = true; // Signal we're in destructor path
0 commit comments