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

Commit e04a28d4 authored by Will Deacon's avatar Will Deacon
Browse files

arm64: debug: re-enable irqs before sending breakpoint SIGTRAP



force_sig_info can sleep under an -rt kernel, so attempting to send a
breakpoint SIGTRAP with interrupts disabled yields the following BUG:

  BUG: sleeping function called from invalid context at
  /kernel-source/kernel/locking/rtmutex.c:917
  in_atomic(): 0, irqs_disabled(): 128, pid: 551, name: test.sh
  CPU: 5 PID: 551 Comm: test.sh Not tainted 4.1.13-rt13 #7
  Hardware name: Freescale Layerscape 2085a RDB Board (DT)
  Call trace:
	 dump_backtrace+0x0/0x128
	 show_stack+0x24/0x30
	 dump_stack+0x80/0xa0
	 ___might_sleep+0x128/0x1a0
	 rt_spin_lock+0x2c/0x40
	 force_sig_info+0xcc/0x210
	 brk_handler.part.2+0x6c/0x80
	 brk_handler+0xd8/0xe8
	 do_debug_exception+0x58/0xb8

This patch fixes the problem by ensuring that interrupts are enabled
prior to sending the SIGTRAP if they were already enabled in the user
context.

Reported-by: default avatarYang Shi <yang.shi@linaro.org>
Signed-off-by: default avatarWill Deacon <will.deacon@arm.com>
parent bcaf669b
Loading
Loading
Loading
Loading
+22 −26
Original line number Diff line number Diff line
@@ -226,11 +226,28 @@ static int call_step_hook(struct pt_regs *regs, unsigned int esr)
	return retval;
}

static void send_user_sigtrap(int si_code)
{
	struct pt_regs *regs = current_pt_regs();
	siginfo_t info = {
		.si_signo	= SIGTRAP,
		.si_errno	= 0,
		.si_code	= si_code,
		.si_addr	= (void __user *)instruction_pointer(regs),
	};

	if (WARN_ON(!user_mode(regs)))
		return;

	if (interrupts_enabled(regs))
		local_irq_enable();

	force_sig_info(SIGTRAP, &info, current);
}

static int single_step_handler(unsigned long addr, unsigned int esr,
			       struct pt_regs *regs)
{
	siginfo_t info;

	/*
	 * If we are stepping a pending breakpoint, call the hw_breakpoint
	 * handler first.
@@ -239,11 +256,7 @@ static int single_step_handler(unsigned long addr, unsigned int esr,
		return 0;

	if (user_mode(regs)) {
		info.si_signo = SIGTRAP;
		info.si_errno = 0;
		info.si_code  = TRAP_HWBKPT;
		info.si_addr  = (void __user *)instruction_pointer(regs);
		force_sig_info(SIGTRAP, &info, current);
		send_user_sigtrap(TRAP_HWBKPT);

		/*
		 * ptrace will disable single step unless explicitly
@@ -307,17 +320,8 @@ static int call_break_hook(struct pt_regs *regs, unsigned int esr)
static int brk_handler(unsigned long addr, unsigned int esr,
		       struct pt_regs *regs)
{
	siginfo_t info;

	if (user_mode(regs)) {
		info = (siginfo_t) {
			.si_signo = SIGTRAP,
			.si_errno = 0,
			.si_code  = TRAP_BRKPT,
			.si_addr  = (void __user *)instruction_pointer(regs),
		};

		force_sig_info(SIGTRAP, &info, current);
		send_user_sigtrap(TRAP_BRKPT);
	} else if (call_break_hook(regs, esr) != DBG_HOOK_HANDLED) {
		pr_warning("Unexpected kernel BRK exception at EL1\n");
		return -EFAULT;
@@ -328,7 +332,6 @@ static int brk_handler(unsigned long addr, unsigned int esr,

int aarch32_break_handler(struct pt_regs *regs)
{
	siginfo_t info;
	u32 arm_instr;
	u16 thumb_instr;
	bool bp = false;
@@ -359,14 +362,7 @@ int aarch32_break_handler(struct pt_regs *regs)
	if (!bp)
		return -EFAULT;

	info = (siginfo_t) {
		.si_signo = SIGTRAP,
		.si_errno = 0,
		.si_code  = TRAP_BRKPT,
		.si_addr  = pc,
	};

	force_sig_info(SIGTRAP, &info, current);
	send_user_sigtrap(TRAP_BRKPT);
	return 0;
}