[PATCH RESEND v3 4/6] signal: Add unsafe_copy_siginfo_to_user32()

From: Christophe Leroy
Date: Mon Sep 13 2021 - 11:24:07 EST


In the same spirit as commit fb05121fd6a2 ("signal: Add
unsafe_get_compat_sigset()"), implement an 'unsafe' version of
copy_siginfo_to_user32() in order to use it within user access blocks.

To do so, we need inline version of copy_siginfo_to_external32() as we
don't want any function call inside user access blocks.

Signed-off-by: Christophe Leroy <christophe.leroy@xxxxxxxxxx>
---
include/linux/compat.h | 83 +++++++++++++++++++++++++++++-
include/linux/signal.h | 58 +++++++++++++++++++++
kernel/signal.c | 114 +----------------------------------------
3 files changed, 141 insertions(+), 114 deletions(-)

diff --git a/include/linux/compat.h b/include/linux/compat.h
index 8e0598c7d1d1..68823f4b86ee 100644
--- a/include/linux/compat.h
+++ b/include/linux/compat.h
@@ -412,6 +412,19 @@ int __copy_siginfo_to_user32(struct compat_siginfo __user *to,
#ifndef copy_siginfo_to_user32
#define copy_siginfo_to_user32 __copy_siginfo_to_user32
#endif
+
+#ifdef CONFIG_COMPAT
+#define unsafe_copy_siginfo_to_user32(to, from, label) do { \
+ struct compat_siginfo __user *__ucs_to = to; \
+ const struct kernel_siginfo *__ucs_from = from; \
+ struct compat_siginfo __ucs_new = {0}; \
+ \
+ __copy_siginfo_to_external32(&__ucs_new, __ucs_from); \
+ unsafe_copy_to_user(__ucs_to, &__ucs_new, \
+ sizeof(struct compat_siginfo), label); \
+} while (0)
+#endif
+
int get_compat_sigevent(struct sigevent *event,
const struct compat_sigevent __user *u_event);

@@ -992,15 +1005,81 @@ static inline bool in_compat_syscall(void) { return false; }
* appropriately converted them already.
*/
#ifndef compat_ptr
-static inline void __user *compat_ptr(compat_uptr_t uptr)
+static __always_inline void __user *compat_ptr(compat_uptr_t uptr)
{
return (void __user *)(unsigned long)uptr;
}
#endif

-static inline compat_uptr_t ptr_to_compat(void __user *uptr)
+static __always_inline compat_uptr_t ptr_to_compat(void __user *uptr)
{
return (u32)(unsigned long)uptr;
}

+static __always_inline void
+__copy_siginfo_to_external32(struct compat_siginfo *to,
+ const struct kernel_siginfo *from)
+{
+ to->si_signo = from->si_signo;
+ to->si_errno = from->si_errno;
+ to->si_code = from->si_code;
+ switch(__siginfo_layout(from->si_signo, from->si_code)) {
+ case SIL_KILL:
+ to->si_pid = from->si_pid;
+ to->si_uid = from->si_uid;
+ break;
+ case SIL_TIMER:
+ to->si_tid = from->si_tid;
+ to->si_overrun = from->si_overrun;
+ to->si_int = from->si_int;
+ break;
+ case SIL_POLL:
+ to->si_band = from->si_band;
+ to->si_fd = from->si_fd;
+ break;
+ case SIL_FAULT:
+ to->si_addr = ptr_to_compat(from->si_addr);
+ break;
+ case SIL_FAULT_TRAPNO:
+ to->si_addr = ptr_to_compat(from->si_addr);
+ to->si_trapno = from->si_trapno;
+ break;
+ case SIL_FAULT_MCEERR:
+ to->si_addr = ptr_to_compat(from->si_addr);
+ to->si_addr_lsb = from->si_addr_lsb;
+ break;
+ case SIL_FAULT_BNDERR:
+ to->si_addr = ptr_to_compat(from->si_addr);
+ to->si_lower = ptr_to_compat(from->si_lower);
+ to->si_upper = ptr_to_compat(from->si_upper);
+ break;
+ case SIL_FAULT_PKUERR:
+ to->si_addr = ptr_to_compat(from->si_addr);
+ to->si_pkey = from->si_pkey;
+ break;
+ case SIL_FAULT_PERF_EVENT:
+ to->si_addr = ptr_to_compat(from->si_addr);
+ to->si_perf_data = from->si_perf_data;
+ to->si_perf_type = from->si_perf_type;
+ break;
+ case SIL_CHLD:
+ to->si_pid = from->si_pid;
+ to->si_uid = from->si_uid;
+ to->si_status = from->si_status;
+ to->si_utime = from->si_utime;
+ to->si_stime = from->si_stime;
+ break;
+ case SIL_RT:
+ to->si_pid = from->si_pid;
+ to->si_uid = from->si_uid;
+ to->si_int = from->si_int;
+ break;
+ case SIL_SYS:
+ to->si_call_addr = ptr_to_compat(from->si_call_addr);
+ to->si_syscall = from->si_syscall;
+ to->si_arch = from->si_arch;
+ break;
+ }
+}
+
#endif /* _LINUX_COMPAT_H */
diff --git a/include/linux/signal.h b/include/linux/signal.h
index 70ea7e741427..637260bc193d 100644
--- a/include/linux/signal.h
+++ b/include/linux/signal.h
@@ -65,6 +65,64 @@ enum siginfo_layout {
SIL_SYS,
};

+static const struct {
+ unsigned char limit, layout;
+} sig_sicodes[] = {
+ [SIGILL] = { NSIGILL, SIL_FAULT },
+ [SIGFPE] = { NSIGFPE, SIL_FAULT },
+ [SIGSEGV] = { NSIGSEGV, SIL_FAULT },
+ [SIGBUS] = { NSIGBUS, SIL_FAULT },
+ [SIGTRAP] = { NSIGTRAP, SIL_FAULT },
+#if defined(SIGEMT)
+ [SIGEMT] = { NSIGEMT, SIL_FAULT },
+#endif
+ [SIGCHLD] = { NSIGCHLD, SIL_CHLD },
+ [SIGPOLL] = { NSIGPOLL, SIL_POLL },
+ [SIGSYS] = { NSIGSYS, SIL_SYS },
+};
+
+static __always_inline enum
+siginfo_layout __siginfo_layout(unsigned sig, int si_code)
+{
+ enum siginfo_layout layout = SIL_KILL;
+
+ if ((si_code > SI_USER) && (si_code < SI_KERNEL)) {
+ if ((sig < ARRAY_SIZE(sig_sicodes)) &&
+ (si_code <= sig_sicodes[sig].limit)) {
+ layout = sig_sicodes[sig].layout;
+ /* Handle the exceptions */
+ if ((sig == SIGBUS) &&
+ (si_code >= BUS_MCEERR_AR) && (si_code <= BUS_MCEERR_AO))
+ layout = SIL_FAULT_MCEERR;
+ else if ((sig == SIGSEGV) && (si_code == SEGV_BNDERR))
+ layout = SIL_FAULT_BNDERR;
+#ifdef SEGV_PKUERR
+ else if ((sig == SIGSEGV) && (si_code == SEGV_PKUERR))
+ layout = SIL_FAULT_PKUERR;
+#endif
+ else if ((sig == SIGTRAP) && (si_code == TRAP_PERF))
+ layout = SIL_FAULT_PERF_EVENT;
+ else if (IS_ENABLED(CONFIG_SPARC) &&
+ (sig == SIGILL) && (si_code == ILL_ILLTRP))
+ layout = SIL_FAULT_TRAPNO;
+ else if (IS_ENABLED(CONFIG_ALPHA) &&
+ ((sig == SIGFPE) ||
+ ((sig == SIGTRAP) && (si_code == TRAP_UNK))))
+ layout = SIL_FAULT_TRAPNO;
+ }
+ else if (si_code <= NSIGPOLL)
+ layout = SIL_POLL;
+ } else {
+ if (si_code == SI_TIMER)
+ layout = SIL_TIMER;
+ else if (si_code == SI_SIGIO)
+ layout = SIL_POLL;
+ else if (si_code < 0)
+ layout = SIL_RT;
+ }
+ return layout;
+}
+
enum siginfo_layout siginfo_layout(unsigned sig, int si_code);

/*
diff --git a/kernel/signal.c b/kernel/signal.c
index 23f168730b7e..0d402bdb174e 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -3249,22 +3249,6 @@ COMPAT_SYSCALL_DEFINE2(rt_sigpending, compat_sigset_t __user *, uset,
}
#endif

-static const struct {
- unsigned char limit, layout;
-} sig_sicodes[] = {
- [SIGILL] = { NSIGILL, SIL_FAULT },
- [SIGFPE] = { NSIGFPE, SIL_FAULT },
- [SIGSEGV] = { NSIGSEGV, SIL_FAULT },
- [SIGBUS] = { NSIGBUS, SIL_FAULT },
- [SIGTRAP] = { NSIGTRAP, SIL_FAULT },
-#if defined(SIGEMT)
- [SIGEMT] = { NSIGEMT, SIL_FAULT },
-#endif
- [SIGCHLD] = { NSIGCHLD, SIL_CHLD },
- [SIGPOLL] = { NSIGPOLL, SIL_POLL },
- [SIGSYS] = { NSIGSYS, SIL_SYS },
-};
-
static bool known_siginfo_layout(unsigned sig, int si_code)
{
if (si_code == SI_KERNEL)
@@ -3286,42 +3270,7 @@ static bool known_siginfo_layout(unsigned sig, int si_code)

enum siginfo_layout siginfo_layout(unsigned sig, int si_code)
{
- enum siginfo_layout layout = SIL_KILL;
- if ((si_code > SI_USER) && (si_code < SI_KERNEL)) {
- if ((sig < ARRAY_SIZE(sig_sicodes)) &&
- (si_code <= sig_sicodes[sig].limit)) {
- layout = sig_sicodes[sig].layout;
- /* Handle the exceptions */
- if ((sig == SIGBUS) &&
- (si_code >= BUS_MCEERR_AR) && (si_code <= BUS_MCEERR_AO))
- layout = SIL_FAULT_MCEERR;
- else if ((sig == SIGSEGV) && (si_code == SEGV_BNDERR))
- layout = SIL_FAULT_BNDERR;
-#ifdef SEGV_PKUERR
- else if ((sig == SIGSEGV) && (si_code == SEGV_PKUERR))
- layout = SIL_FAULT_PKUERR;
-#endif
- else if ((sig == SIGTRAP) && (si_code == TRAP_PERF))
- layout = SIL_FAULT_PERF_EVENT;
- else if (IS_ENABLED(CONFIG_SPARC) &&
- (sig == SIGILL) && (si_code == ILL_ILLTRP))
- layout = SIL_FAULT_TRAPNO;
- else if (IS_ENABLED(CONFIG_ALPHA) &&
- ((sig == SIGFPE) ||
- ((sig == SIGTRAP) && (si_code == TRAP_UNK))))
- layout = SIL_FAULT_TRAPNO;
- }
- else if (si_code <= NSIGPOLL)
- layout = SIL_POLL;
- } else {
- if (si_code == SI_TIMER)
- layout = SIL_TIMER;
- else if (si_code == SI_SIGIO)
- layout = SIL_POLL;
- else if (si_code < 0)
- layout = SIL_RT;
- }
- return layout;
+ return __siginfo_layout(sig, si_code);
}

