Skip to main content

shadow_rs/host/syscall/handler/
unistd.rs

1use std::ffi::{CStr, CString};
2use std::os::unix::ffi::OsStringExt;
3use std::sync::Arc;
4
5use atomic_refcell::AtomicRefCell;
6use linux_api::errno::Errno;
7use linux_api::fcntl::{DescriptorFlags, OFlag};
8use linux_api::posix_types::{kernel_off_t, kernel_pid_t};
9use log::*;
10use shadow_shim_helper_rs::emulated_time::EmulatedTime;
11use shadow_shim_helper_rs::rootedcell::refcell::RootedRefCell;
12use shadow_shim_helper_rs::simulation_time::SimulationTime;
13use shadow_shim_helper_rs::syscall_types::ForeignPtr;
14
15use crate::core::work::task::TaskRef;
16use crate::core::worker::Worker;
17use crate::cshadow as c;
18use crate::host::descriptor::descriptor_table::DescriptorHandle;
19use crate::host::descriptor::shared_buf::SharedBuf;
20use crate::host::descriptor::{
21    CompatFile, Descriptor, DropPosixRecordLocks, File, FileMode, FileStatus, OpenFile, pipe,
22};
23use crate::host::process::{Process, ProcessId};
24use crate::host::syscall::handler::{SyscallContext, SyscallHandler};
25use crate::host::syscall::io::{IoVec, read_cstring_vec};
26use crate::host::syscall::type_formatting::{SyscallBufferArg, SyscallStringArg};
27use crate::host::syscall::types::{ForeignArrayPtr, SyscallError};
28use crate::utility::callback_queue::CallbackQueue;
29use crate::utility::u8_to_i8_slice;
30
31impl SyscallHandler {
32    log_syscall!(
33        close,
34        /* rv */ std::ffi::c_int,
35        /* fd */ std::ffi::c_int,
36    );
37    pub fn close(ctx: &mut SyscallContext, fd: std::ffi::c_int) -> Result<(), SyscallError> {
38        trace!("Trying to close fd {fd}");
39
40        let fd = fd.try_into().or(Err(linux_api::errno::Errno::EBADF))?;
41
42        // according to "man 2 close", in Linux any errors that may occur will happen after the fd is
43        // released, so we should always deregister the descriptor even if there's an error while
44        // closing
45        let desc = ctx
46            .objs
47            .thread
48            .descriptor_table_borrow_mut(ctx.objs.host)
49            .deregister_descriptor(fd)
50            .ok_or(linux_api::errno::Errno::EBADF)?;
51
52        // if there are still valid descriptors to the open file, close() will do nothing
53        // and return None
54        let drop_locks = DropPosixRecordLocks::ForPid(ctx.objs.process.id());
55        CallbackQueue::queue_and_run_with_legacy(|cb_queue| {
56            desc.close(ctx.objs.host, drop_locks, cb_queue)
57        })
58        .unwrap_or(Ok(()))
59    }
60
61    log_syscall!(
62        dup,
63        /* rv */ std::ffi::c_int,
64        /* oldfd */ std::ffi::c_int,
65    );
66    pub fn dup(
67        ctx: &mut SyscallContext,
68        fd: std::ffi::c_int,
69    ) -> Result<DescriptorHandle, SyscallError> {
70        // get the descriptor, or return early if it doesn't exist
71        let mut desc_table = ctx.objs.thread.descriptor_table_borrow_mut(ctx.objs.host);
72        let desc = Self::get_descriptor(&desc_table, fd)?;
73
74        // duplicate the descriptor
75        let new_desc = desc.dup(DescriptorFlags::empty());
76
77        Ok(desc_table
78            .register_descriptor(new_desc)
79            .or(Err(Errno::ENFILE))?)
80    }
81
82    log_syscall!(
83        dup2,
84        /* rv */ std::ffi::c_int,
85        /* oldfd */ std::ffi::c_int,
86        /* newfd */ std::ffi::c_int,
87    );
88    pub fn dup2(
89        ctx: &mut SyscallContext,
90        old_fd: std::ffi::c_int,
91        new_fd: std::ffi::c_int,
92    ) -> Result<DescriptorHandle, SyscallError> {
93        let old_fd = DescriptorHandle::try_from(old_fd).or(Err(Errno::EBADF))?;
94        let new_fd = DescriptorHandle::try_from(new_fd).or(Err(Errno::EBADF))?;
95
96        // get the descriptor, or return early if it doesn't exist
97        let mut desc_table = ctx.objs.thread.descriptor_table_borrow_mut(ctx.objs.host);
98        let desc = Self::get_descriptor(&desc_table, old_fd)?;
99
100        // from 'man 2 dup2': "If oldfd is a valid file descriptor, and newfd has the same
101        // value as oldfd, then dup2() does nothing, and returns newfd"
102        if old_fd == new_fd {
103            return Ok(new_fd);
104        }
105
106        // duplicate the descriptor
107        let new_desc = desc.dup(DescriptorFlags::empty());
108        let replaced_desc = desc_table.register_descriptor_with_fd(new_desc, new_fd);
109
110        // close the replaced descriptor
111        if let Some(replaced_desc) = replaced_desc {
112            let drop_locks = DropPosixRecordLocks::ForPid(ctx.objs.process.id());
113            // from 'man 2 dup2': "If newfd was open, any errors that would have been reported at
114            // close(2) time are lost"
115            CallbackQueue::queue_and_run_with_legacy(|cb_queue| {
116                replaced_desc.close(ctx.objs.host, drop_locks, cb_queue)
117            });
118        }
119
120        // return the new fd
121        Ok(new_fd)
122    }
123
124    log_syscall!(
125        dup3,
126        /* rv */ std::ffi::c_int,
127        /* oldfd */ std::ffi::c_int,
128        /* newfd */ std::ffi::c_int,
129        /* flags */ linux_api::fcntl::OFlag,
130    );
131    pub fn dup3(
132        ctx: &mut SyscallContext,
133        old_fd: std::ffi::c_int,
134        new_fd: std::ffi::c_int,
135        flags: std::ffi::c_int,
136    ) -> Result<DescriptorHandle, SyscallError> {
137        // get the descriptor, or return early if it doesn't exist
138        let mut desc_table = ctx.objs.thread.descriptor_table_borrow_mut(ctx.objs.host);
139        let desc = Self::get_descriptor(&desc_table, old_fd)?;
140
141        // from 'man 2 dup3': "If oldfd equals newfd, then dup3() fails with the error EINVAL"
142        if old_fd == new_fd {
143            return Err(linux_api::errno::Errno::EINVAL.into());
144        }
145
146        let new_fd = new_fd.try_into().or(Err(linux_api::errno::Errno::EBADF))?;
147
148        let Some(flags) = OFlag::from_bits(flags) else {
149            debug!("Invalid flags: {flags}");
150            return Err(linux_api::errno::Errno::EINVAL.into());
151        };
152
153        let mut descriptor_flags = DescriptorFlags::empty();
154
155        // dup3 only supports the O_CLOEXEC flag
156        for flag in flags {
157            match flag {
158                OFlag::O_CLOEXEC => descriptor_flags.insert(DescriptorFlags::FD_CLOEXEC),
159                x if x == OFlag::empty() => {
160                    // The "empty" flag is always present. Ignore.
161                }
162                _ => {
163                    debug!("Invalid flags for dup3: {flags:?}");
164                    return Err(linux_api::errno::Errno::EINVAL.into());
165                }
166            }
167        }
168
169        // duplicate the descriptor
170        let new_desc = desc.dup(descriptor_flags);
171        let replaced_desc = desc_table.register_descriptor_with_fd(new_desc, new_fd);
172
173        // close the replaced descriptor
174        if let Some(replaced_desc) = replaced_desc {
175            let drop_locks = DropPosixRecordLocks::ForPid(ctx.objs.process.id());
176            // from 'man 2 dup3': "If newfd was open, any errors that would have been reported at
177            // close(2) time are lost"
178            CallbackQueue::queue_and_run_with_legacy(|cb_queue| {
179                replaced_desc.close(ctx.objs.host, drop_locks, cb_queue)
180            });
181        }
182
183        // return the new fd
184        Ok(new_fd)
185    }
186
187    log_syscall!(
188        read,
189        /* rv */ isize,
190        /* fd */ std::ffi::c_int,
191        /* buf */ *const std::ffi::c_void,
192        /* count */ usize,
193    );
194    pub fn read(
195        ctx: &mut SyscallContext,
196        fd: std::ffi::c_int,
197        buf_ptr: ForeignPtr<u8>,
198        buf_size: usize,
199    ) -> Result<isize, SyscallError> {
200        // if we were previously blocked, get the active file from the last syscall handler
201        // invocation since it may no longer exist in the descriptor table
202        let file = ctx
203            .objs
204            .thread
205            .syscall_condition()
206            // if this was for a C descriptor, then there won't be an active file object
207            .and_then(|x| x.active_file().cloned());
208
209        let file = match file {
210            // we were previously blocked, so re-use the file from the previous syscall invocation
211            Some(x) => x,
212            // get the file from the descriptor table, or return early if it doesn't exist
213            None => {
214                let desc_table = ctx.objs.thread.descriptor_table_borrow(ctx.objs.host);
215                match Self::get_descriptor(&desc_table, fd)?.file() {
216                    CompatFile::New(file) => file.clone(),
217                    // if it's a legacy file, use the C syscall handler instead
218                    CompatFile::Legacy(_) => {
219                        drop(desc_table);
220                        return Self::legacy_syscall(c::syscallhandler_read, ctx);
221                    }
222                }
223            }
224        };
225
226        let mut result = Self::read_helper(ctx, file.inner_file(), buf_ptr, buf_size, None);
227
228        // if the syscall will block, keep the file open until the syscall restarts
229        if let Some(err) = result.as_mut().err()
230            && let Some(cond) = err.blocked_condition()
231        {
232            cond.set_active_file(file);
233        }
234
235        let bytes_read = result?;
236        Ok(bytes_read)
237    }
238
239    log_syscall!(
240        pread64,
241        /* rv */ isize,
242        /* fd */ std::ffi::c_int,
243        /* buf */ *const std::ffi::c_void,
244        /* count */ usize,
245        /* offset */ kernel_off_t,
246    );
247    pub fn pread64(
248        ctx: &mut SyscallContext,
249        fd: std::ffi::c_int,
250        buf_ptr: ForeignPtr<u8>,
251        buf_size: usize,
252        offset: kernel_off_t,
253    ) -> Result<isize, SyscallError> {
254        // if we were previously blocked, get the active file from the last syscall handler
255        // invocation since it may no longer exist in the descriptor table
256        let file = ctx
257            .objs
258            .thread
259            .syscall_condition()
260            // if this was for a C descriptor, then there won't be an active file object
261            .and_then(|x| x.active_file().cloned());
262
263        let file = match file {
264            // we were previously blocked, so re-use the file from the previous syscall invocation
265            Some(x) => x,
266            // get the file from the descriptor table, or return early if it doesn't exist
267            None => {
268                let desc_table = ctx.objs.thread.descriptor_table_borrow(ctx.objs.host);
269                match Self::get_descriptor(&desc_table, fd)?.file() {
270                    CompatFile::New(file) => file.clone(),
271                    // if it's a legacy file, use the C syscall handler instead
272                    CompatFile::Legacy(_) => {
273                        drop(desc_table);
274                        return Self::legacy_syscall(c::syscallhandler_pread64, ctx);
275                    }
276                }
277            }
278        };
279
280        let mut result = Self::read_helper(ctx, file.inner_file(), buf_ptr, buf_size, Some(offset));
281
282        // if the syscall will block, keep the file open until the syscall restarts
283        if let Some(err) = result.as_mut().err()
284            && let Some(cond) = err.blocked_condition()
285        {
286            cond.set_active_file(file);
287        }
288
289        let bytes_read = result?;
290        Ok(bytes_read)
291    }
292
293    fn read_helper(
294        ctx: &mut SyscallContext,
295        file: &File,
296        buf_ptr: ForeignPtr<u8>,
297        buf_size: usize,
298        offset: Option<kernel_off_t>,
299    ) -> Result<isize, SyscallError> {
300        let iov = IoVec {
301            base: buf_ptr,
302            len: buf_size,
303        };
304        Self::readv_helper(ctx, file, &[iov], offset, 0)
305    }
306
307    log_syscall!(
308        write,
309        /* rv */ isize,
310        /* fd */ std::ffi::c_int,
311        /* buf */ SyscallBufferArg</* count */ 2>,
312        /* count */ usize,
313    );
314    pub fn write(
315        ctx: &mut SyscallContext,
316        fd: std::ffi::c_int,
317        buf_ptr: ForeignPtr<u8>,
318        buf_size: usize,
319    ) -> Result<isize, SyscallError> {
320        // if we were previously blocked, get the active file from the last syscall handler
321        // invocation since it may no longer exist in the descriptor table
322        let file = ctx
323            .objs
324            .thread
325            .syscall_condition()
326            // if this was for a C descriptor, then there won't be an active file object
327            .and_then(|x| x.active_file().cloned());
328
329        let file = match file {
330            // we were previously blocked, so re-use the file from the previous syscall invocation
331            Some(x) => x,
332            // get the file from the descriptor table, or return early if it doesn't exist
333            None => {
334                let desc_table = ctx.objs.thread.descriptor_table_borrow(ctx.objs.host);
335                match Self::get_descriptor(&desc_table, fd)?.file() {
336                    CompatFile::New(file) => file.clone(),
337                    // if it's a legacy file, use the C syscall handler instead
338                    CompatFile::Legacy(_) => {
339                        drop(desc_table);
340                        return Self::legacy_syscall(c::syscallhandler_write, ctx);
341                    }
342                }
343            }
344        };
345
346        let mut result = Self::write_helper(ctx, file.inner_file(), buf_ptr, buf_size, None);
347
348        // if the syscall will block, keep the file open until the syscall restarts
349        if let Some(err) = result.as_mut().err()
350            && let Some(cond) = err.blocked_condition()
351        {
352            cond.set_active_file(file);
353        }
354
355        let bytes_written = result?;
356        Ok(bytes_written)
357    }
358
359    log_syscall!(
360        pwrite64,
361        /* rv */ isize,
362        /* fd */ std::ffi::c_int,
363        /* buf */ SyscallBufferArg</* count */ 2>,
364        /* count */ usize,
365        /* offset */ kernel_off_t,
366    );
367    pub fn pwrite64(
368        ctx: &mut SyscallContext,
369        fd: std::ffi::c_int,
370        buf_ptr: ForeignPtr<u8>,
371        buf_size: usize,
372        offset: kernel_off_t,
373    ) -> Result<isize, SyscallError> {
374        // if we were previously blocked, get the active file from the last syscall handler
375        // invocation since it may no longer exist in the descriptor table
376        let file = ctx
377            .objs
378            .thread
379            .syscall_condition()
380            // if this was for a C descriptor, then there won't be an active file object
381            .and_then(|x| x.active_file().cloned());
382
383        let file = match file {
384            // we were previously blocked, so re-use the file from the previous syscall invocation
385            Some(x) => x,
386            // get the file from the descriptor table, or return early if it doesn't exist
387            None => {
388                let desc_table = ctx.objs.thread.descriptor_table_borrow(ctx.objs.host);
389                match Self::get_descriptor(&desc_table, fd)?.file() {
390                    CompatFile::New(file) => file.clone(),
391                    // if it's a legacy file, use the C syscall handler instead
392                    CompatFile::Legacy(_) => {
393                        drop(desc_table);
394                        return Self::legacy_syscall(c::syscallhandler_pwrite64, ctx);
395                    }
396                }
397            }
398        };
399
400        let mut result =
401            Self::write_helper(ctx, file.inner_file(), buf_ptr, buf_size, Some(offset));
402
403        // if the syscall will block, keep the file open until the syscall restarts
404        if let Some(err) = result.as_mut().err()
405            && let Some(cond) = err.blocked_condition()
406        {
407            cond.set_active_file(file);
408        }
409
410        let bytes_written = result?;
411        Ok(bytes_written)
412    }
413
414    fn write_helper(
415        ctx: &mut SyscallContext,
416        file: &File,
417        buf_ptr: ForeignPtr<u8>,
418        buf_size: usize,
419        offset: Option<kernel_off_t>,
420    ) -> Result<isize, SyscallError> {
421        let iov = IoVec {
422            base: buf_ptr,
423            len: buf_size,
424        };
425        Self::writev_helper(ctx, file, &[iov], offset, 0)
426    }
427
428    log_syscall!(
429        pipe,
430        /* rv */ std::ffi::c_int,
431        /* pipefd */ [std::ffi::c_int; 2],
432    );
433    pub fn pipe(
434        ctx: &mut SyscallContext,
435        fd_ptr: ForeignPtr<[std::ffi::c_int; 2]>,
436    ) -> Result<(), SyscallError> {
437        Self::pipe_helper(ctx, fd_ptr, 0)
438    }
439
440    log_syscall!(
441        pipe2,
442        /* rv */ std::ffi::c_int,
443        /* pipefd */ [std::ffi::c_int; 2],
444        /* flags */ linux_api::fcntl::OFlag,
445    );
446    pub fn pipe2(
447        ctx: &mut SyscallContext,
448        fd_ptr: ForeignPtr<[std::ffi::c_int; 2]>,
449        flags: std::ffi::c_int,
450    ) -> Result<(), SyscallError> {
451        Self::pipe_helper(ctx, fd_ptr, flags)
452    }
453
454    fn pipe_helper(
455        ctx: &mut SyscallContext,
456        fd_ptr: ForeignPtr<[std::ffi::c_int; 2]>,
457        flags: i32,
458    ) -> Result<(), SyscallError> {
459        // make sure they didn't pass a NULL pointer
460        if fd_ptr.is_null() {
461            return Err(linux_api::errno::Errno::EFAULT.into());
462        }
463
464        let Some(flags) = OFlag::from_bits(flags) else {
465            debug!("Invalid flags: {flags}");
466            return Err(Errno::EINVAL.into());
467        };
468
469        let mut file_flags = FileStatus::empty();
470        let mut descriptor_flags = DescriptorFlags::empty();
471
472        for flag in flags.iter() {
473            match flag {
474                OFlag::O_NONBLOCK => file_flags.insert(FileStatus::O_NONBLOCK),
475                OFlag::O_DIRECT => file_flags.insert(FileStatus::O_DIRECT),
476                OFlag::O_CLOEXEC => descriptor_flags.insert(DescriptorFlags::FD_CLOEXEC),
477                x if x == OFlag::empty() => {
478                    // The "empty" flag is always present. Ignore.
479                }
480                unhandled => {
481                    // TODO: return an error and change this to `warn_once_then_debug`?
482                    warn!("Ignoring pipe flag {unhandled:?}");
483                }
484            }
485        }
486
487        // reference-counted buffer for the pipe
488        let buffer = SharedBuf::new(c::CONFIG_PIPE_BUFFER_SIZE.try_into().unwrap());
489        let buffer = Arc::new(AtomicRefCell::new(buffer));
490
491        // reference-counted file object for read end of the pipe
492        let reader = pipe::Pipe::new(FileMode::READ, file_flags);
493        let reader = Arc::new(AtomicRefCell::new(reader));
494
495        // reference-counted file object for write end of the pipe
496        let writer = pipe::Pipe::new(FileMode::WRITE, file_flags);
497        let writer = Arc::new(AtomicRefCell::new(writer));
498
499        // set the file objects to listen for events on the buffer
500        CallbackQueue::queue_and_run_with_legacy(|cb_queue| {
501            pipe::Pipe::connect_to_buffer(&reader, Arc::clone(&buffer), cb_queue);
502            pipe::Pipe::connect_to_buffer(&writer, Arc::clone(&buffer), cb_queue);
503        });
504
505        // file descriptors for the read and write file objects
506        let mut reader_desc = Descriptor::new(CompatFile::New(OpenFile::new(File::Pipe(reader))));
507        let mut writer_desc = Descriptor::new(CompatFile::New(OpenFile::new(File::Pipe(writer))));
508
509        // set the file descriptor flags
510        reader_desc.set_flags(descriptor_flags);
511        writer_desc.set_flags(descriptor_flags);
512
513        // register the file descriptors
514        let mut dt = ctx.objs.thread.descriptor_table_borrow_mut(ctx.objs.host);
515        // unwrap here since the error handling would be messy (need to deregister) and we shouldn't
516        // ever need to worry about this in practice
517        let read_fd = dt.register_descriptor(reader_desc).unwrap();
518        let write_fd = dt.register_descriptor(writer_desc).unwrap();
519
520        // try to write them to the caller
521        let fds = [i32::from(read_fd), i32::from(write_fd)];
522        let write_res = ctx.objs.process.memory_borrow_mut().write(fd_ptr, &fds);
523
524        // clean up in case of error
525        match write_res {
526            Ok(_) => Ok(()),
527            Err(e) => {
528                // Behave as if descriptor was never created. (Though such locks shouldn't exist
529                // for this descriptor type anyway).
530                let dont_drop_locks = DropPosixRecordLocks::False;
531                CallbackQueue::queue_and_run_with_legacy(|cb_queue| {
532                    // ignore any errors when closing
533                    dt.deregister_descriptor(read_fd).unwrap().close(
534                        ctx.objs.host,
535                        dont_drop_locks,
536                        cb_queue,
537                    );
538                    dt.deregister_descriptor(write_fd).unwrap().close(
539                        ctx.objs.host,
540                        dont_drop_locks,
541                        cb_queue,
542                    );
543                });
544                Err(e.into())
545            }
546        }
547    }
548
549    log_syscall!(getpid, /* rv */ linux_api::posix_types::kernel_pid_t);
550    pub fn getpid(ctx: &mut SyscallContext) -> Result<kernel_pid_t, SyscallError> {
551        Ok(ctx.objs.process.id().into())
552    }
553
554    log_syscall!(getppid, /* rv */ linux_api::posix_types::kernel_pid_t);
555    pub fn getppid(ctx: &mut SyscallContext) -> Result<kernel_pid_t, SyscallError> {
556        Ok(ctx.objs.process.parent_id().into())
557    }
558
559    log_syscall!(getpgrp, /* rv */ kernel_pid_t);
560    pub fn getpgrp(ctx: &mut SyscallContext) -> Result<kernel_pid_t, SyscallError> {
561        Ok(ctx.objs.process.group_id().into())
562    }
563
564    log_syscall!(
565        getpgid,
566        /* rv */ kernel_pid_t,
567        /* pid*/ kernel_pid_t,
568    );
569    pub fn getpgid(
570        ctx: &mut SyscallContext,
571        pid: kernel_pid_t,
572    ) -> Result<kernel_pid_t, SyscallError> {
573        if pid == 0 || pid == kernel_pid_t::from(ctx.objs.process.id()) {
574            return Ok(ctx.objs.process.group_id().into());
575        }
576        let pid = ProcessId::try_from(pid).map_err(|_| Errno::EINVAL)?;
577        let Some(process) = ctx.objs.host.process_borrow(pid) else {
578            return Err(Errno::ESRCH.into());
579        };
580        let process = process.borrow(ctx.objs.host.root());
581        Ok(process.group_id().into())
582    }
583
584    log_syscall!(
585        setpgid,
586        /* rv */ std::ffi::c_int,
587        /* pid */ kernel_pid_t,
588        /* pgid */ kernel_pid_t,
589    );
590    pub fn setpgid(
591        ctx: &mut SyscallContext,
592        pid: kernel_pid_t,
593        pgid: kernel_pid_t,
594    ) -> Result<(), SyscallError> {
595        let _processrc_borrow;
596        let _process_borrow;
597        let process: &Process;
598        if pid == 0 || pid == kernel_pid_t::from(ctx.objs.process.id()) {
599            _processrc_borrow = None;
600            _process_borrow = None;
601            process = ctx.objs.process;
602        } else {
603            let pid = ProcessId::try_from(pid).map_err(|_| Errno::EINVAL)?;
604            let Some(pbrc) = ctx.objs.host.process_borrow(pid) else {
605                return Err(Errno::ESRCH.into());
606            };
607            _processrc_borrow = Some(pbrc);
608            _process_borrow = Some(
609                _processrc_borrow
610                    .as_ref()
611                    .unwrap()
612                    .borrow(ctx.objs.host.root()),
613            );
614            process = _process_borrow.as_ref().unwrap();
615        }
616        let pgid = if pgid == 0 {
617            None
618        } else {
619            Some(ProcessId::try_from(pgid).map_err(|_| Errno::EINVAL)?)
620        };
621        if process.id() != ctx.objs.process.id() && process.parent_id() != ctx.objs.process.id() {
622            // `setpgid(2)`: pid is not the calling process and not a child  of
623            // the calling process.
624            return Err(Errno::ESRCH.into());
625        }
626        if let Some(pgid) = pgid
627            && ctx.objs.host.process_session_id_of_group_id(pgid) != Some(process.session_id())
628        {
629            // An attempt was made to move a process into a process group in
630            // a different session
631            return Err(Errno::EPERM.into());
632        }
633        if process.session_id() != ctx.objs.process.session_id() {
634            // `setpgid(2)`: ... or to change the process  group  ID of one of
635            // the children of the calling process and the child was in a
636            // different session
637            return Err(Errno::EPERM.into());
638        }
639        if process.session_id() == process.id() {
640            // `setpgid(2)`: ... or to change the process group ID of a session leader
641            return Err(Errno::EPERM.into());
642        }
643        // TODO: Keep track of whether a process has performed an `execve`.
644        // `setpgid(2): EACCES: An attempt was made to change the process group
645        // ID of one of the children of the calling process and the child had
646        // already performed an execve(2).
647        if let Some(pgid) = pgid {
648            if ctx.objs.host.process_session_id_of_group_id(pgid) != Some(process.session_id()) {
649                // `setpgid(2)`: An attempt was made to move a process into a
650                // process group in a different session
651                return Err(Errno::EPERM.into());
652            }
653            process.set_group_id(pgid);
654        } else {
655            // `setpgid(2)`: If pgid is zero, then the PGID of the process
656            // specified by pid is made the same as its process ID.
657            process.set_group_id(process.id());
658        }
659        Ok(())
660    }
661
662    log_syscall!(
663        getsid,
664        /* rv */ kernel_pid_t,
665        /* pid */ kernel_pid_t,
666    );
667    pub fn getsid(
668        ctx: &mut SyscallContext,
669        pid: kernel_pid_t,
670    ) -> Result<kernel_pid_t, SyscallError> {
671        if pid == 0 {
672            return Ok(ctx.objs.process.session_id().into());
673        }
674        let Ok(pid) = ProcessId::try_from(pid) else {
675            return Err(Errno::EINVAL.into());
676        };
677        let Some(processrc) = ctx.objs.host.process_borrow(pid) else {
678            return Err(Errno::ESRCH.into());
679        };
680        let process = processrc.borrow(ctx.objs.host.root());
681        // No need to check that process is in the same session:
682        //
683        // `getsid(2)`: A process with process ID pid exists, but it is not in
684        // the same session as the calling process, and the implementation
685        // considers this an error... **Linux does not return EPERM**.
686
687        Ok(process.session_id().into())
688    }
689
690    log_syscall!(setsid, /* rv */ kernel_pid_t);
691    pub fn setsid(ctx: &mut SyscallContext) -> Result<kernel_pid_t, SyscallError> {
692        let pid = ctx.objs.process.id();
693        if ctx.objs.host.process_session_id_of_group_id(pid).is_some() {
694            // `setsid(2)`: The process group ID of any process equals the PID
695            // of the calling process.  Thus, in particular, setsid() fails if
696            // the calling process is already a process group leader.
697            return Err(Errno::EPERM.into());
698        }
699
700        // `setsid(2)`: The calling process is the leader of the new session
701        // (i.e., its session ID is made the same as its process ID).
702        ctx.objs.process.set_session_id(pid);
703
704        // `setsid(2)`: The calling  process  also  becomes  the  process group
705        // leader of a new process group in the session (i.e., its process group
706        // ID is made the same as its process ID).
707        ctx.objs.process.set_group_id(pid);
708
709        Ok(pid.into())
710    }
711
712    fn execve_common(
713        ctx: &mut SyscallContext,
714        base_dir: &CStr,
715        path: &CStr,
716        argv_ptr_ptr: ForeignPtr<ForeignPtr<std::ffi::c_char>>,
717        envv_ptr_ptr: ForeignPtr<ForeignPtr<std::ffi::c_char>>,
718        _flags: std::ffi::c_int,
719    ) -> Result<(), SyscallError> {
720        if path.is_empty() {
721            // execve(2): The file pathname or a script or ELF interpreter does not exist.
722            return Err(Errno::ENOENT.into());
723        }
724
725        let path_bytes_with_nul = path.to_bytes_with_nul();
726
727        let _abs_path_storage: Option<CString>;
728        let abs_path: &CStr;
729        if path_bytes_with_nul[0] != b'/' {
730            let base_dir_bytes = base_dir.to_bytes();
731
732            // Maybe TODO: this could be done in place without allocating
733            // and with less copying (but more fiddly and error-prone).
734            let mut tmp = Vec::with_capacity(
735                base_dir_bytes.len() + path_bytes_with_nul.len() + /*separator*/1,
736            );
737            tmp.extend(base_dir_bytes);
738            tmp.push(b'/');
739            tmp.extend(path_bytes_with_nul);
740
741            _abs_path_storage = Some(CString::from_vec_with_nul(tmp).unwrap());
742            abs_path = _abs_path_storage.as_ref().unwrap();
743        } else {
744            _abs_path_storage = None;
745            abs_path = path;
746        }
747
748        // TODO: canonicalize? On one hand that would improve caching behavior
749        // in `verify_plugin_path`; OTOH it does some redundant work with
750        // `verify_plugin_path`. Ideal solution is probably to split up
751        // `verify_plugin_path` a bit.
752
753        // `execve(2)`: Most UNIX implementations impose some limit on the
754        // total size of the command-line  argument  (argv)  and
755        // environment  (envp) strings that may be passed to a new program.
756        // POSIX.1 allows an implementation to advertise this limit using
757        // the ARG_MAX constant
758
759        let argv;
760        let envv;
761        {
762            let mem = ctx.objs.process.memory_borrow();
763            argv = read_cstring_vec(&mem, argv_ptr_ptr)?;
764            envv = read_cstring_vec(&mem, envv_ptr_ptr)?;
765        }
766
767        let mthread = ctx
768            .objs
769            .process
770            .borrow_as_runnable()
771            .unwrap()
772            .spawn_mthread_for_exec(ctx.objs.host, abs_path, argv, envv)?;
773
774        // If we get this far, then we should be able to ultimately succeed.
775        // We need a mutable reference to the Process to update it, though, which we can't
776        // get from here since it's already borrowed immutably.
777        //
778        // So, we return a "blocking" result from this syscall handler, and
779        // schedule an event to update the `Process` and resume execution.
780        //
781        // It's possible that other events may affect the `Process` before this one runs.
782        // We try to handle this gracefully; e.g. if the `Process` has exited before this
783        // event runs, we kill and drop the exec'd `ManagedThread` and carry on.
784        //
785        // TODO: There may be other interactions that aren't handled correctly.
786        // e.g. if the exec'ing thread ends up handling a signal in the meantime.
787        // * We could add a new state "`Execing`" to `Process`, and force any
788        // such events to decide how to deal with it. e.g. signal delivery
789        // events could reschedule themselves to run after the exec has
790        // completed. This seems a bit heavy-weight, though.
791        // * We could add more interior mutability s.t. we don't need mutable
792        // references to the Thread and Process in order to do the necessary
793        // updates. This is a fair bit of extra interior mutability to add
794        // though, and has a side-effect of further complicating read-accesses
795        // to items that are read-mostly.
796        // * We could arrange for syscall handlers to get or be able to get
797        // mutable references to the Thread and Process, so that we can complete
798        // the updates synchronously here. This is currently blocked by the
799        // usage of `worker_getCurrentProcess` and `worker_getCurrentThread`,
800        // which will panic with incompatible borrow errors if those are
801        // borrowed mutably.  There aren't many references left to those though,
802        // maybe we can eliminate them.
803        {
804            let pid = ctx.objs.process.id();
805            let tid = ctx.objs.thread.id();
806
807            // Tasks are currently required to be `Sync` and to implement `Fn`, not just `FnOnce`.
808            // Since `mthread` isn't `Sync`, we need to wrap it in a `RootedRefCell`.
809            // Since we need to consume it, we need to also wrap it in an
810            // `Option` and fail at runtime if this actually gets executed
811            // multiple times.
812            // TODO: Split TaskRef into another type that only requires `FnOnce` and `Send`.
813            let mthread = RootedRefCell::new(ctx.objs.host.root(), Some(mthread));
814            ctx.objs.host.schedule_task_with_delay(
815                TaskRef::new(move |host| {
816                    // Take the `mthread` out of the captured wrapper.
817                    // This task shouldn't run multiple times, so this should be
818                    // infallible.
819                    let mthread = mthread.borrow_mut(host.root()).take().unwrap();
820                    // The exec'ing thread's ID is changed to match the pid, since it's
821                    // the new thread-group-leader.
822                    let new_tglid = {
823                        let Some(processrc) = host.process_borrow(pid) else {
824                            // Can happen if another event runs before this one
825                            // and causes the Process to exit (e.g. exit_group
826                            // called from anothe Thread).
827                            log::debug!("Process {pid:?} disappeared before exec could complete");
828                            mthread.kill_and_drop();
829                            return;
830                        };
831                        Worker::set_active_process(&processrc);
832                        let mut process = processrc.borrow_mut(host.root());
833                        process.update_for_exec(host, tid, mthread);
834                        Worker::clear_active_process();
835                        process.thread_group_leader_id()
836                    };
837                    host.resume(pid, new_tglid);
838                }),
839                SimulationTime::ZERO,
840            );
841        }
842
843        Err(SyscallError::new_blocked_until(EmulatedTime::MAX, false))
844    }
845
846    log_syscall!(
847        execve,
848        /* rv */ i32,
849        /* pathname */ SyscallStringArg,
850        /* argv */ *const std::ffi::c_void,
851        /* envp */ *const std::ffi::c_void,
852    );
853    pub fn execve(
854        ctx: &mut SyscallContext,
855        pathname: ForeignPtr<std::ffi::c_char>,
856        argv: ForeignPtr<ForeignPtr<std::ffi::c_char>>,
857        envp: ForeignPtr<ForeignPtr<std::ffi::c_char>>,
858    ) -> Result<i64, SyscallError> {
859        let mut path_buf = [0u8; linux_api::limits::PATH_MAX];
860        let path_buf_capacity = path_buf.len();
861        let path = ctx.objs.process.memory_borrow().copy_str_from_ptr(
862            &mut path_buf,
863            ForeignArrayPtr::new(pathname.cast::<u8>(), path_buf_capacity),
864        )?;
865
866        Self::execve_common(
867            ctx,
868            &ctx.objs.process.current_working_dir(),
869            path,
870            argv,
871            envp,
872            0,
873        )
874        .map(|_| 0)
875    }
876
877    log_syscall!(
878        execveat,
879        /* rv */ i32,
880        /* dirfd */ std::ffi::c_int,
881        /* pathname */ SyscallStringArg,
882        /* argv */ *const std::ffi::c_void,
883        /* envp */ *const std::ffi::c_void,
884        /* flags */ std::ffi::c_int,
885    );
886    pub fn execveat(
887        _ctx: &mut SyscallContext,
888        _dirfd: std::ffi::c_int,
889        _pathname: ForeignPtr<std::ffi::c_char>,
890        _argv: ForeignPtr<ForeignPtr<std::ffi::c_char>>,
891        _envp: ForeignPtr<ForeignPtr<std::ffi::c_char>>,
892        _flags: std::ffi::c_int,
893    ) -> Result<i64, SyscallError> {
894        // TODO: Implement resolution of the path to the executable,
895        // and then call `execve_common` with that.
896        Err(Errno::ENOSYS.into())
897    }
898
899    log_syscall!(
900        exit_group,
901        /* rv */ std::ffi::c_int,
902        /* error_code */ std::ffi::c_int,
903    );
904    pub fn exit_group(
905        _ctx: &mut SyscallContext,
906        error_code: std::ffi::c_int,
907    ) -> Result<(), SyscallError> {
908        log::trace!("Exit group with exit code {error_code}");
909        Err(SyscallError::Native)
910    }
911
912    log_syscall!(
913        set_tid_address,
914        /* rv */ linux_api::posix_types::kernel_pid_t,
915        /* tidptr */ *const std::ffi::c_int,
916    );
917    pub fn set_tid_address(
918        ctx: &mut SyscallContext,
919        tid_ptr: ForeignPtr<std::ffi::c_int>,
920    ) -> Result<kernel_pid_t, SyscallError> {
921        ctx.objs
922            .thread
923            .set_tid_address(tid_ptr.cast::<libc::pid_t>());
924        Ok(ctx.objs.thread.id().into())
925    }
926
927    log_syscall!(
928        uname,
929        /* rv */ std::ffi::c_int,
930        /* name */ *const std::ffi::c_void,
931    );
932    pub fn uname(
933        ctx: &mut SyscallContext,
934        name_ptr: ForeignPtr<linux_api::utsname::new_utsname>,
935    ) -> Result<(), SyscallError> {
936        // NOTE: On linux x86-64, `SYS_uname` corresponds with `__NR_uname` which calls
937        // `sys_newuname` and not `sys_uname`. The correct mapping is:
938        //
939        // - __NR_oldolduname -> sys_olduname
940        // - __NR_olduname -> sys_uname
941        // - __NR_uname -> sys_newuname
942        //
943        // Some online resources such as the chromium syscall table are incorrect.
944
945        let mut name: linux_api::utsname::new_utsname = shadow_pod::zeroed();
946
947        let nodename = u8_to_i8_slice(ctx.objs.host.info().name.as_bytes());
948
949        // Currently hardcoded with values reported in Debian 12
950        let sysname = u8_to_i8_slice(&b"Linux"[..]);
951        let release = u8_to_i8_slice(&b"6.1.0-25-amd64"[..]);
952        let version = u8_to_i8_slice(&b"#1 SMP PREEMPT_DYNAMIC Debian 6.1.106-3 (2024-08-26)"[..]);
953        let machine = u8_to_i8_slice(&b"x86_64"[..]);
954
955        name.sysname[..sysname.len()].copy_from_slice(sysname);
956        name.nodename[..nodename.len()].copy_from_slice(nodename);
957        name.release[..release.len()].copy_from_slice(release);
958        name.version[..version.len()].copy_from_slice(version);
959        name.machine[..machine.len()].copy_from_slice(machine);
960
961        ctx.objs
962            .process
963            .memory_borrow_mut()
964            .write(name_ptr, &name)?;
965
966        Ok(())
967    }
968
969    log_syscall!(
970        chdir,
971        /* rv */ std::ffi::c_int,
972        /* path */ SyscallStringArg,
973    );
974    pub fn chdir(
975        ctx: &mut SyscallContext,
976        path: ForeignPtr<std::ffi::c_char>,
977    ) -> Result<(), SyscallError> {
978        // The native working directory must match the emulated one
979        // <https://github.com/shadow/shadow/issues/2960>. First execute the
980        // native chdir, propagating any failures.
981        let (process, thread) = ctx.objs.split_thread();
982        thread.native_chdir(&process, path)?;
983
984        // Update our internal copy of the cwd.
985        //
986        // We could try to work it out ourselves based on the previous cwd and
987        // the path we were passed, but this seems a bit tricky and error-prone.
988        //
989        // We could have the managed thread execute a native `getcwd`, but we'd
990        // also need to have it allocate and free memory to use with it, making
991        // this a bit complex and high overhead.
992        //
993        // Instead we use the proc file system. `/proc/<pid>/cwd` should be a
994        // symbolic link to the actual working dir we just set.
995        let procpath = format!("/proc/{}/cwd", thread.native_tid().as_raw_nonzero().get());
996        let newcwd = std::fs::read_link(&procpath)
997            .unwrap_or_else(|e| panic!("Couldn't find new cwd {procpath}: {e:?}"));
998        let mut newcwd = newcwd.into_os_string().into_vec();
999        newcwd.push(0);
1000        let newcwd = CString::from_vec_with_nul(newcwd).unwrap();
1001        process.process.set_current_working_dir(newcwd);
1002        Ok(())
1003    }
1004
1005    log_syscall!(
1006        unlink,
1007        /* rv */ std::ffi::c_int,
1008        /* path */ SyscallStringArg,
1009    );
1010    pub fn unlink(
1011        _ctx: &mut SyscallContext,
1012        _path: ForeignPtr<std::ffi::c_char>,
1013    ) -> Result<(), SyscallError> {
1014        Err(SyscallError::Native)
1015    }
1016}