From patchwork Thu Sep 16 15:12:33 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Vivier X-Patchwork-Id: 512935 Delivered-To: patch@linaro.org Received: by 2002:a02:c816:0:0:0:0:0 with SMTP id p22csp1312306jao; Thu, 16 Sep 2021 08:21:03 -0700 (PDT) X-Google-Smtp-Source: ABdhPJxpXvF+Sr9olg4/93mkRVulpndKavBQGDwPt+w0Np0MizWFuswVmnVhi3aTS81vGRzMyw3n X-Received: by 2002:ac8:7d0d:: with SMTP id g13mr5428857qtb.367.1631805663355; Thu, 16 Sep 2021 08:21:03 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1631805663; cv=none; d=google.com; s=arc-20160816; b=TbMn9zBmpxzaMWmUxNks/hC8r1iAAHy0igJhMS7sgr68DHJsdxZee80iMsTu6eioUL 0uitIdPEA+nFXe0pfjxtzBfKqFrkU6Ei1DPaPqnkPeJbPbnzabd5F0T3bn8PsiC6lTZC noKyM1W6UQWAe56rM/QpdCIc9u7K3x0uUqv5/Jo5r4S8iXjb4DOKQ/NxXtCB/DDnRdGz G0IbxUJLrFjKn/gNzJAcsvV8b6DJq7oSDYYL7pUcxtXBBtVpl8XbxUYKdyI4dYf4qgS1 vaOnzLhZn1F5b7xkIUW0geTr8EqXw3nWAa6L8ad1RDvnI5C2xILEaXI0ONy6WrJcwwbf zJGw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:cc:list-subscribe:list-help:list-post:list-archive :list-unsubscribe:list-id:precedence:content-transfer-encoding :mime-version:references:in-reply-to:message-id:date:subject:to:from; bh=OWVLYKzzRgmVrmszKgT1H7J2LTAckdzqsFn11tCOJK8=; b=SnQz5apE4ob8DGr477NUJdhCUKsJ/mD5YRxPN06E8liQoeUf1EJEelBprBebz/ssZ8 turahRwBkR3HUJzSFX4GRqNYL8aBXUFOvT63JcDO/9iVlqp+s7Q8DTdWI674Pk02+i3M wBvuoypGluPg2o5IPNbiM1M7nx6+xz4mxofmZY3+MgniurxokN6vogBqvDi2DY665Jue 6TeVvXF4kbdM9udC4s9jlGzE5V8/MB++dMeDOtwls595aE6tcl7w24xr18Abt0SiKW9C HCC62sdGH9FjkfL6+MTy+wEsIq4ZbDXx7RWKsksvVXanTijI0qqbZ5/EykVsmhpFZ2gf LMzg== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of qemu-devel-bounces+patch=linaro.org@nongnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom="qemu-devel-bounces+patch=linaro.org@nongnu.org" Return-Path: Received: from lists.gnu.org (lists.gnu.org. [209.51.188.17]) by mx.google.com with ESMTPS id e14si1046040qtm.372.2021.09.16.08.21.03 for (version=TLS1_2 cipher=ECDHE-ECDSA-CHACHA20-POLY1305 bits=256/256); Thu, 16 Sep 2021 08:21:03 -0700 (PDT) Received-SPF: pass (google.com: domain of qemu-devel-bounces+patch=linaro.org@nongnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; Authentication-Results: mx.google.com; spf=pass (google.com: domain of qemu-devel-bounces+patch=linaro.org@nongnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom="qemu-devel-bounces+patch=linaro.org@nongnu.org" Received: from localhost ([::1]:43546 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1mQtC9-00052p-S1 for patch@linaro.org; Thu, 16 Sep 2021 11:21:01 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:36232) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1mQt4K-0002xW-LD for qemu-devel@nongnu.org; Thu, 16 Sep 2021 11:12:56 -0400 Received: from mout.kundenserver.de ([217.72.192.75]:38697) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1mQt4H-0004FU-TU for qemu-devel@nongnu.org; Thu, 16 Sep 2021 11:12:56 -0400 Received: from quad ([82.142.27.6]) by mrelayeu.kundenserver.de (mreue107 [212.227.15.183]) with ESMTPSA (Nemesis) id 1MY6P5-1mPRrz0dBt-00YOrK; Thu, 16 Sep 2021 17:12:42 +0200 From: Laurent Vivier To: qemu-devel@nongnu.org Subject: [PULL 06/10] linux-user: Split safe-syscall macro into its own header Date: Thu, 16 Sep 2021 17:12:33 +0200 Message-Id: <20210916151237.1188301-7-laurent@vivier.eu> X-Mailer: git-send-email 2.31.1 In-Reply-To: <20210916151237.1188301-1-laurent@vivier.eu> References: <20210916151237.1188301-1-laurent@vivier.eu> MIME-Version: 1.0 X-Provags-ID: V03:K1:+Vp8u7c4VeUNyjpDUNpTmJyMbG4avvzcUoQSpQ8CpmxxoFYP07l /xsH7fV4tEzQ8ZJ2EXSq5/6dR0mFvjF5Cmx/wIMBtbGHNpjMKciI5aPFr6X6NQxf+8XiuPD j3GXTWQV5sqy45qmAJ/BBQJ0CbTFKPzogWfTsCU68Y53uvXJ7Blo3NQazhNTgoUQ/C0DQhb UHi/Fcxv7U6/AHsDMfLoQ== X-UI-Out-Filterresults: notjunk:1; V03:K0:7VoIXFLKvxE=:EOE9YHypF6oyD2W4WaTFRh oLpzDSjN8+RIKqYHyZ+RO8zRhlIV6DCpHw1sJV1iQ3fCEM7VK0OLutb0RgM6yM1dBNAfsRnC+ M12rYzy9OOdO/G59yDKQCuozFHOVOXgbEs6k3Zx+n12kvQK1PPJtZVebEOUiAxX5TQtExQW8y Mnlgc8Ph5gZs3TrObI4/YGQFWOATBssZz6pOdtmN/9lP7vJ57WdHBDVp7bAbcM622Bk+wnSUt RXvCaC8PRlpIpOIbzYTepOVAVwqHFwZNeSc4EImIndN87ebsuXL++8VgYkwaAM1zLKUVkqp5x UaQMmKcW8HWEcWd2brRWWE873wvsX3S7qLyhDQqaUT/x5JPITgwQeACCASOSx2i6bJ4DHtspy ucBHeKNVFtpfmySsfa/cekneqhCYGNZjiP3VwNB/QHngiE6MFGFZg9POg5vR67dJB+RJzQq5c 5mmBd4Ac4/Jr4eSUslQnZnhVWFWbLH7Z/R1Ik6+JEColaPE5cj1T2/AtxL1gEiTYl41DVaY3B 3rW3k0UoUZRg73+vz0oQbXyxnukbzdQ0H4x6XBwr+zx9K+56/c3XzGRps3Cc49E0GmiUujBqI 74dDSHVrRPinBNylqxV+4vJWp7ARy3mWU7fXgmwXDLnATxzR2MYGRFtW/jLj8mqq5HmORxxFM nm/00yPXrS9qRTI/FsG/JZeTo/GRWK5lNN50Y+f/BfywALTy1uSZR5lTJSz9mbPImJuZzVJGz gLGETyXykehOHivcvfBY5wqoicNrgtnPaX3b7g== Received-SPF: none client-ip=217.72.192.75; envelope-from=laurent@vivier.eu; helo=mout.kundenserver.de X-Spam_score_int: -18 X-Spam_score: -1.9 X-Spam_bar: - X-Spam_report: (-1.9 / 5.0 requ) BAYES_00=-1.9, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H2=-0.001, SPF_HELO_NONE=0.001, SPF_NONE=0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Peter Maydell , Richard Henderson , =?utf-8?q?Philippe_Mathieu-Daud=C3=A9?= , Laurent Vivier Errors-To: qemu-devel-bounces+patch=linaro.org@nongnu.org Sender: "Qemu-devel" From: Peter Maydell Split the safe-syscall macro from qemu.h into a new safe-syscall.h. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20210908154405.15417-7-peter.maydell@linaro.org> Signed-off-by: Laurent Vivier --- linux-user/qemu.h | 135 --------------------------------- linux-user/safe-syscall.h | 154 ++++++++++++++++++++++++++++++++++++++ linux-user/syscall.c | 1 + 3 files changed, 155 insertions(+), 135 deletions(-) create mode 100644 linux-user/safe-syscall.h -- 2.31.1 diff --git a/linux-user/qemu.h b/linux-user/qemu.h index 0cb799905796..a82a46236e64 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -240,141 +240,6 @@ void probe_guest_base(const char *image_name, #include "qemu/log.h" -/* safe_syscall.S */ - -/** - * safe_syscall: - * @int number: number of system call to make - * ...: arguments to the system call - * - * Call a system call if guest signal not pending. - * This has the same API as the libc syscall() function, except that it - * may return -1 with errno == TARGET_ERESTARTSYS if a signal was pending. - * - * Returns: the system call result, or -1 with an error code in errno - * (Errnos are host errnos; we rely on TARGET_ERESTARTSYS not clashing - * with any of the host errno values.) - */ - -/* - * A guide to using safe_syscall() to handle interactions between guest - * syscalls and guest signals: - * - * Guest syscalls come in two flavours: - * - * (1) Non-interruptible syscalls - * - * These are guest syscalls that never get interrupted by signals and - * so never return EINTR. They can be implemented straightforwardly in - * QEMU: just make sure that if the implementation code has to make any - * blocking calls that those calls are retried if they return EINTR. - * It's also OK to implement these with safe_syscall, though it will be - * a little less efficient if a signal is delivered at the 'wrong' moment. - * - * Some non-interruptible syscalls need to be handled using block_signals() - * to block signals for the duration of the syscall. This mainly applies - * to code which needs to modify the data structures used by the - * host_signal_handler() function and the functions it calls, including - * all syscalls which change the thread's signal mask. - * - * (2) Interruptible syscalls - * - * These are guest syscalls that can be interrupted by signals and - * for which we need to either return EINTR or arrange for the guest - * syscall to be restarted. This category includes both syscalls which - * always restart (and in the kernel return -ERESTARTNOINTR), ones - * which only restart if there is no handler (kernel returns -ERESTARTNOHAND - * or -ERESTART_RESTARTBLOCK), and the most common kind which restart - * if the handler was registered with SA_RESTART (kernel returns - * -ERESTARTSYS). System calls which are only interruptible in some - * situations (like 'open') also need to be handled this way. - * - * Here it is important that the host syscall is made - * via this safe_syscall() function, and *not* via the host libc. - * If the host libc is used then the implementation will appear to work - * most of the time, but there will be a race condition where a - * signal could arrive just before we make the host syscall inside libc, - * and then then guest syscall will not correctly be interrupted. - * Instead the implementation of the guest syscall can use the safe_syscall - * function but otherwise just return the result or errno in the usual - * way; the main loop code will take care of restarting the syscall - * if appropriate. - * - * (If the implementation needs to make multiple host syscalls this is - * OK; any which might really block must be via safe_syscall(); for those - * which are only technically blocking (ie which we know in practice won't - * stay in the host kernel indefinitely) it's OK to use libc if necessary. - * You must be able to cope with backing out correctly if some safe_syscall - * you make in the implementation returns either -TARGET_ERESTARTSYS or - * EINTR though.) - * - * block_signals() cannot be used for interruptible syscalls. - * - * - * How and why the safe_syscall implementation works: - * - * The basic setup is that we make the host syscall via a known - * section of host native assembly. If a signal occurs, our signal - * handler checks the interrupted host PC against the addresse of that - * known section. If the PC is before or at the address of the syscall - * instruction then we change the PC to point at a "return - * -TARGET_ERESTARTSYS" code path instead, and then exit the signal handler - * (causing the safe_syscall() call to immediately return that value). - * Then in the main.c loop if we see this magic return value we adjust - * the guest PC to wind it back to before the system call, and invoke - * the guest signal handler as usual. - * - * This winding-back will happen in two cases: - * (1) signal came in just before we took the host syscall (a race); - * in this case we'll take the guest signal and have another go - * at the syscall afterwards, and this is indistinguishable for the - * guest from the timing having been different such that the guest - * signal really did win the race - * (2) signal came in while the host syscall was blocking, and the - * host kernel decided the syscall should be restarted; - * in this case we want to restart the guest syscall also, and so - * rewinding is the right thing. (Note that "restart" semantics mean - * "first call the signal handler, then reattempt the syscall".) - * The other situation to consider is when a signal came in while the - * host syscall was blocking, and the host kernel decided that the syscall - * should not be restarted; in this case QEMU's host signal handler will - * be invoked with the PC pointing just after the syscall instruction, - * with registers indicating an EINTR return; the special code in the - * handler will not kick in, and we will return EINTR to the guest as - * we should. - * - * Notice that we can leave the host kernel to make the decision for - * us about whether to do a restart of the syscall or not; we do not - * need to check SA_RESTART flags in QEMU or distinguish the various - * kinds of restartability. - */ -#ifdef HAVE_SAFE_SYSCALL -/* The core part of this function is implemented in assembly */ -extern long safe_syscall_base(int *pending, long number, ...); - -#define safe_syscall(...) \ - ({ \ - long ret_; \ - int *psp_ = &((TaskState *)thread_cpu->opaque)->signal_pending; \ - ret_ = safe_syscall_base(psp_, __VA_ARGS__); \ - if (is_error(ret_)) { \ - errno = -ret_; \ - ret_ = -1; \ - } \ - ret_; \ - }) - -#else - -/* - * Fallback for architectures which don't yet provide a safe-syscall assembly - * fragment; note that this is racy! - * This should go away when all host architectures have been updated. - */ -#define safe_syscall syscall - -#endif - /* syscall.c */ int host_to_target_waitstatus(int status); diff --git a/linux-user/safe-syscall.h b/linux-user/safe-syscall.h new file mode 100644 index 000000000000..6bc039026284 --- /dev/null +++ b/linux-user/safe-syscall.h @@ -0,0 +1,154 @@ +/* + * safe-syscall.h: prototypes for linux-user signal-race-safe syscalls + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#ifndef LINUX_USER_SAFE_SYSCALL_H +#define LINUX_USER_SAFE_SYSCALL_H + +/** + * safe_syscall: + * @int number: number of system call to make + * ...: arguments to the system call + * + * Call a system call if guest signal not pending. + * This has the same API as the libc syscall() function, except that it + * may return -1 with errno == TARGET_ERESTARTSYS if a signal was pending. + * + * Returns: the system call result, or -1 with an error code in errno + * (Errnos are host errnos; we rely on TARGET_ERESTARTSYS not clashing + * with any of the host errno values.) + */ + +/* + * A guide to using safe_syscall() to handle interactions between guest + * syscalls and guest signals: + * + * Guest syscalls come in two flavours: + * + * (1) Non-interruptible syscalls + * + * These are guest syscalls that never get interrupted by signals and + * so never return EINTR. They can be implemented straightforwardly in + * QEMU: just make sure that if the implementation code has to make any + * blocking calls that those calls are retried if they return EINTR. + * It's also OK to implement these with safe_syscall, though it will be + * a little less efficient if a signal is delivered at the 'wrong' moment. + * + * Some non-interruptible syscalls need to be handled using block_signals() + * to block signals for the duration of the syscall. This mainly applies + * to code which needs to modify the data structures used by the + * host_signal_handler() function and the functions it calls, including + * all syscalls which change the thread's signal mask. + * + * (2) Interruptible syscalls + * + * These are guest syscalls that can be interrupted by signals and + * for which we need to either return EINTR or arrange for the guest + * syscall to be restarted. This category includes both syscalls which + * always restart (and in the kernel return -ERESTARTNOINTR), ones + * which only restart if there is no handler (kernel returns -ERESTARTNOHAND + * or -ERESTART_RESTARTBLOCK), and the most common kind which restart + * if the handler was registered with SA_RESTART (kernel returns + * -ERESTARTSYS). System calls which are only interruptible in some + * situations (like 'open') also need to be handled this way. + * + * Here it is important that the host syscall is made + * via this safe_syscall() function, and *not* via the host libc. + * If the host libc is used then the implementation will appear to work + * most of the time, but there will be a race condition where a + * signal could arrive just before we make the host syscall inside libc, + * and then then guest syscall will not correctly be interrupted. + * Instead the implementation of the guest syscall can use the safe_syscall + * function but otherwise just return the result or errno in the usual + * way; the main loop code will take care of restarting the syscall + * if appropriate. + * + * (If the implementation needs to make multiple host syscalls this is + * OK; any which might really block must be via safe_syscall(); for those + * which are only technically blocking (ie which we know in practice won't + * stay in the host kernel indefinitely) it's OK to use libc if necessary. + * You must be able to cope with backing out correctly if some safe_syscall + * you make in the implementation returns either -TARGET_ERESTARTSYS or + * EINTR though.) + * + * block_signals() cannot be used for interruptible syscalls. + * + * + * How and why the safe_syscall implementation works: + * + * The basic setup is that we make the host syscall via a known + * section of host native assembly. If a signal occurs, our signal + * handler checks the interrupted host PC against the addresse of that + * known section. If the PC is before or at the address of the syscall + * instruction then we change the PC to point at a "return + * -TARGET_ERESTARTSYS" code path instead, and then exit the signal handler + * (causing the safe_syscall() call to immediately return that value). + * Then in the main.c loop if we see this magic return value we adjust + * the guest PC to wind it back to before the system call, and invoke + * the guest signal handler as usual. + * + * This winding-back will happen in two cases: + * (1) signal came in just before we took the host syscall (a race); + * in this case we'll take the guest signal and have another go + * at the syscall afterwards, and this is indistinguishable for the + * guest from the timing having been different such that the guest + * signal really did win the race + * (2) signal came in while the host syscall was blocking, and the + * host kernel decided the syscall should be restarted; + * in this case we want to restart the guest syscall also, and so + * rewinding is the right thing. (Note that "restart" semantics mean + * "first call the signal handler, then reattempt the syscall".) + * The other situation to consider is when a signal came in while the + * host syscall was blocking, and the host kernel decided that the syscall + * should not be restarted; in this case QEMU's host signal handler will + * be invoked with the PC pointing just after the syscall instruction, + * with registers indicating an EINTR return; the special code in the + * handler will not kick in, and we will return EINTR to the guest as + * we should. + * + * Notice that we can leave the host kernel to make the decision for + * us about whether to do a restart of the syscall or not; we do not + * need to check SA_RESTART flags in QEMU or distinguish the various + * kinds of restartability. + */ +#ifdef HAVE_SAFE_SYSCALL +/* The core part of this function is implemented in assembly */ +extern long safe_syscall_base(int *pending, long number, ...); + +#define safe_syscall(...) \ + ({ \ + long ret_; \ + int *psp_ = &((TaskState *)thread_cpu->opaque)->signal_pending; \ + ret_ = safe_syscall_base(psp_, __VA_ARGS__); \ + if (is_error(ret_)) { \ + errno = -ret_; \ + ret_ = -1; \ + } \ + ret_; \ + }) + +#else + +/* + * Fallback for architectures which don't yet provide a safe-syscall assembly + * fragment; note that this is racy! + * This should go away when all host architectures have been updated. + */ +#define safe_syscall syscall + +#endif + +#endif diff --git a/linux-user/syscall.c b/linux-user/syscall.c index b6c8406e1dc0..9873830b465b 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -131,6 +131,7 @@ #include "signal-common.h" #include "loader.h" #include "user-mmap.h" +#include "safe-syscall.h" #include "qemu/guest-random.h" #include "qemu/selfmap.h" #include "user/syscall-trace.h"