Environment
sqlite3: 3.1.7 (transitive via drift 2.32), loaded through native assets with the hook override:
hooks:
user_defines:
sqlite3:
source: sqlite3mc
- Flutter 3.44.0 stable / Dart 3.12.0
- macOS 26.4.1 (25E253), arm64 (MacBook Pro, Mac17,9)
- App: Flutter desktop POS, several sqlite3mc-encrypted databases (drift
NativeDatabase + occasional raw sqlite3.open health probes)
Symptom
Intermittent hard crash — 3 occurrences in one day of testing, same signature every time:
EXC_BAD_ACCESS (SIGSEGV), KERN_INVALID_ADDRESS at 0x0, pc = 0x0 (Instruction Abort — a branch to NULL)
lr = sqlite3mc sqlite3_update_hook + 48
- Main thread, inside the Dart microtask queue, during
package:sqlite3 Database.close() of a short-lived probe database (sqlite3.open → PRAGMA schema_version → close()), while the app holds other sqlite3mc databases open. close() detaches hooks via sqlite3_update_hook(db, NULL, NULL), which is the crashing frame.
Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0 ??? 0x0 ???
1 sqlite3mc 0x102d5f130 sqlite3_update_hook + 48
2 App 0x117e45590 kDartVmSnapshotInstructions + 21904
...
21 FlutterMacOS 0x103c84a58 tonic::DartMicrotaskQueue::RunMicrotasks() + 176
The smoking gun: the native-asset framework is mapped TWICE in one process
Every crash report's Binary Images section lists the sqlite3mc native asset twice — same UUID, same on-disk path, two different load addresses — and the crashing address (0x102d5f130) lies inside the second mapping:
0x1225a0000 - 0x12274ffff io.flutter.flutter.native-assets.sqlite3mc (1.0) <dc12900b-c778-31a7-9fba-d6bd308fe478>
.../Contents/Frameworks/sqlite3mc.framework/Versions/A/sqlite3mc
0x102d40000 - 0x102eeffff io.flutter.flutter.native-assets.sqlite3mc (1.0) <dc12900b-c778-31a7-9fba-d6bd308fe478>
.../Contents/Frameworks/sqlite3mc.framework/Versions/A/sqlite3mc
With two copies of the library's static state in the process, a sqlite3* handle created through one copy can be passed to a function resolved against the other. If that second copy never ran sqlite3_initialize(), sqlite3GlobalConfig.mutex.xMutexEnter is NULL and sqlite3_update_hook's mutex-enter is a branch to 0 — which matches the register state (pc=0, lr=sqlite3_update_hook+48) exactly.
Two likely-related packaging artifacts from the same hook build:
- Every
flutter build macos emits a warning along the lines of "different framework names for different architectures … ignoring sqlite3mc1.framework" — suggesting the hook produces two asset registrations for one library.
- The app bundle also ships CSQLite.framework alongside sqlite3mc.framework (a third sqlite copy), even though the user-define switches the source to sqlite3mc.
These persist through flutter clean and reproduce on every build with 3.1.7 (and per a quick changelog read, nothing addressing this landed through 3.3.x — happy to be corrected).
Reproduction
Not deterministic — it needs the right interleaving of open/close against multiple encrypted DBs, but it fired three times in one ~11-hour test day (09:30, 10:57, 20:07), always within ~100ms of a burst of sqlite3.open/close() probe activity. I have all three full .ips crash reports and can attach them.
Workaround
We reduced raw open/close frequency (probing each DB file at most once per process launch), which shrinks the window but obviously doesn't fix the dual mapping.
Happy to test patches, dump dyld info from a live process, or provide the full crash reports / a sample app skeleton.
Environment
sqlite3: 3.1.7(transitive via drift 2.32), loaded through native assets with the hook override:NativeDatabase+ occasional rawsqlite3.openhealth probes)Symptom
Intermittent hard crash — 3 occurrences in one day of testing, same signature every time:
EXC_BAD_ACCESS (SIGSEGV),KERN_INVALID_ADDRESS at 0x0, pc = 0x0 (Instruction Abort — a branch to NULL)lr = sqlite3mc sqlite3_update_hook + 48package:sqlite3Database.close()of a short-lived probe database (sqlite3.open→PRAGMA schema_version→close()), while the app holds other sqlite3mc databases open.close()detaches hooks viasqlite3_update_hook(db, NULL, NULL), which is the crashing frame.The smoking gun: the native-asset framework is mapped TWICE in one process
Every crash report's Binary Images section lists the sqlite3mc native asset twice — same UUID, same on-disk path, two different load addresses — and the crashing address (
0x102d5f130) lies inside the second mapping:With two copies of the library's static state in the process, a
sqlite3*handle created through one copy can be passed to a function resolved against the other. If that second copy never ransqlite3_initialize(),sqlite3GlobalConfig.mutex.xMutexEnteris NULL andsqlite3_update_hook's mutex-enter is a branch to 0 — which matches the register state (pc=0,lr=sqlite3_update_hook+48) exactly.Two likely-related packaging artifacts from the same hook build:
flutter build macosemits a warning along the lines of "different framework names for different architectures … ignoring sqlite3mc1.framework" — suggesting the hook produces two asset registrations for one library.These persist through
flutter cleanand reproduce on every build with 3.1.7 (and per a quick changelog read, nothing addressing this landed through 3.3.x — happy to be corrected).Reproduction
Not deterministic — it needs the right interleaving of open/close against multiple encrypted DBs, but it fired three times in one ~11-hour test day (09:30, 10:57, 20:07), always within ~100ms of a burst of
sqlite3.open/close()probe activity. I have all three full.ipscrash reports and can attach them.Workaround
We reduced raw open/close frequency (probing each DB file at most once per process launch), which shrinks the window but obviously doesn't fix the dual mapping.
Happy to test patches, dump
dyldinfo from a live process, or provide the full crash reports / a sample app skeleton.