Skip to content

Commit 4f34ee1

Browse files
committed
Added support for prctl handling thread names
1 parent ca43b55 commit 4f34ee1

File tree

10 files changed

+100
-15
lines changed

10 files changed

+100
-15
lines changed

ci/ci.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ case $HOST_TARGET in
154154
TEST_TARGET=i686-unknown-freebsd run_tests_minimal $BASIC $UNIX time hashmap random threadname pthread fs libc-pipe
155155
TEST_TARGET=x86_64-unknown-illumos run_tests_minimal $BASIC $UNIX time hashmap random thread sync available-parallelism tls libc-pipe
156156
TEST_TARGET=x86_64-pc-solaris run_tests_minimal $BASIC $UNIX time hashmap random thread sync available-parallelism tls libc-pipe
157-
TEST_TARGET=aarch64-linux-android run_tests_minimal $BASIC $UNIX time hashmap pthread --skip threadname
157+
TEST_TARGET=aarch64-linux-android run_tests_minimal $BASIC $UNIX time hashmap threadname pthread
158158
TEST_TARGET=wasm32-wasip2 run_tests_minimal $BASIC wasm
159159
TEST_TARGET=wasm32-unknown-unknown run_tests_minimal no_std empty_main wasm # this target doesn't really have std
160160
TEST_TARGET=thumbv7em-none-eabihf run_tests_minimal no_std

src/shims/unix/android/foreign_items.rs

+4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use rustc_span::Symbol;
22
use rustc_target::spec::abi::Abi;
33

4+
use crate::shims::unix::android::thread::prctl;
45
use crate::*;
56

67
pub fn is_dyn_sym(_name: &str) -> bool {
@@ -25,6 +26,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
2526
this.write_scalar(errno_place.to_ref(this).to_scalar(), dest)?;
2627
}
2728

29+
// Threading
30+
"prctl" => prctl(this, link_name, abi, args, dest)?,
31+
2832
_ => return interp_ok(EmulateItemResult::NotSupported),
2933
}
3034
interp_ok(EmulateItemResult::NeedsReturn)

src/shims/unix/android/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
pub mod foreign_items;
2+
pub mod thread;

src/shims/unix/android/thread.rs

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
use rustc_span::Symbol;
2+
use rustc_target::spec::abi::Abi;
3+
4+
use crate::helpers::check_min_arg_count;
5+
use crate::shims::unix::thread::EvalContextExt as _;
6+
use crate::*;
7+
8+
const TASK_COMM_LEN: usize = 16;
9+
10+
pub fn prctl<'tcx>(
11+
this: &mut MiriInterpCx<'tcx>,
12+
link_name: Symbol,
13+
abi: Abi,
14+
args: &[OpTy<'tcx>],
15+
dest: &MPlaceTy<'tcx>,
16+
) -> InterpResult<'tcx> {
17+
// We do not use `check_shim` here because `prctl` is variadic. The argument
18+
// count is checked bellow.
19+
this.check_abi_and_shim_symbol_clash(abi, Abi::C { unwind: false }, link_name)?;
20+
21+
// FIXME: Use PR_SET/GET_NAME constant when https://github.com/rust-lang/libc/pull/3941 lands.
22+
let pr_set_name = 15;
23+
let pr_get_name = 16;
24+
25+
let [op] = check_min_arg_count("prctl", args)?;
26+
let res = match this.read_scalar(op)?.to_i32()? {
27+
op if op == pr_set_name => {
28+
let [_, name] = check_min_arg_count("prctl(PR_SET_NAME, ...)", args)?;
29+
let name = this.read_scalar(name)?;
30+
let thread = this.pthread_self()?;
31+
let res =
32+
this.pthread_setname_np(thread, name, TASK_COMM_LEN, /* truncate */ true)?;
33+
assert!(res);
34+
Scalar::from_u32(0)
35+
}
36+
op if op == pr_get_name => {
37+
let [_, name] = check_min_arg_count("prctl(PR_GET_NAME, ...)", args)?;
38+
let name = this.read_scalar(name)?;
39+
let thread = this.pthread_self()?;
40+
let len = Scalar::from_target_usize(TASK_COMM_LEN as u64, this);
41+
let res = if this.pthread_getname_np(thread, name, len, /* truncate*/ false)? {
42+
Scalar::from_u32(0)
43+
} else {
44+
throw_ub_format!(
45+
"`prctl(PR_GET_NAME, name)` requires the `name` argument to be at least {TASK_COMM_LEN} bytes long"
46+
);
47+
};
48+
res
49+
}
50+
op => throw_unsup_format!("Miri does not support `prctl` syscall with op={}", op),
51+
};
52+
this.write_scalar(res, dest)?;
53+
interp_ok(())
54+
}

