diff --git a/src/RawPOSIX/Cargo.toml b/src/RawPOSIX/Cargo.toml index 5682de9c..2c0d6544 100644 --- a/src/RawPOSIX/Cargo.toml +++ b/src/RawPOSIX/Cargo.toml @@ -24,6 +24,8 @@ ringbuf = "0.2.6" dashmap = { version = "5.1", features=["serde"] } parking_lot = "0.12" bit-set = "0.5" +nodit = "0.9.2" # Used for VMMAP +quick_cache = "0.6.9" [dependencies.lazy_static] version = "1.0" diff --git a/src/RawPOSIX/src/constants/fs_constants.rs b/src/RawPOSIX/src/constants/fs_constants.rs new file mode 100644 index 00000000..0d73c85f --- /dev/null +++ b/src/RawPOSIX/src/constants/fs_constants.rs @@ -0,0 +1,192 @@ +//! File System Constants Module +//! These constants define file system-related flags and parameters +//! +//! Primary Source References: +//! - Linux kernel v6.5: include/uapi/asm-generic/fcntl.h +//! - Linux kernel v6.5: include/uapi/linux/stat.h +//! - POSIX.1-2017 (IEEE Std 1003.1-2017) + +#![allow(dead_code)] +#![allow(unused_variables)] + +// ===== Standard File Descriptors ===== +pub const STDIN_FILENO: i32 = 0; // File descriptor for standard input +pub const STDOUT_FILENO: i32 = 1; // File descriptor for standard output +pub const STDERR_FILENO: i32 = 2; // File descriptor for standard error + +// ===== File Descriptor Constants ===== +pub const DT_UNKNOWN: u8 = 0; + +pub const STARTINGFD: i32 = 0; // Starting file descriptor number +pub const MAXFD: i32 = 1024; // Maximum number of file descriptors +pub const STARTINGPIPE: i32 = 0; // Starting pipe descriptor number +pub const MAXPIPE: i32 = 1024; // Maximum number of pipes + +// ===== Inode Constants ===== +pub const ROOTDIRECTORYINODE: usize = 1; // Root directory inode number +pub const STREAMINODE: usize = 2; // Stream inode number + +// ===== Pipe Constants ===== +pub const PIPE_CAPACITY: usize = 65536; // Maximum pipe buffer size + +// ===== File Access Permission Flags ===== +pub const F_OK: u32 = 0; // Test for existence +pub const X_OK: u32 = 1; // Test for execute permission +pub const W_OK: u32 = 2; // Test for write permission +pub const R_OK: u32 = 4; // Test for read permission + +// ===== File Access Modes ===== +// Source: include/uapi/asm-generic/fcntl.h +pub const O_RDONLY: i32 = 0o0; // Open read-only +pub const O_WRONLY: i32 = 0o1; // Open write-only +pub const O_RDWR: i32 = 0o2; // Open read-write +pub const O_RDWRFLAGS: i32 = 0o3; // Mask for access modes + +// ===== File Creation and Status Flags ===== +pub const O_CREAT: i32 = 0o100; // Create file if it doesn't exist +pub const O_EXCL: i32 = 0o200; // Error if O_CREAT and file exists +pub const O_NOCTTY: i32 = 0o400; // Don't assign controlling terminal +pub const O_TRUNC: i32 = 0o1000; // Truncate file to zero length +pub const O_APPEND: i32 = 0o2000; // Append mode - writes always at end +pub const O_NONBLOCK: i32 = 0o4000; // Non-blocking mode +pub const O_SYNC: i32 = 0o10000; // Synchronous writes +pub const O_ASYNC: i32 = 0o20000; // Signal-driven I/O +pub const O_CLOEXEC: i32 = 0o2000000; // Close on exec + +pub const DEFAULTTIME: u64 = 1323630836; // Default timestamp value + +// ===== File Permissions ===== +// Source: include/uapi/linux/stat.h +pub const S_IRWXA: u32 = 0o777; // All permissions for all users +pub const S_IRWXU: u32 = 0o700; // User read, write, execute +pub const S_IRUSR: u32 = 0o400; // User read +pub const S_IWUSR: u32 = 0o200; // User write +pub const S_IXUSR: u32 = 0o100; // User execute +pub const S_IRWXG: u32 = 0o070; // Group read, write, execute +pub const S_IRGRP: u32 = 0o040; // Group read +pub const S_IWGRP: u32 = 0o020; // Group write +pub const S_IXGRP: u32 = 0o010; // Group execute +pub const S_IRWXO: u32 = 0o007; // Others read, write, execute +pub const S_IROTH: u32 = 0o004; // Others read +pub const S_IWOTH: u32 = 0o002; // Others write +pub const S_IXOTH: u32 = 0o001; // Others execute + +//Commands for FCNTL +pub const F_DUPFD: i32 = 0; +pub const F_GETFD: i32 = 1; +pub const F_SETFD: i32 = 2; +pub const F_GETFL: i32 = 3; +pub const F_SETFL: i32 = 4; +pub const F_GETLK: i32 = 5; +pub const F_GETLK64: i32 = 5; +pub const F_SETLK: i32 = 6; +pub const F_SETLK64: i32 = 6; +pub const F_SETLKW: i32 = 7; +pub const F_SETLKW64: i32 = 7; +pub const F_SETOWN: i32 = 8; +pub const F_GETOWN: i32 = 9; +pub const F_SETSIG: i32 = 10; +pub const F_GETSIG: i32 = 11; +pub const F_SETLEASE: i32 = 1024; +pub const F_GETLEASE: i32 = 1025; +pub const F_NOTIFY: i32 = 1026; + +//Commands for IOCTL +pub const FIONBIO: u32 = 21537; +pub const FIOASYNC: u32 = 21586; + +//File types for open/stat etc. +pub const S_IFBLK: i32 = 0o60000; +pub const S_IFCHR: i32 = 0o20000; +pub const S_IFDIR: i32 = 0o40000; +pub const S_IFIFO: i32 = 0o10000; +pub const S_IFLNK: i32 = 0o120000; +pub const S_IFREG: i32 = 0o100000; +pub const S_IFSOCK: i32 = 0o140000; +pub const S_FILETYPEFLAGS: i32 = 0o170000; + +//for flock syscall +pub const LOCK_SH: i32 = 1; +pub const LOCK_EX: i32 = 2; +pub const LOCK_UN: i32 = 8; +pub const LOCK_NB: i32 = 4; +//for mmap/munmap syscall +pub const MAP_FIXED: u32 = 16; +pub const MAP_ANONYMOUS: u32 = 32; +pub const MAP_HUGE_SHIFT: i32 = 26; +pub const MAP_HUGETLB: i32 = 262144; + + +pub const SEEK_SET: i32 = 0; // Seek from beginning of file +pub const SEEK_CUR: i32 = 1; // Seek from current position +pub const SEEK_END: i32 = 2; // Seek from end of file + +pub const IPC_PRIVATE: i32 = 0o0; +pub const IPC_CREAT: i32 = 0o1000; +pub const IPC_EXCL: i32 = 0o2000; + +pub const IPC_RMID: i32 = 0; +pub const IPC_SET: i32 = 1; +pub const IPC_STAT: i32 = 2; + +pub const SHM_DEST: i32 = 0o1000; // Destroy segment when last process detaches +pub const SHM_LOCKED: i32 = 0o2000; // Lock segment in memory +pub const SHM_HUGETLB: i32 = 0o4000; // Use huge TLB pages + +pub const SHM_R: i32 = 0o400; // Read permission +pub const SHM_W: i32 = 0o200; // Write permission +pub const SHM_RDONLY: i32 = 0o10000; // Read-only access +pub const SHM_RND: i32 = 0o20000; // Round attach address to SHMLBA +pub const SHM_REMAP: i32 = 0o40000; // Take-over region on attach +pub const SHM_EXEC: i32 = 0o100000; // Execute permission + +pub const SHMMIN: u32 = 1; // Minimum shared memory segment size +pub const SHMMNI: u32 = 4096; // Maximum number of segments system wide +pub const SHMMAX: u32 = 4278190079; // Maximum shared memory segment size +pub const SHMALL: u32 = 4278190079; // Maximum total shared memory system wide +pub const SHMSEG: u32 = SHMMNI; // Maximum segments per process + +pub const SEM_VALUE_MAX: u32 = 2147483647; // Maximum value for a semaphore + +// ===== Memory Protection Flags ===== +// Source: include/uapi/asm-generic/mman-common.h +pub const PROT_NONE: i32 = 0x0; // Page cannot be accessed +pub const PROT_READ: i32 = 0x1; // Page can be read +pub const PROT_WRITE: i32 = 0x2; // Page can be written +pub const PROT_EXEC: i32 = 0x4; // Page can be executed + +// Mask for all protection bits +// Note: Some architectures may support additional bits +pub const PROT_MASK: u32 = 0x7; + +// ===== Memory Mapping Flags ===== +// Source: include/uapi/asm-generic/mman.h +pub const MAP_SHARED: u32 = 0x01; // Share changes with other processes +pub const MAP_PRIVATE: u32 = 0x02; // Changes are private to this process +pub const MAP_SHARING_MASK: u32 = 0x03; // Mask to isolate sharing bits + +pub const MAP_ANON: u32 = 0x20; // Don't use a file descriptor + +// ===== Page Size Constants ===== +// Note: These values are architecture-dependent +// Current values are for x86_64 Linux +pub const PAGESHIFT: u32 = 12; // 4KB pages (1 << 12 = 4096) +pub const PAGESIZE: u32 = 1 << PAGESHIFT; + +// Lind-specific page size constants +pub const MAP_PAGESHIFT: u32 = 16; // Custom value for Lind +pub const MAP_PAGESIZE: u32 = 1 << MAP_PAGESHIFT; + +// ===== Memory Mapping Error Value ===== +// Source: include/uapi/asm-generic/mman-common.h +pub const MAP_FAILED: *mut std::ffi::c_void = (-1isize) as *mut std::ffi::c_void; + +// ===== Memory Remapping Flags ===== +// Source: include/uapi/asm-generic/mman-common.h +pub const MREMAP_MAYMOVE: u32 = 0x01; // Can relocate mapping +pub const MREMAP_FIXED: u32 = 0x02; // New address is specified exactly + +// ===== File Access Modes ===== +// Source: include/uapi/asm-generic/fcntl.h +// NOTE: These should probably be moved to fs_constants.rs +pub const O_ACCMODE: i32 = 0o003; // Mask for file access modes diff --git a/src/RawPOSIX/src/constants/mod.rs b/src/RawPOSIX/src/constants/mod.rs new file mode 100644 index 00000000..265316cf --- /dev/null +++ b/src/RawPOSIX/src/constants/mod.rs @@ -0,0 +1,7 @@ +pub mod fs_constants; +pub mod net_constants; +pub mod sys_constants; + +pub use fs_constants::*; +pub use net_constants::*; +pub use sys_constants::*; diff --git a/src/RawPOSIX/src/constants/net_constants.rs b/src/RawPOSIX/src/constants/net_constants.rs new file mode 100644 index 00000000..55480d0e --- /dev/null +++ b/src/RawPOSIX/src/constants/net_constants.rs @@ -0,0 +1,359 @@ +//! Network Constants Module +//! These constants define network-related flags and parameters +//! +//! Primary Source References: +//! - Linux kernel v6.5: include/uapi/linux/socket.h +//! - Linux kernel v6.5: include/uapi/linux/in.h +//! - Linux kernel v6.5: include/uapi/linux/tcp.h +//! - Linux kernel v6.5: include/uapi/linux/poll.h +//! - Linux kernel v6.5: include/uapi/linux/eventpoll.h +//! - POSIX.1-2017 (IEEE Std 1003.1-2017) + +#![allow(dead_code)] +#![allow(non_upper_case_globals)] + +use crate::interface; + +// ===== Lind-specific Configuration ===== +pub const DEFAULT_HOSTNAME: &str = "Lind"; +pub const BLOCK_TIME: interface::RustDuration = interface::RustDuration::from_micros(100); + +// ===== Socket Types ===== +// Source: include/linux/net.h +pub const SOCK_STREAM: i32 = 1; // Stream (connection) socket +pub const SOCK_DGRAM: i32 = 2; // Datagram (connectionless) socket +pub const SOCK_RAW: i32 = 3; // Raw protocol interface +pub const SOCK_RDM: i32 = 4; // Reliably-delivered message +pub const SOCK_SEQPACKET: i32 = 5; // Sequential packet socket +pub const SOCK_CLOEXEC: i32 = 0o02000000; // Set close-on-exec +pub const SOCK_NONBLOCK: i32 = 0o00004000; // Set non-blocking mode + +// ===== Address Families ===== +// Source: include/linux/socket.h +pub const AF_UNSPEC: i32 = 0; // Unspecified +pub const AF_UNIX: i32 = 1; // Unix domain sockets +pub const AF_LOCAL: i32 = 1; // POSIX name for AF_UNIX +pub const AF_INET: i32 = 2; // Internet IP Protocol +pub const AF_AX25: i32 = 3; // Amateur Radio AX.25 +pub const AF_IPX: i32 = 4; // Novell IPX +pub const AF_APPLETALK: i32 = 5; // AppleTalk DDP +pub const AF_NETROM: i32 = 6; // Amateur Radio NET/ROM +pub const AF_BRIDGE: i32 = 7; // Multiprotocol bridge +pub const AF_ATMPVC: i32 = 8; // ATM PVCs +pub const AF_X25: i32 = 9; // Reserved for X.25 project +pub const AF_INET6: i32 = 10; // IP version 6 +pub const AF_ROSE: i32 = 11; // Amateur Radio X.25 PLP +pub const AF_DECnet: i32 = 12; // Reserved for DECnet project +pub const AF_NETBEUI: i32 = 13; // Reserved for 802.2LLC project +pub const AF_SECURITY: i32 = 14; // Security callback pseudo AF +pub const AF_KEY: i32 = 15; // PF_KEY key management API +pub const AF_NETLINK: i32 = 16; // Netlink +pub const AF_ROUTE: i32 = AF_NETLINK; // Alias to emulate 4.4BSD +pub const AF_PACKET: i32 = 17; // Packet family +pub const AF_ASH: i32 = 18; // Ash +pub const AF_ECONET: i32 = 19; // Acorn Econet +pub const AF_ATMSVC: i32 = 20; // ATM SVCs +pub const AF_RDS: i32 = 21; // RDS sockets +pub const AF_SNA: i32 = 22; // Linux SNA Project +pub const AF_IRDA: i32 = 23; // IRDA sockets +pub const AF_PPPOX: i32 = 24; // PPPoX sockets +pub const AF_WANPIPE: i32 = 25; // Wanpipe API Sockets +pub const AF_LLC: i32 = 26; // Linux LLC +pub const AF_IB: i32 = 27; // Native InfiniBand address +pub const AF_MPLS: i32 = 28; // MPLS +pub const AF_CAN: i32 = 29; // Controller Area Network +pub const AF_TIPC: i32 = 30; // TIPC sockets +pub const AF_BLUETOOTH: i32 = 31; // Bluetooth sockets +pub const AF_IUCV: i32 = 32; // IUCV sockets +pub const AF_RXRPC: i32 = 33; // RxRPC sockets +pub const AF_ISDN: i32 = 34; // mISDN sockets +pub const AF_PHONET: i32 = 35; // Phonet sockets +pub const AF_IEEE802154: i32 = 36; // IEEE802154 sockets +pub const AF_CAIF: i32 = 37; // CAIF sockets +pub const AF_ALG: i32 = 38; // Algorithm sockets +pub const AF_NFC: i32 = 39; // NFC sockets +pub const AF_VSOCK: i32 = 40; // vSockets +pub const AF_KCM: i32 = 41; // Kernel Connection Multiplexor +pub const AF_QIPCRTR: i32 = 42; // Qualcomm IPC Router +pub const AF_SMC: i32 = 43; // SMC sockets +pub const AF_XDP: i32 = 44; // XDP sockets +pub const AF_MCTP: i32 = 45; // Management Component Transport Protocol +pub const AF_MAX: i32 = 46; // Maximum address family value + +// ===== Protocol Families ===== +// Source: include/linux/socket.h +// Note: PF_* constants are aliases for AF_* for backward compatibility +pub const PF_UNSPEC: i32 = AF_UNSPEC; +pub const PF_UNIX: i32 = AF_UNIX; +pub const PF_LOCAL: i32 = AF_LOCAL; +pub const PF_INET: i32 = AF_INET; +pub const PF_AX25: i32 = AF_AX25; +pub const PF_IPX: i32 = AF_IPX; +pub const PF_APPLETALK: i32 = AF_APPLETALK; +pub const PF_NETROM: i32 = AF_NETROM; +pub const PF_BRIDGE: i32 = AF_BRIDGE; +pub const PF_ATMPVC: i32 = AF_ATMPVC; +pub const PF_X25: i32 = AF_X25; +pub const PF_INET6: i32 = AF_INET6; +pub const PF_ROSE: i32 = AF_ROSE; +pub const PF_DECnet: i32 = AF_DECnet; +pub const PF_NETBEUI: i32 = AF_NETBEUI; +pub const PF_SECURITY: i32 = AF_SECURITY; +pub const PF_KEY: i32 = AF_KEY; +pub const PF_NETLINK: i32 = AF_NETLINK; +pub const PF_ROUTE: i32 = AF_ROUTE; +pub const PF_PACKET: i32 = AF_PACKET; +pub const PF_ASH: i32 = AF_ASH; +pub const PF_ECONET: i32 = AF_ECONET; +pub const PF_ATMSVC: i32 = AF_ATMSVC; +pub const PF_RDS: i32 = AF_RDS; +pub const PF_SNA: i32 = AF_SNA; +pub const PF_IRDA: i32 = AF_IRDA; +pub const PF_PPPOX: i32 = AF_PPPOX; +pub const PF_WANPIPE: i32 = AF_WANPIPE; +pub const PF_LLC: i32 = AF_LLC; +pub const PF_IB: i32 = AF_IB; +pub const PF_MPLS: i32 = AF_MPLS; +pub const PF_CAN: i32 = AF_CAN; +pub const PF_TIPC: i32 = AF_TIPC; +pub const PF_BLUETOOTH: i32 = AF_BLUETOOTH; +pub const PF_IUCV: i32 = AF_IUCV; +pub const PF_RXRPC: i32 = AF_RXRPC; +pub const PF_ISDN: i32 = AF_ISDN; +pub const PF_PHONET: i32 = AF_PHONET; +pub const PF_IEEE802154: i32 = AF_IEEE802154; +pub const PF_CAIF: i32 = AF_CAIF; +pub const PF_ALG: i32 = AF_ALG; +pub const PF_NFC: i32 = AF_NFC; +pub const PF_VSOCK: i32 = AF_VSOCK; +pub const PF_KCM: i32 = AF_KCM; +pub const PF_QIPCRTR: i32 = AF_QIPCRTR; +pub const PF_SMC: i32 = AF_SMC; +pub const PF_XDP: i32 = AF_XDP; +pub const PF_MCTP: i32 = AF_MCTP; +pub const PF_MAX: i32 = AF_MAX; + +// ===== IP Protocol Numbers ===== +// Source: include/uapi/linux/in.h +pub const IPPROTO_IP: i32 = 0; // Dummy protocol for TCP +pub const IPPROTO_ICMP: i32 = 1; // Internet Control Message Protocol +pub const IPPROTO_IGMP: i32 = 2; // Internet Group Management Protocol +pub const IPPROTO_GGP: i32 = 3; // Gateway-Gateway Protocol (deprecated) +pub const IPPROTO_IPV4: i32 = 4; // IPv4 encapsulation +pub const IPPROTO_IPIP: i32 = IPPROTO_IPV4; // IP in IP encapsulation +pub const IPPROTO_TCP: i32 = 6; // Transmission Control Protocol +pub const IPPROTO_ST: i32 = 7; // Stream Protocol +pub const IPPROTO_EGP: i32 = 8; // Exterior Gateway Protocol +pub const IPPROTO_PIGP: i32 = 9; // Private Interior Gateway Protocol +pub const IPPROTO_RCCMON: i32 = 10; // BBN RCC Monitoring +pub const IPPROTO_NVPII: i32 = 11; // Network Voice Protocol +pub const IPPROTO_PUP: i32 = 12; // PARC Universal Packet Protocol +pub const IPPROTO_ARGUS: i32 = 13; // ARGUS +pub const IPPROTO_EMCON: i32 = 14; // EMCON +pub const IPPROTO_XNET: i32 = 15; // Cross Net Debugger +pub const IPPROTO_CHAOS: i32 = 16; // Chaos +pub const IPPROTO_UDP: i32 = 17; // User Datagram Protocol +pub const IPPROTO_MUX: i32 = 18; // Multiplexing Protocol +pub const IPPROTO_MEAS: i32 = 19; // DCN Measurement Subsystems +pub const IPPROTO_HMP: i32 = 20; // Host Monitoring Protocol +pub const IPPROTO_PRM: i32 = 21; // Packet Radio Measurement Protocol +pub const IPPROTO_IDP: i32 = 22; // Xerox NS IDP +pub const IPPROTO_TRUNK1: i32 = 23; // Trunk-1 +pub const IPPROTO_TRUNK2: i32 = 24; // Trunk-2 +pub const IPPROTO_LEAF1: i32 = 25; // Leaf-1 +pub const IPPROTO_LEAF2: i32 = 26; // Leaf-2 +pub const IPPROTO_RDP: i32 = 27; // Reliable Datagram Protocol +pub const IPPROTO_IRTP: i32 = 28; // Internet Reliable Transaction Protocol +pub const IPPROTO_TP: i32 = 29; // ISO Transport Protocol Class 4 +pub const IPPROTO_BLT: i32 = 30; // Bulk Data Transfer Protocol +pub const IPPROTO_NSP: i32 = 31; // Network Services Protocol +pub const IPPROTO_INP: i32 = 32; // Merit Internodal Protocol +pub const IPPROTO_SEP: i32 = 33; // Sequential Exchange Protocol +pub const IPPROTO_3PC: i32 = 34; // Third Party Connect Protocol +pub const IPPROTO_IDPR: i32 = 35; // Inter-Domain Policy Routing Protocol +pub const IPPROTO_XTP: i32 = 36; // Xpress Transport Protocol +pub const IPPROTO_DDP: i32 = 37; // Datagram Delivery Protocol +pub const IPPROTO_CMTP: i32 = 38; // Control Message Transport Protocol +pub const IPPROTO_TPXX: i32 = 39; // TP++ Transport Protocol +pub const IPPROTO_IL: i32 = 40; // IL Transport Protocol +pub const IPPROTO_IPV6: i32 = 41; // IPv6 header +pub const IPPROTO_SDRP: i32 = 42; // Source Demand Routing Protocol +pub const IPPROTO_ROUTING: i32 = 43; // IPv6 routing header +pub const IPPROTO_FRAGMENT: i32 = 44; // IPv6 fragmentation header +pub const IPPROTO_IDRP: i32 = 45; // Inter-Domain Routing Protocol +pub const IPPROTO_RSVP: i32 = 46; // Resource Reservation Protocol +pub const IPPROTO_GRE: i32 = 47; // Generic Routing Encapsulation +pub const IPPROTO_MHRP: i32 = 48; // Mobile Host Routing Protocol +pub const IPPROTO_BHA: i32 = 49; // BHA +pub const IPPROTO_ESP: i32 = 50; // IPv6 Encapsulating Security Payload +pub const IPPROTO_AH: i32 = 51; // IPv6 Authentication Header +pub const IPPROTO_INLSP: i32 = 52; // Integrated Net Layer Security Protocol +pub const IPPROTO_SWIPE: i32 = 53; // IP with Encryption +pub const IPPROTO_NHRP: i32 = 54; // Next Hop Resolution Protocol + // 55-57: Unassigned +pub const IPPROTO_ICMPV6: i32 = 58; // ICMPv6 +pub const IPPROTO_NONE: i32 = 59; // IPv6 no next header +pub const IPPROTO_DSTOPTS: i32 = 60; // IPv6 destination options +pub const IPPROTO_AHIP: i32 = 61; // Any host internal protocol +pub const IPPROTO_CFTP: i32 = 62; // CFTP +pub const IPPROTO_HELLO: i32 = 63; // "hello" routing protocol +pub const IPPROTO_SATEXPAK: i32 = 64; // SATNET/Backroom EXPAK +pub const IPPROTO_KRYPTOLAN: i32 = 65; // Kryptolan +pub const IPPROTO_RVD: i32 = 66; // Remote Virtual Disk +pub const IPPROTO_IPPC: i32 = 67; // Pluribus Packet Core +pub const IPPROTO_ADFS: i32 = 68; // Any distributed file system +pub const IPPROTO_SATMON: i32 = 69; // SATNET Monitoring +pub const IPPROTO_VISA: i32 = 70; // VISA Protocol +pub const IPPROTO_IPCV: i32 = 71; // Internet Packet Core Utility +pub const IPPROTO_CPNX: i32 = 72; // Computer Protocol Network Executive +pub const IPPROTO_CPHB: i32 = 73; // Computer Protocol Heart Beat +pub const IPPROTO_WSN: i32 = 74; // Wang Span Network +pub const IPPROTO_PVP: i32 = 75; // Packet Video Protocol +pub const IPPROTO_BRSATMON: i32 = 76; // BackRoom SATNET Monitoring +pub const IPPROTO_ND: i32 = 77; // Sun net disk protocol (temporary) +pub const IPPROTO_WBMON: i32 = 78; // WIDEBAND Monitoring +pub const IPPROTO_WBEXPAK: i32 = 79; // WIDEBAND EXPAK +pub const IPPROTO_EON: i32 = 80; // ISO CNLP +pub const IPPROTO_VMTP: i32 = 81; // Versatile Message Transaction Protocol +pub const IPPROTO_SVMTP: i32 = 82; // Secure VMTP +pub const IPPROTO_VINES: i32 = 83; // VINES +pub const IPPROTO_TTP: i32 = 84; // TTP +pub const IPPROTO_IGP: i32 = 85; // NSFNET-IGP +pub const IPPROTO_DGP: i32 = 86; // Dissimilar Gateway Protocol +pub const IPPROTO_TCF: i32 = 87; // TCF +pub const IPPROTO_IGRP: i32 = 88; // Interior Gateway Routing Protocol +pub const IPPROTO_OSPFIGP: i32 = 89; // OSPF IGP +pub const IPPROTO_SRPC: i32 = 90; // Sprite RPC Protocol +pub const IPPROTO_LARP: i32 = 91; // Locus Address Resolution Protocol +pub const IPPROTO_MTP: i32 = 92; // Multicast Transport Protocol +pub const IPPROTO_AX25: i32 = 93; // AX.25 Frames +pub const IPPROTO_IPEIP: i32 = 94; // IP encapsulated in IP +pub const IPPROTO_MICP: i32 = 95; // Mobile Internetworking Control Protocol +pub const IPPROTO_SCCSP: i32 = 96; // Semaphore Communications Security Protocol +pub const IPPROTO_ETHERIP: i32 = 97; // Ethernet-within-IP Encapsulation +pub const IPPROTO_ENCAP: i32 = 98; // Encapsulation Header +pub const IPPROTO_APES: i32 = 99; // Any private encryption scheme +pub const IPPROTO_GMTP: i32 = 100; // GMTP +pub const IPPROTO_PIM: i32 = 103; // Protocol Independent Multicast +pub const IPPROTO_IPCOMP: i32 = 108; // IP Payload Compression Protocol +pub const IPPROTO_PGM: i32 = 113; // PGM Reliable Transport Protocol +pub const IPPROTO_SCTP: i32 = 132; // Stream Control Transmission Protocol +pub const IPPROTO_DIVERT: i32 = 254; // Divert pseudo-protocol +pub const IPPROTO_RAW: i32 = 255; // Raw IP packets +pub const IPPROTO_MAX: i32 = 256; +pub const IPPROTO_DONE: i32 = 257; // All processing for this packet is done + +// ===== Message Flags ===== +// Source: include/linux/socket.h +pub const MSG_OOB: i32 = 1; // Process out-of-band data +pub const MSG_PEEK: i32 = 2; // Peek at incoming message +pub const MSG_DONTROUTE: i32 = 4; // Don't use local routing +pub const MSG_TRYHARD: i32 = 4; // Synonym for MSG_DONTROUTE for DECnet +pub const MSG_CTRUNC: i32 = 8; // Control data lost before delivery +pub const MSG_PROBE: i32 = 0x10; // Do not send, probe path for MTU +pub const MSG_TRUNC: i32 = 0x20; // Message was truncated +pub const MSG_DONTWAIT: i32 = 0x40; // Non-blocking I/O +pub const MSG_EOR: i32 = 0x80; // End of record +pub const MSG_WAITALL: i32 = 0x100; // Wait for a full request +pub const MSG_FIN: i32 = 0x200; // Sender will send no more +pub const MSG_SYN: i32 = 0x400; // Initiate a connection +pub const MSG_CONFIRM: i32 = 0x800; // Confirm path validity +pub const MSG_RST: i32 = 0x1000; // Reset the connection +pub const MSG_ERRQUEUE: i32 = 0x2000; // Fetch message from error queue +pub const MSG_NOSIGNAL: i32 = 0x4000; // Do not generate SIGPIPE +pub const MSG_MORE: i32 = 0x8000; // Sender will send more +pub const MSG_WAITFORONE: i32 = 0x10000; // Wait for at least one packet +pub const MSG_SENDPAGE_NOPOLICY: i32 = 0x10000; // sendpage() internal: no policy +pub const MSG_SENDPAGE_NOTLAST: i32 = 0x20000; // sendpage() internal: not last page +pub const MSG_BATCH: i32 = 0x40000; // sendmmsg(): more messages coming +pub const MSG_EOF: i32 = MSG_FIN; // Alias for MSG_FIN +pub const MSG_NO_SHARED_FRAGS: i32 = 0x80000; // sendpage() internal: no shared frags +pub const MSG_SENDPAGE_DECRYPTED: i32 = 0x100000; // sendpage() internal: page needs encryption + +// ===== Shutdown Constants ===== +// Source: include/linux/socket.h +pub const SHUT_RD: i32 = 0; // Disable further receives +pub const SHUT_WR: i32 = 1; // Disable further sends +pub const SHUT_RDWR: i32 = 2; // Disable further sends/receives + +// ===== Socket Options ===== +// Source: include/uapi/asm-generic/socket.h +pub const SOL_SOCKET: i32 = 1; // Socket-level options +pub const SO_DEBUG: i32 = 1; // Debug info recording +pub const SO_REUSEADDR: i32 = 2; // Allow reuse of local addresses +pub const SO_TYPE: i32 = 3; // Get socket type +pub const SO_ERROR: i32 = 4; // Get and clear error status +pub const SO_DONTROUTE: i32 = 5; // Use interface addresses +pub const SO_BROADCAST: i32 = 6; // Permit sending of broadcast msgs +pub const SO_SNDBUF: i32 = 7; // Send buffer size +pub const SO_RCVBUF: i32 = 8; // Receive buffer size +pub const SO_SNDBUFFORCE: i32 = 32; // Send buffer size (privileged) +pub const SO_RCVBUFFORCE: i32 = 33; // Receive buffer size (privileged) +pub const SO_KEEPALIVE: i32 = 9; // Keep connections alive +pub const SO_OOBINLINE: i32 = 10; // Leave received OOB data in line +pub const SO_NO_CHECK: i32 = 11; // Disable checksums +pub const SO_PRIORITY: i32 = 12; // Set the protocol-defined priority +pub const SO_LINGER: i32 = 13; // Linger on close if data present +pub const SO_BSDCOMPAT: i32 = 14; // Enable BSD bug-to-bug compatibility +pub const SO_REUSEPORT: i32 = 15; // Allow reuse of address/port pairs +pub const SO_PASSCRED: i32 = 16; // Receive SCM_CREDENTIALS messages +pub const SO_PEERCRED: i32 = 17; // Get socket's peer credentials +pub const SO_RCVLOWAT: i32 = 18; // Receive low-water mark +pub const SO_SNDLOWAT: i32 = 19; // Send low-water mark +pub const SO_RCVTIMEO_OLD: i32 = 20; // Receive timeout (old) +pub const SO_SNDTIMEO_OLD: i32 = 21; // Send timeout (old) +pub const SO_PEERNAME: i32 = 28; // Name of connected peer +pub const SO_ACCEPTCONN: i32 = 30; // Socket has had listen() + +// ===== TCP Options ===== +// Source: include/uapi/linux/tcp.h +pub const SOL_TCP: i32 = IPPROTO_TCP; // TCP protocol level +pub const SOL_UDP: i32 = IPPROTO_UDP; // UDP protocol level + +pub const TCP_NODELAY: i32 = 0x01; // Don't delay send to coalesce packets +pub const TCP_MAXSEG: i32 = 0x02; // Set maximum segment size +pub const TCP_NOPUSH: i32 = 0x04; // Don't push last block of write +pub const TCP_NOOPT: i32 = 0x08; // Don't use TCP options +pub const TCP_KEEPALIVE: i32 = 0x10; // Idle time for keepalive +pub const TCP_CONNECTIONTIMEOUT: i32 = 0x20; // Connection timeout +pub const PERSIST_TIMEOUT: i32 = 0x40; // Persist timeout +pub const TCP_RXT_CONNDROPTIME: i32 = 0x80; // Retransmission timeout before drop +pub const TCP_RXT_FINDROP: i32 = 0x100; // Drop after 3 FIN retransmissions + +// ===== Socket Object ID Range ===== +// Lind-specific constants +pub const MINSOCKOBJID: i32 = 0; +pub const MAXSOCKOBJID: i32 = 1024; + +// ===== Poll Constants ===== +// Source: include/uapi/asm-generic/poll.h +pub const POLLIN: i16 = 0o1; // Ready for reading +pub const POLLPRI: i16 = 0o2; // Priority data ready +pub const POLLOUT: i16 = 0o4; // Ready for writing +pub const POLLERR: i16 = 0o10; // Error condition +pub const POLLHUP: i16 = 0o20; // Hung up +pub const POLLNVAL: i16 = 0o40; // Invalid polling request + +// ===== Epoll Constants ===== +// Source: include/uapi/linux/eventpoll.h +pub const EPOLLIN: i32 = 0x001; // Ready for reading +pub const EPOLLPRI: i32 = 0x002; // Priority data ready +pub const EPOLLOUT: i32 = 0x004; // Ready for writing +pub const EPOLLRDNORM: i32 = 0x040; // Normal data ready for reading +pub const EPOLLRDBAND: i32 = 0x080; // Priority band data ready for reading +pub const EPOLLWRNORM: i32 = 0x100; // Normal data ready for writing +pub const EPOLLWRBAND: i32 = 0x200; // Priority band data ready for writing +pub const EPOLLMSG: i32 = 0x400; // Message ready +pub const EPOLLERR: i32 = 0x008; // Error condition +pub const EPOLLHUP: i32 = 0x010; // Hang up +pub const EPOLLRDHUP: i32 = 0x2000; // Peer closed the connection +pub const EPOLLWAKEUP: i32 = 1 << 29; // Prevent system suspend +pub const EPOLLONESHOT: i32 = 1 << 30; // One-shot edge trigger +pub const EPOLLET: i32 = 1 << 31; // Edge-triggered + +pub const EPOLL_CTL_ADD: i32 = 1; // Add a file descriptor +pub const EPOLL_CTL_DEL: i32 = 2; // Remove a file descriptor +pub const EPOLL_CTL_MOD: i32 = 3; // Change event registration + +pub const FD_SET_MAX_FD: i32 = 1024; // Maximum file descriptor for fd_set + diff --git a/src/RawPOSIX/src/constants/sys_constants.rs b/src/RawPOSIX/src/constants/sys_constants.rs new file mode 100644 index 00000000..d9c91899 --- /dev/null +++ b/src/RawPOSIX/src/constants/sys_constants.rs @@ -0,0 +1,95 @@ +//! System Constants Module +//! +//! Primary Source References: +//! - Linux kernel v6.5: include/uapi/asm-generic/signal.h +//! - Linux kernel v6.5: include/uapi/asm-generic/resource.h +//! - POSIX.1-2017 (IEEE Std 1003.1-2017) +//! - Linux man-pages project: signal(7) + +#![allow(dead_code)] +#![allow(unused_variables)] + +// ===== User and Group ID Constants ===== +// Lind-specific default values +pub const DEFAULT_UID: u32 = 1000; // Default user ID +pub const DEFAULT_GID: u32 = 1000; // Default group ID + +// ===== Resource Limits ===== +// Source: include/uapi/asm-generic/resource.h +pub const SIGNAL_MAX: i32 = 64; // Maximum number of signals + +// File descriptor limits +pub const NOFILE_CUR: u64 = 1024; // Soft limit for number of open files +pub const NOFILE_MAX: u64 = 4 * 1024; // Hard limit for number of open files + +// Stack size limits +pub const STACK_CUR: u64 = 8192 * 1024; // Soft limit for stack size (8MB) +pub const STACK_MAX: u64 = 1 << 32; // Hard limit for stack size (4GB) + +// Resource identifiers +pub const RLIMIT_STACK: u64 = 0; // Limit type for stack size +pub const RLIMIT_NOFILE: u64 = 1; // Limit type for number of files + +// ===== Process Exit Status ===== +// Source: and POSIX standard +pub const EXIT_SUCCESS: i32 = 0; // Successful termination +pub const EXIT_FAILURE: i32 = 1; // Unsuccessful termination + +// ===== Signal Constants ===== +// Source: include/uapi/asm-generic/signal.h +// Reference: https://man7.org/linux/man-pages/man7/signal.7.html +// Note: Signal numbers can vary by architecture. These are for x86/ARM. + +// Terminal control signals +pub const SIGHUP: i32 = 1; // Hangup +pub const SIGINT: i32 = 2; // Interrupt (Ctrl+C) +pub const SIGQUIT: i32 = 3; // Quit (Ctrl+\) +pub const SIGTERM: i32 = 15; // Termination request + +// Error signals +pub const SIGILL: i32 = 4; // Illegal instruction +pub const SIGTRAP: i32 = 5; // Trace/breakpoint trap +pub const SIGABRT: i32 = 6; // Abort program +pub const SIGIOT: i32 = 6; // Alias for SIGABRT +pub const SIGBUS: i32 = 7; // Bus error (bad memory access) +pub const SIGFPE: i32 = 8; // Floating point exception +pub const SIGSEGV: i32 = 11; // Segmentation violation +pub const SIGSYS: i32 = 31; // Bad system call +pub const SIGUNUSED: i32 = 31; // Alias for SIGSYS + +// User-defined signals +pub const SIGUSR1: i32 = 10; // User-defined signal 1 +pub const SIGUSR2: i32 = 12; // User-defined signal 2 + +// Process control signals +pub const SIGCHLD: i32 = 17; // Child stopped or terminated +pub const SIGCONT: i32 = 18; // Continue if stopped +pub const SIGSTOP: i32 = 19; // Stop process +pub const SIGTSTP: i32 = 20; // Stop typed at terminal +pub const SIGTTIN: i32 = 21; // Terminal input for background process +pub const SIGTTOU: i32 = 22; // Terminal output for background process + +// Resource limit signals +pub const SIGXCPU: i32 = 24; // CPU time limit exceeded +pub const SIGXFSZ: i32 = 25; // File size limit exceeded + +// Alarm signals +pub const SIGALRM: i32 = 14; // Timer signal from alarm(2) +pub const SIGVTALRM: i32 = 26; // Virtual timer expired +pub const SIGPROF: i32 = 27; // Profiling timer expired + +// I/O signals +pub const SIGPIPE: i32 = 13; // Broken pipe +pub const SIGURG: i32 = 23; // Urgent condition on socket +pub const SIGWINCH: i32 = 28; // Window resize signal +pub const SIGIO: i32 = 29; // I/O now possible +pub const SIGPOLL: i32 = 29; // Pollable event (same as SIGIO) +pub const SIGPWR: i32 = 30; // Power failure + +// Signal actions +pub const SIG_BLOCK: i32 = 0; // Block signals in signal mask +pub const SIG_UNBLOCK: i32 = 1; // Unblock signals in signal mask +pub const SIG_SETMASK: i32 = 2; // Set the signal mask + +// Timer types +pub const ITIMER_REAL: i32 = 0; // Real-time timer diff --git a/src/RawPOSIX/src/interface/mem.rs b/src/RawPOSIX/src/interface/mem.rs new file mode 100644 index 00000000..4988e87a --- /dev/null +++ b/src/RawPOSIX/src/interface/mem.rs @@ -0,0 +1,441 @@ +use crate::constants::{ + F_GETFL, MAP_ANONYMOUS, MAP_FIXED, MAP_PRIVATE, PROT_EXEC +}; + +use crate::safeposix::vmmap::{MemoryBackingType, Vmmap, VmmapOps}; +use crate::constants::{MAP_SHARED, PROT_NONE, PROT_READ, PROT_WRITE, PAGESHIFT, PAGESIZE}; + +use crate::interface::{cagetable_getref, syscall_error, Errno}; +use crate::safeposix::cage::Cage; +use std::result::Result; + +// heap is placed at the very top of the memory +pub const HEAP_ENTRY_INDEX: u32 = 0; + +/// Round up the address length to be multiple of pages +/// +/// # Arguments +/// * `length` - length of the address +/// +/// # Returns +/// * `u64` - rounded up length +pub fn round_up_page(length: u64) -> u64 { + if length % PAGESIZE as u64 == 0 { + length + } else { + ((length / PAGESIZE as u64) + 1) * PAGESIZE as u64 + } +} + +/// Copies the memory regions from parent to child based on the provided `vmmap` memory layout. +/// +/// This function is designed to replicate the parent's memory space into the child immediately after +/// a `fork_syscall` in Wasmtime. It assumes that the parent and child share the same `vmmap` structure, +/// a valid assumption in this context. +/// +/// The copying behavior varies based on the type of memory region: +/// 1. **PROT_NONE regions**: +/// - No action is taken, as memory regions are already configured with `PROT_NONE` by default. +/// 2. **Shared memory regions**: +/// - The function uses the `mremap` syscall to replicate shared memory efficiently. Refer to `man 2 mremap` for details. +/// 3. **Private memory regions**: +/// - The function uses `std::ptr::copy_nonoverlapping` to copy the memory contents directly. +/// - **TODO**: Investigate whether using `writev` could improve performance for this case. +/// +/// # Arguments +/// * `parent_vmmap` - vmmap struct of parent +/// * `child_vmmap` - vmmap struct of child +pub fn fork_vmmap(parent_vmmap: &Vmmap, child_vmmap: &Vmmap) { + let parent_base = parent_vmmap.base_address.unwrap(); + let child_base = child_vmmap.base_address.unwrap(); + + // iterate through each vmmap entry + for (_interval, entry) in parent_vmmap.entries.iter() { + // if the entry has PROT_NONE, that means the entry is currently not used + if entry.prot == PROT_NONE { continue; } + // translate page number to user address + let addr_st = (entry.page_num << PAGESHIFT) as u32; + let addr_len = (entry.npages << PAGESHIFT) as usize; + + // translate user address to system address + let parent_st = parent_vmmap.user_to_sys(addr_st); + let child_st = child_vmmap.user_to_sys(addr_st); + if entry.flags & (MAP_SHARED as i32) > 0 { + // for shared memory, we are using mremap to fork shared memory + // See "man 2 mremap" for description of what MREMAP_MAYMOVE does with old_size=0 + // when old_address points to a shared mapping + let result = unsafe { libc::mremap(parent_st as *mut libc::c_void, 0, addr_len, libc::MREMAP_MAYMOVE | libc::MREMAP_FIXED, child_st as *mut libc::c_void) }; + } else { + unsafe { + // temporarily enable write on child's memory region to write parent data + libc::mprotect(child_st as *mut libc::c_void, addr_len, PROT_READ | PROT_WRITE); + + // write parent data + // TODO: replace copy_nonoverlapping with writev for potential performance boost + std::ptr::copy_nonoverlapping(parent_st as *const u8, child_st as *mut u8, addr_len); + + // revert child's memory region prot + libc::mprotect(child_st as *mut libc::c_void, addr_len, entry.prot) + }; + } + } +} + +/// Handler of the `munmap_syscall`, interacting with the `vmmap` structure. +/// +/// This function processes the `munmap_syscall` by updating the `vmmap` entries and managing +/// the unmap operation. Instead of invoking the actual `munmap` syscall, the unmap operation +/// is simulated by setting the specified region to `PROT_NONE`. The memory remains valid but +/// becomes inaccessible due to the `PROT_NONE` setting. +/// +/// # Arguments +/// * `cageid` - Identifier of the cage that calls the `munmap` +/// * `addr` - Starting address of the region to unmap +/// * `length` - Length of the region to unmap +/// +/// # Returns +/// * `i32` - 0 for success and -1 for failure +pub fn munmap_handler(cageid: u64, addr: *mut u8, len: usize) -> i32 { + let cage = cagetable_getref(cageid); + + // check if the provided address is multiple of pages + let rounded_addr = round_up_page(addr as u64) as usize; + if rounded_addr != addr as usize { + return syscall_error(Errno::EINVAL, "mmap", "address it not aligned"); + } + + let vmmap = cage.vmmap.read(); + let sysaddr = vmmap.user_to_sys(rounded_addr as u32); + drop(vmmap); + + let rounded_length = round_up_page(len as u64) as usize; + + // we are replacing munmap with mmap because we do not want to really deallocate the memory region + // we just want to set the prot of the memory region back to PROT_NONE + let result = cage.mmap_syscall(sysaddr as *mut u8, rounded_length, PROT_NONE, (MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED) as i32, -1, 0); + if result != sysaddr { + panic!("MAP_FIXED not fixed"); + } + + let mut vmmap = cage.vmmap.write(); + + vmmap.remove_entry(rounded_addr as u32 >> PAGESHIFT, len as u32 >> PAGESHIFT); + + 0 +} + +/// Handles the `mmap_syscall`, interacting with the `vmmap` structure. +/// +/// This function processes the `mmap_syscall` by updating the `vmmap` entries and performing +/// the necessary mmap operations. The handling logic is as follows: +/// 1. Restrict allowed flags to `MAP_FIXED`, `MAP_SHARED`, `MAP_PRIVATE`, and `MAP_ANONYMOUS`. +/// 2. Disallow `PROT_EXEC`; return `EINVAL` if the `prot` argument includes `PROT_EXEC`. +/// 3. If `MAP_FIXED` is not specified, query the `vmmap` structure to locate an available memory region. +/// Otherwise, use the address provided by the user. +/// 4. Invoke the actual `mmap` syscall with the `MAP_FIXED` flag to configure the memory region's protections. +/// 5. Update the corresponding `vmmap` entry. +/// +/// # Arguments +/// * `cageid` - Identifier of the cage that initiated the `mmap` syscall. +/// * `addr` - Starting address of the memory region to mmap. +/// * `len` - Length of the memory region to mmap. +/// * `prot` - Memory protection flags (e.g., `PROT_READ`, `PROT_WRITE`). +/// * `flags` - Mapping flags (e.g., `MAP_SHARED`, `MAP_ANONYMOUS`). +/// * `fildes` - File descriptor associated with the mapping, if applicable. +/// * `off` - Offset within the file, if applicable. +/// +/// # Returns +/// * `u32` - Result of the `mmap` operation. See "man mmap" for details +pub fn mmap_handler(cageid: u64, addr: *mut u8, len: usize, mut prot: i32, mut flags: i32, mut fildes: i32, off: i64) -> u32 { + let cage = cagetable_getref(cageid); + + let mut maxprot = PROT_READ | PROT_WRITE; + + // only these four flags are allowed + let allowed_flags = MAP_FIXED as i32 | MAP_SHARED as i32 | MAP_PRIVATE as i32 | MAP_ANONYMOUS as i32; + if flags & !allowed_flags > 0 { + // truncate flag to remove flags that are not allowed + flags &= allowed_flags; + } + + if prot & PROT_EXEC > 0 { + return syscall_error(Errno::EINVAL, "mmap", "PROT_EXEC is not allowed") as u32; + } + + // check if the provided address is multiple of pages + let rounded_addr = round_up_page(addr as u64); + if rounded_addr != addr as u64 { + return syscall_error(Errno::EINVAL, "mmap", "address it not aligned") as u32; + } + + // offset should be non-negative and multiple of pages + if off < 0 { + return syscall_error(Errno::EINVAL, "mmap", "offset cannot be negative") as u32; + } + let rounded_off = round_up_page(off as u64); + if rounded_off != off as u64 { + return syscall_error(Errno::EINVAL, "mmap", "offset it not aligned") as u32; + } + + // round up length to be multiple of pages + let rounded_length = round_up_page(len as u64); + + let mut useraddr = addr as u32; + // if MAP_FIXED is not set, then we need to find an address for the user + if flags & MAP_FIXED as i32 == 0 { + let mut vmmap = cage.vmmap.write(); + let result; + + // pick an address of appropriate size, anywhere + if useraddr == 0 { + result = vmmap.find_map_space(rounded_length as u32 >> PAGESHIFT, 1); + } else { + // use address user provided as hint to find address + result = vmmap.find_map_space_with_hint(rounded_length as u32 >> PAGESHIFT, 1, addr as u32); + } + + // did not find desired memory region + if result.is_none() { + return syscall_error(Errno::ENOMEM, "mmap", "no memory") as u32; + } + + let space = result.unwrap(); + useraddr = (space.start() << PAGESHIFT) as u32; + } + + flags |= MAP_FIXED as i32; + + // either MAP_PRIVATE or MAP_SHARED should be set, but not both + if (flags & MAP_PRIVATE as i32 == 0) == (flags & MAP_SHARED as i32 == 0) { + return syscall_error(Errno::EINVAL, "mmap", "invalid flags") as u32; + } + + let vmmap = cage.vmmap.read(); + + let sysaddr = vmmap.user_to_sys(useraddr); + + drop(vmmap); + + if rounded_length > 0 { + if flags & MAP_ANONYMOUS as i32 > 0 { + fildes = -1; + } + + let result = cage.mmap_syscall(sysaddr as *mut u8, rounded_length as usize, prot, flags, fildes, off); + + let vmmap = cage.vmmap.read(); + let result = vmmap.sys_to_user(result); + drop(vmmap); + + // if mmap addr is positive, that would mean the mapping is successful and we need to update the vmmap entry + if result >= 0 { + if result != useraddr { + panic!("MAP_FIXED not fixed"); + } + + let mut vmmap = cage.vmmap.write(); + let backing = { + if flags as u32 & MAP_ANONYMOUS > 0 { + MemoryBackingType::Anonymous + } else { + // if we are doing file-backed mapping, we need to set maxprot to the file permission + let flags = cage.fcntl_syscall(fildes, F_GETFL, 0); + if flags < 0 { + return syscall_error(Errno::EINVAL, "mmap", "invalid file descriptor") as u32; + } + maxprot &= flags; + MemoryBackingType::FileDescriptor(fildes as u64) + } + }; + + // update vmmap entry + let _ = vmmap.add_entry_with_overwrite(useraddr >> PAGESHIFT, + (rounded_length >> PAGESHIFT) as u32, + prot, + maxprot, + flags, + backing, + off, + len as i64, + cageid); + } + } + + useraddr as u32 +} + +/// Handles the `sbrk_syscall`, interacting with the `vmmap` structure. +/// +/// This function processes the `sbrk_syscall` by updating the `vmmap` entries and managing +/// the program break. It calculates the target program break after applying the specified +/// increment and delegates further processing to the `brk_handler`. +/// +/// # Arguments +/// * `cageid` - Identifier of the cage that initiated the `sbrk` syscall. +/// * `brk` - Increment to adjust the program break, which can be negative. +/// +/// # Returns +/// * `u32` - Result of the `sbrk` operation. Refer to `man sbrk` for details. +pub fn sbrk_handler(cageid: u64, brk: i32) -> u32 { + let cage = cagetable_getref(cageid); + + // get the heap entry + let mut vmmap = cage.vmmap.read(); + let heap = vmmap.find_page(HEAP_ENTRY_INDEX).unwrap().clone(); + + // program break should always be the same as the heap entry end + assert!(heap.npages == vmmap.program_break); + + // pass 0 to sbrk will just return the current brk + if brk == 0 { + return (PAGESIZE * heap.npages) as u32; + } + + // round up the break to multiple of pages + // brk increment could possibly be negative + let brk_page; + if brk < 0 { + brk_page = -((round_up_page(-brk as u64) >> PAGESHIFT) as i32); + } else { + brk_page = (round_up_page(brk as u64) >> PAGESHIFT) as i32; + } + + // drop the vmmap so that brk_handler will not deadlock + drop(vmmap); + + if brk_handler(cageid, ((heap.npages as i32 + brk_page) << PAGESHIFT) as u32) < 0 { + return syscall_error(Errno::ENOMEM, "sbrk", "no memory") as u32; + } + + // sbrk syscall should return previous brk address before increment + (PAGESIZE * heap.npages) as u32 +} + +/// Handles the `brk_syscall`, interacting with the `vmmap` structure. +/// +/// This function processes the `brk_syscall` by updating the `vmmap` entries and performing +/// the necessary operations to adjust the program break. Specifically, it updates the program +/// break by modifying the end of the heap entry (the first entry in `vmmap`) and invokes `mmap` +/// to adjust the memory protection as needed. +/// +/// # Arguments +/// * `cageid` - Identifier of the cage that initiated the `brk` syscall. +/// * `brk` - The new program break address. +/// +/// # Returns +/// * `u32` - Returns `0` on success or `-1` on failure. +pub fn brk_handler(cageid: u64, brk: u32) -> i32 { + let cage = cagetable_getref(cageid); + + let mut vmmap = cage.vmmap.write(); + let heap = vmmap.find_page(HEAP_ENTRY_INDEX).unwrap().clone(); + + assert!(heap.npages == vmmap.program_break); + + let old_brk_page = heap.npages; + // round up the break to multiple of pages + let brk_page = (round_up_page(brk as u64) >> PAGESHIFT) as u32; + + // if we are incrementing program break, we need to check if we have enough space + if brk_page > old_brk_page { + if vmmap.check_existing_mapping(old_brk_page, brk_page - old_brk_page, 0) { + return syscall_error(Errno::ENOMEM, "brk", "no memory"); + } + } + + // update vmmap entry + vmmap.add_entry_with_overwrite(0, brk_page, heap.prot, heap.maxprot, heap.flags, heap.backing, heap.file_offset, heap.file_size, heap.cage_id); + + let old_heap_end_usr = (old_brk_page * PAGESIZE) as u32; + let old_heap_end_sys = vmmap.user_to_sys(old_heap_end_usr)as *mut u8; + + let new_heap_end_usr = (brk_page * PAGESIZE) as u32; + let new_heap_end_sys = vmmap.user_to_sys(new_heap_end_usr)as *mut u8; + + vmmap.set_program_break(brk_page); + + drop(vmmap); + + // if new brk is larger than old brk + // we need to mmap the new region + if brk_page > old_brk_page { + let ret = cage.mmap_syscall( + old_heap_end_sys, + ((brk_page - old_brk_page) * PAGESIZE) as usize, + heap.prot, + (heap.flags as u32 | MAP_FIXED) as i32, + -1, + 0 + ); + + if ret < 0 { + panic!("brk mmap failed"); + } + } + // if we are shrinking the brk + // we need to do something similar to munmap + // to unmap the extra memory + else if brk_page < old_brk_page { + let ret = cage.mmap_syscall( + new_heap_end_sys, + ((old_brk_page - brk_page) * PAGESIZE) as usize, + PROT_NONE, + (MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED) as i32, + -1, + 0 + ); + + if ret < 0 { + panic!("brk mmap failed"); + } + } + + 0 +} + +/// Validates and converts a virtual memory address to a physical address with protection checks +/// +/// This function performs several critical memory management operations: +/// 1. Validates that the requested memory region is properly mapped +/// 2. Checks protection flags match the requested access +/// 3. Converts virtual addresses to physical addresses +/// +/// # Arguments +/// * `cage` - Reference to the memory cage containing the virtual memory map +/// * `arg` - Virtual memory address to check and convert +/// * `length` - Length of the memory region being accessed +/// * `prot` - Protection flags to validate (read/write/execute) +/// +/// # Returns +/// * `Ok(u64)` - Physical memory address if validation succeeds +/// * `Err(Errno)` - EFAULT if memory access would be invalid +/// +/// # Memory Safety +/// This is a critical security function that prevents invalid memory accesses by: +/// - Ensuring addresses are properly aligned to pages +/// - Validating all pages in the region are mapped with correct permissions +/// - Preventing access outside of allocated memory regions +pub fn check_and_convert_addr_ext(cage: &Cage, arg: u64, length: usize, prot: i32) -> Result { + // Get read lock on virtual memory map + let mut vmmap = cage.vmmap.write(); + + // Calculate page numbers for start and end of region + let page_num = (arg >> PAGESHIFT) as u32; // Starting page number + let end_page = ((arg + length as u64 + PAGESIZE as u64 - 1) >> PAGESHIFT) as u32; // Ending page number (rounded up) + let npages = end_page - page_num; // Total number of pages spanned + + // Validate memory mapping and permissions + if vmmap.check_addr_mapping(page_num, npages, prot).is_none() { + return Err(Errno::EFAULT); // Return error if mapping invalid + } + + // Convert to physical address by adding base address + Ok(vmmap.base_address.unwrap() as u64 + arg) +} + +// Add the base address of the vmmap to the argument +pub fn translate_vmmap_addr(cage: &Cage, arg: u64) -> Result { + // Get read lock on virtual memory map + let vmmap = cage.vmmap.read(); + Ok(vmmap.base_address.unwrap() as u64 + arg) +} \ No newline at end of file diff --git a/src/RawPOSIX/src/interface/misc.rs b/src/RawPOSIX/src/interface/misc.rs index 2476b23f..0f9fcd5c 100644 --- a/src/RawPOSIX/src/interface/misc.rs +++ b/src/RawPOSIX/src/interface/misc.rs @@ -34,7 +34,7 @@ pub use serde_cbor::{ use crate::interface; use crate::interface::errnos::VERBOSE; use crate::interface::types::SigsetType; -use crate::safeposix::syscalls::fs_constants::SEM_VALUE_MAX; +use crate::constants::SEM_VALUE_MAX; use std::sync::LazyLock; use std::time::Duration; diff --git a/src/RawPOSIX/src/interface/mod.rs b/src/RawPOSIX/src/interface/mod.rs index e8c7353b..5a5399c6 100644 --- a/src/RawPOSIX/src/interface/mod.rs +++ b/src/RawPOSIX/src/interface/mod.rs @@ -3,6 +3,7 @@ pub mod errnos; mod file; mod misc; mod timer; +mod mem; pub mod types; pub use comm::*; pub use errnos::*; @@ -10,3 +11,4 @@ pub use file::*; pub use misc::*; pub use timer::*; pub use types::*; +pub use mem::*; diff --git a/src/RawPOSIX/src/lib.rs b/src/RawPOSIX/src/lib.rs index d7f3d819..29e66e4a 100644 --- a/src/RawPOSIX/src/lib.rs +++ b/src/RawPOSIX/src/lib.rs @@ -11,3 +11,4 @@ pub mod interface; pub mod safeposix; pub mod tests; pub mod fdtables; +pub mod constants; diff --git a/src/RawPOSIX/src/safeposix/cage.rs b/src/RawPOSIX/src/safeposix/cage.rs index cdfeaa3f..32cabbb2 100644 --- a/src/RawPOSIX/src/safeposix/cage.rs +++ b/src/RawPOSIX/src/safeposix/cage.rs @@ -1,5 +1,13 @@ #![allow(dead_code)] use crate::interface; +use crate::constants::{ + SIGNAL_MAX, + S_IRWXU, S_IRWXG, S_IRWXO, + PROT_READ, PROT_WRITE, + O_RDONLY, O_WRONLY, O_RDWR, O_CREAT, O_TRUNC, + MAP_SHARED, MAP_PRIVATE, +}; + //going to get the datatypes and errnos from the cage file from now on pub use crate::interface::errnos::{syscall_error, Errno}; @@ -8,9 +16,8 @@ pub use crate::interface::types::{ }; use super::filesystem::normpath; -pub use super::syscalls::fs_constants::*; -pub use super::syscalls::net_constants::*; -pub use super::syscalls::sys_constants::*; +use crate::constants::*; +pub use super::vmmap::*; pub use crate::interface::CAGE_TABLE; @@ -76,7 +83,8 @@ pub struct Cage { // and cage struct is cleaned up, but its exit status are inserted along with its cage id into the end of // its parent cage's zombies list pub zombies: interface::RustLock>, - pub child_num: interface::RustAtomicU64 + pub child_num: interface::RustAtomicU64, + pub vmmap: interface::RustLock, } impl Cage { diff --git a/src/RawPOSIX/src/safeposix/dispatcher.rs b/src/RawPOSIX/src/safeposix/dispatcher.rs index 563f2216..01511e93 100644 --- a/src/RawPOSIX/src/safeposix/dispatcher.rs +++ b/src/RawPOSIX/src/safeposix/dispatcher.rs @@ -116,6 +116,8 @@ const WRITEV_SYSCALL: i32 = 170; const CLONE_SYSCALL: i32 = 171; const WAIT_SYSCALL: i32 = 172; const WAITPID_SYSCALL: i32 = 173; +const BRK_SYSCALL: i32 = 175; +const SBRK_SYSCALL: i32 = 176; const NANOSLEEP_TIME64_SYSCALL : i32 = 181; @@ -135,6 +137,8 @@ use crate::interface::types; use crate::interface::{SigactionStruct, StatData}; use crate::{fdtables, interface}; use crate::interface::errnos::*; +use crate::constants::*; +use crate::interface::translate_vmmap_addr; macro_rules! get_onearg { ($arg: expr) => { @@ -158,6 +162,7 @@ pub extern "C" fn rustposix_thread_init(cageid: u64, signalflag: u64) { cage.sigset .insert(pthreadid, interface::RustAtomicU64::new(0)); } + interface::signalflag_set(signalflag); } @@ -198,89 +203,122 @@ pub fn lind_syscall_api( let ret = match call_number { WRITE_SYSCALL => { + // Handles writing data from user buffer to file descriptor + // Get file descriptor let fd = arg1 as i32; - let buf = (start_address + arg2) as *const u8; let count = arg3 as usize; - interface::cagetable_getref(cageid) - .write_syscall(fd, buf, count) + if count == 0 { + return 0; // Early return for zero-length writes + } + // Get cage reference for memory operations + let cage = interface::cagetable_getref(cageid); + // Convert user buffer address to system address + let buf = translate_vmmap_addr(&cage, arg2).unwrap() as *const u8; + // Perform write operation through cage abstraction + cage.write_syscall(fd, buf, count) } WRITEV_SYSCALL => { let fd = arg1 as i32; - let iovec = (start_address + arg2) as *const interface::IovecStruct; let iovcnt = arg3 as i32; - - interface::cagetable_getref(cageid) - .writev_syscall(fd, iovec, iovcnt) + // Validate count first + if iovcnt <= 0 { + return syscall_error( + Errno::EINVAL, + "writev", + "invalid iovec count" + ); + } + let cage = interface::cagetable_getref(cageid); + // Convert iovec array address + let iov_base = translate_vmmap_addr(&cage, arg2).unwrap() as *const interface::IovecStruct; + // The actual write operation is delegated to the cage implementation + cage.writev_syscall(fd, iov_base, iovcnt) } MUNMAP_SYSCALL => { - let addr = (start_address + arg1) as *mut u8; - let len = arg2 as usize; + let addr = arg1 as *mut u8; + let length = arg2 as usize; + let cage = interface::cagetable_getref(cageid); - interface::cagetable_getref(cageid) - .munmap_syscall(addr, len) + if length == 0 { + return syscall_error( + Errno::EINVAL, + "munmap", + "length cannot be zero" + ); + } + + // Perform the unmapping operation + interface::munmap_handler(cageid, addr, length) } MMAP_SYSCALL => { - let addr = (start_address + arg1) as *mut u8; + let addr = arg1 as *mut u8; let len = arg2 as usize; let prot = arg3 as i32; let flags = arg4 as i32; - let fildes = arg5 as i32; + let fd = arg5 as i32; let off = arg6 as i64; - interface::cagetable_getref(cageid) - .mmap_syscall(addr, len, prot, flags, fildes, off) + // Basic length validation + if len == 0 { + return syscall_error( + Errno::EINVAL, + "mmap", + "length cannot be zero" + ); + } + + interface::mmap_handler(cageid, addr, len, prot, flags, fd, off) as i32 } PREAD_SYSCALL => { let fd = arg1 as i32; - let buf = (start_address + arg2) as *mut u8; let count = arg3 as usize; let offset = arg4 as i64; - - interface::cagetable_getref(cageid) - .pread_syscall(fd, buf, count, offset) + let cage = interface::cagetable_getref(cageid); + let buf = translate_vmmap_addr(&cage, arg2).unwrap() as *mut u8; + cage.pread_syscall(fd, buf, count, offset) } READ_SYSCALL => { let fd = arg1 as i32; - let buf = (start_address + arg2) as *mut u8; let count = arg3 as usize; + let cage = interface::cagetable_getref(cageid); + let buf = translate_vmmap_addr(&cage, arg2).unwrap() as *mut u8; - interface::cagetable_getref(cageid) - .read_syscall(fd, buf, count) + // File descriptor validation and actual read operation + // handled by cage implementation + cage.read_syscall(fd, buf, count) } CLOSE_SYSCALL => { let fd = arg1 as i32; + // File descriptor validation and close operation handled by cage interface::cagetable_getref(cageid) .close_syscall(fd) } ACCESS_SYSCALL => { - let path = match interface::types::get_cstr(start_address + arg1) { - Ok(path_str) => path_str, - Err(_) => return -1, // Handle error appropriately, return an error code - }; + let cage = interface::cagetable_getref(cageid); + let addr = translate_vmmap_addr(&cage, arg1).unwrap(); + let path = interface::types::get_cstr(addr).unwrap(); let amode = arg2 as i32; - interface::cagetable_getref(cageid) - .access_syscall(path, amode) + // Perform access check through cage implementation + cage.access_syscall(path, amode) } OPEN_SYSCALL => { - let path = match interface::types::get_cstr(start_address + arg1) { - Ok(path_str) => path_str, - Err(_) => return -1, // Handle error appropriately, return an error code - }; + let cage = interface::cagetable_getref(cageid); + let addr = translate_vmmap_addr(&cage, arg1).unwrap(); + let path = interface::types::get_cstr(addr).unwrap(); let flags = arg2 as i32; let mode = arg3 as u32; - - interface::cagetable_getref(cageid) - .open_syscall(path, flags, mode) + // Perform open operation through cage implementation + cage.open_syscall(path, flags, mode) } SOCKET_SYSCALL => { @@ -288,51 +326,58 @@ pub fn lind_syscall_api( let socktype = arg2 as i32; let protocol = arg3 as i32; + // Perform socket operation through cage implementation + // Domain, type, and protocol validation handled by cage layer interface::cagetable_getref(cageid) .socket_syscall(domain, socktype, protocol) } CONNECT_SYSCALL => { let fd = arg1 as i32; - let addrlen = arg3 as u32; - let addr = get_onearg!(interface::get_sockaddr(arg2, addrlen)); - - let remoteaddr = match Ok::<&interface::GenSockaddr, i32>(&addr) { - Ok(addr) => addr, - Err(_) => panic!("Failed to get sockaddr"), // Handle error appropriately - }; - interface::cagetable_getref(cageid) - .connect_syscall(fd, remoteaddr) + let cage = interface::cagetable_getref(cageid); + let addr = translate_vmmap_addr(&cage, arg2).unwrap(); + let sockaddr = interface::get_sockaddr(addr as u64, arg3 as u32).unwrap(); + let remoteaddr = &sockaddr; + + // Perform connect operation through cage implementation + // File descriptor validation handled by cage layer + cage.connect_syscall(fd, remoteaddr) } BIND_SYSCALL => { let fd = arg1 as i32; - let addrlen = arg3 as u32; - let addr = interface::get_sockaddr(start_address + arg2, addrlen).unwrap(); - let localaddr = match Ok::<&interface::GenSockaddr, i32>(&addr) { - Ok(addr) => addr, - Err(_) => panic!("Failed to get sockaddr"), // Handle error appropriately - }; - interface::cagetable_getref(cageid) - .bind_syscall(fd, localaddr) + let cage = interface::cagetable_getref(cageid); + let addr = translate_vmmap_addr(&cage, arg2).unwrap(); + let sockaddr = interface::get_sockaddr(addr as u64, arg3 as u32).unwrap(); + let localaddr = &sockaddr; + + // Perform bind operation through cage implementation + // File descriptor validation handled by cage layer + cage.bind_syscall(fd, localaddr) } ACCEPT_SYSCALL => { - let mut addr = interface::GenSockaddr::V4(interface::SockaddrV4::default()); //value doesn't matter + let mut addr = interface::GenSockaddr::V4(interface::SockaddrV4::default()); let nullity1 = interface::arg_nullity(arg2); let nullity2 = interface::arg_nullity(arg3); - + let cage = interface::cagetable_getref(cageid); + // Handle NULL address case (both NULL) if nullity1 && nullity2 { - interface::cagetable_getref(cageid) - .accept_syscall(arg1 as i32, &mut Some(&mut addr)) - } else if !(nullity1 || nullity2) { - let rv = interface::cagetable_getref(cageid) - .accept_syscall(arg1 as i32, &mut Some(&mut addr)); + cage.accept_syscall(arg1 as i32, &mut Some(&mut addr)) + } + // Handle non-NULL case (both non-NULL) + else if !(nullity1 || nullity2) { + // Perform accept operation first + let rv = cage.accept_syscall(arg1 as i32, &mut Some(&mut addr)); if rv >= 0 { - interface::copy_out_sockaddr((start_address + arg2), (start_address + arg3), addr); + let addr2_addr = translate_vmmap_addr(&cage, arg2).unwrap(); + let len_addr = translate_vmmap_addr(&cage, arg3).unwrap(); + interface::copy_out_sockaddr(addr2_addr as u64, len_addr as u64, addr); } rv - } else { + } + // Handle invalid case (one NULL, one non-NULL) + else { syscall_error( Errno::EINVAL, "accept", @@ -343,30 +388,53 @@ pub fn lind_syscall_api( EXEC_SYSCALL => { let child_cageid = arg1 as u64; + + // Perform exec operation through cage implementation + // Child cage validation handled by cage layer interface::cagetable_getref(cageid) .exec_syscall(child_cageid) } EXIT_SYSCALL => { let status = arg1 as i32; + + // Perform exit operation through cage implementation + // Cleanup handled by cage layer interface::cagetable_getref(cageid) .exit_syscall(status) } SELECT_SYSCALL => { + // Get the number of file descriptors to check (highest fd + 1) let nfds = arg1 as i32; - let readfds = interface::get_fdset(arg2).unwrap(); - let writefds = interface::get_fdset(arg3).unwrap(); - let errorfds = interface::get_fdset(arg4).unwrap(); - let rposix_timeout = interface::duration_fromtimeval(arg5).unwrap(); - interface::cagetable_getref(cageid) - .select_syscall(nfds, readfds, writefds, errorfds, rposix_timeout) + // Get reference to the cage for memory operations + let cage = interface::cagetable_getref(cageid); + // Convert readfds buffer address + let readfds_addr = translate_vmmap_addr(&cage, arg2).unwrap(); + let readfds = interface::get_fdset(readfds_addr).unwrap(); + // Convert writefds buffer address + let writefds_addr = translate_vmmap_addr(&cage, arg3).unwrap(); + let writefds = interface::get_fdset(writefds_addr).unwrap(); + // Convert errorfds buffer address + let errorfds_addr = translate_vmmap_addr(&cage, arg4).unwrap(); + let errorfds = interface::get_fdset(errorfds_addr).unwrap(); + // Convert timeout buffer address + let timeout_addr = translate_vmmap_addr(&cage, arg5).unwrap(); + let rposix_timeout = interface::duration_fromtimeval(timeout_addr).unwrap(); + // Delegate to the cage's select implementation + // This will: + // 1. Monitor the specified file descriptors for activity + // 2. Modify the fd_sets to indicate which descriptors are ready + // 3. Return the number of ready descriptors or an error code + cage.select_syscall(nfds, readfds, writefds, errorfds, rposix_timeout) } RENAME_SYSCALL => { - let old_ptr = (start_address + arg1) as *const u8; - let new_ptr = (start_address + arg2) as *const u8; - + let cage = interface::cagetable_getref(cageid); + // Convert old path address + let old_ptr = translate_vmmap_addr(&cage, arg1).unwrap(); + // Convert new path address + let new_ptr = translate_vmmap_addr(&cage, arg2).unwrap(); // Convert the raw pointers to `&str` let old = unsafe { CStr::from_ptr(old_ptr as *const i8).to_str().unwrap() @@ -374,102 +442,106 @@ pub fn lind_syscall_api( let new = unsafe { CStr::from_ptr(new_ptr as *const i8).to_str().unwrap() }; - - interface::cagetable_getref(cageid) - .rename_syscall(old, new) + // Perform rename operation through cage implementation + cage.rename_syscall(old, new) } XSTAT_SYSCALL => { - let fd_ptr = (start_address + arg1) as *const u8; - let buf = match interface::get_statdatastruct(start_address + arg2) { - Ok(val) => val, - Err(errno) => { - return errno; - } - }; - - let fd = unsafe { - CStr::from_ptr(fd_ptr as *const i8).to_str().unwrap() - }; - - interface::cagetable_getref(cageid) - .stat_syscall(fd, buf) + let cage = interface::cagetable_getref(cageid); + // Convert path string address + let path_addr = translate_vmmap_addr(&cage, arg1).unwrap(); + let path = interface::types::get_cstr(path_addr).unwrap(); + // Convert stat buffer address + let buf_addr = translate_vmmap_addr(&cage, arg2).unwrap(); + let buf = interface::get_statdatastruct(buf_addr).unwrap(); + // Perform stat operation through cage implementation + // Results written directly to user buffer by cage layer + cage.stat_syscall(path, buf) } MKDIR_SYSCALL => { - let fd_ptr = (start_address + arg1) as *const u8; + let cage = interface::cagetable_getref(cageid); + // Convert path string address + let path_addr = translate_vmmap_addr(&cage, arg1).unwrap(); + let path = interface::types::get_cstr(path_addr).unwrap(); let mode = arg2 as u32; - - let fd= unsafe { - CStr::from_ptr(fd_ptr as *const i8).to_str().unwrap() - }; - - interface::cagetable_getref(cageid) - .mkdir_syscall(fd, mode) + // Perform mkdir operation through cage implementation + cage.mkdir_syscall(path, mode) } - RMDIR_SYSCALL => { - let fd_ptr = (start_address + arg1) as *const u8; - - let fd= unsafe { - CStr::from_ptr(fd_ptr as *const i8).to_str().unwrap() - }; - - interface::cagetable_getref(cageid) - .rmdir_syscall(fd) + let cage = interface::cagetable_getref(cageid); + // Convert path string address + let path_addr = translate_vmmap_addr(&cage, arg1).unwrap(); + let path = interface::types::get_cstr(path_addr).unwrap(); + // Perform rmdir operation through cage implementation + cage.rmdir_syscall(path) } FCHDIR_SYSCALL => { let fd = arg1 as i32; + // Perform fchdir operation through cage implementation + // File descriptor validation handled by cage layer interface::cagetable_getref(cageid) .fchdir_syscall(fd) } CHDIR_SYSCALL => { - let path = interface::types::get_cstr(start_address + arg1).unwrap(); + let cage = interface::cagetable_getref(cageid); + // Convert path string address + let path_addr = translate_vmmap_addr(&cage, arg1).unwrap(); + let path = interface::types::get_cstr(path_addr).unwrap(); - interface::cagetable_getref(cageid) - .chdir_syscall(path) + // Perform chdir operation through cage implementation + cage.chdir_syscall(path) } GETCWD_SYSCALL => { - let buf = (start_address + arg1) as *mut u8; - let bufsize = arg2 as u32; - - let ret = interface::cagetable_getref(cageid) - .getcwd_syscall(buf, bufsize); + let bufsize = arg2 as usize; + let cage = interface::cagetable_getref(cageid); + // Convert buffer address + let buf_addr = translate_vmmap_addr(&cage, arg1).unwrap(); + let buf = buf_addr as *mut u8; + // Perform getcwd operation through cage implementation + // On success (ret == 0), return the buffer address + let ret = cage.getcwd_syscall(buf, bufsize as u32); if ret == 0 { return arg1 as i32; } ret } FSTATFS_SYSCALL => { let fd = arg1 as i32; - let buf = interface::get_fsdatastruct(start_address + arg2).unwrap(); - - interface::cagetable_getref(cageid) - .fstatfs_syscall(fd, buf) + let cage = interface::cagetable_getref(cageid); + // Convert buffer address + let buf_addr = translate_vmmap_addr(&cage, arg2).unwrap(); + let buf = interface::get_fsdatastruct(buf_addr).unwrap(); + // Perform fstatfs operation through cage implementation + // File descriptor validation and actual operation handled by cage layer + cage.fstatfs_syscall(fd, buf) } CHMOD_SYSCALL => { - let fd_ptr = (start_address + arg1) as *const u8; - let fd= unsafe { - CStr::from_ptr(fd_ptr as *const i8).to_str().unwrap() - }; + let cage = interface::cagetable_getref(cageid); + // Convert path string address + let path_addr = translate_vmmap_addr(&cage, arg1).unwrap(); + let path = interface::types::get_cstr(path_addr).unwrap(); let mode = arg2 as u32; - - interface::cagetable_getref(cageid) - .chmod_syscall(fd, mode) + // Perform chmod operation through cage implementation + cage.chmod_syscall(path, mode) } DUP_SYSCALL => { let fd = arg1 as i32; + + // Convert second argument to Option if it's within valid range let fd2: Option = if arg1 <= i32::MAX as u64 { Some(arg1 as i32) } else { None }; - + + // Perform dup operation through cage implementation + // File descriptor validation handled by cage layer interface::cagetable_getref(cageid) .dup_syscall(fd, fd2) } @@ -478,6 +550,8 @@ pub fn lind_syscall_api( let fd = arg1 as i32; let fd2 = arg2 as i32; + // Perform dup2 operation through cage implementation + // File descriptor validation handled by cage layer interface::cagetable_getref(cageid) .dup2_syscall(fd, fd2) } @@ -485,34 +559,39 @@ pub fn lind_syscall_api( FCHMOD_SYSCALL => { let fd = arg1 as i32; let mode = arg2 as u32; - + + // Perform fchmod operation through cage implementation + // File descriptor validation handled by cage layer interface::cagetable_getref(cageid) .fchmod_syscall(fd, mode) } FXSTAT_SYSCALL => { let fd = arg1 as i32; - let buf = interface::get_statdatastruct(start_address + arg2).unwrap(); - - interface::cagetable_getref(cageid) - .fstat_syscall(fd, buf) + let cage = interface::cagetable_getref(cageid); + // Convert stat buffer address + let buf_addr = translate_vmmap_addr(&cage, arg2).unwrap(); + let buf = interface::get_statdatastruct(buf_addr).unwrap(); + // Perform fstat operation through cage implementation + // File descriptor validation and actual operation handled by cage layer + cage.fstat_syscall(fd, buf) } UNLINK_SYSCALL => { - let fd_ptr = (start_address + arg1) as *const u8; - - let fd = unsafe { - CStr::from_ptr(fd_ptr as *const i8).to_str().unwrap() - }; - - interface::cagetable_getref(cageid) - .unlink_syscall(fd) + let cage = interface::cagetable_getref(cageid); + // Convert path string address + let path_addr = translate_vmmap_addr(&cage, arg1).unwrap(); + let path = interface::types::get_cstr(path_addr).unwrap(); + // Perform unlink operation through cage implementation + cage.unlink_syscall(path) } LINK_SYSCALL => { - let old_ptr = (start_address + arg1) as *const u8; - let new_ptr = (start_address + arg1) as *const u8; - + let cage = interface::cagetable_getref(cageid); + // Convert old path string address + let old_ptr = translate_vmmap_addr(&cage, arg1).unwrap(); + // Convert new path string address + let new_ptr = translate_vmmap_addr(&cage, arg2).unwrap(); let old_fd= unsafe { CStr::from_ptr(old_ptr as *const i8).to_str().unwrap() }; @@ -520,15 +599,17 @@ pub fn lind_syscall_api( CStr::from_ptr(new_ptr as *const i8).to_str().unwrap() }; - interface::cagetable_getref(cageid) - .link_syscall(old_fd, new_fd) + // Perform link operation through cage implementation + cage.link_syscall(old_fd, new_fd) } LSEEK_SYSCALL => { let virtual_fd = arg1 as i32; let offset = arg2 as isize; let whence = arg3 as i32; - + + // Perform lseek operation through cage implementation + // File descriptor validation and bounds checking handled by cage layer interface::cagetable_getref(cageid) .lseek_syscall(virtual_fd, offset, whence) } @@ -538,105 +619,128 @@ pub fn lind_syscall_api( let request = arg2 as u64; let ptrunion = (start_address + arg3) as *mut u8; + // Perform ioctl operation through cage implementation + // Note: We restrict ioctl operations for security interface::cagetable_getref(cageid) .ioctl_syscall(virtual_fd, request, ptrunion) } TRUNCATE_SYSCALL => { - let fd_ptr = (start_address + arg1) as *const u8; + let cage = interface::cagetable_getref(cageid); + // Convert path string address + let path_addr = translate_vmmap_addr(&cage, arg1).unwrap(); + let path = interface::types::get_cstr(path_addr).unwrap(); let length = arg2 as isize; - - let fd = unsafe { - CStr::from_ptr(fd_ptr as *const i8).to_str().unwrap() - }; - - interface::cagetable_getref(cageid) - .truncate_syscall(fd, length) + // Perform truncate operation through cage implementation + cage.truncate_syscall(path, length) } FTRUNCATE_SYSCALL => { let virtual_fd = arg1 as i32; let length = arg2 as isize; - + + // Perform ftruncate operation through cage implementation + // File descriptor validation handled by cage layer interface::cagetable_getref(cageid) .ftruncate_syscall(virtual_fd, length) } GETDENTS_SYSCALL => { let virtual_fd = arg1 as i32; - let buf = (start_address + arg2) as *mut u8; let nbytes = arg3 as u32; - - interface::cagetable_getref(cageid) - .getdents_syscall(virtual_fd, buf, nbytes) + let cage = interface::cagetable_getref(cageid); + // Convert buffer address + let buf = translate_vmmap_addr(&cage, arg2).unwrap() as *mut u8; + // Perform getdents operation through cage implementation + // File descriptor validation handled by cage layer + cage.getdents_syscall(virtual_fd, buf, nbytes) } STATFS_SYSCALL => { - let fd_ptr = (start_address + arg1) as *const u8; - let rposix_databuf = interface::get_fsdatastruct(start_address + arg2).unwrap(); + let cage = interface::cagetable_getref(cageid); + // Convert path string address + let path_addr = translate_vmmap_addr(&cage, arg1).unwrap(); + let path = interface::types::get_cstr(path_addr).unwrap(); - let fd = unsafe { - CStr::from_ptr(fd_ptr as *const i8).to_str().unwrap() - }; + // Convert buffer address + let buf_addr = translate_vmmap_addr(&cage, arg2).unwrap(); + let rposix_databuf = interface::get_fsdatastruct(buf_addr).unwrap(); - interface::cagetable_getref(cageid) - .statfs_syscall(fd, rposix_databuf) + // Perform statfs operation through cage implementation + cage.statfs_syscall(&path, rposix_databuf) } FCNTL_SYSCALL => { let virtual_fd = arg1 as i32; let cmd = arg2 as i32; let arg = arg3 as i32; - + + // Perform fcntl operation through cage implementation + // File descriptor validation and command validation handled by cage layer interface::cagetable_getref(cageid) .fcntl_syscall(virtual_fd, cmd, arg) } RECV_SYSCALL => { let fd = arg1 as i32; - let buf = (start_address + arg2) as *mut u8; - let buflen = arg3 as usize; + let count = arg3 as usize; + let cage = interface::cagetable_getref(cageid); + // Convert buffer address for writing received data + let buf = translate_vmmap_addr(&cage, arg2).unwrap() as *mut u8; let flag = arg4 as i32; - - interface::cagetable_getref(cageid) - .recv_syscall(fd, buf, buflen, flag) + // Perform recv operation through cage implementation + // File descriptor validation handled by cage layer + cage.recv_syscall(fd, buf, count, flag) } SENDTO_SYSCALL => { let fd = arg1 as i32; - let buf = (start_address + arg2) as *const u8; - let buflen = arg3 as usize; + let count = arg3 as usize; + let cage = interface::cagetable_getref(cageid); + // Convert buffer address for reading data to send + let buf = translate_vmmap_addr(&cage, arg2).unwrap() as *const u8; let flag = arg4 as i32; - + + // Get and validate socket address let addrlen = arg6 as u32; - let addr = interface::get_sockaddr(start_address + arg5, addrlen).unwrap(); - - interface::cagetable_getref(cageid) - .sendto_syscall(fd, buf, buflen, flag, &addr) + let addr = match interface::get_sockaddr(start_address + arg5, addrlen) { + Ok(addr) => addr, + Err(_) => return syscall_error(Errno::EFAULT, "sendto", "invalid socket address"), + }; + // Perform sendto operation through cage implementation + // File descriptor validation handled by cage layer + cage.sendto_syscall(fd, buf, count, flag, &addr) } RECVFROM_SYSCALL => { let fd = arg1 as i32; - let buf = (start_address + arg2) as *mut u8; - let buflen = arg3 as usize; + let count = arg3 as usize; + let cage = interface::cagetable_getref(cageid); + // Convert buffer address for writing received data + let buf = translate_vmmap_addr(&cage, arg2).unwrap() as *mut u8; let flag = arg4 as i32; + // Check if address and length arguments are provided let nullity1 = interface::arg_nullity(arg5); let nullity2 = interface::arg_nullity(arg6); - + + // Handle different cases based on address arguments if nullity1 && nullity2 { - interface::cagetable_getref(cageid) - .recvfrom_syscall(fd, buf, buflen, flag, &mut None) - } + // Both address and length are NULL - simple receive + cage.recvfrom_syscall(fd, buf, count, flag, &mut None) + } else if !(nullity1 || nullity2) { - let mut newsockaddr = interface::GenSockaddr::V4(interface::SockaddrV4::default()); //dummy value, rust would complain if we used an uninitialized value here - - let rv = interface::cagetable_getref(cageid) - .recvfrom_syscall(fd, buf, buflen, flag, &mut Some(&mut newsockaddr)); + // Both address and length are provided + // Create a default sockaddr to store the sender's address + let mut newsockaddr = interface::GenSockaddr::V4(interface::SockaddrV4::default()); + // Perform recvfrom operation + let rv = cage.recvfrom_syscall(fd, buf, count, flag, &mut Some(&mut newsockaddr)); if rv >= 0 { + // Copy address information back to user space on success interface::copy_out_sockaddr(start_address + arg5, start_address + arg6, newsockaddr); } rv } else { + // Invalid case: one argument is NULL while the other isn't syscall_error( Errno::EINVAL, "recvfrom", @@ -648,7 +752,9 @@ pub fn lind_syscall_api( FLOCK_SYSCALL => { let virtual_fd = arg1 as i32; let operation = arg2 as i32; - + + // Perform flock operation through cage implementation + // File descriptor validation handled by cage layer interface::cagetable_getref(cageid) .flock_syscall(virtual_fd, operation) } @@ -657,58 +763,67 @@ pub fn lind_syscall_api( let key = arg1 as i32; let size = arg2 as usize; let shmfig = arg3 as i32; - + + // Perform shmget operation through cage implementation interface::cagetable_getref(cageid) .shmget_syscall(key, size, shmfig) } SHMAT_SYSCALL => { let shmid = arg1 as i32; - let shmaddr = (start_address + arg2) as *mut u8; + let cage = interface::cagetable_getref(cageid); + // Convert virtual address to physical address + let shmaddr = translate_vmmap_addr(&cage, arg2).unwrap() as *mut u8; let shmflg = arg3 as i32; - - interface::cagetable_getref(cageid) - .shmat_syscall(shmid, shmaddr, shmflg) + // Perform shmat operation through cage implementation + cage.shmat_syscall(shmid, shmaddr, shmflg) } SHMDT_SYSCALL => { - let shmaddr = (start_address + arg1) as *mut u8; + let cage = interface::cagetable_getref(cageid); + // Convert virtual address to physical address + let shmaddr = translate_vmmap_addr(&cage, arg1).unwrap() as *mut u8; - interface::cagetable_getref(cageid) - .shmdt_syscall(shmaddr) + // Perform shmdt operation through cage implementation + cage.shmdt_syscall(shmaddr) } MUTEX_DESTROY_SYSCALL => { let mutex_handle = arg1 as i32; - + + // Perform mutex destroy operation through cage implementation interface::cagetable_getref(cageid) .mutex_destroy_syscall(mutex_handle) } MUTEX_LOCK_SYSCALL => { let mutex_handle = arg1 as i32; - + + // Perform mutex lock operation through cage implementation interface::cagetable_getref(cageid) .mutex_lock_syscall(mutex_handle) } MUTEX_TRYLOCK_SYSCALL => { let mutex_handle = arg1 as i32; - + + // Perform mutex trylock operation through cage implementation interface::cagetable_getref(cageid) .mutex_trylock_syscall(mutex_handle) } MUTEX_UNLOCK_SYSCALL => { let mutex_handle = arg1 as i32; - + + // Perform mutex unlock operation through cage implementation interface::cagetable_getref(cageid) .mutex_unlock_syscall(mutex_handle) } COND_DESTROY_SYSCALL => { let cv_handle = arg1 as i32; - + + // Perform condition variable destroy operation through cage implementation interface::cagetable_getref(cageid) .cond_destroy_syscall(cv_handle) } @@ -716,21 +831,24 @@ pub fn lind_syscall_api( COND_WAIT_SYSCALL => { let cv_handle = arg1 as i32; let mutex_handle = arg2 as i32; - + + // Perform condition variable wait operation through cage implementation interface::cagetable_getref(cageid) .cond_wait_syscall(cv_handle, mutex_handle) } COND_BROADCAST_SYSCALL => { let cv_handle = arg1 as i32; - + + // Perform condition variable broadcast operation through cage implementation interface::cagetable_getref(cageid) .cond_broadcast_syscall(cv_handle) } COND_SIGNAL_SYSCALL => { let cv_handle = arg1 as i32; - + + // Perform condition variable signal operation through cage implementation interface::cagetable_getref(cageid) .cond_signal_syscall(cv_handle) } @@ -739,54 +857,60 @@ pub fn lind_syscall_api( let sem_handle = arg1 as u32; let pshared = arg2 as i32; let value = arg3 as u32; - + + // Perform semaphore initialization operation through cage implementation interface::cagetable_getref(cageid) .sem_init_syscall(sem_handle, pshared, value) } SEM_WAIT_SYSCALL => { let sem_handle = arg1 as u32; - + + // Perform semaphore wait operation through cage implementation interface::cagetable_getref(cageid) .sem_wait_syscall(sem_handle) } SEM_TRYWAIT_SYSCALL => { let sem_handle = arg1 as u32; - + + // Perform semaphore try wait operation through cage implementation interface::cagetable_getref(cageid) .sem_trywait_syscall(sem_handle) } SEM_POST_SYSCALL => { let sem_handle = arg1 as u32; - + + // Perform semaphore post operation through cage implementation interface::cagetable_getref(cageid) .sem_post_syscall(sem_handle) } SEM_DESTROY_SYSCALL => { let sem_handle = arg1 as u32; - + + // Perform semaphore destroy operation through cage implementation interface::cagetable_getref(cageid) .sem_destroy_syscall(sem_handle) } SEM_GETVALUE_SYSCALL => { let sem_handle = arg1 as u32; - + + // Perform semaphore get value operation through cage implementation interface::cagetable_getref(cageid) .sem_getvalue_syscall(sem_handle) } PWRITE_SYSCALL => { - let virtual_fd = arg1 as i32; - let buf = (start_address + arg2) as *const u8; let count = arg3 as usize; + let cage = interface::cagetable_getref(cageid); + // Convert buffer address to physical address + let buf = translate_vmmap_addr(&cage, arg2).unwrap() as *const u8; + let virtual_fd = arg1 as i32; let offset = arg4 as i64; - - interface::cagetable_getref(cageid) - .pwrite_syscall(virtual_fd, buf, count, offset) + cage.pwrite_syscall(virtual_fd, buf, count, offset) } GETUID_SYSCALL => { @@ -812,6 +936,7 @@ pub fn lind_syscall_api( EPOLL_CREATE_SYSCALL => { let size = arg1 as i32; + // Perform epoll create operation through cage implementation interface::cagetable_getref(cageid) .epoll_create_syscall(size) } @@ -820,8 +945,11 @@ pub fn lind_syscall_api( let virtual_epfd = arg1 as i32; let op = arg2 as i32; let virtual_fd = arg3 as i32; - let epollevent = interface::get_epollevent(arg4).unwrap(); + // Validate and convert epoll_event structure + let epollevent = interface::get_epollevent(arg4).unwrap(); + + // Perform epoll_ctl operation through cage implementation interface::cagetable_getref(cageid) .epoll_ctl_syscall(virtual_epfd, op, virtual_fd, epollevent) } @@ -840,19 +968,21 @@ pub fn lind_syscall_api( let virtual_fd = arg1 as i32; let level = arg2 as i32; let optname = arg3 as i32; - let optval = (start_address + arg4) as *mut u8; - let optlen = arg5 as u32; - - interface::cagetable_getref(cageid) - .setsockopt_syscall( virtual_fd, level, optname, optval, optlen) + let optlen = arg5 as u32; + let cage = interface::cagetable_getref(cageid); + // Convert user space address to physical address + let optval = translate_vmmap_addr(&cage, arg4).unwrap() as *mut u8; + // Perform setsockopt operation through cage implementation + cage.setsockopt_syscall(virtual_fd, level, optname, optval, optlen) } SHUTDOWN_SYSCALL => { let virtual_fd = arg1 as i32; let how = arg2 as i32; + // Perform shutdown operation through cage implementation interface::cagetable_getref(cageid) - .shutdown_syscall( virtual_fd, how) + .shutdown_syscall(virtual_fd, how) } GETPPID_SYSCALL => { @@ -861,13 +991,13 @@ pub fn lind_syscall_api( } SEND_SYSCALL => { - let virtual_fd = arg1 as i32; - let buf = (start_address + arg4) as *const u8; - let buflen = arg3 as usize; + let fd = arg1 as i32; + let count = arg3 as usize; + let cage = interface::cagetable_getref(cageid); + // Convert user space buffer address to physical address + let buf = translate_vmmap_addr(&cage, arg2).unwrap() as *const u8; let flags = arg4 as i32; - - interface::cagetable_getref(cageid) - .send_syscall( virtual_fd, buf, buflen, flags) + cage.send_syscall(fd, buf, count, flags) } LISTEN_SYSCALL => { @@ -888,18 +1018,21 @@ pub fn lind_syscall_api( } GETHOSTNAME_SYSCALL => { - let name = (start_address + arg1) as *mut u8; - let len = arg2 as isize; - let ret = interface::cagetable_getref(cageid) - .gethostname_syscall(name, len); - ret - } + let len = arg2 as usize; + let cage = interface::cagetable_getref(cageid); + // Convert user space buffer address to physical address + let name = translate_vmmap_addr(&cage, arg1).unwrap() as *mut u8; + // Perform gethostname operation through cage implementation + cage.gethostname_syscall(name, len as isize) + } GETIFADDRS_SYSCALL => { - let buf = (start_address + arg1) as *mut u8; let count = arg2 as usize; - interface::cagetable_getref(cageid) - .getifaddrs_syscall(buf, count) + let cage = interface::cagetable_getref(cageid); + // Convert user space buffer address to physical address + let buf = translate_vmmap_addr(&cage, arg1).unwrap() as *mut u8; + // Perform getifaddrs operation through cage implementation + cage.getifaddrs_syscall(buf, count) } KILL_SYSCALL => { @@ -934,25 +1067,29 @@ pub fn lind_syscall_api( } PIPE_SYSCALL => { - let pipe = interface::get_pipearray(start_address + arg1).unwrap(); - - interface::cagetable_getref(cageid) - .pipe_syscall(pipe) + let cage = interface::cagetable_getref(cageid); + // Convert user space buffer address to physical address + let pipe = interface::get_pipearray(translate_vmmap_addr(&cage, arg1).unwrap() as u64).unwrap(); + cage.pipe_syscall(pipe) } PIPE2_SYSCALL => { - let pipe = interface::get_pipearray(start_address + arg1).unwrap(); + let cage = interface::cagetable_getref(cageid); + // Convert user space buffer address to physical address + let pipe = interface::get_pipearray(translate_vmmap_addr(&cage, arg1).unwrap() as u64).unwrap(); let flag = arg2 as i32; - - interface::cagetable_getref(cageid) - .pipe2_syscall(pipe, flag) + cage.pipe2_syscall(pipe, flag) } GETSOCKNAME_SYSCALL => { let fd = arg1 as i32; - - let mut addr = interface::GenSockaddr::V4(interface::SockaddrV4::default()); //value doesn't matter - + let cage = interface::cagetable_getref(cageid); + // Convert user space buffer addresses to physical addresses + let name_addr = translate_vmmap_addr(&cage, arg2).unwrap(); + let namelen_addr = translate_vmmap_addr(&cage, arg3).unwrap(); + // Initialize default socket address structure + let mut addr = interface::GenSockaddr::V4(interface::SockaddrV4::default()); + // Check for null pointers if interface::arg_nullity(arg2) || interface::arg_nullity(arg3) { return syscall_error( Errno::EINVAL, @@ -960,12 +1097,10 @@ pub fn lind_syscall_api( "Either the address or the length were null", ); } - - let rv = interface::cagetable_getref(cageid) - .getsockname_syscall(fd, &mut Some(&mut addr)); - + let rv = cage.getsockname_syscall(fd, &mut Some(&mut addr)); + // Copy out the address if operation was successful if rv >= 0 { - interface::copy_out_sockaddr(start_address + arg2, start_address + arg3, addr); + interface::copy_out_sockaddr(name_addr, namelen_addr, addr); } rv } @@ -974,31 +1109,31 @@ pub fn lind_syscall_api( let virtual_fd = arg1 as i32; let level = arg2 as i32; let optname = arg3 as i32; - - let optval_ptr = (start_address + arg4) as *mut i32; + let cage = interface::cagetable_getref(cageid); + // Convert user space buffer address to physical address + let optval_ptr = translate_vmmap_addr(&cage, arg4).unwrap() as *mut i32; let optval = unsafe { &mut *optval_ptr }; - - interface::cagetable_getref(cageid) - .getsockopt_syscall(virtual_fd, level, optname, optval) + cage.getsockopt_syscall(virtual_fd, level, optname, optval) } SOCKETPAIR_SYSCALL => { let domain = arg1 as i32; let _type = arg2 as i32; let protocol = arg3 as i32; - let virtual_socket_vector = interface::get_sockpair(start_address + arg4).unwrap(); - - interface::cagetable_getref(cageid) - .socketpair_syscall(domain, _type, protocol, virtual_socket_vector) + let cage = interface::cagetable_getref(cageid); + // Convert user space buffer address to physical address + let virtual_socket_vector = interface::get_sockpair(translate_vmmap_addr(&cage, arg4).unwrap() as u64).unwrap(); + cage.socketpair_syscall(domain, _type, protocol, virtual_socket_vector) } POLL_SYSCALL => { let nfds = arg2 as u64; - let pollfds = interface::get_pollstruct_slice(start_address + arg1, nfds as usize).unwrap(); + let cage = interface::cagetable_getref(cageid); + // Convert user space buffer address to physical address + let addr = translate_vmmap_addr(&cage, arg1).unwrap(); + let pollfds = interface::get_pollstruct_slice(addr, nfds as usize).unwrap(); let timeout = arg3 as i32; - - interface::cagetable_getref(cageid) - .poll_syscall(pollfds, nfds, timeout) + cage.poll_syscall(pollfds, nfds, timeout) } GETPID_SYSCALL => { @@ -1013,48 +1148,90 @@ pub fn lind_syscall_api( } FUTEX_SYSCALL => { - let uaddr = (start_address + arg1) as u64; + let cage = interface::cagetable_getref(cageid); + // Convert user space buffer address to physical address + let uaddr = translate_vmmap_addr(&cage, arg1).unwrap(); + // Convert remaining arguments let futex_op = arg2 as u32; let val = arg3 as u32; let timeout = arg4 as u32; let uaddr2 = arg5 as u32; let val3 = arg6 as u32; - - interface::cagetable_getref(cageid) - .futex_syscall(uaddr, futex_op, val, timeout, uaddr2, val3) + cage.futex_syscall(uaddr, futex_op, val, timeout, uaddr2, val3) } NANOSLEEP_TIME64_SYSCALL => { let clockid = arg1 as u32; let flags = arg2 as i32; - let req = (start_address + arg3) as usize; - let rem = (start_address + arg4) as usize; - - interface::cagetable_getref(cageid) - .nanosleep_time64_syscall(clockid, flags, req, rem) + let cage = interface::cagetable_getref(cageid); + // Convert user space buffer address to physical address + let req = translate_vmmap_addr(&cage, arg3).unwrap() as usize; + let rem = translate_vmmap_addr(&cage, arg4).unwrap() as usize; + cage.nanosleep_time64_syscall(clockid, flags, req, rem) } WAIT_SYSCALL => { - let mut status = interface::get_i32_ref(start_address + arg1).unwrap(); - - interface::cagetable_getref(cageid) - .wait_syscall(&mut status) + let cage = interface::cagetable_getref(cageid); + // Convert user space buffer address to physical address + let status_addr = translate_vmmap_addr(&cage, arg1).unwrap() as u64; + let status = interface::get_i32_ref(status_addr).unwrap(); + cage.wait_syscall(status) } WAITPID_SYSCALL => { let pid = arg1 as i32; - let mut status = interface::get_i32_ref(start_address + arg2).unwrap(); + let cage = interface::cagetable_getref(cageid); + // Convert user space buffer address to physical address + let status_addr = translate_vmmap_addr(&cage, arg2).unwrap(); + let status = interface::get_i32_ref(status_addr).unwrap(); let options = arg3 as i32; - interface::cagetable_getref(cageid) - .waitpid_syscall(pid, &mut status, options) + cage.waitpid_syscall(pid, status, options) + } + + SBRK_SYSCALL => { + let brk = arg1 as i32; + + interface::sbrk_handler(cageid, brk) as i32 + } + + BRK_SYSCALL => { + let brk = arg1 as u32; + + interface::brk_handler(cageid, brk) } _ => -1, // Return -1 for unknown syscalls }; + ret } +// set the wasm linear memory base address to vmmap +pub fn init_vmmap_helper(cageid: u64, base_address: usize, program_break: Option) { + let cage = interface::cagetable_getref(cageid); + let mut vmmap = cage.vmmap.write(); + vmmap.set_base_address(base_address); + if program_break.is_some() { + vmmap.set_program_break(program_break.unwrap()); + } +} + +// clone the cage memory. Invoked by wasmtime after cage is forked +pub fn fork_vmmap_helper(parent_cageid: u64, child_cageid: u64) { + let parent_cage = interface::cagetable_getref(parent_cageid); + let child_cage = interface::cagetable_getref(child_cageid); + let parent_vmmap = parent_cage.vmmap.read(); + let child_vmmap = child_cage.vmmap.read(); + + interface::fork_vmmap(&parent_vmmap, &child_vmmap); + + // update program break for child + drop(child_vmmap); + let mut child_vmmap = child_cage.vmmap.write(); + child_vmmap.set_program_break(parent_vmmap.program_break); +} + #[no_mangle] pub fn lindcancelinit(cageid: u64) { let cage = interface::cagetable_getref(cageid); @@ -1141,6 +1318,7 @@ pub fn lindrustinit(verbosity: isize) { sigset: interface::RustHashMap::new(), main_threadid: interface::RustAtomicU64::new(0), interval_timer: interface::IntervalTimer::new(0), + vmmap: interface::RustLock::new(Vmmap::new()), // Initialize empty virtual memory map for new process zombies: interface::RustLock::new(vec![]), child_num: interface::RustAtomicU64::new(0), }; @@ -1158,8 +1336,8 @@ pub fn lindrustinit(verbosity: isize) { // Standard output (fd = 1) is redirected to /dev/null // Standard error (fd = 2) is set to copy of stdout unsafe { - libc::open(dev_null.as_ptr(), libc::O_RDONLY); - libc::open(dev_null.as_ptr(), libc::O_WRONLY); + libc::open(dev_null.as_ptr(), O_RDONLY); + libc::open(dev_null.as_ptr(), O_WRONLY); libc::dup(1); } @@ -1189,6 +1367,7 @@ pub fn lindrustinit(verbosity: isize) { sigset: interface::RustHashMap::new(), main_threadid: interface::RustAtomicU64::new(0), interval_timer: interface::IntervalTimer::new(1), + vmmap: interface::RustLock::new(Vmmap::new()), // Initialize empty virtual memory map for new process zombies: interface::RustLock::new(vec![]), child_num: interface::RustAtomicU64::new(0), }; diff --git a/src/RawPOSIX/src/safeposix/mod.rs b/src/RawPOSIX/src/safeposix/mod.rs index 9957c397..249dcdd3 100644 --- a/src/RawPOSIX/src/safeposix/mod.rs +++ b/src/RawPOSIX/src/safeposix/mod.rs @@ -3,3 +3,4 @@ pub mod dispatcher; pub mod filesystem; pub mod shm; pub mod syscalls; +pub mod vmmap; diff --git a/src/RawPOSIX/src/safeposix/shm.rs b/src/RawPOSIX/src/safeposix/shm.rs index a450af6e..e0329658 100644 --- a/src/RawPOSIX/src/safeposix/shm.rs +++ b/src/RawPOSIX/src/safeposix/shm.rs @@ -1,8 +1,12 @@ // Filesystem metadata struct #![allow(dead_code)] -use super::syscalls::fs_constants::*; -use super::syscalls::sys_constants::*; +use crate::constants::{ + PROT_NONE, PROT_READ, PROT_WRITE, + MAP_SHARED, MAP_PRIVATE, MAP_FIXED, MAP_ANONYMOUS, + SHM_RDONLY, SHM_DEST, +}; + use crate::interface; use libc::*; @@ -91,7 +95,7 @@ impl ShmSegment { shmaddr, self.size as usize, prot, - MAP_SHARED | MAP_FIXED, + (MAP_SHARED as i32) | (MAP_FIXED as i32), fobjfdno, 0, ) @@ -104,7 +108,7 @@ impl ShmSegment { shmaddr, self.size as usize, PROT_NONE, - MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, + (MAP_PRIVATE as i32) | (MAP_ANONYMOUS as i32) | (MAP_FIXED as i32), -1, 0, ); @@ -118,7 +122,7 @@ impl ShmSegment { } } interface::RustHashEntry::Vacant(_) => { - panic!("Cage not avilable in segment attached cages"); + panic!("Cage not available in segment attached cages"); } }; } diff --git a/src/RawPOSIX/src/safeposix/syscalls/fs_calls.rs b/src/RawPOSIX/src/safeposix/syscalls/fs_calls.rs index a7f00de2..515f51c5 100644 --- a/src/RawPOSIX/src/safeposix/syscalls/fs_calls.rs +++ b/src/RawPOSIX/src/safeposix/syscalls/fs_calls.rs @@ -1,10 +1,19 @@ #![allow(dead_code)] use std::fs; -use super::fs_constants; -// File system related system calls -use super::fs_constants::*; -use super::sys_constants; + +// Add constants imports +use crate::constants::{ + S_IRWXU, S_IRWXG, S_IRWXO, + PROT_READ, PROT_WRITE, + O_RDONLY, O_WRONLY, O_RDWR, O_CREAT, O_TRUNC, O_CLOEXEC, + MAP_SHARED, MAP_PRIVATE, + SEEK_SET, SEEK_CUR, SEEK_END, + SHMMIN, SHMMAX, SHM_RDONLY, SHM_DEST, + DEFAULT_UID, DEFAULT_GID, + SEM_VALUE_MAX, STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO +}; + use crate::interface; use crate::interface::get_errno; use crate::interface::handle_errno; @@ -27,6 +36,7 @@ use std::ptr; use std::mem; use crate::fdtables; +use crate::safeposix::cage::Cage; static LIND_ROOT: &str = "/home/lind-wasm/src/RawPOSIX/tmp"; @@ -768,19 +778,24 @@ impl Cage { prot: i32, flags: i32, virtual_fd: i32, - off: i64, - ) -> i32 { + off: i64 + ) -> usize { if virtual_fd != -1 { match fdtables::translate_virtual_fd(self.cageid, virtual_fd as u64) { Ok(kernel_fd) => { let ret = unsafe { - ((libc::mmap(addr as *mut c_void, len, prot, flags, kernel_fd.underfd as i32, off) as i64) - & 0xffffffff) as i32 + (libc::mmap(addr as *mut c_void, len, prot, flags, kernel_fd.underfd as i32, off) as i64) }; - return ret; + + // Check if mmap failed and return the appropriate error if so + if ret == -1 { + return syscall_error(Errno::EINVAL, "mmap", "mmap failed with invalid flags") as usize; + } + + ret as usize }, Err(_e) => { - return syscall_error(Errno::EBADF, "mmap", "Bad File Descriptor"); + return syscall_error(Errno::EBADF, "mmap", "Bad File Descriptor") as usize; } } } else { @@ -790,9 +805,10 @@ impl Cage { }; // Check if mmap failed and return the appropriate error if so if ret == -1 { - return syscall_error(Errno::EINVAL, "mmap", "mmap failed with invalid flags"); + return syscall_error(Errno::EINVAL, "mmap", "mmap failed with invalid flags") as usize; } - return (ret & 0xffffffff) as i32; + + ret as usize } } @@ -1177,7 +1193,7 @@ impl Cage { let metadata = &SHM_METADATA; let prot: i32; if let Some(mut segment) = metadata.shmtable.get_mut(&shmid) { - if 0 != (shmflg & fs_constants::SHM_RDONLY) { + if 0 != (shmflg & SHM_RDONLY) { prot = PROT_READ; } else { prot = PROT_READ | PROT_WRITE; @@ -1911,9 +1927,9 @@ pub fn kernel_close(fdentry: fdtables::FDTableEntry, _count: u64) { // TODO: // Need to update once we merge with vmmap-alice - if kernel_fd == fs_constants::STDIN_FILENO - || kernel_fd == fs_constants::STDOUT_FILENO - || kernel_fd == fs_constants::STDERR_FILENO { + if kernel_fd == STDIN_FILENO + || kernel_fd == STDOUT_FILENO + || kernel_fd == STDERR_FILENO { return; } diff --git a/src/RawPOSIX/src/safeposix/syscalls/fs_constants.rs b/src/RawPOSIX/src/safeposix/syscalls/fs_constants.rs deleted file mode 100644 index 342ca101..00000000 --- a/src/RawPOSIX/src/safeposix/syscalls/fs_constants.rs +++ /dev/null @@ -1,203 +0,0 @@ -// File system related constants -#![allow(dead_code)] -#![allow(unused_variables)] - -use crate::interface; - -/// TODO: -/// This is a temporary location for those three constants. Need to move after -/// merging with vmmap-alice -pub const STDIN_FILENO: i32 = 0; // File descriptor for standard input -pub const STDOUT_FILENO: i32 = 1; // File descriptor for standard output -pub const STDERR_FILENO: i32 = 2; // File descriptor for standard error - -// Define constants using static or const -// Imported into fs_calls file -// pub const DT_UNKNOWN: u8 = 0; - -// pub const STARTINGFD: i32 = 0; -// pub const MAXFD: i32 = 1024; -// pub const STARTINGPIPE: i32 = 0; -// pub const MAXPIPE: i32 = 1024; - -// pub const ROOTDIRECTORYINODE: usize = 1; -// pub const STREAMINODE: usize = 2; - -// pub const PIPE_CAPACITY: usize = 65536; - -// pub const F_OK: u32 = 0; -// pub const X_OK: u32 = 1; -// pub const W_OK: u32 = 2; -// pub const R_OK: u32 = 4; - -// pub const O_RDONLY: i32 = 0o0; -// pub const O_WRONLY: i32 = 0o1; -// pub const O_RDWR: i32 = 0o2; -// pub const O_RDWRFLAGS: i32 = 0o3; - -// pub const O_CREAT: i32 = 0o100; -// pub const O_EXCL: i32 = 0o200; -// pub const O_NOCTTY: i32 = 0o400; -// pub const O_TRUNC: i32 = 0o1000; -// pub const O_APPEND: i32 = 0o2000; -// pub const O_NONBLOCK: i32 = 0o4000; -// // O_NDELAY=O_NONBLOCK -// pub const O_SYNC: i32 = 0o10000; -// // O_FSYNC=O_SYNC -// pub const O_ASYNC: i32 = 0o20000; -// pub const O_CLOEXEC: i32 = 0o2000000; - -// pub const DEFAULTTIME: u64 = 1323630836; - -//Standard flag combinations -pub const S_IRWXA: u32 = 0o777; -pub const S_IRWXU: u32 = 0o700; -pub const S_IRUSR: u32 = 0o400; -pub const S_IWUSR: u32 = 0o200; -pub const S_IXUSR: u32 = 0o100; -pub const S_IRWXG: u32 = 0o070; -pub const S_IRGRP: u32 = 0o040; -pub const S_IWGRP: u32 = 0o020; -pub const S_IXGRP: u32 = 0o010; -pub const S_IRWXO: u32 = 0o007; -pub const S_IROTH: u32 = 0o004; -pub const S_IWOTH: u32 = 0o002; -pub const S_IXOTH: u32 = 0o001; - -// //Commands for FCNTL -// pub const F_DUPFD: i32 = 0; -// pub const F_GETFD: i32 = 1; -// pub const F_SETFD: i32 = 2; -// pub const F_GETFL: i32 = 3; -// pub const F_SETFL: i32 = 4; -// pub const F_GETLK: i32 = 5; -// pub const F_GETLK64: i32 = 5; -// pub const F_SETLK: i32 = 6; -// pub const F_SETLK64: i32 = 6; -// pub const F_SETLKW: i32 = 7; -// pub const F_SETLKW64: i32 = 7; -// pub const F_SETOWN: i32 = 8; -// pub const F_GETOWN: i32 = 9; -// pub const F_SETSIG: i32 = 10; -// pub const F_GETSIG: i32 = 11; -// pub const F_SETLEASE: i32 = 1024; -// pub const F_GETLEASE: i32 = 1025; -// pub const F_NOTIFY: i32 = 1026; - -// //Commands for IOCTL -// pub const FIONBIO: u32 = 21537; -// pub const FIOASYNC: u32 = 21586; - -// //File types for open/stat etc. -// pub const S_IFBLK: i32 = 0o60000; -// pub const S_IFCHR: i32 = 0o20000; -// pub const S_IFDIR: i32 = 0o40000; -// pub const S_IFIFO: i32 = 0o10000; -// pub const S_IFLNK: i32 = 0o120000; -// pub const S_IFREG: i32 = 0o100000; -// pub const S_IFSOCK: i32 = 0o140000; -// pub const S_FILETYPEFLAGS: i32 = 0o170000; - -// //for flock syscall -// pub const LOCK_SH: i32 = 1; -// pub const LOCK_EX: i32 = 2; -// pub const LOCK_UN: i32 = 8; -// pub const LOCK_NB: i32 = 4; -// //for mmap/munmap syscall -// pub const MAP_SHARED: i32 = 1; -// pub const MAP_PRIVATE: i32 = 2; -// pub const MAP_FIXED: i32 = 16; -// pub const MAP_ANONYMOUS: i32 = 32; -// pub const MAP_HUGE_SHIFT: i32 = 26; -// pub const MAP_HUGETLB: i32 = 262144; - -// pub const PROT_NONE: i32 = 0; -// pub const PROT_READ: i32 = 1; -// pub const PROT_WRITE: i32 = 2; -// pub const PROT_EXEC: i32 = 4; - -// pub const SEEK_SET: i32 = 0; -// pub const SEEK_CUR: i32 = 1; -// pub const SEEK_END: i32 = 2; - -// pub const IPC_PRIVATE: i32 = 0o0; -// pub const IPC_CREAT: i32 = 0o1000; -// pub const IPC_EXCL: i32 = 0o2000; - -// pub const IPC_RMID: i32 = 0; -// pub const IPC_SET: i32 = 1; -// pub const IPC_STAT: i32 = 2; - -pub const SHM_DEST: i32 = 0o1000; -pub const SHM_LOCKED: i32 = 0o2000; -pub const SHM_HUGETLB: i32 = 0o4000; - -pub const SHM_R: i32 = 0o400; -pub const SHM_W: i32 = 0o200; -pub const SHM_RDONLY: i32 = 0o10000; -pub const SHM_RND: i32 = 0o20000; -pub const SHM_REMAP: i32 = 0o40000; -pub const SHM_EXEC: i32 = 0o100000; - -pub const SHMMIN: u32 = 1; -pub const SHMMNI: u32 = 4096; -pub const SHMMAX: u32 = 4278190079; // (ULONG_MAX - (1UL << 24)) -pub const SHMALL: u32 = 4278190079; // (ULONG_MAX - (1UL << 24)); -pub const SHMSEG: u32 = SHMMNI; - -pub const SEM_VALUE_MAX: u32 = 2147483647; - -// //device info for char files -// #[derive(interface::SerdeSerialize, interface::SerdeDeserialize, PartialEq, Eq, Debug)] -// pub struct DevNo { -// pub major: u32, -// pub minor: u32, -// } -// pub const NULLDEVNO: DevNo = DevNo { major: 1, minor: 3 }; -// pub const ZERODEVNO: DevNo = DevNo { major: 1, minor: 5 }; -// pub const RANDOMDEVNO: DevNo = DevNo { major: 1, minor: 8 }; -// pub const URANDOMDEVNO: DevNo = DevNo { major: 1, minor: 9 }; - -// pub const FILEDATAPREFIX: &str = "linddata."; - -// pub fn is_reg(mode: u32) -> bool { -// (mode as i32 & S_FILETYPEFLAGS) == S_IFREG -// } - -// pub fn is_chr(mode: u32) -> bool { -// (mode as i32 & S_FILETYPEFLAGS) == S_IFCHR -// } - -// pub fn is_dir(mode: u32) -> bool { -// (mode as i32 & S_FILETYPEFLAGS) == S_IFDIR -// } - -// pub fn is_wronly(flags: i32) -> bool { -// (flags & O_RDWRFLAGS) == O_WRONLY -// } -// pub fn is_rdonly(flags: i32) -> bool { -// (flags & O_RDWRFLAGS) == O_RDONLY -// } - -// //the same as the glibc makedev -// pub fn makedev(dev: &DevNo) -> u64 { -// ((dev.major as u64 & 0x00000fff) << 8) -// | ((dev.major as u64 & 0xfffff000) << 32) -// | ((dev.minor as u64 & 0x000000ff) << 0) -// | ((dev.minor as u64 & 0xffffff00) << 12) -// } - -// //the same as the glibc major and minor functions -// pub fn major(devnum: u64) -> u32 { -// (((devnum & 0x00000000000fff00) >> 8) | ((devnum & 0xfffff00000000000) >> 32)) as u32 -// } -// pub fn minor(devnum: u64) -> u32 { -// (((devnum & 0x00000000000000ff) >> 0) | ((devnum & 0x00000ffffff00000) >> 12)) as u32 -// } - -// pub fn devtuple(devnum: u64) -> DevNo { -// DevNo { -// major: major(devnum), -// minor: minor(devnum), -// } -// } diff --git a/src/RawPOSIX/src/safeposix/syscalls/mod.rs b/src/RawPOSIX/src/safeposix/syscalls/mod.rs index 51b60b43..efb27fb2 100644 --- a/src/RawPOSIX/src/safeposix/syscalls/mod.rs +++ b/src/RawPOSIX/src/safeposix/syscalls/mod.rs @@ -1,12 +1,6 @@ pub mod fs_calls; -pub mod fs_constants; pub mod net_calls; -pub mod net_constants; pub mod sys_calls; -pub mod sys_constants; pub use fs_calls::*; -pub use fs_constants::*; pub use net_calls::*; -pub use net_constants::*; pub use sys_calls::*; -pub use sys_constants::*; diff --git a/src/RawPOSIX/src/safeposix/syscalls/net_calls.rs b/src/RawPOSIX/src/safeposix/syscalls/net_calls.rs index 1fcc75f4..e0f1dd4d 100644 --- a/src/RawPOSIX/src/safeposix/syscalls/net_calls.rs +++ b/src/RawPOSIX/src/safeposix/syscalls/net_calls.rs @@ -1,9 +1,9 @@ #![allow(dead_code)] -use super::net_constants; +use crate::constants::net_constants; use crate::{interface::FdSet, safeposix::cage::*}; use crate::interface::*; use crate::interface; -use super::sys_constants; +use crate::constants::sys_constants; use crate::fdtables::{self, FDTableEntry}; diff --git a/src/RawPOSIX/src/safeposix/syscalls/net_constants.rs b/src/RawPOSIX/src/safeposix/syscalls/net_constants.rs deleted file mode 100644 index c600aeeb..00000000 --- a/src/RawPOSIX/src/safeposix/syscalls/net_constants.rs +++ /dev/null @@ -1,392 +0,0 @@ -// Network related constants -#![allow(dead_code)] -#![allow(non_upper_case_globals)] - -use crate::interface; - -//used for gethostname syscall -pub const DEFAULT_HOSTNAME: &str = "Lind"; -pub const BLOCK_TIME: interface::RustDuration = interface::RustDuration::from_micros(100); - -pub const UDSOCK_CAPACITY: usize = 212992; - -// Define constants using static or const -// Imported into net_calls file - -pub const SOCK_STREAM: i32 = 1; //stream socket -pub const SOCK_DGRAM: i32 = 2; //datagram socket -pub const SOCK_RAW: i32 = 3; //raw protocol interface -pub const SOCK_RDM: i32 = 4; //reliably delivered message -pub const SOCK_SEQPACKET: i32 = 5; //sequenced packet stream -pub const SOCK_CLOEXEC: i32 = 0o02000000; // Atomically set close-on-exec -pub const SOCK_NONBLOCK: i32 = 0o00004000; // Mark as non-blocking - -/* Supported address families. */ -pub const AF_UNSPEC: i32 = 0; -pub const AF_UNIX: i32 = 1; /* Unix domain sockets */ -pub const AF_LOCAL: i32 = 1; /* POSIX name for AF_UNIX */ -pub const AF_INET: i32 = 2; /* Internet IP Protocol */ -pub const AF_AX25: i32 = 3; /* Amateur Radio AX.25 */ -pub const AF_IPX: i32 = 4; /* Novell IPX */ -pub const AF_APPLETALK: i32 = 5; /* AppleTalk DDP */ -pub const AF_NETROM: i32 = 6; /* Amateur Radio NET/ROM */ -pub const AF_BRIDGE: i32 = 7; /* Multiprotocol bridge */ -pub const AF_ATMPVC: i32 = 8; /* ATM PVCs */ -pub const AF_X25: i32 = 9; /* Reserved for X.25 project */ -pub const AF_INET6: i32 = 10; /* IP version 6 */ -pub const AF_ROSE: i32 = 11; /* Amateur Radio X.25 PLP */ -pub const AF_DECnet: i32 = 12; /* Reserved for DECnet project */ -pub const AF_NETBEUI: i32 = 13; /* Reserved for 802.2LLC project*/ -pub const AF_SECURITY: i32 = 14; /* Security callback pseudo AF */ -pub const AF_KEY: i32 = 15; /* PF_KEY key management API */ -pub const AF_NETLINK: i32 = 16; -pub const AF_ROUTE: i32 = AF_NETLINK; /* Alias to emulate 4.4BSD */ -pub const AF_PACKET: i32 = 17; /* Packet family */ -pub const AF_ASH: i32 = 18; /* Ash */ -pub const AF_ECONET: i32 = 19; /* Acorn Econet */ -pub const AF_ATMSVC: i32 = 20; /* ATM SVCs */ -pub const AF_RDS: i32 = 21; /* RDS sockets */ -pub const AF_SNA: i32 = 22; /* Linux SNA Project (nutters!) */ -pub const AF_IRDA: i32 = 23; /* IRDA sockets */ -pub const AF_PPPOX: i32 = 24; /* PPPoX sockets */ -pub const AF_WANPIPE: i32 = 25; /* Wanpipe API Sockets */ -pub const AF_LLC: i32 = 26; /* Linux LLC */ -pub const AF_IB: i32 = 27; /* Native InfiniBand address */ -pub const AF_MPLS: i32 = 28; /* MPLS */ -pub const AF_CAN: i32 = 29; /* Controller Area Network */ -pub const AF_TIPC: i32 = 30; /* TIPC sockets */ -pub const AF_BLUETOOTH: i32 = 31; /* Bluetooth sockets */ -pub const AF_IUCV: i32 = 32; /* IUCV sockets */ -pub const AF_RXRPC: i32 = 33; /* RxRPC sockets */ -pub const AF_ISDN: i32 = 34; /* mISDN sockets */ -pub const AF_PHONET: i32 = 35; /* Phonet sockets */ -pub const AF_IEEE802154: i32 = 36; /* IEEE802154 sockets */ -pub const AF_CAIF: i32 = 37; /* CAIF sockets */ -pub const AF_ALG: i32 = 38; /* Algorithm sockets */ -pub const AF_NFC: i32 = 39; /* NFC sockets */ -pub const AF_VSOCK: i32 = 40; /* vSockets */ -pub const AF_KCM: i32 = 41; /* Kernel Connection Multiplexor*/ -pub const AF_QIPCRTR: i32 = 42; /* Qualcomm IPC Router */ -pub const AF_SMC: i32 = 43; /* smc sockets: reserve number for - * PF_SMC protocol family that - * reuses AF_INET address family - */ -pub const AF_XDP: i32 = 44; /* XDP sockets */ -pub const AF_MCTP: i32 = 45; /* Management component - * transport protocol - */ - -pub const AF_MAX: i32 = 46; /* For now.. */ - -/* Protocol families, same as address families. */ -pub const PF_UNSPEC: i32 = AF_UNSPEC; -pub const PF_UNIX: i32 = AF_UNIX; -pub const PF_LOCAL: i32 = AF_LOCAL; -pub const PF_INET: i32 = AF_INET; -pub const PF_AX25: i32 = AF_AX25; -pub const PF_IPX: i32 = AF_IPX; -pub const PF_APPLETALK: i32 = AF_APPLETALK; -pub const PF_NETROM: i32 = AF_NETROM; -pub const PF_BRIDGE: i32 = AF_BRIDGE; -pub const PF_ATMPVC: i32 = AF_ATMPVC; -pub const PF_X25: i32 = AF_X25; -pub const PF_INET6: i32 = AF_INET6; -pub const PF_ROSE: i32 = AF_ROSE; -pub const PF_DECnet: i32 = AF_DECnet; -pub const PF_NETBEUI: i32 = AF_NETBEUI; -pub const PF_SECURITY: i32 = AF_SECURITY; -pub const PF_KEY: i32 = AF_KEY; -pub const PF_NETLINK: i32 = AF_NETLINK; -pub const PF_ROUTE: i32 = AF_ROUTE; -pub const PF_PACKET: i32 = AF_PACKET; -pub const PF_ASH: i32 = AF_ASH; -pub const PF_ECONET: i32 = AF_ECONET; -pub const PF_ATMSVC: i32 = AF_ATMSVC; -pub const PF_RDS: i32 = AF_RDS; -pub const PF_SNA: i32 = AF_SNA; -pub const PF_IRDA: i32 = AF_IRDA; -pub const PF_PPPOX: i32 = AF_PPPOX; -pub const PF_WANPIPE: i32 = AF_WANPIPE; -pub const PF_LLC: i32 = AF_LLC; -pub const PF_IB: i32 = AF_IB; -pub const PF_MPLS: i32 = AF_MPLS; -pub const PF_CAN: i32 = AF_CAN; -pub const PF_TIPC: i32 = AF_TIPC; -pub const PF_BLUETOOTH: i32 = AF_BLUETOOTH; -pub const PF_IUCV: i32 = AF_IUCV; -pub const PF_RXRPC: i32 = AF_RXRPC; -pub const PF_ISDN: i32 = AF_ISDN; -pub const PF_PHONET: i32 = AF_PHONET; -pub const PF_IEEE802154: i32 = AF_IEEE802154; -pub const PF_CAIF: i32 = AF_CAIF; -pub const PF_ALG: i32 = AF_ALG; -pub const PF_NFC: i32 = AF_NFC; -pub const PF_VSOCK: i32 = AF_VSOCK; -pub const PF_KCM: i32 = AF_KCM; -pub const PF_QIPCRTR: i32 = AF_QIPCRTR; -pub const PF_SMC: i32 = AF_SMC; -pub const PF_XDP: i32 = AF_XDP; -pub const PF_MCTP: i32 = AF_MCTP; -pub const PF_MAX: i32 = AF_MAX; - -// protocols... - -pub const IPPROTO_IP: i32 = 0; // dummy for IP -pub const IPPROTO_ICMP: i32 = 1; // control message protocol -pub const IPPROTO_IGMP: i32 = 2; // group mgmt protocol -pub const IPPROTO_GGP: i32 = 3; // gateway^2 (deprecated) -pub const IPPROTO_IPV4: i32 = 4; // IPv4 encapsulation -pub const IPPROTO_IPIP: i32 = IPPROTO_IPV4; // for compatibility -pub const IPPROTO_TCP: i32 = 6; // tcp -pub const IPPROTO_ST: i32 = 7; // Stream protocol II -pub const IPPROTO_EGP: i32 = 8; // exterior gateway protocol -pub const IPPROTO_PIGP: i32 = 9; // private interior gateway -pub const IPPROTO_RCCMON: i32 = 10; // BBN RCC Monitoring -pub const IPPROTO_NVPII: i32 = 11; // network voice protocol -pub const IPPROTO_PUP: i32 = 12; // pup -pub const IPPROTO_ARGUS: i32 = 13; // Argus -pub const IPPROTO_EMCON: i32 = 14; // EMCON -pub const IPPROTO_XNET: i32 = 15; // Cross Net Debugger -pub const IPPROTO_CHAOS: i32 = 16; // Chaos -pub const IPPROTO_UDP: i32 = 17; // user datagram protocol -pub const IPPROTO_MUX: i32 = 18; // Multiplexing -pub const IPPROTO_MEAS: i32 = 19; // DCN Measurement Subsystems -pub const IPPROTO_HMP: i32 = 20; // Host Monitoring -pub const IPPROTO_PRM: i32 = 21; // Packet Radio Measurement -pub const IPPROTO_IDP: i32 = 22; // xns idp -pub const IPPROTO_TRUNK1: i32 = 23; // Trunk-1 -pub const IPPROTO_TRUNK2: i32 = 24; // Trunk-2 -pub const IPPROTO_LEAF1: i32 = 25; // Leaf-1 -pub const IPPROTO_LEAF2: i32 = 26; // Leaf-2 -pub const IPPROTO_RDP: i32 = 27; // Reliable Data -pub const IPPROTO_IRTP: i32 = 28; // Reliable Transaction -pub const IPPROTO_TP: i32 = 29; // tp-4 w/ class negotiation -pub const IPPROTO_BLT: i32 = 30; // Bulk Data Transfer -pub const IPPROTO_NSP: i32 = 31; // Network Services -pub const IPPROTO_INP: i32 = 32; // Merit Internodal -pub const IPPROTO_SEP: i32 = 33; // Sequential Exchange -pub const IPPROTO_3PC: i32 = 34; // Third Party Connect -pub const IPPROTO_IDPR: i32 = 35; // InterDomain Policy Routing -pub const IPPROTO_XTP: i32 = 36; // XTP -pub const IPPROTO_DDP: i32 = 37; // Datagram Delivery -pub const IPPROTO_CMTP: i32 = 38; // Control Message Transport -pub const IPPROTO_TPXX: i32 = 39; // TP++ Transport -pub const IPPROTO_IL: i32 = 40; // IL transport protocol -pub const IPPROTO_IPV6: i32 = 41; // IP6 header -pub const IPPROTO_SDRP: i32 = 42; // Source Demand Routing -pub const IPPROTO_ROUTING: i32 = 43; // IP6 routing header -pub const IPPROTO_FRAGMENT: i32 = 44; // IP6 fragmentation header -pub const IPPROTO_IDRP: i32 = 45; // InterDomain Routing -pub const IPPROTO_RSVP: i32 = 46; // resource reservation -pub const IPPROTO_GRE: i32 = 47; // General Routing Encap. -pub const IPPROTO_MHRP: i32 = 48; // Mobile Host Routing -pub const IPPROTO_BHA: i32 = 49; // BHA -pub const IPPROTO_ESP: i32 = 50; // IP6 Encap Sec. Payload -pub const IPPROTO_AH: i32 = 51; // IP6 Auth Header -pub const IPPROTO_INLSP: i32 = 52; // Integ. Net Layer Security -pub const IPPROTO_SWIPE: i32 = 53; // IP with encryption -pub const IPPROTO_NHRP: i32 = 54; // Next Hop Resolution - // 55-57: Unassigned -pub const IPPROTO_ICMPV6: i32 = 58; // ICMP6 -pub const IPPROTO_NONE: i32 = 59; // IP6 no next header -pub const IPPROTO_DSTOPTS: i32 = 60; // IP6 destination option -pub const IPPROTO_AHIP: i32 = 61; // any host internal protocol -pub const IPPROTO_CFTP: i32 = 62; // CFTP -pub const IPPROTO_HELLO: i32 = 63; // "hello" routing protocol -pub const IPPROTO_SATEXPAK: i32 = 64; // SATNET/Backroom EXPAK -pub const IPPROTO_KRYPTOLAN: i32 = 65; // Kryptolan -pub const IPPROTO_RVD: i32 = 66; // Remote Virtual Disk -pub const IPPROTO_IPPC: i32 = 67; // Pluribus Packet Core -pub const IPPROTO_ADFS: i32 = 68; // Any distributed FS -pub const IPPROTO_SATMON: i32 = 69; // Satnet Monitoring -pub const IPPROTO_VISA: i32 = 70; // VISA Protocol -pub const IPPROTO_IPCV: i32 = 71; // Packet Core Utility -pub const IPPROTO_CPNX: i32 = 72; // Comp. Prot. Net. Executive -pub const IPPROTO_CPHB: i32 = 73; // Comp. Prot. HeartBeat -pub const IPPROTO_WSN: i32 = 74; // Wang Span Network -pub const IPPROTO_PVP: i32 = 75; // Packet Video Protocol -pub const IPPROTO_BRSATMON: i32 = 76; // BackRoom SATNET Monitoring -pub const IPPROTO_ND: i32 = 77; // Sun net disk proto (temp.) -pub const IPPROTO_WBMON: i32 = 78; // WIDEBAND Monitoring -pub const IPPROTO_WBEXPAK: i32 = 79; // WIDEBAND EXPAK -pub const IPPROTO_EON: i32 = 80; // ISO cnlp -pub const IPPROTO_VMTP: i32 = 81; // VMTP -pub const IPPROTO_SVMTP: i32 = 82; // Secure VMTP -pub const IPPROTO_VINES: i32 = 83; // Banyon VINES -pub const IPPROTO_TTP: i32 = 84; // TTP -pub const IPPROTO_IGP: i32 = 85; // NSFNET-IGP -pub const IPPROTO_DGP: i32 = 86; // dissimilar gateway prot. -pub const IPPROTO_TCF: i32 = 87; // TCF -pub const IPPROTO_IGRP: i32 = 88; // Cisco/GXS IGRP -pub const IPPROTO_OSPFIGP: i32 = 89; // OSPFIGP -pub const IPPROTO_SRPC: i32 = 90; // Strite RPC protocol -pub const IPPROTO_LARP: i32 = 91; // Locus Address Resoloution -pub const IPPROTO_MTP: i32 = 92; // Multicast Transport -pub const IPPROTO_AX25: i32 = 93; // AX.25 Frames -pub const IPPROTO_IPEIP: i32 = 94; // IP encapsulated in IP -pub const IPPROTO_MICP: i32 = 95; // Mobile Int.ing control -pub const IPPROTO_SCCSP: i32 = 96; // Semaphore Comm. security -pub const IPPROTO_ETHERIP: i32 = 97; // Ethernet IP encapsulation -pub const IPPROTO_ENCAP: i32 = 98; // encapsulation header -pub const IPPROTO_APES: i32 = 99; // any private encr. scheme -pub const IPPROTO_GMTP: i32 = 100; // GMTP -pub const IPPROTO_PIM: i32 = 103; // Protocol Independent Mcast -pub const IPPROTO_IPCOMP: i32 = 108; // payload compression (IPComp) -pub const IPPROTO_PGM: i32 = 113; // PGM -pub const IPPROTO_SCTP: i32 = 132; // SCTP -pub const IPPROTO_DIVERT: i32 = 254; // divert pseudo-protocol -pub const IPPROTO_RAW: i32 = 255; // raw IP packet -pub const IPPROTO_MAX: i32 = 256; -// last return value of *_input(), meaning "all job for this pkt is done". -pub const IPPROTO_DONE: i32 = 257; - -pub const MSG_OOB: i32 = 1; -pub const MSG_PEEK: i32 = 2; -pub const MSG_DONTROUTE: i32 = 4; -pub const MSG_TRYHARD: i32 = 4; /* Synonym for MSG_DONTROUTE for DECnet */ -pub const MSG_CTRUNC: i32 = 8; -pub const MSG_PROBE: i32 = 0x10; /* Do not send. Only probe path f.e. for MTU */ -pub const MSG_TRUNC: i32 = 0x20; -pub const MSG_DONTWAIT: i32 = 0x40; /* Nonblocking io */ -pub const MSG_EOR: i32 = 0x80; /* End of record */ -pub const MSG_WAITALL: i32 = 0x100; /* Wait for a full request */ -pub const MSG_FIN: i32 = 0x200; -pub const MSG_SYN: i32 = 0x400; -pub const MSG_CONFIRM: i32 = 0x800; /* Confirm path validity */ -pub const MSG_RST: i32 = 0x1000; -pub const MSG_ERRQUEUE: i32 = 0x2000; /* Fetch message from error queue */ -pub const MSG_NOSIGNAL: i32 = 0x4000; /* Do not generate SIGPIPE */ -pub const MSG_MORE: i32 = 0x8000; /* Sender will send more */ -pub const MSG_WAITFORONE: i32 = 0x10000; /* recvmmsg(): block until 1+ packets avail */ -pub const MSG_SENDPAGE_NOPOLICY: i32 = 0x10000; /* sendpage() internal : do no apply policy */ -pub const MSG_SENDPAGE_NOTLAST: i32 = 0x20000; /* sendpage() internal : not the last page */ -pub const MSG_BATCH: i32 = 0x40000; /* sendmmsg(): more messages coming */ -pub const MSG_EOF: i32 = MSG_FIN; -pub const MSG_NO_SHARED_FRAGS: i32 = 0x80000; /* sendpage() internal : page frags are not shared */ -pub const MSG_SENDPAGE_DECRYPTED: i32 = 0x100000; /* sendpage() internal : page may carry - * plain text and require encryption - */ - -//shutdown -pub const SHUT_RD: i32 = 0; -pub const SHUT_WR: i32 = 1; -pub const SHUT_RDWR: i32 = 2; - -////////////////////// setsockopt / getsockopt... -pub const SOL_SOCKET: i32 = 1; - -pub const SO_DEBUG: i32 = 1; -pub const SO_REUSEADDR: i32 = 2; -pub const SO_TYPE: i32 = 3; -pub const SO_ERROR: i32 = 4; -pub const SO_DONTROUTE: i32 = 5; -pub const SO_BROADCAST: i32 = 6; -pub const SO_SNDBUF: i32 = 7; -pub const SO_RCVBUF: i32 = 8; -pub const SO_SNDBUFFORCE: i32 = 32; -pub const SO_RCVBUFFORCE: i32 = 33; -pub const SO_KEEPALIVE: i32 = 9; -pub const SO_OOBINLINE: i32 = 10; -pub const SO_NO_CHECK: i32 = 11; -pub const SO_PRIORITY: i32 = 12; -pub const SO_LINGER: i32 = 13; -pub const SO_BSDCOMPAT: i32 = 14; -pub const SO_REUSEPORT: i32 = 15; -pub const SO_PASSCRED: i32 = 16; -pub const SO_PEERCRED: i32 = 17; -pub const SO_RCVLOWAT: i32 = 18; -pub const SO_SNDLOWAT: i32 = 19; -pub const SO_RCVTIMEO_OLD: i32 = 20; -pub const SO_SNDTIMEO_OLD: i32 = 21; -pub const SO_PEERNAME: i32 = 28; -pub const SO_ACCEPTCONN: i32 = 30; - -// pub const SO_SECURITY_AUTHENTICATION: i32 = 22; -// pub const SO_SECURITY_ENCRYPTION_TRANSPORT: i32 = 23; -// pub const SO_SECURITY_ENCRYPTION_NETWORK: i32 = 24; - -// pub const SO_BINDTODEVICE: i32 = 25; - -// /* Socket filtering */ -// pub const SO_ATTACH_FILTER: i32 = 26; -// pub const SO_DETACH_FILTER: i32 = 27; - -// pub const SO_TIMESTAMP: i32 = 29; -// pub const SCM_TIMESTAMP: i32 = SO_TIMESTAMP; - -// pub const SO_PEERSEC: i32 = 31; -// pub const SO_PASSSEC: i32 = 34; -// pub const SO_TIMESTAMPNS: i32 = 35; -// pub const SCM_TIMESTAMPNS: i32 = SO_TIMESTAMPNS; - -// pub const SO_MARK: i32 = 36; - -// pub const SO_TIMESTAMPING: i32 = 37; -// pub const SCM_TIMESTAMPING: i32 = SO_TIMESTAMPING; - -// pub const SO_PROTOCOL: i32 = 38; -// pub const SO_DOMAIN: i32 = 39; - -// pub const SO_RXQ_OVFL: i32 = 40; - -// Use this to specify options on a socket. Use the protocol with setsockopt -// to specify something for all sockets with a protocol -pub const SOL_TCP: i32 = IPPROTO_TCP; -pub const SOL_UDP: i32 = IPPROTO_UDP; - -pub const TCP_NODELAY: i32 = 0x01; // don't delay send to coalesce packets -pub const TCP_MAXSEG: i32 = 0x02; // set maximum segment size -pub const TCP_NOPUSH: i32 = 0x04; // don't push last block of write -pub const TCP_NOOPT: i32 = 0x08; // don't use TCP options -pub const TCP_KEEPALIVE: i32 = 0x10; // idle time used when SO_KEEPALIVE is enabled -pub const TCP_CONNECTIONTIMEOUT: i32 = 0x20; // connection timeout -pub const PERSIST_TIMEOUT: i32 = 0x40; // time after which a connection in persist timeout - // will terminate. - // see draft-ananth-tcpm-persist-02.txt -pub const TCP_RXT_CONNDROPTIME: i32 = 0x80; // time after which tcp retransmissions will be - // stopped and the connection will be dropped -pub const TCP_RXT_FINDROP: i32 = 0x100; // When set, a connection is dropped after 3 FINs - -pub const MINSOCKOBJID: i32 = 0; -pub const MAXSOCKOBJID: i32 = 1024; - -//POLL CONSTANTS -pub const POLLIN: i16 = 0o1; // There is data to read. -pub const POLLPRI: i16 = 0o2; //There is urgent data to read. -pub const POLLOUT: i16 = 0o4; // Writing now will not block. -pub const POLLERR: i16 = 0o10; // Error condition. -pub const POLLHUP: i16 = 0o20; // Hung up. -pub const POLLNVAL: i16 = 0o40; // Invalid polling request. - -//EPOLL CONSTANTS -pub const EPOLLIN: i32 = 0x001; -pub const EPOLLPRI: i32 = 0x002; -pub const EPOLLOUT: i32 = 0x004; -pub const EPOLLRDNORM: i32 = 0x040; -pub const EPOLLRDBAND: i32 = 0x080; -pub const EPOLLWRNORM: i32 = 0x100; -pub const EPOLLWRBAND: i32 = 0x200; -pub const EPOLLMSG: i32 = 0x400; -pub const EPOLLERR: i32 = 0x008; -pub const EPOLLHUP: i32 = 0x010; -pub const EPOLLRDHUP: i32 = 0x2000; -pub const EPOLLWAKEUP: i32 = 1 << 29; -pub const EPOLLONESHOT: i32 = 1 << 30; -pub const EPOLLET: i32 = 1 << 31; - -pub const EPOLL_CTL_ADD: i32 = 1; -pub const EPOLL_CTL_DEL: i32 = 2; -pub const EPOLL_CTL_MOD: i32 = 3; - -pub const FD_SET_MAX_FD: i32 = 1024; - -//for internal use -#[derive(Debug, PartialEq, Eq, Clone)] -pub enum ConnState { - NOTCONNECTED, - CONNECTED, - CONNRDONLY, - CONNWRONLY, - LISTEN, - INPROGRESS, -} diff --git a/src/RawPOSIX/src/safeposix/syscalls/sys_calls.rs b/src/RawPOSIX/src/safeposix/syscalls/sys_calls.rs index 0f2492bb..0a43f648 100644 --- a/src/RawPOSIX/src/safeposix/syscalls/sys_calls.rs +++ b/src/RawPOSIX/src/safeposix/syscalls/sys_calls.rs @@ -1,10 +1,19 @@ #![allow(dead_code)] // System related system calls -use super::fs_constants::*; -use super::net_constants::*; -use super::sys_constants; -use super::sys_constants::*; +use crate::constants::{ + DEFAULT_UID, DEFAULT_GID, + SIG_BLOCK, SIG_UNBLOCK, SIG_SETMASK, + SIGNAL_MAX, + ITIMER_REAL, + RLIMIT_NOFILE, RLIMIT_STACK, + NOFILE_CUR, NOFILE_MAX, + STACK_CUR, STACK_MAX, + SHMMIN, SHMMAX, + SHM_RDONLY, SHM_DEST, + SEM_VALUE_MAX, +}; + use crate::interface; use crate::safeposix::cage; use crate::safeposix::cage::*; @@ -140,6 +149,8 @@ impl Cage { for pair in semtable.iter() { new_semtable.insert((*pair.key()).clone(), pair.value().clone()); } + let parent_vmmap = self.vmmap.read(); + let new_vmmap = parent_vmmap.clone(); let cageobj = Cage { cageid: child_cageid, @@ -168,6 +179,7 @@ impl Cage { sigset: newsigset, main_threadid: interface::RustAtomicU64::new(0), interval_timer: interface::IntervalTimer::new(child_cageid), + vmmap: interface::RustLock::new(new_vmmap), // Initialize empty virtual memory map for new process zombies: interface::RustLock::new(vec![]), child_num: interface::RustAtomicU64::new(0), }; @@ -175,6 +187,7 @@ impl Cage { // increment child counter for parent self.child_num.fetch_add(1, interface::RustAtomicOrdering::SeqCst); + let shmtable = &SHM_METADATA.shmtable; //update fields for shared mappings in cage for rev_mapping in cageobj.rev_shm.lock().iter() { @@ -249,6 +262,7 @@ impl Cage { sigset: newsigset, main_threadid: interface::RustAtomicU64::new(0), interval_timer: self.interval_timer.clone_with_new_cageid(child_cageid), + vmmap: interface::RustLock::new(Vmmap::new()), // Fresh clean vmmap // when a process exec-ed, its child relationship should be perserved zombies: interface::RustLock::new(cloned_zombies), child_num: interface::RustAtomicU64::new(child_num), @@ -299,6 +313,7 @@ impl Cage { status } + //------------------------------------WAITPID SYSCALL------------------------------------ /* * waitpid() will return the cageid of waited cage, or 0 when WNOHANG is set and there is no cage already exited @@ -516,7 +531,7 @@ impl Cage { if let Some(some_set) = set { let curr_sigset = sigset.load(interface::RustAtomicOrdering::Relaxed); res = match how { - cage::SIG_BLOCK => { + SIG_BLOCK => { // Block signals in set sigset.store( curr_sigset | *some_set, @@ -524,7 +539,7 @@ impl Cage { ); 0 } - cage::SIG_UNBLOCK => { + SIG_UNBLOCK => { // Unblock signals in set let newset = curr_sigset & !*some_set; let pendingsignals = curr_sigset & some_set; @@ -532,7 +547,7 @@ impl Cage { self.send_pending_signals(pendingsignals, pthreadid); 0 } - cage::SIG_SETMASK => { + SIG_SETMASK => { // Set sigset to set sigset.store(*some_set, interface::RustAtomicOrdering::Relaxed); 0 @@ -550,7 +565,7 @@ impl Cage { old_value: Option<&mut interface::ITimerVal>, ) -> i32 { match which { - sys_constants::ITIMER_REAL => { + ITIMER_REAL => { if let Some(some_old_value) = old_value { let (curr_duration, next_duration) = self.interval_timer.get_itimer(); some_old_value.it_value.tv_sec = curr_duration.as_secs() as i64; @@ -580,11 +595,11 @@ impl Cage { pub fn getrlimit(&self, res_type: u64, rlimit: &mut interface::Rlimit) -> i32 { match res_type { - sys_constants::RLIMIT_NOFILE => { + RLIMIT_NOFILE => { rlimit.rlim_cur = NOFILE_CUR; rlimit.rlim_max = NOFILE_MAX; } - sys_constants::RLIMIT_STACK => { + RLIMIT_STACK => { rlimit.rlim_cur = STACK_CUR; rlimit.rlim_max = STACK_MAX; } @@ -595,7 +610,7 @@ impl Cage { pub fn setrlimit(&self, res_type: u64, _limit_value: u64) -> i32 { match res_type { - sys_constants::RLIMIT_NOFILE => { + RLIMIT_NOFILE => { if NOFILE_CUR > NOFILE_MAX { -1 } else { diff --git a/src/RawPOSIX/src/safeposix/syscalls/sys_constants.rs b/src/RawPOSIX/src/safeposix/syscalls/sys_constants.rs deleted file mode 100644 index 89feb715..00000000 --- a/src/RawPOSIX/src/safeposix/syscalls/sys_constants.rs +++ /dev/null @@ -1,77 +0,0 @@ -// System related constants -#![allow(dead_code)] -#![allow(unused_variables)] - -use crate::interface; - -// Define constants using static or const -// Imported into fs_calls file - -//GID AND UID DEFAULT VALUES - -pub const DEFAULT_UID: u32 = 1000; -pub const DEFAULT_GID: u32 = 1000; - -// RESOURCE LIMITS - -pub const SIGNAL_MAX: i32 = 64; - -pub const NOFILE_CUR: u64 = 1024; -pub const NOFILE_MAX: u64 = 4 * 1024; - -pub const STACK_CUR: u64 = 8192 * 1024; -pub const STACK_MAX: u64 = 1 << 32; - -pub const RLIMIT_STACK: u64 = 0; -pub const RLIMIT_NOFILE: u64 = 1; - -// Constants for exit_syscall status - -pub const EXIT_SUCCESS: i32 = 0; -pub const EXIT_FAILURE: i32 = 1; - -// Signal Table (x86/ARM) -// Based on https://man7.org/linux/man-pages/man7/signal.7.html -pub const SIGHUP: i32 = 1; -pub const SIGINT: i32 = 2; -pub const SIGQUIT: i32 = 3; -pub const SIGILL: i32 = 4; -pub const SIGTRAP: i32 = 5; -pub const SIGABRT: i32 = 6; -pub const SIGIOT: i32 = 6; -pub const SIGBUS: i32 = 7; -// pub const SIGEMT: i32 -pub const SIGFPE: i32 = 8; -pub const SIGKILL: i32 = 9; -pub const SIGUSR1: i32 = 10; -pub const SIGSEGV: i32 = 11; -pub const SIGUSR2: i32 = 12; -pub const SIGPIPE: i32 = 13; -pub const SIGALRM: i32 = 14; -pub const SIGTERM: i32 = 15; -pub const SIGSTKFLT: i32 = 16; -pub const SIGCHLD: i32 = 17; -// pub const SIGCLD: i32 -pub const SIGCONT: i32 = 18; -pub const SIGSTOP: i32 = 19; -pub const SIGTSTP: i32 = 20; -pub const SIGTTIN: i32 = 21; -pub const SIGTTOU: i32 = 22; -pub const SIGURG: i32 = 23; -pub const SIGXCPU: i32 = 24; -pub const SIGXFSZ: i32 = 25; -pub const SIGVTALRM: i32 = 26; -pub const SIGPROF: i32 = 27; -pub const SIGWINCH: i32 = 28; -pub const SIGIO: i32 = 29; -pub const SIGPOLL: i32 = 29; -pub const SIGPWR: i32 = 30; -// pub const SIGINFO: i32 -// pub const SIGLOST: i32 -pub const SIGSYS: i32 = 31; -pub const SIGUNUSED: i32 = 31; - -pub const SIG_BLOCK: i32 = 0; -pub const SIG_UNBLOCK: i32 = 1; -pub const SIG_SETMASK: i32 = 2; -pub const ITIMER_REAL: i32 = 0; diff --git a/src/RawPOSIX/src/safeposix/vmmap.rs b/src/RawPOSIX/src/safeposix/vmmap.rs new file mode 100644 index 00000000..23efa2f5 --- /dev/null +++ b/src/RawPOSIX/src/safeposix/vmmap.rs @@ -0,0 +1,961 @@ +use crate::constants::{ + MAP_ANONYMOUS, MAP_FAILED, MAP_FIXED, MAP_PRIVATE, MAP_SHARED, PAGESHIFT, PROT_EXEC, PROT_NONE, PROT_READ, PROT_WRITE +}; +use std::io; +use nodit::NoditMap; +use nodit::{interval::ie, Interval}; +use crate::fdtables; +use crate::safeposix::cage::syscall_error; +use crate::safeposix::cage::Errno; + +const DEFAULT_VMMAP_SIZE: u32 = 1 << (32 - PAGESHIFT); + +/// Used to identify whether the vmmap entry is backed anonymously, +/// by an fd, or by a shared memory segment +/// +/// This enum represents different types of memory backing: +/// - None: Used as a placeholder when no backing type is available +/// - Anonymous: Memory not backed by any file (e.g. heap allocations) +/// - SharedMemory: Memory backed by a shared memory segment, identified by shmid +/// - FileDescriptor: Memory backed by a file, identified by file descriptor +#[allow(dead_code)] +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub enum MemoryBackingType { + None, // just a dummy value for places where it needs to be passed, but you dont have the value + Anonymous, + SharedMemory(u64), // stores shmid + FileDescriptor(u64), // stores file descriptor addr +} + + +/// An entry in the virtual memory map that contains fields such as page number, number of pages, +/// permissions, file offset, file size, shared memory ID, and backing fields to distinguish memory types. +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct VmmapEntry { + pub page_num: u32, // Base virtual address shifted right by NACL_PAGESHIFT + pub npages: u32, // Number of pages in this mapping + pub prot: i32, // Current memory protection flags (read/write/execute) + pub maxprot: i32, // Maximum allowed protection flags + pub flags: i32, // Memory mapping flags (shared/private/fixed/anonymous) + pub removed: bool, // Flag indicating if entry has been marked for removal + pub file_offset: i64, // Offset into the backing file/device + pub file_size: i64, // Size of the backing store + pub cage_id: u64, // Identifier for the security cage + pub backing: MemoryBackingType, // Type of memory backing for this region +} + + +// Implement methods for VmmapEntry +// Constructor to create a new VmmapEntry +#[allow(dead_code)] +impl VmmapEntry { + /// Creates a new VmmapEntry with the specified parameters + /// + /// Arguments: + /// - page_num: Starting page number of the memory region + /// - npages: Number of pages in the memory region + /// - prot: Initial protection flags for the pages + /// - maxprot: Maximum allowed protection flags + /// - flags: Memory mapping flags + /// - removed: Whether this entry is marked for removal + /// - file_offset: Offset into the backing file/device + /// - file_size: Size of the backing store + /// - cage_id: Security cage identifier + /// - backing: Type of memory backing + /// + /// Returns a new VmmapEntry instance initialized with the provided values + pub fn new( + page_num: u32, + npages: u32, + prot: i32, + maxprot: i32, + flags: i32, + removed: bool, + file_offset: i64, + file_size: i64, + cage_id: u64, //This is the cage id to refer to for file backings + backing: MemoryBackingType, + ) -> Self { + // Create and return a new VmmapEntry with the provided values + return VmmapEntry { + page_num, + npages, + prot, + maxprot, + flags, + removed, + file_offset, + file_size, + cage_id, + backing, + }; + } + + // Gets the maximum protection flags allowed for file-backed memory mappings + // + // Arguments: + // - cage_id: Security cage identifier + // - virtual_fd: Virtual file descriptor + // + // Returns the maximum protection flags as an i32, based on the file's mode + fn get_max_prot(&self, cage_id: u64, virtual_fd: u64) -> i32 { + // Translate the virtual file descriptor to a real one + let wrappedvfd = fdtables::translate_virtual_fd(cage_id, virtual_fd as u64); + if wrappedvfd.is_err() { + return syscall_error(Errno::EBADF, "fstat", "Bad File Descriptor"); + } + let vfd = wrappedvfd.unwrap(); + + // Get file stats using fstat + let mut libc_statbuf: libc::stat = unsafe { std::mem::zeroed() }; + let libcret = unsafe { + libc::fstat(vfd.underfd as i32, &mut libc_statbuf) + }; + + // Return the file mode which contains protection flags + libc_statbuf.st_mode as i32 + } +} + +/// VmmapOps trait provides an interface that can be shared by different virtual memory management implementations, +/// allowing different Vmmap versions to share the same interface. +/// +/// This trait defines the core operations that any virtual memory map implementation must support: +/// - Adding/removing memory mappings +/// - Updating protection flags +/// - Searching for free space +/// - Querying existing mappings +/// - Iterating over memory regions +#[allow(dead_code)] +pub trait VmmapOps { + + // Method to update a memory map entry + fn update( + &mut self, + page_num: u32, + npages: u32, + prot: i32, + maxprot: i32, + flags: i32, + backing: MemoryBackingType, + remove: bool, + file_offset: i64, + file_size: i64, + cage_id: u64, + ) -> Result<(), io::Error>; + + // Method to add a new entry to the memory map + fn add_entry(&mut self, vmmap_entry_ref: VmmapEntry); + + // Method to add an entry with override + fn add_entry_with_overwrite( + &mut self, + page_num: u32, + npages: u32, + prot: i32, + maxprot: i32, + flags: i32, + backing: MemoryBackingType, + file_offset: i64, + file_size: i64, + cage_id: u64, + ) -> Result<(), io::Error>; + + // Method to change protection of a memory region + // Modifies protection for existing pages in the region + // Should be able to handle splitting of existing pages when necessary + // Should maintain mapping consistency while changing protections + fn change_prot(&mut self, page_num: u32, npages: u32, new_prot: i32); + + // Method to remove an entry from the memory map + fn remove_entry(&mut self, page_num: u32, npages: u32) -> Result<(), io::Error>; + + // Method to check if requested pages exist with proper permissions + // NaCl code enforces PROT_READ when any protection exists + // Returns end page number if mapping is found and has proper permissions + fn check_existing_mapping(&self, page_num: u32, npages: u32, prot: i32) -> bool; + + // Method to check address mapping + fn check_addr_mapping(&mut self, page_num: u32, npages: u32, prot: i32) -> Option; + + // Method to find a page in the memory map + fn find_page(&self, page_num: u32) -> Option<&VmmapEntry>; + + // Method to find a mutable page in the memory map + fn find_page_mut(&mut self, page_num: u32) -> Option<&mut VmmapEntry>; + + // Method to iterate over pages + fn find_page_iter( + &self, + page_num: u32, + ) -> impl DoubleEndedIterator, &VmmapEntry)>; + + // Method to iterate over mutable pages + fn find_page_iter_mut( + &mut self, + page_num: u32, + ) -> impl DoubleEndedIterator, &mut VmmapEntry)>; + + + // Method to get the first entry in the memory map + fn first_entry(&self) -> Option<(&Interval, &VmmapEntry)>; + + // Method to get the last entry in the memory map + fn last_entry(&self) -> Option<(&Interval, &VmmapEntry)>; + + // Method to iterate over entries in both directions + fn double_ended_iter(&self) -> impl DoubleEndedIterator, &VmmapEntry)>; + + // Method to iterate over mutable entries in both directions + fn double_ended_iter_mut( + &mut self, + ) -> impl DoubleEndedIterator, &mut VmmapEntry)>; + + // Method to find space in the memory map + // Searches for a contiguous region of at least 'n' free pages + // Returns the interval of the free space + fn find_space(&self, npages: u32) -> Option>; + + // Method to find space above a hint + fn find_space_above_hint(&self, npages: u32, hint: u32) -> Option>; + + // Method to find space for memory mappings with alignment requirements + // Rounds page numbers up to the nearest multiple of pages_per_map + // Returns the interval of the free space + fn find_map_space(&self, num_pages: u32, pages_per_map: u32) -> Option>; + + // Method to find map space with a hint + fn find_map_space_with_hint( + &self, + num_pages: u32, + pages_per_map: u32, + hint: u32, + ) -> Option>; +} + +/// Represents a virtual memory map that manages memory regions and their attributes +/// +/// Fields: +/// - entries: NoditMap storing the memory regions indexed by page number +/// - cached_entry: Optional cached entry for performance optimization +/// - base_address: Optional base address for WASM memory +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct Vmmap { + pub entries: NoditMap, VmmapEntry>, // Keyed by `page_num` + pub cached_entry: Option, // TODO: is this still needed? + // Use Option for safety + pub base_address: Option, // wasm base address. None means uninitialized yet + + pub start_address: u32, // start address of valid vmmap address range + pub end_address: u32, // end address of valid vmmap address range + pub program_break: u32, // program break (i.e. heap bottom) of the memory +} + +#[allow(dead_code)] +impl Vmmap { + /// Creates a new empty virtual memory map + pub fn new() -> Self { + // Initialize a new Vmmap with empty entries and no cached entry or base address + Vmmap { + entries: NoditMap::new(), + cached_entry: None, + base_address: None, + start_address: 0, + end_address: DEFAULT_VMMAP_SIZE, + program_break: 0, + } + } + + /// Rounds up a page number to the nearest multiple of pages_per_map + /// + /// Arguments: + /// - npages: Number of pages to round up + /// - pages_per_map: Alignment requirement in pages + /// + /// Returns the rounded up page number + fn round_page_num_up_to_map_multiple(&self, npages: u32, pages_per_map: u32) -> u32 { + // Add (pages_per_map - 1) to npages and mask off lower bits to round up + (npages + pages_per_map - 1) & !(pages_per_map - 1) + } + + /// Truncates a page number down to the nearest multiple of pages_per_map + /// + /// Arguments: + /// - npages: Number of pages to truncate + /// - pages_per_map: Alignment requirement in pages + /// + /// Returns the truncated page number + fn trunc_page_num_down_to_map_multiple(&self, npages: u32, pages_per_map: u32) -> u32 { + // Mask off lower bits to truncate down to multiple + npages & !(pages_per_map - 1) + } + + /// Sets the base address for WASM memory + /// + /// Arguments: + /// - base_address: The base address to set + pub fn set_base_address(&mut self, base_address: usize) { + // Store the provided base address + self.base_address = Some(base_address); + } + + /// Sets the program break for the memory + /// + /// Arguments: + /// - program_break: The program break to set + pub fn set_program_break(&mut self, program_break: u32) { + self.program_break = program_break; + } + + /// Converts a user address to a system address + /// + /// Arguments: + /// - address: User space address to convert + /// + /// Returns the corresponding system address + pub fn user_to_sys(&self, address: u32) -> usize { + // Add base address to user address to get system address + address as usize + self.base_address.unwrap() + } + + /// Converts a system address to a user address + /// + /// Arguments: + /// - address: System address to convert + /// + /// Returns the corresponding user space address + pub fn sys_to_user(&self, address: usize) -> u32 { + // Subtract base address from system address to get user address + (address as usize - self.base_address.unwrap()) as u32 + } + + // Visits each entry in the vmmap, applying a visitor function to each entry + // + // The visitor function should be used to: + // - Validate memory map consistency + // - Gather statistics about memory usage + // - Perform operations across all entries + // - Support debugging and auditing features + fn visit() {} + + + // Prints detailed debug information about the vmmap's current state + // + // Should output information including: + // - Page ranges and sizes for each mapping + // - Protection flags (current and maximum) + // - Mapping flags + // - Backing store information (Anonymous, File, or Shared Memory) + // - File information (offset and size) when applicable + // - Any gaps in the address space + fn debug() {} +} + +impl VmmapOps for Vmmap { + /// Adds a new entry to the virtual memory map + /// + /// This function inserts a new VmmapEntry into the memory map data structure. + /// The entry is inserted with strict bounds checking to ensure memory safety. + /// + /// Arguments: + /// - vmmap_entry_ref: The VmmapEntry to add containing page numbers, permissions, etc. + /// + /// The interval is created from: + /// - Start: vmmap_entry_ref.page_num + /// - End: vmmap_entry_ref.page_num + vmmap_entry_ref.npages (inclusive) + fn add_entry(&mut self, vmmap_entry_ref: VmmapEntry) { + // Create interval from page range and insert entry with strict bounds checking + let _ = self.entries.insert_strict( + // pages x to y, y included + ie( + vmmap_entry_ref.page_num, + vmmap_entry_ref.page_num + vmmap_entry_ref.npages, + ), + vmmap_entry_ref, + ); + } + + /// Adds a new entry to the virtual memory map with overwrite capability + /// + /// This function creates and inserts a new VmmapEntry, overwriting any existing + /// entries that overlap with the specified page range. + /// + /// Arguments: + /// - page_num: Starting page number for the mapping + /// - npages: Number of pages to map + /// - prot: Current protection flags (read/write/execute) + /// - maxprot: Maximum allowed protection flags + /// - flags: Mapping flags (shared/private/fixed/anonymous) + /// - backing: Type of memory backing (Anonymous/SharedMemory/FileDescriptor) + /// - file_offset: Offset into backing file/device + /// - file_size: Size of backing store + /// - cage_id: Security cage identifier + /// + /// Returns: + /// - Ok(()) on success + /// - Err(io::Error) on failure + fn add_entry_with_overwrite( + &mut self, + page_num: u32, + npages: u32, + prot: i32, + maxprot: i32, + flags: i32, + backing: MemoryBackingType, + file_offset: i64, + file_size: i64, + cage_id: u64, + ) -> Result<(), io::Error> { + // Call update() to handle the insertion with overwrite capability + self.update( + page_num, + npages, + prot, + maxprot, + flags, + backing, + false, // Not removing + file_offset, + file_size, + cage_id, + ) + } + + /// Removes a memory mapping from the specified page range + /// + /// This function will not return any errors pertaining to the page number not mapping + /// to any existing pages, as the remove operation is done on a best efforts basis: + /// 1. First an insert overwrite operation with the below page range is performed, causing + /// a new interval to be created over the provided page range, appropriately partitioning + /// boundary pages. + /// 2. This new interval is then deleted, leaving the underlying range unmapped + /// + /// Arguments: + /// - page_num: Starting page number to remove + /// - npages: Number of pages to remove + /// + /// Returns: + /// - Ok(()) on success + /// - Err(io::Error) on failure + fn remove_entry(&mut self, page_num: u32, npages: u32) -> Result<(), io::Error> { + // Call update() with remove flag set to true + self.update( + page_num, + npages, + 0, + 0, + 0, + MemoryBackingType::None, + true, // Removing + 0, + 0, + 0, + ) + } + + /// Updates or removes a memory mapping entry + /// + /// This is the core function that handles both insertion and removal of memory mappings. + /// It performs validation and maintains mapping consistency. + /// + /// Arguments: + /// - page_num: Starting page number + /// - npages: Number of pages + /// - prot: Current protection flags + /// - maxprot: Maximum allowed protection + /// - flags: Mapping flags + /// - backing: Type of memory backing + /// - remove: If true, removes the mapping instead of updating + /// - file_offset: Offset into backing file + /// - file_size: Size of backing store + /// - cage_id: Security cage identifier + /// + /// Returns: + /// - Ok(()) on success + /// - Err(io::Error) if npages is 0 or other error occurs + fn update( + &mut self, + page_num: u32, + npages: u32, + prot: i32, + maxprot: i32, + flags: i32, + backing: MemoryBackingType, + remove: bool, + file_offset: i64, + file_size: i64, + cage_id: u64, + ) -> Result<(), io::Error> { + // Validate input - number of pages must be non-zero + if npages == 0 { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "Number of pages cannot be zero", + )); + } + + // Calculate page range + let new_region_end_page = page_num + npages; + let new_region_start_page = page_num; + + // Create new entry if not removing + let new_entry = VmmapEntry { + page_num, + npages, + prot, + maxprot, + flags, + backing, + file_offset, + file_size, + removed: false, + cage_id, + }; + + // Insert new entry, overwriting any existing entries in the range + let _ = self + .entries + .insert_overwrite(ie(new_region_start_page, new_region_end_page), new_entry); + + // If removing, delete the entry after insertion + if remove { + // Remove all entries that overlap with the specified range + let _ = self + .entries + .remove_overlapping(ie(new_region_start_page, new_region_end_page)); + } + + Ok(()) + } + + /// Changes memory protection flags for a range of pages + /// + /// This function modifies the protection flags for existing pages in the specified region. + /// It handles splitting of existing pages when necessary to maintain proper protection boundaries. + /// + /// Arguments: + /// - page_num: Starting page number + /// - npages: Number of pages to modify + /// - new_prot: New protection flags to apply + /// + /// Implementation details: + /// - Handles partial overlaps by splitting entries + /// - Maintains mapping consistency during protection changes + /// - Updates protection flags for fully contained pages + fn change_prot(&mut self, page_num: u32, npages: u32, new_prot: i32) { + // Calculate page range + let new_region_end_page = page_num + npages; + let new_region_start_page = page_num; + + // Store intervals that need to be inserted after iteration + let mut to_insert = Vec::new(); + + // Iterate over overlapping entries + for (overlap_interval, entry) in self + .entries + .overlapping_mut(ie(new_region_start_page, new_region_end_page)) + { + let mut ent_start = overlap_interval.start(); + let ent_end = overlap_interval.end(); + + // Case 1: Entry starts before region but extends into it + if ent_start < new_region_start_page && ent_end > new_region_start_page { + to_insert.push(ie(new_region_start_page, ent_end)); + ent_start = new_region_start_page; + } + + // Case 2: Entry extends beyond region end + if ent_start < new_region_end_page && ent_end > new_region_end_page { + to_insert.push(ie(ent_start, new_region_end_page)); + } else { + // Case 3: Entry is fully contained - update protection + entry.prot = new_prot; + } + } + + // Insert new intervals with updated protection + for interval in to_insert { + // Get and clone the entry at the start of the interval + let mut interval_val = self.entries.get_at_point(interval.start()).unwrap().clone(); + // Update protection + interval_val.prot = new_prot; + // Insert the new interval + let _ = self.entries.insert_overwrite(interval, interval_val); + } + } + + /// Checks if a memory mapping exists with specified protection + /// + /// Verifies if a continuous mapping exists for the given page range + /// and checks if the requested protection flags are compatible. + /// + /// Arguments: + /// - page_num: Starting page number to check + /// - npages: Number of pages to verify + /// - prot: Required protection flags + /// + /// Returns: + /// - true if mapping exists with compatible protection + /// - false if mapping doesn't exist or protection is incompatible + fn check_existing_mapping(&self, page_num: u32, npages: u32, prot: i32) -> bool { + // Calculate end page and create interval for region + let region_end_page = page_num + npages; + let region_interval = ie(page_num, region_end_page); + + // Case 1: No overlapping entries exist + if !self.entries.overlaps(region_interval) { + return false; + } + + let mut current_page = page_num; + + // Iterate over overlapping intervals + for (_interval, entry) in self.entries.overlapping(region_interval) { + let ent_end_page = entry.page_num + entry.npages; + let flags = entry.maxprot; + + // Case 2: Region is fully inside existing entry + if entry.page_num <= current_page && region_end_page <= ent_end_page { + return (prot & !flags) == 0; + } + + // Case 3: Region overlaps with current entry + if entry.page_num <= current_page && current_page < ent_end_page { + if (prot & !flags) != 0 { + return false; + } + current_page = ent_end_page; // Move to next region + } + + // Case 4: Gap between entries + if current_page < entry.page_num { + return false; + } + } + + false + } + + /// Checks address mapping with caching optimization + /// + /// Verifies memory mapping and protection flags for a page range, + /// using a cached entry when possible for better performance. + /// + /// Arguments: + /// - page_num: Starting page number + /// - npages: Number of pages to check + /// - prot: Required protection flags + /// + /// Returns: + /// - Some(end_page) if mapping exists and protection is compatible + /// - None if mapping invalid or protection incompatible + /// + /// Implementation details: + /// - Uses cached entry for quick lookups + /// - Enforces PROT_READ when other protections are set + /// - Handles partial overlaps and gaps + fn check_addr_mapping(&mut self, page_num: u32, npages: u32, prot: i32) -> Option { + // Calculate end page of region + let region_end_page = page_num + npages; + + // Case 1: Check cached entry first for performance + if let Some(ref cached_entry) = self.cached_entry { + let ent_end_page = cached_entry.page_num + cached_entry.npages; + let mut flags = cached_entry.prot; + + // Enforce PROT_READ if any protection exists + if flags & (PROT_EXEC | PROT_READ | PROT_WRITE) != PROT_NONE { + flags |= PROT_READ; + } + + // Check if region is inside cached entry with compatible protection + if cached_entry.page_num <= page_num && region_end_page <= ent_end_page { + if prot & !flags == 0 { + return Some(ent_end_page); + } + } + } + + // Case 2: Check overlapping entries if cache miss + let mut current_page = page_num; + for (_, entry) in self.entries.overlapping(ie(page_num, region_end_page)) { + let ent_end_page = entry.page_num + entry.npages; + let mut flags = entry.prot; + + // Enforce PROT_READ if any protection exists + if flags & (PROT_EXEC | PROT_READ | PROT_WRITE) != PROT_NONE { + flags |= PROT_READ; + } + + // Case 2a: Region fully inside entry + if entry.page_num <= current_page && region_end_page <= ent_end_page { + self.cached_entry = Some(entry.clone()); // Update cache + if prot & !flags == 0 { + return Some(ent_end_page); + } + } + // Case 2b: Partial overlap + else if entry.page_num <= current_page && current_page < ent_end_page { + if prot & !flags != 0 { + return None; + } + current_page = ent_end_page; // Move to next region + } + // Case 2c: Gap between entries + else if current_page < entry.page_num { + return None; + } + } + + // Case 3: No valid mapping found + None + } + + /// Finds a page entry in the memory map + /// + /// Arguments: + /// - page_num: Page number to find + /// + /// Returns: + /// - Some(&VmmapEntry) if page exists + /// - None if page not found + fn find_page(&self, page_num: u32) -> Option<&VmmapEntry> { + // Look up entry containing the specified page number + self.entries.get_at_point(page_num) + } + + /// Finds a mutable page entry in the memory map + /// + /// Arguments: + /// - page_num: Page number to find + /// + /// Returns: + /// - Some(&mut VmmapEntry) if page exists + /// - None if page not found + fn find_page_mut(&mut self, page_num: u32) -> Option<&mut VmmapEntry> { + // Look up mutable entry containing the specified page number + self.entries.get_at_point_mut(page_num) + } + + /// Gets the last entry in the memory map + /// + /// Returns: + /// - Some((interval, entry)) containing the last mapping + /// - None if map is empty + fn last_entry(&self) -> Option<(&Interval, &VmmapEntry)> { + // Return the last key-value pair in the map + self.entries.last_key_value() + } + + /// Gets the first entry in the memory map + /// + /// Returns: + /// - Some((interval, entry)) containing the first mapping + /// - None if map is empty + fn first_entry(&self) -> Option<(&Interval, &VmmapEntry)> { + // Return the first key-value pair in the map + self.entries.first_key_value() + } + + /// Creates a double-ended iterator over all entries + /// + /// Returns an iterator that can traverse entries in both directions + fn double_ended_iter(&self) -> impl DoubleEndedIterator, &VmmapEntry)> { + // Return iterator over all entries + self.entries.iter() + } + + /// Creates a double-ended iterator over all entries with mutable access + /// + /// Returns an iterator that can traverse entries in both directions + /// and modify the entries + fn double_ended_iter_mut( + &mut self, + ) -> impl DoubleEndedIterator, &mut VmmapEntry)> { + // Return mutable iterator over all entries + self.entries.iter_mut() + } + + /// Creates an iterator over pages starting from a given page number + /// + /// Arguments: + /// - page_num: Starting page number for iteration + /// + /// Returns: + /// - Iterator over entries from page_num to end of map + /// - Empty iterator if map is empty + fn find_page_iter( + &self, + page_num: u32, + ) -> impl DoubleEndedIterator, &VmmapEntry)> { + if let Some(last_entry) = self.last_entry() { + self.entries.overlapping(ie(page_num, last_entry.0.end())) + } else { + // Return an empty iterator if no last_entry + self.entries.overlapping(ie(page_num, page_num)) + } + } + + /// Creates a mutable iterator over pages starting from a given page number + /// + /// Arguments: + /// - page_num: Starting page number for iteration + /// + /// Returns: + /// - Mutable iterator over entries from page_num to end of map + /// - Empty iterator if map is empty + fn find_page_iter_mut( + &mut self, + page_num: u32, + ) -> impl DoubleEndedIterator, &mut VmmapEntry)> { + if let Some(last_entry) = self.last_entry() { + self.entries + .overlapping_mut(ie(page_num, last_entry.0.end())) + } else { + // Return an empty iterator if no last_entry + self.entries.overlapping_mut(ie(page_num, page_num)) + } + } + + /// Finds available space in the memory map for a new mapping + /// + /// Searches for a gap between existing mappings that can accommodate + /// the requested number of pages. + /// + /// Arguments: + /// - npages: Number of pages needed + /// + /// Returns: + /// - Some(Interval) containing the found space + /// - None if no suitable space found + fn find_space(&self, npages: u32) -> Option> { + let start = self.start_address; + let end = self.end_address; + + let desired_space = npages + 1; // TODO: check if this is correct + + for gap in self + .entries + .gaps_trimmed(ie(start, end)) + { + if gap.end() - gap.start() >= desired_space { + return Some(gap); + } + } + + None + } + + /// Finds available space above a hint address + /// + /// Searches for a gap that can accommodate the requested pages, + /// starting from the hint address. + /// + /// Arguments: + /// - npages: Number of pages needed + /// - hint: Starting address to search from + /// + /// Returns: + /// - Some(Interval) containing the found space + /// - None if no suitable space found + fn find_space_above_hint(&self, npages: u32, hint: u32) -> Option> { + let start = hint; + let end = self.end_address; + + let desired_space = npages + 1; // TODO: check if this is correct + + for gap in self.entries.gaps_trimmed(ie(start, end)) { + if gap.end() - gap.start() >= desired_space { + return Some(gap); + } + } + + None + } + + /// Finds space for a mapping with alignment constraints + /// + /// Searches for available space that satisfies both size and + /// alignment requirements specified by pages_per_map. + /// + /// Arguments: + /// - num_pages: Number of pages needed + /// - pages_per_map: Alignment requirement in pages + /// + /// Returns: + /// - Some(Interval) containing aligned space + /// - None if no suitable space found + /// + /// Implementation details: + /// - Rounds page numbers up to alignment boundaries + /// - Handles alignment constraints for start and end addresses + fn find_map_space(&self, num_pages: u32, pages_per_map: u32) -> Option> { + let start = self.start_address; + let end = self.end_address; + + let rounded_num_pages = + self.round_page_num_up_to_map_multiple(num_pages, pages_per_map); + + for gap in self + .entries + .gaps_trimmed(ie(start, end)) + { + let aligned_start_page = + self.trunc_page_num_down_to_map_multiple(gap.start(), pages_per_map); + let aligned_end_page = + self.round_page_num_up_to_map_multiple(gap.end(), pages_per_map); + + let gap_size = aligned_end_page - aligned_start_page; + if gap_size >= rounded_num_pages { + return Some(ie(aligned_end_page - rounded_num_pages, aligned_end_page)); + } + } + + None + } + + /// Finds aligned space above a hint address + /// + /// Searches for available space that satisfies both size and + /// alignment requirements, starting from the hint address. + /// + /// Arguments: + /// - num_pages: Number of pages needed + /// - pages_per_map: Alignment requirement in pages + /// - hint: Starting address to search from + /// + /// Returns: + /// - Some(Interval) containing aligned space + /// - None if no suitable space found + /// + /// Implementation details: + /// - Rounds page numbers up to alignment boundaries + /// - Handles alignment constraints for start and end addresses + /// - Searches only above the hint address + fn find_map_space_with_hint( + &self, + num_pages: u32, + pages_per_map: u32, + hint: u32, + ) -> Option> { + let start = hint; + let end = self.end_address; + + let rounded_num_pages = + self.round_page_num_up_to_map_multiple(num_pages, pages_per_map); + + for gap in self.entries.gaps_trimmed(ie(start, end)) { + let aligned_start_page = + self.trunc_page_num_down_to_map_multiple(gap.start(), pages_per_map); + let aligned_end_page = + self.round_page_num_up_to_map_multiple(gap.end(), pages_per_map); + + let gap_size = aligned_end_page - aligned_start_page; + if gap_size >= rounded_num_pages { + return Some(ie(aligned_end_page - rounded_num_pages, aligned_end_page)); + } + } + + None + } +} diff --git a/src/RawPOSIX/src/tests/fs_tests.rs b/src/RawPOSIX/src/tests/fs_tests.rs index 89646be0..453948d7 100644 --- a/src/RawPOSIX/src/tests/fs_tests.rs +++ b/src/RawPOSIX/src/tests/fs_tests.rs @@ -10,6 +10,7 @@ pub mod fs_tests { use libc::{c_void, O_DIRECTORY}; use std::fs::OpenOptions; use std::os::unix::fs::PermissionsExt; + use crate::constants::{S_IRWXA,SHMMAX,DEFAULT_UID,DEFAULT_GID}; use crate::interface::{StatData, FSData}; use libc::*; use crate::interface::{ShmidsStruct, get_errno}; @@ -421,7 +422,7 @@ pub mod fs_tests { //Checking if passing 0 as `len` to `mmap_syscall()` //correctly results in 'The value of len is 0` error. let mmap_result = cage.mmap_syscall(0 as *mut u8, 0, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - assert_eq!(mmap_result, -1, "Expected mmap to fail with -1 due to zero length"); + assert_eq!(mmap_result as i32, -EINVAL as i32, "Expected to fail with EINVAL due to zero length"); // Fetch the errno and check that it is `EINVAL` (Invalid argument) let errno = get_errno(); assert_eq!(errno, libc::EINVAL, "Expected errno to be EINVAL for zero-length mmap"); @@ -439,16 +440,18 @@ pub mod fs_tests { let cage = interface::cagetable_getref(1); - //Creating a regular file with `O_RDWR` flag - //making it valid for any mapping. + // Creating a regular file with `O_RDWR` flag + // making it valid for any mapping. let flags: i32 = O_TRUNC | O_CREAT | O_RDWR; let filepath = "/mmapTestFile1"; let fd = cage.open_syscall(filepath, flags, S_IRWXA); - //Writing into that file's first 9 bytes. + // Writing into that file's first 9 bytes. assert_eq!(cage.write_syscall(fd, str2cbuf("Test text"), 9), 9); + // When no flags are specified (flags = 0), mmap should fail with EINVAL let mmap_result = cage.mmap_syscall(0 as *mut u8, 5, PROT_READ | PROT_WRITE, 0, fd, 0); - assert_eq!(mmap_result, -1, "mmap did not fail as expected"); + assert_eq!(mmap_result as i32, -EINVAL as i32, "mmap did not fail with EINVAL as expected"); + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); lindrustfinalize(); } @@ -520,7 +523,8 @@ pub mod fs_tests { //allow reading correctly results in `File descriptor //is not open for reading` error. let mmap_result = cage.mmap_syscall(0 as *mut u8, 5, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - assert_eq!(mmap_result, -1, "Expected mmap to fail"); + assert_eq!(mmap_result as i32, -EINVAL as i32, "Expected to fail with EINVAL"); + // Fetch and print the errno for debugging let error = get_errno(); // Assert that the error is EACCES (Permission denied) @@ -569,7 +573,7 @@ pub mod fs_tests { 0, ); // Check if mmap_syscall returns -1 (failure) - assert_eq!(mmap_result, -1, "Expected mmap to fail due to lack of write permissions"); + assert_eq!(mmap_result as i32, -EINVAL as i32, "Expected to fail with EINVAL due to no write permission"); // Fetch and check the errno for debugging let err = get_errno(); // Ensure the errno is EACCES (Permission denied) @@ -602,7 +606,8 @@ pub mod fs_tests { /* Native linux will return EINVAL - TESTED locally */ let result = cage.mmap_syscall(0 as *mut u8, 5, PROT_READ | PROT_WRITE, MAP_SHARED, fd, -10); - assert_eq!(result, -1, "Expected mmap to fail with -1 for negative offset"); + assert_eq!(result as i32, -EINVAL as i32, "Expected mmap to fail with EINVAL for negative offset"); + // Verify errno is set to EINVAL let errno = get_errno(); assert_eq!(errno, libc::EINVAL, "Expected errno to be EINVAL for negative offset"); @@ -613,8 +618,8 @@ pub mod fs_tests { /* Native linux will return EINVAL - TESTED locally */ let result_beyond_eof = cage.mmap_syscall(0 as *mut u8, 5, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 25); - assert_eq!(result_beyond_eof, -1, "Expected mmap to fail with -1 for offset beyond EOF"); - + assert_eq!(result_beyond_eof as i32, -EINVAL as i32, "Expected mmap to fail with EINVAL for offset beyond EOF"); + // Verify errno is set to EINVAL let errno_beyond_eof = get_errno(); assert_eq!(errno_beyond_eof, libc::EINVAL, "Expected errno to be EINVAL for offset beyond EOF"); @@ -711,7 +716,7 @@ pub mod fs_tests { //Checking if passing the invalid file descriptor //correctly results in `Invalid file descriptor` error. assert_eq!( - cage.mmap_syscall(0 as *mut u8, 5, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0), + cage.mmap_syscall(0 as *mut u8, 5, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0) as i32, -(Errno::EBADF as i32) ); diff --git a/src/RawPOSIX/src/tests/networking_tests.rs b/src/RawPOSIX/src/tests/networking_tests.rs index cdcb9d5d..41d69702 100644 --- a/src/RawPOSIX/src/tests/networking_tests.rs +++ b/src/RawPOSIX/src/tests/networking_tests.rs @@ -2406,7 +2406,8 @@ pub mod net_tests { // this test is used for testing select on AF_UNIX socket pipe writefds // currently would fail since select_syscall does not handle socket pipe // writefds correctly - let byte_chunk: usize = UDSOCK_CAPACITY; + // Unix domain socket buffer size + let byte_chunk: usize = 212992; //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, // and also performs clean env setup diff --git a/src/RawPOSIX/src/tests/sys_tests.rs b/src/RawPOSIX/src/tests/sys_tests.rs index 5d3bbb1d..d45ecd8b 100644 --- a/src/RawPOSIX/src/tests/sys_tests.rs +++ b/src/RawPOSIX/src/tests/sys_tests.rs @@ -6,7 +6,7 @@ pub mod sys_tests { use super::super::*; use crate::interface; - // use crate::safeposix::cage::{FileDescriptor::*, *}; + use crate::constants::{DEFAULT_UID, DEFAULT_GID, EXIT_SUCCESS}; use crate::safeposix::{cage::*, dispatcher::*, filesystem}; #[test] diff --git a/src/RawPOSIX/src/tests/vmmap_test.rs b/src/RawPOSIX/src/tests/vmmap_test.rs new file mode 100644 index 00000000..b62da42b --- /dev/null +++ b/src/RawPOSIX/src/tests/vmmap_test.rs @@ -0,0 +1,699 @@ +#[cfg(test)] +pub mod vmmap_tests { + use super::super::*; + use crate::interface; + use crate::safeposix::{cage::*, dispatcher::*, filesystem}; + use crate::safeposix::syscalls::*; + use crate::interface; + + // Constants for testing + // Base address for test memory regions (4KB aligned) + const TEST_MEM_START: usize = 0x1000; + // Size of test memory regions (4KB) + const TEST_MEM_SIZE: usize = 0x1000; + // Memory protection flags + const PROT_READ: u32 = 1; // Read permission + const PROT_WRITE: u32 = 2; // Write permission + const PROT_EXEC: u32 = 4; // Execute permission + const EXIT_SUCCESS: i32 = 0; + + + /* + Test memory persistence after cage exit + + Layout: + Initial: + Cage A: [----Memory Region----] + [Written: "test_data"] + + After Exit: + Cage A: (cleaned up) + New Cage: [----Memory Region----] + [Should be clean] + */ + #[test] + pub fn ut_lind_vmmap_memory_persistence() { + //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, + // and also performs clean env setup + let _thelock = setup::lock_and_init(); + // Create initial cage + let cage = interface::cagetable_getref(1); + + // Allocate memory region with read/write permissions + let mut vmmap = cage.vmmap.write(); + assert_eq!( + vmmap.map_memory(TEST_MEM_START, TEST_MEM_SIZE, PROT_READ | PROT_WRITE), + Ok(()), + "Failed to map memory" + ); + + // Get the mapped region + let region = vmmap.get_region(TEST_MEM_START) + .expect("Memory region not found"); + + // Verify initial mapping + assert_eq!(region.permissions, PROT_READ | PROT_WRITE); + assert!(region.contains_range(TEST_MEM_START, TEST_MEM_SIZE)); + + // Exit the cage + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + + // Verify cleanup + assert!(interface::cagetable_lookup(1).is_none(), + "Cage still exists after exit"); + + // Create new cage and try to map same region + let new_cage = interface::cagetable_getref(2); + let mut new_vmmap = new_cage.vmmap.write(); + + // Try to map the same memory region + assert_eq!( + new_vmmap.map_memory(TEST_MEM_START, TEST_MEM_SIZE, PROT_READ | PROT_WRITE), + Ok(()), + "Failed to map previously used memory region" + ); + + // Verify new mapping is clean + let new_region = new_vmmap.get_region(TEST_MEM_START) + .expect("New memory region not found"); + + assert_eq!(new_region.permissions, PROT_READ | PROT_WRITE); + assert!(new_region.contains_range(TEST_MEM_START, TEST_MEM_SIZE)); + + // Cleanup new cage + assert_eq!(new_cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + + lindrustfinalize(); + } + + /* + Test basic memory cleanup after exit + + Initial: + Cage A: [----Memory Region----] + + After Exit: + Cage A: (cleaned up) + */ + #[test] + fn ut_lind_test_basic_exit_cleanup() { + let _thelock = setup::lock_and_init(); + + // Setup cage with memory allocation + let cage_a = create_test_cage(); + + // Allocate memory and verify + setup_memory_region(&cage_a, TEST_MEM_START, TEST_MEM_SIZE, PROT_READ | PROT_WRITE); + verify_memory_access(&cage_a, TEST_MEM_START, TEST_MEM_SIZE, PROT_READ | PROT_WRITE); + + // Exit cage + assert_eq!(cage_a.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + + // Verify cleanup + verify_memory_cleanup(cage_a.cageid); + lindrustfinalize(); + } + + /* + Test nested memory regions across multiple cages + + Layout: + Cage A: [----------------] + Cage B: [--------] + Cage C: [----] + */ + #[test] + fn ut_lind_test_nested_overlaps() { + let _thelock = setup::lock_and_init(); + + // Create cages + let cage_a = create_test_cage(); + let cage_b = create_test_cage(); + let cage_c = create_test_cage(); + + // Setup nested memory regions + setup_memory_region(&cage_a, TEST_MEM_START, TEST_MEM_SIZE * 4, PROT_READ | PROT_WRITE); + setup_memory_region(&cage_b, TEST_MEM_START + TEST_MEM_SIZE, TEST_MEM_SIZE * 2, PROT_READ | PROT_WRITE); + setup_memory_region(&cage_c, TEST_MEM_START + TEST_MEM_SIZE * 2, TEST_MEM_SIZE, PROT_READ | PROT_WRITE); + + // Verify initial setup + verify_memory_access(&cage_a, TEST_MEM_START, TEST_MEM_SIZE * 4, PROT_READ | PROT_WRITE); + verify_memory_access(&cage_b, TEST_MEM_START + TEST_MEM_SIZE, TEST_MEM_SIZE * 2, PROT_READ | PROT_WRITE); + verify_memory_access(&cage_c, TEST_MEM_START + TEST_MEM_SIZE * 2, TEST_MEM_SIZE, PROT_READ | PROT_WRITE); + + // Test exits in sequence + assert_eq!(cage_c.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + verify_memory_cleanup(cage_c.cageid); + + // Verify B and A still accessible + verify_memory_access(&cage_b, TEST_MEM_START + TEST_MEM_SIZE, TEST_MEM_SIZE * 2, PROT_READ | PROT_WRITE); + verify_memory_access(&cage_a, TEST_MEM_START, TEST_MEM_SIZE * 4, PROT_READ | PROT_WRITE); + + assert_eq!(cage_b.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + verify_memory_cleanup(cage_b.cageid); + + // Verify A still accessible + verify_memory_access(&cage_a, TEST_MEM_START, TEST_MEM_SIZE * 4, PROT_READ | PROT_WRITE); + + assert_eq!(cage_a.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + verify_memory_cleanup(cage_a.cageid); + lindrustfinalize(); + } + + /* + Test interleaved memory regions + + Layout: + Cage A: [---] [---] [---] + Cage B: [---] [---] [---] + */ + #[test] + fn ut_lind_test_interleaved_regions() { + let _thelock = setup::lock_and_init(); + + let cage_a = create_test_cage(); + let cage_b = create_test_cage(); + + // Setup interleaved regions for Cage A + setup_memory_region(&cage_a, TEST_MEM_START, TEST_MEM_SIZE, PROT_READ | PROT_WRITE); + setup_memory_region(&cage_a, TEST_MEM_START + TEST_MEM_SIZE * 3, TEST_MEM_SIZE, PROT_READ | PROT_WRITE); + setup_memory_region(&cage_a, TEST_MEM_START + TEST_MEM_SIZE * 5, TEST_MEM_SIZE, PROT_READ | PROT_WRITE); + + // Setup interleaved regions for Cage B + setup_memory_region(&cage_b, TEST_MEM_START + TEST_MEM_SIZE, TEST_MEM_SIZE, PROT_READ | PROT_WRITE); + setup_memory_region(&cage_b, TEST_MEM_START + TEST_MEM_SIZE * 4, TEST_MEM_SIZE, PROT_READ | PROT_WRITE); + setup_memory_region(&cage_b, TEST_MEM_START + TEST_MEM_SIZE * 6, TEST_MEM_SIZE, PROT_READ | PROT_WRITE); + + // Verify all regions are accessible + verify_memory_access(&cage_a, TEST_MEM_START, TEST_MEM_SIZE, PROT_READ | PROT_WRITE); + verify_memory_access(&cage_a, TEST_MEM_START + TEST_MEM_SIZE * 3, TEST_MEM_SIZE, PROT_READ | PROT_WRITE); + verify_memory_access(&cage_a, TEST_MEM_START + TEST_MEM_SIZE * 5, TEST_MEM_SIZE, PROT_READ | PROT_WRITE); + + verify_memory_access(&cage_b, TEST_MEM_START + TEST_MEM_SIZE, TEST_MEM_SIZE, PROT_READ | PROT_WRITE); + verify_memory_access(&cage_b, TEST_MEM_START + TEST_MEM_SIZE * 4, TEST_MEM_SIZE, PROT_READ | PROT_WRITE); + verify_memory_access(&cage_b, TEST_MEM_START + TEST_MEM_SIZE * 6, TEST_MEM_SIZE, PROT_READ | PROT_WRITE); + + // Test cleanup + assert_eq!(cage_a.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + verify_memory_cleanup(cage_a.cageid); + + // Verify B's regions still accessible + verify_memory_access(&cage_b, TEST_MEM_START + TEST_MEM_SIZE, TEST_MEM_SIZE, PROT_READ | PROT_WRITE); + verify_memory_access(&cage_b, TEST_MEM_START + TEST_MEM_SIZE * 4, TEST_MEM_SIZE, PROT_READ | PROT_WRITE); + verify_memory_access(&cage_b, TEST_MEM_START + TEST_MEM_SIZE * 6, TEST_MEM_SIZE, PROT_READ | PROT_WRITE); + + assert_eq!(cage_b.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + verify_memory_cleanup(cage_b.cageid); + lindrustfinalize(); + } + + /* + Test circular shared memory dependencies + + Layout: + Cage A → Cage B + ↑ ↓ + Cage C + */ + #[test] + fn ut_lind_test_circular_dependencies() { + let _thelock = setup::lock_and_init(); + + let cage_a = create_test_cage(); + let cage_b = create_test_cage(); + let cage_c = create_test_cage(); + + // Setup circular shared memory regions + setup_memory_region(&cage_a, TEST_MEM_START, TEST_MEM_SIZE, PROT_READ | PROT_WRITE); + setup_memory_region(&cage_b, TEST_MEM_START + TEST_MEM_SIZE, TEST_MEM_SIZE, PROT_READ | PROT_WRITE); + setup_memory_region(&cage_c, TEST_MEM_START + TEST_MEM_SIZE * 2, TEST_MEM_SIZE, PROT_READ | PROT_WRITE); + + // Verify initial access + verify_memory_access(&cage_a, TEST_MEM_START, TEST_MEM_SIZE, PROT_READ | PROT_WRITE); + verify_memory_access(&cage_b, TEST_MEM_START + TEST_MEM_SIZE, TEST_MEM_SIZE, PROT_READ | PROT_WRITE); + verify_memory_access(&cage_c, TEST_MEM_START + TEST_MEM_SIZE * 2, TEST_MEM_SIZE, PROT_READ | PROT_WRITE); + + // Test cleanup in different orders + assert_eq!(cage_b.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + verify_memory_cleanup(cage_b.cageid); + + verify_memory_access(&cage_a, TEST_MEM_START, TEST_MEM_SIZE, PROT_READ | PROT_WRITE); + verify_memory_access(&cage_c, TEST_MEM_START + TEST_MEM_SIZE * 2, TEST_MEM_SIZE, PROT_READ | PROT_WRITE); + + assert_eq!(cage_c.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + verify_memory_cleanup(cage_c.cageid); + + verify_memory_access(&cage_a, TEST_MEM_START, TEST_MEM_SIZE, PROT_READ | PROT_WRITE); + + assert_eq!(cage_a.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + verify_memory_cleanup(cage_a.cageid); + lindrustfinalize(); + } + + /* + Test splitting shared memory regions + + Initial: + [----------------] + + After Split: + [----] [----] [----] + */ + #[test] + fn ut_lind_test_split_regions() { + let _thelock = setup::lock_and_init(); + + let cage = create_test_cage(); + + // Setup initial large region + setup_memory_region(&cage, TEST_MEM_START, TEST_MEM_SIZE * 3, PROT_READ | PROT_WRITE); + verify_memory_access(&cage, TEST_MEM_START, TEST_MEM_SIZE * 3, PROT_READ | PROT_WRITE); + + // Split into three regions with different permissions + let mut vmmap = cage.vmmap.write(); + vmmap.unmap_memory(TEST_MEM_START + TEST_MEM_SIZE, TEST_MEM_SIZE); + + // Verify split regions + verify_memory_access(&cage, TEST_MEM_START, TEST_MEM_SIZE, PROT_READ | PROT_WRITE); + verify_memory_access(&cage, TEST_MEM_START + TEST_MEM_SIZE * 2, TEST_MEM_SIZE, PROT_READ | PROT_WRITE); + + assert!(vmmap.get_region(TEST_MEM_START + TEST_MEM_SIZE).is_none(), + "Middle region should be unmapped"); + + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + verify_memory_cleanup(cage.cageid); + lindrustfinalize(); + } + + /* + Test overlapping regions with different permissions + + Layout: + Cage A: [RW-][R--][RWX] + Cage B: [R--][RWX][RW-] + */ + #[test] + fn ut_lind_test_mixed_permissions() { + let _thelock = setup::lock_and_init(); + + let cage_a = create_test_cage(); + let cage_b = create_test_cage(); + + // Setup regions with different permissions for Cage A + setup_memory_region(&cage_a, TEST_MEM_START, TEST_MEM_SIZE, PROT_READ | PROT_WRITE); + setup_memory_region(&cage_a, TEST_MEM_START + TEST_MEM_SIZE, TEST_MEM_SIZE, PROT_READ); + setup_memory_region(&cage_a, TEST_MEM_START + TEST_MEM_SIZE * 2, TEST_MEM_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC); + + // Setup regions with different permissions for Cage B + setup_memory_region(&cage_b, TEST_MEM_START, TEST_MEM_SIZE, PROT_READ); + setup_memory_region(&cage_b, TEST_MEM_START + TEST_MEM_SIZE, TEST_MEM_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC); + setup_memory_region(&cage_b, TEST_MEM_START + TEST_MEM_SIZE * 2, TEST_MEM_SIZE, PROT_READ | PROT_WRITE); + + // Verify permissions + verify_memory_access(&cage_a, TEST_MEM_START, TEST_MEM_SIZE, PROT_READ | PROT_WRITE); + verify_memory_access(&cage_a, TEST_MEM_START + TEST_MEM_SIZE, TEST_MEM_SIZE, PROT_READ); + verify_memory_access(&cage_a, TEST_MEM_START + TEST_MEM_SIZE * 2, TEST_MEM_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC); + + verify_memory_access(&cage_b, TEST_MEM_START, TEST_MEM_SIZE, PROT_READ); + verify_memory_access(&cage_b, TEST_MEM_START + TEST_MEM_SIZE, TEST_MEM_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC); + verify_memory_access(&cage_b, TEST_MEM_START + TEST_MEM_SIZE * 2, TEST_MEM_SIZE, PROT_READ | PROT_WRITE); + + // Test cleanup + assert_eq!(cage_a.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + verify_memory_cleanup(cage_a.cageid); + + assert_eq!(cage_b.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + verify_memory_cleanup(cage_b.cageid); + lindrustfinalize(); + } + + /* + Test memory boundary conditions + + Layout: + Cage A: [----] + Cage B: [----] (exactly adjacent) + */ + #[test] + fn ut_lind_test_boundary_cases() { + let _thelock = setup::lock_and_init(); + + let cage_a = create_test_cage(); + let cage_b = create_test_cage(); + + // Setup adjacent regions + setup_memory_region(&cage_a, TEST_MEM_START, TEST_MEM_SIZE, PROT_READ | PROT_WRITE); + setup_memory_region(&cage_b, TEST_MEM_START + TEST_MEM_SIZE, TEST_MEM_SIZE, PROT_READ | PROT_WRITE); + + // Verify each region + verify_memory_access(&cage_a, TEST_MEM_START, TEST_MEM_SIZE, PROT_READ | PROT_WRITE); + verify_memory_access(&cage_b, TEST_MEM_START + TEST_MEM_SIZE, TEST_MEM_SIZE, PROT_READ | PROT_WRITE); + + // Test boundary protection + let vmmap_a = cage_a.vmmap.read(); + let vmmap_b = cage_b.vmmap.read(); + + assert!(vmmap_a.get_region(TEST_MEM_START + TEST_MEM_SIZE).is_none(), + "Cage A should not access Cage B's memory"); + assert!(vmmap_b.get_region(TEST_MEM_START).is_none(), + "Cage B should not access Cage A's memory"); + + // Test cleanup + assert_eq!(cage_a.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + verify_memory_cleanup(cage_a.cageid); + + verify_memory_access(&cage_b, TEST_MEM_START + TEST_MEM_SIZE, TEST_MEM_SIZE, PROT_READ | PROT_WRITE); + + assert_eq!(cage_b.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + verify_memory_cleanup(cage_b.cageid); + lindrustfinalize(); + } + + /* + Test dynamic memory region growth + + Initial: + Cage A: [----] + Cage B: [----] + + After Growth: + Cage A: [--------] + Cage B: [----] + */ + #[test] + fn ut_lind_test_dynamic_growth() { + let _thelock = setup::lock_and_init(); + + let cage_a = create_test_cage(); + let cage_b = create_test_cage(); + + // Initial setup + setup_memory_region(&cage_a, TEST_MEM_START, TEST_MEM_SIZE, PROT_READ | PROT_WRITE); + setup_memory_region(&cage_b, TEST_MEM_START + TEST_MEM_SIZE, TEST_MEM_SIZE, PROT_READ | PROT_WRITE); + + // Verify initial state + verify_memory_access(&cage_a, TEST_MEM_START, TEST_MEM_SIZE, PROT_READ | PROT_WRITE); + verify_memory_access(&cage_b, TEST_MEM_START + TEST_MEM_SIZE, TEST_MEM_SIZE, PROT_READ | PROT_WRITE); + + // Grow cage_a's region + let mut vmmap = cage_a.vmmap.write(); + vmmap.map_memory(TEST_MEM_START, TEST_MEM_SIZE * 2, PROT_READ | PROT_WRITE); + + // Verify growth + verify_memory_access(&cage_a, TEST_MEM_START, TEST_MEM_SIZE * 2, PROT_READ | PROT_WRITE); + verify_memory_access(&cage_b, TEST_MEM_START + TEST_MEM_SIZE, TEST_MEM_SIZE, PROT_READ | PROT_WRITE); + + // Cleanup + assert_eq!(cage_a.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + verify_memory_cleanup(cage_a.cageid); + + assert_eq!(cage_b.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + verify_memory_cleanup(cage_b.cageid); + lindrustfinalize(); + } + + /* + Test invalid memory operations + + Cases: + 1. Zero-sized region + 2. Invalid permissions + 3. Overlapping with invalid permissions + 4. Out of bounds access + */ + #[test] + fn ut_lind_test_invalid_memory_operations() { + let _thelock = setup::lock_and_init(); + + let cage = create_test_cage(); + + // Test zero-sized region + assert!(cage.vmmap.write().map_memory(TEST_MEM_START, 0, PROT_READ).is_err()); + + // Test invalid permissions + assert!(cage.vmmap.write().map_memory(TEST_MEM_START, TEST_MEM_SIZE, 0xFF).is_err()); + + // Test valid setup + setup_memory_region(&cage, TEST_MEM_START, TEST_MEM_SIZE, PROT_READ | PROT_WRITE); + + // Test overlapping with invalid permissions + assert!(cage.vmmap.write().map_memory(TEST_MEM_START, TEST_MEM_SIZE, 0xFF).is_err()); + + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + verify_memory_cleanup(cage.cageid); + lindrustfinalize(); + } + + /* + Test concurrent memory access + + Layout: + Multiple cages accessing same memory regions + */ + #[test] + fn ut_lind_test_concurrent_access() { + let _thelock = setup::lock_and_init(); + + use std::sync::Arc; + use std::thread; + + let cage_count = 5; + let cages: Vec> = (0..cage_count) + .map(|_| Arc::new(create_test_cage())) + .collect(); + + // Setup shared region + for cage in &cages { + setup_memory_region(&cage, TEST_MEM_START, TEST_MEM_SIZE, PROT_READ | PROT_WRITE); + } + + // Concurrent access + let handles: Vec<_> = cages.iter().map(|cage| { + let cage = Arc::clone(cage); + thread::spawn(move || { + verify_memory_access(&cage, TEST_MEM_START, TEST_MEM_SIZE, PROT_READ | PROT_WRITE); + }) + }).collect(); + + // Wait for all threads + for handle in handles { + handle.join().unwrap(); + } + + // Cleanup + for cage in cages { + assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + verify_memory_cleanup(cage.cageid); + } + lindrustfinalize(); + } + + /* + Test overlapping regions with conflicting permissions + + Layout: + Initial: + Cage A: [RW-][-W-][RWX] + Cage B: [R--][--X][RW-] + | | | + v v v + Result: [R--][---][RW-] (most restrictive permissions win) + */ + #[test] + fn ut_lind_test_overlapping_permissions_conflict() { + let _thelock = setup::lock_and_init(); + + let cage_a = create_test_cage(); + let cage_b = create_test_cage(); + + // Setup overlapping regions with different permissions + setup_memory_region(&cage_a, TEST_MEM_START, TEST_MEM_SIZE, PROT_READ | PROT_WRITE).unwrap(); + setup_memory_region(&cage_a, TEST_MEM_START + TEST_MEM_SIZE, TEST_MEM_SIZE, PROT_WRITE).unwrap(); + setup_memory_region(&cage_a, TEST_MEM_START + TEST_MEM_SIZE * 2, TEST_MEM_SIZE, + PROT_READ | PROT_WRITE | PROT_EXEC).unwrap(); + + // Try to set conflicting permissions + let result = setup_memory_region(&cage_b, TEST_MEM_START, TEST_MEM_SIZE, PROT_READ); + assert!(result.is_err(), "Should not allow conflicting permissions"); + + // Cleanup + assert_eq!(cage_a.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + verify_memory_cleanup(cage_a.cageid); + + assert_eq!(cage_b.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + verify_memory_cleanup(cage_b.cageid); + lindrustfinalize(); + } + + /* + Test maximum number of shared regions + + Layout: + Cage A: [1][2][3]...[MAX] + Cage B: [1][2][3]...[MAX] + | | | | + Shared Regions + */ + #[test] + fn ut_lind_test_memory_sharing_limits() { + let _thelock = setup::lock_and_init(); + + let cage_a = create_test_cage(); + let cage_b = create_test_cage(); + + const MAX_REGIONS: usize = 10; // Adjust based on system limits + let mut regions = Vec::new(); + + // Try to create maximum number of shared regions + for i in 0..MAX_REGIONS { + let start = TEST_MEM_START + (i * TEST_MEM_SIZE); + let result = setup_memory_region(&cage_a, start, TEST_MEM_SIZE, PROT_READ | PROT_WRITE); + if result.is_ok() { + regions.push(start); + } else { + println!("Max regions reached at {}", i); + break; + } + } + + // Verify all regions are accessible + for &start in ®ions { + verify_memory_access(&cage_a, start, TEST_MEM_SIZE, PROT_READ | PROT_WRITE); + } + + // Cleanup + assert_eq!(cage_a.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + verify_memory_cleanup(cage_a.cageid); + + assert_eq!(cage_b.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + verify_memory_cleanup(cage_b.cageid); + lindrustfinalize(); + } + + /* + Test concurrent map/unmap operations + + Layout: + Thread 1: [Map][Unmap][Map ] + Thread 2: [ Map ][Unmap ] + Thread 3: [ Unmap ][ Map ] + Time -> ------------------> + */ + #[test] + fn ut_lind_test_concurrent_mapping_unmapping() { + let _thelock = setup::lock_and_init(); + + use std::sync::Arc; + use std::thread; + use std::sync::Barrier; + + let cage = Arc::new(create_test_cage()); + let barrier = Arc::new(Barrier::new(3)); + + // Setup initial region + setup_memory_region(&cage, TEST_MEM_START, TEST_MEM_SIZE, PROT_READ | PROT_WRITE).unwrap(); + + // Create threads for concurrent operations + let handles: Vec<_> = (0..3).map(|i| { + let cage = Arc::clone(&cage); + let barrier = Arc::clone(&barrier); + + thread::spawn(move || { + barrier.wait(); + match i { + 0 => { + // Thread 1: Map -> Unmap -> Map + setup_memory_region(&cage, TEST_MEM_START + TEST_MEM_SIZE, + TEST_MEM_SIZE, PROT_READ | PROT_WRITE).unwrap(); + cage.vmmap.write().unmap_memory(TEST_MEM_START + TEST_MEM_SIZE, TEST_MEM_SIZE); + setup_memory_region(&cage, TEST_MEM_START + TEST_MEM_SIZE * 2, + TEST_MEM_SIZE, PROT_READ | PROT_WRITE).unwrap(); + }, + 1 => { + // Thread 2: Map larger region -> Unmap portion + setup_memory_region(&cage, TEST_MEM_START + TEST_MEM_SIZE * 3, + TEST_MEM_SIZE * 2, PROT_READ | PROT_WRITE).unwrap(); + cage.vmmap.write().unmap_memory(TEST_MEM_START + TEST_MEM_SIZE * 4, TEST_MEM_SIZE); + }, + _ => { + // Thread 3: Unmap -> Map different region + cage.vmmap.write().unmap_memory(TEST_MEM_START, TEST_MEM_SIZE); + setup_memory_region(&cage, TEST_MEM_START + TEST_MEM_SIZE * 5, + TEST_MEM_SIZE, PROT_READ | PROT_WRITE).unwrap(); + } + } + }) + }).collect(); + + // Wait for all threads + for handle in handles { + handle.join().unwrap(); + } + + // Verify final state + let vmmap = cage.vmmap.read(); + assert!(vmmap.get_region(TEST_MEM_START).is_none(), "Region should be unmapped"); + assert!(vmmap.get_region(TEST_MEM_START + TEST_MEM_SIZE * 2).is_some(), + "Region should be mapped"); + assert!(vmmap.get_region(TEST_MEM_START + TEST_MEM_SIZE * 3).is_some(), + "Region should be mapped"); + assert!(vmmap.get_region(TEST_MEM_START + TEST_MEM_SIZE * 5).is_some(), + "Region should be mapped"); + + // Cleanup + assert_eq!(Arc::try_unwrap(cage).unwrap().exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + lindrustfinalize(); + } + + // Helper functions + fn create_test_cage() -> Cage { + let cage_id = interface::get_new_cage_id(); + let cage = Cage::new(cage_id); + interface::cagetable_insert(cage_id, cage.clone()); + cage + } + + fn verify_memory_cleanup(cage_id: u64) { + // Check if cage exists in cagetable + assert!(interface::cagetable_lookup(cage_id).is_none(), + "Cage still exists in cagetable after cleanup"); + + // Verify fd table cleanup + assert!(fdtables::get_fdtable_for_cage(cage_id).is_none(), + "FD table still exists after cleanup"); + + // Verify all memory regions are unmapped + assert!(interface::get_memory_regions(cage_id).is_empty(), + "Memory regions still exist after cleanup"); + } + + fn setup_memory_region(cage: &Cage, start: usize, size: usize, permissions: u32) -> Result<(), &'static str> { + if size == 0 { + return Err("Memory region size must be positive"); + } + if permissions & (PROT_READ | PROT_WRITE | PROT_EXEC) != permissions { + return Err("Invalid permissions specified"); + } + + let mut vmmap = cage.vmmap.write(); + vmmap.map_memory(start, size, permissions) + .map_err(|_| "Failed to map memory region") + } + + fn verify_memory_access(cage: &Cage, start: usize, size: usize, expected_perms: u32) { + assert!(size > 0, "Memory region size must be positive"); + + let vmmap = cage.vmmap.read(); + let region = vmmap.get_region(start) + .expect("Memory region not found"); + + assert_eq!(region.permissions, expected_perms, + "Incorrect permissions for memory region"); + assert!(region.contains_range(start, size), + "Memory region does not contain expected range"); + + // Verify boundaries + assert!(region.start <= start, "Region start boundary incorrect"); + assert!(region.start + region.size >= start + size, "Region end boundary incorrect"); + } +} \ No newline at end of file diff --git a/src/glibc/lind_syscall/lind_syscall.c b/src/glibc/lind_syscall/lind_syscall.c index 3acdf687..321f0056 100644 --- a/src/glibc/lind_syscall/lind_syscall.c +++ b/src/glibc/lind_syscall/lind_syscall.c @@ -32,15 +32,23 @@ int __imported_wasi_snapshot_preview1_lind_syscall(unsigned int callnumber, unsi // handled here instead int lind_syscall (unsigned int callnumber, unsigned long long callname, unsigned long long arg1, unsigned long long arg2, unsigned long long arg3, unsigned long long arg4, unsigned long long arg5, unsigned long long arg6) { - int ret = __imported_wasi_snapshot_preview1_lind_syscall(callnumber, callname, arg1, arg2, arg3, arg4, arg5, arg6); - // handle the errno - if(ret < 0) - { - errno = -ret; - } - else - { - errno = 0; - } - return ret; + int ret = __imported_wasi_snapshot_preview1_lind_syscall(callnumber, callname, arg1, arg2, arg3, arg4, arg5, arg6); + // handle the errno + // in rawposix, we use -errno as the return value to indicate the error + // but this may cause some issues for mmap syscall, because mmap syscall + // is returning an 32-bit address, which may overflow the int type (i32) + // luckily we can handle this easily because the return value of mmap is always + // multiple of pages (typically 4096) even when overflow, therefore we can distinguish + // the errno and mmap result by simply checking if the return value is + // within the valid errno range + if(ret < 0 && ret > -256) + { + errno = -ret; + return -1; + } + else + { + errno = 0; + } + return ret; } diff --git a/src/glibc/misc/sbrk.c b/src/glibc/misc/sbrk.c index f055f37c..a9d08a73 100644 --- a/src/glibc/misc/sbrk.c +++ b/src/glibc/misc/sbrk.c @@ -24,6 +24,8 @@ #include #include #include +#include +#include /* Defined in brk.c. */ // This is the "virtual brk" exposed to the caller @@ -41,39 +43,7 @@ extern void *__curbrk; void * __sbrk (intptr_t increment) { - __curbrk = __builtin_wasm_memory_size(0) * PAGESIZE; - - // sbrk(0) returns the current memory size. - if (increment == 0) { - // The wasm spec doesn't guarantee that memory.grow of 0 always succeeds. - return __curbrk; - } - - // FIXME: now two threads calling this sbrk simultaneously - // will lead to the corruption of __curbrk, so we should move - // this implementation into the runtime, and protect the __curbrk - // with mutex (i.e. preventing two sbrk to be executed at the same time) - - void * linear_mem_end = __builtin_wasm_memory_size(0) * PAGESIZE; - void * old_break = __curbrk; - void * new_break = old_break + increment; - - if (new_break <= linear_mem_end) { - // In this case, we don't need to grow linear mem - __curbrk = new_break; - return old_break; - } - - // Now we need to grow linear mem - int new_pages = (new_break - linear_mem_end) / PAGESIZE; - - if (__builtin_wasm_memory_grow(0, new_pages) < 0) { - errno = ENOMEM; - return (void *)-1; - } - - __curbrk = new_break; - return old_break; + return MAKE_SYSCALL(176, "syscall|sbrk", (uint64_t) increment, NOTUSED, NOTUSED, NOTUSED, NOTUSED, NOTUSED); } // void * diff --git a/src/glibc/nptl/allocatestack.c b/src/glibc/nptl/allocatestack.c index c1d3bb4c..061ae129 100644 --- a/src/glibc/nptl/allocatestack.c +++ b/src/glibc/nptl/allocatestack.c @@ -324,8 +324,8 @@ allocate_stack (const struct pthread_attr *attr, struct pthread **pdp, size_t reported_guardsize; size_t reqsize; void *mem; - const int prot = (PROT_READ | PROT_WRITE - | ((GL(dl_stack_flags) & PF_X) ? PROT_EXEC : 0)); + // lind-wasm: deleted PROT_EXEC since lind disallows PROT_EXEC mapping + const int prot = (PROT_READ | PROT_WRITE); /* Adjust the stack size for alignment. */ size &= ~tls_static_align_m1; @@ -363,17 +363,8 @@ allocate_stack (const struct pthread_attr *attr, struct pthread **pdp, /* If a guard page is required, avoid committing memory by first allocate with PROT_NONE and then reserve with required permission excluding the guard page. */ - // mem = __mmap (NULL, size, (guardsize == 0) ? prot : PROT_NONE, - // MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0); - - // Replacement mmap with malloc - Dennis - // BUG: changed the malloc size to 16416 (1/4 of the original) - // since currently there is an issue on malloc that it cannot - // automatically grow the memory if memory is running out. - // Once the issue is fixed, we might be able to change the size - // back - Qianxi Chen - size = 16416; - void* mem = malloc(size); + mem = __mmap (NULL, size, (guardsize == 0) ? prot : PROT_NONE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (mem == NULL) { // Handle memory allocation failure @@ -439,17 +430,18 @@ allocate_stack (const struct pthread_attr *attr, struct pthread **pdp, /* Don't allow setxid until cloned. */ pd->setxid_futex = -1; + // BUG: TLS related stuff is not working in lind-wasm currently /* Allocate the DTV for this thread. */ - if (_dl_allocate_tls (TLS_TPADJ (pd)) == NULL) - { - /* Something went wrong. */ - assert (errno == ENOMEM); + // if (_dl_allocate_tls (TLS_TPADJ (pd)) == NULL) + // { + // /* Something went wrong. */ + // assert (errno == ENOMEM); - /* Free the stack memory we just allocated. */ - (void) __munmap (mem, size); + // /* Free the stack memory we just allocated. */ + // (void) __munmap (mem, size); - return errno; - } + // return errno; + // } /* Prepare to modify global data. */ diff --git a/src/glibc/nptl/pthread_create.c b/src/glibc/nptl/pthread_create.c index fe661c24..d94c0bb6 100644 --- a/src/glibc/nptl/pthread_create.c +++ b/src/glibc/nptl/pthread_create.c @@ -347,7 +347,7 @@ static int create_thread (struct pthread *pd, const struct pthread_attr *attr, args->flags = clone_flags; args->stack = stackaddr; args->stack = stackaddr + pd->stackblock_size - sizeof(struct clone_args) - TLS_TCB_SIZE; - args->stack_size = 16416 - sizeof(struct clone_args) - TLS_TCB_SIZE; + args->stack_size = stacksize - sizeof(struct clone_args) - TLS_TCB_SIZE; args->child_tid = &pd->tid; int ret = __clone_internal(args, &start_thread, pd); diff --git a/src/glibc/sysdeps/unix/sysv/linux/brk.c b/src/glibc/sysdeps/unix/sysv/linux/brk.c index bf38c039..b1665c48 100644 --- a/src/glibc/sysdeps/unix/sysv/linux/brk.c +++ b/src/glibc/sysdeps/unix/sysv/linux/brk.c @@ -20,6 +20,7 @@ #include #include #include +#include /* This must be initialized data because commons can't have aliases. */ // This is the "virtual brk" exposed to the caller @@ -39,32 +40,6 @@ weak_alias (__curbrk, ___brk_addr) int __brk (void *addr) { - __curbrk = __builtin_wasm_memory_size(0) * PAGESIZE; - - // FIXME: now two threads calling this sbrk simultaneously - // will lead to the corruption of __curbrk, so we should move - // this implementation into the runtime, and protect the __curbrk - // with mutex (i.e. preventing two brk/sbrk to be executed at the same time) - - void * linear_mem_end = __builtin_wasm_memory_size(0) * PAGESIZE; - void * old_break = __curbrk; - void * new_break = addr; - - if (new_break <= linear_mem_end) { - // In this case, we don't need to grow linear mem - __curbrk = new_break; - return old_break; - } - - // Now we need to grow linear mem - int new_pages = (new_break - linear_mem_end) / PAGESIZE; - - if (__builtin_wasm_memory_grow(0, new_pages) < 0) { - errno = ENOMEM; - return (void *)-1; - } - - __curbrk = new_break; - return old_break; + return MAKE_SYSCALL(175, "syscall|brk", (uint64_t) addr, NOTUSED, NOTUSED, NOTUSED, NOTUSED, NOTUSED); } weak_alias (__brk, brk) diff --git a/src/wasmtime/crates/lind-common/src/lib.rs b/src/wasmtime/crates/lind-common/src/lib.rs index f21beb2b..30b7b063 100644 --- a/src/wasmtime/crates/lind-common/src/lib.rs +++ b/src/wasmtime/crates/lind-common/src/lib.rs @@ -74,13 +74,13 @@ impl LindCommonCtx { // setjmp call. This function needs to be handled within wasmtime, but it is not an actual syscall so we use a different routine from lind_syscall pub fn lind_setjmp + Clone + Send + 'static + std::marker::Sync, U: Clone + Send + 'static + std::marker::Sync> - (&self, caller: &mut Caller<'_, T>, jmp_buf: i32) -> i32 { + (&self, caller: &mut Caller<'_, T>, jmp_buf: u32) -> i32 { wasmtime_lind_multi_process::setjmp_call(caller, jmp_buf) } // longjmp call. This function needs to be handled within wasmtime, but it is not an actual syscall so we use a different routine from lind_syscall pub fn lind_longjmp + Clone + Send + 'static + std::marker::Sync, U: Clone + Send + 'static + std::marker::Sync> - (&self, caller: &mut Caller<'_, T>, jmp_buf: i32, retval: i32) -> i32 { + (&self, caller: &mut Caller<'_, T>, jmp_buf: u32, retval: i32) -> i32 { wasmtime_lind_multi_process::longjmp_call(caller, jmp_buf, retval) } @@ -144,7 +144,7 @@ pub fn add_to_linker + Clone + Send + 'static + std::marker::S let host = caller.data().clone(); let ctx = get_cx(&host); - ctx.lind_setjmp(&mut caller, jmp_buf) + ctx.lind_setjmp(&mut caller, jmp_buf as u32) }, )?; @@ -156,7 +156,7 @@ pub fn add_to_linker + Clone + Send + 'static + std::marker::S let host = caller.data().clone(); let ctx = get_cx(&host); - ctx.lind_longjmp(&mut caller, jmp_buf, retval) + ctx.lind_longjmp(&mut caller, jmp_buf as u32, retval) }, )?; diff --git a/src/wasmtime/crates/lind-multi-process/src/lib.rs b/src/wasmtime/crates/lind-multi-process/src/lib.rs index 7076c7f3..fdc8b632 100644 --- a/src/wasmtime/crates/lind-multi-process/src/lib.rs +++ b/src/wasmtime/crates/lind-multi-process/src/lib.rs @@ -11,7 +11,7 @@ use std::path::Path; use std::sync::atomic::{AtomicU32, AtomicU64, Ordering}; use std::sync::{Arc, Barrier}; use std::thread; -use wasmtime::{AsContext, AsContextMut, Caller, ExternType, Linker, Module, SharedMemory, Store, Val, OnCalledAction, RewindingReturn, StoreOpaque, InstanceId}; +use wasmtime::{AsContext, AsContextMut, Caller, ExternType, InstanceId, InstantiateType, Linker, Module, OnCalledAction, RewindingReturn, SharedMemory, Store, StoreOpaque, Val}; use wasmtime_environ::MemoryIndex; @@ -269,6 +269,7 @@ impl, - stack_addr: i32, stack_size: i32, child_tid: u64 + stack_addr: u32, stack_size: u32, child_tid: u64 ) -> Result { // get the base address of the memory let handle = caller.as_context().0.instance(InstanceId::from_index(0)); @@ -565,11 +552,11 @@ impl(&mut store, "set_stack_pointer") .unwrap(); - let _ = stack_pointer_setter.call(&mut store, stack_addr - offset); + let _ = stack_pointer_setter.call(&mut store, (stack_addr - offset) as i32); // get the asyncify_rewind_start and module start function let child_rewind_start; @@ -773,7 +760,7 @@ impl, jmp_buf: i32) -> Result { + pub fn setjmp_call(&self, mut caller: &mut Caller<'_, T>, jmp_buf: u32) -> Result { // get the base address of the memory let handle = caller.as_context().0.instance(InstanceId::from_index(0)); let defined_memory = handle.get_memory(MemoryIndex::from_u32(0)); @@ -890,7 +877,7 @@ impl, jmp_buf: i32, retval: i32) -> Result { + pub fn longjmp_call(&self, mut caller: &mut Caller<'_, T>, jmp_buf: u32, retval: i32) -> Result { // get the base address of the memory let handle = caller.as_context().0.instance(InstanceId::from_index(0)); let defined_memory = handle.get_memory(MemoryIndex::from_u32(0)); @@ -965,7 +952,7 @@ impl + Clone + Send + 'static + std::marker::Sync, // entry point of pthread_create syscall pub fn lind_pthread_create + Clone + Send + 'static + std::marker::Sync, U: Clone + Send + 'static + std::marker::Sync> (caller: &mut Caller<'_, T>, - stack_addr: i32, stack_size: i32, child_tid: u64) -> Result { + stack_addr: u32, stack_size: u32, child_tid: u64) -> Result { let host = caller.data().clone(); let ctx = host.get_ctx(); ctx.pthread_create_call(caller, stack_addr, stack_size, child_tid) @@ -1145,7 +1131,7 @@ pub fn clone_syscall + Clone + Send + 'static + std::marker::S } else { // pthread_create - match lind_pthread_create(caller, args.stack as i32, args.stack_size as i32, args.child_tid) { + match lind_pthread_create(caller, args.stack as u32, args.stack_size as u32, args.child_tid) { Ok(res) => res, Err(_e) => -1 } @@ -1181,7 +1167,7 @@ pub fn exit_syscall + Clone + Send + 'static + std::marker::Sy } pub fn setjmp_call + Clone + Send + 'static + std::marker::Sync, U: Clone + Send + 'static + std::marker::Sync> - (caller: &mut Caller<'_, T>, jmp_buf: i32) -> i32 { + (caller: &mut Caller<'_, T>, jmp_buf: u32) -> i32 { // first let's check if the process is currently in rewind state let rewind_res = catch_rewind(caller); if rewind_res.is_some() { @@ -1195,7 +1181,7 @@ pub fn setjmp_call + Clone + Send + 'static + std::marker::Syn } pub fn longjmp_call + Clone + Send + 'static + std::marker::Sync, U: Clone + Send + 'static + std::marker::Sync> - (caller: &mut Caller<'_, T>, jmp_buf: i32, retval: i32) -> i32 { + (caller: &mut Caller<'_, T>, jmp_buf: u32, retval: i32) -> i32 { let host = caller.data().clone(); let ctx = host.get_ctx(); diff --git a/src/wasmtime/crates/wasmtime/Cargo.toml b/src/wasmtime/crates/wasmtime/Cargo.toml index 7eff216c..8b30d470 100644 --- a/src/wasmtime/crates/wasmtime/Cargo.toml +++ b/src/wasmtime/crates/wasmtime/Cargo.toml @@ -59,6 +59,8 @@ smallvec = { workspace = true, optional = true } hashbrown = { workspace = true } libm = "0.2.7" bitflags = { workspace = true } +rawposix = { path = "../rawposix" } +wasmtime-lind-utils = { path = "../lind-utils" } [target.'cfg(target_os = "windows")'.dependencies.windows-sys] workspace = true diff --git a/src/wasmtime/crates/wasmtime/src/runtime.rs b/src/wasmtime/crates/wasmtime/src/runtime.rs index 2aa0c2a2..60817838 100644 --- a/src/wasmtime/crates/wasmtime/src/runtime.rs +++ b/src/wasmtime/crates/wasmtime/src/runtime.rs @@ -42,7 +42,7 @@ pub use code_memory::CodeMemory; pub use externals::*; pub use func::*; pub use gc::*; -pub use instance::{Instance, InstancePre}; +pub use instance::{Instance, InstancePre, InstantiateType}; pub use instantiate::CompiledModule; pub use limits::*; pub use linker::*; diff --git a/src/wasmtime/crates/wasmtime/src/runtime/func.rs b/src/wasmtime/crates/wasmtime/src/runtime/func.rs index e0df9196..f049c06c 100644 --- a/src/wasmtime/crates/wasmtime/src/runtime/func.rs +++ b/src/wasmtime/crates/wasmtime/src/runtime/func.rs @@ -2123,7 +2123,7 @@ impl Caller<'_, T> { .get_export(&mut self.store, name) } - pub fn get_stack_pointer(&mut self) -> Result { + pub fn get_stack_pointer(&mut self) -> Result { self.caller .host_state() .downcast_ref::().ok_or(()).unwrap() diff --git a/src/wasmtime/crates/wasmtime/src/runtime/instance.rs b/src/wasmtime/crates/wasmtime/src/runtime/instance.rs index 515bcaec..0100b237 100644 --- a/src/wasmtime/crates/wasmtime/src/runtime/instance.rs +++ b/src/wasmtime/crates/wasmtime/src/runtime/instance.rs @@ -11,6 +11,9 @@ use crate::{ StoreContext, StoreContextMut, Table, TypedFunc, }; use alloc::sync::Arc; +use rawposix::constants::{MAP_ANONYMOUS, MAP_FIXED, MAP_PRIVATE, PAGESHIFT, PROT_READ, PROT_WRITE}; +use rawposix::safeposix::dispatcher::lind_syscall_api; +use wasmtime_lind_utils::lind_syscall_numbers::MMAP_SYSCALL; use core::ptr::NonNull; use wasmparser::WasmFeatures; use wasmtime_environ::{ @@ -19,6 +22,14 @@ use wasmtime_environ::{ use super::Val; +pub enum InstantiateType { + InstantiateFirst(u64), + InstantiateChild { + parent_pid: u64, + child_pid: u64, + }, +} + /// An instantiated WebAssembly module. /// /// This type represents the instantiation of a [`Module`]. Once instantiated @@ -206,6 +217,71 @@ impl Instance { imports: Imports<'_>, ) -> Result { let (instance, start) = Instance::new_raw(store.0, module, imports)?; + + if let Some(start) = start { + instance.start_raw(store, start)?; + } + Ok(instance) + } + + pub(crate) unsafe fn new_started_impl_with_lind( + store: &mut StoreContextMut<'_, T>, + module: &Module, + imports: Imports<'_>, + instantiate_type: InstantiateType, + ) -> Result { + let (instance, start) = Instance::new_raw(store.0, module, imports)?; + // retrieve the initial memory size + let plans = module.compiled_module().module().memory_plans.clone(); + let plan = plans.get(MemoryIndex::from_u32(0)).unwrap(); + // in wasmtime, one page is 65536 bytes, so we need to convert to pagesize in rawposix + let minimal_pages = plan.memory.minimum * 0x10; + + // initialize the memory + // the memory initialization should happen inside microvisor, so we should discard the original + // memory init in wasmtime and do our own initialization here + match instantiate_type { + // InstantiateFirst: this is the first wasm instance + InstantiateType::InstantiateFirst(pid) => { + // if this is the first wasm instance, we need to + // 1. set memory base address + // 2. manually call mmap_syscall to set up the first memory region + let handle = store.0.instance(InstanceId::from_index(0)); + let defined_memory = handle.get_memory(wasmtime_environ::MemoryIndex::from_u32(0)); + let memory_base = defined_memory.base as usize; + rawposix::safeposix::dispatcher::init_vmmap_helper(pid, memory_base, Some(minimal_pages as u32)); + + lind_syscall_api( + pid, + MMAP_SYSCALL as u32, + 0, + memory_base as u64, + 0, // the first memory region starts from 0 + minimal_pages << PAGESHIFT, // size of first memory region + (PROT_READ | PROT_WRITE) as u64, + (MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED) as u64, + // we need to pass -1 here, but since lind_syscall_api only accepts u64 + // and rust does not directly allow things like -1 as u64, so we end up with this weird thing + (0 - 1) as u64, + 0, + ); + }, + // InstantiateChild: this is the child wasm instance forked by parent + InstantiateType::InstantiateChild { parent_pid, child_pid } => { + // if this is a child, we do not need to specifically set up the first memory region + // since this should be taken care of when we fork the entire memory region from parent + // therefore in this case, we only need to: + // 1. set memory base address + // 2. fork the memory space from parent + let handle = store.0.instance(InstanceId::from_index(0)); + let defined_memory = handle.get_memory(wasmtime_environ::MemoryIndex::from_u32(0)); + let child_address = defined_memory.base as usize; + + rawposix::safeposix::dispatcher::init_vmmap_helper(child_pid, child_address, None); + rawposix::safeposix::dispatcher::fork_vmmap_helper(parent_pid as u64, child_pid); + } + } + if let Some(start) = start { instance.start_raw(store, start)?; } @@ -454,13 +530,13 @@ impl Instance { self._get_export(store, entity, export_name_index) } - pub fn get_stack_pointer(&self, mut store: impl AsContextMut) -> Result { + pub fn get_stack_pointer(&self, mut store: impl AsContextMut) -> Result { if let Some(sp_extern) = self.get_export(store.as_context_mut(), "__stack_pointer") { match sp_extern { Extern::Global(sp) => { match sp.get(store.as_context_mut()) { Val::I32(val) => { - return Ok(val); + return Ok(val as u32); } _ => { // unexpected stack pointer type (not i32) @@ -924,6 +1000,22 @@ impl InstancePre { unsafe { Instance::new_started(&mut store, &self.module, imports.as_ref()) } } + pub fn instantiate_with_lind(&self, mut store: impl AsContextMut, instantiate_type: InstantiateType) -> Result { + let mut store = store.as_context_mut(); + let imports = pre_instantiate_raw( + &mut store.0, + &self.module, + &self.items, + self.host_funcs, + &self.func_refs, + )?; + + // This unsafety should be handled by the type-checking performed by the + // constructor of `InstancePre` to assert that all the imports we're passing + // in match the module we're instantiating. + unsafe { Instance::new_started_impl_with_lind(&mut store, &self.module, imports.as_ref(), instantiate_type) } + } + /// Creates a new instance, running the start function asynchronously /// instead of inline. /// diff --git a/src/wasmtime/crates/wasmtime/src/runtime/linker.rs b/src/wasmtime/crates/wasmtime/src/runtime/linker.rs index ab659c2e..29b3660a 100644 --- a/src/wasmtime/crates/wasmtime/src/runtime/linker.rs +++ b/src/wasmtime/crates/wasmtime/src/runtime/linker.rs @@ -17,6 +17,7 @@ use hashbrown::hash_map::{Entry, HashMap}; use log::warn; use super::store::StoreInner; +use super::InstantiateType; /// Structure used to link wasm modules/instances together. /// @@ -1127,6 +1128,16 @@ impl Linker { .instantiate(store) } + pub fn instantiate_with_lind( + &self, + mut store: impl AsContextMut, + module: &Module, + instantiate_type: InstantiateType, + ) -> Result { + self._instantiate_pre(module, Some(store.as_context_mut().0))? + .instantiate_with_lind(store, instantiate_type) + } + /// Attempts to instantiate the `module` provided. This is the same as /// [`Linker::instantiate`], except for async `Store`s. #[cfg(feature = "async")] diff --git a/src/wasmtime/crates/wasmtime/src/runtime/vm/memory.rs b/src/wasmtime/crates/wasmtime/src/runtime/vm/memory.rs index d36ea1dc..64c8eadb 100644 --- a/src/wasmtime/crates/wasmtime/src/runtime/vm/memory.rs +++ b/src/wasmtime/crates/wasmtime/src/runtime/vm/memory.rs @@ -221,6 +221,8 @@ impl MmapMemory { mut maximum: Option, memory_image: Option<&Arc>, ) -> Result { + // lind-wasm: we enable maximum use of memory at start + maximum = Some(1 << 32); // It's a programmer error for these two configuration values to exceed // the host available address space, so panic if such a configuration is // found (mostly an issue for hypothetical 32-bit hosts). diff --git a/src/wasmtime/crates/wasmtime/src/runtime/vm/mmap.rs b/src/wasmtime/crates/wasmtime/src/runtime/vm/mmap.rs index 2d955d5e..dd4e0f04 100644 --- a/src/wasmtime/crates/wasmtime/src/runtime/vm/mmap.rs +++ b/src/wasmtime/crates/wasmtime/src/runtime/vm/mmap.rs @@ -99,7 +99,9 @@ impl Mmap { assert!(len <= self.len()); assert!(start <= self.len() - len); - self.sys.make_accessible(start, len) + // lind-wasm: mmap prot is managed by rawposix + // so we are skipping wasmtime's sys mmap here + Ok(()) } /// Return the allocated memory as a slice of u8. diff --git a/src/wasmtime/crates/wasmtime/src/runtime/vm/threads/shared_memory.rs b/src/wasmtime/crates/wasmtime/src/runtime/vm/threads/shared_memory.rs index b62ce1f5..9fe5f790 100644 --- a/src/wasmtime/crates/wasmtime/src/runtime/vm/threads/shared_memory.rs +++ b/src/wasmtime/crates/wasmtime/src/runtime/vm/threads/shared_memory.rs @@ -32,7 +32,12 @@ impl SharedMemory { /// Construct a new [`SharedMemory`]. pub fn new(plan: MemoryPlan) -> Result { let (minimum_bytes, maximum_bytes) = Memory::limit_new(&plan, None)?; - let mmap_memory = MmapMemory::new(&plan, minimum_bytes, maximum_bytes, None)?; + let mut mmap_memory = MmapMemory::new(&plan, minimum_bytes, maximum_bytes, None)?; + // lind-wasm: we enable maximum use of memory at start + let max_size = 1 << 32; + if minimum_bytes < max_size { + mmap_memory.grow_to(max_size); + } Self::wrap(&plan, Box::new(mmap_memory), plan.memory) } diff --git a/src/wasmtime/src/commands/run.rs b/src/wasmtime/src/commands/run.rs index 57abdc0d..e4e979f6 100644 --- a/src/wasmtime/src/commands/run.rs +++ b/src/wasmtime/src/commands/run.rs @@ -9,6 +9,7 @@ use crate::common::{Profile, RunCommon, RunTarget}; use anyhow::{anyhow, bail, Context as _, Error, Result}; use clap::Parser; +use rawposix::constants::{MAP_ANONYMOUS, MAP_FIXED, MAP_PRIVATE, PAGESHIFT, PROT_READ, PROT_WRITE}; use rawposix::safeposix::dispatcher::lind_syscall_api; use wasmtime_lind_multi_process::{LindCtx, LindHost}; use wasmtime_lind_common::LindCommonCtx; @@ -19,7 +20,7 @@ use std::sync::atomic::AtomicU64; use std::sync::{Arc, Mutex}; use std::thread; use wasi_common::sync::{ambient_authority, Dir, TcpListener, WasiCtxBuilder}; -use wasmtime::{AsContextMut, Engine, Func, Module, Store, StoreLimits, Val, ValType}; +use wasmtime::{AsContext, AsContextMut, Engine, Func, InstantiateType, Module, Store, StoreLimits, Val, ValType}; use wasmtime_wasi::WasiView; use wasmtime_lind_utils::LindCageManager; @@ -193,7 +194,7 @@ impl RunCommand { // operations that block in the CLI since the CLI doesn't use async to // invoke WebAssembly. let result = wasmtime_wasi::runtime::with_ambient_tokio_runtime(|| { - self.load_main_module(&mut store, &mut linker, &main, modules) + self.load_main_module(&mut store, &mut linker, &main, modules, 1) .with_context(|| { format!( "failed to run main module `{}`", @@ -204,14 +205,19 @@ impl RunCommand { // Load the main wasm module. match result { - Ok(_) => { + Ok(res) => { + let mut code = 0; + let retval = res.get(0).unwrap(); + if let Val::I32(res) = retval { + code = *res; + } // exit the cage lind_syscall_api( 1, EXIT_SYSCALL as u32, 0, 0, - 0, + code as u64, 0, 0, 0, @@ -369,7 +375,7 @@ impl RunCommand { // operations that block in the CLI since the CLI doesn't use async to // invoke WebAssembly. let result = wasmtime_wasi::runtime::with_ambient_tokio_runtime(|| { - self.load_main_module(&mut store, &mut linker, &main, modules) + self.load_main_module(&mut store, &mut linker, &main, modules, pid as u64) .with_context(|| { format!( "failed to run child module `{}`", @@ -512,6 +518,7 @@ impl RunCommand { linker: &mut CliLinker, module: &RunTarget, modules: Vec<(String, Module)>, + pid: u64, ) -> Result> { // The main module might be allowed to have unknown imports, which // should be defined as traps: @@ -545,7 +552,7 @@ impl RunCommand { let result = match linker { CliLinker::Core(linker) => { let module = module.unwrap_core(); - let instance = linker.instantiate(&mut *store, &module).context(format!( + let instance = linker.instantiate_with_lind(&mut *store, &module, InstantiateType::InstantiateFirst(pid)).context(format!( "failed to instantiate {:?}", self.module_and_args[0] ))?; diff --git a/tests/unit-tests/memory_tests/deterministic/malloc_large.c b/tests/unit-tests/memory_tests/deterministic/malloc_large.c new file mode 100644 index 00000000..ec738f18 --- /dev/null +++ b/tests/unit-tests/memory_tests/deterministic/malloc_large.c @@ -0,0 +1,13 @@ +#include +#include +#include + +int main() { + // try with extremely large malloc + char *buf = malloc(0x10000000); + + *buf = 42; + printf("%p: %d\n", buf, *buf); + + return 0; +} \ No newline at end of file diff --git a/tests/unit-tests/memory_tests/deterministic/mmap_complicated.c b/tests/unit-tests/memory_tests/deterministic/mmap_complicated.c new file mode 100644 index 00000000..d8d20bff --- /dev/null +++ b/tests/unit-tests/memory_tests/deterministic/mmap_complicated.c @@ -0,0 +1,74 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +int main() { + // Define the size of the shared memory + size_t mem_size = 1024; + + // Create shared memory region using mmap + char *shared_mem = (char*)mmap(NULL, mem_size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); + if (shared_mem == MAP_FAILED) { + perror("mmap"); + exit(EXIT_FAILURE); + } + + // Fork a child process + pid_t pid = fork(); + if (pid < 0) { + perror("fork"); + munmap(shared_mem, mem_size); + exit(EXIT_FAILURE); + } + + if (pid == 0) { + // Child process + printf("Child: Writing to shared memory.\n"); + const char *child_message = "Hello from the child process!"; + strncpy(shared_mem, child_message, mem_size); + + // Sleep to simulate some work + sleep(2); + + printf("Child: Reading from shared memory: '%s'\n", shared_mem); + + // Unmap shared memory in the child + if (munmap(shared_mem, mem_size) != 0) { + perror("munmap in child"); + exit(EXIT_FAILURE); + } + + printf("Child: Exiting.\n"); + } else { + // Parent process + printf("Parent: Waiting for child to write.\n"); + + // Sleep to simulate waiting for the child + sleep(1); + + printf("Parent: Reading from shared memory: '%s'\n", shared_mem); + + const char *parent_message = "Hello from the parent process!"; + strncpy(shared_mem, parent_message, mem_size); + + // Wait for the child to finish + wait(NULL); + + printf("Parent: Reading modified shared memory: '%s'\n", shared_mem); + + // Unmap shared memory in the parent + if (munmap(shared_mem, mem_size) != 0) { + perror("munmap in parent"); + exit(EXIT_FAILURE); + } + + printf("Parent: Exiting.\n"); + } + + return 0; +} diff --git a/tests/unit-tests/memory_tests/deterministic/mmap_file.c b/tests/unit-tests/memory_tests/deterministic/mmap_file.c new file mode 100644 index 00000000..8bab9e4e --- /dev/null +++ b/tests/unit-tests/memory_tests/deterministic/mmap_file.c @@ -0,0 +1,53 @@ +#include +#include +#include +#include +#include +#include + +#define FILE_PATH "example.txt" +#define FILE_SIZE 4096 + +int main() { + // Create or open a file + int fd = open(FILE_PATH, O_RDWR | O_CREAT, 0666); + if (fd == -1) { + perror("open"); + exit(EXIT_FAILURE); + } + + // Ensure the file has the desired size + if (ftruncate(fd, FILE_SIZE) == -1) { + perror("ftruncate"); + close(fd); + exit(EXIT_FAILURE); + } + + // Map the file into memory + void* addr = mmap(NULL, FILE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (addr == MAP_FAILED) { + perror("mmap"); + close(fd); + exit(EXIT_FAILURE); + } + + // Close the file descriptor as it's no longer needed + close(fd); + + // Write data to the mapped memory + const char* message = "Hello, mmap!\0"; + memcpy(addr, message, strlen(message)); + + printf("Data written to memory-mapped file: %s\n", (char*)addr); + + // Read back the data from the mapped memory + printf("Data read back from memory-mapped file: %s\n", (char*)addr); + + // Unmap the memory + if (munmap(addr, FILE_SIZE) == -1) { + perror("munmap"); + exit(EXIT_FAILURE); + } + + return 0; +} diff --git a/tests/unit-tests/memory_tests/deterministic/mmap_shared.c b/tests/unit-tests/memory_tests/deterministic/mmap_shared.c new file mode 100644 index 00000000..c21c4e66 --- /dev/null +++ b/tests/unit-tests/memory_tests/deterministic/mmap_shared.c @@ -0,0 +1,43 @@ +#include +#include +#include +#include + +int main() { + int *addr1 = mmap(NULL, 10, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS, -1, 0); + int *addr2 = mmap(NULL, 10, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + + if(addr1 < 0) + { + perror("mmap"); + exit(1); + } + + if(addr2 < 0) + { + perror("mmap"); + exit(1); + } + + *addr1 = 1234; + *addr2 = 4321; + printf("parent value: %d, %d\n", *addr1, *addr2); + + if(fork()) { + // parent + printf("parent value after fork: %d, %d\n", *addr1, *addr2); + sleep(1); + *addr1 = 2333; + *addr2 = 3332; + printf("parent value after modification: %d, %d\n", *addr1, *addr2); + } else { + // child + printf("child value after fork: %d, %d\n", *addr1, *addr2); + sleep(2); + printf("child value after modification: %d, %d\n", *addr1, *addr2); + } + + return 0; +} diff --git a/tests/unit-tests/memory_tests/deterministic/sbrk.c b/tests/unit-tests/memory_tests/deterministic/sbrk.c new file mode 100644 index 00000000..de48a77c --- /dev/null +++ b/tests/unit-tests/memory_tests/deterministic/sbrk.c @@ -0,0 +1,33 @@ +#include +#include +#include + +int main() { + // Allocate memory using sbrk + size_t size = 1024; // Allocate 1024 bytes + void *initial_brk = sbrk(0); // Get the current program break + void *new_brk = sbrk(size); // Increment the program break + + if (new_brk == (void *)-1) { + perror("sbrk failed"); + return 1; + } + + printf("Initial program break: %p\n", initial_brk); + printf("New program break after allocation: %p\n", sbrk(0)); + + // Use the allocated memory + char *buffer = (char *)new_brk; + strcpy(buffer, "Hello, sbrk memory!"); + printf("Content in allocated memory: %s\n", buffer); + + // Deallocate memory by moving the program break back + if (sbrk(-size) == (void *)-1) { + perror("sbrk failed to deallocate"); + return 1; + } + + printf("Program break after deallocation: %p\n", sbrk(0)); + + return 0; +}