Skip to content

Commit

Permalink
fix panic-safety in specialized Zip::next_back
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
the8472 committed Jun 19, 2021
1 parent 88ba8ad commit 8b51854
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 1 deletion.
3 changes: 2 additions & 1 deletion library/core/src/iter/adapters/zip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -295,9 +295,10 @@ where
let sz_a = self.a.size();
if A::MAY_HAVE_SIDE_EFFECT && sz_a > self.len {
for _ in 0..sz_a - self.len {
self.a_len -= 1;
self.a.next_back();
}
self.a_len = self.len;
debug_assert_eq!(self.a_len, self.len);
}
let sz_b = self.b.size();
if B::MAY_HAVE_SIDE_EFFECT && sz_b > self.len {
Expand Down
25 changes: 25 additions & 0 deletions library/core/tests/iter/adapters/zip.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use super::*;
use core::iter::*;
use std::panic::catch_unwind;
use std::panic::AssertUnwindSafe;

#[test]
fn test_zip_nth() {
Expand Down Expand Up @@ -232,6 +234,29 @@ fn test_zip_trusted_random_access_composition() {
assert_eq!(z2.next().unwrap(), ((1, 1), 1));
}

#[test]
fn test_zip_trusted_random_access_next_back_drop() {
let mut counter = 0;

let it = [42].iter().map(|e| {
let c = counter;
counter += 1;
if c == 0 {
panic!("bomb");
}

e
});
let it2 = [(); 0].iter();
let mut zip = it.zip(it2);
catch_unwind(AssertUnwindSafe(|| {
zip.next_back();
}))
.unwrap_err();
assert!(zip.next().is_none());
assert_eq!(counter, 1);
}

#[test]
fn test_double_ended_zip() {
let xs = [1, 2, 3, 4, 5, 6];
Expand Down

0 comments on commit 8b51854

Please sign in to comment.