* [PATCH v1 1/8] powerpc/signal32: Convert to scoped user access
2026-05-22 9:56 [PATCH v1 0/8] powerpc/signal: Convert to scoped user access Christophe Leroy (CS GROUP)
@ 2026-05-22 9:56 ` Christophe Leroy (CS GROUP)
2026-05-22 9:56 ` [PATCH v1 2/8] powerpc/signal64: Untangle setup_tm_sigcontexts() and user_access_begin() Christophe Leroy (CS GROUP)
` (6 subsequent siblings)
7 siblings, 0 replies; 13+ messages in thread
From: Christophe Leroy (CS GROUP) @ 2026-05-22 9:56 UTC (permalink / raw)
To: Michael Ellerman, Nicholas Piggin, Madhavan Srinivasan
Cc: Christophe Leroy (CS GROUP), linux-kernel, linuxppc-dev
Commit 861574d51bbd ("powerpc/uaccess: Implement masked user access")
provides optimised user access by avoiding the cost of access_ok().
Convert signal32 functions to scoped user access.
Scoped user access also make the code simpler.
Signed-off-by: Christophe Leroy (CS GROUP) <chleroy@kernel.org>
---
arch/powerpc/kernel/signal_32.c | 456 +++++++++++++++-----------------
1 file changed, 217 insertions(+), 239 deletions(-)
diff --git a/arch/powerpc/kernel/signal_32.c b/arch/powerpc/kernel/signal_32.c
index 7a718ed32b27..f5d5139a1426 100644
--- a/arch/powerpc/kernel/signal_32.c
+++ b/arch/powerpc/kernel/signal_32.c
@@ -468,98 +468,98 @@ static long restore_user_regs(struct pt_regs *regs,
{
unsigned int save_r2 = 0;
unsigned long msr;
-#ifdef CONFIG_VSX
- int i;
-#endif
- if (!user_read_access_begin(sr, sizeof(*sr)))
- return 1;
- /*
- * restore general registers but not including MSR or SOFTE. Also
- * take care of keeping r2 (TLS) intact if not a signal
- */
- if (!sig)
- save_r2 = (unsigned int)regs->gpr[2];
- unsafe_restore_general_regs(regs, sr, failed);
- set_trap_norestart(regs);
- unsafe_get_user(msr, &sr->mc_gregs[PT_MSR], failed);
- if (!sig)
- regs->gpr[2] = (unsigned long) save_r2;
-
- /* if doing signal return, restore the previous little-endian mode */
- if (sig)
- regs_set_return_msr(regs, (regs->msr & ~MSR_LE) | (msr & MSR_LE));
+ scoped_user_read_access(sr, failed) {
+ /*
+ * restore general registers but not including MSR or SOFTE. Also
+ * take care of keeping r2 (TLS) intact if not a signal
+ */
+ if (!sig)
+ save_r2 = (unsigned int)regs->gpr[2];
+ unsafe_restore_general_regs(regs, sr, failed);
+ set_trap_norestart(regs);
+ unsafe_get_user(msr, &sr->mc_gregs[PT_MSR], failed);
+ if (!sig)
+ regs->gpr[2] = (unsigned long)save_r2;
+
+ /* if doing signal return, restore the previous little-endian mode */
+ if (sig)
+ regs_set_return_msr(regs, (regs->msr & ~MSR_LE) | (msr & MSR_LE));
#ifdef CONFIG_ALTIVEC
- /*
- * Force the process to reload the altivec registers from
- * current->thread when it next does altivec instructions
- */
- regs_set_return_msr(regs, regs->msr & ~MSR_VEC);
- if (msr & MSR_VEC) {
- /* restore altivec registers from the stack */
- unsafe_copy_from_user(¤t->thread.vr_state, &sr->mc_vregs,
- sizeof(sr->mc_vregs), failed);
- current->thread.used_vr = true;
- } else if (current->thread.used_vr)
- memset(¤t->thread.vr_state, 0,
- ELF_NVRREG * sizeof(vector128));
-
- /* Always get VRSAVE back */
- unsafe_get_user(current->thread.vrsave, (u32 __user *)&sr->mc_vregs[32], failed);
- if (cpu_has_feature(CPU_FTR_ALTIVEC))
- mtspr(SPRN_VRSAVE, current->thread.vrsave);
+ /*
+ * Force the process to reload the altivec registers from
+ * current->thread when it next does altivec instructions
+ */
+ regs_set_return_msr(regs, regs->msr & ~MSR_VEC);
+ if (msr & MSR_VEC) {
+ /* restore altivec registers from the stack */
+ unsafe_copy_from_user(¤t->thread.vr_state, &sr->mc_vregs,
+ sizeof(sr->mc_vregs), failed);
+ current->thread.used_vr = true;
+ } else if (current->thread.used_vr) {
+ memset(¤t->thread.vr_state, 0,
+ ELF_NVRREG * sizeof(vector128));
+ }
+
+ /* Always get VRSAVE back */
+ unsafe_get_user(current->thread.vrsave, (u32 __user *)&sr->mc_vregs[32], failed);
+ if (cpu_has_feature(CPU_FTR_ALTIVEC))
+ mtspr(SPRN_VRSAVE, current->thread.vrsave);
#endif /* CONFIG_ALTIVEC */
- unsafe_copy_fpr_from_user(current, &sr->mc_fregs, failed);
+ unsafe_copy_fpr_from_user(current, &sr->mc_fregs, failed);
#ifdef CONFIG_VSX
- /*
- * Force the process to reload the VSX registers from
- * current->thread when it next does VSX instruction.
- */
- regs_set_return_msr(regs, regs->msr & ~MSR_VSX);
- if (msr & MSR_VSX) {
/*
- * Restore altivec registers from the stack to a local
- * buffer, then write this out to the thread_struct
+ * Force the process to reload the VSX registers from
+ * current->thread when it next does VSX instruction.
*/
- unsafe_copy_vsx_from_user(current, &sr->mc_vsregs, failed);
- current->thread.used_vsr = true;
- } else if (current->thread.used_vsr)
- for (i = 0; i < 32 ; i++)
- current->thread.fp_state.fpr[i][TS_VSRLOWOFFSET] = 0;
+ regs_set_return_msr(regs, regs->msr & ~MSR_VSX);
+ if (msr & MSR_VSX) {
+ /*
+ * Restore altivec registers from the stack to a local
+ * buffer, then write this out to the thread_struct
+ */
+ unsafe_copy_vsx_from_user(current, &sr->mc_vsregs, failed);
+ current->thread.used_vsr = true;
+ } else if (current->thread.used_vsr) {
+ int i;
+
+ for (i = 0; i < 32 ; i++)
+ current->thread.fp_state.fpr[i][TS_VSRLOWOFFSET] = 0;
+ }
#endif /* CONFIG_VSX */
- /*
- * force the process to reload the FP registers from
- * current->thread when it next does FP instructions
- */
- regs_set_return_msr(regs, regs->msr & ~(MSR_FP | MSR_FE0 | MSR_FE1));
+ /*
+ * force the process to reload the FP registers from
+ * current->thread when it next does FP instructions
+ */
+ regs_set_return_msr(regs, regs->msr & ~(MSR_FP | MSR_FE0 | MSR_FE1));
#ifdef CONFIG_SPE
- /*
- * Force the process to reload the spe registers from
- * current->thread when it next does spe instructions.
- * Since this is user ABI, we must enforce the sizing.
- */
- BUILD_BUG_ON(sizeof(current->thread.spe) != ELF_NEVRREG * sizeof(u32));
- regs_set_return_msr(regs, regs->msr & ~MSR_SPE);
- if (msr & MSR_SPE) {
- /* restore spe registers from the stack */
- unsafe_copy_from_user(¤t->thread.spe, &sr->mc_vregs,
- sizeof(current->thread.spe), failed);
- current->thread.used_spe = true;
- } else if (current->thread.used_spe)
- memset(¤t->thread.spe, 0, sizeof(current->thread.spe));
-
- /* Always get SPEFSCR back */
- unsafe_get_user(current->thread.spefscr, (u32 __user *)&sr->mc_vregs + ELF_NEVRREG, failed);
-#endif /* CONFIG_SPE */
+ /*
+ * Force the process to reload the spe registers from
+ * current->thread when it next does spe instructions.
+ * Since this is user ABI, we must enforce the sizing.
+ */
+ BUILD_BUG_ON(sizeof(current->thread.spe) != ELF_NEVRREG * sizeof(u32));
+ regs_set_return_msr(regs, regs->msr & ~MSR_SPE);
+ if (msr & MSR_SPE) {
+ /* restore spe registers from the stack */
+ unsafe_copy_from_user(¤t->thread.spe, &sr->mc_vregs,
+ sizeof(current->thread.spe), failed);
+ current->thread.used_spe = true;
+ } else if (current->thread.used_spe) {
+ memset(¤t->thread.spe, 0, sizeof(current->thread.spe));
+ }
- user_read_access_end();
+ /* Always get SPEFSCR back */
+ unsafe_get_user(current->thread.spefscr,
+ (u32 __user *)&sr->mc_vregs + ELF_NEVRREG, failed);
+#endif /* CONFIG_SPE */
+ }
return 0;
failed:
- user_read_access_end();
return 1;
}
@@ -574,7 +574,6 @@ static long restore_tm_user_regs(struct pt_regs *regs,
struct mcontext __user *tm_sr)
{
unsigned long msr, msr_hi;
- int i;
if (tm_suspend_disabled)
return 1;
@@ -585,86 +584,81 @@ static long restore_tm_user_regs(struct pt_regs *regs,
* TFHAR is restored from the checkpointed NIP; TEXASR and TFIAR
* were set by the signal delivery.
*/
- if (!user_read_access_begin(sr, sizeof(*sr)))
- return 1;
-
- unsafe_restore_general_regs(¤t->thread.ckpt_regs, sr, failed);
- unsafe_get_user(current->thread.tm_tfhar, &sr->mc_gregs[PT_NIP], failed);
- unsafe_get_user(msr, &sr->mc_gregs[PT_MSR], failed);
+ scoped_user_read_access(sr, failed) {
+ unsafe_restore_general_regs(¤t->thread.ckpt_regs, sr, failed);
+ unsafe_get_user(current->thread.tm_tfhar, &sr->mc_gregs[PT_NIP], failed);
+ unsafe_get_user(msr, &sr->mc_gregs[PT_MSR], failed);
- /* Restore the previous little-endian mode */
- regs_set_return_msr(regs, (regs->msr & ~MSR_LE) | (msr & MSR_LE));
+ /* Restore the previous little-endian mode */
+ regs_set_return_msr(regs, (regs->msr & ~MSR_LE) | (msr & MSR_LE));
- regs_set_return_msr(regs, regs->msr & ~MSR_VEC);
- if (msr & MSR_VEC) {
- /* restore altivec registers from the stack */
- unsafe_copy_from_user(¤t->thread.ckvr_state, &sr->mc_vregs,
- sizeof(sr->mc_vregs), failed);
- current->thread.used_vr = true;
- } else if (current->thread.used_vr) {
- memset(¤t->thread.vr_state, 0,
- ELF_NVRREG * sizeof(vector128));
- memset(¤t->thread.ckvr_state, 0,
- ELF_NVRREG * sizeof(vector128));
- }
+ regs_set_return_msr(regs, regs->msr & ~MSR_VEC);
+ if (msr & MSR_VEC) {
+ /* restore altivec registers from the stack */
+ unsafe_copy_from_user(¤t->thread.ckvr_state, &sr->mc_vregs,
+ sizeof(sr->mc_vregs), failed);
+ current->thread.used_vr = true;
+ } else if (current->thread.used_vr) {
+ memset(¤t->thread.vr_state, 0, ELF_NVRREG * sizeof(vector128));
+ memset(¤t->thread.ckvr_state, 0, ELF_NVRREG * sizeof(vector128));
+ }
- /* Always get VRSAVE back */
- unsafe_get_user(current->thread.ckvrsave,
- (u32 __user *)&sr->mc_vregs[32], failed);
- if (cpu_has_feature(CPU_FTR_ALTIVEC))
- mtspr(SPRN_VRSAVE, current->thread.ckvrsave);
+ /* Always get VRSAVE back */
+ unsafe_get_user(current->thread.ckvrsave,
+ (u32 __user *)&sr->mc_vregs[32], failed);
+ if (cpu_has_feature(CPU_FTR_ALTIVEC))
+ mtspr(SPRN_VRSAVE, current->thread.ckvrsave);
- regs_set_return_msr(regs, regs->msr & ~(MSR_FP | MSR_FE0 | MSR_FE1));
+ regs_set_return_msr(regs, regs->msr & ~(MSR_FP | MSR_FE0 | MSR_FE1));
- unsafe_copy_fpr_from_user(current, &sr->mc_fregs, failed);
+ unsafe_copy_fpr_from_user(current, &sr->mc_fregs, failed);
- regs_set_return_msr(regs, regs->msr & ~MSR_VSX);
- if (msr & MSR_VSX) {
- /*
- * Restore altivec registers from the stack to a local
- * buffer, then write this out to the thread_struct
- */
- unsafe_copy_ckvsx_from_user(current, &sr->mc_vsregs, failed);
- current->thread.used_vsr = true;
- } else if (current->thread.used_vsr)
- for (i = 0; i < 32 ; i++) {
- current->thread.fp_state.fpr[i][TS_VSRLOWOFFSET] = 0;
- current->thread.ckfp_state.fpr[i][TS_VSRLOWOFFSET] = 0;
+ regs_set_return_msr(regs, regs->msr & ~MSR_VSX);
+ if (msr & MSR_VSX) {
+ /*
+ * Restore altivec registers from the stack to a local
+ * buffer, then write this out to the thread_struct
+ */
+ unsafe_copy_ckvsx_from_user(current, &sr->mc_vsregs, failed);
+ current->thread.used_vsr = true;
+ } else if (current->thread.used_vsr) {
+ int i;
+
+ for (i = 0; i < 32 ; i++) {
+ current->thread.fp_state.fpr[i][TS_VSRLOWOFFSET] = 0;
+ current->thread.ckfp_state.fpr[i][TS_VSRLOWOFFSET] = 0;
+ }
}
+ }
- user_read_access_end();
-
- if (!user_read_access_begin(tm_sr, sizeof(*tm_sr)))
- return 1;
+ scoped_user_read_access(tm_sr, failed) {
+ unsafe_restore_general_regs(regs, tm_sr, failed);
- unsafe_restore_general_regs(regs, tm_sr, failed);
+ /* restore altivec registers from the stack */
+ if (msr & MSR_VEC)
+ unsafe_copy_from_user(¤t->thread.vr_state, &tm_sr->mc_vregs,
+ sizeof(sr->mc_vregs), failed);
- /* restore altivec registers from the stack */
- if (msr & MSR_VEC)
- unsafe_copy_from_user(¤t->thread.vr_state, &tm_sr->mc_vregs,
- sizeof(sr->mc_vregs), failed);
+ /* Always get VRSAVE back */
+ unsafe_get_user(current->thread.vrsave,
+ (u32 __user *)&tm_sr->mc_vregs[32], failed);
- /* Always get VRSAVE back */
- unsafe_get_user(current->thread.vrsave,
- (u32 __user *)&tm_sr->mc_vregs[32], failed);
+ unsafe_copy_ckfpr_from_user(current, &tm_sr->mc_fregs, failed);
- unsafe_copy_ckfpr_from_user(current, &tm_sr->mc_fregs, failed);
+ if (msr & MSR_VSX) {
+ /*
+ * Restore altivec registers from the stack to a local
+ * buffer, then write this out to the thread_struct
+ */
+ unsafe_copy_vsx_from_user(current, &tm_sr->mc_vsregs, failed);
+ current->thread.used_vsr = true;
+ }
- if (msr & MSR_VSX) {
- /*
- * Restore altivec registers from the stack to a local
- * buffer, then write this out to the thread_struct
- */
- unsafe_copy_vsx_from_user(current, &tm_sr->mc_vsregs, failed);
- current->thread.used_vsr = true;
+ /* Get the top half of the MSR from the user context */
+ unsafe_get_user(msr_hi, &tm_sr->mc_gregs[PT_MSR], failed);
+ msr_hi <<= 32;
}
- /* Get the top half of the MSR from the user context */
- unsafe_get_user(msr_hi, &tm_sr->mc_gregs[PT_MSR], failed);
- msr_hi <<= 32;
-
- user_read_access_end();
-
/* If TM bits are set to the reserved value, it's an invalid context */
if (MSR_TM_RESV(msr_hi))
return 1;
@@ -712,7 +706,6 @@ static long restore_tm_user_regs(struct pt_regs *regs,
return 0;
failed:
- user_read_access_end();
return 1;
}
#else
@@ -737,8 +730,6 @@ int handle_rt_signal32(struct ksignal *ksig, sigset_t *oldset,
struct task_struct *tsk)
{
struct rt_sigframe __user *frame;
- struct mcontext __user *mctx;
- struct mcontext __user *tm_mctx = NULL;
unsigned long newsp = 0;
unsigned long tramp;
struct pt_regs *regs = tsk->thread.regs;
@@ -747,52 +738,53 @@ int handle_rt_signal32(struct ksignal *ksig, sigset_t *oldset,
/* Set up Signal Frame */
frame = get_sigframe(ksig, tsk, sizeof(*frame), 1);
- mctx = &frame->uc.uc_mcontext;
-#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
- tm_mctx = &frame->uc_transact.uc_mcontext;
-#endif
if (MSR_TM_ACTIVE(msr))
prepare_save_tm_user_regs();
else
prepare_save_user_regs(1);
- if (!user_access_begin(frame, sizeof(*frame)))
- goto badframe;
+ scoped_user_rw_access(frame, badframe) {
+ struct mcontext __user *mctx;
+ struct mcontext __user *tm_mctx = NULL;
- /* Put the siginfo & fill in most of the ucontext */
- unsafe_put_user(0, &frame->uc.uc_flags, failed);
+ mctx = &frame->uc.uc_mcontext;
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+ tm_mctx = &frame->uc_transact.uc_mcontext;
+#endif
+ /* Put the siginfo & fill in most of the ucontext */
+ unsafe_put_user(0, &frame->uc.uc_flags, badframe);
#ifdef CONFIG_PPC64
- unsafe_compat_save_altstack(&frame->uc.uc_stack, regs->gpr[1], failed);
+ unsafe_compat_save_altstack(&frame->uc.uc_stack, regs->gpr[1], badframe);
#else
- unsafe_save_altstack(&frame->uc.uc_stack, regs->gpr[1], failed);
+ unsafe_save_altstack(&frame->uc.uc_stack, regs->gpr[1], badframe);
#endif
- unsafe_put_user(to_user_ptr(&frame->uc.uc_mcontext), &frame->uc.uc_regs, failed);
+ unsafe_put_user(to_user_ptr(&frame->uc.uc_mcontext), &frame->uc.uc_regs, badframe);
- if (MSR_TM_ACTIVE(msr)) {
+ if (MSR_TM_ACTIVE(msr)) {
#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
- unsafe_put_user((unsigned long)&frame->uc_transact,
- &frame->uc.uc_link, failed);
- unsafe_put_user((unsigned long)tm_mctx,
- &frame->uc_transact.uc_regs, failed);
+ unsafe_put_user((unsigned long)&frame->uc_transact,
+ &frame->uc.uc_link, badframe);
+ unsafe_put_user((unsigned long)tm_mctx,
+ &frame->uc_transact.uc_regs, badframe);
#endif
- unsafe_save_tm_user_regs(regs, mctx, tm_mctx, msr, failed);
- } else {
- unsafe_put_user(0, &frame->uc.uc_link, failed);
- unsafe_save_user_regs(regs, mctx, tm_mctx, 1, failed);
- }
+ unsafe_save_tm_user_regs(regs, mctx, tm_mctx, msr, badframe);
+ } else {
+ unsafe_put_user(0, &frame->uc.uc_link, badframe);
+ unsafe_save_user_regs(regs, mctx, tm_mctx, 1, badframe);
+ }
- /* Save user registers on the stack */
- if (tsk->mm->context.vdso) {
- tramp = VDSO32_SYMBOL(tsk->mm->context.vdso, sigtramp_rt32);
- } else {
- tramp = (unsigned long)mctx->mc_pad;
- unsafe_put_user(PPC_RAW_LI(_R0, __NR_rt_sigreturn), &mctx->mc_pad[0], failed);
- unsafe_put_user(PPC_RAW_SC(), &mctx->mc_pad[1], failed);
- asm("dcbst %y0; sync; icbi %y0; sync" :: "Z" (mctx->mc_pad[0]));
+ /* Save user registers on the stack */
+ if (tsk->mm->context.vdso) {
+ tramp = VDSO32_SYMBOL(tsk->mm->context.vdso, sigtramp_rt32);
+ } else {
+ tramp = (unsigned long)mctx->mc_pad;
+ unsafe_put_user(PPC_RAW_LI(_R0, __NR_rt_sigreturn), &mctx->mc_pad[0],
+ badframe);
+ unsafe_put_user(PPC_RAW_SC(), &mctx->mc_pad[1], badframe);
+ asm("dcbst %y0; sync; icbi %y0; sync" :: "Z" (mctx->mc_pad[0]));
+ }
+ unsafe_put_sigset_t(&frame->uc.uc_sigmask, oldset, badframe);
}
- unsafe_put_sigset_t(&frame->uc.uc_sigmask, oldset, failed);
-
- user_access_end();
if (copy_siginfo_to_user(&frame->info, &ksig->info))
goto badframe;
@@ -820,9 +812,6 @@ int handle_rt_signal32(struct ksignal *ksig, sigset_t *oldset,
return 0;
-failed:
- user_access_end();
-
badframe:
signal_fault(tsk, regs, "handle_rt_signal32", frame);
@@ -837,8 +826,6 @@ int handle_signal32(struct ksignal *ksig, sigset_t *oldset,
{
struct sigcontext __user *sc;
struct sigframe __user *frame;
- struct mcontext __user *mctx;
- struct mcontext __user *tm_mctx = NULL;
unsigned long newsp = 0;
unsigned long tramp;
struct pt_regs *regs = tsk->thread.regs;
@@ -847,46 +834,49 @@ int handle_signal32(struct ksignal *ksig, sigset_t *oldset,
/* Set up Signal Frame */
frame = get_sigframe(ksig, tsk, sizeof(*frame), 1);
- mctx = &frame->mctx;
-#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
- tm_mctx = &frame->mctx_transact;
-#endif
if (MSR_TM_ACTIVE(msr))
prepare_save_tm_user_regs();
else
prepare_save_user_regs(1);
- if (!user_access_begin(frame, sizeof(*frame)))
- goto badframe;
- sc = (struct sigcontext __user *) &frame->sctx;
+ scoped_user_rw_access(frame, badframe) {
+ struct mcontext __user *mctx;
+ struct mcontext __user *tm_mctx = NULL;
+
+ mctx = &frame->mctx;
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+ tm_mctx = &frame->mctx_transact;
+#endif
+ sc = (struct sigcontext __user *)&frame->sctx;
#if _NSIG != 64
#error "Please adjust handle_signal()"
#endif
- unsafe_put_user(to_user_ptr(ksig->ka.sa.sa_handler), &sc->handler, failed);
- unsafe_put_user(oldset->sig[0], &sc->oldmask, failed);
+ unsafe_put_user(to_user_ptr(ksig->ka.sa.sa_handler), &sc->handler, badframe);
+ unsafe_put_user(oldset->sig[0], &sc->oldmask, badframe);
#ifdef CONFIG_PPC64
- unsafe_put_user((oldset->sig[0] >> 32), &sc->_unused[3], failed);
+ unsafe_put_user((oldset->sig[0] >> 32), &sc->_unused[3], badframe);
#else
- unsafe_put_user(oldset->sig[1], &sc->_unused[3], failed);
+ unsafe_put_user(oldset->sig[1], &sc->_unused[3], badframe);
#endif
- unsafe_put_user(to_user_ptr(mctx), &sc->regs, failed);
- unsafe_put_user(ksig->sig, &sc->signal, failed);
+ unsafe_put_user(to_user_ptr(mctx), &sc->regs, badframe);
+ unsafe_put_user(ksig->sig, &sc->signal, badframe);
- if (MSR_TM_ACTIVE(msr))
- unsafe_save_tm_user_regs(regs, mctx, tm_mctx, msr, failed);
- else
- unsafe_save_user_regs(regs, mctx, tm_mctx, 1, failed);
-
- if (tsk->mm->context.vdso) {
- tramp = VDSO32_SYMBOL(tsk->mm->context.vdso, sigtramp32);
- } else {
- tramp = (unsigned long)mctx->mc_pad;
- unsafe_put_user(PPC_RAW_LI(_R0, __NR_sigreturn), &mctx->mc_pad[0], failed);
- unsafe_put_user(PPC_RAW_SC(), &mctx->mc_pad[1], failed);
- asm("dcbst %y0; sync; icbi %y0; sync" :: "Z" (mctx->mc_pad[0]));
+ if (MSR_TM_ACTIVE(msr))
+ unsafe_save_tm_user_regs(regs, mctx, tm_mctx, msr, badframe);
+ else
+ unsafe_save_user_regs(regs, mctx, tm_mctx, 1, badframe);
+
+ if (tsk->mm->context.vdso) {
+ tramp = VDSO32_SYMBOL(tsk->mm->context.vdso, sigtramp32);
+ } else {
+ tramp = (unsigned long)mctx->mc_pad;
+ unsafe_put_user(PPC_RAW_LI(_R0, __NR_sigreturn), &mctx->mc_pad[0],
+ badframe);
+ unsafe_put_user(PPC_RAW_SC(), &mctx->mc_pad[1], badframe);
+ asm("dcbst %y0; sync; icbi %y0; sync" :: "Z" (mctx->mc_pad[0]));
+ }
}
- user_access_end();
regs->link = tramp;
@@ -908,9 +898,6 @@ int handle_signal32(struct ksignal *ksig, sigset_t *oldset,
return 0;
-failed:
- user_access_end();
-
badframe:
signal_fault(tsk, regs, "handle_signal32", frame);
@@ -922,21 +909,19 @@ static int do_setcontext(struct ucontext __user *ucp, struct pt_regs *regs, int
sigset_t set;
struct mcontext __user *mcp;
- if (!user_read_access_begin(ucp, sizeof(*ucp)))
- return -EFAULT;
-
- unsafe_get_sigset_t(&set, &ucp->uc_sigmask, failed);
+ scoped_user_read_access(ucp, failed) {
+ unsafe_get_sigset_t(&set, &ucp->uc_sigmask, failed);
#ifdef CONFIG_PPC64
- {
- u32 cmcp;
+ {
+ u32 cmcp;
- unsafe_get_user(cmcp, &ucp->uc_regs, failed);
- mcp = (struct mcontext __user *)(u64)cmcp;
- }
+ unsafe_get_user(cmcp, &ucp->uc_regs, failed);
+ mcp = (struct mcontext __user *)(u64)cmcp;
+ }
#else
- unsafe_get_user(mcp, &ucp->uc_regs, failed);
+ unsafe_get_user(mcp, &ucp->uc_regs, failed);
#endif
- user_read_access_end();
+ }
set_current_blocked(&set);
if (restore_user_regs(regs, mcp, sig))
@@ -945,7 +930,6 @@ static int do_setcontext(struct ucontext __user *ucp, struct pt_regs *regs, int
return 0;
failed:
- user_read_access_end();
return -EFAULT;
}
@@ -960,13 +944,10 @@ static int do_setcontext_tm(struct ucontext __user *ucp,
u32 cmcp;
u32 tm_cmcp;
- if (!user_read_access_begin(ucp, sizeof(*ucp)))
- return -EFAULT;
-
- unsafe_get_sigset_t(&set, &ucp->uc_sigmask, failed);
- unsafe_get_user(cmcp, &ucp->uc_regs, failed);
-
- user_read_access_end();
+ scoped_user_read_access(ucp, failed) {
+ unsafe_get_sigset_t(&set, &ucp->uc_sigmask, failed);
+ unsafe_get_user(cmcp, &ucp->uc_regs, failed);
+ }
if (__get_user(tm_cmcp, &tm_ucp->uc_regs))
return -EFAULT;
@@ -981,7 +962,6 @@ static int do_setcontext_tm(struct ucontext __user *ucp,
return 0;
failed:
- user_read_access_end();
return -EFAULT;
}
#endif
@@ -1051,12 +1031,11 @@ SYSCALL_DEFINE3(swapcontext, struct ucontext __user *, old_ctx,
mctx = (struct mcontext __user *)
((unsigned long) &old_ctx->uc_mcontext & ~0xfUL);
prepare_save_user_regs(ctx_has_vsx_region);
- if (!user_write_access_begin(old_ctx, ctx_size))
- return -EFAULT;
- unsafe_save_user_regs(regs, mctx, NULL, ctx_has_vsx_region, failed);
- unsafe_put_sigset_t(&old_ctx->uc_sigmask, ¤t->blocked, failed);
- unsafe_put_user(to_user_ptr(mctx), &old_ctx->uc_regs, failed);
- user_write_access_end();
+ scoped_user_write_access_size(old_ctx, ctx_size, failed) {
+ unsafe_save_user_regs(regs, mctx, NULL, ctx_has_vsx_region, failed);
+ unsafe_put_sigset_t(&old_ctx->uc_sigmask, ¤t->blocked, failed);
+ unsafe_put_user(to_user_ptr(mctx), &old_ctx->uc_regs, failed);
+ }
}
if (new_ctx == NULL)
return 0;
@@ -1084,7 +1063,6 @@ SYSCALL_DEFINE3(swapcontext, struct ucontext __user *, old_ctx,
return 0;
failed:
- user_write_access_end();
return -EFAULT;
}
--
2.54.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH v1 2/8] powerpc/signal64: Untangle setup_tm_sigcontexts() and user_access_begin()
2026-05-22 9:56 [PATCH v1 0/8] powerpc/signal: Convert to scoped user access Christophe Leroy (CS GROUP)
2026-05-22 9:56 ` [PATCH v1 1/8] powerpc/signal32: " Christophe Leroy (CS GROUP)
@ 2026-05-22 9:56 ` Christophe Leroy (CS GROUP)
2026-05-22 11:12 ` David Laight
2026-05-22 9:56 ` [PATCH v1 3/8] powerpc/signal64: Convert to scoped user access Christophe Leroy (CS GROUP)
` (5 subsequent siblings)
7 siblings, 1 reply; 13+ messages in thread
From: Christophe Leroy (CS GROUP) @ 2026-05-22 9:56 UTC (permalink / raw)
To: Michael Ellerman, Nicholas Piggin, Madhavan Srinivasan
Cc: Christophe Leroy (CS GROUP), linux-kernel, linuxppc-dev
Call setup_tm_sigcontexts() before opening user access to avoid
having to close and open again.
Signed-off-by: Christophe Leroy (CS GROUP) <chleroy@kernel.org>
---
arch/powerpc/kernel/signal_64.c | 22 +++++++++-------------
1 file changed, 9 insertions(+), 13 deletions(-)
diff --git a/arch/powerpc/kernel/signal_64.c b/arch/powerpc/kernel/signal_64.c
index 86bb5bb4c143..3849af21e1d8 100644
--- a/arch/powerpc/kernel/signal_64.c
+++ b/arch/powerpc/kernel/signal_64.c
@@ -873,6 +873,15 @@ int handle_rt_signal64(struct ksignal *ksig, sigset_t *set,
if (!MSR_TM_ACTIVE(msr))
prepare_setup_sigcontext(tsk);
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+ if (MSR_TM_ACTIVE(msr))
+ err |= setup_tm_sigcontexts(&frame->uc.uc_mcontext,
+ &frame->uc_transact.uc_mcontext,
+ tsk, ksig->sig, NULL,
+ (unsigned long)ksig->ka.sa.sa_handler,
+ msr);
+
+#endif
if (!user_write_access_begin(frame, sizeof(*frame)))
goto badframe;
@@ -889,19 +898,6 @@ int handle_rt_signal64(struct ksignal *ksig, sigset_t *set,
* ucontext_t (for transactional state) with its uc_link ptr.
*/
unsafe_put_user(&frame->uc_transact, &frame->uc.uc_link, badframe_block);
-
- user_write_access_end();
-
- err |= setup_tm_sigcontexts(&frame->uc.uc_mcontext,
- &frame->uc_transact.uc_mcontext,
- tsk, ksig->sig, NULL,
- (unsigned long)ksig->ka.sa.sa_handler,
- msr);
-
- if (!user_write_access_begin(&frame->uc.uc_sigmask,
- sizeof(frame->uc.uc_sigmask)))
- goto badframe;
-
#endif
} else {
unsafe_put_user(0, &frame->uc.uc_link, badframe_block);
--
2.54.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: [PATCH v1 2/8] powerpc/signal64: Untangle setup_tm_sigcontexts() and user_access_begin()
2026-05-22 9:56 ` [PATCH v1 2/8] powerpc/signal64: Untangle setup_tm_sigcontexts() and user_access_begin() Christophe Leroy (CS GROUP)
@ 2026-05-22 11:12 ` David Laight
2026-05-22 12:06 ` Christophe Leroy (CS GROUP)
0 siblings, 1 reply; 13+ messages in thread
From: David Laight @ 2026-05-22 11:12 UTC (permalink / raw)
To: Christophe Leroy (CS GROUP)
Cc: Michael Ellerman, Nicholas Piggin, Madhavan Srinivasan,
linux-kernel, linuxppc-dev
On Fri, 22 May 2026 11:56:02 +0200
"Christophe Leroy (CS GROUP)" <chleroy@kernel.org> wrote:
> Call setup_tm_sigcontexts() before opening user access to avoid
> having to close and open again.
>
> Signed-off-by: Christophe Leroy (CS GROUP) <chleroy@kernel.org>
> ---
> arch/powerpc/kernel/signal_64.c | 22 +++++++++-------------
> 1 file changed, 9 insertions(+), 13 deletions(-)
>
> diff --git a/arch/powerpc/kernel/signal_64.c b/arch/powerpc/kernel/signal_64.c
> index 86bb5bb4c143..3849af21e1d8 100644
> --- a/arch/powerpc/kernel/signal_64.c
> +++ b/arch/powerpc/kernel/signal_64.c
> @@ -873,6 +873,15 @@ int handle_rt_signal64(struct ksignal *ksig, sigset_t *set,
> if (!MSR_TM_ACTIVE(msr))
> prepare_setup_sigcontext(tsk);
>
> +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
> + if (MSR_TM_ACTIVE(msr))
Can't that be done without the ugly #ifdef?
I assume MSR_TM_ACTIVE() will be zero - so it will all get optimised away.
-- David
> + err |= setup_tm_sigcontexts(&frame->uc.uc_mcontext,
> + &frame->uc_transact.uc_mcontext,
> + tsk, ksig->sig, NULL,
> + (unsigned long)ksig->ka.sa.sa_handler,
> + msr);
> +
> +#endif
> if (!user_write_access_begin(frame, sizeof(*frame)))
> goto badframe;
>
> @@ -889,19 +898,6 @@ int handle_rt_signal64(struct ksignal *ksig, sigset_t *set,
> * ucontext_t (for transactional state) with its uc_link ptr.
> */
> unsafe_put_user(&frame->uc_transact, &frame->uc.uc_link, badframe_block);
> -
> - user_write_access_end();
> -
> - err |= setup_tm_sigcontexts(&frame->uc.uc_mcontext,
> - &frame->uc_transact.uc_mcontext,
> - tsk, ksig->sig, NULL,
> - (unsigned long)ksig->ka.sa.sa_handler,
> - msr);
> -
> - if (!user_write_access_begin(&frame->uc.uc_sigmask,
> - sizeof(frame->uc.uc_sigmask)))
> - goto badframe;
> -
> #endif
> } else {
> unsafe_put_user(0, &frame->uc.uc_link, badframe_block);
^ permalink raw reply [flat|nested] 13+ messages in thread* Re: [PATCH v1 2/8] powerpc/signal64: Untangle setup_tm_sigcontexts() and user_access_begin()
2026-05-22 11:12 ` David Laight
@ 2026-05-22 12:06 ` Christophe Leroy (CS GROUP)
2026-05-22 12:44 ` Christophe Leroy (CS GROUP)
0 siblings, 1 reply; 13+ messages in thread
From: Christophe Leroy (CS GROUP) @ 2026-05-22 12:06 UTC (permalink / raw)
To: David Laight
Cc: Michael Ellerman, Nicholas Piggin, Madhavan Srinivasan,
linux-kernel, linuxppc-dev
Le 22/05/2026 à 13:12, David Laight a écrit :
> On Fri, 22 May 2026 11:56:02 +0200
> "Christophe Leroy (CS GROUP)" <chleroy@kernel.org> wrote:
>
>> Call setup_tm_sigcontexts() before opening user access to avoid
>> having to close and open again.
>>
>> Signed-off-by: Christophe Leroy (CS GROUP) <chleroy@kernel.org>
>> ---
>> arch/powerpc/kernel/signal_64.c | 22 +++++++++-------------
>> 1 file changed, 9 insertions(+), 13 deletions(-)
>>
>> diff --git a/arch/powerpc/kernel/signal_64.c b/arch/powerpc/kernel/signal_64.c
>> index 86bb5bb4c143..3849af21e1d8 100644
>> --- a/arch/powerpc/kernel/signal_64.c
>> +++ b/arch/powerpc/kernel/signal_64.c
>> @@ -873,6 +873,15 @@ int handle_rt_signal64(struct ksignal *ksig, sigset_t *set,
>> if (!MSR_TM_ACTIVE(msr))
>> prepare_setup_sigcontext(tsk);
>>
>> +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
>> + if (MSR_TM_ACTIVE(msr))
>
> Can't that be done without the ugly #ifdef?
> I assume MSR_TM_ACTIVE() will be zero - so it will all get optimised away.
Yes but struct rt_sigframe field uc_transact only exists when
CONFIG_PPC_TRANSACTIONAL_MEM is defined.
And that would also require a stub setup_tm_sigcontexts()
Chistophe
>
> -- David
>
>> + err |= setup_tm_sigcontexts(&frame->uc.uc_mcontext,
>> + &frame->uc_transact.uc_mcontext,
>> + tsk, ksig->sig, NULL,
>> + (unsigned long)ksig->ka.sa.sa_handler,
>> + msr);
>> +
>> +#endif
>> if (!user_write_access_begin(frame, sizeof(*frame)))
>> goto badframe;
>>
>> @@ -889,19 +898,6 @@ int handle_rt_signal64(struct ksignal *ksig, sigset_t *set,
>> * ucontext_t (for transactional state) with its uc_link ptr.
>> */
>> unsafe_put_user(&frame->uc_transact, &frame->uc.uc_link, badframe_block);
>> -
>> - user_write_access_end();
>> -
>> - err |= setup_tm_sigcontexts(&frame->uc.uc_mcontext,
>> - &frame->uc_transact.uc_mcontext,
>> - tsk, ksig->sig, NULL,
>> - (unsigned long)ksig->ka.sa.sa_handler,
>> - msr);
>> -
>> - if (!user_write_access_begin(&frame->uc.uc_sigmask,
>> - sizeof(frame->uc.uc_sigmask)))
>> - goto badframe;
>> -
>> #endif
>> } else {
>> unsafe_put_user(0, &frame->uc.uc_link, badframe_block);
>
^ permalink raw reply [flat|nested] 13+ messages in thread* Re: [PATCH v1 2/8] powerpc/signal64: Untangle setup_tm_sigcontexts() and user_access_begin()
2026-05-22 12:06 ` Christophe Leroy (CS GROUP)
@ 2026-05-22 12:44 ` Christophe Leroy (CS GROUP)
2026-05-22 18:55 ` David Laight
0 siblings, 1 reply; 13+ messages in thread
From: Christophe Leroy (CS GROUP) @ 2026-05-22 12:44 UTC (permalink / raw)
To: David Laight
Cc: Michael Ellerman, Nicholas Piggin, Madhavan Srinivasan,
linux-kernel, linuxppc-dev
Le 22/05/2026 à 14:06, Christophe Leroy (CS GROUP) a écrit :
>
>
> Le 22/05/2026 à 13:12, David Laight a écrit :
>> On Fri, 22 May 2026 11:56:02 +0200
>> "Christophe Leroy (CS GROUP)" <chleroy@kernel.org> wrote:
>>
>>> Call setup_tm_sigcontexts() before opening user access to avoid
>>> having to close and open again.
>>>
>>> Signed-off-by: Christophe Leroy (CS GROUP) <chleroy@kernel.org>
>>> ---
>>> arch/powerpc/kernel/signal_64.c | 22 +++++++++-------------
>>> 1 file changed, 9 insertions(+), 13 deletions(-)
>>>
>>> diff --git a/arch/powerpc/kernel/signal_64.c b/arch/powerpc/kernel/
>>> signal_64.c
>>> index 86bb5bb4c143..3849af21e1d8 100644
>>> --- a/arch/powerpc/kernel/signal_64.c
>>> +++ b/arch/powerpc/kernel/signal_64.c
>>> @@ -873,6 +873,15 @@ int handle_rt_signal64(struct ksignal *ksig,
>>> sigset_t *set,
>>> if (!MSR_TM_ACTIVE(msr))
>>> prepare_setup_sigcontext(tsk);
>>> +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
>>> + if (MSR_TM_ACTIVE(msr))
>>
>> Can't that be done without the ugly #ifdef?
>> I assume MSR_TM_ACTIVE() will be zero - so it will all get optimised
>> away.
>
> Yes but struct rt_sigframe field uc_transact only exists when
> CONFIG_PPC_TRANSACTIONAL_MEM is defined.
>
> And that would also require a stub setup_tm_sigcontexts()
After thinking once more, I think we can do the following, is it better
for you ?
diff --git a/arch/powerpc/kernel/signal_64.c
b/arch/powerpc/kernel/signal_64.c
index 86bb5bb4c143..c70732e8002d 100644
--- a/arch/powerpc/kernel/signal_64.c
+++ b/arch/powerpc/kernel/signal_64.c
@@ -203,8 +203,7 @@ static long notrace __unsafe_setup_sigcontext(struct
sigcontext __user *sc,
* examine the transactional registers in the 2nd sigcontext to
determine the
* real origin of the signal.
*/
-static long setup_tm_sigcontexts(struct sigcontext __user *sc,
- struct sigcontext __user *tm_sc,
+static long setup_tm_sigcontexts(struct rt_sigframe __user *frame,
struct task_struct *tsk,
int signr, sigset_t *set, unsigned long handler,
unsigned long msr)
@@ -217,6 +216,8 @@ static long setup_tm_sigcontexts(struct sigcontext
__user *sc,
* Userland shall check AT_HWCAP to know wether it can rely on the
* v_regs pointer or not.
*/
+ struct sigcontext __user *sc = &frame->uc.uc_mcontext;
+ struct sigcontext __user *tm_sc = &frame->uc_transact.uc_mcontext;
#ifdef CONFIG_ALTIVEC
elf_vrreg_t __user *v_regs = sigcontext_vmx_regs(sc);
elf_vrreg_t __user *tm_v_regs = sigcontext_vmx_regs(tm_sc);
@@ -325,6 +326,14 @@ static long setup_tm_sigcontexts(struct sigcontext
__user *sc,
return err;
}
+#else
+static long setup_tm_sigcontexts(struct rt_sigframe __user *frame,
+ struct task_struct *tsk,
+ int signr, sigset_t *set, unsigned long handler,
+ unsigned long msr)
+{
+ return -EINVAL;
+}
#endif
/*
@@ -872,6 +881,9 @@ int handle_rt_signal64(struct ksignal *ksig,
sigset_t *set,
*/
if (!MSR_TM_ACTIVE(msr))
prepare_setup_sigcontext(tsk);
+ else
+ err |= setup_tm_sigcontexts(frame, tsk, ksig->sig, NULL,
+ (unsigned long)ksig->ka.sa.sa_handler, msr);
if (!user_write_access_begin(frame, sizeof(*frame)))
goto badframe;
@@ -889,19 +901,6 @@ int handle_rt_signal64(struct ksignal *ksig,
sigset_t *set,
* ucontext_t (for transactional state) with its uc_link ptr.
*/
unsafe_put_user(&frame->uc_transact, &frame->uc.uc_link,
badframe_block);
-
- user_write_access_end();
-
- err |= setup_tm_sigcontexts(&frame->uc.uc_mcontext,
- &frame->uc_transact.uc_mcontext,
- tsk, ksig->sig, NULL,
- (unsigned long)ksig->ka.sa.sa_handler,
- msr);
-
- if (!user_write_access_begin(&frame->uc.uc_sigmask,
- sizeof(frame->uc.uc_sigmask)))
- goto badframe;
-
#endif
} else {
unsafe_put_user(0, &frame->uc.uc_link, badframe_block);
Christophe
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: [PATCH v1 2/8] powerpc/signal64: Untangle setup_tm_sigcontexts() and user_access_begin()
2026-05-22 12:44 ` Christophe Leroy (CS GROUP)
@ 2026-05-22 18:55 ` David Laight
0 siblings, 0 replies; 13+ messages in thread
From: David Laight @ 2026-05-22 18:55 UTC (permalink / raw)
To: Christophe Leroy (CS GROUP)
Cc: Michael Ellerman, Nicholas Piggin, Madhavan Srinivasan,
linux-kernel, linuxppc-dev
On Fri, 22 May 2026 14:44:55 +0200
"Christophe Leroy (CS GROUP)" <chleroy@kernel.org> wrote:
> Le 22/05/2026 à 14:06, Christophe Leroy (CS GROUP) a écrit :
> >
> >
> > Le 22/05/2026 à 13:12, David Laight a écrit :
> >> On Fri, 22 May 2026 11:56:02 +0200
> >> "Christophe Leroy (CS GROUP)" <chleroy@kernel.org> wrote:
> >>
> >>> Call setup_tm_sigcontexts() before opening user access to avoid
> >>> having to close and open again.
> >>>
> >>> Signed-off-by: Christophe Leroy (CS GROUP) <chleroy@kernel.org>
> >>> ---
> >>> arch/powerpc/kernel/signal_64.c | 22 +++++++++-------------
> >>> 1 file changed, 9 insertions(+), 13 deletions(-)
> >>>
> >>> diff --git a/arch/powerpc/kernel/signal_64.c b/arch/powerpc/kernel/
> >>> signal_64.c
> >>> index 86bb5bb4c143..3849af21e1d8 100644
> >>> --- a/arch/powerpc/kernel/signal_64.c
> >>> +++ b/arch/powerpc/kernel/signal_64.c
> >>> @@ -873,6 +873,15 @@ int handle_rt_signal64(struct ksignal *ksig,
> >>> sigset_t *set,
> >>> if (!MSR_TM_ACTIVE(msr))
> >>> prepare_setup_sigcontext(tsk);
> >>> +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
> >>> + if (MSR_TM_ACTIVE(msr))
> >>
> >> Can't that be done without the ugly #ifdef?
> >> I assume MSR_TM_ACTIVE() will be zero - so it will all get optimised
> >> away.
> >
> > Yes but struct rt_sigframe field uc_transact only exists when
> > CONFIG_PPC_TRANSACTIONAL_MEM is defined.
> >
> > And that would also require a stub setup_tm_sigcontexts()
>
> After thinking once more, I think we can do the following, is it better
> for you ?
Certainly more like the expected style.
setup_tm_sigcontexts() will get inlined, so it doesn't matter what values
are passed as the arguments.
-- David
>
> diff --git a/arch/powerpc/kernel/signal_64.c
> b/arch/powerpc/kernel/signal_64.c
> index 86bb5bb4c143..c70732e8002d 100644
> --- a/arch/powerpc/kernel/signal_64.c
> +++ b/arch/powerpc/kernel/signal_64.c
> @@ -203,8 +203,7 @@ static long notrace __unsafe_setup_sigcontext(struct
> sigcontext __user *sc,
> * examine the transactional registers in the 2nd sigcontext to
> determine the
> * real origin of the signal.
> */
> -static long setup_tm_sigcontexts(struct sigcontext __user *sc,
> - struct sigcontext __user *tm_sc,
> +static long setup_tm_sigcontexts(struct rt_sigframe __user *frame,
> struct task_struct *tsk,
> int signr, sigset_t *set, unsigned long handler,
> unsigned long msr)
> @@ -217,6 +216,8 @@ static long setup_tm_sigcontexts(struct sigcontext
> __user *sc,
> * Userland shall check AT_HWCAP to know wether it can rely on the
> * v_regs pointer or not.
> */
> + struct sigcontext __user *sc = &frame->uc.uc_mcontext;
> + struct sigcontext __user *tm_sc = &frame->uc_transact.uc_mcontext;
> #ifdef CONFIG_ALTIVEC
> elf_vrreg_t __user *v_regs = sigcontext_vmx_regs(sc);
> elf_vrreg_t __user *tm_v_regs = sigcontext_vmx_regs(tm_sc);
> @@ -325,6 +326,14 @@ static long setup_tm_sigcontexts(struct sigcontext
> __user *sc,
>
> return err;
> }
> +#else
> +static long setup_tm_sigcontexts(struct rt_sigframe __user *frame,
> + struct task_struct *tsk,
> + int signr, sigset_t *set, unsigned long handler,
> + unsigned long msr)
> +{
> + return -EINVAL;
> +}
> #endif
>
> /*
> @@ -872,6 +881,9 @@ int handle_rt_signal64(struct ksignal *ksig,
> sigset_t *set,
> */
> if (!MSR_TM_ACTIVE(msr))
> prepare_setup_sigcontext(tsk);
> + else
> + err |= setup_tm_sigcontexts(frame, tsk, ksig->sig, NULL,
> + (unsigned long)ksig->ka.sa.sa_handler, msr);
>
> if (!user_write_access_begin(frame, sizeof(*frame)))
> goto badframe;
> @@ -889,19 +901,6 @@ int handle_rt_signal64(struct ksignal *ksig,
> sigset_t *set,
> * ucontext_t (for transactional state) with its uc_link ptr.
> */
> unsafe_put_user(&frame->uc_transact, &frame->uc.uc_link,
> badframe_block);
> -
> - user_write_access_end();
> -
> - err |= setup_tm_sigcontexts(&frame->uc.uc_mcontext,
> - &frame->uc_transact.uc_mcontext,
> - tsk, ksig->sig, NULL,
> - (unsigned long)ksig->ka.sa.sa_handler,
> - msr);
> -
> - if (!user_write_access_begin(&frame->uc.uc_sigmask,
> - sizeof(frame->uc.uc_sigmask)))
> - goto badframe;
> -
> #endif
> } else {
> unsafe_put_user(0, &frame->uc.uc_link, badframe_block);
>
>
> Christophe
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v1 3/8] powerpc/signal64: Convert to scoped user access
2026-05-22 9:56 [PATCH v1 0/8] powerpc/signal: Convert to scoped user access Christophe Leroy (CS GROUP)
2026-05-22 9:56 ` [PATCH v1 1/8] powerpc/signal32: " Christophe Leroy (CS GROUP)
2026-05-22 9:56 ` [PATCH v1 2/8] powerpc/signal64: Untangle setup_tm_sigcontexts() and user_access_begin() Christophe Leroy (CS GROUP)
@ 2026-05-22 9:56 ` Christophe Leroy (CS GROUP)
2026-05-22 9:56 ` [PATCH v1 4/8] powerpc/signal64: Access function descriptor with " Christophe Leroy (CS GROUP)
` (4 subsequent siblings)
7 siblings, 0 replies; 13+ messages in thread
From: Christophe Leroy (CS GROUP) @ 2026-05-22 9:56 UTC (permalink / raw)
To: Michael Ellerman, Nicholas Piggin, Madhavan Srinivasan
Cc: Christophe Leroy (CS GROUP), linux-kernel, linuxppc-dev
Commit 861574d51bbd ("powerpc/uaccess: Implement masked user access")
provides optimised user access by avoiding the cost of access_ok().
Convert signal64 functions to scoped user access.
Scoped user access also make the code simpler.
Signed-off-by: Christophe Leroy (CS GROUP) <chleroy@kernel.org>
---
arch/powerpc/kernel/signal_64.c | 81 +++++++++++++--------------------
1 file changed, 32 insertions(+), 49 deletions(-)
diff --git a/arch/powerpc/kernel/signal_64.c b/arch/powerpc/kernel/signal_64.c
index 3849af21e1d8..ee8166fd83dc 100644
--- a/arch/powerpc/kernel/signal_64.c
+++ b/arch/powerpc/kernel/signal_64.c
@@ -683,15 +683,12 @@ SYSCALL_DEFINE3(swapcontext, struct ucontext __user *, old_ctx,
if (old_ctx != NULL) {
prepare_setup_sigcontext(current);
- if (!user_write_access_begin(old_ctx, ctx_size))
- return -EFAULT;
-
- unsafe_setup_sigcontext(&old_ctx->uc_mcontext, current, 0, NULL,
- 0, ctx_has_vsx_region, efault_out);
- unsafe_copy_to_user(&old_ctx->uc_sigmask, ¤t->blocked,
- sizeof(sigset_t), efault_out);
-
- user_write_access_end();
+ scoped_user_write_access_size(old_ctx, ctx_size, efault_out) {
+ unsafe_setup_sigcontext(&old_ctx->uc_mcontext, current, 0, NULL,
+ 0, ctx_has_vsx_region, efault_out);
+ unsafe_copy_to_user(&old_ctx->uc_sigmask, ¤t->blocked,
+ sizeof(sigset_t), efault_out);
+ }
}
if (new_ctx == NULL)
return 0;
@@ -717,14 +714,12 @@ SYSCALL_DEFINE3(swapcontext, struct ucontext __user *, old_ctx,
}
set_current_blocked(&set);
- if (!user_read_access_begin(new_ctx, ctx_size))
- return -EFAULT;
- if (__unsafe_restore_sigcontext(current, NULL, 0, &new_ctx->uc_mcontext)) {
- user_read_access_end();
- force_exit_sig(SIGSEGV);
- return -EFAULT;
+ scoped_user_read_access_size(new_ctx, ctx_size, efault_out) {
+ if (__unsafe_restore_sigcontext(current, NULL, 0, &new_ctx->uc_mcontext)) {
+ force_exit_sig(SIGSEGV);
+ return -EFAULT;
+ }
}
- user_read_access_end();
/* This returns like rt_sigreturn */
set_thread_flag(TIF_RESTOREALL);
@@ -732,7 +727,6 @@ SYSCALL_DEFINE3(swapcontext, struct ucontext __user *, old_ctx,
return 0;
efault_out:
- user_write_access_end();
return -EFAULT;
}
@@ -815,6 +809,7 @@ SYSCALL_DEFINE0(rt_sigreturn)
&uc_transact->uc_mcontext))
goto badframe;
} else {
+ struct sigcontext __user *uc_mcontext = &uc->uc_mcontext;
/*
* Fall through, for non-TM restore
*
@@ -829,13 +824,8 @@ SYSCALL_DEFINE0(rt_sigreturn)
*/
regs_set_return_msr(current->thread.regs,
current->thread.regs->msr & ~MSR_TS_MASK);
- if (!user_read_access_begin(&uc->uc_mcontext, sizeof(uc->uc_mcontext)))
- goto badframe;
-
- unsafe_restore_sigcontext(current, NULL, 1, &uc->uc_mcontext,
- badframe_block);
-
- user_read_access_end();
+ scoped_user_read_access(uc_mcontext, badframe)
+ unsafe_restore_sigcontext(current, NULL, 1, uc_mcontext, badframe);
}
if (restore_altstack(&uc->uc_stack))
@@ -845,8 +835,6 @@ SYSCALL_DEFINE0(rt_sigreturn)
return 0;
-badframe_block:
- user_read_access_end();
badframe:
signal_fault(current, regs, "rt_sigreturn", uc);
@@ -882,32 +870,29 @@ int handle_rt_signal64(struct ksignal *ksig, sigset_t *set,
msr);
#endif
- if (!user_write_access_begin(frame, sizeof(*frame)))
- goto badframe;
+ scoped_user_write_access(frame, badframe) {
+ unsafe_put_user(&frame->info, &frame->pinfo, badframe);
+ unsafe_put_user(&frame->uc, &frame->puc, badframe);
- unsafe_put_user(&frame->info, &frame->pinfo, badframe_block);
- unsafe_put_user(&frame->uc, &frame->puc, badframe_block);
+ /* Create the ucontext. */
+ unsafe_put_user(0, &frame->uc.uc_flags, badframe);
+ unsafe_save_altstack(&frame->uc.uc_stack, regs->gpr[1], badframe);
- /* Create the ucontext. */
- unsafe_put_user(0, &frame->uc.uc_flags, badframe_block);
- unsafe_save_altstack(&frame->uc.uc_stack, regs->gpr[1], badframe_block);
-
- if (MSR_TM_ACTIVE(msr)) {
+ if (MSR_TM_ACTIVE(msr)) {
#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
- /* The ucontext_t passed to userland points to the second
- * ucontext_t (for transactional state) with its uc_link ptr.
- */
- unsafe_put_user(&frame->uc_transact, &frame->uc.uc_link, badframe_block);
+ /* The ucontext_t passed to userland points to the second
+ * ucontext_t (for transactional state) with its uc_link ptr.
+ */
+ unsafe_put_user(&frame->uc_transact, &frame->uc.uc_link, badframe);
#endif
- } else {
- unsafe_put_user(0, &frame->uc.uc_link, badframe_block);
- unsafe_setup_sigcontext(&frame->uc.uc_mcontext, tsk, ksig->sig,
- NULL, (unsigned long)ksig->ka.sa.sa_handler,
- 1, badframe_block);
- }
+ } else {
+ unsafe_put_user(0, &frame->uc.uc_link, badframe);
+ unsafe_setup_sigcontext(&frame->uc.uc_mcontext, tsk, ksig->sig, NULL,
+ (unsigned long)ksig->ka.sa.sa_handler, 1, badframe);
+ }
- unsafe_copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set), badframe_block);
- user_write_access_end();
+ unsafe_copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set), badframe);
+ }
/* Save the siginfo outside of the unsafe block. */
if (copy_siginfo_to_user(&frame->info, &ksig->info))
@@ -964,8 +949,6 @@ int handle_rt_signal64(struct ksignal *ksig, sigset_t *set,
return 0;
-badframe_block:
- user_write_access_end();
badframe:
signal_fault(current, regs, "handle_rt_signal64", frame);
--
2.54.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH v1 4/8] powerpc/signal64: Access function descriptor with scoped user access
2026-05-22 9:56 [PATCH v1 0/8] powerpc/signal: Convert to scoped user access Christophe Leroy (CS GROUP)
` (2 preceding siblings ...)
2026-05-22 9:56 ` [PATCH v1 3/8] powerpc/signal64: Convert to scoped user access Christophe Leroy (CS GROUP)
@ 2026-05-22 9:56 ` Christophe Leroy (CS GROUP)
2026-05-22 9:56 ` [PATCH v1 5/8] powerpc/signal: Include the new stack frame inside the user access block Christophe Leroy (CS GROUP)
` (3 subsequent siblings)
7 siblings, 0 replies; 13+ messages in thread
From: Christophe Leroy (CS GROUP) @ 2026-05-22 9:56 UTC (permalink / raw)
To: Michael Ellerman, Nicholas Piggin, Madhavan Srinivasan
Cc: Christophe Leroy (CS GROUP), linux-kernel, linuxppc-dev
Access the function descriptor of the handler within a scoped
user access block.
Signed-off-by: Christophe Leroy (CS GROUP) <chleroy@kernel.org>
---
arch/powerpc/kernel/signal_64.c | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/arch/powerpc/kernel/signal_64.c b/arch/powerpc/kernel/signal_64.c
index ee8166fd83dc..bf7fc579d572 100644
--- a/arch/powerpc/kernel/signal_64.c
+++ b/arch/powerpc/kernel/signal_64.c
@@ -928,8 +928,10 @@ int handle_rt_signal64(struct ksignal *ksig, sigset_t *set,
struct func_desc __user *ptr =
(struct func_desc __user *)ksig->ka.sa.sa_handler;
- err |= get_user(regs->ctr, &ptr->addr);
- err |= get_user(regs->gpr[2], &ptr->toc);
+ scoped_user_read_access(ptr, badfunc) {
+ unsafe_get_user(regs->ctr, &ptr->addr, badfunc);
+ unsafe_get_user(regs->gpr[2], &ptr->toc, badfunc);
+ }
}
/* enter the signal handler in native-endian mode */
@@ -952,5 +954,10 @@ int handle_rt_signal64(struct ksignal *ksig, sigset_t *set,
badframe:
signal_fault(current, regs, "handle_rt_signal64", frame);
+ return 1;
+
+badfunc:
+ signal_fault(current, regs, __func__, (void __user *)ksig->ka.sa.sa_handler);
+
return 1;
}
--
2.54.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH v1 5/8] powerpc/signal: Include the new stack frame inside the user access block
2026-05-22 9:56 [PATCH v1 0/8] powerpc/signal: Convert to scoped user access Christophe Leroy (CS GROUP)
` (3 preceding siblings ...)
2026-05-22 9:56 ` [PATCH v1 4/8] powerpc/signal64: Access function descriptor with " Christophe Leroy (CS GROUP)
@ 2026-05-22 9:56 ` Christophe Leroy (CS GROUP)
2026-05-22 9:56 ` [PATCH v1 6/8] signal: Add unsafe_copy_siginfo_to_user() Christophe Leroy (CS GROUP)
` (2 subsequent siblings)
7 siblings, 0 replies; 13+ messages in thread
From: Christophe Leroy (CS GROUP) @ 2026-05-22 9:56 UTC (permalink / raw)
To: Michael Ellerman, Nicholas Piggin, Madhavan Srinivasan
Cc: Christophe Leroy (CS GROUP), linux-kernel, linuxppc-dev
Include the new stack frame inside the user access block and set it up
using unsafe_put_user().
On an mpc 8321 (book3s/32) the improvment is about 4% on a process
sending a signal to itself.
Signed-off-by: Christophe Leroy (CS GROUP) <chleroy@kernel.org>
---
arch/powerpc/kernel/signal_32.c | 28 ++++++++++++----------------
arch/powerpc/kernel/signal_64.c | 13 ++++++-------
2 files changed, 18 insertions(+), 23 deletions(-)
diff --git a/arch/powerpc/kernel/signal_32.c b/arch/powerpc/kernel/signal_32.c
index f5d5139a1426..6b1fbd95b07d 100644
--- a/arch/powerpc/kernel/signal_32.c
+++ b/arch/powerpc/kernel/signal_32.c
@@ -730,7 +730,7 @@ int handle_rt_signal32(struct ksignal *ksig, sigset_t *oldset,
struct task_struct *tsk)
{
struct rt_sigframe __user *frame;
- unsigned long newsp = 0;
+ unsigned long __user *newsp;
unsigned long tramp;
struct pt_regs *regs = tsk->thread.regs;
/* Save the thread's msr before get_tm_stackpointer() changes it */
@@ -738,12 +738,13 @@ int handle_rt_signal32(struct ksignal *ksig, sigset_t *oldset,
/* Set up Signal Frame */
frame = get_sigframe(ksig, tsk, sizeof(*frame), 1);
+ newsp = (unsigned long __user *)((unsigned long)frame - (__SIGNAL_FRAMESIZE + 16));
if (MSR_TM_ACTIVE(msr))
prepare_save_tm_user_regs();
else
prepare_save_user_regs(1);
- scoped_user_rw_access(frame, badframe) {
+ scoped_user_rw_access_size(newsp, __SIGNAL_FRAMESIZE + 16 + sizeof(*frame), badframe) {
struct mcontext __user *mctx;
struct mcontext __user *tm_mctx = NULL;
@@ -784,6 +785,9 @@ int handle_rt_signal32(struct ksignal *ksig, sigset_t *oldset,
asm("dcbst %y0; sync; icbi %y0; sync" :: "Z" (mctx->mc_pad[0]));
}
unsafe_put_sigset_t(&frame->uc.uc_sigmask, oldset, badframe);
+
+ /* create a stack frame for the caller of the handler */
+ unsafe_put_user(regs->gpr[1], newsp, badframe);
}
if (copy_siginfo_to_user(&frame->info, &ksig->info))
@@ -795,13 +799,8 @@ int handle_rt_signal32(struct ksignal *ksig, sigset_t *oldset,
tsk->thread.fp_state.fpscr = 0; /* turn off all fp exceptions */
#endif
- /* create a stack frame for the caller of the handler */
- newsp = ((unsigned long)frame) - (__SIGNAL_FRAMESIZE + 16);
- if (put_user(regs->gpr[1], (u32 __user *)newsp))
- goto badframe;
-
/* Fill registers for signal handler */
- regs->gpr[1] = newsp;
+ regs->gpr[1] = (unsigned long)newsp;
regs->gpr[3] = ksig->sig;
regs->gpr[4] = (unsigned long)&frame->info;
regs->gpr[5] = (unsigned long)&frame->uc;
@@ -826,7 +825,7 @@ int handle_signal32(struct ksignal *ksig, sigset_t *oldset,
{
struct sigcontext __user *sc;
struct sigframe __user *frame;
- unsigned long newsp = 0;
+ unsigned long __user *newsp;
unsigned long tramp;
struct pt_regs *regs = tsk->thread.regs;
/* Save the thread's msr before get_tm_stackpointer() changes it */
@@ -834,12 +833,13 @@ int handle_signal32(struct ksignal *ksig, sigset_t *oldset,
/* Set up Signal Frame */
frame = get_sigframe(ksig, tsk, sizeof(*frame), 1);
+ newsp = (unsigned long __user *)((unsigned long)frame - __SIGNAL_FRAMESIZE);
if (MSR_TM_ACTIVE(msr))
prepare_save_tm_user_regs();
else
prepare_save_user_regs(1);
- scoped_user_rw_access(frame, badframe) {
+ scoped_user_rw_access_size(newsp, __SIGNAL_FRAMESIZE + sizeof(*frame), badframe) {
struct mcontext __user *mctx;
struct mcontext __user *tm_mctx = NULL;
@@ -876,6 +876,7 @@ int handle_signal32(struct ksignal *ksig, sigset_t *oldset,
unsafe_put_user(PPC_RAW_SC(), &mctx->mc_pad[1], badframe);
asm("dcbst %y0; sync; icbi %y0; sync" :: "Z" (mctx->mc_pad[0]));
}
+ unsafe_put_user(regs->gpr[1], newsp, badframe);
}
regs->link = tramp;
@@ -884,12 +885,7 @@ int handle_signal32(struct ksignal *ksig, sigset_t *oldset,
tsk->thread.fp_state.fpscr = 0; /* turn off all fp exceptions */
#endif
- /* create a stack frame for the caller of the handler */
- newsp = ((unsigned long)frame) - __SIGNAL_FRAMESIZE;
- if (put_user(regs->gpr[1], (u32 __user *)newsp))
- goto badframe;
-
- regs->gpr[1] = newsp;
+ regs->gpr[1] = (unsigned long)newsp;
regs->gpr[3] = ksig->sig;
regs->gpr[4] = (unsigned long) sc;
regs_set_return_ip(regs, (unsigned long) ksig->ka.sa.sa_handler);
diff --git a/arch/powerpc/kernel/signal_64.c b/arch/powerpc/kernel/signal_64.c
index bf7fc579d572..67de29cf581a 100644
--- a/arch/powerpc/kernel/signal_64.c
+++ b/arch/powerpc/kernel/signal_64.c
@@ -846,13 +846,14 @@ int handle_rt_signal64(struct ksignal *ksig, sigset_t *set,
struct task_struct *tsk)
{
struct rt_sigframe __user *frame;
- unsigned long newsp = 0;
+ unsigned long __user *newsp;
long err = 0;
struct pt_regs *regs = tsk->thread.regs;
/* Save the thread's msr before get_tm_stackpointer() changes it */
unsigned long msr = regs->msr;
frame = get_sigframe(ksig, tsk, sizeof(*frame), 0);
+ newsp = (unsigned long __user *)((unsigned long)frame - __SIGNAL_FRAMESIZE);
/*
* This only applies when calling unsafe_setup_sigcontext() and must be
@@ -870,7 +871,7 @@ int handle_rt_signal64(struct ksignal *ksig, sigset_t *set,
msr);
#endif
- scoped_user_write_access(frame, badframe) {
+ scoped_user_write_access_size(newsp, __SIGNAL_FRAMESIZE + sizeof(*frame), badframe) {
unsafe_put_user(&frame->info, &frame->pinfo, badframe);
unsafe_put_user(&frame->uc, &frame->puc, badframe);
@@ -892,6 +893,8 @@ int handle_rt_signal64(struct ksignal *ksig, sigset_t *set,
}
unsafe_copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set), badframe);
+ /* Allocate a dummy caller frame for the signal handler. */
+ unsafe_put_user(regs->gpr[1], newsp, badframe);
}
/* Save the siginfo outside of the unsafe block. */
@@ -911,10 +914,6 @@ int handle_rt_signal64(struct ksignal *ksig, sigset_t *set,
regs_set_return_ip(regs, (unsigned long) &frame->tramp[0]);
}
- /* Allocate a dummy caller frame for the signal handler. */
- newsp = ((unsigned long)frame) - __SIGNAL_FRAMESIZE;
- err |= put_user(regs->gpr[1], (unsigned long __user *)newsp);
-
/* Set up "regs" so we "return" to the signal handler. */
if (is_elf2_task()) {
regs->ctr = (unsigned long) ksig->ka.sa.sa_handler;
@@ -936,7 +935,7 @@ int handle_rt_signal64(struct ksignal *ksig, sigset_t *set,
/* enter the signal handler in native-endian mode */
regs_set_return_msr(regs, (regs->msr & ~MSR_LE) | (MSR_KERNEL & MSR_LE));
- regs->gpr[1] = newsp;
+ regs->gpr[1] = (unsigned long)newsp;
regs->gpr[3] = ksig->sig;
regs->result = 0;
if (ksig->ka.sa.sa_flags & SA_SIGINFO) {
--
2.54.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH v1 6/8] signal: Add unsafe_copy_siginfo_to_user()
2026-05-22 9:56 [PATCH v1 0/8] powerpc/signal: Convert to scoped user access Christophe Leroy (CS GROUP)
` (4 preceding siblings ...)
2026-05-22 9:56 ` [PATCH v1 5/8] powerpc/signal: Include the new stack frame inside the user access block Christophe Leroy (CS GROUP)
@ 2026-05-22 9:56 ` Christophe Leroy (CS GROUP)
2026-05-22 9:56 ` [PATCH v1 7/8] powerpc/uaccess: Add unsafe_clear_user() Christophe Leroy (CS GROUP)
2026-05-22 9:56 ` [PATCH v1 8/8] powerpc/signal: Use unsafe_copy_siginfo_to_user() Christophe Leroy (CS GROUP)
7 siblings, 0 replies; 13+ messages in thread
From: Christophe Leroy (CS GROUP) @ 2026-05-22 9:56 UTC (permalink / raw)
To: Michael Ellerman, Nicholas Piggin, Madhavan Srinivasan
Cc: Christophe Leroy (CS GROUP), linux-kernel, linuxppc-dev
In the same spirit as commit fb05121fd6a2 ("signal: Add
unsafe_get_compat_sigset()"), implement an 'unsafe' version of
copy_siginfo_to_user() in order to use it within user access blocks.
For that, also add an 'unsafe' version of clear_user().
This commit adds the generic fallback for unsafe_clear_user().
Architectures wanting to use unsafe_copy_siginfo_to_user() within a
user_access_begin() section have to make sure they have their
own unsafe_clear_user().
Signed-off-by: Christophe Leroy (CS GROUP) <chleroy@kernel.org>
---
include/linux/signal.h | 15 +++++++++++++++
include/linux/uaccess.h | 1 +
kernel/signal.c | 5 -----
3 files changed, 16 insertions(+), 5 deletions(-)
diff --git a/include/linux/signal.h b/include/linux/signal.h
index f19816832f05..3ee6c9463f8b 100644
--- a/include/linux/signal.h
+++ b/include/linux/signal.h
@@ -36,6 +36,21 @@ static inline void copy_siginfo_to_external(siginfo_t *to,
int copy_siginfo_to_user(siginfo_t __user *to, const kernel_siginfo_t *from);
int copy_siginfo_from_user(kernel_siginfo_t *to, const siginfo_t __user *from);
+static __always_inline char __user *si_expansion(const siginfo_t __user *info)
+{
+ return ((char __user *)info) + sizeof(struct kernel_siginfo);
+}
+
+#define unsafe_copy_siginfo_to_user(to, from, label) do { \
+ siginfo_t __user *__ucs_to = to; \
+ const kernel_siginfo_t *__ucs_from = from; \
+ char __user *__ucs_expansion = si_expansion(__ucs_to); \
+ \
+ unsafe_copy_to_user(__ucs_to, __ucs_from, \
+ sizeof(struct kernel_siginfo), label); \
+ unsafe_clear_user(__ucs_expansion, SI_EXPANSION_SIZE, label); \
+} while (0)
+
enum siginfo_layout {
SIL_KILL,
SIL_TIMER,
diff --git a/include/linux/uaccess.h b/include/linux/uaccess.h
index 56328601218c..43e573b172a2 100644
--- a/include/linux/uaccess.h
+++ b/include/linux/uaccess.h
@@ -636,6 +636,7 @@ do { \
#define unsafe_put_user(x,p,e) unsafe_op_wrap(__put_user(x,p),e)
#define unsafe_copy_to_user(d,s,l,e) unsafe_op_wrap(__copy_to_user(d,s,l),e)
#define unsafe_copy_from_user(d,s,l,e) unsafe_op_wrap(__copy_from_user(d,s,l),e)
+#define unsafe_clear_user(d, l, e) unsafe_op_wrap(__clear_user(d, l), e)
static inline unsigned long user_access_save(void) { return 0UL; }
static inline void user_access_restore(unsigned long flags) { }
#endif /* !user_access_begin */
diff --git a/kernel/signal.c b/kernel/signal.c
index 2d102e025883..2c5eb741fe8c 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -3493,11 +3493,6 @@ enum siginfo_layout siginfo_layout(unsigned sig, int si_code)
return layout;
}
-static inline char __user *si_expansion(const siginfo_t __user *info)
-{
- return ((char __user *)info) + sizeof(struct kernel_siginfo);
-}
-
int copy_siginfo_to_user(siginfo_t __user *to, const kernel_siginfo_t *from)
{
char __user *expansion = si_expansion(to);
--
2.54.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH v1 7/8] powerpc/uaccess: Add unsafe_clear_user()
2026-05-22 9:56 [PATCH v1 0/8] powerpc/signal: Convert to scoped user access Christophe Leroy (CS GROUP)
` (5 preceding siblings ...)
2026-05-22 9:56 ` [PATCH v1 6/8] signal: Add unsafe_copy_siginfo_to_user() Christophe Leroy (CS GROUP)
@ 2026-05-22 9:56 ` Christophe Leroy (CS GROUP)
2026-05-22 9:56 ` [PATCH v1 8/8] powerpc/signal: Use unsafe_copy_siginfo_to_user() Christophe Leroy (CS GROUP)
7 siblings, 0 replies; 13+ messages in thread
From: Christophe Leroy (CS GROUP) @ 2026-05-22 9:56 UTC (permalink / raw)
To: Michael Ellerman, Nicholas Piggin, Madhavan Srinivasan
Cc: Christophe Leroy (CS GROUP), linux-kernel, linuxppc-dev
Implement unsafe_clear_user() for powerpc.
It's a copy/paste of unsafe_copy_to_user() with value 0 as source.
It may be improved in a later patch by using 'dcbz' instruction
to zeroize full cache lines at once.
Signed-off-by: Christophe Leroy (CS GROUP) <chleroy@kernel.org>
---
arch/powerpc/include/asm/uaccess.h | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/arch/powerpc/include/asm/uaccess.h b/arch/powerpc/include/asm/uaccess.h
index e98c628e3899..ef6711d1278b 100644
--- a/arch/powerpc/include/asm/uaccess.h
+++ b/arch/powerpc/include/asm/uaccess.h
@@ -588,6 +588,26 @@ do { \
unsafe_put_user(*(u8*)(_src + _i), (u8 __user *)(_dst + _i), e); \
} while (0)
+#define unsafe_clear_user(d, l, e) \
+do { \
+ u8 __user *_dst = (u8 __user *)(d); \
+ size_t _len = (l); \
+ int _i; \
+ \
+ for (_i = 0; _i < (_len & ~(sizeof(u64) - 1)); _i += sizeof(u64)) \
+ unsafe_put_user(0, (u64 __user *)(_dst + _i), e); \
+ if (_len & 4) { \
+ unsafe_put_user(0, (u32 __user *)(_dst + _i), e); \
+ _i += 4; \
+ } \
+ if (_len & 2) { \
+ unsafe_put_user(0, (u16 __user *)(_dst + _i), e); \
+ _i += 2; \
+ } \
+ if (_len & 1) \
+ unsafe_put_user(0, (u8 __user *)(_dst + _i), e); \
+} while (0)
+
#define arch_get_kernel_nofault(dst, src, type, err_label) \
__get_user_size_goto(*((type *)(dst)), \
(__force type __user *)(src), sizeof(type), err_label)
--
2.54.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH v1 8/8] powerpc/signal: Use unsafe_copy_siginfo_to_user()
2026-05-22 9:56 [PATCH v1 0/8] powerpc/signal: Convert to scoped user access Christophe Leroy (CS GROUP)
` (6 preceding siblings ...)
2026-05-22 9:56 ` [PATCH v1 7/8] powerpc/uaccess: Add unsafe_clear_user() Christophe Leroy (CS GROUP)
@ 2026-05-22 9:56 ` Christophe Leroy (CS GROUP)
7 siblings, 0 replies; 13+ messages in thread
From: Christophe Leroy (CS GROUP) @ 2026-05-22 9:56 UTC (permalink / raw)
To: Michael Ellerman, Nicholas Piggin, Madhavan Srinivasan
Cc: Christophe Leroy (CS GROUP), linux-kernel, linuxppc-dev
Use unsafe_copy_siginfo_to_user() in order to do the copy
within the user access block.
On an mpc 8321 (book3s/32) the improvment is about 5% on a process
sending a signal to itself.
Signed-off-by: Christophe Leroy (CS GROUP) <chleroy@kernel.org>
---
arch/powerpc/kernel/signal_32.c | 18 +++++++++---------
arch/powerpc/kernel/signal_64.c | 5 +----
2 files changed, 10 insertions(+), 13 deletions(-)
diff --git a/arch/powerpc/kernel/signal_32.c b/arch/powerpc/kernel/signal_32.c
index 6b1fbd95b07d..99a3efa874eb 100644
--- a/arch/powerpc/kernel/signal_32.c
+++ b/arch/powerpc/kernel/signal_32.c
@@ -716,12 +716,6 @@ static long restore_tm_user_regs(struct pt_regs *regs, struct mcontext __user *s
}
#endif
-#ifdef CONFIG_PPC64
-
-#define copy_siginfo_to_user copy_siginfo_to_user32
-
-#endif /* CONFIG_PPC64 */
-
/*
* Set up a signal frame for a "real-time" signal handler
* (one which gets siginfo).
@@ -735,6 +729,7 @@ int handle_rt_signal32(struct ksignal *ksig, sigset_t *oldset,
struct pt_regs *regs = tsk->thread.regs;
/* Save the thread's msr before get_tm_stackpointer() changes it */
unsigned long msr = regs->msr;
+ compat_siginfo_t uinfo;
/* Set up Signal Frame */
frame = get_sigframe(ksig, tsk, sizeof(*frame), 1);
@@ -744,6 +739,9 @@ int handle_rt_signal32(struct ksignal *ksig, sigset_t *oldset,
else
prepare_save_user_regs(1);
+ if (IS_ENABLED(CONFIG_COMPAT))
+ copy_siginfo_to_external32(&uinfo, &ksig->info);
+
scoped_user_rw_access_size(newsp, __SIGNAL_FRAMESIZE + 16 + sizeof(*frame), badframe) {
struct mcontext __user *mctx;
struct mcontext __user *tm_mctx = NULL;
@@ -785,14 +783,16 @@ int handle_rt_signal32(struct ksignal *ksig, sigset_t *oldset,
asm("dcbst %y0; sync; icbi %y0; sync" :: "Z" (mctx->mc_pad[0]));
}
unsafe_put_sigset_t(&frame->uc.uc_sigmask, oldset, badframe);
+ if (IS_ENABLED(CONFIG_COMPAT))
+ unsafe_copy_to_user(&frame->info, &uinfo, sizeof(frame->info), badframe);
+ else
+ unsafe_copy_siginfo_to_user((void __user *)&frame->info, &ksig->info,
+ badframe);
/* create a stack frame for the caller of the handler */
unsafe_put_user(regs->gpr[1], newsp, badframe);
}
- if (copy_siginfo_to_user(&frame->info, &ksig->info))
- goto badframe;
-
regs->link = tramp;
#ifdef CONFIG_PPC_FPU_REGS
diff --git a/arch/powerpc/kernel/signal_64.c b/arch/powerpc/kernel/signal_64.c
index 67de29cf581a..fa18ac43eb2a 100644
--- a/arch/powerpc/kernel/signal_64.c
+++ b/arch/powerpc/kernel/signal_64.c
@@ -893,14 +893,11 @@ int handle_rt_signal64(struct ksignal *ksig, sigset_t *set,
}
unsafe_copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set), badframe);
+ unsafe_copy_siginfo_to_user(&frame->info, &ksig->info, badframe);
/* Allocate a dummy caller frame for the signal handler. */
unsafe_put_user(regs->gpr[1], newsp, badframe);
}
- /* Save the siginfo outside of the unsafe block. */
- if (copy_siginfo_to_user(&frame->info, &ksig->info))
- goto badframe;
-
/* Make sure signal handler doesn't get spurious FP exceptions */
tsk->thread.fp_state.fpscr = 0;
--
2.54.0
^ permalink raw reply related [flat|nested] 13+ messages in thread