From 1fb6a87d0bc3a730ec90578cfdfb2ef7ac5b5d11 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 13 Aug 2021 14:18:03 +0100 Subject: [PATCH 1/7] linux-user/aarch64: Set siginfo_t addr field for SIGTRAP signals When generating a TRAP_BRKPT SIGTRAP, set the siginfo_t addr field to the PC where the breakpoint/singlestep trap occurred; this is what the kernel does for this signal for this architecture. Fixes: Coverity 1459154 Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-Id: <20210813131809.28655-2-peter.maydell@linaro.org> Signed-off-by: Laurent Vivier --- linux-user/aarch64/cpu_loop.c | 1 + 1 file changed, 1 insertion(+) diff --git a/linux-user/aarch64/cpu_loop.c b/linux-user/aarch64/cpu_loop.c index 5425b85659..980e734e54 100644 --- a/linux-user/aarch64/cpu_loop.c +++ b/linux-user/aarch64/cpu_loop.c @@ -150,6 +150,7 @@ void cpu_loop(CPUARMState *env) info.si_signo = TARGET_SIGTRAP; info.si_errno = 0; info.si_code = TARGET_TRAP_BRKPT; + info._sifields._sigfault._addr = env->pc; queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); break; case EXCP_SEMIHOST: From 1af354120dc4d9187ee1162b95ac84aafd7c4df0 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 13 Aug 2021 14:18:04 +0100 Subject: [PATCH 2/7] linux-user/arm: Set siginfo_t addr field for SIGTRAP signals When generating a TRAP_BRKPT SIGTRAP, set the siginfo_t addr field to the PC where the breakpoint/singlestep trap occurred; this is what the kernel does for this signal for this architecture. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-Id: <20210813131809.28655-3-peter.maydell@linaro.org> Signed-off-by: Laurent Vivier --- linux-user/arm/cpu_loop.c | 1 + 1 file changed, 1 insertion(+) diff --git a/linux-user/arm/cpu_loop.c b/linux-user/arm/cpu_loop.c index 07032b3006..0900d18105 100644 --- a/linux-user/arm/cpu_loop.c +++ b/linux-user/arm/cpu_loop.c @@ -455,6 +455,7 @@ void cpu_loop(CPUARMState *env) info.si_signo = TARGET_SIGTRAP; info.si_errno = 0; info.si_code = TARGET_TRAP_BRKPT; + info._sifields._sigfault._addr = env->regs[15]; queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); break; case EXCP_KERNEL_TRAP: From babe6d5c88b587d30f72f31a81ce87610b68e952 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 13 Aug 2021 14:18:05 +0100 Subject: [PATCH 3/7] linux-user/arm: Use force_sig() to deliver fpa11 emulation SIGFPE In the Arm target code, when the fpa11 emulation code tells us we need to send the guest a SIGFPE, we do this with queue_signal(), but we are using the wrong si_type, and we aren't setting the _sifields union members corresponding to either the si_type we are using or the si_type we should be using. As the existing comment notes, the kernel code for this calls the old send_sig() function to deliver the signal. This eventually results in the kernel's signal handling code fabricating a siginfo_t with a SI_KERNEL code and a zero pid and uid. For QEMU this means we need to use QEMU_SI_KILL. We already have a function for that: force_sig() sets up the whole target_siginfo_t the way we need it. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-Id: <20210813131809.28655-4-peter.maydell@linaro.org> Signed-off-by: Laurent Vivier --- linux-user/arm/cpu_loop.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/linux-user/arm/cpu_loop.c b/linux-user/arm/cpu_loop.c index 0900d18105..fb78a1aab3 100644 --- a/linux-user/arm/cpu_loop.c +++ b/linux-user/arm/cpu_loop.c @@ -268,16 +268,13 @@ static bool emulate_arm_fpa11(CPUARMState *env, uint32_t opcode) ts->fpa.fpsr |= raise & ~enabled; if (raise & enabled) { - target_siginfo_t info = { }; - /* * The kernel's nwfpe emulator does not pass a real si_code. - * It merely uses send_sig(SIGFPE, current, 1). + * It merely uses send_sig(SIGFPE, current, 1), which results in + * __send_signal() filling out SI_KERNEL with pid and uid 0 (under + * the "SEND_SIG_PRIV" case). That's what our force_sig() does. */ - info.si_signo = TARGET_SIGFPE; - info.si_code = TARGET_SI_KERNEL; - - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + force_sig(TARGET_SIGFPE); } else { env->regs[15] += 4; } From 819121b9b08a41ccfcde2e18eb782f8f6b2912f1 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 13 Aug 2021 14:18:06 +0100 Subject: [PATCH 4/7] linux-user: Zero out target_siginfo_t in force_sig() The target_siginfo_t we populate in force_sig() will eventually get copied onto the target's stack. Zero it out so that any extra padding in the sifields union is consistently zero when the guest sees it. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-Id: <20210813131809.28655-5-peter.maydell@linaro.org> Signed-off-by: Laurent Vivier --- linux-user/signal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linux-user/signal.c b/linux-user/signal.c index f8346f5ec5..910b9dc6f7 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -641,7 +641,7 @@ void force_sig(int sig) { CPUState *cpu = thread_cpu; CPUArchState *env = cpu->env_ptr; - target_siginfo_t info; + target_siginfo_t info = {}; info.si_signo = sig; info.si_errno = 0; From af7969605eed067320fe9eca80f1aa35b67ec46d Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 13 Aug 2021 14:18:07 +0100 Subject: [PATCH 5/7] linux-user: Provide new force_sig_fault() function In many places in the linux-user code we need to queue a signal for the guest using the QEMU_SI_FAULT si_type. This requires that the caller sets up and passes us a target_siginfo, including setting the appropriate part of the _sifields union for the si_type. In a number of places the code forgets to set the _sifields union field. Provide a new force_sig_fault() function, which does the same thing as the Linux kernel function of that name -- it takes the signal number, the si_code value and the address to use in _sifields._sigfault, and assembles the target_siginfo itself. This makes the callsites simpler and means it's harder to forget to pass in an address value. We follow force_sig() and the kernel's force_sig_fault() in not requiring the caller to pass in the CPU pointer but always acting on the CPU of the current thread. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-Id: <20210813131809.28655-6-peter.maydell@linaro.org> Signed-off-by: Laurent Vivier --- linux-user/signal-common.h | 1 + linux-user/signal.c | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/linux-user/signal-common.h b/linux-user/signal-common.h index 58ea23f6ea..79511becb4 100644 --- a/linux-user/signal-common.h +++ b/linux-user/signal-common.h @@ -40,6 +40,7 @@ void tswap_siginfo(target_siginfo_t *tinfo, void set_sigmask(const sigset_t *set); void force_sig(int sig); void force_sigsegv(int oldsig); +void force_sig_fault(int sig, int code, abi_ulong addr); #if defined(TARGET_ARCH_HAS_SETUP_FRAME) void setup_frame(int sig, struct target_sigaction *ka, target_sigset_t *set, CPUArchState *env); diff --git a/linux-user/signal.c b/linux-user/signal.c index 910b9dc6f7..2038216455 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -651,6 +651,23 @@ void force_sig(int sig) queue_signal(env, info.si_signo, QEMU_SI_KILL, &info); } +/* + * Force a synchronously taken QEMU_SI_FAULT signal. For QEMU the + * 'force' part is handled in process_pending_signals(). + */ +void force_sig_fault(int sig, int code, abi_ulong addr) +{ + CPUState *cpu = thread_cpu; + CPUArchState *env = cpu->env_ptr; + target_siginfo_t info = {}; + + info.si_signo = sig; + info.si_errno = 0; + info.si_code = code; + info._sifields._sigfault._addr = addr; + queue_signal(env, sig, QEMU_SI_FAULT, &info); +} + /* Force a SIGSEGV if we couldn't write to memory trying to set * up the signal frame. oldsig is the signal we were trying to handle * at the point of failure. From 4c90f0ba9d949073935b320aefd133b4d369f70a Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 13 Aug 2021 14:18:08 +0100 Subject: [PATCH 6/7] linux-user/arm: Use force_sig_fault() Use the new force_sig_fault() function instead of setting up a target_siginfo_t and calling queue_signal(). Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-Id: <20210813131809.28655-7-peter.maydell@linaro.org> Signed-off-by: Laurent Vivier --- linux-user/arm/cpu_loop.c | 53 +++++++++++---------------------------- 1 file changed, 15 insertions(+), 38 deletions(-) diff --git a/linux-user/arm/cpu_loop.c b/linux-user/arm/cpu_loop.c index fb78a1aab3..ae09adcb95 100644 --- a/linux-user/arm/cpu_loop.c +++ b/linux-user/arm/cpu_loop.c @@ -94,7 +94,6 @@ static void arm_kernel_cmpxchg64_helper(CPUARMState *env) { uint64_t oldval, newval, val; uint32_t addr, cpsr; - target_siginfo_t info; /* Based on the 32 bit code in do_kernel_trap */ @@ -143,12 +142,9 @@ segv: end_exclusive(); /* We get the PC of the entry address - which is as good as anything, on a real kernel what you get depends on which mode it uses. */ - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; /* XXX: check env->error_code */ - info.si_code = TARGET_SEGV_MAPERR; - info._sifields._sigfault._addr = env->exception.vaddress; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + force_sig_fault(TARGET_SIGSEGV, TARGET_SEGV_MAPERR, + env->exception.vaddress); } /* Handle a jump to the kernel code page. */ @@ -286,8 +282,6 @@ void cpu_loop(CPUARMState *env) CPUState *cs = env_cpu(env); int trapnr; unsigned int n, insn; - target_siginfo_t info; - uint32_t addr; abi_ulong ret; for(;;) { @@ -322,11 +316,8 @@ void cpu_loop(CPUARMState *env) break; } - info.si_signo = TARGET_SIGILL; - info.si_errno = 0; - info.si_code = TARGET_ILL_ILLOPN; - info._sifields._sigfault._addr = env->regs[15]; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + force_sig_fault(TARGET_SIGILL, TARGET_ILL_ILLOPN, + env->regs[15]); } break; case EXCP_SWI: @@ -394,18 +385,14 @@ void cpu_loop(CPUARMState *env) * Otherwise SIGILL. This includes any SWI with * immediate not originally 0x9fxxxx, because * of the earlier XOR. + * Like the real kernel, we report the addr of the + * SWI in the siginfo si_addr but leave the PC + * pointing at the insn after the SWI. */ - info.si_signo = TARGET_SIGILL; - info.si_errno = 0; - info.si_code = TARGET_ILL_ILLTRP; - info._sifields._sigfault._addr = env->regs[15]; - if (env->thumb) { - info._sifields._sigfault._addr -= 2; - } else { - info._sifields._sigfault._addr -= 4; - } - queue_signal(env, info.si_signo, - QEMU_SI_FAULT, &info); + abi_ulong faultaddr = env->regs[15]; + faultaddr -= env->thumb ? 2 : 4; + force_sig_fault(TARGET_SIGILL, TARGET_ILL_ILLTRP, + faultaddr); } break; } @@ -436,24 +423,14 @@ void cpu_loop(CPUARMState *env) break; case EXCP_PREFETCH_ABORT: case EXCP_DATA_ABORT: - addr = env->exception.vaddress; - { - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - /* XXX: check env->error_code */ - info.si_code = TARGET_SEGV_MAPERR; - info._sifields._sigfault._addr = addr; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } + /* XXX: check env->error_code */ + force_sig_fault(TARGET_SIGSEGV, TARGET_SEGV_MAPERR, + env->exception.vaddress); break; case EXCP_DEBUG: case EXCP_BKPT: excp_debug: - info.si_signo = TARGET_SIGTRAP; - info.si_errno = 0; - info.si_code = TARGET_TRAP_BRKPT; - info._sifields._sigfault._addr = env->regs[15]; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->regs[15]); break; case EXCP_KERNEL_TRAP: if (do_kernel_trap(env)) From fce9608d02b665fdc3ab7b23f1a911ba6c66775b Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 13 Aug 2021 14:18:09 +0100 Subject: [PATCH 7/7] linux-user/aarch64: Use force_sig_fault() Use the new force_sig_fault() function instead of setting up a target_siginfo_t and calling queue_signal(). Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-Id: <20210813131809.28655-8-peter.maydell@linaro.org> Signed-off-by: Laurent Vivier --- linux-user/aarch64/cpu_loop.c | 33 ++++++++------------------------- 1 file changed, 8 insertions(+), 25 deletions(-) diff --git a/linux-user/aarch64/cpu_loop.c b/linux-user/aarch64/cpu_loop.c index 980e734e54..034b737435 100644 --- a/linux-user/aarch64/cpu_loop.c +++ b/linux-user/aarch64/cpu_loop.c @@ -79,9 +79,8 @@ void cpu_loop(CPUARMState *env) { CPUState *cs = env_cpu(env); - int trapnr, ec, fsc; + int trapnr, ec, fsc, si_code; abi_long ret; - target_siginfo_t info; for (;;) { cpu_exec_start(cs); @@ -110,18 +109,10 @@ void cpu_loop(CPUARMState *env) /* just indicate that signals should be handled asap */ break; case EXCP_UDEF: - info.si_signo = TARGET_SIGILL; - info.si_errno = 0; - info.si_code = TARGET_ILL_ILLOPN; - info._sifields._sigfault._addr = env->pc; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + force_sig_fault(TARGET_SIGILL, TARGET_ILL_ILLOPN, env->pc); break; case EXCP_PREFETCH_ABORT: case EXCP_DATA_ABORT: - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - info._sifields._sigfault._addr = env->exception.vaddress; - /* We should only arrive here with EC in {DATAABORT, INSNABORT}. */ ec = syn_get_ec(env->exception.syndrome); assert(ec == EC_DATAABORT || ec == EC_INSNABORT); @@ -130,28 +121,24 @@ void cpu_loop(CPUARMState *env) fsc = extract32(env->exception.syndrome, 0, 6); switch (fsc) { case 0x04 ... 0x07: /* Translation fault, level {0-3} */ - info.si_code = TARGET_SEGV_MAPERR; + si_code = TARGET_SEGV_MAPERR; break; case 0x09 ... 0x0b: /* Access flag fault, level {1-3} */ case 0x0d ... 0x0f: /* Permission fault, level {1-3} */ - info.si_code = TARGET_SEGV_ACCERR; + si_code = TARGET_SEGV_ACCERR; break; case 0x11: /* Synchronous Tag Check Fault */ - info.si_code = TARGET_SEGV_MTESERR; + si_code = TARGET_SEGV_MTESERR; break; default: g_assert_not_reached(); } - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + force_sig_fault(TARGET_SIGSEGV, si_code, env->exception.vaddress); break; case EXCP_DEBUG: case EXCP_BKPT: - info.si_signo = TARGET_SIGTRAP; - info.si_errno = 0; - info.si_code = TARGET_TRAP_BRKPT; - info._sifields._sigfault._addr = env->pc; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->pc); break; case EXCP_SEMIHOST: env->xregs[0] = do_common_semihosting(cs); @@ -171,11 +158,7 @@ void cpu_loop(CPUARMState *env) /* Check for MTE asynchronous faults */ if (unlikely(env->cp15.tfsr_el[0])) { env->cp15.tfsr_el[0] = 0; - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - info._sifields._sigfault._addr = 0; - info.si_code = TARGET_SEGV_MTEAERR; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + force_sig_fault(TARGET_SIGSEGV, TARGET_SEGV_MTEAERR, 0); } process_pending_signals(env);