shadow_rs/host/
process.rs

1//! An emulated Linux process.
2
3use std::cell::{Cell, Ref, RefCell, RefMut};
4use std::collections::BTreeMap;
5use std::ffi::{CStr, CString, c_char, c_void};
6use std::fmt::Write;
7use std::num::TryFromIntError;
8use std::ops::{Deref, DerefMut};
9use std::os::fd::AsRawFd;
10use std::path::{Path, PathBuf};
11use std::sync::Arc;
12use std::sync::atomic::Ordering;
13#[cfg(feature = "perf_timers")]
14use std::time::Duration;
15
16use linux_api::errno::Errno;
17use linux_api::fcntl::OFlag;
18use linux_api::posix_types::Pid;
19use linux_api::sched::{CloneFlags, SuidDump};
20use linux_api::signal::{
21    LinuxDefaultAction, SigActionFlags, Signal, SignalFromI32Error, defaultaction, siginfo_t,
22    sigset_t,
23};
24use log::{debug, trace, warn};
25use rustix::process::{WaitOptions, WaitStatus};
26use shadow_shim_helper_rs::HostId;
27use shadow_shim_helper_rs::explicit_drop::{ExplicitDrop, ExplicitDropper};
28use shadow_shim_helper_rs::rootedcell::Root;
29use shadow_shim_helper_rs::rootedcell::rc::RootedRc;
30use shadow_shim_helper_rs::rootedcell::refcell::RootedRefCell;
31use shadow_shim_helper_rs::shim_shmem::ProcessShmem;
32use shadow_shim_helper_rs::simulation_time::SimulationTime;
33use shadow_shim_helper_rs::syscall_types::{ForeignPtr, ManagedPhysicalMemoryAddr};
34use shadow_shmem::allocator::ShMemBlock;
35
36use super::descriptor::descriptor_table::{DescriptorHandle, DescriptorTable};
37use super::descriptor::listener::StateEventSource;
38use super::descriptor::{FileSignals, FileState};
39use super::host::Host;
40use super::memory_manager::{MemoryManager, ProcessMemoryRef, ProcessMemoryRefMut};
41use super::syscall::formatter::StraceFmtMode;
42use super::syscall::types::ForeignArrayPtr;
43use super::thread::{Thread, ThreadId};
44use super::timer::Timer;
45use crate::core::configuration::{ProcessFinalState, RunningVal};
46use crate::core::work::task::TaskRef;
47use crate::core::worker::Worker;
48use crate::cshadow;
49use crate::host::context::ProcessContext;
50use crate::host::descriptor::Descriptor;
51use crate::host::managed_thread::ManagedThread;
52use crate::host::syscall::formatter::FmtOptions;
53use crate::utility::callback_queue::CallbackQueue;
54#[cfg(feature = "perf_timers")]
55use crate::utility::perf_timer::PerfTimer;
56use crate::utility::{self, debug_assert_cloexec};
57
58/// Virtual pid of a shadow process
59#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone, Ord, PartialOrd)]
60pub struct ProcessId(u32);
61
62impl ProcessId {
63    // The first Process to run after boot is the "init" process, and has pid=1.
64    // In Shadow simulations, this roughly corresponds to Shadow itself. e.g.
65    // processes spawned by Shadow itself have a parent pid of 1.
66    pub const INIT: Self = ProcessId(1);
67
68    /// Returns what the `ProcessId` would be of a `Process` whose thread
69    /// group leader has id `thread_group_leader_tid`.
70    pub fn from_thread_group_leader_tid(thread_group_leader_tid: ThreadId) -> Self {
71        ProcessId::try_from(libc::pid_t::from(thread_group_leader_tid)).unwrap()
72    }
73}
74
75impl std::fmt::Display for ProcessId {
76    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
77        write!(f, "{}", self.0)
78    }
79}
80
81impl TryFrom<u32> for ProcessId {
82    type Error = TryFromIntError;
83
84    fn try_from(val: u32) -> Result<Self, Self::Error> {
85        // we don't actually want the value as a `pid_t`, we just want to make sure it can be
86        // converted successfully
87        let _ = libc::pid_t::try_from(val)?;
88        Ok(ProcessId(val))
89    }
90}
91
92impl TryFrom<libc::pid_t> for ProcessId {
93    type Error = TryFromIntError;
94
95    fn try_from(value: libc::pid_t) -> Result<Self, Self::Error> {
96        Ok(ProcessId(value.try_into()?))
97    }
98}
99
100impl From<ProcessId> for u32 {
101    fn from(val: ProcessId) -> Self {
102        val.0
103    }
104}
105
106impl From<ProcessId> for libc::pid_t {
107    fn from(val: ProcessId) -> Self {
108        val.0.try_into().unwrap()
109    }
110}
111
112impl From<ThreadId> for ProcessId {
113    fn from(value: ThreadId) -> Self {
114        ProcessId::try_from(libc::pid_t::from(value)).unwrap()
115    }
116}
117
118#[derive(Debug, Copy, Clone, Eq, PartialEq)]
119pub enum ExitStatus {
120    Normal(i32),
121    Signaled(Signal),
122    /// The process was killed by Shadow rather than exiting "naturally" as part
123    /// of the simulation. Currently this only happens when the process is still
124    /// running when the simulation stop_time is reached.
125    ///
126    /// A signal delivered via `shutdown_signal` does not result in this status;
127    /// e.g. if the process is killed directly by the signal the ExitStatus will
128    /// be `Signaled`; if the process handles the signal and exits by calling
129    /// `exit`, the status will be `Normal`.
130    StoppedByShadow,
131}
132
133#[derive(Debug)]
134struct StraceLogging {
135    file: RootedRefCell<std::fs::File>,
136    options: FmtOptions,
137}
138
139/// Parts of the process that are present in all states.
140struct Common {
141    id: ProcessId,
142    host_id: HostId,
143
144    // Parent pid (aka `ppid`), as returned e.g. by `getppid`.  This can change
145    // at runtime if the original parent exits and is reaped.
146    parent_pid: Cell<ProcessId>,
147
148    // Process group id (aka `pgid`), as returned e.g. by `getpgid`.
149    group_id: Cell<ProcessId>,
150
151    // Session id, as returned e.g. by `getsid`.
152    session_id: Cell<ProcessId>,
153
154    // Signal to send to parent on death.
155    exit_signal: Option<Signal>,
156
157    // unique id of the program that this process should run
158    name: CString,
159
160    // the name of the executable as provided in shadow's config, for logging purposes
161    plugin_name: CString,
162
163    // absolute path to the process's working directory.
164    // This must remain in sync with the actual working dir of the native process.
165    // See https://github.com/shadow/shadow/issues/2960
166    working_dir: CString,
167}
168
169impl Common {
170    fn id(&self) -> ProcessId {
171        self.id
172    }
173
174    fn physical_address(&self, vptr: ForeignPtr<()>) -> ManagedPhysicalMemoryAddr {
175        // We currently don't keep a true system-wide virtual <-> physical address
176        // mapping. Instead we simply assume that no shadow processes map the same
177        // underlying physical memory, and that therefore (pid, virtual address)
178        // uniquely defines a physical address.
179        //
180        // If we ever want to support futexes in memory shared between processes,
181        // we'll need to change this.  The most foolproof way to do so is probably
182        // to change ManagedPhysicalMemoryAddr to be a bigger struct that identifies where
183        // the mapped region came from (e.g. what file), and the offset into that
184        // region. Such "fat" physical pointers might make memory management a
185        // little more cumbersome though, e.g. when using them as keys in the futex
186        // table.
187        //
188        // Alternatively we could hash the region+offset to a 64-bit value, but
189        // then we'd need to deal with potential collisions. On average we'd expect
190        // a collision after 2**32 physical addresses; i.e. they *probably*
191        // wouldn't happen in practice for realistic simulations.
192
193        // Linux uses the bottom 48-bits for user-space virtual addresses, giving
194        // us 16 bits for the pid.
195        const PADDR_BITS: i32 = 64;
196        const VADDR_BITS: i32 = 48;
197        const PID_BITS: i32 = 16;
198        assert_eq!(PADDR_BITS, PID_BITS + VADDR_BITS);
199
200        let high_part: u64 = u64::from(u32::from(self.id())) << VADDR_BITS;
201        assert_eq!(
202            ProcessId::try_from((high_part >> VADDR_BITS) as u32),
203            Ok(self.id())
204        );
205
206        let low_part = u64::from(vptr);
207        assert_eq!(low_part >> VADDR_BITS, 0);
208
209        ManagedPhysicalMemoryAddr::from(high_part | low_part)
210    }
211
212    fn name(&self) -> &str {
213        self.name.to_str().unwrap()
214    }
215
216    pub fn thread_group_leader_id(&self) -> ThreadId {
217        // tid of the thread group leader is equal to the pid.
218        ThreadId::from(self.id())
219    }
220}
221
222/// A process that is currently runnable.
223pub struct RunnableProcess {
224    common: Common,
225
226    // Expected end state, if any. We'll report an error if this is present and
227    // doesn't match the actual exit status.
228    //
229    // This will be None e.g. for processes created via `fork` instead of
230    // spawned directly from Shadow's config file. In those cases it's the
231    // parent's responsibility to reap and interpret the exit status.
232    expected_final_state: Option<ProcessFinalState>,
233
234    // Shared memory allocation for shared state with shim.
235    shim_shared_mem_block: ShMemBlock<'static, ProcessShmem>,
236
237    // Shared with forked Processes
238    strace_logging: Option<Arc<StraceLogging>>,
239
240    // The shim's log file. This gets dup'd into the ManagedProcess
241    // where the shim can write to it directly. We persist it to handle the case
242    // where we need to recreatea a ManagedProcess and have it continue writing
243    // to the same file.
244    //
245    // Shared with forked Processes
246    shimlog_file: Arc<std::fs::File>,
247
248    // "dumpable" state, as manipulated via the prctl operations PR_SET_DUMPABLE
249    // and PR_GET_DUMPABLE.
250    dumpable: Cell<SuidDump>,
251
252    native_pid: Pid,
253
254    // timer that tracks the amount of CPU time we spend on plugin execution and processing
255    #[cfg(feature = "perf_timers")]
256    cpu_delay_timer: RefCell<PerfTimer>,
257    #[cfg(feature = "perf_timers")]
258    total_run_time: Cell<Duration>,
259
260    itimer_real: RefCell<Timer>,
261
262    // The `RootedRc` lets us hold a reference to a thread without holding a
263    // reference to the thread list. e.g. this lets us implement the `clone`
264    // syscall, which adds a thread to the list while we have a reference to the
265    // parent thread.
266    threads: RefCell<BTreeMap<ThreadId, RootedRc<RootedRefCell<Thread>>>>,
267
268    // References to `Self::memory_manager` cached on behalf of C code using legacy
269    // C memory access APIs.
270    // TODO: Remove these when we've migrated Shadow off of the APIs that need
271    // them (probably by migrating all the calling code to Rust).
272    //
273    // SAFETY: Must be before memory_manager for drop order.
274    unsafe_borrow_mut: RefCell<Option<UnsafeBorrowMut>>,
275    unsafe_borrows: RefCell<Vec<UnsafeBorrow>>,
276
277    // `clone(2)` documents that if `CLONE_THREAD` is set, then `CLONE_VM` must
278    // also be set. Hence all threads in a process always share the same virtual
279    // address space, and hence we have a `MemoryManager` at the `Process` level
280    // rather than the `Thread` level.
281    // SAFETY: Must come after `unsafe_borrows` and `unsafe_borrow_mut`.
282    // Boxed to avoid invalidating those if Self is moved.
283    memory_manager: Box<RefCell<MemoryManager>>,
284
285    // Listeners for child-events.
286    // e.g. these listeners are notified when a child of this process exits.
287    child_process_event_listeners: RefCell<StateEventSource>,
288}
289
290impl RunnableProcess {
291    /// Spawn a `ManagedThread` corresponding to the given `exec` syscall
292    /// parameters.  Intended for use by the `exec` syscall handlers. Whether it
293    /// succeeds or fails, does *not* mutate `self`, though `self`'s strace and
294    /// shim log files will be passed into the new `ManagedThread`.
295    ///
296    /// In case the native `exec` syscall fails, the corresponding error is returned.
297    pub fn spawn_mthread_for_exec(
298        &self,
299        host: &Host,
300        plugin_path: &CStr,
301        argv: Vec<CString>,
302        envv: Vec<CString>,
303    ) -> Result<ManagedThread, Errno> {
304        ManagedThread::spawn(
305            plugin_path,
306            argv,
307            envv,
308            self.strace_logging
309                .as_ref()
310                .map(|s| s.file.borrow(host.root()))
311                .as_deref(),
312            &self.shimlog_file,
313            host.preload_paths(),
314        )
315    }
316
317    /// Call after a thread has exited. Removes the thread and does corresponding cleanup and notifications.
318    fn reap_thread(&self, host: &Host, threadrc: RootedRc<RootedRefCell<Thread>>) {
319        let threadrc = ExplicitDropper::new(threadrc, |t| {
320            t.explicit_drop_recursive(host.root(), host);
321        });
322        let thread = threadrc.borrow(host.root());
323
324        assert!(!thread.is_running());
325
326        // If the `clear_child_tid` attribute on the thread is set, and there are
327        // any other threads left alive in the process, perform a futex wake on
328        // that address. This mechanism is typically used in `pthread_join` etc.
329        // See `set_tid_address(2)`.
330        let clear_child_tid_pvp = thread.get_tid_address();
331        if !clear_child_tid_pvp.is_null() && !self.threads.borrow().is_empty() {
332            self.memory_manager
333                .borrow_mut()
334                .write(clear_child_tid_pvp, &0)
335                .unwrap();
336
337            // Wake the corresponding futex.
338            let futexes = host.futextable_borrow();
339            let addr = self
340                .common
341                .physical_address(clear_child_tid_pvp.cast::<()>());
342
343            if let Some(futex) = futexes.get(addr) {
344                futex.wake(1);
345            }
346        }
347    }
348
349    /// This cleans up memory references left over from legacy C code; usually
350    /// a syscall handler.
351    ///
352    /// Writes the leftover mutable ref to memory (if any), and frees
353    /// all memory refs.
354    pub fn free_unsafe_borrows_flush(&self) -> Result<(), Errno> {
355        self.unsafe_borrows.borrow_mut().clear();
356
357        let unsafe_borrow_mut = self.unsafe_borrow_mut.borrow_mut().take();
358        if let Some(borrow) = unsafe_borrow_mut {
359            borrow.flush()
360        } else {
361            Ok(())
362        }
363    }
364
365    /// This cleans up memory references left over from legacy C code; usually
366    /// a syscall handler.
367    ///
368    /// Frees all memory refs without writing back to memory.
369    pub fn free_unsafe_borrows_noflush(&self) {
370        self.unsafe_borrows.borrow_mut().clear();
371
372        let unsafe_borrow_mut = self.unsafe_borrow_mut.borrow_mut().take();
373        if let Some(borrow) = unsafe_borrow_mut {
374            borrow.noflush();
375        }
376    }
377
378    #[track_caller]
379    pub fn memory_borrow(&self) -> impl Deref<Target = MemoryManager> + '_ {
380        self.memory_manager.borrow()
381    }
382
383    #[track_caller]
384    pub fn memory_borrow_mut(&self) -> impl DerefMut<Target = MemoryManager> + '_ {
385        self.memory_manager.borrow_mut()
386    }
387
388    pub fn strace_logging_options(&self) -> Option<FmtOptions> {
389        self.strace_logging.as_ref().map(|x| x.options)
390    }
391
392    /// If strace logging is disabled, this function will do nothing and return `None`.
393    pub fn with_strace_file<T>(&self, f: impl FnOnce(&mut std::fs::File) -> T) -> Option<T> {
394        // TODO: get Host from caller. Would need t update syscall-logger.
395        Worker::with_active_host(|host| {
396            let strace_logging = self.strace_logging.as_ref()?;
397            let mut file = strace_logging.file.borrow_mut(host.root());
398            Some(f(&mut file))
399        })
400        .unwrap()
401    }
402
403    pub fn native_pid(&self) -> Pid {
404        self.native_pid
405    }
406
407    #[track_caller]
408    fn first_live_thread(&self, root: &Root) -> Option<Ref<RootedRc<RootedRefCell<Thread>>>> {
409        Ref::filter_map(self.threads.borrow(), |threads| {
410            threads.values().next().inspect(|thread| {
411                // There shouldn't be any non-running threads in the table.
412                assert!(thread.borrow(root).is_running());
413            })
414        })
415        .ok()
416    }
417
418    /// Returns a dynamically borrowed reference to the first live thread.
419    /// This is meant primarily for the MemoryManager.
420    #[track_caller]
421    pub fn first_live_thread_borrow(
422        &self,
423        root: &Root,
424    ) -> Option<impl Deref<Target = RootedRc<RootedRefCell<Thread>>> + '_> {
425        self.first_live_thread(root)
426    }
427
428    #[track_caller]
429    fn thread(&self, virtual_tid: ThreadId) -> Option<Ref<RootedRc<RootedRefCell<Thread>>>> {
430        Ref::filter_map(self.threads.borrow(), |threads| threads.get(&virtual_tid)).ok()
431    }
432
433    #[track_caller]
434    pub fn thread_borrow(
435        &self,
436        virtual_tid: ThreadId,
437    ) -> Option<impl Deref<Target = RootedRc<RootedRefCell<Thread>>> + '_> {
438        self.thread(virtual_tid)
439    }
440
441    // Disposes of `self`, returning the internal `Common` for reuse.
442    // Used internally when changing states.
443    fn into_common(self) -> Common {
444        // There shouldn't be any outstanding unsafe borrows when changing
445        // states, since that would indicate C code might still have a pointer
446        // to memory.
447        assert!(self.unsafe_borrow_mut.take().is_none());
448        assert!(self.unsafe_borrows.take().is_empty());
449
450        self.common
451    }
452
453    /// Starts the CPU delay timer.
454    /// Panics if the timer is already running.
455    #[cfg(feature = "perf_timers")]
456    pub fn start_cpu_delay_timer(&self) {
457        self.cpu_delay_timer.borrow_mut().start()
458    }
459
460    /// Stop the timer and return the most recent (not cumulative) duration.
461    /// Panics if the timer was not already running.
462    #[cfg(feature = "perf_timers")]
463    pub fn stop_cpu_delay_timer(&self, host: &Host) -> Duration {
464        let mut timer = self.cpu_delay_timer.borrow_mut();
465        timer.stop();
466        let total_elapsed = timer.elapsed();
467        let prev_total = self.total_run_time.replace(total_elapsed);
468        let delta = total_elapsed - prev_total;
469
470        host.cpu_borrow_mut().add_delay(delta);
471
472        delta
473    }
474
475    fn interrupt_with_signal(&self, host: &Host, signal: Signal) {
476        let threads = self.threads.borrow();
477        for thread in threads.values() {
478            let thread = thread.borrow(host.root());
479            {
480                let thread_shmem = thread.shmem();
481                let host_lock = host.shim_shmem_lock_borrow().unwrap();
482                let thread_shmem_protected = thread_shmem.protected.borrow(&host_lock.root);
483                let blocked_signals = thread_shmem_protected.blocked_signals;
484                if blocked_signals.has(signal) {
485                    continue;
486                }
487            }
488            let Some(mut cond) = thread.syscall_condition_mut() else {
489                // Defensively handle this gracefully, but it probably shouldn't happen.
490                // The only thread in the process not blocked on a syscall should be
491                // the current-running thread (if any), but the caller should have
492                // delivered the signal synchronously instead of using this function
493                // in that case.
494                warn!("thread {:?} has no syscall_condition. How?", thread.id());
495                continue;
496            };
497            cond.wakeup_for_signal(host, signal);
498            break;
499        }
500    }
501
502    /// Send the signal described in `siginfo` to `process`. `current_thread`
503    /// should be set if there is one (e.g. if this is being called from a syscall
504    /// handler), and `None` otherwise (e.g. when called from a timer expiration event).
505    ///
506    /// An event will be scheduled to deliver the signal unless `current_thread`
507    /// is set, and belongs to the process `self`, and doesn't have the signal
508    /// blocked.  In that the signal will be processed synchronously when
509    /// returning from the current syscall.
510    pub fn signal(&self, host: &Host, current_thread: Option<&Thread>, siginfo_t: &siginfo_t) {
511        let signal = match siginfo_t.signal() {
512            Ok(s) => s,
513            Err(SignalFromI32Error(0)) => return,
514            Err(SignalFromI32Error(n)) => panic!("Bad signo {n}"),
515        };
516
517        // Scope for `process_shmem_protected`
518        {
519            let host_shmem = host.shim_shmem_lock_borrow().unwrap();
520            let mut process_shmem_protected = self
521                .shim_shared_mem_block
522                .protected
523                .borrow_mut(&host_shmem.root);
524            // SAFETY: We don't try to call any of the function pointers.
525            let action = unsafe { process_shmem_protected.signal_action(signal) };
526            match unsafe { action.handler() } {
527                linux_api::signal::SignalHandler::Handler(_) => (),
528                linux_api::signal::SignalHandler::Action(_) => (),
529                linux_api::signal::SignalHandler::SigIgn => return,
530                linux_api::signal::SignalHandler::SigDfl => {
531                    if defaultaction(signal) == LinuxDefaultAction::IGN {
532                        return;
533                    }
534                }
535            }
536
537            if process_shmem_protected.pending_signals.has(signal) {
538                // Signal is already pending. From signal(7):In the case where a
539                // standard signal is already pending, the siginfo_t structure (see
540                // sigaction(2)) associated with that signal is not overwritten on
541                // arrival of subsequent instances of the same signal.
542                return;
543            }
544            process_shmem_protected.pending_signals.add(signal);
545            process_shmem_protected.set_pending_standard_siginfo(signal, siginfo_t);
546        }
547
548        if let Some(thread) = current_thread {
549            if thread.process_id() == self.common.id() {
550                let host_shmem = host.shim_shmem_lock_borrow().unwrap();
551                let threadmem = thread.shmem();
552                let threadprotmem = threadmem.protected.borrow(&host_shmem.root);
553                if !threadprotmem.blocked_signals.has(signal) {
554                    // Target process is this process, and current thread hasn't blocked
555                    // the signal.  It will be delivered to this thread when it resumes.
556                    return;
557                }
558            }
559        }
560
561        self.interrupt_with_signal(host, signal);
562    }
563
564    /// Adds a new thread to the process and schedules it to run.
565    /// Intended for use by `clone`.
566    pub fn add_thread(&self, host: &Host, thread: RootedRc<RootedRefCell<Thread>>) {
567        let pid = self.common.id();
568        let tid = thread.borrow(host.root()).id();
569        self.threads.borrow_mut().insert(tid, thread);
570
571        // Schedule thread to start. We're giving the caller's reference to thread
572        // to the TaskRef here, which is why we don't increment its ref count to
573        // create the TaskRef, but do decrement it on cleanup.
574        let task = TaskRef::new(move |host| {
575            host.resume(pid, tid);
576        });
577        host.schedule_task_with_delay(task, SimulationTime::ZERO);
578    }
579
580    /// Create a new `Process`, forked from `self`, with the thread `new_thread_group_leader`.
581    pub fn new_forked_process(
582        &self,
583        host: &Host,
584        flags: CloneFlags,
585        exit_signal: Option<Signal>,
586        new_thread_group_leader: RootedRc<RootedRefCell<Thread>>,
587    ) -> RootedRc<RootedRefCell<Process>> {
588        let new_tgl_tid;
589        let native_pid;
590        {
591            let new_tgl = new_thread_group_leader.borrow(host.root());
592            new_tgl_tid = new_tgl.id();
593            native_pid = new_tgl.native_pid();
594        }
595        let pid = ProcessId::from_thread_group_leader_tid(new_tgl_tid);
596        assert_eq!(
597            pid,
598            new_thread_group_leader.borrow(host.root()).process_id()
599        );
600        let plugin_name = self.common.plugin_name.clone();
601        let name = make_name(host, plugin_name.to_str().unwrap(), pid);
602
603        let parent_pid = if flags.contains(CloneFlags::CLONE_PARENT) {
604            self.common.parent_pid.get()
605        } else {
606            self.common.id
607        };
608
609        // Process group is always inherited from the parent process.
610        let process_group_id = self.common.group_id.get();
611
612        // Session is always inherited from the parent process.
613        let session_id = self.common.session_id.get();
614
615        let common = Common {
616            id: pid,
617            host_id: host.id(),
618            name,
619            plugin_name,
620            working_dir: self.common.working_dir.clone(),
621            parent_pid: Cell::new(parent_pid),
622            group_id: Cell::new(process_group_id),
623            session_id: Cell::new(session_id),
624            exit_signal,
625        };
626
627        // The child will log to the same strace log file. Entries contain thread IDs,
628        // though it might be tricky to map those back to processes.
629        let strace_logging = self.strace_logging.as_ref().cloned();
630
631        // `fork(2)`:
632        //  > The child does not inherit timers from its parent
633        //  > (setitimer(2), alarm(2), timer_create(2)).
634        let itimer_real = RefCell::new(Timer::new(move |host| itimer_real_expiration(host, pid)));
635
636        let threads = RefCell::new(BTreeMap::from([(new_tgl_tid, new_thread_group_leader)]));
637
638        let shim_shared_mem = ProcessShmem::new(
639            &host.shim_shmem_lock_borrow().unwrap().root,
640            host.shim_shmem().serialize(),
641            host.id(),
642            strace_logging
643                .as_ref()
644                .map(|x| x.file.borrow(host.root()).as_raw_fd()),
645        );
646        let shim_shared_mem_block = shadow_shmem::allocator::shmalloc(shim_shared_mem);
647
648        let runnable_process = RunnableProcess {
649            common,
650            expected_final_state: None,
651            shim_shared_mem_block,
652            strace_logging,
653            dumpable: self.dumpable.clone(),
654            native_pid,
655            #[cfg(feature = "perf_timers")]
656            cpu_delay_timer: RefCell::new(PerfTimer::new_stopped()),
657            #[cfg(feature = "perf_timers")]
658            total_run_time: Cell::new(Duration::ZERO),
659            itimer_real,
660            threads,
661            unsafe_borrow_mut: RefCell::new(None),
662            unsafe_borrows: RefCell::new(Vec::new()),
663            memory_manager: Box::new(RefCell::new(unsafe { MemoryManager::new(native_pid) })),
664            child_process_event_listeners: Default::default(),
665            shimlog_file: self.shimlog_file.clone(),
666        };
667        let child_process = Process {
668            state: RefCell::new(Some(ProcessState::Runnable(runnable_process))),
669        };
670        RootedRc::new(host.root(), RootedRefCell::new(host.root(), child_process))
671    }
672
673    /// Shared memory for this process.
674    pub fn shmem(&self) -> impl Deref<Target = ShMemBlock<'static, ProcessShmem>> + '_ {
675        &self.shim_shared_mem_block
676    }
677}
678
679impl ExplicitDrop for RunnableProcess {
680    type ExplicitDropParam = Host;
681    type ExplicitDropResult = ();
682
683    fn explicit_drop(mut self, host: &Self::ExplicitDropParam) -> Self::ExplicitDropResult {
684        let threads = std::mem::take(self.threads.get_mut());
685        for thread in threads.into_values() {
686            thread.explicit_drop_recursive(host.root(), host);
687        }
688    }
689}
690
691/// A process that has exited.
692pub struct ZombieProcess {
693    common: Common,
694
695    exit_status: ExitStatus,
696}
697
698impl ZombieProcess {
699    pub fn exit_status(&self) -> ExitStatus {
700        self.exit_status
701    }
702
703    /// Process that can reap this zombie process, if any.
704    pub fn reaper<'host>(
705        &self,
706        host: &'host Host,
707    ) -> Option<impl Deref<Target = RootedRc<RootedRefCell<Process>>> + 'host> {
708        let parent_pid = self.common.parent_pid.get();
709        if parent_pid == ProcessId::INIT {
710            return None;
711        }
712        let parentrc = host.process_borrow(parent_pid)?;
713
714        // If the parent has *explicitly* ignored the exit signal, then it
715        // doesn't reap.
716        //
717        // `waitpid(2)`:
718        // > POSIX.1-2001 specifies that if the disposition of SIGCHLD is set to SIG_IGN or the SA_NOCLDWAIT flag is set for SIGCHLD  (see
719        // > sigaction(2)),  then  children  that  terminate  do not become zombies and a call to wait() or waitpid() will block until all
720        // > children have terminated, and then fail with errno set to ECHILD.  (The original POSIX standard left the behavior of  setting
721        // > SIGCHLD to SIG_IGN unspecified.  Note that even though the default disposition of SIGCHLD is "ignore", explicitly setting the
722        // > disposition to SIG_IGN results in different treatment of zombie process children.)
723        //
724        // TODO: validate that this applies to whatever signal is configured as the exit
725        // signal, even if it's not SIGCHLD.
726        if let Some(exit_signal) = self.common.exit_signal {
727            let parent = parentrc.borrow(host.root());
728            let parent_shmem = parent.shmem();
729            let host_shmem_lock = host.shim_shmem_lock_borrow().unwrap();
730            let parent_shmem_protected = parent_shmem.protected.borrow(&host_shmem_lock.root);
731            // SAFETY: We don't dereference function pointers.
732            let action = unsafe { parent_shmem_protected.signal_action(exit_signal) };
733            if action.is_ignore() {
734                return None;
735            }
736        }
737
738        Some(parentrc)
739    }
740
741    fn notify_parent_of_exit(&self, host: &Host) {
742        let Some(exit_signal) = self.common.exit_signal else {
743            trace!("Not notifying parent of exit: no signal specified");
744            return;
745        };
746        let parent_pid = self.common.parent_pid.get();
747        if parent_pid == ProcessId::INIT {
748            trace!("Not notifying parent of exit: parent is 'init'");
749            return;
750        }
751        let Some(parent_rc) = host.process_borrow(parent_pid) else {
752            trace!("Not notifying parent of exit: parent {parent_pid:?} not found");
753            return;
754        };
755        let parent = parent_rc.borrow(host.root());
756        let siginfo = self.exit_siginfo(exit_signal);
757
758        let Some(parent_runnable) = parent.as_runnable() else {
759            trace!("Not notifying parent of exit: {parent_pid:?} not running");
760            debug_panic!("Non-running parent process shouldn't be possible.");
761            #[allow(unreachable_code)]
762            {
763                return;
764            }
765        };
766        parent_runnable.signal(host, None, &siginfo);
767        CallbackQueue::queue_and_run_with_legacy(|q| {
768            let mut parent_child_listeners =
769                parent_runnable.child_process_event_listeners.borrow_mut();
770            parent_child_listeners.notify_listeners(
771                FileState::CHILD_EVENT,
772                FileState::CHILD_EVENT,
773                FileSignals::empty(),
774                q,
775            );
776        });
777    }
778
779    /// Construct a siginfo containing information about how the process exited.
780    /// Used internally to send a signal to the parent process, and by the
781    /// `waitid` syscall handler.
782    ///
783    /// `exit_signal` is the signal to set in the `siginfo_t`.
784    pub fn exit_siginfo(&self, exit_signal: Signal) -> siginfo_t {
785        match self.exit_status {
786            ExitStatus::Normal(exit_code) => siginfo_t::new_for_sigchld_exited(
787                exit_signal,
788                self.common.id.into(),
789                0,
790                exit_code,
791                0,
792                0,
793            ),
794            ExitStatus::Signaled(fatal_signal) => {
795                // This ought to be `siginfo_t::new_for_sigchld_dumped` if
796                // the child dumped core, but that depends on various other
797                // system variables outside of our control. We always report
798                // that no core was dropped for determinism.
799                siginfo_t::new_for_sigchld_killed(
800                    exit_signal,
801                    self.common.id.into(),
802                    0,
803                    fatal_signal,
804                    0,
805                    0,
806                )
807            }
808
809            ExitStatus::StoppedByShadow => unreachable!(),
810        }
811    }
812}
813
814/// Inner implementation of a simulated process.
815// We could box the variants, but it's unclear whether it's really worth the extra code and extra
816// allocations. Most of the values of this type will be in the larger `Runnable` variant rather than
817// the smaller `Zombie` variant anyways.
818#[allow(clippy::large_enum_variant)]
819enum ProcessState {
820    Runnable(RunnableProcess),
821    Zombie(ZombieProcess),
822}
823
824impl ProcessState {
825    fn common(&self) -> &Common {
826        match self {
827            ProcessState::Runnable(r) => &r.common,
828            ProcessState::Zombie(z) => &z.common,
829        }
830    }
831
832    fn common_mut(&mut self) -> &mut Common {
833        match self {
834            ProcessState::Runnable(r) => &mut r.common,
835            ProcessState::Zombie(z) => &mut z.common,
836        }
837    }
838
839    fn as_runnable(&self) -> Option<&RunnableProcess> {
840        match self {
841            ProcessState::Runnable(r) => Some(r),
842            ProcessState::Zombie(_) => None,
843        }
844    }
845
846    fn as_runnable_mut(&mut self) -> Option<&mut RunnableProcess> {
847        match self {
848            ProcessState::Runnable(r) => Some(r),
849            ProcessState::Zombie(_) => None,
850        }
851    }
852
853    fn as_zombie(&self) -> Option<&ZombieProcess> {
854        match self {
855            ProcessState::Runnable(_) => None,
856            ProcessState::Zombie(z) => Some(z),
857        }
858    }
859}
860
861impl ExplicitDrop for ProcessState {
862    type ExplicitDropParam = Host;
863    type ExplicitDropResult = ();
864
865    fn explicit_drop(self, host: &Self::ExplicitDropParam) -> Self::ExplicitDropResult {
866        match self {
867            ProcessState::Runnable(r) => r.explicit_drop(host),
868            ProcessState::Zombie(_) => (),
869        }
870    }
871}
872
873/// A simulated process.
874pub struct Process {
875    // Most of the implementation should be in [`ProcessState`].
876    // This wrapper allows us to change the state.
877    state: RefCell<Option<ProcessState>>,
878}
879
880fn itimer_real_expiration(host: &Host, pid: ProcessId) {
881    let Some(process) = host.process_borrow(pid) else {
882        debug!("Process {:?} no longer exists", pid);
883        return;
884    };
885    let process = process.borrow(host.root());
886    let Some(runnable) = process.as_runnable() else {
887        debug!("Process {:?} no longer running", &*process.name());
888        return;
889    };
890    let timer = runnable.itimer_real.borrow();
891    // The siginfo_t structure only has an i32. Presumably we want to just truncate in
892    // case of overflow.
893    let expiration_count = timer.expiration_count() as i32;
894    let siginfo_t = siginfo_t::new_for_timer(Signal::SIGALRM, 0, expiration_count);
895    process.signal(host, None, &siginfo_t);
896}
897
898impl Process {
899    fn common(&self) -> Ref<Common> {
900        Ref::map(self.state.borrow(), |state| {
901            state.as_ref().unwrap().common()
902        })
903    }
904
905    fn common_mut(&self) -> RefMut<Common> {
906        RefMut::map(self.state.borrow_mut(), |state| {
907            state.as_mut().unwrap().common_mut()
908        })
909    }
910
911    fn as_runnable(&self) -> Option<Ref<RunnableProcess>> {
912        Ref::filter_map(self.state.borrow(), |state| {
913            state.as_ref().unwrap().as_runnable()
914        })
915        .ok()
916    }
917
918    fn as_runnable_mut(&self) -> Option<RefMut<RunnableProcess>> {
919        RefMut::filter_map(self.state.borrow_mut(), |state| {
920            state.as_mut().unwrap().as_runnable_mut()
921        })
922        .ok()
923    }
924
925    /// Borrows a reference to the internal [`RunnableProcess`] if `self` is runnable.
926    pub fn borrow_as_runnable(&self) -> Option<impl Deref<Target = RunnableProcess> + '_> {
927        self.as_runnable()
928    }
929
930    fn as_zombie(&self) -> Option<Ref<ZombieProcess>> {
931        Ref::filter_map(self.state.borrow(), |state| {
932            state.as_ref().unwrap().as_zombie()
933        })
934        .ok()
935    }
936
937    /// Borrows a reference to the internal [`ZombieProcess`] if `self` is a zombie.
938    pub fn borrow_as_zombie(&self) -> Option<impl Deref<Target = ZombieProcess> + '_> {
939        self.as_zombie()
940    }
941
942    /// Spawn a new process. The process will be runnable via [`Self::resume`]
943    /// once it has been added to the `Host`'s process list.
944    pub fn spawn(
945        host: &Host,
946        plugin_name: CString,
947        plugin_path: &CStr,
948        argv: Vec<CString>,
949        envv: Vec<CString>,
950        pause_for_debugging: bool,
951        strace_logging_options: Option<FmtOptions>,
952        expected_final_state: ProcessFinalState,
953    ) -> Result<RootedRc<RootedRefCell<Process>>, Errno> {
954        debug!("starting process '{:?}'", plugin_name);
955
956        let main_thread_id = host.get_new_thread_id();
957        let process_id = ProcessId::from(main_thread_id);
958
959        let desc_table = RootedRc::new(
960            host.root(),
961            RootedRefCell::new(host.root(), DescriptorTable::new()),
962        );
963        let itimer_real = RefCell::new(Timer::new(move |host| {
964            itimer_real_expiration(host, process_id)
965        }));
966
967        let name = make_name(host, plugin_name.to_str().unwrap(), process_id);
968
969        let mut file_basename = PathBuf::new();
970        file_basename.push(host.data_dir_path());
971        file_basename.push(format!(
972            "{exe_name}.{id}",
973            exe_name = plugin_name.to_str().unwrap(),
974            id = u32::from(process_id)
975        ));
976
977        let strace_logging = strace_logging_options.map(|options| {
978            let file =
979                std::fs::File::create(Self::static_output_file_name(&file_basename, "strace"))
980                    .unwrap();
981            debug_assert_cloexec(&file);
982            Arc::new(StraceLogging {
983                file: RootedRefCell::new(host.root(), file),
984                options,
985            })
986        });
987
988        let shim_shared_mem = ProcessShmem::new(
989            &host.shim_shmem_lock_borrow().unwrap().root,
990            host.shim_shmem().serialize(),
991            host.id(),
992            strace_logging
993                .as_ref()
994                .map(|x| x.file.borrow(host.root()).as_raw_fd()),
995        );
996        let shim_shared_mem_block = shadow_shmem::allocator::shmalloc(shim_shared_mem);
997
998        let working_dir = utility::pathbuf_to_nul_term_cstring(
999            std::fs::canonicalize(host.data_dir_path()).unwrap(),
1000        );
1001
1002        {
1003            let mut descriptor_table = desc_table.borrow_mut(host.root());
1004            Self::open_stdio_file_helper(
1005                &mut descriptor_table,
1006                libc::STDIN_FILENO.try_into().unwrap(),
1007                "/dev/null".into(),
1008                OFlag::O_RDONLY,
1009            );
1010
1011            let name = Self::static_output_file_name(&file_basename, "stdout");
1012            Self::open_stdio_file_helper(
1013                &mut descriptor_table,
1014                libc::STDOUT_FILENO.try_into().unwrap(),
1015                name,
1016                OFlag::O_WRONLY,
1017            );
1018
1019            let name = Self::static_output_file_name(&file_basename, "stderr");
1020            Self::open_stdio_file_helper(
1021                &mut descriptor_table,
1022                libc::STDERR_FILENO.try_into().unwrap(),
1023                name,
1024                OFlag::O_WRONLY,
1025            );
1026        }
1027
1028        let shimlog_file = Arc::new(
1029            std::fs::File::create(Self::static_output_file_name(&file_basename, "shimlog"))
1030                .unwrap(),
1031        );
1032        debug_assert_cloexec(&shimlog_file);
1033
1034        let mthread = ManagedThread::spawn(
1035            plugin_path,
1036            argv,
1037            envv,
1038            strace_logging
1039                .as_ref()
1040                .map(|s| s.file.borrow(host.root()))
1041                .as_deref(),
1042            &shimlog_file,
1043            host.preload_paths(),
1044        )?;
1045        let native_pid = mthread.native_pid();
1046        let main_thread =
1047            Thread::wrap_mthread(host, mthread, desc_table, process_id, main_thread_id).unwrap();
1048
1049        debug!("process '{:?}' started", plugin_name);
1050
1051        if pause_for_debugging {
1052            // will block until logger output has been flushed
1053            // there is a race condition where other threads may log between the
1054            // `eprintln` and `raise` below, but it should be rare
1055            log::logger().flush();
1056
1057            // Use a single `eprintln` to ensure we hold the lock for the whole message.
1058            // Defensively pre-construct a single string so that `eprintln` is
1059            // more likely to use a single `write` call, to minimize the chance
1060            // of more lines being written to stdout in the meantime, and in
1061            // case of C code writing to `STDERR` directly without taking Rust's
1062            // lock.
1063            let msg = format!(
1064                "\
1065              \n** Pausing with SIGTSTP to enable debugger attachment to managed process\
1066              \n** '{plugin_name:?}' (pid {native_pid:?}).\
1067              \n** If running Shadow under Bash, resume Shadow by pressing Ctrl-Z to background\
1068              \n** this task, and then typing \"fg\".\
1069              \n** If running GDB, resume Shadow by typing \"signal SIGCONT\"."
1070            );
1071            eprintln!("{}", msg);
1072
1073            rustix::process::kill_process(rustix::process::getpid(), rustix::process::Signal::Tstp)
1074                .unwrap();
1075        }
1076
1077        let memory_manager = unsafe { MemoryManager::new(native_pid) };
1078        let threads = RefCell::new(BTreeMap::from([(
1079            main_thread_id,
1080            RootedRc::new(host.root(), RootedRefCell::new(host.root(), main_thread)),
1081        )]));
1082
1083        let common = Common {
1084            id: process_id,
1085            host_id: host.id(),
1086            working_dir,
1087            name,
1088            plugin_name,
1089            parent_pid: Cell::new(ProcessId::INIT),
1090            group_id: Cell::new(ProcessId::INIT),
1091            session_id: Cell::new(ProcessId::INIT),
1092            // Exit signal is moot; since parent is INIT there will never
1093            // be a valid target for it.
1094            exit_signal: None,
1095        };
1096        Ok(RootedRc::new(
1097            host.root(),
1098            RootedRefCell::new(
1099                host.root(),
1100                Self {
1101                    state: RefCell::new(Some(ProcessState::Runnable(RunnableProcess {
1102                        common,
1103                        expected_final_state: Some(expected_final_state),
1104                        shim_shared_mem_block,
1105                        memory_manager: Box::new(RefCell::new(memory_manager)),
1106                        itimer_real,
1107                        strace_logging,
1108                        dumpable: Cell::new(SuidDump::SUID_DUMP_USER),
1109                        native_pid,
1110                        unsafe_borrow_mut: RefCell::new(None),
1111                        unsafe_borrows: RefCell::new(Vec::new()),
1112                        threads,
1113                        #[cfg(feature = "perf_timers")]
1114                        cpu_delay_timer: RefCell::new(PerfTimer::new_stopped()),
1115                        #[cfg(feature = "perf_timers")]
1116                        total_run_time: Cell::new(Duration::ZERO),
1117                        child_process_event_listeners: Default::default(),
1118                        shimlog_file,
1119                    }))),
1120                },
1121            ),
1122        ))
1123    }
1124
1125    pub fn id(&self) -> ProcessId {
1126        self.common().id
1127    }
1128
1129    pub fn parent_id(&self) -> ProcessId {
1130        self.common().parent_pid.get()
1131    }
1132
1133    pub fn set_parent_id(&self, pid: ProcessId) {
1134        self.common().parent_pid.set(pid)
1135    }
1136
1137    pub fn group_id(&self) -> ProcessId {
1138        self.common().group_id.get()
1139    }
1140
1141    pub fn set_group_id(&self, id: ProcessId) {
1142        self.common().group_id.set(id)
1143    }
1144
1145    pub fn session_id(&self) -> ProcessId {
1146        self.common().session_id.get()
1147    }
1148
1149    pub fn set_session_id(&self, id: ProcessId) {
1150        self.common().session_id.set(id)
1151    }
1152
1153    pub fn host_id(&self) -> HostId {
1154        self.common().host_id
1155    }
1156
1157    /// Get process's "dumpable" state, as manipulated by the prctl operations `PR_SET_DUMPABLE` and
1158    /// `PR_GET_DUMPABLE`.
1159    pub fn dumpable(&self) -> SuidDump {
1160        self.as_runnable().unwrap().dumpable.get()
1161    }
1162
1163    /// Set process's "dumpable" state, as manipulated by the prctl operations `PR_SET_DUMPABLE` and
1164    /// `PR_GET_DUMPABLE`.
1165    pub fn set_dumpable(&self, val: SuidDump) {
1166        assert!(val == SuidDump::SUID_DUMP_DISABLE || val == SuidDump::SUID_DUMP_USER);
1167        self.as_runnable().unwrap().dumpable.set(val)
1168    }
1169
1170    /// Deprecated wrapper for `RunnableProcess::start_cpu_delay_timer`
1171    #[cfg(feature = "perf_timers")]
1172    pub fn start_cpu_delay_timer(&self) {
1173        self.as_runnable().unwrap().start_cpu_delay_timer()
1174    }
1175
1176    /// Deprecated wrapper for `RunnableProcess::stop_cpu_delay_timer`
1177    #[cfg(feature = "perf_timers")]
1178    pub fn stop_cpu_delay_timer(&self, host: &Host) -> Duration {
1179        self.as_runnable().unwrap().stop_cpu_delay_timer(host)
1180    }
1181
1182    pub fn thread_group_leader_id(&self) -> ThreadId {
1183        self.common().thread_group_leader_id()
1184    }
1185
1186    /// Resume execution of `tid` (if it exists).
1187    /// Should only be called from `Host::resume`.
1188    pub fn resume(&self, host: &Host, tid: ThreadId) {
1189        trace!("Continuing thread {} in process {}", tid, self.id());
1190
1191        let threadrc = {
1192            let Some(runnable) = self.as_runnable() else {
1193                debug!("Process {} is no longer running", &*self.name());
1194                return;
1195            };
1196            let threads = runnable.threads.borrow();
1197            let Some(thread) = threads.get(&tid) else {
1198                debug!("Thread {} no longer exists", tid);
1199                return;
1200            };
1201            // Clone the thread reference, so that we don't hold a dynamically
1202            // borrowed reference to the thread list while running the thread.
1203            thread.clone(host.root())
1204        };
1205        let threadrc = ExplicitDropper::new(threadrc, |t| {
1206            t.explicit_drop_recursive(host.root(), host);
1207        });
1208        let thread = threadrc.borrow(host.root());
1209
1210        Worker::set_active_thread(&threadrc);
1211
1212        #[cfg(feature = "perf_timers")]
1213        self.start_cpu_delay_timer();
1214
1215        Process::set_shared_time(host);
1216
1217        // Discard any unapplied latency.
1218        // We currently only want this mechanism to force a yield if the thread itself
1219        // never yields; we don't want unapplied latency to accumulate and force a yield
1220        // under normal circumstances.
1221        host.shim_shmem_lock_borrow_mut()
1222            .unwrap()
1223            .unapplied_cpu_latency = SimulationTime::ZERO;
1224
1225        let ctx = ProcessContext::new(host, self);
1226        let res = thread.resume(&ctx);
1227
1228        #[cfg(feature = "perf_timers")]
1229        {
1230            let delay = self.stop_cpu_delay_timer(host);
1231            debug!("process '{}' ran for {:?}", &*self.name(), delay);
1232        }
1233        #[cfg(not(feature = "perf_timers"))]
1234        debug!("process '{}' done continuing", &*self.name());
1235
1236        match res {
1237            crate::host::thread::ResumeResult::Blocked => {
1238                debug!(
1239                    "thread {tid} in process '{}' still running, but blocked",
1240                    &*self.name()
1241                );
1242            }
1243            crate::host::thread::ResumeResult::ExitedThread(return_code) => {
1244                debug!(
1245                    "thread {tid} in process '{}' exited with code {return_code}",
1246                    &*self.name(),
1247                );
1248                let (threadrc, last_thread) = {
1249                    let runnable = self.as_runnable().unwrap();
1250                    let mut threads = runnable.threads.borrow_mut();
1251                    let threadrc = threads.remove(&tid).unwrap();
1252                    (threadrc, threads.is_empty())
1253                };
1254                self.as_runnable().unwrap().reap_thread(host, threadrc);
1255                if last_thread {
1256                    self.handle_process_exit(host, false);
1257                }
1258            }
1259            crate::host::thread::ResumeResult::ExitedProcess => {
1260                debug!(
1261                    "Process {} exited while running thread {tid}",
1262                    &*self.name(),
1263                );
1264                self.handle_process_exit(host, false);
1265            }
1266        };
1267
1268        Worker::clear_active_thread();
1269    }
1270
1271    /// Terminate the Process.
1272    ///
1273    /// Should only be called from [`Host::free_all_applications`].
1274    pub fn stop(&self, host: &Host) {
1275        // Scope for `runnable`
1276        {
1277            let Some(runnable) = self.as_runnable() else {
1278                debug!("process {} has already stopped", &*self.name());
1279                return;
1280            };
1281            debug!("terminating process {}", &*self.name());
1282
1283            #[cfg(feature = "perf_timers")]
1284            runnable.start_cpu_delay_timer();
1285
1286            if let Err(err) = rustix::process::kill_process(
1287                runnable.native_pid().into(),
1288                rustix::process::Signal::Kill,
1289            ) {
1290                warn!("kill: {:?}", err);
1291            }
1292
1293            #[cfg(feature = "perf_timers")]
1294            {
1295                let delay = runnable.stop_cpu_delay_timer(host);
1296                debug!("process '{}' stopped in {:?}", &*self.name(), delay);
1297            }
1298            #[cfg(not(feature = "perf_timers"))]
1299            debug!("process '{}' stopped", &*self.name());
1300        }
1301
1302        // Mutates `self.state`, so we need to have dropped `runnable`.
1303        self.handle_process_exit(host, true);
1304    }
1305
1306    /// See `RunnableProcess::signal`.
1307    ///
1308    /// No-op if the `self` is a `ZombieProcess`.
1309    pub fn signal(&self, host: &Host, current_thread: Option<&Thread>, siginfo_t: &siginfo_t) {
1310        // Using full-match here to force update if we add more states later.
1311        match self.state.borrow().as_ref().unwrap() {
1312            ProcessState::Runnable(r) => r.signal(host, current_thread, siginfo_t),
1313            ProcessState::Zombie(_) => {
1314                // Sending a signal to a zombie process is a no-op.
1315                debug!("Process {} no longer running", &*self.name());
1316            }
1317        }
1318    }
1319
1320    fn open_stdio_file_helper(
1321        descriptor_table: &mut DescriptorTable,
1322        fd: DescriptorHandle,
1323        path: PathBuf,
1324        access_mode: OFlag,
1325    ) {
1326        let stdfile = unsafe { cshadow::regularfile_new() };
1327        let cwd = rustix::process::getcwd(Vec::new()).unwrap();
1328        let path = utility::pathbuf_to_nul_term_cstring(path);
1329        // "Convert" to libc int, assuming here that the kernel's `OFlag` values
1330        // are compatible with libc's values.
1331        // XXX: We're assuming here that the kernel and libc flags are ABI
1332        // compatible, which isn't guaranteed, but is mostly true in practice.
1333        // TODO: We probably ought to change `regularfile_open` and friends to
1334        // use a direct syscall instead of libc's wrappers, and explicitly take
1335        // the kernel version of flags, mode, etc.
1336        let access_mode = access_mode.bits();
1337        let errorcode = unsafe {
1338            cshadow::regularfile_open(
1339                stdfile,
1340                path.as_ptr(),
1341                access_mode | libc::O_CREAT | libc::O_TRUNC,
1342                libc::S_IRUSR | libc::S_IWUSR | libc::S_IRGRP | libc::S_IROTH,
1343                cwd.as_ptr(),
1344            )
1345        };
1346        if errorcode != 0 {
1347            panic!(
1348                "Opening {}: {:?}",
1349                path.to_str().unwrap(),
1350                linux_api::errno::Errno::try_from(-errorcode).unwrap()
1351            );
1352        }
1353        let desc = unsafe {
1354            Descriptor::from_legacy_file(
1355                stdfile as *mut cshadow::LegacyFile,
1356                linux_api::fcntl::OFlag::empty(),
1357            )
1358        };
1359        let prev = descriptor_table.register_descriptor_with_fd(desc, fd);
1360        assert!(prev.is_none());
1361        trace!(
1362            "Successfully opened fd {} at {}",
1363            fd,
1364            path.to_str().unwrap()
1365        );
1366    }
1367
1368    // Needed during early init, before `Self` is created.
1369    fn static_output_file_name(file_basename: &Path, extension: &str) -> PathBuf {
1370        let mut path = file_basename.to_owned().into_os_string();
1371        path.push(".");
1372        path.push(extension);
1373        path.into()
1374    }
1375
1376    pub fn name(&self) -> impl Deref<Target = str> + '_ {
1377        Ref::map(self.common(), |c| c.name.to_str().unwrap())
1378    }
1379
1380    pub fn plugin_name(&self) -> impl Deref<Target = str> + '_ {
1381        Ref::map(self.common(), |c| c.plugin_name.to_str().unwrap())
1382    }
1383
1384    /// Deprecated wrapper for `RunnableProcess::memory_borrow_mut`
1385    #[track_caller]
1386    pub fn memory_borrow_mut(&self) -> impl DerefMut<Target = MemoryManager> + '_ {
1387        std_util::nested_ref::NestedRefMut::map(self.as_runnable().unwrap(), |runnable| {
1388            runnable.memory_manager.borrow_mut()
1389        })
1390    }
1391
1392    /// Deprecated wrapper for `RunnableProcess::memory_borrow`
1393    #[track_caller]
1394    pub fn memory_borrow(&self) -> impl Deref<Target = MemoryManager> + '_ {
1395        std_util::nested_ref::NestedRef::map(self.as_runnable().unwrap(), |runnable| {
1396            runnable.memory_manager.borrow()
1397        })
1398    }
1399
1400    /// Deprecated wrapper for `RunnableProcess::strace_logging_options`
1401    pub fn strace_logging_options(&self) -> Option<FmtOptions> {
1402        self.as_runnable().unwrap().strace_logging_options()
1403    }
1404
1405    /// Deprecated wrapper for `RunnableProcess::with_strace_file`
1406    pub fn with_strace_file<T>(&self, f: impl FnOnce(&mut std::fs::File) -> T) -> Option<T> {
1407        self.as_runnable().unwrap().with_strace_file(f)
1408    }
1409
1410    /// Deprecated wrapper for `RunnableProcess::native_pid`
1411    pub fn native_pid(&self) -> Pid {
1412        self.as_runnable().unwrap().native_pid()
1413    }
1414
1415    /// Deprecated wrapper for `RunnableProcess::realtime_timer_borrow`
1416    #[track_caller]
1417    pub fn realtime_timer_borrow(&self) -> impl Deref<Target = Timer> + '_ {
1418        std_util::nested_ref::NestedRef::map(self.as_runnable().unwrap(), |runnable| {
1419            runnable.itimer_real.borrow()
1420        })
1421    }
1422
1423    /// Deprecated wrapper for `RunnableProcess::realtime_timer_borrow_mut`
1424    #[track_caller]
1425    pub fn realtime_timer_borrow_mut(&self) -> impl DerefMut<Target = Timer> + '_ {
1426        std_util::nested_ref::NestedRefMut::map(self.as_runnable().unwrap(), |runnable| {
1427            runnable.itimer_real.borrow_mut()
1428        })
1429    }
1430
1431    /// Deprecated wrapper for `RunnableProcess::first_live_thread_borrow`
1432    #[track_caller]
1433    pub fn first_live_thread_borrow(
1434        &self,
1435        root: &Root,
1436    ) -> Option<impl Deref<Target = RootedRc<RootedRefCell<Thread>>> + '_> {
1437        std_util::nested_ref::NestedRef::filter_map(self.as_runnable()?, |runnable| {
1438            runnable.first_live_thread(root)
1439        })
1440    }
1441
1442    /// Deprecated wrapper for `RunnableProcess::thread_borrow`
1443    pub fn thread_borrow(
1444        &self,
1445        virtual_tid: ThreadId,
1446    ) -> Option<impl Deref<Target = RootedRc<RootedRefCell<Thread>>> + '_> {
1447        std_util::nested_ref::NestedRef::filter_map(self.as_runnable()?, |runnable| {
1448            runnable.thread(virtual_tid)
1449        })
1450    }
1451
1452    /// Deprecated wrapper for [`RunnableProcess::free_unsafe_borrows_flush`].
1453    pub fn free_unsafe_borrows_flush(&self) -> Result<(), Errno> {
1454        self.as_runnable().unwrap().free_unsafe_borrows_flush()
1455    }
1456
1457    /// Deprecated wrapper for [`RunnableProcess::free_unsafe_borrows_noflush`].
1458    pub fn free_unsafe_borrows_noflush(&self) {
1459        self.as_runnable().unwrap().free_unsafe_borrows_noflush()
1460    }
1461
1462    pub fn physical_address(&self, vptr: ForeignPtr<()>) -> ManagedPhysicalMemoryAddr {
1463        self.common().physical_address(vptr)
1464    }
1465
1466    pub fn is_running(&self) -> bool {
1467        self.as_runnable().is_some()
1468    }
1469
1470    /// Transitions `self` from a `RunnableProcess` to a `ZombieProcess`.
1471    fn handle_process_exit(&self, host: &Host, killed_by_shadow: bool) {
1472        debug!(
1473            "process '{}' has completed or is otherwise no longer running",
1474            &*self.name()
1475        );
1476
1477        // Take and dispose of all of the threads.
1478        // TODO: consider doing this while the `self.state` mutable reference is held
1479        // as with the other cleanup below. Right now this breaks some C code that expects
1480        // to be able to lookup the thread's process name.
1481        {
1482            let runnable = self.as_runnable().unwrap();
1483            let threads = std::mem::take(&mut *runnable.threads.borrow_mut());
1484            for (_tid, threadrc) in threads.into_iter() {
1485                threadrc.borrow(host.root()).handle_process_exit();
1486                runnable.reap_thread(host, threadrc);
1487            }
1488        }
1489
1490        // Intentionally hold the borrow on self.state to ensure the state
1491        // transition is "atomic".
1492        let mut opt_state = self.state.borrow_mut();
1493
1494        let state = opt_state.take().unwrap();
1495        let ProcessState::Runnable(runnable) = state else {
1496            unreachable!("Tried to handle process exit of non-running process");
1497        };
1498
1499        #[cfg(feature = "perf_timers")]
1500        debug!(
1501            "total runtime for process '{}' was {:?}",
1502            runnable.common.name(),
1503            runnable.total_run_time.get()
1504        );
1505
1506        let wait_res: Option<WaitStatus> =
1507            rustix::process::waitpid(Some(runnable.native_pid().into()), WaitOptions::empty())
1508                .unwrap_or_else(|e| {
1509                    panic!("Error waiting for {:?}: {:?}", runnable.native_pid(), e)
1510                });
1511        let wait_status = wait_res.unwrap();
1512        let exit_status = if killed_by_shadow {
1513            if wait_status.terminating_signal()
1514                != Some(Signal::SIGKILL.as_i32().try_into().unwrap())
1515            {
1516                warn!("Unexpected waitstatus after killed by shadow: {wait_status:?}");
1517            }
1518            ExitStatus::StoppedByShadow
1519        } else if let Some(code) = wait_status.exit_status() {
1520            ExitStatus::Normal(code.try_into().unwrap())
1521        } else if let Some(signal) = wait_status.terminating_signal() {
1522            ExitStatus::Signaled(Signal::try_from(i32::try_from(signal).unwrap()).unwrap())
1523        } else {
1524            panic!(
1525                "Unexpected status: {wait_status:?} for pid {:?}",
1526                runnable.native_pid()
1527            );
1528        };
1529
1530        let (main_result_string, log_level) = {
1531            let mut s = format!(
1532                "process '{name}' exited with status {exit_status:?}",
1533                name = runnable.common.name()
1534            );
1535            if let Some(expected_final_state) = runnable.expected_final_state {
1536                let actual_final_state = match exit_status {
1537                    ExitStatus::Normal(i) => ProcessFinalState::Exited { exited: i },
1538                    ExitStatus::Signaled(s) => ProcessFinalState::Signaled {
1539                        // This conversion will fail on realtime signals, but that
1540                        // should currently be impossible since we don't support
1541                        // sending realtime signals.
1542                        signaled: s.try_into().unwrap(),
1543                    },
1544                    ExitStatus::StoppedByShadow => ProcessFinalState::Running(RunningVal::Running),
1545                };
1546                if expected_final_state == actual_final_state {
1547                    (s, log::Level::Debug)
1548                } else {
1549                    Worker::increment_plugin_error_count();
1550                    write!(s, "; expected end state was {expected_final_state} but was {actual_final_state}").unwrap();
1551                    (s, log::Level::Error)
1552                }
1553            } else {
1554                (s, log::Level::Debug)
1555            }
1556        };
1557        log::log!(log_level, "{}", main_result_string);
1558
1559        let zombie = ZombieProcess {
1560            common: runnable.into_common(),
1561            exit_status,
1562        };
1563        zombie.notify_parent_of_exit(host);
1564
1565        *opt_state = Some(ProcessState::Zombie(zombie));
1566    }
1567
1568    /// Deprecated wrapper for `RunnableProcess::add_thread`
1569    pub fn add_thread(&self, host: &Host, thread: RootedRc<RootedRefCell<Thread>>) {
1570        self.as_runnable().unwrap().add_thread(host, thread)
1571    }
1572
1573    /// FIXME: still needed? Time is now updated more granularly in the Thread code
1574    /// when xferring control to/from shim.
1575    fn set_shared_time(host: &Host) {
1576        let mut host_shmem = host.shim_shmem_lock_borrow_mut().unwrap();
1577        host_shmem.max_runahead_time = Worker::max_event_runahead_time(host);
1578        host.shim_shmem()
1579            .sim_time
1580            .store(Worker::current_time().unwrap(), Ordering::Relaxed);
1581    }
1582
1583    /// Deprecated wrapper for `RunnableProcess::shmem`
1584    pub fn shmem(&self) -> impl Deref<Target = ShMemBlock<'static, ProcessShmem>> + '_ {
1585        Ref::map(self.as_runnable().unwrap(), |r| &r.shim_shared_mem_block)
1586    }
1587
1588    /// Resource usage, as returned e.g. by the `getrusage` syscall.
1589    pub fn rusage(&self) -> linux_api::resource::rusage {
1590        warn_once_then_debug!(
1591            "resource usage (rusage) tracking unimplemented; Returning bogus zeroed values"
1592        );
1593        // TODO: Actually track some of these.
1594        // Assuming we want to support `RUSAGE_THREAD` in the `getrusage`
1595        // syscall, we'll actually want to track at the thread level, and either
1596        // increment at both thread and process level at the points where we do
1597        // the tracking, or dynamically iterate over the threads here and sum
1598        // the results.
1599        linux_api::resource::rusage {
1600            ru_utime: linux_api::time::kernel_old_timeval {
1601                tv_sec: 0,
1602                tv_usec: 0,
1603            },
1604            ru_stime: linux_api::time::kernel_old_timeval {
1605                tv_sec: 0,
1606                tv_usec: 0,
1607            },
1608            ru_maxrss: 0,
1609            ru_ixrss: 0,
1610            ru_idrss: 0,
1611            ru_isrss: 0,
1612            ru_minflt: 0,
1613            ru_majflt: 0,
1614            ru_nswap: 0,
1615            ru_inblock: 0,
1616            ru_oublock: 0,
1617            ru_msgsnd: 0,
1618            ru_msgrcv: 0,
1619            ru_nsignals: 0,
1620            ru_nvcsw: 0,
1621            ru_nivcsw: 0,
1622        }
1623    }
1624
1625    /// Signal that will be sent to parent process on exit. Typically `Some(SIGCHLD)`.
1626    pub fn exit_signal(&self) -> Option<Signal> {
1627        self.common().exit_signal
1628    }
1629
1630    pub fn current_working_dir(&self) -> impl Deref<Target = CString> + '_ {
1631        Ref::map(self.common(), |common| &common.working_dir)
1632    }
1633
1634    /// Set the process's working directory.
1635    /// This must be kept in sync with the actual working dir of the native process.
1636    /// See <https://github.com/shadow/shadow/issues/2960>
1637    // TODO: This ought to be at the thread level, to support `CLONE_FS`.
1638    pub fn set_current_working_dir(&self, path: CString) {
1639        self.common_mut().working_dir = path;
1640    }
1641
1642    /// Update `self` to complete an `exec` syscall from thread `tid`, replacing
1643    /// the running managed process with `mthread`.
1644    pub fn update_for_exec(&mut self, host: &Host, tid: ThreadId, mthread: ManagedThread) {
1645        let Some(mut runnable) = self.as_runnable_mut() else {
1646            // This could happen if another event runs before the "execve completion" event
1647            // and kills the process. e.g. another thread in the process could run and
1648            // execute the `exit_group` syscall.
1649            log::debug!(
1650                "Process {:?} exited before it could complete execve",
1651                self.id()
1652            );
1653            mthread.kill_and_drop();
1654            return;
1655        };
1656        let old_native_pid = std::mem::replace(&mut runnable.native_pid, mthread.native_pid());
1657
1658        // Kill the previous native process
1659        rustix::process::kill_process(old_native_pid.into(), rustix::process::Signal::Kill)
1660            .expect("Unable to send kill signal to managed process {old_native_pid:?}");
1661        let wait_res = rustix::process::waitpid(Some(old_native_pid.into()), WaitOptions::empty())
1662            .unwrap()
1663            .unwrap();
1664        assert_eq!(
1665            wait_res.terminating_signal(),
1666            Some(Signal::SIGKILL.as_i32().try_into().unwrap())
1667        );
1668
1669        let execing_thread = runnable.threads.borrow_mut().remove(&tid).unwrap();
1670
1671        // Dispose of all threads other than the thread that's running `exec`.
1672        for (_tid, thread) in runnable.threads.replace(BTreeMap::new()) {
1673            // Notify the ManagedThread that the native process has exited.
1674            thread.borrow(host.root()).mthread().handle_process_exit();
1675
1676            thread.explicit_drop_recursive(host.root(), host);
1677        }
1678
1679        // Recreate the `MemoryManager`
1680        {
1681            // We can't safely replace the memory manager if there are outstanding
1682            // unsafe references in C code. There shouldn't be any, though, since
1683            // this is only called from the `execve` and `execveat` syscall handlers,
1684            // which are in Rust.
1685            let unsafe_borrow_mut = runnable.unsafe_borrow_mut.borrow();
1686            let unsafe_borrows = runnable.unsafe_borrows.borrow();
1687            assert!(unsafe_borrow_mut.is_none());
1688            assert!(unsafe_borrows.is_empty());
1689            // Replace the MM, while still holding the references to the unsafe borrows
1690            // to ensure none exist.
1691            runnable
1692                .memory_manager
1693                .replace(unsafe { MemoryManager::new(mthread.native_pid()) });
1694        }
1695
1696        let new_tid = runnable.common.thread_group_leader_id();
1697        log::trace!(
1698            "updating for exec; pid:{pid}, tid:{tid:?}, new_tid:{new_tid:?}",
1699            pid = runnable.common.id
1700        );
1701        execing_thread
1702            .borrow_mut(host.root())
1703            .update_for_exec(host, mthread, new_tid);
1704
1705        runnable
1706            .threads
1707            .borrow_mut()
1708            .insert(new_tid, execing_thread);
1709
1710        // Exit signal is reset to SIGCHLD.
1711        runnable.common.exit_signal = Some(Signal::SIGCHLD);
1712
1713        // Reset signal actions to default.
1714        // `execve(2)`:
1715        // POSIX.1 specifies that the dispositions of any signals that
1716        // are ignored or set to the default are left unchanged.  POSIX.1
1717        // specifies one exception: if SIGCHLD is being ignored, then an
1718        // implementation may leave the disposition unchanged or reset it
1719        // to the default; Linux does the former.
1720        let host_shmem_prot = host.shim_shmem_lock_borrow_mut().unwrap();
1721        let mut shmem_prot = runnable
1722            .shim_shared_mem_block
1723            .protected
1724            .borrow_mut(&host_shmem_prot.root);
1725        for signal in Signal::standard_signals() {
1726            let current_action = unsafe { shmem_prot.signal_action(signal) };
1727            if !(current_action.is_default()
1728                || current_action.is_ignore()
1729                || signal == Signal::SIGCHLD && current_action.is_ignore())
1730            {
1731                unsafe {
1732                    *shmem_prot.signal_action_mut(signal) = linux_api::signal::sigaction::new_raw(
1733                        linux_api::signal::SignalHandler::SigDfl,
1734                        SigActionFlags::empty(),
1735                        sigset_t::EMPTY,
1736                        None,
1737                    )
1738                };
1739            }
1740        }
1741    }
1742}
1743
1744impl Drop for Process {
1745    fn drop(&mut self) {
1746        // Should have been explicitly dropped.
1747        debug_assert!(self.state.borrow().is_none());
1748    }
1749}
1750
1751impl ExplicitDrop for Process {
1752    type ExplicitDropParam = Host;
1753    type ExplicitDropResult = ();
1754
1755    fn explicit_drop(mut self, host: &Self::ExplicitDropParam) -> Self::ExplicitDropResult {
1756        // Should normally only be dropped in the zombie state.
1757        debug_assert!(self.as_zombie().is_some() || std::thread::panicking());
1758
1759        let state = self.state.get_mut().take().unwrap();
1760        state.explicit_drop(host);
1761    }
1762}
1763
1764/// Tracks a memory reference made by a legacy C memory-read API.
1765struct UnsafeBorrow {
1766    // Must come before `manager`, so that it's dropped first, since it's
1767    // borrowed from it.
1768    _memory: ProcessMemoryRef<'static, u8>,
1769    _manager: Ref<'static, MemoryManager>,
1770}
1771
1772impl UnsafeBorrow {
1773    /// Creates a raw readable pointer, and saves an instance of `Self` into
1774    /// `process` for later clean-up.
1775    ///
1776    /// # Safety
1777    ///
1778    /// The pointer is invalidated when one of the Process memory flush methods is called.
1779    unsafe fn readable_ptr(
1780        process: &Process,
1781        ptr: ForeignArrayPtr<u8>,
1782    ) -> Result<*const c_void, Errno> {
1783        let runnable = process.as_runnable().unwrap();
1784        let manager = runnable.memory_manager.borrow();
1785        // SAFETY: We ensure that the `memory` is dropped before the `manager`,
1786        // and `Process` ensures that this whole object is dropped before
1787        // `MemoryManager` can be moved, freed, etc.
1788        let manager = unsafe {
1789            std::mem::transmute::<Ref<'_, MemoryManager>, Ref<'static, MemoryManager>>(manager)
1790        };
1791        let memory = manager.memory_ref(ptr)?;
1792        let memory = unsafe {
1793            std::mem::transmute::<ProcessMemoryRef<'_, u8>, ProcessMemoryRef<'static, u8>>(memory)
1794        };
1795        let vptr = memory.as_ptr() as *mut c_void;
1796        runnable.unsafe_borrows.borrow_mut().push(Self {
1797            _manager: manager,
1798            _memory: memory,
1799        });
1800        Ok(vptr)
1801    }
1802
1803    /// Creates a raw readable string, and saves an instance of `Self` into
1804    /// `process` for later clean-up.
1805    ///
1806    /// # Safety
1807    ///
1808    /// The pointer is invalidated when one of the Process memory flush methods is called.
1809    unsafe fn readable_string(
1810        process: &Process,
1811        ptr: ForeignArrayPtr<c_char>,
1812    ) -> Result<(*const c_char, libc::size_t), Errno> {
1813        let runnable = process.as_runnable().unwrap();
1814        let manager = runnable.memory_manager.borrow();
1815        // SAFETY: We ensure that the `memory` is dropped before the `manager`,
1816        // and `Process` ensures that this whole object is dropped before
1817        // `MemoryManager` can be moved, freed, etc.
1818        let manager = unsafe {
1819            std::mem::transmute::<Ref<'_, MemoryManager>, Ref<'static, MemoryManager>>(manager)
1820        };
1821        let ptr = ptr.cast_u8();
1822        let memory = manager.memory_ref_prefix(ptr)?;
1823        let memory = unsafe {
1824            std::mem::transmute::<ProcessMemoryRef<'_, u8>, ProcessMemoryRef<'static, u8>>(memory)
1825        };
1826        if !memory.contains(&0) {
1827            return Err(Errno::ENAMETOOLONG);
1828        }
1829        assert_eq!(std::mem::size_of::<c_char>(), std::mem::size_of::<u8>());
1830        let ptr = memory.as_ptr() as *const c_char;
1831        let len = memory.len();
1832        runnable.unsafe_borrows.borrow_mut().push(Self {
1833            _manager: manager,
1834            _memory: memory,
1835        });
1836        Ok((ptr, len))
1837    }
1838}
1839
1840// Safety: Normally the Ref would make this non-Send, since it could end then
1841// end up trying to manipulate the source RefCell (which is !Sync) from multiple
1842// threads.  We ensure that these objects never escape Process, which itself is
1843// non-Sync, ensuring this doesn't happen.
1844//
1845// This is admittedly hand-wavy and making some assumptions about the
1846// implementation of RefCell, but this whole type is temporary scaffolding to
1847// support legacy C code.
1848unsafe impl Send for UnsafeBorrow {}
1849
1850/// Tracks a memory reference made by a legacy C memory-write API.
1851struct UnsafeBorrowMut {
1852    // Must come before `manager`, so that it's dropped first, since it's
1853    // borrowed from it.
1854    memory: Option<ProcessMemoryRefMut<'static, u8>>,
1855    _manager: RefMut<'static, MemoryManager>,
1856}
1857
1858impl UnsafeBorrowMut {
1859    /// Creates a raw writable pointer, and saves an instance of `Self` into
1860    /// `process` for later clean-up. The initial contents of the pointer is unspecified.
1861    ///
1862    /// # Safety
1863    ///
1864    /// The pointer is invalidated when one of the Process memory flush methods is called.
1865    unsafe fn writable_ptr(
1866        process: &Process,
1867        ptr: ForeignArrayPtr<u8>,
1868    ) -> Result<*mut c_void, Errno> {
1869        let runnable = process.as_runnable().unwrap();
1870        let manager = runnable.memory_manager.borrow_mut();
1871        // SAFETY: We ensure that the `memory` is dropped before the `manager`,
1872        // and `Process` ensures that this whole object is dropped before
1873        // `MemoryManager` can be moved, freed, etc.
1874        let mut manager = unsafe {
1875            std::mem::transmute::<RefMut<'_, MemoryManager>, RefMut<'static, MemoryManager>>(
1876                manager,
1877            )
1878        };
1879        let memory = manager.memory_ref_mut_uninit(ptr)?;
1880        let mut memory = unsafe {
1881            std::mem::transmute::<ProcessMemoryRefMut<'_, u8>, ProcessMemoryRefMut<'static, u8>>(
1882                memory,
1883            )
1884        };
1885        let vptr = memory.as_mut_ptr() as *mut c_void;
1886        let prev = runnable.unsafe_borrow_mut.borrow_mut().replace(Self {
1887            _manager: manager,
1888            memory: Some(memory),
1889        });
1890        assert!(prev.is_none());
1891        Ok(vptr)
1892    }
1893
1894    /// Creates a raw mutable pointer, and saves an instance of `Self` into
1895    /// `process` for later clean-up.
1896    ///
1897    /// # Safety
1898    ///
1899    /// The pointer is invalidated when one of the Process memory flush methods is called.
1900    unsafe fn mutable_ptr(
1901        process: &Process,
1902        ptr: ForeignArrayPtr<u8>,
1903    ) -> Result<*mut c_void, Errno> {
1904        let runnable = process.as_runnable().unwrap();
1905        let manager = runnable.memory_manager.borrow_mut();
1906        // SAFETY: We ensure that the `memory` is dropped before the `manager`,
1907        // and `Process` ensures that this whole object is dropped before
1908        // `MemoryManager` can be moved, freed, etc.
1909        let mut manager = unsafe {
1910            std::mem::transmute::<RefMut<'_, MemoryManager>, RefMut<'static, MemoryManager>>(
1911                manager,
1912            )
1913        };
1914        let memory = manager.memory_ref_mut(ptr)?;
1915        let mut memory = unsafe {
1916            std::mem::transmute::<ProcessMemoryRefMut<'_, u8>, ProcessMemoryRefMut<'static, u8>>(
1917                memory,
1918            )
1919        };
1920        let vptr = memory.as_mut_ptr() as *mut c_void;
1921        let prev = runnable.unsafe_borrow_mut.borrow_mut().replace(Self {
1922            _manager: manager,
1923            memory: Some(memory),
1924        });
1925        assert!(prev.is_none());
1926        Ok(vptr)
1927    }
1928
1929    /// Free this reference, writing back to process memory.
1930    fn flush(mut self) -> Result<(), Errno> {
1931        self.memory.take().unwrap().flush()
1932    }
1933
1934    /// Free this reference without writing back to process memory.
1935    fn noflush(mut self) {
1936        self.memory.take().unwrap().noflush()
1937    }
1938}
1939
1940// Safety: Normally the RefMut would make this non-Send, since it could end then
1941// end up trying to manipulate the source RefCell (which is !Sync) from multiple
1942// threads.  We ensure that these objects never escape Process, which itself is
1943// non-Sync, ensuring this doesn't happen.
1944//
1945// This is admittedly hand-wavy and making some assumptions about the implementation of
1946// RefCell, but this whole type is temporary scaffolding to support legacy C code.
1947unsafe impl Send for UnsafeBorrowMut {}
1948
1949fn make_name(host: &Host, exe_name: &str, id: ProcessId) -> CString {
1950    CString::new(format!(
1951        "{host_name}.{exe_name}.{id}",
1952        host_name = host.name(),
1953        exe_name = exe_name,
1954        id = u32::from(id)
1955    ))
1956    .unwrap()
1957}
1958
1959mod export {
1960    use std::os::raw::c_void;
1961
1962    use libc::size_t;
1963    use log::trace;
1964    use shadow_shim_helper_rs::notnull::*;
1965    use shadow_shim_helper_rs::shim_shmem::export::ShimShmemProcess;
1966    use shadow_shim_helper_rs::syscall_types::UntypedForeignPtr;
1967
1968    use super::*;
1969    use crate::utility::HostTreePointer;
1970
1971    /// Copy `n` bytes from `src` to `dst`. Returns 0 on success or -EFAULT if any of
1972    /// the specified range couldn't be accessed. Always succeeds with n==0.
1973    #[unsafe(no_mangle)]
1974    pub extern "C-unwind" fn process_readPtr(
1975        proc: *const Process,
1976        dst: *mut c_void,
1977        src: UntypedForeignPtr,
1978        n: usize,
1979    ) -> i32 {
1980        let proc = unsafe { proc.as_ref().unwrap() };
1981        let src = ForeignArrayPtr::new(src.cast::<u8>(), n);
1982        let dst = unsafe { std::slice::from_raw_parts_mut(notnull_mut_debug(dst) as *mut u8, n) };
1983
1984        match proc.memory_borrow().copy_from_ptr(dst, src) {
1985            Ok(_) => 0,
1986            Err(e) => {
1987                trace!("Couldn't read {:?} into {:?}: {:?}", src, dst, e);
1988                e.to_negated_i32()
1989            }
1990        }
1991    }
1992
1993    /// Copy `n` bytes from `src` to `dst`. Returns 0 on success or -EFAULT if any of
1994    /// the specified range couldn't be accessed. The write is flushed immediately.
1995    #[unsafe(no_mangle)]
1996    pub unsafe extern "C-unwind" fn process_writePtr(
1997        proc: *const Process,
1998        dst: UntypedForeignPtr,
1999        src: *const c_void,
2000        n: usize,
2001    ) -> i32 {
2002        let proc = unsafe { proc.as_ref().unwrap() };
2003        let dst = ForeignArrayPtr::new(dst.cast::<u8>(), n);
2004        let src = unsafe { std::slice::from_raw_parts(notnull_debug(src) as *const u8, n) };
2005        match proc.memory_borrow_mut().copy_to_ptr(dst, src) {
2006            Ok(_) => 0,
2007            Err(e) => {
2008                trace!("Couldn't write {:?} into {:?}: {:?}", src, dst, e);
2009                e.to_negated_i32()
2010            }
2011        }
2012    }
2013
2014    /// Make the data at plugin_src available in shadow's address space.
2015    ///
2016    /// The returned pointer is invalidated when one of the process memory flush
2017    /// methods is called; typically after a syscall has completed.
2018    #[unsafe(no_mangle)]
2019    pub unsafe extern "C-unwind" fn process_getReadablePtr(
2020        proc: *const Process,
2021        plugin_src: UntypedForeignPtr,
2022        n: usize,
2023    ) -> *const c_void {
2024        let proc = unsafe { proc.as_ref().unwrap() };
2025        let plugin_src = ForeignArrayPtr::new(plugin_src.cast::<u8>(), n);
2026        unsafe { UnsafeBorrow::readable_ptr(proc, plugin_src).unwrap_or(std::ptr::null()) }
2027    }
2028
2029    /// Returns a writable pointer corresponding to the named region. The
2030    /// initial contents of the returned memory are unspecified.
2031    ///
2032    /// The returned pointer is invalidated when one of the process memory flush
2033    /// methods is called; typically after a syscall has completed.
2034    ///
2035    /// CAUTION: if the unspecified contents aren't overwritten, and the pointer
2036    /// isn't explicitly freed via `process_freePtrsWithoutFlushing`, those
2037    /// unspecified contents may be written back into process memory.
2038    #[unsafe(no_mangle)]
2039    pub unsafe extern "C-unwind" fn process_getWriteablePtr(
2040        proc: *const Process,
2041        plugin_src: UntypedForeignPtr,
2042        n: usize,
2043    ) -> *mut c_void {
2044        let proc = unsafe { proc.as_ref().unwrap() };
2045        let plugin_src = ForeignArrayPtr::new(plugin_src.cast::<u8>(), n);
2046        unsafe { UnsafeBorrowMut::writable_ptr(proc, plugin_src).unwrap_or(std::ptr::null_mut()) }
2047    }
2048
2049    /// Returns a writeable pointer corresponding to the specified src. Use when
2050    /// the data at the given address needs to be both read and written.
2051    ///
2052    /// The returned pointer is invalidated when one of the process memory flush
2053    /// methods is called; typically after a syscall has completed.
2054    #[unsafe(no_mangle)]
2055    pub unsafe extern "C-unwind" fn process_getMutablePtr(
2056        proc: *const Process,
2057        plugin_src: UntypedForeignPtr,
2058        n: usize,
2059    ) -> *mut c_void {
2060        let proc = unsafe { proc.as_ref().unwrap() };
2061        let plugin_src = ForeignArrayPtr::new(plugin_src.cast::<u8>(), n);
2062        unsafe { UnsafeBorrowMut::mutable_ptr(proc, plugin_src).unwrap_or(std::ptr::null_mut()) }
2063    }
2064
2065    /// Reads up to `n` bytes into `str`.
2066    ///
2067    /// Returns:
2068    /// strlen(str) on success.
2069    /// -ENAMETOOLONG if there was no NULL byte in the first `n` characters.
2070    /// -EFAULT if the string extends beyond the accessible address space.
2071    #[unsafe(no_mangle)]
2072    pub unsafe extern "C-unwind" fn process_readString(
2073        proc: *const Process,
2074        strbuf: *mut libc::c_char,
2075        ptr: UntypedForeignPtr,
2076        maxlen: libc::size_t,
2077    ) -> libc::ssize_t {
2078        let proc = unsafe { proc.as_ref().unwrap() };
2079        let memory_manager = proc.memory_borrow();
2080        let buf =
2081            unsafe { std::slice::from_raw_parts_mut(notnull_mut_debug(strbuf) as *mut u8, maxlen) };
2082        let cstr = match memory_manager
2083            .copy_str_from_ptr(buf, ForeignArrayPtr::new(ptr.cast::<u8>(), maxlen))
2084        {
2085            Ok(cstr) => cstr,
2086            Err(e) => return e.to_negated_i32() as isize,
2087        };
2088        cstr.to_bytes().len().try_into().unwrap()
2089    }
2090
2091    /// Reads up to `n` bytes into `str`.
2092    ///
2093    /// Returns:
2094    /// strlen(str) on success.
2095    /// -ENAMETOOLONG if there was no NULL byte in the first `n` characters.
2096    /// -EFAULT if the string extends beyond the accessible address space.
2097    #[unsafe(no_mangle)]
2098    pub unsafe extern "C-unwind" fn process_getReadableString(
2099        proc: *const Process,
2100        plugin_src: UntypedForeignPtr,
2101        n: usize,
2102        out_str: *mut *const c_char,
2103        out_strlen: *mut size_t,
2104    ) -> i32 {
2105        let proc = unsafe { proc.as_ref().unwrap() };
2106        let ptr = ForeignArrayPtr::new(plugin_src.cast::<c_char>(), n);
2107        match unsafe { UnsafeBorrow::readable_string(proc, ptr) } {
2108            Ok((str, strlen)) => {
2109                assert!(!out_str.is_null());
2110                unsafe { out_str.write(str) };
2111                if !out_strlen.is_null() {
2112                    unsafe { out_strlen.write(strlen) };
2113                }
2114                0
2115            }
2116            Err(e) => e.to_negated_i32(),
2117        }
2118    }
2119
2120    /// Returns the processID that was assigned to us in process_new
2121    #[unsafe(no_mangle)]
2122    pub unsafe extern "C-unwind" fn process_getProcessID(proc: *const Process) -> libc::pid_t {
2123        let proc = unsafe { proc.as_ref().unwrap() };
2124        proc.id().into()
2125    }
2126
2127    #[unsafe(no_mangle)]
2128    pub unsafe extern "C-unwind" fn process_getName(proc: *const Process) -> *const c_char {
2129        let proc = unsafe { proc.as_ref().unwrap() };
2130        proc.common().name.as_ptr()
2131    }
2132
2133    /// Safety:
2134    ///
2135    /// The returned pointer is invalidated when the host shmem lock is released, e.g. via
2136    /// Host::unlock_shmem.
2137    #[unsafe(no_mangle)]
2138    pub unsafe extern "C-unwind" fn process_getSharedMem(
2139        proc: *const Process,
2140    ) -> *const ShimShmemProcess {
2141        let proc = unsafe { proc.as_ref().unwrap() };
2142        std::ptr::from_ref(proc.as_runnable().unwrap().shim_shared_mem_block.deref())
2143    }
2144
2145    #[unsafe(no_mangle)]
2146    pub unsafe extern "C-unwind" fn process_getWorkingDir(proc: *const Process) -> *const c_char {
2147        let proc = unsafe { proc.as_ref().unwrap() };
2148        proc.common().working_dir.as_ptr()
2149    }
2150
2151    #[unsafe(no_mangle)]
2152    pub unsafe extern "C-unwind" fn process_straceLoggingMode(
2153        proc: *const Process,
2154    ) -> StraceFmtMode {
2155        let proc = unsafe { proc.as_ref().unwrap() };
2156        proc.strace_logging_options().into()
2157    }
2158
2159    #[unsafe(no_mangle)]
2160    pub unsafe extern "C-unwind" fn process_getNativePid(proc: *const Process) -> libc::pid_t {
2161        let proc = unsafe { proc.as_ref().unwrap() };
2162        proc.native_pid().as_raw_nonzero().get()
2163    }
2164
2165    /// Flushes and invalidates all previously returned readable/writable plugin
2166    /// pointers, as if returning control to the plugin. This can be useful in
2167    /// conjunction with `thread_nativeSyscall` operations that touch memory, or
2168    /// to gracefully handle failed writes.
2169    ///
2170    /// Returns 0 on success or a negative errno on failure.
2171    #[unsafe(no_mangle)]
2172    pub unsafe extern "C-unwind" fn process_flushPtrs(proc: *const Process) -> i32 {
2173        let proc = unsafe { proc.as_ref().unwrap() };
2174        match proc.free_unsafe_borrows_flush() {
2175            Ok(_) => 0,
2176            Err(e) => e.to_negated_i32(),
2177        }
2178    }
2179
2180    /// Frees all readable/writable foreign pointers. Unlike process_flushPtrs, any
2181    /// previously returned writable pointer is *not* written back. Useful
2182    /// if an uninitialized writable pointer was obtained via `process_getWriteablePtr`,
2183    /// and we end up not wanting to write anything after all (in particular, don't
2184    /// write back whatever garbage data was in the uninialized bueffer).
2185    #[unsafe(no_mangle)]
2186    pub unsafe extern "C-unwind" fn process_freePtrsWithoutFlushing(proc: *const Process) {
2187        let proc = unsafe { proc.as_ref().unwrap() };
2188        proc.free_unsafe_borrows_noflush();
2189    }
2190
2191    #[unsafe(no_mangle)]
2192    pub unsafe extern "C-unwind" fn process_getThread(
2193        proc: *const Process,
2194        tid: libc::pid_t,
2195    ) -> *const Thread {
2196        let proc = unsafe { proc.as_ref().unwrap() };
2197        Worker::with_active_host(|host| {
2198            let tid = ThreadId::try_from(tid).unwrap();
2199            let Some(thread) = proc.thread_borrow(tid) else {
2200                return std::ptr::null();
2201            };
2202            let thread = thread.borrow(host.root());
2203            &*thread
2204        })
2205        .unwrap()
2206    }
2207
2208    /// Returns a pointer to an arbitrary live thread in the process.
2209    #[unsafe(no_mangle)]
2210    pub unsafe extern "C-unwind" fn process_firstLiveThread(proc: *const Process) -> *const Thread {
2211        let proc = unsafe { proc.as_ref().unwrap() };
2212        Worker::with_active_host(|host| {
2213            let Some(thread) = proc.first_live_thread_borrow(host.root()) else {
2214                return std::ptr::null();
2215            };
2216            let thread = thread.borrow(host.root());
2217            &*thread
2218        })
2219        .unwrap()
2220    }
2221
2222    #[unsafe(no_mangle)]
2223    pub unsafe extern "C-unwind" fn process_isRunning(proc: *const Process) -> bool {
2224        let proc = unsafe { proc.as_ref().unwrap() };
2225        proc.is_running()
2226    }
2227
2228    // FIXME: still needed? Time is now updated more granularly in the Thread code
2229    // when xferring control to/from shim.
2230    #[unsafe(no_mangle)]
2231    pub unsafe extern "C-unwind" fn process_setSharedTime() {
2232        Worker::with_active_host(Process::set_shared_time).unwrap();
2233    }
2234
2235    #[unsafe(no_mangle)]
2236    pub unsafe extern "C-unwind" fn process_getPhysicalAddress(
2237        proc: *const Process,
2238        vptr: UntypedForeignPtr,
2239    ) -> ManagedPhysicalMemoryAddr {
2240        let proc = unsafe { proc.as_ref().unwrap() };
2241        proc.physical_address(vptr)
2242    }
2243
2244    #[unsafe(no_mangle)]
2245    pub unsafe extern "C-unwind" fn process_addChildEventListener(
2246        host: *const Host,
2247        process: *const Process,
2248        listener: *mut cshadow::StatusListener,
2249    ) {
2250        let host = unsafe { host.as_ref().unwrap() };
2251        let process = unsafe { process.as_ref().unwrap() };
2252        let listener = HostTreePointer::new_for_host(host.id(), listener);
2253        process
2254            .borrow_as_runnable()
2255            .unwrap()
2256            .child_process_event_listeners
2257            .borrow_mut()
2258            .add_legacy_listener(listener)
2259    }
2260
2261    #[unsafe(no_mangle)]
2262    pub unsafe extern "C-unwind" fn process_removeChildEventListener(
2263        _host: *const Host,
2264        process: *const Process,
2265        listener: *mut cshadow::StatusListener,
2266    ) {
2267        let process = unsafe { process.as_ref().unwrap() };
2268        process
2269            .borrow_as_runnable()
2270            .unwrap()
2271            .child_process_event_listeners
2272            .borrow_mut()
2273            .remove_legacy_listener(listener)
2274    }
2275}