Skip to content

Commit 7383ee0

Browse files
guoren83palmer-dabbelt
authored andcommitted
riscv: compat: signal: Add rt_frame implementation
Implement compat_setup_rt_frame for sigcontext save & restore. The main process is the same with signal, but the rv32 pt_regs' size is different from rv64's, so we needs convert them. Signed-off-by: Guo Ren <guoren@linux.alibaba.com> Signed-off-by: Guo Ren <guoren@kernel.org> Reviewed-by: Palmer Dabbelt <palmer@rivosinc.com> Tested-by: Heiko Stuebner <heiko@sntech.de> Link: https://lore.kernel.org/r/20220405071314.3225832-19-guoren@kernel.org Signed-off-by: Palmer Dabbelt <palmer@rivosinc.com>
1 parent 3092eb4 commit 7383ee0

4 files changed

Lines changed: 268 additions & 1 deletion

File tree

arch/riscv/include/asm/signal32.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/* SPDX-License-Identifier: GPL-2.0-only */
2+
3+
#ifndef __ASM_SIGNAL32_H
4+
#define __ASM_SIGNAL32_H
5+
6+
#if IS_ENABLED(CONFIG_COMPAT)
7+
int compat_setup_rt_frame(struct ksignal *ksig, sigset_t *set,
8+
struct pt_regs *regs);
9+
#else
10+
static inline
11+
int compat_setup_rt_frame(struct ksignal *ksig, sigset_t *set,
12+
struct pt_regs *regs)
13+
{
14+
return -1;
15+
}
16+
#endif
17+
18+
#endif

arch/riscv/kernel/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,4 +70,5 @@ obj-$(CONFIG_JUMP_LABEL) += jump_label.o
7070

7171
obj-$(CONFIG_EFI) += efi.o
7272
obj-$(CONFIG_COMPAT) += compat_syscall_table.o
73+
obj-$(CONFIG_COMPAT) += compat_signal.o
7374
obj-$(CONFIG_COMPAT) += compat_vdso/

