aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric W. Biederman <ebiederm@xmission.com>2018-09-25 06:59:31 -0400
committerEric W. Biederman <ebiederm@xmission.com>2018-10-03 10:50:39 -0400
commit4ce5f9c9e7546915c559ffae594e6d73f918db00 (patch)
treedc7eeac8b4956f0b2dfc6209e1398896a59896f5
parentae7795bc6187a15ec51cf258abae656a625f9980 (diff)
signal: Use a smaller struct siginfo in the kernel
We reserve 128 bytes for struct siginfo but only use about 48 bytes on 64bit and 32 bytes on 32bit. Someday we might use more but it is unlikely to be anytime soon. Userspace seems content with just enough bytes of siginfo to implement sigqueue. Or in the case of checkpoint/restart reinjecting signals the kernel has sent. Reducing the stack footprint and the work to copy siginfo around from 2 cachelines to 1 cachelines seems worth doing even if I don't have benchmarks to show a performance difference. Suggested-by: Linus Torvalds <torvalds@linux-foundation.org> Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
-rw-r--r--include/linux/signal.h2
-rw-r--r--include/linux/signal_types.h5
-rw-r--r--kernel/signal.c82
3 files changed, 67 insertions, 22 deletions
diff --git a/include/linux/signal.h b/include/linux/signal.h
index 70031b10b918..706a499d1eb1 100644
--- a/include/linux/signal.h
+++ b/include/linux/signal.h
@@ -22,6 +22,8 @@ static inline void clear_siginfo(kernel_siginfo_t *info)
22 memset(info, 0, sizeof(*info)); 22 memset(info, 0, sizeof(*info));
23} 23}
24 24
25#define SI_EXPANSION_SIZE (sizeof(struct siginfo) - sizeof(struct kernel_siginfo))
26
25int copy_siginfo_to_user(siginfo_t __user *to, const kernel_siginfo_t *from); 27int copy_siginfo_to_user(siginfo_t __user *to, const kernel_siginfo_t *from);
26int copy_siginfo_from_user(kernel_siginfo_t *to, const siginfo_t __user *from); 28int copy_siginfo_from_user(kernel_siginfo_t *to, const siginfo_t __user *from);
27 29
diff --git a/include/linux/signal_types.h b/include/linux/signal_types.h
index 2a40a9c5e4ad..f8a90ae9c6ec 100644
--- a/include/linux/signal_types.h
+++ b/include/linux/signal_types.h
@@ -10,10 +10,7 @@
10#include <uapi/linux/signal.h> 10#include <uapi/linux/signal.h>
11 11
12typedef struct kernel_siginfo { 12typedef struct kernel_siginfo {
13 union { 13 __SIGINFO;
14 __SIGINFO;
15 int _si_pad[SI_MAX_SIZE/sizeof(int)];
16 };
17} kernel_siginfo_t; 14} kernel_siginfo_t;
18 15
19/* 16/*
diff --git a/kernel/signal.c b/kernel/signal.c
index 161cad4e448c..1c2dd117fee0 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -2844,27 +2844,48 @@ COMPAT_SYSCALL_DEFINE2(rt_sigpending, compat_sigset_t __user *, uset,
2844} 2844}
2845#endif 2845#endif
2846 2846
2847static const struct {
2848 unsigned char limit, layout;
2849} sig_sicodes[] = {
2850 [SIGILL] = { NSIGILL, SIL_FAULT },
2851 [SIGFPE] = { NSIGFPE, SIL_FAULT },
2852 [SIGSEGV] = { NSIGSEGV, SIL_FAULT },
2853 [SIGBUS] = { NSIGBUS, SIL_FAULT },
2854 [SIGTRAP] = { NSIGTRAP, SIL_FAULT },
2855#if defined(SIGEMT)
2856 [SIGEMT] = { NSIGEMT, SIL_FAULT },
2857#endif
2858 [SIGCHLD] = { NSIGCHLD, SIL_CHLD },
2859 [SIGPOLL] = { NSIGPOLL, SIL_POLL },
2860 [SIGSYS] = { NSIGSYS, SIL_SYS },
2861};
2862
2863static bool known_siginfo_layout(int sig, int si_code)
2864{
2865 if (si_code == SI_KERNEL)
2866 return true;
2867 else if ((si_code > SI_USER)) {
2868 if (sig_specific_sicodes(sig)) {
2869 if (si_code <= sig_sicodes[sig].limit)
2870 return true;
2871 }
2872 else if (si_code <= NSIGPOLL)
2873 return true;
2874 }
2875 else if (si_code >= SI_DETHREAD)
2876 return true;
2877 else if (si_code == SI_ASYNCNL)
2878 return true;
2879 return false;
2880}
2881
2847enum siginfo_layout siginfo_layout(int sig, int si_code) 2882enum siginfo_layout siginfo_layout(int sig, int si_code)
2848{ 2883{
2849 enum siginfo_layout layout = SIL_KILL; 2884 enum siginfo_layout layout = SIL_KILL;
2850 if ((si_code > SI_USER) && (si_code < SI_KERNEL)) { 2885 if ((si_code > SI_USER) && (si_code < SI_KERNEL)) {
2851 static const struct { 2886 if ((sig < ARRAY_SIZE(sig_sicodes)) &&
2852 unsigned char limit, layout; 2887 (si_code <= sig_sicodes[sig].limit)) {
2853 } filter[] = { 2888 layout = sig_sicodes[sig].layout;
2854 [SIGILL] = { NSIGILL, SIL_FAULT },
2855 [SIGFPE] = { NSIGFPE, SIL_FAULT },
2856 [SIGSEGV] = { NSIGSEGV, SIL_FAULT },
2857 [SIGBUS] = { NSIGBUS, SIL_FAULT },
2858 [SIGTRAP] = { NSIGTRAP, SIL_FAULT },
2859#if defined(SIGEMT)
2860 [SIGEMT] = { NSIGEMT, SIL_FAULT },
2861#endif
2862 [SIGCHLD] = { NSIGCHLD, SIL_CHLD },
2863 [SIGPOLL] = { NSIGPOLL, SIL_POLL },
2864 [SIGSYS] = { NSIGSYS, SIL_SYS },
2865 };
2866 if ((sig < ARRAY_SIZE(filter)) && (si_code <= filter[sig].limit)) {
2867 layout = filter[sig].layout;
2868 /* Handle the exceptions */ 2889 /* Handle the exceptions */
2869 if ((sig == SIGBUS) && 2890 if ((sig == SIGBUS) &&
2870 (si_code >= BUS_MCEERR_AR) && (si_code <= BUS_MCEERR_AO)) 2891 (si_code >= BUS_MCEERR_AR) && (si_code <= BUS_MCEERR_AO))
@@ -2889,17 +2910,42 @@ enum siginfo_layout siginfo_layout(int sig, int si_code)
2889 return layout; 2910 return layout;
2890} 2911}
2891 2912
2913static inline char __user *si_expansion(const siginfo_t __user *info)
2914{
2915 return ((char __user *)info) + sizeof(struct kernel_siginfo);
2916}
2917
2892int copy_siginfo_to_user(siginfo_t __user *to, const kernel_siginfo_t *from) 2918int copy_siginfo_to_user(siginfo_t __user *to, const kernel_siginfo_t *from)
2893{ 2919{
2920 char __user *expansion = si_expansion(to);
2894 if (copy_to_user(to, from , sizeof(struct kernel_siginfo))) 2921 if (copy_to_user(to, from , sizeof(struct kernel_siginfo)))
2895 return -EFAULT; 2922 return -EFAULT;
2923 if (clear_user(expansion, SI_EXPANSION_SIZE))
2924 return -EFAULT;
2896 return 0; 2925 return 0;
2897} 2926}
2898 2927
2899int copy_siginfo_from_user(kernel_siginfo_t *to, const siginfo_t __user *from) 2928int copy_siginfo_from_user(kernel_siginfo_t *to, const siginfo_t __user *from)
2900{ 2929{
2901 if (copy_from_user(to, from, sizeof(struct siginfo))) 2930 if (copy_from_user(to, from, sizeof(struct kernel_siginfo)))
2902 return -EFAULT; 2931 return -EFAULT;
2932 if (unlikely(!known_siginfo_layout(to->si_signo, to->si_code))) {
2933 char __user *expansion = si_expansion(from);
2934 char buf[SI_EXPANSION_SIZE];
2935 int i;
2936 /*
2937 * An unknown si_code might need more than
2938 * sizeof(struct kernel_siginfo) bytes. Verify all of the
2939 * extra bytes are 0. This guarantees copy_siginfo_to_user
2940 * will return this data to userspace exactly.
2941 */
2942 if (copy_from_user(&buf, expansion, SI_EXPANSION_SIZE))
2943 return -EFAULT;
2944 for (i = 0; i < SI_EXPANSION_SIZE; i++) {
2945 if (buf[i] != 0)
2946 return -E2BIG;
2947 }
2948 }
2903 return 0; 2949 return 0;
2904} 2950}
2905 2951