Skip to content

fix: replace from_utf8_unchecked with from_utf8 in SQLite column name handling#4221

Merged
abonander merged 2 commits intolaunchbadge:mainfrom
barry3406:fix/sqlite-column-name-utf8-safety
Apr 9, 2026
Merged

fix: replace from_utf8_unchecked with from_utf8 in SQLite column name handling#4221
abonander merged 2 commits intolaunchbadge:mainfrom
barry3406:fix/sqlite-column-name-utf8-safety

Conversation

@barry3406
Copy link
Copy Markdown
Contributor

Summary

Fixes #4192

The SQLite driver uses from_utf8_unchecked() to convert C strings returned by SQLite's C API (column names, table names, etc.) into Rust &str values. This is a soundness violation: SQLite does not guarantee that these strings are valid UTF-8, and from_utf8_unchecked on non-UTF-8 input produces an invalid &str, which is undefined behavior in Rust.

What's wrong

SQLite's C API functions like sqlite3_column_name(), sqlite3_column_table_name(), sqlite3_column_origin_name(), sqlite3_column_database_name(), sqlite3_column_decltype(), sqlite3_bind_parameter_name(), and sqlite3_sql() can all return non-UTF-8 strings. The previous code used from_utf8_unchecked on these return values, which:

  1. Violates Rust's safety invariant that all &str values must be valid UTF-8
  2. Can be triggered through a safe public API (no unsafe required by the caller)
  3. Constitutes undefined behavior per the Rust reference

The fix

All 8 call sites in sqlx-sqlite/src/statement/handle.rs are replaced:

  • from_utf8_unchecked(...) becomes from_utf8(...).expect("descriptive message")
  • The unused from_utf8_unchecked import is removed

This converts potential undefined behavior into a defined panic with a clear error message identifying which SQLite C API function returned non-UTF-8 data.

Behavioral impact

  • For valid UTF-8 inputs (all practical usage): Zero behavioral change. from_utf8 returns Ok and .expect() unwraps it, producing the same &str as before.
  • For invalid UTF-8 inputs: Instead of silently producing an invalid &str (UB), the program panics with a clear message. This is the correct behavior since the rest of the codebase assumes valid UTF-8 strings.

barry3406 and others added 2 commits April 9, 2026 06:19
…andle

Replace all uses of `from_utf8_unchecked` with safe `from_utf8` in the
SQLite statement handle to fix a soundness issue.

SQLite allows non-UTF-8 column names via its C API, but
`from_utf8_unchecked` assumes valid UTF-8 without checking. This can
produce invalid `&str` values through a safe public API, which is
undefined behavior in Rust.

Using `from_utf8().expect()` instead converts potential UB into a
defined panic with a clear message. There is no behavioral change for
valid UTF-8 inputs, which covers all practical usage.

Fixes launchbadge#4192

from_utf8_unchecked(CStr::from_ptr(raw).to_bytes())
from_utf8(CStr::from_ptr(raw).to_bytes())
.expect("sqlite3_sql() returned non-UTF-8 string")
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Turning these into panics is a step up from undefined behavior but long-term we might want to make this a recoverable error.

This is fine to merge as-is, I just wanted to get that on record.

@abonander abonander merged commit 4249cb4 into launchbadge:main Apr 9, 2026
147 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

sqlite: invalid UTF-8 column names can reach safe Column::name() via from_utf8_unchecked

2 participants