Rollup merge of #86452 - the8472:fix-zip-drop-safety, r=m-ou-se

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.

fixes #86443
This commit is contained in:
Yuki Okushi 2021-06-21 09:42:17 +09:00 committed by GitHub
commit 504c378159
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 29 additions and 1 deletions

View File

@ -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 {

View File

@ -232,6 +232,33 @@ fn test_zip_trusted_random_access_composition() {
assert_eq!(z2.next().unwrap(), ((1, 1), 1));
}
#[test]
#[cfg(panic = "unwind")]
fn test_zip_trusted_random_access_next_back_drop() {
use std::panic::catch_unwind;
use std::panic::AssertUnwindSafe;
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];