Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 8e8dd57

Browse files
committed
Auto merge of rust-lang#3953 - YohDeadfall:glibc-thread-name, r=RalfJung
Fixed pthread_getname_np impl for glibc The behavior of `glibc` differs a bit different for `pthread_getname_np` from other implementations. It requires the buffer to be at least 16 bytes wide without exception. [Docs](https://www.man7.org/linux/man-pages/man3/pthread_setname_np.3.html): ``` The pthread_getname_np() function can be used to retrieve the name of the thread. The thread argument specifies the thread whose name is to be retrieved. The buffer name is used to return the thread name; size specifies the number of bytes available in name. The buffer specified by name should be at least 16 characters in length. The returned thread name in the output buffer will be null terminated. ``` [Source](https://sourceware.org/git/?p=glibc.git;a=blob;f=nptl/pthread_getname.c;hb=dff8da6b3e89b986bb7f6b1ec18cf65d5972e307#l37): ```c int __pthread_getname_np (pthread_t th, char *buf, size_t len) { const struct pthread *pd = (const struct pthread *) th; /* Unfortunately the kernel headers do not export the TASK_COMM_LEN macro. So we have to define it here. */ #define TASK_COMM_LEN 16 if (len < TASK_COMM_LEN) return ERANGE; ```
2 parents 87058a4 + f64c9c6 commit 8e8dd57

File tree

2 files changed

+80
-24
lines changed

2 files changed

+80
-24
lines changed

src/tools/miri/src/shims/unix/linux/foreign_items.rs

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ use crate::machine::{SIGRTMAX, SIGRTMIN};
99
use crate::shims::unix::*;
1010
use crate::*;
1111

12+
// The documentation of glibc complains that the kernel never exposes
13+
// TASK_COMM_LEN through the headers, so it's assumed to always be 16 bytes
14+
// long including a null terminator.
15+
const TASK_COMM_LEN: usize = 16;
16+
1217
pub fn is_dyn_sym(name: &str) -> bool {
1318
matches!(name, "statx")
1419
}
@@ -74,22 +79,29 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
7479
"pthread_setname_np" => {
7580
let [thread, name] =
7681
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
77-
let max_len = 16;
7882
let res = this.pthread_setname_np(
7983
this.read_scalar(thread)?,
8084
this.read_scalar(name)?,
81-
max_len,
85+
TASK_COMM_LEN,
8286
)?;
8387
this.write_scalar(res, dest)?;
8488
}
8589
"pthread_getname_np" => {
8690
let [thread, name, len] =
8791
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
88-
let res = this.pthread_getname_np(
89-
this.read_scalar(thread)?,
90-
this.read_scalar(name)?,
91-
this.read_scalar(len)?,
92-
)?;
92+
// The function's behavior isn't portable between platforms.
93+
// In case of glibc, the length of the output buffer must
94+
// be not shorter than TASK_COMM_LEN.
95+
let len = this.read_scalar(len)?;
96+
let res = if len.to_target_usize(this)? < TASK_COMM_LEN as u64 {
97+
this.eval_libc("ERANGE")
98+
} else {
99+
this.pthread_getname_np(
100+
this.read_scalar(thread)?,
101+
this.read_scalar(name)?,
102+
len,
103+
)?
104+
};
93105
this.write_scalar(res, dest)?;
94106
}
95107
"gettid" => {

src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs

Lines changed: 61 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ use std::ffi::CString;
55
use std::thread;
66

77
fn main() {
8+
// The short name should be shorter than 16 bytes which POSIX promises
9+
// for thread names. The length includes a null terminator.
10+
let short_name = "test_named".to_owned();
811
let long_name = std::iter::once("test_named_thread_truncation")
912
.chain(std::iter::repeat(" yada").take(100))
1013
.collect::<String>();
@@ -48,23 +51,64 @@ fn main() {
4851
}
4952
}
5053

51-
let result = thread::Builder::new().name(long_name.clone()).spawn(move || {
52-
// Rust remembers the full thread name itself.
53-
assert_eq!(thread::current().name(), Some(long_name.as_str()));
54+
thread::Builder::new()
55+
.name(short_name.clone())
56+
.spawn(move || {
57+
// Rust remembers the full thread name itself.
58+
assert_eq!(thread::current().name(), Some(short_name.as_str()));
5459

55-
// But the system is limited -- make sure we successfully set a truncation.
56-
let mut buf = vec![0u8; long_name.len() + 1];
57-
assert_eq!(get_thread_name(&mut buf), 0);
58-
let cstr = CStr::from_bytes_until_nul(&buf).unwrap();
59-
assert!(cstr.to_bytes().len() >= 15, "name is too short: len={}", cstr.to_bytes().len()); // POSIX seems to promise at least 15 chars
60-
assert!(long_name.as_bytes().starts_with(cstr.to_bytes()));
60+
// Note that glibc requires 15 bytes long buffer exculding a null terminator.
61+
// Otherwise, `pthread_getname_np` returns an error.
62+
let mut buf = vec![0u8; short_name.len().max(15) + 1];
63+
assert_eq!(get_thread_name(&mut buf), 0);
6164

62-
// Also test directly calling pthread_setname to check its return value.
63-
assert_eq!(set_thread_name(&cstr), 0);
64-
// But with a too long name it should fail (except on FreeBSD where the
65-
// function has no return, hence cannot indicate failure).
66-
#[cfg(not(target_os = "freebsd"))]
67-
assert_ne!(set_thread_name(&CString::new(long_name).unwrap()), 0);
68-
});
69-
result.unwrap().join().unwrap();
65+
let cstr = CStr::from_bytes_until_nul(&buf).unwrap();
66+
// POSIX seems to promise at least 15 chars excluding a null terminator.
67+
assert_eq!(short_name.as_bytes(), cstr.to_bytes());
68+
69+
// Also test directly calling pthread_setname to check its return value.
70+
assert_eq!(set_thread_name(&cstr), 0);
71+
72+
// For glibc used by linux-gnu there should be a failue,
73+
// if a shorter than 16 bytes buffer is provided, even if that would be
74+
// large enough for the thread name.
75+
#[cfg(target_os = "linux")]
76+
assert_eq!(get_thread_name(&mut buf[..15]), libc::ERANGE);
77+
})
78+
.unwrap()
79+
.join()
80+
.unwrap();
81+
82+
thread::Builder::new()
83+
.name(long_name.clone())
84+
.spawn(move || {
85+
// Rust remembers the full thread name itself.
86+
assert_eq!(thread::current().name(), Some(long_name.as_str()));
87+
88+
// But the system is limited -- make sure we successfully set a truncation.
89+
// Note that there's no specific to glibc buffer requirement, since the value
90+
// `long_name` is longer than 16 bytes including a null terminator.
91+
let mut buf = vec![0u8; long_name.len() + 1];
92+
assert_eq!(get_thread_name(&mut buf), 0);
93+
94+
let cstr = CStr::from_bytes_until_nul(&buf).unwrap();
95+
// POSIX seems to promise at least 15 chars excluding a null terminator.
96+
assert!(
97+
cstr.to_bytes().len() >= 15,
98+
"name is too short: len={}",
99+
cstr.to_bytes().len()
100+
);
101+
assert!(long_name.as_bytes().starts_with(cstr.to_bytes()));
102+
103+
// Also test directly calling pthread_setname to check its return value.
104+
assert_eq!(set_thread_name(&cstr), 0);
105+
106+
// But with a too long name it should fail (except on FreeBSD where the
107+
// function has no return, hence cannot indicate failure).
108+
#[cfg(not(target_os = "freebsd"))]
109+
assert_ne!(set_thread_name(&CString::new(long_name).unwrap()), 0);
110+
})
111+
.unwrap()
112+
.join()
113+
.unwrap();
70114
}

0 commit comments

Comments
 (0)