qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH v2 16/16] [RFC] linux-user: add support for multiple


From: Miloš Stojanović
Subject: [Qemu-devel] [PATCH v2 16/16] [RFC] linux-user: add support for multiplexing signals in rt_sigqueueinfo(), rt_tgsigqueueinfo(), kill() and tgkill() syscalls.
Date: Mon, 15 May 2017 16:59:56 +0200

Add support for multiplexing in the host_signal_handler() function and in
system calls rt_sigqueueinfo()/rt_tgsigqueueinfo(), tgkill(), kill() for
the case when pid > 0.

The rt_sigqueueinfo()/rt_tgsigqueueinfo() system calls multiplex target
signals which are out of the host range by setting the si_errno value to
the actual value of the signal and sending the signal to the MUX_SIG
signal number. The host_signal_handler() will pull out the multiplexed
signals and set their signal number to the correct value. That value
should be in the si_errno field of the siginfo_t structure. The si_errno
field is used here but this implementation can be replaced with any other
unused field in the uinfo structure.

The emulation of larger target signal range is done by spoofing the system
call info, adding the signal number to the si_errno field, and sending it
to the host multiplex queue via rt_sigqueueinfo()/rt_tgsigqueueinfo().
In order to send a signal using rt_sigqueueinfo()/rt_tgsigqueueinfo() with
si_code SI_USER or SI_TKILL to another thread or process, we need to
disguise it as some other signal from the kernel range because the host
kernel doesn't allow direct impersonations of those signals. This is done
with SIG_SPOOF which moves the si_code to the nearest unused kernel si_code
value. After the signal is successfully sent the host_signal_handler() of
the receiving thread/process will turn it back into the proper kill/tgkill
signal, before it gets processed.

The tkill() system call as well as kill() with the argument pid <= 0
couldn't be implemented simply using this method because it requires
acquiring information about, and sending simultaneous signals to multiple
threads or processes. These functionalities are out of the scope of
rt_sigqueueinfo()/rt_tgsigqueueinfo().

Signed-off-by: Miloš Stojanović <address@hidden>
---
 linux-user/signal.c       | 22 +++++++++++++
 linux-user/syscall.c      | 81 +++++++++++++++++++++++++++++++++++++++++++++++
 linux-user/syscall_defs.h |  8 +++++
 3 files changed, 111 insertions(+)

diff --git a/linux-user/signal.c b/linux-user/signal.c
index ceaccab..f80a462 100644
--- a/linux-user/signal.c
+++ b/linux-user/signal.c
@@ -787,6 +787,28 @@ static void host_signal_handler(int host_signum, siginfo_t 
*info,
     sig = host_to_target_signal(host_signum);
     if (sig < 1 || sig > TARGET_NSIG)
         return;
+
+#ifdef MUX_SIG
+    if (sig == MUX_SIG) {
+        /* return the spoofed kill/tgkill signals into standard form */
+        if (info->si_code == SIG_SPOOF(SI_USER)) {
+            info->si_code = SI_USER;
+        } else if (info->si_code == SIG_SPOOF(SI_TKILL)) {
+            info->si_code = SI_TKILL;
+        }
+
+        /*
+         * We assume that si_errno field will remain intact during signal
+         * processing on the host. If it changes, the signal will be sent to
+         * the wrong number (most likely to MUX_SIG).
+         */
+        /* get the actual target signal number */
+        int target_sig = info->si_errno;
+        if (target_sig >= _NSIG && target_sig < TARGET_NSIG) {
+            sig = target_sig;
+        }
+    }
+#endif
     trace_user_host_signal(env, host_signum, sig);
 
     rewind_if_in_safe_syscall(puc);
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index f4ce6a8..8190575 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -7123,6 +7123,24 @@ static inline abi_long host_to_target_stat64(void 
*cpu_env,
     return 0;
 }
 
+#ifdef MUX_SIG
+static inline int multiplex(abi_long *arg, siginfo_t *uinfo)
+{
+    if (*arg >= _NSIG && *arg < TARGET_NSIG) {
+        /*
+         * Using si_errno to transfer the signal number assumes that the field
+         * doesn't change its value before it gets handled in the
+         * host_signal_handler().
+         */
+        uinfo->si_errno = *arg;
+        *arg = MUX_SIG;
+        uinfo->si_signo = MUX_SIG;
+    }
+
+    return 0;
+}
+#endif
+
 /* ??? Using host futex calls even when target atomic operations
    are not really atomic probably breaks things.  However implementing
    futexes locally would make futexes shared between multiple processes
@@ -8258,7 +8276,42 @@ abi_long do_syscall(void *cpu_env, int num, abi_long 
arg1,
         break;
 #endif
     case TARGET_NR_kill:
+#ifdef MUX_SIG
+        if (arg2 >= _NSIG && arg2 < TARGET_NSIG) {
+            siginfo_t info;
+
+            info.si_errno = arg2;
+            info.si_signo = MUX_SIG;
+            info.si_code = SIG_SPOOF(SI_USER);
+            info.si_pid = getpid();
+            info.si_uid = getuid();
+
+            /* pid > 0 */
+            if (arg1 > 0) {
+                ret = get_errno(sys_rt_sigqueueinfo(arg1, MUX_SIG, &info));
+            } else {
+                ret = -TARGET_EINVAL;
+            }
+            /*
+             * TODO: In order to implement kill with rt_tgsigqueueinfo() for
+             * cases where pid <= 0 one needs to get a list of all the relevant
+             * processes and simultaniously send the signal to them.
+             * Missing:
+             * (pid = 0):
+             *     send to every process in the process group of
+             *     the calling process
+             * (pid = -1):
+             *     send to every process for which the calling process
+             *     has permission to send signals, except for process 1 (init)
+             * (pid < -1):
+             *     send to every process in the process group whose ID is -pid
+             */
+        } else {
+            ret = get_errno(safe_kill(arg1, target_to_host_signal(arg2)));
+        }
+#else
         ret = get_errno(safe_kill(arg1, target_to_host_signal(arg2)));
