VYPR
Critical severityNVD Advisory· Published Dec 31, 2020· Updated Aug 4, 2024

CVE-2020-35870

CVE-2020-35870

Description

An issue was discovered in the rusqlite crate before 0.23.0 for Rust. Memory safety can be violated via an Auxdata API use-after-free.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

CVE-2020-35870: Use-after-free in rusqlite crate's Auxdata API before 0.23.0 can lead to memory safety violations.

Vulnerability

CVE-2020-35870 is a use-after-free vulnerability in the rusqlite crate for Rust, affecting versions prior to 0.23.0. The issue resides in the Auxdata API, which allows custom SQLite functions to associate auxiliary data with a function call [3]. The use-after-free occurs when auxiliary data is accessed after it has been freed, violating memory safety guarantees that Rust typically enforces [1].

Exploitation

Exploitation requires the use of the affected Auxdata API in custom SQLite functions registered via rusqlite. An attacker would need to convince a user or application to run SQL that invokes these vulnerable functions. No authentication is required beyond the ability to execute SQL queries against the database. The attack surface is limited to applications that rely on user-defined scalar or aggregate functions that leverage the Auxdata API [2][4].

Impact

A successful exploit can lead to memory corruption, which may enable an attacker to read or write arbitrary memory, potentially leading to information disclosure, denial of service, or remote code execution in the context of the affected application. The Rust memory safety model is bypassed, undermining the guarantees of the language [3].

Mitigation

The vulnerability is fixed in rusqlite version 0.23.0. Users should update to this version or later. There are no known workarounds for earlier versions; upgrading is the only reliable mitigation [1][4]. The RustSec Advisory database (RUSTSEC-2020-0014) lists this CVE alongside other related memory safety issues in the same crate [3].

AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
rusqlitecrates.io
< 0.23.00.23.0

Affected products

2

Patches

2
54043c803c83

Prep release 0.23.0

https://github.com/rusqlite/rusqliteThom ChiovoloniApr 23, 2020via osv
1 file changed · +1 1
  • Cargo.toml+1 1 modified
    @@ -1,6 +1,6 @@
     [package]
     name = "rusqlite"
    -version = "0.22.0"
    +version = "0.23.0"
     authors = ["The rusqlite developers"]
     edition = "2018"
     description = "Ergonomic wrapper for SQLite"
    
2ef3628dac35

Actually fix auxdata api...

https://github.com/rusqlite/rusqliteThom ChiovoloniApr 13, 2020via ghsa
1 file changed · +54 65
  • src/functions.rs+54 65 modified
    @@ -12,6 +12,8 @@
     //! use regex::Regex;
     //! use rusqlite::functions::FunctionFlags;
     //! use rusqlite::{Connection, Error, Result, NO_PARAMS};
    +//! use std::sync::Arc;
    +//! type BoxError = Box<dyn std::error::Error + Send + Sync + 'static>;
     //!
     //! fn add_regexp_function(db: &Connection) -> Result<()> {
     //!     db.create_scalar_function(
    @@ -20,34 +22,19 @@
     //!         FunctionFlags::SQLITE_UTF8 | FunctionFlags::SQLITE_DETERMINISTIC,
     //!         move |ctx| {
     //!             assert_eq!(ctx.len(), 2, "called with unexpected number of arguments");
    -//!
    -//!             let saved_re: Option<&Regex> = ctx.get_aux(0)?;
    -//!             let new_re = match saved_re {
    -//!                 None => {
    -//!                     let s = ctx.get::<String>(0)?;
    -//!                     match Regex::new(&s) {
    -//!                         Ok(r) => Some(r),
    -//!                         Err(err) => return Err(Error::UserFunctionError(Box::new(err))),
    -//!                     }
    -//!                 }
    -//!                 Some(_) => None,
    -//!             };
    -//!
    +//!             let regexp: Arc<Regex> = ctx
    +//!                 .get_or_create_aux(0, |vr| -> Result<_, BoxError> {
    +//!                     Ok(Regex::new(vr.as_str()?)?)
    +//!                 })?;
     //!             let is_match = {
    -//!                 let re = saved_re.unwrap_or_else(|| new_re.as_ref().unwrap());
    -//!
     //!                 let text = ctx
     //!                     .get_raw(1)
     //!                     .as_str()
     //!                     .map_err(|e| Error::UserFunctionError(e.into()))?;
     //!
    -//!                 re.is_match(text)
    +//!                 regexp.is_match(text)
     //!             };
     //!
    -//!             if let Some(re) = new_re {
    -//!                 ctx.set_aux(0, re);
    -//!             }
    -//!
     //!             Ok(is_match)
     //!         },
     //!     )
    @@ -67,11 +54,12 @@
     //!     Ok(())
     //! }
     //! ```
    -use std::any::TypeId;
    +use std::any::Any;
     use std::os::raw::{c_int, c_void};
     use std::panic::{catch_unwind, RefUnwindSafe, UnwindSafe};
     use std::ptr;
     use std::slice;
    +use std::sync::Arc;
     
     use crate::ffi;
     use crate::ffi::sqlite3_context;
    @@ -121,6 +109,7 @@ unsafe extern "C" fn free_boxed_value<T>(p: *mut c_void) {
     pub struct Context<'a> {
         ctx: *mut sqlite3_context,
         args: &'a [*mut sqlite3_value],
    +    // conn: PhantomData<&'conn mut Connection>,
     }
     
     impl Context<'_> {
    @@ -174,47 +163,60 @@ impl Context<'_> {
             unsafe { ValueRef::from_value(arg) }
         }
     
    +    pub fn get_or_create_aux<T, E, F>(&self, arg: c_int, func: F) -> Result<Arc<T>>
    +    where
    +        T: Send + Sync + 'static,
    +        E: Into<Box<dyn std::error::Error + Send + Sync + 'static>>,
    +        F: FnOnce(ValueRef<'_>) -> Result<T, E>,
    +    {
    +        if let Some(v) = self.get_aux(arg)? {
    +            Ok(v)
    +        } else {
    +            let vr = self.get_raw(arg as usize);
    +            self.set_aux(
    +                arg,
    +                func(vr).map_err(|e| Error::UserFunctionError(e.into()))?,
    +            )
    +        }
    +    }
    +
         /// Sets the auxilliary data associated with a particular parameter. See
         /// https://www.sqlite.org/c3ref/get_auxdata.html for a discussion of
         /// this feature, or the unit tests of this module for an example.
    -    pub fn set_aux<T: 'static>(&self, arg: c_int, value: T) {
    -        let boxed = Box::into_raw(Box::new(AuxData {
    -            id: TypeId::of::<T>(),
    -            value,
    -        }));
    +    pub fn set_aux<T: Send + Sync + 'static>(&self, arg: c_int, value: T) -> Result<Arc<T>> {
    +        let orig: Arc<T> = Arc::new(value);
    +        let inner: AuxInner = orig.clone();
    +        let outer = Box::new(inner);
    +        let raw: *mut AuxInner = Box::into_raw(outer);
             unsafe {
                 ffi::sqlite3_set_auxdata(
                     self.ctx,
                     arg,
    -                boxed as *mut c_void,
    -                Some(free_boxed_value::<AuxData<T>>),
    +                raw as *mut _,
    +                Some(free_boxed_value::<AuxInner>),
                 )
             };
    +        Ok(orig)
         }
     
    -    /// Gets the auxilliary data that was associated with a given parameter
    -    /// via `set_aux`. Returns `Ok(None)` if no data has been associated,
    -    /// and .
    -    pub fn get_aux<T: 'static>(&self, arg: c_int) -> Result<Option<&T>> {
    -        let p = unsafe { ffi::sqlite3_get_auxdata(self.ctx, arg) as *const AuxData<T> };
    +    /// Gets the auxilliary data that was associated with a given parameter via
    +    /// `set_aux`. Returns `Ok(None)` if no data has been associated, and
    +    /// Ok(Some(v)) if it has. Returns an error if the requested type does not
    +    /// match.
    +    pub fn get_aux<T: Send + Sync + 'static>(&self, arg: c_int) -> Result<Option<Arc<T>>> {
    +        let p = unsafe { ffi::sqlite3_get_auxdata(self.ctx, arg) as *const AuxInner };
             if p.is_null() {
                 Ok(None)
             } else {
    -            let id = unsafe { (*p).id };
    -            if TypeId::of::<T>() != id {
    -                Err(Error::GetAuxWrongType)
    -            } else {
    -                Ok(Some(unsafe { &(*p).value }))
    -            }
    +            let v: AuxInner = AuxInner::clone(unsafe { &*p });
    +            v.downcast::<T>()
    +                .map(Some)
    +                .map_err(|_| Error::GetAuxWrongType)
             }
         }
     }
     
    -#[repr(C)]
    -struct AuxData<T: 'static> {
    -    id: TypeId,
    -    value: T,
    -}
    +type AuxInner = Arc<dyn Any + Send + Sync + 'static>;
     
     /// `feature = "functions"` Aggregate is the callback interface for user-defined
     /// aggregate function.
    @@ -776,34 +778,21 @@ mod test {
         // expression multiple times within one query.
         fn regexp_with_auxilliary(ctx: &Context<'_>) -> Result<bool> {
             assert_eq!(ctx.len(), 2, "called with unexpected number of arguments");
    -
    -        let saved_re: Option<&Regex> = ctx.get_aux(0)?;
    -        let new_re = match saved_re {
    -            None => {
    -                let s = ctx.get::<String>(0)?;
    -                match Regex::new(&s) {
    -                    Ok(r) => Some(r),
    -                    Err(err) => return Err(Error::UserFunctionError(Box::new(err))),
    -                }
    -            }
    -            Some(_) => None,
    -        };
    +        type BoxError = Box<dyn std::error::Error + Send + Sync + 'static>;
    +        let regexp: std::sync::Arc<Regex> = ctx
    +            .get_or_create_aux(0, |vr| -> Result<_, BoxError> {
    +                Ok(Regex::new(vr.as_str()?)?)
    +            })?;
     
             let is_match = {
    -            let re = saved_re.unwrap_or_else(|| new_re.as_ref().unwrap());
    -
                 let text = ctx
                     .get_raw(1)
                     .as_str()
                     .map_err(|e| Error::UserFunctionError(e.into()))?;
     
    -            re.is_match(text)
    +            regexp.is_match(text)
             };
     
    -        if let Some(re) = new_re {
    -            ctx.set_aux(0, re);
    -        }
    -
             Ok(is_match)
         }
     
    @@ -878,10 +867,10 @@ mod test {
             let db = Connection::open_in_memory().unwrap();
             db.create_scalar_function("example", 2, FunctionFlags::default(), |ctx| {
                 if !ctx.get::<bool>(1)? {
    -                ctx.set_aux::<i64>(0, 100);
    +                ctx.set_aux::<i64>(0, 100)?;
                 } else {
                     assert_eq!(ctx.get_aux::<String>(0), Err(Error::GetAuxWrongType));
    -                assert_eq!(ctx.get_aux::<i64>(0), Ok(Some(&100)));
    +                assert_eq!(*ctx.get_aux::<i64>(0).unwrap().unwrap(), 100);
                 }
                 Ok(true)
             })
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

5

News mentions

0

No linked articles in our index yet.