diff options
| author | bors <bors@rust-lang.org> | 2021-03-28 06:32:34 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2021-03-28 06:32:34 +0000 |
| commit | 5208f63ba8ec70a2a7a074d7ecd59a94693286fc (patch) | |
| tree | b7cf431d74816b089406921bb79739f94ebe0b0d | |
| parent | 1df20569dd07d91ed270ea9cfc2dbb9f56700703 (diff) | |
| parent | 26a62701e42d10c03ce5f2f911e7d5edeefa2f0f (diff) | |
| download | rust-5208f63ba8ec70a2a7a074d7ecd59a94693286fc.tar.gz rust-5208f63ba8ec70a2a7a074d7ecd59a94693286fc.zip | |
Auto merge of #81728 - Qwaz:fix-80335, r=joshtriplett
Fixes API soundness issue in join() Fixes #80335
| -rw-r--r-- | library/alloc/src/str.rs | 44 | ||||
| -rw-r--r-- | library/alloc/tests/str.rs | 30 |
2 files changed, 55 insertions, 19 deletions
diff --git a/library/alloc/src/str.rs b/library/alloc/src/str.rs index 3730b407972..879af7cf4dd 100644 --- a/library/alloc/src/str.rs +++ b/library/alloc/src/str.rs @@ -92,8 +92,8 @@ impl<S: Borrow<str>> Join<&str> for [S] { } } -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; @@ -104,7 +104,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); } }, )* @@ -112,11 +113,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 { @@ -155,30 +158,33 @@ where // 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) }) .expect("attempt to join into collection with len > usize::MAX"); - // crucial for safety - let mut result = Vec::with_capacity(len); - assert!(result.capacity() >= len); + // prepare an uninitialized buffer + 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); + + // 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); } result } diff --git a/library/alloc/tests/str.rs b/library/alloc/tests/str.rs index 604835e6cc4..6df8d8c2f35 100644 --- a/library/alloc/tests/str.rs +++ b/library/alloc/tests/str.rs @@ -161,6 +161,36 @@ fn test_join_for_different_lengths_with_long_separator() { } #[test] +fn test_join_isue_80335() { + use core::{borrow::Borrow, cell::Cell}; + + struct WeirdBorrow { + state: Cell<bool>, + } + + impl Default for WeirdBorrow { + fn default() -> Self { + WeirdBorrow { state: Cell::new(false) } + } + } + + impl Borrow<str> 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() { assert_eq!("ab", unsafe { "abc".get_unchecked(0..2) }); |
