:p
atchew
Login
From: Helge Deller <deller@gmx.de> The following changes since commit 6d17fd91f6cf88df5cb2205e578640d72605cc43: Merge tag 'pull-nvme-20260518' of https://gitlab.com/birkelund/qemu into staging (2026-05-18 08:33:20 -0400) are available in the Git repository at: https://github.com/hdeller/qemu-hppa.git tags/linux-user-next-pull-request for you to fetch changes up to 1e000f9671aed33a3f86d2fdc61f991424628bb4: linux-user: Fix a memory leak when pthread_create fails (2026-05-19 15:49:35 +0200) ---------------------------------------------------------------- linux-user patches pthread_create() failure path cleanups, sh4 libunwind/sigtramp fixes and a (emulated) dynamic linker fix for AT_EXECFN. ---------------------------------------------------------------- Helge Deller (1): linux-user: Fix AT_EXECFN in AUXV for symlinked programs Matt Turner (2): linux-user/sh4: Fix target_ucontext tuc_link field type linux-user/sh4: Fix setup_sigtramp to match Linux kernel trampoline pattern Warner Losh (1): linux-user: Fix a memory leak when pthread_create fails linux-user/main.c | 6 +++-- linux-user/sh4/signal.c | 46 +++++++++++++++++++++++++++---------- linux-user/syscall.c | 24 ++++++++++++------- linux-user/user-internals.h | 1 + 4 files changed, 55 insertions(+), 22 deletions(-) -- 2.54.0
From: Helge Deller <deller@gmx.de> The AT_EXECFN entry in AUXV needs to keep the value which was used when the program was started. Especially for symlinked programs qemu should not try to resolve the realpath. Here is a reproducer: (arm64-chroot)root@p100:/# cd /usr/bin (arm64-chroot)root@p100:/usr/bin# ln -s echo testprog (arm64-chroot)root@p100:/usr/bin# LD_SHOW_AUXV=1 ./testprog | grep AT_EXECFN AT_EXECFN: ./testprog In this example, "./testprog" is the correct output, and not "/usr/bin/echo". This patch fixes parts of commit 258bec39 ("linux-user: Fix access to /proc/self/exe"). Fixes: 258bec39 ("linux-user: Fix access to /proc/self/exe") Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3379 Signed-off-by: Helge Deller <deller@gmx.de> --- linux-user/main.c | 6 ++++-- linux-user/syscall.c | 14 +++++++------- linux-user/user-internals.h | 1 + 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/linux-user/main.c b/linux-user/main.c index XXXXXXX..XXXXXXX 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp) } /* Resolve executable file name to full path name */ - if (realpath(exec_path, real_exec_path)) { - exec_path = real_exec_path; + /* Keep how we started the program in exec_path, e.g. "./my_program" */ + /* Store real path in real_exec_path, e.g. "/usr/local/bin/my_program" */ + if (!realpath(exec_path, real_exec_path)) { + printf("Could not resolve %s\n", exec_path); } /* diff --git a/linux-user/syscall.c b/linux-user/syscall.c index XXXXXXX..XXXXXXX 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -XXX,XX +XXX,XX @@ static int maybe_do_fake_open(CPUArchState *cpu_env, int dirfd, return -1; } if (safe) { - return safe_openat(dirfd, exec_path, flags, mode); + return safe_openat(dirfd, real_exec_path, flags, mode); } else { - return openat(dirfd, exec_path, flags, mode); + return openat(dirfd, real_exec_path, flags, mode); } } @@ -XXX,XX +XXX,XX @@ ssize_t do_guest_readlink(const char *pathname, char *buf, size_t bufsiz) * Don't worry about sign mismatch as earlier mapping * logic would have thrown a bad address error. */ - ret = MIN(strlen(exec_path), bufsiz); + ret = MIN(strlen(real_exec_path), bufsiz); /* We cannot NUL terminate the string. */ - memcpy(buf, exec_path, ret); + memcpy(buf, real_exec_path, ret); } else { ret = readlink(path(pathname), buf, bufsiz); } @@ -XXX,XX +XXX,XX @@ static int do_execv(CPUArchState *cpu_env, int dirfd, const char *exe = p; if (is_proc_myself(p, "exe")) { - exe = exec_path; + exe = real_exec_path; } ret = is_execveat ? safe_execveat(dirfd, exe, argp, envp, flags) @@ -XXX,XX +XXX,XX @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, * Don't worry about sign mismatch as earlier mapping * logic would have thrown a bad address error. */ - ret = MIN(strlen(exec_path), arg4); + ret = MIN(strlen(real_exec_path), arg4); /* We cannot NUL terminate the string. */ - memcpy(p2, exec_path, ret); + memcpy(p2, real_exec_path, ret); } else { ret = get_errno(readlinkat(arg1, path(p), p2, arg4)); } diff --git a/linux-user/user-internals.h b/linux-user/user-internals.h index XXXXXXX..XXXXXXX 100644 --- a/linux-user/user-internals.h +++ b/linux-user/user-internals.h @@ -XXX,XX +XXX,XX @@ #include "exec/translation-block.h" extern char *exec_path; +extern char real_exec_path[PATH_MAX]; void init_task_state(TaskState *ts); void task_settid(TaskState *); void stop_all_tasks(void); -- 2.54.0
From: Matt Turner <mattst88@gmail.com> tuc_link is declared as 'struct target_ucontext *', which is a HOST pointer. On a 64-bit host running a 32-bit SH4 target, this is 8 bytes instead of the 4 bytes the target expects, padding pushes tuc_mcontext 8 bytes past its correct offset. When a signal handler receives ucontext_t *, every field accessed through uc_mcontext (gregs[], pc, pr, ...) is read from the wrong address. In particular the saved PC comes back as a garbage stack value, which breaks any code that initialises a libunwind cursor from the signal context. Fix it by using abi_ulong, which is always sized to the target ABI (4 bytes for SH4), matching the layout the kernel and glibc agree on. This is the same pattern used by arm/signal.c. Also remove the (unsigned long *) cast from the __put_user that zeros tuc_link. The cast was harmless when tuc_link was pointer-sized (8 bytes matching unsigned long on a 64-bit host), but after the type change __put_user's sizeof dispatch would select stq_le_p (8-byte write) for a now-4-byte field, silently overwriting the start of tuc_stack. Neither this fix nor the companion setup_sigtramp fix is independently sufficient: this fix corrects register values read from the signal context but libunwind still cannot detect the frame without the correct trampoline pattern; that fix makes the frame detectable but register reads remain garbage without the correct ucontext layout. Together they fix the following libunwind tests on a 64-bit host: Gtest-sig-context, Gtest-trace, Ltest-init-local-signal, Ltest-sig-context, Ltest-trace Signed-off-by: Matt Turner <mattst88@gmail.com> Cc: qemu-stable@nongnu.org Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Helge Deller <deller@gmx.de> --- linux-user/sh4/signal.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/linux-user/sh4/signal.c b/linux-user/sh4/signal.c index XXXXXXX..XXXXXXX 100644 --- a/linux-user/sh4/signal.c +++ b/linux-user/sh4/signal.c @@ -XXX,XX +XXX,XX @@ struct target_sigframe struct target_ucontext { target_ulong tuc_flags; - struct target_ucontext *tuc_link; + abi_ulong tuc_link; target_stack_t tuc_stack; struct target_sigcontext tuc_mcontext; target_sigset_t tuc_sigmask; /* mask last for extensibility */ @@ -XXX,XX +XXX,XX @@ void setup_rt_frame(int sig, struct target_sigaction *ka, /* Create the ucontext. */ __put_user(0, &frame->uc.tuc_flags); - __put_user(0, (unsigned long *)&frame->uc.tuc_link); + __put_user(0, &frame->uc.tuc_link); target_save_altstack(&frame->uc.tuc_stack, regs); setup_sigcontext(&frame->uc.tuc_mcontext, regs, set->sig[0]); -- 2.54.0
From: Matt Turner <mattst88@gmail.com> QEMU used MOVW(2) (0x9300), which loads the syscall number from PC+4, instead of the kernel's MOVW(7) (0x9305), which loads from PC+14. The kernel uses five "or r0,r0" nop pads between TRAP_NOARG and the syscall number word to reach that offset. libunwind's unw_is_signal_frame checks for the exact kernel byte pattern 0xc3109305 at the frame PC, so QEMU's compact layout was not detected, breaking unwinding through signal frames. Expand each trampoline from 6 to 16 bytes matching the kernel layout defined in arch/sh/kernel/signal_32.c: #define MOVW(n) (0x9300|((n)-2)) /* Move mem word at PC+n to R3 */ #define TRAP_NOARG 0xc310 /* Syscall w/no args (NR in R3) */ #define OR_R0_R0 0x200b /* or r0,r0 (insert to avoid hardware bug) */ __put_user(MOVW(7), &frame->retcode[0]); /* 0x9305 */ __put_user(TRAP_NOARG, &frame->retcode[1]); /* 0xc310 */ __put_user(OR_R0_R0, &frame->retcode[2]); /* 0x200b */ __put_user(OR_R0_R0, &frame->retcode[3]); /* 0x200b */ __put_user(OR_R0_R0, &frame->retcode[4]); /* 0x200b */ __put_user(OR_R0_R0, &frame->retcode[5]); /* 0x200b */ __put_user(OR_R0_R0, &frame->retcode[6]); /* 0x200b */ __put_user((__NR_sigreturn), &frame->retcode[7]); The first two halfwords (MOVW(7) || TRAP_NOARG = 0xc3109305) form the 32-bit value libunwind checks at the frame PC, followed by two OR_R0_R0 halfwords (0x200b200b) at PC+4. The same layout applies to the rt_sigreturn trampoline (lines 366-373 of signal_32.c). Neither this fix nor the companion tuc_link fix is independently sufficient: this fix makes signal frames detectable but register reads remain garbage without the correct ucontext layout; that fix corrects the ucontext layout but libunwind still cannot detect the frame without the correct trampoline pattern. Together they fix the following libunwind tests on a 64-bit host: Gtest-sig-context, Gtest-trace, Ltest-init-local-signal, Ltest-sig-context, Ltest-trace Signed-off-by: Matt Turner <mattst88@gmail.com> Cc: qemu-stable@nongnu.org Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Helge Deller <deller@gmx.de> --- linux-user/sh4/signal.c | 42 +++++++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/linux-user/sh4/signal.c b/linux-user/sh4/signal.c index XXXXXXX..XXXXXXX 100644 --- a/linux-user/sh4/signal.c +++ b/linux-user/sh4/signal.c @@ -XXX,XX +XXX,XX @@ badframe: return -QEMU_ESIGRETURN; } +/* + * "or r0,r0" nop used by the Linux kernel inline sigreturn trampolines to + * avoid a hardware bug (OR_R0_R0 in arch/sh/kernel/signal_32.c). Five of + * these nops follow TRAP_NOARG, placing the syscall number word 14 bytes + * past the MOVW(7) instruction (at MOVW(7)'s load offset). This yields the + * fixed 16-byte layout that libunwind's unw_is_signal_frame detects: + * [MOVW(7), TRAP_NOARG, 5x NOP_OR, .word syscall_nr] + */ +#define NOP_OR 0x200b + void setup_sigtramp(abi_ulong sigtramp_page) { - uint16_t *tramp = lock_user(VERIFY_WRITE, sigtramp_page, 2 * 6, 0); + uint16_t *tramp = lock_user(VERIFY_WRITE, sigtramp_page, 2 * 16, 0); assert(tramp != NULL); + /* sigreturn trampoline (non-RT) at offset 0 */ default_sigreturn = sigtramp_page; - __put_user(MOVW(2), &tramp[0]); + __put_user(MOVW(7), &tramp[0]); __put_user(TRAP_NOARG, &tramp[1]); - __put_user(TARGET_NR_sigreturn, &tramp[2]); - - default_rt_sigreturn = sigtramp_page + 6; - __put_user(MOVW(2), &tramp[3]); - __put_user(TRAP_NOARG, &tramp[4]); - __put_user(TARGET_NR_rt_sigreturn, &tramp[5]); - - unlock_user(tramp, sigtramp_page, 2 * 6); + __put_user(NOP_OR, &tramp[2]); + __put_user(NOP_OR, &tramp[3]); + __put_user(NOP_OR, &tramp[4]); + __put_user(NOP_OR, &tramp[5]); + __put_user(NOP_OR, &tramp[6]); + __put_user(TARGET_NR_sigreturn, &tramp[7]); + + /* rt_sigreturn trampoline at offset 16 */ + default_rt_sigreturn = sigtramp_page + 16; + __put_user(MOVW(7), &tramp[8]); + __put_user(TRAP_NOARG, &tramp[9]); + __put_user(NOP_OR, &tramp[10]); + __put_user(NOP_OR, &tramp[11]); + __put_user(NOP_OR, &tramp[12]); + __put_user(NOP_OR, &tramp[13]); + __put_user(NOP_OR, &tramp[14]); + __put_user(TARGET_NR_rt_sigreturn, &tramp[15]); + + unlock_user(tramp, sigtramp_page, 2 * 16); } -- 2.54.0
From: Warner Losh <imp@bsdimp.com> Fix one of the TODO items when creating a new thread: release the copied cpu and free the task state. Signed-off-by: Warner Losh <imp@bsdimp.com> Reviewed-by: Helge Deller <deller@gmx.de> Signed-off-by: Helge Deller <deller@gmx.de> --- linux-user/syscall.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index XXXXXXX..XXXXXXX 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -XXX,XX +XXX,XX @@ static int do_fork(CPUArchState *env, unsigned int flags, abi_ulong newsp, cpu->random_seed = qemu_guest_random_seed_thread_part1(); ret = pthread_create(&info.thread, &attr, clone_func, &info); - /* TODO: Free new CPU state if thread creation failed. */ sigprocmask(SIG_SETMASK, &info.sigmask, NULL); pthread_attr_destroy(&attr); @@ -XXX,XX +XXX,XX @@ static int do_fork(CPUArchState *env, unsigned int flags, abi_ulong newsp, pthread_cond_wait(&info.cond, &info.mutex); ret = info.tid; } else { + errno = ret; ret = -1; + object_unparent(OBJECT(new_cpu)); + object_unref(OBJECT(new_cpu)); +#ifdef TARGET_AARCH64 + if (ts->gcs_base) { + target_munmap(ts->gcs_base, ts->gcs_size); + } +#endif + g_free(ts); } pthread_mutex_unlock(&info.mutex); pthread_cond_destroy(&info.cond); -- 2.54.0
The following changes since commit 2db91528542672cf0db78b3f2cc0e22b36302b38: Merge tag 'pull-vfio-20260527' of https://github.com/legoater/qemu into staging (2026-05-27 14:45:58 -0400) are available in the Git repository at: https://github.com/hdeller/qemu-hppa.git tags/linux-user-next-pull-request for you to fetch changes up to 07e701716b43da1a86c1045871c1eb613cf5e53a: linux-user: Move cpu_copy() to user-internals.h (2026-05-29 23:40:53 +0200) ---------------------------------------------------------------- linux user patches A series of patches for linux-user, specifically many FPU fixes in signal handling code for sh4, mips, ppc and s390x (from Matt Turner), a madvise() improvement (from me), and qemu header cleanups (from Peter Maydell). --- v3: Fix build failure due to unknown MADV_COLLAPSE constant in madivise() patch v2: Dropped the "ARM cortex-m55 program loading fix" and the FPU alpha patch ---------------------------------------------------------------- Helge Deller (2): linux-user: Implement finer grained madivse() syscall linux-user: Fix typo in function documentation for pgb_addr_set() Matt Turner (6): linux-user/ppc: restore fp_status from FPSCR on sigreturn linux-user/mips: save/restore FCSR across signal delivery linux-user/sh4: preserve T/M/Q bits across signal delivery linux-user/sh4: restore FP rounding mode on sigreturn target/sh4: sync fp_status when gdb writes FPSCR linux-user/s390x: restore fpu_status rounding mode from FPC on sigreturn Peter Maydell (2): linux-user: Move init_main_thread() prototype to user-internals.h linux-user: Move cpu_copy() to user-internals.h linux-user/elfload.c | 2 +- linux-user/mips/signal.c | 7 +++++++ linux-user/mmap.c | 34 +++++++++++++++++++++++++++++++++- linux-user/ppc/signal.c | 2 +- linux-user/qemu.h | 5 ----- linux-user/s390x/signal.c | 6 +++++- linux-user/sh4/signal.c | 19 ++++++++++++++++--- linux-user/user-internals.h | 18 ++++++++++++++++++ target/mips/cpu.h | 3 +++ target/mips/fpu.c | 5 +++++ target/s390x/cpu.h | 1 + target/s390x/tcg/fpu_helper.c | 20 ++++++++++++++------ target/sh4/cpu.h | 3 +++ target/sh4/gdbstub.c | 2 +- target/sh4/op_helper.c | 7 ++++++- tests/tcg/sh4/Makefile.target | 7 ------- 16 files changed, 114 insertions(+), 27 deletions(-) -- 2.54.0
From: Matt Turner <mattst88@gmail.com> restore_user_regs() restores the PPC FPSCR with a direct assignment: env->fpscr = (uint32_t) fpscr; ppc_store_fpscr() exists precisely to write FPSCR and keep the derived env->fp_status in sync: it calls fpscr_set_rounding_mode() to update the softfloat rounding mode, and set_float_rebias_overflow/underflow() to reflect the FP_OE/FP_UE enable bits. The direct assignment bypasses all of this. On sigreturn, interrupted code resumes with whatever rounding mode and overflow/underflow-rebias state the signal handler last installed in fp_status, rather than the state that was saved at signal delivery. Replace the direct assign with ppc_store_fpscr(). The FPSCR_MTFS_MASK applied inside ppc_store_fpscr() only excludes the computed FP_FEX and FP_VX bits, which it re-derives correctly from the exception and enable bits in the restored value. Fixes: bcd4933a23 ("linux-user: ppc signal handling") Cc: qemu-stable@nongnu.org Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Matt Turner <mattst88@gmail.com> Signed-off-by: Helge Deller <deller@gmx.de> --- linux-user/ppc/signal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linux-user/ppc/signal.c b/linux-user/ppc/signal.c index XXXXXXX..XXXXXXX 100644 --- a/linux-user/ppc/signal.c +++ b/linux-user/ppc/signal.c @@ -XXX,XX +XXX,XX @@ static void restore_user_regs(CPUPPCState *env, __get_user(*fpr, &frame->mc_fregs[i]); } __get_user(fpscr, &frame->mc_fregs[32]); - env->fpscr = (uint32_t) fpscr; + ppc_store_fpscr(env, (uint32_t) fpscr); } #if !defined(TARGET_PPC64) -- 2.54.0
From: Matt Turner <mattst88@gmail.com> QEMU keeps the MIPS FPU control/status register (FCSR, fcr31) in env->active_fpu.fcr31. The rounding mode, flush-to-zero (FS), and NaN-2008 mode bits in fcr31 are reflected into the derived env->active_fpu.fp_status via set_float_rounding_mode() and friends; every architectural write to FCSR goes through helper_ctc1() which calls restore_fp_status() to keep the two in sync. Both target_sigcontext variants (O32 and N32/N64) have an sc_fpc_csr field that holds FCSR, but setup_sigcontext() never wrote it and restore_sigcontext() never read it. As a result: - The signal frame always delivered sc_fpc_csr == 0 to the handler, so sigaction(SA_SIGINFO) handlers that inspect the interrupted context see the wrong FCSR. - On sigreturn, active_fpu.fcr31 retained whatever value the signal handler last installed (if any), and active_fpu.fp_status was never resynced. Interrupted code resumed with the wrong rounding mode, FS flag, and NaN-2008 semantics. Fix setup_sigcontext() to save fcr31 into sc_fpc_csr. Fix restore_sigcontext() to read it back (masked to fcr31_rw_bitmask as the kernel does) and call cpu_mips_restore_fp_status() to resync fp_status from the restored fcr31. Add cpu_mips_restore_fp_status() in target/mips/fpu.c (which already defines ieee_rm and includes fpu_helper.h), and declare it in cpu.h. Fixes: 084d0497a0 ("mips-linux-user: Save and restore fpu and dsp from sigcontext") Cc: qemu-stable@nongnu.org Signed-off-by: Matt Turner <mattst88@gmail.com> Signed-off-by: Helge Deller <deller@gmx.de> --- linux-user/mips/signal.c | 7 +++++++ target/mips/cpu.h | 3 +++ target/mips/fpu.c | 5 +++++ 3 files changed, 15 insertions(+) diff --git a/linux-user/mips/signal.c b/linux-user/mips/signal.c index XXXXXXX..XXXXXXX 100644 --- a/linux-user/mips/signal.c +++ b/linux-user/mips/signal.c @@ -XXX,XX +XXX,XX @@ static inline void setup_sigcontext(CPUMIPSState *regs, for (i = 0; i < 32; ++i) { __put_user(regs->active_fpu.fpr[i].d, &sc->sc_fpregs[i]); } + __put_user(regs->active_fpu.fcr31, &sc->sc_fpc_csr); } static inline void @@ -XXX,XX +XXX,XX @@ restore_sigcontext(CPUMIPSState *regs, struct target_sigcontext *sc) for (i = 0; i < 32; ++i) { __get_user(regs->active_fpu.fpr[i].d, &sc->sc_fpregs[i]); } + { + uint32_t fcr31; + __get_user(fcr31, &sc->sc_fpc_csr); + regs->active_fpu.fcr31 = fcr31 & regs->active_fpu.fcr31_rw_bitmask; + cpu_mips_restore_fp_status(regs); + } } /* diff --git a/target/mips/cpu.h b/target/mips/cpu.h index XXXXXXX..XXXXXXX 100644 --- a/target/mips/cpu.h +++ b/target/mips/cpu.h @@ -XXX,XX +XXX,XX @@ void cpu_mips_clock_init(MIPSCPU *cpu); /* helper.c */ target_ulong exception_resume_pc(CPUMIPSState *env); +/* fpu.c */ +void cpu_mips_restore_fp_status(CPUMIPSState *env); + /** * mips_cpu_create_with_clock: * @typename: a MIPS CPU type. diff --git a/target/mips/fpu.c b/target/mips/fpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/mips/fpu.c +++ b/target/mips/fpu.c @@ -XXX,XX +XXX,XX @@ const FloatRoundMode ieee_rm[4] = { float_round_down }; +void cpu_mips_restore_fp_status(CPUMIPSState *env) +{ + restore_fp_status(env); +} + const char fregnames[32][4] = { "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15", -- 2.54.0
From: Matt Turner <mattst88@gmail.com> 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> --- linux-user/sh4/signal.c | 12 ++++++++++-- tests/tcg/sh4/Makefile.target | 7 ------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/linux-user/sh4/signal.c b/linux-user/sh4/signal.c index XXXXXXX..XXXXXXX 100644 --- a/linux-user/sh4/signal.c +++ b/linux-user/sh4/signal.c @@ -XXX,XX +XXX,XX @@ static void setup_sigcontext(struct target_sigcontext *sc, COPY(gregs[14]); COPY(gregs[15]); COPY(gbr); COPY(mach); COPY(macl); COPY(pr); - COPY(sr); COPY(pc); + COPY(pc); #undef COPY + /* The T, M and Q bits live outside env->sr; fold them back in. */ + __put_user(cpu_read_sr(regs), &sc->sc_sr); for (i=0; i<16; i++) { __put_user(regs->fregs[i], &sc->sc_fpregs[i]); @@ -XXX,XX +XXX,XX @@ static void restore_sigcontext(CPUSH4State *regs, struct target_sigcontext *sc) COPY(gregs[14]); COPY(gregs[15]); COPY(gbr); COPY(mach); COPY(macl); COPY(pr); - COPY(sr); COPY(pc); + COPY(pc); #undef COPY + /* The T, M and Q bits live outside env->sr; unfold them. */ + { + uint32_t sr; + __get_user(sr, &sc->sc_sr); + cpu_write_sr(regs, sr); + } for (i=0; i<16; i++) { __get_user(regs->fregs[i], &sc->sc_fpregs[i]); diff --git a/tests/tcg/sh4/Makefile.target b/tests/tcg/sh4/Makefile.target index XXXXXXX..XXXXXXX 100644 --- a/tests/tcg/sh4/Makefile.target +++ b/tests/tcg/sh4/Makefile.target @@ -XXX,XX +XXX,XX @@ # SuperH specific tweaks # -# This triggers failures for sh4-linux about 10% of the time. -# Random SIGSEGV at unpredictable guest address, cause unknown. -run-signals: signals - $(call skip-test, $<, "BROKEN") -run-plugin-signals-with-%: - $(call skip-test, $<, "BROKEN") - VPATH += $(SRC_PATH)/tests/tcg/sh4 test-macl: CFLAGS += -O -g -- 2.54.0
From: Matt Turner <mattst88@gmail.com> The SH4 FPSCR rounding-mode (RM) and denormal (DN) bits are not held only in env->fpscr: they are also reflected into the derived env->fp_status via set_float_rounding_mode()/set_flush_to_zero(). The guest keeps the two in sync by routing every write to FPSCR through helper_ld_fpscr(). restore_sigcontext() wrote the saved value straight into env->fpscr and never touched env->fp_status, so on sigreturn the interrupted code resumed with whatever FP rounding mode and flush-to-zero setting the signal handler last installed. (regs->flags = 0 forces the FR/SZ/PR TB flags to be recomputed, but fp_status is runtime float state, not a TB flag, so it was left stale.) This is the FP analogue of the T/M/Q bit problem just fixed for the integer status register. Factor the FPSCR -> fp_status synchronisation out of helper_ld_fpscr() into cpu_load_fpscr() and use it from restore_sigcontext() so the rounding mode round-trips correctly across signal delivery. 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> --- linux-user/sh4/signal.c | 7 ++++++- target/sh4/cpu.h | 3 +++ target/sh4/op_helper.c | 7 ++++++- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/linux-user/sh4/signal.c b/linux-user/sh4/signal.c index XXXXXXX..XXXXXXX 100644 --- a/linux-user/sh4/signal.c +++ b/linux-user/sh4/signal.c @@ -XXX,XX +XXX,XX @@ static void restore_sigcontext(CPUSH4State *regs, struct target_sigcontext *sc) for (i=0; i<16; i++) { __get_user(regs->fregs[i], &sc->sc_fpregs[i]); } - __get_user(regs->fpscr, &sc->sc_fpscr); + /* Resync the derived float_status state, not just env->fpscr. */ + { + uint32_t fpscr; + __get_user(fpscr, &sc->sc_fpscr); + cpu_load_fpscr(regs, fpscr); + } __get_user(regs->fpul, &sc->sc_fpul); regs->tra = -1; /* disable syscall checks */ diff --git a/target/sh4/cpu.h b/target/sh4/cpu.h index XXXXXXX..XXXXXXX 100644 --- a/target/sh4/cpu.h +++ b/target/sh4/cpu.h @@ -XXX,XX +XXX,XX @@ static inline void cpu_write_sr(CPUSH4State *env, uint32_t sr) env->sr = sr & ~((1u << SR_M) | (1u << SR_Q) | (1u << SR_T)); } +/* Set FPSCR and the derived float_status rounding/flush-to-zero state. */ +void cpu_load_fpscr(CPUSH4State *env, uint32_t val); + #endif /* SH4_CPU_H */ diff --git a/target/sh4/op_helper.c b/target/sh4/op_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/sh4/op_helper.c +++ b/target/sh4/op_helper.c @@ -XXX,XX +XXX,XX @@ void helper_macw(CPUSH4State *env, int32_t arg0, int32_t arg1) } } -void helper_ld_fpscr(CPUSH4State *env, uint32_t val) +void cpu_load_fpscr(CPUSH4State *env, uint32_t val) { env->fpscr = val & FPSCR_MASK; if ((val & FPSCR_RM_MASK) == FPSCR_RM_ZERO) { @@ -XXX,XX +XXX,XX @@ void helper_ld_fpscr(CPUSH4State *env, uint32_t val) set_flush_to_zero((val & FPSCR_DN) != 0, &env->fp_status); } +void helper_ld_fpscr(CPUSH4State *env, uint32_t val) +{ + cpu_load_fpscr(env, val); +} + static void update_fpscr(CPUSH4State *env, uintptr_t retaddr) { int xcpt, cause, enable; -- 2.54.0
From: Matt Turner <mattst88@gmail.com> sh4_cpu_gdb_write_register() wrote the incoming FPSCR value straight into env->fpscr, leaving the derived env->fp_status (rounding mode and flush-to-zero) stale, so a gdb-initiated FPSCR change did not take effect for subsequent FP operations. Use cpu_load_fpscr() instead, the same way the adjacent case already uses cpu_write_sr() for SR. 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> --- target/sh4/gdbstub.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/sh4/gdbstub.c b/target/sh4/gdbstub.c index XXXXXXX..XXXXXXX 100644 --- a/target/sh4/gdbstub.c +++ b/target/sh4/gdbstub.c @@ -XXX,XX +XXX,XX @@ int superh_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) env->fpul = ldl_p(mem_buf); break; case 24: - env->fpscr = ldl_p(mem_buf); + cpu_load_fpscr(env, ldl_p(mem_buf)); break; case 25 ... 40: if (env->fpscr & FPSCR_FR) { -- 2.54.0
From: Matt Turner <mattst88@gmail.com> QEMU keeps the s390x floating-point control register (FPC) in env->fpc. The rounding mode bits [2:0] of FPC are reflected into the derived env->fpu_status via set_float_rounding_mode(); every architectural write to FPC goes through HELPER(sfpc) which keeps the two in sync. restore_sigregs() restored FPC with a direct assignment: __get_user(env->fpc, &sc->fpregs.fpc); This wrote env->fpc correctly but never updated env->fpu_status, so on sigreturn the interrupted code resumed with whatever rounding mode the signal handler last installed in fpu_status. Factor the two-step "write fpc + sync fpu_status" logic out of HELPER(sfpc) into cpu_s390x_load_fpc(), declare it in cpu.h, and call it from restore_sigregs() in place of the direct assignment. cpu_s390x_load_fpc() partially reuses the sanity check from HELPER(sfpc): if the FPC value has an invalid rounding mode or reserved bits set, it falls back to 0, matching the kernel's fpu_lfpc_safe() behavior where a corrupt signal frame value causes a specification exception and 0 is used instead. HELPER(sfpc) now calls cpu_s390x_load_fpc() after its full specification-exception check, including the FEAT_FLOATING_POINT_EXT test that is not needed for the signal restore path. Fixes: 2941e0fa05 ("linux-user/s390x: Save/restore fpc when handling a signal") Cc: qemu-stable@nongnu.org Signed-off-by: Matt Turner <mattst88@gmail.com> Signed-off-by: Helge Deller <deller@gmx.de> --- linux-user/s390x/signal.c | 6 +++++- target/s390x/cpu.h | 1 + target/s390x/tcg/fpu_helper.c | 20 ++++++++++++++------ 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/linux-user/s390x/signal.c b/linux-user/s390x/signal.c index XXXXXXX..XXXXXXX 100644 --- a/linux-user/s390x/signal.c +++ b/linux-user/s390x/signal.c @@ -XXX,XX +XXX,XX @@ static void restore_sigregs(CPUS390XState *env, target_sigregs *sc) for (i = 0; i < 16; i++) { __get_user(env->aregs[i], &sc->regs.acrs[i]); } - __get_user(env->fpc, &sc->fpregs.fpc); + { + uint32_t fpc; + __get_user(fpc, &sc->fpregs.fpc); + cpu_s390x_load_fpc(env, fpc); + } for (i = 0; i < 16; i++) { __get_user(*get_freg(env, i), &sc->fpregs.fprs[i]); } diff --git a/target/s390x/cpu.h b/target/s390x/cpu.h index XXXXXXX..XXXXXXX 100644 --- a/target/s390x/cpu.h +++ b/target/s390x/cpu.h @@ -XXX,XX +XXX,XX @@ void s390_init_sigp(void); /* helper.c */ void s390_cpu_set_psw(CPUS390XState *env, uint64_t mask, uint64_t addr); uint64_t s390_cpu_get_psw_mask(CPUS390XState *env); +void cpu_s390x_load_fpc(CPUS390XState *env, uint32_t fpc); /* outside of target/s390x/ */ S390CPU *s390_cpu_addr2state(uint16_t cpu_addr); diff --git a/target/s390x/tcg/fpu_helper.c b/target/s390x/tcg/fpu_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/s390x/tcg/fpu_helper.c +++ b/target/s390x/tcg/fpu_helper.c @@ -XXX,XX +XXX,XX @@ static const int fpc_to_rnd[8] = { float_round_to_odd, }; +void cpu_s390x_load_fpc(CPUS390XState *env, uint32_t fpc) +{ + /* + * Mimic kernel fpu_lfpc_safe(): a corrupt signal frame value that would + * trigger a specification exception instead results in FPC being set to 0. + */ + if (fpc_to_rnd[fpc & 0x7] == -1 || fpc & 0x03030088u) { + fpc = 0; + } + env->fpc = fpc; + set_float_rounding_mode(fpc_to_rnd[fpc & 0x7], &env->fpu_status); +} + /* set fpc */ void HELPER(sfpc)(CPUS390XState *env, uint64_t fpc) { @@ -XXX,XX +XXX,XX @@ void HELPER(sfpc)(CPUS390XState *env, uint64_t fpc) (!s390_has_feat(S390_FEAT_FLOATING_POINT_EXT) && fpc & 0x4)) { tcg_s390_program_interrupt(env, PGM_SPECIFICATION, GETPC()); } - - /* Install everything in the main FPC. */ - env->fpc = fpc; - - /* Install the rounding mode in the shadow fpu_status. */ - set_float_rounding_mode(fpc_to_rnd[fpc & 0x7], &env->fpu_status); + cpu_s390x_load_fpc(env, fpc); } /* set fpc and signal */ -- 2.54.0
Although most madvise() values are hints, some are important and are checked by userspace, especially by security-relevant applications like BoringSLL. So, return -EINVAL for those functions which we don't emulate. Signed-off-by: Helge Deller <deller@gmx.de> Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3489 --- linux-user/mmap.c | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/linux-user/mmap.c b/linux-user/mmap.c index XXXXXXX..XXXXXXX 100644 --- a/linux-user/mmap.c +++ b/linux-user/mmap.c @@ -XXX,XX +XXX,XX @@ abi_long target_madvise(abi_ulong start, abi_ulong len_in, int advice) case TARGET_MADV_KEEPONFORK: /* parisc */ advice = MADV_KEEPONFORK; break; - /* we do not care about the other MADV_xxx values yet */ + /* all other MADV_xxx values are the same across architectures */ } /* @@ -XXX,XX +XXX,XX @@ abi_long target_madvise(abi_ulong start, abi_ulong len_in, int advice) */ mmap_lock(); switch (advice) { + case MADV_NORMAL: + case MADV_RANDOM: + case MADV_SEQUENTIAL: + case MADV_WILLNEED: + case MADV_DOFORK: + case MADV_FREE: + case MADV_COLD: + case MADV_PAGEOUT: + ret = 0; /* OK */ + break; + case MADV_REMOVE: + ret = -EOPNOTSUPP; + break; case MADV_DONTDUMP: page_set_flags(start, start + len - 1, PAGE_DONTDUMP, 0); break; @@ -XXX,XX +XXX,XX @@ abi_long target_madvise(abi_ulong start, abi_ulong len_in, int advice) page_reset_target_data(start, start + len - 1); } } + break; + case MADV_DONTFORK: + case MADV_HWPOISON: + case MADV_MERGEABLE: + case MADV_UNMERGEABLE: + case MADV_HUGEPAGE: + case MADV_NOHUGEPAGE: + case MADV_POPULATE_READ: + case MADV_POPULATE_WRITE: +#ifdef MADV_COLLAPSE + case MADV_COLLAPSE: +#endif + case -1: /* BoringSSL uses -1 to check if the environment is broken */ + ret = -EINVAL; + break; + default: + qemu_log_mask(LOG_UNIMP, "Unhandled madvise(%d) call.\n", advice); + ret = -EINVAL; /* not yet known advise */ + break; } mmap_unlock(); -- 2.54.0
The third parameter is called guest_hiaddr. Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Signed-off-by: Helge Deller <deller@gmx.de> --- linux-user/elfload.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index XXXXXXX..XXXXXXX 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -XXX,XX +XXX,XX @@ static bool pgb_try_mmap_set(const PGBAddrs *ga, uintptr_t base, uintptr_t brk) * pgb_addr_set: * @ga: output set of guest addrs * @guest_loaddr: guest image low address - * @guest_loaddr: guest image high address + * @guest_hiaddr: guest image high address * @identity: create for identity mapping * * Fill in @ga with the image, COMMPAGE and NULL page. -- 2.54.0
From: Peter Maydell <peter.maydell@linaro.org> The init_main_thread() prototype is needed only by code internal to linux-user/, so it doesn't need to be in qemu.h (which is also pulled in by various files outside linux-user/). Move the prototype to user-internals.h, and give it a documentation comment. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Helge Deller <deller@gmx.de> Signed-off-by: Helge Deller <deller@gmx.de> --- linux-user/qemu.h | 2 -- linux-user/user-internals.h | 15 +++++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/linux-user/qemu.h b/linux-user/qemu.h index XXXXXXX..XXXXXXX 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -XXX,XX +XXX,XX @@ void *lock_user_string(abi_ulong guest_addr); /* Clone cpu state */ CPUArchState *cpu_copy(CPUArchState *env); -void init_main_thread(CPUState *cs, struct image_info *info); - #endif /* QEMU_H */ diff --git a/linux-user/user-internals.h b/linux-user/user-internals.h index XXXXXXX..XXXXXXX 100644 --- a/linux-user/user-internals.h +++ b/linux-user/user-internals.h @@ -XXX,XX +XXX,XX @@ static inline void begin_parallel_context(CPUState *cs) } } +/** + * init_main_thread: Set CPU state for main thread + * @cs: CPU context to set + * @info: information about the image being loaded + * + * This function must be provided by the per-target code. It should + * set the initial CPU state based on the information about the + * starting binary in @image_info. This will be at a minimum setting + * the initial guest program counter and stack pointer; it should + * also set up any other guest register values where the Linux ABI + * defines that they start set to some other value than what the + * guest CPU architecture gives you out of reset. + */ +void init_main_thread(CPUState *cs, struct image_info *info); + /* * Include target-specific struct and function definitions; * they may need access to the target-independent structures -- 2.54.0
From: Peter Maydell <peter.maydell@linaro.org> We only use cpu_copy() inside linux-user, so we don't need to have the prototype in qemu.h available to code outside linux-user; move it to user-internals.h. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Helge Deller <deller@gmx.de> Signed-off-by: Helge Deller <deller@gmx.de> --- linux-user/qemu.h | 3 --- linux-user/user-internals.h | 3 +++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/linux-user/qemu.h b/linux-user/qemu.h index XXXXXXX..XXXXXXX 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -XXX,XX +XXX,XX @@ void *lock_user_string(abi_ulong guest_addr); #define unlock_user_struct(host_ptr, guest_addr, copy) \ unlock_user(host_ptr, guest_addr, (copy) ? sizeof(*host_ptr) : 0) -/* Clone cpu state */ -CPUArchState *cpu_copy(CPUArchState *env); - #endif /* QEMU_H */ diff --git a/linux-user/user-internals.h b/linux-user/user-internals.h index XXXXXXX..XXXXXXX 100644 --- a/linux-user/user-internals.h +++ b/linux-user/user-internals.h @@ -XXX,XX +XXX,XX @@ static inline void begin_parallel_context(CPUState *cs) */ void init_main_thread(CPUState *cs, struct image_info *info); +/* Clone cpu state */ +CPUArchState *cpu_copy(CPUArchState *env); + /* * Include target-specific struct and function definitions; * they may need access to the target-independent structures -- 2.54.0