From bcce7e18db7efccd75ab40a2b9b5f9d367d70d63 Mon Sep 17 00:00:00 2001
From: 4lDO2 <4lDO2@protonmail.com>
Date: Mon, 15 Jul 2024 23:24:41 +0200
Subject: [PATCH] Implement SA_RESTART for read and write.

---
 redox-rt/src/arch/x86_64.rs |  1 +
 redox-rt/src/signal.rs      |  4 ++-
 redox-rt/src/sys.rs         |  6 +++++
 tests/Makefile              |  1 +
 tests/sa_restart.c          | 53 +++++++++++++++++++++++++++++++++++++
 5 files changed, 64 insertions(+), 1 deletion(-)
 create mode 100644 tests/sa_restart.c

diff --git a/redox-rt/src/arch/x86_64.rs b/redox-rt/src/arch/x86_64.rs
index 0b3ffa68..4e53aef3 100644
--- a/redox-rt/src/arch/x86_64.rs
+++ b/redox-rt/src/arch/x86_64.rs
@@ -26,6 +26,7 @@ pub struct SigArea {
     pub altstack_bottom: usize,
     pub disable_signals_depth: u64,
     pub pctl: usize, // TODO: find out how to correctly reference that static
+    pub last_sig_was_restart: bool,
 }
 
 #[repr(C, align(16))]
diff --git a/redox-rt/src/signal.rs b/redox-rt/src/signal.rs
index 3c21bde1..c55b6f31 100644
--- a/redox-rt/src/signal.rs
+++ b/redox-rt/src/signal.rs
@@ -44,7 +44,6 @@ unsafe fn inner(stack: &mut SigStack) {
 
     // asm counts from 0
     stack.sig_num += 1;
-
     arch_pre(stack, &mut *os.arch.get());
 
     let sigaction = {
@@ -61,6 +60,7 @@ unsafe fn inner(stack: &mut SigStack) {
         }
         action
     };
+    let shall_restart = sigaction.flags.contains(SigactionFlags::RESTART);
 
     let handler = match sigaction.kind {
         SigactionKind::Ignore => {
@@ -123,6 +123,8 @@ unsafe fn inner(stack: &mut SigStack) {
 
     //let _ = syscall::write(1, alloc::format!("will return to {:x?}\n", stack.regs.eip).as_bytes());
 
+    (*os.arch.get()).last_sig_was_restart = shall_restart;
+
     // And re-enable them again
     control_flags.store(control_flags.load(Ordering::Relaxed) & !SigcontrolFlags::INHIBIT_DELIVERY.bits(), Ordering::Release);
     core::sync::atomic::compiler_fence(Ordering::Acquire);
diff --git a/redox-rt/src/sys.rs b/redox-rt/src/sys.rs
index 8cc3a0db..d450a562 100644
--- a/redox-rt/src/sys.rs
+++ b/redox-rt/src/sys.rs
@@ -2,16 +2,22 @@ use syscall::error::{Result, Error, EINTR};
 
 use crate::arch::manually_enter_trampoline;
 use crate::signal::tmp_disable_signals;
+use crate::Tcb;
 
 #[inline]
 fn wrapper(mut f: impl FnMut() -> Result<usize>) -> Result<usize> {
     loop {
+        let _guard = tmp_disable_signals();
+        let rt_sigarea = unsafe { &Tcb::current().unwrap().os_specific };
         let res = f();
 
         if res == Err(Error::new(EINTR)) {
             unsafe {
                 manually_enter_trampoline();
             }
+            if unsafe { (*rt_sigarea.arch.get()).last_sig_was_restart } {
+                continue;
+            }
         }
 
         return res;
diff --git a/tests/Makefile b/tests/Makefile
index c27e6d85..2f4b35d1 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -31,6 +31,7 @@ EXPECT_NAMES=\
 	setjmp \
 	sigaction \
 	signal \
+	sa_restart \
 	stdio/all \
 	stdio/buffer \
 	stdio/fgets \
diff --git a/tests/sa_restart.c b/tests/sa_restart.c
new file mode 100644
index 00000000..75b08681
--- /dev/null
+++ b/tests/sa_restart.c
@@ -0,0 +1,53 @@
+#include <assert.h>
+#include <signal.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "test_helpers.h"
+
+void action(int sig, siginfo_t *info, void *context) {
+    assert (sig == SIGUSR1);
+    (void)info;
+    (void)context;
+    char *msg = "Signal handler\n";
+    write(1, msg, strlen(msg));
+    _exit(0);
+}
+
+int main(void) {
+    int status;
+
+    struct sigaction act;
+    act.sa_sigaction = action;
+    act.sa_flags = SA_RESTART;
+    sigemptyset(&act.sa_mask);
+
+    status = sigaction(SIGUSR1, &act, NULL);
+    ERROR_IF(sigaction, status, == -1);
+
+    int fds[2];
+    status = pipe(fds);
+    ERROR_IF(pipe, status, == -1);
+
+    int parent = getpid();
+
+    status = fork();
+    ERROR_IF(fork, status, == -1);
+
+    if (status == 0) {
+        for (int i = 0; i < 1000; i++) {
+            status = kill(parent, SIGUSR1);
+            ERROR_IF(kill, status, == -1);
+        }
+        return 0;
+    }
+
+    char buffer[1];
+    status = read(fds[0], buffer, 1);
+    ERROR_IF(read, status, == -1);
+
+    return 1;
+}
-- 
GitLab