diff mbox series

[24/30] bsd-user/signal.c: setup_frame

Message ID 20220109161923.85683-25-imp@bsdimp.com
State New
Headers show
Series bsd-user: upstream our signal implementation | expand

Commit Message

Warner Losh Jan. 9, 2022, 4:19 p.m. UTC
setup_frame sets up a signalled stack frame. Associated routines to
extract the pointer to the stack frame and to support alternate stacks.

Signed-off-by: Stacey Son <sson@FreeBSD.org>
Signed-off-by: Kyle Evans <kevans@freebsd.org>
Signed-off-by: Warner Losh <imp@bsdimp.com>
---
 bsd-user/signal.c | 166 ++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 144 insertions(+), 22 deletions(-)

Comments

Peter Maydell Jan. 14, 2022, 11:40 a.m. UTC | #1
On Sun, 9 Jan 2022 at 16:36, Warner Losh <imp@bsdimp.com> wrote:
>
> setup_frame sets up a signalled stack frame. Associated routines to
> extract the pointer to the stack frame and to support alternate stacks.
>
> Signed-off-by: Stacey Son <sson@FreeBSD.org>
> Signed-off-by: Kyle Evans <kevans@freebsd.org>
> Signed-off-by: Warner Losh <imp@bsdimp.com>
> ---
>  bsd-user/signal.c | 166 ++++++++++++++++++++++++++++++++++++++++------
>  1 file changed, 144 insertions(+), 22 deletions(-)
>
> diff --git a/bsd-user/signal.c b/bsd-user/signal.c
> index 8dadc9a39a7..8e1427553da 100644
> --- a/bsd-user/signal.c
> +++ b/bsd-user/signal.c
> @@ -30,11 +30,27 @@
>   * fork.
>   */
>
> +static target_stack_t target_sigaltstack_used = {
> +    .ss_sp = 0,
> +    .ss_size = 0,
> +    .ss_flags = TARGET_SS_DISABLE,
> +};

sigaltstacks are per-thread, so this needs to be in the TaskState,
not global. (We fixed this in linux-user in commit 5bfce0b74fbd5d5
in 2019: the change is relatively small.)

> +
>  static struct target_sigaction sigact_table[TARGET_NSIG];
>  static void host_signal_handler(int host_sig, siginfo_t *info, void *puc);
>  static void target_to_host_sigset_internal(sigset_t *d,
>          const target_sigset_t *s);
>
> +static inline int on_sig_stack(unsigned long sp)
> +{
> +    return sp - target_sigaltstack_used.ss_sp < target_sigaltstack_used.ss_size;
> +}
> +
> +static inline int sas_ss_flags(unsigned long sp)
> +{
> +    return target_sigaltstack_used.ss_size == 0 ? SS_DISABLE : on_sig_stack(sp)
> +        ? SS_ONSTACK : 0;
> +}
>
>  int host_to_target_signal(int sig)
>  {
> @@ -336,28 +352,6 @@ void queue_signal(CPUArchState *env, int sig, target_siginfo_t *info)
>      return;
>  }
>
> -static int fatal_signal(int sig)
> -{
> -
> -    switch (sig) {
> -    case TARGET_SIGCHLD:
> -    case TARGET_SIGURG:
> -    case TARGET_SIGWINCH:
> -    case TARGET_SIGINFO:
> -        /* Ignored by default. */
> -        return 0;
> -    case TARGET_SIGCONT:
> -    case TARGET_SIGSTOP:
> -    case TARGET_SIGTSTP:
> -    case TARGET_SIGTTIN:
> -    case TARGET_SIGTTOU:
> -        /* Job control signals.  */
> -        return 0;
> -    default:
> -        return 1;
> -    }
> -}

There wasn't any need to move this function, I think ?

