Skip to content

Unaligned error with async generator field #1919

Closed
rust-lang/rust
#91303
@cuviper

Description

@cuviper

The following code in the playground reports a Miri error:

use futures::executor::block_on;
use futures::AsyncReadExt;

async fn hello_world() {
    let data = [0u8; 1];
    let mut reader = &data[..];

    let mut marker = [0u8; 1];
    reader.read_exact(&mut marker).await.unwrap();
}

fn main() {
    let future = hello_world();
    block_on(future);
}
error: Undefined Behavior: accessing memory with alignment 1, but alignment 2 is required
  --> src/main.rs:8:22
   |
8  |     let mut marker = [0u8; 1];
   |                      ^^^^^^^^ accessing memory with alignment 1, but alignment 2 is required
   |
   = 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
error notes

   = note: inside closure at src/main.rs:8:22
   = note: inside `<std::future::from_generator::GenFuture<[static generator@src/main.rs:4:24: 10:2]> as futures::Future>::poll` at /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/future/mod.rs:80:19
   = note: inside closure at /playground/.cargo/registry/src/github.com-1ecc6299db9ec823/futures-executor-0.3.17/src/local_pool.rs:315:23
   = note: inside closure at /playground/.cargo/registry/src/github.com-1ecc6299db9ec823/futures-executor-0.3.17/src/local_pool.rs:90:37
   = note: inside `std::thread::LocalKey::<std::sync::Arc<futures::futures_executor::local_pool::ThreadNotify>>::try_with::<[closure@futures::futures_executor::local_pool::run_executor<(), [closure@futures::futures_executor::block_on<std::future::from_generator::GenFuture<[static generator@src/main.rs:4:24: 10:2]>>::{closure#0}]>::{closure#0}], ()>` at /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/thread/local.rs:399:16
   = note: inside `std::thread::LocalKey::<std::sync::Arc<futures::futures_executor::local_pool::ThreadNotify>>::with::<[closure@futures::futures_executor::local_pool::run_executor<(), [closure@futures::futures_executor::block_on<std::future::from_generator::GenFuture<[static generator@src/main.rs:4:24: 10:2]>>::{closure#0}]>::{closure#0}], ()>` at /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/thread/local.rs:375:9
   = note: inside `futures::futures_executor::local_pool::run_executor::<(), [closure@futures::futures_executor::block_on<std::future::from_generator::GenFuture<[static generator@src/main.rs:4:24: 10:2]>>::{closure#0}]>` at /playground/.cargo/registry/src/github.com-1ecc6299db9ec823/futures-executor-0.3.17/src/local_pool.rs:86:5
   = note: inside `futures::futures_executor::block_on::<std::future::from_generator::GenFuture<[static generator@src/main.rs:4:24: 10:2]>>` at /playground/.cargo/registry/src/github.com-1ecc6299db9ec823/futures-executor-0.3.17/src/local_pool.rs:315:5
note: inside `main` at src/main.rs:14:5
  --> src/main.rs:14:5
   |
14 |     block_on(future);
   |     ^^^^^^^^^^^^^^^^
   = note: inside `<fn() as std::ops::FnOnce<()>>::call_once - shim(fn())` at /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:227:5
   = note: inside `std::sys_common::backtrace::__rust_begin_short_backtrace::<fn(), ()>` at /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/sys_common/backtrace.rs:123:18
   = note: inside closure at /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/rt.rs:145:18
   = note: inside `std::ops::function::impls::<impl std::ops::FnOnce<()> for &dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe>::call_once` at /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:259:13
   = note: inside `std::panicking::r#try::do_call::<&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe, i32>` at /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panicking.rs:406:40
   = note: inside `std::panicking::r#try::<i32, &dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe>` at /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panicking.rs:370:19
   = note: inside `std::panic::catch_unwind::<&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe, i32>` at /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panic.rs:133:14
   = note: inside closure at /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/rt.rs:128:48
   = note: inside `std::panicking::r#try::do_call::<[closure@std::rt::lang_start_internal::{closure#2}], isize>` at /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panicking.rs:406:40
   = note: inside `std::panicking::r#try::<isize, [closure@std::rt::lang_start_internal::{closure#2}]>` at /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panicking.rs:370:19
   = note: inside `std::panic::catch_unwind::<[closure@std::rt::lang_start_internal::{closure#2}], isize>` at /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panic.rs:133:14
   = note: inside `std::rt::lang_start_internal` at /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/rt.rs:128:20
   = note: inside `std::rt::lang_start::<()>` at /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/rt.rs:144:17

I believe Miri is wrong here, but I don't really know how it represents async code.

When I look at the LLVM IR, I see a memset(i8* align 2 ...) for this marker within the generator Suspend0 state. That alignment comes from PlaceRef::project_field's effective_field_align, because the struct has alignment 8 and the field is at offset 42, so the field will always be at alignment 2.

You can also bump this around in the example by changing the data length. At 2, marker moves to offset 43 and alignment 1, and miri is happy. At 3 we get offset 44 and alignment 4, and at 7 we get offset 48 and alignment 8, both miri errors.

So it seems Miri is seeing the projection alignment, but from the error span it looks like it may be applying that requirement to the initialization value?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions