From 34f7d82d89311e776f01a7d463ac555db3e632d6 Mon Sep 17 00:00:00 2001 From: Olle Lukowski Date: Sat, 14 Mar 2026 16:19:29 +0100 Subject: [PATCH 01/22] feat(syscalls): wrap a bunch of Linux 1.0 syscalls --- system/linux/syscalls/src/aarch64.rs | 6 + system/linux/syscalls/src/lib.rs | 169 ++++++++++++++++++++++++++- system/linux/syscalls/src/x86_64.rs | 10 ++ 3 files changed, 183 insertions(+), 2 deletions(-) diff --git a/system/linux/syscalls/src/aarch64.rs b/system/linux/syscalls/src/aarch64.rs index f5e6875..78e50a8 100644 --- a/system/linux/syscalls/src/aarch64.rs +++ b/system/linux/syscalls/src/aarch64.rs @@ -11,12 +11,18 @@ pub enum Sysno { Close = 57, /// Write = 64, + /// + Acct = 89, /// Exit = 93, /// Kill = 129, + /// + Adjtimex = 171, /// Getpid = 172, + /// + Brk = 214, /// Mremap = 216, /// diff --git a/system/linux/syscalls/src/lib.rs b/system/linux/syscalls/src/lib.rs index d3311cb..0dce4b7 100644 --- a/system/linux/syscalls/src/lib.rs +++ b/system/linux/syscalls/src/lib.rs @@ -15,7 +15,12 @@ pub mod arch { pub use super::x86_64::*; } -use libc::{c_char, c_int, c_void, mode_t, off_t, pid_t, size_t, ssize_t}; +use libc::{ + c_char, c_int, c_void, mode_t, off_t, pid_t, size_t, ssize_t, timex, +}; + +#[cfg(not(target_arch = "aarch64"))] +use libc::c_uint; #[cfg(not(miri))] use arch::{ @@ -38,6 +43,90 @@ pub fn getpid() -> pid_t { } } +/// +/// +/// Returns the raw kernel return value. +/// Negative values in `[-4095, -1]` represent `errno`. +/// +/// # Safety +/// - `path` must a pointer to a null-terminated string, +/// that must be readable until the null terminator (see [`core::ptr::read`]), +/// or [`core::ptr::null()`] to disable accounting. +pub unsafe fn acct(path: *const c_char) -> c_int { + // SAFETY: guaranteed by caller. + #[cfg(not(miri))] + return unsafe { syscall1(Sysno::Acct, path as _) } as _; + + #[cfg(miri)] + { + _ = path; + // Syscall not supported by Miri + -libc::ENOSYS as c_int + } +} + +/// +/// +/// Returns the raw kernel return value. +/// Negative values in `[-4095, -1]` represent `errno`. +/// +/// # Safety +/// - `buf` must be a valid pointer to a [`timex`] struct, +/// that must be readable/writable until the syscall completes +/// (see [`core::ptr::read`] and [`core::ptr::write`] for details). +pub unsafe fn adjtimex(buf: *mut timex) -> c_int { + // SAFETY: guaranteed by caller. + #[cfg(not(miri))] + return unsafe { syscall1(Sysno::Adjtimex, buf as _) } as _; + + #[cfg(miri)] + { + _ = buf; + // Syscall not supported by Miri + -libc::ENOSYS as c_int + } +} + +/// +/// +/// Returns the raw kernel return value. +/// Negative values in `[-4095, -1]` represent `errno`. +#[cfg(not(target_arch = "aarch64"))] +pub fn alarm(seconds: c_uint) -> c_uint { + // SAFETY: alarm is safe to call. + #[cfg(not(miri))] + return unsafe { syscall1(Sysno::Alarm, seconds as _) } as _; + + // Syscall not supported by Miri + #[cfg(miri)] + { + _ = seconds; + 0 + } +} + +/// +/// +/// Returns the raw kernel return value. +/// Negative values in `[-4095, -1]` represent `errno`. +/// +/// # Safety +/// On success, any pointers or references to memory with an address greater +/// than or equal to `addr` are no longer valid. +pub unsafe fn brk(addr: *mut c_void) -> c_int { + // SAFETY: guaranteed by caller. + #[cfg(not(miri))] + return unsafe { syscall1(Sysno::Brk, addr.addr()) } as _; + + // SAFETY: guaranteed by caller. + #[cfg(miri)] + { + _ = addr; + // Syscall not supported by Miri + -libc::ENOSYS as c_int + } +} + /// /// /// Returns the raw kernel return value. @@ -77,6 +166,29 @@ pub fn exit(status: c_int) -> ! { } } +/// +/// +/// Returns the raw kernel return value. +/// Negative values in `[-4095, -1]` represent `errno`. +/// +/// # Safety +/// - `path` must a pointer to a null-terminated string, +/// that must be readable until the null terminator (see [`core::ptr::read`]). +#[cfg(not(target_arch = "aarch64"))] +pub unsafe fn access(path: *const c_char, mode: c_int) -> c_int { + // SAFETY: access is safe to call. + #[cfg(not(miri))] + return unsafe { syscall2(Sysno::Access, path as _, mode as _) } as _; + + #[cfg(miri)] + { + _ = path; + _ = mode; + // Syscall not supported by Miri + -libc::ENOSYS as c_int + } +} + /// /// /// Returns the raw kernel return value. @@ -237,14 +349,57 @@ mod tests { use super::{close, getpid, mmap, mremap, write}; + #[cfg(not(any(miri, target_arch = "aarch64")))] + use super::{access, alarm}; + #[cfg(not(miri))] - use super::{kill, openat}; + use { + super::{acct, adjtimex, brk, kill, openat}, + core::mem::MaybeUninit, + libc::timex, + }; #[test] fn test_getpid() { assert!(getpid() > 0); } + #[test] + #[cfg(not(miri))] + fn test_acct() { + // will fail due to lack of permission + let ret = unsafe { acct(ptr::null()) }; + + assert!(ret < 0); + } + + #[test] + #[cfg(not(miri))] + fn test_adjtimex() { + let mut tx: MaybeUninit = MaybeUninit::zeroed(); + + // SAFETY: we are passing a valid pointer to a `timex` struct. + let ret = unsafe { adjtimex((&raw mut tx).cast()) }; + + assert!(ret >= 0); + } + + #[test] + #[cfg(not(any(miri, target_arch = "aarch64")))] + fn test_alarm() { + _ = alarm(0); // cancel any pending alarm + } + + #[test] + #[cfg(not(miri))] + fn test_brk() { + // SAFETY: we are passing a obviously invalid pointer to `brk`, which + // should return the current program break instead of changing it. + let ret = unsafe { brk(ptr::null_mut()) }; + + assert!(ret >= 0); + } + #[test] fn test_close() { let ret = close(-1); @@ -252,6 +407,16 @@ mod tests { assert!(ret < 0); } + #[test] + #[cfg(not(any(miri, target_arch = "aarch64")))] + fn test_access() { + // SAFETY: the pointer we are passing is readable until the null + // terminator (which is the only byte). + let ret = unsafe { access(c"".as_ptr(), 0) }; + + assert!(ret < 0); + } + #[test] #[cfg(not(miri))] fn test_kill() { diff --git a/system/linux/syscalls/src/x86_64.rs b/system/linux/syscalls/src/x86_64.rs index 8212a27..f20a3a9 100644 --- a/system/linux/syscalls/src/x86_64.rs +++ b/system/linux/syscalls/src/x86_64.rs @@ -11,14 +11,24 @@ pub enum Sysno { Close = 3, /// Mmap = 9, + /// + Brk = 12, + /// + Access = 21, /// Mremap = 25, + /// + Alarm = 37, /// Getpid = 39, /// Exit = 60, /// Kill = 62, + /// + Adjtimex = 159, + /// + Acct = 163, /// Openat = 257, } From 4620ecd5c8a8cbbe4eee13365eb079d1cb71430d Mon Sep 17 00:00:00 2001 From: Olle Lukowski Date: Sat, 14 Mar 2026 17:06:39 +0100 Subject: [PATCH 02/22] fix brk --- system/linux/syscalls/src/lib.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/system/linux/syscalls/src/lib.rs b/system/linux/syscalls/src/lib.rs index 0dce4b7..cbb2752 100644 --- a/system/linux/syscalls/src/lib.rs +++ b/system/linux/syscalls/src/lib.rs @@ -27,6 +27,9 @@ use arch::{ Sysno, syscall0, syscall1, syscall2, syscall3, syscall4, syscall5, syscall6, }; +#[cfg(miri)] +use core::ptr; + /// /// /// Returns the raw kernel return value. @@ -113,17 +116,16 @@ pub fn alarm(seconds: c_uint) -> c_uint { /// # Safety /// On success, any pointers or references to memory with an address greater /// than or equal to `addr` are no longer valid. -pub unsafe fn brk(addr: *mut c_void) -> c_int { +pub unsafe fn brk(addr: *mut c_void) -> *mut c_void { // SAFETY: guaranteed by caller. #[cfg(not(miri))] return unsafe { syscall1(Sysno::Brk, addr.addr()) } as _; - // SAFETY: guaranteed by caller. #[cfg(miri)] { _ = addr; // Syscall not supported by Miri - -libc::ENOSYS as c_int + ptr::null_mut() } } @@ -395,9 +397,7 @@ mod tests { fn test_brk() { // SAFETY: we are passing a obviously invalid pointer to `brk`, which // should return the current program break instead of changing it. - let ret = unsafe { brk(ptr::null_mut()) }; - - assert!(ret >= 0); + _ = unsafe { brk(ptr::null_mut()) }; } #[test] From 82dc5ed62bd97da3e194363af02cd03246b42931 Mon Sep 17 00:00:00 2001 From: Olle Lukowski Date: Sat, 14 Mar 2026 17:32:34 +0100 Subject: [PATCH 03/22] fix brk docs --- system/linux/syscalls/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/system/linux/syscalls/src/lib.rs b/system/linux/syscalls/src/lib.rs index cbb2752..5767557 100644 --- a/system/linux/syscalls/src/lib.rs +++ b/system/linux/syscalls/src/lib.rs @@ -111,7 +111,6 @@ pub fn alarm(seconds: c_uint) -> c_uint { /// /// /// Returns the raw kernel return value. -/// Negative values in `[-4095, -1]` represent `errno`. /// /// # Safety /// On success, any pointers or references to memory with an address greater From bc96287d6fa7894ded43e5d09fddaa1bed54103c Mon Sep 17 00:00:00 2001 From: Olle Lukowski Date: Sat, 14 Mar 2026 20:35:07 +0100 Subject: [PATCH 04/22] more stuff --- system/linux/syscalls/src/aarch64.rs | 6 + system/linux/syscalls/src/lib.rs | 269 ++++++++++++++++++++++++++- system/linux/syscalls/src/x86_64.rs | 12 ++ 3 files changed, 279 insertions(+), 8 deletions(-) diff --git a/system/linux/syscalls/src/aarch64.rs b/system/linux/syscalls/src/aarch64.rs index 78e50a8..846df0e 100644 --- a/system/linux/syscalls/src/aarch64.rs +++ b/system/linux/syscalls/src/aarch64.rs @@ -5,6 +5,10 @@ use core::arch::asm; #[non_exhaustive] #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum Sysno { + /// + Chdir = 49, + /// + Chroot = 51, /// Openat = 56, /// @@ -25,6 +29,8 @@ pub enum Sysno { Brk = 214, /// Mremap = 216, + /// + Clone = 220, /// Mmap = 222, } diff --git a/system/linux/syscalls/src/lib.rs b/system/linux/syscalls/src/lib.rs index 5767557..0d9a914 100644 --- a/system/linux/syscalls/src/lib.rs +++ b/system/linux/syscalls/src/lib.rs @@ -16,7 +16,8 @@ pub mod arch { } use libc::{ - c_char, c_int, c_void, mode_t, off_t, pid_t, size_t, ssize_t, timex, + c_char, c_int, c_long, c_ulong, c_void, mode_t, off_t, pid_t, size_t, + ssize_t, timex, }; #[cfg(not(target_arch = "aarch64"))] @@ -58,7 +59,7 @@ pub fn getpid() -> pid_t { pub unsafe fn acct(path: *const c_char) -> c_int { // SAFETY: guaranteed by caller. #[cfg(not(miri))] - return unsafe { syscall1(Sysno::Acct, path as _) } as _; + return unsafe { syscall1(Sysno::Acct, path.addr()) } as _; #[cfg(miri)] { @@ -80,7 +81,7 @@ pub unsafe fn acct(path: *const c_char) -> c_int { pub unsafe fn adjtimex(buf: *mut timex) -> c_int { // SAFETY: guaranteed by caller. #[cfg(not(miri))] - return unsafe { syscall1(Sysno::Adjtimex, buf as _) } as _; + return unsafe { syscall1(Sysno::Adjtimex, buf.addr()) } as _; #[cfg(miri)] { @@ -114,7 +115,8 @@ pub fn alarm(seconds: c_uint) -> c_uint { /// /// # Safety /// On success, any pointers or references to memory with an address greater -/// than or equal to `addr` are no longer valid. +/// than or equal to `addr` are no longer valid if `addr` is less than the +/// current program break value. pub unsafe fn brk(addr: *mut c_void) -> *mut c_void { // SAFETY: guaranteed by caller. #[cfg(not(miri))] @@ -128,6 +130,48 @@ pub unsafe fn brk(addr: *mut c_void) -> *mut c_void { } } +/// +/// +/// Returns the raw kernel return value. +/// Negative values in `[-4095, -1]` represent `errno`. +/// +/// # Safety +/// - `path` must a pointer to a null-terminated string, +/// that must be readable until the null terminator (see [`core::ptr::read`]). +pub unsafe fn chdir(path: *const c_char) -> c_int { + // SAFETY: guaranteed by caller. + #[cfg(not(miri))] + return unsafe { syscall1(Sysno::Chdir, path.addr()) } as _; + + #[cfg(miri)] + { + _ = path; + // Syscall not supported by Miri (when isolation is enabled) + -libc::ENOSYS as c_int + } +} + +/// +/// +/// Returns the raw kernel return value. +/// Negative values in `[-4095, -1]` represent `errno`. +/// +/// # Safety +/// - `path` must a pointer to a null-terminated string, +/// that must be readable until the null terminator (see [`core::ptr::read`]). +pub unsafe fn chroot(path: *const c_char) -> c_int { + // SAFETY: guaranteed by caller. + #[cfg(not(miri))] + return unsafe { syscall1(Sysno::Chroot, path.addr()) } as _; + + #[cfg(miri)] + { + _ = path; + // Syscall not supported by Miri + -libc::ENOSYS as c_int + } +} + /// /// /// Returns the raw kernel return value. @@ -179,7 +223,7 @@ pub fn exit(status: c_int) -> ! { pub unsafe fn access(path: *const c_char, mode: c_int) -> c_int { // SAFETY: access is safe to call. #[cfg(not(miri))] - return unsafe { syscall2(Sysno::Access, path as _, mode as _) } as _; + return unsafe { syscall2(Sysno::Access, path.addr(), mode as _) } as _; #[cfg(miri)] { @@ -190,6 +234,76 @@ pub unsafe fn access(path: *const c_char, mode: c_int) -> c_int { } } +/// +/// +/// Returns the raw kernel return value. +/// Negative values in `[-4095, -1]` represent `errno`. +/// +/// # Safety +/// - `path` must a pointer to a null-terminated string, +/// that must be readable until the null terminator (see [`core::ptr::read`]). +#[cfg(not(target_arch = "aarch64"))] +pub unsafe fn chmod(path: *const c_char, mode: mode_t) -> c_int { + // SAFETY: guaranteed by caller. + #[cfg(not(miri))] + return unsafe { syscall2(Sysno::Chmod, path.addr(), mode as _) } as _; + + #[cfg(miri)] + { + _ = path; + _ = mode; + // Syscall not supported by Miri + -libc::ENOSYS as c_int + } +} + +/// +/// +/// Returns the raw kernel return value. +/// Negative values in `[-4095, -1]` represent `errno`. +/// +/// # Safety +/// - `path` must a pointer to a null-terminated string, +/// that must be readable until the null terminator (see [`core::ptr::read`]). +#[cfg(not(target_arch = "aarch64"))] +pub unsafe fn creat(path: *const c_char, mode: mode_t) -> c_int { + // SAFETY: guaranteed by caller. + #[cfg(not(miri))] + return unsafe { syscall2(Sysno::Creat, path.addr(), mode as _) } as _; + + #[cfg(miri)] + { + _ = path; + _ = mode; + // Syscall not supported by Miri + -libc::ENOSYS as c_int + } +} + +/// +/// +/// Returns the raw kernel return value. +/// Negative values in `[-4095, -1]` represent `errno`. +/// +/// # Safety +/// - `name` must a pointer to a null-terminated string, +/// that must be readable until the null terminator (see [`core::ptr::read`]). +#[cfg(not(target_arch = "aarch64"))] +pub unsafe fn create_module(name: *const c_char, size: size_t) -> c_long { + // SAFETY: guaranteed by caller. + #[cfg(not(miri))] + return unsafe { syscall2(Sysno::CreateModule, name.addr(), size as _) } + as _; + + #[cfg(miri)] + { + _ = name; + _ = size; + // Syscall not supported by Miri + -libc::ENOSYS as c_long + } +} + /// /// /// Returns the raw kernel return value. @@ -265,6 +379,74 @@ pub unsafe fn openat( } } +/// +/// +/// Returns the raw kernel return value. +/// Negative values in `[-4095, -1]` represent `errno`. +/// +/// # Safety +/// - If this call succeeds, execution continues in both the parent and the +/// child. The caller must ensure that running the child with the provided +/// `flags` is sound. +/// - If the child will run on `stack`, `stack` must point to writable memory +/// that is valid for use as the child stack. +/// - If `flags` contains [`libc::CLONE_PARENT_SETTID`], `parent_tid` must be a +/// valid writable pointer to a [`c_int`] until the syscall completes. +/// - If `flags` contains [`libc::CLONE_CHILD_SETTID`], `child_tid` must point +/// to writable memory where the kernel may store the child TID in the child. +/// - If `flags` contains [`libc::CLONE_CHILD_CLEARTID`], `child_tid` must +/// point to writable memory that remains valid until the child exits, +/// because the kernel may clear it at thread exit. +/// - If `flags` contains [`libc::CLONE_SETTLS`], `tls` must be a valid TLS +/// value for the new thread. +/// - If the child shares memory with the parent, the caller must ensure that no +/// pointers, references, or other Rust aliasing assumptions are violated. +pub unsafe fn clone( + flags: c_ulong, + stack: *mut c_void, + parent_tid: *mut c_int, + child_tid: *mut c_int, + tls: c_ulong, +) -> c_long { + #[cfg(not(miri))] + { + #[cfg(target_arch = "x86_64")] + return unsafe { + syscall5( + Sysno::Clone, + flags as _, + stack.addr(), + parent_tid.addr(), + child_tid.addr(), + tls as _, + ) + } as _; + + #[cfg(target_arch = "aarch64")] + return unsafe { + syscall5( + Sysno::Clone, + flags as _, + stack.addr(), + parent_tid.addr(), + tls as _, + child_tid.addr(), + ) + } as _; + } + + #[cfg(miri)] + { + _ = flags; + _ = stack; + _ = parent_tid; + _ = child_tid; + _ = tls; + // Syscall not supported by Miri + -libc::ENOSYS as c_long + } +} + /// /// /// Returns the raw kernel return value. @@ -351,13 +533,13 @@ mod tests { use super::{close, getpid, mmap, mremap, write}; #[cfg(not(any(miri, target_arch = "aarch64")))] - use super::{access, alarm}; + use super::{access, alarm, chmod, creat, create_module}; #[cfg(not(miri))] use { - super::{acct, adjtimex, brk, kill, openat}, + super::{acct, adjtimex, brk, chdir, chroot, clone, kill, openat}, core::mem::MaybeUninit, - libc::timex, + libc::{c_ulong, timex}, }; #[test] @@ -399,6 +581,28 @@ mod tests { _ = unsafe { brk(ptr::null_mut()) }; } + #[test] + #[cfg(not(miri))] + fn test_chdir() { + // SAFETY: the pointer we are passing is readable until the null + // terminator (which is the only byte). + let ret = unsafe { chdir(c"".as_ptr()) }; + + assert!(ret < 0); // chdir should fail with an empty path + } + + #[test] + #[cfg(not(miri))] + fn test_chroot() { + // SAFETY: the pointer we are passing is readable until the null + // terminator (which is the only byte). + let ret = unsafe { chroot(c"".as_ptr()) }; + + // chroot should fail with an empty path, and lack + // of permissions + assert!(ret < 0); + } + #[test] fn test_close() { let ret = close(-1); @@ -416,6 +620,38 @@ mod tests { assert!(ret < 0); } + #[test] + #[cfg(not(any(miri, target_arch = "aarch64")))] + fn test_chmod() { + // SAFETY: the pointer we are passing is readable until the null + // terminator (which is the only byte). + let ret = unsafe { chmod(c"".as_ptr(), 0) }; + + assert!(ret < 0); + } + + #[test] + #[cfg(not(any(miri, target_arch = "aarch64")))] + fn test_creat() { + // SAFETY: the pointer we are passing is readable until the null + // terminator (which is the only byte). + let ret = unsafe { creat(c"".as_ptr(), 0) }; + + assert!(ret < 0); + } + + #[test] + #[cfg(not(any(miri, target_arch = "aarch64")))] + fn test_create_module() { + // SAFETY: the pointer we are passing is readable until the null + // terminator (which is the only byte). + let ret = unsafe { create_module(c"".as_ptr(), 0) }; + + // will most likely be -ENOSYS since the syscall was removed in + // Linux 2.6. + assert!(ret < 0); + } + #[test] #[cfg(not(miri))] fn test_kill() { @@ -447,6 +683,23 @@ mod tests { assert!(ret < 0); } + #[test] + #[cfg(not(miri))] + fn test_clone() { + // SAFETY: the call will fail due to invalid flags + let ret = unsafe { + clone( + (libc::CLONE_SIGHAND | libc::CLONE_CLEAR_SIGHAND) as c_ulong, + ptr::null_mut(), + ptr::null_mut(), + ptr::null_mut(), + 0, + ) + }; + + assert!(ret < 0); + } + #[test] fn test_mremap() { // SAFETY: we are not using `libc::MREMAP_FIXED`, and this won't diff --git a/system/linux/syscalls/src/x86_64.rs b/system/linux/syscalls/src/x86_64.rs index f20a3a9..3a1aa2d 100644 --- a/system/linux/syscalls/src/x86_64.rs +++ b/system/linux/syscalls/src/x86_64.rs @@ -21,14 +21,26 @@ pub enum Sysno { Alarm = 37, /// Getpid = 39, + /// + Clone = 56, /// Exit = 60, /// Kill = 62, + /// + Chdir = 80, + /// + Creat = 85, + /// + Chmod = 90, /// Adjtimex = 159, + /// + Chroot = 161, /// Acct = 163, + /// + CreateModule = 174, /// Openat = 257, } From af09227c1ed65a58859aba6a68c4a9faa069cf3b Mon Sep 17 00:00:00 2001 From: Olle Lukowski Date: Sat, 14 Mar 2026 22:04:17 +0100 Subject: [PATCH 05/22] delete_module syscall --- system/linux/syscalls/src/aarch64.rs | 2 ++ system/linux/syscalls/src/lib.rs | 39 +++++++++++++++++++++++++++- system/linux/syscalls/src/x86_64.rs | 2 ++ 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/system/linux/syscalls/src/aarch64.rs b/system/linux/syscalls/src/aarch64.rs index 846df0e..d467ae5 100644 --- a/system/linux/syscalls/src/aarch64.rs +++ b/system/linux/syscalls/src/aarch64.rs @@ -19,6 +19,8 @@ pub enum Sysno { Acct = 89, /// Exit = 93, + /// + DeleteModule = 106, /// Kill = 129, /// diff --git a/system/linux/syscalls/src/lib.rs b/system/linux/syscalls/src/lib.rs index 0d9a914..f625ee0 100644 --- a/system/linux/syscalls/src/lib.rs +++ b/system/linux/syscalls/src/lib.rs @@ -304,6 +304,29 @@ pub unsafe fn create_module(name: *const c_char, size: size_t) -> c_long { } } +/// +/// +/// Returns the raw kernel return value. +/// Negative values in `[-4095, -1]` represent `errno`. +/// +/// # Safety +/// - `name` must a pointer to a null-terminated string, +/// that must be readable until the null terminator (see [`core::ptr::read`]). +pub unsafe fn delete_module(name: *const c_char, flags: c_int) -> c_int { + // SAFETY: guaranteed by caller. + #[cfg(not(miri))] + return unsafe { syscall2(Sysno::DeleteModule, name.addr(), flags as _) } + as _; + + #[cfg(miri)] + { + _ = name; + _ = flags; + // Syscall not supported by Miri + -libc::ENOSYS as c_int + } +} + /// /// /// Returns the raw kernel return value. @@ -537,7 +560,10 @@ mod tests { #[cfg(not(miri))] use { - super::{acct, adjtimex, brk, chdir, chroot, clone, kill, openat}, + super::{ + acct, adjtimex, brk, chdir, chroot, clone, delete_module, kill, + openat, + }, core::mem::MaybeUninit, libc::{c_ulong, timex}, }; @@ -652,6 +678,17 @@ mod tests { assert!(ret < 0); } + #[test] + #[cfg(not(miri))] + fn test_delete_module() { + // SAFETY: the pointer we are passing is readable until the null + // terminator (which is the only byte). + let ret = unsafe { delete_module(c"".as_ptr(), 0) }; + + // will fail due to lack of permission and invalid module name + assert!(ret < 0); + } + #[test] #[cfg(not(miri))] fn test_kill() { diff --git a/system/linux/syscalls/src/x86_64.rs b/system/linux/syscalls/src/x86_64.rs index 3a1aa2d..1b9431b 100644 --- a/system/linux/syscalls/src/x86_64.rs +++ b/system/linux/syscalls/src/x86_64.rs @@ -41,6 +41,8 @@ pub enum Sysno { Acct = 163, /// CreateModule = 174, + /// + DeleteModule = 176, /// Openat = 257, } From 90d2e3b4e025ad1e64449775f6e440a178300721 Mon Sep 17 00:00:00 2001 From: Olle Lukowski Date: Sat, 14 Mar 2026 22:12:18 +0100 Subject: [PATCH 06/22] dup syscall --- system/linux/syscalls/src/aarch64.rs | 2 ++ system/linux/syscalls/src/lib.rs | 28 ++++++++++++++++++++++++++-- system/linux/syscalls/src/x86_64.rs | 2 ++ 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/system/linux/syscalls/src/aarch64.rs b/system/linux/syscalls/src/aarch64.rs index d467ae5..ff50391 100644 --- a/system/linux/syscalls/src/aarch64.rs +++ b/system/linux/syscalls/src/aarch64.rs @@ -5,6 +5,8 @@ use core::arch::asm; #[non_exhaustive] #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum Sysno { + /// + Dup = 23, /// Chdir = 49, /// diff --git a/system/linux/syscalls/src/lib.rs b/system/linux/syscalls/src/lib.rs index f625ee0..6d3303f 100644 --- a/system/linux/syscalls/src/lib.rs +++ b/system/linux/syscalls/src/lib.rs @@ -188,6 +188,22 @@ pub fn close(fd: c_int) -> c_int { } } +/// +/// +/// Returns the raw kernel return value. +/// Negative values in `[-4095, -1]` represent `errno`. +pub fn dup(oldfd: c_int) -> c_int { + // SAFETY: dup is safe to call. + #[cfg(not(miri))] + return unsafe { syscall1(Sysno::Dup, oldfd as _) } as _; + + // SAFETY: dup is safe to call. + #[cfg(miri)] + unsafe { + libc::dup(oldfd) + } +} + /// /// /// Returns the raw kernel return value. @@ -561,8 +577,8 @@ mod tests { #[cfg(not(miri))] use { super::{ - acct, adjtimex, brk, chdir, chroot, clone, delete_module, kill, - openat, + acct, adjtimex, brk, chdir, chroot, clone, delete_module, dup, + kill, openat, }, core::mem::MaybeUninit, libc::{c_ulong, timex}, @@ -636,6 +652,14 @@ mod tests { assert!(ret < 0); } + #[test] + #[cfg(not(miri))] + fn test_dup() { + let ret = dup(-1); + + assert!(ret < 0); + } + #[test] #[cfg(not(any(miri, target_arch = "aarch64")))] fn test_access() { diff --git a/system/linux/syscalls/src/x86_64.rs b/system/linux/syscalls/src/x86_64.rs index 1b9431b..392e94a 100644 --- a/system/linux/syscalls/src/x86_64.rs +++ b/system/linux/syscalls/src/x86_64.rs @@ -17,6 +17,8 @@ pub enum Sysno { Access = 21, /// Mremap = 25, + /// + Dup = 32, /// Alarm = 37, /// From f2a36825bfac10753d106980c6f27197ce7bcd53 Mon Sep 17 00:00:00 2001 From: Olle Lukowski Date: Sat, 14 Mar 2026 22:35:42 +0100 Subject: [PATCH 07/22] test dup under miri --- system/linux/syscalls/src/lib.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/system/linux/syscalls/src/lib.rs b/system/linux/syscalls/src/lib.rs index 6d3303f..ba3474f 100644 --- a/system/linux/syscalls/src/lib.rs +++ b/system/linux/syscalls/src/lib.rs @@ -569,7 +569,7 @@ pub unsafe fn mmap( mod tests { use core::ptr; - use super::{close, getpid, mmap, mremap, write}; + use super::{close, dup, getpid, mmap, mremap, write}; #[cfg(not(any(miri, target_arch = "aarch64")))] use super::{access, alarm, chmod, creat, create_module}; @@ -577,8 +577,8 @@ mod tests { #[cfg(not(miri))] use { super::{ - acct, adjtimex, brk, chdir, chroot, clone, delete_module, dup, - kill, openat, + acct, adjtimex, brk, chdir, chroot, clone, delete_module, kill, + openat, }, core::mem::MaybeUninit, libc::{c_ulong, timex}, @@ -653,7 +653,6 @@ mod tests { } #[test] - #[cfg(not(miri))] fn test_dup() { let ret = dup(-1); From 295a3de5c980f5c66231416716990d573e4d5bc6 Mon Sep 17 00:00:00 2001 From: Olle Lukowski Date: Sat, 14 Mar 2026 22:44:17 +0100 Subject: [PATCH 08/22] dup2 syscall --- system/linux/syscalls/src/lib.rs | 28 ++++++++++++++++++++++++++++ system/linux/syscalls/src/x86_64.rs | 2 ++ 2 files changed, 30 insertions(+) diff --git a/system/linux/syscalls/src/lib.rs b/system/linux/syscalls/src/lib.rs index ba3474f..c95ad07 100644 --- a/system/linux/syscalls/src/lib.rs +++ b/system/linux/syscalls/src/lib.rs @@ -343,6 +343,23 @@ pub unsafe fn delete_module(name: *const c_char, flags: c_int) -> c_int { } } +/// +/// +/// Returns the raw kernel return value. +/// Negative values in `[-4095, -1]` represent `errno`. +#[cfg(not(target_arch = "aarch64"))] +pub fn dup2(oldfd: c_int, newfd: c_int) -> c_int { + // SAFETY: dup2 is safe to call. + #[cfg(not(miri))] + return unsafe { syscall2(Sysno::Dup2, oldfd as _, newfd as _) } as _; + + // SAFETY: dup2 is safe to call. + #[cfg(miri)] + unsafe { + libc::dup2(oldfd, newfd) + } +} + /// /// /// Returns the raw kernel return value. @@ -571,6 +588,9 @@ mod tests { use super::{close, dup, getpid, mmap, mremap, write}; + #[cfg(not(target_arch = "aarch64"))] + use super::dup2; + #[cfg(not(any(miri, target_arch = "aarch64")))] use super::{access, alarm, chmod, creat, create_module}; @@ -712,6 +732,14 @@ mod tests { assert!(ret < 0); } + #[test] + #[cfg(not(target_arch = "aarch64"))] + fn test_dup2() { + let ret = dup2(-1, -1); + + assert!(ret < 0); + } + #[test] #[cfg(not(miri))] fn test_kill() { diff --git a/system/linux/syscalls/src/x86_64.rs b/system/linux/syscalls/src/x86_64.rs index 392e94a..63234c3 100644 --- a/system/linux/syscalls/src/x86_64.rs +++ b/system/linux/syscalls/src/x86_64.rs @@ -19,6 +19,8 @@ pub enum Sysno { Mremap = 25, /// Dup = 32, + /// + Dup2 = 33, /// Alarm = 37, /// From 3862ca327b4017745aa453ea3b7b48d77a56bf39 Mon Sep 17 00:00:00 2001 From: Olle Lukowski Date: Sat, 14 Mar 2026 22:58:37 +0100 Subject: [PATCH 09/22] execve syscall --- system/linux/syscalls/src/aarch64.rs | 2 + system/linux/syscalls/src/lib.rs | 56 ++++++++++++++++++++++++++-- system/linux/syscalls/src/x86_64.rs | 2 + 3 files changed, 57 insertions(+), 3 deletions(-) diff --git a/system/linux/syscalls/src/aarch64.rs b/system/linux/syscalls/src/aarch64.rs index ff50391..f602f87 100644 --- a/system/linux/syscalls/src/aarch64.rs +++ b/system/linux/syscalls/src/aarch64.rs @@ -35,6 +35,8 @@ pub enum Sysno { Mremap = 216, /// Clone = 220, + /// + Execve = 221, /// Mmap = 222, } diff --git a/system/linux/syscalls/src/lib.rs b/system/linux/syscalls/src/lib.rs index c95ad07..ab8b6d4 100644 --- a/system/linux/syscalls/src/lib.rs +++ b/system/linux/syscalls/src/lib.rs @@ -378,6 +378,43 @@ pub fn kill(pid: pid_t, sig: c_int) -> c_int { } } +/// +/// +/// Returns the raw kernel return value. +/// Negative values in `[-4095, -1]` represent `errno`. +/// On success, this function does not return (the process image is replaced). +/// +/// # Safety +/// - `path` must be a pointer to a null-terminated string, +/// that must be readable until the null terminator (see [`core::ptr::read`]). +/// - `argv` must be a pointer to an array of pointers to null-terminated +/// strings, terminated by a null pointer. Each string must be readable +/// until the null terminator. +/// - `envp` must be a pointer to an array of pointers to null-terminated +/// strings (or [`core::ptr::null()`] for an empty environment), terminated +/// by a null pointer. Each string must be readable until the null +/// terminator. +pub unsafe fn execve( + path: *const c_char, + argv: *const *const c_char, + envp: *const *const c_char, +) -> c_int { + // SAFETY: guaranteed by caller. + #[cfg(not(miri))] + return unsafe { + syscall3(Sysno::Execve, path.addr(), argv.addr(), envp.addr()) + } as _; + + #[cfg(miri)] + { + _ = path; + _ = argv; + _ = envp; + // Syscall not supported by Miri + -libc::ENOSYS as c_int + } +} + /// /// /// Returns the raw kernel return value. @@ -597,11 +634,11 @@ mod tests { #[cfg(not(miri))] use { super::{ - acct, adjtimex, brk, chdir, chroot, clone, delete_module, kill, - openat, + acct, adjtimex, brk, chdir, chroot, clone, delete_module, execve, + kill, openat, }, core::mem::MaybeUninit, - libc::{c_ulong, timex}, + libc::{c_char, c_ulong, timex}, }; #[test] @@ -732,6 +769,19 @@ mod tests { assert!(ret < 0); } + #[test] + #[cfg(not(miri))] + fn test_execve() { + // SAFETY: the pointers we are passing point to valid null-terminated + // strings/arrays, and execve will fail due to invalid path. + let argv = [ptr::null::()]; + let envp = [ptr::null::()]; + let ret = unsafe { execve(c"".as_ptr(), argv.as_ptr(), envp.as_ptr()) }; + + // execve will fail with an empty path + assert!(ret < 0); + } + #[test] #[cfg(not(target_arch = "aarch64"))] fn test_dup2() { diff --git a/system/linux/syscalls/src/x86_64.rs b/system/linux/syscalls/src/x86_64.rs index 63234c3..1489115 100644 --- a/system/linux/syscalls/src/x86_64.rs +++ b/system/linux/syscalls/src/x86_64.rs @@ -27,6 +27,8 @@ pub enum Sysno { Getpid = 39, /// Clone = 56, + /// + Execve = 59, /// Exit = 60, /// From fa8cc1ba74735aaef08dae0816046deb835dd3b8 Mon Sep 17 00:00:00 2001 From: Olle Lukowski Date: Sat, 14 Mar 2026 23:08:39 +0100 Subject: [PATCH 10/22] fchdir syscall --- system/linux/syscalls/src/aarch64.rs | 2 ++ system/linux/syscalls/src/lib.rs | 53 ++++++++++++++++++++-------- system/linux/syscalls/src/x86_64.rs | 2 ++ 3 files changed, 43 insertions(+), 14 deletions(-) diff --git a/system/linux/syscalls/src/aarch64.rs b/system/linux/syscalls/src/aarch64.rs index f602f87..974fd37 100644 --- a/system/linux/syscalls/src/aarch64.rs +++ b/system/linux/syscalls/src/aarch64.rs @@ -9,6 +9,8 @@ pub enum Sysno { Dup = 23, /// Chdir = 49, + /// + Fchdir = 50, /// Chroot = 51, /// diff --git a/system/linux/syscalls/src/lib.rs b/system/linux/syscalls/src/lib.rs index ab8b6d4..0d6f57d 100644 --- a/system/linux/syscalls/src/lib.rs +++ b/system/linux/syscalls/src/lib.rs @@ -227,6 +227,23 @@ pub fn exit(status: c_int) -> ! { } } +/// +/// +/// Returns the raw kernel return value. +/// Negative values in `[-4095, -1]` represent `errno`. +pub fn fchdir(fd: c_int) -> c_int { + // SAFETY: fchdir is safe to call. + #[cfg(not(miri))] + return unsafe { syscall1(Sysno::Fchdir, fd as _) } as _; + + #[cfg(miri)] + { + _ = fd; + // Syscall not supported by Miri + -libc::ENOSYS as c_int + } +} + /// /// /// Returns the raw kernel return value. @@ -635,7 +652,7 @@ mod tests { use { super::{ acct, adjtimex, brk, chdir, chroot, clone, delete_module, execve, - kill, openat, + fchdir, kill, openat, }, core::mem::MaybeUninit, libc::{c_char, c_ulong, timex}, @@ -716,6 +733,14 @@ mod tests { assert!(ret < 0); } + #[test] + #[cfg(not(miri))] + fn test_fchdir() { + let ret = fchdir(-1); + + assert!(ret < 0); + } + #[test] #[cfg(not(any(miri, target_arch = "aarch64")))] fn test_access() { @@ -769,19 +794,6 @@ mod tests { assert!(ret < 0); } - #[test] - #[cfg(not(miri))] - fn test_execve() { - // SAFETY: the pointers we are passing point to valid null-terminated - // strings/arrays, and execve will fail due to invalid path. - let argv = [ptr::null::()]; - let envp = [ptr::null::()]; - let ret = unsafe { execve(c"".as_ptr(), argv.as_ptr(), envp.as_ptr()) }; - - // execve will fail with an empty path - assert!(ret < 0); - } - #[test] #[cfg(not(target_arch = "aarch64"))] fn test_dup2() { @@ -800,6 +812,19 @@ mod tests { assert_eq!(ret, 0); } + #[test] + #[cfg(not(miri))] + fn test_execve() { + // SAFETY: the pointers we are passing point to valid null-terminated + // strings/arrays, and execve will fail due to invalid path. + let argv = [ptr::null::()]; + let envp = [ptr::null::()]; + let ret = unsafe { execve(c"".as_ptr(), argv.as_ptr(), envp.as_ptr()) }; + + // execve will fail with an empty path + assert!(ret < 0); + } + #[test] fn test_write() { let msg = b"test\n"; diff --git a/system/linux/syscalls/src/x86_64.rs b/system/linux/syscalls/src/x86_64.rs index 1489115..8ac6e8c 100644 --- a/system/linux/syscalls/src/x86_64.rs +++ b/system/linux/syscalls/src/x86_64.rs @@ -35,6 +35,8 @@ pub enum Sysno { Kill = 62, /// Chdir = 80, + /// + Fchdir = 81, /// Creat = 85, /// From a022d8403a7c50f294d2efbe0112b2ab0cc6e841 Mon Sep 17 00:00:00 2001 From: Olle Lukowski Date: Sat, 14 Mar 2026 23:41:38 +0100 Subject: [PATCH 11/22] fchmod syscall --- system/linux/syscalls/src/aarch64.rs | 2 ++ system/linux/syscalls/src/lib.rs | 28 +++++++++++++++++++++++++++- system/linux/syscalls/src/x86_64.rs | 2 ++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/system/linux/syscalls/src/aarch64.rs b/system/linux/syscalls/src/aarch64.rs index 974fd37..70c87b9 100644 --- a/system/linux/syscalls/src/aarch64.rs +++ b/system/linux/syscalls/src/aarch64.rs @@ -13,6 +13,8 @@ pub enum Sysno { Fchdir = 50, /// Chroot = 51, + /// + Fchmod = 52, /// Openat = 56, /// diff --git a/system/linux/syscalls/src/lib.rs b/system/linux/syscalls/src/lib.rs index 0d6f57d..00a5e40 100644 --- a/system/linux/syscalls/src/lib.rs +++ b/system/linux/syscalls/src/lib.rs @@ -360,6 +360,24 @@ pub unsafe fn delete_module(name: *const c_char, flags: c_int) -> c_int { } } +/// +/// +/// Returns the raw kernel return value. +/// Negative values in `[-4095, -1]` represent `errno`. +pub fn fchmod(fd: c_int, mode: mode_t) -> c_int { + // SAFETY: fchmod is safe to call. + #[cfg(not(miri))] + return unsafe { syscall2(Sysno::Fchmod, fd as _, mode as _) } as _; + + #[cfg(miri)] + { + _ = fd; + _ = mode; + // Syscall not supported by Miri + -libc::ENOSYS as c_int + } +} + /// /// /// Returns the raw kernel return value. @@ -652,7 +670,7 @@ mod tests { use { super::{ acct, adjtimex, brk, chdir, chroot, clone, delete_module, execve, - fchdir, kill, openat, + fchdir, fchmod, kill, openat, }, core::mem::MaybeUninit, libc::{c_char, c_ulong, timex}, @@ -802,6 +820,14 @@ mod tests { assert!(ret < 0); } + #[test] + #[cfg(not(miri))] + fn test_fchmod() { + let ret = fchmod(-1, 0); + + assert!(ret < 0); + } + #[test] #[cfg(not(miri))] fn test_kill() { diff --git a/system/linux/syscalls/src/x86_64.rs b/system/linux/syscalls/src/x86_64.rs index 8ac6e8c..9c734df 100644 --- a/system/linux/syscalls/src/x86_64.rs +++ b/system/linux/syscalls/src/x86_64.rs @@ -41,6 +41,8 @@ pub enum Sysno { Creat = 85, /// Chmod = 90, + /// + Fchmod = 91, /// Adjtimex = 159, /// From 6628dc1df171b9ce2ed171ba7df7bca0862c4f69 Mon Sep 17 00:00:00 2001 From: Olle Lukowski Date: Sun, 15 Mar 2026 08:03:40 +0100 Subject: [PATCH 12/22] fchown syscall --- system/linux/syscalls/src/aarch64.rs | 2 ++ system/linux/syscalls/src/lib.rs | 34 +++++++++++++++++++++++++--- system/linux/syscalls/src/x86_64.rs | 2 ++ 3 files changed, 35 insertions(+), 3 deletions(-) diff --git a/system/linux/syscalls/src/aarch64.rs b/system/linux/syscalls/src/aarch64.rs index 70c87b9..2bf87ef 100644 --- a/system/linux/syscalls/src/aarch64.rs +++ b/system/linux/syscalls/src/aarch64.rs @@ -15,6 +15,8 @@ pub enum Sysno { Chroot = 51, /// Fchmod = 52, + /// + Fchown = 55, /// Openat = 56, /// diff --git a/system/linux/syscalls/src/lib.rs b/system/linux/syscalls/src/lib.rs index 00a5e40..13e234f 100644 --- a/system/linux/syscalls/src/lib.rs +++ b/system/linux/syscalls/src/lib.rs @@ -16,8 +16,8 @@ pub mod arch { } use libc::{ - c_char, c_int, c_long, c_ulong, c_void, mode_t, off_t, pid_t, size_t, - ssize_t, timex, + c_char, c_int, c_long, c_ulong, c_void, gid_t, mode_t, off_t, pid_t, + size_t, ssize_t, timex, uid_t, }; #[cfg(not(target_arch = "aarch64"))] @@ -450,6 +450,26 @@ pub unsafe fn execve( } } +/// +/// +/// Returns the raw kernel return value. +/// Negative values in `[-4095, -1]` represent `errno`. +pub fn fchown(fd: c_int, owner: uid_t, group: gid_t) -> c_int { + // SAFETY: fchown is safe to call. + #[cfg(not(miri))] + return unsafe { syscall3(Sysno::Fchown, fd as _, owner as _, group as _) } + as _; + + #[cfg(miri)] + { + _ = fd; + _ = owner; + _ = group; + // Syscall not supported by Miri + -libc::ENOSYS as c_int + } +} + /// /// /// Returns the raw kernel return value. @@ -670,7 +690,7 @@ mod tests { use { super::{ acct, adjtimex, brk, chdir, chroot, clone, delete_module, execve, - fchdir, fchmod, kill, openat, + fchdir, fchmod, fchown, kill, openat, }, core::mem::MaybeUninit, libc::{c_char, c_ulong, timex}, @@ -851,6 +871,14 @@ mod tests { assert!(ret < 0); } + #[test] + #[cfg(not(miri))] + fn test_fchown() { + let ret = fchown(-1, 0, 0); + + assert!(ret < 0); + } + #[test] fn test_write() { let msg = b"test\n"; diff --git a/system/linux/syscalls/src/x86_64.rs b/system/linux/syscalls/src/x86_64.rs index 9c734df..8494652 100644 --- a/system/linux/syscalls/src/x86_64.rs +++ b/system/linux/syscalls/src/x86_64.rs @@ -43,6 +43,8 @@ pub enum Sysno { Chmod = 90, /// Fchmod = 91, + /// + Fchown = 93, /// Adjtimex = 159, /// From e83d58768d61296b506fe2a86db93f6bb01d7153 Mon Sep 17 00:00:00 2001 From: Olle Lukowski Date: Sun, 15 Mar 2026 09:05:57 +0100 Subject: [PATCH 13/22] fcntl syscall --- system/linux/syscalls/src/aarch64.rs | 2 ++ system/linux/syscalls/src/lib.rs | 25 ++++++++++++++++++++++++- system/linux/syscalls/src/x86_64.rs | 2 ++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/system/linux/syscalls/src/aarch64.rs b/system/linux/syscalls/src/aarch64.rs index 2bf87ef..7795572 100644 --- a/system/linux/syscalls/src/aarch64.rs +++ b/system/linux/syscalls/src/aarch64.rs @@ -7,6 +7,8 @@ use core::arch::asm; pub enum Sysno { /// Dup = 23, + /// + Fcntl = 25, /// Chdir = 49, /// diff --git a/system/linux/syscalls/src/lib.rs b/system/linux/syscalls/src/lib.rs index 13e234f..92a30b4 100644 --- a/system/linux/syscalls/src/lib.rs +++ b/system/linux/syscalls/src/lib.rs @@ -470,6 +470,22 @@ pub fn fchown(fd: c_int, owner: uid_t, group: gid_t) -> c_int { } } +/// +/// +/// Returns the raw kernel return value. +/// Negative values in `[-4095, -1]` represent `errno`. +pub fn fcntl(fd: c_int, cmd: c_int, arg: c_ulong) -> c_int { + // SAFETY: fcntl is safe to call. + #[cfg(not(miri))] + return unsafe { syscall3(Sysno::Fcntl, fd as _, cmd as _, arg as _) } as _; + + // SAFETY: fcntl is safe to call. + #[cfg(miri)] + unsafe { + libc::fcntl(fd, cmd, arg) + } +} + /// /// /// Returns the raw kernel return value. @@ -678,7 +694,7 @@ pub unsafe fn mmap( mod tests { use core::ptr; - use super::{close, dup, getpid, mmap, mremap, write}; + use super::{close, dup, fcntl, getpid, mmap, mremap, write}; #[cfg(not(target_arch = "aarch64"))] use super::dup2; @@ -879,6 +895,13 @@ mod tests { assert!(ret < 0); } + #[test] + fn test_fcntl() { + let ret = fcntl(-1, 0, 0); + + assert!(ret < 0); + } + #[test] fn test_write() { let msg = b"test\n"; diff --git a/system/linux/syscalls/src/x86_64.rs b/system/linux/syscalls/src/x86_64.rs index 8494652..405fe2a 100644 --- a/system/linux/syscalls/src/x86_64.rs +++ b/system/linux/syscalls/src/x86_64.rs @@ -33,6 +33,8 @@ pub enum Sysno { Exit = 60, /// Kill = 62, + /// + Fcntl = 72, /// Chdir = 80, /// From 64f49bb0b0645787d9b08249cf15f068d39b989d Mon Sep 17 00:00:00 2001 From: Olle Lukowski Date: Sun, 15 Mar 2026 09:10:57 +0100 Subject: [PATCH 14/22] miri fixes --- system/linux/syscalls/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/linux/syscalls/src/lib.rs b/system/linux/syscalls/src/lib.rs index 92a30b4..6831127 100644 --- a/system/linux/syscalls/src/lib.rs +++ b/system/linux/syscalls/src/lib.rs @@ -474,7 +474,7 @@ pub fn fchown(fd: c_int, owner: uid_t, group: gid_t) -> c_int { /// /// Returns the raw kernel return value. /// Negative values in `[-4095, -1]` represent `errno`. -pub fn fcntl(fd: c_int, cmd: c_int, arg: c_ulong) -> c_int { +pub fn fcntl(fd: c_int, cmd: c_int, arg: c_int) -> c_int { // SAFETY: fcntl is safe to call. #[cfg(not(miri))] return unsafe { syscall3(Sysno::Fcntl, fd as _, cmd as _, arg as _) } as _; From 6807c2db8a17035446fa374a93a6de3e9f7c3aae Mon Sep 17 00:00:00 2001 From: Olle Lukowski Date: Sun, 15 Mar 2026 09:28:32 +0100 Subject: [PATCH 15/22] fork syscall --- system/linux/syscalls/src/lib.rs | 33 ++++++++++++++++++++++++++--- system/linux/syscalls/src/x86_64.rs | 2 ++ 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/system/linux/syscalls/src/lib.rs b/system/linux/syscalls/src/lib.rs index 6831127..ce4b7f3 100644 --- a/system/linux/syscalls/src/lib.rs +++ b/system/linux/syscalls/src/lib.rs @@ -31,6 +31,25 @@ use arch::{ #[cfg(miri)] use core::ptr; +/// +/// +/// Returns the raw kernel return value. +/// Negative values in `[-4095, -1]` represent `errno`. +/// On success, the child process receives 0 and the parent receives the child's +/// PID. +#[cfg(not(target_arch = "aarch64"))] +pub fn fork() -> pid_t { + // SAFETY: fork is safe to call. + #[cfg(not(miri))] + return unsafe { syscall0(Sysno::Fork) } as _; + + #[cfg(miri)] + { + // Syscall not supported by Miri + -libc::ENOSYS as pid_t + } +} + /// /// /// Returns the raw kernel return value. @@ -657,8 +676,8 @@ pub unsafe fn mremap( /// Negative values in `[-4095, -1]` represent `errno`. /// /// # Safety -/// - If `flags` contains [`libc::MAP_FIXED`], the range `[addr, addr + length)` must -/// not overlap any existing mapping that should be preserved; the kernel +/// - If `flags` contains [`libc::MAP_FIXED`], the range `[addr, addr + length)` +/// must not overlap any existing mapping that should be preserved; the kernel /// will silently clobber it, invalidating any pointers or references into /// that region. pub unsafe fn mmap( @@ -700,7 +719,7 @@ mod tests { use super::dup2; #[cfg(not(any(miri, target_arch = "aarch64")))] - use super::{access, alarm, chmod, creat, create_module}; + use super::{access, alarm, chmod, creat, create_module, fork}; #[cfg(not(miri))] use { @@ -712,6 +731,14 @@ mod tests { libc::{c_char, c_ulong, timex}, }; + #[test] + #[cfg(not(target_arch = "aarch64"))] + fn test_fork() { + fork(); + // Both parent and child continue here without checking result + // to maintain determinism. + } + #[test] fn test_getpid() { assert!(getpid() > 0); diff --git a/system/linux/syscalls/src/x86_64.rs b/system/linux/syscalls/src/x86_64.rs index 405fe2a..24e74d5 100644 --- a/system/linux/syscalls/src/x86_64.rs +++ b/system/linux/syscalls/src/x86_64.rs @@ -27,6 +27,8 @@ pub enum Sysno { Getpid = 39, /// Clone = 56, + /// + Fork = 57, /// Execve = 59, /// From 0e9954115ef7be0098a173a986fdfb17d6c5afd6 Mon Sep 17 00:00:00 2001 From: Olle Lukowski Date: Sun, 15 Mar 2026 09:30:36 +0100 Subject: [PATCH 16/22] fix miri again --- system/linux/syscalls/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/linux/syscalls/src/lib.rs b/system/linux/syscalls/src/lib.rs index ce4b7f3..0dd2794 100644 --- a/system/linux/syscalls/src/lib.rs +++ b/system/linux/syscalls/src/lib.rs @@ -732,7 +732,7 @@ mod tests { }; #[test] - #[cfg(not(target_arch = "aarch64"))] + #[cfg(not(any(miri, target_arch = "aarch64")))] fn test_fork() { fork(); // Both parent and child continue here without checking result From 173c69f2e67936d3d44dc4e406de7a77e69d4dd7 Mon Sep 17 00:00:00 2001 From: Olle Lukowski Date: Sun, 15 Mar 2026 10:27:46 +0100 Subject: [PATCH 17/22] fstat syscall --- system/linux/syscalls/src/aarch64.rs | 2 ++ system/linux/syscalls/src/lib.rs | 40 +++++++++++++++++++++++++--- system/linux/syscalls/src/x86_64.rs | 2 ++ 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/system/linux/syscalls/src/aarch64.rs b/system/linux/syscalls/src/aarch64.rs index 7795572..5135c01 100644 --- a/system/linux/syscalls/src/aarch64.rs +++ b/system/linux/syscalls/src/aarch64.rs @@ -25,6 +25,8 @@ pub enum Sysno { Close = 57, /// Write = 64, + /// + Fstat = 80, /// Acct = 89, /// diff --git a/system/linux/syscalls/src/lib.rs b/system/linux/syscalls/src/lib.rs index 0dd2794..57c3707 100644 --- a/system/linux/syscalls/src/lib.rs +++ b/system/linux/syscalls/src/lib.rs @@ -17,7 +17,7 @@ pub mod arch { use libc::{ c_char, c_int, c_long, c_ulong, c_void, gid_t, mode_t, off_t, pid_t, - size_t, ssize_t, timex, uid_t, + size_t, ssize_t, stat, timex, uid_t, }; #[cfg(not(target_arch = "aarch64"))] @@ -397,6 +397,27 @@ pub fn fchmod(fd: c_int, mode: mode_t) -> c_int { } } +/// +/// +/// Returns the raw kernel return value. +/// Negative values in `[-4095, -1]` represent `errno`. +/// +/// # Safety +/// - `statbuf` must be a valid pointer to a [`stat`] struct, +/// that must be writable for the duration of the syscall +/// (see [`core::ptr::write`] for details). +pub unsafe fn fstat(fd: c_int, statbuf: *mut stat) -> c_int { + // SAFETY: guaranteed by caller. + #[cfg(not(miri))] + return unsafe { syscall2(Sysno::Fstat, fd as _, statbuf.addr()) } as _; + + // SAFETY: guaranteed by caller. + #[cfg(miri)] + unsafe { + libc::fstat(fd, statbuf) + } +} + /// /// /// Returns the raw kernel return value. @@ -713,7 +734,11 @@ pub unsafe fn mmap( mod tests { use core::ptr; - use super::{close, dup, fcntl, getpid, mmap, mremap, write}; + use super::{close, dup, fcntl, fstat, getpid, mmap, mremap, write}; + + use core::mem::MaybeUninit; + + use libc::stat; #[cfg(not(target_arch = "aarch64"))] use super::dup2; @@ -727,7 +752,6 @@ mod tests { acct, adjtimex, brk, chdir, chroot, clone, delete_module, execve, fchdir, fchmod, fchown, kill, openat, }, - core::mem::MaybeUninit, libc::{c_char, c_ulong, timex}, }; @@ -891,6 +915,16 @@ mod tests { assert!(ret < 0); } + #[test] + fn test_fstat() { + let mut statbuf: MaybeUninit = MaybeUninit::uninit(); + + // SAFETY: we are passing a valid pointer to a `stat` struct. + let ret = unsafe { fstat(-1, (&raw mut statbuf).cast()) }; + + assert!(ret < 0); + } + #[test] #[cfg(not(miri))] fn test_kill() { diff --git a/system/linux/syscalls/src/x86_64.rs b/system/linux/syscalls/src/x86_64.rs index 24e74d5..763bfbb 100644 --- a/system/linux/syscalls/src/x86_64.rs +++ b/system/linux/syscalls/src/x86_64.rs @@ -9,6 +9,8 @@ pub enum Sysno { Write = 1, /// Close = 3, + /// + Fstat = 5, /// Mmap = 9, /// From f8bfa8e923f84bd0ad7e205e9dbf742623f71ad8 Mon Sep 17 00:00:00 2001 From: Olle Lukowski Date: Sun, 15 Mar 2026 10:33:09 +0100 Subject: [PATCH 18/22] disable fstat test for miri --- system/linux/syscalls/src/lib.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/system/linux/syscalls/src/lib.rs b/system/linux/syscalls/src/lib.rs index 57c3707..6884c7e 100644 --- a/system/linux/syscalls/src/lib.rs +++ b/system/linux/syscalls/src/lib.rs @@ -411,10 +411,12 @@ pub unsafe fn fstat(fd: c_int, statbuf: *mut stat) -> c_int { #[cfg(not(miri))] return unsafe { syscall2(Sysno::Fstat, fd as _, statbuf.addr()) } as _; - // SAFETY: guaranteed by caller. #[cfg(miri)] - unsafe { - libc::fstat(fd, statbuf) + { + _ = fd; + _ = statbuf; + // Syscall not supported by Miri (when isolation is enabled) + -libc::ENOSYS as c_int } } @@ -736,10 +738,6 @@ mod tests { use super::{close, dup, fcntl, fstat, getpid, mmap, mremap, write}; - use core::mem::MaybeUninit; - - use libc::stat; - #[cfg(not(target_arch = "aarch64"))] use super::dup2; @@ -752,7 +750,8 @@ mod tests { acct, adjtimex, brk, chdir, chroot, clone, delete_module, execve, fchdir, fchmod, fchown, kill, openat, }, - libc::{c_char, c_ulong, timex}, + core::mem::MaybeUninit, + libc::{c_char, c_ulong, stat, timex}, }; #[test] @@ -916,6 +915,7 @@ mod tests { } #[test] + #[cfg(not(miri))] fn test_fstat() { let mut statbuf: MaybeUninit = MaybeUninit::uninit(); From a0aa4a04e8c5c329f571626bd8635a298341dda7 Mon Sep 17 00:00:00 2001 From: Olle Lukowski Date: Sun, 15 Mar 2026 10:47:29 +0100 Subject: [PATCH 19/22] fstatfs syscall --- system/linux/syscalls/src/aarch64.rs | 2 ++ system/linux/syscalls/src/lib.rs | 40 +++++++++++++++++++++++++--- system/linux/syscalls/src/x86_64.rs | 2 ++ 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/system/linux/syscalls/src/aarch64.rs b/system/linux/syscalls/src/aarch64.rs index 5135c01..89bc8e9 100644 --- a/system/linux/syscalls/src/aarch64.rs +++ b/system/linux/syscalls/src/aarch64.rs @@ -9,6 +9,8 @@ pub enum Sysno { Dup = 23, /// Fcntl = 25, + /// + Fstatfs = 44, /// Chdir = 49, /// diff --git a/system/linux/syscalls/src/lib.rs b/system/linux/syscalls/src/lib.rs index 6884c7e..450aaaf 100644 --- a/system/linux/syscalls/src/lib.rs +++ b/system/linux/syscalls/src/lib.rs @@ -17,7 +17,7 @@ pub mod arch { use libc::{ c_char, c_int, c_long, c_ulong, c_void, gid_t, mode_t, off_t, pid_t, - size_t, ssize_t, stat, timex, uid_t, + size_t, ssize_t, stat, statfs, timex, uid_t, }; #[cfg(not(target_arch = "aarch64"))] @@ -420,6 +420,29 @@ pub unsafe fn fstat(fd: c_int, statbuf: *mut stat) -> c_int { } } +/// +/// +/// Returns the raw kernel return value. +/// Negative values in `[-4095, -1]` represent `errno`. +/// +/// # Safety +/// - `buf` must be a valid pointer to a [`statfs`] struct, +/// that must be writable for the duration of the syscall +/// (see [`core::ptr::write`] for details). +pub unsafe fn fstatfs(fd: c_int, buf: *mut statfs) -> c_int { + // SAFETY: guaranteed by caller. + #[cfg(not(miri))] + return unsafe { syscall2(Sysno::Fstatfs, fd as _, buf.addr()) } as _; + + #[cfg(miri)] + { + _ = fd; + _ = buf; + // Syscall not supported by Miri + -libc::ENOSYS as c_int + } +} + /// /// /// Returns the raw kernel return value. @@ -748,10 +771,10 @@ mod tests { use { super::{ acct, adjtimex, brk, chdir, chroot, clone, delete_module, execve, - fchdir, fchmod, fchown, kill, openat, + fchdir, fchmod, fchown, fstatfs, kill, openat, }, core::mem::MaybeUninit, - libc::{c_char, c_ulong, stat, timex}, + libc::{c_char, c_ulong, stat, statfs, timex}, }; #[test] @@ -925,6 +948,17 @@ mod tests { assert!(ret < 0); } + #[test] + #[cfg(not(miri))] + fn test_fstatfs() { + let mut buf: MaybeUninit = MaybeUninit::uninit(); + + // SAFETY: we are passing a valid pointer to a `statfs` struct. + let ret = unsafe { fstatfs(-1, (&raw mut buf).cast()) }; + + assert!(ret < 0); + } + #[test] #[cfg(not(miri))] fn test_kill() { diff --git a/system/linux/syscalls/src/x86_64.rs b/system/linux/syscalls/src/x86_64.rs index 763bfbb..bdfafa0 100644 --- a/system/linux/syscalls/src/x86_64.rs +++ b/system/linux/syscalls/src/x86_64.rs @@ -51,6 +51,8 @@ pub enum Sysno { Fchmod = 91, /// Fchown = 93, + /// + Fstatfs = 138, /// Adjtimex = 159, /// From f70475b1f38b532c12bc55e087c23024b0db0689 Mon Sep 17 00:00:00 2001 From: Olle Lukowski Date: Sun, 15 Mar 2026 10:56:01 +0100 Subject: [PATCH 20/22] fsync syscall --- system/linux/syscalls/src/aarch64.rs | 2 ++ system/linux/syscalls/src/lib.rs | 27 ++++++++++++++++++++++++++- system/linux/syscalls/src/x86_64.rs | 2 ++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/system/linux/syscalls/src/aarch64.rs b/system/linux/syscalls/src/aarch64.rs index 89bc8e9..2ef0bac 100644 --- a/system/linux/syscalls/src/aarch64.rs +++ b/system/linux/syscalls/src/aarch64.rs @@ -29,6 +29,8 @@ pub enum Sysno { Write = 64, /// Fstat = 80, + /// + Fsync = 82, /// Acct = 89, /// diff --git a/system/linux/syscalls/src/lib.rs b/system/linux/syscalls/src/lib.rs index 450aaaf..3dfe02b 100644 --- a/system/linux/syscalls/src/lib.rs +++ b/system/linux/syscalls/src/lib.rs @@ -443,6 +443,23 @@ pub unsafe fn fstatfs(fd: c_int, buf: *mut statfs) -> c_int { } } +/// +/// +/// Returns the raw kernel return value. +/// Negative values in `[-4095, -1]` represent `errno`. +pub fn fsync(fd: c_int) -> c_int { + // SAFETY: fsync is safe to call. + #[cfg(not(miri))] + return unsafe { syscall1(Sysno::Fsync, fd as _) } as _; + + #[cfg(miri)] + { + _ = fd; + // Syscall not supported by Miri (when isolation is enabled) + -libc::ENOSYS as c_int + } +} + /// /// /// Returns the raw kernel return value. @@ -771,7 +788,7 @@ mod tests { use { super::{ acct, adjtimex, brk, chdir, chroot, clone, delete_module, execve, - fchdir, fchmod, fchown, fstatfs, kill, openat, + fchdir, fchmod, fchown, fstatfs, fsync, kill, openat, }, core::mem::MaybeUninit, libc::{c_char, c_ulong, stat, statfs, timex}, @@ -959,6 +976,14 @@ mod tests { assert!(ret < 0); } + #[test] + #[cfg(not(miri))] + fn test_fsync() { + let ret = fsync(-1); + + assert!(ret < 0); + } + #[test] #[cfg(not(miri))] fn test_kill() { diff --git a/system/linux/syscalls/src/x86_64.rs b/system/linux/syscalls/src/x86_64.rs index bdfafa0..a366e46 100644 --- a/system/linux/syscalls/src/x86_64.rs +++ b/system/linux/syscalls/src/x86_64.rs @@ -39,6 +39,8 @@ pub enum Sysno { Kill = 62, /// Fcntl = 72, + /// + Fsync = 74, /// Chdir = 80, /// From aadb1dbd7ccacce74c07de2cfbf6b87eb7fd2b04 Mon Sep 17 00:00:00 2001 From: Olle Lukowski Date: Sun, 15 Mar 2026 10:58:05 +0100 Subject: [PATCH 21/22] move import --- system/linux/syscalls/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system/linux/syscalls/src/lib.rs b/system/linux/syscalls/src/lib.rs index 3dfe02b..33458df 100644 --- a/system/linux/syscalls/src/lib.rs +++ b/system/linux/syscalls/src/lib.rs @@ -776,7 +776,7 @@ pub unsafe fn mmap( mod tests { use core::ptr; - use super::{close, dup, fcntl, fstat, getpid, mmap, mremap, write}; + use super::{close, dup, fcntl, getpid, mmap, mremap, write}; #[cfg(not(target_arch = "aarch64"))] use super::dup2; @@ -788,7 +788,7 @@ mod tests { use { super::{ acct, adjtimex, brk, chdir, chroot, clone, delete_module, execve, - fchdir, fchmod, fchown, fstatfs, fsync, kill, openat, + fchdir, fchmod, fchown, fstat, fstatfs, fsync, kill, openat, }, core::mem::MaybeUninit, libc::{c_char, c_ulong, stat, statfs, timex}, From daf69b5e0075eff4d0f6b50c9ac64e0560f5cfa3 Mon Sep 17 00:00:00 2001 From: Olle Lukowski Date: Sun, 15 Mar 2026 11:06:52 +0100 Subject: [PATCH 22/22] ftruncate syscall --- system/linux/syscalls/src/aarch64.rs | 2 ++ system/linux/syscalls/src/lib.rs | 29 +++++++++++++++++++++++++++- system/linux/syscalls/src/x86_64.rs | 2 ++ 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/system/linux/syscalls/src/aarch64.rs b/system/linux/syscalls/src/aarch64.rs index 2ef0bac..522d9a5 100644 --- a/system/linux/syscalls/src/aarch64.rs +++ b/system/linux/syscalls/src/aarch64.rs @@ -11,6 +11,8 @@ pub enum Sysno { Fcntl = 25, /// Fstatfs = 44, + /// + Ftruncate = 46, /// Chdir = 49, /// diff --git a/system/linux/syscalls/src/lib.rs b/system/linux/syscalls/src/lib.rs index 33458df..1f668c3 100644 --- a/system/linux/syscalls/src/lib.rs +++ b/system/linux/syscalls/src/lib.rs @@ -443,6 +443,24 @@ pub unsafe fn fstatfs(fd: c_int, buf: *mut statfs) -> c_int { } } +/// +/// +/// Returns the raw kernel return value. +/// Negative values in `[-4095, -1]` represent `errno`. +pub fn ftruncate(fd: c_int, length: off_t) -> c_int { + // SAFETY: ftruncate is safe to call. + #[cfg(not(miri))] + return unsafe { syscall2(Sysno::Ftruncate, fd as _, length as _) } as _; + + #[cfg(miri)] + { + _ = fd; + _ = length; + // Syscall not supported by Miri (when isolation is enabled) + -libc::ENOSYS as c_int + } +} + /// /// /// Returns the raw kernel return value. @@ -788,7 +806,8 @@ mod tests { use { super::{ acct, adjtimex, brk, chdir, chroot, clone, delete_module, execve, - fchdir, fchmod, fchown, fstat, fstatfs, fsync, kill, openat, + fchdir, fchmod, fchown, fstat, fstatfs, fsync, ftruncate, kill, + openat, }, core::mem::MaybeUninit, libc::{c_char, c_ulong, stat, statfs, timex}, @@ -976,6 +995,14 @@ mod tests { assert!(ret < 0); } + #[test] + #[cfg(not(miri))] + fn test_ftruncate() { + let ret = ftruncate(-1, 0); + + assert!(ret < 0); + } + #[test] #[cfg(not(miri))] fn test_fsync() { diff --git a/system/linux/syscalls/src/x86_64.rs b/system/linux/syscalls/src/x86_64.rs index a366e46..8cc39d1 100644 --- a/system/linux/syscalls/src/x86_64.rs +++ b/system/linux/syscalls/src/x86_64.rs @@ -41,6 +41,8 @@ pub enum Sysno { Fcntl = 72, /// Fsync = 74, + /// + Ftruncate = 77, /// Chdir = 80, ///