Skip to content

Commit

Permalink
Added support for prctl handling thread names
Browse files Browse the repository at this point in the history
  • Loading branch information
YohDeadfall committed Sep 20, 2024
1 parent 894c264 commit 4dc0032
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 0 deletions.
54 changes: 54 additions & 0 deletions src/shims/unix/linux/foreign_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,60 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}

// Threading
"prctl" => {
// We do not use `check_shim` here because `syscall` is variadic. The argument
// count is checked bellow.
this.check_abi_and_shim_symbol_clash(abi, Abi::C { unwind: false }, link_name)?;

if args.is_empty() {
throw_ub_format!(
"incorrect number of arguments for prctl: got 0, expected at least 1"
);
}

// Since only two prctl calls are currently handled, and both expext just two arguments,
// it makes sense to handle validation here, outside of the match block below.
if args.len() < 2 {
throw_ub_format!(
"incorrect number of arguments for `PR_SET_NAME` prctl: got {}, expected at least 2",
args.len()
);
}

for i in 0..args.len() {
let val = this.read_scalar(&args[i])?.to_i32()?;
if val != 0 {
throw_ub_format!(
"incorrect value of {}th argument for 'PR_SET_NAME' prctl: got {}, expected 0L",
i,
val
);
}
}

let op = this.read_scalar(&args[0])?.to_i32()?;
let pr_set_name = this.eval_libc_i32("PR_SET_NAME");
let pr_get_name = this.eval_libc_i32("PR_GET_NAME");

let res = match op {
op if op == pr_set_name => {
let tid = this.linux_gettid()?;
let name = this.read_scalar(&args[1])?;
let name_len = 16;

this.pthread_setname_np(tid, name, name_len)?
}
op if op == pr_get_name => {
let tid = this.linux_gettid()?;
let name = this.read_scalar(&args[1])?;
let name_len = Scalar::from_target_usize(16, this);

this.pthread_getname_np(tid, name, name_len)?
}
_ => return Ok(EmulateItemResult::NotSupported),
};
this.write_scalar(res, dest)?;
}
"pthread_setname_np" => {
let [thread, name] =
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
Expand Down
56 changes: 56 additions & 0 deletions tests/pass-dep/libc/prctl.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
//@ignore-target: windows # No pthreads on Windows

Check failure on line 1 in tests/pass-dep/libc/prctl.rs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, x86_64-unknown-linux-gnu)

test got exit status: 1

compilation failed, but was expected to succeed

Check failure on line 1 in tests/pass-dep/libc/prctl.rs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, x86_64-unknown-linux-gnu)

test generated output

you likely need to bless the tests with `./miri test --bless`

Check failure on line 1 in tests/pass-dep/libc/prctl.rs

View workflow job for this annotation

GitHub Actions / build (macos-14, aarch64-apple-darwin)

test got exit status: 1

compilation failed, but was expected to succeed

Check failure on line 1 in tests/pass-dep/libc/prctl.rs

View workflow job for this annotation

GitHub Actions / build (macos-14, aarch64-apple-darwin)

test generated output

you likely need to bless the tests with `./miri test --bless`
use std::ffi::CStr;
#[cfg(not(target_os = "freebsd"))]
use std::ffi::CString;
use std::thread;

fn main() {
let long_name = std::iter::once("test_named_thread_truncation")
.chain(std::iter::repeat(" yada").take(100))
.collect::<String>();

fn set_thread_name(name: &CStr) -> i32 {
cfg_if::cfg_if! {
if #[cfg(any(target_os = "linux", target_os = "illumos", target_os = "solaris", target_os = "freebsd"))] {
unsafe { libc::pthread_setname_np(libc::pthread_self(), name.as_ptr().cast()) }
} else {
compile_error!("set_thread_name not supported for this OS")
}
}
}

fn get_thread_name(name: &mut [u8]) -> i32 {
cfg_if::cfg_if! {
if #[cfg(any(
target_os = "linux",
target_os = "illumos",
target_os = "solaris",
target_os = "freebsd",
))] {
unsafe {
libc::pthread_getname_np(libc::pthread_self(), name.as_mut_ptr().cast(), name.len())
}
} else {
compile_error!("get_thread_name not supported for this OS")
}
}
}

let result = thread::Builder::new().name(long_name.clone()).spawn(move || {
// Rust remembers the full thread name itself.
assert_eq!(thread::current().name(), Some(long_name.as_str()));

// But the system is limited -- make sure we successfully set a truncation.
let mut buf = vec![0u8; long_name.len() + 1];
assert_eq!(get_thread_name(&mut buf), 0);
let cstr = CStr::from_bytes_until_nul(&buf).unwrap();
assert!(cstr.to_bytes().len() >= 15, "name is too short: len={}", cstr.to_bytes().len()); // POSIX seems to promise at least 15 chars
assert!(long_name.as_bytes().starts_with(cstr.to_bytes()));

// Also test directly calling pthread_setname to check its return value.
assert_eq!(set_thread_name(&cstr), 0);
// But with a too long name it should fail.
assert_ne!(set_thread_name(&CString::new(long_name).unwrap()), 0);
});
result.unwrap().join().unwrap();
}

0 comments on commit 4dc0032

Please sign in to comment.