-
Notifications
You must be signed in to change notification settings - Fork 352
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
Make Atomic RMWs always fail on read-only memory #2463
Comments
That said, doing this the "obvious" way will also trigger all the other write hooks, and I am not sure that would be correct. See rust-lang/unsafe-code-guidelines#355 for that discussion. |
In particular this becomes relevant for the Stacked Borrows successor, where we want to see rust-lang/unsafe-code-guidelines#303 fixed -- that would also "fix" Miri to accept the original example, which we don't want. |
I think (at least DW) atomic load also needs to fail on read-only memory. The instructions generated by Also, on pre-v6 ARM linux, atomic load calls |
What's DW?
Uh wtf what?^^ |
I can't read assembly very well but it seems this is using a compare_exchange to implement a load? Interesting. But that's a hardware architecture thing, so I think it is enough to just reject this on read-only memory, but it doesn't mean we need to consider this a write for any other part of the memory model. Stacked Borrows treating this as read-only should still be sound, right? For example, if another thread does non-atomic loads concurrently with the first thread doing a failing compare_exchange, or one of these u128 atomic loads -- can anything strange happen, are there ever "wrong values" in memory for some intermediate moment in time, or is this really just a strange quirk of the x86 page tables we have to work around?
But the value written is the same as the value that was there, i.e., it is guaranteed to be a NOP write, right? On the hardware level this then should mean it's not really observable by other threads? |
double-width
Yeah, IMO, the problem is using atomic operations on read-only memory, and since the value is not changed, things like concurrent non-atomic loads from other threads should be fine. (If not, things like emulating 8-bit CAS using 32-bit CAS used on mips, powerpc, riscv, etc. would also be unsound. Since they also write to the memory around the target memory)
I believe this is a read-only memory-specific problem, but I don't believe this should be considered an x86-specific problem -- I tested a 128-bit atomic load into read-only memory on aarch64 linux and I got SIGSEGV.
Probably depends on the architecture; architectures that implement CAS using LL/SC may cause weak CAS in other threads to fail (I can confirm this on aarch64). |
The things you learn...
Legally speaking it could have failed anyway even if no other thread did anything, so that is fine. |
Yeah, that is definitely okay.
Well, I should probably have said, "emulate 8-bit/16-bit atomic RMWs using 32-bit atomic operations" to be exact. (Some architectures lack instructions for atomic RMWs that are smaller than the word size (32-bit). On such architectures, 8-bit and 16-bit atomic RMWs are emulated using 32-bit CAS loop or LL/SC loop. Rounded down the address of the pointer to a multiple of 4, load the 32-bit value, mask the value as only to operate a part of it, and store it by 32-bit CAS or SC...) If the memory model considers a write that is guaranteed to be no-op as "write", it should not be possible to implement atomic in such a way, because concurrent non-atomic reads to memory around an atomic value will be considered data race. |
The Rust memory model does, so this would not be a legal implementation in Rust. But if this is done at the end of the pipeline when generating assembly, it is fine -- hardware memory models AFAIK don't have a problem with this. |
You are right. I should have said something like "If hardware memory models consider a write to non-read-only memory that is guaranteed to be no-op as write..." |
Yes. I hope the targets that emulate small CAS using larger CASes don't have problems with concurrent non-mutating writes. :D |
…ackh726 add method to get the mutability of an AllocId Miri needs this for rust-lang/miri#2463.
…ackh726 add method to get the mutability of an AllocId Miri needs this for rust-lang/miri#2463.
This example should probably fail even with Stacked Borrows disabled. (It also segfaults at runtime.)
The text was updated successfully, but these errors were encountered: