summaryrefslogtreecommitdiffstats
path: root/sys/compat
diff options
context:
space:
mode:
authorkib <kib@FreeBSD.org>2008-10-19 10:02:26 +0000
committerkib <kib@FreeBSD.org>2008-10-19 10:02:26 +0000
commit29ccf7d1668ab3c3e3e82c53dbf69d4c5a9be824 (patch)
treeb27837b90bdbe0e63b7ff72ce0c5c2b29bb7cbd5 /sys/compat
parent40d90afbe6bf6ae24e4859c61cb929f3f0ccc441 (diff)
downloadFreeBSD-src-29ccf7d1668ab3c3e3e82c53dbf69d4c5a9be824.zip
FreeBSD-src-29ccf7d1668ab3c3e3e82c53dbf69d4c5a9be824.tar.gz
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 <njoly pasteur fr> Submitted by: dchangin MFC after: 1 month
Diffstat (limited to 'sys/compat')
-rw-r--r--sys/compat/linux/linux_signal.c113
-rw-r--r--sys/compat/linux/linux_signal.h3
2 files changed, 92 insertions, 24 deletions
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 <sys/syscallsubr.h>
#include <sys/sysproto.h>
+#include <security/audit/audit.h>
+
#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)
OpenPOWER on IntegriCloud