about summary refs log tree commit diff
diff options
context:
space:
mode:
authorChris Denton <chris@chrisdenton.dev>2024-07-18 13:42:11 +0000
committerChris Denton <chris@chrisdenton.dev>2024-07-18 13:45:20 +0000
commita605e2f498ce9383d2b9ee44a796e5316c7a5e6c (patch)
tree63eb56bc02dc7df92a0d997a39a97d75d1cd5ea2
parentb01a977b0737e177f9b03949b908c250020d0119 (diff)
downloadrust-a605e2f498ce9383d2b9ee44a796e5316c7a5e6c.tar.gz
rust-a605e2f498ce9383d2b9ee44a796e5316c7a5e6c.zip
Safely enforce thread name requirements
-rw-r--r--library/std/src/thread/mod.rs39
1 files changed, 28 insertions, 11 deletions
diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs
index c8ee365392f..87d5c2f742c 100644
--- a/library/std/src/thread/mod.rs
+++ b/library/std/src/thread/mod.rs
@@ -161,7 +161,7 @@ mod tests;
 use crate::any::Any;
 use crate::cell::{OnceCell, UnsafeCell};
 use crate::env;
-use crate::ffi::{CStr, CString};
+use crate::ffi::CStr;
 use crate::fmt;
 use crate::io;
 use crate::marker::PhantomData;
@@ -487,11 +487,7 @@ impl Builder {
             amt
         });
 
-        let my_thread = name.map_or_else(Thread::new_unnamed, |name| unsafe {
-            Thread::new(
-                CString::new(name).expect("thread name may not contain interior null bytes"),
-            )
-        });
+        let my_thread = name.map_or_else(Thread::new_unnamed, |name| Thread::new(name.into()));
         let their_thread = my_thread.clone();
 
         let my_packet: Arc<Packet<'scope, T>> = Arc::new(Packet {
@@ -1273,10 +1269,34 @@ impl ThreadId {
 /// The internal representation of a `Thread`'s name.
 enum ThreadName {
     Main,
-    Other(CString),
+    Other(ThreadNameString),
     Unnamed,
 }
 
+// This module ensures private fields are kept private, which is necessary to enforce the safety requirements.
+mod thread_name_string {
+    use crate::ffi::{CStr, CString};
+
+    /// Like a `String` it's guaranteed UTF-8 and like a `CString` it's null terminated.
+    pub(crate) struct ThreadNameString {
+        inner: CString,
+    }
+    impl core::ops::Deref for ThreadNameString {
+        type Target = CStr;
+        fn deref(&self) -> &CStr {
+            &self.inner
+        }
+    }
+    impl From<String> for ThreadNameString {
+        fn from(s: String) -> Self {
+            Self {
+                inner: CString::new(s).expect("thread name may not contain interior null bytes"),
+            }
+        }
+    }
+}
+pub(crate) use thread_name_string::ThreadNameString;
+
 /// The internal representation of a `Thread` handle
 struct Inner {
     name: ThreadName, // Guaranteed to be UTF-8
@@ -1316,10 +1336,7 @@ pub struct Thread {
 
 impl Thread {
     /// Used only internally to construct a thread object without spawning.
-    ///
-    /// # Safety
-    /// `name` must be valid UTF-8.
-    pub(crate) unsafe fn new(name: CString) -> Thread {
+    pub(crate) fn new(name: ThreadNameString) -> Thread {
         unsafe { Self::new_inner(ThreadName::Other(name)) }
     }