mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-25 16:24:46 +00:00
Fixed pthread get/set name for macOS
This commit is contained in:
parent
5cdee0781f
commit
56c0612003
@ -39,6 +39,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
this.read_scalar(thread)?,
|
||||
this.read_scalar(name)?,
|
||||
this.read_scalar(len)?,
|
||||
false,
|
||||
)?;
|
||||
}
|
||||
|
||||
|
@ -84,6 +84,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
this.read_scalar(name)?,
|
||||
TASK_COMM_LEN,
|
||||
)?;
|
||||
let res = if res { Scalar::from_u32(0) } else { this.eval_libc("ERANGE") };
|
||||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
"pthread_getname_np" => {
|
||||
@ -93,14 +94,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// In case of glibc, the length of the output buffer must
|
||||
// be not shorter than TASK_COMM_LEN.
|
||||
let len = this.read_scalar(len)?;
|
||||
let res = if len.to_target_usize(this)? < TASK_COMM_LEN as u64 {
|
||||
this.eval_libc("ERANGE")
|
||||
} else {
|
||||
this.pthread_getname_np(
|
||||
let res = if len.to_target_usize(this)? >= TASK_COMM_LEN as u64
|
||||
&& this.pthread_getname_np(
|
||||
this.read_scalar(thread)?,
|
||||
this.read_scalar(name)?,
|
||||
len,
|
||||
)?
|
||||
/* truncate*/ false,
|
||||
)? {
|
||||
Scalar::from_u32(0)
|
||||
} else {
|
||||
this.eval_libc("ERANGE")
|
||||
};
|
||||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
|
@ -164,13 +164,28 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// Threading
|
||||
"pthread_setname_np" => {
|
||||
let [name] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
|
||||
|
||||
// The real implementation has logic in two places:
|
||||
// * in userland at https://github.com/apple-oss-distributions/libpthread/blob/c032e0b076700a0a47db75528a282b8d3a06531a/src/pthread.c#L1178-L1200,
|
||||
// * in kernel at https://github.com/apple-oss-distributions/xnu/blob/8d741a5de7ff4191bf97d57b9f54c2f6d4a15585/bsd/kern/proc_info.c#L3218-L3227.
|
||||
//
|
||||
// The function in libc calls the kernel to validate
|
||||
// the security policies and the input. If all of the requirements
|
||||
// are met, then the name is set and 0 is returned. Otherwise, if
|
||||
// the specified name is lomnger than MAXTHREADNAMESIZE, then
|
||||
// ENAMETOOLONG is returned.
|
||||
//
|
||||
// FIXME: the real implementation maybe returns ESRCH if the thread ID is invalid.
|
||||
let thread = this.pthread_self()?;
|
||||
let max_len = this.eval_libc("MAXTHREADNAMESIZE").to_target_usize(this)?;
|
||||
let res = this.pthread_setname_np(
|
||||
let res = if this.pthread_setname_np(
|
||||
thread,
|
||||
this.read_scalar(name)?,
|
||||
max_len.try_into().unwrap(),
|
||||
)?;
|
||||
this.eval_libc("MAXTHREADNAMESIZE").to_target_usize(this)?.try_into().unwrap(),
|
||||
)? {
|
||||
Scalar::from_u32(0)
|
||||
} else {
|
||||
this.eval_libc("ENAMETOOLONG")
|
||||
};
|
||||
// Contrary to the manpage, `pthread_setname_np` on macOS still
|
||||
// returns an integer indicating success.
|
||||
this.write_scalar(res, dest)?;
|
||||
@ -178,10 +193,23 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
"pthread_getname_np" => {
|
||||
let [thread, name, len] =
|
||||
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
|
||||
let res = this.pthread_getname_np(
|
||||
|
||||
// The function's behavior isn't portable between platforms.
|
||||
// In case of macOS, a truncated name (due to a too small buffer)
|
||||
// does not lead to an error.
|
||||
//
|
||||
// For details, see the implementation at
|
||||
// https://github.com/apple-oss-distributions/libpthread/blob/c032e0b076700a0a47db75528a282b8d3a06531a/src/pthread.c#L1160-L1175.
|
||||
// The key part is the strlcpy, which truncates the resulting value,
|
||||
// but always null terminates (except for zero sized buffers).
|
||||
//
|
||||
// FIXME: the real implementation returns ESRCH if the thread ID is invalid.
|
||||
let res = Scalar::from_u32(0);
|
||||
this.pthread_getname_np(
|
||||
this.read_scalar(thread)?,
|
||||
this.read_scalar(name)?,
|
||||
this.read_scalar(len)?,
|
||||
/* truncate */ true,
|
||||
)?;
|
||||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
|
@ -31,16 +31,20 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
this.read_scalar(name)?,
|
||||
max_len,
|
||||
)?;
|
||||
let res = if res { Scalar::from_u32(0) } else { this.eval_libc("ERANGE") };
|
||||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
"pthread_getname_np" => {
|
||||
let [thread, name, len] =
|
||||
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
|
||||
// https://github.com/illumos/illumos-gate/blob/c56822be04b6c157c8b6f2281e47214c3b86f657/usr/src/lib/libc/port/threads/thr.c#L2449-L2480
|
||||
let res = this.pthread_getname_np(
|
||||
this.read_scalar(thread)?,
|
||||
this.read_scalar(name)?,
|
||||
this.read_scalar(len)?,
|
||||
/* truncate */ false,
|
||||
)?;
|
||||
let res = if res { Scalar::from_u32(0) } else { this.eval_libc("ERANGE") };
|
||||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
|
||||
|
@ -63,38 +63,41 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
interp_ok(Scalar::from_uint(thread_id.to_u32(), this.libc_ty_layout("pthread_t").size))
|
||||
}
|
||||
|
||||
/// Set the name of the current thread. `max_name_len` is the maximal length of the name
|
||||
/// including the null terminator.
|
||||
/// Set the name of the specified thread. If the name including the null terminator
|
||||
/// is longer than `name_max_len`, then `false` is returned.
|
||||
fn pthread_setname_np(
|
||||
&mut self,
|
||||
thread: Scalar,
|
||||
name: Scalar,
|
||||
max_name_len: usize,
|
||||
) -> InterpResult<'tcx, Scalar> {
|
||||
name_max_len: usize,
|
||||
) -> InterpResult<'tcx, bool> {
|
||||
let this = self.eval_context_mut();
|
||||
|
||||
let thread = thread.to_int(this.libc_ty_layout("pthread_t").size)?;
|
||||
let thread = ThreadId::try_from(thread).unwrap();
|
||||
let name = name.to_pointer(this)?;
|
||||
|
||||
let name = this.read_c_str(name)?.to_owned();
|
||||
|
||||
// Comparing with `>=` to account for null terminator.
|
||||
if name.len() >= max_name_len {
|
||||
return interp_ok(this.eval_libc("ERANGE"));
|
||||
if name.len() >= name_max_len {
|
||||
return interp_ok(false);
|
||||
}
|
||||
|
||||
this.set_thread_name(thread, name);
|
||||
|
||||
interp_ok(Scalar::from_u32(0))
|
||||
interp_ok(true)
|
||||
}
|
||||
|
||||
/// Get the name of the specified thread. If the thread name doesn't fit
|
||||
/// the buffer, then if `truncate` is set the truncated name is written out,
|
||||
/// otherwise `false` is returned.
|
||||
fn pthread_getname_np(
|
||||
&mut self,
|
||||
thread: Scalar,
|
||||
name_out: Scalar,
|
||||
len: Scalar,
|
||||
) -> InterpResult<'tcx, Scalar> {
|
||||
truncate: bool,
|
||||
) -> InterpResult<'tcx, bool> {
|
||||
let this = self.eval_context_mut();
|
||||
|
||||
let thread = thread.to_int(this.libc_ty_layout("pthread_t").size)?;
|
||||
@ -104,9 +107,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
|
||||
// FIXME: we should use the program name if the thread name is not set
|
||||
let name = this.get_thread_name(thread).unwrap_or(b"<unnamed>").to_owned();
|
||||
let (success, _written) = this.write_c_str(&name, name_out, len)?;
|
||||
let name = match truncate {
|
||||
true => &name[..name.len().min(len.try_into().unwrap_or(usize::MAX).saturating_sub(1))],
|
||||
false => &name,
|
||||
};
|
||||
|
||||
interp_ok(if success { Scalar::from_u32(0) } else { this.eval_libc("ERANGE") })
|
||||
let (success, _written) = this.write_c_str(name, name_out, len)?;
|
||||
|
||||
interp_ok(success)
|
||||
}
|
||||
|
||||
fn sched_yield(&mut self) -> InterpResult<'tcx, ()> {
|
||||
|
@ -74,6 +74,26 @@ fn main() {
|
||||
// large enough for the thread name.
|
||||
#[cfg(target_os = "linux")]
|
||||
assert_eq!(get_thread_name(&mut buf[..15]), libc::ERANGE);
|
||||
|
||||
// Solaris compatible implementations return an error,
|
||||
// if the buffer is shorter than the thread name.
|
||||
#[cfg(any(target_os = "illumos", target_os = "solaris"))]
|
||||
assert_eq!(get_thread_name(&mut buf[..4]), libc::ERANGE);
|
||||
|
||||
// For libc implementation for macOS it's not an error
|
||||
// for a buffer being too short for the thread name.
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
// Ensure that a zero sized buffer returns no error.
|
||||
assert_eq!(get_thread_name(&mut buf[..0]), 0);
|
||||
|
||||
// Ensure that a shorter tnan required buffer still returns no error,
|
||||
// and gives a prefix of the thread name.
|
||||
assert_eq!(get_thread_name(&mut buf[..4]), 0);
|
||||
let cstr = CStr::from_bytes_until_nul(&buf).unwrap();
|
||||
assert_eq!(cstr.to_bytes_with_nul().len(), 4);
|
||||
assert!(short_name.as_bytes().starts_with(cstr.to_bytes()));
|
||||
}
|
||||
})
|
||||
.unwrap()
|
||||
.join()
|
||||
@ -105,8 +125,12 @@ fn main() {
|
||||
|
||||
// But with a too long name it should fail (except on FreeBSD where the
|
||||
// function has no return, hence cannot indicate failure).
|
||||
#[cfg(not(target_os = "freebsd"))]
|
||||
assert_ne!(set_thread_name(&CString::new(long_name).unwrap()), 0);
|
||||
// On macOS, the error code is different.
|
||||
#[cfg(not(any(target_os = "freebsd", target_os = "macos")))]
|
||||
assert_eq!(set_thread_name(&CString::new(long_name).unwrap()), libc::ERANGE);
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
assert_eq!(set_thread_name(&CString::new(long_name).unwrap()), libc::ENAMETOOLONG);
|
||||
})
|
||||
.unwrap()
|
||||
.join()
|
||||
|
Loading…
Reference in New Issue
Block a user