Skip to content

Commit a9de7a6

Browse files
bors[bot]taiki-e
andauthored
Merge #1
1: Remove miri hack r=taiki-e a=taiki-e Use currently use a hack to avoid rust-lang/rust#69488 and to make sure that Miri errors for atomic load/store of integers containing uninitialized bytes (which is probably not a problem and uncharted territory at best [1] [2] [3], and can be detected by `-Zmiri-check-number-validity` [4]), do not mask Miri errors for the use of uninitialized bytes (which is definitely a problem). https://github.com/taiki-e/atomic-memcpy/blob/3507fef17534e4825b2b303d04702b4678e29dd0/src/lib.rs#L426-L450 [1]: crossbeam-rs/crossbeam#315 [2]: rust-lang/unsafe-code-guidelines#158 [3]: rust-lang/unsafe-code-guidelines#71 [4]: rust-lang/miri#1904 However, this actually causes another "unsupported operation" Miri error. ``` error: unsupported operation: unable to turn pointer into raw bytes --> /Users/taiki/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/src/rust/library/core/src/ptr/mod.rs:701:9 | 701 | copy_nonoverlapping(src, tmp.as_mut_ptr(), 1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into raw bytes | = help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support ``` Co-authored-by: Taiki Endo <[email protected]>
2 parents 3507fef + b1c9ebd commit a9de7a6

File tree

6 files changed

+45
-54
lines changed

6 files changed

+45
-54
lines changed

.github/workflows/ci.yml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -146,11 +146,12 @@ jobs:
146146
component: miri
147147
- run: cargo miri test --workspace -- --test-threads=1
148148
env:
149-
MIRIFLAGS: -Zmiri-symbolic-alignment-check -Zmiri-tag-raw-pointers -Zmiri-disable-isolation
150-
# tests/padding.rs intentional contains tests for cases that are incompatible with -Zmiri-check-number-validity.
151-
- run: cargo miri test --test test -- --test-threads=1
149+
MIRIFLAGS: -Zmiri-check-number-validity -Zmiri-tag-raw-pointers -Zmiri-disable-isolation
150+
- run: cargo miri test --workspace -- --test-threads=1
152151
env:
153152
MIRIFLAGS: -Zmiri-check-number-validity -Zmiri-symbolic-alignment-check -Zmiri-tag-raw-pointers -Zmiri-disable-isolation
153+
# -Zmiri-symbolic-alignment-check is incompatible with the code that does manual integer arithmetic to ensure alignment.
154+
RUSTFLAGS: ${{ env.RUSTFLAGS }} --cfg atomic_memcpy_symbolic_alignment_check_compat
154155

155156
san:
156157
strategy:

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ Note: In this file, do not use the hard wrap in the middle of a sentence for com
1010

1111
## [Unreleased]
1212

