Skip to content

Commit 2e8a1ac

Browse files
kevin-brodsky-armwilldeacon
authored andcommitted
arm64: signal: Improve POR_EL0 handling to avoid uaccess failures
Reset POR_EL0 to "allow all" before writing the signal frame, preventing spurious uaccess failures. When POE is supported, the POR_EL0 register constrains memory accesses based on the target page's POIndex (pkey). This raises the question: what constraints should apply to a signal handler? The current answer is that POR_EL0 is reset to POR_EL0_INIT when invoking the handler, giving it full access to POIndex 0. This is in line with x86's MPK support and remains unchanged. This is only part of the story, though. POR_EL0 constrains all unprivileged memory accesses, meaning that uaccess routines such as put_user() are also impacted. As a result POR_EL0 may prevent the signal frame from being written to the signal stack (ultimately causing a SIGSEGV). This is especially concerning when an alternate signal stack is used, because userspace may want to prevent access to it outside of signal handlers. There is currently no provision for that: POR_EL0 is reset after writing to the stack, and POR_EL0_INIT only enables access to POIndex 0. This patch ensures that POR_EL0 is reset to its most permissive state before the signal stack is accessed. Once the signal frame has been fully written, POR_EL0 is still set to POR_EL0_INIT - it is up to the signal handler to enable access to additional pkeys if needed. As to sigreturn(), it expects having access to the stack like any other syscall; we only need to ensure that POR_EL0 is restored from the signal frame after all uaccess calls. This approach is in line with the recent x86/pkeys series [1]. Resetting POR_EL0 early introduces some complications, in that we can no longer read the register directly in preserve_poe_context(). This is addressed by introducing a struct (user_access_state) and helpers to manage any such register impacting user accesses (uaccess and accesses in userspace). Things look like this on signal delivery: 1. Save original POR_EL0 into struct [save_reset_user_access_state()] 2. Set POR_EL0 to "allow all" [save_reset_user_access_state()] 3. Create signal frame 4. Write saved POR_EL0 value to the signal frame [preserve_poe_context()] 5. Finalise signal frame 6. If all operations succeeded: a. Set POR_EL0 to POR_EL0_INIT [set_handler_user_access_state()] b. Else reset POR_EL0 to its original value [restore_user_access_state()] If any step fails when setting up the signal frame, the process will be sent a SIGSEGV, which it may be able to handle. Step 6.b ensures that the original POR_EL0 is saved in the signal frame when delivering that SIGSEGV (so that the original value is restored by sigreturn). The return path (sys_rt_sigreturn) doesn't strictly require any change since restore_poe_context() is already called last. However, to avoid uaccess calls being accidentally added after that point, we use the same approach as in the delivery path, i.e. separating uaccess from writing to the register: 1. Read saved POR_EL0 value from the signal frame [restore_poe_context()] 2. Set POR_EL0 to the saved value [restore_user_access_state()] [1] https://lore.kernel.org/lkml/20240802061318.2140081-1-aruna.ramakrishna@oracle.com/ Fixes: 9160f7e ("arm64: add POE signal support") Reviewed-by: Catalin Marinas <catalin.marinas@arm.com> Signed-off-by: Kevin Brodsky <kevin.brodsky@arm.com> Link: https://lore.kernel.org/r/20241029144539.111155-2-kevin.brodsky@arm.com Signed-off-by: Will Deacon <will@kernel.org>
1 parent c83212d commit 2e8a1ac

File tree

1 file changed

+78
-14
lines changed

1 file changed

+78
-14
lines changed

arch/arm64/kernel/signal.c

Lines changed: 78 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <linux/ratelimit.h>
2020
#include <linux/rseq.h>
2121
#include <linux/syscalls.h>
22+
#include <linux/pkeys.h>
2223

2324
#include <asm/daifflags.h>
2425
#include <asm/debug-monitors.h>
@@ -66,10 +67,63 @@ struct rt_sigframe_user_layout {
6667
unsigned long end_offset;
6768
};
6869

