aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDave Martin <Dave.Martin@arm.com>2017-06-20 13:23:39 -0400
committerWill Deacon <will.deacon@arm.com>2017-06-23 13:20:18 -0400
commit33f082614c3443d937f50fe936f284f62bbb4a1b (patch)
treef3d1f5fc5eaa4a3dca12e052a19ff6f462c5c880
parent8effeaaf2cacc8a8007d3089e253e7baaff57bb7 (diff)
arm64: signal: Allow expansion of the signal frame
This patch defines an extra_context signal frame record that can be used to describe an expanded signal frame, and modifies the context block allocator and signal frame setup and parsing code to create, populate, parse and decode this block as necessary. To avoid abuse by userspace, parse_user_sigframe() attempts to ensure that: * no more than one extra_context is accepted; * the extra context data is a sensible size, and properly placed and aligned. The extra_context data is required to start at the first 16-byte aligned address immediately after the dummy terminator record following extra_context in rt_sigframe.__reserved[] (as ensured during signal delivery). This serves as a sanity-check that the signal frame has not been moved or copied without taking the extra data into account. Signed-off-by: Dave Martin <Dave.Martin@arm.com> Reviewed-by: Catalin Marinas <catalin.marinas@arm.com> [will: add __force annotation when casting extra_datap to __user pointer] Signed-off-by: Will Deacon <will.deacon@arm.com>
-rw-r--r--arch/arm64/include/uapi/asm/sigcontext.h38
-rw-r--r--arch/arm64/kernel/signal.c195
2 files changed, 214 insertions, 19 deletions
diff --git a/arch/arm64/include/uapi/asm/sigcontext.h b/arch/arm64/include/uapi/asm/sigcontext.h
index 1328a2c14371..f0a76b9fcd6e 100644
--- a/arch/arm64/include/uapi/asm/sigcontext.h
+++ b/arch/arm64/include/uapi/asm/sigcontext.h
@@ -41,9 +41,10 @@ struct sigcontext {
41 * 41 *
42 * 0x210 fpsimd_context 42 * 0x210 fpsimd_context
43 * 0x10 esr_context 43 * 0x10 esr_context
44 * 0x20 extra_context (optional)
44 * 0x10 terminator (null _aarch64_ctx) 45 * 0x10 terminator (null _aarch64_ctx)
45 * 46 *
46 * 0xdd0 (reserved for future allocation) 47 * 0xdb0 (reserved for future allocation)
47 * 48 *
48 * New records that can exceed this space need to be opt-in for userspace, so 49 * New records that can exceed this space need to be opt-in for userspace, so
49 * that an expanded signal frame is not generated unexpectedly. The mechanism 50 * that an expanded signal frame is not generated unexpectedly. The mechanism
@@ -80,4 +81,39 @@ struct esr_context {
80 __u64 esr; 81 __u64 esr;
81}; 82};
82 83
84/*
85 * extra_context: describes extra space in the signal frame for
86 * additional structures that don't fit in sigcontext.__reserved[].
87 *
88 * Note:
89 *
90 * 1) fpsimd_context, esr_context and extra_context must be placed in
91 * sigcontext.__reserved[] if present. They cannot be placed in the
92 * extra space. Any other record can be placed either in the extra
93 * space or in sigcontext.__reserved[], unless otherwise specified in
94 * this file.
95 *
96 * 2) There must not be more than one extra_context.
97 *
98 * 3) If extra_context is present, it must be followed immediately in
99 * sigcontext.__reserved[] by the terminating null _aarch64_ctx.
100 *
101 * 4) The extra space to which datap points must start at the first
102 * 16-byte aligned address immediately after the terminating null
103 * _aarch64_ctx that follows the extra_context structure in
104 * __reserved[]. The extra space may overrun the end of __reserved[],
105 * as indicated by a sufficiently large value for the size field.
106 *
107 * 5) The extra space must itself be terminated with a null
108 * _aarch64_ctx.
109 */
110#define EXTRA_MAGIC 0x45585401
111
112struct extra_context {
113 struct _aarch64_ctx head;
114 __u64 datap; /* 16-byte aligned pointer to extra space cast to __u64 */
115 __u32 size; /* size in bytes of the extra space */
116 __u32 __reserved[3];
117};
118
83#endif /* _UAPI__ASM_SIGCONTEXT_H */ 119#endif /* _UAPI__ASM_SIGCONTEXT_H */
diff --git a/arch/arm64/kernel/signal.c b/arch/arm64/kernel/signal.c
index fa787e6ac7c2..089c3747995d 100644
--- a/arch/arm64/kernel/signal.c
+++ b/arch/arm64/kernel/signal.c
@@ -25,6 +25,7 @@
25#include <linux/freezer.h> 25#include <linux/freezer.h>
26#include <linux/stddef.h> 26#include <linux/stddef.h>
27#include <linux/uaccess.h> 27#include <linux/uaccess.h>
28#include <linux/sizes.h>
28#include <linux/string.h> 29#include <linux/string.h>
29#include <linux/tracehook.h> 30#include <linux/tracehook.h>
30#include <linux/ratelimit.h> 31#include <linux/ratelimit.h>
@@ -60,18 +61,27 @@ struct rt_sigframe_user_layout {
60 61
61 unsigned long fpsimd_offset; 62 unsigned long fpsimd_offset;
62 unsigned long esr_offset; 63 unsigned long esr_offset;
64 unsigned long extra_offset;
63 unsigned long end_offset; 65 unsigned long end_offset;
64}; 66};
65 67
68#define BASE_SIGFRAME_SIZE round_up(sizeof(struct rt_sigframe), 16)
69#define TERMINATOR_SIZE round_up(sizeof(struct _aarch64_ctx), 16)
70#define EXTRA_CONTEXT_SIZE round_up(sizeof(struct extra_context), 16)
71
66static void init_user_layout(struct rt_sigframe_user_layout *user) 72static void init_user_layout(struct rt_sigframe_user_layout *user)
67{ 73{
74 const size_t reserved_size =
75 sizeof(user->sigframe->uc.uc_mcontext.__reserved);
76
68 memset(user, 0, sizeof(*user)); 77 memset(user, 0, sizeof(*user));
69 user->size = offsetof(struct rt_sigframe, uc.uc_mcontext.__reserved); 78 user->size = offsetof(struct rt_sigframe, uc.uc_mcontext.__reserved);
70 79
71 user->limit = user->size + 80 user->limit = user->size + reserved_size;
72 sizeof(user->sigframe->uc.uc_mcontext.__reserved) - 81
73 round_up(sizeof(struct _aarch64_ctx), 16); 82 user->limit -= TERMINATOR_SIZE;
74 /* ^ reserve space for terminator */ 83 user->limit -= EXTRA_CONTEXT_SIZE;
84 /* Reserve space for extension and terminator ^ */
75} 85}
76 86
77static size_t sigframe_size(struct rt_sigframe_user_layout const *user) 87static size_t sigframe_size(struct rt_sigframe_user_layout const *user)
@@ -80,6 +90,52 @@ static size_t sigframe_size(struct rt_sigframe_user_layout const *user)
80} 90}
81 91
82/* 92/*
93 * Sanity limit on the approximate maximum size of signal frame we'll
94 * try to generate. Stack alignment padding and the frame record are
95 * not taken into account. This limit is not a guarantee and is
96 * NOT ABI.
97 */
98#define SIGFRAME_MAXSZ SZ_64K
99
100static int __sigframe_alloc(struct rt_sigframe_user_layout *user,
101 unsigned long *offset, size_t size, bool extend)
102{
103 size_t padded_size = round_up(size, 16);
104
105 if (padded_size > user->limit - user->size &&
106 !user->extra_offset &&
107 extend) {
108 int ret;
109
110 user->limit += EXTRA_CONTEXT_SIZE;
111 ret = __sigframe_alloc(user, &user->extra_offset,
112 sizeof(struct extra_context), false);
113 if (ret) {
114 user->limit -= EXTRA_CONTEXT_SIZE;
115 return ret;
116 }
117
118 /* Reserve space for the __reserved[] terminator */
119 user->size += TERMINATOR_SIZE;
120
121 /*
122 * Allow expansion up to SIGFRAME_MAXSZ, ensuring space for
123 * the terminator:
124 */
125 user->limit = SIGFRAME_MAXSZ - TERMINATOR_SIZE;
126 }
127
128 /* Still not enough space? Bad luck! */
129 if (padded_size > user->limit - user->size)
130 return -ENOMEM;
131
132 *offset = user->size;
133 user->size += padded_size;
134
135 return 0;
136}
137
138/*
83 * Allocate space for an optional record of <size> bytes in the user 139 * Allocate space for an optional record of <size> bytes in the user
84 * signal frame. The offset from the signal frame base address to the 140 * signal frame. The offset from the signal frame base address to the
85 * allocated block is assigned to *offset. 141 * allocated block is assigned to *offset.
@@ -87,11 +143,24 @@ static size_t sigframe_size(struct rt_sigframe_user_layout const *user)
87static int sigframe_alloc(struct rt_sigframe_user_layout *user, 143static int sigframe_alloc(struct rt_sigframe_user_layout *user,
88 unsigned long *offset, size_t size) 144 unsigned long *offset, size_t size)
89{ 145{
90 size_t padded_size = round_up(size, 16); 146 return __sigframe_alloc(user, offset, size, true);
147}
91 148
92 *offset = user->size; 149/* Allocate the null terminator record and prevent further allocations */
93 user->size += padded_size; 150static int sigframe_alloc_end(struct rt_sigframe_user_layout *user)
151{
152 int ret;
153
154 /* Un-reserve the space reserved for the terminator: */
155 user->limit += TERMINATOR_SIZE;
94 156
157 ret = sigframe_alloc(user, &user->end_offset,
158 sizeof(struct _aarch64_ctx));
159 if (ret)
160 return ret;
161
162 /* Prevent further allocation: */
163 user->limit = user->size;
95 return 0; 164 return 0;
96} 165}
97 166
@@ -162,6 +231,8 @@ static int parse_user_sigframe(struct user_ctxs *user,
162 char __user *base = (char __user *)&sc->__reserved; 231 char __user *base = (char __user *)&sc->__reserved;
163 size_t offset = 0; 232 size_t offset = 0;
164 size_t limit = sizeof(sc->__reserved); 233 size_t limit = sizeof(sc->__reserved);
234 bool have_extra_context = false;
235 char const __user *const sfp = (char const __user *)sf;
165 236
166 user->fpsimd = NULL; 237 user->fpsimd = NULL;
167 238
@@ -171,6 +242,12 @@ static int parse_user_sigframe(struct user_ctxs *user,
171 while (1) { 242 while (1) {
172 int err = 0; 243 int err = 0;
173 u32 magic, size; 244 u32 magic, size;
245 char const __user *userp;
246 struct extra_context const __user *extra;
247 u64 extra_datap;
248 u32 extra_size;
249 struct _aarch64_ctx const __user *end;
250 u32 end_magic, end_size;
174 251
175 if (limit - offset < sizeof(*head)) 252 if (limit - offset < sizeof(*head))
176 goto invalid; 253 goto invalid;
@@ -208,6 +285,64 @@ static int parse_user_sigframe(struct user_ctxs *user,
208 /* ignore */ 285 /* ignore */
209 break; 286 break;
210 287
288 case EXTRA_MAGIC:
289 if (have_extra_context)
290 goto invalid;
291
292 if (size < sizeof(*extra))
293 goto invalid;
294
295 userp = (char const __user *)head;
296
297 extra = (struct extra_context const __user *)userp;
298 userp += size;
299
300 __get_user_error(extra_datap, &extra->datap, err);
301 __get_user_error(extra_size, &extra->size, err);
302 if (err)
303 return err;
304
305 /* Check for the dummy terminator in __reserved[]: */
306
307 if (limit - offset - size < TERMINATOR_SIZE)
308 goto invalid;
309
310 end = (struct _aarch64_ctx const __user *)userp;
311 userp += TERMINATOR_SIZE;
312
313 __get_user_error(end_magic, &end->magic, err);
314 __get_user_error(end_size, &end->size, err);
315 if (err)
316 return err;
317
318 if (end_magic || end_size)
319 goto invalid;
320
321 /* Prevent looping/repeated parsing of extra_context */
322 have_extra_context = true;
323
324 base = (__force void __user *)extra_datap;
325 if (!IS_ALIGNED((unsigned long)base, 16))
326 goto invalid;
327
328 if (!IS_ALIGNED(extra_size, 16))
329 goto invalid;
330
331 if (base != userp)
332 goto invalid;
333
334 /* Reject "unreasonably large" frames: */
335 if (extra_size > sfp + SIGFRAME_MAXSZ - userp)
336 goto invalid;
337
338 /*
339 * Ignore trailing terminator in __reserved[]
340 * and start parsing extra data:
341 */
342 offset = 0;
343 limit = extra_size;
344 continue;
345
211 default: 346 default:
212 goto invalid; 347 goto invalid;
213 } 348 }
@@ -318,17 +453,7 @@ static int setup_sigframe_layout(struct rt_sigframe_user_layout *user)
318 return err; 453 return err;
319 } 454 }
320 455
321 /* 456 return sigframe_alloc_end(user);
322 * Allocate space for the terminator record.
323 * HACK: here we undo the reservation of space for the end record.
324 * This bodge should be replaced with a cleaner approach later on.
325 */
326 user->limit = offsetof(struct rt_sigframe, uc.uc_mcontext.__reserved) +
327 sizeof(user->sigframe->uc.uc_mcontext.__reserved);
328
329 err = sigframe_alloc(user, &user->end_offset,
330 sizeof(struct _aarch64_ctx));
331 return err;
332} 457}
333 458
334 459
@@ -369,6 +494,40 @@ static int setup_sigframe(struct rt_sigframe_user_layout *user,
369 __put_user_error(current->thread.fault_code, &esr_ctx->esr, err); 494 __put_user_error(current->thread.fault_code, &esr_ctx->esr, err);
370 } 495 }
371 496
497 if (err == 0 && user->extra_offset) {
498 char __user *sfp = (char __user *)user->sigframe;
499 char __user *userp =
500 apply_user_offset(user, user->extra_offset);
501
502 struct extra_context __user *extra;
503 struct _aarch64_ctx __user *end;
504 u64 extra_datap;
505 u32 extra_size;
506
507 extra = (struct extra_context __user *)userp;
508 userp += EXTRA_CONTEXT_SIZE;
509
510 end = (struct _aarch64_ctx __user *)userp;
511 userp += TERMINATOR_SIZE;
512
513 /*
514 * extra_datap is just written to the signal frame.
515 * The value gets cast back to a void __user *
516 * during sigreturn.
517 */
518 extra_datap = (__force u64)userp;
519 extra_size = sfp + round_up(user->size, 16) - userp;
520
521 __put_user_error(EXTRA_MAGIC, &extra->head.magic, err);
522 __put_user_error(EXTRA_CONTEXT_SIZE, &extra->head.size, err);
523 __put_user_error(extra_datap, &extra->datap, err);
524 __put_user_error(extra_size, &extra->size, err);
525
526 /* Add the terminator */
527 __put_user_error(0, &end->magic, err);
528 __put_user_error(0, &end->size, err);
529 }
530
372 /* set the "end" magic */ 531 /* set the "end" magic */
373 if (err == 0) { 532 if (err == 0) {
374 struct _aarch64_ctx __user *end = 533 struct _aarch64_ctx __user *end =