Skip to content

Commit 71994fb

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. 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 71994fb

File tree

11 files changed

+295
-10
lines changed

11 files changed

+295
-10
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/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: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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)