From patchwork Fri May 12 11:02:24 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Milos Stojanovic X-Patchwork-Id: 761639 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3wPVhl5Yj2z9s78 for ; Fri, 12 May 2017 23:10:47 +1000 (AEST) Received: from localhost ([::1]:53728 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1d9ALF-0003IO-BC for incoming@patchwork.ozlabs.org; Fri, 12 May 2017 09:10:45 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:50048) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1d98LL-00043w-43 for qemu-devel@nongnu.org; Fri, 12 May 2017 07:02:45 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1d98LI-0006Se-C1 for qemu-devel@nongnu.org; Fri, 12 May 2017 07:02:43 -0400 Received: from mx2.rt-rk.com ([89.216.37.149]:46760 helo=mail.rt-rk.com) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1d98LI-0006Ri-0z for qemu-devel@nongnu.org; Fri, 12 May 2017 07:02:40 -0400 Received: from localhost (localhost [127.0.0.1]) by mail.rt-rk.com (Postfix) with ESMTP id 9616C1A49ED; Fri, 12 May 2017 13:02:12 +0200 (CEST) X-Virus-Scanned: amavisd-new at rt-rk.com Received: from rtrkw488-lin.domain.local (unknown [10.10.14.90]) by mail.rt-rk.com (Postfix) with ESMTPSA id 41A591A49EC; Fri, 12 May 2017 13:02:12 +0200 (CEST) From: =?UTF-8?q?Milo=C5=A1=20Stojanovi=C4=87?= To: qemu-devel@nongnu.org, riku.voipio@iki.fi Date: Fri, 12 May 2017 13:02:24 +0200 Message-Id: <1494586944-7253-17-git-send-email-Milos.Stojanovic@rt-rk.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1494586944-7253-1-git-send-email-Milos.Stojanovic@rt-rk.com> References: <1494586944-7253-1-git-send-email-Milos.Stojanovic@rt-rk.com> MIME-Version: 1.0 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x [fuzzy] X-Received-From: 89.216.37.149 X-Mailman-Approved-At: Fri, 12 May 2017 09:02:56 -0400 Subject: [Qemu-devel] [PATCH 16/16] [RFC] linux-user: add support for multiplexing signals in rt_sigqueueinfo(), rt_tgsigqueueinfo(), kill() and tgkill() syscalls. X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Milos.Stojanovic@rt-rk.com Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: "Qemu-devel" 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ć --- 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 3d51424..eaa6643 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 {