Skip to content

std::ptr::copy() can read from uninitialized statics (unsound) #142532

@theemathas

Description

@theemathas

It is possible to create references to statics while you're still initializing it. Normally, attempting to read from such a reference will cause a compile-time error, with the message "encountered static that tried to initialize itself with itself". However, reading from such a reference with std::ptr::copy() (and also std::ptr::copy_nonoverlapping() and std::ptr::read_unaligned(), but not std::ptr::read()) does not cause this error. For example:

use std::mem::MaybeUninit;

pub static X: (i32, MaybeUninit<i32>) = (1, foo(&X.0));

const fn foo(x: &i32) -> MaybeUninit<i32> {
    let mut temp = MaybeUninit::<i32>::uninit();
    unsafe {
        std::ptr::copy(x, temp.as_mut_ptr(), 1);
    }
    temp
}

This code compiles fine, and leaves X.1 as an uninitialized value.

This can cause supposedly-sound API using unsafe code to become unsound. For example:

Example unsoundness
mod module {
    use std::mem::MaybeUninit;
    
    // SAFETY invariant: The MaybeUninit is always initialized
    pub struct YesInit(MaybeUninit<i32>);
    
    impl YesInit {
        pub const fn new(x: &i32) -> Self {
            let ptr = &raw const *x;
            let mut temp = MaybeUninit::<i32>::uninit();
            unsafe {
                // SAFETY: We're reading from a reference into a local variable.
                // So, both pointers are valid.
                std::ptr::copy(ptr, temp.as_mut_ptr(), 1);
            }
            // SAFETY: We just initialized it from a reference, which must be initialized
            Self(temp)
        }
        
        pub fn to_inner(&self) -> i32 {
            unsafe {
                // SAFETY: Guaranteed by safety invariant
                self.0.assume_init()
            }
        }
    }
}

use module::YesInit;

static X: (i32, YesInit) = (1, YesInit::new(&X.0));

fn main() {
    println!("{}", X.1.to_inner());
}

In this code, the YesInit type exposes an API that I think should be sound. However, the weirdness with std::ptr::copy means that this API can be used to cause undefined behavior. Running this code with Miri reports: "error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory" inside the to_inner() call, which happens at run time, not compile time.

See also #142404 for shenanigans with uninitialized statics.

Meta

Reproducible on the playground with 1.89.0-nightly (2025-06-11 e703dff8fe220b78195c)

@rustbot labels +I-unsound +A-const-eval

Metadata

Metadata

Assignees

Labels

A-const-evalArea: Constant evaluation, covers all const contexts (static, const fn, ...)C-bugCategory: This is a bug.I-unsoundIssue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/SoundnessP-highHigh priorityT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.T-langRelevant to the language team

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions