about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJonas Schievink <jonasschievink@gmail.com>2020-10-29 17:05:28 +0100
committerGitHub <noreply@github.com>2020-10-29 17:05:28 +0100
commit48c4afbf9c29880dd946067d1c9aee1e7f75834a (patch)
tree4edb3ac88fd5324a1b070600afced7d02ac4134c
parenta01e5f8c0dcf27462f93a174b179c0595eab3e43 (diff)
parent1f6f917f73a4372f098e9b19560b5945be145dc3 (diff)
downloadrust-48c4afbf9c29880dd946067d1c9aee1e7f75834a.tar.gz
rust-48c4afbf9c29880dd946067d1c9aee1e7f75834a.zip
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)
-rw-r--r--library/alloc/src/string.rs10
-rw-r--r--library/alloc/tests/string.rs15
2 files changed, 21 insertions, 4 deletions
diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs
index 72ed036637d..ce216e5336e 100644
--- a/library/alloc/src/string.rs
+++ b/library/alloc/src/string.rs
@@ -1235,6 +1235,10 @@ impl String {
         let mut del_bytes = 0;
         let mut idx = 0;
 
+        unsafe {
+            self.vec.set_len(0);
+        }
+
         while idx < len {
             let ch = unsafe { self.get_unchecked(idx..len).chars().next().unwrap() };
             let ch_len = ch.len_utf8();
@@ -1255,10 +1259,8 @@ impl String {
             idx += ch_len;
         }
 
-        if del_bytes > 0 {
-            unsafe {
-                self.vec.set_len(len - del_bytes);
-            }
+        unsafe {
+            self.vec.set_len(len - del_bytes);
         }
     }
 
diff --git a/library/alloc/tests/string.rs b/library/alloc/tests/string.rs
index a6e41b21b61..b28694186b6 100644
--- a/library/alloc/tests/string.rs
+++ b/library/alloc/tests/string.rs
@@ -1,6 +1,7 @@
 use std::borrow::Cow;
 use std::collections::TryReserveError::*;
 use std::ops::Bound::*;
+use std::panic;
 
 pub trait IntoCow<'a, B: ?Sized>
 where
@@ -378,6 +379,20 @@ fn test_retain() {
 
     s.retain(|_| false);
     assert_eq!(s, "");
+
+    let mut s = String::from("0รจ0");
+    let _ = panic::catch_unwind(panic::AssertUnwindSafe(|| {
+        let mut count = 0;
+        s.retain(|_| {
+            count += 1;
+            match count {
+                1 => false,
+                2 => true,
+                _ => panic!(),
+            }
+        });
+    }));
+    assert!(std::str::from_utf8(s.as_bytes()).is_ok());
 }
 
 #[test]