Skip to content

Commit 78495d5

Browse files
committed
Fix unsound behaviour with null characters in thread names (issue #32475)
Previously, the thread name (&str) was converted to a CString in the new thread, but outside unwind::try, causing a panic to continue into FFI. This patch changes that behaviour, so that the panic instead happens in the parent thread (where panic infrastructure is properly set up), not the new thread. This could potentially be a breaking change for architectures who don't support thread names. Signed-off-by: David Henningsson <[email protected]>
1 parent d7a7168 commit 78495d5

File tree

3 files changed

+30
-20
lines changed

3 files changed

+30
-20
lines changed

src/libstd/sys/unix/thread.rs

+11-16
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use prelude::v1::*;
1313
use alloc::boxed::FnBox;
1414
use cmp;
1515
#[cfg(not(any(target_env = "newlib", target_os = "solaris")))]
16-
use ffi::CString;
16+
use ffi::CStr;
1717
use io;
1818
use libc;
1919
use mem;
@@ -84,48 +84,43 @@ impl Thread {
8484
#[cfg(any(target_os = "linux",
8585
target_os = "android",
8686
target_os = "emscripten"))]
87-
pub fn set_name(name: &str) {
87+
pub fn set_name(name: &CStr) {
8888
const PR_SET_NAME: libc::c_int = 15;
89-
let cname = CString::new(name).unwrap_or_else(|_| {
90-
panic!("thread name may not contain interior null bytes")
91-
});
9289
// pthread wrapper only appeared in glibc 2.12, so we use syscall
9390
// directly.
9491
unsafe {
95-
libc::prctl(PR_SET_NAME, cname.as_ptr() as libc::c_ulong, 0, 0, 0);
92+
libc::prctl(PR_SET_NAME, name.as_ptr() as libc::c_ulong, 0, 0, 0);
9693
}
9794
}
9895

9996
#[cfg(any(target_os = "freebsd",
10097
target_os = "dragonfly",
10198
target_os = "bitrig",
10299
target_os = "openbsd"))]
103-
pub fn set_name(name: &str) {
104-
let cname = CString::new(name).unwrap();
100+
pub fn set_name(name: &CStr) {
105101
unsafe {
106-
libc::pthread_set_name_np(libc::pthread_self(), cname.as_ptr());
102+
libc::pthread_set_name_np(libc::pthread_self(), name.as_ptr());
107103
}
108104
}
109105

110106
#[cfg(any(target_os = "macos", target_os = "ios"))]
111-
pub fn set_name(name: &str) {
112-
let cname = CString::new(name).unwrap();
107+
pub fn set_name(name: &CStr) {
113108
unsafe {
114-
libc::pthread_setname_np(cname.as_ptr());
109+
libc::pthread_setname_np(name.as_ptr());
115110
}
116111
}
117112

118113
#[cfg(target_os = "netbsd")]
119-
pub fn set_name(name: &str) {
114+
pub fn set_name(name: &CStr) {
115+
use ffi::CString;
120116
let cname = CString::new(&b"%s"[..]).unwrap();
121-
let carg = CString::new(name).unwrap();
122117
unsafe {
123118
libc::pthread_setname_np(libc::pthread_self(), cname.as_ptr(),
124-
carg.as_ptr() as *mut libc::c_void);
119+
name.as_ptr() as *mut libc::c_void);
125120
}
126121
}
127122
#[cfg(any(target_env = "newlib", target_os = "solaris"))]
128-
pub fn set_name(_name: &str) {
123+
pub fn set_name(_name: &CStr) {
129124
// Newlib and Illumos has no way to set a thread name.
130125
}
131126

src/libstd/sys/windows/thread.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ impl Thread {
5454
}
5555
}
5656

57-
pub fn set_name(_name: &str) {
57+
pub fn set_name(_name: &CStr) {
5858
// Windows threads are nameless
5959
// The names in MSVC debugger are obtained using a "magic" exception,
6060
// which requires a use of MS C++ extensions.

src/libstd/thread/mod.rs

+18-3
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,8 @@ use any::Any;
166166
use cell::UnsafeCell;
167167
use fmt;
168168
use io;
169+
use str;
170+
use ffi::{CStr, CString};
169171
use sync::{Mutex, Condvar, Arc};
170172
use sys::thread as imp;
171173
use sys_common::thread_info;
@@ -267,7 +269,7 @@ impl Builder {
267269
let their_packet = my_packet.clone();
268270

269271
let main = move || {
270-
if let Some(name) = their_thread.name() {
272+
if let Some(name) = their_thread.cname() {
271273
imp::Thread::set_name(name);
272274
}
273275
unsafe {
@@ -450,7 +452,7 @@ pub fn park_timeout(dur: Duration) {
450452

451453
/// The internal representation of a `Thread` handle
452454
struct Inner {
453-
name: Option<String>,
455+
name: Option<CString>, // Guaranteed to be UTF-8
454456
lock: Mutex<bool>, // true when there is a buffered unpark
455457
cvar: Condvar,
456458
}
@@ -465,9 +467,12 @@ pub struct Thread {
465467
impl Thread {
466468
// Used only internally to construct a thread object without spawning
467469
fn new(name: Option<String>) -> Thread {
470+
let cname = name.map(|n| CString::new(n).unwrap_or_else(|_| {
471+
panic!("thread name may not contain interior null bytes")
472+
}));
468473
Thread {
469474
inner: Arc::new(Inner {
470-
name: name,
475+
name: cname,
471476
lock: Mutex::new(false),
472477
cvar: Condvar::new(),
473478
})
@@ -489,6 +494,10 @@ impl Thread {
489494
/// Gets the thread's name.
490495
#[stable(feature = "rust1", since = "1.0.0")]
491496
pub fn name(&self) -> Option<&str> {
497+
self.cname().map(|s| unsafe { str::from_utf8_unchecked(s.to_bytes()) } )
498+
}
499+
500+
fn cname(&self) -> Option<&CStr> {
492501
self.inner.name.as_ref().map(|s| &**s)
493502
}
494503
}
@@ -622,6 +631,12 @@ mod tests {
622631
}).unwrap().join().unwrap();
623632
}
624633

634+
#[test]
635+
#[should_panic]
636+
fn test_invalid_named_thread() {
637+
let _ = Builder::new().name("ada l\0velace".to_string()).spawn(|| {});
638+
}
639+
625640
#[test]
626641
fn test_run_basic() {
627642
let (tx, rx) = channel();

0 commit comments

Comments
 (0)