src/shims/unix/freebsd/foreign_items.rs

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
2929
this.read_scalar(thread)?,
3030
this.read_scalar(name)?,
3131
max_len,
32+
/* truncate */ false,
3233
)?;
3334
}
3435
"pthread_get_name_np" => {

src/shims/unix/linux/foreign_items.rs

+1
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
8484
this.read_scalar(thread)?,
8585
this.read_scalar(name)?,
8686
TASK_COMM_LEN,
87+
/* truncate */ false,
8788
)?;
8889
let res = if res { Scalar::from_u32(0) } else { this.eval_libc("ERANGE") };
8990
this.write_scalar(res, dest)?;

src/shims/unix/macos/foreign_items.rs

+1
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
181181
thread,
182182
this.read_scalar(name)?,
183183
this.eval_libc("MAXTHREADNAMESIZE").to_target_usize(this)?.try_into().unwrap(),
184+
/* truncate */ false,
184185
)? {
185186
Scalar::from_u32(0)
186187
} else {

src/shims/unix/solarish/foreign_items.rs

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
3030
this.read_scalar(thread)?,
3131
this.read_scalar(name)?,
3232
max_len,
33+
/* truncate */ false,
3334
)?;
3435
let res = if res { Scalar::from_u32(0) } else { this.eval_libc("ERANGE") };
3536
this.write_scalar(res, dest)?;

src/shims/unix/thread.rs

+9-3
Original file line numberDiff line numberDiff line change
@@ -64,23 +64,29 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
6464
}
6565

6666
/// Set the name of the specified thread. If the name including the null terminator
67-
/// is longer than `name_max_len`, then `false` is returned.
67+
/// is longer or equals to `name_max_len`, then if `truncate` is set the truncated name
68+
/// is used as the thread name, otherwise `false` is returned.
6869
fn pthread_setname_np(
6970
&mut self,
7071
thread: Scalar,
7172
name: Scalar,
7273
name_max_len: usize,
74+
truncate: bool,
7375
) -> InterpResult<'tcx, bool> {
7476
let this = self.eval_context_mut();
7577

7678
let thread = thread.to_int(this.libc_ty_layout("pthread_t").size)?;
7779
let thread = ThreadId::try_from(thread).unwrap();
7880
let name = name.to_pointer(this)?;
79-
let name = this.read_c_str(name)?.to_owned();
81+
let mut name = this.read_c_str(name)?.to_owned();
8082

8183
// Comparing with `>=` to account for null terminator.
8284
if name.len() >= name_max_len {
83-
return interp_ok(false);
85+
if truncate {
86+
name.drain(name_max_len - 1..);
87+
} else {
88+
return interp_ok(false);
89+
}
8490
}
8591

8692
this.set_thread_name(thread, name);

tests/pass-dep/libc/pthread-threadname.rs renamed to tests/pass-dep/libc/threadname.rs

+27-11
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
//@ignore-target: windows # No pthreads on Windows
1+
//@ignore-target: windows # No pthreads or prctl on Windows
22
use std::ffi::{CStr, CString};
33
use std::thread;
44

55
const MAX_THREAD_NAME_LEN: usize = {
66
cfg_if::cfg_if! {
7-
if #[cfg(any(target_os = "linux"))] {
7+
if #[cfg(any(target_os = "linux", target_os = "android"))] {
88
16
99
} else if #[cfg(any(target_os = "illumos", target_os = "solaris"))] {
1010
32
@@ -36,6 +36,10 @@ fn main() {
3636
0
3737
} else if #[cfg(target_os = "macos")] {
3838
unsafe { libc::pthread_setname_np(name.as_ptr().cast()) }
39+
} else if #[cfg(target_os = "android")] {
40+
// FIXME: Use PR_SET_NAME constant when https://github.com/rust-lang/libc/pull/3941 lands.
41+
const PR_SET_NAME: i32 = 15;
42+
unsafe { libc::prctl(PR_SET_NAME, name.as_ptr().cast::<libc::c_char>()) }
3943
} else {
4044
compile_error!("set_thread_name not supported for this OS")
4145
}
@@ -59,6 +63,10 @@ fn main() {
5963
libc::pthread_get_name_np(libc::pthread_self(), name.as_mut_ptr().cast(), name.len())
6064
};
6165
0
66+
} else if #[cfg(target_os = "android")] {
67+
// FIXME: Use PR_GET_NAME constant when https://github.com/rust-lang/libc/pull/3941 lands.
68+
const PR_GET_NAME: i32 = 16;
69+
unsafe { libc::prctl(PR_GET_NAME, name.as_mut_ptr().cast::<libc::c_char>()) }
6270
} else {
6371
compile_error!("get_thread_name not supported for this OS")
6472
}
@@ -98,30 +106,32 @@ fn main() {
98106
}
99107

