summaryrefslogtreecommitdiff
blob: b4f2215cc236746717d05010eae8b86f5804d23c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
From 6d43225bfb08ec91f7476b76c7fec632c4a096ef Mon Sep 17 00:00:00 2001
From: Yechan Bae <yechan@gatech.edu>
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 70e0c7dba5eab..a7584c6b65100 100644
--- a/library/alloc/src/str.rs
+++ b/library/alloc/src/str.rs
@@ -90,8 +90,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;
@@ -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 @@ 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)
@@ -161,22 +164,25 @@ where
         .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 604835e6cc4a6..6df8d8c2f354f 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<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() {

From 26a62701e42d10c03ce5f2f911e7d5edeefa2f0f Mon Sep 17 00:00:00 2001
From: Yechan Bae <yechan@gatech.edu>
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 a7584c6b65100..4d1e876457b8e 100644
--- a/library/alloc/src/str.rs
+++ b/library/alloc/src/str.rs
@@ -163,7 +163,7 @@ where
         })
         .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 @@ where
         // 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);
     }