VDB
Sign up
LOW

GHSA-ff9q-rm55-q7qr

diesel-async may expose uninitialized padding bytes for MySQL temporal columns

Details

### Summary

diesel-async exposes uninitialized stack padding to safe code on every read of a MySQL `DATE`, `TIME`, `DATETIME`, or `TIMESTAMP` column. Reading that buffer is undefined behavior, and the leaked bytes can contain stale heap/stack contents, so this is both a soundness bug and a potential information-disclosure vector.

### Details

In `diesel-async/src/mysql/row.rs` (lines 65-103), `MysqlRow::get` builds a `MysqlTime` from the parsed `mysql_async::Value` and then fabricates the byte buffer that downstream `FromSql` impls expect like this:

```rust let date = MysqlTime::new(/* fields from Value::Date / Value::Time */); let buffer = unsafe { let ptr = &date as *const MysqlTime as *const u8; let slice = std::slice::from_raw_parts(ptr, std::mem::size_of::<MysqlTime>()); slice.to_vec() }; ```

`MysqlTime` is `#[repr(C)]` with 3 bytes of padding after `bool neg` (Linux x86_64, offsets 0x21..0x23). The literal construction leaves that padding uninitialized, and `to_vec()` carries it into a `Vec<u8>` that becomes the `MysqlValue`'s backing buffer, reachable from safe code via `MysqlValue::as_bytes() -> &[u8]`.

`diesel` itself avoids this by going through `MaybeUninit::<MysqlTime>::zeroed()` + `ptr::copy_nonoverlapping` (see `diesel/src/mysql/value.rs:43-94`); the same pattern would fix this. Alternatively, write the bytes diesel's `FromSql` reads without round-tripping through a `MysqlTime` value.

### PoC

`Cargo.toml`: ```toml [dependencies] diesel = { version = "~2.3.0", default-features = false, features = ["mysql_backend"] } diesel-async = { version = "=0.8.0", features = ["mysql"] } mysql_common = { version = "0.35", default-features = false } ```

`src/main.rs`: ```rust use diesel::row::{Field, Row}; use diesel_async::{AsyncConnectionCore, AsyncMysqlConnection}; use mysql_common::{constants::ColumnType, packets::Column, prelude::FromRow, value::Value};

type MysqlRow = <AsyncMysqlConnection as AsyncConnectionCore>::Row<'static, 'static>;

fn main() { let cols = std::sync::Arc::from([Column::new(ColumnType::MYSQL_TYPE_DATE)]); let raw = mysql_common::row::new_row(vec![Value::Date(2024, 1, 1, 0, 0, 0, 0)], cols); let row: MysqlRow = FromRow::from_row(raw);

let field = row.get(0).unwrap(); let bytes = field.value().unwrap().as_bytes(); let _: u64 = bytes.iter().map(|&b| b as u64).sum(); // UB: hits padding } ```

Miri output:

``` error: Undefined Behavior: reading memory at alloc844[0x21..0x22], but memory is uninitialized at [0x21..0x22], and this operation requires initialized memory --> src/main.rs:14:37 | 14 | let _: u64 = bytes.iter().map(|&b| b as u64).sum(); // UB: hits padding | ^ Undefined Behavior occurred here | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = note: stack backtrace: 0: main::{closure#0} at src/main.rs:14:37: 14:38 1: std::iter::adapters::map::map_fold::<&u8, u64, u64, {closure@src/main.rs:14:35: 14:39}, {closure@<u64 as std::iter::Sum>::sum<std::iter::Map<std::slice::Iter<'_, u8>, {closure@src/main.rs:14:35: 14:39}>>::{closure#0}}>::{closure#0} at /home/paolobarbolini/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/iter/adapters/map.rs:88:28: 88:34 2: <std::slice::Iter<'_, u8> as std::iter::Iterator>::fold::<u64, {closure@std::iter::adapters::map::map_fold<&u8, u64, u64, {closure@src/main.rs:14:35: 14:39}, {closure@<u64 as std::iter::Sum>::sum<std::iter::Map<std::slice::Iter<'_, u8>, {closure@src/main.rs:14:35: 14:39}>>::{closure#0}}>::{closure#0}}> at /home/paolobarbolini/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/slice/iter/macros.rs:279:27: 279:85 3: <std::iter::Map<std::slice::Iter<'_, u8>, {closure@src/main.rs:14:35: 14:39}> as std::iter::Iterator>::fold::<u64, {closure@<u64 as std::iter::Sum>::sum<std::iter::Map<std::slice::Iter<'_, u8>, {closure@src/main.rs:14:35: 14:39}>>::{closure#0}}> at /home/paolobarbolini/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/iter/adapters/map.rs:128:9: 128:50 4: <u64 as std::iter::Sum>::sum::<std::iter::Map<std::slice::Iter<'_, u8>, {closure@src/main.rs:14:35: 14:39}>> at /home/paolobarbolini/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/iter/traits/accum.rs:52:17: 56:18 5: <std::iter::Map<std::slice::Iter<'_, u8>, {closure@src/main.rs:14:35: 14:39}> as std::iter::Iterator>::sum::<u64> at /home/paolobarbolini/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/iter/traits/iterator.rs:3676:9: 3676:23 6: main at src/main.rs:14:18: 14:55

Uninitialized memory occurred at alloc844[0x21..0x22], in this allocation: alloc844 (Rust heap, size: 48, align: 1) { 0x00 │ e8 07 00 00 01 00 00 00 01 00 00 00 00 00 00 00 │ ................ 0x10 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ 0x20 │ 00 __ __ __ 01 00 00 00 00 00 00 00 __ __ __ __ │ .░░░........░░░░ } ```

### Impact

Soundness bug in safe API surface of `diesel-async`'s MySQL backend. Affects every user of `AsyncMysqlConnection` whose queries return a temporal column.

AI disclosure: this issue was found via Claude Code running Claude Opus 4.7.

Are you affected?

Enter the version of the package you're using.

Affected packages

crates.io/diesel-async
Introduced in: 0.1.0Fixed in: 0.9.0

Upgrade diesel-async to 0.9.0 or newer (ecosystem crates.io).

References