70+
/*
71+
* Holds any EL0-controlled state that influences unprivileged memory accesses.
72+
* This includes both accesses done in userspace and uaccess done in the kernel.
73+
*
74+
* This state needs to be carefully managed to ensure that it doesn't cause
75+
* uaccess to fail when setting up the signal frame, and the signal handler
76+
* itself also expects a well-defined state when entered.
77+
*/
78+
struct user_access_state {
79+
u64 por_el0;
80+
};
81+
6982
#define BASE_SIGFRAME_SIZE round_up(sizeof(struct rt_sigframe), 16)
7083
#define TERMINATOR_SIZE round_up(sizeof(struct _aarch64_ctx), 16)
7184
#define EXTRA_CONTEXT_SIZE round_up(sizeof(struct extra_context), 16)
7285

86+
/*
87+
* Save the user access state into ua_state and reset it to disable any
88+
* restrictions.
89+
*/
90+
static void save_reset_user_access_state(struct user_access_state *ua_state)
91+
{
92+
if (system_supports_poe()) {
93+
u64 por_enable_all = 0;
94+
95+
for (int pkey = 0; pkey < arch_max_pkey(); pkey++)
96+
por_enable_all |= POE_RXW << (pkey * POR_BITS_PER_PKEY);
97+
98+
ua_state->por_el0 = read_sysreg_s(SYS_POR_EL0);
99+
write_sysreg_s(por_enable_all, SYS_POR_EL0);
100+
/* Ensure that any subsequent uaccess observes the updated value */
101+
isb();
102+
}
103+
}
104+
105+
/*
106+
* Set the user access state for invoking the signal handler.
107+
*
108+
* No uaccess should be done after that function is called.
109+
*/
110+
static void set_handler_user_access_state(void)
111+
{
112+
if (system_supports_poe())
113+
write_sysreg_s(POR_EL0_INIT, SYS_POR_EL0);
114+
}
115+
116+
/*
117+
* Restore the user access state to the values saved in ua_state.
118+
*
119+
* No uaccess should be done after that function is called.
120+
*/
121+
static void restore_user_access_state(const struct user_access_state *ua_state)
122+
{
123+
if (system_supports_poe())
124+
write_sysreg_s(ua_state->por_el0, SYS_POR_EL0);
125+
}
126+
73127
static void init_user_layout(struct rt_sigframe_user_layout *user)
74128
{
75129
const size_t reserved_size =
@@ -261,18 +315,20 @@ static int restore_fpmr_context(struct user_ctxs *user)
261315
return err;
262316
}
263317

264-
static int preserve_poe_context(struct poe_context __user *ctx)
318+
static int preserve_poe_context(struct poe_context __user *ctx,
319+
const struct user_access_state *ua_state)
265320
{
266321
int err = 0;
267322

268323
__put_user_error(POE_MAGIC, &ctx->head.magic, err);
269324
__put_user_error(sizeof(*ctx), &ctx->head.size, err);
270-
__put_user_error(read_sysreg_s(SYS_POR_EL0), &ctx->por_el0, err);
325+
__put_user_error(ua_state->por_el0, &ctx->por_el0, err);
271326

272327
return err;
273328
}
274329

275-
static int restore_poe_context(struct user_ctxs *user)
330+
static int restore_poe_context(struct user_ctxs *user,
331+
struct user_access_state *ua_state)
276332
{
277333
u64 por_el0;
278334
int err = 0;
@@ -282,7 +338,7 @@ static int restore_poe_context(struct user_ctxs *user)
282338

283339
__get_user_error(por_el0, &(user->poe->por_el0), err);
284340
if (!err)
285-
write_sysreg_s(por_el0, SYS_POR_EL0);
341+
ua_state->por_el0 = por_el0;
286342

287343
return err;
288344
}
@@ -850,7 +906,8 @@ static int parse_user_sigframe(struct user_ctxs *user,
850906
}
851907

