Skip to content

Commit 411c1cf

Browse files
mrutland-armPeter Zijlstra
authored andcommitted
arm64/entry: Fix arm64-specific rseq brokenness
Mathias Stearn reports that since v6.19, there are two big issues affecting rseq: (1) On arm64 specifically, rseq critical sections aren't aborted when they should be. (2) The 'cpu_id_start' field is no longer written by the kernel in all cases it used to be, including some cases where TCMalloc depends on the kernel clobbering the field. This patch fixes issue #1. This patch DOES NOT fix issue #2, which will need to be addressed by other patches. The arm64-specific brokenness is a result of commits: 2fc0e4b ("rseq: Record interrupt from user space") 39a1675 ("rseq: Optimize event setting") The first commit failed to add a call to rseq_note_user_irq_entry() on arm64. Thus arm64 never sets rseq_event::user_irq to record that it may be necessary to abort an active rseq critical section upon return to userspace. On its own, this commit had no functional impact as the value of rseq_event::user_irq was not consumed. The second commit relied upon rseq_event::user_irq to determine whether or not to bother to perform rseq work when returning to userspace. As rseq_event::user_irq wasn't set on arm64, this work would be skipped, and consequently an active rseq critical section would not be aborted. Fix this by giving arm64 syscall-specific entry/exit paths, and performing the relevant logic in syscall and non-syscall paths, including calling rseq_note_user_irq_entry() for non-syscall entry. Currently arm64 cannot use syscall_enter_from_user_mode(), syscall_exit_to_user_mode(), and irqentry_exit_to_user_mode(), due to ordering constraints with exception masking, and risk of ABI breakage for syscall tracing/audit/etc. For the moment the entry/exit logic is left as arm64-specific, directly using enter_from_user_mode() and exit_to_user_mode(), but mirroring the generic code. I intend to follow up with refactoring/cleanup, as we did for kernel mode entry paths in commit: 041aa7a ("entry: Split preemption from irqentry_exit_to_kernel_mode()") ... which will allow arm64 to use the GENERIC_IRQ_ENTRY functions directly. Fixes: 39a1675 ("rseq: Optimize event setting") Reported-by: Mathias Stearn <mathias@mongodb.com> Signed-off-by: Mark Rutland <mark.rutland@arm.com> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Acked-by: Catalin Marinas <catalin.marinas@arm.com> Link: https://lore.kernel.org/regressions/CAHnCjA25b+nO2n5CeifknSKHssJpPrjnf+dtr7UgzRw4Zgu=oA@mail.gmail.com/ Link: https://patch.msgid.link/20260508142023.3268622-1-mark.rutland@arm.com
1 parent 9f6d929 commit 411c1cf

3 files changed

Lines changed: 24 additions & 34 deletions

File tree

arch/arm64/kernel/entry-common.c

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,13 @@ static void noinstr arm64_exit_to_kernel_mode(struct pt_regs *regs,
6262
irqentry_exit_to_kernel_mode_after_preempt(regs, state);
6363
}
6464

