Skip to main content

shadow_rs/host/
thread.rs

1//! An emulated Linux thread.
2
3use std::cell::{Cell, RefCell};
4use std::ops::{Deref, DerefMut};
5
6use linux_api::errno::Errno;
7use linux_api::fcntl::DescriptorFlags;
8use linux_api::mman::{MapFlags, ProtFlags};
9use linux_api::posix_types::Pid;
10use linux_api::sched::{Sched, SchedFlags, sched_attr};
11use linux_api::signal::stack_t;
12use shadow_shim_helper_rs::HostId;
13use shadow_shim_helper_rs::explicit_drop::ExplicitDrop;
14use shadow_shim_helper_rs::rootedcell::rc::RootedRc;
15use shadow_shim_helper_rs::rootedcell::refcell::RootedRefCell;
16use shadow_shim_helper_rs::shim_shmem::{HostShmemProtected, ThreadShmem};
17use shadow_shim_helper_rs::syscall_types::{ForeignPtr, SyscallReg};
18use shadow_shim_helper_rs::util::SendPointer;
19use shadow_shmem::allocator::{ShMemBlock, shmalloc};
20
21use super::context::ProcessContext;
22use super::descriptor::descriptor_table::{DescriptorHandle, DescriptorTable};
23use super::host::Host;
24use super::managed_thread::{self, ManagedThread};
25use super::process::{Process, ProcessId};
26use crate::cshadow as c;
27use crate::host::descriptor::DropPosixRecordLocks;
28use crate::host::syscall::condition::{SyscallConditionRef, SyscallConditionRefMut};
29use crate::host::syscall::handler::SyscallHandler;
30use crate::utility::callback_queue::CallbackQueue;
31use crate::utility::{IsSend, ObjectCounter, syscall};
32
33/// The thread's state after having been allowed to execute some code.
34#[derive(Debug)]
35#[must_use]
36pub enum ResumeResult {
37    /// Blocked on a syscall.
38    Blocked,
39    /// The thread has exited with the given code.
40    ExitedThread(i32),
41    /// The process has exited.
42    ExitedProcess,
43}
44
45/// A virtual Thread in Shadow. Currently a thin wrapper around the C Thread,
46/// which this object owns, and frees on Drop.
47pub struct Thread {
48    id: ThreadId,
49    host_id: HostId,
50    process_id: ProcessId,
51    sched_policy: Cell<Sched>,
52    sched_attr: Cell<sched_attr>,
53    // If non-NULL, this address should be cleared and futex-awoken on thread exit.
54    // See set_tid_address(2).
55    tid_address: Cell<ForeignPtr<libc::pid_t>>,
56    shim_shared_memory: ShMemBlock<'static, ThreadShmem>,
57    syscallhandler: RootedRefCell<SyscallHandler>,
58    /// Descriptor table; potentially shared with other threads and processes.
59    // TODO: Consider using an Arc instead of RootedRc, particularly if this
60    // continues to be the only RootedRc member. Cloning this object currently
61    // only done when creating a child process or thread, and if we don't have
62    // any RootedRc members we could get rid of the requirement to explicitly
63    // drop Thread.
64    desc_table: Option<RootedRc<RootedRefCell<DescriptorTable>>>,
65    // TODO: convert to SyscallCondition (Rust wrapper for c::SysCallCondition).
66    // Non-trivial because SyscallCondition is currently not `Send`.
67    cond: Cell<SendPointer<c::SysCallCondition>>,
68    /// The native, managed thread
69    mthread: RefCell<ManagedThread>,
70    _counter: ObjectCounter,
71}
72
73impl IsSend for Thread {}
74
75impl Thread {
76    /// Minimal wrapper around the native managed thread.
77    pub fn mthread(&self) -> impl Deref<Target = ManagedThread> + '_ {
78        self.mthread.borrow()
79    }
80
81    /// Update this thread to be the new thread group leader as part of an
82    /// `execve` or `execveat` syscall.  Replaces the managed thread with
83    /// `mthread` and updates the thread ID.
84    pub fn update_for_exec(&mut self, host: &Host, mthread: ManagedThread, new_tid: ThreadId) {
85        self.mthread.replace(mthread).handle_process_exit();
86        self.tid_address.set(ForeignPtr::null());
87
88        // Update shmem
89        {
90            // We potentially need to update the thread-id. It doesn't currently
91            // have interior mutability, and since mutating it is rare, it seems
92            // nicer to get a mutable copy of the current  shared memory, update
93            // it, and alloc a new block, vs. adding another layer of interior
94            // mutability at all the other points we access it.
95
96            let host_shmem_prot = host.shim_shmem_lock_borrow().unwrap();
97
98            let mut thread_shmem =
99                ThreadShmem::clone(&self.shim_shared_memory, &host_shmem_prot.root);
100
101            // thread id is updated to make this the new thread group leader.
102            thread_shmem.tid = new_tid.into();
103
104            // sigaltstack is reset to disabled.
105            unsafe {
106                *thread_shmem
107                    .protected
108                    .borrow_mut(&host_shmem_prot.root)
109                    .sigaltstack_mut() = stack_t::new(
110                    std::ptr::null_mut(),
111                    linux_api::signal::SigAltStackFlags::SS_DISABLE,
112                    0,
113                )
114            };
115
116            self.shim_shared_memory = shmalloc(thread_shmem);
117        }
118
119        self.syscallhandler = RootedRefCell::new(
120            host.root(),
121            SyscallHandler::new(
122                host.id(),
123                self.process_id,
124                new_tid,
125                host.params.use_syscall_counters,
126            ),
127        );
128
129        // Update descriptor table
130        {
131            // Descriptor table is unshared
132            let desc_table_rc = self.desc_table.take().unwrap();
133            let mut desc_table = DescriptorTable::clone(&desc_table_rc.borrow(host.root()));
134            // Don't inadvertently drop locks as part of this bookkeeping.
135            // In particular, fcntl(2): "Record locks are ... are preserved
136            // across an execve(2)".
137            let dont_drop_locks = DropPosixRecordLocks::False;
138            desc_table_rc.explicit_drop_recursive(host.root(), (host, dont_drop_locks));
139
140            // Any descriptors with CLOEXEC are closed.
141            let to_close: Vec<DescriptorHandle> = desc_table
142                .iter()
143                .filter_map(|(handle, descriptor)| {
144                    if descriptor.flags().contains(DescriptorFlags::FD_CLOEXEC) {
145                        Some(*handle)
146                    } else {
147                        None
148                    }
149                })
150                .collect();
151
152            CallbackQueue::queue_and_run_with_legacy(|q| {
153                for handle in to_close {
154                    let drop_locks = DropPosixRecordLocks::ForPid(self.process_id());
155                    log::trace!("Unregistering FD_CLOEXEC descriptor {handle:?}");
156                    if let Some(Err(e)) = desc_table
157                        .deregister_descriptor(handle)
158                        .unwrap()
159                        .close(host, drop_locks, q)
160                    {
161                        log::debug!("Error closing {handle:?}: {e:?}");
162                    };
163                }
164            });
165
166            self.desc_table = Some(RootedRc::new(
167                host.root(),
168                RootedRefCell::new(host.root(), desc_table),
169            ));
170        }
171
172        if let Some(c) = unsafe { self.cond.get_mut().ptr().as_mut() } {
173            unsafe { c::syscallcondition_cancel(c) };
174            unsafe { c::syscallcondition_unref(c) };
175        }
176        self.cond = Cell::new(unsafe { SendPointer::new(std::ptr::null_mut()) });
177
178        self.id = new_tid;
179    }
180
181    /// Have the plugin thread natively execute the given syscall.
182    fn native_syscall_raw(
183        &self,
184        ctx: &ProcessContext,
185        n: i64,
186        args: &[SyscallReg],
187    ) -> libc::c_long {
188        self.mthread
189            .borrow()
190            .native_syscall(&ctx.with_thread(self), n, args)
191            .into()
192    }
193
194    /// Have the plugin thread natively execute the given syscall.
195    fn native_syscall(
196        &self,
197        ctx: &ProcessContext,
198        n: i64,
199        args: &[SyscallReg],
200    ) -> Result<SyscallReg, Errno> {
201        syscall::raw_return_value_to_result(self.native_syscall_raw(ctx, n, args))
202    }
203
204    pub fn process_id(&self) -> ProcessId {
205        self.process_id
206    }
207
208    pub fn host_id(&self) -> HostId {
209        self.host_id
210    }
211
212    pub fn native_pid(&self) -> Pid {
213        self.mthread.borrow().native_pid()
214    }
215
216    pub fn native_tid(&self) -> Pid {
217        self.mthread.borrow().native_tid()
218    }
219
220    pub fn id(&self) -> ThreadId {
221        self.id
222    }
223
224    pub fn sched_policy(&self) -> Sched {
225        self.sched_policy.get()
226    }
227
228    pub fn sched_priority(&self) -> std::ffi::c_int {
229        self.sched_attr.get().sched_priority.try_into().unwrap()
230    }
231
232    pub fn sched_reset_on_fork(&self) -> bool {
233        let flags = self.sched_attr.get().sched_flags;
234        flags == u64::try_from(SchedFlags::SCHED_FLAG_RESET_ON_FORK.bits()).unwrap()
235    }
236
237    pub fn set_sched_attrs(&self, policy: Sched, reset_on_fork: bool, priority: std::ffi::c_int) {
238        self.sched_policy.set(policy);
239
240        let mut sched_attr = self.sched_attr.get();
241        sched_attr.sched_policy = u32::try_from(i32::from(policy)).unwrap();
242        sched_attr.sched_flags = if reset_on_fork {
243            u64::try_from(SchedFlags::SCHED_FLAG_RESET_ON_FORK.bits()).unwrap()
244        } else {
245            0
246        };
247        sched_attr.sched_priority = priority.try_into().unwrap();
248        self.sched_attr.set(sched_attr);
249    }
250
251    /// Returns whether the given thread is its thread group (aka process) leader.
252    /// Typically this is true for the first thread created in a process.
253    pub fn is_leader(&self) -> bool {
254        self.id == self.process_id.into()
255    }
256
257    pub fn syscall_condition(&self) -> Option<SyscallConditionRef<'_>> {
258        // We check the for null explicitly here instead of using `as_mut` to
259        // construct and match an `Option<&mut c::SysCallCondition>`, since it's
260        // difficult to ensure we're not breaking any Rust aliasing rules when
261        // constructing a mutable reference.
262        let c = self.cond.get().ptr();
263        if c.is_null() {
264            None
265        } else {
266            Some(unsafe { SyscallConditionRef::borrow_from_c(c) })
267        }
268    }
269
270    pub fn syscall_condition_mut(&self) -> Option<SyscallConditionRefMut<'_>> {
271        // We can't safely use `as_mut` here, since that would construct a mutable reference,
272        // and we can't prove no other reference exists.
273        let c = self.cond.get().ptr();
274        if c.is_null() {
275            None
276        } else {
277            Some(unsafe { SyscallConditionRefMut::borrow_from_c(c) })
278        }
279    }
280
281    pub fn cleanup_syscall_condition(&self) {
282        if let Some(c) = unsafe {
283            self.cond
284                .replace(SendPointer::new(std::ptr::null_mut()))
285                .ptr()
286                .as_mut()
287        } {
288            unsafe { c::syscallcondition_cancel(c) };
289            unsafe { c::syscallcondition_unref(c) };
290        }
291    }
292
293    pub fn descriptor_table(&self) -> &RootedRc<RootedRefCell<DescriptorTable>> {
294        self.desc_table.as_ref().unwrap()
295    }
296
297    #[track_caller]
298    pub fn descriptor_table_borrow<'a>(
299        &'a self,
300        host: &'a Host,
301    ) -> impl Deref<Target = DescriptorTable> + 'a {
302        self.desc_table.as_ref().unwrap().borrow(host.root())
303    }
304
305    #[track_caller]
306    pub fn descriptor_table_borrow_mut<'a>(
307        &'a self,
308        host: &'a Host,
309    ) -> impl DerefMut<Target = DescriptorTable> + 'a {
310        self.desc_table.as_ref().unwrap().borrow_mut(host.root())
311    }
312
313    /// Natively execute munmap(2) on the given thread.
314    pub fn native_munmap(
315        &self,
316        ctx: &ProcessContext,
317        ptr: ForeignPtr<u8>,
318        size: usize,
319    ) -> Result<(), Errno> {
320        self.native_syscall(ctx, libc::SYS_munmap, &[ptr.into(), size.into()])?;
321        Ok(())
322    }
323
324    /// Natively execute mmap(2) on the given thread.
325    pub fn native_mmap(
326        &self,
327        ctx: &ProcessContext,
328        addr: ForeignPtr<u8>,
329        len: usize,
330        prot: ProtFlags,
331        flags: MapFlags,
332        fd: i32,
333        offset: i64,
334    ) -> Result<ForeignPtr<u8>, Errno> {
335        Ok(self
336            .native_syscall(
337                ctx,
338                libc::SYS_mmap,
339                &[
340                    SyscallReg::from(addr),
341                    SyscallReg::from(len),
342                    SyscallReg::from(prot.bits()),
343                    SyscallReg::from(flags.bits()),
344                    SyscallReg::from(fd),
345                    SyscallReg::from(offset),
346                ],
347            )?
348            .into())
349    }
350
351    /// Natively execute mremap(2) on the given thread.
352    pub fn native_mremap(
353        &self,
354        ctx: &ProcessContext,
355        old_addr: ForeignPtr<u8>,
356        old_len: usize,
357        new_len: usize,
358        flags: i32,
359        new_addr: ForeignPtr<u8>,
360    ) -> Result<ForeignPtr<u8>, Errno> {
361        Ok(self
362            .native_syscall(
363                ctx,
364                libc::SYS_mremap,
365                &[
366                    SyscallReg::from(old_addr),
367                    SyscallReg::from(old_len),
368                    SyscallReg::from(new_len),
369                    SyscallReg::from(flags),
370                    SyscallReg::from(new_addr),
371                ],
372            )?
373            .into())
374    }
375
376    /// Natively execute mmap(2) on the given thread.
377    pub fn native_mprotect(
378        &self,
379        ctx: &ProcessContext,
380        addr: ForeignPtr<u8>,
381        len: usize,
382        prot: ProtFlags,
383    ) -> Result<(), Errno> {
384        self.native_syscall(
385            ctx,
386            libc::SYS_mprotect,
387            &[
388                SyscallReg::from(addr),
389                SyscallReg::from(len),
390                SyscallReg::from(prot.bits()),
391            ],
392        )?;
393        Ok(())
394    }
395
396    /// Natively execute open(2) on the given thread.
397    pub fn native_open(
398        &self,
399        ctx: &ProcessContext,
400        pathname: ForeignPtr<u8>,
401        flags: i32,
402        mode: i32,
403    ) -> Result<i32, Errno> {
404        let res = self.native_syscall(
405            ctx,
406            libc::SYS_open,
407            &[
408                SyscallReg::from(pathname),
409                SyscallReg::from(flags),
410                SyscallReg::from(mode),
411            ],
412        );
413        Ok(i32::from(res?))
414    }
415
416    /// Natively execute close(2) on the given thread.
417    pub fn native_close(&self, ctx: &ProcessContext, fd: i32) -> Result<(), Errno> {
418        self.native_syscall(ctx, libc::SYS_close, &[SyscallReg::from(fd)])?;
419        Ok(())
420    }
421
422    /// Natively execute brk(2) on the given thread.
423    pub fn native_brk(
424        &self,
425        ctx: &ProcessContext,
426        addr: ForeignPtr<u8>,
427    ) -> Result<ForeignPtr<u8>, Errno> {
428        let res = self.native_syscall(ctx, libc::SYS_brk, &[SyscallReg::from(addr)])?;
429        Ok(ForeignPtr::from(res))
430    }
431
432    /// Natively execute a chdir(2) syscall on the given thread.
433    pub fn native_chdir(
434        &self,
435        ctx: &ProcessContext,
436        pathname: ForeignPtr<std::ffi::c_char>,
437    ) -> Result<i32, Errno> {
438        let res = self.native_syscall(ctx, libc::SYS_chdir, &[SyscallReg::from(pathname)]);
439        Ok(i32::from(res?))
440    }
441
442    /// Allocates some space in the plugin's memory. Use `get_writeable_ptr` to write to it, and
443    /// `flush` to ensure that the write is flushed to the plugin's memory.
444    pub fn malloc_foreign_ptr(
445        &self,
446        ctx: &ProcessContext,
447        size: usize,
448    ) -> Result<ForeignPtr<u8>, Errno> {
449        // SAFETY: No pointer specified; can't pass a bad one.
450        self.native_mmap(
451            ctx,
452            ForeignPtr::null(),
453            size,
454            ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
455            MapFlags::MAP_PRIVATE | MapFlags::MAP_ANONYMOUS,
456            -1,
457            0,
458        )
459    }
460
461    /// Frees a pointer previously returned by `malloc_foreign_ptr`
462    pub fn free_foreign_ptr(
463        &self,
464        ctx: &ProcessContext,
465        ptr: ForeignPtr<u8>,
466        size: usize,
467    ) -> Result<(), Errno> {
468        self.native_munmap(ctx, ptr, size)?;
469        Ok(())
470    }
471
472    /// Create a new `Thread`, wrapping `mthread`. Intended for use by
473    /// syscall handlers such as `clone`.
474    pub fn wrap_mthread(
475        host: &Host,
476        mthread: ManagedThread,
477        desc_table: RootedRc<RootedRefCell<DescriptorTable>>,
478        pid: ProcessId,
479        tid: ThreadId,
480    ) -> Thread {
481        Self {
482            mthread: RefCell::new(mthread),
483            syscallhandler: RootedRefCell::new(
484                host.root(),
485                SyscallHandler::new(host.id(), pid, tid, host.params.use_syscall_counters),
486            ),
487            cond: Cell::new(unsafe { SendPointer::new(std::ptr::null_mut()) }),
488            id: tid,
489            host_id: host.id(),
490            process_id: pid,
491            sched_policy: Cell::new(Sched::SCHED_NORMAL),
492            sched_attr: Cell::new(sched_attr {
493                size: u32::try_from(std::mem::size_of::<sched_attr>()).unwrap(),
494                sched_policy: u32::try_from(i32::from(Sched::SCHED_NORMAL)).unwrap(),
495                sched_flags: 0,
496                sched_nice: 0,
497                sched_priority: 0,
498                sched_runtime: 0,
499                sched_deadline: 0,
500                sched_period: 0,
501                sched_util_min: 0,
502                sched_util_max: 0,
503            }),
504            tid_address: Cell::new(ForeignPtr::null()),
505            shim_shared_memory: shmalloc(ThreadShmem::new(
506                &host.shim_shmem_lock_borrow().unwrap(),
507                tid.into(),
508            )),
509            desc_table: Some(desc_table),
510            _counter: ObjectCounter::new("Thread"),
511        }
512    }
513
514    /// Shared memory for this thread.
515    pub fn shmem(&self) -> &ShMemBlock<'_, ThreadShmem> {
516        &self.shim_shared_memory
517    }
518
519    pub fn resume(&self, ctx: &ProcessContext) -> ResumeResult {
520        // Ensure the condition isn't triggered again, but don't clear it yet.
521        // Syscall handler can still access.
522        if let Some(c) = unsafe { self.cond.get().ptr().as_mut() } {
523            unsafe { c::syscallcondition_cancel(c) };
524        }
525
526        let mut syscall_handler = self.syscallhandler.borrow_mut(ctx.host.root());
527
528        let res = self
529            .mthread
530            .borrow()
531            .resume(&ctx.with_thread(self), &mut syscall_handler);
532
533        // Now we're done with old condition.
534        if let Some(c) = unsafe {
535            self.cond
536                .replace(SendPointer::new(std::ptr::null_mut()))
537                .ptr()
538                .as_mut()
539        } {
540            unsafe { c::syscallcondition_unref(c) };
541        }
542
543        match res {
544            managed_thread::ResumeResult::Blocked(cond) => {
545                // Wait on new condition.
546                let cond = cond.into_inner();
547                self.cond.set(unsafe { SendPointer::new(cond) });
548                if let Some(cond) = unsafe { cond.as_mut() } {
549                    unsafe { c::syscallcondition_waitNonblock(cond, ctx.host, ctx.process, self) }
550                }
551                ResumeResult::Blocked
552            }
553            managed_thread::ResumeResult::ExitedThread(c) => ResumeResult::ExitedThread(c),
554            managed_thread::ResumeResult::ExitedProcess => ResumeResult::ExitedProcess,
555        }
556    }
557
558    pub fn handle_process_exit(&self) {
559        self.cleanup_syscall_condition();
560        self.mthread.borrow().handle_process_exit();
561    }
562
563    pub fn return_code(&self) -> Option<i32> {
564        self.mthread.borrow().return_code()
565    }
566
567    pub fn is_running(&self) -> bool {
568        self.mthread.borrow().is_running()
569    }
570
571    pub fn get_tid_address(&self) -> ForeignPtr<libc::pid_t> {
572        self.tid_address.get()
573    }
574
575    /// Sets the `clear_child_tid` attribute as for `set_tid_address(2)`. The thread will perform a
576    /// futex-wake operation on the given address on termination.
577    pub fn set_tid_address(&self, ptr: ForeignPtr<libc::pid_t>) {
578        self.tid_address.set(ptr)
579    }
580
581    pub fn unblocked_signal_pending(
582        &self,
583        process: &Process,
584        host_shmem: &HostShmemProtected,
585    ) -> bool {
586        debug_assert_eq!(process.id(), self.process_id);
587
588        let thread_shmem_protected = self.shmem().protected.borrow(&host_shmem.root);
589
590        let unblocked_signals = !thread_shmem_protected.blocked_signals;
591        let pending_signals = self
592            .shmem()
593            .protected
594            .borrow(&host_shmem.root)
595            .pending_signals
596            | process
597                .shmem()
598                .protected
599                .borrow(&host_shmem.root)
600                .pending_signals;
601
602        !(pending_signals & unblocked_signals).is_empty()
603    }
604}
605
606impl Drop for Thread {
607    fn drop(&mut self) {
608        if let Some(c) = unsafe { self.cond.get_mut().ptr().as_mut() } {
609            unsafe { c::syscallcondition_cancel(c) };
610            unsafe { c::syscallcondition_unref(c) };
611        }
612    }
613}
614
615impl ExplicitDrop for Thread {
616    type ExplicitDropParam<'p> = &'p Host;
617    type ExplicitDropResult = ();
618
619    fn explicit_drop<'p>(mut self, host: Self::ExplicitDropParam<'p>) {
620        if let Some(table) = self.desc_table.take() {
621            // If this is the last reference to the given table, do drop posix
622            // record locks. Typically this should only be the case if this is
623            // the last thread in the process.
624            let drop_locks = DropPosixRecordLocks::ForPid(self.process_id());
625            table.explicit_drop_recursive(host.root(), (host, drop_locks));
626        }
627    }
628}
629
630#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone, Ord, PartialOrd)]
631pub struct ThreadId(u32);
632
633impl TryFrom<libc::pid_t> for ThreadId {
634    type Error = <u32 as TryFrom<libc::pid_t>>::Error;
635
636    fn try_from(value: libc::pid_t) -> Result<Self, Self::Error> {
637        Ok(Self(u32::try_from(value)?))
638    }
639}
640
641impl From<ProcessId> for ThreadId {
642    fn from(value: ProcessId) -> Self {
643        // A process ID is also a valid thread ID
644        ThreadId(value.into())
645    }
646}
647
648impl From<ThreadId> for libc::pid_t {
649    fn from(val: ThreadId) -> Self {
650        val.0.try_into().unwrap()
651    }
652}
653
654impl std::fmt::Display for ThreadId {
655    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
656        write!(f, "{}", self.0)
657    }
658}
659
660mod export {
661    use shadow_shim_helper_rs::shim_shmem::export::{ShimShmemHostLock, ShimShmemThread};
662    use shadow_shim_helper_rs::syscall_types::UntypedForeignPtr;
663
664    use super::*;
665    use crate::core::worker::Worker;
666    use crate::host::descriptor::socket::Socket;
667    use crate::host::descriptor::socket::inet::InetSocket;
668    use crate::host::descriptor::{CompatFile, Descriptor, File};
669
670    /// Make the requested syscall from within the plugin.
671    ///
672    /// Does *not* flush or invalidate MemoryManager pointers, such as those
673    /// obtained through `process_getReadablePtr` etc.
674    ///
675    /// Arguments are treated opaquely. e.g. no pointer-marshalling is done.
676    ///
677    /// The return value is the value returned by the syscall *instruction*.
678    /// You can map to a corresponding errno value with syscall_rawReturnValueToErrno.
679    //
680    // Rust doesn't support declaring a function with varargs (...), but this
681    // declaration is ABI compatible with a caller who sees this function declared
682    // with arguments `Thread* thread, long n, ...`. We manually generate that declartion
683    // in our bindings.
684    #[unsafe(no_mangle)]
685    unsafe extern "C-unwind" fn thread_nativeSyscall(
686        thread: *const Thread,
687        n: libc::c_long,
688        arg1: SyscallReg,
689        arg2: SyscallReg,
690        arg3: SyscallReg,
691        arg4: SyscallReg,
692        arg5: SyscallReg,
693        arg6: SyscallReg,
694    ) -> libc::c_long {
695        let thread = unsafe { thread.as_ref().unwrap() };
696        Worker::with_active_host(|host| {
697            Worker::with_active_process(|process| {
698                thread.native_syscall_raw(
699                    &ProcessContext::new(host, process),
700                    n,
701                    &[arg1, arg2, arg3, arg4, arg5, arg6],
702                )
703            })
704            .unwrap()
705        })
706        .unwrap()
707    }
708
709    #[unsafe(no_mangle)]
710    pub unsafe extern "C-unwind" fn thread_getID(thread: *const Thread) -> libc::pid_t {
711        let thread = unsafe { thread.as_ref().unwrap() };
712        thread.id().into()
713    }
714
715    /// Gets the `clear_child_tid` attribute, as set by `thread_setTidAddress`.
716    #[unsafe(no_mangle)]
717    pub unsafe extern "C-unwind" fn thread_getTidAddress(
718        thread: *const Thread,
719    ) -> UntypedForeignPtr {
720        let thread = unsafe { thread.as_ref().unwrap() };
721        thread.get_tid_address().cast::<()>()
722    }
723
724    /// Returns a typed pointer to memory shared with the shim (which is backed by
725    /// the block returned by thread_getShMBlock).
726    #[unsafe(no_mangle)]
727    pub unsafe extern "C-unwind" fn thread_sharedMem(
728        thread: *const Thread,
729    ) -> *const ShimShmemThread {
730        let thread = unsafe { thread.as_ref().unwrap() };
731        &*thread.shim_shared_memory
732    }
733
734    #[unsafe(no_mangle)]
735    pub unsafe extern "C-unwind" fn thread_getProcess(thread: *const Thread) -> *const Process {
736        let thread = unsafe { thread.as_ref().unwrap() };
737        Worker::with_active_host(|host| {
738            let process = host.process_borrow(thread.process_id).unwrap();
739            let p: &Process = &process.borrow(host.root());
740            std::ptr::from_ref(p)
741        })
742        .unwrap()
743    }
744
745    #[unsafe(no_mangle)]
746    pub unsafe extern "C-unwind" fn thread_getHost(thread: *const Thread) -> *const Host {
747        let thread = unsafe { thread.as_ref().unwrap() };
748        Worker::with_active_host(|host| {
749            assert_eq!(host.id(), thread.host_id());
750            std::ptr::from_ref(host)
751        })
752        .unwrap()
753    }
754
755    #[unsafe(no_mangle)]
756    pub unsafe extern "C-unwind" fn thread_clearSysCallCondition(thread: *const Thread) {
757        let thread = unsafe { thread.as_ref().unwrap() };
758        thread.cleanup_syscall_condition();
759    }
760
761    /// Returns true iff there is an unblocked, unignored signal pending for this
762    /// thread (or its process).
763    #[unsafe(no_mangle)]
764    pub unsafe extern "C-unwind" fn thread_unblockedSignalPending(
765        thread: *const Thread,
766        host_lock: *const ShimShmemHostLock,
767    ) -> bool {
768        let thread = unsafe { thread.as_ref().unwrap() };
769        let host_lock = unsafe { host_lock.as_ref().unwrap() };
770
771        Worker::with_active_host(|host| {
772            let process = host.process_borrow(thread.process_id()).unwrap();
773            let process = process.borrow(host.root());
774            thread.unblocked_signal_pending(&process, host_lock)
775        })
776        .unwrap()
777    }
778
779    /// Register a `Descriptor`. This takes ownership of the descriptor and you must not access it
780    /// after.
781    #[unsafe(no_mangle)]
782    pub extern "C-unwind" fn thread_registerDescriptor(
783        thread: *const Thread,
784        desc: *mut Descriptor,
785    ) -> libc::c_int {
786        let thread = unsafe { thread.as_ref().unwrap() };
787        let desc = Descriptor::from_raw(desc).unwrap();
788
789        Worker::with_active_host(|host| {
790            thread
791                .descriptor_table_borrow_mut(host)
792                .register_descriptor(*desc)
793                .unwrap()
794                .into()
795        })
796        .unwrap()
797    }
798
799    /// Get a temporary reference to a descriptor.
800    #[unsafe(no_mangle)]
801    pub extern "C-unwind" fn thread_getRegisteredDescriptor(
802        thread: *const Thread,
803        handle: libc::c_int,
804    ) -> *const Descriptor {
805        let thread = unsafe { thread.as_ref().unwrap() };
806
807        let handle = match handle.try_into() {
808            Ok(i) => i,
809            Err(_) => {
810                log::debug!("Attempted to get a descriptor with handle {handle}");
811                return std::ptr::null();
812            }
813        };
814
815        Worker::with_active_host(
816            |host| match thread.descriptor_table_borrow(host).get(handle) {
817                Some(d) => std::ptr::from_ref(d),
818                None => std::ptr::null(),
819            },
820        )
821        .unwrap()
822    }
823
824    /// Get a temporary mutable reference to a descriptor.
825    #[unsafe(no_mangle)]
826    pub extern "C-unwind" fn thread_getRegisteredDescriptorMut(
827        thread: *const Thread,
828        handle: libc::c_int,
829    ) -> *mut Descriptor {
830        let thread = unsafe { thread.as_ref().unwrap() };
831
832        let handle = match handle.try_into() {
833            Ok(i) => i,
834            Err(_) => {
835                log::debug!("Attempted to get a descriptor with handle {handle}");
836                return std::ptr::null_mut();
837            }
838        };
839
840        Worker::with_active_host(|host| {
841            match thread.descriptor_table_borrow_mut(host).get_mut(handle) {
842                Some(d) => d as *mut Descriptor,
843                None => std::ptr::null_mut(),
844            }
845        })
846        .unwrap()
847    }
848
849    /// Get a temporary reference to a legacy file.
850    #[unsafe(no_mangle)]
851    pub unsafe extern "C-unwind" fn thread_getRegisteredLegacyFile(
852        thread: *const Thread,
853        handle: libc::c_int,
854    ) -> *mut c::LegacyFile {
855        let thread = unsafe { thread.as_ref().unwrap() };
856
857        let handle = match handle.try_into() {
858            Ok(i) => i,
859            Err(_) => {
860                log::debug!("Attempted to get a descriptor with handle {handle}");
861                return std::ptr::null_mut();
862            }
863        };
864
865        Worker::with_active_host(|host| {
866        match thread.descriptor_table_borrow(host).get(handle).map(|x| x.file()) {
867            Some(CompatFile::Legacy(file)) => file.ptr(),
868            Some(CompatFile::New(file)) => {
869                // we have a special case for the legacy C TCP objects
870                if let File::Socket(Socket::Inet(InetSocket::LegacyTcp(tcp))) = file.inner_file() {
871                    tcp.borrow().as_legacy_file()
872                } else {
873                    log::warn!(
874                        "A descriptor exists for fd={handle}, but it is not a legacy file. Returning NULL."
875                    );
876                    std::ptr::null_mut()
877                }
878            }
879            None => std::ptr::null_mut(),
880        }
881        }).unwrap()
882    }
883}