Skip to content

Commit 866c64e

Browse files
committed
Fix possible soundness issue in ensure_subset_of
1 parent 05af66a commit 866c64e

File tree

8 files changed

+112
-110
lines changed

8 files changed

+112
-110
lines changed

library/alloc/src/collections/vec_deque/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1063,7 +1063,7 @@ impl<T> VecDeque<T> {
10631063
where
10641064
R: RangeBounds<usize>,
10651065
{
1066-
let Range { start, end } = range.ensure_subset_of(..self.len());
1066+
let Range { start, end } = Range::ensure_subset_of(range, ..self.len());
10671067
let tail = self.wrap_add(self.tail, start);
10681068
let head = self.wrap_add(self.tail, end);
10691069
(tail, head)

library/alloc/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@
112112
#![feature(or_patterns)]
113113
#![feature(pattern)]
114114
#![feature(ptr_internals)]
115-
#![feature(range_bounds_ensure_subset_of)]
115+
#![feature(range_ensure_subset_of)]
116116
#![feature(rustc_attrs)]
117117
#![feature(receiver_trait)]
118118
#![cfg_attr(bootstrap, feature(min_const_generics))]

library/alloc/src/string.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1510,7 +1510,7 @@ impl String {
15101510
// of the vector version. The data is just plain bytes.
15111511
// Because the range removal happens in Drop, if the Drain iterator is leaked,
15121512
// the removal will not happen.
1513-
let Range { start, end } = range.ensure_subset_of(..self.len());
1513+
let Range { start, end } = Range::ensure_subset_of(range, ..self.len());
15141514
assert!(self.is_char_boundary(start));
15151515
assert!(self.is_char_boundary(end));
15161516

library/alloc/src/vec/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1597,7 +1597,7 @@ impl<T, A: Allocator> Vec<T, A> {
15971597
// the hole, and the vector length is restored to the new length.
15981598
//
15991599
let len = self.len();
1600-
let Range { start, end } = range.ensure_subset_of(..len);
1600+
let Range { start, end } = Range::ensure_subset_of(range, ..len);
16011601

16021602
unsafe {
16031603
// set self.vec length's to start, to be safe in case Drain is leaked

library/core/src/ops/range.rs

Lines changed: 97 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,103 @@ impl<Idx: PartialOrd<Idx>> Range<Idx> {
151151
}
152152
}
153153

154+
impl Range<usize> {
155+
/// Performs bounds-checking of a range.
156+
///
157+
/// This method is similar to [`Index::index`] for slices, but it returns a
158+
/// `Range` equivalent to `range`. You can use this method to turn any range
159+
/// into `start` and `end` values.
160+
///
161+
/// `bounds` is the range of the slice to use for bounds-checking. It should
162+
/// be a [`RangeTo`] range that ends at the length of the slice.
163+
///
164+
/// The returned `Range` is safe to pass to [`slice::get_unchecked`] and
165+
/// [`slice::get_unchecked_mut`] for slices with the given range.
166+
///
167+
/// [`slice::get_unchecked`]: ../../std/primitive.slice.html#method.get_unchecked
168+
/// [`slice::get_unchecked_mut`]: ../../std/primitive.slice.html#method.get_unchecked_mut
169+
///
170+
/// # Panics
171+
///
172+
/// Panics if `range` would be out of bounds.
173+
///
174+
/// # Examples
175+
///
176+
/// ```
177+
/// #![feature(range_ensure_subset_of)]
178+
///
179+
/// use std::ops::Range;
180+
///
181+
/// let v = [10, 40, 30];
182+
/// assert_eq!(1..2, Range::ensure_subset_of(1..2, ..v.len()));
183+
/// assert_eq!(0..2, Range::ensure_subset_of(..2, ..v.len()));
184+
/// assert_eq!(1..3, Range::ensure_subset_of(1.., ..v.len()));
185+
/// ```
186+
///
187+
/// Panics when [`Index::index`] would panic:
188+
///
189+
/// ```should_panic
190+
/// #![feature(range_ensure_subset_of)]
191+
///
192+
/// use std::ops::Range;
193+
///
194+
/// Range::ensure_subset_of(2..1, ..3);
195+
/// ```
196+
///
197+
/// ```should_panic
198+
/// #![feature(range_ensure_subset_of)]
199+
///
200+
/// use std::ops::Range;
201+
///
202+
/// Range::ensure_subset_of(1..4, ..3);
203+
/// ```
204+
///
205+
/// ```should_panic
206+
/// #![feature(range_ensure_subset_of)]
207+
///
208+
/// use std::ops::Range;
209+
///
210+
/// Range::ensure_subset_of(1..=usize::MAX, ..3);
211+
/// ```
212+
///
213+
/// [`Index::index`]: crate::ops::Index::index
214+
#[track_caller]
215+
#[unstable(feature = "range_ensure_subset_of", issue = "76393")]
216+
pub fn ensure_subset_of<R>(range: R, bounds: RangeTo<usize>) -> Self
217+
where
218+
R: RangeBounds<usize>,
219+
{
220+
let len = bounds.end;
221+
222+
let start: Bound<&usize> = range.start_bound();
223+
let start = match start {
224+
Bound::Included(&start) => start,
225+
Bound::Excluded(start) => {
226+
start.checked_add(1).unwrap_or_else(|| slice_start_index_overflow_fail())
227+
}
228+
Bound::Unbounded => 0,
229+
};
230+
231+
let end: Bound<&usize> = range.end_bound();
232+
let end = match end {
233+
Bound::Included(end) => {
234+
end.checked_add(1).unwrap_or_else(|| slice_end_index_overflow_fail())
235+
}
236+
Bound::Excluded(&end) => end,
237+
Bound::Unbounded => len,
238+
};
239+
240+
if start > end {
241+
slice_index_order_fail(start, end);
242+
}
243+
if end > len {
244+
slice_end_index_len_fail(end, len);
245+
}
246+
247+
Self { start, end }
248+
}
249+
}
250+
154251
/// A range only bounded inclusively below (`start..`).
155252
///
156253
/// The `RangeFrom` `start..` contains all values with `x >= start`.
@@ -764,101 +861,6 @@ pub trait RangeBounds<T: ?Sized> {
764861
#[stable(feature = "collections_range", since = "1.28.0")]
765862
fn end_bound(&self) -> Bound<&T>;
766863

767-
/// Performs bounds-checking of this range.
768-
///
769-
/// This method is similar to [`Index::index`] for slices, but it returns a
770-
/// [`Range`] equivalent to this range. You can use this method to turn any
771-
/// range into `start` and `end` values.
772-
///
773-
/// The given range is the range of the slice to use for bounds-checking. It
774-
/// should be a [`RangeTo`] range that ends at the length of the slice.
775-
///
776-
/// The returned [`Range`] is safe to pass to [`slice::get_unchecked`] and
777-
/// [`slice::get_unchecked_mut`] for slices with the given range.
778-
///
779-
/// [`slice::get_unchecked`]: ../../std/primitive.slice.html#method.get_unchecked
780-
/// [`slice::get_unchecked_mut`]: ../../std/primitive.slice.html#method.get_unchecked_mut
781-
///
782-
/// # Panics
783-
///
784-
/// Panics if the range would be out of bounds.
785-
///
786-
/// # Examples
787-
///
788-
/// ```
789-
/// #![feature(range_bounds_ensure_subset_of)]
790-
///
791-
/// use std::ops::RangeBounds;
792-
///
793-
/// let v = [10, 40, 30];
794-
/// assert_eq!(1..2, (1..2).ensure_subset_of(..v.len()));
795-
/// assert_eq!(0..2, (..2).ensure_subset_of(..v.len()));
796-
/// assert_eq!(1..3, (1..).ensure_subset_of(..v.len()));
797-
/// ```
798-
///
799-
/// Panics when [`Index::index`] would panic:
800-
///
801-
/// ```should_panic
802-
/// #![feature(range_bounds_ensure_subset_of)]
803-
///
804-
/// use std::ops::RangeBounds;
805-
///
806-
/// (2..1).ensure_subset_of(..3);
807-
/// ```
808-
///
809-
/// ```should_panic
810-
/// #![feature(range_bounds_ensure_subset_of)]
811-
///
812-
/// use std::ops::RangeBounds;
813-
///
814-
/// (1..4).ensure_subset_of(..3);
815-
/// ```
816-
///
817-
/// ```should_panic
818-
/// #![feature(range_bounds_ensure_subset_of)]
819-
///
820-
/// use std::ops::RangeBounds;
821-
///
822-
/// (1..=usize::MAX).ensure_subset_of(..3);
823-
/// ```
824-
///
825-
/// [`Index::index`]: crate::ops::Index::index
826-
#[track_caller]
827-
#[unstable(feature = "range_bounds_ensure_subset_of", issue = "76393")]
828-
fn ensure_subset_of(self, range: RangeTo<usize>) -> Range<usize>
829-
where
830-
Self: RangeBounds<usize>,
831-
{
832-
let len = range.end;
833-
834-
let start: Bound<&usize> = self.start_bound();
835-
let start = match start {
836-
Bound::Included(&start) => start,
837-
Bound::Excluded(start) => {
838-
start.checked_add(1).unwrap_or_else(|| slice_start_index_overflow_fail())
839-
}
840-
Bound::Unbounded => 0,
841-
};
842-
843-
let end: Bound<&usize> = self.end_bound();
844-
let end = match end {
845-
Bound::Included(end) => {
846-
end.checked_add(1).unwrap_or_else(|| slice_end_index_overflow_fail())
847-
}
848-
Bound::Excluded(&end) => end,
849-
Bound::Unbounded => len,
850-
};
851-
852-
if start > end {
853-
slice_index_order_fail(start, end);
854-
}
855-
if end > len {
856-
slice_end_index_len_fail(end, len);
857-
}
858-
859-
Range { start, end }
860-
}
861-
862864
/// Returns `true` if `item` is contained in the range.
863865
///
864866
/// # Examples

library/core/src/slice/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3052,7 +3052,7 @@ impl<T> [T] {
30523052
where
30533053
T: Copy,
30543054
{
3055-
let Range { start: src_start, end: src_end } = src.ensure_subset_of(..self.len());
3055+
let Range { start: src_start, end: src_end } = Range::ensure_subset_of(src, ..self.len());
30563056
let count = src_end - src_start;
30573057
assert!(dest <= self.len() - count, "dest is out of bounds");
30583058
// SAFETY: the conditions for `ptr::copy` have all been checked above,

src/doc/unstable-book/src/library-features/range-bounds-ensure-subset-of.md

Lines changed: 0 additions & 10 deletions
This file was deleted.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# `range_ensure_subset_of`
2+
3+
The tracking issue for this feature is: [#76393]
4+
5+
------------------------
6+
7+
This adds [`Range::ensure_subset_of`].
8+
9+
[#76393]: https://github.com/rust-lang/rust/issues/76393
10+
[`Range::ensure_subset_of`]: https://doc.rust-lang.org/std/ops/struct.Range.html#method.ensure_subset_of

0 commit comments

Comments
 (0)