> -
>  /*
>   * Force a synchronously taken QEMU_SI_FAULT signal. For QEMU the
>   * 'force' part is handled in process_pending_signals().
> @@ -484,6 +478,134 @@ static void host_signal_handler(int host_sig, siginfo_t *info, void *puc)
>      cpu_exit(thread_cpu);
>  }
>
> +static int fatal_signal(int sig)
> +{
> +
> +    switch (sig) {
> +    case TARGET_SIGCHLD:
> +    case TARGET_SIGURG:
> +    case TARGET_SIGWINCH:
> +    case TARGET_SIGINFO:
> +        /* Ignored by default. */
> +        return 0;
> +    case TARGET_SIGCONT:
> +    case TARGET_SIGSTOP:
> +    case TARGET_SIGTSTP:
> +    case TARGET_SIGTTIN:
> +    case TARGET_SIGTTOU:
> +        /* Job control signals.  */
> +        return 0;
> +    default:
> +        return 1;
> +    }
> +}
> +
> +static inline abi_ulong get_sigframe(struct target_sigaction *ka,
> +        CPUArchState *regs, size_t frame_size)
> +{
> +    abi_ulong sp;
> +
> +    /* Use default user stack */
> +    sp = get_sp_from_cpustate(regs);
> +
> +    if ((ka->sa_flags & TARGET_SA_ONSTACK) && (sas_ss_flags(sp) == 0)) {
> +        sp = target_sigaltstack_used.ss_sp +
> +            target_sigaltstack_used.ss_size;
> +    }
> +
> +#if defined(TARGET_MIPS) || defined(TARGET_ARM)
> +    return (sp - frame_size) & ~7;
> +#elif defined(TARGET_AARCH64)
> +    return (sp - frame_size) & ~15;
> +#else
> +    return sp - frame_size;
> +#endif

We don't need to do it in this patchseries, but you should strongly
consider pulling the architecture-specifics out in a way that
avoids this kind of ifdef ladder.

> +}
> +
> +/* compare to mips/mips/pm_machdep.c and sparc64/sparc64/machdep.c sendsig() */
> +static void setup_frame(int sig, int code, struct target_sigaction *ka,
> +    target_sigset_t *set, target_siginfo_t *tinfo, CPUArchState *regs)
> +{
> +    struct target_sigframe *frame;
> +    abi_ulong frame_addr;
> +    int i;
> +
> +    frame_addr = get_sigframe(ka, regs, sizeof(*frame));
> +    trace_user_setup_frame(regs, frame_addr);
> +    if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) {
> +        goto give_sigsegv;

FreeBSD for Arm (haven't checked other BSDs or other archs)
gives a SIGILL for the "can't write signal frame to stack"
case, I think:
https://github.com/freebsd/freebsd-src/blob/main/sys/arm/arm/exec_machdep.c#L316
I don't understand why they picked SIGILL, SIGSEGV seems much more
logical to me, but we should follow the kernel behaviour.

> +    }
> +
> +    memset(frame, 0, sizeof(*frame));
> +#if defined(TARGET_MIPS)
> +    int mflags = on_sig_stack(frame_addr) ? TARGET_MC_ADD_MAGIC :
> +        TARGET_MC_SET_ONSTACK | TARGET_MC_ADD_MAGIC;
> +#else
> +    int mflags = 0;
> +#endif
> +    if (get_mcontext(regs, &frame->sf_uc.uc_mcontext, mflags)) {
> +        goto give_sigsegv;

The FreeBSD kernel get_mcontext() can't fail -- why can ours ?
(This matters because SIGSEGV may not be the right response to
whatever the failure case is.)

> +    }
> +
> +    for (i = 0; i < TARGET_NSIG_WORDS; i++) {
> +        if (__put_user(set->__bits[i], &frame->sf_uc.uc_sigmask.__bits[i])) {
> +            goto give_sigsegv;

__get_user() and __put_user() in QEMU can't fail, so you don't
need to check for errors here, unlike the non-double-underscore
versions. At some point you might want to take the current linux-user
versions of these user-access functions/macros: it looks like bsd-user
has an older version which doesn't handle the case where the guest
has looser alignment restrictions than the host. The new ones
actually expand to do { ... } while(0) statements which won't
be valid inside an if() condition.

(Historical note: the reason QEMU's __put_user/__get_user ever had
return values at all is that the linux-user code was copy-and-pasted
from the Linux kernel. In the Linux kernel handling of writing
data to userspace is/was error-checked on every write, whereas
QEMU does the "is this writable" test once with the lock_user
function and then can assume all the writes to that area succeed.
But we still started with a lot of copy-pasted code that was doing
unnecessary checks on __put_user and __get_user return values.
FreeBSD seems to handle write-checking in yet a third way, by
assembling the struct in kernel-space and checking for writability
once at the end when it copies the whole block out to userspace.)

> +        }
> +    }
> +
> +    if (tinfo) {
> +        frame->sf_si.si_signo = tinfo->si_signo;
> +        frame->sf_si.si_errno = tinfo->si_errno;
> +        frame->sf_si.si_code = tinfo->si_code;
> +        frame->sf_si.si_pid = tinfo->si_pid;
> +        frame->sf_si.si_uid = tinfo->si_uid;
> +        frame->sf_si.si_status = tinfo->si_status;
> +        frame->sf_si.si_addr = tinfo->si_addr;
> +
> +        if (TARGET_SIGILL == sig || TARGET_SIGFPE == sig ||
> +                TARGET_SIGSEGV == sig || TARGET_SIGBUS == sig ||
> +                TARGET_SIGTRAP == sig) {
> +            frame->sf_si._reason._fault._trapno = tinfo->_reason._fault._trapno;
> +        }
> +
> +        /*
> +         * If si_code is one of SI_QUEUE, SI_TIMER, SI_ASYNCIO, or
> +         * SI_MESGQ, then si_value contains the application-specified
> +         * signal value. Otherwise, the contents of si_value are
> +         * undefined.
> +         */
> +        if (SI_QUEUE == code || SI_TIMER == code || SI_ASYNCIO == code ||
> +                SI_MESGQ == code) {
> +            frame->sf_si.si_value.sival_int = tinfo->si_value.sival_int;
> +        }
> +
> +        if (SI_TIMER == code) {
> +            frame->sf_si._reason._timer._timerid =
> +                tinfo->_reason._timer._timerid;
> +            frame->sf_si._reason._timer._overrun =
> +                tinfo->_reason._timer._overrun;
> +        }
> +
> +#ifdef SIGPOLL
> +        if (SIGPOLL == sig) {
> +            frame->sf_si._reason._band = tinfo->_reason._band;
> +        }
> +#endif

This seems to be yet a third set of the logic for handling
target_siginfo_t's union, to go along with tswap_siginfo() and
host_to_target_siginfo_noswap(), except that the logic here is
different. linux-user calls tswap_siginfo() in its signal-frame
setup code.

> +
> +    }
> +
> +    if (set_sigtramp_args(regs, sig, frame, frame_addr, ka)) {
> +        goto give_sigsegv;
> +    }

set_sigtramp_args() can't fail. (Not sure why it has a non-void
return type.)

