about summary refs log tree commit diff
diff options
context:
space:
mode:
authorTyler Mandry <tmandry@gmail.com>2020-01-24 00:30:56 -0800
committerGitHub <noreply@github.com>2020-01-24 00:30:56 -0800
commite7752aefdc0765f9350cbe5d563d6f46f9045e26 (patch)
tree1ac0f937d02fcd94986e94400f70d13324c7ea25
parent143059deafe1e5df3cbbaf4d47f5461af7bcf8c3 (diff)
parent9d3e84432dae2e96a5e0f97be18ee09b5a2217b1 (diff)
downloadrust-e7752aefdc0765f9350cbe5d563d6f46f9045e26.tar.gz
rust-e7752aefdc0765f9350cbe5d563d6f46f9045e26.zip
Rollup merge of #68469 - ollie27:skip_count, r=sfackler
Avoid overflow in `std::iter::Skip::count`

The call to `count` on the inner iterator can overflow even if `Skip` itself would return less that `usize::max_value()` items.

Fixes #68139
-rw-r--r--src/libcore/iter/adapters/mod.rs10
-rw-r--r--src/test/ui/iterators/skip-count-overflow.rs8
2 files changed, 16 insertions, 2 deletions
diff --git a/src/libcore/iter/adapters/mod.rs b/src/libcore/iter/adapters/mod.rs
index 6eb837ed0fe..5787b9174ed 100644
--- a/src/libcore/iter/adapters/mod.rs
+++ b/src/libcore/iter/adapters/mod.rs
@@ -1815,8 +1815,14 @@ where
     }
 
     #[inline]
-    fn count(self) -> usize {
-        self.iter.count().saturating_sub(self.n)
+    fn count(mut self) -> usize {
+        if self.n > 0 {
+            // nth(n) skips n+1
+            if self.iter.nth(self.n - 1).is_none() {
+                return 0;
+            }
+        }
+        self.iter.count()
     }
 
     #[inline]
diff --git a/src/test/ui/iterators/skip-count-overflow.rs b/src/test/ui/iterators/skip-count-overflow.rs
new file mode 100644
index 00000000000..d8efc948664
--- /dev/null
+++ b/src/test/ui/iterators/skip-count-overflow.rs
@@ -0,0 +1,8 @@
+// run-pass
+// only-32bit too impatient for 2⁶⁴ items
+// compile-flags: -C overflow-checks -C opt-level=3
+
+fn main() {
+    let i = (0..usize::max_value()).chain(0..10).skip(usize::max_value());
+    assert_eq!(i.count(), 10);
+}