Version
v24.16.0
Platform
Linux e6fd926f804d 6.8.0-124-generic #124-Ubuntu SMP PREEMPT_DYNAMIC Tue May 26 13:00:45 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux
Subsystem
sqlite
What steps will reproduce the bug?
Execute the following JS code, with sqlite compiled with SQLITE_ENABLE_API_ARMOR:
const { DatabaseSync } = require('node:sqlite');
const db = new DatabaseSync('/nonexistent-dir/x.db', { open: false, readOnly: true });
try { db.open(); } catch {} // Can't open, now a "sick" handle
console.log(db.isOpen); // true
db.function('f', () => {}); // create_function_v2 -> SQLITE_MISUSE_BKPT, `UserDefinedFunction* user_data` leaked
This will trigger a leak via sqlite's API abuse detector. See below for the ASAN output.
How often does it reproduce? Is there a required condition?
Always if compiled with SQLITE_ENABLE_API_ARMOR
What is the expected behavior? Why is that the expected behavior?
No leak. In fact, it should not be possible to get into this situation in the first place: the API misuse guard of the sqlite API should never trigger. Node.js should block getting into this situation.
What do you see instead?
=================================================================
==370239==ERROR: LeakSanitizer: detected memory leaks
Direct leak of 32 byte(s) in 1 object(s) allocated from:
#0 0x6391a602b411 in operator new(unsigned long) (/work/node/out/Debug/node+0xae2b411) (BuildId: af8ea042dbd52fe317d72d87d06d3dbfb9aa7fa9)
#1 0x6391a71cdaa7 in node::sqlite::DatabaseSync::CustomFunction(v8::FunctionCallbackInfo<v8::Value> const&) /work/node/out/../src/node_sqlite.cc:1401:7
#2 0x714010e12f8c in Builtins_CallApiCallbackGeneric embedded.o
#3 0x714010e11512 in Builtins_InterpreterEntryTrampoline embedded.o
#4 0x714010e11512 in Builtins_InterpreterEntryTrampoline embedded.o
#5 0x714010e11512 in Builtins_InterpreterEntryTrampoline embedded.o
#6 0x714010e11512 in Builtins_InterpreterEntryTrampoline embedded.o
#7 0x714010e11512 in Builtins_InterpreterEntryTrampoline embedded.o
#8 0x714010e11512 in Builtins_InterpreterEntryTrampoline embedded.o
#9 0x714010e11512 in Builtins_InterpreterEntryTrampoline embedded.o
#10 0x714010e11512 in Builtins_InterpreterEntryTrampoline embedded.o
#11 0x714010e0df26 in Builtins_JSEntryTrampoline embedded.o
#12 0x714010e0dc66 in Builtins_JSEntry embedded.o
#13 0x6391a7e71586 in v8::internal::GeneratedCode<unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, long, unsigned long**>::Call(unsigned long, unsigned long, unsigned long, unsigned long, long, unsigned long**) /work/node/out/../deps/v8/src/execution/simulator.h:212:12
#14 0x6391a7e71586 in v8::internal::(anonymous namespace)::Invoke(v8::internal::Isolate*, v8::internal::(anonymous namespace)::InvokeParams const&) /work/node/out/../deps/v8/src/execution/execution.cc:442:22
#15 0x6391a7e6e194 in v8::internal::Execution::Call(v8::internal::Isolate*, v8::internal::DirectHandle<v8::internal::Object>, v8::internal::DirectHandle<v8::internal::Object>, v8::base::Vector<v8::internal::DirectHandle<v8::internal::Object> const>) /work/node/out/../deps/v8/src/execution/execution.cc:532:10
#16 0x531000015287 (<unknown module>)
SUMMARY: AddressSanitizer: 32 byte(s) leaked in 1 allocation(s).
Additional information
Found with an experimental static-dynamic hybrid analyzer I'm developing.
Version
v24.16.0
Platform
Subsystem
sqlite
What steps will reproduce the bug?
Execute the following JS code, with sqlite compiled with
SQLITE_ENABLE_API_ARMOR:This will trigger a leak via sqlite's API abuse detector. See below for the ASAN output.
How often does it reproduce? Is there a required condition?
Always if compiled with
SQLITE_ENABLE_API_ARMORWhat is the expected behavior? Why is that the expected behavior?
No leak. In fact, it should not be possible to get into this situation in the first place: the API misuse guard of the sqlite API should never trigger. Node.js should block getting into this situation.
What do you see instead?
Additional information
Found with an experimental static-dynamic hybrid analyzer I'm developing.