13+
- Fix "unsupported operation: unable to turn pointer into raw bytes" Miri error. ([#1](https://github.com/taiki-e/atomic-memcpy/pull/1))
14+
1315
## [0.1.0] - 2022-02-12
1416

1517
Initial release

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,12 @@ See [P1478R1][p1478r1] for more.
1818
- If the alignment of the type being copied is the same as the pointer width, `atomic_load` is possible to produce an assembly roughly equivalent to the case of using volatile read + atomic fence on many platforms. (See [`tests/asm-test/asm`][asm-test] directory for more).
1919
- If the alignment of the type being copied is smaller than the pointer width, there will be some performance degradation. However, it is implemented in such a way that it does not cause extreme performance degradation at least on x86_64. (See [the implementation comments of `atomic_load`][implementation] for more.) It is possible that there is still room for improvement, especially on non-x86_64 platforms.
2020
- Optimization for the case where the alignment of the type being copied is larger than the pointer width has not yet been fully investigated. It is possible that there is still room for improvement, especially on 32-bit platforms where `AtomicU64` is available.
21-
- If the type being copied contains uninitialized bytes (e.g., padding), it is incompatible with `-Zmiri-check-number-validity`. This will probably not be resolved until something like `AtomicMaybeUninit` is supported.
21+
- If the type being copied contains uninitialized bytes (e.g., padding), it is incompatible with `-Zmiri-check-number-validity`. This will probably not be resolved until something like `AtomicMaybeUninit` is supported. **Note**: Due to [Miri cannot track uninitialized bytes on a per byte basis for partially initialized scalars][rust-lang/rust#69488], Miri may report this case as an access to an uninitialized byte, regardless of whether the uninitialized byte is actually accessed or not.
2222

2323
[p1478r1]: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1478r1.html
2424
[implementation]: https://github.com/taiki-e/atomic-memcpy/blob/279d7041e48fae0943a50102ebab97e7ed3977ae/src/lib.rs#L359-L403
2525
[asm-test]: tests/asm-test/asm
26+
[rust-lang/rust#69488]: https://github.com/rust-lang/rust/issues/69488
2627

2728
## License
2829

src/lib.rs

Lines changed: 12 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -213,13 +213,17 @@ mod imp {
213213
sync::atomic::{AtomicU16, AtomicUsize, Ordering},
214214
};
215215

216+
#[cfg(not(atomic_memcpy_symbolic_alignment_check_compat))]
216217
#[cfg(target_pointer_width = "32")]
217218
type Half = u16;
219+
#[cfg(not(atomic_memcpy_symbolic_alignment_check_compat))]
218220
#[cfg(target_pointer_width = "32")]
219221
type AtomicHalf = AtomicU16;
220222

223+
#[cfg(not(atomic_memcpy_symbolic_alignment_check_compat))]
221224
#[cfg(target_pointer_width = "64")]
222225
type Half = u32;
226+
#[cfg(not(atomic_memcpy_symbolic_alignment_check_compat))]
223227
#[cfg(target_pointer_width = "64")]
224228
type AtomicHalf = AtomicU32;
225229

@@ -327,9 +331,10 @@ mod imp {
327331
}
328332
}
329333

334+
#[cfg(not(atomic_memcpy_symbolic_alignment_check_compat))]
335+
#[cfg(not(target_pointer_width = "16"))]
330336
#[cfg_attr(feature = "inline-always", inline(always))]
331337
#[cfg_attr(not(feature = "inline-always"), inline)]
332-
#[cfg(not(target_pointer_width = "16"))]
333338
pub(super) unsafe fn atomic_load_half(&mut self) {
334339
use super::{AtomicHalf, Half};
335340
debug_assert!(self.remaining() >= mem::size_of::<Half>());
@@ -423,45 +428,15 @@ mod imp {
423428
return result;
424429
}
425430

426-
// HACK: Miri cannot track uninitialized bytes on a per byte basis for
427-
// partially initialized scalars: https://github.com/rust-lang/rust/issues/69488
428-
//
429-
// This hack allows miri to properly track the use of uninitialized
430-
// bytes. See also tests/uninit.rs that is a test to check if
431-
// valgrind/sanitizer/miri can properly detect the use of uninitialized
432-
// bytes.
433-
//
434-
// Note: With or without this hack, atomic load/store of integers
435-
// containing uninitialized bytes is technically an undefined behavior.
436-
// The only purpose of this hack is to make sure that Miri errors for
437-
// atomic load/store of integers containing uninitialized bytes
438-
// (which is probably not a problem and uncharted territory at best [1] [2] [3],
439-
// and can be detected by `-Zmiri-check-number-validity` [4]),
440-
// do not mask Miri errors for the use of uninitialized bytes (which is definitely a problem).
441-
// See also tests/padding.rs.
442-
//
443-
// [1] https://github.com/crossbeam-rs/crossbeam/issues/315
444-
// [2] https://github.com/rust-lang/unsafe-code-guidelines/issues/158
445-
// [3] https://github.com/rust-lang/unsafe-code-guidelines/issues/71
446-
// [4] https://github.com/rust-lang/miri/pull/1904
447-
//
448-
// rust-lang/rust#69488 affects only CTFE(compile-time function evaluation)/Miri
449-
// and atomic operations cannot be called in const context, so our code
450-
// is only affected in the case of cfg(miri).
451-
if cfg!(miri) {
452-
let mut state = load::LoadState::new(result.as_mut_ptr(), src);
453-
state.atomic_load_u8(state.remaining());
454-
debug_assert_eq!(state.remaining(), 0);
455-
return result;
456-
}
457-
458431
// Branch 1: If the alignment of `T` is less than usize, but `T` can be read as
459432
// at least one or more usize, compute the align offset and read it
460433
// like `(&[AtomicU8], &[AtomicUsize], &[AtomicU8])`.
461434
if mem::align_of::<T>() < mem::align_of::<AtomicUsize>()
462435
&& mem::size_of::<T>() >= mem::size_of::<usize>() * 4
463436
{
464437
let mut state = load::LoadState::new(result.as_mut_ptr(), src);
438+
// -Zmiri-symbolic-alignment-check is incompatible with the code that does manual integer arithmetic to ensure alignment.
439+
#[cfg(not(atomic_memcpy_symbolic_alignment_check_compat))]
465440
#[cfg(not(target_pointer_width = "16"))]
466441
{
467442
// Since the caller guarantees that the pointer is properly aligned,
@@ -702,9 +677,10 @@ mod imp {
702677
}
703678
}
704679

680+
#[cfg(not(atomic_memcpy_symbolic_alignment_check_compat))]
681+
#[cfg(not(target_pointer_width = "16"))]
705682
#[cfg_attr(feature = "inline-always", inline(always))]
706683
#[cfg_attr(not(feature = "inline-always"), inline)]
707-
#[cfg(not(target_pointer_width = "16"))]
708684
pub(super) unsafe fn atomic_store_half(&mut self) {
709685
use super::{AtomicHalf, Half};
710686
debug_assert!(self.remaining() >= mem::size_of::<Half>());
@@ -769,25 +745,17 @@ mod imp {
769745
return;
770746
}
771747

772-
// HACK: See the `atomic_load` function for the detailed comment.
773-
if cfg!(miri) {
774-
let mut state = store::StoreState::new(dst, &*val);
775-
state.atomic_store_u8(state.remaining());
776-
debug_assert_eq!(state.remaining(), 0);
777-
mem::forget(guard);
778-
return;
779-
}
780-
781748
// Branch 1: If the alignment of `T` is less than usize, but `T` can be write as
782749
// at least one or more usize, compute the align offset and write it
783750
// like `(&[AtomicU8], &[AtomicUsize], &[AtomicU8])`.
784751
if mem::align_of::<T>() < mem::align_of::<AtomicUsize>()
785752
&& mem::size_of::<T>() >= mem::size_of::<usize>() * 4
786753
{
787754
let mut state = store::StoreState::new(dst, &*val);
755+
// See the `atomic_load` function for the detailed comment.
756+
#[cfg(not(atomic_memcpy_symbolic_alignment_check_compat))]
788757
#[cfg(not(target_pointer_width = "16"))]
789758
{
790-
// See the `atomic_load` function for the detailed comment.
791759
if mem::align_of::<T>() >= mem::align_of::<Half>() {
792760
if dst as usize % mem::size_of::<usize>() == 0 {
793761
// SAFETY:

tests/padding.rs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
1-
// https://github.com/rust-lang/unsafe-code-guidelines/issues/71
2-
// https://github.com/rust-lang/miri/pull/1904
3-
//
4-
// With miri:
5-
// MIRIFLAGS='-Zmiri-check-number-validity' cargo miri test --test padding -- --test-threads=1
6-
71
use std::{cell::UnsafeCell, mem, sync::atomic::Ordering};
82

93
use atomic_memcpy::{atomic_load, atomic_store};
104

115
#[test]
126
fn enum_padding() {
7+
// Miri cannot track uninitialized bytes on a per byte basis for partially
8+
// initialized scalars: https://github.com/rust-lang/rust/issues/69488
9+
// See also https://github.com/crossbeam-rs/crossbeam/issues/748#issuecomment-1022432401
10+
if cfg!(miri) {
11+
return;
12+
}
13+
1314
#[allow(dead_code)]
1415
#[repr(align(8))]
1516
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -37,6 +38,13 @@ fn enum_padding() {
3738

3839
#[test]
3940
fn union_padding() {
41+
// Miri cannot track uninitialized bytes on a per byte basis for partially
42+
// initialized scalars: https://github.com/rust-lang/rust/issues/69488
43+
// See also https://github.com/crossbeam-rs/crossbeam/issues/748#issuecomment-1022432401
44+
if cfg!(miri) {
45+
return;
46+
}
47+
4048
#[allow(dead_code)]
4149
#[repr(C, align(8))]
4250
#[derive(Clone, Copy)]

tests/test.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,17 @@ fn basic_unit() {
6060
}
6161
}
6262

63+
#[test]
64+
fn basic_ptr() {
65+
unsafe {
66+
let x = UnsafeCell::<*mut u8>::new(ptr::null_mut());
67+
assert!(atomic_load(x.get(), Ordering::Relaxed).assume_init().is_null());
68+
let mut v = 0u8;
69+
atomic_store(x.get(), &mut v, Ordering::Relaxed);
70+
assert!(!atomic_load(x.get(), Ordering::Relaxed).assume_init().is_null());
71+
}
72+
}
73+
6374
#[cfg(not(feature = "no-panic"))]
6475
#[track_caller]
6576
fn assert_panic<T: std::fmt::Debug>(f: impl FnOnce() -> T) -> std::string::String {

0 commit comments

Comments
 (0)