From: Waldemar Kozaczuk <jwkozac...@gmail.com>
Committer: WALDEMAR KOZACZUK <jwkozac...@gmail.com>
Branch: master

signals: make signal mask layout match Linux

In theory, it would not matter how we encode the information about
which signals are masked as long as our implementation of it stays
completely internal. But in reality, when we implement rt_sigprocmask
and other related syscalls, the users of those like glibc rely on
the format of the bitmask used by Linux kernel. In Linux, the least
significant bit (or the most right) represents the mask of the signal
number 1, the 2nd bit represents the mask of the signal number 2, etc
all the way to the maximum number 64. So we must ensure that our internal
sigset structure can fit all these 64 bits and nothing more as some
runtimes like golang assume the oldset argument of the rt_sigprocmask
can be as small as NSIG/8 (8 bytes).

To get or set a specific signal mask bit, we simply substract 1
from the signal number - <mask index> = signum - 1.

This patch in effect reverts the commit 0e13562f8ebb90f66fb1f24324099526f87d9dfc
and changes the nsignals constant back to the value 64. And besides
adjusting the code that accesses signal mask to match that of Linux (signo -1),
it also changes code that accesses signal_actions and waiters to
use the similar access pattern (signo - 1) to make all things consistent
and sane.

Fixes #1255

Signed-off-by: Waldemar Kozaczuk <jwkozac...@gmail.com>

---
diff --git a/libc/signal.cc b/libc/signal.cc
--- a/libc/signal.cc
+++ b/libc/signal.cc
@@ -69,6 +69,8 @@ inline bool is_sig_ign(const struct sigaction &sa) {
     return (!(sa.sa_flags & SA_SIGINFO) && sa.sa_handler == SIG_IGN);
 }
 
+//Similar to signal actions and mask, list of "waiters" for a given signal
+//with number "signo" is stored at the index "signo - 1"
 typedef std::list<sched::thread *> thread_list;
 static std::array<thread_list, nsignals> waiters;
 mutex waiters_mutex;
@@ -78,29 +80,30 @@ int wake_up_signal_waiters(int signo)
     SCOPE_LOCK(waiters_mutex);
     int woken = 0;
 
-    for (auto& t: waiters[signo]) {
+    unsigned sigidx = signo - 1;
+    for (auto& t: waiters[sigidx]) {
         woken++;
         t->remote_thread_local_var<int>(thread_pending_signal) = signo;
         t->wake();
     }
     return woken;
 }
 
-void wait_for_signal(int signo)
+void wait_for_signal(unsigned int sigidx)
 {
     SCOPE_LOCK(waiters_mutex);
-    waiters[signo].push_front(sched::thread::current());
+    waiters[sigidx].push_front(sched::thread::current());
 }
 
-void unwait_for_signal(sched::thread *t, int signo)
+void unwait_for_signal(sched::thread *t, unsigned int sigidx)
 {
     SCOPE_LOCK(waiters_mutex);
-    waiters[signo].remove(t);
+    waiters[sigidx].remove(t);
 }
 
-void unwait_for_signal(int signo)
+void unwait_for_signal(unsigned int sigidx)
 {
-    unwait_for_signal(sched::thread::current(), signo);
+    unwait_for_signal(sched::thread::current(), sigidx);
 }
 
 void __attribute__((constructor)) signals_register_thread_notifier()
@@ -120,7 +123,8 @@ void __attribute__((constructor)) 
signals_register_thread_notifier()
 
 void generate_signal(siginfo_t &siginfo, exception_frame* ef)
 {
-    if (pthread_self() && thread_signals()->mask[siginfo.si_signo]) {
+    unsigned sigidx = siginfo.si_signo - 1;
+    if (pthread_self() && thread_signals()->mask[sigidx]) {
         // FIXME: need to find some other thread to deliver
         // FIXME: the signal to.
         //
@@ -129,11 +133,11 @@ void generate_signal(siginfo_t &siginfo, exception_frame* 
ef)
         // needs to be running to generate them. So definitely not waiting.
         abort();
     }
-    if (is_sig_dfl(signal_actions[siginfo.si_signo])) {
+    if (is_sig_dfl(signal_actions[sigidx])) {
         // Our default is to abort the process
         abort();
-    } else if(!is_sig_ign(signal_actions[siginfo.si_signo])) {
-        arch::build_signal_frame(ef, siginfo, 
signal_actions[siginfo.si_signo]);
+    } else if(!is_sig_ign(signal_actions[sigidx])) {
+        arch::build_signal_frame(ef, siginfo, signal_actions[sigidx]);
     }
 }
 
@@ -166,21 +170,36 @@ int sigfillset(sigset_t *sigset)
 OSV_LIBC_API
 int sigaddset(sigset_t *sigset, int signum)
 {
-    from_libc(sigset)->mask.set(signum);
+    if (signum < 1 || signum > (int)nsignals) {
+        errno = EINVAL;
+        return -1;
+    }
+    unsigned sigidx = signum - 1;
+    from_libc(sigset)->mask.set(sigidx);
     return 0;
 }
 
 OSV_LIBC_API
 int sigdelset(sigset_t *sigset, int signum)
 {
-    from_libc(sigset)->mask.reset(signum);
+    if (signum < 1 || signum > (int)nsignals) {
+        errno = EINVAL;
+        return -1;
+    }
+    unsigned sigidx = signum - 1;
+    from_libc(sigset)->mask.reset(sigidx);
     return 0;
 }
 
 OSV_LIBC_API
 int sigismember(const sigset_t *sigset, int signum)
 {
-    return from_libc(sigset)->mask.test(signum);
+    if (signum < 1 || signum > (int)nsignals) {
+        errno = EINVAL;
+        return -1;
+    }
+    unsigned sigidx = signum - 1;
+    return from_libc(sigset)->mask.test(sigidx);
 }
 
 OSV_LIBC_API
@@ -231,15 +250,16 @@ OSV_LIBC_API
 int sigaction(int signum, const struct sigaction* act, struct sigaction* 
oldact)
 {
     // FIXME: We do not support any sa_flags besides SA_SIGINFO.
-    if (signum < 0 || signum >= (int)nsignals) {
+    if (signum < 1 || signum > (int)nsignals) {
         errno = EINVAL;
         return -1;
     }
+    unsigned sigidx = signum - 1;
     if (oldact) {
-        *oldact = signal_actions[signum];
+        *oldact = signal_actions[sigidx];
     }
     if (act) {
-        signal_actions[signum] = *act;
+        signal_actions[sigidx] = *act;
     }
     return 0;
 }
@@ -282,6 +302,10 @@ sighandler_t __sysv_signal(int signum, sighandler_t 
handler)
 OSV_LIBC_API
 int sigignore(int signum)
 {
+    if (signum < 1 || signum > (int)nsignals) {
+        errno = EINVAL;
+        return -1;
+    }
     struct sigaction act;
     act.sa_flags = 0;
     sigemptyset(&act.sa_mask);
@@ -333,16 +357,17 @@ int kill(pid_t pid, int sig)
         // testing the pid.
         return 0;
     }
-    if (sig < 0 || sig >= (int)nsignals) {
+    if (sig < 0 || sig > (int)nsignals) {
         errno = EINVAL;
         return -1;
     }
-    if (is_sig_dfl(signal_actions[sig])) {
+    unsigned sigidx = sig - 1;
+    if (is_sig_dfl(signal_actions[sigidx])) {
         // Our default is to power off.
         debug("Uncaught signal %d (\"%s\"). Powering off.\n",
                 sig, strsignal(sig));
         osv::poweroff();
-    } else if(!is_sig_ign(signal_actions[sig])) {
+    } else if(!is_sig_ign(signal_actions[sigidx])) {
         if ((pid == OSV_PID) || (pid == 0) || (pid == -1)) {
             // This semantically means signalling everybody. So we will signal
             // every thread that is waiting for this.
@@ -362,11 +387,11 @@ int kill(pid_t pid, int sig)
         // The newly created thread is tagged as an application one
         // to make sure that user provided signal handler code has access to 
all
         // the features like syscall stack which matters for Golang apps
-        const auto sa = signal_actions[sig];
+        const auto sa = signal_actions[sigidx];
         auto t = sched::thread::make([=] {
             if (sa.sa_flags & SA_RESETHAND) {
-                signal_actions[sig].sa_flags = 0;
-                signal_actions[sig].sa_handler = SIG_DFL;
+                signal_actions[sigidx].sa_flags = 0;
+                signal_actions[sigidx].sa_handler = SIG_DFL;
             }
             if (sa.sa_flags & SA_SIGINFO) {
                 // FIXME: proper second (siginfo) and third (context) 
arguments (See example in call_signal_handler)
@@ -498,7 +523,7 @@ void itimer::work()
                         _due = _no_alarm;
                     }
                     kill(getpid(), _signum);
-                    if(!is_sig_ign(signal_actions[_signum])) {
+                    if(!is_sig_ign(signal_actions[_signum - 1])) {
                         _owner_thread->interrupted(true);
                     }
                 } else {
diff --git a/libc/signal.hh b/libc/signal.hh
--- a/libc/signal.hh
+++ b/libc/signal.hh
@@ -14,12 +14,31 @@
 
 namespace osv {
 
-static const unsigned nsignals = 65;
-
+static const unsigned nsignals = 64;
+static_assert(nsignals == NSIG - 1); //NSIG is actually 65 in both glibc and 
musl headers
+
+//In theory, it would not matter how we encode the information about
+//which signals are masked as long as our implementation of it stays
+//completely internal. But in reality, when we implement rt_sigprocmask
+//and other related syscalls, the users of those like glibc rely on
+//the format of the bitmask used by Linux kernel. In Linux, the least
+//significant bit (or the most right) represents the mask of the signal
+//number 1, the 2nd bit represents the mask of the signal number 2, etc
+//all the way to the maximum number 64. So we must ensure that our internal
+//sigset structure can fit all these 64 bits and nothing more as some
+//runtimes like golang assume the oldset argument of the rt_sigprocmask
+//can be as small as NSIG/8 (8 bytes).
+//To get or set a specific signal mask bit, we simply substract 1
+//from the signal number - <mask index> = signum - 1
 struct sigset {
     std::bitset<nsignals> mask;
 };
 
+static_assert(sizeof(sigset) == NSIG / 8,
+    "size of sigset does not match the corresponding one in Linux kernel");
+
+//We use the same 0-based index access pattern, where the signal action
+//of a given signal with number "signo" is stored at the index "signo - 1"
 extern struct sigaction signal_actions[nsignals];
 
 sigset* from_libc(sigset_t* s);

-- 
You received this message because you are subscribed to the Google Groups "OSv 
Development" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to osv-dev+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/osv-dev/00000000000029fadf060954e38e%40google.com.

Reply via email to