Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2b3f8e87 authored by Michael Neuling's avatar Michael Neuling Committed by Benjamin Herrenschmidt
Browse files

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: default avatarMichael Neuling <mikey@neuling.org>
Cc: <stable@vger.kernel.org> # v3.9
Signed-off-by: default avatarBenjamin Herrenschmidt <benh@kernel.crashing.org>
parent b75c100e
Loading
Loading
Loading
Loading
+19 −0
Original line number Original line Diff line number Diff line
@@ -147,6 +147,25 @@ Example signal handler:
      fix_the_problem(ucp->dar);
      fix_the_problem(ucp->dar);
    }
    }


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.



Failure cause codes used by kernel
Failure cause codes used by kernel
==================================
==================================
+4 −9
Original line number Original line Diff line number Diff line
@@ -409,21 +409,16 @@ static inline void prefetchw(const void *x)
#endif
#endif


#ifdef CONFIG_PPC64
#ifdef CONFIG_PPC64
static inline unsigned long get_clean_sp(struct pt_regs *regs, int is_32)
static inline unsigned long get_clean_sp(unsigned long sp, int is_32)
{
{
	unsigned long sp;

	if (is_32)
	if (is_32)
		sp = regs->gpr[1] & 0x0ffffffffUL;
		return sp & 0x0ffffffffUL;
	else
		sp = regs->gpr[1];

	return sp;
	return sp;
}
}
#else
#else
static inline unsigned long get_clean_sp(struct pt_regs *regs, int is_32)
static inline unsigned long get_clean_sp(unsigned long sp, int is_32)
{
{
	return regs->gpr[1];
	return sp;
}
}
#endif
#endif


+3 −0
Original line number Original line Diff line number Diff line
@@ -3,5 +3,8 @@


#define __ARCH_HAS_SA_RESTORER
#define __ARCH_HAS_SA_RESTORER
#include <uapi/asm/signal.h>
#include <uapi/asm/signal.h>
#include <uapi/asm/ptrace.h>

extern unsigned long get_tm_stackpointer(struct pt_regs *regs);


#endif /* _ASM_POWERPC_SIGNAL_H */
#endif /* _ASM_POWERPC_SIGNAL_H */
+38 −2
Original line number Original line Diff line number Diff line
@@ -18,6 +18,7 @@
#include <asm/uaccess.h>
#include <asm/uaccess.h>
#include <asm/unistd.h>
#include <asm/unistd.h>
#include <asm/debug.h>
#include <asm/debug.h>
#include <asm/tm.h>


#include "signal.h"
#include "signal.h"


@@ -30,13 +31,13 @@ int show_unhandled_signals = 1;
/*
/*
 * Allocate space for the signal frame
 * Allocate space for the signal frame
 */
 */
void __user * get_sigframe(struct k_sigaction *ka, struct pt_regs *regs,
void __user * get_sigframe(struct k_sigaction *ka, unsigned long sp,
			   size_t frame_size, int is_32)
			   size_t frame_size, int is_32)
{
{
        unsigned long oldsp, newsp;
        unsigned long oldsp, newsp;


        /* Default to using normal stack */
        /* Default to using normal stack */
        oldsp = get_clean_sp(regs, is_32);
        oldsp = get_clean_sp(sp, is_32);


	/* Check for alt stack */
	/* Check for alt stack */
	if ((ka->sa.sa_flags & SA_ONSTACK) &&
	if ((ka->sa.sa_flags & SA_ONSTACK) &&
@@ -175,3 +176,38 @@ void do_notify_resume(struct pt_regs *regs, unsigned long thread_info_flags)


	user_enter();
	user_enter();
}
}

unsigned long get_tm_stackpointer(struct pt_regs *regs)
{
	/* 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.
	 */

#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
	if (MSR_TM_ACTIVE(regs->msr)) {
		tm_enable();
		tm_reclaim(&current->thread, regs->msr, TM_CAUSE_SIGNAL);
		if (MSR_TM_TRANSACTIONAL(regs->msr))
			return current->thread.ckpt_regs.gpr[1];
	}
#endif
	return regs->gpr[1];
}
+1 −1
Original line number Original line Diff line number Diff line
@@ -12,7 +12,7 @@


extern void do_notify_resume(struct pt_regs *regs, unsigned long thread_info_flags);
extern void do_notify_resume(struct pt_regs *regs, unsigned long thread_info_flags);


extern void __user * get_sigframe(struct k_sigaction *ka, struct pt_regs *regs,
extern void __user * get_sigframe(struct k_sigaction *ka, unsigned long sp,
				  size_t frame_size, int is_32);
				  size_t frame_size, int is_32);


extern int handle_signal32(unsigned long sig, struct k_sigaction *ka,
extern int handle_signal32(unsigned long sig, struct k_sigaction *ka,
Loading