about summary refs log tree commit diff
path: root/src/libcore/ops
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2020-02-08 03:46:56 +0000
committerbors <bors@rust-lang.org>2020-02-08 03:46:56 +0000
commit6cad7542da2f10e2110f942de4db59716bacb3df (patch)
treec3344ccc601a6bbdebd9b69ee1a92e4e81cfceac /src/libcore/ops
parent8498c5f5b02dbb4ed58a1eb4901b0b733342c35f (diff)
parenta81c59f9b84b6519785a4e0ae9234107d149f454 (diff)
downloadrust-6cad7542da2f10e2110f942de4db59716bacb3df.tar.gz
rust-6cad7542da2f10e2110f942de4db59716bacb3df.zip
Auto merge of #68358 - matthewjasper:spec-fix, r=nikomatsakis
Remove some unsound specializations

This removes the unsound and exploitable specializations in the standard library

* The `PartialEq` and `Hash` implementations for  `RangeInclusive` are changed to avoid specialization.
* The `PartialOrd` specialization for slices now specializes on a limited set of concrete types.
* Added some tests for the soundness problems.
Diffstat (limited to 'src/libcore/ops')
-rw-r--r--src/libcore/ops/range.rs38
1 files changed, 15 insertions, 23 deletions
diff --git a/src/libcore/ops/range.rs b/src/libcore/ops/range.rs
index d38b3516569..6c0bc6bbbad 100644
--- a/src/libcore/ops/range.rs
+++ b/src/libcore/ops/range.rs
@@ -343,38 +343,21 @@ pub struct RangeInclusive<Idx> {
     pub(crate) is_empty: Option<bool>,
     // This field is:
     //  - `None` when next() or next_back() was never called
-    //  - `Some(false)` when `start <= end` assuming no overflow
-    //  - `Some(true)` otherwise
+    //  - `Some(false)` when `start < end`
+    //  - `Some(true)` when `end < start`
+    //  - `Some(false)` when `start == end` and the range hasn't yet completed iteration
+    //  - `Some(true)` when `start == end` and the range has completed iteration
     // The field cannot be a simple `bool` because the `..=` constructor can
     // accept non-PartialOrd types, also we want the constructor to be const.
 }
 
-trait RangeInclusiveEquality: Sized {
-    fn canonicalized_is_empty(range: &RangeInclusive<Self>) -> bool;
-}
-
-impl<T> RangeInclusiveEquality for T {
-    #[inline]
-    default fn canonicalized_is_empty(range: &RangeInclusive<Self>) -> bool {
-        range.is_empty.unwrap_or_default()
-    }
-}
-
-impl<T: PartialOrd> RangeInclusiveEquality for T {
-    #[inline]
-    fn canonicalized_is_empty(range: &RangeInclusive<Self>) -> bool {
-        range.is_empty()
-    }
-}
-
 #[stable(feature = "inclusive_range", since = "1.26.0")]
 impl<Idx: PartialEq> PartialEq for RangeInclusive<Idx> {
     #[inline]
     fn eq(&self, other: &Self) -> bool {
         self.start == other.start
             && self.end == other.end
-            && RangeInclusiveEquality::canonicalized_is_empty(self)
-                == RangeInclusiveEquality::canonicalized_is_empty(other)
+            && self.is_exhausted() == other.is_exhausted()
     }
 }
 
@@ -386,7 +369,8 @@ impl<Idx: Hash> Hash for RangeInclusive<Idx> {
     fn hash<H: Hasher>(&self, state: &mut H) {
         self.start.hash(state);
         self.end.hash(state);
-        RangeInclusiveEquality::canonicalized_is_empty(self).hash(state);
+        // Ideally we would hash `is_exhausted` here as well, but there's no
+        // way for us to call it.
     }
 }
 
@@ -485,6 +469,14 @@ impl<Idx: fmt::Debug> fmt::Debug for RangeInclusive<Idx> {
     }
 }
 
+impl<Idx: PartialEq<Idx>> RangeInclusive<Idx> {
+    // Returns true if this is a range that started non-empty, and was iterated
+    // to exhaustion.
+    fn is_exhausted(&self) -> bool {
+        Some(true) == self.is_empty && self.start == self.end
+    }
+}
+
 impl<Idx: PartialOrd<Idx>> RangeInclusive<Idx> {
     /// Returns `true` if `item` is contained in the range.
     ///