#include <linux/freezer.h>
#include <linux/stddef.h>
#include <linux/uaccess.h>
+#include <linux/string.h>
#include <linux/tracehook.h>
#include <linux/ratelimit.h>
struct rt_sigframe_user_layout {
struct rt_sigframe __user *sigframe;
struct frame_record __user *next_frame;
+
+ unsigned long size; /* size of allocated sigframe data */
+ unsigned long limit; /* largest allowed size */
+
+ unsigned long fpsimd_offset;
+ unsigned long esr_offset;
+ unsigned long end_offset;
};
+static void init_user_layout(struct rt_sigframe_user_layout *user)
+{
+ memset(user, 0, sizeof(*user));
+ user->size = offsetof(struct rt_sigframe, uc.uc_mcontext.__reserved);
+
+ user->limit = user->size +
+ sizeof(user->sigframe->uc.uc_mcontext.__reserved) -
+ round_up(sizeof(struct _aarch64_ctx), 16);
+ /* ^ reserve space for terminator */
+}
+
+static size_t sigframe_size(struct rt_sigframe_user_layout const *user)
+{
+ return round_up(max(user->size, sizeof(struct rt_sigframe)), 16);
+}
+
+static void __user *apply_user_offset(
+ struct rt_sigframe_user_layout const *user, unsigned long offset)
+{
+ char __user *base = (char __user *)user->sigframe;
+
+ return base + offset;
+}
+
static int preserve_fpsimd_context(struct fpsimd_context __user *ctx)
{
struct fpsimd_state *fpsimd = ¤t->thread.fpsimd_state;
struct rt_sigframe __user *sf)
{
struct sigcontext __user *const sc = &sf->uc.uc_mcontext;
- struct _aarch64_ctx __user *head =
- (struct _aarch64_ctx __user *)&sc->__reserved;
+ struct _aarch64_ctx __user *head;
+ char __user *base = (char __user *)&sc->__reserved;
size_t offset = 0;
+ size_t limit = sizeof(sc->__reserved);
user->fpsimd = NULL;
+ if (!IS_ALIGNED((unsigned long)base, 16))
+ goto invalid;
+
while (1) {
- int err;
+ int err = 0;
u32 magic, size;
- head = (struct _aarch64_ctx __user *)&sc->__reserved[offset];
- if (!IS_ALIGNED((unsigned long)head, 16))
+ if (limit - offset < sizeof(*head))
goto invalid;
- err = 0;
+ if (!IS_ALIGNED(offset, 16))
+ goto invalid;
+
+ head = (struct _aarch64_ctx __user *)(base + offset);
__get_user_error(magic, &head->magic, err);
__get_user_error(size, &head->size, err);
if (err)
return err;
+ if (limit - offset < size)
+ goto invalid;
+
switch (magic) {
case 0:
if (size)
if (user->fpsimd)
goto invalid;
- if (offset > sizeof(sc->__reserved) -
- sizeof(*user->fpsimd) ||
- size < sizeof(*user->fpsimd))
+ if (size < sizeof(*user->fpsimd))
goto invalid;
user->fpsimd = (struct fpsimd_context __user *)head;
if (size < sizeof(*head))
goto invalid;
- if (size > sizeof(sc->__reserved) - (sizeof(*head) + offset))
+ if (limit - offset < size)
goto invalid;
offset += size;
return 0;
}
+/* Determine the layout of optional records in the signal frame */
+static int setup_sigframe_layout(struct rt_sigframe_user_layout *user)
+{
+ user->fpsimd_offset = user->size;
+ user->size += round_up(sizeof(struct fpsimd_context), 16);
+
+ /* fault information, if valid */
+ if (current->thread.fault_code) {
+ user->esr_offset = user->size;
+ user->size += round_up(sizeof(struct esr_context), 16);
+ }
+
+ /* set the "end" magic */
+ user->end_offset = user->size;
+
+ return 0;
+}
+
+
static int setup_sigframe(struct rt_sigframe_user_layout *user,
struct pt_regs *regs, sigset_t *set)
{
int i, err = 0;
struct rt_sigframe __user *sf = user->sigframe;
- void *aux = sf->uc.uc_mcontext.__reserved;
- struct _aarch64_ctx *end;
/* set up the stack frame for unwinding */
__put_user_error(regs->regs[29], &user->next_frame->fp, err);
err |= __copy_to_user(&sf->uc.uc_sigmask, set, sizeof(*set));
if (err == 0) {
- struct fpsimd_context *fpsimd_ctx =
- container_of(aux, struct fpsimd_context, head);
+ struct fpsimd_context __user *fpsimd_ctx =
+ apply_user_offset(user, user->fpsimd_offset);
err |= preserve_fpsimd_context(fpsimd_ctx);
- aux += sizeof(*fpsimd_ctx);
}
/* fault information, if valid */
- if (current->thread.fault_code) {
- struct esr_context *esr_ctx =
- container_of(aux, struct esr_context, head);
+ if (err == 0 && user->esr_offset) {
+ struct esr_context __user *esr_ctx =
+ apply_user_offset(user, user->esr_offset);
+
__put_user_error(ESR_MAGIC, &esr_ctx->head.magic, err);
__put_user_error(sizeof(*esr_ctx), &esr_ctx->head.size, err);
__put_user_error(current->thread.fault_code, &esr_ctx->esr, err);
- aux += sizeof(*esr_ctx);
}
/* set the "end" magic */
- end = aux;
- __put_user_error(0, &end->magic, err);
- __put_user_error(0, &end->size, err);
+ if (err == 0) {
+ struct _aarch64_ctx __user *end =
+ apply_user_offset(user, user->end_offset);
+
+ __put_user_error(0, &end->magic, err);
+ __put_user_error(0, &end->size, err);
+ }
return err;
}
struct ksignal *ksig, struct pt_regs *regs)
{
unsigned long sp, sp_top;
+ int err;
+
+ init_user_layout(user);
+ err = setup_sigframe_layout(user);
+ if (err)
+ return err;
sp = sp_top = sigsp(regs->sp, ksig);
sp = round_down(sp - sizeof(struct frame_record), 16);
user->next_frame = (struct frame_record __user *)sp;
- sp = round_down(sp - sizeof(struct rt_sigframe), 16);
+ sp = round_down(sp, 16) - sigframe_size(user);
user->sigframe = (struct rt_sigframe __user *)sp;
/*