Skip to content

Commit f64c9c6

Browse files
committed
Fixed pthread_getname_np impl for glibc
1 parent edc5c1e commit f64c9c6

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)