From e0a38117369288ac9113e63fa28fe590603547cc Mon Sep 17 00:00:00 2001
From: 4lDO2 <4lDO2@protonmail.com>
Date: Wed, 27 Mar 2024 10:30:49 +0100
Subject: [PATCH] Protect fork() and clone() behind a rwlock.

rlct_clone will acquire a read lock, whereas fork will acquire a write
lock. The write lock is necessary because the fork will clone the file
table, which would result in other threads' fork/clone file descriptors
not being closed. If an address space switch fd never gets closed, fork
child processes and new threads, may never switch address spaces before
they are started, which has resulted in hard-to-debug RIP=0 instr fetch
page faults.
---
 src/platform/redox/clone.rs | 22 ++++++++++++++++++++++
 src/platform/redox/mod.rs   | 10 +++++++---
 src/sync/rwlock.rs          |  2 +-
 3 files changed, 30 insertions(+), 4 deletions(-)

diff --git a/src/platform/redox/clone.rs b/src/platform/redox/clone.rs
index 4bb4da8e..336c0937 100644
--- a/src/platform/redox/clone.rs
+++ b/src/platform/redox/clone.rs
@@ -7,6 +7,8 @@ use syscall::{
     SetSighandlerData, SIGCONT,
 };
 
+use crate::sync::rwlock::Rwlock;
+
 use super::{
     extra::{create_set_addr_space_buf, FdGuard},
     signal::sighandler_function,
@@ -14,6 +16,26 @@ use super::{
 
 pub use redox_exec::*;
 
+static CLONE_LOCK: Rwlock = Rwlock::new(crate::pthread::Pshared::Private);
+
+struct Guard;
+impl Drop for Guard {
+    fn drop(&mut self) {
+        CLONE_LOCK.unlock()
+    }
+}
+
+pub fn rdlock() -> impl Drop {
+    CLONE_LOCK.acquire_read_lock(None);
+
+    Guard
+}
+pub fn wrlock() -> impl Drop {
+    CLONE_LOCK.acquire_write_lock(None);
+
+    Guard
+}
+
 /// Spawns a new context sharing the same address space as the current one (i.e. a new thread).
 pub unsafe fn rlct_clone_impl(stack: *mut usize) -> Result<usize> {
     let cur_pid_fd = FdGuard::new(syscall::open("thisproc:current/open_via_dup", O_CLOEXEC)?);
diff --git a/src/platform/redox/mod.rs b/src/platform/redox/mod.rs
index 8023077a..36c844bc 100644
--- a/src/platform/redox/mod.rs
+++ b/src/platform/redox/mod.rs
@@ -277,7 +277,9 @@ impl Pal for Sys {
     }
 
     fn fork() -> pid_t {
-        e(clone::fork_impl()) as pid_t
+        let _guard = clone::wrlock();
+        let res = clone::fork_impl();
+        e(res) as pid_t
     }
 
     // FIXME: unsound
@@ -789,8 +791,10 @@ impl Pal for Sys {
     unsafe fn rlct_clone(
         stack: *mut usize,
     ) -> Result<crate::pthread::OsTid, crate::pthread::Errno> {
-        clone::rlct_clone_impl(stack)
-            .map(|context_id| crate::pthread::OsTid { context_id })
+        let _guard = clone::rdlock();
+        let res = clone::rlct_clone_impl(stack);
+
+        res.map(|context_id| crate::pthread::OsTid { context_id })
             .map_err(|error| crate::pthread::Errno(error.errno))
     }
     unsafe fn rlct_kill(
diff --git a/src/sync/rwlock.rs b/src/sync/rwlock.rs
index 30aae21c..fe066a17 100644
--- a/src/sync/rwlock.rs
+++ b/src/sync/rwlock.rs
@@ -16,7 +16,7 @@ const EXCLUSIVE: u32 = COUNT_MASK;
 // TODO: Add futex ops that use bitmasks.
 
 impl Rwlock {
-    pub fn new(_pshared: Pshared) -> Self {
+    pub const fn new(_pshared: Pshared) -> Self {
         Self {
             state: AtomicU32::new(0),
         }
-- 
GitLab