tursodatabase / tursodatabase/libsql

autoinit is broken for `libsql_open_v3`

Open
#2,169 0 comments 0 reactions 0 assignees View on GitHub

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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.