From fae53442d2f048a6fdbb218e9e0bc06be34e1a61 Mon Sep 17 00:00:00 2001 From: Anway Agte <62791369+Anway-Agte@users.noreply.github.com> Date: Fri, 19 Jul 2024 16:09:43 -0400 Subject: [PATCH] Exec syscall (#305) * initial commit * exec comments and tests added * commits resolved * comments resolved * minor change in test case * minor changes * comments resolution --------- Co-authored-by: lind --- src/safeposix/syscalls/sys_calls.rs | 58 +++++++++-- src/tests/mod.rs | 1 + src/tests/sys_tests.rs | 146 +++++++++++++++++----------- 3 files changed, 143 insertions(+), 62 deletions(-) diff --git a/src/safeposix/syscalls/sys_calls.rs b/src/safeposix/syscalls/sys_calls.rs index c4f2fea13..4aec87b99 100644 --- a/src/safeposix/syscalls/sys_calls.rs +++ b/src/safeposix/syscalls/sys_calls.rs @@ -409,16 +409,50 @@ impl Cage { 0 } + /// ### Description + /// + /// The exec system call replaces the current Cage object image with a new + /// Cage object and exec is called immediately after a process(Cage) forks. + /// For our purposes this translates to removing the parent cage object + /// from the cagetable and adding the child object to it. We also close + /// any file descriptors that the parent object holds if the `O_CLOEX` + /// flag has been set on that file descriptor + /// + /// ### Arguments + /// + /// `child_cageid`: uid of the new child Cage object + /// + /// ### Returns + /// + /// Returns 0 upon successfully update the current running image + /// + /// ### Errors + /// + /// This syscall doesn't directly have any cases where it returns an error + /// + /// ### Panics + /// + /// This function doesn't directly panic - but the unmap memory mappings + /// function panics if it cannot create an shm entry or if the unwrapping + /// on the file descriptor fails due to an invalid fd + /// + /// For more information please refer to - [https://man7.org/linux/man-pages/man3/exec.3.html] pub fn exec_syscall(&self, child_cageid: u64) -> i32 { + // We remove the current running process from the cagetable interface::cagetable_remove(self.cageid); - + // Function call to unmap shared memory mappings of the current process self.unmap_shm_mappings(); + // Initialize an empty vector to hold file descriptors let mut cloexecvec = vec![]; for fd in 0..MAXFD { + // Get mutex of the file descriptor let checkedfd = self.get_filedescriptor(fd).unwrap(); let unlocked_fd = checkedfd.read(); if let Some(filedesc_enum) = &*unlocked_fd { + // For each valid file descriptor we chech if the O_CLOEXEC flag is set or not + // The O_CLOEXEC flag determines whether the fd should be closed upon calling + // exec or not if match filedesc_enum { File(f) => f.flags & O_CLOEXEC, Stream(s) => s.flags & O_CLOEXEC, @@ -427,21 +461,30 @@ impl Cage { Epoll(p) => p.flags & O_CLOEXEC, } != 0 { + // If the flag is set - we add the fd to our vector cloexecvec.push(fd); } } } + //For each fd in our close vector list + //We call the close_syscall which takes in a file decsriptor + //and closes it for fdnum in cloexecvec { self.close_syscall(fdnum); } - // we grab the parent cages main threads sigset and store it at 0 - // this way the child can initialize the sigset properly when it establishes its - // own mainthreadid + // We clone the parent cage's main threads and store them at index 0 + // This is done since there isn't a thread established for the child Cage object + // yet - And there is no threadId to store it at. + // The child Cage object can then initialize and store the sigset appropriately + // when it establishes its own main thread id. + // A sigset is a data structure that keeps track of which signals are affected by the process let newsigset = interface::RustHashMap::new(); if !interface::RUSTPOSIX_TESTSUITE.load(interface::RustAtomicOrdering::Relaxed) { - // we don't add these for the test suite + // When rustposix runs independently (not as Lind paired with NaCL runtime) we + // do not handle signals The test suite runs rustposix independently + // and hence we do not handle signals for the test suite let mainsigsetatomic = self .sigset .get( @@ -456,6 +499,9 @@ impl Cage { newsigset.insert(0, mainsigset); } + // Initialize a new cage object to replace the current running image + // We set all the ids to -1 to indicate the loading phase + // We clone the fd table with the memories unmapped let newcage = Cage { cageid: child_cageid, cwd: interface::RustLock::new(self.cwd.read().clone()), @@ -477,8 +523,8 @@ impl Cage { main_threadid: interface::RustAtomicU64::new(0), interval_timer: self.interval_timer.clone_with_new_cageid(child_cageid), }; - //wasteful clone of fdtable, but mutability constraints exist + // Insert new cage with updated fd tables to be inserted in the cagetable interface::cagetable_insert(child_cageid, newcage); 0 } diff --git a/src/tests/mod.rs b/src/tests/mod.rs index cd6494e52..1514fe72e 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -4,6 +4,7 @@ mod fs_tests; mod ipc_tests; mod networking_tests; +mod sys_tests; use rand::Rng; use std::net::{TcpListener, UdpSocket}; diff --git a/src/tests/sys_tests.rs b/src/tests/sys_tests.rs index c37bd32ab..05e70d0c1 100644 --- a/src/tests/sys_tests.rs +++ b/src/tests/sys_tests.rs @@ -1,105 +1,139 @@ -#![allow(dead_code)] //suppress warning for these functions not being used in targets other than the tests +#![allow(dead_code)] //suppress warning for these functions not being used in targets other than the + // tests #[allow(unused_parens)] -#[cfg(test)] -pub mod test_sys { +#[cfg(test)] +pub mod sys_tests { use super::super::*; use crate::interface; - use crate::safeposix::syscalls::sys_calls::*; - - pub fn test_sys() { - ut_lind_getpid(); - ut_lind_getppid(); - ut_lind_getegid(); - ut_lind_getuid(); - ut_lind_geteuid(); - ut_lind_getgid(); - ut_lind_fork(); - } + use crate::safeposix::cage::{FileDescriptor::*, *}; + use crate::safeposix::{cage::*, dispatcher::*, filesystem}; + #[test] pub fn ut_lind_getpid() { - lindrustinit(0); - let cage = interface::cagetable_getref(1); - assert_eq!(cage.getpid_syscall(),1); + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + let cage = interface::cagetable_getref(1); + assert_eq!(cage.getpid_syscall(), 1); lindrustfinalize(); - } + } + #[test] pub fn ut_lind_getppid() { - lindrustinit(0); - let cage = interface::cagetable_getref(1); + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + let cage = interface::cagetable_getref(1); cage.fork_syscall(2); let cage2 = interface::cagetable_getref(2); - assert_eq!(cage2.getppid_syscall(),1); - lindrustfinalize(); - - } + assert_eq!(cage2.getppid_syscall(), 1); + lindrustfinalize(); + } + #[test] pub fn ut_lind_getuid() { - lindrustinit(0); + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + let cage = interface::cagetable_getref(1); // The first call to geteuid always returns -1 - assert_eq!(cage.getuid_syscall(),-1); + assert_eq!(cage.getuid_syscall(), -1); // Subsequent calls return the default value - assert_eq!(cage.getuid_syscall(),DEFAULT_UID); + assert_eq!(cage.getuid_syscall(), DEFAULT_UID as i32); lindrustfinalize() } + #[test] pub fn ut_lind_geteuid() { - lindrustinit(0); + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); let cage = interface::cagetable_getref(1); // The first call to geteuid always returns -1 - assert_eq!(cage.geteuid_syscall(),-1); + assert_eq!(cage.geteuid_syscall(), -1); // Subsequent calls return the default value - assert_eq!(cage.geteuid_syscall(),DEFAULT_UID); + assert_eq!(cage.geteuid_syscall(), DEFAULT_UID as i32); lindrustfinalize() } + #[test] pub fn ut_lind_getgid() { - lindrustinit(0); + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); let cage = interface::cagetable_getref(1); // The first call to geteuid always returns -1 - assert_eq!(cage.getgid_syscall(),-1); + assert_eq!(cage.getgid_syscall(), -1); // Subsequent calls return the default value - assert_eq!(cage.getgid_syscall(),DEFAULT_GID); + assert_eq!(cage.getgid_syscall(), DEFAULT_GID as i32); lindrustfinalize() - } + } + #[test] pub fn ut_lind_getegid() { - lindrustinit(0); + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); let cage = interface::cagetable_getref(1); // The first call to geteuid always returns -1 - assert_eq!(cage.getegid_syscall(),-1); + assert_eq!(cage.getegid_syscall(), -1); // Subsequent calls return the default value - assert_eq!(cage.getegid_syscall(),DEFAULT_GID); + assert_eq!(cage.getegid_syscall(), DEFAULT_GID as i32); lindrustfinalize() - } + } + #[test] pub fn ut_lind_fork() { // Since the fork syscall is heavily tested in relation to other syscalls // we only perform simple checks for testing the sanity of the fork syscall - lindrustinit(0); - let cage = interface::cagetable_getref(1); - // Spawn a new child object using the fork syscall - cage.fork_syscall(2); + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + let cage = interface::cagetable_getref(1); + // Spawn a new child object using the fork syscall + cage.fork_syscall(2); // Search for the new cage object with cage_id = 2 - let child_cage = interface::cagetable_getref(2); + let child_cage = interface::cagetable_getref(2); // Assert the parent value is the the id of the first cage object - assert_eq!(child_cage.getpid_syscall(),1); - // Assert that the cage id of the child is the value passed in the original fork syscall - assert_eq!(child_cage.getuid(),2); - // Assert that the cwd is the same as the parent cage object - assert_eq!(child_cage.cwd.read(),cage.cwd.read()) + assert_eq!(child_cage.getppid_syscall(), 1); + // Assert that the cage id of the child is the value passed in the original fork + // syscall + assert_eq!(child_cage.getuid_syscall(), -1); + assert_eq!(child_cage.getuid_syscall(), DEFAULT_UID as i32); + lindrustfinalize(); } + #[test] pub fn ut_lind_exit() { - // Since exit function is heavily used and tested in other syscalls and their tests - // We only perform preliminary checks for checking the sanity of this syscall - // We don't check for cases such as exiting a cage twice - since the exiting process - // is handled by the NaCl runtime - and it ensures that a cage does not exit twice - lindrustinit(0); + // Since exit function is heavily used and tested in other syscalls and their + // tests We only perform preliminary checks for checking the sanity of + // this syscall We don't check for cases such as exiting a cage twice - + // since the exiting process is handled by the NaCl runtime - and it + // ensures that a cage does not exit twice acquiring a lock on TESTMUTEX + // prevents other tests from running concurrently, and also performs + // clean env setup + let _thelock = setup::lock_and_init(); let cage = interface::cagetable_getref(1); // Call the exit call - assert_eq(cage.exit_syscall(EXIT_SUCCESS),EXIT_SUCCESS); - lindrustfinalize(); + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + lindrustfinalize(); } -} + #[test] + pub fn ut_lind_exec() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + let cage1 = interface::cagetable_getref(1); + // Spawn a new child + cage1.fork_syscall(2); + // Assert that the fork was correct + let child_cage = interface::cagetable_getref(2); + assert_eq!(child_cage.getuid_syscall(), -1); + assert_eq!(child_cage.getuid_syscall(), DEFAULT_UID as i32); + // Spawn exec and check if it returns 0 + assert_eq!(cage1.exec_syscall(2), 0); + lindrustfinalize(); + } +}