65+
static __always_inline void arm64_syscall_enter_from_user_mode(struct pt_regs *regs)
66+
{
67+
enter_from_user_mode(regs);
68+
mte_disable_tco_entry(current);
69+
sme_enter_from_user_mode();
70+
}
71+
6572
/*
6673
* Handle IRQ/context state management when entering from user mode.
6774
* Before this function is called it is not safe to call regular kernel code,
@@ -70,20 +77,30 @@ static void noinstr arm64_exit_to_kernel_mode(struct pt_regs *regs,
7077
static __always_inline void arm64_enter_from_user_mode(struct pt_regs *regs)
7178
{
7279
enter_from_user_mode(regs);
80+
rseq_note_user_irq_entry();
7381
mte_disable_tco_entry(current);
7482
sme_enter_from_user_mode();
7583
}
7684

85+
static __always_inline void arm64_syscall_exit_to_user_mode(struct pt_regs *regs)
86+
{
87+
local_irq_disable();
88+
syscall_exit_to_user_mode_prepare(regs);
89+
local_daif_mask();
90+
sme_exit_to_user_mode();
91+
mte_check_tfsr_exit();
92+
exit_to_user_mode();
93+
}
94+
7795
/*
7896
* Handle IRQ/context state management when exiting to user mode.
7997
* After this function returns it is not safe to call regular kernel code,
8098
* instrumentable code, or any code which may trigger an exception.
8199
*/
82-
83100
static __always_inline void arm64_exit_to_user_mode(struct pt_regs *regs)
84101
{
85102
local_irq_disable();
86-
exit_to_user_mode_prepare_legacy(regs);
103+
irqentry_exit_to_user_mode_prepare(regs);
87104
local_daif_mask();
88105
sme_exit_to_user_mode();
89106
mte_check_tfsr_exit();
@@ -92,7 +109,7 @@ static __always_inline void arm64_exit_to_user_mode(struct pt_regs *regs)
92109

93110
asmlinkage void noinstr asm_exit_to_user_mode(struct pt_regs *regs)
94111
{
95-
arm64_exit_to_user_mode(regs);
112+
arm64_syscall_exit_to_user_mode(regs);
96113
}
97114

98115
/*
@@ -716,12 +733,12 @@ static void noinstr el0_brk64(struct pt_regs *regs, unsigned long esr)
716733

717734
static void noinstr el0_svc(struct pt_regs *regs)
718735
{
719-
arm64_enter_from_user_mode(regs);
736+
arm64_syscall_enter_from_user_mode(regs);
720737
cortex_a76_erratum_1463225_svc_handler();
721738
fpsimd_syscall_enter();
722739
local_daif_restore(DAIF_PROCCTX);
723740
do_el0_svc(regs);
724-
arm64_exit_to_user_mode(regs);
741+
arm64_syscall_exit_to_user_mode(regs);
725742
fpsimd_syscall_exit();
726743
}
727744

@@ -868,11 +885,11 @@ static void noinstr el0_cp15(struct pt_regs *regs, unsigned long esr)
868885

869886
static void noinstr el0_svc_compat(struct pt_regs *regs)
870887
{
871-
arm64_enter_from_user_mode(regs);
888+
arm64_syscall_enter_from_user_mode(regs);
872889
cortex_a76_erratum_1463225_svc_handler();
873890
local_daif_restore(DAIF_PROCCTX);
874891
do_el0_svc_compat(regs);
875-
arm64_exit_to_user_mode(regs);
892+
arm64_syscall_exit_to_user_mode(regs);
876893
}
877894

878895
static void noinstr el0_bkpt32(struct pt_regs *regs, unsigned long esr)

include/linux/irq-entry-common.h

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -218,14 +218,6 @@ static __always_inline void __exit_to_user_mode_validate(void)
218218
lockdep_sys_exit();
219219
}
220220

221-
/* Temporary workaround to keep ARM64 alive */
222-
static __always_inline void exit_to_user_mode_prepare_legacy(struct pt_regs *regs)
223-
{
224-
__exit_to_user_mode_prepare(regs, EXIT_TO_USER_MODE_WORK);
225-
rseq_exit_to_user_mode_legacy();
226-
__exit_to_user_mode_validate();
227-
}
228-
229221
/**
230222
* syscall_exit_to_user_mode_prepare - call exit_to_user_mode_loop() if required
231223
* @regs: Pointer to pt_regs on entry stack

include/linux/rseq_entry.h

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -749,24 +749,6 @@ static __always_inline void rseq_irqentry_exit_to_user_mode(void)
749749
ev->events = 0;
750750
}
751751

752-
/* Required to keep ARM64 working */
753-
static __always_inline void rseq_exit_to_user_mode_legacy(void)
754-
{
755-
struct rseq_event *ev = &current->rseq.event;
756-
757-
rseq_stat_inc(rseq_stats.exit);
758-
759-
if (static_branch_unlikely(&rseq_debug_enabled))
760-
WARN_ON_ONCE(ev->sched_switch);
761-
762-
/*
763-
* Ensure that event (especially user_irq) is cleared when the
764-
* interrupt did not result in a schedule and therefore the
765-
* rseq processing did not clear it.
766-
*/
767-
ev->events = 0;
768-
}
769-
770752
void __rseq_debug_syscall_return(struct pt_regs *regs);
771753

772754
static __always_inline void rseq_debug_syscall_return(struct pt_regs *regs)
@@ -782,7 +764,6 @@ static inline bool rseq_exit_to_user_mode_restart(struct pt_regs *regs, unsigned
782764
}
783765
static inline void rseq_syscall_exit_to_user_mode(void) { }
784766
static inline void rseq_irqentry_exit_to_user_mode(void) { }
785-
static inline void rseq_exit_to_user_mode_legacy(void) { }
786767
static inline void rseq_debug_syscall_return(struct pt_regs *regs) { }
787768
static inline bool rseq_grant_slice_extension(unsigned long ti_work, unsigned long mask) { return false; }
788769
#endif /* !CONFIG_RSEQ */

0 commit comments

Comments
 (0)