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

miri weak memory emulation: put previous value into initial store buffer #128942

Merged
merged 1 commit into from
Aug 27, 2024

Conversation

RalfJung
Copy link
Member

Fixes rust-lang/miri#2164 by doing a read before each atomic write so that we can initialize the store buffer. The read suppresses memory access hooks and UB exceptions, to avoid otherwise influencing the program behavior. If the read fails, we store that as None in the store buffer, so that when an atomic read races with the first atomic write to some memory and previously the memory was uninitialized, we can report UB due to reading uninit memory.

@cbeuw this changes a bit the way we initialize the store buffers. Not sure if you still remember all this code, but if you could have a look to make sure this still makes sense, that would be great. :)

r? @saethlin

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Aug 10, 2024
@rustbot
Copy link
Collaborator

rustbot commented Aug 10, 2024

The Miri subtree was changed

cc @rust-lang/miri

Some changes occurred to the CTFE / Miri engine

cc @rust-lang/miri

// The program didn't actually do a read, so suppress the memory access hooks.
// This is also a very special exception where we just ignore an error -- if this read
// was UB e.g. because the memory is uninitialized, we don't want to know!
let old_val = this.run_for_validation(|| this.read_scalar(dest)).ok();
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically we only need to do this read if there wasn't a store buffer here yet. But to actually realize that we'd have to do the buffered_atomic_write before write_scalar, and then give buffered_atomic_write a closure that reads the previous value if needed. I am not sure doing the buffered store before the real store works of if there are some subtle invariants here preventing this...

Copy link
Contributor

@cbeuw cbeuw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, I'm assuming this is made possible by the new run_for_validation function

// access
val,
// access.
val: Some(val),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason to represent an uninit store buffer as one containing a store element with None val, instead of just an empty buffer with no elements?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is related to the new test I added: if the memory is uninitialized when the atomic object is created, an outdated read hitting that initial state should properly report UB.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So we can initially have uninitialized memory, but we do not have a mechanism to write uninitialized bytes later?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, indeed. Currently all atomic intrinsics take iN or *const T types which must be initialized.

If we ever have MaybeUninit atomics, this will become a lot more tricky, since the value could be partially initialized.

@RalfJung
Copy link
Member Author

@saethlin how should we proceed here? Is there some way I can make reviewing simpler? Should we look for another reviewer?

Most of this is in Miri where I'd probably self-approve based on @cbeuw having taken a look, but we do need to make a function in rustc public to make this work which is why this became a rustc PR.

@saethlin
Copy link
Member

I'll review this today, that's how we will proceed :p

@RalfJung
Copy link
Member Author

RalfJung commented Aug 26, 2024 via email

@saethlin
Copy link
Member

I have a question about the implementation, but it's just idle curiosity. Thanks for the reminder.

@bors r+

@bors
Copy link
Contributor

bors commented Aug 26, 2024

📌 Commit 8b18c6b has been approved by saethlin

It is now in the queue for this repository.

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Aug 26, 2024
workingjubilee added a commit to workingjubilee/rustc that referenced this pull request Aug 27, 2024
…saethlin

miri weak memory emulation: put previous value into initial store buffer

Fixes rust-lang/miri#2164 by doing a read before each atomic write so that we can initialize the store buffer. The read suppresses memory access hooks and UB exceptions, to avoid otherwise influencing the program behavior. If the read fails, we store that as `None` in the store buffer, so that when an atomic read races with the first atomic write to some memory and previously the memory was uninitialized, we can report UB due to reading uninit memory.

`@cbeuw` this changes a bit the way we initialize the store buffers. Not sure if you still remember all this code, but if you could have a look to make sure this still makes sense, that would be great. :)

r? `@saethlin`
bors added a commit to rust-lang-ci/rust that referenced this pull request Aug 27, 2024
…kingjubilee

Rollup of 9 pull requests

Successful merges:

 - rust-lang#126985 (Implement `-Z embed-source` (DWARFv5 source code embedding extension))
 - rust-lang#127922 (Add unsafe to extern blocks in style guide)
 - rust-lang#128731 (simd_shuffle intrinsic: allow argument to be passed as vector)
 - rust-lang#128935 (More work on `zstd` compression)
 - rust-lang#128942 (miri weak memory emulation: put previous value into initial store buffer)
 - rust-lang#129418 (rustc: Simplify getting sysroot library directory)
 - rust-lang#129490 (Add Trusty OS as tier 3 target)
 - rust-lang#129559 (float types: document NaN bit pattern guarantees)
 - rust-lang#129642 (Bump backtrace to rust-lang/backtrace@fc37b22)

r? `@ghost`
`@rustbot` modify labels: rollup
bors added a commit to rust-lang-ci/rust that referenced this pull request Aug 27, 2024
Rollup of 9 pull requests

Successful merges:

 - rust-lang#126985 (Implement `-Z embed-source` (DWARFv5 source code embedding extension))
 - rust-lang#127922 (Add unsafe to extern blocks in style guide)
 - rust-lang#128731 (simd_shuffle intrinsic: allow argument to be passed as vector)
 - rust-lang#128935 (More work on `zstd` compression)
 - rust-lang#128942 (miri weak memory emulation: put previous value into initial store buffer)
 - rust-lang#129418 (rustc: Simplify getting sysroot library directory)
 - rust-lang#129490 (Add Trusty OS as tier 3 target)
 - rust-lang#129536 (Add `f16` and `f128` inline ASM support for `aarch64`)
 - rust-lang#129559 (float types: document NaN bit pattern guarantees)

r? `@ghost`
`@rustbot` modify labels: rollup
@bors bors merged commit 427019e into rust-lang:master Aug 27, 2024
6 checks passed
@rustbot rustbot added this to the 1.82.0 milestone Aug 27, 2024
rust-timer added a commit to rust-lang-ci/rust that referenced this pull request Aug 27, 2024
Rollup merge of rust-lang#128942 - RalfJung:interpret-weak-memory, r=saethlin

miri weak memory emulation: put previous value into initial store buffer

Fixes rust-lang/miri#2164 by doing a read before each atomic write so that we can initialize the store buffer. The read suppresses memory access hooks and UB exceptions, to avoid otherwise influencing the program behavior. If the read fails, we store that as `None` in the store buffer, so that when an atomic read races with the first atomic write to some memory and previously the memory was uninitialized, we can report UB due to reading uninit memory.

``@cbeuw`` this changes a bit the way we initialize the store buffers. Not sure if you still remember all this code, but if you could have a look to make sure this still makes sense, that would be great. :)

r? ``@saethlin``
@RalfJung RalfJung deleted the interpret-weak-memory branch August 28, 2024 05:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Initialisation value of atomic types can no longer be observed if the first atomic access is a store
5 participants