+#endif
         break;
 #ifdef TARGET_NR_rename
     case TARGET_NR_rename:
@@ -8929,6 +8982,9 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
             }
             target_to_host_siginfo(&uinfo, p);
             unlock_user(p, arg3, 0);
+#ifdef MUX_SIG
+            multiplex(&arg2, &uinfo);
+#endif
             ret = get_errno(sys_rt_sigqueueinfo(arg1, arg2, &uinfo));
         }
         break;
@@ -8942,6 +8998,9 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
             }
             target_to_host_siginfo(&uinfo, p);
             unlock_user(p, arg4, 0);
+#ifdef MUX_SIG
+            multiplex(&arg3, &uinfo);
+#endif
             ret = get_errno(sys_rt_tgsigqueueinfo(arg1, arg2, arg3, &uinfo));
         }
         break;
@@ -11743,12 +11802,34 @@ abi_long do_syscall(void *cpu_env, int num, abi_long 
arg1,
 #endif
 
     case TARGET_NR_tkill:
+        /*
+         * TODO: In order to implement tkill with rt_sigqueueinfo() one needs
+         * to get a list of all the threads with the specifiend tid and
+         * simultaniously send the signal to them.
+         */
         ret = get_errno(safe_tkill((int)arg1, target_to_host_signal(arg2)));
         break;
 
     case TARGET_NR_tgkill:
+#ifdef MUX_SIG
+        if (arg3 >= _NSIG && arg3 < TARGET_NSIG) {
+            siginfo_t info;
+
+            info.si_errno = arg3;
+            info.si_signo = MUX_SIG;
+            info.si_code = SIG_SPOOF(SI_TKILL);
+            info.si_pid = getpid();
+            info.si_uid = getuid();
+
+            ret = get_errno(sys_rt_tgsigqueueinfo(arg1, arg2, MUX_SIG, &info));
+        } else {
+            ret = get_errno(safe_tgkill((int)arg1, (int)arg2,
+                            target_to_host_signal(arg3)));
+        }
+#else
         ret = get_errno(safe_tgkill((int)arg1, (int)arg2,
                         target_to_host_signal(arg3)));
+#endif
         break;
 
 #ifdef TARGET_NR_set_robust_list
diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h
index 42089fc..16cab53 100644
--- a/linux-user/syscall_defs.h
+++ b/linux-user/syscall_defs.h
@@ -412,6 +412,14 @@ struct target_dirent64 {
  * the host signal masks.
  */
 #define TRACK_TARGET_SIGMASK
+
+/*
+ * This macro is used to change a kill/tgkill signal so it can be sent through
+ * rt_sigqueueinfo()/rt_tgsigqueueinfo(), since the host kernel doesn't allow
+ * direct impersonations of those signals. Subtracting 8 from the code moves
+ * it to the nearest unused kernel si_code value.
+ */
+#define SIG_SPOOF(code)  ((code) - 8)
 #endif
 
 typedef struct {
-- 
1.9.1




reply via email to

[Prev in Thread] Current Thread [Next in Thread]