Your RSA-2048 keys break in 2030. Find every one of them before attackers do.
🦀 crates.io

GHSA-ff9q-rm55-q7qr

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

Published
May 7, 2026
Updated
May 7, 2026
Affected
1 pkg
Patched
1 / 1
Exploits
None indexed

Blast Radius

1 pkg affected
🦀diesel-async

Real-time download stats are indexed for npm and PyPI packages. This vulnerability affects crates.io packages — download data is not available via public APIs for these ecosystems.

Description

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:

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:

[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:

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.

Affected Packages

1 total 1 fixed
EcosystemPackageVulnerable rangeFix
🦀crates.iodiesel-async0.1.0&&< 0.9.00.9.0

Detection & mitigation playbook

Open-source dependency
  1. Detect

    Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for diesel-async. O3's reachability analysis confirms whether the vulnerable code path is actually invoked in your application, so you act on real exposure instead of every transitive match.

  2. Fix

    Update diesel-async to 0.9.0 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms GHSA-ff9q-rm55-q7qr is resolved across your whole dependency graph.

  3. Workarounds

    If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.

  4. How O3 protects you

    O3 pinpoints whether GHSA-ff9q-rm55-q7qr is reachable in your code and exactly where to fix it, then blocks exploitation in production at runtime until the patched version is deployed.

Tailored to GHSA-ff9q-rm55-q7qr. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.

Frequently Asked Questions

### 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(/* fiel
O3 Security · Impact-Aware SCA

Is GHSA-ff9q-rm55-q7qr in your dependencies?

O3 detects GHSA-ff9q-rm55-q7qr across crates.io dependencies and uses function-level reachability to confirm whether the vulnerable code path is actually reachable — not just present. No false positives.