Skip to content

Commit d103522

Browse files
committed
Check integer overflow/underflow in Range* iterators
1 parent 513906c commit d103522

File tree

2 files changed

+62
-25
lines changed

2 files changed

+62
-25
lines changed

src/libcore/iter/range.rs

Lines changed: 50 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
use convert::TryFrom;
1212
use mem;
13-
use ops::{self, Add, Sub};
13+
use ops;
1414
use usize;
1515

1616
use super::{FusedIterator, TrustedLen};
@@ -36,14 +36,11 @@ pub trait Step: Clone + PartialOrd + Sized {
3636
/// Replaces this step with `0`, returning itself
3737
fn replace_zero(&mut self) -> Self;
3838

39-
/// Adds one to this step, returning the result
40-
fn add_one(&self) -> Self;
41-
42-
/// Subtracts one to this step, returning the result
43-
fn sub_one(&self) -> Self;
44-
4539
/// Add an usize, returning None on overflow
4640
fn add_usize(&self, n: usize) -> Option<Self>;
41+
42+
/// Subtracts an usize, returning None on overflow
43+
fn sub_usize(&self, n: usize) -> Option<Self>;
4744
}
4845

4946
// These are still macro-generated because the integer literals resolve to different types.
@@ -58,16 +55,6 @@ macro_rules! step_identical_methods {
5855
fn replace_zero(&mut self) -> Self {
5956
mem::replace(self, 0)
6057
}
61-
62-
#[inline]
63-
fn add_one(&self) -> Self {
64-
Add::add(*self, 1)
65-
}
66-
67-
#[inline]
68-
fn sub_one(&self) -> Self {
69-
Sub::sub(*self, 1)
70-
}
7158
}
7259
}
7360

@@ -96,6 +83,14 @@ macro_rules! step_impl_unsigned {
9683
}
9784
}
9885

86+
#[inline]
87+
fn sub_usize(&self, n: usize) -> Option<Self> {
88+
match <$t>::try_from(n) {
89+
Ok(n_as_t) => self.checked_sub(n_as_t),
90+
Err(_) => None,
91+
}
92+
}
93+
9994
step_identical_methods!();
10095
}
10196
)*)
@@ -136,6 +131,23 @@ macro_rules! step_impl_signed {
136131
Err(_) => None,
137132
}
138133
}
134+
#[inline]
135+
fn sub_usize(&self, n: usize) -> Option<Self> {
136+
match <$unsigned>::try_from(n) {
137+
Ok(n_as_unsigned) => {
138+
// Wrapping in unsigned space handles cases like
139+
// `-120_i8.add_usize(200) == Some(80_i8)`,
140+
// even though 200_usize is out of range for i8.
141+
let wrapped = (*self as $unsigned).wrapping_sub(n_as_unsigned) as $t;
142+
if wrapped <= *self {
143+
Some(wrapped)
144+
} else {
145+
None // Subtraction underflowed
146+
}
147+
}
148+
Err(_) => None,
149+
}
150+
}
139151

140152
step_identical_methods!();
141153
}
@@ -158,6 +170,11 @@ macro_rules! step_impl_no_between {
158170
self.checked_add(n as $t)
159171
}
160172

