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

Fix oob get_unchecked, overlapping _nonoverlapping #21

Merged
merged 1 commit into from
Jun 19, 2022

Conversation

saethlin
Copy link
Contributor

Nearly all of the fixes here are actually found by the standard library's debug assertions, if you build with -Zbuild-std you can turn them on. But for code like this, I think Miri is the more appropriate tool for ensuring that a validity regression doesn't occur, especially because the test cases here are small enough and provide good coverage to deal with the slowdown of a checking interpreter.

In quite a few places this code wants a pointer into a slice, and it
gets that pointer by calling get_unchecked. This is potentially valid,
but in general extremely dangerous because get_unchecked is a slice
method even when called on a Vec, so regardless of whether the pointer
would point to valid memory, if the index is out of bounds of the slice,
that's UB. Offsetting pointers provides the less surprising semantics.
ptr::copy_nonoverlapping(self.dropped.as_ptr(), &mut self.slice[self.write], self.dropped.len());
ptr::copy_nonoverlapping(self.dropped.as_ptr(), self.slice.as_mut_ptr().add(self.write), self.dropped.len());
Copy link
Contributor Author

Choose a reason for hiding this comment

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

In this case, the pointer created here is a pointer to a single element, but this call actually needs a pointer which has provenance over a range. This could be created with something like &mut self.slice[self.write..][..self.dropped.len()] as *mut T but just offsetting a pointer to the whole slice is simpler.

ptr::copy_nonoverlapping(value, vec.get_unchecked_mut(old_len), 1);
ptr::copy_nonoverlapping(value, vec.as_mut_ptr().add(old_len), 1);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This get_unchecked_mut is always out-of-bounds by definition. If you can tolerate a recent Rust version, this could also be implemented with &mut vec.spare_capacity_mut()[0], though I think pointer offsetting alone is totally fine here.

Comment on lines -226 to +227
ptr::copy_nonoverlapping(slice.get_unchecked(source), slice.get_unchecked_mut(dest), 1);
let ptr = slice.as_mut_ptr();
ptr::copy_nonoverlapping(ptr.add(source), ptr.add(dest), 1);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This change is only required with -Zmiri-tag-raw-pointers. But also, this could be implemented with slice::swap_unchecked if that were stabilized.

Comment on lines -320 to +324
s.slice.get_unchecked(s.write),
s.dropped.get_unchecked_mut(old_len),
s.slice.as_ptr().add(s.write),
s.dropped.as_mut_ptr().add(old_len),
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Both of these indexes are out-of-bounds for the slice, but inbounds for the backing allocation.

Comment on lines +270 to +272
if read != s.write {
unsafe_copy(s.slice, read, s.write);
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Some of the calls here to unsafe_copy pass 0 for both the read and write index, making this an overlapping copy.

Copy link
Owner

@emilk emilk left a comment

Choose a reason for hiding this comment

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

This is awesome, thanks!

@emilk emilk merged commit 00e508b into emilk:master Jun 19, 2022
@emilk
Copy link
Owner

emilk commented Jun 19, 2022

dmsort 1.0.2 has been published with these fixes!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants