Skip to content

Commit 8829bd5

Browse files
committed
Add shims for gettid-esque functions
Various platforms provide a function to return the current OS thread ID, but they all use a slightly different name. Add shims for these functions for Apple, FreeBSD, and Windows, with tests to account for those and a few more platforms that are not yet supported by Miri. The syscall and extern symbol is included as well on Linux. These should be useful in general but should also help support printing the OS thread ID in panic messages [1]. [1]: #115746
1 parent 8da6239 commit 8829bd5

File tree

12 files changed

+309
-12
lines changed

12 files changed

+309
-12
lines changed

src/tools/miri/src/helpers.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1346,12 +1346,25 @@ pub fn check_min_vararg_count<'a, 'tcx, const N: usize>(
13461346
return interp_ok(ops);
13471347
}
13481348
throw_ub_format!(
1349-
"not enough variadic arguments for `{name}`: got {}, expected at least {}",
1349+
"not enough variadic arguments for `{name}`: got {}, expected at least {N}",
13501350
args.len(),
1351-
N
13521351
)
13531352
}
13541353

1354+
/// Error if there are not exactly `N` varidic arguments.
1355+
pub fn check_exact_vararg_count<'a, 'tcx, const N: usize>(
1356+
name: &'a str,
1357+
args: &'a [OpTy<'tcx>],
1358+
) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]> {
1359+
if N != args.len() {
1360+
throw_ub_format!(
1361+
"incorrect number of varidic arguments for `{name}`: got {}, expected {N}",
1362+
args.len(),
1363+
);
1364+
}
1365+
interp_ok(args.try_into().unwrap())
1366+
}
1367+
13551368
pub fn isolation_abort_error<'tcx>(name: &str) -> InterpResult<'tcx> {
13561369
throw_machine_stop!(TerminationInfo::UnsupportedInIsolation(format!(
13571370
"{name} not available when isolation is enabled",

src/tools/miri/src/shims/env.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,29 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
110110
}
111111
}
112112

113+
/// Get the process identifier.
113114
fn get_pid(&self) -> u32 {
114115
let this = self.eval_context_ref();
115116
if this.machine.communicate() { std::process::id() } else { 1000 }
116117
}
118+
119+
/// Get an "OS" thread ID for the current thread.
120+
fn get_current_tid(&self) -> u32 {
121+
self.get_tid(self.eval_context_ref().machine.threads.active_thread())
122+
}
123+
124+
/// Get an "OS" thread ID for any thread.
125+
fn get_tid(&self, thread: ThreadId) -> u32 {
126+
let this = self.eval_context_ref();
127+
let index = thread.to_u32();
128+
let target_os = &this.tcx.sess.target.os;
129+
if target_os == "linux" || target_os == "netbsd" {
130+
// On Linux, the main thread has PID == TID so we uphold this. NetBSD also appears
131+
// to exhibit the same behavior, though I can't find a citation.
132+
this.get_pid().strict_add(index)
133+
} else {
134+
// Other platforms do not document any relationship between PID and TID.
135+
index
136+
}
137+
}
117138
}

src/tools/miri/src/shims/extern_static.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ impl<'tcx> MiriMachine<'tcx> {
7070
ecx,
7171
&["__cxa_thread_atexit_impl", "__clock_gettime64"],
7272
)?;
73-
Self::weak_symbol_extern_statics(ecx, &["getrandom", "statx"])?;
73+
Self::weak_symbol_extern_statics(ecx, &["getrandom", "gettid", "statx"])?;
7474
}
7575
"freebsd" => {
7676
Self::null_ptr_extern_statics(ecx, &["__cxa_thread_atexit_impl"])?;

src/tools/miri/src/shims/unix/env.rs

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use rustc_data_structures::fx::FxHashMap;
77
use rustc_index::IndexVec;
88
use rustc_middle::ty::Ty;
99
use rustc_middle::ty::layout::LayoutOf;
10+
use rustc_span::Symbol;
1011

1112
use crate::*;
1213

@@ -275,15 +276,56 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
275276
interp_ok(Scalar::from_u32(this.get_pid()))
276277
}
277278