173+
#[inline]
174+
fn sub_usize(&self, n: usize) -> Option<Self> {
175+
self.checked_sub(n as $t)
176+
}
177+
161178
step_identical_methods!();
162179
}
163180
)*)
@@ -214,7 +231,8 @@ impl<A: Step> Iterator for ops::Range<A> {
214231
#[inline]
215232
fn next(&mut self) -> Option<A> {
216233
if self.start < self.end {
217-
let mut n = self.start.add_one();
234+
// `start + 1` should not overflow since `end` exists such that `start < end`
235+
let mut n = self.start.add_usize(1).expect("overflow in Range::next");
218236
mem::swap(&mut n, &mut self.start);
219237
Some(n)
220238
} else {
@@ -234,7 +252,8 @@ impl<A: Step> Iterator for ops::Range<A> {
234252
fn nth(&mut self, n: usize) -> Option<A> {
235253
if let Some(plus_n) = self.start.add_usize(n) {
236254
if plus_n < self.end {
237-
self.start = plus_n.add_one();
255+
// `plus_n + 1` should not overflow since `end` exists such that `plus_n < end`
256+
self.start = plus_n.add_usize(1).expect("overflow in Range::nth");
238257
return Some(plus_n)
239258
}
240259
}
@@ -263,7 +282,8 @@ impl<A: Step> DoubleEndedIterator for ops::Range<A> {
263282
#[inline]
264283
fn next_back(&mut self) -> Option<A> {
265284
if self.start < self.end {
266-
self.end = self.end.sub_one();
285+
// `end - 1` should not overflow since `start` exists such that `start < end`
286+
self.end = self.end.sub_usize(1).expect("overflow in Range::nth_back");
267287
Some(self.end.clone())
268288
} else {
269289
None
@@ -280,7 +300,8 @@ impl<A: Step> Iterator for ops::RangeFrom<A> {
280300

281301
#[inline]
282302
fn next(&mut self) -> Option<A> {
283-
let mut n = self.start.add_one();
303+
// Overflow can happen here. Panic when it does.
304+
let mut n = self.start.add_usize(1).expect("overflow in RangeFrom::next");
284305
mem::swap(&mut n, &mut self.start);
285306
Some(n)
286307
}
@@ -292,8 +313,9 @@ impl<A: Step> Iterator for ops::RangeFrom<A> {
292313

293314
#[inline]
294315
fn nth(&mut self, n: usize) -> Option<A> {
316+
// Overflow can happen here. Panic when it does.
295317
let plus_n = self.start.add_usize(n).expect("overflow in RangeFrom::nth");
296-
self.start = plus_n.add_one();
318+
self.start = plus_n.add_usize(1).expect("overflow in RangeFrom::nth");
297319
Some(plus_n)
298320
}
299321
}
@@ -311,7 +333,8 @@ impl<A: Step> Iterator for ops::RangeInclusive<A> {
311333

312334
match self.start.partial_cmp(&self.end) {
313335
Some(Less) => {
314-
let n = self.start.add_one();
336+
// `start + 1` should not overflow since `end` exists such that `start < end`
337+
let n = self.start.add_usize(1).expect("overflow in RangeInclusive::next");
315338
Some(mem::replace(&mut self.start, n))
316339
},
317340
Some(Equal) => {
@@ -342,7 +365,8 @@ impl<A: Step> Iterator for ops::RangeInclusive<A> {
342365

343366
match plus_n.partial_cmp(&self.end) {
344367
Some(Less) => {
345-
self.start = plus_n.add_one();
368+
// `plus_n + 1` should not overflow since `end` exists such that `plus_n < end`
369+
self.start = plus_n.add_usize(1).expect("overflow in RangeInclusive::nth");
346370
return Some(plus_n)
347371
}
348372
Some(Equal) => {
@@ -368,7 +392,8 @@ impl<A: Step> DoubleEndedIterator for ops::RangeInclusive<A> {
368392

369393
match self.start.partial_cmp(&self.end) {
370394
Some(Less) => {
371-
let n = self.end.sub_one();
395+
// `end - 1` should not overflow since `start` exists such that `start < end`
396+
let n = self.end.sub_usize(1).expect("overflow in RangeInclusive::next_back");
372397
Some(mem::replace(&mut self.end, n))
373398
},
374399
Some(Equal) => {

src/libcore/tests/iter.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1168,6 +1168,18 @@ fn test_range_step() {
11681168
assert_eq!((isize::MIN..isize::MAX).step_by(1).size_hint(), (usize::MAX, Some(usize::MAX)));
11691169
}
11701170

1171+
#[test]
1172+
#[should_panic]
1173+
fn test_range_from_next_overflow() {
1174+
(255u8..).next();
1175+
}
1176+
1177+
#[test]
1178+
#[should_panic]
1179+
fn test_range_from_nth_overflow() {
1180+
(200u8..).nth(55);
1181+
}
1182+
11711183
#[test]
11721184
fn test_repeat() {
11731185
let mut it = repeat(42);

0 commit comments

Comments
 (0)