From 0ac4520a0d9dea03cc843c53d8be685073694861 Mon Sep 17 00:00:00 2001 From: Carl Sverre <82591+carlsverre@users.noreply.github.com> Date: Wed, 10 Dec 2025 22:31:01 -0800 Subject: [PATCH] handle panics --- .github/workflows/rust.yml | 14 +++++++++++--- Cargo.toml | 3 ++- examples/memvfs.rs | 8 ++++++-- examples/test_memvfs.sql | 2 +- examples/test_memvfs_panic.sql | 20 ++++++++++++++++++++ src/lib.rs | 2 +- src/vfs.rs | 26 ++++++++++++++++++++++++++ 7 files changed, 67 insertions(+), 8 deletions(-) create mode 100644 examples/test_memvfs_panic.sql diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 9278eb0..e2f16a0 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -37,15 +37,23 @@ jobs: run: curl -LsSf https://get.nexte.st/latest/linux | tar zxf - -C /usr/local/bin - name: Build - run: cargo build --all-features + run: | + cargo build --no-default-features --features static + cargo build --no-default-features --features static,std + cargo build --no-default-features --features dynamic + cargo build --no-default-features --features dynamic,std - name: Test - run: cargo nextest run --all-features + run: | + cargo nextest run --no-default-features --features static,dynamic + cargo nextest run --no-default-features --features static,dynamic,std - name: Test memvfs run: | - cargo build --example memvfs --features dynamic + cargo build --example memvfs --features dynamic,std cat examples/test_memvfs.sql | sqlite3 + cat examples/test_memvfs_panic.sql | sqlite3 >/tmp/memvfs_panic 2>&1 \ + || grep -q "unknown error (2)" /tmp/memvfs_panic - name: Clippy uses: auguwu/clippy-action@94a9ff2f6920180b89e5c03d121d0af04a9d3e03 # 1.4.0 diff --git a/Cargo.toml b/Cargo.toml index 55aa26b..7db6b20 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,8 +29,9 @@ bindgen = { version = "0.72", default-features = false } default = ["static"] static = [] dynamic = [] +std = [] [[example]] name = "memvfs" crate-type = ["cdylib"] -required-features = ["dynamic"] +required-features = ["dynamic", "std"] diff --git a/examples/memvfs.rs b/examples/memvfs.rs index 402a602..02a614c 100644 --- a/examples/memvfs.rs +++ b/examples/memvfs.rs @@ -1,4 +1,4 @@ -// cargo build --example memvfs --features dynamic +// cargo build --example memvfs --features dynamic,std use std::{ffi::c_void, os::raw::c_char, sync::Arc}; @@ -181,7 +181,11 @@ impl Vfs for MemVfs { pragma: Pragma<'_>, ) -> Result, PragmaErr> { log::debug!("pragma: file={:?}, pragma={:?}", handle.name, pragma); - Err(PragmaErr::NotFound) + if pragma.name == "memvfs_panic" { + panic!("testing panic") + } else { + Err(PragmaErr::NotFound) + } } } diff --git a/examples/test_memvfs.sql b/examples/test_memvfs.sql index 2acabcd..a7ec83e 100644 --- a/examples/test_memvfs.sql +++ b/examples/test_memvfs.sql @@ -1,6 +1,6 @@ -- Load the memvfs extension and open a new connection using it -- Build the memvfs extension using the following command: --- cargo build --example memvfs --features dynamic +-- cargo build --example memvfs --features dynamic,std -- uncomment to enable verbose logs -- .log stderr diff --git a/examples/test_memvfs_panic.sql b/examples/test_memvfs_panic.sql new file mode 100644 index 0000000..94f0063 --- /dev/null +++ b/examples/test_memvfs_panic.sql @@ -0,0 +1,20 @@ +-- Load the memvfs extension and open a new connection using it +-- Build the memvfs extension using the following command: +-- cargo build --example memvfs --features dynamic,std + +-- uncomment to enable verbose logs +-- .log stderr + +.load target/debug/examples/libmemvfs.so +.open main.db +.mode table +.log stdout + +.databases +.vfsinfo + +-- ensure that panics are handled +pragma memvfs_panic; + +-- but they cause all future calls to also fail! +CREATE TABLE t1(a, b); diff --git a/src/lib.rs b/src/lib.rs index 4210456..76a3c13 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ -#![no_std] +#![cfg_attr(not(feature = "std"), no_std)] extern crate alloc; pub mod vars { diff --git a/src/vfs.rs b/src/vfs.rs index f07579c..b71109a 100644 --- a/src/vfs.rs +++ b/src/vfs.rs @@ -37,6 +37,9 @@ pub const DEFAULT_DEVICE_CHARACTERISTICS: i32 = /// A `SQLite3` extended error code pub type SqliteErr = i32; +// construct a custom SQLITE_INTERNAL error code for tracking panics +pub const ERROR_PANIC: SqliteErr = vars::SQLITE_INTERNAL | (128 << 8); + pub type VfsResult = Result; // FileWrapper needs to be repr(C) and have sqlite3_file as it's first member @@ -79,6 +82,29 @@ impl PragmaErr { } } +#[cfg(feature = "std")] +fn fallible(mut cb: impl FnMut() -> Result) -> i32 { + use std::sync::atomic::Ordering; + use std::{panic::AssertUnwindSafe, sync::atomic::AtomicBool}; + + // once we panic, all future calls into the VFS will also panic as we can't + // be sure that we are unwind safe + static POISONED: AtomicBool = AtomicBool::new(false); + if POISONED.load(Ordering::Relaxed) { + return ERROR_PANIC; + } + + let inner = AssertUnwindSafe(|| cb().unwrap_or_else(|err| err)); + match std::panic::catch_unwind(inner) { + Ok(i) => i, + Err(_err) => { + POISONED.store(true, Ordering::SeqCst); + ERROR_PANIC + } + } +} + +#[cfg(not(feature = "std"))] fn fallible(mut cb: impl FnMut() -> Result) -> i32 { cb().unwrap_or_else(|err| err) }