[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCHv2] target-arm/abi32: check for segfault in do_kernel
From: |
Seraphime Kirkovski |
Subject: |
[Qemu-devel] [PATCHv2] target-arm/abi32: check for segfault in do_kernel_trap |
Date: |
Fri, 6 Jan 2017 17:21:16 +0100 |
Currently, the cmpxchg implementation tests whether the destination address
is readable:
- if it is, we read the value and continue with the comparison
- if isn't, i.e. access to addr would segfault, we assume that src != dest
rather than queuing a SIGSEGV.
The same problem exists in the case where src == dest: the code doesn't
check whether put_user_u32 succeeds.
This fixes both problems by sending a SIGSEGV when the destination address
is inaccessible.
Signed-off-by: Seraphime Kirkovski <address@hidden>
---
This accounts for Peter Maydell's remarks.
The refactoring here extracts the cmpxchg code from do_kernel_trap in its own
function.
linux-user/main.c | 72 ++++++++++++++++++++++++++++++++++++-------------------
1 file changed, 47 insertions(+), 25 deletions(-)
diff --git a/linux-user/main.c b/linux-user/main.c
index c1d5eb4d6f..ed38ebe0c1 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -441,7 +441,7 @@ static void arm_kernel_cmpxchg64_helper(CPUARMState *env)
uint32_t addr, cpsr;
target_siginfo_t info;
- /* Based on the 32 bit code in do_kernel_trap */
+ /* Based on the 32 bit code in arm_kernel_cmpxchg_helper */
/* XXX: This only works between threads, not between processes.
It's probably possible to implement this with native host
@@ -496,41 +496,63 @@ segv:
queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
}
+/*
+ * 32bit cmpxchg kernel user helper.
+ * See the note above arm_kernel_cmpxchg64_helper.
+ */
+static void arm_kernel_cmpxchg_helper(CPUARMState *env)
+{
+ uint32_t addr;
+ uint32_t cpsr;
+ uint32_t val;
+ target_siginfo_t info;
+
+ /* XXX: This only works between threads, not between processes.
+ It's probably possible to implement this with native host
+ operations. However things like ldrex/strex are much harder so
+ there's not much point trying. */
+ start_exclusive();
+ cpsr = cpsr_read(env);
+ addr = env->regs[2];
+ if (get_user_u32(val, addr))
+ goto segv;
+ if (val == env->regs[0]) {
+ val = env->regs[1];
+ if (put_user_u32(val, addr))
+ goto segv;
+ env->regs[0] = 0;
+ cpsr |= CPSR_C;
+ } else {
+ env->regs[0] = -1;
+ cpsr &= ~CPSR_C;
+ }
+ cpsr_write(env, cpsr, CPSR_C, CPSRWriteByInstr);
+ end_exclusive();
+ return;
+
+segv:
+ end_exclusive();
+ env->exception.vaddress = addr;
+ 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);
+}
+
/* Handle a jump to the kernel code page. */
static int
do_kernel_trap(CPUARMState *env)
{
uint32_t addr;
- uint32_t cpsr;
- uint32_t val;
switch (env->regs[15]) {
case 0xffff0fa0: /* __kernel_memory_barrier */
/* ??? No-op. Will need to do better for SMP. */
break;
case 0xffff0fc0: /* __kernel_cmpxchg */
- /* XXX: This only works between threads, not between processes.
- It's probably possible to implement this with native host
- operations. However things like ldrex/strex are much harder so
- there's not much point trying. */
- start_exclusive();
- cpsr = cpsr_read(env);
- addr = env->regs[2];
- /* FIXME: This should SEGV if the access fails. */
- if (get_user_u32(val, addr))
- val = ~env->regs[0];
- if (val == env->regs[0]) {
- val = env->regs[1];
- /* FIXME: Check for segfaults. */
- put_user_u32(val, addr);
- env->regs[0] = 0;
- cpsr |= CPSR_C;
- } else {
- env->regs[0] = -1;
- cpsr &= ~CPSR_C;
- }
- cpsr_write(env, cpsr, CPSR_C, CPSRWriteByInstr);
- end_exclusive();
+ arm_kernel_cmpxchg_helper(env);
break;
case 0xffff0fe0: /* __kernel_get_tls */
env->regs[0] = cpu_get_tls(env);
--
2.11.0
- [Qemu-devel] [PATCHv2] target-arm/abi32: check for segfault in do_kernel_trap,
Seraphime Kirkovski <=