From 70f17ca715d3d7e2fd79cc909b95fd3a6357c13e Mon Sep 17 00:00:00 2001 From: Yechan Bae Date: Wed, 3 Feb 2021 16:36:33 -0500 Subject: [PATCH 1/2] Fixes #80335 --- library/alloc/src/str.rs | 42 ++++++++++++++++++++++---------------- library/alloc/tests/str.rs | 30 +++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 18 deletions(-) diff --git a/library/alloc/src/str.rs b/library/alloc/src/str.rs index 70e0c7dba5ea..a7584c6b6510 100644 --- a/library/alloc/src/str.rs +++ b/library/alloc/src/str.rs @@ -90,8 +90,8 @@ fn join(slice: &Self, sep: &str) -> String { } } -macro_rules! spezialize_for_lengths { - ($separator:expr, $target:expr, $iter:expr; $($num:expr),*) => { +macro_rules! specialize_for_lengths { + ($separator:expr, $target:expr, $iter:expr; $($num:expr),*) => {{ let mut target = $target; let iter = $iter; let sep_bytes = $separator; @@ -102,7 +102,8 @@ macro_rules! spezialize_for_lengths { $num => { for s in iter { copy_slice_and_advance!(target, sep_bytes); - copy_slice_and_advance!(target, s.borrow().as_ref()); + let content_bytes = s.borrow().as_ref(); + copy_slice_and_advance!(target, content_bytes); } }, )* @@ -110,11 +111,13 @@ macro_rules! spezialize_for_lengths { // arbitrary non-zero size fallback for s in iter { copy_slice_and_advance!(target, sep_bytes); - copy_slice_and_advance!(target, s.borrow().as_ref()); + let content_bytes = s.borrow().as_ref(); + copy_slice_and_advance!(target, content_bytes); } } } - }; + target + }} } macro_rules! copy_slice_and_advance { @@ -153,7 +156,7 @@ fn join_generic_copy(slice: &[S], sep: &[T]) -> Vec // if the `len` calculation overflows, we'll panic // we would have run out of memory anyway and the rest of the function requires // the entire Vec pre-allocated for safety - let len = sep_len + let reserved_len = sep_len .checked_mul(iter.len()) .and_then(|n| { slice.iter().map(|s| s.borrow().as_ref().len()).try_fold(n, usize::checked_add) @@ -161,22 +164,25 @@ fn join_generic_copy(slice: &[S], sep: &[T]) -> Vec .expect("attempt to join into collection with len > usize::MAX"); // crucial for safety - let mut result = Vec::with_capacity(len); - assert!(result.capacity() >= len); + let mut result = Vec::with_capacity(reserved_len); + debug_assert!(result.capacity() >= reserved_len); result.extend_from_slice(first.borrow().as_ref()); unsafe { - { - let pos = result.len(); - let target = result.get_unchecked_mut(pos..len); - - // copy separator and slices over without bounds checks - // generate loops with hardcoded offsets for small separators - // massive improvements possible (~ x2) - spezialize_for_lengths!(sep, target, iter; 0, 1, 2, 3, 4); - } - result.set_len(len); + let pos = result.len(); + let target = result.get_unchecked_mut(pos..reserved_len); + + // copy separator and slices over without bounds checks + // generate loops with hardcoded offsets for small separators + // massive improvements possible (~ x2) + let remain = specialize_for_lengths!(sep, target, iter; 0, 1, 2, 3, 4); + + // issue #80335: A weird borrow implementation can return different + // slices for the length calculation and the actual copy, so + // `remain.len()` might be non-zero. + let result_len = reserved_len - remain.len(); + result.set_len(result_len); } result } diff --git a/library/alloc/tests/str.rs b/library/alloc/tests/str.rs index 604835e6cc4a..6df8d8c2f354 100644 --- a/library/alloc/tests/str.rs +++ b/library/alloc/tests/str.rs @@ -160,6 +160,36 @@ fn test_join_for_different_lengths_with_long_separator() { test_join!("~~~~~a~~~~~bc", ["", "a", "bc"], "~~~~~"); } +#[test] +fn test_join_isue_80335() { + use core::{borrow::Borrow, cell::Cell}; + + struct WeirdBorrow { + state: Cell, + } + + impl Default for WeirdBorrow { + fn default() -> Self { + WeirdBorrow { state: Cell::new(false) } + } + } + + impl Borrow for WeirdBorrow { + fn borrow(&self) -> &str { + let state = self.state.get(); + if state { + "0" + } else { + self.state.set(true); + "123456" + } + } + } + + let arr: [WeirdBorrow; 3] = Default::default(); + test_join!("0-0-0", arr, "-"); +} + #[test] #[cfg_attr(miri, ignore)] // Miri is too slow fn test_unsafe_slice() { -- 2.31.1 From 10020817d2e6756be1ff2ac3c182af97cf7fe904 Mon Sep 17 00:00:00 2001 From: Yechan Bae Date: Sat, 20 Mar 2021 13:42:54 -0400 Subject: [PATCH 2/2] Update the comment --- library/alloc/src/str.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/library/alloc/src/str.rs b/library/alloc/src/str.rs index a7584c6b6510..4d1e876457b8 100644 --- a/library/alloc/src/str.rs +++ b/library/alloc/src/str.rs @@ -163,7 +163,7 @@ fn join_generic_copy(slice: &[S], sep: &[T]) -> Vec }) .expect("attempt to join into collection with len > usize::MAX"); - // crucial for safety + // prepare an uninitialized buffer let mut result = Vec::with_capacity(reserved_len); debug_assert!(result.capacity() >= reserved_len); @@ -178,9 +178,9 @@ fn join_generic_copy(slice: &[S], sep: &[T]) -> Vec // massive improvements possible (~ x2) let remain = specialize_for_lengths!(sep, target, iter; 0, 1, 2, 3, 4); - // issue #80335: A weird borrow implementation can return different - // slices for the length calculation and the actual copy, so - // `remain.len()` might be non-zero. + // A weird borrow implementation may return different + // slices for the length calculation and the actual copy. + // Make sure we don't expose uninitialized bytes to the caller. let result_len = reserved_len - remain.len(); result.set_len(result_len); } -- 2.31.1