shadow_shim/
lib.rs

1#![cfg_attr(not(test), no_std)]
2// https://github.com/rust-lang/rfcs/blob/master/text/2585-unsafe-block-in-unsafe-fn.md
3#![deny(unsafe_op_in_unsafe_fn)]
4
5use core::cell::{Cell, RefCell};
6use core::ffi::CStr;
7use core::mem::MaybeUninit;
8
9use crate::tls::ShimTlsVar;
10
11use linux_api::signal::{SigProcMaskAction, rt_sigprocmask};
12use num_enum::{IntoPrimitive, TryFromPrimitive};
13use shadow_shim_helper_rs::ipc::IPCData;
14use shadow_shim_helper_rs::shim_event::{ShimEventStartReq, ShimEventToShadow, ShimEventToShim};
15use shadow_shim_helper_rs::shim_shmem::{HostShmem, ManagerShmem, ProcessShmem, ThreadShmem};
16use shadow_shim_helper_rs::simulation_time::SimulationTime;
17use shadow_shim_helper_rs::syscall_types::ForeignPtr;
18use shadow_shmem::allocator::{ShMemBlockAlias, ShMemBlockSerialized, shdeserialize};
19use tls::ThreadLocalStorage;
20use vasi_sync::lazy_lock::LazyLock;
21use vasi_sync::scmutex::SelfContainedMutex;
22
23/// cbindgen:ignore
24mod bindings {
25    #![allow(unused)]
26    #![allow(non_upper_case_globals)]
27    #![allow(non_camel_case_types)]
28    #![allow(non_snake_case)]
29    // https://github.com/rust-lang/rust/issues/66220
30    #![allow(improper_ctypes)]
31    include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
32}
33
34pub mod clone;
35pub mod mmap_box;
36pub mod preempt;
37pub mod reinit_auxvec_random;
38pub mod shimlogger;
39pub mod syscall;
40pub mod tls;
41
42pub use shimlogger::export as shimlogger_export;
43
44pub mod signals;
45
46pub fn simtime() -> Option<SimulationTime> {
47    SimulationTime::from_c_simtime(unsafe { bindings::shim_sys_get_simtime_nanos() })
48}
49
50/// Values of this enum describes whether something occurred within the context
51/// of the shadow (shim) code, or the application/plugin code.
52///
53/// Methods of this enum interact with a private thread-local that tracks the
54/// current `ExecutionContext` for that thread.
55// See `CURRENT_EXECUTION_CONTEXT`.
56#[derive(Debug, Copy, Clone, Eq, PartialEq, IntoPrimitive, TryFromPrimitive)]
57#[repr(u8)]
58pub enum ExecutionContext {
59    // Redirect through constants below so that we can access in const contexts.
60    Shadow = EXECUTION_CONTEXT_SHADOW_CONST,
61    Application = EXECUTION_CONTEXT_APPLICATION_CONST,
62}
63
64pub const EXECUTION_CONTEXT_SHADOW_CONST: u8 = 0;
65pub const EXECUTION_CONTEXT_APPLICATION_CONST: u8 = 1;
66
67/// The `ExecutionContext` of the current thread. Should only be manipulated via
68/// methods of `ExecutionContext`.
69static CURRENT_EXECUTION_CONTEXT: ShimTlsVar<Cell<ExecutionContext>> =
70    ShimTlsVar::new(&SHIM_TLS, || Cell::new(ExecutionContext::Application));
71
72impl ExecutionContext {
73    /// Returns the current context for the current thread.
74    pub fn current() -> ExecutionContext {
75        CURRENT_EXECUTION_CONTEXT.get().get()
76    }
77
78    /// Enter this context for the current thread, and return a restorer that
79    /// will restore the previous context when dropped.
80    pub fn enter(&self) -> ExecutionContextRestorer {
81        ExecutionContextRestorer {
82            prev: self.enter_without_restorer(),
83        }
84    }
85
86    /// Enter this context for the current thread, *without* creating a
87    /// restorer. Returns the previous context.
88    pub fn enter_without_restorer(&self) -> ExecutionContext {
89        let current_execution_ctx = CURRENT_EXECUTION_CONTEXT.get();
90        let peeked_prev = current_execution_ctx.get();
91
92        // Potentially enable/disable preemption, being careful that the current
93        // context is set to shadow when calling other internal functions that
94        // require it.
95        let replaced_prev = match (peeked_prev, *self) {
96            (ExecutionContext::Shadow, ExecutionContext::Application) => {
97                // Call preempt::enable before changing context from shadow, so
98                // that it can access shim state.
99                // SAFETY: We only ever switch threads from the shadow execution
100                // context, and we disable preemption when entering the shadow
101                // execution context, so preemption should be disabled for all
102                // other threads in this process.
103                unsafe { preempt::enable() };
104                current_execution_ctx.replace(*self)
105            }
106            (ExecutionContext::Application, ExecutionContext::Shadow) => {
107                // Change context to shadow before calling preempt::disable, so
108                // that it can access shim state.
109                let c = current_execution_ctx.replace(*self);
110                preempt::disable();
111                c
112            }
113            (ExecutionContext::Application, ExecutionContext::Application) => {
114                // No need to actually replace.
115                ExecutionContext::Application
116            }
117            (ExecutionContext::Shadow, ExecutionContext::Shadow) => {
118                // No need to actually replace.
119                ExecutionContext::Shadow
120            }
121        };
122        // It *shouldn't* be possible for the execution context to have changed
123        // out from under us in between the initial peek and the actual
124        // replacement.
125        assert_eq!(peeked_prev, replaced_prev);
126        peeked_prev
127    }
128}
129
130/// Restores an execution context when droped.
131#[must_use]
132#[derive(Debug)]
133pub struct ExecutionContextRestorer {
134    prev: ExecutionContext,
135}
136
137impl ExecutionContextRestorer {
138    /// Returns the context that this object will restore.
139    pub fn ctx(&self) -> ExecutionContext {
140        self.prev
141    }
142}
143
144impl Drop for ExecutionContextRestorer {
145    fn drop(&mut self) {
146        ExecutionContext::enter_without_restorer(&self.prev);
147    }
148}
149
150// We use a page for a stack guard, and up to another page to page-align the
151// stack guard. We assume 4k pages here but detect at runtime if this is too small.
152const SHIM_SIGNAL_STACK_GUARD_OVERHEAD: usize = 4096 * 2;
153
154mod tls_thread_signal_stack {
155    use super::*;
156
157    static THREAD_SIGNAL_STACK: ShimTlsVar<Cell<*mut core::ffi::c_void>> =
158        ShimTlsVar::new(&SHIM_TLS, || Cell::new(core::ptr::null_mut()));
159
160    // Shouldn't need to make this very large, but needs to be big enough to run the
161    // managed process's signal handlers as well - possibly recursively.
162    //
163    // Stack space that's *never* used shouldn't ever become resident, but an
164    // occasional deep stack could force the pages to be resident ever after.  To
165    // mitigate that, we could consider `madvise(MADV_DONTNEED)` after running
166    // signal handlers, to let the OS reclaim the (now-popped) signal handler stack
167    // frames.
168    const SHIM_SIGNAL_STACK_MIN_USABLE_SIZE: usize = 1024 * 100;
169    const SHIM_SIGNAL_STACK_SIZE: usize =
170        SHIM_SIGNAL_STACK_GUARD_OVERHEAD + SHIM_SIGNAL_STACK_MIN_USABLE_SIZE;
171
172    /// Allocates and installs a signal stack. This is to ensure that our
173    /// signal handlers have enough stack space; otherwise we can run out in managed
174    /// processes that use small stacks.
175    ///
176    /// This should be called once per thread before any signal handlers run.
177    /// Panics if already called on the current thread.
178    pub fn init() {
179        debug_assert_eq!(ExecutionContext::current(), ExecutionContext::Shadow);
180        if THREAD_SIGNAL_STACK.get().get().is_null() {
181            // Allocate
182            let new_stack = unsafe {
183                rustix::mm::mmap_anonymous(
184                    core::ptr::null_mut(),
185                    SHIM_SIGNAL_STACK_SIZE,
186                    rustix::mm::ProtFlags::READ | rustix::mm::ProtFlags::WRITE,
187                    rustix::mm::MapFlags::PRIVATE,
188                )
189            }
190            .unwrap();
191
192            // Save to thread-local, so that we can deallocate on thread exit
193            assert!(
194                THREAD_SIGNAL_STACK.get().replace(new_stack).is_null(),
195                "Allocated signal stack twice for current thread"
196            );
197
198            // Set up guard page
199            unsafe { rustix::mm::mprotect(new_stack, 4096, rustix::mm::MprotectFlags::empty()) }
200                .unwrap();
201        } else {
202            // We get here after forking.
203            //
204            // We still have the signal stack allocated in the new process.
205            // We still need to install it though, below.
206        }
207
208        // Install via `sigaltstack`. The kernel will switch to this stack when
209        // invoking one of our signal handlers.
210        let stack_descriptor = linux_api::signal::stack_t {
211            ss_sp: THREAD_SIGNAL_STACK.get().get(),
212            ss_size: SHIM_SIGNAL_STACK_SIZE.try_into().unwrap(),
213            // Clear the alternate stack settings on entry to signal handler, and
214            // restore it on exit.  Otherwise a signal handler invoked while another
215            // is running on the same thread would clobber the first handler's stack.
216            // Instead we want the second handler to push a new frame on the alt
217            // stack that's already installed.
218            ss_flags: linux_api::signal::SigAltStackFlags::SS_AUTODISARM.bits(),
219        };
220        unsafe {
221            linux_api::signal::sigaltstack(Some(&stack_descriptor), None).unwrap();
222        }
223    }
224
225    /// # Safety
226    ///
227    /// After calling this function, the current thread must ensure the following
228    /// sequence can't happen before exiting:
229    ///
230    /// * Another thread runs.
231    /// * That thread also frees its stack.
232    /// * This thread runs again on the signal stack (e.g. by handling a new signal).
233    ///
234    /// Generally in the shim we rely on Shadow's scheduling model to ensure
235    /// this, since we know Shadow won't permit another thread to run
236    /// preemptively before the curent thread has a chance to finish exiting.
237    pub unsafe fn free() {
238        debug_assert_eq!(ExecutionContext::current(), ExecutionContext::Shadow);
239        // A signal stack waiting to be freed.
240        //
241        // We can't free the current thread's signal stack, since we may be running on it.
242        // Instead we save the pointer to this global, and free the pointer that was already
243        // there, if any.
244        //
245        // We smuggle the pointer through as a `usize`, since pointers aren't `Sync`.
246        static FREE_SIGNAL_STACK: SelfContainedMutex<usize> = SelfContainedMutex::const_new(0);
247
248        let mut free_signal_stack = FREE_SIGNAL_STACK.lock();
249        let this_thread_stack = THREAD_SIGNAL_STACK.get().replace(core::ptr::null_mut());
250        let stack_to_free_now =
251            core::mem::replace(&mut *free_signal_stack, this_thread_stack as usize);
252        if stack_to_free_now != 0 {
253            unsafe {
254                rustix::mm::munmap(
255                    stack_to_free_now as *mut core::ffi::c_void,
256                    SHIM_SIGNAL_STACK_SIZE,
257                )
258            }
259            .unwrap();
260        }
261    }
262}
263
264/// Per-thread IPC channel between the shim, running in a managed process, and
265/// the shadow process.
266mod tls_ipc {
267    use super::*;
268    static IPC_DATA_BLOCK: ShimTlsVar<RefCell<Option<ShMemBlockAlias<IPCData>>>> =
269        ShimTlsVar::new(&SHIM_TLS, || RefCell::new(None));
270
271    // Panics if this thread's IPC hasn't been initialized yet.
272    pub fn with<O>(f: impl FnOnce(&IPCData) -> O) -> O {
273        debug_assert_eq!(ExecutionContext::current(), ExecutionContext::Shadow);
274        let ipc = IPC_DATA_BLOCK.get();
275        let ipc = ipc.borrow();
276        ipc.as_ref().map(|block| f(block)).unwrap()
277    }
278
279    /// The previous value, if any, is dropped.
280    ///
281    /// # Safety
282    ///
283    /// `blk` must contained a serialized block referencing a `ShMemBlock` of type `IPCData`.
284    /// The `ShMemBlock` must outlive the current thread.
285    pub unsafe fn set(blk: &ShMemBlockSerialized) {
286        debug_assert_eq!(ExecutionContext::current(), ExecutionContext::Shadow);
287        let blk: ShMemBlockAlias<IPCData> = unsafe { shdeserialize(blk) };
288        IPC_DATA_BLOCK.get().replace(Some(blk));
289    }
290}
291
292mod tls_thread_shmem {
293    use super::*;
294
295    static SHMEM: ShimTlsVar<RefCell<Option<ShMemBlockAlias<ThreadShmem>>>> =
296        ShimTlsVar::new(&SHIM_TLS, || RefCell::new(None));
297
298    /// Panics if `set` hasn't been called yet.
299    pub fn with<O>(f: impl FnOnce(&ThreadShmem) -> O) -> O {
300        debug_assert_eq!(ExecutionContext::current(), ExecutionContext::Shadow);
301        f(SHMEM.get().borrow().as_ref().unwrap())
302    }
303
304    /// The previous value, if any, is dropped.
305    ///
306    /// # Safety
307    ///
308    /// `blk` must contained a serialized block referencing a `ShMemBlock` of
309    /// type `ThreadShmem`.  The `ShMemBlock` must outlive the current thread.
310    pub unsafe fn set(blk: &ShMemBlockSerialized) {
311        debug_assert_eq!(ExecutionContext::current(), ExecutionContext::Shadow);
312        // SAFETY: Caller guarantees correct type.
313        let blk = unsafe { shdeserialize(blk) };
314        SHMEM.get().borrow_mut().replace(blk);
315    }
316}
317
318mod global_manager_shmem {
319    use super::*;
320
321    // This is set explicitly, so needs a Mutex.
322    static INITIALIZER: SelfContainedMutex<Option<ShMemBlockSerialized>> =
323        SelfContainedMutex::const_new(None);
324
325    // The actual block is in a `LazyLock`, which is much faster to access.
326    // It uses `INITIALIZER` to do its one-time init.
327    static SHMEM: LazyLock<ShMemBlockAlias<ManagerShmem>> = LazyLock::const_new(|| {
328        debug_assert_eq!(ExecutionContext::current(), ExecutionContext::Shadow);
329        let serialized = INITIALIZER.lock().take().unwrap();
330        unsafe { shdeserialize(&serialized) }
331    });
332
333    /// # Safety
334    ///
335    /// `blk` must contained a serialized block referencing a `ShMemBlock` of type `ManagerShmem`.
336    /// The `ShMemBlock` must outlive this process.
337    pub unsafe fn set(blk: &ShMemBlockSerialized) {
338        debug_assert_eq!(ExecutionContext::current(), ExecutionContext::Shadow);
339        assert!(!SHMEM.initd());
340        assert!(INITIALIZER.lock().replace(*blk).is_none());
341        // Ensure that `try_get` returns true (without it having to take the
342        // `INITIALIZER` lock to check), and that we fail early if `SHMEM` can't
343        // actually be initialized.
344        SHMEM.force();
345    }
346
347    /// Panics if `set` hasn't been called yet.
348    pub fn get() -> impl core::ops::Deref<Target = ShMemBlockAlias<'static, ManagerShmem>> + 'static
349    {
350        debug_assert_eq!(ExecutionContext::current(), ExecutionContext::Shadow);
351        SHMEM.force()
352    }
353
354    pub fn try_get()
355    -> Option<impl core::ops::Deref<Target = ShMemBlockAlias<'static, ManagerShmem>> + 'static>
356    {
357        debug_assert_eq!(ExecutionContext::current(), ExecutionContext::Shadow);
358        if !SHMEM.initd() {
359            // No need to do the more-expensive `INITIALIZER` check; `set`
360            // forces `SHMEM` to initialize.
361            None
362        } else {
363            Some(get())
364        }
365    }
366}
367
368mod global_host_shmem {
369    use super::*;
370
371    // This is set explicitly, so needs a Mutex.
372    static INITIALIZER: SelfContainedMutex<Option<ShMemBlockSerialized>> =
373        SelfContainedMutex::const_new(None);
374
375    // The actual block is in a `LazyLock`, which is much faster to access.
376    // It uses `INITIALIZER` to do its one-time init.
377    static SHMEM: LazyLock<ShMemBlockAlias<HostShmem>> = LazyLock::const_new(|| {
378        debug_assert_eq!(ExecutionContext::current(), ExecutionContext::Shadow);
379        let serialized = INITIALIZER.lock().take().unwrap();
380        unsafe { shdeserialize(&serialized) }
381    });
382
383    /// # Safety
384    ///
385    /// `blk` must contained a serialized block referencing a `ShMemBlock` of type `HostShmem`.
386    /// The `ShMemBlock` must outlive this process.
387    pub unsafe fn set(blk: &ShMemBlockSerialized) {
388        debug_assert_eq!(ExecutionContext::current(), ExecutionContext::Shadow);
389        assert!(!SHMEM.initd());
390        assert!(INITIALIZER.lock().replace(*blk).is_none());
391        // Ensure that `try_get` returns true (without it having to take the
392        // `INITIALIZER` lock to check), and that we fail early if `SHMEM` can't
393        // actually be initialized.
394        SHMEM.force();
395    }
396
397    /// Panics if `set` hasn't been called yet.
398    pub fn get() -> impl core::ops::Deref<Target = ShMemBlockAlias<'static, HostShmem>> + 'static {
399        debug_assert_eq!(ExecutionContext::current(), ExecutionContext::Shadow);
400        SHMEM.force()
401    }
402
403    pub fn try_get()
404    -> Option<impl core::ops::Deref<Target = ShMemBlockAlias<'static, HostShmem>> + 'static> {
405        debug_assert_eq!(ExecutionContext::current(), ExecutionContext::Shadow);
406        if !SHMEM.initd() {
407            // No need to do the more-expensive `INITIALIZER` check; `set`
408            // forces `SHMEM` to initialize.
409            None
410        } else {
411            Some(get())
412        }
413    }
414}
415
416mod tls_process_shmem {
417    use super::*;
418
419    static SHMEM: ShimTlsVar<RefCell<Option<ShMemBlockAlias<ProcessShmem>>>> =
420        ShimTlsVar::new(&SHIM_TLS, || RefCell::new(None));
421
422    /// Panics if `set` hasn't been called yet.
423    pub fn with<O>(f: impl FnOnce(&ProcessShmem) -> O) -> O {
424        debug_assert_eq!(ExecutionContext::current(), ExecutionContext::Shadow);
425        f(SHMEM.get().borrow().as_ref().unwrap())
426    }
427
428    /// The previous value, if any, is dropped.
429    ///
430    /// # Safety
431    ///
432    /// `blk` must contained a serialized block referencing a `ShMemBlock` of
433    /// type `ProcessShmem`.  The `ShMemBlock` must outlive the current thread.
434    pub unsafe fn set(blk: &ShMemBlockSerialized) {
435        debug_assert_eq!(ExecutionContext::current(), ExecutionContext::Shadow);
436        // SAFETY: Caller guarantees correct type.
437        let blk = unsafe { shdeserialize(blk) };
438        SHMEM.get().borrow_mut().replace(blk);
439    }
440}
441
442// Force cargo to link against crates that aren't (yet) referenced from Rust
443// code (but are referenced from this crate's C code).
444// https://github.com/rust-lang/cargo/issues/9391
445extern crate log_c2rust;
446extern crate logger;
447extern crate shadow_shim_helper_rs;
448extern crate shadow_shmem;
449extern crate shadow_tsc;
450
451/// Global instance of thread local storage for use in the shim.
452///
453/// SAFETY: We ensure that every thread unregisters itself before exiting,
454/// via [`release_and_exit_current_thread`].
455static SHIM_TLS: ThreadLocalStorage = unsafe { ThreadLocalStorage::new(tls::Mode::Native) };
456
457/// Release this thread's shim thread local storage and exit the thread.
458///
459/// Should be called by every thread that accesses thread local storage.
460///
461/// Panics if there are still any live references to this thread's [`ShimTlsVar`]s.
462///
463/// # Safety
464///
465/// In the case that this function somehow panics, caller must not
466/// access thread local storage again from the current thread, e.g.
467/// using `std::panic::catch_unwind` or a custom panic hook.
468pub unsafe fn release_and_exit_current_thread(exit_status: i32) -> ! {
469    debug_assert_eq!(ExecutionContext::current(), ExecutionContext::Shadow);
470    // Block all signals, to ensure a signal handler can't run and attempt to
471    // access thread local storage.
472    rt_sigprocmask(
473        SigProcMaskAction::SIG_BLOCK,
474        &linux_api::signal::sigset_t::FULL,
475        None,
476    )
477    .unwrap();
478
479    // SAFETY: No code can access thread local storage in between deregistration
480    // and exit, unless `unregister_curren_thread` itself panics.
481    unsafe { SHIM_TLS.unregister_current_thread() }
482
483    linux_api::exit::exit_raw(exit_status).unwrap();
484    unreachable!()
485}
486
487/// Perform once-per-thread initialization for the shim.
488///
489/// Unlike `init_process` this must only be called once - we do so explicitly
490/// when creating a new managed thread.
491///
492/// Uses C ABI so that we can call from `asm`.
493extern "C" fn init_thread() {
494    debug_assert_eq!(ExecutionContext::current(), ExecutionContext::Shadow);
495    unsafe { bindings::_shim_child_thread_init_preload() };
496    log::trace!("Finished shim thread init");
497}
498
499/// Ensure once-per-process init for the shim is done.
500///
501/// Safe and cheap to call repeatedly; e.g. from API entry points.
502fn init_process() {
503    debug_assert_eq!(ExecutionContext::current(), ExecutionContext::Shadow);
504    static STARTED_INIT: LazyLock<()> = LazyLock::const_new(|| ());
505    if STARTED_INIT.initd() {
506        // Avoid recursion in initialization.
507        //
508        // TODO: This shouldn't be necessary once we've gotten rid of all
509        // calls to libc from the shim's initialization.
510        return;
511    }
512    STARTED_INIT.force();
513
514    unsafe { bindings::_shim_parent_init_preload() };
515    log::trace!("Finished shim global init");
516}
517
518/// Wait for "start" event from Shadow, using it to set things up for the
519/// current thread, and if `is_first_thread` is true then also for the current
520/// process.
521fn wait_for_start_event(is_first_thread: bool) {
522    debug_assert_eq!(ExecutionContext::current(), ExecutionContext::Shadow);
523    log::trace!("waiting for start event");
524
525    let mut working_dir = [0u8; linux_api::limits::PATH_MAX];
526    let working_dir_ptr;
527    let working_dir_len;
528    if is_first_thread {
529        working_dir_ptr = ForeignPtr::from_raw_ptr(working_dir.as_mut_ptr());
530        working_dir_len = working_dir.len();
531    } else {
532        working_dir_ptr = ForeignPtr::null();
533        working_dir_len = 0;
534    }
535
536    let mut thread_blk_serialized = MaybeUninit::<ShMemBlockSerialized>::uninit();
537    let mut process_blk_serialized = MaybeUninit::<ShMemBlockSerialized>::uninit();
538    let start_req = ShimEventToShadow::StartReq(ShimEventStartReq {
539        thread_shmem_block_to_init: ForeignPtr::from_raw_ptr(thread_blk_serialized.as_mut_ptr()),
540        process_shmem_block_to_init: ForeignPtr::from_raw_ptr(process_blk_serialized.as_mut_ptr()),
541        initial_working_dir_to_init: working_dir_ptr,
542        initial_working_dir_to_init_len: working_dir_len,
543    });
544    let res = tls_ipc::with(|ipc| {
545        ipc.to_shadow().send(start_req);
546        ipc.from_shadow().receive().unwrap()
547    });
548    let ShimEventToShim::StartRes(res) = res else {
549        panic!("Unexpected response: {res:?}");
550    };
551    if is_first_thread {
552        // SAFETY: We're ensuring serial execution in this process, and no other
553        // Rust code in this library should have tried accessing the auxiliary
554        // vector yet, so no references should exist.
555        //
556        // WARNING: It's possible that the dynamic linker/loader or constructors
557        // in other dynamically linked libraries *have* run, and that rewriting
558        // this value here will violate safety assumptions in those objects.
559        // Fortunately we haven't observed this in practice.
560        unsafe { reinit_auxvec_random::reinit_auxvec_random(&res.auxvec_random) };
561    }
562
563    // SAFETY: shadow should have initialized
564    let thread_blk_serialized = unsafe { thread_blk_serialized.assume_init() };
565    // SAFETY: blk should be of the correct type and outlive this thread.
566    unsafe { tls_thread_shmem::set(&thread_blk_serialized) };
567
568    // SAFETY: shadow should have initialized
569    let process_blk_serialized = unsafe { process_blk_serialized.assume_init() };
570    // SAFETY: blk should be of the correct type and outlive this process.
571    unsafe { tls_process_shmem::set(&process_blk_serialized) };
572
573    // TODO: Instead use posix_spawn_file_actions_addchdir_np in the shadow process,
574    // which was added in glibc 2.29. Currently this is blocked on debian-10, which
575    // uses glibc 2.28.
576    if is_first_thread {
577        let working_dir = CStr::from_bytes_until_nul(&working_dir).unwrap();
578        rustix::process::chdir(working_dir).unwrap();
579    }
580}
581
582// Rust's linking of a `cdylib` only considers Rust `pub extern "C-unwind"` entry
583// points, and the symbols those recursively used, to be used. i.e. any function
584// called from outside of the shim needs to be exported from the Rust code. We
585// wrap some C implementations here.
586pub mod export {
587    use core::ops::Deref;
588
589    use super::*;
590
591    /// # Safety
592    ///
593    /// The syscall itself must be safe to make.
594    #[unsafe(no_mangle)]
595    pub unsafe extern "C-unwind" fn shim_api_syscall(
596        n: core::ffi::c_long,
597        arg1: u64,
598        arg2: u64,
599        arg3: u64,
600        arg4: u64,
601        arg5: u64,
602        arg6: u64,
603    ) -> i64 {
604        let _prev = ExecutionContext::Shadow.enter();
605        unsafe {
606            bindings::shimc_api_syscall(_prev.ctx().into(), n, arg1, arg2, arg3, arg4, arg5, arg6)
607        }
608    }
609
610    /// # Safety
611    ///
612    /// Pointers must be dereferenceable.
613    #[unsafe(no_mangle)]
614    pub unsafe extern "C-unwind" fn shim_api_getaddrinfo(
615        node: *const core::ffi::c_char,
616        service: *const core::ffi::c_char,
617        hints: *const libc::addrinfo,
618        res: *mut *mut libc::addrinfo,
619    ) -> i32 {
620        let _prev = ExecutionContext::Shadow.enter();
621        unsafe { bindings::shimc_api_getaddrinfo(node, service, hints, res) }
622    }
623
624    /// # Safety
625    ///
626    /// * Pointers must be dereferenceable.
627    /// * `res` is invalidated afterwards.
628    #[unsafe(no_mangle)]
629    pub unsafe extern "C-unwind" fn shim_api_freeaddrinfo(res: *mut libc::addrinfo) {
630        let _prev = ExecutionContext::Shadow.enter();
631        unsafe { bindings::shimc_api_freeaddrinfo(res) }
632    }
633
634    /// # Safety
635    ///
636    /// Pointers must be dereferenceable
637    #[unsafe(no_mangle)]
638    pub unsafe extern "C-unwind" fn shim_api_getifaddrs(ifap: *mut *mut libc::ifaddrs) -> i32 {
639        // We *don't* enter ExecutionContext::Shadow here, because this
640        // implementation is pure "userspace"; it doesn't directly access shadow
641        // internal functionality, but *does* use libc in a way that we want the
642        // underlying syscalls to be interposed.
643        unsafe { bindings::shimc_api_getifaddrs(ifap) }
644    }
645
646    /// # Safety
647    ///
648    /// * Pointers must be dereferenceable.
649    /// * `ifa` is invalidated afterwards.
650    #[unsafe(no_mangle)]
651    pub unsafe extern "C-unwind" fn shim_api_freeifaddrs(ifa: *mut libc::ifaddrs) {
652        // We *don't* enter ExecutionContext::Shadow here, because this
653        // implementation is pure "userspace"; it doesn't directly access shadow
654        // internal functionality, but *does* use libc in a way that we want the
655        // underlying syscalls to be interposed.
656        unsafe { bindings::shimc_api_freeifaddrs(ifa) }
657    }
658
659    /// Sets the flag determining whether syscalls are passed through natively, and
660    /// returns the old value.
661    ///
662    /// Typical usage is to set this to the desired value at the beginning of an
663    /// operation, and restore the old value afterwards.
664    #[unsafe(no_mangle)]
665    pub extern "C-unwind" fn shim_swapExecutionContext(new: ExecutionContext) -> ExecutionContext {
666        new.enter_without_restorer()
667    }
668
669    /// Whether syscall interposition is currently enabled.
670    #[unsafe(no_mangle)]
671    pub extern "C-unwind" fn shim_getExecutionContext() -> ExecutionContext {
672        ExecutionContext::current()
673    }
674
675    /// Allocates and installs a signal stack.
676    ///
677    /// This is to ensure that our signal handlers have enough stack space;
678    /// otherwise we can run out in managed processes that use small stacks.
679    ///
680    /// This should be called once per thread before any signal handlers run.
681    /// Panics if already called on the current thread.
682    #[unsafe(no_mangle)]
683    pub extern "C-unwind" fn _shim_init_signal_stack() {
684        tls_thread_signal_stack::init();
685    }
686
687    /// # Safety
688    ///
689    /// The current thread must exit before:
690    /// * Another thread runs.
691    /// * That thread also frees its stack.
692    /// * This thread runs again on the signal stack (e.g. by handling a new signal).
693    ///
694    /// Generally in the shim we rely on Shadow's scheduling model to ensure
695    /// this, since we know Shadow won't permit another thread to run
696    /// preemptively before the curent thread has a chance to finish exiting.
697    #[unsafe(no_mangle)]
698    pub unsafe extern "C-unwind" fn shim_freeSignalStack() {
699        unsafe { tls_thread_signal_stack::free() };
700    }
701
702    /// # Safety
703    ///
704    /// stdin must contained a serialized block of
705    /// type `IPCData`, which outlives the current thread.
706    #[unsafe(no_mangle)]
707    pub unsafe extern "C-unwind" fn _shim_parent_init_ipc() {
708        let mut bytes = [0; core::mem::size_of::<ShMemBlockSerialized>()];
709        let bytes_read = rustix::io::read(
710            unsafe { rustix::fd::BorrowedFd::borrow_raw(libc::STDIN_FILENO) },
711            &mut bytes,
712        )
713        .unwrap();
714        // Implement looping? We should get it all in one read, though.
715        assert_eq!(bytes_read, bytes.len());
716        let ipc_blk = shadow_pod::from_array(&bytes);
717        // SAFETY: caller is responsible for `set`'s preconditions.
718        unsafe { tls_ipc::set(&ipc_blk) };
719    }
720
721    /// This thread's IPC channel. Panics if it hasn't been initialized yet.
722    ///
723    /// # Safety
724    ///
725    /// The returned pointer must not outlive the current thread.
726    #[unsafe(no_mangle)]
727    pub unsafe extern "C-unwind" fn shim_thisThreadEventIPC() -> *const IPCData {
728        tls_ipc::with(core::ptr::from_ref)
729    }
730
731    /// This thread's IPC channel. Panics if it hasn't been initialized yet.
732    ///
733    /// # Safety
734    ///
735    /// The returned pointer must not outlive the current thread.
736    #[unsafe(no_mangle)]
737    pub unsafe extern "C-unwind" fn shim_threadSharedMem()
738    -> *const shadow_shim_helper_rs::shim_shmem::export::ShimShmemThread {
739        tls_thread_shmem::with(core::ptr::from_ref)
740    }
741
742    #[unsafe(no_mangle)]
743    pub extern "C-unwind" fn _shim_load() {
744        init_process();
745    }
746
747    /// Should be used to exit every thread in the shim.
748    ///
749    /// # Safety
750    ///
751    /// In the case that this function somehow panics, caller must not
752    /// access thread local storage again from the current thread, e.g.
753    /// using `std::panic::catch_unwind` or a custom panic hook.
754    #[unsafe(no_mangle)]
755    pub unsafe extern "C-unwind" fn shim_release_and_exit_current_thread(status: i32) {
756        unsafe { release_and_exit_current_thread(status) }
757    }
758
759    #[unsafe(no_mangle)]
760    pub extern "C-unwind" fn shim_managerSharedMem()
761    -> *const shadow_shim_helper_rs::shim_shmem::export::ShimShmemManager {
762        let rv = global_manager_shmem::try_get();
763        rv.map(|x| {
764            let rv: &shadow_shim_helper_rs::shim_shmem::export::ShimShmemManager = x.deref();
765            // We know this pointer will be live for the lifetime of the
766            // process, and that we never construct a mutable reference to the
767            // underlying data.
768            core::ptr::from_ref(rv)
769        })
770        .unwrap_or(core::ptr::null())
771    }
772
773    #[unsafe(no_mangle)]
774    pub extern "C-unwind" fn shim_hostSharedMem()
775    -> *const shadow_shim_helper_rs::shim_shmem::export::ShimShmemHost {
776        let rv = global_host_shmem::try_get();
777        rv.map(|x| {
778            let rv: &shadow_shim_helper_rs::shim_shmem::export::ShimShmemHost = x.deref();
779            // We know this pointer will be live for the lifetime of the
780            // process, and that we never construct a mutable reference to the
781            // underlying data.
782            core::ptr::from_ref(rv)
783        })
784        .unwrap_or(core::ptr::null())
785    }
786
787    #[unsafe(no_mangle)]
788    pub extern "C-unwind" fn shim_processSharedMem()
789    -> *const shadow_shim_helper_rs::shim_shmem::export::ShimShmemProcess {
790        tls_process_shmem::with(|process| {
791            // We know this pointer will be live for the lifetime of the
792            // process, and that we never construct a mutable reference to the
793            // underlying data.
794            core::ptr::from_ref(process)
795        })
796    }
797
798    /// Wait for start event from shadow, from a newly spawned thread.
799    #[unsafe(no_mangle)]
800    pub extern "C-unwind" fn _shim_preload_only_child_ipc_wait_for_start_event() {
801        wait_for_start_event(false);
802    }
803
804    #[unsafe(no_mangle)]
805    pub extern "C-unwind" fn _shim_ipc_wait_for_start_event() {
806        wait_for_start_event(true);
807    }
808
809    #[unsafe(no_mangle)]
810    pub extern "C-unwind" fn _shim_parent_init_manager_shm() {
811        unsafe { global_manager_shmem::set(&global_host_shmem::get().manager_shmem) }
812    }
813
814    #[unsafe(no_mangle)]
815    pub extern "C-unwind" fn _shim_parent_init_host_shm() {
816        tls_process_shmem::with(|process| unsafe { global_host_shmem::set(&process.host_shmem) });
817    }
818
819    #[unsafe(no_mangle)]
820    pub extern "C-unwind" fn _shim_parent_close_stdin() {
821        unsafe { rustix::io::close(libc::STDIN_FILENO) };
822    }
823}