Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
156 changes: 125 additions & 31 deletions src/safeposix/syscalls/sys_calls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ use crate::safeposix::filesystem::{decref_dir, metawalk, Inode, FS_METADATA};
use crate::safeposix::net::NET_METADATA;
use crate::safeposix::shm::SHM_METADATA;

use std::sync::Arc as RustRfc;
Comment thread
Anway-Agte marked this conversation as resolved.


impl Cage {
fn unmap_shm_mappings(&self) {
Expand Down Expand Up @@ -69,15 +69,61 @@ impl Cage {
}
}

/// ### Description
///
///'fork_syscall` creates a new process (cage object)
/// The newly created child process is an exact copy of the
/// parent process (the process that calls fork)
/// apart from it's cage_id and the parent_id
/// In this function we clone the mutex table, condition variables table,
/// semaphore table and the file descriptors and create
/// a new Cage object with these cloned tables.
/// We also update the shared memory mappings - and create mappings
/// from the new Cage object the the
/// parent Cage object's memory mappings.
///
/// ### Arguments
///
/// It accepts one parameter:
///
/// * `child_cageid` : an integer representing the pid of the child process
///
/// ### Errors
///
/// There are 2 scenarios where the call to `fork_syscall` might return an error
///
/// * When the RawMutex::create() call fails to create a new Mutex object
/// * When the RawCondvar::create() call fails to create a new Condition Variable object
///
/// ### Returns
///
/// On success it returns a value of 0, and the new child Cage object is added to Cagetable
Comment thread
Anway-Agte marked this conversation as resolved.
///
/// ### Panics
///
/// This system call has no scenarios where it panics
///
/// To learn more about the syscall and possible error values, see
/// [fork(2)](https://man7.org/linux/man-pages/man2/fork.2.html)

pub fn fork_syscall(&self, child_cageid: u64) -> i32 {
//construct a new mutex in the child cage where each initialized mutex is in the parent cage
//Create a new mutex table that replicates the mutex table of the parent (calling) Cage object
//Since the child process inherits all the locks that the parent process holds,
let mutextable = self.mutex_table.read();
// Initialize the child object's mutex table
let mut new_mutex_table = vec![];
//Loop through each element in the mutex table
//Each entry in the mutex table represents a `lock` which the parent process holds
//Copying them into the child's Cage exhibits the inheritance of the lock
for elem in mutextable.iter() {
if elem.is_some() {
//If the mutex is `Some` - we create a new mutex and store it in the child's mutex table
//The create method returns a new struct obejct that represents a Mutex
let new_mutex_result = interface::RawMutex::create();
match new_mutex_result {
// If the mutex creation is successful we push it on the child's table
Ok(new_mutex) => new_mutex_table.push(Some(interface::RustRfc::new(new_mutex))),
// If the mutex creation returns an error, we abort the system call and return the appropriate error
Err(_) => {
match Errno::from_discriminant(interface::get_errno()) {
Ok(i) => {
Expand All @@ -94,19 +140,30 @@ impl Cage {
}
}
} else {
// If the mutex is `None` - we mimic the same behavior in the child's mutex table
new_mutex_table.push(None);
}
}
drop(mutextable);

//construct a new condvar in the child cage where each initialized condvar is in the parent cage
//Construct a replica of the condition variables table in the child cage object
//This table stores condition variables - which are special variables that the process
//uses to determine whether certain conditions have been met or not. Threads use condition variables
//to stop or resume their operation depending on the value of these variables.
//Read the CondVar table of the calling process
let cvtable = self.cv_table.read();
// Initialize the table for the child process
let mut new_cv_table = vec![];
// Loop through all the variables in the parent's table
for elem in cvtable.iter() {
if elem.is_some() {
//Create a condvar to store in the child's Cage object
//Returns the condition variable struct object which implements theb signal, wait, broadcast and timed_wait methods
let new_cv_result = interface::RawCondvar::create();
match new_cv_result {
// If the result of the creation of the RawCondVar is successful - push it onto the child's mutex table
Ok(new_cv) => new_cv_table.push(Some(interface::RustRfc::new(new_cv))),
// If the creation was unsucessful - return an Error
Err(_) => {
match Errno::from_discriminant(interface::get_errno()) {
Ok(i) => {
Expand All @@ -123,18 +180,25 @@ impl Cage {
}
}
} else {
// If the value is None - mimic the behavior in the child's condition variable table
new_cv_table.push(None);
}
}
drop(cvtable);

//construct new cage struct with a cloned fdtable
//Clone the file descriptor table in the child's Cage object
//Each entry in the file descriptor table points to an open file description which
//in turn references the actual inodes of the files on disk
let newfdtable = init_fdtable();
//Loop from 0 to maximum value of file descriptor index
for fd in 0..MAXFD {
let checkedfd = self.get_filedescriptor(fd).unwrap();
Comment thread
Anway-Agte marked this conversation as resolved.
//Get the lock for the file descriptor
let unlocked_fd = checkedfd.read();
if let Some(filedesc_enum) = &*unlocked_fd {
// Check the type of the file descriptor
match filedesc_enum {
// If the fd is linked to a file
Comment thread
Anway-Agte marked this conversation as resolved.
File(_normalfile_filedesc_obj) => {
let inodenum_option = if let File(f) = filedesc_enum {
Some(f.inode)
Expand All @@ -143,8 +207,10 @@ impl Cage {
};

if let Some(inodenum) = inodenum_option {
//increment the reference count on the inode
let mut inode = FS_METADATA.inodetable.get_mut(&inodenum).unwrap();
//Since the child Cage also inherits the parent's fd table
//We increment the reference count on the actual inodes of the files on disk
//Since the child Cage is a new process that also references those files
match *inode {
Inode::File(ref mut f) => {
f.refcount += 1;
Expand All @@ -161,52 +227,56 @@ impl Cage {
}
}
}
// If the fd is linked to a pipe increment the ref count of the pipe
Pipe(pipe_filedesc_obj) => {
pipe_filedesc_obj.pipe.incr_ref(pipe_filedesc_obj.flags)
}
// If the fd is linked to a socket increment the ref count of the socket
Socket(socket_filedesc_obj) => {
// checking whether this is a domain socket
// Check if it is a domain socket
let sock_tmp = socket_filedesc_obj.handle.clone();
let mut sockhandle = sock_tmp.write();
let socket_type = sockhandle.domain;
//Here we only increment the reference for AF_UNIX socket type
//Since these are the only sockets that have an inode associated with them
if socket_type == AF_UNIX {
//Increment the appropriate reference counter of the correct socket
//Each socket has two pipes associated with them - a read and write pipe
//Here we grab these two pipes and increment their references individually
//And also increment the reference count of the socket as a whole
if let Some(sockinfo) = &sockhandle.unix_info {
if let Some(sendpipe) = sockinfo.sendpipe.as_ref() {
sendpipe.incr_ref(O_WRONLY);
}
if let Some(receivepipe) = sockinfo.receivepipe.as_ref() {
receivepipe.incr_ref(O_RDONLY);
}
if let Some(uinfo) = &mut sockhandle.unix_info {
if let Inode::Socket(ref mut sock) =
*(FS_METADATA.inodetable.get_mut(&uinfo.inode).unwrap())
if let Inode::Socket(ref mut sock) =
*(FS_METADATA.inodetable.get_mut(&sockinfo.inode).unwrap())
{
sock.refcount += 1;
}
}
}
}
drop(sockhandle);
let sock_tmp = socket_filedesc_obj.handle.clone();
let mut sockhandle = sock_tmp.write();
if let Some(uinfo) = &mut sockhandle.unix_info {
if let Inode::Socket(ref mut sock) =
*(FS_METADATA.inodetable.get_mut(&uinfo.inode).unwrap())
{
sock.refcount += 1;
}
}
}
_ => {}
}
Comment thread
Anway-Agte marked this conversation as resolved.
Comment thread
Anway-Agte marked this conversation as resolved.

let newfdobj = filedesc_enum.clone();

// Insert the file descriptor object into the new file descriptor table
let _insertval = newfdtable[fd as usize].write().insert(newfdobj);
//add deep copied fd to fd table

}
}

//We read the current working directory of the parent Cage object
let cwd_container = self.cwd.read();
//We try to resolve the inode of the current working directory - if the
//resolution is successful we update the reference count of the current working directory
//since the newly created Child cage object also references the same directory
//If the resolution is not successful - the code panics since the cwd's inode cannot be resolved
//correctly
if let Some(cwdinodenum) = metawalk(&cwd_container) {
Comment thread
Anway-Agte marked this conversation as resolved.
if let Inode::Dir(ref mut cwddir) =
*(FS_METADATA.inodetable.get_mut(&cwdinodenum).unwrap())
Expand All @@ -219,12 +289,17 @@ impl Cage {
panic!("We changed from a directory that was not a directory in chdir!");
}

// we grab the parent cages main threads sigset and store it at 0
// we do this because we haven't established a thread for the cage yet, and dont have a threadid to store it at
// 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 and 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.
let newsigset = interface::RustHashMap::new();
// Here we check if Lind is being run under the test suite or not
if !interface::RUSTPOSIX_TESTSUITE.load(interface::RustAtomicOrdering::Relaxed) {
Comment thread
Anway-Agte marked this conversation as resolved.
// 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(
Expand All @@ -236,29 +311,33 @@ impl Cage {
let mainsigset = interface::RustAtomicU64::new(
mainsigsetatomic.load(interface::RustAtomicOrdering::Relaxed),
);
// Insert the parent cage object's main threads sigset and store them at index 0
newsigset.insert(0, mainsigset);
}

/*
* Construct a new semaphore table in child cage which equals to the one in the parent cage
*/
// Construct a new semaphore table in child cage which equals to the one in the parent cage
let semtable = &self.sem_table;
let new_semtable: interface::RustHashMap<
u32,
interface::RustRfc<interface::RustSemaphore>,
> = interface::RustHashMap::new();
// Loop all pairs
// Loop all pairs of semaphores and insert their copies into the new semaphore table
// Each pair consists of a key which is 32 bit unsigned integer
// And a Semaphore Object implemented as RustSemaphore
for pair in semtable.iter() {
Comment thread
namanlalitnyu marked this conversation as resolved.
new_semtable.insert((*pair.key()).clone(), pair.value().clone());
}

// Create a new cage object using the cloned tables and the child id passed as a parameter
let cageobj = Cage {
cageid: child_cageid,
cwd: interface::RustLock::new(self.cwd.read().clone()),
// Setting the parent to be the current Cage object
parent: self.cageid,
// Setting the fd table with our cloned fd table
filedescriptortable: newfdtable,
cancelstatus: interface::RustAtomicBool::new(false),
// This happens because self.getgid tries to copy atomic value which does not implement "Copy" trait; self.getgid.load returns i32.
// Intitialize IDs with the default value
getgid: interface::RustAtomicI32::new(
self.getgid.load(interface::RustAtomicOrdering::Relaxed),
),
Expand All @@ -271,28 +350,43 @@ impl Cage {
geteuid: interface::RustAtomicI32::new(
self.geteuid.load(interface::RustAtomicOrdering::Relaxed),
),
// Clone the reverse shm mappings
rev_shm: interface::Mutex::new((*self.rev_shm.lock()).clone()),
// Setting the mutex tables with our copy of the mutex table
mutex_table: interface::RustLock::new(new_mutex_table),
// Setting the condition variables table with our copy
cv_table: interface::RustLock::new(new_cv_table),
// Setting the semaphores table with our copy
sem_table: new_semtable,
// Creating a new empty table for storing threads of the child Cage object
thread_table: interface::RustHashMap::new(),
// Cloning the signal handler of the parent Cage object
signalhandler: self.signalhandler.clone(),
// Setting the signal set with the cloned and altered sigset
sigset: newsigset,
// Creating a new copy for the pending signal set
pendingsigset: interface::RustHashMap::new(),
// Setting the main thread id to 0 - since it is uninitialized
main_threadid: interface::RustAtomicU64::new(0),
// Creating a new timer for the process with id = child_cageid
interval_timer: interface::IntervalTimer::new(child_cageid),
};

let shmtable = &SHM_METADATA.shmtable;
//update fields for shared mappings in cage
// Updating the shared mappings in the child cage object
// Loop through all the reverse mappings in the new cage object
for rev_mapping in cageobj.rev_shm.lock().iter() {
let mut shment = shmtable.get_mut(&rev_mapping.1).unwrap();
shment.shminfo.shm_nattch += 1;
// Get the references of the curret cage id
let refs = shment.attached_cages.get(&self.cageid).unwrap();
// Copy the references
let childrefs = refs.clone();
drop(refs);
// Create references from the new Cage object to the copied references
shment.attached_cages.insert(child_cageid, childrefs);
}
// Inserting the child Cage object at the appropriate index in the Cage table
interface::cagetable_insert(child_cageid, cageobj);

0
Expand Down
22 changes: 22 additions & 0 deletions src/tests/sys_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ pub mod test_sys {
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();
}

pub fn ut_lind_getpid() {
Expand Down Expand Up @@ -68,5 +73,22 @@ pub mod test_sys {
lindrustfinalize()
}

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);
// Search for the new cage object with cage_id = 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())
}

Comment thread
Anway-Agte marked this conversation as resolved.
}