@samitouri / QOSamiQemu / commits / 6bf4c0295c

linux-user/sh4: preserve T/M/Q bits across signal delivery

QEMU keeps the SH4 T, M and Q status-register bits outside env->sr, in the dedicated env->sr_t, env->sr_m and env->sr_q fields; cpu_read_sr() folds them back into the architectural SR value and cpu_write_sr() splits them back out. setup_sigcontext() saved the bare env->sr (so the T/M/Q bits were always zero in the signal frame) and restore_sigcontext() wrote the value straight back into env->sr without updating sr_t/sr_m/sr_q. As a result the T bit was never preserved across signal delivery: on sigreturn the interrupted code resumed with whatever T value the signal handler last left behind. Any conditional branch (or addc/subc/rotcl/div1, etc.) immediately following the interrupted instruction could then take the wrong path. This is the cause of the long-standing intermittent failures of the tests/tcg/multiarch/signals.c test on sh4, which was marked BROKEN. With a SIGRTMIN timer firing every millisecond across many threads, the race was hit a few percent of the time and corrupted the guest heap, surfacing as a SIGSEGV in memset, a malloc assertion, or an rseq registration abort. Traced on a deterministic rr recording: a cmp/hi set T=0, the timer signal interrupted the very next instruction (a bf), the handler left T=1, and the resumed bf took glibc calloc's MORECORE_CLEARS branch, using the old top-chunk size as the clear length for a freshly split small chunk and running memset off the end of the heap. Fix setup_sigcontext()/restore_sigcontext() to use cpu_read_sr() and cpu_write_sr() so the T, M and Q bits round-trip correctly, and drop the BROKEN annotation on the sh4 signals test. Fixes: c3b5bc8ab3 ("SH4: Signal handling for the user space emulator, by Magnus Damm.") Cc: qemu-stable@nongnu.org Reviewed-by: Yoshinori Sato <yoshinori.sato@nifty.com> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Matt Turner <mattst88@gmail.com> Signed-off-by: Helge Deller <deller@gmx.de>

Matt Turner committed May 25, 2026 at 11:26 UTC 6bf4c0295cccf74f3c5c0b674328b97d6fd1505c
2 files changed +10 -9
linux-user/sh4/signal.c
+10 -2
@@ -131,8 +131,10 @@ static void setup_sigcontext(struct target_sigcontext *sc,
131 COPY(gregs[14]); COPY(gregs[15]);
132 COPY(gbr); COPY(mach);
133 COPY(macl); COPY(pr);
134 - COPY(sr); COPY(pc);
134 + COPY(pc);
135 #undef COPY
136 + /* The T, M and Q bits live outside env->sr; fold them back in. */
137 + __put_user(cpu_read_sr(regs), &sc->sc_sr);
138
139 for (i=0; i<16; i++) {
140 __put_user(regs->fregs[i], &sc->sc_fpregs[i]);
@@ -159,8 +161,14 @@ static void restore_sigcontext(CPUSH4State *regs, struct target_sigcontext *sc)
161 COPY(gregs[14]); COPY(gregs[15]);
162 COPY(gbr); COPY(mach);
163 COPY(macl); COPY(pr);
162 - COPY(sr); COPY(pc);
164 + COPY(pc);
165 #undef COPY
166 + /* The T, M and Q bits live outside env->sr; unfold them. */
167 + {
168 + uint32_t sr;
169 + __get_user(sr, &sc->sc_sr);
170 + cpu_write_sr(regs, sr);
171 + }
172
173 for (i=0; i<16; i++) {
174 __get_user(regs->fregs[i], &sc->sc_fpregs[i]);
tests/tcg/sh4/Makefile.target
-7
@@ -3,13 +3,6 @@
3 # SuperH specific tweaks
4 #
5
6 -# This triggers failures for sh4-linux about 10% of the time.
7 -# Random SIGSEGV at unpredictable guest address, cause unknown.
8 -run-signals: signals
9 - $(call skip-test, $<, "BROKEN")
10 -run-plugin-signals-with-%:
11 - $(call skip-test, $<, "BROKEN")
12 -
6 VPATH += $(SRC_PATH)/tests/tcg/sh4
7
8 test-macl: CFLAGS += -O -g