Skip to main content

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