Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Michael Neuling and committed by
Benjamin Herrenschmidt
2b3f8e87 b75c100e

+74 -36
+19
Documentation/powerpc/transactional_memory.txt
··· 147 147 fix_the_problem(ucp->dar); 148 148 } 149 149 150 + When in an active transaction that takes a signal, we need to be careful with 151 + the stack. It's possible that the stack has moved back up after the tbegin. 152 + The obvious case here is when the tbegin is called inside a function that 153 + returns before a tend. In this case, the stack is part of the checkpointed 154 + transactional memory state. If we write over this non transactionally or in 155 + suspend, we are in trouble because if we get a tm abort, the program counter and 156 + stack pointer will be back at the tbegin but our in memory stack won't be valid 157 + anymore. 158 + 159 + To avoid this, when taking a signal in an active transaction, we need to use 160 + the stack pointer from the checkpointed state, rather than the speculated 161 + state. This ensures that the signal context (written tm suspended) will be 162 + written below the stack required for the rollback. The transaction is aborted 163 + becuase of the treclaim, so any memory written between the tbegin and the 164 + signal will be rolled back anyway. 165 + 166 + For signals taken in non-TM or suspended mode, we use the 167 + normal/non-checkpointed stack pointer. 168 + 150 169 151 170 Failure cause codes used by kernel 152 171 ==================================
+4 -9
arch/powerpc/include/asm/processor.h
··· 409 409 #endif 410 410 411 411 #ifdef CONFIG_PPC64 412 - static inline unsigned long get_clean_sp(struct pt_regs *regs, int is_32) 412 + static inline unsigned long get_clean_sp(unsigned long sp, int is_32) 413 413 { 414 - unsigned long sp; 415 - 416 414 if (is_32) 417 - sp = regs->gpr[1] & 0x0ffffffffUL; 418 - else 419 - sp = regs->gpr[1]; 420 - 415 + return sp & 0x0ffffffffUL; 421 416 return sp; 422 417 } 423 418 #else 424 - static inline unsigned long get_clean_sp(struct pt_regs *regs, int is_32) 419 + static inline unsigned long get_clean_sp(unsigned long sp, int is_32) 425 420 { 426 - return regs->gpr[1]; 421 + return sp; 427 422 } 428 423 #endif 429 424
+3
arch/powerpc/include/asm/signal.h
··· 3 3 4 4 #define __ARCH_HAS_SA_RESTORER 5 5 #include <uapi/asm/signal.h> 6 + #include <uapi/asm/ptrace.h> 7 + 8 + extern unsigned long get_tm_stackpointer(struct pt_regs *regs); 6 9 7 10 #endif /* _ASM_POWERPC_SIGNAL_H */
+38 -2
arch/powerpc/kernel/signal.c
··· 18 18 #include <asm/uaccess.h> 19 19 #include <asm/unistd.h> 20 20 #include <asm/debug.h> 21 + #include <asm/tm.h> 21 22 22 23 #include "signal.h" 23 24 ··· 31 30 /* 32 31 * Allocate space for the signal frame 33 32 */ 34 - void __user * get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, 33 + void __user * get_sigframe(struct k_sigaction *ka, unsigned long sp, 35 34 size_t frame_size, int is_32) 36 35 { 37 36 unsigned long oldsp, newsp; 38 37 39 38 /* Default to using normal stack */ 40 - oldsp = get_clean_sp(regs, is_32); 39 + oldsp = get_clean_sp(sp, is_32); 41 40 42 41 /* Check for alt stack */ 43 42 if ((ka->sa.sa_flags & SA_ONSTACK) && ··· 175 174 } 176 175 177 176 user_enter(); 177 + } 178 + 179 + unsigned long get_tm_stackpointer(struct pt_regs *regs) 180 + { 181 + /* When in an active transaction that takes a signal, we need to be 182 + * careful with the stack. It's possible that the stack has moved back 183 + * up after the tbegin. The obvious case here is when the tbegin is 184 + * called inside a function that returns before a tend. In this case, 185 + * the stack is part of the checkpointed transactional memory state. 186 + * If we write over this non transactionally or in suspend, we are in 187 + * trouble because if we get a tm abort, the program counter and stack 188 + * pointer will be back at the tbegin but our in memory stack won't be 189 + * valid anymore. 190 + * 191 + * To avoid this, when taking a signal in an active transaction, we 192 + * need to use the stack pointer from the checkpointed state, rather 193 + * than the speculated state. This ensures that the signal context 194 + * (written tm suspended) will be written below the stack required for 195 + * the rollback. The transaction is aborted becuase of the treclaim, 196 + * so any memory written between the tbegin and the signal will be 197 + * rolled back anyway. 198 + * 199 + * For signals taken in non-TM or suspended mode, we use the 200 + * normal/non-checkpointed stack pointer. 201 + */ 202 + 203 + #ifdef CONFIG_PPC_TRANSACTIONAL_MEM 204 + if (MSR_TM_ACTIVE(regs->msr)) { 205 + tm_enable(); 206 + tm_reclaim(&current->thread, regs->msr, TM_CAUSE_SIGNAL); 207 + if (MSR_TM_TRANSACTIONAL(regs->msr)) 208 + return current->thread.ckpt_regs.gpr[1]; 209 + } 210 + #endif 211 + return regs->gpr[1]; 178 212 }
+1 -1
arch/powerpc/kernel/signal.h
··· 12 12 13 13 extern void do_notify_resume(struct pt_regs *regs, unsigned long thread_info_flags); 14 14 15 - extern void __user * get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, 15 + extern void __user * get_sigframe(struct k_sigaction *ka, unsigned long sp, 16 16 size_t frame_size, int is_32); 17 17 18 18 extern int handle_signal32(unsigned long sig, struct k_sigaction *ka,
+2 -8
arch/powerpc/kernel/signal_32.c
··· 503 503 { 504 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 506 /* Make sure floating point registers are stored in regs */ 513 507 flush_fp_to_thread(current); 514 508 ··· 959 965 960 966 /* Set up Signal Frame */ 961 967 /* Put a Real Time Context onto stack */ 962 - rt_sf = get_sigframe(ka, regs, sizeof(*rt_sf), 1); 968 + rt_sf = get_sigframe(ka, get_tm_stackpointer(regs), sizeof(*rt_sf), 1); 963 969 addr = rt_sf; 964 970 if (unlikely(rt_sf == NULL)) 965 971 goto badframe; ··· 1397 1403 unsigned long tramp; 1398 1404 1399 1405 /* Set up Signal Frame */ 1400 - frame = get_sigframe(ka, regs, sizeof(*frame), 1); 1406 + frame = get_sigframe(ka, get_tm_stackpointer(regs), sizeof(*frame), 1); 1401 1407 if (unlikely(frame == NULL)) 1402 1408 goto badframe; 1403 1409 sc = (struct sigcontext __user *) &frame->sctx;
+7 -16
arch/powerpc/kernel/signal_64.c
··· 154 154 * As above, but Transactional Memory is in use, so deliver sigcontexts 155 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 158 - * 'normal' sigcontext registers with rolled-back register values such that a 159 - * simple signal handler sees a correct checkpointed register state. 160 - * If interested, a TM-aware sighandler can examine the transactional registers 161 - * in the 2nd sigcontext to determine the real origin of the signal. 157 + * To do this, we treclaim (done before entering here) to gather both sets of 158 + * registers and set up the 'normal' sigcontext registers with rolled-back 159 + * register values such that a simple signal handler sees a correct 160 + * checkpointed register state. If interested, a TM-aware sighandler can 161 + * examine the transactional registers in the 2nd sigcontext to determine the 162 + * real origin of the signal. 162 163 */ 163 164 static long setup_tm_sigcontexts(struct sigcontext __user *sc, 164 165 struct sigcontext __user *tm_sc, ··· 184 183 long err = 0; 185 184 186 185 BUG_ON(!MSR_TM_ACTIVE(regs->msr)); 187 - 188 - /* tm_reclaim rolls back all reg states, saving checkpointed (older) 189 - * GPRs to thread.ckpt_regs and (if used) FPRs to (newer) 190 - * thread.transact_fp and/or VRs to (newer) thread.transact_vr. 191 - * THEN we save out FP/VRs, if necessary, to the checkpointed (older) 192 - * thread.fr[]/vr[]s. The transactional (newer) GPRs are on the 193 - * stack, in *regs. 194 - */ 195 - tm_enable(); 196 - tm_reclaim(&current->thread, msr, TM_CAUSE_SIGNAL); 197 186 198 187 flush_fp_to_thread(current); 199 188 ··· 702 711 unsigned long newsp = 0; 703 712 long err = 0; 704 713 705 - frame = get_sigframe(ka, regs, sizeof(*frame), 0); 714 + frame = get_sigframe(ka, get_tm_stackpointer(regs), sizeof(*frame), 0); 706 715 if (unlikely(frame == NULL)) 707 716 goto badframe; 708 717