Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Data race false positive due to delayed allocation optimization #3242

Closed
RalfJung opened this issue Dec 26, 2023 · 1 comment · Fixed by rust-lang/rust#129828
Closed

Data race false positive due to delayed allocation optimization #3242

RalfJung opened this issue Dec 26, 2023 · 1 comment · Fixed by rust-lang/rust#129828
Labels
A-data-race Area: data race detector C-bug Category: This is a bug. I-false-UB Impact: makes Miri falsely report UB, i.e., a false positive (with default settings)

Comments

@RalfJung
Copy link
Member

RalfJung commented Dec 26, 2023

The following code should be fine, but Miri raises an error:

use std::sync::atomic::Ordering::*;
use std::sync::atomic::*;

static P: AtomicPtr<u8> = AtomicPtr::new(core::ptr::null_mut());

fn main() {
    // Create the local variable, and initialize it.
    let mut val: u8 = 0;

    let t1 = std::thread::spawn(|| {
        while P.load(Relaxed).is_null() {
            std::hint::spin_loop();
        }
        unsafe {
            // Initialize `*P`.
            let ptr = P.load(Relaxed);
            *ptr = 127;
        }
    });

    // Actually generate memory for the local variable.
    // This is the time its value is actually written to memory:
    // that's *after* the thread above was spawned!
    P.store(std::ptr::addr_of_mut!(val), Relaxed);
    
    // Wait for the thread to be done.
    t1.join().unwrap();

    // Read initialized value.
    assert_eq!(val, 127);
}

The issue has been described well by CAD97 here:

Without having dived into Miri, it likely is a false positive due to places not being allocated into memory until their reference is taken. It's an optimization for most cases (there's a lot of bookkeeping happening for allocations in Miri) but results in a false positive here. Assuming that's a correct assessment, the fix would be to assign a timestamp corresponding to the local place initialization when reifying into an allocation, instead of the timestamp of the reification itself.

@RalfJung RalfJung added I-false-UB Impact: makes Miri falsely report UB, i.e., a false positive (with default settings) A-data-race Area: data race detector labels Dec 26, 2023
@RalfJung RalfJung added the C-bug Category: This is a bug. label Apr 18, 2024
@RalfJung
Copy link
Member Author

RalfJung commented Apr 21, 2024

the fix would be to assign a timestamp corresponding to the local place initialization

That's in general quite tricky, since we support this kind of delayed initialization for ScalarPair -- so we'd have to track a timestamp for each pair component. Also this breaks our usual memory abstractions pretty badly.

There's a fairly easy solution, which is to put the write done at the time of actual (late) initialization at timestamp 0. That will avoid false UB but will introduce bugs where we miss UB, such as in this example.

I think currently my preferred solution is

  • Either disable "delayed init" for all mutable local variables (let mut and interior mutability). However so far we've avoided making let vs let mut actually meaningful, and with Tree Borrows it is legal to write to immutable let variables...
  • Or do a static analysis when pushing the stack frame to determine upfront which locals have their address taken, so that we can put those in memory from the start.

rust-timer added a commit to rust-lang-ci/rust that referenced this issue Sep 15, 2024
Rollup merge of rust-lang#129828 - RalfJung:miri-data-race, r=saethlin

miri: treat non-memory local variables properly for data race detection

Fixes rust-lang/miri#3242

Miri has an optimization where some local variables are not represented in memory until something forces them to be stored in memory (most notably, creating a pointer/reference to the local will do that). However, for a subsystem triggering on memory accesses -- such as the data race detector -- this means that the memory access seems to happen only when the local is moved to memory, instead of at the time that it actually happens. This can lead to UB reports in programs that do not actually have UB.

This PR fixes that by adding machine hooks for reads and writes to such efficiently represented local variables. The data race system tracks those very similar to how it would track reads and writes to addressable memory, and when a local is moved to memory, the clocks get overwritten with the information stored for the local.
github-actions bot pushed a commit that referenced this issue Sep 16, 2024
miri: treat non-memory local variables properly for data race detection

Fixes #3242

Miri has an optimization where some local variables are not represented in memory until something forces them to be stored in memory (most notably, creating a pointer/reference to the local will do that). However, for a subsystem triggering on memory accesses -- such as the data race detector -- this means that the memory access seems to happen only when the local is moved to memory, instead of at the time that it actually happens. This can lead to UB reports in programs that do not actually have UB.

This PR fixes that by adding machine hooks for reads and writes to such efficiently represented local variables. The data race system tracks those very similar to how it would track reads and writes to addressable memory, and when a local is moved to memory, the clocks get overwritten with the information stored for the local.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-data-race Area: data race detector C-bug Category: This is a bug. I-false-UB Impact: makes Miri falsely report UB, i.e., a false positive (with default settings)
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant