From 29ccf7d1668ab3c3e3e82c53dbf69d4c5a9be824 Mon Sep 17 00:00:00 2001 From: kib Date: Sun, 19 Oct 2008 10:02:26 +0000 Subject: Correctly fill siginfo for the signals delivered by linux tkill/tgkill. It is required for async cancellation to work. Fix PROC_LOCK leak in linux_tgkill when signal delivery attempt is made to not linux process. Do not call em_find(p, ...) with p unlocked. Move common code for linux_tkill() and linux_tgkill() into linux_do_tkill(). Change linux siginfo_t definition to match actual linux one. Extend uid fields to 4 bytes from 2. The extension does not change structure layout and is binary compatible with previous definition, because i386 is little endian, and each uid field has 2 byte padding after it. Reported by: Nicolas Joly Submitted by: dchangin MFC after: 1 month --- sys/compat/linux/linux_signal.c | 113 +++++++++++++++++++++++++++++++--------- sys/compat/linux/linux_signal.h | 3 ++ 2 files changed, 92 insertions(+), 24 deletions(-) (limited to 'sys/compat') diff --git a/sys/compat/linux/linux_signal.c b/sys/compat/linux/linux_signal.c index d40d9d7..9bbc268 100644 --- a/sys/compat/linux/linux_signal.c +++ b/sys/compat/linux/linux_signal.c @@ -39,6 +39,8 @@ __FBSDID("$FreeBSD$"); #include #include +#include + #include "opt_compat.h" #ifdef COMPAT_LINUX32 @@ -535,45 +537,75 @@ linux_kill(struct thread *td, struct linux_kill_args *args) return (kill(td, &tmp)); } -int -linux_tgkill(struct thread *td, struct linux_tgkill_args *args) +static int +linux_do_tkill(struct thread *td, l_int tgid, l_int pid, l_int signum) { - struct linux_emuldata *em; - struct linux_kill_args ka; + struct proc *proc = td->td_proc; + struct linux_emuldata *em; struct proc *p; + ksiginfo_t ksi; + int error; -#ifdef DEBUG - if (ldebug(tgkill)) - printf(ARGS(tgkill, "%d, %d, %d"), args->tgid, args->pid, args->sig); -#endif - - ka.pid = args->pid; - ka.signum = args->sig; + AUDIT_ARG(signum, signum); + AUDIT_ARG(pid, pid); - if (args->tgid == -1) - return linux_kill(td, &ka); + /* + * Allow signal 0 as a means to check for privileges + */ + if (!LINUX_SIG_VALID(signum) && signum != 0) + return (EINVAL); - if ((p = pfind(args->pid)) == NULL) - return ESRCH; + if (signum > 0 && signum <= LINUX_SIGTBLSZ) + signum = linux_to_bsd_signal[_SIG_IDX(signum)]; - if (p->p_sysent != &elf_linux_sysvec) - return ESRCH; + if ((p = pfind(pid)) == NULL) { + if ((p = zpfind(pid)) == NULL) + return (ESRCH); + } - PROC_UNLOCK(p); + AUDIT_ARG(process, p); + error = p_cansignal(td, p, signum); + if (error) + goto out; + error = ESRCH; em = em_find(p, EMUL_DONTLOCK); if (em == NULL) { #ifdef DEBUG - printf("emuldata not found in tgkill.\n"); + printf("emuldata not found in do_tkill.\n"); #endif - return ESRCH; + goto out; } + if (tgid > 0 && em->shared->group_pid != tgid) + goto out; + + ksiginfo_init(&ksi); + ksi.ksi_signo = signum; + ksi.ksi_code = LINUX_SI_TKILL; + ksi.ksi_errno = 0; + ksi.ksi_pid = proc->p_pid; + ksi.ksi_uid = proc->p_ucred->cr_ruid; - if (em->shared->group_pid != args->tgid) - return ESRCH; + error = tdsignal(p, NULL, ksi.ksi_signo, &ksi); - return linux_kill(td, &ka); +out: + PROC_UNLOCK(p); + return (error); +} + +int +linux_tgkill(struct thread *td, struct linux_tgkill_args *args) +{ + +#ifdef DEBUG + if (ldebug(tgkill)) + printf(ARGS(tgkill, "%d, %d, %d"), args->tgid, args->pid, args->sig); +#endif + if (args->pid <= 0 || args->tgid <=0) + return (EINVAL); + + return (linux_do_tkill(td, args->tgid, args->pid, args->sig)); } int @@ -583,6 +615,39 @@ linux_tkill(struct thread *td, struct linux_tkill_args *args) if (ldebug(tkill)) printf(ARGS(tkill, "%i, %i"), args->tid, args->sig); #endif + if (args->tid <= 0) + return (EINVAL); + + return (linux_do_tkill(td, 0, args->tid, args->sig)); +} + +void +ksiginfo_to_lsiginfo(ksiginfo_t *ksi, l_siginfo_t *lsi, l_int sig) +{ + + lsi->lsi_signo = sig; + lsi->lsi_code = ksi->ksi_code; - return (linux_kill(td, (struct linux_kill_args *) args)); + switch (sig) { + case LINUX_SIGPOLL: + /* XXX si_fd? */ + lsi->lsi_band = ksi->ksi_band; + break; + case LINUX_SIGCHLD: + lsi->lsi_pid = ksi->ksi_pid; + lsi->lsi_uid = ksi->ksi_uid; + lsi->lsi_status = ksi->ksi_status; + break; + case LINUX_SIGBUS: + case LINUX_SIGILL: + case LINUX_SIGFPE: + case LINUX_SIGSEGV: + lsi->lsi_addr = PTROUT(ksi->ksi_addr); + break; + default: + /* XXX SI_TIMER etc... */ + lsi->lsi_pid = ksi->ksi_pid; + lsi->lsi_uid = ksi->ksi_uid; + break; + } } diff --git a/sys/compat/linux/linux_signal.h b/sys/compat/linux/linux_signal.h index 5f6d5bd..ba780e9 100644 --- a/sys/compat/linux/linux_signal.h +++ b/sys/compat/linux/linux_signal.h @@ -31,9 +31,12 @@ #ifndef _LINUX_SIGNAL_H_ #define _LINUX_SIGNAL_H_ +#define LINUX_SI_TKILL -6; + void linux_to_bsd_sigset(l_sigset_t *, sigset_t *); void bsd_to_linux_sigset(sigset_t *, l_sigset_t *); int linux_do_sigaction(struct thread *, int, l_sigaction_t *, l_sigaction_t *); +void ksiginfo_to_lsiginfo(ksiginfo_t *ksi, l_siginfo_t *lsi, l_int sig); #define LINUX_SIG_VALID(sig) ((sig) <= LINUX_NSIG && (sig) > 0) -- cgit v1.1