Get/set thread name shims return errors for invalid handles
This commit is contained in:
parent
b17cf41306
commit
1254d8e75e
@ -1,7 +1,6 @@
|
|||||||
//! Implements threads.
|
//! Implements threads.
|
||||||
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::num::TryFromIntError;
|
|
||||||
use std::sync::atomic::Ordering::Relaxed;
|
use std::sync::atomic::Ordering::Relaxed;
|
||||||
use std::task::Poll;
|
use std::task::Poll;
|
||||||
use std::time::{Duration, SystemTime};
|
use std::time::{Duration, SystemTime};
|
||||||
@ -127,26 +126,6 @@ fn index(self) -> usize {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<u64> for ThreadId {
|
|
||||||
type Error = TryFromIntError;
|
|
||||||
fn try_from(id: u64) -> Result<Self, Self::Error> {
|
|
||||||
u32::try_from(id).map(Self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<i128> for ThreadId {
|
|
||||||
type Error = TryFromIntError;
|
|
||||||
fn try_from(id: i128) -> Result<Self, Self::Error> {
|
|
||||||
u32::try_from(id).map(Self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<u32> for ThreadId {
|
|
||||||
fn from(id: u32) -> Self {
|
|
||||||
Self(id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ThreadId> for u64 {
|
impl From<ThreadId> for u64 {
|
||||||
fn from(t: ThreadId) -> Self {
|
fn from(t: ThreadId) -> Self {
|
||||||
t.0.into()
|
t.0.into()
|
||||||
@ -448,6 +427,10 @@ pub enum TimeoutAnchor {
|
|||||||
Absolute,
|
Absolute,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An error signaling that the requested thread doesn't exist.
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct ThreadNotFound;
|
||||||
|
|
||||||
/// A set of threads.
|
/// A set of threads.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ThreadManager<'tcx> {
|
pub struct ThreadManager<'tcx> {
|
||||||
@ -509,6 +492,16 @@ pub(crate) fn init(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn thread_id_try_from(&self, id: impl TryInto<u32>) -> Result<ThreadId, ThreadNotFound> {
|
||||||
|
if let Ok(id) = id.try_into()
|
||||||
|
&& usize::try_from(id).is_ok_and(|id| id < self.threads.len())
|
||||||
|
{
|
||||||
|
Ok(ThreadId(id))
|
||||||
|
} else {
|
||||||
|
Err(ThreadNotFound)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Check if we have an allocation for the given thread local static for the
|
/// Check if we have an allocation for the given thread local static for the
|
||||||
/// active thread.
|
/// active thread.
|
||||||
fn get_thread_local_alloc_id(&self, def_id: DefId) -> Option<StrictPointer> {
|
fn get_thread_local_alloc_id(&self, def_id: DefId) -> Option<StrictPointer> {
|
||||||
@ -534,6 +527,7 @@ pub fn active_thread_stack_mut(
|
|||||||
) -> &mut Vec<Frame<'tcx, Provenance, FrameExtra<'tcx>>> {
|
) -> &mut Vec<Frame<'tcx, Provenance, FrameExtra<'tcx>>> {
|
||||||
&mut self.threads[self.active_thread].stack
|
&mut self.threads[self.active_thread].stack
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn all_stacks(
|
pub fn all_stacks(
|
||||||
&self,
|
&self,
|
||||||
) -> impl Iterator<Item = (ThreadId, &[Frame<'tcx, Provenance, FrameExtra<'tcx>>])> {
|
) -> impl Iterator<Item = (ThreadId, &[Frame<'tcx, Provenance, FrameExtra<'tcx>>])> {
|
||||||
@ -868,6 +862,11 @@ fn run_on_stack_empty(&mut self) -> InterpResult<'tcx, Poll<()>> {
|
|||||||
// Public interface to thread management.
|
// Public interface to thread management.
|
||||||
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
|
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
|
||||||
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||||
|
#[inline]
|
||||||
|
fn thread_id_try_from(&self, id: impl TryInto<u32>) -> Result<ThreadId, ThreadNotFound> {
|
||||||
|
self.eval_context_ref().machine.threads.thread_id_try_from(id)
|
||||||
|
}
|
||||||
|
|
||||||
/// Get a thread-specific allocation id for the given thread-local static.
|
/// Get a thread-specific allocation id for the given thread-local static.
|
||||||
/// If needed, allocate a new one.
|
/// If needed, allocate a new one.
|
||||||
fn get_or_create_thread_local_alloc(
|
fn get_or_create_thread_local_alloc(
|
||||||
@ -1160,8 +1159,7 @@ fn active_thread_stack_mut<'a>(
|
|||||||
/// Set the name of the current thread. The buffer must not include the null terminator.
|
/// Set the name of the current thread. The buffer must not include the null terminator.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn set_thread_name(&mut self, thread: ThreadId, new_thread_name: Vec<u8>) {
|
fn set_thread_name(&mut self, thread: ThreadId, new_thread_name: Vec<u8>) {
|
||||||
let this = self.eval_context_mut();
|
self.eval_context_mut().machine.threads.set_thread_name(thread, new_thread_name);
|
||||||
this.machine.threads.set_thread_name(thread, new_thread_name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
use rustc_span::Symbol;
|
use rustc_span::Symbol;
|
||||||
|
|
||||||
use crate::helpers::check_min_arg_count;
|
use crate::helpers::check_min_arg_count;
|
||||||
use crate::shims::unix::thread::EvalContextExt as _;
|
use crate::shims::unix::thread::{EvalContextExt as _, ThreadNameResult};
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
const TASK_COMM_LEN: usize = 16;
|
const TASK_COMM_LEN: usize = 16;
|
||||||
@ -32,7 +32,7 @@ pub fn prctl<'tcx>(
|
|||||||
// https://www.man7.org/linux/man-pages/man2/PR_SET_NAME.2const.html
|
// https://www.man7.org/linux/man-pages/man2/PR_SET_NAME.2const.html
|
||||||
let res =
|
let res =
|
||||||
this.pthread_setname_np(thread, name, TASK_COMM_LEN, /* truncate */ true)?;
|
this.pthread_setname_np(thread, name, TASK_COMM_LEN, /* truncate */ true)?;
|
||||||
assert!(res);
|
assert_eq!(res, ThreadNameResult::Ok);
|
||||||
Scalar::from_u32(0)
|
Scalar::from_u32(0)
|
||||||
}
|
}
|
||||||
op if op == pr_get_name => {
|
op if op == pr_get_name => {
|
||||||
@ -46,7 +46,7 @@ pub fn prctl<'tcx>(
|
|||||||
CheckInAllocMsg::MemoryAccessTest,
|
CheckInAllocMsg::MemoryAccessTest,
|
||||||
)?;
|
)?;
|
||||||
let res = this.pthread_getname_np(thread, name, len, /* truncate*/ false)?;
|
let res = this.pthread_getname_np(thread, name, len, /* truncate*/ false)?;
|
||||||
assert!(res);
|
assert_eq!(res, ThreadNameResult::Ok);
|
||||||
Scalar::from_u32(0)
|
Scalar::from_u32(0)
|
||||||
}
|
}
|
||||||
op => throw_unsup_format!("Miri does not support `prctl` syscall with op={}", op),
|
op => throw_unsup_format!("Miri does not support `prctl` syscall with op={}", op),
|
||||||
|
@ -603,13 +603,13 @@ fn emulate_foreign_item_inner(
|
|||||||
}
|
}
|
||||||
"pthread_join" => {
|
"pthread_join" => {
|
||||||
let [thread, retval] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
|
let [thread, retval] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
|
||||||
this.pthread_join(thread, retval)?;
|
let res = this.pthread_join(thread, retval)?;
|
||||||
this.write_null(dest)?;
|
this.write_scalar(res, dest)?;
|
||||||
}
|
}
|
||||||
"pthread_detach" => {
|
"pthread_detach" => {
|
||||||
let [thread] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
|
let [thread] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
|
||||||
this.pthread_detach(thread)?;
|
let res = this.pthread_detach(thread)?;
|
||||||
this.write_null(dest)?;
|
this.write_scalar(res, dest)?;
|
||||||
}
|
}
|
||||||
"pthread_self" => {
|
"pthread_self" => {
|
||||||
let [] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
|
let [] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
|
||||||
|
@ -81,13 +81,17 @@ fn emulate_foreign_item_inner(
|
|||||||
"pthread_setname_np" => {
|
"pthread_setname_np" => {
|
||||||
let [thread, name] =
|
let [thread, name] =
|
||||||
this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
|
this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
|
||||||
let res = this.pthread_setname_np(
|
let res = match this.pthread_setname_np(
|
||||||
this.read_scalar(thread)?,
|
this.read_scalar(thread)?,
|
||||||
this.read_scalar(name)?,
|
this.read_scalar(name)?,
|
||||||
TASK_COMM_LEN,
|
TASK_COMM_LEN,
|
||||||
/* truncate */ false,
|
/* truncate */ false,
|
||||||
)?;
|
)? {
|
||||||
let res = if res { Scalar::from_u32(0) } else { this.eval_libc("ERANGE") };
|
ThreadNameResult::Ok => Scalar::from_u32(0),
|
||||||
|
ThreadNameResult::NameTooLong => this.eval_libc("ERANGE"),
|
||||||
|
// Act like we faild to open `/proc/self/task/$tid/comm`.
|
||||||
|
ThreadNameResult::ThreadNotFound => this.eval_libc("ENOENT"),
|
||||||
|
};
|
||||||
this.write_scalar(res, dest)?;
|
this.write_scalar(res, dest)?;
|
||||||
}
|
}
|
||||||
"pthread_getname_np" => {
|
"pthread_getname_np" => {
|
||||||
@ -97,14 +101,18 @@ fn emulate_foreign_item_inner(
|
|||||||
// In case of glibc, the length of the output buffer must
|
// In case of glibc, the length of the output buffer must
|
||||||
// be not shorter than TASK_COMM_LEN.
|
// be not shorter than TASK_COMM_LEN.
|
||||||
let len = this.read_scalar(len)?;
|
let len = this.read_scalar(len)?;
|
||||||
let res = if len.to_target_usize(this)? >= TASK_COMM_LEN as u64
|
let res = if len.to_target_usize(this)? >= TASK_COMM_LEN as u64 {
|
||||||
&& this.pthread_getname_np(
|
match this.pthread_getname_np(
|
||||||
this.read_scalar(thread)?,
|
this.read_scalar(thread)?,
|
||||||
this.read_scalar(name)?,
|
this.read_scalar(name)?,
|
||||||
len,
|
len,
|
||||||
/* truncate*/ false,
|
/* truncate*/ false,
|
||||||
)? {
|
)? {
|
||||||
Scalar::from_u32(0)
|
ThreadNameResult::Ok => Scalar::from_u32(0),
|
||||||
|
ThreadNameResult::NameTooLong => unreachable!(),
|
||||||
|
// Act like we faild to open `/proc/self/task/$tid/comm`.
|
||||||
|
ThreadNameResult::ThreadNotFound => this.eval_libc("ENOENT"),
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
this.eval_libc("ERANGE")
|
this.eval_libc("ERANGE")
|
||||||
};
|
};
|
||||||
|
@ -181,18 +181,16 @@ fn emulate_foreign_item_inner(
|
|||||||
// are met, then the name is set and 0 is returned. Otherwise, if
|
// are met, then the name is set and 0 is returned. Otherwise, if
|
||||||
// the specified name is lomnger than MAXTHREADNAMESIZE, then
|
// the specified name is lomnger than MAXTHREADNAMESIZE, then
|
||||||
// ENAMETOOLONG is returned.
|
// ENAMETOOLONG is returned.
|
||||||
//
|
|
||||||
// FIXME: the real implementation maybe returns ESRCH if the thread ID is invalid.
|
|
||||||
let thread = this.pthread_self()?;
|
let thread = this.pthread_self()?;
|
||||||
let res = if this.pthread_setname_np(
|
let res = match this.pthread_setname_np(
|
||||||
thread,
|
thread,
|
||||||
this.read_scalar(name)?,
|
this.read_scalar(name)?,
|
||||||
this.eval_libc("MAXTHREADNAMESIZE").to_target_usize(this)?.try_into().unwrap(),
|
this.eval_libc("MAXTHREADNAMESIZE").to_target_usize(this)?.try_into().unwrap(),
|
||||||
/* truncate */ false,
|
/* truncate */ false,
|
||||||
)? {
|
)? {
|
||||||
Scalar::from_u32(0)
|
ThreadNameResult::Ok => Scalar::from_u32(0),
|
||||||
} else {
|
ThreadNameResult::NameTooLong => this.eval_libc("ENAMETOOLONG"),
|
||||||
this.eval_libc("ENAMETOOLONG")
|
ThreadNameResult::ThreadNotFound => unreachable!(),
|
||||||
};
|
};
|
||||||
// Contrary to the manpage, `pthread_setname_np` on macOS still
|
// Contrary to the manpage, `pthread_setname_np` on macOS still
|
||||||
// returns an integer indicating success.
|
// returns an integer indicating success.
|
||||||
@ -210,15 +208,17 @@ fn emulate_foreign_item_inner(
|
|||||||
// https://github.com/apple-oss-distributions/libpthread/blob/c032e0b076700a0a47db75528a282b8d3a06531a/src/pthread.c#L1160-L1175.
|
// 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,
|
// The key part is the strlcpy, which truncates the resulting value,
|
||||||
// but always null terminates (except for zero sized buffers).
|
// but always null terminates (except for zero sized buffers).
|
||||||
//
|
let res = match this.pthread_getname_np(
|
||||||
// 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(thread)?,
|
||||||
this.read_scalar(name)?,
|
this.read_scalar(name)?,
|
||||||
this.read_scalar(len)?,
|
this.read_scalar(len)?,
|
||||||
/* truncate */ true,
|
/* truncate */ true,
|
||||||
)?;
|
)? {
|
||||||
|
ThreadNameResult::Ok => Scalar::from_u32(0),
|
||||||
|
// `NameTooLong` is possible when the buffer is zero sized,
|
||||||
|
ThreadNameResult::NameTooLong => Scalar::from_u32(0),
|
||||||
|
ThreadNameResult::ThreadNotFound => this.eval_libc("ESRCH"),
|
||||||
|
};
|
||||||
this.write_scalar(res, dest)?;
|
this.write_scalar(res, dest)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
pub use self::linux::epoll::EpollInterestTable;
|
pub use self::linux::epoll::EpollInterestTable;
|
||||||
pub use self::mem::EvalContextExt as _;
|
pub use self::mem::EvalContextExt as _;
|
||||||
pub use self::sync::EvalContextExt as _;
|
pub use self::sync::EvalContextExt as _;
|
||||||
pub use self::thread::EvalContextExt as _;
|
pub use self::thread::{EvalContextExt as _, ThreadNameResult};
|
||||||
pub use self::unnamed_socket::EvalContextExt as _;
|
pub use self::unnamed_socket::EvalContextExt as _;
|
||||||
|
|
||||||
// Make up some constants.
|
// Make up some constants.
|
||||||
|
@ -26,26 +26,33 @@ fn emulate_foreign_item_inner(
|
|||||||
// THREAD_NAME_MAX allows a thread name of 31+1 length
|
// THREAD_NAME_MAX allows a thread name of 31+1 length
|
||||||
// https://github.com/illumos/illumos-gate/blob/7671517e13b8123748eda4ef1ee165c6d9dba7fe/usr/src/uts/common/sys/thread.h#L613
|
// https://github.com/illumos/illumos-gate/blob/7671517e13b8123748eda4ef1ee165c6d9dba7fe/usr/src/uts/common/sys/thread.h#L613
|
||||||
let max_len = 32;
|
let max_len = 32;
|
||||||
let res = this.pthread_setname_np(
|
// See https://illumos.org/man/3C/pthread_setname_np for the error codes.
|
||||||
|
let res = match this.pthread_setname_np(
|
||||||
this.read_scalar(thread)?,
|
this.read_scalar(thread)?,
|
||||||
this.read_scalar(name)?,
|
this.read_scalar(name)?,
|
||||||
max_len,
|
max_len,
|
||||||
/* truncate */ false,
|
/* truncate */ false,
|
||||||
)?;
|
)? {
|
||||||
let res = if res { Scalar::from_u32(0) } else { this.eval_libc("ERANGE") };
|
ThreadNameResult::Ok => Scalar::from_u32(0),
|
||||||
|
ThreadNameResult::NameTooLong => this.eval_libc("ERANGE"),
|
||||||
|
ThreadNameResult::ThreadNotFound => this.eval_libc("ESRCH"),
|
||||||
|
};
|
||||||
this.write_scalar(res, dest)?;
|
this.write_scalar(res, dest)?;
|
||||||
}
|
}
|
||||||
"pthread_getname_np" => {
|
"pthread_getname_np" => {
|
||||||
let [thread, name, len] =
|
let [thread, name, len] =
|
||||||
this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
|
this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
|
||||||
// https://github.com/illumos/illumos-gate/blob/c56822be04b6c157c8b6f2281e47214c3b86f657/usr/src/lib/libc/port/threads/thr.c#L2449-L2480
|
// See https://illumos.org/man/3C/pthread_getname_np for the error codes.
|
||||||
let res = this.pthread_getname_np(
|
let res = match this.pthread_getname_np(
|
||||||
this.read_scalar(thread)?,
|
this.read_scalar(thread)?,
|
||||||
this.read_scalar(name)?,
|
this.read_scalar(name)?,
|
||||||
this.read_scalar(len)?,
|
this.read_scalar(len)?,
|
||||||
/* truncate */ false,
|
/* truncate */ false,
|
||||||
)?;
|
)? {
|
||||||
let res = if res { Scalar::from_u32(0) } else { this.eval_libc("ERANGE") };
|
ThreadNameResult::Ok => Scalar::from_u32(0),
|
||||||
|
ThreadNameResult::NameTooLong => this.eval_libc("ERANGE"),
|
||||||
|
ThreadNameResult::ThreadNotFound => this.eval_libc("ESRCH"),
|
||||||
|
};
|
||||||
this.write_scalar(res, dest)?;
|
this.write_scalar(res, dest)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,13 @@
|
|||||||
|
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub enum ThreadNameResult {
|
||||||
|
Ok,
|
||||||
|
NameTooLong,
|
||||||
|
ThreadNotFound,
|
||||||
|
}
|
||||||
|
|
||||||
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
|
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
|
||||||
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||||
fn pthread_create(
|
fn pthread_create(
|
||||||
@ -30,7 +37,11 @@ fn pthread_create(
|
|||||||
interp_ok(())
|
interp_ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pthread_join(&mut self, thread: &OpTy<'tcx>, retval: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
|
fn pthread_join(
|
||||||
|
&mut self,
|
||||||
|
thread: &OpTy<'tcx>,
|
||||||
|
retval: &OpTy<'tcx>,
|
||||||
|
) -> InterpResult<'tcx, Scalar> {
|
||||||
let this = self.eval_context_mut();
|
let this = self.eval_context_mut();
|
||||||
|
|
||||||
if !this.ptr_is_null(this.read_pointer(retval)?)? {
|
if !this.ptr_is_null(this.read_pointer(retval)?)? {
|
||||||
@ -38,22 +49,26 @@ fn pthread_join(&mut self, thread: &OpTy<'tcx>, retval: &OpTy<'tcx>) -> InterpRe
|
|||||||
throw_unsup_format!("Miri supports pthread_join only with retval==NULL");
|
throw_unsup_format!("Miri supports pthread_join only with retval==NULL");
|
||||||
}
|
}
|
||||||
|
|
||||||
let thread_id = this.read_scalar(thread)?.to_int(this.libc_ty_layout("pthread_t").size)?;
|
let thread = this.read_scalar(thread)?.to_int(this.libc_ty_layout("pthread_t").size)?;
|
||||||
this.join_thread_exclusive(thread_id.try_into().expect("thread ID should fit in u32"))?;
|
let Ok(thread) = this.thread_id_try_from(thread) else {
|
||||||
|
return interp_ok(this.eval_libc("ESRCH"));
|
||||||
|
};
|
||||||
|
|
||||||
interp_ok(())
|
this.join_thread_exclusive(thread)?;
|
||||||
|
|
||||||
|
interp_ok(Scalar::from_u32(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pthread_detach(&mut self, thread: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
|
fn pthread_detach(&mut self, thread: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
|
||||||
let this = self.eval_context_mut();
|
let this = self.eval_context_mut();
|
||||||
|
|
||||||
let thread_id = this.read_scalar(thread)?.to_int(this.libc_ty_layout("pthread_t").size)?;
|
let thread = this.read_scalar(thread)?.to_int(this.libc_ty_layout("pthread_t").size)?;
|
||||||
this.detach_thread(
|
let Ok(thread) = this.thread_id_try_from(thread) else {
|
||||||
thread_id.try_into().expect("thread ID should fit in u32"),
|
return interp_ok(this.eval_libc("ESRCH"));
|
||||||
/*allow_terminated_joined*/ false,
|
};
|
||||||
)?;
|
this.detach_thread(thread, /*allow_terminated_joined*/ false)?;
|
||||||
|
|
||||||
interp_ok(())
|
interp_ok(Scalar::from_u32(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pthread_self(&mut self) -> InterpResult<'tcx, Scalar> {
|
fn pthread_self(&mut self) -> InterpResult<'tcx, Scalar> {
|
||||||
@ -65,18 +80,21 @@ fn pthread_self(&mut self) -> InterpResult<'tcx, Scalar> {
|
|||||||
|
|
||||||
/// Set the name of the specified thread. If the name including the null terminator
|
/// Set the name of the specified thread. If the name including the null terminator
|
||||||
/// is longer or equals to `name_max_len`, then if `truncate` is set the truncated name
|
/// is longer or equals to `name_max_len`, then if `truncate` is set the truncated name
|
||||||
/// is used as the thread name, otherwise `false` is returned.
|
/// is used as the thread name, otherwise [`ThreadNameResult::NameTooLong`] is returned.
|
||||||
|
/// If the specified thread wasn't found, [`ThreadNameResult::ThreadNotFound`] is returned.
|
||||||
fn pthread_setname_np(
|
fn pthread_setname_np(
|
||||||
&mut self,
|
&mut self,
|
||||||
thread: Scalar,
|
thread: Scalar,
|
||||||
name: Scalar,
|
name: Scalar,
|
||||||
name_max_len: usize,
|
name_max_len: usize,
|
||||||
truncate: bool,
|
truncate: bool,
|
||||||
) -> InterpResult<'tcx, bool> {
|
) -> InterpResult<'tcx, ThreadNameResult> {
|
||||||
let this = self.eval_context_mut();
|
let this = self.eval_context_mut();
|
||||||
|
|
||||||
let thread = thread.to_int(this.libc_ty_layout("pthread_t").size)?;
|
let thread = thread.to_int(this.libc_ty_layout("pthread_t").size)?;
|
||||||
let thread = ThreadId::try_from(thread).unwrap();
|
let Ok(thread) = this.thread_id_try_from(thread) else {
|
||||||
|
return interp_ok(ThreadNameResult::ThreadNotFound);
|
||||||
|
};
|
||||||
let name = name.to_pointer(this)?;
|
let name = name.to_pointer(this)?;
|
||||||
let mut name = this.read_c_str(name)?.to_owned();
|
let mut name = this.read_c_str(name)?.to_owned();
|
||||||
|
|
||||||
@ -85,29 +103,32 @@ fn pthread_setname_np(
|
|||||||
if truncate {
|
if truncate {
|
||||||
name.truncate(name_max_len.saturating_sub(1));
|
name.truncate(name_max_len.saturating_sub(1));
|
||||||
} else {
|
} else {
|
||||||
return interp_ok(false);
|
return interp_ok(ThreadNameResult::NameTooLong);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.set_thread_name(thread, name);
|
this.set_thread_name(thread, name);
|
||||||
|
|
||||||
interp_ok(true)
|
interp_ok(ThreadNameResult::Ok)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the name of the specified thread. If the thread name doesn't fit
|
/// 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,
|
/// the buffer, then if `truncate` is set the truncated name is written out,
|
||||||
/// otherwise `false` is returned.
|
/// otherwise [`ThreadNameResult::NameTooLong`] is returned. If the specified
|
||||||
|
/// thread wasn't found, [`ThreadNameResult::ThreadNotFound`] is returned.
|
||||||
fn pthread_getname_np(
|
fn pthread_getname_np(
|
||||||
&mut self,
|
&mut self,
|
||||||
thread: Scalar,
|
thread: Scalar,
|
||||||
name_out: Scalar,
|
name_out: Scalar,
|
||||||
len: Scalar,
|
len: Scalar,
|
||||||
truncate: bool,
|
truncate: bool,
|
||||||
) -> InterpResult<'tcx, bool> {
|
) -> InterpResult<'tcx, ThreadNameResult> {
|
||||||
let this = self.eval_context_mut();
|
let this = self.eval_context_mut();
|
||||||
|
|
||||||
let thread = thread.to_int(this.libc_ty_layout("pthread_t").size)?;
|
let thread = thread.to_int(this.libc_ty_layout("pthread_t").size)?;
|
||||||
let thread = ThreadId::try_from(thread).unwrap();
|
let Ok(thread) = this.thread_id_try_from(thread) else {
|
||||||
|
return interp_ok(ThreadNameResult::ThreadNotFound);
|
||||||
|
};
|
||||||
let name_out = name_out.to_pointer(this)?;
|
let name_out = name_out.to_pointer(this)?;
|
||||||
let len = len.to_target_usize(this)?;
|
let len = len.to_target_usize(this)?;
|
||||||
|
|
||||||
@ -119,8 +140,9 @@ fn pthread_getname_np(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let (success, _written) = this.write_c_str(name, name_out, len)?;
|
let (success, _written) = this.write_c_str(name, name_out, len)?;
|
||||||
|
let res = if success { ThreadNameResult::Ok } else { ThreadNameResult::NameTooLong };
|
||||||
|
|
||||||
interp_ok(success)
|
interp_ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sched_yield(&mut self) -> InterpResult<'tcx, ()> {
|
fn sched_yield(&mut self) -> InterpResult<'tcx, ()> {
|
||||||
|
@ -10,6 +10,10 @@
|
|||||||
use crate::shims::windows::*;
|
use crate::shims::windows::*;
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
|
// The NTSTATUS STATUS_INVALID_HANDLE (0xC0000008) encoded as a HRESULT by setting the N bit.
|
||||||
|
// (https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/0642cb2f-2075-4469-918c-4441e69c548a)
|
||||||
|
const STATUS_INVALID_HANDLE: u32 = 0xD0000008;
|
||||||
|
|
||||||
pub fn is_dyn_sym(name: &str) -> bool {
|
pub fn is_dyn_sym(name: &str) -> bool {
|
||||||
// std does dynamic detection for these symbols
|
// std does dynamic detection for these symbols
|
||||||
matches!(
|
matches!(
|
||||||
@ -484,14 +488,14 @@ fn emulate_foreign_item_inner(
|
|||||||
let thread_id =
|
let thread_id =
|
||||||
this.CreateThread(security, stacksize, start, arg, flags, thread)?;
|
this.CreateThread(security, stacksize, start, arg, flags, thread)?;
|
||||||
|
|
||||||
this.write_scalar(Handle::Thread(thread_id).to_scalar(this), dest)?;
|
this.write_scalar(Handle::Thread(thread_id.to_u32()).to_scalar(this), dest)?;
|
||||||
}
|
}
|
||||||
"WaitForSingleObject" => {
|
"WaitForSingleObject" => {
|
||||||
let [handle, timeout] =
|
let [handle, timeout] =
|
||||||
this.check_shim(abi, ExternAbi::System { unwind: false }, link_name, args)?;
|
this.check_shim(abi, ExternAbi::System { unwind: false }, link_name, args)?;
|
||||||
|
|
||||||
let ret = this.WaitForSingleObject(handle, timeout)?;
|
let ret = this.WaitForSingleObject(handle, timeout)?;
|
||||||
this.write_scalar(Scalar::from_u32(ret), dest)?;
|
this.write_scalar(ret, dest)?;
|
||||||
}
|
}
|
||||||
"GetCurrentThread" => {
|
"GetCurrentThread" => {
|
||||||
let [] =
|
let [] =
|
||||||
@ -510,15 +514,20 @@ fn emulate_foreign_item_inner(
|
|||||||
let name = this.read_wide_str(this.read_pointer(name)?)?;
|
let name = this.read_wide_str(this.read_pointer(name)?)?;
|
||||||
|
|
||||||
let thread = match Handle::from_scalar(handle, this)? {
|
let thread = match Handle::from_scalar(handle, this)? {
|
||||||
Some(Handle::Thread(thread)) => thread,
|
Some(Handle::Thread(thread)) => this.thread_id_try_from(thread),
|
||||||
Some(Handle::Pseudo(PseudoHandle::CurrentThread)) => this.active_thread(),
|
Some(Handle::Pseudo(PseudoHandle::CurrentThread)) => Ok(this.active_thread()),
|
||||||
_ => this.invalid_handle("SetThreadDescription")?,
|
_ => this.invalid_handle("SetThreadDescription")?,
|
||||||
};
|
};
|
||||||
|
let res = match thread {
|
||||||
|
Ok(thread) => {
|
||||||
// FIXME: use non-lossy conversion
|
// FIXME: use non-lossy conversion
|
||||||
this.set_thread_name(thread, String::from_utf16_lossy(&name).into_bytes());
|
this.set_thread_name(thread, String::from_utf16_lossy(&name).into_bytes());
|
||||||
|
Scalar::from_u32(0)
|
||||||
|
}
|
||||||
|
Err(_) => Scalar::from_u32(STATUS_INVALID_HANDLE),
|
||||||
|
};
|
||||||
|
|
||||||
this.write_null(dest)?;
|
this.write_scalar(res, dest)?;
|
||||||
}
|
}
|
||||||
"GetThreadDescription" => {
|
"GetThreadDescription" => {
|
||||||
let [handle, name_ptr] =
|
let [handle, name_ptr] =
|
||||||
@ -528,20 +537,25 @@ fn emulate_foreign_item_inner(
|
|||||||
let name_ptr = this.deref_pointer(name_ptr)?; // the pointer where we should store the ptr to the name
|
let name_ptr = this.deref_pointer(name_ptr)?; // the pointer where we should store the ptr to the name
|
||||||
|
|
||||||
let thread = match Handle::from_scalar(handle, this)? {
|
let thread = match Handle::from_scalar(handle, this)? {
|
||||||
Some(Handle::Thread(thread)) => thread,
|
Some(Handle::Thread(thread)) => this.thread_id_try_from(thread),
|
||||||
Some(Handle::Pseudo(PseudoHandle::CurrentThread)) => this.active_thread(),
|
Some(Handle::Pseudo(PseudoHandle::CurrentThread)) => Ok(this.active_thread()),
|
||||||
_ => this.invalid_handle("GetThreadDescription")?,
|
_ => this.invalid_handle("GetThreadDescription")?,
|
||||||
};
|
};
|
||||||
|
let (name, res) = match thread {
|
||||||
|
Ok(thread) => {
|
||||||
// Looks like the default thread name is empty.
|
// Looks like the default thread name is empty.
|
||||||
let name = this.get_thread_name(thread).unwrap_or(b"").to_owned();
|
let name = this.get_thread_name(thread).unwrap_or(b"").to_owned();
|
||||||
let name = this.alloc_os_str_as_wide_str(
|
let name = this.alloc_os_str_as_wide_str(
|
||||||
bytes_to_os_str(&name)?,
|
bytes_to_os_str(&name)?,
|
||||||
MiriMemoryKind::WinLocal.into(),
|
MiriMemoryKind::WinLocal.into(),
|
||||||
)?;
|
)?;
|
||||||
|
(Scalar::from_maybe_pointer(name, this), Scalar::from_u32(0))
|
||||||
|
}
|
||||||
|
Err(_) => (Scalar::null_ptr(this), Scalar::from_u32(STATUS_INVALID_HANDLE)),
|
||||||
|
};
|
||||||
|
|
||||||
this.write_scalar(Scalar::from_maybe_pointer(name, this), &name_ptr)?;
|
this.write_scalar(name, &name_ptr)?;
|
||||||
|
this.write_scalar(res, dest)?;
|
||||||
this.write_null(dest)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Miscellaneous
|
// Miscellaneous
|
||||||
@ -630,9 +644,9 @@ fn emulate_foreign_item_inner(
|
|||||||
let [handle] =
|
let [handle] =
|
||||||
this.check_shim(abi, ExternAbi::System { unwind: false }, link_name, args)?;
|
this.check_shim(abi, ExternAbi::System { unwind: false }, link_name, args)?;
|
||||||
|
|
||||||
this.CloseHandle(handle)?;
|
let ret = this.CloseHandle(handle)?;
|
||||||
|
|
||||||
this.write_int(1, dest)?;
|
this.write_scalar(ret, dest)?;
|
||||||
}
|
}
|
||||||
"GetModuleFileNameW" => {
|
"GetModuleFileNameW" => {
|
||||||
let [handle, filename, size] =
|
let [handle, filename, size] =
|
||||||
|
@ -14,7 +14,7 @@ pub enum PseudoHandle {
|
|||||||
pub enum Handle {
|
pub enum Handle {
|
||||||
Null,
|
Null,
|
||||||
Pseudo(PseudoHandle),
|
Pseudo(PseudoHandle),
|
||||||
Thread(ThreadId),
|
Thread(u32),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PseudoHandle {
|
impl PseudoHandle {
|
||||||
@ -51,7 +51,7 @@ fn data(self) -> u32 {
|
|||||||
match self {
|
match self {
|
||||||
Self::Null => 0,
|
Self::Null => 0,
|
||||||
Self::Pseudo(pseudo_handle) => pseudo_handle.value(),
|
Self::Pseudo(pseudo_handle) => pseudo_handle.value(),
|
||||||
Self::Thread(thread) => thread.to_u32(),
|
Self::Thread(thread) => thread,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,7 +95,7 @@ fn new(discriminant: u32, data: u32) -> Option<Self> {
|
|||||||
match discriminant {
|
match discriminant {
|
||||||
Self::NULL_DISCRIMINANT if data == 0 => Some(Self::Null),
|
Self::NULL_DISCRIMINANT if data == 0 => Some(Self::Null),
|
||||||
Self::PSEUDO_DISCRIMINANT => Some(Self::Pseudo(PseudoHandle::from_value(data)?)),
|
Self::PSEUDO_DISCRIMINANT => Some(Self::Pseudo(PseudoHandle::from_value(data)?)),
|
||||||
Self::THREAD_DISCRIMINANT => Some(Self::Thread(data.into())),
|
Self::THREAD_DISCRIMINANT => Some(Self::Thread(data)),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -154,17 +154,22 @@ fn invalid_handle(&mut self, function_name: &str) -> InterpResult<'tcx, !> {
|
|||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn CloseHandle(&mut self, handle_op: &OpTy<'tcx>) -> InterpResult<'tcx> {
|
fn CloseHandle(&mut self, handle_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
|
||||||
let this = self.eval_context_mut();
|
let this = self.eval_context_mut();
|
||||||
|
|
||||||
let handle = this.read_scalar(handle_op)?;
|
let handle = this.read_scalar(handle_op)?;
|
||||||
|
let ret = match Handle::from_scalar(handle, this)? {
|
||||||
match Handle::from_scalar(handle, this)? {
|
Some(Handle::Thread(thread)) => {
|
||||||
Some(Handle::Thread(thread)) =>
|
if let Ok(thread) = this.thread_id_try_from(thread) {
|
||||||
this.detach_thread(thread, /*allow_terminated_joined*/ true)?,
|
this.detach_thread(thread, /*allow_terminated_joined*/ true)?;
|
||||||
|
this.eval_windows("c", "TRUE")
|
||||||
|
} else {
|
||||||
|
this.invalid_handle("CloseHandle")?
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => this.invalid_handle("CloseHandle")?,
|
_ => this.invalid_handle("CloseHandle")?,
|
||||||
}
|
};
|
||||||
|
|
||||||
interp_ok(())
|
interp_ok(ret)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,14 +59,18 @@ fn WaitForSingleObject(
|
|||||||
&mut self,
|
&mut self,
|
||||||
handle_op: &OpTy<'tcx>,
|
handle_op: &OpTy<'tcx>,
|
||||||
timeout_op: &OpTy<'tcx>,
|
timeout_op: &OpTy<'tcx>,
|
||||||
) -> InterpResult<'tcx, u32> {
|
) -> InterpResult<'tcx, Scalar> {
|
||||||
let this = self.eval_context_mut();
|
let this = self.eval_context_mut();
|
||||||
|
|
||||||
let handle = this.read_scalar(handle_op)?;
|
let handle = this.read_scalar(handle_op)?;
|
||||||
let timeout = this.read_scalar(timeout_op)?.to_u32()?;
|
let timeout = this.read_scalar(timeout_op)?.to_u32()?;
|
||||||
|
|
||||||
let thread = match Handle::from_scalar(handle, this)? {
|
let thread = match Handle::from_scalar(handle, this)? {
|
||||||
Some(Handle::Thread(thread)) => thread,
|
Some(Handle::Thread(thread)) =>
|
||||||
|
match this.thread_id_try_from(thread) {
|
||||||
|
Ok(thread) => thread,
|
||||||
|
Err(_) => this.invalid_handle("WaitForSingleObject")?,
|
||||||
|
},
|
||||||
// Unlike on posix, the outcome of joining the current thread is not documented.
|
// Unlike on posix, the outcome of joining the current thread is not documented.
|
||||||
// On current Windows, it just deadlocks.
|
// On current Windows, it just deadlocks.
|
||||||
Some(Handle::Pseudo(PseudoHandle::CurrentThread)) => this.active_thread(),
|
Some(Handle::Pseudo(PseudoHandle::CurrentThread)) => this.active_thread(),
|
||||||
@ -79,6 +83,6 @@ fn WaitForSingleObject(
|
|||||||
|
|
||||||
this.join_thread(thread)?;
|
this.join_thread(thread)?;
|
||||||
|
|
||||||
interp_ok(0)
|
interp_ok(this.eval_windows("c", "WAIT_OBJECT_0"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -199,4 +199,29 @@ fn get_thread_name(name: &mut [u8]) -> i32 {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.join()
|
.join()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
// Now set the name for a non-existing thread and verify error codes.
|
||||||
|
// (FreeBSD doesn't return an error code.)
|
||||||
|
#[cfg(not(target_os = "freebsd"))]
|
||||||
|
{
|
||||||
|
let invalid_thread = 0xdeadbeef;
|
||||||
|
let error = {
|
||||||
|
cfg_if::cfg_if! {
|
||||||
|
if #[cfg(target_os = "linux")] {
|
||||||
|
libc::ENOENT
|
||||||
|
} else {
|
||||||
|
libc::ESRCH
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#[cfg(not(target_os = "macos"))]
|
||||||
|
{
|
||||||
|
// macOS has no `setname` function accepting a thread id as the first argument.
|
||||||
|
let res = unsafe { libc::pthread_setname_np(invalid_thread, [0].as_ptr()) };
|
||||||
|
assert_eq!(res, error);
|
||||||
|
}
|
||||||
|
let mut buf = [0; 64];
|
||||||
|
let res = unsafe { libc::pthread_getname_np(invalid_thread, buf.as_mut_ptr(), buf.len()) };
|
||||||
|
assert_eq!(res, error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user