aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/kernel
diff options
context:
space:
mode:
authorMichael Neuling <mikey@neuling.org>2013-05-26 14:09:41 -0400
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2013-05-31 18:29:23 -0400
commit2b3f8e87cf99a33fb6faf5026d7147748bbd77b6 (patch)
treedfeb4cb63821ec34279d26b0ac7a35d96316b648 /arch/powerpc/kernel
parentb75c100ef24894bd2c8b52e123bcc5f191c5d9fd (diff)
powerpc/tm: Fix userspace stack corruption on signal delivery for active transactions
When in an active transaction that takes a signal, we need to be careful with the stack. It's possible that the stack has moved back up after the tbegin. The obvious case here is when the tbegin is called inside a function that returns before a tend. In this case, the stack is part of the checkpointed transactional memory state. If we write over this non transactionally or in suspend, we are in trouble because if we get a tm abort, the program counter and stack pointer will be back at the tbegin but our in memory stack won't be valid anymore. To avoid this, when taking a signal in an active transaction, we need to use the stack pointer from the checkpointed state, rather than the speculated state. This ensures that the signal context (written tm suspended) will be written below the stack required for the rollback. The transaction is aborted becuase of the treclaim, so any memory written between the tbegin and the signal will be rolled back anyway. For signals taken in non-TM or suspended mode, we use the normal/non-checkpointed stack pointer. Tested with 64 and 32 bit signals Signed-off-by: Michael Neuling <mikey@neuling.org> Cc: <stable@vger.kernel.org> # v3.9 Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'arch/powerpc/kernel')
-rw-r--r--arch/powerpc/kernel/signal.c40
-rw-r--r--arch/powerpc/kernel/signal.h2
-rw-r--r--arch/powerpc/kernel/signal_32.c10
-rw-r--r--arch/powerpc/kernel/signal_64.c23
4 files changed, 48 insertions, 27 deletions
diff --git a/arch/powerpc/kernel/signal.c b/arch/powerpc/kernel/signal.c
index 577a8aa69c6e..457e97aa2945 100644
--- a/arch/powerpc/kernel/signal.c
+++ b/arch/powerpc/kernel/signal.c
@@ -18,6 +18,7 @@
18#include <asm/uaccess.h> 18#include <asm/uaccess.h>
19#include <asm/unistd.h> 19#include <asm/unistd.h>
20#include <asm/debug.h> 20#include <asm/debug.h>
21#include <asm/tm.h>
21 22
22#include "signal.h" 23#include "signal.h"
23 24
@@ -30,13 +31,13 @@ int show_unhandled_signals = 1;
30/* 31/*
31 * Allocate space for the signal frame 32 * Allocate space for the signal frame
32 */ 33 */
33void __user * get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, 34void __user * get_sigframe(struct k_sigaction *ka, unsigned long sp,
34 size_t frame_size, int is_32) 35 size_t frame_size, int is_32)
35{ 36{
36 unsigned long oldsp, newsp; 37 unsigned long oldsp, newsp;
37 38
38 /* Default to using normal stack */ 39 /* Default to using normal stack */
39 oldsp = get_clean_sp(regs, is_32); 40 oldsp = get_clean_sp(sp, is_32);
40 41
41 /* Check for alt stack */ 42 /* Check for alt stack */
42 if ((ka->sa.sa_flags & SA_ONSTACK) && 43 if ((ka->sa.sa_flags & SA_ONSTACK) &&
@@ -175,3 +176,38 @@ void do_notify_resume(struct pt_regs *regs, unsigned long thread_info_flags)
175 176
176 user_enter(); 177 user_enter();
177} 178}
179
180unsigned long get_tm_stackpointer(struct pt_regs *regs)
181{
182 /* When in an active transaction that takes a signal, we need to be
183 * careful with the stack. It's possible that the stack has moved back
184 * up after the tbegin. The obvious case here is when the tbegin is
185 * called inside a function that returns before a tend. In this case,
186 * the stack is part of the checkpointed transactional memory state.
187 * If we write over this non transactionally or in suspend, we are in
188 * trouble because if we get a tm abort, the program counter and stack
189 * pointer will be back at the tbegin but our in memory stack won't be
190 * valid anymore.
191 *
192 * To avoid this, when taking a signal in an active transaction, we
193 * need to use the stack pointer from the checkpointed state, rather
194 * than the speculated state. This ensures that the signal context
195 * (written tm suspended) will be written below the stack required for
196 * the rollback. The transaction is aborted becuase of the treclaim,
197 * so any memory written between the tbegin and the signal will be
198 * rolled back anyway.
199 *
200 * For signals taken in non-TM or suspended mode, we use the
201 * normal/non-checkpointed stack pointer.
202 */
203
204#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
205 if (MSR_TM_ACTIVE(regs->msr)) {
206 tm_enable();
207 tm_reclaim(&current->thread, regs->msr, TM_CAUSE_SIGNAL);
208 if (MSR_TM_TRANSACTIONAL(regs->msr))
209 return current->thread.ckpt_regs.gpr[1];
210 }
211#endif
212 return regs->gpr[1];
213}
diff --git a/arch/powerpc/kernel/signal.h b/arch/powerpc/kernel/signal.h
index ec84c901ceab..c69b9aeb9f23 100644
--- a/arch/powerpc/kernel/signal.h
+++ b/arch/powerpc/kernel/signal.h
@@ -12,7 +12,7 @@
12 12
13extern void do_notify_resume(struct pt_regs *regs, unsigned long thread_info_flags); 13extern void do_notify_resume(struct pt_regs *regs, unsigned long thread_info_flags);
14 14
15extern void __user * get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, 15extern void __user * get_sigframe(struct k_sigaction *ka, unsigned long sp,
16 size_t frame_size, int is_32); 16 size_t frame_size, int is_32);
17 17
18extern int handle_signal32(unsigned long sig, struct k_sigaction *ka, 18extern int handle_signal32(unsigned long sig, struct k_sigaction *ka,
diff --git a/arch/powerpc/kernel/signal_32.c b/arch/powerpc/kernel/signal_32.c
index 95068bf569ad..201385c3a1ae 100644
--- a/arch/powerpc/kernel/signal_32.c
+++ b/arch/powerpc/kernel/signal_32.c
@@ -503,12 +503,6 @@ static int save_tm_user_regs(struct pt_regs *regs,
503{ 503{
504 unsigned long msr = regs->msr; 504 unsigned long msr = regs->msr;
505 505
506 /* tm_reclaim rolls back all reg states, updating thread.ckpt_regs,
507 * thread.transact_fpr[], thread.transact_vr[], etc.
508 */
509 tm_enable();
510 tm_reclaim(&current->thread, msr, TM_CAUSE_SIGNAL);
511
512 /* Make sure floating point registers are stored in regs */ 506 /* Make sure floating point registers are stored in regs */
513 flush_fp_to_thread(current); 507 flush_fp_to_thread(current);
514 508
@@ -965,7 +959,7 @@ int handle_rt_signal32(unsigned long sig, struct k_sigaction *ka,
965 959
966 /* Set up Signal Frame */ 960 /* Set up Signal Frame */
967 /* Put a Real Time Context onto stack */ 961 /* Put a Real Time Context onto stack */
968 rt_sf = get_sigframe(ka, regs, sizeof(*rt_sf), 1); 962 rt_sf = get_sigframe(ka, get_tm_stackpointer(regs), sizeof(*rt_sf), 1);
969 addr = rt_sf; 963 addr = rt_sf;
970 if (unlikely(rt_sf == NULL)) 964 if (unlikely(rt_sf == NULL))
971 goto badframe; 965 goto badframe;
@@ -1403,7 +1397,7 @@ int handle_signal32(unsigned long sig, struct k_sigaction *ka,
1403 unsigned long tramp; 1397 unsigned long tramp;
1404 1398
1405 /* Set up Signal Frame */ 1399 /* Set up Signal Frame */
1406 frame = get_sigframe(ka, regs, sizeof(*frame), 1); 1400 frame = get_sigframe(ka, get_tm_stackpointer(regs), sizeof(*frame), 1);
1407 if (unlikely(frame == NULL)) 1401 if (unlikely(frame == NULL))
1408 goto badframe; 1402 goto badframe;
1409 sc = (struct sigcontext __user *) &frame->sctx; 1403 sc = (struct sigcontext __user *) &frame->sctx;
diff --git a/arch/powerpc/kernel/signal_64.c b/arch/powerpc/kernel/signal_64.c
index c1794286098c..345947367ec0 100644
--- a/arch/powerpc/kernel/signal_64.c
+++ b/arch/powerpc/kernel/signal_64.c
@@ -154,11 +154,12 @@ static long setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs,
154 * As above, but Transactional Memory is in use, so deliver sigcontexts 154 * As above, but Transactional Memory is in use, so deliver sigcontexts
155 * containing checkpointed and transactional register states. 155 * containing checkpointed and transactional register states.
156 * 156 *
157 * To do this, we treclaim to gather both sets of registers and set up the 157 * To do this, we treclaim (done before entering here) to gather both sets of
158 * 'normal' sigcontext registers with rolled-back register values such that a 158 * registers and set up the 'normal' sigcontext registers with rolled-back
159 * simple signal handler sees a correct checkpointed register state. 159 * register values such that a simple signal handler sees a correct
160 * If interested, a TM-aware sighandler can examine the transactional registers 160 * checkpointed register state. If interested, a TM-aware sighandler can
161 * in the 2nd sigcontext to determine the real origin of the signal. 161 * examine the transactional registers in the 2nd sigcontext to determine the
162 * real origin of the signal.
162 */ 163 */
163static long setup_tm_sigcontexts(struct sigcontext __user *sc, 164static long setup_tm_sigcontexts(struct sigcontext __user *sc,
164 struct sigcontext __user *tm_sc, 165 struct sigcontext __user *tm_sc,
@@ -184,16 +185,6 @@ static long setup_tm_sigcontexts(struct sigcontext __user *sc,
184 185
185 BUG_ON(!MSR_TM_ACTIVE(regs->msr)); 186 BUG_ON(!MSR_TM_ACTIVE(regs->msr));
186 187
187 /* tm_reclaim rolls back all reg states, saving checkpointed (older)
188 * GPRs to thread.ckpt_regs and (if used) FPRs to (newer)
189 * thread.transact_fp and/or VRs to (newer) thread.transact_vr.
190 * THEN we save out FP/VRs, if necessary, to the checkpointed (older)
191 * thread.fr[]/vr[]s. The transactional (newer) GPRs are on the
192 * stack, in *regs.
193 */
194 tm_enable();
195 tm_reclaim(&current->thread, msr, TM_CAUSE_SIGNAL);
196
197 flush_fp_to_thread(current); 188 flush_fp_to_thread(current);
198 189
199#ifdef CONFIG_ALTIVEC 190#ifdef CONFIG_ALTIVEC
@@ -711,7 +702,7 @@ int handle_rt_signal64(int signr, struct k_sigaction *ka, siginfo_t *info,
711 unsigned long newsp = 0; 702 unsigned long newsp = 0;
712 long err = 0; 703 long err = 0;
713 704
714 frame = get_sigframe(ka, regs, sizeof(*frame), 0); 705 frame = get_sigframe(ka, get_tm_stackpointer(regs), sizeof(*frame), 0);
715 if (unlikely(frame == NULL)) 706 if (unlikely(frame == NULL))
716 goto badframe; 707 goto badframe;
717 708