278-
fn linux_gettid(&mut self) -> InterpResult<'tcx, Scalar> {
279+
/// The `gettid`-like function for Unix platforms that take no parameters and return a 32-bit
280+
/// integer. It is not always named "gettid".
281+
fn unix_gettid(&mut self, link_name: Symbol) -> InterpResult<'tcx, Scalar> {
279282
let this = self.eval_context_ref();
280-
this.assert_target_os("linux", "gettid");
283+
let target_os = &self.eval_context_ref().tcx.sess.target.os;
284+
285+
// Various platforms have a similar function with different names.
286+
match (target_os.as_ref(), link_name.as_str()) {
287+
("linux" | "android", "gettid") => (),
288+
("openbsd", "getthrid") => (),
289+
("freebsd", "pthread_getthreadid_np") => (),
290+
("netbsd", "_lwp_self") => (),
291+
(_, name) => panic!("`{name}` is not supported on {target_os}"),
292+
};
293+
294+
// For most platforms the return type is an `i32`, but some are unsigned. The TID
295+
// will always be positive so we don't need to differentiate.
296+
interp_ok(Scalar::from_u32(this.get_current_tid()))
297+
}
281298

282-
let index = this.machine.threads.active_thread().to_u32();
299+
/// The Apple-specific `int pthread_threadid_np(pthread_t thread, uint64_t *thread_id)`, which
300+
/// allows querying the ID for arbitrary threads.
301+
fn apple_pthread_threadip_np(
302+
&mut self,
303+
thread_op: &OpTy<'tcx>,
304+
tid_op: &OpTy<'tcx>,
305+
) -> InterpResult<'tcx, Scalar> {
306+
let this = self.eval_context_mut();
307+
308+
let target_vendor = &this.tcx.sess.target.vendor;
309+
assert_eq!(
310+
target_vendor, "apple",
311+
"`pthread_threadid_np` is not supported on target vendor {target_vendor}",
312+
);
313+
314+
let thread = this.read_scalar(thread_op)?.to_int(this.libc_ty_layout("pthread_t").size)?;
315+
let thread = if thread == 0 {
316+
// Null thread ID indicates that we are querying the active thread.
317+
this.machine.threads.active_thread()
318+
} else {
319+
let Ok(thread) = this.thread_id_try_from(thread) else {
320+
return interp_ok(this.eval_libc("ESRCH"));
321+
};
322+
thread
323+
};
283324

284-
// Compute a TID for this thread, ensuring that the main thread has PID == TID.
285-
let tid = this.get_pid().strict_add(index);
325+
let tid_dest = this.deref_pointer_as(tid_op, this.machine.layouts.u64)?;
326+
this.write_int(this.get_tid(thread), &tid_dest)?;
286327

287-
interp_ok(Scalar::from_u32(tid))
328+
// Never an error if we only ever check the current thread.
329+
interp_ok(Scalar::from_u32(0))
288330
}
289331
}

src/tools/miri/src/shims/unix/freebsd/foreign_items.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
5656
};
5757
this.write_scalar(res, dest)?;
5858
}
59+
"pthread_getthreadid_np" => {
60+
let [] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
61+
let result = this.unix_gettid(link_name)?;
62+
this.write_scalar(result, dest)?;
63+
}
5964

6065
"cpuset_getaffinity" => {
6166
// The "same" kind of api as `sched_getaffinity` but more fine grained control for FreeBSD specifically.

src/tools/miri/src/shims/unix/linux/foreign_items.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use crate::*;
1818
const TASK_COMM_LEN: u64 = 16;
1919

2020
pub fn is_dyn_sym(name: &str) -> bool {
21-
matches!(name, "statx")
21+
matches!(name, "gettid" | "statx")
2222
}
2323

2424
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
@@ -117,7 +117,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
117117
}
118118
"gettid" => {
119119
let [] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
120-
let result = this.linux_gettid()?;
120+
let result = this.unix_gettid(link_name)?;
121121
this.write_scalar(result, dest)?;
122122
}
123123

src/tools/miri/src/shims/unix/linux_like/syscall.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ use rustc_middle::ty::Ty;
33
use rustc_span::Symbol;
44
use rustc_target::callconv::FnAbi;
55

