Skip to content

Commit 48c4afb

Browse files
Rollup merge of #78499 - SkiFire13:fix-string-retain, r=m-ou-se
Prevent String::retain from creating non-utf8 strings when abusing panic Fixes #78498 The idea is the same as `Vec::drain`, set the len to 0 so that nobody can observe the broken invariant if it escapes the function (in this case if `f` panics)
2 parents a01e5f8 + 1f6f917 commit 48c4afb

File tree

2 files changed

+21
-4
lines changed

2 files changed

+21
-4
lines changed

library/alloc/src/string.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -1235,6 +1235,10 @@ impl String {
12351235
let mut del_bytes = 0;
12361236
let mut idx = 0;
12371237

1238+
unsafe {
1239+
self.vec.set_len(0);
1240+
}
1241+
12381242
while idx < len {
12391243
let ch = unsafe { self.get_unchecked(idx..len).chars().next().unwrap() };
12401244
let ch_len = ch.len_utf8();
@@ -1255,10 +1259,8 @@ impl String {
12551259
idx += ch_len;
12561260
}
12571261

1258-
if del_bytes > 0 {
1259-
unsafe {
1260-
self.vec.set_len(len - del_bytes);
1261-
}
1262+
unsafe {
1263+
self.vec.set_len(len - del_bytes);
12621264
}
12631265
}
12641266

library/alloc/tests/string.rs

+15
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::borrow::Cow;
22
use std::collections::TryReserveError::*;
33
use std::ops::Bound::*;
4+
use std::panic;
45

56
pub trait IntoCow<'a, B: ?Sized>
67
where
@@ -378,6 +379,20 @@ fn test_retain() {
378379

379380
s.retain(|_| false);
380381
assert_eq!(s, "");
382+
383+
let mut s = String::from("0è0");
384+
let _ = panic::catch_unwind(panic::AssertUnwindSafe(|| {
385+
let mut count = 0;
386+
s.retain(|_| {
387+
count += 1;
388+
match count {
389+
1 => false,
390+
2 => true,
391+
_ => panic!(),
392+
}
393+
});
394+
}));
395+
assert!(std::str::from_utf8(s.as_bytes()).is_ok());
381396
}
382397

383398
#[test]

0 commit comments

Comments
 (0)