CVE-2020-35873
Description
An issue was discovered in the rusqlite crate before 0.23.0 for Rust. Memory safety can be violated because sessions.rs has a use-after-free.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Use-after-free in rusqlite's sessions.rs prior to 0.23.0 allows memory safety violation.
Vulnerability
CVE-2020-35873 is a use-after-free bug in the rusqlite crate for Rust, affecting versions before 0.23.0. The flaw exists in sessions.rs, where a reference to freed memory could be used, leading to undefined behavior. This is part of a group of memory safety issues addressed in the same release [3].
Exploitation
The vulnerability can be triggered by code that uses the sessions API provided by rusqlite. An attacker would need to craft input or control program flow to cause the dangling reference to be accessed. No special authentication is required beyond normal Rust API usage; the bug manifests purely through the crate's internal logic [1][2].
Impact
Successful exploitation could allow an attacker to read or write arbitrary memory, potentially leading to data corruption, information disclosure, or arbitrary code execution. The severity is heightened because memory safety violations in Rust are unexpected and undermine the language's guarantees [3][4].
Mitigation
The issue is fixed in rusqlite version 0.23.0 and later. Users should update their Cargo.toml dependency to "0.23.0" or newer. There is no workaround for older versions; updating is the only reliable mitigation [4].
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.
| Package | Affected versions | Patched versions |
|---|---|---|
rusqlitecrates.io | < 0.23.0 | 0.23.0 |
Affected products
2- Rust/rusqlitedescription
Patches
254043c803c83Prep release 0.23.0
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"
ac30e169ae51Use SmallCString in most places
3 files changed · +16 −12
src/inner_connection.rs+7 −6 modified@@ -1,4 +1,4 @@ -use std::ffi::CString; +use std::ffi::CStr; use std::os::raw::{c_char, c_int}; #[cfg(feature = "load_extension")] use std::path::Path; @@ -8,7 +8,7 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; use super::ffi; -use super::{str_for_sqlite, str_to_cstring}; +use super::str_for_sqlite; use super::{Connection, InterruptHandle, OpenFlags, Result}; use crate::error::{error_from_handle, error_from_sqlite_code, Error}; use crate::raw_statement::RawStatement; @@ -51,9 +51,9 @@ impl InnerConnection { } pub fn open_with_flags( - c_path: &CString, + c_path: &CStr, flags: OpenFlags, - vfs: Option<&CString>, + vfs: Option<&CStr>, ) -> Result<InnerConnection> { #[cfg(not(feature = "bundled"))] ensure_valid_sqlite_version(); @@ -171,7 +171,8 @@ impl InnerConnection { } pub fn execute_batch(&mut self, sql: &str) -> Result<()> { - let c_sql = str_to_cstring(sql)?; + // use CString instead of SmallCString because it's probably big. + let c_sql = std::ffi::CString::new(sql)?; unsafe { let r = ffi::sqlite3_exec( self.db(), @@ -196,7 +197,7 @@ impl InnerConnection { unsafe { let mut errmsg: *mut c_char = ptr::null_mut(); let r = if let Some(entry_point) = entry_point { - let c_entry = str_to_cstring(entry_point)?; + let c_entry = crate::str_to_cstring(entry_point)?; ffi::sqlite3_load_extension( self.db, dylib_str.as_ptr(),
src/lib.rs+4 −3 modified@@ -129,6 +129,7 @@ mod version; pub mod vtab; pub(crate) mod util; +pub(crate) use util::SmallCString; // Number of cached prepared statements we'll hold on to. const STATEMENT_CACHE_DEFAULT_CAPACITY: usize = 16; @@ -233,8 +234,8 @@ unsafe fn errmsg_to_string(errmsg: *const c_char) -> String { String::from_utf8_lossy(c_slice).into_owned() } -fn str_to_cstring(s: &str) -> Result<CString> { - Ok(CString::new(s)?) +fn str_to_cstring(s: &str) -> Result<SmallCString> { + Ok(SmallCString::new(s)?) } /// Returns `Ok((string ptr, len as c_int, SQLITE_STATIC | SQLITE_TRANSIENT))` @@ -301,7 +302,7 @@ pub enum DatabaseName<'a> { feature = "modern_sqlite" ))] impl DatabaseName<'_> { - fn to_cstring(&self) -> Result<CString> { + fn to_cstring(&self) -> Result<util::SmallCString> { use self::DatabaseName::{Attached, Main, Temp}; match *self { Main => str_to_cstring("main"),
src/session.rs+5 −3 modified@@ -102,10 +102,11 @@ impl Session<'_> { /// Attach a table. `None` means all tables. pub fn attach(&mut self, table: Option<&str>) -> Result<()> { let table = if let Some(table) = table { - str_to_cstring(table)?.as_ptr() + Some(str_to_cstring(table)?) } else { - ptr::null() + None }; + let table = table.as_ref().map(|s| s.as_ptr()).unwrap_or(ptr::null()); unsafe { check!(ffi::sqlite3session_attach(self.s, table)) }; Ok(()) } @@ -156,7 +157,8 @@ impl Session<'_> { /// Load the difference between tables. pub fn diff(&mut self, from: DatabaseName<'_>, table: &str) -> Result<()> { let from = from.to_cstring()?; - let table = str_to_cstring(table)?.as_ptr(); + let table = str_to_cstring(table)?; + let table = table.as_ptr(); unsafe { let mut errmsg = ptr::null_mut(); let r =
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- github.com/advisories/GHSA-q3cc-7p7g-392cghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2020-35873ghsaADVISORY
- github.com/rusqlite/rusqlite/commit/ac30e169ae51b262bc8cf7026469851ce39b23c6ghsaWEB
- github.com/rusqlite/rusqlite/releases/tag/0.23.0ghsax_refsource_MISCWEB
- rustsec.org/advisories/RUSTSEC-2020-0014.htmlghsax_refsource_MISCWEB
News mentions
0No linked articles in our index yet.