Christophe Leroy <christophe.le...@csgroup.eu> writes: > 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.
I don't understand. What is wrong with? #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; \ \ copy_siginfo_to_external32(&__ucs_new, __ucs_from); \ unsafe_copy_to_user(__ucs_to, &__ucs_new, \ sizeof(struct compat_siginfo), label); \ } while (0) Your replacement of "memset(to, 0, sizeof(*to))" with "struct compat_siginfo __ucs_new = {0}". is actively unsafe as the compiler is free not to initialize any holes in the structure to 0 in the later case. Is there something about the unsafe macros that I am not aware of that makes it improper to actually call C functions? Is that a requirement for the instructions that change the user space access behavior? >From the looks of this change all that you are doing is making it so that all of copy_siginfo_to_external32 is being inlined. If that is not a hard requirement of the instructions it seems like the wrong thing to do here. copy_siginfo_to_external32 has not failures so it does not need to be inlined so you can jump to the label. Eric > > 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,