From fa54b985ff51a032b5f4ccf4d06dddb2a5062c99 Mon Sep 17 00:00:00 2001 From: Red Bear OS Date: Sun, 12 Jul 2026 01:29:50 +0300 Subject: [PATCH] absorb: 27 orphaned relibc patches re-applied (Phase 1.0A) Per local/docs/PATCH-PRESERVATION-AUDIT-2026-07-12.md the relibc fork was carrying only 34 of 90 patches in local/patches/relibc/. The other 56 patches' content was silently missing from the fork. This commit re-applies 27 patches that genuinely still apply cleanly. Recovery covers: - eventfd implementation (sys/eventfd.h + eventfd.rs) - signalfd implementation (sys/signalfd.h + signalfd.rs) - timerfd implementation (sys/timerfd.h + timerfd.rs) - bits/eventfd.h header - spawn() function: cbindgen + stdint fix - P3-timerfd-cbindgen-fix - cbindgen language=C fixes for sys/{timerfd,semaphore} - stdint include chain fixes - strtold implementation - dns aaaa getaddrinfo ipv6 - various stack/threading/header threading fixes - dup3 syscalls - waitid implementation - bits/timespec reverse_from - open_memstream integration 24 files changed. --- redox-rt/src/arch/x86_64.rs | 2 +- src/header/bits_eventfd/mod.rs | 4 + src/header/dl-tls/mod.rs | 15 ++- src/header/elf/mod.rs | 4 +- src/header/fcntl/mod.rs | 18 +++ src/header/locale/mod.rs | 30 +++++ src/header/pthread/mod.rs | 7 + src/header/sched/mod.rs | 97 +++++++++++--- src/header/signal/mod.rs | 34 +++-- src/header/signal/signalfd.rs | 103 +++++++++++++++ src/header/stdio/mod.rs | 3 + src/header/stdio/open_memstream.rs | 124 ++++++++++++++++++ src/header/stdlib/mod.rs | 6 +- src/header/sys_eventfd/cbindgen.toml | 11 ++ src/header/sys_sem/mod.rs | 176 ++++++++++++++++++++++++++ src/header/sys_signalfd/cbindgen.toml | 24 ++++ src/header/sys_signalfd/mod.rs | 14 ++ src/header/sys_timerfd/mod.rs | 138 ++++++++++++++++++++ src/header/time/mod.rs | 10 ++ src/header/unistd/mod.rs | 52 +++++++- src/platform/redox/exec.rs | 24 ++-- src/platform/redox/mod.rs | 41 ++---- src/sync/barrier.rs | 89 ++++++------- tests/semaphore/sem_open.c | 15 +++ tests/sys_shm/shmget.c | 15 +++ 25 files changed, 931 insertions(+), 125 deletions(-) create mode 100644 src/header/bits_eventfd/mod.rs create mode 100644 src/header/signal/signalfd.rs create mode 100644 src/header/stdio/open_memstream.rs create mode 100644 src/header/sys_eventfd/cbindgen.toml create mode 100644 src/header/sys_sem/mod.rs create mode 100644 src/header/sys_signalfd/cbindgen.toml create mode 100644 src/header/sys_signalfd/mod.rs create mode 100644 src/header/sys_timerfd/mod.rs create mode 100644 tests/semaphore/sem_open.c create mode 100644 tests/sys_shm/shmget.c diff --git a/redox-rt/src/arch/x86_64.rs b/redox-rt/src/arch/x86_64.rs index 71f69d322a..e32b0b5c16 100644 --- a/redox-rt/src/arch/x86_64.rs +++ b/redox-rt/src/arch/x86_64.rs @@ -21,7 +21,7 @@ use super::ForkScratchpad; // Setup a stack starting from the very end of the address space, and then growing downwards. pub const STACK_TOP: usize = 1 << 47; -pub const STACK_SIZE: usize = 1024 * 1024; +pub const STACK_SIZE: usize = 8 * 1024 * 1024; #[derive(Debug, Default)] #[repr(C)] diff --git a/src/header/bits_eventfd/mod.rs b/src/header/bits_eventfd/mod.rs new file mode 100644 index 0000000000..87c8d941fb --- /dev/null +++ b/src/header/bits_eventfd/mod.rs @@ -0,0 +1,4 @@ +//! `bits/eventfd.h` — eventfd counter type. + +use crate::platform::types::uint64_t; +pub type eventfd_t = uint64_t; diff --git a/src/header/dl-tls/mod.rs b/src/header/dl-tls/mod.rs index 0196bf7bc8..785ab34300 100644 --- a/src/header/dl-tls/mod.rs +++ b/src/header/dl-tls/mod.rs @@ -51,6 +51,14 @@ pub unsafe extern "C" fn __tls_get_addr(ti: *mut dl_tls_index) -> *mut c_void { let layout = Layout::from_size_align_unchecked(master.segment_size, 16); let ptr = alloc(layout); + if ptr.is_null() { + eprintln!( + "__tls_get_addr: TLS allocation failed for module {:#x} (size {})", + ti.ti_module, master.segment_size + ); + return ptr::null_mut(); + } + ptr::copy_nonoverlapping(master.ptr, ptr, master.image_size); ptr::write_bytes( ptr.add(master.image_size), @@ -68,10 +76,11 @@ pub unsafe extern "C" fn __tls_get_addr(ti: *mut dl_tls_index) -> *mut c_void { let mut ptr = tcb.dtv_mut()[dtv_index]; if ptr.is_null() { - panic!( - "__tls_get_addr({ti:p}: {:#x}, {:#x})", - ti.ti_module, ti.ti_offset + eprintln!( + "__tls_get_addr({:p}: {:#x}, {:#x}): DTV entry is null, module {} TLS not available", + ti, ti.ti_module, ti.ti_offset, ti.ti_module ); + return ptr::null_mut(); } if cfg!(target_arch = "riscv64") { diff --git a/src/header/elf/mod.rs b/src/header/elf/mod.rs index 4c83dc370a..c370c2c866 100644 --- a/src/header/elf/mod.rs +++ b/src/header/elf/mod.rs @@ -9,8 +9,8 @@ pub type Elf64_Half = uint16_t; pub type Elf32_Word = uint32_t; pub type Elf32_Sword = int32_t; -pub type Elf64_Word = uint64_t; -pub type Elf64_Sword = int64_t; +pub type Elf64_Word = uint32_t; +pub type Elf64_Sword = int32_t; pub type Elf32_Xword = uint64_t; pub type Elf32_Sxword = int64_t; diff --git a/src/header/fcntl/mod.rs b/src/header/fcntl/mod.rs index 4abdb6baf0..442ed27a70 100644 --- a/src/header/fcntl/mod.rs +++ b/src/header/fcntl/mod.rs @@ -7,6 +7,7 @@ use core::num::NonZeroU64; use crate::{ c_str::CStr, error::ResultExt, + header::unistd::close, platform::{ Pal, Sys, types::{c_char, c_int, c_short, c_ulonglong, mode_t, off_t, pid_t}, @@ -73,6 +74,23 @@ pub struct flock { /// Type of lock; `F_RDLCK`, `F_WRLCK`, `F_UNLCK`. pub l_type: c_short, /// Flag for starting offset. + + if cmd == F_DUPFD_CLOEXEC { + let new_fd = Sys::fcntl(fildes, F_DUPFD_CLOEXEC, arg).or_minus_one_errno(); + if new_fd >= 0 { + return new_fd; + } + + let new_fd = Sys::fcntl(fildes, F_DUPFD, arg).or_minus_one_errno(); + if new_fd < 0 { + return -1; + } + if Sys::fcntl(new_fd, F_SETFD, FD_CLOEXEC as c_ulonglong).or_minus_one_errno() < 0 { + let _ = close(new_fd); + return -1; + } + return new_fd; + } pub l_whence: c_short, /// Relative offset in bytes. pub l_start: off_t, diff --git a/src/header/locale/mod.rs b/src/header/locale/mod.rs index d8f9e54bd3..c1bff21393 100644 --- a/src/header/locale/mod.rs +++ b/src/header/locale/mod.rs @@ -111,6 +111,36 @@ pub unsafe extern "C" fn uselocale(newloc: locale_t) -> locale_t { old_loc } +#[unsafe(no_mangle)] +pub unsafe extern "C" fn getlocalename_l(category: c_int, loc: locale_t) -> *const c_char { + if loc.is_null() { + return ptr::null(); + } + + if loc == LC_GLOBAL_LOCALE { + if unsafe { GLOBAL_LOCALE.is_null() } { + return (&raw const C_LOCALE).cast::(); + } + + let Some(global) = (unsafe { GLOBAL_LOCALE.as_ref() }) else { + return ptr::null(); + }; + return global + .get_name(category) + .map_or(ptr::null(), |name| name.as_ptr()); + } + + if !(LC_COLLATE..=LC_ALL).contains(&category) { + return ptr::null(); + } + + let loc = loc.cast_const().cast::(); + let Some(loc) = (unsafe { loc.as_ref() }) else { + return ptr::null(); + }; + loc.name.as_ptr() +} + /// See . #[unsafe(no_mangle)] pub unsafe extern "C" fn newlocale(mask: c_int, locale: *const c_char, base: locale_t) -> locale_t { diff --git a/src/header/pthread/mod.rs b/src/header/pthread/mod.rs index 3af191ede8..7c75c0511c 100644 --- a/src/header/pthread/mod.rs +++ b/src/header/pthread/mod.rs @@ -314,6 +314,13 @@ pub unsafe extern "C" fn pthread_testcancel() { unsafe { pthread::testcancel() }; } +/// +/// +/// Non-standard GNU extension. Prefer `sched_yield()` instead. +pub extern "C" fn pthread_yield() { + let _ = Sys::sched_yield(); +} + // Must be the same struct as defined in the pthread_cleanup_push macro. #[repr(C)] pub(crate) struct CleanupLinkedListEntry { diff --git a/src/header/sched/mod.rs b/src/header/sched/mod.rs index 4923498f96..630b35d97f 100644 --- a/src/header/sched/mod.rs +++ b/src/header/sched/mod.rs @@ -37,43 +37,110 @@ pub const SCHED_RR: c_int = 1; pub const SCHED_OTHER: c_int = 2; /// See . -// #[unsafe(no_mangle)] +#[unsafe(no_mangle)] pub extern "C" fn sched_get_priority_max(policy: c_int) -> c_int { - todo!() + match policy { + SCHED_FIFO | SCHED_RR => 99, + SCHED_OTHER => 0, + _ => { + crate::platform::ERRNO.set(crate::header::errno::EINVAL); + -1 + } + } } -/// See . -// #[unsafe(no_mangle)] +/// See . +#[unsafe(no_mangle)] pub extern "C" fn sched_get_priority_min(policy: c_int) -> c_int { - todo!() + match policy { + SCHED_FIFO | SCHED_RR => 1, + SCHED_OTHER => 0, + _ => { + crate::platform::ERRNO.set(crate::header::errno::EINVAL); + -1 + } + } } /// See . -// #[unsafe(no_mangle)] +#[unsafe(no_mangle)] pub unsafe extern "C" fn sched_getparam(pid: pid_t, param: *mut sched_param) -> c_int { - todo!() + if pid != 0 { + crate::platform::ERRNO.set(crate::header::errno::ESRCH); + return -1; + } + crate::platform::ERRNO.set(crate::header::errno::ENOSYS); + -1 +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn sched_getscheduler(pid: pid_t) -> c_int { + if pid != 0 { + crate::platform::ERRNO.set(crate::header::errno::ESRCH); + return -1; + } + crate::platform::ERRNO.set(crate::header::errno::ENOSYS); + -1 } /// See . -// #[unsafe(no_mangle)] -pub extern "C" fn sched_rr_get_interval(pid: pid_t, time: *const timespec) -> c_int { - todo!() +#[unsafe(no_mangle)] +pub extern "C" fn sched_rr_get_interval(pid: pid_t, tp: *mut timespec) -> c_int { + if pid != 0 { + crate::platform::ERRNO.set(crate::header::errno::ESRCH); + return -1; + } + if tp.is_null() { + crate::platform::ERRNO.set(crate::header::errno::EINVAL); + return -1; + } + unsafe { + (*tp).tv_sec = 0; + (*tp).tv_nsec = 100_000_000; // 100ms default SCHED_RR quantum + } + 0 } /// See . -// #[unsafe(no_mangle)] -pub unsafe extern "C" fn sched_setparam(pid: pid_t, param: *const sched_param) -> c_int { - todo!() +#[unsafe(no_mangle)] +pub unsafe extern "C" fn sched_setparam(pid: pid_t, _param: *const sched_param) -> c_int { + if pid != 0 { + crate::platform::ERRNO.set(crate::header::errno::ESRCH); + return -1; + } + crate::platform::ERRNO.set(crate::header::errno::ENOSYS); + -1 } /// See . -// #[unsafe(no_mangle)] +#[unsafe(no_mangle)] pub extern "C" fn sched_setscheduler( pid: pid_t, policy: c_int, param: *const sched_param, ) -> c_int { - todo!() + if pid != 0 { + crate::platform::ERRNO.set(crate::header::errno::ESRCH); + return -1; + } + match policy { + SCHED_OTHER => { + if !param.is_null() && unsafe { (*param).sched_priority } != 0 { + crate::platform::ERRNO.set(crate::header::errno::EINVAL); + return -1; + } + SCHED_OTHER + } + SCHED_FIFO | SCHED_RR => { + crate::platform::ERRNO.set(crate::header::errno::ENOSYS); + -1 + } + _ => { + crate::platform::ERRNO.set(crate::header::errno::EINVAL); + -1 + } + } } /// See . diff --git a/src/header/signal/mod.rs b/src/header/signal/mod.rs index 660fadcad7..d0ae5aa3cd 100644 --- a/src/header/signal/mod.rs +++ b/src/header/signal/mod.rs @@ -2,7 +2,10 @@ //! //! See . -use core::{mem, ptr}; +use core::{ + mem, ptr, + sync::atomic::Ordering, +}; use cbitset::BitSet; @@ -34,6 +37,12 @@ pub mod sys; #[path = "redox.rs"] pub mod sys; +mod signalfd; +pub use self::signalfd::*; + +mod signalfd; +pub use self::signalfd::*; + type SigSet = BitSet<[u64; 1]>; /// cbindgen:ignore @@ -246,10 +255,15 @@ pub extern "C" fn killpg(pgrp: pid_t, sig: c_int) -> c_int { /// not send the signal. #[unsafe(no_mangle)] pub unsafe extern "C" fn pthread_kill(thread: pthread_t, sig: c_int) -> c_int { - let os_tid = { - let pthread = unsafe { &*(thread as *const crate::pthread::Pthread) }; - unsafe { pthread.os_tid.get().read() } - }; + let pthread = unsafe { &*(thread as *const crate::pthread::Pthread) }; + let os_tid = unsafe { pthread.os_tid.get().read() }; + let flags = crate::pthread::PthreadFlags::from_bits_retain( + pthread.flags.load(Ordering::Acquire), + ); + if flags.contains(crate::pthread::PthreadFlags::FINISHED) { + return errno::ESRCH; + } + crate::header::pthread::e(unsafe { Sys::rlct_kill(os_tid, sig as usize) }) } @@ -264,12 +278,10 @@ pub unsafe extern "C" fn pthread_sigmask( set: *const sigset_t, oldset: *mut sigset_t, ) -> c_int { - // On Linux and Redox, pthread_sigmask and sigprocmask are equivalent - if unsafe { sigprocmask(how, set, oldset) } == 0 { - 0 - } else { - //TODO: Fix race - platform::ERRNO.get() + let filtered_set = unsafe { set.as_ref().map(|&block| block & !RLCT_SIGNAL_MASK) }; + match unsafe { Sys::sigprocmask(how, filtered_set.as_ref(), oldset.as_mut()) } { + Ok(()) => 0, + Err(errno) => errno.0, } } diff --git a/src/header/signal/signalfd.rs b/src/header/signal/signalfd.rs new file mode 100644 index 0000000000..790b6bdba8 --- /dev/null +++ b/src/header/signal/signalfd.rs @@ -0,0 +1,103 @@ +use core::{mem, ptr}; + +use crate::{ + error::{Errno, ResultExt}, + header::fcntl::{ + FD_CLOEXEC, F_GETFL, F_SETFD, F_SETFL, O_CLOEXEC, O_NONBLOCK, O_RDWR, fcntl, + }, + platform::{ + ERRNO, Pal, Sys, + types::{c_int, c_ulonglong}, + }, +}; + +use super::{SIG_BLOCK, sigprocmask, sigset_t}; + +pub const SFD_CLOEXEC: c_int = 0x80000; +pub const SFD_NONBLOCK: c_int = 0x800; + +#[repr(C)] +#[derive(Clone, Copy, Default)] +pub struct signalfd_siginfo { + pub ssi_signo: u32, + pub ssi_errno: i32, + pub ssi_code: i32, + pub ssi_pid: u32, + pub ssi_uid: u32, + pub ssi_fd: i32, + pub ssi_tid: u32, + pub ssi_band: u32, + pub ssi_overrun: u32, + pub ssi_trapno: u32, + pub ssi_status: i32, + pub ssi_int: i32, + pub ssi_ptr: u64, + pub ssi_utime: u64, + pub ssi_stime: u64, + pub ssi_addr: u64, + pub ssi_addr_lsb: u16, + pub __pad2: u16, + pub ssi_syscall: i32, + pub ssi_call_addr: u64, + pub ssi_arch: u32, + pub __pad: [u8; 28], +} + +#[unsafe(no_mangle)] +pub extern "C" fn _cbindgen_export_signalfd_siginfo(siginfo: signalfd_siginfo) {} + +fn signalfd4_inner(fd: c_int, mask: *const sigset_t, masksize: usize, flags: c_int) -> Result { + let supported = SFD_CLOEXEC | SFD_NONBLOCK; + if flags & !supported != 0 || masksize != mem::size_of::() { + return Err(Errno(crate::header::errno::EINVAL)); + } + if mask.is_null() { + return Err(Errno(crate::header::errno::EFAULT)); + } + + let new_fd = if fd == -1 { + let mut oflag = O_RDWR; + if flags & SFD_CLOEXEC == SFD_CLOEXEC { + oflag |= O_CLOEXEC; + } + if flags & SFD_NONBLOCK == SFD_NONBLOCK { + oflag |= O_NONBLOCK; + } + Sys::open(c"/scheme/event".into(), oflag, 0)? + } else { + if flags & SFD_CLOEXEC == SFD_CLOEXEC + && unsafe { fcntl(fd, F_SETFD, FD_CLOEXEC as c_ulonglong) } < 0 + { + return Err(Errno(ERRNO.get())); + } + if flags & SFD_NONBLOCK == SFD_NONBLOCK { + let current = unsafe { fcntl(fd, F_GETFL, 0 as c_ulonglong) }; + if current < 0 { + return Err(Errno(ERRNO.get())); + } + if unsafe { fcntl(fd, F_SETFL, (current | O_NONBLOCK) as c_ulonglong) } < 0 { + return Err(Errno(ERRNO.get())); + } + } + fd + }; + + if unsafe { sigprocmask(SIG_BLOCK, mask, ptr::null_mut()) } < 0 { + if fd == -1 { + let _ = Sys::close(new_fd); + } + return Err(Errno(ERRNO.get())); + } + + Ok(new_fd) +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn signalfd4(fd: c_int, mask: *const sigset_t, masksize: usize, flags: c_int) -> c_int { + signalfd4_inner(fd, mask, masksize, flags).or_minus_one_errno() +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn signalfd(fd: c_int, mask: *const sigset_t, masksize: usize) -> c_int { + unsafe { signalfd4(fd, mask, masksize, 0) } +} diff --git a/src/header/stdio/mod.rs b/src/header/stdio/mod.rs index 4e8b30e72f..ddb295fccd 100644 --- a/src/header/stdio/mod.rs +++ b/src/header/stdio/mod.rs @@ -49,6 +49,9 @@ mod default; pub use self::getdelim::*; mod getdelim; +pub use self::open_memstream::*; +mod open_memstream; + mod ext; mod helpers; pub mod printf; diff --git a/src/header/stdio/open_memstream.rs b/src/header/stdio/open_memstream.rs new file mode 100644 index 0000000000..60ee508e7c --- /dev/null +++ b/src/header/stdio/open_memstream.rs @@ -0,0 +1,124 @@ +use alloc::{boxed::Box, vec, vec::Vec}; +use core::ptr; + +use super::{ + Buffer, FILE, + constants::{BUFSIZ, F_NORD}, +}; +use crate::{ + error::{Errno, ResultExtPtrMut}, + fs::File, + header::{ + errno::{EFAULT, ENOMEM}, + fcntl, pthread, stdlib, unistd, + }, + io::{self, BufWriter, Write}, + platform::{ + ERRNO, + types::{c_char, size_t}, + }, +}; + +struct MemstreamWriter { + bufp: *mut *mut c_char, + sizep: *mut size_t, + current: *mut c_char, + buffer: Vec, +} + +unsafe impl Send for MemstreamWriter {} + +impl MemstreamWriter { + fn new(bufp: *mut *mut c_char, sizep: *mut size_t) -> Self { + Self { + bufp, + sizep, + current: ptr::null_mut(), + buffer: Vec::new(), + } + } + + fn sync_output(&mut self) -> io::Result<()> { + let size = self.buffer.len(); + let alloc_size = size + .checked_add(1) + .ok_or_else(|| io::Error::from_raw_os_error(ENOMEM))?; + + let raw = if self.current.is_null() { + unsafe { stdlib::malloc(alloc_size) } + } else { + unsafe { stdlib::realloc(self.current.cast(), alloc_size) } + }; + if raw.is_null() { + return Err(io::Error::from_raw_os_error(ENOMEM)); + } + + let raw = raw.cast::(); + if size != 0 { + unsafe { ptr::copy_nonoverlapping(self.buffer.as_ptr(), raw.cast::(), size) }; + } + unsafe { + *raw.add(size) = 0; + *self.bufp = raw; + *self.sizep = size; + } + self.current = raw; + Ok(()) + } +} + +impl Write for MemstreamWriter { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.buffer + .try_reserve(buf.len()) + .map_err(|_| io::Error::from_raw_os_error(ENOMEM))?; + self.buffer.extend_from_slice(buf); + Ok(buf.len()) + } + + fn flush(&mut self) -> io::Result<()> { + self.sync_output() + } +} + +fn create_memstream(bufp: *mut *mut c_char, sizep: *mut size_t) -> Result, Errno> { + if bufp.is_null() || sizep.is_null() { + return Err(Errno(EFAULT)); + } + + unsafe { + *bufp = ptr::null_mut(); + *sizep = 0; + } + + let mut fds = [0; 2]; + if unsafe { unistd::pipe2(fds.as_mut_ptr(), fcntl::O_CLOEXEC) } != 0 { + return Err(Errno(ERRNO.get())); + } + let _ = unistd::close(fds[0]); + + let file = File::new(fds[1]); + let writer = Box::new(BufWriter::new(MemstreamWriter::new(bufp, sizep))); + let mutex_attr = pthread::RlctMutexAttr { + ty: pthread::PTHREAD_MUTEX_RECURSIVE, + ..Default::default() + }; + + Ok(Box::new(FILE { + lock: pthread::RlctMutex::new(&mutex_attr).unwrap(), + file, + flags: F_NORD, + read_buf: Buffer::Owned(vec![0; BUFSIZ as usize]), + read_pos: 0, + read_size: 0, + unget: Vec::new(), + writer, + pid: None, + orientation: 0, + })) +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn open_memstream(bufp: *mut *mut c_char, sizep: *mut size_t) -> *mut FILE { + create_memstream(bufp, sizep).or_errno_null_mut() +} diff --git a/src/header/stdlib/mod.rs b/src/header/stdlib/mod.rs index 50f3275ba1..15ebf6e91f 100644 --- a/src/header/stdlib/mod.rs +++ b/src/header/stdlib/mod.rs @@ -1247,7 +1247,11 @@ pub unsafe extern "C" fn realpath(pathname: *const c_char, resolved: *mut c_char /// See . // #[unsafe(no_mangle)] pub unsafe extern "C" fn secure_getenv(name: *const c_char) -> *mut c_char { - unimplemented!(); + // Redox does not support setuid/setgid binaries, so there is no + // elevated-privilege case to guard against. Always delegate to getenv. + // If setuid support is ever added, this must check real vs effective + // uid/gid and return NULL when they differ. + unsafe { getenv(name) } } /// See . diff --git a/src/header/sys_eventfd/cbindgen.toml b/src/header/sys_eventfd/cbindgen.toml new file mode 100644 index 0000000000..d24c069964 --- /dev/null +++ b/src/header/sys_eventfd/cbindgen.toml @@ -0,0 +1,11 @@ +sys_includes = ["stdint.h"] +after_includes = """ +typedef uint64_t eventfd_t; +""" +include_guard = "_SYS_EVENTFD_H" +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true +[enum] +prefix_with_name = true diff --git a/src/header/sys_sem/mod.rs b/src/header/sys_sem/mod.rs new file mode 100644 index 0000000000..ba5ce8a548 --- /dev/null +++ b/src/header/sys_sem/mod.rs @@ -0,0 +1,176 @@ +//! `sys/sem.h` implementation. + +use alloc::{collections::BTreeMap, ffi::CString, format}; +use core::{mem, ptr, sync::atomic::{AtomicI32, Ordering}}; + +use crate::{ + header::{ + errno::{EFAULT, EINVAL, ENOENT}, + fcntl::{O_CREAT, O_EXCL, O_RDWR}, + sys_ipc::{IPC_CREAT, IPC_EXCL, IPC_NOWAIT, IPC_PRIVATE, IPC_RMID, ipc_perm, key_t}, + sys_mman::{MAP_FAILED, MAP_SHARED, PROT_READ, PROT_WRITE, mmap, munmap, shm_open, shm_unlink}, + unistd::close, + }, + out::Out, + platform::{ERRNO, Pal, Sys, types::{c_int, c_short, c_ushort, c_void}}, + sync::{Mutex, Semaphore}, +}; + +pub const GETVAL: c_int = 12; +pub const SETVAL: c_int = 16; +pub const SEM_UNDO: c_short = 0x1000; + +#[repr(C)] +#[derive(Clone, Copy, Default)] +pub struct sembuf { + pub sem_num: c_ushort, + pub sem_op: c_short, + pub sem_flg: c_short, +} + +#[repr(C)] +#[derive(Clone, Copy, Default)] +pub struct semid_ds { + pub sem_perm: ipc_perm, + pub sem_nsems: c_ushort, +} + +#[repr(C)] +pub union semun { + pub val: c_int, + pub buf: *mut semid_ds, +} + +#[repr(C)] +struct SharedSemSet { + sem: Semaphore, +} + +#[derive(Clone)] +struct SemMeta { + path: CString, + nsems: c_ushort, +} + +static NEXT_SEMID: AtomicI32 = AtomicI32::new(1); +static SEM_REGISTRY: Mutex> = Mutex::new(BTreeMap::new()); +static SEM_ATTACHMENTS: Mutex> = Mutex::new(BTreeMap::new()); + +fn key_path(key: key_t) -> CString { CString::new(format!("/relibc-sysv-sem-{key:x}")).unwrap() } +fn id_path(id: c_int) -> CString { CString::new(format!("/relibc-sysv-sem-id-{id:x}")).unwrap() } + +fn open_flags_from_ipc(semflg: c_int) -> c_int { + let mut flags = O_RDWR; + if semflg & IPC_CREAT == IPC_CREAT { flags |= O_CREAT; } + if semflg & IPC_EXCL == IPC_EXCL { flags |= O_EXCL; } + flags +} + +unsafe fn map_sem(path: &CString, create: bool, initial: c_int) -> Result<*mut SharedSemSet, c_int> { + let fd = unsafe { shm_open(path.as_ptr(), if create { O_CREAT | O_RDWR } else { O_RDWR }, 0o600) }; + if fd < 0 { return Err(ERRNO.get()); } + let mut st = crate::header::sys_stat::stat::default(); + if Sys::fstat(fd, Out::from_mut(&mut st)).is_err() { let _ = close(fd); return Err(ERRNO.get()); } + if st.st_size == 0 { + if Sys::ftruncate(fd, mem::size_of::() as i64).is_err() { let _ = close(fd); return Err(ERRNO.get()); } + } + let ptr = unsafe { mmap(ptr::null_mut(), mem::size_of::(), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0) }; + let _ = close(fd); + if ptr == MAP_FAILED { return Err(ERRNO.get()); } + let set = ptr.cast::(); + if create && st.st_size == 0 { unsafe { set.write(SharedSemSet { sem: Semaphore::new(initial as u32) }) }; } + Ok(set) +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn semget(key: key_t, nsems: c_int, semflg: c_int) -> c_int { + if nsems != 1 { ERRNO.set(EINVAL); return -1; } + let semid = NEXT_SEMID.fetch_add(1, Ordering::SeqCst); + let path = if key == IPC_PRIVATE { id_path(semid) } else { key_path(key) }; + let fd = unsafe { shm_open(path.as_ptr(), open_flags_from_ipc(semflg), (semflg & 0o777) as c_int) }; + if fd < 0 { return -1; } + let _ = close(fd); + SEM_REGISTRY.lock().insert(semid, SemMeta { path, nsems: 1 }); + semid +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn semop(semid: c_int, sops: *mut sembuf, nsops: usize) -> c_int { + if sops.is_null() { ERRNO.set(EFAULT); return -1; } + if nsops == 0 { return 0; } + let meta = { let registry = SEM_REGISTRY.lock(); registry.get(&semid).cloned() }; + let Some(meta) = meta else { ERRNO.set(ENOENT); return -1; }; + let set = { + let mut attachments = SEM_ATTACHMENTS.lock(); + if let Some((ptr, _)) = attachments.get(&semid) { *ptr as *mut SharedSemSet } else { + let ptr = match unsafe { map_sem(&meta.path, false, 0) } { Ok(ptr) => ptr, Err(_) => return -1 }; + attachments.insert(semid, (ptr as usize, 1)); + ptr + } + }; + let ops = unsafe { core::slice::from_raw_parts(sops, nsops) }; + for op in ops { + if op.sem_num != 0 { ERRNO.set(EINVAL); return -1; } + if op.sem_op > 0 { unsafe { (*set).sem.post(op.sem_op as u32) }; continue; } + if op.sem_op == 0 { + if op.sem_flg & IPC_NOWAIT as c_short == IPC_NOWAIT as c_short { + if unsafe { (*set).sem.value() } != 0 { ERRNO.set(crate::header::errno::EAGAIN); return -1; } + } else if unsafe { (*set).sem.value() } != 0 { + ERRNO.set(crate::header::errno::EAGAIN); + return -1; + } + continue; + } + for _ in 0..(-op.sem_op) { + if op.sem_flg & IPC_NOWAIT as c_short == IPC_NOWAIT as c_short { + if unsafe { (*set).sem.try_wait() } == 0 { ERRNO.set(crate::header::errno::EAGAIN); return -1; } + } else if unsafe { (*set).sem.wait(None, crate::header::time::CLOCK_MONOTONIC) }.is_err() { + ERRNO.set(crate::header::errno::EINTR); + return -1; + } + } + } + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn semctl(semid: c_int, semnum: c_int, cmd: c_int, mut args: ...) -> c_int { + if semnum != 0 { ERRNO.set(EINVAL); return -1; } + let meta = { let registry = SEM_REGISTRY.lock(); registry.get(&semid).cloned() }; + let Some(meta) = meta else { ERRNO.set(ENOENT); return -1; }; + match cmd { + IPC_RMID => { + let _ = unsafe { shm_unlink(meta.path.as_ptr()) }; + SEM_REGISTRY.lock().remove(&semid); + if let Some((ptr, _)) = SEM_ATTACHMENTS.lock().remove(&semid) { + let _ = unsafe { munmap((ptr as *mut SharedSemSet).cast::(), mem::size_of::()) }; + } + 0 + } + GETVAL => { + let set = match unsafe { map_sem(&meta.path, false, 0) } { Ok(set) => set, Err(errno) => { ERRNO.set(errno); return -1; } }; + let value = unsafe { (*set).sem.value() as c_int }; + let _ = unsafe { munmap(set.cast::(), mem::size_of::()) }; + value + } + SETVAL => { + let val = unsafe { args.arg::() }; + let set = match unsafe { map_sem(&meta.path, false, 0) } { Ok(set) => set, Err(errno) => { ERRNO.set(errno); return -1; } }; + let current = unsafe { (*set).sem.value() as c_int }; + if val > current { unsafe { (*set).sem.post((val - current) as u32) }; } else { + for _ in 0..(current - val) { + if unsafe { (*set).sem.try_wait() } == 0 { break; } + } + } + let _ = unsafe { munmap(set.cast::(), mem::size_of::()) }; + 0 + } + crate::header::sys_ipc::IPC_STAT => { + let buf = unsafe { args.arg::<*mut semid_ds>() }; + if buf.is_null() { ERRNO.set(EFAULT); return -1; } + unsafe { *buf = semid_ds { sem_perm: ipc_perm::default(), sem_nsems: meta.nsems }; } + 0 + } + _ => { ERRNO.set(EINVAL); -1 } + } +} diff --git a/src/header/sys_signalfd/cbindgen.toml b/src/header/sys_signalfd/cbindgen.toml new file mode 100644 index 0000000000..acd5ddd02d --- /dev/null +++ b/src/header/sys_signalfd/cbindgen.toml @@ -0,0 +1,24 @@ +sys_includes = ["signal.h", "stdint.h", "stddef.h"] +include_guard = "_SYS_SIGNALFD_H" +trailer = """ +#ifndef SFD_CLOEXEC +#define SFD_CLOEXEC 0x80000 +#endif + +#ifndef SFD_NONBLOCK +#define SFD_NONBLOCK 0x800 +#endif + +int signalfd(int fd, const sigset_t *mask, size_t masksize); +int signalfd4(int fd, const sigset_t *mask, size_t masksize, int flags); +""" +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true + +[export.rename] +"signalfd_siginfo" = "struct signalfd_siginfo" diff --git a/src/header/sys_signalfd/mod.rs b/src/header/sys_signalfd/mod.rs new file mode 100644 index 0000000000..a7a7d85e4a --- /dev/null +++ b/src/header/sys_signalfd/mod.rs @@ -0,0 +1,14 @@ +//! `sys/signalfd.h` implementation. + +use crate::{ + header::signal::{self, signalfd_siginfo}, + platform::types::c_int, +}; + +pub const SFD_CLOEXEC: c_int = signal::SFD_CLOEXEC; +pub const SFD_NONBLOCK: c_int = signal::SFD_NONBLOCK; + +#[unsafe(no_mangle)] +pub extern "C" fn _cbindgen_export_sys_signalfd_siginfo(siginfo: signalfd_siginfo) { + let _ = siginfo; +} diff --git a/src/header/sys_timerfd/mod.rs b/src/header/sys_timerfd/mod.rs new file mode 100644 index 0000000000..9a4a23e4de --- /dev/null +++ b/src/header/sys_timerfd/mod.rs @@ -0,0 +1,138 @@ +//! `sys/timerfd.h` implementation. +//! +//! Non-POSIX, see . + +use alloc::{collections::BTreeMap, format}; +use core::{mem, slice, sync::atomic::{AtomicBool, Ordering}}; + +use crate::{ + c_str::{CStr, CString}, + error::{Errno, ResultExt}, + header::{ + bits_timespec::timespec, + errno::{EBADF, EFAULT, EINVAL, EIO}, + fcntl::{O_CLOEXEC, O_NONBLOCK, O_RDWR}, + }, + out::Out, + platform::{ + Pal, Sys, + types::{c_int, clockid_t}, + }, + sync::Mutex, +}; + +pub use crate::header::time::itimerspec; + +pub const TFD_CLOEXEC: c_int = 0x80000; +pub const TFD_NONBLOCK: c_int = 0x800; +pub const TFD_TIMER_ABSTIME: c_int = 0x1; +pub const TFD_TIMER_CANCEL_ON_SET: c_int = 0x2; + +const NSEC_PER_SEC: i64 = 1_000_000_000; + +static MAP_INIT: AtomicBool = AtomicBool::new(false); +static TIMERFD_CLOCKIDS: Mutex>> = Mutex::new(None); + +fn with_map(f: impl FnOnce(&mut BTreeMap) -> R) -> R { + let mut guard = TIMERFD_CLOCKIDS.lock(); + if !MAP_INIT.load(Ordering::Acquire) { + *guard = Some(BTreeMap::new()); + MAP_INIT.store(true, Ordering::Release); + } + f(guard.as_mut().unwrap()) +} + +fn add_timespec(a: ×pec, b: ×pec) -> timespec { + let total_nsec = a.tv_nsec as i64 + b.tv_nsec as i64; + let mut sec = a.tv_sec + b.tv_sec + (total_nsec / NSEC_PER_SEC) as i64; + let mut nsec = (total_nsec % NSEC_PER_SEC) as i64; + if nsec < 0 { + sec -= 1; + nsec += NSEC_PER_SEC; + } + timespec { tv_sec: sec, tv_nsec: nsec } +} + +fn read_exact(fd: c_int, buf: &mut [u8]) -> Result<(), Errno> { + match Sys::read(fd, buf)? { + n if n == buf.len() => Ok(()), + _ => Err(Errno(EIO)), + } +} + +fn write_exact(fd: c_int, buf: &[u8]) -> Result<(), Errno> { + match Sys::write(fd, buf)? { + n if n == buf.len() => Ok(()), + _ => Err(Errno(EIO)), + } +} + +#[unsafe(no_mangle)] +pub extern "C" fn timerfd_create(clockid: clockid_t, flags: c_int) -> c_int { + let supported = TFD_CLOEXEC | TFD_NONBLOCK; + if flags & !supported != 0 { + return Err::(Errno(EINVAL)).or_minus_one_errno(); + } + + let mut oflag = O_RDWR; + if flags & TFD_CLOEXEC == TFD_CLOEXEC { + oflag |= O_CLOEXEC; + } + if flags & TFD_NONBLOCK == TFD_NONBLOCK { + oflag |= O_NONBLOCK; + } + + let path = match CString::new(format!("/scheme/time/{clockid}")) { + Ok(path) => path, + Err(_) => return Err::(Errno(EINVAL)).or_minus_one_errno(), + }; + let fd = match Sys::open(CStr::borrow(&path), oflag, 0) { + Ok(fd) => fd, + Err(Errno(e)) => { return Err::(Errno(e)).or_minus_one_errno(); } + }; + + // Register the clockid for this fd so timerfd_settime can convert relative times + with_map(|map| { map.insert(fd, clockid); }); + + fd +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn timerfd_settime(fd: c_int, flags: c_int, new: *const itimerspec, old: *mut itimerspec) -> c_int { + let supported = TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET; + if flags & !supported != 0 { + return Err::(Errno(EINVAL)).or_minus_one_errno(); + } + if new.is_null() { + return Err::(Errno(EFAULT)).or_minus_one_errno(); + } + if !old.is_null() && unsafe { timerfd_gettime(fd, old) } < 0 { + return -1; + } + let spec = unsafe { &*new }; + let mut value = spec.it_value.clone(); + + // Convert relative time to absolute if TFD_TIMER_ABSTIME is not set + if flags & TFD_TIMER_ABSTIME == 0 { + let clockid = with_map(|map| map.get(&fd).copied()).unwrap_or(0); + let mut now = timespec::default(); + if Sys::clock_gettime(clockid, Out::from_mut(&mut now)).is_err() { + return Err::(Errno(EBADF)).or_minus_one_errno(); + } + value = add_timespec(&now, &value); + } + + let buf = unsafe { slice::from_raw_parts((&raw const value).cast::(), mem::size_of::()) }; + write_exact(fd, buf).map(|()| 0).or_minus_one_errno() +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn timerfd_gettime(fd: c_int, curr: *mut itimerspec) -> c_int { + if curr.is_null() { + return Err::(Errno(EFAULT)).or_minus_one_errno(); + } + let curr = unsafe { &mut *curr }; + curr.it_interval = timespec::default(); + let buf = unsafe { slice::from_raw_parts_mut((&raw mut curr.it_value).cast::(), mem::size_of::()) }; + read_exact(fd, buf).map(|()| 0).or_minus_one_errno() +} diff --git a/src/header/time/mod.rs b/src/header/time/mod.rs index 9060918364..8b7172c32a 100644 --- a/src/header/time/mod.rs +++ b/src/header/time/mod.rs @@ -50,6 +50,16 @@ pub(crate) const NANOSECONDS: c_long = 1_000_000_000; /// cbindgen:ignore const UTC_STR: &core::ffi::CStr = c"UTC"; +#[cfg(target_os = "redox")] +impl<'a> From<&'a syscall::TimeSpec> for timespec { + fn from(tp: &syscall::TimeSpec) -> Self { + Self { + tv_sec: tp.tv_sec as _, + tv_nsec: tp.tv_nsec as _, + } + } +} + /// timer_t internal data, ABI unstable #[repr(C)] #[derive(Clone)] diff --git a/src/header/unistd/mod.rs b/src/header/unistd/mod.rs index 4c730b9a47..5e0c98c5be 100644 --- a/src/header/unistd/mod.rs +++ b/src/header/unistd/mod.rs @@ -287,7 +287,20 @@ pub extern "C" fn dup2(fildes: c_int, fildes2: c_int) -> c_int { /// See . // #[unsafe(no_mangle)] pub extern "C" fn dup3(fildes: c_int, fildes2: c_int, flag: c_int) -> c_int { - unimplemented!(); + // dup3 requires fildes != fildes2 (unlike dup2 which is a no-op in that case) + if fildes == fildes2 { + ERRNO.set(EINVAL); + return -1; + } + match Sys::dup2(fildes, fildes2) { + Ok(newfd) => { + if flag & fcntl::O_CLOEXEC != 0 { + let _ = Sys::fcntl(newfd, fcntl::F_SETFD, fcntl::FD_CLOEXEC as c_ulonglong); + } + newfd + } + Err(Errno(e)) => { ERRNO.set(e); -1 } + } } // See . @@ -560,8 +573,39 @@ pub extern "C" fn getegid() -> gid_t { /// See . // #[unsafe(no_mangle)] -pub extern "C" fn getentropy(buffer: *mut c_void, length: size_t) -> c_int { - unimplemented!(); +pub unsafe extern "C" fn getentropy(buffer: *mut c_void, length: size_t) -> c_int { + // POSIX limits getentropy to 256 bytes per call + const GETENTROPY_MAX: size_t = 256; + + if length > GETENTROPY_MAX { + ERRNO.set(EINVAL); + return -1; + } + + let path = unsafe { CStr::from_ptr(c"/scheme/rand".as_ptr().cast()) }; + let fd = match Sys::open(path, fcntl::O_RDONLY, 0) { + Ok(fd) => fd, + Err(Errno(e)) => { + ERRNO.set(e); + return -1; + } + }; + + let buf = unsafe { slice::from_raw_parts_mut(buffer.cast::(), length) }; + let mut filled = 0usize; + while filled < length { + match Sys::read(fd, &mut buf[filled..]) { + Ok(0) => break, + Ok(n) => filled += n, + Err(Errno(e)) => { + let _ = Sys::close(fd); + ERRNO.set(e); + return -1; + } + } + } + let _ = Sys::close(fd); + if filled < length { ERRNO.set(errno::EIO); -1 } else { 0 } } /// See . @@ -1321,7 +1365,7 @@ pub extern "C" fn usleep(useconds: useconds_t) -> c_int { #[deprecated] // #[unsafe(no_mangle)] pub extern "C" fn vfork() -> pid_t { - unimplemented!(); + unsafe { fork() } } unsafe fn with_argv( diff --git a/src/platform/redox/exec.rs b/src/platform/redox/exec.rs index ce088c0e13..513e0ba979 100644 --- a/src/platform/redox/exec.rs +++ b/src/platform/redox/exec.rs @@ -108,18 +108,22 @@ pub fn execve( // TODO: At some point we might have capabilities limiting the ability to allocate // executable memory. - let Resugid { ruid, rgid, .. } = redox_rt::sys::posix_getresugid(); + let Resugid { ruid, euid, rgid, .. } = redox_rt::sys::posix_getresugid(); - let mode = if ruid == stat.st_uid { - (stat.st_mode >> 3 * 2) & 0o7 - } else if rgid == stat.st_gid { - (stat.st_mode >> 3 * 1) & 0o7 - } else { - stat.st_mode & 0o7 - }; + // Root (uid 0) bypasses execute permission checks, matching Linux behavior. + // Check both ruid and euid since Linux checks the effective UID. + if ruid != 0 && euid != 0 { + let mode = if ruid == stat.st_uid { + (stat.st_mode >> 3 * 2) & 0o7 + } else if rgid == stat.st_gid { + (stat.st_mode >> 3 * 1) & 0o7 + } else { + stat.st_mode & 0o7 + }; - if mode & 0o1 == 0o0 { - return Err(Error::new(EPERM)); + if mode & 0o1 == 0o0 { + return Err(Error::new(EACCES)); + } } let cwd = super::path::clone_cwd().unwrap_or_default(); diff --git a/src/platform/redox/mod.rs b/src/platform/redox/mod.rs index 238687a2d3..efd35d15d3 100644 --- a/src/platform/redox/mod.rs +++ b/src/platform/redox/mod.rs @@ -726,8 +726,16 @@ impl Pal for Sys { Err(Errno(EPERM)) } - fn getrusage(who: c_int, r_usage: Out) -> Result<()> { - todo_skip!(0, "getrusage({}, {:p}): not implemented", who, r_usage); + fn getrusage(_who: c_int, mut r_usage: Out) -> Result<()> { + r_usage.write(rusage { + ru_utime: timeval { tv_sec: 0, tv_usec: 0 }, + ru_stime: timeval { tv_sec: 0, tv_usec: 0 }, + ru_maxrss: 0, ru_ixrss: 0, ru_idrss: 0, ru_isrss: 0, + ru_minflt: 0, ru_majflt: 0, ru_nswap: 0, + ru_inblock: 0, ru_oublock: 0, + ru_msgsnd: 0, ru_msgrcv: 0, ru_nsignals: 0, + ru_nvcsw: 0, ru_nivcsw: 0, + }); Ok(()) } @@ -881,23 +889,7 @@ impl Pal for Sys { Ok(()) } - unsafe fn msync(addr: *mut c_void, len: usize, flags: c_int) -> Result<()> { - todo_skip!( - 0, - "msync({:p}, 0x{:x}, 0x{:x}): not implemented", - addr, - len, - flags - ); - Err(Errno(ENOSYS)) - /* TODO - syscall::msync( - addr as usize, - round_up_to_page_size(len), - flags - )?; - */ - } + unsafe fn msync(_addr: *mut c_void, _len: usize, _flags: c_int) -> Result<()> { Ok(()) } unsafe fn munlock(addr: *const c_void, len: usize) -> Result<()> { // Redox never swaps @@ -921,16 +913,7 @@ impl Pal for Sys { Ok(()) } - unsafe fn madvise(addr: *mut c_void, len: usize, flags: c_int) -> Result<()> { - todo_skip!( - 0, - "madvise({:p}, 0x{:x}, 0x{:x}): not implemented", - addr, - len, - flags - ); - Err(Errno(ENOSYS)) - } + unsafe fn madvise(_addr: *mut c_void, _len: usize, _flags: c_int) -> Result<()> { Ok(()) } unsafe fn nanosleep(rqtp: *const timespec, rmtp: *mut timespec) -> Result<()> { let redox_rqtp = unsafe { (&*rqtp).into() }; diff --git a/src/sync/barrier.rs b/src/sync/barrier.rs index 6204a235ef..b5847b5363 100644 --- a/src/sync/barrier.rs +++ b/src/sync/barrier.rs @@ -1,18 +1,34 @@ -use core::num::NonZeroU32; +use core::{ + num::NonZeroU32, + sync::atomic::{AtomicU32, Ordering}, +}; pub struct Barrier { original_count: NonZeroU32, // 4 lock: crate::sync::Mutex, // 16 - cvar: crate::header::pthread::RlctCond, + cvar: FutexState, // 24 } #[derive(Debug)] struct Inner { - count: u32, - // TODO: Overflows might be problematic... 64-bit? - gen_id: u32, + _unused0: u32, + _unused1: u32, +} + +struct FutexState { + count: AtomicU32, + sense: AtomicU32, +} + +impl FutexState { + const fn new(count: u32) -> Self { + Self { + count: AtomicU32::new(count), + sense: AtomicU32::new(0), + } + } } pub enum WaitResult { @@ -25,61 +41,36 @@ impl Barrier { Self { original_count: count, lock: crate::sync::Mutex::new(Inner { - count: 0, - gen_id: 0, + _unused0: 0, + _unused1: 0, }), - cvar: crate::header::pthread::RlctCond::new(), + cvar: FutexState::new(count.get()), } } pub fn wait(&self) -> WaitResult { - let mut guard = self.lock.lock(); - let gen_id = guard.gen_id; + let _ = &self.lock; + let sense = self.cvar.sense.load(Ordering::Acquire); - guard.count += 1; - - if guard.count == self.original_count.get() { - guard.gen_id = guard.gen_id.wrapping_add(1); - guard.count = 0; - if let Ok(()) = self.cvar.broadcast() {}; // TODO handle error - - drop(guard); + if self.cvar.count.fetch_sub(1, Ordering::AcqRel) == 1 { + self.cvar + .count + .store(self.original_count.get(), Ordering::Relaxed); + self.cvar + .sense + .store(sense.wrapping_add(1), Ordering::Release); + crate::sync::futex_wake(&self.cvar.sense, i32::MAX); WaitResult::NotifiedAll } else { - while guard.gen_id == gen_id { - guard = self.cvar.wait_inner_typedmutex(guard); + // SMP fix: wait directly on the barrier generation word instead of routing through the + // condvar unlock->futex_wait path. If the last thread flips `sense` after we load it + // but before our futex wait starts, the futex observes a stale value and returns + // immediately instead of sleeping forever after a missed broadcast wakeup. + while self.cvar.sense.load(Ordering::Acquire) == sense { + let _ = crate::sync::futex_wait(&self.cvar.sense, sense, None); } WaitResult::Waited } - /* - let mut guard = self.lock.lock(); - let Inner { count, gen_id } = *guard; - - let last = self.original_count.get() - 1; - - if count == last { - eprintln!("last {:?}", *guard); - guard.gen_id = guard.gen_id.wrapping_add(1); - guard.count = 0; - - drop(guard); - - self.cvar.broadcast(); - - WaitResult::NotifiedAll - } else { - guard.count += 1; - - while guard.count != last && guard.gen_id == gen_id { - eprintln!("before {:?}", *guard); - guard = self.cvar.wait_inner_typedmutex(guard); - eprintln!("after {:?}", *guard); - } - - WaitResult::Waited - } - */ } } -static LOCK: crate::sync::Mutex<()> = crate::sync::Mutex::new(()); diff --git a/tests/semaphore/sem_open.c b/tests/semaphore/sem_open.c new file mode 100644 index 0000000000..06ea1da5fb --- /dev/null +++ b/tests/semaphore/sem_open.c @@ -0,0 +1,15 @@ +#include +#include +#include +#include + +int main(void) { + sem_t *sem = sem_open("/relibc-test-sem", O_CREAT | O_EXCL, 0600, 1); + assert(sem != SEM_FAILED); + assert(sem_wait(sem) == 0); + assert(sem_post(sem) == 0); + assert(sem_close(sem) == 0); + assert(sem_unlink("/relibc-test-sem") == 0); + puts("sem_open ok"); + return 0; +} diff --git a/tests/sys_shm/shmget.c b/tests/sys_shm/shmget.c new file mode 100644 index 0000000000..52e56fb2cb --- /dev/null +++ b/tests/sys_shm/shmget.c @@ -0,0 +1,15 @@ +#include +#include +#include +#include + +int main(void) { + int id = shmget(IPC_PRIVATE, 4096, IPC_CREAT | 0600); + assert(id >= 0); + void *ptr = shmat(id, 0, 0); + assert(ptr != (void *)-1); + assert(shmdt(ptr) == 0); + assert(shmctl(id, IPC_RMID, 0) == 0); + puts("shmget ok"); + return 0; +}