Skip to content

Commit 8b51854

Browse files
committed
fix panic-safety in specialized Zip::next_back
This was unsound since a panic in a.next_back() would result in the length not being updated which would then lead to the same element being revisited in the side-effect preserving code.
1 parent 88ba8ad commit 8b51854

File tree

2 files changed

+27
-1
lines changed
  • library/core

2 files changed

+27
-1
lines changed

library/core/src/iter/adapters/zip.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -295,9 +295,10 @@ where
295295
let sz_a = self.a.size();
296296
if A::MAY_HAVE_SIDE_EFFECT && sz_a > self.len {
297297
for _ in 0..sz_a - self.len {
298+
self.a_len -= 1;
298299
self.a.next_back();
299300
}
300-
self.a_len = self.len;
301+
debug_assert_eq!(self.a_len, self.len);
301302
}
302303
let sz_b = self.b.size();
303304
if B::MAY_HAVE_SIDE_EFFECT && sz_b > self.len {

library/core/tests/iter/adapters/zip.rs

+25
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use super::*;
22
use core::iter::*;
3+
use std::panic::catch_unwind;
4+
use std::panic::AssertUnwindSafe;
35

46
#[test]
57
fn test_zip_nth() {
@@ -232,6 +234,29 @@ fn test_zip_trusted_random_access_composition() {
232234
assert_eq!(z2.next().unwrap(), ((1, 1), 1));
233235
}
234236

237+
#[test]
238+
fn test_zip_trusted_random_access_next_back_drop() {
239+
let mut counter = 0;
240+
241+
let it = [42].iter().map(|e| {
242+
let c = counter;
243+
counter += 1;
244+
if c == 0 {
245+
panic!("bomb");
246+
}
247+
248+
e
249+
});
250+
let it2 = [(); 0].iter();
251+
let mut zip = it.zip(it2);
252+
catch_unwind(AssertUnwindSafe(|| {
253+
zip.next_back();
254+
}))
255+
.unwrap_err();
256+
assert!(zip.next().is_none());
257+
assert_eq!(counter, 1);
258+
}
259+
235260
#[test]
236261
fn test_double_ended_zip() {
237262
let xs = [1, 2, 3, 4, 5, 6];

0 commit comments

Comments
 (0)