1use 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#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone, Ord, PartialOrd)]
60pub struct ProcessId(u32);
61
62impl ProcessId {
63 pub const INIT: Self = ProcessId(1);
67
68 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 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 StoppedByShadow,
131}
132
133#[derive(Debug)]
134struct StraceLogging {
135 file: RootedRefCell<std::fs::File>,
136 options: FmtOptions,
137}
138
139struct Common {
141 id: ProcessId,
142 host_id: HostId,
143
144 parent_pid: Cell<ProcessId>,
147
148 group_id: Cell<ProcessId>,
150
151 session_id: Cell<ProcessId>,
153
154 exit_signal: Option<Signal>,
156
157 parent_death_signal: Cell<Option<Signal>>,
160
161 name: CString,
163
164 plugin_name: CString,
166
167 working_dir: CString,
171
172 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 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 ThreadId::from(self.id())
227 }
228}
229
230pub struct RunnableProcess {
232 common: Common,
233
234 expected_final_state: Option<ProcessFinalState>,
241
242 shim_shared_mem_block: ShMemBlock<'static, ProcessShmem>,
244
245 strace_logging: Option<Arc<StraceLogging>>,
247
248 shimlog_file: Arc<std::fs::File>,
255
256 dumpable: Cell<SuidDump>,
259
260 native_pid: Pid,
261
262 #[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 threads: RefCell<BTreeMap<ThreadId, RootedRc<RootedRefCell<Thread>>>>,
275
276 memory_manager: RefCell<MemoryManager>,
281
282 child_process_event_listeners: RefCell<StateEventSource>,
285}
286
287impl RunnableProcess {
288 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 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 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 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 pub fn with_strace_file<T>(&self, f: impl FnOnce(&mut std::fs::File) -> T) -> Option<T> {
362 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 assert!(thread.borrow(root).is_running());
381 })
382 })
383 .ok()
384 }
385
386 #[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 fn into_common(self) -> Common {
412 self.common
413 }
414
415 #[cfg(feature = "perf_timers")]
418 pub fn start_cpu_delay_timer(&self) {
419 self.cpu_delay_timer.borrow_mut().start()
420 }
421
422 #[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 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 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 {
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 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 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 return;
520 }
521 }
522
523 self.interrupt_with_signal(host, signal);
524 }
525
526 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 let task = TaskRef::new(move |host| {
537 host.resume(pid, tid);
538 });
539 host.schedule_task_with_delay(task, SimulationTime::ZERO);
540 }
541
542 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 let process_group_id = self.common.group_id.get();
573
574 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 let strace_logging = self.strace_logging.as_ref().cloned();
594
595 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 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
653pub 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 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 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 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 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 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#[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
835pub struct Process {
837 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 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 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 pub fn borrow_as_zombie(&self) -> Option<impl Deref<Target = ZombieProcess> + '_> {
901 self.as_zombie()
902 }
903
904 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 log::logger().flush();
1018
1019 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 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 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: 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 pub fn dumpable(&self) -> SuidDump {
1143 self.as_runnable().unwrap().dumpable.get()
1144 }
1145
1146 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 #[cfg(feature = "perf_timers")]
1155 pub fn start_cpu_delay_timer(&self) {
1156 self.as_runnable().unwrap().start_cpu_delay_timer()
1157 }
1158
1159 #[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 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 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 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 pub fn stop(&self, host: &Host) {
1258 {
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 self.handle_process_exit(host, true);
1287 }
1288
1289 pub fn signal(&self, host: &Host, current_thread: Option<&Thread>, info: &siginfo_t) {
1293 match self.state.borrow().as_ref().unwrap() {
1295 ProcessState::Runnable(r) => r.signal(host, current_thread, info),
1296 ProcessState::Zombie(_) => {
1297 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 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 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 #[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 #[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 pub fn strace_logging_options(&self) -> Option<FmtOptions> {
1385 self.as_runnable().unwrap().strace_logging_options()
1386 }
1387
1388 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 pub fn native_pid(&self) -> Pid {
1395 self.as_runnable().unwrap().native_pid()
1396 }
1397
1398 #[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 #[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 #[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 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 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 {
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 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 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 pub fn add_thread(&self, host: &Host, thread: RootedRc<RootedRefCell<Thread>>) {
1541 self.as_runnable().unwrap().add_thread(host, thread)
1542 }
1543
1544 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 return Ok(());
1560 };
1561 if new_rlim.rlim_cur > new_rlim.rlim_max {
1562 return Err(linux_api::errno::Errno::EINVAL);
1563 }
1564 if new_rlim.rlim_max > cur.rlim_max {
1569 return Err(linux_api::errno::Errno::EPERM);
1570 }
1571
1572 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 return Ok(());
1582 };
1583
1584 let mut native_rlim = shadow_pod::zeroed();
1588 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 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 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 unsafe { linux_api::resource::prlimit64(native_pid, resource, Some(&new_rlim), None) }
1621 .unwrap();
1622
1623 Ok(())
1624 }
1625
1626 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 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 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 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 pub fn exit_signal(&self) -> Option<Signal> {
1680 self.common().exit_signal
1681 }
1682
1683 pub fn parent_death_signal(&self) -> Option<Signal> {
1685 self.common().parent_death_signal.get()
1686 }
1687
1688 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 pub fn set_current_working_dir(&self, path: CString) {
1702 self.common_mut().working_dir = path;
1703 }
1704
1705 pub fn update_for_exec(&mut self, host: &Host, tid: ThreadId, mthread: ManagedThread) {
1708 let Some(mut runnable) = self.as_runnable_mut() else {
1709 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 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 for (_tid, thread) in runnable.threads.replace(BTreeMap::new()) {
1736 thread.borrow(host.root()).mthread().handle_process_exit();
1738
1739 thread.explicit_drop_recursive(host.root(), host);
1740 }
1741
1742 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 runnable.common.exit_signal = Some(Signal::SIGCHLD);
1763
1764 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 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 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 #[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 #[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 #[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 #[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 #[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 #[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 #[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}