Fix possible soundness issue in `ensure_subset_of`

This commit is contained in:
dylni 2021-01-18 09:22:17 -05:00
parent 9d29793614
commit cb647f3e8e
8 changed files with 112 additions and 110 deletions

View File

@ -1063,7 +1063,7 @@ impl<T> VecDeque<T> {
where
R: RangeBounds<usize>,
{
let Range { start, end } = range.ensure_subset_of(..self.len());
let Range { start, end } = Range::ensure_subset_of(range, ..self.len());
let tail = self.wrap_add(self.tail, start);
let head = self.wrap_add(self.tail, end);
(tail, head)

View File

@ -115,7 +115,7 @@
#![feature(or_patterns)]
#![feature(pattern)]
#![feature(ptr_internals)]
#![feature(range_bounds_ensure_subset_of)]
#![feature(range_ensure_subset_of)]
#![feature(rustc_attrs)]
#![feature(receiver_trait)]
#![cfg_attr(bootstrap, feature(min_const_generics))]

View File

@ -1510,7 +1510,7 @@ impl String {
// of the vector version. The data is just plain bytes.
// Because the range removal happens in Drop, if the Drain iterator is leaked,
// the removal will not happen.
let Range { start, end } = range.ensure_subset_of(..self.len());
let Range { start, end } = Range::ensure_subset_of(range, ..self.len());
assert!(self.is_char_boundary(start));
assert!(self.is_char_boundary(end));

View File

@ -1650,7 +1650,7 @@ impl<T, A: Allocator> Vec<T, A> {
// the hole, and the vector length is restored to the new length.
//
let len = self.len();
let Range { start, end } = range.ensure_subset_of(..len);
let Range { start, end } = Range::ensure_subset_of(range, ..len);
unsafe {
// set self.vec length's to start, to be safe in case Drain is leaked

View File

@ -151,6 +151,103 @@ impl<Idx: PartialOrd<Idx>> Range<Idx> {
}
}
impl Range<usize> {
/// Performs bounds-checking of a range.
///
/// This method is similar to [`Index::index`] for slices, but it returns a
/// `Range` equivalent to `range`. You can use this method to turn any range
/// into `start` and `end` values.
///
/// `bounds` is the range of the slice to use for bounds-checking. It should
/// be a [`RangeTo`] range that ends at the length of the slice.
///
/// The returned `Range` is safe to pass to [`slice::get_unchecked`] and
/// [`slice::get_unchecked_mut`] for slices with the given range.
///
/// [`slice::get_unchecked`]: ../../std/primitive.slice.html#method.get_unchecked
/// [`slice::get_unchecked_mut`]: ../../std/primitive.slice.html#method.get_unchecked_mut
///
/// # Panics
///
/// Panics if `range` would be out of bounds.
///
/// # Examples
///
/// ```
/// #![feature(range_ensure_subset_of)]
///
/// use std::ops::Range;
///
/// let v = [10, 40, 30];
/// assert_eq!(1..2, Range::ensure_subset_of(1..2, ..v.len()));
/// assert_eq!(0..2, Range::ensure_subset_of(..2, ..v.len()));
/// assert_eq!(1..3, Range::ensure_subset_of(1.., ..v.len()));
/// ```
///
/// Panics when [`Index::index`] would panic:
///
/// ```should_panic
/// #![feature(range_ensure_subset_of)]
///
/// use std::ops::Range;
///
/// Range::ensure_subset_of(2..1, ..3);
/// ```
///
/// ```should_panic
/// #![feature(range_ensure_subset_of)]
///
/// use std::ops::Range;
///
/// Range::ensure_subset_of(1..4, ..3);
/// ```
///
/// ```should_panic
/// #![feature(range_ensure_subset_of)]
///
/// use std::ops::Range;
///
/// Range::ensure_subset_of(1..=usize::MAX, ..3);
/// ```
///
/// [`Index::index`]: crate::ops::Index::index
#[track_caller]
#[unstable(feature = "range_ensure_subset_of", issue = "76393")]
pub fn ensure_subset_of<R>(range: R, bounds: RangeTo<usize>) -> Self
where
R: RangeBounds<usize>,
{
let len = bounds.end;
let start: Bound<&usize> = range.start_bound();
let start = match start {
Bound::Included(&start) => start,
Bound::Excluded(start) => {
start.checked_add(1).unwrap_or_else(|| slice_start_index_overflow_fail())
}
Bound::Unbounded => 0,
};
let end: Bound<&usize> = range.end_bound();
let end = match end {
Bound::Included(end) => {
end.checked_add(1).unwrap_or_else(|| slice_end_index_overflow_fail())
}
Bound::Excluded(&end) => end,
Bound::Unbounded => len,
};
if start > end {
slice_index_order_fail(start, end);
}
if end > len {
slice_end_index_len_fail(end, len);
}
Self { start, end }
}
}
/// A range only bounded inclusively below (`start..`).
///
/// The `RangeFrom` `start..` contains all values with `x >= start`.
@ -764,101 +861,6 @@ pub trait RangeBounds<T: ?Sized> {
#[stable(feature = "collections_range", since = "1.28.0")]
fn end_bound(&self) -> Bound<&T>;
/// Performs bounds-checking of this range.
///
/// This method is similar to [`Index::index`] for slices, but it returns a
/// [`Range`] equivalent to this range. You can use this method to turn any
/// range into `start` and `end` values.
///
/// The given range is the range of the slice to use for bounds-checking. It
/// should be a [`RangeTo`] range that ends at the length of the slice.
///
/// The returned [`Range`] is safe to pass to [`slice::get_unchecked`] and
/// [`slice::get_unchecked_mut`] for slices with the given range.
///
/// [`slice::get_unchecked`]: ../../std/primitive.slice.html#method.get_unchecked
/// [`slice::get_unchecked_mut`]: ../../std/primitive.slice.html#method.get_unchecked_mut
///
/// # Panics
///
/// Panics if the range would be out of bounds.
///
/// # Examples
///
/// ```
/// #![feature(range_bounds_ensure_subset_of)]
///
/// use std::ops::RangeBounds;
///
/// let v = [10, 40, 30];
/// assert_eq!(1..2, (1..2).ensure_subset_of(..v.len()));
/// assert_eq!(0..2, (..2).ensure_subset_of(..v.len()));
/// assert_eq!(1..3, (1..).ensure_subset_of(..v.len()));
/// ```
///
/// Panics when [`Index::index`] would panic:
///
/// ```should_panic
/// #![feature(range_bounds_ensure_subset_of)]
///
/// use std::ops::RangeBounds;
///
/// (2..1).ensure_subset_of(..3);
/// ```
///
/// ```should_panic
/// #![feature(range_bounds_ensure_subset_of)]
///
/// use std::ops::RangeBounds;
///
/// (1..4).ensure_subset_of(..3);
/// ```
///
/// ```should_panic
/// #![feature(range_bounds_ensure_subset_of)]
///
/// use std::ops::RangeBounds;
///
/// (1..=usize::MAX).ensure_subset_of(..3);
/// ```
///
/// [`Index::index`]: crate::ops::Index::index
#[track_caller]
#[unstable(feature = "range_bounds_ensure_subset_of", issue = "76393")]
fn ensure_subset_of(self, range: RangeTo<usize>) -> Range<usize>
where
Self: RangeBounds<usize>,
{
let len = range.end;
let start: Bound<&usize> = self.start_bound();
let start = match start {
Bound::Included(&start) => start,
Bound::Excluded(start) => {
start.checked_add(1).unwrap_or_else(|| slice_start_index_overflow_fail())
}
Bound::Unbounded => 0,
};
let end: Bound<&usize> = self.end_bound();
let end = match end {
Bound::Included(end) => {
end.checked_add(1).unwrap_or_else(|| slice_end_index_overflow_fail())
}
Bound::Excluded(&end) => end,
Bound::Unbounded => len,
};
if start > end {
slice_index_order_fail(start, end);
}
if end > len {
slice_end_index_len_fail(end, len);
}
Range { start, end }
}
/// Returns `true` if `item` is contained in the range.
///
/// # Examples

View File

@ -3052,7 +3052,7 @@ impl<T> [T] {
where
T: Copy,
{
let Range { start: src_start, end: src_end } = src.ensure_subset_of(..self.len());
let Range { start: src_start, end: src_end } = Range::ensure_subset_of(src, ..self.len());
let count = src_end - src_start;
assert!(dest <= self.len() - count, "dest is out of bounds");
// SAFETY: the conditions for `ptr::copy` have all been checked above,

View File

@ -1,10 +0,0 @@
# `range_bounds_ensure_subset_of`
The tracking issue for this feature is: [#76393]
------------------------
This adds [`RangeBounds::ensure_subset_of`].
[#76393]: https://github.com/rust-lang/rust/issues/76393
[`RangeBounds::ensure_subset_of`]: https://doc.rust-lang.org/nightly/std/ops/trait.RangeBounds.html#method.ensure_subset_of

View File

@ -0,0 +1,10 @@
# `range_ensure_subset_of`
The tracking issue for this feature is: [#76393]
------------------------
This adds [`Range::ensure_subset_of`].
[#76393]: https://github.com/rust-lang/rust/issues/76393
[`Range::ensure_subset_of`]: https://doc.rust-lang.org/std/ops/struct.Range.html#method.ensure_subset_of