about summary refs log tree commit diff
diff options
context:
space:
mode:
authorThe 8472 <git@infinite-source.de>2024-03-13 22:38:05 +0100
committerThe 8472 <git@infinite-source.de>2024-03-14 00:57:02 +0100
commitbe33586adc13444e7d08c4269344db2dce6c2a03 (patch)
treef02eb1d48b649749ef659bd697884fe8ff6ca260
parent3cbb93223f33024db464a4df27a13c7cce870173 (diff)
downloadrust-be33586adc13444e7d08c4269344db2dce6c2a03.tar.gz
rust-be33586adc13444e7d08c4269344db2dce6c2a03.zip
fix unsoundness in Step::forward_unchecked for signed integers
-rw-r--r--library/core/src/iter/range.rs30
-rw-r--r--library/core/tests/iter/range.rs5
2 files changed, 33 insertions, 2 deletions
diff --git a/library/core/src/iter/range.rs b/library/core/src/iter/range.rs
index 68937161e04..49135704f27 100644
--- a/library/core/src/iter/range.rs
+++ b/library/core/src/iter/range.rs
@@ -184,8 +184,25 @@ pub trait Step: Clone + PartialOrd + Sized {
     }
 }
 
-// These are still macro-generated because the integer literals resolve to different types.
-macro_rules! step_identical_methods {
+// Separate impls for signed ranges because the distance within a signed range can be larger
+// than the signed::MAX value. Therefore `as` casting to the signed type would be incorrect.
+macro_rules! step_signed_methods {
+    ($unsigned: ty) => {
+        #[inline]
+        unsafe fn forward_unchecked(start: Self, n: usize) -> Self {
+            // SAFETY: the caller has to guarantee that `start + n` doesn't overflow.
+            unsafe { start.checked_add_unsigned(n as $unsigned).unwrap_unchecked() }
+        }
+
+        #[inline]
+        unsafe fn backward_unchecked(start: Self, n: usize) -> Self {
+            // SAFETY: the caller has to guarantee that `start - n` doesn't overflow.
+            unsafe { start.checked_sub_unsigned(n as $unsigned).unwrap_unchecked() }
+        }
+    };
+}
+
+macro_rules! step_unsigned_methods {
     () => {
         #[inline]
         unsafe fn forward_unchecked(start: Self, n: usize) -> Self {
@@ -198,7 +215,12 @@ macro_rules! step_identical_methods {
             // SAFETY: the caller has to guarantee that `start - n` doesn't overflow.
             unsafe { start.unchecked_sub(n as Self) }
         }
+    };
+}
 
+// These are still macro-generated because the integer literals resolve to different types.
+macro_rules! step_identical_methods {
+    () => {
         #[inline]
         #[allow(arithmetic_overflow)]
         #[rustc_inherit_overflow_checks]
@@ -239,6 +261,7 @@ macro_rules! step_integer_impls {
             #[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")]
             impl Step for $u_narrower {
                 step_identical_methods!();
+                step_unsigned_methods!();
 
                 #[inline]
                 fn steps_between(start: &Self, end: &Self) -> Option<usize> {
@@ -271,6 +294,7 @@ macro_rules! step_integer_impls {
             #[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")]
             impl Step for $i_narrower {
                 step_identical_methods!();
+                step_signed_methods!($u_narrower);
 
                 #[inline]
                 fn steps_between(start: &Self, end: &Self) -> Option<usize> {
@@ -335,6 +359,7 @@ macro_rules! step_integer_impls {
             #[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")]
             impl Step for $u_wider {
                 step_identical_methods!();
+                step_unsigned_methods!();
 
                 #[inline]
                 fn steps_between(start: &Self, end: &Self) -> Option<usize> {
@@ -360,6 +385,7 @@ macro_rules! step_integer_impls {
             #[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")]
             impl Step for $i_wider {
                 step_identical_methods!();
+                step_signed_methods!($u_wider);
 
                 #[inline]
                 fn steps_between(start: &Self, end: &Self) -> Option<usize> {
diff --git a/library/core/tests/iter/range.rs b/library/core/tests/iter/range.rs
index 9af07119a89..e31db0732e0 100644
--- a/library/core/tests/iter/range.rs
+++ b/library/core/tests/iter/range.rs
@@ -325,6 +325,11 @@ fn test_range_advance_by() {
     assert_eq!(Ok(()), r.advance_back_by(usize::MAX));
 
     assert_eq!((r.start, r.end), (0u128 + usize::MAX as u128, u128::MAX - usize::MAX as u128));
+
+    // issue 122420, Step::forward_unchecked was unsound for signed integers
+    let mut r = -128i8..127;
+    assert_eq!(Ok(()), r.advance_by(200));
+    assert_eq!(r.next(), Some(72));
 }
 
 #[test]