Skip to content

Commit 5531927

Browse files
committed
Auto merge of #91616 - saethlin:sort_unchecked-sb-fix, r=Mark-Simulacrum
Fix #91306 by deriving all access from a single *mut T Fixes #91306. The previous code is invalid because the first argument to `copy_nonoverlapping` is invalidated by the mutable borrow taken out to construct the second argument. I believe this patch fixes that, and this code should now pass Miri with `-Ztag-raw-pointers`, ~~but I'm currently stuck trying to run my reproducer with a this patched version of the standard library (alternatively, running Miri on the standard library tests itself would suffice).~~ Ralf walked me through this on Zulip. I've also added fixes for 7 more problems other than those I reported. Most of them are easy to hit by calling sort_unstable on random arrays. I don't have reproducers for every change, but they seem pretty clear-cut to me. But I did only start learning stacked borrows 2 days ago so that might be a large dash of Dunning-Kruger.
2 parents 1d01550 + 3a0fa03 commit 5531927

File tree

1 file changed

+20
-17
lines changed

1 file changed

+20
-17
lines changed

Diff for: library/core/src/slice/sort.rs

+20-17
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ where
3333
F: FnMut(&T, &T) -> bool,
3434
{
3535
let len = v.len();
36-
// SAFETY: The unsafe operations below involves indexing without a bound check (`get_unchecked` and `get_unchecked_mut`)
37-
// and copying memory (`ptr::copy_nonoverlapping`).
36+
// SAFETY: The unsafe operations below involves indexing without a bounds check (by offsetting a
37+
// pointer) and copying memory (`ptr::copy_nonoverlapping`).
3838
//
3939
// a. Indexing:
4040
// 1. We checked the size of the array to >=2.
@@ -55,17 +55,18 @@ where
5555
// operation panics, `hole` will get dropped and automatically write the element back
5656
// into the slice.
5757
let mut tmp = mem::ManuallyDrop::new(ptr::read(v.get_unchecked(0)));
58-
let mut hole = CopyOnDrop { src: &mut *tmp, dest: v.get_unchecked_mut(1) };
59-
ptr::copy_nonoverlapping(v.get_unchecked(1), v.get_unchecked_mut(0), 1);
58+
let v = v.as_mut_ptr();
59+
let mut hole = CopyOnDrop { src: &mut *tmp, dest: v.add(1) };
60+
ptr::copy_nonoverlapping(v.add(1), v.add(0), 1);
6061

6162
for i in 2..len {
62-
if !is_less(v.get_unchecked(i), &*tmp) {
63+
if !is_less(&*v.add(i), &*tmp) {
6364
break;
6465
}
6566

6667
// Move `i`-th element one place to the left, thus shifting the hole to the right.
67-
ptr::copy_nonoverlapping(v.get_unchecked(i), v.get_unchecked_mut(i - 1), 1);
68-
hole.dest = v.get_unchecked_mut(i);
68+
ptr::copy_nonoverlapping(v.add(i), v.add(i - 1), 1);
69+
hole.dest = v.add(i);
6970
}
7071
// `hole` gets dropped and thus copies `tmp` into the remaining hole in `v`.
7172
}
@@ -78,8 +79,8 @@ where
7879
F: FnMut(&T, &T) -> bool,
7980
{
8081
let len = v.len();
81-
// SAFETY: The unsafe operations below involves indexing without a bound check (`get_unchecked` and `get_unchecked_mut`)
82-
// and copying memory (`ptr::copy_nonoverlapping`).
82+
// SAFETY: The unsafe operations below involves indexing without a bound check (by offsetting a
83+
// pointer) and copying memory (`ptr::copy_nonoverlapping`).
8384
//
8485
// a. Indexing:
8586
// 1. We checked the size of the array to >= 2.
@@ -100,17 +101,18 @@ where
100101
// operation panics, `hole` will get dropped and automatically write the element back
101102
// into the slice.
102103
let mut tmp = mem::ManuallyDrop::new(ptr::read(v.get_unchecked(len - 1)));
103-
let mut hole = CopyOnDrop { src: &mut *tmp, dest: v.get_unchecked_mut(len - 2) };
104-
ptr::copy_nonoverlapping(v.get_unchecked(len - 2), v.get_unchecked_mut(len - 1), 1);
104+
let v = v.as_mut_ptr();
105+
let mut hole = CopyOnDrop { src: &mut *tmp, dest: v.add(len - 2) };
106+
ptr::copy_nonoverlapping(v.add(len - 2), v.add(len - 1), 1);
105107

106108
for i in (0..len - 2).rev() {
107-
if !is_less(&*tmp, v.get_unchecked(i)) {
109+
if !is_less(&*tmp, &*v.add(i)) {
108110
break;
109111
}
110112

111113
// Move `i`-th element one place to the right, thus shifting the hole to the left.
112-
ptr::copy_nonoverlapping(v.get_unchecked(i), v.get_unchecked_mut(i + 1), 1);
113-
hole.dest = v.get_unchecked_mut(i);
114+
ptr::copy_nonoverlapping(v.add(i), v.add(i + 1), 1);
115+
hole.dest = v.add(i);
114116
}
115117
// `hole` gets dropped and thus copies `tmp` into the remaining hole in `v`.
116118
}
@@ -302,7 +304,7 @@ where
302304
if start_l == end_l {
303305
// Trace `block_l` elements from the left side.
304306
start_l = MaybeUninit::slice_as_mut_ptr(&mut offsets_l);
305-
end_l = MaybeUninit::slice_as_mut_ptr(&mut offsets_l);
307+
end_l = start_l;
306308
let mut elem = l;
307309

308310
for i in 0..block_l {
@@ -328,7 +330,7 @@ where
328330
if start_r == end_r {
329331
// Trace `block_r` elements from the right side.
330332
start_r = MaybeUninit::slice_as_mut_ptr(&mut offsets_r);
331-
end_r = MaybeUninit::slice_as_mut_ptr(&mut offsets_r);
333+
end_r = start_r;
332334
let mut elem = r;
333335

334336
for i in 0..block_r {
@@ -579,7 +581,8 @@ where
579581

580582
// Swap the found pair of out-of-order elements.
581583
r -= 1;
582-
ptr::swap(v.get_unchecked_mut(l), v.get_unchecked_mut(r));
584+
let ptr = v.as_mut_ptr();
585+
ptr::swap(ptr.add(l), ptr.add(r));
583586
l += 1;
584587
}
585588
}

0 commit comments

Comments
 (0)