6-
use crate::helpers::check_min_vararg_count;
6+
use crate::helpers::{check_exact_vararg_count, check_min_vararg_count};
7+
use crate::shims::unix::env::EvalContextExt;
78
use crate::shims::unix::linux_like::eventfd::EvalContextExt as _;
89
use crate::shims::unix::linux_like::sync::futex;
910
use crate::*;
@@ -24,6 +25,7 @@ pub fn syscall<'tcx>(
2425
let sys_getrandom = ecx.eval_libc("SYS_getrandom").to_target_usize(ecx)?;
2526
let sys_futex = ecx.eval_libc("SYS_futex").to_target_usize(ecx)?;
2627
let sys_eventfd2 = ecx.eval_libc("SYS_eventfd2").to_target_usize(ecx)?;
28+
let sys_gettid = ecx.eval_libc("SYS_gettid").to_target_usize(ecx)?;
2729

2830
match ecx.read_target_usize(op)? {
2931
// `libc::syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), GRND_NONBLOCK)`
@@ -53,6 +55,11 @@ pub fn syscall<'tcx>(
5355
let result = ecx.eventfd(initval, flags)?;
5456
ecx.write_int(result.to_i32()?, dest)?;
5557
}
58+
num if num == sys_gettid => {
59+
let [] = check_exact_vararg_count("syscall(SYS_gettid, ...)", varargs)?;
60+
let result = ecx.unix_gettid(Symbol::intern("gettid"))?;
61+
ecx.write_int(result.to_u32()?, dest)?;
62+
}
5663
num => {
5764
throw_unsup_format!("syscall: unsupported syscall number {num}");
5865
}

src/tools/miri/src/shims/unix/macos/foreign_items.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
222222
};
223223
this.write_scalar(res, dest)?;
224224
}
225+
"pthread_threadid_np" => {
226+
let [thread, tid_ptr] = this.check_shim(abi, CanonAbi::C, link_name, args)?;
227+
let res = this.apple_pthread_threadip_np(thread, tid_ptr)?;
228+
this.write_scalar(res, dest)?;
229+
}
225230

226231
// Synchronization primitives
227232
"os_sync_wait_on_address" => {

src/tools/miri/src/shims/windows/foreign_items.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -630,6 +630,22 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
630630
this.write_scalar(name, &name_ptr)?;
631631
this.write_scalar(res, dest)?;
632632
}
633+
"GetThreadId" => {
634+
let [handle] = this.check_shim(abi, sys_conv, link_name, args)?;
635+
let handle = this.read_handle(handle, "GetThreadId")?;
636+
637+
let thread = match handle {
638+
Handle::Thread(thread) => thread,
639+
Handle::Pseudo(PseudoHandle::CurrentThread) => this.active_thread(),
640+
_ => this.invalid_handle("GetThreadDescription")?,
641+
};
642+
643+
this.write_scalar(Scalar::from_u32(this.get_tid(thread)), dest)?;
644+
}
645+
"GetCurrentThreadId" => {
646+
let [] = this.check_shim(abi, sys_conv, link_name, args)?;
647+
this.write_scalar(Scalar::from_u32(this.get_tid(this.active_thread())), dest)?;
648+
}
633649

634650
// Miscellaneous
635651
"ExitProcess" => {
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
//@only-target: linux android
2+
3+
fn main() {
4+
// gettid takes no parameters, this should be rejected
5+
unsafe { libc::syscall(libc::SYS_gettid, 0) }; //~ERROR: incorrect number of varidic arguments
6+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error: Undefined Behavior: incorrect number of varidic arguments for `syscall(SYS_gettid, ...)`: got 1, expected 0
2+
--> tests/fail-dep/libc/invalid_syscall_use_gettid.rs:LL:CC
3+
|
4+
LL | unsafe { libc::syscall(libc::SYS_gettid, 0) };
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here
6+
|
7+
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
8+
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
9+
= note: BACKTRACE:
10+
= note: inside `main` at tests/fail-dep/libc/invalid_syscall_use_gettid.rs:LL:CC
11+
12+
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
13+
14+
error: aborting due to 1 previous error
15+

0 commit comments

Comments
 (0)