Skip to content

Commit 4ce5f9c

Browse files
committed
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>
1 parent ae7795b commit 4ce5f9c

File tree

3 files changed

+67
-22
lines changed

3 files changed

+67
-22
lines changed

include/linux/signal.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ static inline void clear_siginfo(kernel_siginfo_t *info)
2222
memset(info, 0, sizeof(*info));
2323
}
2424

25+
#define SI_EXPANSION_SIZE (sizeof(struct siginfo) - sizeof(struct kernel_siginfo))
26+
2527
int copy_siginfo_to_user(siginfo_t __user *to, const kernel_siginfo_t *from);
2628
int copy_siginfo_from_user(kernel_siginfo_t *to, const siginfo_t __user *from);
2729

include/linux/signal_types.h

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,7 @@
1010
#include <uapi/linux/signal.h>
1111

1212
typedef struct kernel_siginfo {
13-
union {
14-
__SIGINFO;
15-
int _si_pad[SI_MAX_SIZE/sizeof(int)];
16-
};
13+
__SIGINFO;
1714
} kernel_siginfo_t;
1815

1916
/*

kernel/signal.c

Lines changed: 64 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2844,27 +2844,48 @@ COMPAT_SYSCALL_DEFINE2(rt_sigpending, compat_sigset_t __user *, uset,
28442844
}
28452845
#endif
28462846

2847+
static 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+
2863+
static 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+
28472882
enum siginfo_layout siginfo_layout(int sig, int si_code)
28482883
{
28492884
enum siginfo_layout layout = SIL_KILL;
28502885
if ((si_code > SI_USER) && (si_code < SI_KERNEL)) {
2851-
static const struct {
2852-
unsigned char limit, layout;
2853-
} filter[] = {
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;
2886+
if ((sig < ARRAY_SIZE(sig_sicodes)) &&
2887+
(si_code <= sig_sicodes[sig].limit)) {
2888+
layout = sig_sicodes[sig].layout;
28682889
/* Handle the exceptions */
28692890
if ((sig == SIGBUS) &&
28702891
(si_code >= BUS_MCEERR_AR) && (si_code <= BUS_MCEERR_AO))
@@ -2889,17 +2910,42 @@ enum siginfo_layout siginfo_layout(int sig, int si_code)
28892910
return layout;
28902911
}
28912912

2913+
static inline char __user *si_expansion(const siginfo_t __user *info)
2914+
{
2915+
return ((char __user *)info) + sizeof(struct kernel_siginfo);
2916+
}
2917+
28922918
int copy_siginfo_to_user(siginfo_t __user *to, const kernel_siginfo_t *from)
28932919
{
2920+
char __user *expansion = si_expansion(to);
28942921
if (copy_to_user(to, from , sizeof(struct kernel_siginfo)))
28952922
return -EFAULT;
2923+
if (clear_user(expansion, SI_EXPANSION_SIZE))
2924+
return -EFAULT;
28962925
return 0;
28972926
}
28982927

28992928
int copy_siginfo_from_user(kernel_siginfo_t *to, const siginfo_t __user *from)
29002929
{
2901-
if (copy_from_user(to, from, sizeof(struct siginfo)))
2930+
if (copy_from_user(to, from, sizeof(struct kernel_siginfo)))
29022931
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+
}
29032949
return 0;
29042950
}
29052951

0 commit comments

Comments
 (0)