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 /library/alloc/src | |
| 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
Diffstat (limited to 'library/alloc/src')
| -rw-r--r-- | library/alloc/src/str.rs | 44 |
1 files changed, 25 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 } |
