A start at an implemention of linux_rt_sendsig & linux_rt_sigreturn

and associated user-level signal trampoline glue.

Without this patch, an SA_SIGINFO style handler can be installed by a linux
app, but if the handler accesses its sip argument, it will get a garbage
pointer and likely segfault.

We currently supply a valid pointer, but its contents are mainly
garbage.  Filling this in properly is future work.

This is the second of 3 commits that will get IBM's JDK 1.3 working with
FreeBSD ...
This commit is contained in:
gallatin 2000-10-17 00:00:20 +00:00
parent 029626a413
commit 7df6b1ab5d
8 changed files with 361 additions and 6 deletions

View File

@ -94,7 +94,6 @@ DUMMY(query_module);
DUMMY(nfsservctl);
DUMMY(getresgid);
DUMMY(prctl);
DUMMY(rt_sigreturn);
DUMMY(rt_sigpending);
DUMMY(rt_sigtimedwait);
DUMMY(rt_sigqueueinfo);

View File

@ -56,7 +56,7 @@ linux_to_bsd_sigset(linux_sigset_t *lss, sigset_t *bss)
}
}
static void
void
bsd_to_linux_sigset(sigset_t *bss, linux_sigset_t *lss)
{
int b, l;

View File

@ -31,6 +31,8 @@
#ifndef _I386_LINUX_LINUX_H_
#define _I386_LINUX_LINUX_H_
#include <sys/signal.h> /* for sigval union */
#include <i386/linux/linux_syscall.h>
#ifdef MALLOC_DECLARE
@ -221,6 +223,73 @@ struct linux_sigcontext {
int sc_cr2;
};
struct linux_ucontext {
unsigned long uc_flags;
void *uc_link;
linux_stack_t uc_stack;
struct linux_sigcontext uc_mcontext;
linux_sigset_t uc_sigmask;
};
#define LINUX_SI_MAX_SIZE 128
#define LINUX_SI_PAD_SIZE ((LINUX_SI_MAX_SIZE/sizeof(int)) - 3)
typedef struct siginfo {
int lsi_signo;
int lsi_errno;
int lsi_code;
union {
int _pad[LINUX_SI_PAD_SIZE];
struct {
linux_pid_t _pid;
linux_uid_t _uid;
} _kill;
struct {
unsigned int _timer1;
unsigned int _timer2;
} _timer;
struct {
linux_pid_t _pid; /* sender's pid */
linux_uid_t _uid; /* sender's uid */
union sigval _sigval;
} _rt;
struct {
linux_pid_t _pid; /* which child */
linux_uid_t _uid; /* sender's uid */
int _status; /* exit code */
linux_clock_t _utime;
linux_clock_t _stime;
} _sigchld;
struct {
void *_addr; /* faulting insn/memory ref. */
} _sigfault;
struct {
int _band; /* POLL_IN, POLL_OUT, POLL_MSG */
int _fd;
} _sigpoll;
} _sifields;
} linux_siginfo_t;
#define lsi_pid _sifields._kill._pid
#define lsi_uid _sifields._kill._uid
#define lsi_status _sifields._sigchld._status
#define lsi_utime _sifields._sigchld._utime
#define lsi_stime _sifields._sigchld._stime
#define lsi_value _sifields._rt._sigval
#define lsi_int _sifields._rt._sigval.sival_int
#define lsi_ptr _sifields._rt._sigval.sival_ptr
#define lsi_addr _sifields._sigfault._addr
#define lsi_band _sifields._sigpoll._band
#define lsi_fd _sifields._sigpoll._fd
/*
* We make the stack look like Linux expects it when calling a signal
* handler, but use the BSD way of calling the handler and sigreturn().
@ -233,10 +302,21 @@ struct linux_sigframe {
linux_handler_t sf_handler;
};
struct linux_rt_sigframe {
int sf_sig;
linux_siginfo_t *sf_siginfo;;
struct linux_ucontext *sf_ucontext;
linux_siginfo_t sf_si;
struct linux_ucontext sf_sc;
linux_handler_t sf_handler;
};
extern int bsd_to_linux_signal[];
extern int linux_to_bsd_signal[];
extern struct sysentvec linux_sysvec;
extern struct sysentvec elf_linux_sysvec;
void bsd_to_linux_sigset(sigset_t *bss, linux_sigset_t *lss);
/*
* Pluggable ioctl handlers

View File

@ -94,7 +94,6 @@ DUMMY(query_module);
DUMMY(nfsservctl);
DUMMY(getresgid);
DUMMY(prctl);
DUMMY(rt_sigreturn);
DUMMY(rt_sigpending);
DUMMY(rt_sigtimedwait);
DUMMY(rt_sigqueueinfo);

View File

@ -11,3 +11,7 @@ ASSYM(LINUX_SIGF_HANDLER, offsetof(struct linux_sigframe, sf_handler));
ASSYM(LINUX_SIGF_SC, offsetof(struct linux_sigframe, sf_sc));
ASSYM(LINUX_SC_GS, offsetof(struct linux_sigcontext, sc_gs));
ASSYM(LINUX_SC_EFLAGS, offsetof(struct linux_sigcontext, sc_eflags));
ASSYM(LINUX_RT_SIGF_HANDLER, offsetof(struct linux_rt_sigframe, sf_handler));
ASSYM(LINUX_RT_SIGF_UC, offsetof(struct linux_rt_sigframe, sf_sc));

View File

@ -14,11 +14,27 @@ NON_GPROF_ENTRY(linux_sigcode)
int $0x80 /* enter kernel with args */
0: jmp 0b
ALIGN_TEXT
/* XXXXX */
_linux_rt_sigcode:
call *LINUX_RT_SIGF_HANDLER(%esp)
leal LINUX_RT_SIGF_UC(%esp),%ebx /* linux ucp */
movl LINUX_SC_GS(%ebx),%gs
push %eax /* fake ret addr */
movl $LINUX_SYS_linux_rt_sigreturn,%eax /* linux_rt_sigreturn() */
int $0x80 /* enter kernel with args */
0: jmp 0b
ALIGN_TEXT
/* XXXXX */
_linux_esigcode:
.data
.globl _linux_szsigcode
.globl _linux_szsigcode, _linux_sznonrtsigcode
_linux_szsigcode:
.long _linux_esigcode-_linux_sigcode
_linux_sznonrtsigcode:
.long _linux_rt_sigcode-_linux_sigcode
.text

View File

@ -38,6 +38,7 @@
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/sysproto.h>
#include <sys/sysent.h>
#include <sys/imgact.h>
#include <sys/imgact_aout.h>
@ -192,6 +193,139 @@ elf_linux_fixup(register_t **stack_base, struct image_params *imgp)
}
extern int _ucodesel, _udatasel;
extern unsigned long _linux_sznonrtsigcode;
static void
linux_rt_sendsig(sig_t catcher, int sig, sigset_t *mask, u_long code)
{
register struct proc *p = curproc;
register struct trapframe *regs;
struct linux_rt_sigframe *fp, frame;
struct sigacts *psp = p->p_sigacts;
int oonstack;
regs = p->p_md.md_regs;
oonstack = p->p_sigstk.ss_flags & SS_ONSTACK;
#ifdef DEBUG
printf("Linux-emul(%ld): linux_rt_sendsig(%p, %d, %p, %lu)\n",
(long)p->p_pid, catcher, sig, (void*)mask, code);
#endif
/*
* Allocate space for the signal handler context.
*/
if ((p->p_flag & P_ALTSTACK) && !oonstack &&
SIGISMEMBER(psp->ps_sigonstack, sig)) {
fp = (struct linux_rt_sigframe *)(p->p_sigstk.ss_sp +
p->p_sigstk.ss_size - sizeof(struct linux_rt_sigframe));
p->p_sigstk.ss_flags |= SS_ONSTACK;
} else {
fp = (struct linux_rt_sigframe *)regs->tf_esp - 1;
}
/*
* grow() will return FALSE if the fp will not fit inside the stack
* and the stack can not be grown. useracc will return FALSE
* if access is denied.
*/
if ((grow_stack (p, (int)fp) == FALSE) ||
!useracc((caddr_t)fp, sizeof (struct linux_rt_sigframe),
VM_PROT_WRITE)) {
/*
* Process has trashed its stack; give it an illegal
* instruction to halt it in its tracks.
*/
SIGACTION(p, SIGILL) = SIG_DFL;
SIGDELSET(p->p_sigignore, SIGILL);
SIGDELSET(p->p_sigcatch, SIGILL);
SIGDELSET(p->p_sigmask, SIGILL);
#ifdef DEBUG
printf("Linux-emul(%ld): linux_rt_sendsig -- bad stack %p, SS_ONSTACK: 0x%x ",
(long)p->p_pid, fp, p->p_sigstk.ss_flags & SS_ONSTACK);
#endif
psignal(p, SIGILL);
return;
}
/*
* Build the argument list for the signal handler.
*/
if (p->p_sysent->sv_sigtbl)
if (sig <= p->p_sysent->sv_sigsize)
sig = p->p_sysent->sv_sigtbl[_SIG_IDX(sig)];
frame.sf_handler = catcher;
frame.sf_sig = sig;
frame.sf_siginfo = &fp->sf_si;
frame.sf_ucontext = &fp->sf_sc;
/* Fill siginfo structure. */
frame.sf_si.lsi_signo = sig;
frame.sf_si.lsi_code = code;
frame.sf_si.lsi_addr = (void *)regs->tf_err;
/*
* Build the signal context to be used by sigreturn.
*/
frame.sf_sc.uc_mcontext.sc_mask = mask->__bits[0];
frame.sf_sc.uc_mcontext.sc_gs = rgs();
frame.sf_sc.uc_mcontext.sc_fs = regs->tf_fs;
frame.sf_sc.uc_mcontext.sc_es = regs->tf_es;
frame.sf_sc.uc_mcontext.sc_ds = regs->tf_ds;
frame.sf_sc.uc_mcontext.sc_edi = regs->tf_edi;
frame.sf_sc.uc_mcontext.sc_esi = regs->tf_esi;
frame.sf_sc.uc_mcontext.sc_ebp = regs->tf_ebp;
frame.sf_sc.uc_mcontext.sc_ebx = regs->tf_ebx;
frame.sf_sc.uc_mcontext.sc_edx = regs->tf_edx;
frame.sf_sc.uc_mcontext.sc_ecx = regs->tf_ecx;
frame.sf_sc.uc_mcontext.sc_eax = regs->tf_eax;
frame.sf_sc.uc_mcontext.sc_eip = regs->tf_eip;
frame.sf_sc.uc_mcontext.sc_cs = regs->tf_cs;
frame.sf_sc.uc_mcontext.sc_eflags = regs->tf_eflags;
frame.sf_sc.uc_mcontext.sc_esp_at_signal = regs->tf_esp;
frame.sf_sc.uc_mcontext.sc_ss = regs->tf_ss;
frame.sf_sc.uc_mcontext.sc_err = regs->tf_err;
frame.sf_sc.uc_mcontext.sc_trapno = code; /* XXX ???? */
/*
* Build the remainder of the ucontext struct to be used by sigreturn.
*/
frame.sf_sc.uc_flags = 0; /* XXX ??? */
frame.sf_sc.uc_link = NULL; /* XXX ??? */
frame.sf_sc.uc_stack.ss_sp = p->p_sigstk.ss_sp;
frame.sf_sc.uc_stack.ss_flags =
bsd_to_linux_sigaltstack(p->p_sigstk.ss_flags);
frame.sf_sc.uc_stack.ss_size = p->p_sigstk.ss_size;
#ifdef DEBUG
printf("Linux-emul(%ld): rt_sendsig flags: 0x%x, sp: %p, ss: 0x%x, mask: 0x%x\n",
(long)p->p_pid, frame.sf_sc.uc_stack.ss_flags, p->p_sigstk.ss_sp,
p->p_sigstk.ss_size, frame.sf_sc.uc_mcontext.sc_mask);
#endif
bsd_to_linux_sigset(&p->p_sigmask, &frame.sf_sc.uc_sigmask);
if (copyout(&frame, fp, sizeof(frame)) != 0) {
/*
* Process has trashed its stack; give it an illegal
* instruction to halt it in its tracks.
*/
sigexit(p, SIGILL);
/* NOTREACHED */
}
/*
* Build context to run handler in.
*/
regs->tf_esp = (int)fp;
regs->tf_eip = PS_STRINGS - *(p->p_sysent->sv_szsigcode) +
_linux_sznonrtsigcode;
regs->tf_eflags &= ~PSL_VM;
regs->tf_cs = _ucodesel;
regs->tf_ds = _udatasel;
regs->tf_es = _udatasel;
regs->tf_fs = _udatasel;
load_gs(_udatasel);
regs->tf_ss = _udatasel;
}
/*
* Send an interrupt to process.
@ -220,6 +354,13 @@ linux_sendsig(sig_t catcher, int sig, sigset_t *mask, u_long code)
printf("Linux-emul(%ld): linux_sendsig(%p, %d, %p, %lu)\n",
(long)p->p_pid, catcher, sig, (void*)mask, code);
#endif
if (SIGISMEMBER(p->p_sigacts->ps_siginfo, sig)) {
/* Signal handler installed with SA_SIGINFO. */
linux_rt_sendsig(catcher, sig, mask, code);
return;
}
/*
* Allocate space for the signal handler context.
*/
@ -398,6 +539,122 @@ linux_sigreturn(p, args)
return (EJUSTRETURN);
}
/*
* System call to cleanup state after a signal
* has been taken. Reset signal mask and
* stack state from context left by rt_sendsig (above).
* Return to previous pc and psl as specified by
* context left by sendsig. Check carefully to
* make sure that the user has not modified the
* psl to gain improper privileges or to cause
* a machine fault.
*/
int
linux_rt_sigreturn(p, args)
struct proc *p;
struct linux_rt_sigreturn_args *args;
{
struct sigaltstack_args sasargs;
struct linux_ucontext uc;
struct linux_sigcontext *context;
linux_stack_t *lss;
stack_t *ss;
register struct trapframe *regs;
int eflags;
caddr_t sg = stackgap_init();
regs = p->p_md.md_regs;
#ifdef DEBUG
printf("Linux-emul(%ld): linux_rt_sigreturn(%p)\n",
(long)p->p_pid, (void *)args->ucp);
#endif
/*
* The trampoline code hands us the u_context.
* It is unsafe to keep track of it ourselves, in the event that a
* program jumps out of a signal handler.
*/
if (copyin((caddr_t)args->ucp, &uc, sizeof(uc)) != 0)
return (EFAULT);
context = &uc.uc_mcontext;
/*
* Check for security violations.
*/
#define EFLAGS_SECURE(ef, oef) ((((ef) ^ (oef)) & ~PSL_USERCHANGE) == 0)
eflags = context->sc_eflags;
/*
* XXX do allow users to change the privileged flag PSL_RF. The
* cpu sets PSL_RF in tf_eflags for faults. Debuggers should
* sometimes set it there too. tf_eflags is kept in the signal
* context during signal handling and there is no other place
* to remember it, so the PSL_RF bit may be corrupted by the
* signal handler without us knowing. Corruption of the PSL_RF
* bit at worst causes one more or one less debugger trap, so
* allowing it is fairly harmless.
*/
if (!EFLAGS_SECURE(eflags & ~PSL_RF, regs->tf_eflags & ~PSL_RF)) {
return(EINVAL);
}
/*
* Don't allow users to load a valid privileged %cs. Let the
* hardware check for invalid selectors, excess privilege in
* other selectors, invalid %eip's and invalid %esp's.
*/
#define CS_SECURE(cs) (ISPL(cs) == SEL_UPL)
if (!CS_SECURE(context->sc_cs)) {
trapsignal(p, SIGBUS, T_PROTFLT);
return(EINVAL);
}
p->p_sigstk.ss_flags &= ~SS_ONSTACK;
SIGSETOLD(p->p_sigmask, context->sc_mask);
SIG_CANTMASK(p->p_sigmask);
/*
* Restore signal context->
*/
/* %gs was restored by the trampoline. */
regs->tf_fs = context->sc_fs;
regs->tf_es = context->sc_es;
regs->tf_ds = context->sc_ds;
regs->tf_edi = context->sc_edi;
regs->tf_esi = context->sc_esi;
regs->tf_ebp = context->sc_ebp;
regs->tf_ebx = context->sc_ebx;
regs->tf_edx = context->sc_edx;
regs->tf_ecx = context->sc_ecx;
regs->tf_eax = context->sc_eax;
regs->tf_eip = context->sc_eip;
regs->tf_cs = context->sc_cs;
regs->tf_eflags = eflags;
regs->tf_esp = context->sc_esp_at_signal;
regs->tf_ss = context->sc_ss;
/*
* call sigaltstack & ignore results..
*/
ss = stackgap_alloc(&sg, sizeof(stack_t));
lss = &uc.uc_stack;
ss->ss_sp = lss->ss_sp;
ss->ss_size = (lss->ss_size >= LINUX_MINSIGSTKSZ &&
lss->ss_size < MINSIGSTKSZ) ? MINSIGSTKSZ : lss->ss_size;
ss->ss_flags = linux_to_bsd_sigaltstack(lss->ss_flags);
#ifdef DEBUG
printf("Linux-emul(%ld): rt_sigret flags: 0x%x, sp: %p, ss: 0x%x, mask: 0x%x\n",
(long)p->p_pid, ss->ss_flags, ss->ss_sp, ss->ss_size, context->sc_mask);
#endif
sasargs.ss = ss;
sasargs.oss = NULL;
(void) sigaltstack(p, &sasargs);
return (EJUSTRETURN);
}
static void
linux_prepsyscall(struct trapframe *tf, int *args, u_int *code, caddr_t *params)
{

View File

@ -253,7 +253,7 @@
171 STD LINUX { int linux_getresgid(linux_gid_t *rgid, \
linux_gid_t *egid, linux_gid_t *sgid); }
172 STD LINUX { int linux_prctl(void); }
173 STD LINUX { int linux_rt_sigreturn(void); }
173 STD LINUX { int linux_rt_sigreturn(struct linux_ucontext *ucp); }
174 STD LINUX { int linux_rt_sigaction(int sig, \
linux_sigaction_t *act, \
linux_sigaction_t *oact, \