git: fc13bfeae58f - stable/13 - linux(4): Properly convert linux siginfo to native siginfo add input validation.

From: Dmitry Chagin <dchagin_at_FreeBSD.org>
Date: Fri, 17 Jun 2022 19:29:06 UTC
The branch stable/13 has been updated by dchagin:

URL: https://cgit.FreeBSD.org/src/commit/?id=fc13bfeae58f1be3d03971caa9a3078cdf66878c

commit fc13bfeae58f1be3d03971caa9a3078cdf66878c
Author:     Dmitry Chagin <dchagin@FreeBSD.org>
AuthorDate: 2021-06-07 02:55:34 +0000
Commit:     Dmitry Chagin <dchagin@FreeBSD.org>
CommitDate: 2022-06-17 19:28:00 +0000

    linux(4): Properly convert linux siginfo to native siginfo
    add input validation.
    
    MFC after:      2 weeks
    
    (cherry picked from commit 9c1045ff00935a4bb0592d8c00ca1981d3e6eb8b)
---
 sys/compat/linux/linux_mib.h    |  1 +
 sys/compat/linux/linux_signal.c | 59 ++++++++++++++++++++++++++++++-----------
 sys/compat/linux/linux_signal.h |  3 ++-
 3 files changed, 47 insertions(+), 16 deletions(-)

diff --git a/sys/compat/linux/linux_mib.h b/sys/compat/linux/linux_mib.h
index 8f2b87e5f3a6..4d2578f91990 100644
--- a/sys/compat/linux/linux_mib.h
+++ b/sys/compat/linux/linux_mib.h
@@ -59,6 +59,7 @@ int	linux_kernver(struct thread *td);
 
 #define	LINUX_KERNVER_2004000	LINUX_KERNVER(2,4,0)
 #define	LINUX_KERNVER_2006000	LINUX_KERNVER(2,6,0)
+#define	LINUX_KERNVER_2006039	LINUX_KERNVER(2,6,39)
 
 #define	linux_use26(t)		(linux_kernver(t) >= LINUX_KERNVER_2006000)
 
diff --git a/sys/compat/linux/linux_signal.c b/sys/compat/linux/linux_signal.c
index 50dae4d90d43..467d2810a228 100644
--- a/sys/compat/linux/linux_signal.c
+++ b/sys/compat/linux/linux_signal.c
@@ -50,6 +50,7 @@ __FBSDID("$FreeBSD$");
 #include <machine/../linux/linux.h>
 #include <machine/../linux/linux_proto.h>
 #endif
+#include <compat/linux/linux_mib.h>
 #include <compat/linux/linux_signal.h>
 #include <compat/linux/linux_timer.h>
 #include <compat/linux/linux_util.h>
@@ -656,17 +657,40 @@ siginfo_to_lsiginfo(const siginfo_t *si, l_siginfo_t *lsi, l_int sig)
 	}
 }
 
-void
-lsiginfo_to_ksiginfo(const l_siginfo_t *lsi, ksiginfo_t *ksi, int sig)
+int
+lsiginfo_to_siginfo(struct thread *td, const l_siginfo_t *lsi,
+    siginfo_t *si, int sig)
 {
 
-	ksi->ksi_signo = sig;
-	ksi->ksi_code = lsi->lsi_code;	/* XXX. Convert. */
-	ksi->ksi_pid = lsi->lsi_pid;
-	ksi->ksi_uid = lsi->lsi_uid;
-	ksi->ksi_status = lsi->lsi_status;
-	ksi->ksi_addr = PTRIN(lsi->lsi_addr);
-	ksi->ksi_info.si_value.sival_int = lsi->lsi_int;
+	switch (lsi->lsi_code) {
+	case LINUX_SI_TKILL:
+		if (linux_kernver(td) >= LINUX_KERNVER_2006039) {
+			linux_msg(td, "SI_TKILL forbidden since 2.6.39");
+			return (EPERM);
+		}
+		si->si_code = SI_LWP;
+	case LINUX_SI_QUEUE:
+		si->si_code = SI_QUEUE;
+		break;
+	case LINUX_SI_TIMER:
+		si->si_code = SI_TIMER;
+		break;
+	case LINUX_SI_MESGQ:
+		si->si_code = SI_MESGQ;
+		break;
+	case LINUX_SI_ASYNCIO:
+		si->si_code = SI_ASYNCIO;
+		break;
+	default:
+		si->si_code = lsi->lsi_code;
+		break;
+	}
+
+	si->si_signo = sig;
+	si->si_pid = td->td_proc->p_pid;
+	si->si_uid = td->td_ucred->cr_ruid;
+	si->si_value.sival_ptr = PTRIN(lsi->lsi_value.sival_ptr);
+	return (0);
 }
 
 int
@@ -686,9 +710,14 @@ linux_rt_sigqueueinfo(struct thread *td, struct linux_rt_sigqueueinfo_args *args
 		return (error);
 
 	if (linfo.lsi_code >= 0)
+		/* SI_USER, SI_KERNEL */
 		return (EPERM);
 
 	sig = linux_to_bsd_signal(args->sig);
+	ksiginfo_init(&ksi);
+	error = lsiginfo_to_siginfo(td, &linfo, &ksi.ksi_info, sig);
+	if (error != 0)
+		return (error);
 
 	error = ESRCH;
 	if ((p = pfind_any(args->pid)) != NULL) {
@@ -697,9 +726,6 @@ linux_rt_sigqueueinfo(struct thread *td, struct linux_rt_sigqueueinfo_args *args
 			PROC_UNLOCK(p);
 			return (error);
 		}
-
-		ksiginfo_init(&ksi);
-		lsiginfo_to_ksiginfo(&linfo, &ksi, sig);
 		error = tdsendsignal(p, NULL, sig, &ksi);
 		PROC_UNLOCK(p);
 	}
@@ -726,13 +752,16 @@ linux_rt_tgsigqueueinfo(struct thread *td, struct linux_rt_tgsigqueueinfo_args *
 	if (linfo.lsi_code >= 0)
 		return (EPERM);
 
+	sig = linux_to_bsd_signal(args->sig);
+	ksiginfo_init(&ksi);
+	error = lsiginfo_to_siginfo(td, &linfo, &ksi.ksi_info, sig);
+	if (error != 0)
+		return (error);
+
 	tds = linux_tdfind(td, args->tid, args->tgid);
 	if (tds == NULL)
 		return (ESRCH);
 
-	sig = linux_to_bsd_signal(args->sig);
-	ksiginfo_init(&ksi);
-	lsiginfo_to_ksiginfo(&linfo, &ksi, sig);
 	return (linux_do_tkill(td, tds, &ksi));
 }
 
diff --git a/sys/compat/linux/linux_signal.h b/sys/compat/linux/linux_signal.h
index 5df74fb71029..bb34613fa2be 100644
--- a/sys/compat/linux/linux_signal.h
+++ b/sys/compat/linux/linux_signal.h
@@ -46,6 +46,7 @@
 int linux_do_sigaction(struct thread *, int, l_sigaction_t *, l_sigaction_t *);
 void ksiginfo_to_lsiginfo(const ksiginfo_t *ksi, l_siginfo_t *lsi, l_int sig);
 void siginfo_to_lsiginfo(const siginfo_t *si, l_siginfo_t *lsi, l_int sig);
-void lsiginfo_to_ksiginfo(const l_siginfo_t *lsi, ksiginfo_t *ksi, int sig);
+int lsiginfo_to_siginfo(struct thread *td, const l_siginfo_t *lsi,
+		siginfo_t *si, int sig);
 
 #endif /* _LINUX_SIGNAL_H_ */