Commit 1a133985 authored by Mao Minkai's avatar Mao Minkai Committed by guzitao
Browse files

sw64: fix incorrect gp after uretprobe triggered

Sunway inclusion
category: feature
bugzilla: https://gitee.com/openeuler/kernel/issues/I56OLG



--------------------------------

SW64 use r26 to calculate gp after function return, so r26 needs to be
restored when uretprobe trampoline is hit.

Since uretprobe is handled in generic code, we will modify r26 before we
return to user space.

Signed-off-by: default avatarMao Minkai <maominkai@wxiat.com>
Reviewed-by: default avatarHe Sheng <hesheng@wxiat.com>
Signed-off-by: default avatarGu Zitao <guzitao@wxiat.com>
parent 6c0ddc05
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -35,4 +35,6 @@ struct arch_uprobe_task {
	unsigned long saved_trap_nr;
};

extern void sw64_fix_uretprobe(struct pt_regs *regs);

#endif /* _ASM_SW64_UPROBES_H */
+4 −2
Original line number Diff line number Diff line
@@ -267,12 +267,14 @@ do_entIF(unsigned long inst_type, struct pt_regs *regs)
		case BREAK_KPROBE_SS:
			if (notify_die(DIE_SSTEPBP, "single_step", regs, 0, 0, SIGTRAP) == NOTIFY_STOP)
				return;
#ifdef CONFIG_UPROBES
		case UPROBE_BRK_UPROBE:
			if (notify_die(DIE_UPROBE, "uprobe", regs, 0, 0, SIGTRAP) == NOTIFY_STOP)
				return;
				return sw64_fix_uretprobe(regs);
		case UPROBE_BRK_UPROBE_XOL:
			if (notify_die(DIE_UPROBE_XOL, "uprobe_xol", regs, 0, 0, SIGTRAP) == NOTIFY_STOP)
				return;
				return sw64_fix_uretprobe(regs);
#endif
		}

		if (user_mode(regs))
+38 −0
Original line number Diff line number Diff line
@@ -151,3 +151,41 @@ bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs)
{
	return 0;
}

/*
 * struct xol_area and get_trampoline_vaddr() are copied from
 * kernel/events/uprobes.c to avoid modifying arch-independent
 * code.
 */
struct xol_area {
	wait_queue_head_t		wq;
	atomic_t			slot_count;
	unsigned long			*bitmap;
	struct vm_special_mapping	xol_mapping;
	struct page			*pages[2];
	unsigned long			vaddr;
};

static unsigned long get_trampoline_vaddr(void)
{
	struct xol_area *area;
	unsigned long trampoline_vaddr = -1;

	area = READ_ONCE(current->mm->uprobes_state.xol_area);
	if (area)
		trampoline_vaddr = area->vaddr;

	return trampoline_vaddr;
}

void sw64_fix_uretprobe(struct pt_regs *regs)
{
	unsigned long bp_vaddr;

	bp_vaddr = uprobe_get_swbp_addr(regs);
	/*
	 * regs->pc has been changed to orig_ret_vaddr in handle_trampoline().
	 */
	if (bp_vaddr == get_trampoline_vaddr())
		regs->r26 = regs->pc;
}