852908
static int restore_sigframe(struct pt_regs *regs,
853-
struct rt_sigframe __user *sf)
909+
struct rt_sigframe __user *sf,
910+
struct user_access_state *ua_state)
854911
{
855912
sigset_t set;
856913
int i, err;
@@ -899,7 +956,7 @@ static int restore_sigframe(struct pt_regs *regs,
899956
err = restore_zt_context(&user);
900957

901958
if (err == 0 && system_supports_poe() && user.poe)
902-
err = restore_poe_context(&user);
959+
err = restore_poe_context(&user, ua_state);
903960

904961
return err;
905962
}
@@ -908,6 +965,7 @@ SYSCALL_DEFINE0(rt_sigreturn)
908965
{
909966
struct pt_regs *regs = current_pt_regs();
910967
struct rt_sigframe __user *frame;
968+
struct user_access_state ua_state;
911969

912970
/* Always make any pending restarted system calls return -EINTR */
913971
current->restart_block.fn = do_no_restart_syscall;
@@ -924,12 +982,14 @@ SYSCALL_DEFINE0(rt_sigreturn)
924982
if (!access_ok(frame, sizeof (*frame)))
925983
goto badframe;
926984

927-
if (restore_sigframe(regs, frame))
985+
if (restore_sigframe(regs, frame, &ua_state))
928986
goto badframe;
929987

930988
if (restore_altstack(&frame->uc.uc_stack))
931989
goto badframe;
932990

991+
restore_user_access_state(&ua_state);
992+
933993
return regs->regs[0];
934994

935995
badframe:
@@ -1035,7 +1095,8 @@ static int setup_sigframe_layout(struct rt_sigframe_user_layout *user,
10351095
}
10361096

10371097
static int setup_sigframe(struct rt_sigframe_user_layout *user,
1038-
struct pt_regs *regs, sigset_t *set)
1098+
struct pt_regs *regs, sigset_t *set,
1099+
const struct user_access_state *ua_state)
10391100
{
10401101
int i, err = 0;
10411102
struct rt_sigframe __user *sf = user->sigframe;
@@ -1097,10 +1158,9 @@ static int setup_sigframe(struct rt_sigframe_user_layout *user,
10971158
struct poe_context __user *poe_ctx =
10981159
apply_user_offset(user, user->poe_offset);
10991160

1100-
err |= preserve_poe_context(poe_ctx);
1161+
err |= preserve_poe_context(poe_ctx, ua_state);
11011162
}
11021163

1103-
11041164
/* ZA state if present */
11051165
if (system_supports_sme() && err == 0 && user->za_offset) {
11061166
struct za_context __user *za_ctx =
@@ -1237,9 +1297,6 @@ static void setup_return(struct pt_regs *regs, struct k_sigaction *ka,
12371297
sme_smstop();
12381298
}
12391299

1240-
if (system_supports_poe())
1241-
write_sysreg_s(POR_EL0_INIT, SYS_POR_EL0);
1242-
12431300
if (ka->sa.sa_flags & SA_RESTORER)
12441301
sigtramp = ka->sa.sa_restorer;
12451302
else
@@ -1253,20 +1310,22 @@ static int setup_rt_frame(int usig, struct ksignal *ksig, sigset_t *set,
12531310
{
12541311
struct rt_sigframe_user_layout user;
12551312
struct rt_sigframe __user *frame;
1313+
struct user_access_state ua_state;
12561314
int err = 0;
12571315

12581316
fpsimd_signal_preserve_current_state();
12591317

12601318
if (get_sigframe(&user, ksig, regs))
12611319
return 1;
12621320

1321+
save_reset_user_access_state(&ua_state);
12631322
frame = user.sigframe;
12641323

12651324
__put_user_error(0, &frame->uc.uc_flags, err);
12661325
__put_user_error(NULL, &frame->uc.uc_link, err);
12671326

12681327
err |= __save_altstack(&frame->uc.uc_stack, regs->sp);
1269-
err |= setup_sigframe(&user, regs, set);
1328+
err |= setup_sigframe(&user, regs, set, &ua_state);
12701329
if (err == 0) {
12711330
setup_return(regs, &ksig->ka, &user, usig);
12721331
if (ksig->ka.sa.sa_flags & SA_SIGINFO) {
@@ -1276,6 +1335,11 @@ static int setup_rt_frame(int usig, struct ksignal *ksig, sigset_t *set,
12761335
}
12771336
}
12781337

1338+
if (err == 0)
1339+
set_handler_user_access_state();
1340+
else
1341+
restore_user_access_state(&ua_state);
1342+
12791343
return err;
12801344
}
12811345

0 commit comments

Comments
 (0)