Skip to content

Commit 44ec5f4

Browse files
Russell KingLee Jones
authored andcommitted
ARM: mm: fix alignment handler faults under memory pressure
[ Upstream commit 67e15fa ] 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: Jing Xiangfeng <jingxiangfeng@huawei.com> Fixes: b255188 ("ARM: fix scheduling while atomic warning in alignment handling code") Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> Signed-off-by: Sasha Levin <sashal@kernel.org> Signed-off-by: Lee Jones <lee.jones@linaro.org> Change-Id: I189f5a65c949f6bd4785b5388175468657b2bb2b
1 parent b8f9fa1 commit 44ec5f4

1 file changed

Lines changed: 36 additions & 8 deletions

File tree

arch/arm/mm/alignment.c

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -767,17 +767,47 @@ do_alignment_t32_to_handler(unsigned long *pinstr, struct pt_regs *regs,
767767
return NULL;
768768
}
769769

770+
static int alignment_get_arm(struct pt_regs *regs, u32 *ip, unsigned long *inst)
771+
{
772+
u32 instr = 0;
773+
int fault;
774+
775+
if (user_mode(regs))
776+
fault = get_user(instr, ip);
777+
else
778+
fault = probe_kernel_address(ip, instr);
779+
780+
*inst = __mem_to_opcode_arm(instr);
781+
782+
return fault;
783+
}
784+
785+
static int alignment_get_thumb(struct pt_regs *regs, u16 *ip, u16 *inst)
786+
{
787+
u16 instr = 0;
788+
int fault;
789+
790+
if (user_mode(regs))
791+
fault = get_user(instr, ip);
792+
else
793+
fault = probe_kernel_address(ip, instr);
794+
795+
*inst = __mem_to_opcode_thumb16(instr);
796+
797+
return fault;
798+
}
799+
770800
static int
771801
do_alignment(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
772802
{
773803
union offset_union uninitialized_var(offset);
774804
unsigned long instr = 0, instrptr;
775805
int (*handler)(unsigned long addr, unsigned long instr, struct pt_regs *regs);
776806
unsigned int type;
777-
unsigned int fault;
778807
u16 tinstr = 0;
779808
int isize = 4;
780809
int thumb2_32b = 0;
810+
int fault;
781811

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

787817
if (thumb_mode(regs)) {
788818
u16 *ptr = (u16 *)(instrptr & ~1);
789-
fault = probe_kernel_address(ptr, tinstr);
790-
tinstr = __mem_to_opcode_thumb16(tinstr);
819+
820+
fault = alignment_get_thumb(regs, ptr, &tinstr);
791821
if (!fault) {
792822
if (cpu_architecture() >= CPU_ARCH_ARMv7 &&
793823
IS_T32(tinstr)) {
794824
/* Thumb-2 32-bit */
795-
u16 tinst2 = 0;
796-
fault = probe_kernel_address(ptr + 1, tinst2);
797-
tinst2 = __mem_to_opcode_thumb16(tinst2);
825+
u16 tinst2;
826+
fault = alignment_get_thumb(regs, ptr + 1, &tinst2);
798827
instr = __opcode_thumb32_compose(tinstr, tinst2);
799828
thumb2_32b = 1;
800829
} else {
@@ -803,8 +832,7 @@ do_alignment(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
803832
}
804833
}
805834
} else {
806-
fault = probe_kernel_address(instrptr, instr);
807-
instr = __mem_to_opcode_arm(instr);
835+
fault = alignment_get_arm(regs, (void *)instrptr, &instr);
808836
}
809837

810838
if (fault) {

0 commit comments

Comments
 (0)