tursodatabase / tursodatabase/libsql
autoinit is broken for `libsql_open_v3`
Nobody has claimed this yet.
- Dominant language
- C
- Stars
- 17.2k
- Forks
- 531
- Avg merge
- 1h 12m
- Merged PRs (30d)
- 1
Description
Hi, its documented that calling sqlite3_initialize is required before using certain other functions.
However the library also supports "autoinit" whereby, when necessary, sqlite3_initialize is automatically called in the process of executing those functions:
https://github.com/tursodatabase/libsql/blob/268d16933c595498a2131f812b0e09adef5fb47c/libsql-sqlite3/src/main.c#L200-L204
One such function is openDatabase which is used by the sqlite3_open* and libsql_open* APIs.
However, there is a potential issue with libsql_open_v3 as it tries to perform memory allocation before invoking openDatabase via a call to make_ref_counted_wal_manager:
https://github.com/tursodatabase/libsql/blob/268d16933c595498a2131f812b0e09adef5fb47c/libsql-sqlite3/src/main.c#L3860-L3874
Thus, while libsql_open_v3 intends to automatically initialize, it actually does it too late, leading to a crash.
(found via automated fuzzing)
The following testcase demonstrates the issue:
testcase.cpp
#include <cstdint>
extern "C" {
#include "sqlite3.h"
#include "sqlite3ext.h"
}
int main(){
libsql_wal_manager wm{};
wm.bUsesShm = 0;
// Provide non-NULL stubs for required callbacks
wm.xOpen = +[](wal_manager_impl*, sqlite3_vfs*, sqlite3_file*, int, long long, const char*, libsql_wal*)->int{return 1; };
wm.xClose = +[](wal_manager_impl*, wal_impl*, sqlite3*, int, int, unsigned char*)->int{ return 0; };
wm.xLogDestroy = +[](wal_manager_impl*, sqlite3_vfs*, const char*)->int{ return 0; };
wm.xLogExists = +[](wal_manager_impl*, sqlite3_vfs*, const char*, int *pResOut)->int{ if(!pResOut) return 1; *pResOut=0; return 0; };
wm.xDestroy = +[](wal_manager_impl*){};
wm.pData = nullptr;
const char *fname = ":memory:";
sqlite3 *db=nullptr;
int flags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE;
// This call crashes inside libsql_open_v3 with pc=0x0
int rc = libsql_open_v3(fname, &db, flags, nullptr, wm);
(void)rc; (void)db;
return 0;
}
crash report
==12==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x000000000000 bp 0x7ffd3c874770 sp 0x7ffd3c8746a8 T0)
==12==Hint: pc points to the zero page.
==12==The signal is caused by a READ memory access.
==12==Hint: address points to the zero page.
#0 0x0 (<unknown module>)
#1 0x5650461d476e in main /fuzz/workspace/test.cpp:22:12
#2 0x7faec1d67d8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV (<unknown module>)
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in libsql-sqlite3/src/main.c at the libsql_open_v3 and openDatabase paths referenced in the issue, then run the supplied testcase.cpp with AddressSanitizer. Done means the testcase no longer crashes when libsql_open_v3 is called without prior sqlite3_initialize, while preserving the documented API behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- c
- Domain
- api, databases
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Stale
- Clarity
- Clearly specified
- Newbie friendliness
- 48/100