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.le...@csgroup.eu>
---
 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

Reply via email to