Skip to content

Commit c89e643

Browse files
committed
Fix invalid slice access in String::retain
1 parent d95d304 commit c89e643

File tree

1 file changed

+22
-15
lines changed

1 file changed

+22
-15
lines changed

library/alloc/src/string.rs

+22-15
Original file line numberDiff line numberDiff line change
@@ -1233,37 +1233,44 @@ impl String {
12331233
where
12341234
F: FnMut(char) -> bool,
12351235
{
1236-
let len = self.len();
1237-
let mut del_bytes = 0;
1238-
let mut idx = 0;
1236+
struct SetLenOnDrop<'a> {
1237+
s: &'a mut String,
1238+
idx: usize,
1239+
del_bytes: usize,
1240+
}
12391241

1240-
unsafe {
1241-
self.vec.set_len(0);
1242+
impl<'a> Drop for SetLenOnDrop<'a> {
1243+
fn drop(&mut self) {
1244+
let new_len = self.idx - self.del_bytes;
1245+
debug_assert!(new_len <= self.s.len());
1246+
unsafe { self.s.vec.set_len(new_len) };
1247+
}
12421248
}
12431249

1244-
while idx < len {
1245-
let ch = unsafe { self.get_unchecked(idx..len).chars().next().unwrap() };
1250+
let len = self.len();
1251+
let mut guard = SetLenOnDrop { s: self, idx: 0, del_bytes: 0 };
1252+
1253+
while guard.idx < len {
1254+
let ch = unsafe { guard.s.get_unchecked(guard.idx..len).chars().next().unwrap() };
12461255
let ch_len = ch.len_utf8();
12471256

12481257
if !f(ch) {
1249-
del_bytes += ch_len;
1250-
} else if del_bytes > 0 {
1258+
guard.del_bytes += ch_len;
1259+
} else if guard.del_bytes > 0 {
12511260
unsafe {
12521261
ptr::copy(
1253-
self.vec.as_ptr().add(idx),
1254-
self.vec.as_mut_ptr().add(idx - del_bytes),
1262+
guard.s.vec.as_ptr().add(guard.idx),
1263+
guard.s.vec.as_mut_ptr().add(guard.idx - guard.del_bytes),
12551264
ch_len,
12561265
);
12571266
}
12581267
}
12591268

12601269
// Point idx to the next char
1261-
idx += ch_len;
1270+
guard.idx += ch_len;
12621271
}
12631272

1264-
unsafe {
1265-
self.vec.set_len(len - del_bytes);
1266-
}
1273+
drop(guard);
12671274
}
12681275

12691276
/// Inserts a character into this `String` at a byte position.

0 commit comments

Comments
 (0)