Commit 4d330cee authored by Timothy E Baldwin's avatar Timothy E Baldwin Committed by Riku Voipio
Browse files

linux-user: Provide safe_syscall for fixing races between signals and syscalls



If a signal is delivered immediately before a blocking system call the
handler will only be called after the system call returns, which may be a
long time later or never.

This is fixed by using a function (safe_syscall) that checks if a guest
signal is pending prior to making a system call, and if so does not call the
system call and returns -TARGET_ERESTARTSYS. If a signal is received between
the check and the system call host_signal_handler() rewinds execution to
before the check. This rewinding has the effect of closing the race window
so that safe_syscall will reliably either (a) go into the host syscall
with no unprocessed guest signals pending or or (b) return
-TARGET_ERESTARTSYS so that the caller can deal with the signals.
Implementing this requires a per-host-architecture assembly language
fragment.

This will also resolve the mishandling of the SA_RESTART flag where
we would restart a host system call and not call the guest signal handler
until the syscall finally completed -- syscall restarting now always
happens at the guest syscall level so the guest signal handler will run.
(The host syscall will never be restarted because if the host kernel
rewinds the PC to point at the syscall insn for a restart then our
host_signal_handler() will see this and arrange the guest PC rewind.)

This commit contains the infrastructure for implementing safe_syscall
and the assembly language fragment for x86-64, but does not change any
syscalls to use it.

Signed-off-by: default avatarTimothy Edward Baldwin <T.E.Baldwin99@members.leeds.ac.uk>
Message-id: 1441497448-32489-14-git-send-email-T.E.Baldwin99@members.leeds.ac.uk
[PMM:
 * Avoid having an architecture if-ladder in configure by putting
   linux-user/host/$(ARCH) on the include path and including
   safe-syscall.inc.S from it
 * Avoid ifdef ladder in signal.c by creating new hostdep.h to hold
   host-architecture-specific things
 * Added copyright/license header to safe-syscall.inc.S
 * Rewrote commit message
 * Added comments to safe-syscall.inc.S
 * Changed calling convention of safe_syscall() to match syscall()
   (returns -1 and host error in errno on failure)
 * Added a long comment in qemu.h about how to use safe_syscall()
   to implement guest syscalls.
]
RV: squashed Peters "fixup! linux-user: compile on non-x86-64 hosts"
patch
Signed-off-by: default avatarPeter Maydell <peter.maydell@linaro.org>
parent 71a8f7fe
Loading
Loading
Loading
Loading
+6 −1
Original line number Diff line number Diff line
@@ -108,7 +108,12 @@ obj-$(CONFIG_LIBDECNUMBER) += libdecnumber/dpd/decimal128.o

ifdef CONFIG_LINUX_USER

QEMU_CFLAGS+=-I$(SRC_PATH)/linux-user/$(TARGET_ABI_DIR) -I$(SRC_PATH)/linux-user
# Note that we only add linux-user/host/$ARCH if it exists, and
# that it must come before linux-user/host/generic in the search path.
QEMU_CFLAGS+=-I$(SRC_PATH)/linux-user/$(TARGET_ABI_DIR) \
             $(patsubst %,-I%,$(wildcard $(SRC_PATH)/linux-user/host/$(ARCH))) \
             -I$(SRC_PATH)/linux-user/host/generic \
             -I$(SRC_PATH)/linux-user

obj-y += linux-user/
obj-y += gdbstub.o thunk.o user-exec.o
+2 −1
Original line number Diff line number Diff line
obj-y = main.o syscall.o strace.o mmap.o signal.o \
	elfload.o linuxload.o uaccess.o uname.o
	elfload.o linuxload.o uaccess.o uname.o \
	safe-syscall.o

obj-$(TARGET_HAS_BFLT) += flatload.o
obj-$(TARGET_I386) += vm86.o
+20 −0
Original line number Diff line number Diff line
/*
 * hostdep.h : fallback generic version of header for things
 * which are dependent on the host architecture
 *
 *  * Written by Peter Maydell <peter.maydell@linaro.org>
 *
 * Copyright (C) 2016 Linaro Limited
 *
 * This work is licensed under the terms of the GNU GPL, version 2 or later.
 * See the COPYING file in the top-level directory.
 */

#ifndef QEMU_HOSTDEP_H
#define QEMU_HOSTDEP_H

/* This is the fallback header which is only used if the host
 * architecture doesn't provide one in linux-user/host/$ARCH.
 */

