linux-user: Make sigaltstack stacks per-thread
The alternate signal stack set up by the sigaltstack syscall is supposed to be per-thread. We were incorrectly implementing it as process-wide. This causes problems for guest binaries that rely on this. Notably the Go runtime does, and so we were seeing crashes caused by races where two guest threads might incorrectly both execute on the same stack simultaneously. Replace the global target_sigaltstack_used with a field sigaltstack_used in the TaskState, and make all the references to the old global instead get a pointer to the TaskState and use the field. Fixes: https://bugs.launchpad.net/qemu/+bug/1696773 Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Tested-by: Laurent Vivier <laurent@vivier.eu> Reviewed-by: Laurent Vivier <laurent@vivier.eu> Message-Id: <20190725131645.19501-1-peter.maydell@linaro.org> Signed-off-by: Laurent Vivier <laurent@vivier.eu>
This commit is contained in:
parent
fff3159900
commit
5bfce0b74f
@ -111,10 +111,11 @@ void setup_rt_frame(int sig, struct target_sigaction *ka,
|
|||||||
abi_ulong frame_addr, sp, haddr;
|
abi_ulong frame_addr, sp, haddr;
|
||||||
struct target_rt_sigframe *frame;
|
struct target_rt_sigframe *frame;
|
||||||
int i;
|
int i;
|
||||||
|
TaskState *ts = (TaskState *)thread_cpu->opaque;
|
||||||
|
|
||||||
sp = get_sp_from_cpustate(env);
|
sp = get_sp_from_cpustate(env);
|
||||||
if ((ka->sa_flags & TARGET_SA_ONSTACK) && !sas_ss_flags(sp)) {
|
if ((ka->sa_flags & TARGET_SA_ONSTACK) && !sas_ss_flags(sp)) {
|
||||||
sp = (target_sigaltstack_used.ss_sp + 0x7f) & ~0x3f;
|
sp = (ts->sigaltstack_used.ss_sp + 0x7f) & ~0x3f;
|
||||||
}
|
}
|
||||||
frame_addr = QEMU_ALIGN_UP(sp, 64);
|
frame_addr = QEMU_ALIGN_UP(sp, 64);
|
||||||
sp = frame_addr + PARISC_RT_SIGFRAME_SIZE32;
|
sp = frame_addr + PARISC_RT_SIGFRAME_SIZE32;
|
||||||
|
@ -180,6 +180,11 @@ void stop_all_tasks(void)
|
|||||||
void init_task_state(TaskState *ts)
|
void init_task_state(TaskState *ts)
|
||||||
{
|
{
|
||||||
ts->used = 1;
|
ts->used = 1;
|
||||||
|
ts->sigaltstack_used = (struct target_sigaltstack) {
|
||||||
|
.ss_sp = 0,
|
||||||
|
.ss_size = 0,
|
||||||
|
.ss_flags = TARGET_SS_DISABLE,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
CPUArchState *cpu_copy(CPUArchState *env)
|
CPUArchState *cpu_copy(CPUArchState *env)
|
||||||
|
@ -151,6 +151,8 @@ typedef struct TaskState {
|
|||||||
*/
|
*/
|
||||||
int signal_pending;
|
int signal_pending;
|
||||||
|
|
||||||
|
/* This thread's sigaltstack, if it has one */
|
||||||
|
struct target_sigaltstack sigaltstack_used;
|
||||||
} __attribute__((aligned(16))) TaskState;
|
} __attribute__((aligned(16))) TaskState;
|
||||||
|
|
||||||
extern char *exec_path;
|
extern char *exec_path;
|
||||||
|
@ -19,7 +19,6 @@
|
|||||||
|
|
||||||
#ifndef SIGNAL_COMMON_H
|
#ifndef SIGNAL_COMMON_H
|
||||||
#define SIGNAL_COMMON_H
|
#define SIGNAL_COMMON_H
|
||||||
extern struct target_sigaltstack target_sigaltstack_used;
|
|
||||||
|
|
||||||
int on_sig_stack(unsigned long sp);
|
int on_sig_stack(unsigned long sp);
|
||||||
int sas_ss_flags(unsigned long sp);
|
int sas_ss_flags(unsigned long sp);
|
||||||
|
@ -25,12 +25,6 @@
|
|||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
#include "signal-common.h"
|
#include "signal-common.h"
|
||||||
|
|
||||||
struct target_sigaltstack target_sigaltstack_used = {
|
|
||||||
.ss_sp = 0,
|
|
||||||
.ss_size = 0,
|
|
||||||
.ss_flags = TARGET_SS_DISABLE,
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct target_sigaction sigact_table[TARGET_NSIG];
|
static struct target_sigaction sigact_table[TARGET_NSIG];
|
||||||
|
|
||||||
static void host_signal_handler(int host_signum, siginfo_t *info,
|
static void host_signal_handler(int host_signum, siginfo_t *info,
|
||||||
@ -251,13 +245,17 @@ void set_sigmask(const sigset_t *set)
|
|||||||
|
|
||||||
int on_sig_stack(unsigned long sp)
|
int on_sig_stack(unsigned long sp)
|
||||||
{
|
{
|
||||||
return (sp - target_sigaltstack_used.ss_sp
|
TaskState *ts = (TaskState *)thread_cpu->opaque;
|
||||||
< target_sigaltstack_used.ss_size);
|
|
||||||
|
return (sp - ts->sigaltstack_used.ss_sp
|
||||||
|
< ts->sigaltstack_used.ss_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
int sas_ss_flags(unsigned long sp)
|
int sas_ss_flags(unsigned long sp)
|
||||||
{
|
{
|
||||||
return (target_sigaltstack_used.ss_size == 0 ? SS_DISABLE
|
TaskState *ts = (TaskState *)thread_cpu->opaque;
|
||||||
|
|
||||||
|
return (ts->sigaltstack_used.ss_size == 0 ? SS_DISABLE
|
||||||
: on_sig_stack(sp) ? SS_ONSTACK : 0);
|
: on_sig_stack(sp) ? SS_ONSTACK : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -266,17 +264,21 @@ abi_ulong target_sigsp(abi_ulong sp, struct target_sigaction *ka)
|
|||||||
/*
|
/*
|
||||||
* This is the X/Open sanctioned signal stack switching.
|
* This is the X/Open sanctioned signal stack switching.
|
||||||
*/
|
*/
|
||||||
|
TaskState *ts = (TaskState *)thread_cpu->opaque;
|
||||||
|
|
||||||
if ((ka->sa_flags & TARGET_SA_ONSTACK) && !sas_ss_flags(sp)) {
|
if ((ka->sa_flags & TARGET_SA_ONSTACK) && !sas_ss_flags(sp)) {
|
||||||
return target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size;
|
return ts->sigaltstack_used.ss_sp + ts->sigaltstack_used.ss_size;
|
||||||
}
|
}
|
||||||
return sp;
|
return sp;
|
||||||
}
|
}
|
||||||
|
|
||||||
void target_save_altstack(target_stack_t *uss, CPUArchState *env)
|
void target_save_altstack(target_stack_t *uss, CPUArchState *env)
|
||||||
{
|
{
|
||||||
__put_user(target_sigaltstack_used.ss_sp, &uss->ss_sp);
|
TaskState *ts = (TaskState *)thread_cpu->opaque;
|
||||||
|
|
||||||
|
__put_user(ts->sigaltstack_used.ss_sp, &uss->ss_sp);
|
||||||
__put_user(sas_ss_flags(get_sp_from_cpustate(env)), &uss->ss_flags);
|
__put_user(sas_ss_flags(get_sp_from_cpustate(env)), &uss->ss_flags);
|
||||||
__put_user(target_sigaltstack_used.ss_size, &uss->ss_size);
|
__put_user(ts->sigaltstack_used.ss_size, &uss->ss_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* siginfo conversion */
|
/* siginfo conversion */
|
||||||
@ -708,12 +710,13 @@ abi_long do_sigaltstack(abi_ulong uss_addr, abi_ulong uoss_addr, abi_ulong sp)
|
|||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
struct target_sigaltstack oss;
|
struct target_sigaltstack oss;
|
||||||
|
TaskState *ts = (TaskState *)thread_cpu->opaque;
|
||||||
|
|
||||||
/* XXX: test errors */
|
/* XXX: test errors */
|
||||||
if(uoss_addr)
|
if(uoss_addr)
|
||||||
{
|
{
|
||||||
__put_user(target_sigaltstack_used.ss_sp, &oss.ss_sp);
|
__put_user(ts->sigaltstack_used.ss_sp, &oss.ss_sp);
|
||||||
__put_user(target_sigaltstack_used.ss_size, &oss.ss_size);
|
__put_user(ts->sigaltstack_used.ss_size, &oss.ss_size);
|
||||||
__put_user(sas_ss_flags(sp), &oss.ss_flags);
|
__put_user(sas_ss_flags(sp), &oss.ss_flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -760,8 +763,8 @@ abi_long do_sigaltstack(abi_ulong uss_addr, abi_ulong uoss_addr, abi_ulong sp)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
target_sigaltstack_used.ss_sp = ss.ss_sp;
|
ts->sigaltstack_used.ss_sp = ss.ss_sp;
|
||||||
target_sigaltstack_used.ss_size = ss.ss_size;
|
ts->sigaltstack_used.ss_size = ss.ss_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (uoss_addr) {
|
if (uoss_addr) {
|
||||||
|
Loading…
Reference in New Issue
Block a user