Skip to content

Commit 33f0826

Browse files
Dave Martinwildea01
authored andcommitted
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>
1 parent 8effeaa commit 33f0826

File tree

2 files changed

+214
-19
lines changed

2 files changed

+214
-19
lines changed

arch/arm64/include/uapi/asm/sigcontext.h

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,10 @@ struct sigcontext {
4141
*
4242
* 0x210 fpsimd_context
4343
* 0x10 esr_context
44+
* 0x20 extra_context (optional)
4445
* 0x10 terminator (null _aarch64_ctx)
4546
*
46-
* 0xdd0 (reserved for future allocation)
47+
* 0xdb0 (reserved for future allocation)
4748
*
4849
* New records that can exceed this space need to be opt-in for userspace, so
4950
* that an expanded signal frame is not generated unexpectedly. The mechanism
@@ -80,4 +81,39 @@ struct esr_context {
8081
__u64 esr;
8182
};
8283

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+
112+
struct 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+
83119
#endif /* _UAPI__ASM_SIGCONTEXT_H */

arch/arm64/kernel/signal.c

Lines changed: 177 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include <linux/freezer.h>
2626
#include <linux/stddef.h>
2727
#include <linux/uaccess.h>
28+
#include <linux/sizes.h>
2829
#include <linux/string.h>
2930
#include <linux/tracehook.h>
3031
#include <linux/ratelimit.h>
@@ -60,25 +61,80 @@ struct rt_sigframe_user_layout {
6061

6162
unsigned long fpsimd_offset;
6263
unsigned long esr_offset;
64+
unsigned long extra_offset;
6365
unsigned long end_offset;
6466
};
6567

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+
6672
static void init_user_layout(struct rt_sigframe_user_layout *user)
6773
{
74+
const size_t reserved_size =
75+
sizeof(user->sigframe->uc.uc_mcontext.__reserved);
76+
6877
memset(user, 0, sizeof(*user));
6978
user->size = offsetof(struct rt_sigframe, uc.uc_mcontext.__reserved);
7079

71-
user->limit = user->size +
72-
sizeof(user->sigframe->uc.uc_mcontext.__reserved) -
73-
round_up(sizeof(struct _aarch64_ctx), 16);
74-
/* ^ reserve space for terminator */
80+
user->limit = user->size + reserved_size;
81+
82+
user->limit -= TERMINATOR_SIZE;
83+
user->limit -= EXTRA_CONTEXT_SIZE;
84+
/* Reserve space for extension and terminator ^ */
7585
}
7686

7787
static size_t sigframe_size(struct rt_sigframe_user_layout const *user)
7888
{
7989
return round_up(max(user->size, sizeof(struct rt_sigframe)), 16);
8090
}
8191

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+
100+
static 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+
82138
/*
83139
* Allocate space for an optional record of <size> bytes in the user
84140
* signal frame. The offset from the signal frame base address to the
@@ -87,11 +143,24 @@ static size_t sigframe_size(struct rt_sigframe_user_layout const *user)
87143
static int sigframe_alloc(struct rt_sigframe_user_layout *user,
88144
unsigned long *offset, size_t size)
89145
{
90-
size_t padded_size = round_up(size, 16);
146+
return __sigframe_alloc(user, offset, size, true);
147+
}
91148

92-
*offset = user->size;
93-
user->size += padded_size;
149+
/* Allocate the null terminator record and prevent further allocations */
150+
static 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;
94156

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;
95164
return 0;
96165
}
97166

@@ -162,6 +231,8 @@ static int parse_user_sigframe(struct user_ctxs *user,
162231
char __user *base = (char __user *)&sc->__reserved;
163232
size_t offset = 0;
164233
size_t limit = sizeof(sc->__reserved);
234+
bool have_extra_context = false;
235+
char const __user *const sfp = (char const __user *)sf;
165236

166237
user->fpsimd = NULL;
167238

@@ -171,6 +242,12 @@ static int parse_user_sigframe(struct user_ctxs *user,
171242
while (1) {
172243
int err = 0;
173244
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;
174251

175252
if (limit - offset < sizeof(*head))
176253
goto invalid;
@@ -208,6 +285,64 @@ static int parse_user_sigframe(struct user_ctxs *user,
208285
/* ignore */
209286
break;
210287

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+
211346
default:
212347
goto invalid;
213348
}
@@ -318,17 +453,7 @@ static int setup_sigframe_layout(struct rt_sigframe_user_layout *user)
318453
return err;
319454
}
320455

321-
/*
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;
456+
return sigframe_alloc_end(user);
332457
}
333458

334459

@@ -369,6 +494,40 @@ static int setup_sigframe(struct rt_sigframe_user_layout *user,
369494
__put_user_error(current->thread.fault_code, &esr_ctx->esr, err);
370495
}
371496

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+
372531
/* set the "end" magic */
373532
if (err == 0) {
374533
struct _aarch64_ctx __user *end =

0 commit comments

Comments
 (0)