#endif
+38 −0
Original line number Diff line number Diff line
/*
 * hostdep.h : things which are dependent on the host architecture
 *
 *  * Written by Peter Maydell <peter.maydell@linaro.org>
 *
 * Copyright (C) 2016 Linaro Limited
 *
 * This work is licensed under the terms of the GNU GPL, version 2 or later.
 * See the COPYING file in the top-level directory.
 */

#ifndef QEMU_HOSTDEP_H
#define QEMU_HOSTDEP_H

/* We have a safe-syscall.inc.S */
#define HAVE_SAFE_SYSCALL

#ifndef __ASSEMBLER__

/* These are defined by the safe-syscall.inc.S file */
extern char safe_syscall_start[];
extern char safe_syscall_end[];

/* Adjust the signal context to rewind out of safe-syscall if we're in it */
static inline void rewind_if_in_safe_syscall(void *puc)
{
    struct ucontext *uc = puc;
    greg_t *pcreg = &uc->uc_mcontext.gregs[REG_RIP];

    if (*pcreg > (uintptr_t)safe_syscall_start
        && *pcreg < (uintptr_t)safe_syscall_end) {
        *pcreg = (uintptr_t)safe_syscall_start;
    }
}

#endif /* __ASSEMBLER__ */

#endif
+81 −0
Original line number Diff line number Diff line
/*
 * safe-syscall.inc.S : host-specific assembly fragment
 * to handle signals occurring at the same time as system calls.
 * This is intended to be included by linux-user/safe-syscall.S
 *
 * Copyright (C) 2015 Timothy Edward Baldwin <T.E.Baldwin99@members.leeds.ac.uk>
 *
 * This work is licensed under the terms of the GNU GPL, version 2 or later.
 * See the COPYING file in the top-level directory.
 */

        .global safe_syscall_base
        .global safe_syscall_start
        .global safe_syscall_end
        .type   safe_syscall_base, @function

        /* This is the entry point for making a system call. The calling
         * convention here is that of a C varargs function with the
         * first argument an 'int *' to the signal_pending flag, the
         * second one the system call number (as a 'long'), and all further
         * arguments being syscall arguments (also 'long').
         * We return a long which is the syscall's return value, which
         * may be negative-errno on failure. Conversion to the
         * -1-and-errno-set convention is done by the calling wrapper.
         */
safe_syscall_base:
        /* This saves a frame pointer and aligns the stack for the syscall.
         * (It's unclear if the syscall ABI has the same stack alignment
         * requirements as the userspace function call ABI, but better safe than
         * sorry. Appendix A2 of http://www.x86-64.org/documentation/abi.pdf
         * does not list any ABI differences regarding stack alignment.)
         */
        push    %rbp

        /* The syscall calling convention isn't the same as the
         * C one:
         * we enter with rdi == *signal_pending
         *               rsi == syscall number
         *               rdx, rcx, r8, r9, (stack), (stack) == syscall arguments
         *               and return the result in rax
         * and the syscall instruction needs
         *               rax == syscall number
         *               rdi, rsi, rdx, r10, r8, r9 == syscall arguments
         *               and returns the result in rax
         * Shuffle everything around appropriately.
         * Note that syscall will trash rcx and r11.
         */
        mov     %rsi, %rax /* syscall number */
        mov     %rdi, %rbp /* signal_pending pointer */
        /* and the syscall arguments */
        mov     %rdx, %rdi
        mov     %rcx, %rsi
        mov     %r8,  %rdx
        mov     %r9,  %r10
        mov     16(%rsp), %r8
        mov     24(%rsp), %r9

        /* This next sequence of code works in conjunction with the
         * rewind_if_safe_syscall_function(). If a signal is taken
         * and the interrupted PC is anywhere between 'safe_syscall_start'
         * and 'safe_syscall_end' then we rewind it to 'safe_syscall_start'.
         * The code sequence must therefore be able to cope with this, and
         * the syscall instruction must be the final one in the sequence.
         */
safe_syscall_start:
        /* if signal_pending is non-zero, don't do the call */
        testl   $1, (%rbp)
        jnz     return_ERESTARTSYS
        syscall
safe_syscall_end:
        /* code path for having successfully executed the syscall */
        pop     %rbp
        ret

return_ERESTARTSYS:
        /* code path when we didn't execute the syscall */
        mov     $-TARGET_ERESTARTSYS, %rax
        pop     %rbp
        ret

        .size   safe_syscall_base, .-safe_syscall_base
Loading