int copy_siginfo_to_user(siginfo_t __user *to, const kernel_siginfo_t *from)
@@ -3389,66 +3338,7 @@ void copy_siginfo_to_external32(struct compat_siginfo *to,
{
memset(to, 0, sizeof(*to));

- to->si_signo = from->si_signo;
- to->si_errno = from->si_errno;
- to->si_code = from->si_code;
- switch(siginfo_layout(from->si_signo, from->si_code)) {
- case SIL_KILL:
- to->si_pid = from->si_pid;
- to->si_uid = from->si_uid;
- break;
- case SIL_TIMER:
- to->si_tid = from->si_tid;
- to->si_overrun = from->si_overrun;
- to->si_int = from->si_int;
- break;
- case SIL_POLL:
- to->si_band = from->si_band;
- to->si_fd = from->si_fd;
- break;
- case SIL_FAULT:
- to->si_addr = ptr_to_compat(from->si_addr);
- break;
- case SIL_FAULT_TRAPNO:
- to->si_addr = ptr_to_compat(from->si_addr);
- to->si_trapno = from->si_trapno;
- break;
- case SIL_FAULT_MCEERR:
- to->si_addr = ptr_to_compat(from->si_addr);
- to->si_addr_lsb = from->si_addr_lsb;
- break;
- case SIL_FAULT_BNDERR:
- to->si_addr = ptr_to_compat(from->si_addr);
- to->si_lower = ptr_to_compat(from->si_lower);
- to->si_upper = ptr_to_compat(from->si_upper);
- break;
- case SIL_FAULT_PKUERR:
- to->si_addr = ptr_to_compat(from->si_addr);
- to->si_pkey = from->si_pkey;
- break;
- case SIL_FAULT_PERF_EVENT:
- to->si_addr = ptr_to_compat(from->si_addr);
- to->si_perf_data = from->si_perf_data;
- to->si_perf_type = from->si_perf_type;
- break;
- case SIL_CHLD:
- to->si_pid = from->si_pid;
- to->si_uid = from->si_uid;
- to->si_status = from->si_status;
- to->si_utime = from->si_utime;
- to->si_stime = from->si_stime;
- break;
- case SIL_RT:
- to->si_pid = from->si_pid;
- to->si_uid = from->si_uid;
- to->si_int = from->si_int;
- break;
- case SIL_SYS:
- to->si_call_addr = ptr_to_compat(from->si_call_addr);
- to->si_syscall = from->si_syscall;
- to->si_arch = from->si_arch;
- break;
- }
+ __copy_siginfo_to_external32(to, from);
}

int __copy_siginfo_to_user32(struct compat_siginfo __user *to,
--
2.31.1