> +
> +    unlock_user_struct(frame, frame_addr, 1);
> +    return;
> +
> +give_sigsegv:
> +    unlock_user_struct(frame, frame_addr, 1);
> +    force_sig(TARGET_SIGSEGV);
> +}
> +
>  void signal_init(void)
>  {
>      TaskState *ts = (TaskState *)thread_cpu->opaque;
> --
> 2.33.1

thanks
-- PMM
Warner Losh Jan. 17, 2022, 6:58 a.m. UTC | #2
On Fri, Jan 14, 2022 at 4:40 AM Peter Maydell <peter.maydell@linaro.org>
wrote:

> On Sun, 9 Jan 2022 at 16:36, Warner Losh <imp@bsdimp.com> wrote:
> >
> > setup_frame sets up a signalled stack frame. Associated routines to
> > extract the pointer to the stack frame and to support alternate stacks.
> >
> > Signed-off-by: Stacey Son <sson@FreeBSD.org>
> > Signed-off-by: Kyle Evans <kevans@freebsd.org>
> > Signed-off-by: Warner Losh <imp@bsdimp.com>
> > ---
> >  bsd-user/signal.c | 166 ++++++++++++++++++++++++++++++++++++++++------
> >  1 file changed, 144 insertions(+), 22 deletions(-)
> >
> > diff --git a/bsd-user/signal.c b/bsd-user/signal.c
> > index 8dadc9a39a7..8e1427553da 100644
> > --- a/bsd-user/signal.c
> > +++ b/bsd-user/signal.c
> > @@ -30,11 +30,27 @@
> >   * fork.
> >   */
> >
> > +static target_stack_t target_sigaltstack_used = {
> > +    .ss_sp = 0,
> > +    .ss_size = 0,
> > +    .ss_flags = TARGET_SS_DISABLE,
> > +};
>
> sigaltstacks are per-thread, so this needs to be in the TaskState,
> not global. (We fixed this in linux-user in commit 5bfce0b74fbd5d5
> in 2019: the change is relatively small.)
>

Done. I saw go mentioned, which I know doesn't work today, so it's
a good step in that direction...


> > +
> >  static struct target_sigaction sigact_table[TARGET_NSIG];
> >  static void host_signal_handler(int host_sig, siginfo_t *info, void
> *puc);
> >  static void target_to_host_sigset_internal(sigset_t *d,
> >          const target_sigset_t *s);
> >
> > +static inline int on_sig_stack(unsigned long sp)
> > +{
> > +    return sp - target_sigaltstack_used.ss_sp <
> target_sigaltstack_used.ss_size;
> > +}
> > +
> > +static inline int sas_ss_flags(unsigned long sp)
> > +{
> > +    return target_sigaltstack_used.ss_size == 0 ? SS_DISABLE :
> on_sig_stack(sp)
> > +        ? SS_ONSTACK : 0;
> > +}
> >
> >  int host_to_target_signal(int sig)
> >  {
> > @@ -336,28 +352,6 @@ void queue_signal(CPUArchState *env, int sig,
> target_siginfo_t *info)
> >      return;
> >  }
> >
> > -static int fatal_signal(int sig)
> > -{
> > -
> > -    switch (sig) {
> > -    case TARGET_SIGCHLD:
> > -    case TARGET_SIGURG:
> > -    case TARGET_SIGWINCH:
> > -    case TARGET_SIGINFO:
> > -        /* Ignored by default. */
> > -        return 0;
> > -    case TARGET_SIGCONT:
> > -    case TARGET_SIGSTOP:
> > -    case TARGET_SIGTSTP:
> > -    case TARGET_SIGTTIN:
> > -    case TARGET_SIGTTOU:
> > -        /* Job control signals.  */
> > -        return 0;
> > -    default:
> > -        return 1;
> > -    }
> > -}
>
> There wasn't any need to move this function, I think ?
>

No, there was some other conflict during rebase getting the patch train
ready that I thought I'd cleaned up, but this was fallout from that which
I overlooked. I've undone it...


> > -
> >  /*
> >   * Force a synchronously taken QEMU_SI_FAULT signal. For QEMU the
> >   * 'force' part is handled in process_pending_signals().
> > @@ -484,6 +478,134 @@ static void host_signal_handler(int host_sig,
> siginfo_t *info, void *puc)
> >      cpu_exit(thread_cpu);
> >  }
> >
> > +static int fatal_signal(int sig)
> > +{
> > +
> > +    switch (sig) {
> > +    case TARGET_SIGCHLD:
> > +    case TARGET_SIGURG:
> > +    case TARGET_SIGWINCH:
> > +    case TARGET_SIGINFO:
> > +        /* Ignored by default. */
> > +        return 0;
> > +    case TARGET_SIGCONT:
> > +    case TARGET_SIGSTOP:
> > +    case TARGET_SIGTSTP:
> > +    case TARGET_SIGTTIN:
> > +    case TARGET_SIGTTOU:
> > +        /* Job control signals.  */
> > +        return 0;
> > +    default:
> > +        return 1;
> > +    }
> > +}
> > +
> > +static inline abi_ulong get_sigframe(struct target_sigaction *ka,
> > +        CPUArchState *regs, size_t frame_size)
> > +{
> > +    abi_ulong sp;
> > +
> > +    /* Use default user stack */
> > +    sp = get_sp_from_cpustate(regs);
> > +
> > +    if ((ka->sa_flags & TARGET_SA_ONSTACK) && (sas_ss_flags(sp) == 0)) {
> > +        sp = target_sigaltstack_used.ss_sp +
> > +            target_sigaltstack_used.ss_size;
> > +    }
> > +
> > +#if defined(TARGET_MIPS) || defined(TARGET_ARM)
> > +    return (sp - frame_size) & ~7;
> > +#elif defined(TARGET_AARCH64)
> > +    return (sp - frame_size) & ~15;
> > +#else
> > +    return sp - frame_size;
> > +#endif
>
> We don't need to do it in this patchseries, but you should strongly
> consider pulling the architecture-specifics out in a way that
> avoids this kind of ifdef ladder.
>

Totally agreed. I debated fixing this before I started this patch
run, but I decided to pick my battles... I'll fix this in a follow up.


> > +}
> > +
> > +/* compare to mips/mips/pm_machdep.c and sparc64/sparc64/machdep.c
> sendsig() */
>

