owner 1147503 [email protected]
affects 1147503 + src:thunderbird src:rust-rusqlite
thanks

Hello,

I did some digging into this today. I was able to get the relevant backtrace, 
and I have a good idea on what's happening.

Here's the backtrace:

#0  0x0000000000000000 in ?? ()
#1  0x00007fffde894416 in sqlite3VdbeMemGrow () from 
/usr/lib/x86_64-linux-gnu/libsqlite3.so.0
#2  0x00007fffde895058 in sqlite3VdbeMemSetText () from 
/usr/lib/x86_64-linux-gnu/libsqlite3.so.0
#3  0x00007fffde88a9c3 in ?? () from /usr/lib/x86_64-linux-gnu/libsqlite3.so.0
#4  0x00007fffce198d1d in rusqlite::statement::Statement::bind_parameter<&&dyn 
rusqlite::types::to_sql::ToSql> (self=<optimized out>, param=<optimized out>, ndx=1) at 
/usr/share/cargo/registry/rusqlite-0.40.2/src/statement.rs:646
#5  0x00007fffce1a9e6c in rusqlite::statement::Statement::bind_parameters<&[&dyn 
rusqlite::types::to_sql::ToSql; 2]> (self=0x7fffd37fd120, params=0x7fffd37fd2d0) at 
/usr/share/cargo/registry/rusqlite-0.40.2/src/statement.rs:486
#6  rusqlite::params::{impl#49}::__bind_in<dyn rusqlite::types::to_sql::ToSql> 
(self=0x7fffd37fd2d0, stmt=0x7fffd37fd120) at 
/usr/share/cargo/registry/rusqlite-0.40.2/src/params.rs:298
#7  rusqlite::statement::Statement::execute<&[&dyn rusqlite::types::to_sql::ToSql; 
2]> (self=0x7fffd37fd120, params=0x7fffd37fd2d0) at 
/usr/share/cargo/registry/rusqlite-0.40.2/src/statement.rs:109
#8  rusqlite::{impl#3}::execute::{closure#0}<&[&dyn 
rusqlite::types::to_sql::ToSql; 2]> (stmt=...) at 
/usr/share/cargo/registry/rusqlite-0.40.2/src/lib.rs:621
#9  core::result::Result<rusqlite::statement::Statement, 
rusqlite::error::Error>::and_then<rusqlite::statement::Statement, rusqlite::error::Error, usize, 
rusqlite::{impl#3}::execute::{closure_env#0}<&[&dyn rusqlite::types::to_sql::ToSql; 2]>> 
(self=..., op=...) at /usr/src/rustc-1.95.0/library/core/src/result.rs:1493
#10 rusqlite::Connection::execute<&[&dyn rusqlite::types::to_sql::ToSql; 2]> 
(self=0x7fffd37fdb10, sql=..., params=0x7fffd37fd2d0) at 
/usr/share/cargo/registry/rusqlite-0.40.2/src/lib.rs:621
#11 sequoia_octopus_librnp::wot::WoT::import_validity (managed_by_tb=..., 
validity=..., tx=<optimized out>) at src/wot.rs:573
#12 sequoia_octopus_librnp::wot::WoT::update (self=0x7fffd37fda78, now=...) at 
src/wot.rs:497
#13 sequoia_octopus_librnp::wot::WoT::background_thread (self=0x7fffd37fda78) 
at src/wot.rs:216
#14 0x00007fffce225bbc in 
sequoia_octopus_librnp::wot::{impl#1}::background_thread_start::{closure#0} () 
at src/wot.rs:95
#15 
std::sys::backtrace::__rust_begin_short_backtrace<sequoia_octopus_librnp::wot::{impl#1}::background_thread_start::{closure_env#0},
 ()> (f=...) at /usr/src/rustc-1.95.0/library/std/src/sys/backtrace.rs:166
#16 0x00007fffce236dcc in 
std::thread::lifecycle::spawn_unchecked::{closure#1}::{closure#0}<sequoia_octopus_librnp::wot::{impl#1}::background_thread_start::{closure_env#0},
 ()> () at /usr/src/rustc-1.95.0/library/std/src/thread/lifecycle.rs:91
#17 core::panic::unwind_safe::{impl#23}::call_once<(), 
std::thread::lifecycle::spawn_unchecked::{closure#1}::{closure_env#0}<sequoia_octopus_librnp::wot::{impl#1}::background_thread_start::{closure_env#0},
 ()>> (self=...) at /usr/src/rustc-1.95.0/library/core/src/panic/unwind_safe.rs:274
#18 
std::panicking::catch_unwind::do_call<core::panic::unwind_safe::AssertUnwindSafe<std::thread::lifecycle::spawn_unchecked::{closure#1}::{closure_env#0}<sequoia_octopus_librnp::wot::{impl#1}::background_thread_start::{closure_env#0},
 ()>>, ()> (data=<error reading variable: Cannot access memory at address 0x0>) at 
/usr/src/rustc-1.95.0/library/std/src/panicking.rs:581
#19 std::panicking::catch_unwind<(), 
core::panic::unwind_safe::AssertUnwindSafe<std::thread::lifecycle::spawn_unchecked::{closure#1}::{closure_env#0}<sequoia_octopus_librnp::wot::{impl#1}::background_thread_start::{closure_env#0},
 ()>>> (f=...) at /usr/src/rustc-1.95.0/library/std/src/panicking.rs:544
#20 
std::panic::catch_unwind<core::panic::unwind_safe::AssertUnwindSafe<std::thread::lifecycle::spawn_unchecked::{closure#1}::{closure_env#0}<sequoia_octopus_librnp::wot::{impl#1}::background_thread_start::{closure_env#0},
 ()>>, ()> (f=...) at /usr/src/rustc-1.95.0/library/std/src/panic.rs:359
#21 
std::thread::lifecycle::spawn_unchecked::{closure#1}<sequoia_octopus_librnp::wot::{impl#1}::background_thread_start::{closure_env#0},
 ()> () at /usr/src/rustc-1.95.0/library/std/src/thread/lifecycle.rs:89
#22 
core::ops::function::FnOnce::call_once<std::thread::lifecycle::spawn_unchecked::{closure_env#1}<sequoia_octopus_librnp::wot::{impl#1}::background_thread_start::{closure_env#0},
 ()>, ()> () at /usr/src/rustc-1.95.0/library/core/src/ops/function.rs:250
#23 0x00007fffce8105df in alloc::boxed::{impl#31}::call_once<(), (dyn 
core::ops::function::FnOnce<(), Output=()> + core::marker::Send), alloc::alloc::Global> 
(self=..., args=<optimized out>) at library/alloc/src/boxed.rs:2240
#24 std::sys::thread::unix::{impl#2}::new::thread_start 
(data=data@entry=0x7fffd3bfeee0) at library/std/src/sys/thread/unix.rs:118
#25 0x00005555555815b0 in set_alt_signal_stack_and_start (params=<optimized 
out>) at ./mozglue/interposers/pthread_create_interposer.cpp:81
#26 0x00007ffff7989058 in ?? () from /usr/lib/x86_64-linux-gnu/libc.so.6
#27 0x00007ffff7a07218 in ?? () from /usr/lib/x86_64-linux-gnu/libc.so.6

The important part of the trace starts at frame #11.

sequoia-octopus-librnp has its own src/wot.rs, and this is the Web-of-Trust 
background thread. WoT::background_thread() calls WoT::update(), which 
eventually calls WoT::import_validity(). The latter performs SQL statements 
against Thunderbird's OpenPGP database. The Octopus crate is explicitly a 
Thunderbird RNP replacement and is built as a cdylib; its upstream manifest 
also has a direct rusqlite dependency.

The relevant call chain from the backtrace is therefore:

sequoia_octopus_librnp::wot::WoT::background_thread()
  -> WoT::update()
    -> WoT::import_validity()
      -> rusqlite::Connection/Transaction::execute()
        -> rusqlite::Statement::bind_parameter()
          -> sqlite3_bind_text64()

The last Rust frame is particularly useful. In rusqlite 0.40.2, 
src/statement.rs:646 is:

ValueRef::Text(s) => unsafe {
    let (c_str, len, destructor) = str_for_sqlite(s);
    ffi::sqlite3_bind_text64(
        ptr,
        ndx as c_int,
        c_str,
        len,
        destructor,
        ffi::SQLITE_UTF8 as _, // TODO SQLITE_UTF8_ZT
    )
},

This is the exact call site shown by the backtrace.

The next three frames are the interesting part:

#3  ... from /usr/lib/x86_64-linux-gnu/libsqlite3.so.0
#2  sqlite3VdbeMemSetText() from /usr/lib/x86_64-linux-gnu/libsqlite3.so.0
#1  sqlite3VdbeMemGrow()     from /usr/lib/x86_64-linux-gnu/libsqlite3.so.0

So the sqlite3_bind_text64() call from rusqlite is actually executing inside 
Debian's system libsqlite3.so.0.

That matters because Thunderbird also ships its own private SQLite 
implementation as /usr/lib/thunderbird/libmozsqlite3.so. I checked the exported 
symbol list shipped by Thunderbird. Its sqlite.symbols file explicitly exports:

sqlite3_bind_text
sqlite3_result_text

but does not export:

sqlite3_bind_text64
sqlite3_result_text64

This means sqlite3_bind_text64 is not part of the exported libmozsqlite3.so ABI.

SQLite itself does have the sqlite3_bind_text64() API, so this is not an SQLite 
API removal. The issue is the particular ABI exposed by Thunderbird's embedded 
SQLite.

The backtrace is consistent with a symbol-resolution/ABI mismatch: rusqlite 
requests sqlite3_bind_text64(), but the implementation that actually runs is 
the system SQLite implementation, while Thunderbird has a separate embedded 
SQLite library in the same process with a deliberately narrower exported API.

The crash then occurs entirely inside the system SQLite implementation:

sqlite3_bind_text64()
  -> sqlite3VdbeMemSetText()
    -> sqlite3VdbeMemGrow()
      -> NULL/invalid function call at 0x0

In other words, this is not an ordinary Rust panic or an SQLite error return. 
We are dereferencing invalid state inside SQLite's internal VDBE memory 
machinery.

This also explains why simply having both SQLite libraries installed is not 
harmless here. Octopus is a shared library loaded into Thunderbird rather than 
a standalone application, and its unresolved SQLite symbols participate in 
Thunderbird's existing ELF symbol namespace.

The proposed Debian fix therefore changes the text-binding call in rusqlite 
from:

sqlite3_bind_text64(...)

to:

sqlite3_bind_text(...)

The latter is explicitly exported by Thunderbird's libmozsqlite3.so, so this 
avoids requesting the unexported sqlite3_bind_text64 symbol when the Rust code 
is loaded into Thunderbird.

I am also applying the analogous change from sqlite3_result_text64() to 
sqlite3_result_text(), because Thunderbird's exported SQLite ABI likewise 
contains sqlite3_result_text but not sqlite3_result_text64.

I think the longer-term question is whether Thunderbird's sqlite.symbols should 
export the two *_text64 entry points instead. If they are intended to be part 
of the supported embedded SQLite ABI, adding them there would avoid requiring 
downstream Rust consumers to work around the restricted export list.

For the immediate Debian issue, however, using the SQLite entry points that 
Thunderbird actually exports appears to be the minimal fix for the 
Octopus/Thunderbird crash.

I'm uploading this fix now, please do follow up if the issue isn't fixed. I'm 
able to see that it's fixed on my end, but it's possible your setup is 
different.

Best regards,
Simon Quigley
[email protected]

Attachment: OpenPGP_signature.asc
Description: OpenPGP digital signature

Reply via email to