From 7df6b1ab5dadadbedb7f1f29dbd4aa90f61d38fc Mon Sep 17 00:00:00 2001 From: gallatin Date: Tue, 17 Oct 2000 00:00:20 +0000 Subject: [PATCH] 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 ... --- sys/alpha/linux/linux_dummy.c | 1 - sys/compat/linux/linux_signal.c | 2 +- sys/i386/linux/linux.h | 80 ++++++++++ sys/i386/linux/linux_dummy.c | 1 - sys/i386/linux/linux_genassym.c | 4 + sys/i386/linux/linux_locore.s | 20 ++- sys/i386/linux/linux_sysvec.c | 257 ++++++++++++++++++++++++++++++++ sys/i386/linux/syscalls.master | 2 +- 8 files changed, 361 insertions(+), 6 deletions(-) diff --git a/sys/alpha/linux/linux_dummy.c b/sys/alpha/linux/linux_dummy.c index 7afe4b343db1..6340a1077e58 100644 --- a/sys/alpha/linux/linux_dummy.c +++ b/sys/alpha/linux/linux_dummy.c @@ -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); diff --git a/sys/compat/linux/linux_signal.c b/sys/compat/linux/linux_signal.c index f8b966162251..82705b0b06c1 100644 --- a/sys/compat/linux/linux_signal.c +++ b/sys/compat/linux/linux_signal.c @@ -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; diff --git a/sys/i386/linux/linux.h b/sys/i386/linux/linux.h index f399f054441f..4ac9447e644f 100644 --- a/sys/i386/linux/linux.h +++ b/sys/i386/linux/linux.h @@ -31,6 +31,8 @@ #ifndef _I386_LINUX_LINUX_H_ #define _I386_LINUX_LINUX_H_ +#include /* for sigval union */ + #include #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 diff --git a/sys/i386/linux/linux_dummy.c b/sys/i386/linux/linux_dummy.c index 7afe4b343db1..6340a1077e58 100644 --- a/sys/i386/linux/linux_dummy.c +++ b/sys/i386/linux/linux_dummy.c @@ -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); diff --git a/sys/i386/linux/linux_genassym.c b/sys/i386/linux/linux_genassym.c index 25e20af9c790..7888cb33649f 100644 --- a/sys/i386/linux/linux_genassym.c +++ b/sys/i386/linux/linux_genassym.c @@ -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)); + + diff --git a/sys/i386/linux/linux_locore.s b/sys/i386/linux/linux_locore.s index 8aad7f7ceb84..688762852a5e 100644 --- a/sys/i386/linux/linux_locore.s +++ b/sys/i386/linux/linux_locore.s @@ -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 + + + diff --git a/sys/i386/linux/linux_sysvec.c b/sys/i386/linux/linux_sysvec.c index 8bed55641ce6..12b5a293b0e3 100644 --- a/sys/i386/linux/linux_sysvec.c +++ b/sys/i386/linux/linux_sysvec.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -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) { diff --git a/sys/i386/linux/syscalls.master b/sys/i386/linux/syscalls.master index 47df49a7ec92..3bef3e002cc1 100644 --- a/sys/i386/linux/syscalls.master +++ b/sys/i386/linux/syscalls.master @@ -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, \