Rollup merge of #82554 - SkiFire13:fix-string-retain-unsoundness, r=m-ou-se
Fix invalid slice access in String::retain As noted in #78499, the previous fix was technically still unsound because it accessed elements of a slice outside its bounds (even though they were still inside the same allocation). This PR addresses that concern by switching to a dropguard approach.
This commit is contained in:
commit
da143d38e4
1 changed files with 23 additions and 16 deletions
|
@ -1289,37 +1289,44 @@ impl String {
|
||||||
where
|
where
|
||||||
F: FnMut(char) -> bool,
|
F: FnMut(char) -> bool,
|
||||||
{
|
{
|
||||||
let len = self.len();
|
struct SetLenOnDrop<'a> {
|
||||||
let mut del_bytes = 0;
|
s: &'a mut String,
|
||||||
let mut idx = 0;
|
idx: usize,
|
||||||
|
del_bytes: usize,
|
||||||
unsafe {
|
|
||||||
self.vec.set_len(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
while idx < len {
|
impl<'a> Drop for SetLenOnDrop<'a> {
|
||||||
let ch = unsafe { self.get_unchecked(idx..len).chars().next().unwrap() };
|
fn drop(&mut self) {
|
||||||
|
let new_len = self.idx - self.del_bytes;
|
||||||
|
debug_assert!(new_len <= self.s.len());
|
||||||
|
unsafe { self.s.vec.set_len(new_len) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let len = self.len();
|
||||||
|
let mut guard = SetLenOnDrop { s: self, idx: 0, del_bytes: 0 };
|
||||||
|
|
||||||
|
while guard.idx < len {
|
||||||
|
let ch = unsafe { guard.s.get_unchecked(guard.idx..len).chars().next().unwrap() };
|
||||||
let ch_len = ch.len_utf8();
|
let ch_len = ch.len_utf8();
|
||||||
|
|
||||||
if !f(ch) {
|
if !f(ch) {
|
||||||
del_bytes += ch_len;
|
guard.del_bytes += ch_len;
|
||||||
} else if del_bytes > 0 {
|
} else if guard.del_bytes > 0 {
|
||||||
unsafe {
|
unsafe {
|
||||||
ptr::copy(
|
ptr::copy(
|
||||||
self.vec.as_ptr().add(idx),
|
guard.s.vec.as_ptr().add(guard.idx),
|
||||||
self.vec.as_mut_ptr().add(idx - del_bytes),
|
guard.s.vec.as_mut_ptr().add(guard.idx - guard.del_bytes),
|
||||||
ch_len,
|
ch_len,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Point idx to the next char
|
// Point idx to the next char
|
||||||
idx += ch_len;
|
guard.idx += ch_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe {
|
drop(guard);
|
||||||
self.vec.set_len(len - del_bytes);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Inserts a character into this `String` at a byte position.
|
/// Inserts a character into this `String` at a byte position.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue