Michal Suchanek d457b8
From 2464cc4c345699adea52c7aef75707207cb8a2f6 Mon Sep 17 00:00:00 2001
Michal Suchanek d457b8
From: Gustavo Luiz Duarte <gustavold@linux.ibm.com>
Michal Suchanek d457b8
Date: Tue, 11 Feb 2020 00:38:29 -0300
Michal Suchanek d457b8
Subject: [PATCH] powerpc/tm: Fix clearing MSR[TS] in current when reclaiming
Michal Suchanek d457b8
 on signal delivery
Michal Suchanek d457b8
Michal Suchanek d457b8
References: bsc#1118338 ltc#173734
Petr Tesarik dc675f
Patch-mainline: v5.6-rc3
Michal Suchanek d457b8
Git-commit: 2464cc4c345699adea52c7aef75707207cb8a2f6
Michal Suchanek d457b8
Michal Suchanek d457b8
After a treclaim, we expect to be in non-transactional state. If we
Michal Suchanek d457b8
don't clear the current thread's MSR[TS] before we get preempted, then
Michal Suchanek d457b8
tm_recheckpoint_new_task() will recheckpoint and we get rescheduled in
Michal Suchanek d457b8
suspended transaction state.
Michal Suchanek d457b8
Michal Suchanek d457b8
When handling a signal caught in transactional state,
Michal Suchanek d457b8
handle_rt_signal64() calls get_tm_stackpointer() that treclaims the
Michal Suchanek d457b8
transaction using tm_reclaim_current() but without clearing the
Michal Suchanek d457b8
thread's MSR[TS]. This can cause the TM Bad Thing exception below if
Michal Suchanek d457b8
later we pagefault and get preempted trying to access the user's
Michal Suchanek d457b8
sigframe, using __put_user(). Afterwards, when we are rescheduled back
Michal Suchanek d457b8
into do_page_fault() (but now in suspended state since the thread's
Michal Suchanek d457b8
MSR[TS] was not cleared), upon executing 'rfid' after completion of
Michal Suchanek d457b8
the page fault handling, the exception is raised because a transition
Michal Suchanek d457b8
from suspended to non-transactional state is invalid.
Michal Suchanek d457b8
Michal Suchanek d457b8
  Unexpected TM Bad Thing exception at c00000000000de44 (msr 0x8000000302a03031) tm_scratch=800000010280b033
Michal Suchanek d457b8
  Oops: Unrecoverable exception, sig: 6 [#1]
Michal Suchanek d457b8
  LE PAGE_SIZE=64K MMU=Hash SMP NR_CPUS=2048 NUMA pSeries
Michal Suchanek d457b8
  CPU: 25 PID: 15547 Comm: a.out Not tainted 5.4.0-rc2 #32
Michal Suchanek d457b8
  NIP:  c00000000000de44 LR: c000000000034728 CTR: 0000000000000000
Michal Suchanek d457b8
  REGS: c00000003fe7bd70 TRAP: 0700   Not tainted  (5.4.0-rc2)
Michal Suchanek d457b8
  MSR:  8000000302a03031 <SF,VEC,VSX,FP,ME,IR,DR,LE,TM[SE]>  CR: 44000884  XER: 00000000
Michal Suchanek d457b8
  CFAR: c00000000000dda4 IRQMASK: 0
Michal Suchanek d457b8
  PACATMSCRATCH: 800000010280b033
Michal Suchanek d457b8
  GPR00: c000000000034728 c000000f65a17c80 c000000001662800 00007fffacf3fd78
Michal Suchanek d457b8
  GPR04: 0000000000001000 0000000000001000 0000000000000000 c000000f611f8af0
Michal Suchanek d457b8
  GPR08: 0000000000000000 0000000078006001 0000000000000000 000c000000000000
Michal Suchanek d457b8
  GPR12: c000000f611f84b0 c00000003ffcb200 0000000000000000 0000000000000000
Michal Suchanek d457b8
  GPR16: 0000000000000000 0000000000000000 0000000000000000 0000000000000000
Michal Suchanek d457b8
  GPR20: 0000000000000000 0000000000000000 0000000000000000 c000000f611f8140
Michal Suchanek d457b8
  GPR24: 0000000000000000 00007fffacf3fd68 c000000f65a17d90 c000000f611f7800
Michal Suchanek d457b8
  GPR28: c000000f65a17e90 c000000f65a17e90 c000000001685e18 00007fffacf3f000
Michal Suchanek d457b8
  NIP [c00000000000de44] fast_exception_return+0xf4/0x1b0
Michal Suchanek d457b8
  LR [c000000000034728] handle_rt_signal64+0x78/0xc50
Michal Suchanek d457b8
  Call Trace:
Michal Suchanek d457b8
  [c000000f65a17c80] [c000000000034710] handle_rt_signal64+0x60/0xc50 (unreliable)
Michal Suchanek d457b8
  [c000000f65a17d30] [c000000000023640] do_notify_resume+0x330/0x460
Michal Suchanek d457b8
  [c000000f65a17e20] [c00000000000dcc4] ret_from_except_lite+0x70/0x74
Michal Suchanek d457b8
  Instruction dump:
Michal Suchanek d457b8
  7c4ff120 e8410170 7c5a03a6 38400000 f8410060 e8010070 e8410080 e8610088
Michal Suchanek d457b8
  60000000 60000000 e8810090 e8210078 <4c000024> 48000000 e8610178 88ed0989
Michal Suchanek d457b8
  ---[ end trace 93094aa44b442f87 ]---
Michal Suchanek d457b8
Michal Suchanek d457b8
The simplified sequence of events that triggers the above exception is:
Michal Suchanek d457b8
Michal Suchanek d457b8
  ...				# userspace in NON-TRANSACTIONAL state
Michal Suchanek d457b8
  tbegin			# userspace in TRANSACTIONAL state
Michal Suchanek d457b8
  signal delivery		# kernelspace in SUSPENDED state
Michal Suchanek d457b8
  handle_rt_signal64()
Michal Suchanek d457b8
    get_tm_stackpointer()
Michal Suchanek d457b8
      treclaim			# kernelspace in NON-TRANSACTIONAL state
Michal Suchanek d457b8
    __put_user()
Michal Suchanek d457b8
      page fault happens. We will never get back here because of the TM Bad Thing exception.
Michal Suchanek d457b8
Michal Suchanek d457b8
  page fault handling kicks in and we voluntarily preempt ourselves
Michal Suchanek d457b8
  do_page_fault()
Michal Suchanek d457b8
    __schedule()
Michal Suchanek d457b8
      __switch_to(other_task)
Michal Suchanek d457b8
Michal Suchanek d457b8
  our task is rescheduled and we recheckpoint because the thread's MSR[TS] was not cleared
Michal Suchanek d457b8
  __switch_to(our_task)
Michal Suchanek d457b8
    switch_to_tm()
Michal Suchanek d457b8
      tm_recheckpoint_new_task()
Michal Suchanek d457b8
        trechkpt			# kernelspace in SUSPENDED state
Michal Suchanek d457b8
Michal Suchanek d457b8
  The page fault handling resumes, but now we are in suspended transaction state
Michal Suchanek d457b8
  do_page_fault()    completes
Michal Suchanek d457b8
  rfid     <----- trying to get back where the page fault happened (we were non-transactional back then)
Michal Suchanek d457b8
  TM Bad Thing			# illegal transition from suspended to non-transactional
Michal Suchanek d457b8
Michal Suchanek d457b8
This patch fixes that issue by clearing the current thread's MSR[TS]
Michal Suchanek d457b8
just after treclaim in get_tm_stackpointer() so that we stay in
Michal Suchanek d457b8
non-transactional state in case we are preempted. In order to make
Michal Suchanek d457b8
treclaim and clearing the thread's MSR[TS] atomic from a preemption
Michal Suchanek d457b8
perspective when CONFIG_PREEMPT is set, preempt_disable/enable() is
Michal Suchanek d457b8
used. It's also necessary to save the previous value of the thread's
Michal Suchanek d457b8
MSR before get_tm_stackpointer() is called so that it can be exposed
Michal Suchanek d457b8
to the signal handler later in setup_tm_sigcontexts() to inform the
Michal Suchanek d457b8
userspace MSR at the moment of the signal delivery.
Michal Suchanek d457b8
Michal Suchanek d457b8
Found with tm-signal-context-force-tm kernel selftest.
Michal Suchanek d457b8
Michal Suchanek d457b8
Fixes: 2b0a576d15e0 ("powerpc: Add new transactional memory state to the signal context")
Michal Suchanek d457b8
Cc: stable@vger.kernel.org # v3.9
Michal Suchanek d457b8
Signed-off-by: Gustavo Luiz Duarte <gustavold@linux.ibm.com>
Michal Suchanek d457b8
Acked-by: Michael Neuling <mikey@neuling.org>
Michal Suchanek d457b8
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Michal Suchanek d457b8
Link: https://lore.kernel.org/r/20200211033831.11165-1-gustavold@linux.ibm.com
Michal Suchanek d457b8
Acked-by: Michal Suchanek <msuchanek@suse.de>
Michal Suchanek d457b8
---
Michal Suchanek d457b8
 arch/powerpc/kernel/signal.c    | 17 +++++++++++++++--
Michal Suchanek d457b8
 arch/powerpc/kernel/signal_32.c | 28 ++++++++++++++--------------
Michal Suchanek d457b8
 arch/powerpc/kernel/signal_64.c | 22 ++++++++++------------
Michal Suchanek d457b8
 3 files changed, 39 insertions(+), 28 deletions(-)
Michal Suchanek d457b8
Michal Suchanek d457b8
diff --git a/arch/powerpc/kernel/signal.c b/arch/powerpc/kernel/signal.c
Michal Suchanek d457b8
index e6c30cee6abf..d215f9554553 100644
Michal Suchanek d457b8
--- a/arch/powerpc/kernel/signal.c
Michal Suchanek d457b8
+++ b/arch/powerpc/kernel/signal.c
Michal Suchanek d457b8
@@ -200,14 +200,27 @@ unsigned long get_tm_stackpointer(struct task_struct *tsk)
Michal Suchanek d457b8
 	 * normal/non-checkpointed stack pointer.
Michal Suchanek d457b8
 	 */
Michal Suchanek d457b8
 
Michal Suchanek d457b8
+	unsigned long ret = tsk->thread.regs->gpr[1];
Michal Suchanek d457b8
+
Michal Suchanek d457b8
 #ifdef CONFIG_PPC_TRANSACTIONAL_MEM
Michal Suchanek d457b8
 	BUG_ON(tsk != current);
Michal Suchanek d457b8
 
Michal Suchanek d457b8
 	if (MSR_TM_ACTIVE(tsk->thread.regs->msr)) {
Michal Suchanek d457b8
+		preempt_disable();
Michal Suchanek d457b8
 		tm_reclaim_current(TM_CAUSE_SIGNAL);
Michal Suchanek d457b8
 		if (MSR_TM_TRANSACTIONAL(tsk->thread.regs->msr))
Michal Suchanek d457b8
-			return tsk->thread.ckpt_regs.gpr[1];
Michal Suchanek d457b8
+			ret = tsk->thread.ckpt_regs.gpr[1];
Michal Suchanek d457b8
+
Michal Suchanek d457b8
+		/*
Michal Suchanek d457b8
+		 * If we treclaim, we must clear the current thread's TM bits
Michal Suchanek d457b8
+		 * before re-enabling preemption. Otherwise we might be
Michal Suchanek d457b8
+		 * preempted and have the live MSR[TS] changed behind our back
Michal Suchanek d457b8
+		 * (tm_recheckpoint_new_task() would recheckpoint). Besides, we
Michal Suchanek d457b8
+		 * enter the signal handler in non-transactional state.
Michal Suchanek d457b8
+		 */
Michal Suchanek d457b8
+		tsk->thread.regs->msr &= ~MSR_TS_MASK;
Michal Suchanek d457b8
+		preempt_enable();
Michal Suchanek d457b8
 	}
Michal Suchanek d457b8
 #endif
Michal Suchanek d457b8
-	return tsk->thread.regs->gpr[1];
Michal Suchanek d457b8
+	return ret;
Michal Suchanek d457b8
 }
Michal Suchanek d457b8
diff --git a/arch/powerpc/kernel/signal_32.c b/arch/powerpc/kernel/signal_32.c
Michal Suchanek d457b8
index 98600b276f76..1b090a76b444 100644
Michal Suchanek d457b8
--- a/arch/powerpc/kernel/signal_32.c
Michal Suchanek d457b8
+++ b/arch/powerpc/kernel/signal_32.c
Michal Suchanek d457b8
@@ -489,19 +489,11 @@ static int save_user_regs(struct pt_regs *regs, struct mcontext __user *frame,
Michal Suchanek d457b8
  */
Michal Suchanek d457b8
 static int save_tm_user_regs(struct pt_regs *regs,
Michal Suchanek d457b8
 			     struct mcontext __user *frame,
Michal Suchanek d457b8
-			     struct mcontext __user *tm_frame, int sigret)
Michal Suchanek d457b8
+			     struct mcontext __user *tm_frame, int sigret,
Michal Suchanek d457b8
+			     unsigned long msr)
Michal Suchanek d457b8
 {
Michal Suchanek d457b8
-	unsigned long msr = regs->msr;
Michal Suchanek d457b8
-
Michal Suchanek d457b8
 	WARN_ON(tm_suspend_disabled);
Michal Suchanek d457b8
 
Michal Suchanek d457b8
-	/* Remove TM bits from thread's MSR.  The MSR in the sigcontext
Michal Suchanek d457b8
-	 * just indicates to userland that we were doing a transaction, but we
Michal Suchanek d457b8
-	 * don't want to return in transactional state.  This also ensures
Michal Suchanek d457b8
-	 * that flush_fp_to_thread won't set TIF_RESTORE_TM again.
Michal Suchanek d457b8
-	 */
Michal Suchanek d457b8
-	regs->msr &= ~MSR_TS_MASK;
Michal Suchanek d457b8
-
Michal Suchanek d457b8
 	/* Save both sets of general registers */
Michal Suchanek d457b8
 	if (save_general_regs(&current->thread.ckpt_regs, frame)
Michal Suchanek d457b8
 	    || save_general_regs(regs, tm_frame))
Michal Suchanek d457b8
@@ -912,6 +904,10 @@ int handle_rt_signal32(struct ksignal *ksig, sigset_t *oldset,
Michal Suchanek d457b8
 	int sigret;
Michal Suchanek d457b8
 	unsigned long tramp;
Michal Suchanek d457b8
 	struct pt_regs *regs = tsk->thread.regs;
Michal Suchanek d457b8
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
Michal Suchanek d457b8
+	/* Save the thread's msr before get_tm_stackpointer() changes it */
Michal Suchanek d457b8
+	unsigned long msr = regs->msr;
Michal Suchanek d457b8
+#endif
Michal Suchanek d457b8
 
Michal Suchanek d457b8
 	BUG_ON(tsk != current);
Michal Suchanek d457b8
 
Michal Suchanek d457b8
@@ -944,13 +940,13 @@ int handle_rt_signal32(struct ksignal *ksig, sigset_t *oldset,
Michal Suchanek d457b8
 
Michal Suchanek d457b8
 #ifdef CONFIG_PPC_TRANSACTIONAL_MEM
Michal Suchanek d457b8
 	tm_frame = &rt_sf->uc_transact.uc_mcontext;
Michal Suchanek d457b8
-	if (MSR_TM_ACTIVE(regs->msr)) {
Michal Suchanek d457b8
+	if (MSR_TM_ACTIVE(msr)) {
Michal Suchanek d457b8
 		if (__put_user((unsigned long)&rt_sf->uc_transact,
Michal Suchanek d457b8
 			       &rt_sf->uc.uc_link) ||
Michal Suchanek d457b8
 		    __put_user((unsigned long)tm_frame,
Michal Suchanek d457b8
 			       &rt_sf->uc_transact.uc_regs))
Michal Suchanek d457b8
 			goto badframe;
Michal Suchanek d457b8
-		if (save_tm_user_regs(regs, frame, tm_frame, sigret))
Michal Suchanek d457b8
+		if (save_tm_user_regs(regs, frame, tm_frame, sigret, msr))
Michal Suchanek d457b8
 			goto badframe;
Michal Suchanek d457b8
 	}
Michal Suchanek d457b8
 	else
Michal Suchanek d457b8
@@ -1369,6 +1365,10 @@ int handle_signal32(struct ksignal *ksig, sigset_t *oldset,
Michal Suchanek d457b8
 	int sigret;
Michal Suchanek d457b8
 	unsigned long tramp;
Michal Suchanek d457b8
 	struct pt_regs *regs = tsk->thread.regs;
Michal Suchanek d457b8
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
Michal Suchanek d457b8
+	/* Save the thread's msr before get_tm_stackpointer() changes it */
Michal Suchanek d457b8
+	unsigned long msr = regs->msr;
Michal Suchanek d457b8
+#endif
Michal Suchanek d457b8
 
Michal Suchanek d457b8
 	BUG_ON(tsk != current);
Michal Suchanek d457b8
 
Michal Suchanek d457b8
@@ -1402,9 +1402,9 @@ int handle_signal32(struct ksignal *ksig, sigset_t *oldset,
Michal Suchanek d457b8
 
Michal Suchanek d457b8
 #ifdef CONFIG_PPC_TRANSACTIONAL_MEM
Michal Suchanek d457b8
 	tm_mctx = &frame->mctx_transact;
Michal Suchanek d457b8
-	if (MSR_TM_ACTIVE(regs->msr)) {
Michal Suchanek d457b8
+	if (MSR_TM_ACTIVE(msr)) {
Michal Suchanek d457b8
 		if (save_tm_user_regs(regs, &frame->mctx, &frame->mctx_transact,
Michal Suchanek d457b8
-				      sigret))
Michal Suchanek d457b8
+				      sigret, msr))
Michal Suchanek d457b8
 			goto badframe;
Michal Suchanek d457b8
 	}
Michal Suchanek d457b8
 	else
Michal Suchanek d457b8
diff --git a/arch/powerpc/kernel/signal_64.c b/arch/powerpc/kernel/signal_64.c
Michal Suchanek d457b8
index 117515564ec7..84ed2e77ef9c 100644
Michal Suchanek d457b8
--- a/arch/powerpc/kernel/signal_64.c
Michal Suchanek d457b8
+++ b/arch/powerpc/kernel/signal_64.c
Michal Suchanek d457b8
@@ -192,7 +192,8 @@ static long setup_sigcontext(struct sigcontext __user *sc,
Michal Suchanek d457b8
 static long setup_tm_sigcontexts(struct sigcontext __user *sc,
Michal Suchanek d457b8
 				 struct sigcontext __user *tm_sc,
Michal Suchanek d457b8
 				 struct task_struct *tsk,
Michal Suchanek d457b8
-				 int signr, sigset_t *set, unsigned long handler)
Michal Suchanek d457b8
+				 int signr, sigset_t *set, unsigned long handler,
Michal Suchanek d457b8
+				 unsigned long msr)
Michal Suchanek d457b8
 {
Michal Suchanek d457b8
 	/* When CONFIG_ALTIVEC is set, we _always_ setup v_regs even if the
Michal Suchanek d457b8
 	 * process never used altivec yet (MSR_VEC is zero in pt_regs of
Michal Suchanek d457b8
@@ -207,12 +208,11 @@ static long setup_tm_sigcontexts(struct sigcontext __user *sc,
Michal Suchanek d457b8
 	elf_vrreg_t __user *tm_v_regs = sigcontext_vmx_regs(tm_sc);
Michal Suchanek d457b8
 #endif
Michal Suchanek d457b8
 	struct pt_regs *regs = tsk->thread.regs;
Michal Suchanek d457b8
-	unsigned long msr = tsk->thread.regs->msr;
Michal Suchanek d457b8
 	long err = 0;
Michal Suchanek d457b8
 
Michal Suchanek d457b8
 	BUG_ON(tsk != current);
Michal Suchanek d457b8
 
Michal Suchanek d457b8
-	BUG_ON(!MSR_TM_ACTIVE(regs->msr));
Michal Suchanek d457b8
+	BUG_ON(!MSR_TM_ACTIVE(msr));
Michal Suchanek d457b8
 
Michal Suchanek d457b8
 	WARN_ON(tm_suspend_disabled);
Michal Suchanek d457b8
 
Michal Suchanek d457b8
@@ -222,13 +222,6 @@ static long setup_tm_sigcontexts(struct sigcontext __user *sc,
Michal Suchanek d457b8
 	 */
Michal Suchanek d457b8
 	msr |= tsk->thread.ckpt_regs.msr & (MSR_FP | MSR_VEC | MSR_VSX);
Michal Suchanek d457b8
 
Michal Suchanek d457b8
-	/* Remove TM bits from thread's MSR.  The MSR in the sigcontext
Michal Suchanek d457b8
-	 * just indicates to userland that we were doing a transaction, but we
Michal Suchanek d457b8
-	 * don't want to return in transactional state.  This also ensures
Michal Suchanek d457b8
-	 * that flush_fp_to_thread won't set TIF_RESTORE_TM again.
Michal Suchanek d457b8
-	 */
Michal Suchanek d457b8
-	regs->msr &= ~MSR_TS_MASK;
Michal Suchanek d457b8
-
Michal Suchanek d457b8
 #ifdef CONFIG_ALTIVEC
Michal Suchanek d457b8
 	err |= __put_user(v_regs, &sc->v_regs);
Michal Suchanek d457b8
 	err |= __put_user(tm_v_regs, &tm_sc->v_regs);
Michal Suchanek d457b8
@@ -824,6 +817,10 @@ int handle_rt_signal64(struct ksignal *ksig, sigset_t *set,
Michal Suchanek d457b8
 	unsigned long newsp = 0;
Michal Suchanek d457b8
 	long err = 0;
Michal Suchanek d457b8
 	struct pt_regs *regs = tsk->thread.regs;
Michal Suchanek d457b8
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
Michal Suchanek d457b8
+	/* Save the thread's msr before get_tm_stackpointer() changes it */
Michal Suchanek d457b8
+	unsigned long msr = regs->msr;
Michal Suchanek d457b8
+#endif
Michal Suchanek d457b8
 
Michal Suchanek d457b8
 	BUG_ON(tsk != current);
Michal Suchanek d457b8
 
Michal Suchanek d457b8
@@ -841,7 +838,7 @@ int handle_rt_signal64(struct ksignal *ksig, sigset_t *set,
Michal Suchanek d457b8
 	err |= __put_user(0, &frame->uc.uc_flags);
Michal Suchanek d457b8
 	err |= __save_altstack(&frame->uc.uc_stack, regs->gpr[1]);
Michal Suchanek d457b8
 #ifdef CONFIG_PPC_TRANSACTIONAL_MEM
Michal Suchanek d457b8
-	if (MSR_TM_ACTIVE(regs->msr)) {
Michal Suchanek d457b8
+	if (MSR_TM_ACTIVE(msr)) {
Michal Suchanek d457b8
 		/* The ucontext_t passed to userland points to the second
Michal Suchanek d457b8
 		 * ucontext_t (for transactional state) with its uc_link ptr.
Michal Suchanek d457b8
 		 */
Michal Suchanek d457b8
@@ -849,7 +846,8 @@ int handle_rt_signal64(struct ksignal *ksig, sigset_t *set,
Michal Suchanek d457b8
 		err |= setup_tm_sigcontexts(&frame->uc.uc_mcontext,
Michal Suchanek d457b8
 					    &frame->uc_transact.uc_mcontext,
Michal Suchanek d457b8
 					    tsk, ksig->sig, NULL,
Michal Suchanek d457b8
-					    (unsigned long)ksig->ka.sa.sa_handler);
Michal Suchanek d457b8
+					    (unsigned long)ksig->ka.sa.sa_handler,
Michal Suchanek d457b8
+					    msr);
Michal Suchanek d457b8
 	} else
Michal Suchanek d457b8
 #endif
Michal Suchanek d457b8
 	{
Michal Suchanek d457b8
-- 
Michal Suchanek d457b8
2.23.0
Michal Suchanek d457b8