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.
815enum ProcessState {
816    Runnable(RunnableProcess),
817    Zombie(ZombieProcess),
818}
819
820impl ProcessState {
821    fn common(&self) -> &Common {
822        match self {
823            ProcessState::Runnable(r) => &r.common,
824            ProcessState::Zombie(z) => &z.common,
825        }
826    }
827
828    fn common_mut(&mut self) -> &mut Common {
829        match self {
830            ProcessState::Runnable(r) => &mut r.common,
831            ProcessState::Zombie(z) => &mut z.common,
832        }
833    }
834
835    fn as_runnable(&self) -> Option<&RunnableProcess> {
836        match self {
837            ProcessState::Runnable(r) => Some(r),
838            ProcessState::Zombie(_) => None,
839        }
840    }
841
842    fn as_runnable_mut(&mut self) -> Option<&mut RunnableProcess> {
843        match self {
844            ProcessState::Runnable(r) => Some(r),
845            ProcessState::Zombie(_) => None,
846        }
847    }
848
849    fn as_zombie(&self) -> Option<&ZombieProcess> {
850        match self {
851            ProcessState::Runnable(_) => None,
852            ProcessState::Zombie(z) => Some(z),
853        }
854    }
855}
856
857impl ExplicitDrop for ProcessState {
858    type ExplicitDropParam = Host;
859    type ExplicitDropResult = ();
860
861    fn explicit_drop(self, host: &Self::ExplicitDropParam) -> Self::ExplicitDropResult {
862        match self {
863            ProcessState::Runnable(r) => r.explicit_drop(host),
864            ProcessState::Zombie(_) => (),
865        }
866    }
867}
868
869/// A simulated process.
870pub struct Process {
871    // Most of the implementation should be in [`ProcessState`].
872    // This wrapper allows us to change the state.
873    state: RefCell<Option<ProcessState>>,
874}
875
876fn itimer_real_expiration(host: &Host, pid: ProcessId) {
877    let Some(process) = host.process_borrow(pid) else {
878        debug!("Process {:?} no longer exists", pid);
879        return;
880    };
881    let process = process.borrow(host.root());
882    let Some(runnable) = process.as_runnable() else {
883        debug!("Process {:?} no longer running", &*process.name());
884        return;
885    };
886    let timer = runnable.itimer_real.borrow();
887    // The siginfo_t structure only has an i32. Presumably we want to just truncate in
888    // case of overflow.
889    let expiration_count = timer.expiration_count() as i32;
890    let siginfo_t = siginfo_t::new_for_timer(Signal::SIGALRM, 0, expiration_count);
891    process.signal(host, None, &siginfo_t);
892}
893
894impl Process {
895    fn common(&self) -> Ref<Common> {
896        Ref::map(self.state.borrow(), |state| {
897            state.as_ref().unwrap().common()
898        })
899    }
900
901    fn common_mut(&self) -> RefMut<Common> {
902        RefMut::map(self.state.borrow_mut(), |state| {
903            state.as_mut().unwrap().common_mut()
904        })
905    }
906
907    fn as_runnable(&self) -> Option<Ref<RunnableProcess>> {
908        Ref::filter_map(self.state.borrow(), |state| {
909            state.as_ref().unwrap().as_runnable()
910        })
911        .ok()
912    }
913
914    fn as_runnable_mut(&self) -> Option<RefMut<RunnableProcess>> {
915        RefMut::filter_map(self.state.borrow_mut(), |state| {
916            state.as_mut().unwrap().as_runnable_mut()
917        })
918        .ok()
919    }
920
921    /// Borrows a reference to the internal [`RunnableProcess`] if `self` is runnable.
922    pub fn borrow_as_runnable(&self) -> Option<impl Deref<Target = RunnableProcess> + '_> {
923        self.as_runnable()
924    }
925
926    fn as_zombie(&self) -> Option<Ref<ZombieProcess>> {
927        Ref::filter_map(self.state.borrow(), |state| {
928            state.as_ref().unwrap().as_zombie()
929        })
930        .ok()
931    }
932
933    /// Borrows a reference to the internal [`ZombieProcess`] if `self` is a zombie.
934    pub fn borrow_as_zombie(&self) -> Option<impl Deref<Target = ZombieProcess> + '_> {
935        self.as_zombie()
936    }
937
938    /// Spawn a new process. The process will be runnable via [`Self::resume`]
939    /// once it has been added to the `Host`'s process list.
940    pub fn spawn(
941        host: &Host,
942        plugin_name: CString,
943        plugin_path: &CStr,
944        argv: Vec<CString>,
945        envv: Vec<CString>,
946        pause_for_debugging: bool,
947        strace_logging_options: Option<FmtOptions>,
948        expected_final_state: ProcessFinalState,
949    ) -> Result<RootedRc<RootedRefCell<Process>>, Errno> {
950        debug!("starting process '{:?}'", plugin_name);
951
952        let main_thread_id = host.get_new_thread_id();
953        let process_id = ProcessId::from(main_thread_id);
954
955        let desc_table = RootedRc::new(
956            host.root(),
957            RootedRefCell::new(host.root(), DescriptorTable::new()),
958        );
959        let itimer_real = RefCell::new(Timer::new(move |host| {
960            itimer_real_expiration(host, process_id)
961        }));
962
963        let name = make_name(host, plugin_name.to_str().unwrap(), process_id);
964
965        let mut file_basename = PathBuf::new();
966        file_basename.push(host.data_dir_path());
967        file_basename.push(format!(
968            "{exe_name}.{id}",
969            exe_name = plugin_name.to_str().unwrap(),
970            id = u32::from(process_id)
971        ));
972
973        let strace_logging = strace_logging_options.map(|options| {
974            let file =
975                std::fs::File::create(Self::static_output_file_name(&file_basename, "strace"))
976                    .unwrap();
977            debug_assert_cloexec(&file);
978            Arc::new(StraceLogging {
979                file: RootedRefCell::new(host.root(), file),
980                options,
981            })
982        });
983
984        let shim_shared_mem = ProcessShmem::new(
985            &host.shim_shmem_lock_borrow().unwrap().root,
986            host.shim_shmem().serialize(),
987            host.id(),
988            strace_logging
989                .as_ref()
990                .map(|x| x.file.borrow(host.root()).as_raw_fd()),
991        );
992        let shim_shared_mem_block = shadow_shmem::allocator::shmalloc(shim_shared_mem);
993
994        let working_dir = utility::pathbuf_to_nul_term_cstring(
995            std::fs::canonicalize(host.data_dir_path()).unwrap(),
996        );
997
998        {
999            let mut descriptor_table = desc_table.borrow_mut(host.root());
1000            Self::open_stdio_file_helper(
1001                &mut descriptor_table,
1002                libc::STDIN_FILENO.try_into().unwrap(),
1003                "/dev/null".into(),
1004                OFlag::O_RDONLY,
1005            );
1006
1007            let name = Self::static_output_file_name(&file_basename, "stdout");
1008            Self::open_stdio_file_helper(
1009                &mut descriptor_table,
1010                libc::STDOUT_FILENO.try_into().unwrap(),
1011                name,
1012                OFlag::O_WRONLY,
1013            );
1014
1015            let name = Self::static_output_file_name(&file_basename, "stderr");
1016            Self::open_stdio_file_helper(
1017                &mut descriptor_table,
1018                libc::STDERR_FILENO.try_into().unwrap(),
1019                name,
1020                OFlag::O_WRONLY,
1021            );
1022        }
1023
1024        let shimlog_file = Arc::new(
1025            std::fs::File::create(Self::static_output_file_name(&file_basename, "shimlog"))
1026                .unwrap(),
1027        );
1028        debug_assert_cloexec(&shimlog_file);
1029
1030        let mthread = ManagedThread::spawn(
1031            plugin_path,
1032            argv,
1033            envv,
1034            strace_logging
1035                .as_ref()
1036                .map(|s| s.file.borrow(host.root()))
1037                .as_deref(),
1038            &shimlog_file,
1039            host.preload_paths(),
1040        )?;
1041        let native_pid = mthread.native_pid();
1042        let main_thread =
1043            Thread::wrap_mthread(host, mthread, desc_table, process_id, main_thread_id).unwrap();
1044
1045        debug!("process '{:?}' started", plugin_name);
1046
1047        if pause_for_debugging {
1048            // will block until logger output has been flushed
1049            // there is a race condition where other threads may log between the
1050            // `eprintln` and `raise` below, but it should be rare
1051            log::logger().flush();
1052
1053            // Use a single `eprintln` to ensure we hold the lock for the whole message.
1054            // Defensively pre-construct a single string so that `eprintln` is
1055            // more likely to use a single `write` call, to minimize the chance
1056            // of more lines being written to stdout in the meantime, and in
1057            // case of C code writing to `STDERR` directly without taking Rust's
1058            // lock.
1059            let msg = format!(
1060                "\
1061              \n** Pausing with SIGTSTP to enable debugger attachment to managed process\
1062              \n** '{plugin_name:?}' (pid {native_pid:?}).\
1063              \n** If running Shadow under Bash, resume Shadow by pressing Ctrl-Z to background\
1064              \n** this task, and then typing \"fg\".\
1065              \n** If running GDB, resume Shadow by typing \"signal SIGCONT\"."
1066            );
1067            eprintln!("{}", msg);
1068
1069            rustix::process::kill_process(rustix::process::getpid(), rustix::process::Signal::Tstp)
1070                .unwrap();
1071        }
1072
1073        let memory_manager = unsafe { MemoryManager::new(native_pid) };
1074        let threads = RefCell::new(BTreeMap::from([(
1075            main_thread_id,
1076            RootedRc::new(host.root(), RootedRefCell::new(host.root(), main_thread)),
1077        )]));
1078
1079        let common = Common {
1080            id: process_id,
1081            host_id: host.id(),
1082            working_dir,
1083            name,
1084            plugin_name,
1085            parent_pid: Cell::new(ProcessId::INIT),
1086            group_id: Cell::new(ProcessId::INIT),
1087            session_id: Cell::new(ProcessId::INIT),
1088            // Exit signal is moot; since parent is INIT there will never
1089            // be a valid target for it.
1090            exit_signal: None,
1091        };
1092        Ok(RootedRc::new(
1093            host.root(),
1094            RootedRefCell::new(
1095                host.root(),
1096                Self {
1097                    state: RefCell::new(Some(ProcessState::Runnable(RunnableProcess {
1098                        common,
1099                        expected_final_state: Some(expected_final_state),
1100                        shim_shared_mem_block,
1101                        memory_manager: Box::new(RefCell::new(memory_manager)),
1102                        itimer_real,
1103                        strace_logging,
1104                        dumpable: Cell::new(SuidDump::SUID_DUMP_USER),
1105                        native_pid,
1106                        unsafe_borrow_mut: RefCell::new(None),
1107                        unsafe_borrows: RefCell::new(Vec::new()),
1108                        threads,
1109                        #[cfg(feature = "perf_timers")]
1110                        cpu_delay_timer: RefCell::new(PerfTimer::new_stopped()),
1111                        #[cfg(feature = "perf_timers")]
1112                        total_run_time: Cell::new(Duration::ZERO),
1113                        child_process_event_listeners: Default::default(),
1114                        shimlog_file,
1115                    }))),
1116                },
1117            ),
1118        ))
1119    }
1120
1121    pub fn id(&self) -> ProcessId {
1122        self.common().id
1123    }
1124
1125    pub fn parent_id(&self) -> ProcessId {
1126        self.common().parent_pid.get()
1127    }
1128
1129    pub fn set_parent_id(&self, pid: ProcessId) {
1130        self.common().parent_pid.set(pid)
1131    }
1132
1133    pub fn group_id(&self) -> ProcessId {
1134        self.common().group_id.get()
1135    }
1136
1137    pub fn set_group_id(&self, id: ProcessId) {
1138        self.common().group_id.set(id)
1139    }
1140
1141    pub fn session_id(&self) -> ProcessId {
1142        self.common().session_id.get()
1143    }
1144
1145    pub fn set_session_id(&self, id: ProcessId) {
1146        self.common().session_id.set(id)
1147    }
1148
1149    pub fn host_id(&self) -> HostId {
1150        self.common().host_id
1151    }
1152
1153    /// Get process's "dumpable" state, as manipulated by the prctl operations `PR_SET_DUMPABLE` and
1154    /// `PR_GET_DUMPABLE`.
1155    pub fn dumpable(&self) -> SuidDump {
1156        self.as_runnable().unwrap().dumpable.get()
1157    }
1158
1159    /// Set process's "dumpable" state, as manipulated by the prctl operations `PR_SET_DUMPABLE` and
1160    /// `PR_GET_DUMPABLE`.
1161    pub fn set_dumpable(&self, val: SuidDump) {
1162        assert!(val == SuidDump::SUID_DUMP_DISABLE || val == SuidDump::SUID_DUMP_USER);
1163        self.as_runnable().unwrap().dumpable.set(val)
1164    }
1165
1166    /// Deprecated wrapper for `RunnableProcess::start_cpu_delay_timer`
1167    #[cfg(feature = "perf_timers")]
1168    pub fn start_cpu_delay_timer(&self) {
1169        self.as_runnable().unwrap().start_cpu_delay_timer()
1170    }
1171
1172    /// Deprecated wrapper for `RunnableProcess::stop_cpu_delay_timer`
1173    #[cfg(feature = "perf_timers")]
1174    pub fn stop_cpu_delay_timer(&self, host: &Host) -> Duration {
1175        self.as_runnable().unwrap().stop_cpu_delay_timer(host)
1176    }
1177
1178    pub fn thread_group_leader_id(&self) -> ThreadId {
1179        self.common().thread_group_leader_id()
1180    }
1181
1182    /// Resume execution of `tid` (if it exists).
1183    /// Should only be called from `Host::resume`.
1184    pub fn resume(&self, host: &Host, tid: ThreadId) {
1185        trace!("Continuing thread {} in process {}", tid, self.id());
1186
1187        let threadrc = {
1188            let Some(runnable) = self.as_runnable() else {
1189                debug!("Process {} is no longer running", &*self.name());
1190                return;
1191            };
1192            let threads = runnable.threads.borrow();
1193            let Some(thread) = threads.get(&tid) else {
1194                debug!("Thread {} no longer exists", tid);
1195                return;
1196            };
1197            // Clone the thread reference, so that we don't hold a dynamically
1198            // borrowed reference to the thread list while running the thread.
1199            thread.clone(host.root())
1200        };
1201        let threadrc = ExplicitDropper::new(threadrc, |t| {
1202            t.explicit_drop_recursive(host.root(), host);
1203        });
1204        let thread = threadrc.borrow(host.root());
1205
1206        Worker::set_active_thread(&threadrc);
1207
1208        #[cfg(feature = "perf_timers")]
1209        self.start_cpu_delay_timer();
1210
1211        Process::set_shared_time(host);
1212
1213        // Discard any unapplied latency.
1214        // We currently only want this mechanism to force a yield if the thread itself
1215        // never yields; we don't want unapplied latency to accumulate and force a yield
1216        // under normal circumstances.
1217        host.shim_shmem_lock_borrow_mut()
1218            .unwrap()
1219            .unapplied_cpu_latency = SimulationTime::ZERO;
1220
1221        let ctx = ProcessContext::new(host, self);
1222        let res = thread.resume(&ctx);
1223
1224        #[cfg(feature = "perf_timers")]
1225        {
1226            let delay = self.stop_cpu_delay_timer(host);
1227            debug!("process '{}' ran for {:?}", &*self.name(), delay);
1228        }
1229        #[cfg(not(feature = "perf_timers"))]
1230        debug!("process '{}' done continuing", &*self.name());
1231
1232        match res {
1233            crate::host::thread::ResumeResult::Blocked => {
1234                debug!(
1235                    "thread {tid} in process '{}' still running, but blocked",
1236                    &*self.name()
1237                );
1238            }
1239            crate::host::thread::ResumeResult::ExitedThread(return_code) => {
1240                debug!(
1241                    "thread {tid} in process '{}' exited with code {return_code}",
1242                    &*self.name(),
1243                );
1244                let (threadrc, last_thread) = {
1245                    let runnable = self.as_runnable().unwrap();
1246                    let mut threads = runnable.threads.borrow_mut();
1247                    let threadrc = threads.remove(&tid).unwrap();
1248                    (threadrc, threads.is_empty())
1249                };
1250                self.as_runnable().unwrap().reap_thread(host, threadrc);
1251                if last_thread {
1252                    self.handle_process_exit(host, false);
1253                }
1254            }
1255            crate::host::thread::ResumeResult::ExitedProcess => {
1256                debug!(
1257                    "Process {} exited while running thread {tid}",
1258                    &*self.name(),
1259                );
1260                self.handle_process_exit(host, false);
1261            }
1262        };
1263
1264        Worker::clear_active_thread();
1265    }
1266
1267    /// Terminate the Process.
1268    ///
1269    /// Should only be called from [`Host::free_all_applications`].
1270    pub fn stop(&self, host: &Host) {
1271        // Scope for `runnable`
1272        {
1273            let Some(runnable) = self.as_runnable() else {
1274                debug!("process {} has already stopped", &*self.name());
1275                return;
1276            };
1277            debug!("terminating process {}", &*self.name());
1278
1279            #[cfg(feature = "perf_timers")]
1280            runnable.start_cpu_delay_timer();
1281
1282            if let Err(err) = rustix::process::kill_process(
1283                runnable.native_pid().into(),
1284                rustix::process::Signal::Kill,
1285            ) {
1286                warn!("kill: {:?}", err);
1287            }
1288
1289            #[cfg(feature = "perf_timers")]
1290            {
1291                let delay = runnable.stop_cpu_delay_timer(host);
1292                debug!("process '{}' stopped in {:?}", &*self.name(), delay);
1293            }
1294            #[cfg(not(feature = "perf_timers"))]
1295            debug!("process '{}' stopped", &*self.name());
1296        }
1297
1298        // Mutates `self.state`, so we need to have dropped `runnable`.
1299        self.handle_process_exit(host, true);
1300    }
1301
1302    /// See `RunnableProcess::signal`.
1303    ///
1304    /// No-op if the `self` is a `ZombieProcess`.
1305    pub fn signal(&self, host: &Host, current_thread: Option<&Thread>, siginfo_t: &siginfo_t) {
1306        // Using full-match here to force update if we add more states later.
1307        match self.state.borrow().as_ref().unwrap() {
1308            ProcessState::Runnable(r) => r.signal(host, current_thread, siginfo_t),
1309            ProcessState::Zombie(_) => {
1310                // Sending a signal to a zombie process is a no-op.
1311                debug!("Process {} no longer running", &*self.name());
1312            }
1313        }
1314    }
1315
1316    fn open_stdio_file_helper(
1317        descriptor_table: &mut DescriptorTable,
1318        fd: DescriptorHandle,
1319        path: PathBuf,
1320        access_mode: OFlag,
1321    ) {
1322        let stdfile = unsafe { cshadow::regularfile_new() };
1323        let cwd = rustix::process::getcwd(Vec::new()).unwrap();
1324        let path = utility::pathbuf_to_nul_term_cstring(path);
1325        // "Convert" to libc int, assuming here that the kernel's `OFlag` values
1326        // are compatible with libc's values.
1327        // XXX: We're assuming here that the kernel and libc flags are ABI
1328        // compatible, which isn't guaranteed, but is mostly true in practice.
1329        // TODO: We probably ought to change `regularfile_open` and friends to
1330        // use a direct syscall instead of libc's wrappers, and explicitly take
1331        // the kernel version of flags, mode, etc.
1332        let access_mode = access_mode.bits();
1333        let errorcode = unsafe {
1334            cshadow::regularfile_open(
1335                stdfile,
1336                path.as_ptr(),
1337                access_mode | libc::O_CREAT | libc::O_TRUNC,
1338                libc::S_IRUSR | libc::S_IWUSR | libc::S_IRGRP | libc::S_IROTH,
1339                cwd.as_ptr(),
1340            )
1341        };
1342        if errorcode != 0 {
1343            panic!(
1344                "Opening {}: {:?}",
1345                path.to_str().unwrap(),
1346                linux_api::errno::Errno::try_from(-errorcode).unwrap()
1347            );
1348        }
1349        let desc = unsafe {
1350            Descriptor::from_legacy_file(
1351                stdfile as *mut cshadow::LegacyFile,
1352                linux_api::fcntl::OFlag::empty(),
1353            )
1354        };
1355        let prev = descriptor_table.register_descriptor_with_fd(desc, fd);
1356        assert!(prev.is_none());
1357        trace!(
1358            "Successfully opened fd {} at {}",
1359            fd,
1360            path.to_str().unwrap()
1361        );
1362    }
1363
1364    // Needed during early init, before `Self` is created.
1365    fn static_output_file_name(file_basename: &Path, extension: &str) -> PathBuf {
1366        let mut path = file_basename.to_owned().into_os_string();
1367        path.push(".");
1368        path.push(extension);
1369        path.into()
1370    }
1371
1372    pub fn name(&self) -> impl Deref<Target = str> + '_ {
1373        Ref::map(self.common(), |c| c.name.to_str().unwrap())
1374    }
1375
1376    pub fn plugin_name(&self) -> impl Deref<Target = str> + '_ {
1377        Ref::map(self.common(), |c| c.plugin_name.to_str().unwrap())
1378    }
1379
1380    /// Deprecated wrapper for `RunnableProcess::memory_borrow_mut`
1381    #[track_caller]
1382    pub fn memory_borrow_mut(&self) -> impl DerefMut<Target = MemoryManager> + '_ {
1383        std_util::nested_ref::NestedRefMut::map(self.as_runnable().unwrap(), |runnable| {
1384            runnable.memory_manager.borrow_mut()
1385        })
1386    }
1387
1388    /// Deprecated wrapper for `RunnableProcess::memory_borrow`
1389    #[track_caller]
1390    pub fn memory_borrow(&self) -> impl Deref<Target = MemoryManager> + '_ {
1391        std_util::nested_ref::NestedRef::map(self.as_runnable().unwrap(), |runnable| {
1392            runnable.memory_manager.borrow()
1393        })
1394    }
1395
1396    /// Deprecated wrapper for `RunnableProcess::strace_logging_options`
1397    pub fn strace_logging_options(&self) -> Option<FmtOptions> {
1398        self.as_runnable().unwrap().strace_logging_options()
1399    }
1400
1401    /// Deprecated wrapper for `RunnableProcess::with_strace_file`
1402    pub fn with_strace_file<T>(&self, f: impl FnOnce(&mut std::fs::File) -> T) -> Option<T> {
1403        self.as_runnable().unwrap().with_strace_file(f)
1404    }
1405
1406    /// Deprecated wrapper for `RunnableProcess::native_pid`
1407    pub fn native_pid(&self) -> Pid {
1408        self.as_runnable().unwrap().native_pid()
1409    }
1410
1411    /// Deprecated wrapper for `RunnableProcess::realtime_timer_borrow`
1412    #[track_caller]
1413    pub fn realtime_timer_borrow(&self) -> impl Deref<Target = Timer> + '_ {
1414        std_util::nested_ref::NestedRef::map(self.as_runnable().unwrap(), |runnable| {
1415            runnable.itimer_real.borrow()
1416        })
1417    }
1418
1419    /// Deprecated wrapper for `RunnableProcess::realtime_timer_borrow_mut`
1420    #[track_caller]
1421    pub fn realtime_timer_borrow_mut(&self) -> impl DerefMut<Target = Timer> + '_ {
1422        std_util::nested_ref::NestedRefMut::map(self.as_runnable().unwrap(), |runnable| {
1423            runnable.itimer_real.borrow_mut()
1424        })
1425    }
1426
1427    /// Deprecated wrapper for `RunnableProcess::first_live_thread_borrow`
1428    #[track_caller]
1429    pub fn first_live_thread_borrow(
1430        &self,
1431        root: &Root,
1432    ) -> Option<impl Deref<Target = RootedRc<RootedRefCell<Thread>>> + '_> {
1433        std_util::nested_ref::NestedRef::filter_map(self.as_runnable()?, |runnable| {
1434            runnable.first_live_thread(root)
1435        })
1436    }
1437
1438    /// Deprecated wrapper for `RunnableProcess::thread_borrow`
1439    pub fn thread_borrow(
1440        &self,
1441        virtual_tid: ThreadId,
1442    ) -> Option<impl Deref<Target = RootedRc<RootedRefCell<Thread>>> + '_> {
1443        std_util::nested_ref::NestedRef::filter_map(self.as_runnable()?, |runnable| {
1444            runnable.thread(virtual_tid)
1445        })
1446    }
1447
1448    /// Deprecated wrapper for [`RunnableProcess::free_unsafe_borrows_flush`].
1449    pub fn free_unsafe_borrows_flush(&self) -> Result<(), Errno> {
1450        self.as_runnable().unwrap().free_unsafe_borrows_flush()
1451    }
1452
1453    /// Deprecated wrapper for [`RunnableProcess::free_unsafe_borrows_noflush`].
1454    pub fn free_unsafe_borrows_noflush(&self) {
1455        self.as_runnable().unwrap().free_unsafe_borrows_noflush()
1456    }
1457
1458    pub fn physical_address(&self, vptr: ForeignPtr<()>) -> ManagedPhysicalMemoryAddr {
1459        self.common().physical_address(vptr)
1460    }
1461
1462    pub fn is_running(&self) -> bool {
1463        self.as_runnable().is_some()
1464    }
1465
1466    /// Transitions `self` from a `RunnableProcess` to a `ZombieProcess`.
1467    fn handle_process_exit(&self, host: &Host, killed_by_shadow: bool) {
1468        debug!(
1469            "process '{}' has completed or is otherwise no longer running",
1470            &*self.name()
1471        );
1472
1473        // Take and dispose of all of the threads.
1474        // TODO: consider doing this while the `self.state` mutable reference is held
1475        // as with the other cleanup below. Right now this breaks some C code that expects
1476        // to be able to lookup the thread's process name.
1477        {
1478            let runnable = self.as_runnable().unwrap();
1479            let threads = std::mem::take(&mut *runnable.threads.borrow_mut());
1480            for (_tid, threadrc) in threads.into_iter() {
1481                threadrc.borrow(host.root()).handle_process_exit();
1482                runnable.reap_thread(host, threadrc);
1483            }
1484        }
1485
1486        // Intentionally hold the borrow on self.state to ensure the state
1487        // transition is "atomic".
1488        let mut opt_state = self.state.borrow_mut();
1489
1490        let state = opt_state.take().unwrap();
1491        let ProcessState::Runnable(runnable) = state else {
1492            unreachable!("Tried to handle process exit of non-running process");
1493        };
1494
1495        #[cfg(feature = "perf_timers")]
1496        debug!(
1497            "total runtime for process '{}' was {:?}",
1498            runnable.common.name(),
1499            runnable.total_run_time.get()
1500        );
1501
1502        let wait_res: Option<WaitStatus> =
1503            rustix::process::waitpid(Some(runnable.native_pid().into()), WaitOptions::empty())
1504                .unwrap_or_else(|e| {
1505                    panic!("Error waiting for {:?}: {:?}", runnable.native_pid(), e)
1506                });
1507        let wait_status = wait_res.unwrap();
1508        let exit_status = if killed_by_shadow {
1509            if wait_status.terminating_signal()
1510                != Some(Signal::SIGKILL.as_i32().try_into().unwrap())
1511            {
1512                warn!("Unexpected waitstatus after killed by shadow: {wait_status:?}");
1513            }
1514            ExitStatus::StoppedByShadow
1515        } else if let Some(code) = wait_status.exit_status() {
1516            ExitStatus::Normal(code.try_into().unwrap())
1517        } else if let Some(signal) = wait_status.terminating_signal() {
1518            ExitStatus::Signaled(Signal::try_from(i32::try_from(signal).unwrap()).unwrap())
1519        } else {
1520            panic!(
1521                "Unexpected status: {wait_status:?} for pid {:?}",
1522                runnable.native_pid()
1523            );
1524        };
1525
1526        let (main_result_string, log_level) = {
1527            let mut s = format!(
1528                "process '{name}' exited with status {exit_status:?}",
1529                name = runnable.common.name()
1530            );
1531            if let Some(expected_final_state) = runnable.expected_final_state {
1532                let actual_final_state = match exit_status {
1533                    ExitStatus::Normal(i) => ProcessFinalState::Exited { exited: i },
1534                    ExitStatus::Signaled(s) => ProcessFinalState::Signaled {
1535                        // This conversion will fail on realtime signals, but that
1536                        // should currently be impossible since we don't support
1537                        // sending realtime signals.
1538                        signaled: s.try_into().unwrap(),
1539                    },
1540                    ExitStatus::StoppedByShadow => ProcessFinalState::Running(RunningVal::Running),
1541                };
1542                if expected_final_state == actual_final_state {
1543                    (s, log::Level::Debug)
1544                } else {
1545                    Worker::increment_plugin_error_count();
1546                    write!(s, "; expected end state was {expected_final_state} but was {actual_final_state}").unwrap();
1547                    (s, log::Level::Error)
1548                }
1549            } else {
1550                (s, log::Level::Debug)
1551            }
1552        };
1553        log::log!(log_level, "{}", main_result_string);
1554
1555        let zombie = ZombieProcess {
1556            common: runnable.into_common(),
1557            exit_status,
1558        };
1559        zombie.notify_parent_of_exit(host);
1560
1561        *opt_state = Some(ProcessState::Zombie(zombie));
1562    }
1563
1564    /// Deprecated wrapper for `RunnableProcess::add_thread`
1565    pub fn add_thread(&self, host: &Host, thread: RootedRc<RootedRefCell<Thread>>) {
1566        self.as_runnable().unwrap().add_thread(host, thread)
1567    }
1568
1569    /// FIXME: still needed? Time is now updated more granularly in the Thread code
1570    /// when xferring control to/from shim.
1571    fn set_shared_time(host: &Host) {
1572        let mut host_shmem = host.shim_shmem_lock_borrow_mut().unwrap();
1573        host_shmem.max_runahead_time = Worker::max_event_runahead_time(host);
1574        host.shim_shmem()
1575            .sim_time
1576            .store(Worker::current_time().unwrap(), Ordering::Relaxed);
1577    }
1578
1579    /// Deprecated wrapper for `RunnableProcess::shmem`
1580    pub fn shmem(&self) -> impl Deref<Target = ShMemBlock<'static, ProcessShmem>> + '_ {
1581        Ref::map(self.as_runnable().unwrap(), |r| &r.shim_shared_mem_block)
1582    }
1583
1584    /// Resource usage, as returned e.g. by the `getrusage` syscall.
1585    pub fn rusage(&self) -> linux_api::resource::rusage {
1586        warn_once_then_debug!(
1587            "resource usage (rusage) tracking unimplemented; Returning bogus zeroed values"
1588        );
1589        // TODO: Actually track some of these.
1590        // Assuming we want to support `RUSAGE_THREAD` in the `getrusage`
1591        // syscall, we'll actually want to track at the thread level, and either
1592        // increment at both thread and process level at the points where we do
1593        // the tracking, or dynamically iterate over the threads here and sum
1594        // the results.
1595        linux_api::resource::rusage {
1596            ru_utime: linux_api::time::kernel_old_timeval {
1597                tv_sec: 0,
1598                tv_usec: 0,
1599            },
1600            ru_stime: linux_api::time::kernel_old_timeval {
1601                tv_sec: 0,
1602                tv_usec: 0,
1603            },
1604            ru_maxrss: 0,
1605            ru_ixrss: 0,
1606            ru_idrss: 0,
1607            ru_isrss: 0,
1608            ru_minflt: 0,
1609            ru_majflt: 0,
1610            ru_nswap: 0,
1611            ru_inblock: 0,
1612            ru_oublock: 0,
1613            ru_msgsnd: 0,
1614            ru_msgrcv: 0,
1615            ru_nsignals: 0,
1616            ru_nvcsw: 0,
1617            ru_nivcsw: 0,
1618        }
1619    }
1620
1621    /// Signal that will be sent to parent process on exit. Typically `Some(SIGCHLD)`.
1622    pub fn exit_signal(&self) -> Option<Signal> {
1623        self.common().exit_signal
1624    }
1625
1626    pub fn current_working_dir(&self) -> impl Deref<Target = CString> + '_ {
1627        Ref::map(self.common(), |common| &common.working_dir)
1628    }
1629
1630    /// Set the process's working directory.
1631    /// This must be kept in sync with the actual working dir of the native process.
1632    /// See <https://github.com/shadow/shadow/issues/2960>
1633    // TODO: This ought to be at the thread level, to support `CLONE_FS`.
1634    pub fn set_current_working_dir(&self, path: CString) {
1635        self.common_mut().working_dir = path;
1636    }
1637
1638    /// Update `self` to complete an `exec` syscall from thread `tid`, replacing
1639    /// the running managed process with `mthread`.
1640    pub fn update_for_exec(&mut self, host: &Host, tid: ThreadId, mthread: ManagedThread) {
1641        let Some(mut runnable) = self.as_runnable_mut() else {
1642            // This could happen if another event runs before the "execve completion" event
1643            // and kills the process. e.g. another thread in the process could run and
1644            // execute the `exit_group` syscall.
1645            log::debug!(
1646                "Process {:?} exited before it could complete execve",
1647                self.id()
1648            );
1649            mthread.kill_and_drop();
1650            return;
1651        };
1652        let old_native_pid = std::mem::replace(&mut runnable.native_pid, mthread.native_pid());
1653
1654        // Kill the previous native process
1655        rustix::process::kill_process(old_native_pid.into(), rustix::process::Signal::Kill)
1656            .expect("Unable to send kill signal to managed process {old_native_pid:?}");
1657        let wait_res = rustix::process::waitpid(Some(old_native_pid.into()), WaitOptions::empty())
1658            .unwrap()
1659            .unwrap();
1660        assert_eq!(
1661            wait_res.terminating_signal(),
1662            Some(Signal::SIGKILL.as_i32().try_into().unwrap())
1663        );
1664
1665        let execing_thread = runnable.threads.borrow_mut().remove(&tid).unwrap();
1666
1667        // Dispose of all threads other than the thread that's running `exec`.
1668        for (_tid, thread) in runnable.threads.replace(BTreeMap::new()) {
1669            // Notify the ManagedThread that the native process has exited.
1670            thread.borrow(host.root()).mthread().handle_process_exit();
1671
1672            thread.explicit_drop_recursive(host.root(), host);
1673        }
1674
1675        // Recreate the `MemoryManager`
1676        {
1677            // We can't safely replace the memory manager if there are outstanding
1678            // unsafe references in C code. There shouldn't be any, though, since
1679            // this is only called from the `execve` and `execveat` syscall handlers,
1680            // which are in Rust.
1681            let unsafe_borrow_mut = runnable.unsafe_borrow_mut.borrow();
1682            let unsafe_borrows = runnable.unsafe_borrows.borrow();
1683            assert!(unsafe_borrow_mut.is_none());
1684            assert!(unsafe_borrows.is_empty());
1685            // Replace the MM, while still holding the references to the unsafe borrows
1686            // to ensure none exist.
1687            runnable
1688                .memory_manager
1689                .replace(unsafe { MemoryManager::new(mthread.native_pid()) });
1690        }
1691
1692        let new_tid = runnable.common.thread_group_leader_id();
1693        log::trace!(
1694            "updating for exec; pid:{pid}, tid:{tid:?}, new_tid:{new_tid:?}",
1695            pid = runnable.common.id
1696        );
1697        execing_thread
1698            .borrow_mut(host.root())
1699            .update_for_exec(host, mthread, new_tid);
1700
1701        runnable
1702            .threads
1703            .borrow_mut()
1704            .insert(new_tid, execing_thread);
1705
1706        // Exit signal is reset to SIGCHLD.
1707        runnable.common.exit_signal = Some(Signal::SIGCHLD);
1708
1709        // Reset signal actions to default.
1710        // `execve(2)`:
1711        // POSIX.1 specifies that the dispositions of any signals that
1712        // are ignored or set to the default are left unchanged.  POSIX.1
1713        // specifies one exception: if SIGCHLD is being ignored, then an
1714        // implementation may leave the disposition unchanged or reset it
1715        // to the default; Linux does the former.
1716        let host_shmem_prot = host.shim_shmem_lock_borrow_mut().unwrap();
1717        let mut shmem_prot = runnable
1718            .shim_shared_mem_block
1719            .protected
1720            .borrow_mut(&host_shmem_prot.root);
1721        for signal in Signal::standard_signals() {
1722            let current_action = unsafe { shmem_prot.signal_action(signal) };
1723            if !(current_action.is_default()
1724                || current_action.is_ignore()
1725                || signal == Signal::SIGCHLD && current_action.is_ignore())
1726            {
1727                unsafe {
1728                    *shmem_prot.signal_action_mut(signal) = linux_api::signal::sigaction::new_raw(
1729                        linux_api::signal::SignalHandler::SigDfl,
1730                        SigActionFlags::empty(),
1731                        sigset_t::EMPTY,
1732                        None,
1733                    )
1734                };
1735            }
1736        }
1737    }
1738}
1739
1740impl Drop for Process {
1741    fn drop(&mut self) {
1742        // Should have been explicitly dropped.
1743        debug_assert!(self.state.borrow().is_none());
1744    }
1745}
1746
1747impl ExplicitDrop for Process {
1748    type ExplicitDropParam = Host;
1749    type ExplicitDropResult = ();
1750
1751    fn explicit_drop(mut self, host: &Self::ExplicitDropParam) -> Self::ExplicitDropResult {
1752        // Should normally only be dropped in the zombie state.
1753        debug_assert!(self.as_zombie().is_some() || std::thread::panicking());
1754
1755        let state = self.state.get_mut().take().unwrap();
1756        state.explicit_drop(host);
1757    }
1758}
1759
1760/// Tracks a memory reference made by a legacy C memory-read API.
1761struct UnsafeBorrow {
1762    // Must come before `manager`, so that it's dropped first, since it's
1763    // borrowed from it.
1764    _memory: ProcessMemoryRef<'static, u8>,
1765    _manager: Ref<'static, MemoryManager>,
1766}
1767
1768impl UnsafeBorrow {
1769    /// Creates a raw readable pointer, and saves an instance of `Self` into
1770    /// `process` for later clean-up.
1771    ///
1772    /// # Safety
1773    ///
1774    /// The pointer is invalidated when one of the Process memory flush methods is called.
1775    unsafe fn readable_ptr(
1776        process: &Process,
1777        ptr: ForeignArrayPtr<u8>,
1778    ) -> Result<*const c_void, Errno> {
1779        let runnable = process.as_runnable().unwrap();
1780        let manager = runnable.memory_manager.borrow();
1781        // SAFETY: We ensure that the `memory` is dropped before the `manager`,
1782        // and `Process` ensures that this whole object is dropped before
1783        // `MemoryManager` can be moved, freed, etc.
1784        let manager = unsafe {
1785            std::mem::transmute::<Ref<'_, MemoryManager>, Ref<'static, MemoryManager>>(manager)
1786        };
1787        let memory = manager.memory_ref(ptr)?;
1788        let memory = unsafe {
1789            std::mem::transmute::<ProcessMemoryRef<'_, u8>, ProcessMemoryRef<'static, u8>>(memory)
1790        };
1791        let vptr = memory.as_ptr() as *mut c_void;
1792        runnable.unsafe_borrows.borrow_mut().push(Self {
1793            _manager: manager,
1794            _memory: memory,
1795        });
1796        Ok(vptr)
1797    }
1798
1799    /// Creates a raw readable string, and saves an instance of `Self` into
1800    /// `process` for later clean-up.
1801    ///
1802    /// # Safety
1803    ///
1804    /// The pointer is invalidated when one of the Process memory flush methods is called.
1805    unsafe fn readable_string(
1806        process: &Process,
1807        ptr: ForeignArrayPtr<c_char>,
1808    ) -> Result<(*const c_char, libc::size_t), Errno> {
1809        let runnable = process.as_runnable().unwrap();
1810        let manager = runnable.memory_manager.borrow();
1811        // SAFETY: We ensure that the `memory` is dropped before the `manager`,
1812        // and `Process` ensures that this whole object is dropped before
1813        // `MemoryManager` can be moved, freed, etc.
1814        let manager = unsafe {
1815            std::mem::transmute::<Ref<'_, MemoryManager>, Ref<'static, MemoryManager>>(manager)
1816        };
1817        let ptr = ptr.cast_u8();
1818        let memory = manager.memory_ref_prefix(ptr)?;
1819        let memory = unsafe {
1820            std::mem::transmute::<ProcessMemoryRef<'_, u8>, ProcessMemoryRef<'static, u8>>(memory)
1821        };
1822        if !memory.contains(&0) {
1823            return Err(Errno::ENAMETOOLONG);
1824        }
1825        assert_eq!(std::mem::size_of::<c_char>(), std::mem::size_of::<u8>());
1826        let ptr = memory.as_ptr() as *const c_char;
1827        let len = memory.len();
1828        runnable.unsafe_borrows.borrow_mut().push(Self {
1829            _manager: manager,
1830            _memory: memory,
1831        });
1832        Ok((ptr, len))
1833    }
1834}
1835
1836// Safety: Normally the Ref would make this non-Send, since it could end then
1837// end up trying to manipulate the source RefCell (which is !Sync) from multiple
1838// threads.  We ensure that these objects never escape Process, which itself is
1839// non-Sync, ensuring this doesn't happen.
1840//
1841// This is admittedly hand-wavy and making some assumptions about the
1842// implementation of RefCell, but this whole type is temporary scaffolding to
1843// support legacy C code.
1844unsafe impl Send for UnsafeBorrow {}
1845
1846/// Tracks a memory reference made by a legacy C memory-write API.
1847struct UnsafeBorrowMut {
1848    // Must come before `manager`, so that it's dropped first, since it's
1849    // borrowed from it.
1850    memory: Option<ProcessMemoryRefMut<'static, u8>>,
1851    _manager: RefMut<'static, MemoryManager>,
1852}
1853
1854impl UnsafeBorrowMut {
1855    /// Creates a raw writable pointer, and saves an instance of `Self` into
1856    /// `process` for later clean-up. The initial contents of the pointer is unspecified.
1857    ///
1858    /// # Safety
1859    ///
1860    /// The pointer is invalidated when one of the Process memory flush methods is called.
1861    unsafe fn writable_ptr(
1862        process: &Process,
1863        ptr: ForeignArrayPtr<u8>,
1864    ) -> Result<*mut c_void, Errno> {
1865        let runnable = process.as_runnable().unwrap();
1866        let manager = runnable.memory_manager.borrow_mut();
1867        // SAFETY: We ensure that the `memory` is dropped before the `manager`,
1868        // and `Process` ensures that this whole object is dropped before
1869        // `MemoryManager` can be moved, freed, etc.
1870        let mut manager = unsafe {
1871            std::mem::transmute::<RefMut<'_, MemoryManager>, RefMut<'static, MemoryManager>>(
1872                manager,
1873            )
1874        };
1875        let memory = manager.memory_ref_mut_uninit(ptr)?;
1876        let mut memory = unsafe {
1877            std::mem::transmute::<ProcessMemoryRefMut<'_, u8>, ProcessMemoryRefMut<'static, u8>>(
1878                memory,
1879            )
1880        };
1881        let vptr = memory.as_mut_ptr() as *mut c_void;
1882        let prev = runnable.unsafe_borrow_mut.borrow_mut().replace(Self {
1883            _manager: manager,
1884            memory: Some(memory),
1885        });
1886        assert!(prev.is_none());
1887        Ok(vptr)
1888    }
1889
1890    /// Creates a raw mutable pointer, and saves an instance of `Self` into
1891    /// `process` for later clean-up.
1892    ///
1893    /// # Safety
1894    ///
1895    /// The pointer is invalidated when one of the Process memory flush methods is called.
1896    unsafe fn mutable_ptr(
1897        process: &Process,
1898        ptr: ForeignArrayPtr<u8>,
1899    ) -> Result<*mut c_void, Errno> {
1900        let runnable = process.as_runnable().unwrap();
1901        let manager = runnable.memory_manager.borrow_mut();
1902        // SAFETY: We ensure that the `memory` is dropped before the `manager`,
1903        // and `Process` ensures that this whole object is dropped before
1904        // `MemoryManager` can be moved, freed, etc.
1905        let mut manager = unsafe {
1906            std::mem::transmute::<RefMut<'_, MemoryManager>, RefMut<'static, MemoryManager>>(
1907                manager,
1908            )
1909        };
1910        let memory = manager.memory_ref_mut(ptr)?;
1911        let mut memory = unsafe {
1912            std::mem::transmute::<ProcessMemoryRefMut<'_, u8>, ProcessMemoryRefMut<'static, u8>>(
1913                memory,
1914            )
1915        };
1916        let vptr = memory.as_mut_ptr() as *mut c_void;
1917        let prev = runnable.unsafe_borrow_mut.borrow_mut().replace(Self {
1918            _manager: manager,
1919            memory: Some(memory),
1920        });
1921        assert!(prev.is_none());
1922        Ok(vptr)
1923    }
1924
1925    /// Free this reference, writing back to process memory.
1926    fn flush(mut self) -> Result<(), Errno> {
1927        self.memory.take().unwrap().flush()
1928    }
1929
1930    /// Free this reference without writing back to process memory.
1931    fn noflush(mut self) {
1932        self.memory.take().unwrap().noflush()
1933    }
1934}
1935
1936// Safety: Normally the RefMut would make this non-Send, since it could end then
1937// end up trying to manipulate the source RefCell (which is !Sync) from multiple
1938// threads.  We ensure that these objects never escape Process, which itself is
1939// non-Sync, ensuring this doesn't happen.
1940//
1941// This is admittedly hand-wavy and making some assumptions about the implementation of
1942// RefCell, but this whole type is temporary scaffolding to support legacy C code.
1943unsafe impl Send for UnsafeBorrowMut {}
1944
1945fn make_name(host: &Host, exe_name: &str, id: ProcessId) -> CString {
1946    CString::new(format!(
1947        "{host_name}.{exe_name}.{id}",
1948        host_name = host.name(),
1949        exe_name = exe_name,
1950        id = u32::from(id)
1951    ))
1952    .unwrap()
1953}
1954
1955mod export {
1956    use std::os::raw::c_void;
1957
1958    use libc::size_t;
1959    use log::trace;
1960    use shadow_shim_helper_rs::notnull::*;
1961    use shadow_shim_helper_rs::shim_shmem::export::ShimShmemProcess;
1962    use shadow_shim_helper_rs::syscall_types::UntypedForeignPtr;
1963
1964    use super::*;
1965    use crate::utility::HostTreePointer;
1966
1967    /// Copy `n` bytes from `src` to `dst`. Returns 0 on success or -EFAULT if any of
1968    /// the specified range couldn't be accessed. Always succeeds with n==0.
1969    #[unsafe(no_mangle)]
1970    pub extern "C-unwind" fn process_readPtr(
1971        proc: *const Process,
1972        dst: *mut c_void,
1973        src: UntypedForeignPtr,
1974        n: usize,
1975    ) -> i32 {
1976        let proc = unsafe { proc.as_ref().unwrap() };
1977        let src = ForeignArrayPtr::new(src.cast::<u8>(), n);
1978        let dst = unsafe { std::slice::from_raw_parts_mut(notnull_mut_debug(dst) as *mut u8, n) };
1979
1980        match proc.memory_borrow().copy_from_ptr(dst, src) {
1981            Ok(_) => 0,
1982            Err(e) => {
1983                trace!("Couldn't read {:?} into {:?}: {:?}", src, dst, e);
1984                e.to_negated_i32()
1985            }
1986        }
1987    }
1988
1989    /// Copy `n` bytes from `src` to `dst`. Returns 0 on success or -EFAULT if any of
1990    /// the specified range couldn't be accessed. The write is flushed immediately.
1991    #[unsafe(no_mangle)]
1992    pub unsafe extern "C-unwind" fn process_writePtr(
1993        proc: *const Process,
1994        dst: UntypedForeignPtr,
1995        src: *const c_void,
1996        n: usize,
1997    ) -> i32 {
1998        let proc = unsafe { proc.as_ref().unwrap() };
1999        let dst = ForeignArrayPtr::new(dst.cast::<u8>(), n);
2000        let src = unsafe { std::slice::from_raw_parts(notnull_debug(src) as *const u8, n) };
2001        match proc.memory_borrow_mut().copy_to_ptr(dst, src) {
2002            Ok(_) => 0,
2003            Err(e) => {
2004                trace!("Couldn't write {:?} into {:?}: {:?}", src, dst, e);
2005                e.to_negated_i32()
2006            }
2007        }
2008    }
2009
2010    /// Make the data at plugin_src available in shadow's address space.
2011    ///
2012    /// The returned pointer is invalidated when one of the process memory flush
2013    /// methods is called; typically after a syscall has completed.
2014    #[unsafe(no_mangle)]
2015    pub unsafe extern "C-unwind" fn process_getReadablePtr(
2016        proc: *const Process,
2017        plugin_src: UntypedForeignPtr,
2018        n: usize,
2019    ) -> *const c_void {
2020        let proc = unsafe { proc.as_ref().unwrap() };
2021        let plugin_src = ForeignArrayPtr::new(plugin_src.cast::<u8>(), n);
2022        unsafe { UnsafeBorrow::readable_ptr(proc, plugin_src).unwrap_or(std::ptr::null()) }
2023    }
2024
2025    /// Returns a writable pointer corresponding to the named region. The
2026    /// initial contents of the returned memory are unspecified.
2027    ///
2028    /// The returned pointer is invalidated when one of the process memory flush
2029    /// methods is called; typically after a syscall has completed.
2030    ///
2031    /// CAUTION: if the unspecified contents aren't overwritten, and the pointer
2032    /// isn't explicitly freed via `process_freePtrsWithoutFlushing`, those
2033    /// unspecified contents may be written back into process memory.
2034    #[unsafe(no_mangle)]
2035    pub unsafe extern "C-unwind" fn process_getWriteablePtr(
2036        proc: *const Process,
2037        plugin_src: UntypedForeignPtr,
2038        n: usize,
2039    ) -> *mut c_void {
2040        let proc = unsafe { proc.as_ref().unwrap() };
2041        let plugin_src = ForeignArrayPtr::new(plugin_src.cast::<u8>(), n);
2042        unsafe { UnsafeBorrowMut::writable_ptr(proc, plugin_src).unwrap_or(std::ptr::null_mut()) }
2043    }
2044
2045    /// Returns a writeable pointer corresponding to the specified src. Use when
2046    /// the data at the given address needs to be both read and written.
2047    ///
2048    /// The returned pointer is invalidated when one of the process memory flush
2049    /// methods is called; typically after a syscall has completed.
2050    #[unsafe(no_mangle)]
2051    pub unsafe extern "C-unwind" fn process_getMutablePtr(
2052        proc: *const Process,
2053        plugin_src: UntypedForeignPtr,
2054        n: usize,
2055    ) -> *mut c_void {
2056        let proc = unsafe { proc.as_ref().unwrap() };
2057        let plugin_src = ForeignArrayPtr::new(plugin_src.cast::<u8>(), n);
2058        unsafe { UnsafeBorrowMut::mutable_ptr(proc, plugin_src).unwrap_or(std::ptr::null_mut()) }
2059    }
2060
2061    /// Reads up to `n` bytes into `str`.
2062    ///
2063    /// Returns:
2064    /// strlen(str) on success.
2065    /// -ENAMETOOLONG if there was no NULL byte in the first `n` characters.
2066    /// -EFAULT if the string extends beyond the accessible address space.
2067    #[unsafe(no_mangle)]
2068    pub unsafe extern "C-unwind" fn process_readString(
2069        proc: *const Process,
2070        strbuf: *mut libc::c_char,
2071        ptr: UntypedForeignPtr,
2072        maxlen: libc::size_t,
2073    ) -> libc::ssize_t {
2074        let proc = unsafe { proc.as_ref().unwrap() };
2075        let memory_manager = proc.memory_borrow();
2076        let buf =
2077            unsafe { std::slice::from_raw_parts_mut(notnull_mut_debug(strbuf) as *mut u8, maxlen) };
2078        let cstr = match memory_manager
2079            .copy_str_from_ptr(buf, ForeignArrayPtr::new(ptr.cast::<u8>(), maxlen))
2080        {
2081            Ok(cstr) => cstr,
2082            Err(e) => return e.to_negated_i32() as isize,
2083        };
2084        cstr.to_bytes().len().try_into().unwrap()
2085    }
2086
2087    /// Reads up to `n` bytes into `str`.
2088    ///
2089    /// Returns:
2090    /// strlen(str) on success.
2091    /// -ENAMETOOLONG if there was no NULL byte in the first `n` characters.
2092    /// -EFAULT if the string extends beyond the accessible address space.
2093    #[unsafe(no_mangle)]
2094    pub unsafe extern "C-unwind" fn process_getReadableString(
2095        proc: *const Process,
2096        plugin_src: UntypedForeignPtr,
2097        n: usize,
2098        out_str: *mut *const c_char,
2099        out_strlen: *mut size_t,
2100    ) -> i32 {
2101        let proc = unsafe { proc.as_ref().unwrap() };
2102        let ptr = ForeignArrayPtr::new(plugin_src.cast::<c_char>(), n);
2103        match unsafe { UnsafeBorrow::readable_string(proc, ptr) } {
2104            Ok((str, strlen)) => {
2105                assert!(!out_str.is_null());
2106                unsafe { out_str.write(str) };
2107                if !out_strlen.is_null() {
2108                    unsafe { out_strlen.write(strlen) };
2109                }
2110                0
2111            }
2112            Err(e) => e.to_negated_i32(),
2113        }
2114    }
2115
2116    /// Returns the processID that was assigned to us in process_new
2117    #[unsafe(no_mangle)]
2118    pub unsafe extern "C-unwind" fn process_getProcessID(proc: *const Process) -> libc::pid_t {
2119        let proc = unsafe { proc.as_ref().unwrap() };
2120        proc.id().into()
2121    }
2122
2123    #[unsafe(no_mangle)]
2124    pub unsafe extern "C-unwind" fn process_getName(proc: *const Process) -> *const c_char {
2125        let proc = unsafe { proc.as_ref().unwrap() };
2126        proc.common().name.as_ptr()
2127    }
2128
2129    /// Safety:
2130    ///
2131    /// The returned pointer is invalidated when the host shmem lock is released, e.g. via
2132    /// Host::unlock_shmem.
2133    #[unsafe(no_mangle)]
2134    pub unsafe extern "C-unwind" fn process_getSharedMem(
2135        proc: *const Process,
2136    ) -> *const ShimShmemProcess {
2137        let proc = unsafe { proc.as_ref().unwrap() };
2138        std::ptr::from_ref(proc.as_runnable().unwrap().shim_shared_mem_block.deref())
2139    }
2140
2141    #[unsafe(no_mangle)]
2142    pub unsafe extern "C-unwind" fn process_getWorkingDir(proc: *const Process) -> *const c_char {
2143        let proc = unsafe { proc.as_ref().unwrap() };
2144        proc.common().working_dir.as_ptr()
2145    }
2146
2147    #[unsafe(no_mangle)]
2148    pub unsafe extern "C-unwind" fn process_straceLoggingMode(
2149        proc: *const Process,
2150    ) -> StraceFmtMode {
2151        let proc = unsafe { proc.as_ref().unwrap() };
2152        proc.strace_logging_options().into()
2153    }
2154
2155    #[unsafe(no_mangle)]
2156    pub unsafe extern "C-unwind" fn process_getNativePid(proc: *const Process) -> libc::pid_t {
2157        let proc = unsafe { proc.as_ref().unwrap() };
2158        proc.native_pid().as_raw_nonzero().get()
2159    }
2160
2161    /// Flushes and invalidates all previously returned readable/writable plugin
2162    /// pointers, as if returning control to the plugin. This can be useful in
2163    /// conjunction with `thread_nativeSyscall` operations that touch memory, or
2164    /// to gracefully handle failed writes.
2165    ///
2166    /// Returns 0 on success or a negative errno on failure.
2167    #[unsafe(no_mangle)]
2168    pub unsafe extern "C-unwind" fn process_flushPtrs(proc: *const Process) -> i32 {
2169        let proc = unsafe { proc.as_ref().unwrap() };
2170        match proc.free_unsafe_borrows_flush() {
2171            Ok(_) => 0,
2172            Err(e) => e.to_negated_i32(),
2173        }
2174    }
2175
2176    /// Frees all readable/writable foreign pointers. Unlike process_flushPtrs, any
2177    /// previously returned writable pointer is *not* written back. Useful
2178    /// if an uninitialized writable pointer was obtained via `process_getWriteablePtr`,
2179    /// and we end up not wanting to write anything after all (in particular, don't
2180    /// write back whatever garbage data was in the uninialized bueffer).
2181    #[unsafe(no_mangle)]
2182    pub unsafe extern "C-unwind" fn process_freePtrsWithoutFlushing(proc: *const Process) {
2183        let proc = unsafe { proc.as_ref().unwrap() };
2184        proc.free_unsafe_borrows_noflush();
2185    }
2186
2187    #[unsafe(no_mangle)]
2188    pub unsafe extern "C-unwind" fn process_getThread(
2189        proc: *const Process,
2190        tid: libc::pid_t,
2191    ) -> *const Thread {
2192        let proc = unsafe { proc.as_ref().unwrap() };
2193        Worker::with_active_host(|host| {
2194            let tid = ThreadId::try_from(tid).unwrap();
2195            let Some(thread) = proc.thread_borrow(tid) else {
2196                return std::ptr::null();
2197            };
2198            let thread = thread.borrow(host.root());
2199            &*thread
2200        })
2201        .unwrap()
2202    }
2203
2204    /// Returns a pointer to an arbitrary live thread in the process.
2205    #[unsafe(no_mangle)]
2206    pub unsafe extern "C-unwind" fn process_firstLiveThread(proc: *const Process) -> *const Thread {
2207        let proc = unsafe { proc.as_ref().unwrap() };
2208        Worker::with_active_host(|host| {
2209            let Some(thread) = proc.first_live_thread_borrow(host.root()) else {
2210                return std::ptr::null();
2211            };
2212            let thread = thread.borrow(host.root());
2213            &*thread
2214        })
2215        .unwrap()
2216    }
2217
2218    #[unsafe(no_mangle)]
2219    pub unsafe extern "C-unwind" fn process_isRunning(proc: *const Process) -> bool {
2220        let proc = unsafe { proc.as_ref().unwrap() };
2221        proc.is_running()
2222    }
2223
2224    // FIXME: still needed? Time is now updated more granularly in the Thread code
2225    // when xferring control to/from shim.
2226    #[unsafe(no_mangle)]
2227    pub unsafe extern "C-unwind" fn process_setSharedTime() {
2228        Worker::with_active_host(Process::set_shared_time).unwrap();
2229    }
2230
2231    #[unsafe(no_mangle)]
2232    pub unsafe extern "C-unwind" fn process_getPhysicalAddress(
2233        proc: *const Process,
2234        vptr: UntypedForeignPtr,
2235    ) -> ManagedPhysicalMemoryAddr {
2236        let proc = unsafe { proc.as_ref().unwrap() };
2237        proc.physical_address(vptr)
2238    }
2239
2240    #[unsafe(no_mangle)]
2241    pub unsafe extern "C-unwind" fn process_addChildEventListener(
2242        host: *const Host,
2243        process: *const Process,
2244        listener: *mut cshadow::StatusListener,
2245    ) {
2246        let host = unsafe { host.as_ref().unwrap() };
2247        let process = unsafe { process.as_ref().unwrap() };
2248        let listener = HostTreePointer::new_for_host(host.id(), listener);
2249        process
2250            .borrow_as_runnable()
2251            .unwrap()
2252            .child_process_event_listeners
2253            .borrow_mut()
2254            .add_legacy_listener(listener)
2255    }
2256
2257    #[unsafe(no_mangle)]
2258    pub unsafe extern "C-unwind" fn process_removeChildEventListener(
2259        _host: *const Host,
2260        process: *const Process,
2261        listener: *mut cshadow::StatusListener,
2262    ) {
2263        let process = unsafe { process.as_ref().unwrap() };
2264        process
2265            .borrow_as_runnable()
2266            .unwrap()
2267            .child_process_event_listeners
2268            .borrow_mut()
2269            .remove_legacy_listener(listener)
2270    }
2271}