Skip to content

Commit 3b894b6

Browse files
Mikulas Patockarth7680
Mikulas Patocka
authored andcommitted
linux-user/sh4: Fix crashes on signal delivery
sh4 uses gUSA (general UserSpace Atomicity) to provide atomicity on CPUs that don't have atomic instructions. A gUSA region that adds 1 to an atomic variable stored in @r2 looks like this: 4004b6: 03 c7 mova 4004c4 <gusa+0x10>,r0 4004b8: f3 61 mov r15,r1 4004ba: 09 00 nop 4004bc: fa ef mov #-6,r15 4004be: 22 63 mov.l @r2,r3 4004c0: 01 73 add #1,r3 4004c2: 32 22 mov.l r3,@r2 4004c4: 13 6f mov r1,r15 R0 contains a pointer to the end of the gUSA region R1 contains the saved stack pointer R15 contains negative length of the gUSA region When this region is interrupted by a signal, the kernel detects if R15 >= -128U. If yes, the kernel rolls back PC to the beginning of the region and restores SP by copying R1 to R15. The problem happens if we are interrupted by a signal at address 4004c4. R15 still holds the value -6, but the atomic value was already written by an instruction at address 4004c2. In this situation we can't undo the gUSA. The function unwind_gusa does nothing, the signal handler attempts to push a signal frame to the address -6 and crashes. This patch fixes it, so that if we are interrupted at the last instruction in a gUSA region, we copy R1 to R15 to restore the correct stack pointer and avoid crashing. There's another bug: if we are interrupted in a delay slot, we save the address of the instruction in the delay slot. We must save the address of the previous instruction. Cc: [email protected] Signed-off-by: Mikulas Patocka <[email protected]> Reviewed-by: Yoshinori Sato <[email protected]> Message-Id: <[email protected]> Reviewed-by: Richard Henderson <[email protected]> Signed-off-by: Richard Henderson <[email protected]>
1 parent 6fad9b4 commit 3b894b6

File tree

1 file changed

+8
-0
lines changed

1 file changed

+8
-0
lines changed

linux-user/sh4/signal.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,14 @@ static void unwind_gusa(CPUSH4State *regs)
104104

105105
/* Reset the SP to the saved version in R1. */
106106
regs->gregs[15] = regs->gregs[1];
107+
} else if (regs->gregs[15] >= -128u && regs->pc == regs->gregs[0]) {
108+
/* If we are on the last instruction of a gUSA region, we must reset
109+
the SP, otherwise we would be pushing the signal context to
110+
invalid memory. */
111+
regs->gregs[15] = regs->gregs[1];
112+
} else if (regs->flags & TB_FLAG_DELAY_SLOT) {
113+
/* If we are in a delay slot, push the previous instruction. */
114+
regs->pc -= 2;
107115
}
108116
}
109117

0 commit comments

Comments
 (0)