Skip to content

Commit 2e03626

Browse files
committed
Added support for prctl handling thread names
1 parent 894c264 commit 2e03626

File tree

2 files changed

+110
-0
lines changed

2 files changed

+110
-0
lines changed

src/shims/unix/linux/foreign_items.rs

+54
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,60 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
7272
}
7373

7474
// Threading
75+
"prctl" => {
76+
// We do not use `check_shim` here because `syscall` is variadic. The argument
77+
// count is checked bellow.
78+
this.check_abi_and_shim_symbol_clash(abi, Abi::C { unwind: false }, link_name)?;
79+
80+
if args.is_empty() {
81+
throw_ub_format!(
82+
"incorrect number of arguments for prctl: got 0, expected at least 1"
83+
);
84+
}
85+
86+
// Since only two prctl calls are currently handled, and both expext just two arguments,
87+
// it makes sense to handle validation here, outside of the match block below.
88+
if args.len() < 2 {
89+
throw_ub_format!(
90+
"incorrect number of arguments for `PR_SET_NAME` prctl: got {}, expected at least 2",
91+
args.len()
92+
);
93+
}
94+
95+
for (i, arg) in args.iter().enumerate() {
96+
let val = this.read_scalar(arg)?.to_i32()?;
97+
if val != 0 {
98+
throw_ub_format!(
99+
"incorrect value of {}th argument for 'PR_SET_NAME' prctl: got {}, expected 0L",
100+
i,
101+
val
102+
);
103+
}
104+
}
105+
106+
let op = this.read_scalar(&args[0])?.to_i32()?;
107+
let pr_set_name = this.eval_libc_i32("PR_SET_NAME");
108+
let pr_get_name = this.eval_libc_i32("PR_GET_NAME");
109+
110+
let res = match op {
111+
op if op == pr_set_name => {
112+
let tid = this.linux_gettid()?;
113+
let name = this.read_scalar(&args[1])?;
114+
let name_len = 16;
115+
116+
this.pthread_setname_np(tid, name, name_len)?
117+
}
118+
op if op == pr_get_name => {
119+
let tid = this.linux_gettid()?;
120+
let name = this.read_scalar(&args[1])?;
121+
let name_len = Scalar::from_target_usize(16, this);
122+
123+
this.pthread_getname_np(tid, name, name_len)?
124+
}
125+
_ => return Ok(EmulateItemResult::NotSupported),
126+
};
127+
this.write_scalar(res, dest)?;
128+
}
75129
"pthread_setname_np" => {
76130
let [thread, name] =
77131
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;

tests/pass-dep/libc/prctl.rs

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
//@ignore-target: windows # No prctl on Windows
2+
//@ignore-target: apple # No prctl on macOS
3+
use std::ffi::CStr;
4+
use std::ffi::CString;
5+
use std::thread;
6+
7+
fn main() {
8+
let long_name = std::iter::once("test_named_thread_truncation")
9+
.chain(std::iter::repeat(" yada").take(100))
10+
.collect::<String>();
11+
12+
fn set_thread_name(name: &CStr) -> i32 {
13+
cfg_if::cfg_if! {
14+
if #[cfg(any(target_os = "linux", target_os = "illumos", target_os = "solaris", target_os = "freebsd"))] {
15+
unsafe { libc::pthread_setname_np(libc::pthread_self(), name.as_ptr().cast()) }
16+
} else {
17+
compile_error!("set_thread_name not supported for this OS")
18+
}
19+
}
20+
}
21+
22+
fn get_thread_name(name: &mut [u8]) -> i32 {
23+
cfg_if::cfg_if! {
24+
if #[cfg(any(
25+
target_os = "linux",
26+
target_os = "illumos",
27+
target_os = "solaris",
28+
target_os = "freebsd",
29+
))] {
30+
unsafe {
31+
libc::pthread_getname_np(libc::pthread_self(), name.as_mut_ptr().cast(), name.len())
32+
}
33+
} else {
34+
compile_error!("get_thread_name not supported for this OS")
35+
}
36+
}
37+
}
38+
39+
let result = thread::Builder::new().name(long_name.clone()).spawn(move || {
40+
// Rust remembers the full thread name itself.
41+
assert_eq!(thread::current().name(), Some(long_name.as_str()));
42+
43+
// But the system is limited -- make sure we successfully set a truncation.
44+
let mut buf = vec![0u8; long_name.len() + 1];
45+
assert_eq!(get_thread_name(&mut buf), 0);
46+
let cstr = CStr::from_bytes_until_nul(&buf).unwrap();
47+
assert!(cstr.to_bytes().len() >= 15, "name is too short: len={}", cstr.to_bytes().len()); // POSIX seems to promise at least 15 chars
48+
assert!(long_name.as_bytes().starts_with(cstr.to_bytes()));
49+
50+
// Also test directly calling pthread_setname to check its return value.
51+
assert_eq!(set_thread_name(&cstr), 0);
52+
// But with a too long name it should fail.
53+
assert_ne!(set_thread_name(&CString::new(long_name).unwrap()), 0);
54+
});
55+
result.unwrap().join().unwrap();
56+
}

0 commit comments

Comments
 (0)