Skip to content

sqlite: SqliteError::try_new() unsafely assumes sqlite3_errmsg() is UTF-8 #4193

@meng-xu-cs

Description

@meng-xu-cs

I have found these related issues/pull requests

N/A

Description

sqlx-sqlite currently builds SqliteError.message from sqlite3_errmsg() using str::from_utf8_unchecked():

let message = unsafe {
    let msg = sqlite3_errmsg(handle);
    debug_assert!(!msg.is_null());
    str::from_utf8_unchecked(CStr::from_ptr(msg).to_bytes()).to_owned()
};

That assumption is not valid for sqlite3_errmsg(). SQLite documents that if schema element names contain invalid UTF, error messages generated through sqlite3_errmsg() may also be invalid UTF.

Because SQLx exposes the result as a Rust String / &str, this is a safe-API soundness issue: a malicious or corrupted SQLite file can cause SQLx to construct an invalid Rust string.

This is distinct from the sqlite3_errstr() path in from_code(): SQLite documents sqlite3_errstr() as UTF-8, so the problem is specifically the sqlite3_errmsg() path.

Reproduction steps

Run the following SQL with any SQLite client (Python example included below):

CREATE TABLE t(x);
PRAGMA writable_schema = ON;
INSERT INTO sqlite_schema(type, name, tbl_name, rootpage, sql)
VALUES (
  'view',
  CAST(X'626164FF76696577' AS TEXT),
  CAST(X'626164FF76696577' AS TEXT),
  0,
  'CREATE VIEW whatever AS SELECT FROM t'
);

Python helper:

import sqlite3

con = sqlite3.connect("bad.db")
cur = con.cursor()
cur.execute("CREATE TABLE t(x)")
cur.execute("PRAGMA writable_schema=ON")
cur.execute(
    """
    INSERT INTO sqlite_schema(type, name, tbl_name, rootpage, sql)
    VALUES (
      'view',
      CAST(X'626164FF76696577' AS TEXT),
      CAST(X'626164FF76696577' AS TEXT),
      0,
      'CREATE VIEW whatever AS SELECT FROM t'
    )
    """
)
con.commit()
con.close()

Step 2: Trigger the error through safe SQLx code

use sqlx::{Connection, sqlite::SqliteConnection};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut conn = SqliteConnection::connect("sqlite://bad.db").await?;

    // Any schema-dependent statement is enough.
    let err = sqlx::query("SELECT * FROM t")
        .execute(&mut conn)
        .await
        .unwrap_err();

    // On an affected build, this path is already UB because SQLx may have
    // created an invalid Rust string from sqlite3_errmsg().
    eprintln!("{err}");

    Ok(())
}

For comparison, a direct sqlite3_exec("SELECT * FROM t") on the crafted DB returns an error message whose raw bytes include 0xff:

malformed database schema (bad\xffview) - near "FROM": syntax error

SQLx version

main branch

Enabled SQLx features

default

Database server and version

SQLite

Operating system

MacOS

Rust version

1.94.0 (4a4ef493e 2026-03-02)

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions