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