From b30a00e09d95860dd7df094a150c04424435f962 Mon Sep 17 00:00:00 2001 From: lind Date: Fri, 12 Jul 2024 20:30:43 +0000 Subject: [PATCH 01/13] first commit --- src/safeposix/syscalls/fs_calls.rs | 57 ++++++++++++++++++++++++++---- 1 file changed, 50 insertions(+), 7 deletions(-) diff --git a/src/safeposix/syscalls/fs_calls.rs b/src/safeposix/syscalls/fs_calls.rs index ca5e9af64..76c5de730 100644 --- a/src/safeposix/syscalls/fs_calls.rs +++ b/src/safeposix/syscalls/fs_calls.rs @@ -4979,17 +4979,50 @@ impl Cage { None } - //------------------SHMGET SYSCALL------------------ - - pub fn shmget_syscall(&self, key: i32, size: usize, shmflg: i32) -> i32 { + /// ### Description + /// + /// `shmget_syscall` returns the shared memory segment identifier associated with a particular `key` + /// If a key doesn't exist or equals `IPC_PRIVATE`, shmget creates a new + /// memory segment and attaches it to the key, however we only create a new memory segment if the key + /// doesn't exist, and return an error if key equals `IPC_PRIVATE` + /// + /// ### Returns + /// + /// An 32 bit integer which represens the identifier of the memory segment associated with the key + /// + /// ### Arguments + /// + /// `key` : An i32 value that references a memory segment + /// `size` : Size of the memory segment to be created if key doesn't exist + /// `shmflag` : mode flags which indicate whether to create a new key or not + /// + /// ### Errors + /// + /// * ENOENT : the key equals the `IPC_PRIVATE` constant + /// * EEXIST : key exists and yet either `IPC_CREAT` or `IPC_EXCL` are passed as flags + /// * ENOENT : key did not exist and the `IPC_CREAT` flag was not passed + /// * EINVAL : the size passed was less than the minimum size of segment or greater than the maximum possible size + /// + /// ### Panics + /// + /// There are no cases where the function directly panics + /// + pub fn shmget_syscall(&self, key: i32, size: usize, shmflg: i32) -> i32 { + //Check if the key passed equals the IPC_PRIVATE flag if key == IPC_PRIVATE { + // Return error since this is not suppported currently return syscall_error(Errno::ENOENT, "shmget", "IPC_PRIVATE not implemented"); } + // Variable to store shmid let shmid: i32; + // data of the shm table let metadata = &SHM_METADATA; + // Check if there exists a memory segment associated with the key passed as argument match metadata.shmkeyidtable.entry(key) { + // If there exists a memory segment at that key interface::RustHashEntry::Occupied(occupied) => { + // Produce an error if invalid flags are used with a valid key if (IPC_CREAT | IPC_EXCL) == (shmflg & (IPC_CREAT | IPC_EXCL)) { return syscall_error( Errno::EEXIST, @@ -4997,9 +5030,12 @@ impl Cage { "key already exists and IPC_CREAT and IPC_EXCL were used", ); } + // Get the id of the occupied memory segment shmid = *occupied.get(); } + // If the memory segment doesn't exist interface::RustHashEntry::Vacant(vacant) => { + // Return an error if IPC_CREAT was not specified if 0 == (shmflg & IPC_CREAT) { return syscall_error( Errno::ENOENT, @@ -5008,6 +5044,9 @@ impl Cage { ); } + // If memory segment doesn't exist and IPC_CREAT was specified - we create a new memory segment + + // Check if the size passed is a valid value if (size as u32) < SHMMIN || (size as u32) > SHMMAX { return syscall_error( Errno::EINVAL, @@ -5016,11 +5055,13 @@ impl Cage { ); } + // Generate a new id for the new memory segment shmid = metadata.new_keyid(); + // Insert new id in the hash table entry pointed by the key vacant.insert(shmid); - let mode = (shmflg & 0x1FF) as u16; // mode is 9 least signficant bits of shmflag, even if we dont really do - // anything with them - + // Mode of the new segment is the 9 least significant bits of the shmflag + let mode = (shmflg & 0x1FF) as u16; + // Create a new segment with the key, size, cageid of the calling process let segment = new_shm_segment( key, size, @@ -5029,10 +5070,12 @@ impl Cage { DEFAULT_GID, mode, ); + // Insert the newly created segment in the SHM table with its key metadata.shmtable.insert(shmid, segment); } }; - shmid // return the shmid + // Return the shmid + shmid } //------------------SHMAT SYSCALL------------------ From f3eed8e8f77aa701b46dfb0ad922bdddd5398ff5 Mon Sep 17 00:00:00 2001 From: lind Date: Wed, 17 Jul 2024 16:19:48 +0000 Subject: [PATCH 02/13] conflicts resolved --- src/tests/fs_tests.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/tests/fs_tests.rs b/src/tests/fs_tests.rs index 4a62d52d7..9e9d2ceed 100644 --- a/src/tests/fs_tests.rs +++ b/src/tests/fs_tests.rs @@ -4034,6 +4034,31 @@ pub mod fs_tests { assert_eq!(cage.close_syscall(fd), 0); assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + pub fn ut_lind_fs_shmget_syscall(){ + // acquire locks and start env cleanup + let _thelock = setup::lock_and_init(); + let cage = interface::cagetable_getref(1); + + let key = 33123; + // Get shmid of a memory segment / create a new one if it doesn't exist + let shmid = cage.shmget_syscall(33123, 1024, IPC_CREAT); + assert_eq!(shmid,4); + + // Check error upon asking for a valid key and passing the IPC_CREAT or IPC_EXCL flag + assert_eq!(cage.shmget_syscall(key, 1024, IPC_CREAT),-(Errno::EEXIST as i32)); + // assert_eq!(cage.shmget_syscall(key, 1024, IPC_EXCL),-(Errno::EEXIST as i32)); + + // Check if the function returns a correct shmid upon asking with a key that we know exists + assert_eq!(cage.shmget_syscall(key, 1024,0666),shmid); + + // Check if the function returns the correct error when we don't pass IPC_CREAT for a key that doesn't exist + assert_eq!(cage.shmget_syscall(123456, 1024, 0),-(Errno::ENOENT as i32)); + + // Check if the size error is returned correctly + assert_eq!(cage.shmget_syscall(123456, (SHMMAX + 10 )as usize, IPC_CREAT),-(Errno::EINVAL as i32)); + assert_eq!(cage.shmget_syscall(123456, 0 as usize, IPC_CREAT),-(Errno::EINVAL as i32)); + lindrustfinalize(); } } +} From 52a366177f2c4e668619d95cafc30e6fe1c4dd59 Mon Sep 17 00:00:00 2001 From: lind Date: Thu, 18 Jul 2024 03:04:51 +0000 Subject: [PATCH 03/13] changes --- src/tests/fs_tests.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/tests/fs_tests.rs b/src/tests/fs_tests.rs index 9e9d2ceed..581dad2be 100644 --- a/src/tests/fs_tests.rs +++ b/src/tests/fs_tests.rs @@ -4034,6 +4034,10 @@ pub mod fs_tests { assert_eq!(cage.close_syscall(fd), 0); assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + lindrustfinalize(); + } + + pub fn ut_lind_fs_shmget_syscall(){ // acquire locks and start env cleanup let _thelock = setup::lock_and_init(); @@ -4044,9 +4048,8 @@ pub mod fs_tests { let shmid = cage.shmget_syscall(33123, 1024, IPC_CREAT); assert_eq!(shmid,4); - // Check error upon asking for a valid key and passing the IPC_CREAT or IPC_EXCL flag - assert_eq!(cage.shmget_syscall(key, 1024, IPC_CREAT),-(Errno::EEXIST as i32)); - // assert_eq!(cage.shmget_syscall(key, 1024, IPC_EXCL),-(Errno::EEXIST as i32)); + // Check error upon asking for a valid key and passing the IPC_CREAT and IPC_EXCL flag + assert_eq!(cage.shmget_syscall(key, 1024, IPC_CREAT & IPC_EXCL),-(Errno::EEXIST as i32)); // Check if the function returns a correct shmid upon asking with a key that we know exists assert_eq!(cage.shmget_syscall(key, 1024,0666),shmid); @@ -4061,4 +4064,4 @@ pub mod fs_tests { lindrustfinalize(); } } -} + From cd4117b8407c82003edfe3c1738dc8d526cedb9e Mon Sep 17 00:00:00 2001 From: lind Date: Thu, 18 Jul 2024 03:10:11 +0000 Subject: [PATCH 04/13] final commit --- src/tests/fs_tests.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/tests/fs_tests.rs b/src/tests/fs_tests.rs index 581dad2be..74c94ba48 100644 --- a/src/tests/fs_tests.rs +++ b/src/tests/fs_tests.rs @@ -4034,10 +4034,6 @@ pub mod fs_tests { assert_eq!(cage.close_syscall(fd), 0); assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); - lindrustfinalize(); - } - - pub fn ut_lind_fs_shmget_syscall(){ // acquire locks and start env cleanup let _thelock = setup::lock_and_init(); @@ -4048,8 +4044,9 @@ pub mod fs_tests { let shmid = cage.shmget_syscall(33123, 1024, IPC_CREAT); assert_eq!(shmid,4); - // Check error upon asking for a valid key and passing the IPC_CREAT and IPC_EXCL flag - assert_eq!(cage.shmget_syscall(key, 1024, IPC_CREAT & IPC_EXCL),-(Errno::EEXIST as i32)); + // Check error upon asking for a valid key and passing the IPC_CREAT or IPC_EXCL flag + assert_eq!(cage.shmget_syscall(key, 1024, IPC_CREAT),-(Errno::EEXIST as i32 )); + // assert_eq!(cage.shmget_syscall(key, 1024, IPC_EXCL),-(Errno::EEXIST as i32)); // Check if the function returns a correct shmid upon asking with a key that we know exists assert_eq!(cage.shmget_syscall(key, 1024,0666),shmid); @@ -4064,4 +4061,4 @@ pub mod fs_tests { lindrustfinalize(); } } - +} From 1a0bb88f7c45236e9f69236d564cc749e4f3a950 Mon Sep 17 00:00:00 2001 From: lind Date: Thu, 18 Jul 2024 03:10:40 +0000 Subject: [PATCH 05/13] errors --- src/tests/fs_tests.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/tests/fs_tests.rs b/src/tests/fs_tests.rs index 74c94ba48..9ac5ac406 100644 --- a/src/tests/fs_tests.rs +++ b/src/tests/fs_tests.rs @@ -4034,6 +4034,9 @@ pub mod fs_tests { assert_eq!(cage.close_syscall(fd), 0); assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + lindrustfinalize(); + } + pub fn ut_lind_fs_shmget_syscall(){ // acquire locks and start env cleanup let _thelock = setup::lock_and_init(); @@ -4061,4 +4064,4 @@ pub mod fs_tests { lindrustfinalize(); } } -} + From 4dbc6ce966eb3887afb8f8d091ffa95babaf7e6f Mon Sep 17 00:00:00 2001 From: lind Date: Thu, 18 Jul 2024 03:12:42 +0000 Subject: [PATCH 06/13] changes --- src/tests/fs_tests.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/tests/fs_tests.rs b/src/tests/fs_tests.rs index 9ac5ac406..74c94ba48 100644 --- a/src/tests/fs_tests.rs +++ b/src/tests/fs_tests.rs @@ -4034,9 +4034,6 @@ pub mod fs_tests { assert_eq!(cage.close_syscall(fd), 0); assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); - lindrustfinalize(); - } - pub fn ut_lind_fs_shmget_syscall(){ // acquire locks and start env cleanup let _thelock = setup::lock_and_init(); @@ -4064,4 +4061,4 @@ pub mod fs_tests { lindrustfinalize(); } } - +} From ceff017427e87811c66469abac66fe1fa289d812 Mon Sep 17 00:00:00 2001 From: lind Date: Thu, 18 Jul 2024 03:13:50 +0000 Subject: [PATCH 07/13] changes --- src/safeposix/syscalls/net_calls.rs | 128 +-------- src/tests/networking_tests.rs | 407 ++++++++-------------------- 2 files changed, 132 insertions(+), 403 deletions(-) diff --git a/src/safeposix/syscalls/net_calls.rs b/src/safeposix/syscalls/net_calls.rs index 22ebf9af8..b23fa819c 100644 --- a/src/safeposix/syscalls/net_calls.rs +++ b/src/safeposix/syscalls/net_calls.rs @@ -3475,77 +3475,12 @@ impl Cage { return 0; } - /// ## ------------------POLL SYSCALL------------------ - /// ### Description - /// poll_syscall performs a similar task to select_syscall: it waits for - /// one of a set of file descriptors to become ready to perform I/O. - - /// ### Function Arguments - /// The `poll_syscall()` receives two arguments: - /// * `fds` - The set of file descriptors to be monitored is specified in - /// the fds argument, which is an array of PollStruct structures - /// containing three fields: fd, events and revents. events and revents - /// are requested events and returned events, respectively. The field fd - /// contains a file descriptor for an open file. If this field is - /// negative, then the corresponding events field is ignored and the - /// revents field returns zero. The field events is an input parameter, a - /// bit mask specifying the events the application is interested in for - /// the file descriptor fd. The bits returned in revents can include any - /// of those specified in events, or POLLNVAL. The bits that may be - /// set/returned in events and revents are: 1. POLLIN: There is data to - /// read. 2. POLLPRI: There is some exceptional condition on the file - /// descriptor, currently not supported 3. POLLOUT: Writing is now - /// possible, though a write larger than the available space in a socket - /// or pipe will still block - /// 4. POLLNVAL: Invalid request: fd not open (only returned in revents; - /// ignored in events). - /// * `timeout` - The timeout argument is a RustDuration structure that - /// specifies the interval that poll() should block waiting for a file - /// descriptor to become ready. The call will block until either: 1. a - /// file descriptor becomes ready; 2. the call is interrupted by a signal - /// handler; 3. the timeout expires. - - /// ### Returns - /// On success, poll_syscall returns a nonnegative value which is the - /// number of elements in the pollfds whose revents fields have been - /// set to a nonzero value (indicating an event or an error). A - /// return value of zero indicates that the system call timed out - /// before any file descriptors became ready. - /// - /// ### Errors - /// * EINTR - A signal was caught. - /// * EINVAL - fd exceeds the FD_SET_MAX_FD. - /// - /// ### Panics - /// No panic is expected from this syscall pub fn poll_syscall( &self, fds: &mut [PollStruct], timeout: Option, ) -> i32 { - // timeout is supposed to be in milliseconds - - // current implementation of poll_syscall is based on select_syscall - // which gives several issues: - // 1. according to standards, select_syscall should only support file descriptor - // that is smaller than 1024, while poll_syscall should not have such - // limitation but our implementation of poll_syscall is actually calling - // select_syscall directly which would mean poll_syscall would also have the - // 1024 maximum size limitation However, rustposix itself only support file - // descriptor that is smaller than 1024 which solves this issue automatically - // in an interesting way - // 2. current implementation of poll_syscall is very inefficient, that it passes - // each of the file descriptor into select_syscall one by one. A better - // solution might be transforming pollstruct into fdsets and pass into - // select_syscall once (TODO). A even more efficienct way would be completely - // rewriting poll_syscall so it does not depend on select_syscall anymore. - // This is also how Linux does for poll_syscall since Linux claims that poll - // have a better performance than select. - // 3. several revent value such as POLLERR (which should be set when pipe is - // broken), or POLLHUP (when peer closed its channel) are not possible to - // monitor. Since select_syscall does not have these features, so our - // poll_syscall, which derived from select_syscall, would subsequently not be - // able to support these features. + //timeout is supposed to be in milliseconds let mut return_code: i32 = 0; let start_time = interface::starttimer(); @@ -3555,26 +3490,9 @@ impl Cage { None => interface::RustDuration::MAX, }; - // according to standard, we should clear all revents - for structpoll in &mut *fds { - structpoll.revents = 0; - } - - // we loop until either timeout - // or any of the file descriptor is ready loop { - // iterate through each file descriptor for structpoll in &mut *fds { - // get the file descriptor let fd = structpoll.fd; - - // according to standard, we should ignore all file descriptor - // that is smaller than 0 - if fd < 0 { - continue; - } - - // get the associated events to monitor let events = structpoll.events; // init FdSet structures @@ -3582,25 +3500,24 @@ impl Cage { let writes = &mut interface::FdSet::new(); let errors = &mut interface::FdSet::new(); - // POLLIN for readable fd + //read if events & POLLIN > 0 { reads.set(fd) } - // POLLOUT for writable fd + //write if events & POLLOUT > 0 { writes.set(fd) } - // POLLPRI for except fd - if events & POLLPRI > 0 { + //err + if events & POLLERR > 0 { errors.set(fd) } - // this mask is used for storing final revent result let mut mask: i16 = 0; - // here we just call select_syscall with timeout of zero, - // which essentially just check each fd set once then return - // NOTE that the nfds argument is highest fd + 1 + //0 essentially sets the timeout to the max value allowed (which is almost + // always more than enough time) NOTE that the nfds argument is + // highest fd + 1 let selectret = Self::select_syscall( &self, fd + 1, @@ -3609,44 +3526,23 @@ impl Cage { Some(errors), Some(interface::RustDuration::ZERO), ); - // if there is any file descriptor ready if selectret > 0 { - // is the file descriptor ready to read? - mask |= if !reads.is_empty() { POLLIN } else { 0 }; - // is the file descriptor ready to write? - mask |= if !writes.is_empty() { POLLOUT } else { 0 }; - // is there any exception conditions on the file descriptor? - mask |= if !errors.is_empty() { POLLPRI } else { 0 }; - // this file descriptor is ready for something, - // increment the return value + mask += if !reads.is_empty() { POLLIN } else { 0 }; + mask += if !writes.is_empty() { POLLOUT } else { 0 }; + mask += if !errors.is_empty() { POLLERR } else { 0 }; return_code += 1; } else if selectret < 0 { - // if there is any error, first check if the error - // is EBADF, which refers to invalid file descriptor error - // in this case, we should set POLLNVAL to revent - if selectret == -(Errno::EBADF as i32) { - mask |= POLLNVAL; - // according to standard, return value is the number of fds - // with non-zero revent, which may indicate an error as well - return_code += 1; - } else { - return selectret; - } + return selectret; } - // set the revents structpoll.revents = mask; } - // we break if there is any file descriptor ready - // or timeout is reached if return_code != 0 || interface::readtimer(start_time) > end_time { break; } else { - // otherwise, check for signal and loop again if interface::sigcheck() { return syscall_error(Errno::EINTR, "poll", "interrupted function call"); } - // We yield to let other threads continue if we've found no ready descriptors interface::lind_yield(); } } diff --git a/src/tests/networking_tests.rs b/src/tests/networking_tests.rs index da94213b3..1c0cf0c18 100644 --- a/src/tests/networking_tests.rs +++ b/src/tests/networking_tests.rs @@ -1081,43 +1081,18 @@ pub mod net_tests { #[test] #[ignore] pub fn ut_lind_net_poll() { - // test for poll monitoring on multiple different file descriptors: - // 1. regular file - // 2. AF_INET server socket waiting for two clients - // 3. AF_INET server socket's connection file descriptor with clients - // 4. AF_UNIX server socket's connection file descriptor with a client - // 5. pipe - - // acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + //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); - // creating regular file's file descriptor let filefd = cage.open_syscall("/netpolltest.txt", O_CREAT | O_EXCL | O_RDWR, S_IRWXA); assert!(filefd > 0); - // creating socket file descriptors let serversockfd = cage.socket_syscall(AF_INET, SOCK_STREAM, 0); let clientsockfd1 = cage.socket_syscall(AF_INET, SOCK_STREAM, 0); let clientsockfd2 = cage.socket_syscall(AF_INET, SOCK_STREAM, 0); - let serversockfd_unix = cage.socket_syscall(AF_UNIX, SOCK_STREAM, 0); - let clientsockfd_unix = cage.socket_syscall(AF_UNIX, SOCK_STREAM, 0); - - assert!(serversockfd > 0); - assert!(clientsockfd1 > 0); - assert!(clientsockfd2 > 0); - assert!(serversockfd_unix > 0); - assert!(clientsockfd_unix > 0); - - // creating a pipe - let mut pipefds = PipeArray { - readfd: -1, - writefd: -1, - }; - assert_eq!(cage.pipe_syscall(&mut pipefds), 0); - - // create a INET address let port: u16 = generate_random_port(); let sockaddr = interface::SockaddrV4 { @@ -1129,319 +1104,177 @@ pub mod net_tests { padding: 0, }; let socket = interface::GenSockaddr::V4(sockaddr); //127.0.0.1 from bytes above - - //binding to a socket - let serversockaddr_unix = - interface::new_sockaddr_unix(AF_UNIX as u16, "server_poll".as_bytes()); - let serversocket_unix = interface::GenSockaddr::Unix(serversockaddr_unix); - - let clientsockaddr_unix = - interface::new_sockaddr_unix(AF_UNIX as u16, "client_poll".as_bytes()); - let clientsocket_unix = interface::GenSockaddr::Unix(clientsockaddr_unix); - - assert_eq!(cage.bind_syscall(serversockfd_unix, &serversocket_unix), 0); - assert_eq!(cage.bind_syscall(clientsockfd_unix, &clientsocket_unix), 0); - assert_eq!(cage.listen_syscall(serversockfd_unix, 1), 0); - assert_eq!(cage.bind_syscall(serversockfd, &socket), 0); assert_eq!(cage.listen_syscall(serversockfd, 4), 0); - // create a PollStruct for each fd let serverpoll = interface::PollStruct { fd: serversockfd, events: POLLIN, revents: 0, }; - - let serverunixpoll = interface::PollStruct { - fd: serversockfd_unix, - events: POLLIN, - revents: 0, - }; - let filepoll = interface::PollStruct { fd: filefd, - events: POLLIN | POLLOUT, - revents: 0, - }; - - let pipepoll = interface::PollStruct { - fd: pipefds.readfd, events: POLLIN, revents: 0, }; + let mut polled = vec![serverpoll, filepoll]; - let mut polled = vec![filepoll, serverpoll, serverunixpoll, pipepoll]; - - assert_eq!(cage.fork_syscall(2), 0); // used for AF_INET thread client 1 - assert_eq!(cage.fork_syscall(3), 0); // used for AF_INET thread client 2 - assert_eq!(cage.fork_syscall(4), 0); // used for AF_UNIX thread client - - assert_eq!(cage.fork_syscall(5), 0); // used for pipe thread - - assert_eq!(cage.close_syscall(clientsockfd1), 0); - assert_eq!(cage.close_syscall(clientsockfd2), 0); - assert_eq!(cage.close_syscall(clientsockfd_unix), 0); - - // this barrier have to ensure that the clients finish the connect before we do - // the poll due to an unfixed bug (`close` could block when other - // thread/cage is `accept`) - let barrier = Arc::new(Barrier::new(3)); - let barrier_clone1 = barrier.clone(); - let barrier_clone2 = barrier.clone(); - - // this barrier is used for control the flow the pipe - let barrier_pipe = Arc::new(Barrier::new(2)); - let barrier_pipe_clone = barrier_pipe.clone(); - - // due to an unfixed bug in ref counter of AF_UNIX socket pipe - // have to make sure all the threads exits only after the AF_UNIX test finished - let barrier_exit = Arc::new(Barrier::new(4)); - let barrier_exit_clone1 = barrier_exit.clone(); - let barrier_exit_clone2 = barrier_exit.clone(); - let barrier_exit_clone3 = barrier_exit.clone(); - - // client 1 connects to the server to send and recv data - let threadclient1 = interface::helper_thread(move || { + cage.fork_syscall(2); + //client 1 connects to the server to send and recv data... + let thread1 = interface::helper_thread(move || { + interface::sleep(interface::RustDuration::from_millis(30)); let cage2 = interface::cagetable_getref(2); - assert_eq!(cage2.close_syscall(serversockfd), 0); - assert_eq!(cage2.close_syscall(clientsockfd2), 0); - // connect to server assert_eq!(cage2.connect_syscall(clientsockfd1, &socket), 0); - barrier_clone1.wait(); - - // send message to server - assert_eq!(cage2.send_syscall(clientsockfd1, str2cbuf("test"), 4, 0), 4); - - interface::sleep(interface::RustDuration::from_millis(1)); - - // receive message from server - let mut buf = sizecbuf(4); - assert_eq!(cage2.recv_syscall(clientsockfd1, buf.as_mut_ptr(), 4, 0), 4); - assert_eq!(cbuf2str(&buf), "test"); + assert_eq!( + cage2.send_syscall(clientsockfd1, str2cbuf(&"test"), 4, 0), + 4 + ); + //giving it a longer pause time to that it can process all of the data that it + // is recieving + interface::sleep(interface::RustDuration::from_millis(100)); - assert_eq!(cage2.close_syscall(clientsockfd1), 0); - barrier_exit_clone1.wait(); + assert_eq!(cage2.close_syscall(serversockfd), 0); cage2.exit_syscall(EXIT_SUCCESS); }); - // client 2 connects to the server to send and recv data - let threadclient2 = interface::helper_thread(move || { + cage.fork_syscall(3); + //client 2 connects to the server to send and recv data... + let thread2 = interface::helper_thread(move || { + //give it a longer time so that it can sufficiently process all of the data + interface::sleep(interface::RustDuration::from_millis(45)); let cage3 = interface::cagetable_getref(3); - assert_eq!(cage3.close_syscall(serversockfd), 0); - assert_eq!(cage3.close_syscall(clientsockfd1), 0); - // connect to server assert_eq!(cage3.connect_syscall(clientsockfd2, &socket), 0); - barrier_clone2.wait(); - - // send message to server - assert_eq!(cage3.send_syscall(clientsockfd2, str2cbuf("test"), 4, 0), 4); - - interface::sleep(interface::RustDuration::from_millis(1)); - - // receive message from server - let mut buf = sizecbuf(4); - let mut result: i32; - loop { - result = cage3.recv_syscall(clientsockfd2, buf.as_mut_ptr(), 4, 0); - if result != -libc::EINTR { - break; // if the error was EINTR, retry the syscall - } - } - assert_eq!(result, 4); - assert_eq!(cbuf2str(&buf), "test"); - - assert_eq!(cage3.close_syscall(clientsockfd2), 0); - barrier_exit_clone2.wait(); - cage3.exit_syscall(EXIT_SUCCESS); - }); - - let threadclient_unix = interface::helper_thread(move || { - let cage4 = interface::cagetable_getref(4); - assert_eq!(cage4.close_syscall(serversockfd_unix), 0); - assert_eq!(cage4.close_syscall(serversockfd), 0); - - // connect to server assert_eq!( - cage4.connect_syscall(clientsockfd_unix, &serversocket_unix), - 0 - ); - - // send message to server - assert_eq!( - cage4.send_syscall(clientsockfd_unix, str2cbuf("test"), 4, 0), + cage3.send_syscall(clientsockfd2, str2cbuf(&"test"), 4, 0), 4 ); - interface::sleep(interface::RustDuration::from_millis(1)); - - // recieve message from server - let mut buf = sizecbuf(4); - let mut result: i32; - loop { - result = cage4.recv_syscall(clientsockfd_unix, buf.as_mut_ptr(), 4, 0); - if result != -libc::EINTR { - break; // if the error was EINTR, retry the syscall - } - } - assert_eq!(result, 4); - assert_eq!(cbuf2str(&buf), "test"); - - assert_eq!(cage4.close_syscall(clientsockfd_unix), 0); - cage4.exit_syscall(EXIT_SUCCESS); - }); - - let thread_pipe = interface::helper_thread(move || { - let cage5 = interface::cagetable_getref(5); - - interface::sleep(interface::RustDuration::from_millis(1)); - // send message to pipe - assert_eq!(cage5.write_syscall(pipefds.writefd, str2cbuf("test"), 4), 4); - - let mut buf = sizecbuf(5); - // wait until peer read the message - barrier_pipe_clone.wait(); - - // read the message sent by peer - assert_eq!(cage5.read_syscall(pipefds.readfd, buf.as_mut_ptr(), 5), 5); - assert_eq!(cbuf2str(&buf), "test2"); + interface::sleep(interface::RustDuration::from_millis(100)); - barrier_exit_clone3.wait(); - cage5.exit_syscall(EXIT_SUCCESS); + assert_eq!(cage3.close_syscall(serversockfd), 0); + cage3.exit_syscall(EXIT_SUCCESS); }); - barrier.wait(); - // acting as the server and processing the request - // Server loop to handle connections and I/O - // Check for any activity in any of the Input sockets - for counter in 0..600 { - let poll_result = cage.poll_syscall(&mut polled.as_mut_slice(), None); - assert!(poll_result >= 0); // check for error - - // clearfds stores the fds that should be removed from polled at the end of the - // iteration - let mut clearfds = vec![]; - // addfds stores the fds that should be added to polled at the end of the - // iteration - let mut addfds = vec![]; + //acting as the server and processing the request + let thread3 = interface::helper_thread(move || { + let mut infds: Vec; + let mut outfds: Vec; + for _counter in 0..600 { + //start a while true loop for processing requests + let pollretvalue = cage.poll_syscall( + &mut polled.as_mut_slice(), + Some(interface::RustDuration::ZERO), + ); + assert!(pollretvalue >= 0); - // check for readfds - for poll in &mut polled { - // If the socket returned was listerner socket, then there's a new conn., so we - // accept it, and put the client socket in the list of Inputs. - if poll.fd == serversockfd { - if poll.revents & POLLIN != 0 { - let mut sockgarbage = - interface::GenSockaddr::V4(interface::SockaddrV4::default()); - let sockfd = cage.accept_syscall(poll.fd as i32, &mut sockgarbage); - assert!(sockfd > 0); - // new connection is estalished, add it to readfds and writefds + infds = vec![]; + outfds = vec![]; - addfds.push(interface::PollStruct { - fd: sockfd, - events: POLLIN | POLLOUT, - revents: 0, - }); + for polledfile in &mut polled { + if polledfile.revents & POLLIN != 0 { + infds.push(polledfile.fd); } - } else if poll.fd == filefd { - // poll on regular file should always success - // therefore revents should be set for filefd at the first iteration - assert_eq!(counter, 0); - assert_eq!(poll.revents, POLLIN | POLLOUT); - // remove file fd from poll - clearfds.push(filefd); - } else if poll.fd == serversockfd_unix { - if poll.revents & POLLIN != 0 { - // unix socket - let mut sockgarbage = interface::GenSockaddr::Unix( - interface::new_sockaddr_unix(AF_UNIX as u16, "".as_bytes()), - ); - let sockfd = cage.accept_syscall(poll.fd as i32, &mut sockgarbage); - assert!(sockfd > 0); - // new connection is estalished, add it to poll - addfds.push(interface::PollStruct { - fd: sockfd, - events: POLLIN | POLLOUT, - revents: 0, - }); + if polledfile.revents & POLLOUT != 0 { + outfds.push(polledfile.fd); } - } else if poll.fd == pipefds.readfd { - if poll.revents & POLLIN != 0 { - // pipe - let mut buf = sizecbuf(4); - // read the message from peer - assert_eq!(cage.read_syscall(pipefds.readfd, buf.as_mut_ptr(), 4), 4); - assert_eq!(cbuf2str(&buf), "test"); + } - // write the message from peer - assert_eq!( - cage.write_syscall(pipefds.writefd, str2cbuf("test2"), 5) as usize, - 5 - ); - barrier_pipe.wait(); + //check for any activity in the input sockets + for sockfd in infds { + //If the socket returned was listerner socket, then there's a new connection + //so we accept it, and put the client socket in the list of inputs. + if sockfd == serversockfd { + let port: u16 = generate_random_port(); + let sockaddr = interface::SockaddrV4 { + sin_family: AF_INET as u16, + sin_port: port.to_be(), + sin_addr: interface::V4Addr { + s_addr: u32::from_ne_bytes([127, 0, 0, 1]), + }, + padding: 0, + }; + let mut addr = interface::GenSockaddr::V4(sockaddr); //127.0.0.1 from bytes above - // pipe poll test done - clearfds.push(pipefds.readfd); - } - } else { - if poll.revents & POLLIN != 0 { + let newsockfd = cage.accept_syscall(sockfd, &mut addr); + polled.push(interface::PollStruct { + fd: newsockfd, + events: POLLIN, + revents: 0, + }) + } else if sockfd == filefd { + //Write to a file... + assert_eq!(cage.write_syscall(sockfd, str2cbuf("test"), 4), 4); + assert_eq!(cage.lseek_syscall(sockfd, 0, SEEK_SET), 0); + //Once the write is successful into a file, modify the file descriptor so + // that its ready for reading out of the file. + for polledfile in &mut polled { + if polledfile.fd == sockfd { + polledfile.events = POLLOUT; + break; + } + } + } else { //If the socket is in established conn., then we recv the data. If there's // no data, then close the client socket. let mut buf = sizecbuf(4); - let mut recvresult: i32; + let mut result: i32; loop { - // receive message from peer - recvresult = cage.recv_syscall(poll.fd as i32, buf.as_mut_ptr(), 4, 0); - if recvresult != -libc::EINTR { + result = cage.recv_syscall(sockfd, buf.as_mut_ptr(), 4, 0); + if result != -libc::EINTR { + assert_eq!(result & !4, 0); //This must be 0 or 4 to be correct, either the socket is good for + // recieving or it's closed break; // if the error was EINTR, retry the // syscall } } - if recvresult == 4 { - if cbuf2str(&buf) == "test" { - continue; + if result == 4 { + assert_eq!(cbuf2str(&buf), "test"); + //This socket is ready for writing, modify the socket descriptor to be + // in read-write mode. This socket can write data out to network + for polledfile in &mut polled { + if polledfile.fd == sockfd { + polledfile.events = POLLOUT; + break; + } } - } else if recvresult == -libc::ECONNRESET { - // peer closed the connection - println!("Connection reset by peer on socket {}", poll.fd); - assert_eq!(cage.close_syscall(poll.fd as i32), 0); - clearfds.push(poll.fd); + } else { + //No data means remote socket closed, hence close the client socket in + // server, also remove this socket from polling. + assert_eq!(cage.close_syscall(sockfd), 0); + polled.retain(|x| x.fd != sockfd); } } - if poll.revents & POLLOUT != 0 { - // Data is sent out this socket, it's no longer ready for writing - // clear the POLLOUT from events - assert_eq!(cage.send_syscall(poll.fd as i32, str2cbuf("test"), 4, 0), 4); - poll.events &= !POLLOUT; - } } - } - // clear fds - polled.retain(|x| { - for fd in &clearfds { - if *fd == x.fd { - return false; + + for sockfd in outfds { + if sockfd == filefd { + let mut read_buf1 = sizecbuf(4); + assert_eq!(cage.read_syscall(sockfd, read_buf1.as_mut_ptr(), 4), 4); + assert_eq!(cbuf2str(&read_buf1), "test"); + //test for file finished, remove from polling. + polled.retain(|x| x.fd != sockfd); + } else { + //Data is sent out of this socket, it's no longer ready for writing, modify + // it only read mode. + assert_eq!(cage.send_syscall(sockfd, str2cbuf(&"test"), 4, 0), 4); + for polledfile in &mut polled { + if polledfile.fd == sockfd { + polledfile.events = POLLIN; + } + } } } - return true; - }); - // add new fds - polled.extend(addfds); - } - assert_eq!(cage.close_syscall(serversockfd), 0); - assert_eq!(cage.close_syscall(serversockfd_unix), 0); - - // let threads exit - barrier_exit.wait(); + } + assert_eq!(cage.close_syscall(serversockfd), 0); + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + }); - threadclient1.join().unwrap(); - threadclient2.join().unwrap(); - threadclient_unix.join().unwrap(); - thread_pipe.join().unwrap(); + thread1.join().unwrap(); + thread2.join().unwrap(); + thread3.join().unwrap(); - assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); lindrustfinalize(); } From 4c33d7744f5f88116f12ec480a130fc10c8c9e6c Mon Sep 17 00:00:00 2001 From: lind Date: Thu, 18 Jul 2024 03:29:30 +0000 Subject: [PATCH 08/13] tests added --- src/tests/fs_tests.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/tests/fs_tests.rs b/src/tests/fs_tests.rs index 74c94ba48..3c579c6ea 100644 --- a/src/tests/fs_tests.rs +++ b/src/tests/fs_tests.rs @@ -4060,5 +4060,32 @@ pub mod fs_tests { lindrustfinalize(); } + + pub fn ut_lind_fs_shmget_syscall(){ + // acquire locks and start env cleanup + let _thelock = setup::lock_and_init(); + let cage = interface::cagetable_getref(1); + + let key = 33123; + // Get shmid of a memory segment / create a new one if it doesn't exist + let shmid = cage.shmget_syscall(33123, 1024, IPC_CREAT); + assert_eq!(shmid,4); + + // Check error upon asking for a valid key and passing the IPC_CREAT or IPC_EXCL flag + assert_eq!(cage.shmget_syscall(key, 1024, IPC_CREAT),-(Errno::EEXIST as i32 )); + // assert_eq!(cage.shmget_syscall(key, 1024, IPC_EXCL),-(Errno::EEXIST as i32)); + + // Check if the function returns a correct shmid upon asking with a key that we know exists + assert_eq!(cage.shmget_syscall(key, 1024,0666),shmid); + + // Check if the function returns the correct error when we don't pass IPC_CREAT for a key that doesn't exist + assert_eq!(cage.shmget_syscall(123456, 1024, 0),-(Errno::ENOENT as i32)); + + // Check if the size error is returned correctly + assert_eq!(cage.shmget_syscall(123456, (SHMMAX + 10 )as usize, IPC_CREAT),-(Errno::EINVAL as i32)); + assert_eq!(cage.shmget_syscall(123456, 0 as usize, IPC_CREAT),-(Errno::EINVAL as i32)); + + lindrustfinalize(); + } } } From 1cc01320cc00c2a2b4e2f851b4df21b72c87e7a0 Mon Sep 17 00:00:00 2001 From: lind Date: Thu, 18 Jul 2024 03:33:39 +0000 Subject: [PATCH 09/13] test added --- src/tests/fs_tests.rs | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/src/tests/fs_tests.rs b/src/tests/fs_tests.rs index 3c579c6ea..f0247f35c 100644 --- a/src/tests/fs_tests.rs +++ b/src/tests/fs_tests.rs @@ -4034,29 +4034,6 @@ pub mod fs_tests { assert_eq!(cage.close_syscall(fd), 0); assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); - pub fn ut_lind_fs_shmget_syscall(){ - // acquire locks and start env cleanup - let _thelock = setup::lock_and_init(); - let cage = interface::cagetable_getref(1); - - let key = 33123; - // Get shmid of a memory segment / create a new one if it doesn't exist - let shmid = cage.shmget_syscall(33123, 1024, IPC_CREAT); - assert_eq!(shmid,4); - - // Check error upon asking for a valid key and passing the IPC_CREAT or IPC_EXCL flag - assert_eq!(cage.shmget_syscall(key, 1024, IPC_CREAT),-(Errno::EEXIST as i32 )); - // assert_eq!(cage.shmget_syscall(key, 1024, IPC_EXCL),-(Errno::EEXIST as i32)); - - // Check if the function returns a correct shmid upon asking with a key that we know exists - assert_eq!(cage.shmget_syscall(key, 1024,0666),shmid); - - // Check if the function returns the correct error when we don't pass IPC_CREAT for a key that doesn't exist - assert_eq!(cage.shmget_syscall(123456, 1024, 0),-(Errno::ENOENT as i32)); - - // Check if the size error is returned correctly - assert_eq!(cage.shmget_syscall(123456, (SHMMAX + 10 )as usize, IPC_CREAT),-(Errno::EINVAL as i32)); - assert_eq!(cage.shmget_syscall(123456, 0 as usize, IPC_CREAT),-(Errno::EINVAL as i32)); lindrustfinalize(); } @@ -4088,4 +4065,3 @@ pub mod fs_tests { lindrustfinalize(); } } -} From b39772c6bfb9d0d2b84364f9f9ad96949e84c609 Mon Sep 17 00:00:00 2001 From: lind Date: Thu, 18 Jul 2024 03:39:24 +0000 Subject: [PATCH 10/13] conflicts resolved --- src/safeposix/syscalls/net_calls.rs | 130 ++++++++- src/tests/networking_tests.rs | 409 ++++++++++++++++++++-------- 2 files changed, 405 insertions(+), 134 deletions(-) diff --git a/src/safeposix/syscalls/net_calls.rs b/src/safeposix/syscalls/net_calls.rs index b23fa819c..517f5206a 100644 --- a/src/safeposix/syscalls/net_calls.rs +++ b/src/safeposix/syscalls/net_calls.rs @@ -3475,12 +3475,77 @@ impl Cage { return 0; } + /// ## ------------------POLL SYSCALL------------------ + /// ### Description + /// poll_syscall performs a similar task to select_syscall: it waits for + /// one of a set of file descriptors to become ready to perform I/O. + + /// ### Function Arguments + /// The `poll_syscall()` receives two arguments: + /// * `fds` - The set of file descriptors to be monitored is specified in + /// the fds argument, which is an array of PollStruct structures + /// containing three fields: fd, events and revents. events and revents + /// are requested events and returned events, respectively. The field fd + /// contains a file descriptor for an open file. If this field is + /// negative, then the corresponding events field is ignored and the + /// revents field returns zero. The field events is an input parameter, a + /// bit mask specifying the events the application is interested in for + /// the file descriptor fd. The bits returned in revents can include any + /// of those specified in events, or POLLNVAL. The bits that may be + /// set/returned in events and revents are: 1. POLLIN: There is data to + /// read. 2. POLLPRI: There is some exceptional condition on the file + /// descriptor, currently not supported 3. POLLOUT: Writing is now + /// possible, though a write larger than the available space in a socket + /// or pipe will still block + /// 4. POLLNVAL: Invalid request: fd not open (only returned in revents; + /// ignored in events). + /// * `timeout` - The timeout argument is a RustDuration structure that + /// specifies the interval that poll() should block waiting for a file + /// descriptor to become ready. The call will block until either: 1. a + /// file descriptor becomes ready; 2. the call is interrupted by a signal + /// handler; 3. the timeout expires. + + /// ### Returns + /// On success, poll_syscall returns a nonnegative value which is the + /// number of elements in the pollfds whose revents fields have been + /// set to a nonzero value (indicating an event or an error). A + /// return value of zero indicates that the system call timed out + /// before any file descriptors became ready. + /// + /// ### Errors + /// * EINTR - A signal was caught. + /// * EINVAL - fd exceeds the FD_SET_MAX_FD. + /// + /// ### Panics + /// No panic is expected from this syscall pub fn poll_syscall( &self, fds: &mut [PollStruct], timeout: Option, ) -> i32 { - //timeout is supposed to be in milliseconds + // timeout is supposed to be in milliseconds + + // current implementation of poll_syscall is based on select_syscall + // which gives several issues: + // 1. according to standards, select_syscall should only support file descriptor + // that is smaller than 1024, while poll_syscall should not have such + // limitation but our implementation of poll_syscall is actually calling + // select_syscall directly which would mean poll_syscall would also have the + // 1024 maximum size limitation However, rustposix itself only support file + // descriptor that is smaller than 1024 which solves this issue automatically + // in an interesting way + // 2. current implementation of poll_syscall is very inefficient, that it passes + // each of the file descriptor into select_syscall one by one. A better + // solution might be transforming pollstruct into fdsets and pass into + // select_syscall once (TODO). A even more efficienct way would be completely + // rewriting poll_syscall so it does not depend on select_syscall anymore. + // This is also how Linux does for poll_syscall since Linux claims that poll + // have a better performance than select. + // 3. several revent value such as POLLERR (which should be set when pipe is + // broken), or POLLHUP (when peer closed its channel) are not possible to + // monitor. Since select_syscall does not have these features, so our + // poll_syscall, which derived from select_syscall, would subsequently not be + // able to support these features. let mut return_code: i32 = 0; let start_time = interface::starttimer(); @@ -3490,9 +3555,26 @@ impl Cage { None => interface::RustDuration::MAX, }; + // according to standard, we should clear all revents + for structpoll in &mut *fds { + structpoll.revents = 0; + } + + // we loop until either timeout + // or any of the file descriptor is ready loop { + // iterate through each file descriptor for structpoll in &mut *fds { + // get the file descriptor let fd = structpoll.fd; + + // according to standard, we should ignore all file descriptor + // that is smaller than 0 + if fd < 0 { + continue; + } + + // get the associated events to monitor let events = structpoll.events; // init FdSet structures @@ -3500,24 +3582,25 @@ impl Cage { let writes = &mut interface::FdSet::new(); let errors = &mut interface::FdSet::new(); - //read + // POLLIN for readable fd if events & POLLIN > 0 { reads.set(fd) } - //write + // POLLOUT for writable fd if events & POLLOUT > 0 { writes.set(fd) } - //err - if events & POLLERR > 0 { + // POLLPRI for except fd + if events & POLLPRI > 0 { errors.set(fd) } + // this mask is used for storing final revent result let mut mask: i16 = 0; - //0 essentially sets the timeout to the max value allowed (which is almost - // always more than enough time) NOTE that the nfds argument is - // highest fd + 1 + // here we just call select_syscall with timeout of zero, + // which essentially just check each fd set once then return + // NOTE that the nfds argument is highest fd + 1 let selectret = Self::select_syscall( &self, fd + 1, @@ -3526,23 +3609,44 @@ impl Cage { Some(errors), Some(interface::RustDuration::ZERO), ); + // if there is any file descriptor ready if selectret > 0 { - mask += if !reads.is_empty() { POLLIN } else { 0 }; - mask += if !writes.is_empty() { POLLOUT } else { 0 }; - mask += if !errors.is_empty() { POLLERR } else { 0 }; + // is the file descriptor ready to read? + mask |= if !reads.is_empty() { POLLIN } else { 0 }; + // is the file descriptor ready to write? + mask |= if !writes.is_empty() { POLLOUT } else { 0 }; + // is there any exception conditions on the file descriptor? + mask |= if !errors.is_empty() { POLLPRI } else { 0 }; + // this file descriptor is ready for something, + // increment the return value return_code += 1; } else if selectret < 0 { - return selectret; + // if there is any error, first check if the error + // is EBADF, which refers to invalid file descriptor error + // in this case, we should set POLLNVAL to revent + if selectret == -(Errno::EBADF as i32) { + mask |= POLLNVAL; + // according to standard, return value is the number of fds + // with non-zero revent, which may indicate an error as well + return_code += 1; + } else { + return selectret; + } } + // set the revents structpoll.revents = mask; } + // we break if there is any file descriptor ready + // or timeout is reached if return_code != 0 || interface::readtimer(start_time) > end_time { break; } else { + // otherwise, check for signal and loop again if interface::sigcheck() { return syscall_error(Errno::EINTR, "poll", "interrupted function call"); } + // We yield to let other threads continue if we've found no ready descriptors interface::lind_yield(); } } @@ -4161,4 +4265,4 @@ impl Cage { return syscall_error(Errno::EOPNOTSUPP, "getifaddrs", "invalid ifaddrs length"); } } -} +} \ No newline at end of file diff --git a/src/tests/networking_tests.rs b/src/tests/networking_tests.rs index 1c0cf0c18..31cde9133 100644 --- a/src/tests/networking_tests.rs +++ b/src/tests/networking_tests.rs @@ -1081,18 +1081,43 @@ pub mod net_tests { #[test] #[ignore] pub fn ut_lind_net_poll() { - //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // test for poll monitoring on multiple different file descriptors: + // 1. regular file + // 2. AF_INET server socket waiting for two clients + // 3. AF_INET server socket's connection file descriptor with clients + // 4. AF_UNIX server socket's connection file descriptor with a client + // 5. pipe + + // 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); + // creating regular file's file descriptor let filefd = cage.open_syscall("/netpolltest.txt", O_CREAT | O_EXCL | O_RDWR, S_IRWXA); assert!(filefd > 0); + // creating socket file descriptors let serversockfd = cage.socket_syscall(AF_INET, SOCK_STREAM, 0); let clientsockfd1 = cage.socket_syscall(AF_INET, SOCK_STREAM, 0); let clientsockfd2 = cage.socket_syscall(AF_INET, SOCK_STREAM, 0); + let serversockfd_unix = cage.socket_syscall(AF_UNIX, SOCK_STREAM, 0); + let clientsockfd_unix = cage.socket_syscall(AF_UNIX, SOCK_STREAM, 0); + + assert!(serversockfd > 0); + assert!(clientsockfd1 > 0); + assert!(clientsockfd2 > 0); + assert!(serversockfd_unix > 0); + assert!(clientsockfd_unix > 0); + + // creating a pipe + let mut pipefds = PipeArray { + readfd: -1, + writefd: -1, + }; + assert_eq!(cage.pipe_syscall(&mut pipefds), 0); + + // create a INET address let port: u16 = generate_random_port(); let sockaddr = interface::SockaddrV4 { @@ -1104,177 +1129,319 @@ pub mod net_tests { padding: 0, }; let socket = interface::GenSockaddr::V4(sockaddr); //127.0.0.1 from bytes above + + //binding to a socket + let serversockaddr_unix = + interface::new_sockaddr_unix(AF_UNIX as u16, "server_poll".as_bytes()); + let serversocket_unix = interface::GenSockaddr::Unix(serversockaddr_unix); + + let clientsockaddr_unix = + interface::new_sockaddr_unix(AF_UNIX as u16, "client_poll".as_bytes()); + let clientsocket_unix = interface::GenSockaddr::Unix(clientsockaddr_unix); + + assert_eq!(cage.bind_syscall(serversockfd_unix, &serversocket_unix), 0); + assert_eq!(cage.bind_syscall(clientsockfd_unix, &clientsocket_unix), 0); + assert_eq!(cage.listen_syscall(serversockfd_unix, 1), 0); + assert_eq!(cage.bind_syscall(serversockfd, &socket), 0); assert_eq!(cage.listen_syscall(serversockfd, 4), 0); + // create a PollStruct for each fd let serverpoll = interface::PollStruct { fd: serversockfd, events: POLLIN, revents: 0, }; + + let serverunixpoll = interface::PollStruct { + fd: serversockfd_unix, + events: POLLIN, + revents: 0, + }; + let filepoll = interface::PollStruct { fd: filefd, + events: POLLIN | POLLOUT, + revents: 0, + }; + + let pipepoll = interface::PollStruct { + fd: pipefds.readfd, events: POLLIN, revents: 0, }; - let mut polled = vec![serverpoll, filepoll]; - cage.fork_syscall(2); - //client 1 connects to the server to send and recv data... - let thread1 = interface::helper_thread(move || { - interface::sleep(interface::RustDuration::from_millis(30)); + let mut polled = vec![filepoll, serverpoll, serverunixpoll, pipepoll]; + + assert_eq!(cage.fork_syscall(2), 0); // used for AF_INET thread client 1 + assert_eq!(cage.fork_syscall(3), 0); // used for AF_INET thread client 2 + assert_eq!(cage.fork_syscall(4), 0); // used for AF_UNIX thread client + + assert_eq!(cage.fork_syscall(5), 0); // used for pipe thread + + assert_eq!(cage.close_syscall(clientsockfd1), 0); + assert_eq!(cage.close_syscall(clientsockfd2), 0); + assert_eq!(cage.close_syscall(clientsockfd_unix), 0); + + // this barrier have to ensure that the clients finish the connect before we do + // the poll due to an unfixed bug (`close` could block when other + // thread/cage is `accept`) + let barrier = Arc::new(Barrier::new(3)); + let barrier_clone1 = barrier.clone(); + let barrier_clone2 = barrier.clone(); + + // this barrier is used for control the flow the pipe + let barrier_pipe = Arc::new(Barrier::new(2)); + let barrier_pipe_clone = barrier_pipe.clone(); + + // due to an unfixed bug in ref counter of AF_UNIX socket pipe + // have to make sure all the threads exits only after the AF_UNIX test finished + let barrier_exit = Arc::new(Barrier::new(4)); + let barrier_exit_clone1 = barrier_exit.clone(); + let barrier_exit_clone2 = barrier_exit.clone(); + let barrier_exit_clone3 = barrier_exit.clone(); + + // client 1 connects to the server to send and recv data + let threadclient1 = interface::helper_thread(move || { let cage2 = interface::cagetable_getref(2); + assert_eq!(cage2.close_syscall(serversockfd), 0); + assert_eq!(cage2.close_syscall(clientsockfd2), 0); + // connect to server assert_eq!(cage2.connect_syscall(clientsockfd1, &socket), 0); - assert_eq!( - cage2.send_syscall(clientsockfd1, str2cbuf(&"test"), 4, 0), - 4 - ); - //giving it a longer pause time to that it can process all of the data that it - // is recieving - interface::sleep(interface::RustDuration::from_millis(100)); + barrier_clone1.wait(); - assert_eq!(cage2.close_syscall(serversockfd), 0); + // send message to server + assert_eq!(cage2.send_syscall(clientsockfd1, str2cbuf("test"), 4, 0), 4); + + interface::sleep(interface::RustDuration::from_millis(1)); + + // receive message from server + let mut buf = sizecbuf(4); + assert_eq!(cage2.recv_syscall(clientsockfd1, buf.as_mut_ptr(), 4, 0), 4); + assert_eq!(cbuf2str(&buf), "test"); + + assert_eq!(cage2.close_syscall(clientsockfd1), 0); + barrier_exit_clone1.wait(); cage2.exit_syscall(EXIT_SUCCESS); }); - cage.fork_syscall(3); - //client 2 connects to the server to send and recv data... - let thread2 = interface::helper_thread(move || { - //give it a longer time so that it can sufficiently process all of the data - interface::sleep(interface::RustDuration::from_millis(45)); + // client 2 connects to the server to send and recv data + let threadclient2 = interface::helper_thread(move || { let cage3 = interface::cagetable_getref(3); + assert_eq!(cage3.close_syscall(serversockfd), 0); + assert_eq!(cage3.close_syscall(clientsockfd1), 0); + // connect to server assert_eq!(cage3.connect_syscall(clientsockfd2, &socket), 0); + barrier_clone2.wait(); + + // send message to server + assert_eq!(cage3.send_syscall(clientsockfd2, str2cbuf("test"), 4, 0), 4); + + interface::sleep(interface::RustDuration::from_millis(1)); + + // receive message from server + let mut buf = sizecbuf(4); + let mut result: i32; + loop { + result = cage3.recv_syscall(clientsockfd2, buf.as_mut_ptr(), 4, 0); + if result != -libc::EINTR { + break; // if the error was EINTR, retry the syscall + } + } + assert_eq!(result, 4); + assert_eq!(cbuf2str(&buf), "test"); + + assert_eq!(cage3.close_syscall(clientsockfd2), 0); + barrier_exit_clone2.wait(); + cage3.exit_syscall(EXIT_SUCCESS); + }); + + let threadclient_unix = interface::helper_thread(move || { + let cage4 = interface::cagetable_getref(4); + assert_eq!(cage4.close_syscall(serversockfd_unix), 0); + assert_eq!(cage4.close_syscall(serversockfd), 0); + + // connect to server assert_eq!( - cage3.send_syscall(clientsockfd2, str2cbuf(&"test"), 4, 0), + cage4.connect_syscall(clientsockfd_unix, &serversocket_unix), + 0 + ); + + // send message to server + assert_eq!( + cage4.send_syscall(clientsockfd_unix, str2cbuf("test"), 4, 0), 4 ); - interface::sleep(interface::RustDuration::from_millis(100)); + interface::sleep(interface::RustDuration::from_millis(1)); - assert_eq!(cage3.close_syscall(serversockfd), 0); - cage3.exit_syscall(EXIT_SUCCESS); + // recieve message from server + let mut buf = sizecbuf(4); + let mut result: i32; + loop { + result = cage4.recv_syscall(clientsockfd_unix, buf.as_mut_ptr(), 4, 0); + if result != -libc::EINTR { + break; // if the error was EINTR, retry the syscall + } + } + assert_eq!(result, 4); + assert_eq!(cbuf2str(&buf), "test"); + + assert_eq!(cage4.close_syscall(clientsockfd_unix), 0); + cage4.exit_syscall(EXIT_SUCCESS); }); - //acting as the server and processing the request - let thread3 = interface::helper_thread(move || { - let mut infds: Vec; - let mut outfds: Vec; - for _counter in 0..600 { - //start a while true loop for processing requests - let pollretvalue = cage.poll_syscall( - &mut polled.as_mut_slice(), - Some(interface::RustDuration::ZERO), - ); - assert!(pollretvalue >= 0); + let thread_pipe = interface::helper_thread(move || { + let cage5 = interface::cagetable_getref(5); + + interface::sleep(interface::RustDuration::from_millis(1)); + // send message to pipe + assert_eq!(cage5.write_syscall(pipefds.writefd, str2cbuf("test"), 4), 4); + + let mut buf = sizecbuf(5); + // wait until peer read the message + barrier_pipe_clone.wait(); + + // read the message sent by peer + assert_eq!(cage5.read_syscall(pipefds.readfd, buf.as_mut_ptr(), 5), 5); + assert_eq!(cbuf2str(&buf), "test2"); + + barrier_exit_clone3.wait(); + cage5.exit_syscall(EXIT_SUCCESS); + }); + + barrier.wait(); + // acting as the server and processing the request + // Server loop to handle connections and I/O + // Check for any activity in any of the Input sockets + for counter in 0..600 { + let poll_result = cage.poll_syscall(&mut polled.as_mut_slice(), None); + assert!(poll_result >= 0); // check for error + + // clearfds stores the fds that should be removed from polled at the end of the + // iteration + let mut clearfds = vec![]; + // addfds stores the fds that should be added to polled at the end of the + // iteration + let mut addfds = vec![]; - infds = vec![]; - outfds = vec![]; + // check for readfds + for poll in &mut polled { + // If the socket returned was listerner socket, then there's a new conn., so we + // accept it, and put the client socket in the list of Inputs. + if poll.fd == serversockfd { + if poll.revents & POLLIN != 0 { + let mut sockgarbage = + interface::GenSockaddr::V4(interface::SockaddrV4::default()); + let sockfd = cage.accept_syscall(poll.fd as i32, &mut sockgarbage); + assert!(sockfd > 0); + // new connection is estalished, add it to readfds and writefds - for polledfile in &mut polled { - if polledfile.revents & POLLIN != 0 { - infds.push(polledfile.fd); + addfds.push(interface::PollStruct { + fd: sockfd, + events: POLLIN | POLLOUT, + revents: 0, + }); } - if polledfile.revents & POLLOUT != 0 { - outfds.push(polledfile.fd); + } else if poll.fd == filefd { + // poll on regular file should always success + // therefore revents should be set for filefd at the first iteration + assert_eq!(counter, 0); + assert_eq!(poll.revents, POLLIN | POLLOUT); + // remove file fd from poll + clearfds.push(filefd); + } else if poll.fd == serversockfd_unix { + if poll.revents & POLLIN != 0 { + // unix socket + let mut sockgarbage = interface::GenSockaddr::Unix( + interface::new_sockaddr_unix(AF_UNIX as u16, "".as_bytes()), + ); + let sockfd = cage.accept_syscall(poll.fd as i32, &mut sockgarbage); + assert!(sockfd > 0); + // new connection is estalished, add it to poll + addfds.push(interface::PollStruct { + fd: sockfd, + events: POLLIN | POLLOUT, + revents: 0, + }); } - } + } else if poll.fd == pipefds.readfd { + if poll.revents & POLLIN != 0 { + // pipe + let mut buf = sizecbuf(4); + // read the message from peer + assert_eq!(cage.read_syscall(pipefds.readfd, buf.as_mut_ptr(), 4), 4); + assert_eq!(cbuf2str(&buf), "test"); - //check for any activity in the input sockets - for sockfd in infds { - //If the socket returned was listerner socket, then there's a new connection - //so we accept it, and put the client socket in the list of inputs. - if sockfd == serversockfd { - let port: u16 = generate_random_port(); - let sockaddr = interface::SockaddrV4 { - sin_family: AF_INET as u16, - sin_port: port.to_be(), - sin_addr: interface::V4Addr { - s_addr: u32::from_ne_bytes([127, 0, 0, 1]), - }, - padding: 0, - }; - let mut addr = interface::GenSockaddr::V4(sockaddr); //127.0.0.1 from bytes above + // write the message from peer + assert_eq!( + cage.write_syscall(pipefds.writefd, str2cbuf("test2"), 5) as usize, + 5 + ); + barrier_pipe.wait(); - let newsockfd = cage.accept_syscall(sockfd, &mut addr); - polled.push(interface::PollStruct { - fd: newsockfd, - events: POLLIN, - revents: 0, - }) - } else if sockfd == filefd { - //Write to a file... - assert_eq!(cage.write_syscall(sockfd, str2cbuf("test"), 4), 4); - assert_eq!(cage.lseek_syscall(sockfd, 0, SEEK_SET), 0); - //Once the write is successful into a file, modify the file descriptor so - // that its ready for reading out of the file. - for polledfile in &mut polled { - if polledfile.fd == sockfd { - polledfile.events = POLLOUT; - break; - } - } - } else { + // pipe poll test done + clearfds.push(pipefds.readfd); + } + } else { + if poll.revents & POLLIN != 0 { //If the socket is in established conn., then we recv the data. If there's // no data, then close the client socket. let mut buf = sizecbuf(4); - let mut result: i32; + let mut recvresult: i32; loop { - result = cage.recv_syscall(sockfd, buf.as_mut_ptr(), 4, 0); - if result != -libc::EINTR { - assert_eq!(result & !4, 0); //This must be 0 or 4 to be correct, either the socket is good for - // recieving or it's closed + // receive message from peer + recvresult = cage.recv_syscall(poll.fd as i32, buf.as_mut_ptr(), 4, 0); + if recvresult != -libc::EINTR { break; // if the error was EINTR, retry the // syscall } } - if result == 4 { - assert_eq!(cbuf2str(&buf), "test"); - //This socket is ready for writing, modify the socket descriptor to be - // in read-write mode. This socket can write data out to network - for polledfile in &mut polled { - if polledfile.fd == sockfd { - polledfile.events = POLLOUT; - break; - } + if recvresult == 4 { + if cbuf2str(&buf) == "test" { + continue; } - } else { - //No data means remote socket closed, hence close the client socket in - // server, also remove this socket from polling. - assert_eq!(cage.close_syscall(sockfd), 0); - polled.retain(|x| x.fd != sockfd); + } else if recvresult == -libc::ECONNRESET { + // peer closed the connection + println!("Connection reset by peer on socket {}", poll.fd); + assert_eq!(cage.close_syscall(poll.fd as i32), 0); + clearfds.push(poll.fd); } } - } - - for sockfd in outfds { - if sockfd == filefd { - let mut read_buf1 = sizecbuf(4); - assert_eq!(cage.read_syscall(sockfd, read_buf1.as_mut_ptr(), 4), 4); - assert_eq!(cbuf2str(&read_buf1), "test"); - //test for file finished, remove from polling. - polled.retain(|x| x.fd != sockfd); - } else { - //Data is sent out of this socket, it's no longer ready for writing, modify - // it only read mode. - assert_eq!(cage.send_syscall(sockfd, str2cbuf(&"test"), 4, 0), 4); - for polledfile in &mut polled { - if polledfile.fd == sockfd { - polledfile.events = POLLIN; - } - } + if poll.revents & POLLOUT != 0 { + // Data is sent out this socket, it's no longer ready for writing + // clear the POLLOUT from events + assert_eq!(cage.send_syscall(poll.fd as i32, str2cbuf("test"), 4, 0), 4); + poll.events &= !POLLOUT; } } } - assert_eq!(cage.close_syscall(serversockfd), 0); - assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); - }); + // clear fds + polled.retain(|x| { + for fd in &clearfds { + if *fd == x.fd { + return false; + } + } + return true; + }); + // add new fds + polled.extend(addfds); + } + assert_eq!(cage.close_syscall(serversockfd), 0); + assert_eq!(cage.close_syscall(serversockfd_unix), 0); - thread1.join().unwrap(); - thread2.join().unwrap(); - thread3.join().unwrap(); + // let threads exit + barrier_exit.wait(); + threadclient1.join().unwrap(); + threadclient2.join().unwrap(); + threadclient_unix.join().unwrap(); + thread_pipe.join().unwrap(); + + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); lindrustfinalize(); } @@ -4526,4 +4693,4 @@ pub mod net_tests { assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); lindrustfinalize(); } -} +} \ No newline at end of file From 2600c8fbeef307ee4f66955b7e5c5b2fd42f57b0 Mon Sep 17 00:00:00 2001 From: lind Date: Thu, 18 Jul 2024 19:59:31 +0000 Subject: [PATCH 11/13] comments addressed --- src/safeposix/syscalls/fs_calls.rs | 13 +++++++++---- src/tests/fs_tests.rs | 3 +-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/safeposix/syscalls/fs_calls.rs b/src/safeposix/syscalls/fs_calls.rs index 76c5de730..b0ba36e7b 100644 --- a/src/safeposix/syscalls/fs_calls.rs +++ b/src/safeposix/syscalls/fs_calls.rs @@ -4982,9 +4982,11 @@ impl Cage { /// ### Description /// /// `shmget_syscall` returns the shared memory segment identifier associated with a particular `key` - /// If a key doesn't exist or equals `IPC_PRIVATE`, shmget creates a new - /// memory segment and attaches it to the key, however we only create a new memory segment if the key - /// doesn't exist, and return an error if key equals `IPC_PRIVATE` + /// If a key doesn't exist, shmget creates a new memory segment and attaches it to the key. + /// Traditionally if the value of the key equals `IPC_PRIVATE`, we also create a new memory segment which + /// is not associated with a key during this syscall, + /// but for our implementaion, we return an error and only create a new memory + /// segment when the IPC_CREAT flag is specified in the`shmflag` argument. /// /// ### Returns /// @@ -4995,6 +4997,10 @@ impl Cage { /// `key` : An i32 value that references a memory segment /// `size` : Size of the memory segment to be created if key doesn't exist /// `shmflag` : mode flags which indicate whether to create a new key or not + /// The `shmflag` is composed of the following + /// * IPC_CREAT - specify that the system call creates a new segment + /// * IPC_EXCL - this flag is used with IPC_CREAT to cause this function to fail when IPC_CREAT is also used + /// and the key passed has a memory segment associated with it. /// /// ### Errors /// @@ -5045,7 +5051,6 @@ impl Cage { } // If memory segment doesn't exist and IPC_CREAT was specified - we create a new memory segment - // Check if the size passed is a valid value if (size as u32) < SHMMIN || (size as u32) > SHMMAX { return syscall_error( diff --git a/src/tests/fs_tests.rs b/src/tests/fs_tests.rs index f0247f35c..417826208 100644 --- a/src/tests/fs_tests.rs +++ b/src/tests/fs_tests.rs @@ -4049,8 +4049,7 @@ pub mod fs_tests { assert_eq!(shmid,4); // Check error upon asking for a valid key and passing the IPC_CREAT or IPC_EXCL flag - assert_eq!(cage.shmget_syscall(key, 1024, IPC_CREAT),-(Errno::EEXIST as i32 )); - // assert_eq!(cage.shmget_syscall(key, 1024, IPC_EXCL),-(Errno::EEXIST as i32)); + assert_eq!(cage.shmget_syscall(key, 1024, IPC_CREAT | IPC_EXCL),-(Errno::EEXIST as i32 )); // Check if the function returns a correct shmid upon asking with a key that we know exists assert_eq!(cage.shmget_syscall(key, 1024,0666),shmid); From 4d6c1a3e1b64c90d6df4d4476079c66771221596 Mon Sep 17 00:00:00 2001 From: lind Date: Thu, 18 Jul 2024 23:49:13 +0000 Subject: [PATCH 12/13] tests added --- src/tests/fs_tests.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/tests/fs_tests.rs b/src/tests/fs_tests.rs index 417826208..193bcf0a6 100644 --- a/src/tests/fs_tests.rs +++ b/src/tests/fs_tests.rs @@ -4048,9 +4048,12 @@ pub mod fs_tests { let shmid = cage.shmget_syscall(33123, 1024, IPC_CREAT); assert_eq!(shmid,4); - // Check error upon asking for a valid key and passing the IPC_CREAT or IPC_EXCL flag + // Check error upon asking for a valid key and passing the IPC_CREAT and IPC_EXCL flag assert_eq!(cage.shmget_syscall(key, 1024, IPC_CREAT | IPC_EXCL),-(Errno::EEXIST as i32 )); + // Check error when passing IPC_CREAT flag as the key + assert_eq!(cage.shmget_syscall(IPC_PRIVATE,1024,IPC_PRIVATE),-(Errno::ENOENT as i32)); + // Check if the function returns a correct shmid upon asking with a key that we know exists assert_eq!(cage.shmget_syscall(key, 1024,0666),shmid); From b3a15dd91cc2bc673806eb605468a45a8811709c Mon Sep 17 00:00:00 2001 From: lind Date: Mon, 22 Jul 2024 20:58:03 +0000 Subject: [PATCH 13/13] fixed newline --- src/safeposix/syscalls/net_calls.rs | 2 +- src/tests/networking_tests.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/safeposix/syscalls/net_calls.rs b/src/safeposix/syscalls/net_calls.rs index 517f5206a..22ebf9af8 100644 --- a/src/safeposix/syscalls/net_calls.rs +++ b/src/safeposix/syscalls/net_calls.rs @@ -4265,4 +4265,4 @@ impl Cage { return syscall_error(Errno::EOPNOTSUPP, "getifaddrs", "invalid ifaddrs length"); } } -} \ No newline at end of file +} diff --git a/src/tests/networking_tests.rs b/src/tests/networking_tests.rs index 31cde9133..da94213b3 100644 --- a/src/tests/networking_tests.rs +++ b/src/tests/networking_tests.rs @@ -4693,4 +4693,4 @@ pub mod net_tests { assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); lindrustfinalize(); } -} \ No newline at end of file +}