arch/riscv/kernel/compat_signal.c

Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
// SPDX-License-Identifier: GPL-2.0-or-later
2+
3+
#include <linux/compat.h>
4+
#include <linux/signal.h>
5+
#include <linux/uaccess.h>
6+
#include <linux/syscalls.h>
7+
#include <linux/linkage.h>
8+
9+
#include <asm/csr.h>
10+
#include <asm/signal32.h>
11+
#include <asm/switch_to.h>
12+
#include <asm/ucontext.h>
13+
#include <asm/vdso.h>
14+
15+
#define COMPAT_DEBUG_SIG 0
16+
17+
struct compat_sigcontext {
18+
struct compat_user_regs_struct sc_regs;
19+
union __riscv_fp_state sc_fpregs;
20+
};
21+
22+
struct compat_ucontext {
23+
compat_ulong_t uc_flags;
24+
struct compat_ucontext *uc_link;
25+
compat_stack_t uc_stack;
26+
sigset_t uc_sigmask;
27+
/* There's some padding here to allow sigset_t to be expanded in the
28+
* future. Though this is unlikely, other architectures put uc_sigmask
29+
* at the end of this structure and explicitly state it can be
30+
* expanded, so we didn't want to box ourselves in here. */
31+
__u8 __unused[1024 / 8 - sizeof(sigset_t)];
32+
/* We can't put uc_sigmask at the end of this structure because we need
33+
* to be able to expand sigcontext in the future. For example, the
34+
* vector ISA extension will almost certainly add ISA state. We want
35+
* to ensure all user-visible ISA state can be saved and restored via a
36+
* ucontext, so we're putting this at the end in order to allow for
37+
* infinite extensibility. Since we know this will be extended and we
38+
* assume sigset_t won't be extended an extreme amount, we're
39+
* prioritizing this. */
40+
struct compat_sigcontext uc_mcontext;
41+
};
42+
43+
struct compat_rt_sigframe {
44+
struct compat_siginfo info;
45+
struct compat_ucontext uc;
46+
};
47+
48+
#ifdef CONFIG_FPU
49+
static long compat_restore_fp_state(struct pt_regs *regs,
50+
union __riscv_fp_state __user *sc_fpregs)
51+
{
52+
long err;
53+
struct __riscv_d_ext_state __user *state = &sc_fpregs->d;
54+
size_t i;
55+
56+
err = __copy_from_user(&current->thread.fstate, state, sizeof(*state));
57+
if (unlikely(err))
58+
return err;
59+
60+
fstate_restore(current, regs);
61+
62+
/* We support no other extension state at this time. */
63+
for (i = 0; i < ARRAY_SIZE(sc_fpregs->q.reserved); i++) {
64+
u32 value;
65+
66+
err = __get_user(value, &sc_fpregs->q.reserved[i]);
67+
if (unlikely(err))
68+
break;
69+
if (value != 0)
70+
return -EINVAL;
71+
}
72+
73+
return err;
74+
}
75+
76+
static long compat_save_fp_state(struct pt_regs *regs,
77+
union __riscv_fp_state __user *sc_fpregs)
78+
{
79+
long err;
80+
struct __riscv_d_ext_state __user *state = &sc_fpregs->d;
81+
size_t i;
82+
83+
fstate_save(current, regs);
84+
err = __copy_to_user(state, &current->thread.fstate, sizeof(*state));
85+
if (unlikely(err))
86+
return err;
87+
88+
/* We support no other extension state at this time. */
89+
for (i = 0; i < ARRAY_SIZE(sc_fpregs->q.reserved); i++) {
90+
err = __put_user(0, &sc_fpregs->q.reserved[i]);
91+
if (unlikely(err))
92+
break;
93+
}
94+
95+
return err;
96+
}
97+
#else
98+
#define compat_save_fp_state(task, regs) (0)
99+
#define compat_restore_fp_state(task, regs) (0)
100+
#endif
101+
102+
static long compat_restore_sigcontext(struct pt_regs *regs,
103+
struct compat_sigcontext __user *sc)
104+
{
105+
long err;
106+
struct compat_user_regs_struct cregs;
107+
108+
/* sc_regs is structured the same as the start of pt_regs */
109+
err = __copy_from_user(&cregs, &sc->sc_regs, sizeof(sc->sc_regs));
110+
111+
cregs_to_regs(&cregs, regs);
112+
113+
/* Restore the floating-point state. */
114+
if (has_fpu())
115+
err |= compat_restore_fp_state(regs, &sc->sc_fpregs);
116+
return err;
117+
}
118+
119+
COMPAT_SYSCALL_DEFINE0(rt_sigreturn)
120+
{
121+
struct pt_regs *regs = current_pt_regs();
122+
struct compat_rt_sigframe __user *frame;
123+
struct task_struct *task;
124+
sigset_t set;
125+
126+
/* Always make any pending restarted system calls return -EINTR */
127+
current->restart_block.fn = do_no_restart_syscall;
128+
129+
frame = (struct compat_rt_sigframe __user *)regs->sp;
130+
131+
if (!access_ok(frame, sizeof(*frame)))
132+
goto badframe;
133+
134+
if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
135+
goto badframe;
136+
137+
set_current_blocked(&set);
138+
139+
if (compat_restore_sigcontext(regs, &frame->uc.uc_mcontext))
140+
goto badframe;
141+
142+
if (compat_restore_altstack(&frame->uc.uc_stack))
143+
goto badframe;
144+
145+
return regs->a0;
146+
147+
badframe:
148+
task = current;
149+
if (show_unhandled_signals) {
150+
pr_info_ratelimited(
151+
"%s[%d]: bad frame in %s: frame=%p pc=%p sp=%p\n",
152+
task->comm, task_pid_nr(task), __func__,
153+
frame, (void *)regs->epc, (void *)regs->sp);
154+
}
155+
force_sig(SIGSEGV);
156+
return 0;
157+
}
158+
159+
static long compat_setup_sigcontext(struct compat_rt_sigframe __user *frame,
160+
struct pt_regs *regs)
161+
{
162+
struct compat_sigcontext __user *sc = &frame->uc.uc_mcontext;
163+
struct compat_user_regs_struct cregs;
164+
long err;
165+
166+
regs_to_cregs(&cregs, regs);
167+
168+
/* sc_regs is structured the same as the start of pt_regs */
169+
err = __copy_to_user(&sc->sc_regs, &cregs, sizeof(sc->sc_regs));
170+
/* Save the floating-point state. */
171+
if (has_fpu())
172+
err |= compat_save_fp_state(regs, &sc->sc_fpregs);
173+
return err;
174+
}
175+
176+
static inline void __user *compat_get_sigframe(struct ksignal *ksig,
177+
struct pt_regs *regs, size_t framesize)
178+
{
179+
unsigned long sp;
180+
/* Default to using normal stack */
181+
sp = regs->sp;
182+
183+
/*
184+
* If we are on the alternate signal stack and would overflow it, don't.
185+
* Return an always-bogus address instead so we will die with SIGSEGV.
186+
*/
187+
if (on_sig_stack(sp) && !likely(on_sig_stack(sp - framesize)))
188+
return (void __user __force *)(-1UL);
189+
190+
/* This is the X/Open sanctioned signal stack switching. */
191+
sp = sigsp(sp, ksig) - framesize;
192+
193+
/* Align the stack frame. */
194+
sp &= ~0xfUL;
195+
196+
return (void __user *)sp;
197+
}
198+
199+
int compat_setup_rt_frame(struct ksignal *ksig, sigset_t *set,
200+
struct pt_regs *regs)
201+
{
202+
struct compat_rt_sigframe __user *frame;
203+
long err = 0;
204+
205+
frame = compat_get_sigframe(ksig, regs, sizeof(*frame));
206+
if (!access_ok(frame, sizeof(*frame)))
207+
return -EFAULT;
208+
209+
err |= copy_siginfo_to_user32(&frame->info, &ksig->info);
210+
211+
/* Create the ucontext. */
212+
err |= __put_user(0, &frame->uc.uc_flags);
213+
err |= __put_user(NULL, &frame->uc.uc_link);
214+
err |= __compat_save_altstack(&frame->uc.uc_stack, regs->sp);
215+
err |= compat_setup_sigcontext(frame, regs);
216+
err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
217+
if (err)
218+
return -EFAULT;
219+
220+
regs->ra = (unsigned long)COMPAT_VDSO_SYMBOL(
221+
current->mm->context.vdso, rt_sigreturn);
222+
223+
/*
224+
* Set up registers for signal handler.
225+
* Registers that we don't modify keep the value they had from
226+
* user-space at the time we took the signal.
227+
* We always pass siginfo and mcontext, regardless of SA_SIGINFO,
228+
* since some things rely on this (e.g. glibc's debug/segfault.c).
229+
*/
230+
regs->epc = (unsigned long)ksig->ka.sa.sa_handler;
231+
regs->sp = (unsigned long)frame;
232+
regs->a0 = ksig->sig; /* a0: signal number */
233+
regs->a1 = (unsigned long)(&frame->info); /* a1: siginfo pointer */
234+
regs->a2 = (unsigned long)(&frame->uc); /* a2: ucontext pointer */
235+
236+
#if COMPAT_DEBUG_SIG
237+
pr_info("SIG deliver (%s:%d): sig=%d pc=%p ra=%p sp=%p\n",
238+
current->comm, task_pid_nr(current), ksig->sig,
239+
(void *)regs->epc, (void *)regs->ra, frame);
240+
#endif
241+
242+
return 0;
243+
}

arch/riscv/kernel/signal.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
* Copyright (C) 2012 Regents of the University of California
77
*/
88

9+
#include <linux/compat.h>
910
#include <linux/signal.h>
1011
#include <linux/uaccess.h>
1112
#include <linux/syscalls.h>
@@ -14,6 +15,7 @@
1415

1516
#include <asm/ucontext.h>
1617
#include <asm/vdso.h>
18+
#include <asm/signal32.h>
1719
#include <asm/switch_to.h>
1820
#include <asm/csr.h>
1921

@@ -261,7 +263,10 @@ static void handle_signal(struct ksignal *ksig, struct pt_regs *regs)
261263
rseq_signal_deliver(ksig, regs);
262264

263265
/* Set up the stack frame */
264-
ret = setup_rt_frame(ksig, oldset, regs);
266+
if (is_compat_task())
267+
ret = compat_setup_rt_frame(ksig, oldset, regs);
268+
else
269+
ret = setup_rt_frame(ksig, oldset, regs);
265270

266271
signal_setup_done(ret, ksig, 0);
267272
}

0 commit comments

Comments
 (0)