shadow_shim_helper_rs/
shim_shmem.rs

1use linux_api::signal::{Signal, sigaction, siginfo_t, sigset_t, stack_t};
2use shadow_shmem::allocator::{ShMemBlock, ShMemBlockSerialized};
3use vasi::VirtualAddressSpaceIndependent;
4use vasi_sync::scmutex::SelfContainedMutex;
5
6use crate::HostId;
7use crate::option::FfiOption;
8use crate::{
9    emulated_time::{AtomicEmulatedTime, EmulatedTime},
10    rootedcell::{Root, refcell::RootedRefCell},
11    simulation_time::SimulationTime,
12};
13
14// Validates that a type is safe to store in shared memory.
15macro_rules! assert_shmem_safe {
16    ($t:ty, $testfnname:ident) => {
17        // Must be Sync, since it will be simultaneously available to multiple
18        // threads (and processes).
19        static_assertions::assert_impl_all!($t: Sync);
20
21        // Must be VirtualAddressSpaceIndpendent, since it may be simultaneously
22        // mapped into different virtual address spaces.
23        static_assertions::assert_impl_all!($t: VirtualAddressSpaceIndependent);
24
25        // Must have a stable layout.
26        // This property is important if it's possible for code compiled in
27        // different `rustc` invocations to access the shared memory. Theoretically,
28        // with the current Shadow build layout, it *shouldn't* be needed, since this
29        // code should be compiled only once before linking into both Shadow and the shim.
30        // It would be easy to lose that property without noticing though, and end up with
31        // very subtle memory bugs.
32        //
33        // We could also potentially dispense with this requirement for shared
34        // memory only ever accessed via a dynamically linked library. Such a
35        // library can only provided C abi public functions though.
36        //
37        // TODO: Consider instead implementing a trait like FFISafe, with a
38        // derive-macro that validates that the type itself has an appropriate
39        // `repr`, and that all of its fields are FFISafe. We could then
40        // implement a trait `trait IsShmemSafe: Sync +
41        // VirtualAddressSpaceIndpendent + FFISafe` instead of this macro
42        // `assert_shmem_safe`, and enforce it in e.g. APIs that set up and
43        // initialize shared memory.
44        #[deny(improper_ctypes_definitions)]
45        unsafe extern "C-unwind" fn $testfnname(_: $t) {}
46    };
47}
48
49#[derive(VirtualAddressSpaceIndependent)]
50#[repr(C)]
51pub struct NativePreemptionConfig {
52    /// Maximum wall-clock time to allow managed code to run before preempting
53    /// to move time forward.
54    // Using `kernel_old_timeval` here leaks implementation details from the
55    // shim a bit, but lets us do the fiddly time conversion and potential error
56    // reporting from the shadow process up-front.
57    pub native_duration: linux_api::time::kernel_old_timeval,
58    /// Amount of simulation-time to move forward after `native_duration_micros`
59    /// has elapsed without returning control to Shadow.
60    pub sim_duration: SimulationTime,
61}
62
63#[derive(VirtualAddressSpaceIndependent)]
64#[repr(C)]
65pub struct ManagerShmem {
66    pub log_start_time_micros: i64,
67    pub native_preemption_config: FfiOption<NativePreemptionConfig>,
68    pub emulate_cpuid: bool,
69}
70assert_shmem_safe!(ManagerShmem, _managershmem_test_fn);
71
72#[derive(VirtualAddressSpaceIndependent)]
73#[repr(C)]
74pub struct HostShmem {
75    pub host_id: HostId,
76
77    pub protected: SelfContainedMutex<HostShmemProtected>,
78
79    // Whether to model unblocked syscalls as taking non-zero time.
80    // TODO: Move to a "ShimShmemGlobal" struct if we make one.
81    pub model_unblocked_syscall_latency: bool,
82
83    // Maximum accumulated CPU latency before updating clock.
84    // TODO: Move to a "ShimShmemGlobal" struct if we make one, and if this
85    // stays a global constant; Or down into the process if we make it a
86    // per-process option.
87    pub max_unapplied_cpu_latency: SimulationTime,
88
89    // How much to move time forward for each unblocked syscall.
90    // TODO: Move to a "ShimShmemGlobal" struct if we make one, and if this
91    // stays a global constant; Or down into the process if we make it a
92    // per-process option.
93    pub unblocked_syscall_latency: SimulationTime,
94
95    // How much to move time forward for each unblocked vdso "syscall".
96    // TODO: Move to a "ShimShmemGlobal" struct if we make one, and if this
97    // stays a global constant; Or down into the process if we make it a
98    // per-process option.
99    pub unblocked_vdso_latency: SimulationTime,
100
101    // Native pid of the Shadow simulator process.
102    pub shadow_pid: libc::pid_t,
103
104    // Emulated CPU TSC clock rate, for rdtsc emulation.
105    pub tsc_hz: u64,
106
107    // Current simulation time.
108    pub sim_time: AtomicEmulatedTime,
109
110    pub shim_log_level: logger::LogLevel,
111
112    pub manager_shmem: ShMemBlockSerialized,
113}
114assert_shmem_safe!(HostShmem, _hostshmem_test_fn);
115
116impl HostShmem {
117    #[allow(clippy::too_many_arguments)]
118    pub fn new(
119        host_id: HostId,
120        model_unblocked_syscall_latency: bool,
121        max_unapplied_cpu_latency: SimulationTime,
122        unblocked_syscall_latency: SimulationTime,
123        unblocked_vdso_latency: SimulationTime,
124        shadow_pid: libc::pid_t,
125        tsc_hz: u64,
126        shim_log_level: ::logger::LogLevel,
127        manager_shmem: &ShMemBlock<ManagerShmem>,
128    ) -> Self {
129        Self {
130            host_id,
131            protected: SelfContainedMutex::new(HostShmemProtected {
132                host_id,
133                root: Root::new(),
134                unapplied_cpu_latency: SimulationTime::ZERO,
135                max_runahead_time: EmulatedTime::MIN,
136            }),
137            model_unblocked_syscall_latency,
138            max_unapplied_cpu_latency,
139            unblocked_syscall_latency,
140            unblocked_vdso_latency,
141            shadow_pid,
142            tsc_hz,
143            sim_time: AtomicEmulatedTime::new(EmulatedTime::MIN),
144            shim_log_level,
145            manager_shmem: manager_shmem.serialize(),
146        }
147    }
148
149    pub fn protected(&self) -> &SelfContainedMutex<HostShmemProtected> {
150        &self.protected
151    }
152}
153
154#[derive(VirtualAddressSpaceIndependent)]
155#[repr(C)]
156pub struct HostShmemProtected {
157    pub host_id: HostId,
158
159    pub root: Root,
160
161    // Modeled CPU latency that hasn't been applied to the clock yet.
162    pub unapplied_cpu_latency: SimulationTime,
163
164    // Max simulation time to which sim_time may be incremented.  Moving time
165    // beyond this value requires the current thread to be rescheduled.
166    pub max_runahead_time: EmulatedTime,
167}
168
169#[derive(VirtualAddressSpaceIndependent)]
170#[repr(C)]
171pub struct ProcessShmem {
172    host_id: HostId,
173
174    /// Handle to shared memory for the Host
175    pub host_shmem: ShMemBlockSerialized,
176    pub strace_fd: FfiOption<libc::c_int>,
177
178    pub protected: RootedRefCell<ProcessShmemProtected>,
179}
180assert_shmem_safe!(ProcessShmem, _test_processshmem_fn);
181
182impl ProcessShmem {
183    pub fn new(
184        host_root: &Root,
185        host_shmem: ShMemBlockSerialized,
186        host_id: HostId,
187        strace_fd: Option<libc::c_int>,
188    ) -> Self {
189        Self {
190            host_id,
191            host_shmem,
192            strace_fd: strace_fd.into(),
193            protected: RootedRefCell::new(
194                host_root,
195                ProcessShmemProtected {
196                    host_id,
197                    pending_signals: sigset_t::EMPTY,
198                    pending_standard_siginfos: [siginfo_t::default();
199                        Signal::STANDARD_MAX.as_i32() as usize],
200                    signal_actions: [sigaction::default(); Signal::MAX.as_i32() as usize],
201                },
202            ),
203        }
204    }
205}
206
207#[derive(VirtualAddressSpaceIndependent)]
208#[repr(C)]
209pub struct ProcessShmemProtected {
210    pub host_id: HostId,
211
212    // Process-directed pending signals.
213    pub pending_signals: sigset_t,
214
215    // siginfo for each of the standard signals.
216    // SAFETY: we ensure the internal pointers aren't dereferenced
217    // outside of its original virtual address space.
218    #[unsafe_assume_virtual_address_space_independent]
219    pending_standard_siginfos: [siginfo_t; Signal::STANDARD_MAX.as_i32() as usize],
220
221    // actions for both standard and realtime signals.
222    // We currently support configuring handlers for realtime signals, but not
223    // actually delivering them. This is to handle the case where handlers are
224    // defensively installed, but not used in practice.
225    // SAFETY: we ensure the internal pointers aren't dereferenced
226    // outside of its original virtual address space.
227    #[unsafe_assume_virtual_address_space_independent]
228    signal_actions: [sigaction; Signal::MAX.as_i32() as usize],
229}
230
231// We have several arrays indexed by signal number - 1.
232fn signal_idx(signal: Signal) -> usize {
233    (i32::from(signal) - 1) as usize
234}
235
236impl ProcessShmemProtected {
237    pub fn pending_standard_siginfo(&self, signal: Signal) -> Option<&siginfo_t> {
238        if self.pending_signals.has(signal) {
239            Some(&self.pending_standard_siginfos[signal_idx(signal)])
240        } else {
241            None
242        }
243    }
244
245    pub fn set_pending_standard_siginfo(&mut self, signal: Signal, info: &siginfo_t) {
246        assert!(self.pending_signals.has(signal));
247        self.pending_standard_siginfos[signal_idx(signal)] = *info;
248    }
249
250    /// # Safety
251    ///
252    /// Only valid if pointers in `src` sigactions are valid in `self`'s address
253    /// space (e.g. `src` is a parent that just forked this process).
254    pub unsafe fn clone_signal_actions(&mut self, src: &Self) {
255        self.signal_actions = src.signal_actions
256    }
257
258    /// # Safety
259    ///
260    /// Function pointers in `shd_kernel_sigaction::u` are valid only
261    /// from corresponding managed process, and may be libc::SIG_DFL or
262    /// libc::SIG_IGN.
263    pub unsafe fn signal_action(&self, signal: Signal) -> &sigaction {
264        &self.signal_actions[signal_idx(signal)]
265    }
266
267    /// # Safety
268    ///
269    /// Function pointers in `shd_kernel_sigaction::u` are valid only
270    /// from corresponding managed process, and may be libc::SIG_DFL or
271    /// libc::SIG_IGN.
272    pub unsafe fn signal_action_mut(&mut self, signal: Signal) -> &mut sigaction {
273        &mut self.signal_actions[signal_idx(signal)]
274    }
275
276    /// This drops all pending signals. Intended primarily for use with exec.
277    pub fn clear_pending_signals(&mut self) {
278        self.pending_signals = sigset_t::EMPTY;
279    }
280
281    pub fn take_pending_unblocked_signal(
282        &mut self,
283        thread: &ThreadShmemProtected,
284    ) -> Option<(Signal, siginfo_t)> {
285        let pending_unblocked_signals = self.pending_signals & !thread.blocked_signals;
286        if pending_unblocked_signals.is_empty() {
287            None
288        } else {
289            let signal = pending_unblocked_signals.lowest().unwrap();
290            let info = *self.pending_standard_siginfo(signal).unwrap();
291            self.pending_signals.del(signal);
292            Some((signal, info))
293        }
294    }
295}
296
297#[derive(VirtualAddressSpaceIndependent)]
298#[repr(C)]
299pub struct ThreadShmem {
300    pub host_id: HostId,
301    pub tid: libc::pid_t,
302
303    pub protected: RootedRefCell<ThreadShmemProtected>,
304}
305assert_shmem_safe!(ThreadShmem, _test_threadshmem_fn);
306
307impl ThreadShmem {
308    pub fn new(host: &HostShmemProtected, tid: libc::pid_t) -> Self {
309        Self {
310            host_id: host.host_id,
311            tid,
312            protected: RootedRefCell::new(
313                &host.root,
314                ThreadShmemProtected {
315                    host_id: host.host_id,
316                    pending_signals: sigset_t::EMPTY,
317                    pending_standard_siginfos: [siginfo_t::default();
318                        Signal::STANDARD_MAX.as_i32() as usize],
319                    blocked_signals: sigset_t::EMPTY,
320                    sigaltstack: StackWrapper(stack_t {
321                        ss_sp: core::ptr::null_mut(),
322                        ss_flags: libc::SS_DISABLE,
323                        ss_size: 0,
324                    }),
325                },
326            ),
327        }
328    }
329
330    /// Create a copy of `Self`. We can't implement the `Clone` trait since we
331    /// need the `root`.
332    pub fn clone(&self, root: &Root) -> Self {
333        Self {
334            host_id: self.host_id,
335            tid: self.tid,
336            protected: RootedRefCell::new(root, *self.protected.borrow(root)),
337        }
338    }
339}
340
341#[derive(VirtualAddressSpaceIndependent, Copy, Clone)]
342#[repr(C)]
343pub struct ThreadShmemProtected {
344    pub host_id: HostId,
345
346    // Thread-directed pending signals.
347    pub pending_signals: sigset_t,
348
349    // siginfo for each of the 32 standard signals.
350    // SAFETY: we ensure the internal pointers aren't dereferenced
351    // outside of its original virtual address space.
352    #[unsafe_assume_virtual_address_space_independent]
353    pending_standard_siginfos: [siginfo_t; Signal::STANDARD_MAX.as_i32() as usize],
354
355    // Signal mask, e.g. as set by `sigprocmask`.
356    // We don't use sigset_t since glibc uses a much larger bitfield than
357    // actually supported by the kernel.
358    pub blocked_signals: sigset_t,
359
360    // Configured alternate signal stack for this thread.
361    sigaltstack: StackWrapper,
362}
363
364impl ThreadShmemProtected {
365    pub fn pending_standard_siginfo(&self, signal: Signal) -> Option<&siginfo_t> {
366        if self.pending_signals.has(signal) {
367            Some(&self.pending_standard_siginfos[signal_idx(signal)])
368        } else {
369            None
370        }
371    }
372
373    pub fn set_pending_standard_siginfo(&mut self, signal: Signal, info: &siginfo_t) {
374        assert!(self.pending_signals.has(signal));
375        self.pending_standard_siginfos[signal_idx(signal)] = *info;
376    }
377
378    /// # Safety
379    ///
380    /// `stack_t::ss_sp` must not be dereferenced except from corresponding
381    /// managed thread.
382    pub unsafe fn sigaltstack(&self) -> &stack_t {
383        &self.sigaltstack.0
384    }
385
386    /// # Safety
387    ///
388    /// `stack_t::ss_sp` must not be dereferenced except from corresponding
389    /// managed thread. Must be set to either core::ptr::null_mut, or a pointer valid
390    /// in the managed thread.
391    pub unsafe fn sigaltstack_mut(&mut self) -> &mut stack_t {
392        &mut self.sigaltstack.0
393    }
394
395    pub fn take_pending_unblocked_signal(&mut self) -> Option<(Signal, siginfo_t)> {
396        let pending_unblocked_signals = self.pending_signals & !self.blocked_signals;
397        if pending_unblocked_signals.is_empty() {
398            None
399        } else {
400            let signal = pending_unblocked_signals.lowest().unwrap();
401            let info = *self.pending_standard_siginfo(signal).unwrap();
402            self.pending_signals.del(signal);
403            Some((signal, info))
404        }
405    }
406}
407
408#[derive(Copy, Clone)]
409#[repr(transparent)]
410struct StackWrapper(stack_t);
411
412// SAFETY: We ensure the contained pointer isn't dereferenced
413// except from the owning thread.
414unsafe impl Send for StackWrapper {}
415
416// SAFETY: We ensure the contained pointers isn't dereferenced
417// except from the original virtual address space: in the shim.
418unsafe impl VirtualAddressSpaceIndependent for StackWrapper {}
419
420/// Take the next unblocked thread- *or* process-directed signal.
421pub fn take_pending_unblocked_signal(
422    lock: &HostShmemProtected,
423    process: &ProcessShmem,
424    thread: &ThreadShmem,
425) -> Option<(Signal, siginfo_t)> {
426    let mut thread_protected = thread.protected.borrow_mut(&lock.root);
427    thread_protected
428        .take_pending_unblocked_signal()
429        .or_else(|| {
430            let mut process_protected = process.protected.borrow_mut(&lock.root);
431            process_protected.take_pending_unblocked_signal(&thread_protected)
432        })
433}
434
435pub mod export {
436    use core::sync::atomic::Ordering;
437
438    use bytemuck::TransparentWrapper;
439    use linux_api::signal::{linux_sigaction, linux_sigset_t, linux_stack_t};
440    use vasi_sync::scmutex::SelfContainedMutexGuard;
441
442    use super::*;
443    use crate::{emulated_time::CEmulatedTime, simulation_time::CSimulationTime};
444
445    // Legacy type names; keeping the more verbose names for the C API, since
446    // they're not namespaced.
447    pub type ShimShmemManager = ManagerShmem;
448    pub type ShimShmemHost = HostShmem;
449    pub type ShimShmemHostLock = HostShmemProtected;
450    pub type ShimShmemProcess = ProcessShmem;
451    pub type ShimShmemThread = ThreadShmem;
452
453    /// # Safety
454    ///
455    /// `host` must be valid. The returned pointer must not be accessed from other threads.
456    #[unsafe(no_mangle)]
457    pub unsafe extern "C-unwind" fn shimshmemhost_lock(
458        host: *const ShimShmemHost,
459    ) -> *mut ShimShmemHostLock {
460        let host = unsafe { host.as_ref().unwrap() };
461        let mut guard: SelfContainedMutexGuard<ShimShmemHostLock> = host.protected().lock();
462        let lock: &mut ShimShmemHostLock = &mut guard;
463        let lock = core::ptr::from_mut(lock);
464        guard.disconnect();
465        lock
466    }
467
468    /// # Safety
469    ///
470    /// `host` and `lock` must be valid.
471    #[unsafe(no_mangle)]
472    pub unsafe extern "C-unwind" fn shimshmemhost_unlock(
473        host: *const ShimShmemHost,
474        lock: *mut *mut ShimShmemHostLock,
475    ) {
476        let host = unsafe { host.as_ref().unwrap() };
477        let guard = SelfContainedMutexGuard::reconnect(&host.protected);
478        assert_eq!(host.host_id, guard.host_id);
479
480        let p_lock = unsafe { lock.as_mut().unwrap() };
481        *p_lock = core::ptr::null_mut();
482    }
483
484    /// # Safety
485    ///
486    /// Pointer args must be safely dereferenceable.
487    #[unsafe(no_mangle)]
488    pub unsafe extern "C-unwind" fn shimshmem_getShadowPid(
489        host_mem: *const ShimShmemHost,
490    ) -> libc::pid_t {
491        let host_mem = unsafe { host_mem.as_ref().unwrap() };
492        host_mem.shadow_pid
493    }
494
495    /// # Safety
496    ///
497    /// Pointer args must be safely dereferenceable.
498    #[unsafe(no_mangle)]
499    pub unsafe extern "C-unwind" fn shimshmem_getTscHz(host_mem: *const ShimShmemHost) -> u64 {
500        let host_mem = unsafe { host_mem.as_ref().unwrap() };
501        host_mem.tsc_hz
502    }
503
504    /// # Safety
505    ///
506    /// Pointer args must be safely dereferenceable.
507    #[unsafe(no_mangle)]
508    pub unsafe extern "C-unwind" fn shimshmem_getLogLevel(
509        host_mem: *const ShimShmemHost,
510    ) -> ::logger::LogLevel {
511        let host_mem = unsafe { host_mem.as_ref().unwrap() };
512        host_mem.shim_log_level
513    }
514
515    /// # Safety
516    ///
517    /// Pointer args must be safely dereferenceable.
518    #[unsafe(no_mangle)]
519    pub unsafe extern "C-unwind" fn shimshmem_getEmulatedTime(
520        host_mem: *const ShimShmemHost,
521    ) -> CEmulatedTime {
522        let host_mem = unsafe { host_mem.as_ref().unwrap() };
523        EmulatedTime::to_c_emutime(Some(host_mem.sim_time.load(Ordering::Relaxed)))
524    }
525
526    /// # Safety
527    ///
528    /// Pointer args must be safely dereferenceable.
529    #[unsafe(no_mangle)]
530    pub unsafe extern "C-unwind" fn shimshmem_setEmulatedTime(
531        host_mem: *const ShimShmemHost,
532        t: CEmulatedTime,
533    ) {
534        let host_mem = unsafe { host_mem.as_ref().unwrap() };
535        host_mem
536            .sim_time
537            .store(EmulatedTime::from_c_emutime(t).unwrap(), Ordering::Relaxed);
538    }
539
540    /// # Safety
541    ///
542    /// Pointer args must be safely dereferenceable.
543    #[unsafe(no_mangle)]
544    pub unsafe extern "C-unwind" fn shimshmem_getMaxRunaheadTime(
545        host_mem: *const ShimShmemHostLock,
546    ) -> CEmulatedTime {
547        let host_mem = unsafe { host_mem.as_ref().unwrap() };
548        EmulatedTime::to_c_emutime(Some(host_mem.max_runahead_time))
549    }
550
551    /// # Safety
552    ///
553    /// Pointer args must be safely dereferenceable.
554    #[unsafe(no_mangle)]
555    pub unsafe extern "C-unwind" fn shimshmem_setMaxRunaheadTime(
556        host_mem: *mut ShimShmemHostLock,
557        t: CEmulatedTime,
558    ) {
559        let host_mem = unsafe { host_mem.as_mut().unwrap() };
560        host_mem.max_runahead_time = EmulatedTime::from_c_emutime(t).unwrap();
561    }
562
563    /// # Safety
564    ///
565    /// Pointer args must be safely dereferenceable.
566    #[unsafe(no_mangle)]
567    pub unsafe extern "C-unwind" fn shimshmem_getProcessStraceFd(
568        process: *const ShimShmemProcess,
569    ) -> libc::c_int {
570        let process_mem = unsafe { process.as_ref().unwrap() };
571        process_mem.strace_fd.unwrap_or(-1)
572    }
573
574    /// # Safety
575    ///
576    /// Pointer args must be safely dereferenceable.
577    #[unsafe(no_mangle)]
578    pub unsafe extern "C-unwind" fn shimshmem_getSignalAction(
579        lock: *const ShimShmemHostLock,
580        process: *const ShimShmemProcess,
581        sig: i32,
582    ) -> linux_sigaction {
583        let process_mem = unsafe { process.as_ref().unwrap() };
584        let lock = unsafe { lock.as_ref().unwrap() };
585        let protected = process_mem.protected.borrow(&lock.root);
586        unsafe { sigaction::peel(*protected.signal_action(Signal::try_from(sig).unwrap())) }
587    }
588
589    /// # Safety
590    ///
591    /// Pointer args must be safely dereferenceable.
592    #[unsafe(no_mangle)]
593    pub unsafe extern "C-unwind" fn shimshmem_setSignalAction(
594        lock: *const ShimShmemHostLock,
595        process: *const ShimShmemProcess,
596        sig: i32,
597        action: *const linux_sigaction,
598    ) {
599        let process_mem = unsafe { process.as_ref().unwrap() };
600        let lock = unsafe { lock.as_ref().unwrap() };
601        let action = sigaction::wrap_ref(unsafe { action.as_ref().unwrap() });
602        let mut protected = process_mem.protected.borrow_mut(&lock.root);
603        unsafe { *protected.signal_action_mut(Signal::try_from(sig).unwrap()) = *action };
604    }
605
606    #[unsafe(no_mangle)]
607    pub extern "C-unwind" fn shimshmemthread_size() -> usize {
608        core::mem::size_of::<ThreadShmem>()
609    }
610
611    /// # Safety
612    ///
613    /// Pointer args must be safely dereferenceable.
614    #[unsafe(no_mangle)]
615    pub unsafe extern "C-unwind" fn shimshmem_getThreadId(
616        thread: *const ShimShmemThread,
617    ) -> libc::pid_t {
618        let thread_mem = unsafe { thread.as_ref().unwrap() };
619        thread_mem.tid
620    }
621
622    /// # Safety
623    ///
624    /// Pointer args must be safely dereferenceable.
625    #[unsafe(no_mangle)]
626    pub unsafe extern "C-unwind" fn shimshmem_getBlockedSignals(
627        lock: *const ShimShmemHostLock,
628        thread: *const ShimShmemThread,
629    ) -> linux_sigset_t {
630        let thread_mem = unsafe { thread.as_ref().unwrap() };
631        let lock = unsafe { lock.as_ref().unwrap() };
632        let protected = thread_mem.protected.borrow(&lock.root);
633        sigset_t::peel(protected.blocked_signals)
634    }
635
636    /// Set the process's pending signal set.
637    ///
638    /// # Safety
639    ///
640    /// Pointer args must be safely dereferenceable.
641    #[unsafe(no_mangle)]
642    pub unsafe extern "C-unwind" fn shimshmem_setBlockedSignals(
643        lock: *const ShimShmemHostLock,
644        thread: *const ShimShmemThread,
645        s: linux_sigset_t,
646    ) {
647        let thread_mem = unsafe { thread.as_ref().unwrap() };
648        let lock = unsafe { lock.as_ref().unwrap() };
649        let mut protected = thread_mem.protected.borrow_mut(&lock.root);
650        protected.blocked_signals = sigset_t::wrap(s);
651    }
652
653    /// Get the signal stack as set by `sigaltstack(2)`.
654    ///
655    /// # Safety
656    ///
657    /// Pointer args must be safely dereferenceable.
658    #[unsafe(no_mangle)]
659    pub unsafe extern "C-unwind" fn shimshmem_getSigAltStack(
660        lock: *const ShimShmemHostLock,
661        thread: *const ShimShmemThread,
662    ) -> linux_stack_t {
663        let thread_mem = unsafe { thread.as_ref().unwrap() };
664        let lock = unsafe { lock.as_ref().unwrap() };
665        let protected = thread_mem.protected.borrow(&lock.root);
666        *unsafe { protected.sigaltstack() }
667    }
668
669    /// Set the signal stack as set by `sigaltstack(2)`.
670    ///
671    /// # Safety
672    ///
673    /// Pointer args must be safely dereferenceable.
674    #[unsafe(no_mangle)]
675    pub unsafe extern "C-unwind" fn shimshmem_setSigAltStack(
676        lock: *const ShimShmemHostLock,
677        thread: *const ShimShmemThread,
678        stack: linux_stack_t,
679    ) {
680        let thread_mem = unsafe { thread.as_ref().unwrap() };
681        let lock = unsafe { lock.as_ref().unwrap() };
682        let mut protected = thread_mem.protected.borrow_mut(&lock.root);
683        *unsafe { protected.sigaltstack_mut() } = stack;
684    }
685
686    /// # Safety
687    ///
688    /// Pointer args must be safely dereferenceable.
689    #[unsafe(no_mangle)]
690    pub unsafe extern "C-unwind" fn shimshmem_incrementUnappliedCpuLatency(
691        lock: *mut ShimShmemHostLock,
692        dt: CSimulationTime,
693    ) {
694        let lock = unsafe { lock.as_mut().unwrap() };
695        lock.unapplied_cpu_latency += SimulationTime::from_c_simtime(dt).unwrap();
696    }
697
698    /// # Safety
699    ///
700    /// Pointer args must be safely dereferenceable.
701    #[unsafe(no_mangle)]
702    pub unsafe extern "C-unwind" fn shimshmem_getUnappliedCpuLatency(
703        lock: *const ShimShmemHostLock,
704    ) -> CSimulationTime {
705        let lock = unsafe { lock.as_ref().unwrap() };
706        SimulationTime::to_c_simtime(Some(lock.unapplied_cpu_latency))
707    }
708
709    /// # Safety
710    ///
711    /// Pointer args must be safely dereferenceable.
712    #[unsafe(no_mangle)]
713    pub unsafe extern "C-unwind" fn shimshmem_resetUnappliedCpuLatency(
714        lock: *mut ShimShmemHostLock,
715    ) {
716        let lock = unsafe { lock.as_mut().unwrap() };
717        lock.unapplied_cpu_latency = SimulationTime::ZERO;
718    }
719
720    /// Get whether to model latency of unblocked syscalls.
721    ///
722    /// # Safety
723    ///
724    /// Pointer args must be safely dereferenceable.
725    #[unsafe(no_mangle)]
726    pub unsafe extern "C-unwind" fn shimshmem_getModelUnblockedSyscallLatency(
727        host: *const ShimShmemHost,
728    ) -> bool {
729        let host = unsafe { host.as_ref().unwrap() };
730        host.model_unblocked_syscall_latency
731    }
732
733    /// Get the configured maximum unblocked syscall latency to accumulate before
734    /// yielding.
735    ///
736    /// # Safety
737    ///
738    /// Pointer args must be safely dereferenceable.
739    #[unsafe(no_mangle)]
740    pub unsafe extern "C-unwind" fn shimshmem_maxUnappliedCpuLatency(
741        host: *const ShimShmemHost,
742    ) -> CSimulationTime {
743        let host = unsafe { host.as_ref().unwrap() };
744        SimulationTime::to_c_simtime(Some(host.max_unapplied_cpu_latency))
745    }
746
747    /// Get the configured latency to emulate for each unblocked syscall.
748    ///
749    /// # Safety
750    ///
751    /// Pointer args must be safely dereferenceable.
752    #[unsafe(no_mangle)]
753    pub unsafe extern "C-unwind" fn shimshmem_unblockedSyscallLatency(
754        host: *const ShimShmemHost,
755    ) -> CSimulationTime {
756        let host = unsafe { host.as_ref().unwrap() };
757        SimulationTime::to_c_simtime(Some(host.unblocked_syscall_latency))
758    }
759
760    /// Get the configured latency to emulate for each unblocked vdso "syscall".
761    ///
762    /// # Safety
763    ///
764    /// Pointer args must be safely dereferenceable.
765    #[unsafe(no_mangle)]
766    pub unsafe extern "C-unwind" fn shimshmem_unblockedVdsoLatency(
767        host: *const ShimShmemHost,
768    ) -> CSimulationTime {
769        let host = unsafe { host.as_ref().unwrap() };
770        SimulationTime::to_c_simtime(Some(host.unblocked_vdso_latency))
771    }
772
773    /// Get the logging start time
774    ///
775    /// # Safety
776    ///
777    /// Pointer args must be safely dereferenceable.
778    #[unsafe(no_mangle)]
779    pub unsafe extern "C-unwind" fn shimshmem_getLoggingStartTime(
780        manager: *const ShimShmemManager,
781    ) -> i64 {
782        let manager = unsafe { manager.as_ref().unwrap() };
783        manager.log_start_time_micros
784    }
785
786    /// Get whether cpuid emulation is enabled.
787    ///
788    /// # Safety
789    ///
790    /// Pointer args must be safely dereferenceable.
791    #[unsafe(no_mangle)]
792    pub unsafe extern "C-unwind" fn shimshmem_getEmulateCpuid(
793        manager: *const ShimShmemManager,
794    ) -> bool {
795        let manager = unsafe { manager.as_ref().unwrap() };
796        manager.emulate_cpuid
797    }
798}