From patchwork Wed Jun 8 13:30:03 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Riku Voipio X-Patchwork-Id: 9164751 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id D064360572 for ; Wed, 8 Jun 2016 13:53:15 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id BF053281FE for ; Wed, 8 Jun 2016 13:53:15 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id B3BDA282DC; Wed, 8 Jun 2016 13:53:15 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-5.8 required=2.0 tests=BAYES_00,DKIM_SIGNED, FSL_HELO_HOME, RCVD_IN_DNSWL_HI, T_DKIM_INVALID autolearn=ham version=3.3.1 Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 72A43281FE for ; Wed, 8 Jun 2016 13:53:14 +0000 (UTC) Received: from localhost ([::1]:57124 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1bAduz-00080J-HH for patchwork-qemu-devel@patchwork.kernel.org; Wed, 08 Jun 2016 09:53:13 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:37080) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1bAdZV-0004O9-9g for qemu-devel@nongnu.org; Wed, 08 Jun 2016 09:31:07 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1bAdZP-0005mt-D6 for qemu-devel@nongnu.org; Wed, 08 Jun 2016 09:31:00 -0400 Received: from mail-lf0-x230.google.com ([2a00:1450:4010:c07::230]:33681) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1bAdZP-0005mc-0X for qemu-devel@nongnu.org; Wed, 08 Jun 2016 09:30:55 -0400 Received: by mail-lf0-x230.google.com with SMTP id s64so5904019lfe.0 for ; Wed, 08 Jun 2016 06:30:54 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=K7z8EXqa4/Wi2mqEltuOcVR4w5neaDMDdPNYf20iP00=; b=JQD68on24rsH0ax0vN+xGloFZeqqd2oq9klJ8rGc4+22vDqUmz+U56MKv+09VclqhQ vpu3pphI0/FWRzyrOQ+TqTIx3nXL09/crqZSQdA2kwpwOE/Cqt38Tx6YBnLqV88ypi3D GQbc5iq+qlLiafYeJNpXAXK40sgi0n9+Xbvc8= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=K7z8EXqa4/Wi2mqEltuOcVR4w5neaDMDdPNYf20iP00=; b=CK7yUAYmduOSfsDzxCtf9vtdzgn68Xa5RB/KqpFBBiNUqUqXbTkTDZLURGw71FT4YF fHmCCSJWagzxuFOPtmHH2lmzPEbLrarBEIMcNQhiX2BDkiI/3zIkyfqHCuj9y1ggYLua O7L8HbcoyQdSknFjPEhvsnFXcAQepKn96fdXVn6mcGq7q7A45ye+U0Hb4WlYxet+Qgl4 l4stOvhVz66bT2obG5LgNBzlDhlVvjFD/OwMrsy4VmtlLrFJZ/rKmmgIUK96Nl9KElSb cneCPRGcXYzZpgzrnM5gbZX2dRMBeKIvPWuGLxaQqtCUissWAi+jWLp7axRb9O7G3Cyz piJg== X-Gm-Message-State: ALyK8tLL1Sqs1lHRrmSfearSZmw9mYnqBJskWcrw7nnooGKs3YPuvJEz3qyoN3RmeKpexnvX X-Received: by 10.25.145.140 with SMTP id t134mr3677810lfd.231.1465392654075; Wed, 08 Jun 2016 06:30:54 -0700 (PDT) Received: from beaming.home (91-157-170-157.elisa-laajakaista.fi. [91.157.170.157]) by smtp.gmail.com with ESMTPSA id 2sm139854lja.37.2016.06.08.06.30.52 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 08 Jun 2016 06:30:53 -0700 (PDT) From: riku.voipio@linaro.org To: qemu-devel@nongnu.org Date: Wed, 8 Jun 2016 16:30:03 +0300 Message-Id: X-Mailer: git-send-email 2.1.4 In-Reply-To: References: X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 2a00:1450:4010:c07::230 Subject: [Qemu-devel] [PULL 22/44] linux-user: Use both si_code and si_signo when converting siginfo_t 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: Peter Maydell Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" X-Virus-Scanned: ClamAV using ClamSMTP From: Peter Maydell The siginfo_t struct includes a union. The correct way to identify which fields of the union are relevant is complicated, because we have to use a combination of the si_code and si_signo to figure out which of the union's members are valid. (Within the host kernel it is always possible to tell, but the kernel carefully avoids giving userspace the high 16 bits of si_code, so we don't have the information to do this the easy way...) We therefore make our best guess, bearing in mind that a guest can spoof most of the si_codes via rt_sigqueueinfo() if it likes. Once we have made our guess, we record it in the top 16 bits of the si_code, so that tswap_siginfo() later can use it. tswap_siginfo() then strips these top bits out before writing si_code to the guest (sign-extending the lower bits). This fixes a bug where fields were sometimes wrong; in particular the LTP kill10 test went into an infinite loop because its signal handler got a si_pid value of 0 rather than the pid of the sending process. As part of this change, we switch to using __put_user() in the tswap_siginfo code which writes out the byteswapped values to the target memory, in case the target memory pointer is not sufficiently aligned for the host CPU's requirements. Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/signal.c | 165 ++++++++++++++++++++++++++++++++-------------- linux-user/syscall_defs.h | 15 +++++ 2 files changed, 131 insertions(+), 49 deletions(-) diff --git a/linux-user/signal.c b/linux-user/signal.c index b21d6bf..8ea0cbf 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -17,6 +17,7 @@ * along with this program; if not, see . */ #include "qemu/osdep.h" +#include "qemu/bitops.h" #include #include @@ -274,70 +275,129 @@ static inline void host_to_target_siginfo_noswap(target_siginfo_t *tinfo, const siginfo_t *info) { int sig = host_to_target_signal(info->si_signo); + int si_code = info->si_code; + int si_type; tinfo->si_signo = sig; tinfo->si_errno = 0; tinfo->si_code = info->si_code; - if (sig == TARGET_SIGILL || sig == TARGET_SIGFPE || sig == TARGET_SIGSEGV - || sig == TARGET_SIGBUS || sig == TARGET_SIGTRAP) { - /* Should never come here, but who knows. The information for - the target is irrelevant. */ - tinfo->_sifields._sigfault._addr = 0; - } else if (sig == TARGET_SIGIO) { - tinfo->_sifields._sigpoll._band = info->si_band; - tinfo->_sifields._sigpoll._fd = info->si_fd; - } else if (sig == TARGET_SIGCHLD) { - tinfo->_sifields._sigchld._pid = info->si_pid; - tinfo->_sifields._sigchld._uid = info->si_uid; - tinfo->_sifields._sigchld._status + /* This is awkward, because we have to use a combination of + * the si_code and si_signo to figure out which of the union's + * members are valid. (Within the host kernel it is always possible + * to tell, but the kernel carefully avoids giving userspace the + * high 16 bits of si_code, so we don't have the information to + * do this the easy way...) We therefore make our best guess, + * bearing in mind that a guest can spoof most of the si_codes + * via rt_sigqueueinfo() if it likes. + * + * Once we have made our guess, we record it in the top 16 bits of + * the si_code, so that tswap_siginfo() later can use it. + * tswap_siginfo() will strip these top bits out before writing + * si_code to the guest (sign-extending the lower bits). + */ + + switch (si_code) { + case SI_USER: + case SI_TKILL: + case SI_KERNEL: + /* Sent via kill(), tkill() or tgkill(), or direct from the kernel. + * These are the only unspoofable si_code values. + */ + tinfo->_sifields._kill._pid = info->si_pid; + tinfo->_sifields._kill._uid = info->si_uid; + si_type = QEMU_SI_KILL; + break; + default: + /* Everything else is spoofable. Make best guess based on signal */ + switch (sig) { + case TARGET_SIGCHLD: + tinfo->_sifields._sigchld._pid = info->si_pid; + tinfo->_sifields._sigchld._uid = info->si_uid; + tinfo->_sifields._sigchld._status = host_to_target_waitstatus(info->si_status); - tinfo->_sifields._sigchld._utime = info->si_utime; - tinfo->_sifields._sigchld._stime = info->si_stime; - } else if (sig >= TARGET_SIGRTMIN) { - tinfo->_sifields._rt._pid = info->si_pid; - tinfo->_sifields._rt._uid = info->si_uid; - /* XXX: potential problem if 64 bit */ - tinfo->_sifields._rt._sigval.sival_ptr + tinfo->_sifields._sigchld._utime = info->si_utime; + tinfo->_sifields._sigchld._stime = info->si_stime; + si_type = QEMU_SI_CHLD; + break; + case TARGET_SIGIO: + tinfo->_sifields._sigpoll._band = info->si_band; + tinfo->_sifields._sigpoll._fd = info->si_fd; + si_type = QEMU_SI_POLL; + break; + default: + /* Assume a sigqueue()/mq_notify()/rt_sigqueueinfo() source. */ + tinfo->_sifields._rt._pid = info->si_pid; + tinfo->_sifields._rt._uid = info->si_uid; + /* XXX: potential problem if 64 bit */ + tinfo->_sifields._rt._sigval.sival_ptr = (abi_ulong)(unsigned long)info->si_value.sival_ptr; + si_type = QEMU_SI_RT; + break; + } + break; } + + tinfo->si_code = deposit32(si_code, 16, 16, si_type); } static void tswap_siginfo(target_siginfo_t *tinfo, const target_siginfo_t *info) { - int sig = info->si_signo; - tinfo->si_signo = tswap32(sig); - tinfo->si_errno = tswap32(info->si_errno); - tinfo->si_code = tswap32(info->si_code); - - if (sig == TARGET_SIGILL || sig == TARGET_SIGFPE || sig == TARGET_SIGSEGV - || sig == TARGET_SIGBUS || sig == TARGET_SIGTRAP) { - tinfo->_sifields._sigfault._addr - = tswapal(info->_sifields._sigfault._addr); - } else if (sig == TARGET_SIGIO) { - tinfo->_sifields._sigpoll._band - = tswap32(info->_sifields._sigpoll._band); - tinfo->_sifields._sigpoll._fd = tswap32(info->_sifields._sigpoll._fd); - } else if (sig == TARGET_SIGCHLD) { - tinfo->_sifields._sigchld._pid - = tswap32(info->_sifields._sigchld._pid); - tinfo->_sifields._sigchld._uid - = tswap32(info->_sifields._sigchld._uid); - tinfo->_sifields._sigchld._status - = tswap32(info->_sifields._sigchld._status); - tinfo->_sifields._sigchld._utime - = tswapal(info->_sifields._sigchld._utime); - tinfo->_sifields._sigchld._stime - = tswapal(info->_sifields._sigchld._stime); - } else if (sig >= TARGET_SIGRTMIN) { - tinfo->_sifields._rt._pid = tswap32(info->_sifields._rt._pid); - tinfo->_sifields._rt._uid = tswap32(info->_sifields._rt._uid); - tinfo->_sifields._rt._sigval.sival_ptr - = tswapal(info->_sifields._rt._sigval.sival_ptr); + int si_type = extract32(info->si_code, 16, 16); + int si_code = sextract32(info->si_code, 0, 16); + + __put_user(info->si_signo, &tinfo->si_signo); + __put_user(info->si_errno, &tinfo->si_errno); + __put_user(si_code, &tinfo->si_code); + + /* We can use our internal marker of which fields in the structure + * are valid, rather than duplicating the guesswork of + * host_to_target_siginfo_noswap() here. + */ + switch (si_type) { + case QEMU_SI_KILL: + __put_user(info->_sifields._kill._pid, &tinfo->_sifields._kill._pid); + __put_user(info->_sifields._kill._uid, &tinfo->_sifields._kill._uid); + break; + case QEMU_SI_TIMER: + __put_user(info->_sifields._timer._timer1, + &tinfo->_sifields._timer._timer1); + __put_user(info->_sifields._timer._timer2, + &tinfo->_sifields._timer._timer2); + break; + case QEMU_SI_POLL: + __put_user(info->_sifields._sigpoll._band, + &tinfo->_sifields._sigpoll._band); + __put_user(info->_sifields._sigpoll._fd, + &tinfo->_sifields._sigpoll._fd); + break; + case QEMU_SI_FAULT: + __put_user(info->_sifields._sigfault._addr, + &tinfo->_sifields._sigfault._addr); + break; + case QEMU_SI_CHLD: + __put_user(info->_sifields._sigchld._pid, + &tinfo->_sifields._sigchld._pid); + __put_user(info->_sifields._sigchld._uid, + &tinfo->_sifields._sigchld._uid); + __put_user(info->_sifields._sigchld._status, + &tinfo->_sifields._sigchld._status); + __put_user(info->_sifields._sigchld._utime, + &tinfo->_sifields._sigchld._utime); + __put_user(info->_sifields._sigchld._stime, + &tinfo->_sifields._sigchld._stime); + break; + case QEMU_SI_RT: + __put_user(info->_sifields._rt._pid, &tinfo->_sifields._rt._pid); + __put_user(info->_sifields._rt._uid, &tinfo->_sifields._rt._uid); + __put_user(info->_sifields._rt._sigval.sival_ptr, + &tinfo->_sifields._rt._sigval.sival_ptr); + break; + default: + g_assert_not_reached(); } } - void host_to_target_siginfo(target_siginfo_t *tinfo, const siginfo_t *info) { host_to_target_siginfo_noswap(tinfo, info); @@ -505,6 +565,13 @@ int queue_signal(CPUArchState *env, int sig, target_siginfo_t *info) trace_user_queue_signal(env, sig); + /* Currently all callers define siginfo structures which + * use the _sifields._sigfault union member, so we can + * set the type here. If that changes we should push this + * out so the si_type is passed in by callers. + */ + info->si_code = deposit32(info->si_code, 16, 16, QEMU_SI_FAULT); + ts->sync_signal.info = *info; ts->sync_signal.pending = sig; /* signal that a new signal is pending */ diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index 34af15a..124754f 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -673,6 +673,21 @@ typedef struct { #define TARGET_SI_PAD_SIZE ((TARGET_SI_MAX_SIZE - TARGET_SI_PREAMBLE_SIZE) / sizeof(int)) +/* Within QEMU the top 16 bits of si_code indicate which of the parts of + * the union in target_siginfo is valid. This only applies between + * host_to_target_siginfo_noswap() and tswap_siginfo(); it does not + * appear either within host siginfo_t or in target_siginfo structures + * which we get from the guest userspace program. (The Linux kernel + * does a similar thing with using the top bits for its own internal + * purposes but not letting them be visible to userspace.) + */ +#define QEMU_SI_KILL 0 +#define QEMU_SI_TIMER 1 +#define QEMU_SI_POLL 2 +#define QEMU_SI_FAULT 3 +#define QEMU_SI_CHLD 4 +#define QEMU_SI_RT 5 + typedef struct target_siginfo { #ifdef TARGET_MIPS int si_signo;