100108
// Test what happens when the buffer is too short even for the short name.
101-
let res = get_thread_name(&mut buf[..4]);
102109
cfg_if::cfg_if! {
103110
if #[cfg(any(target_os = "freebsd", target_os = "macos"))] {
104111
// On macOS and FreeBSD it's not an error for the buffer to be
105112
// too short for the thread name -- they truncate instead.
113+
let res = get_thread_name(&mut buf[..4]);
106114
assert_eq!(res, 0);
107115
let cstr = CStr::from_bytes_until_nul(&buf).unwrap();
108116
assert_eq!(cstr.to_bytes_with_nul().len(), 4);
109117
assert!(short_name.as_bytes().starts_with(cstr.to_bytes()));
110-
} else {
118+
} else if #[cfg(not(target_os = "android"))] {
111119
// The rest should give an error.
120+
let res = get_thread_name(&mut buf[..4]);
112121
assert_eq!(res, libc::ERANGE);
113122
}
114123
}
115124

116125
// Test zero-sized buffer.
117-
let res = get_thread_name(&mut []);
118126
cfg_if::cfg_if! {
119127
if #[cfg(any(target_os = "freebsd", target_os = "macos"))] {
120128
// On macOS and FreeBSD it's not an error for the buffer to be
121129
// too short for the thread name -- even with size 0.
130+
let res = get_thread_name(&mut []);
122131
assert_eq!(res, 0);
123-
} else {
132+
} else if #[cfg(not(target_os = "android"))] {
124133
// The rest should give an error.
134+
let res = get_thread_name(&mut []);
125135
assert_eq!(res, libc::ERANGE);
126136
}
127137
}
@@ -140,10 +150,15 @@ fn main() {
140150
// Names of all size are supported.
141151
assert!(cstr.to_bytes_with_nul().len() <= MAX_THREAD_NAME_LEN);
142152
assert_eq!(res, 0);
143-
} else if #[cfg(target_os = "macos")] {
144-
// Name is too long.
153+
} else if #[cfg(target_os = "android")] {
154+
// Names are truncated by the Linux kernel.
145155
assert!(cstr.to_bytes_with_nul().len() > MAX_THREAD_NAME_LEN);
146-
assert_eq!(res, libc::ENAMETOOLONG);
156+
assert_eq!(res, 0);
157+
158+
let mut buf = vec![0u8; MAX_THREAD_NAME_LEN];
159+
assert_eq!(get_thread_name(&mut buf), 0);
160+
let cstr = CStr::from_bytes_until_nul(&buf).unwrap();
161+
assert_eq!(cstr.to_bytes(), &long_name.as_bytes()[..(MAX_THREAD_NAME_LEN - 1)]);
147162
} else {
148163
// Name is too long.
149164
assert!(cstr.to_bytes_with_nul().len() > MAX_THREAD_NAME_LEN);
@@ -164,16 +179,17 @@ fn main() {
164179
assert_eq!(cstr.to_bytes(), truncated_name.as_bytes());
165180

166181
// Test what happens when our buffer is just one byte too small.
167-
let res = get_thread_name(&mut buf[..truncated_name.len()]);
168182
cfg_if::cfg_if! {
169183
if #[cfg(any(target_os = "freebsd", target_os = "macos"))] {
170184
// On macOS and FreeBSD it's not an error for the buffer to be
171185
// too short for the thread name -- they truncate instead.
186+
let res = get_thread_name(&mut buf[..truncated_name.len()]);
172187
assert_eq!(res, 0);
173188
let cstr = CStr::from_bytes_until_nul(&buf).unwrap();
174189
assert_eq!(cstr.to_bytes(), &truncated_name.as_bytes()[..(truncated_name.len() - 1)]);
175-
} else {
190+
} else if #[cfg(not(target_os = "android"))] {
176191
// The rest should give an error.
192+
let res = get_thread_name(&mut buf[..truncated_name.len()]);
177193
assert_eq!(res, libc::ERANGE);
178194
}
179195
}

0 commit comments

Comments
 (0)