Two dead architectures... I've updated the comments...  and the filename
which
they live in...


> > +static void setup_frame(int sig, int code, struct target_sigaction *ka,
> > +    target_sigset_t *set, target_siginfo_t *tinfo, CPUArchState *regs)
> > +{
> > +    struct target_sigframe *frame;
> > +    abi_ulong frame_addr;
> > +    int i;
> > +
> > +    frame_addr = get_sigframe(ka, regs, sizeof(*frame));
> > +    trace_user_setup_frame(regs, frame_addr);
> > +    if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) {
> > +        goto give_sigsegv;
>
> FreeBSD for Arm (haven't checked other BSDs or other archs)
> gives a SIGILL for the "can't write signal frame to stack"
> case, I think:
>
> https://github.com/freebsd/freebsd-src/blob/main/sys/arm/arm/exec_machdep.c#L316
> I don't understand why they picked SIGILL, SIGSEGV seems much more
> logical to me, but we should follow the kernel behaviour.
>

This is a good thing to find. I'm going to have to study all the
architectures, but
the first 5 I looked at all returned SIGILL, so this code has to change to
reflect
that...


> > +    }
> > +
> > +    memset(frame, 0, sizeof(*frame));
> > +#if defined(TARGET_MIPS)
> > +    int mflags = on_sig_stack(frame_addr) ? TARGET_MC_ADD_MAGIC :
> > +        TARGET_MC_SET_ONSTACK | TARGET_MC_ADD_MAGIC;
> > +#else
> > +    int mflags = 0;
> > +#endif
> > +    if (get_mcontext(regs, &frame->sf_uc.uc_mcontext, mflags)) {
> > +        goto give_sigsegv;
>
> The FreeBSD kernel get_mcontext() can't fail -- why can ours ?
> (This matters because SIGSEGV may not be the right response to
> whatever the failure case is.)
>

    if (mcp->mc_vfp_size != 0 && mcp->mc_vfp_size !=
sizeof(target_mcontext_vfp_t)) {

is what I try to validate, but looking at the kernel code it does NOT
return vfp, so that
check is bogus. I need to remove it (I'd added as part of a prior review
without fully
checking before I made the change, to be honest).


> > +    }
> > +
> > +    for (i = 0; i < TARGET_NSIG_WORDS; i++) {
> > +        if (__put_user(set->__bits[i],
> &frame->sf_uc.uc_sigmask.__bits[i])) {
> > +            goto give_sigsegv;
>
> __get_user() and __put_user() in QEMU can't fail, so you don't
> need to check for errors here, unlike the non-double-underscore
> versions. At some point you might want to take the current linux-user
> versions of these user-access functions/macros: it looks like bsd-user
> has an older version which doesn't handle the case where the guest
> has looser alignment restrictions than the host. The new ones
> actually expand to do { ... } while(0) statements which won't
> be valid inside an if() condition.
>

OK. I'll make a sweep in addition to fixing this.


> (Historical note: the reason QEMU's __put_user/__get_user ever had
> return values at all is that the linux-user code was copy-and-pasted
> from the Linux kernel. In the Linux kernel handling of writing
> data to userspace is/was error-checked on every write, whereas
> QEMU does the "is this writable" test once with the lock_user
> function and then can assume all the writes to that area succeed.
> But we still started with a lot of copy-pasted code that was doing
> unnecessary checks on __put_user and __get_user return values.
> FreeBSD seems to handle write-checking in yet a third way, by
> assembling the struct in kernel-space and checking for writability
> once at the end when it copies the whole block out to userspace.)
>

We're nothing if not creative... But this is old school Unix which started
doing this in V7 which started doing it for stat and ftime system calls...


> > +        }
> > +    }
> > +
> > +    if (tinfo) {
> > +        frame->sf_si.si_signo = tinfo->si_signo;
> > +        frame->sf_si.si_errno = tinfo->si_errno;
> > +        frame->sf_si.si_code = tinfo->si_code;
> > +        frame->sf_si.si_pid = tinfo->si_pid;
> > +        frame->sf_si.si_uid = tinfo->si_uid;
> > +        frame->sf_si.si_status = tinfo->si_status;
> > +        frame->sf_si.si_addr = tinfo->si_addr;
> > +
> > +        if (TARGET_SIGILL == sig || TARGET_SIGFPE == sig ||
> > +                TARGET_SIGSEGV == sig || TARGET_SIGBUS == sig ||
> > +                TARGET_SIGTRAP == sig) {
> > +            frame->sf_si._reason._fault._trapno =
> tinfo->_reason._fault._trapno;
> > +        }
> > +
> > +        /*
> > +         * If si_code is one of SI_QUEUE, SI_TIMER, SI_ASYNCIO, or
> > +         * SI_MESGQ, then si_value contains the application-specified
> > +         * signal value. Otherwise, the contents of si_value are
> > +         * undefined.
> > +         */
> > +        if (SI_QUEUE == code || SI_TIMER == code || SI_ASYNCIO == code
> ||
> > +                SI_MESGQ == code) {
> > +            frame->sf_si.si_value.sival_int = tinfo->si_value.sival_int;
> > +        }
> > +
> > +        if (SI_TIMER == code) {
> > +            frame->sf_si._reason._timer._timerid =
> > +                tinfo->_reason._timer._timerid;
> > +            frame->sf_si._reason._timer._overrun =
> > +                tinfo->_reason._timer._overrun;
> > +        }
> > +
> > +#ifdef SIGPOLL
> > +        if (SIGPOLL == sig) {
> > +            frame->sf_si._reason._band = tinfo->_reason._band;
> > +        }
> > +#endif
>
> This seems to be yet a third set of the logic for handling
> target_siginfo_t's union, to go along with tswap_siginfo() and
> host_to_target_siginfo_noswap(), except that the logic here is
> different. linux-user calls tswap_siginfo() in its signal-frame
> setup code.
>

Yea, I need to get that sorted out as well.. It's my biggest remaining
item to resolve (except maybe for the comments for do_* you made,
I've not yet completely digested them when I saw they were more
than reviewed by).


> > +
> > +    }
> > +
> > +    if (set_sigtramp_args(regs, sig, frame, frame_addr, ka)) {
> > +        goto give_sigsegv;
> > +    }
>
> set_sigtramp_args() can't fail. (Not sure why it has a non-void
> return type.)
>

OK. I'll fix this as well.


> > +
> > +    unlock_user_struct(frame, frame_addr, 1);
> > +    return;
> > +
> > +give_sigsegv:
> > +    unlock_user_struct(frame, frame_addr, 1);
> > +    force_sig(TARGET_SIGSEGV);
> > +}
> > +
> >  void signal_init(void)
> >  {
> >      TaskState *ts = (TaskState *)thread_cpu->opaque;
> > --
> > 2.33.1
>
> thanks
>

No, thank you. This is quite helpful and got me to look at a number of new
places and understand some historic background.

Warmer -- PMM
Warner Losh Jan. 17, 2022, 7:24 a.m. UTC | #3
On Sun, Jan 16, 2022 at 11:58 PM Warner Losh <imp@bsdimp.com> wrote:

> > +    if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) {
>> > +        goto give_sigsegv;
>>
>> FreeBSD for Arm (haven't checked other BSDs or other archs)
>> gives a SIGILL for the "can't write signal frame to stack"
>> case, I think:
>>
>> https://github.com/freebsd/freebsd-src/blob/main/sys/arm/arm/exec_machdep.c#L316
>> I don't understand why they picked SIGILL, SIGSEGV seems much more
>> logical to me, but we should follow the kernel behaviour.
>>
>
> This is a good thing to find. I'm going to have to study all the
> architectures, but
> the first 5 I looked at all returned SIGILL, so this code has to change to
> reflect
> that...
>

Sorry to follow up my own message, but  this dates to 4.1BSD (4BSD sent a
SIGKILL),
but it's not present in V7, 32V or 3BSD.

So it's very old-school BSD behavior, dating from 1981 :)

Warner
diff mbox series

Patch

diff --git a/bsd-user/signal.c b/bsd-user/signal.c
index 8dadc9a39a7..8e1427553da 100644
--- a/bsd-user/signal.c
+++ b/bsd-user/signal.c
@@ -30,11 +30,27 @@ 
  * fork.
  */
 
+static target_stack_t target_sigaltstack_used = {
+    .ss_sp = 0,
+    .ss_size = 0,
+    .ss_flags = TARGET_SS_DISABLE,
+};
+
 static struct target_sigaction sigact_table[TARGET_NSIG];
 static void host_signal_handler(int host_sig, siginfo_t *info, void *puc);
 static void target_to_host_sigset_internal(sigset_t *d,
         const target_sigset_t *s);
 
+static inline int on_sig_stack(unsigned long sp)
+{
+    return sp - target_sigaltstack_used.ss_sp < target_sigaltstack_used.ss_size;
+}
+
+static inline int sas_ss_flags(unsigned long sp)
+{
+    return target_sigaltstack_used.ss_size == 0 ? SS_DISABLE : on_sig_stack(sp)
+        ? SS_ONSTACK : 0;
+}
 
 int host_to_target_signal(int sig)
 {
@@ -336,28 +352,6 @@  void queue_signal(CPUArchState *env, int sig, target_siginfo_t *info)
     return;
 }
 
-static int fatal_signal(int sig)
-{
-
-    switch (sig) {
-    case TARGET_SIGCHLD:
-    case TARGET_SIGURG:
-    case TARGET_SIGWINCH:
-    case TARGET_SIGINFO:
-        /* Ignored by default. */
-        return 0;
-    case TARGET_SIGCONT:
-    case TARGET_SIGSTOP:
-    case TARGET_SIGTSTP:
-    case TARGET_SIGTTIN:
-    case TARGET_SIGTTOU:
-        /* Job control signals.  */
-        return 0;
-    default:
-        return 1;
-    }
-}
-
 /*
  * Force a synchronously taken QEMU_SI_FAULT signal. For QEMU the
  * 'force' part is handled in process_pending_signals().
@@ -484,6 +478,134 @@  static void host_signal_handler(int host_sig, siginfo_t *info, void *puc)
     cpu_exit(thread_cpu);
 }
 
+static int fatal_signal(int sig)
+{
+
+    switch (sig) {
+    case TARGET_SIGCHLD:
+    case TARGET_SIGURG:
+    case TARGET_SIGWINCH:
+    case TARGET_SIGINFO:
+        /* Ignored by default. */
+        return 0;
+    case TARGET_SIGCONT:
+    case TARGET_SIGSTOP:
+    case TARGET_SIGTSTP:
+    case TARGET_SIGTTIN:
+    case TARGET_SIGTTOU:
+        /* Job control signals.  */
+        return 0;
+    default:
+        return 1;
+    }
+}
+
+static inline abi_ulong get_sigframe(struct target_sigaction *ka,
+        CPUArchState *regs, size_t frame_size)
+{
+    abi_ulong sp;
+
+    /* Use default user stack */
+    sp = get_sp_from_cpustate(regs);
+
+    if ((ka->sa_flags & TARGET_SA_ONSTACK) && (sas_ss_flags(sp) == 0)) {
+        sp = target_sigaltstack_used.ss_sp +
+            target_sigaltstack_used.ss_size;
+    }
+
+#if defined(TARGET_MIPS) || defined(TARGET_ARM)
+    return (sp - frame_size) & ~7;
+#elif defined(TARGET_AARCH64)
+    return (sp - frame_size) & ~15;
+#else
+    return sp - frame_size;
+#endif
+}
+
+/* compare to mips/mips/pm_machdep.c and sparc64/sparc64/machdep.c sendsig() */
+static void setup_frame(int sig, int code, struct target_sigaction *ka,
+    target_sigset_t *set, target_siginfo_t *tinfo, CPUArchState *regs)
+{
+    struct target_sigframe *frame;
+    abi_ulong frame_addr;
+    int i;
+
+    frame_addr = get_sigframe(ka, regs, sizeof(*frame));
+    trace_user_setup_frame(regs, frame_addr);
+    if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) {
+        goto give_sigsegv;
+    }
+
+    memset(frame, 0, sizeof(*frame));
+#if defined(TARGET_MIPS)
+    int mflags = on_sig_stack(frame_addr) ? TARGET_MC_ADD_MAGIC :
+        TARGET_MC_SET_ONSTACK | TARGET_MC_ADD_MAGIC;
+#else
+    int mflags = 0;
+#endif
+    if (get_mcontext(regs, &frame->sf_uc.uc_mcontext, mflags)) {
+        goto give_sigsegv;
+    }
+
+    for (i = 0; i < TARGET_NSIG_WORDS; i++) {
+        if (__put_user(set->__bits[i], &frame->sf_uc.uc_sigmask.__bits[i])) {
+            goto give_sigsegv;
+        }
+    }
+
+    if (tinfo) {
+        frame->sf_si.si_signo = tinfo->si_signo;
+        frame->sf_si.si_errno = tinfo->si_errno;
+        frame->sf_si.si_code = tinfo->si_code;
+        frame->sf_si.si_pid = tinfo->si_pid;
+        frame->sf_si.si_uid = tinfo->si_uid;
+        frame->sf_si.si_status = tinfo->si_status;
+        frame->sf_si.si_addr = tinfo->si_addr;
+
+        if (TARGET_SIGILL == sig || TARGET_SIGFPE == sig ||
+                TARGET_SIGSEGV == sig || TARGET_SIGBUS == sig ||
+                TARGET_SIGTRAP == sig) {
+            frame->sf_si._reason._fault._trapno = tinfo->_reason._fault._trapno;
+        }
+
+        /*
+         * If si_code is one of SI_QUEUE, SI_TIMER, SI_ASYNCIO, or
+         * SI_MESGQ, then si_value contains the application-specified
+         * signal value. Otherwise, the contents of si_value are
+         * undefined.
+         */
+        if (SI_QUEUE == code || SI_TIMER == code || SI_ASYNCIO == code ||
+                SI_MESGQ == code) {
+            frame->sf_si.si_value.sival_int = tinfo->si_value.sival_int;
+        }
+
+        if (SI_TIMER == code) {
+            frame->sf_si._reason._timer._timerid =
+                tinfo->_reason._timer._timerid;
+            frame->sf_si._reason._timer._overrun =
+                tinfo->_reason._timer._overrun;
+        }
+
+#ifdef SIGPOLL
+        if (SIGPOLL == sig) {
+            frame->sf_si._reason._band = tinfo->_reason._band;
+        }
+#endif
+
+    }
+
+    if (set_sigtramp_args(regs, sig, frame, frame_addr, ka)) {
+        goto give_sigsegv;
+    }
+
+    unlock_user_struct(frame, frame_addr, 1);
+    return;
+
+give_sigsegv:
+    unlock_user_struct(frame, frame_addr, 1);
+    force_sig(TARGET_SIGSEGV);
+}
+
 void signal_init(void)
 {
     TaskState *ts = (TaskState *)thread_cpu->opaque;