Commit 67e15fa5 authored by Russell King's avatar Russell King
Browse files

ARM: mm: fix alignment handler faults under memory pressure



When the system has high memory pressure, the page containing the
instruction may be paged out.  Using probe_kernel_address() means that
if the page is swapped out, the resulting page fault will not be
handled because page faults are disabled by this function.

Use get_user() to read the instruction instead.

Reported-by: default avatarJing Xiangfeng <jingxiangfeng@huawei.com>
Fixes: b255188f ("ARM: fix scheduling while atomic warning in alignment handling code")
Signed-off-by: default avatarRussell King <rmk+kernel@armlinux.org.uk>
parent 4d856f72
Loading
Loading
Loading
Loading
+36 −8
Original line number Diff line number Diff line
@@ -767,6 +767,36 @@ do_alignment_t32_to_handler(unsigned long *pinstr, struct pt_regs *regs,
	return NULL;
}

static int alignment_get_arm(struct pt_regs *regs, u32 *ip, unsigned long *inst)
{
	u32 instr = 0;
	int fault;

	if (user_mode(regs))
		fault = get_user(instr, ip);
	else
		fault = probe_kernel_address(ip, instr);

	*inst = __mem_to_opcode_arm(instr);

	return fault;
}

static int alignment_get_thumb(struct pt_regs *regs, u16 *ip, u16 *inst)
{
	u16 instr = 0;
	int fault;

	if (user_mode(regs))
		fault = get_user(instr, ip);
	else
		fault = probe_kernel_address(ip, instr);

	*inst = __mem_to_opcode_thumb16(instr);

	return fault;
}

static int
do_alignment(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
{
@@ -774,10 +804,10 @@ do_alignment(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
	unsigned long instr = 0, instrptr;
	int (*handler)(unsigned long addr, unsigned long instr, struct pt_regs *regs);
	unsigned int type;
	unsigned int fault;
	u16 tinstr = 0;
	int isize = 4;
	int thumb2_32b = 0;
	int fault;

	if (interrupts_enabled(regs))
		local_irq_enable();
@@ -786,15 +816,14 @@ do_alignment(unsigned long addr, unsigned int fsr, struct pt_regs *regs)

	if (thumb_mode(regs)) {
		u16 *ptr = (u16 *)(instrptr & ~1);
		fault = probe_kernel_address(ptr, tinstr);
		tinstr = __mem_to_opcode_thumb16(tinstr);

		fault = alignment_get_thumb(regs, ptr, &tinstr);
		if (!fault) {
			if (cpu_architecture() >= CPU_ARCH_ARMv7 &&
			    IS_T32(tinstr)) {
				/* Thumb-2 32-bit */
				u16 tinst2 = 0;
				fault = probe_kernel_address(ptr + 1, tinst2);
				tinst2 = __mem_to_opcode_thumb16(tinst2);
				u16 tinst2;
				fault = alignment_get_thumb(regs, ptr + 1, &tinst2);
				instr = __opcode_thumb32_compose(tinstr, tinst2);
				thumb2_32b = 1;
			} else {
@@ -803,8 +832,7 @@ do_alignment(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
			}
		}
	} else {
		fault = probe_kernel_address((void *)instrptr, instr);
		instr = __mem_to_opcode_arm(instr);
		fault = alignment_get_arm(regs, (void *)instrptr, &instr);
	}

	if (fault) {