[PATCH] ARM: Fix "external abort on non-linefetch" kernel panic caused by userspace

Russell King (Oracle) linux at armlinux.org.uk
Sat Jul 6 00:24:39 PDT 2024


On Sat, Jul 06, 2024 at 11:20:05AM +0800, wanglinhui wrote:
> 0x16800000 is a peripheral physical address that supports only
> 4-byte-aligned access.
> 
> Use /dev/mem to enable the user space to access 0x16800000. Then userspace
> unexpectedly tried to read four bytes from 0x16800001 (actually access
> its virtual address), which caused the kernel to trigger an
> "external abort on non-linefetch" panic:
> 
>   Unhandled fault: external abort on non-linefetch (0x1018) at 0x0100129b
>   [0100129b] *pgd=85038831, *pte=16801703, *ppte=16801e33
>   Internal error: : 1018 [#1] SMP ARM
>   ...
>   CPU: 2 PID: xxxx Comm: xxxx Tainted: G           O      5.10.0 #1
>   Hardware name: Hisilicon A9
>   PC is at do_alignment_ldrstr+0xb8/0x100
>   LR is at 0xc1f203fc
>   psr: 200f0313
>   sp : c7081ed4  ip : 00000008  fp : 00000011
>   r10: b42250c8  r9 : c7081f0c  r8 : c7081fb0
>   r7 : 0100129b  r6 : 00000004  r5 : 00000000  r4 : e5908000
>   r3 : 00000000  r2 : c7081f0c  r1 : 200f0210  r0 : 0100129b
>   Flags: nzCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment user
>   Control: 1ac5387d  Table: 82c3c04a  DAC: 55555555
>   Process LcnNCoreTask (pid: 4049, stack limit = 0x14066b0e)
>   Call trace:
>     do_alignment_ldrstr
>     --do_alignment
>     ----do_DataAbort
>     ------__dabt_usr
> 
> It triggers a data abort exception twice. The first time occurs when
> an unaligned address is accessed in user mode. The second time occurs
> when the peripheral address is actually accessed in kernel mode,
> and it crashes the kernel. However, the code location for the second
> data abort is as follows:
> 
>   ```
>   #define __get8_unaligned_check(ins, val, addr, err) \
>   	__asm__(\
>    ARM("1: "ins" %1, [%2], #1\n") \ <-- Second data abort is triggered here
>    THUMB("1: "ins" %1, [%2]\n") \
>    THUMB(" add %2, %2, #1\n") \
>   	"2:\n" \
>   	" .pushsection .text.fixup,\"ax\"\n" \
>   ```
> 
> It is an exception table entry that can be fixed up.
> 
> There is another test that indicates that
> "external abort on non-linefetch" needs to be fixed up.
> 
> Similarly, use /dev/mem to map 0x16800000 to the user space.
> Pass 0x16800001 (actually passes its virtual address) to the
> kernel via the write() system call and write 1 byte.
> It also causes the kernel to trigger an
> "external abort on non-linefetch" panic:
> 
>   Unhandled fault: external abort on non-linefetch (0x1018) at 0xb6f95000
>   [b6f95000] *pgd=83fb6831, *pte=16800783, *ppte=16800e33
>   Internal error: : 1018 [#1] SMP ARM
>   ...
>   CPU: 1 PID: xxxx Comm: xxxx Tainted: G           O      5.10.0 #1
>   Hardware name: Hisilicon A9
>   PC is at __get_user_1+0x14/0x20
>   LR is at iov_iter_fault_in_readable+0x7c/0x198
>   psr: 800b0213
>   sp : c195be18  ip : 00000001  fp : c35a2478
>   r10: c06b5260  r9 : 00000000  r8 : c356fee0
>   r7 : ffffe000  r6 : b6f95000  r5 : 00000001  r4 : c195bf10
>   r3 : b6f95000  r2 : f7f95000  r1 : beffffff  r0 : b6f95000
>   Call trace looks like:
>     __get_user_1
>     --iov_iter_fault_in_readable
>     ----generic_perform_write
>     ------__generic_file_write_iter
>     --------generic_file_write_iter
> 
> The location of the instruction that triggers the data abort
> is as follows:
>   ```
>   ENTRY(__get_user_1)
>   	check_uaccess r0, 1, r1, r2, __get_user_bad
>   1: TUSER(ldrb) r2, [r0] <-- Data abort is triggered here
>   	mov r0, #0
>   	ret lr
>   ENDPROC(__get_user_1)
>   _ASM_NOKPROBE(__get_user_1)
>   ```
> It is also an exception table entry that can be fixed up.
> 
> Address passed in from user space should not crash the kernel.
> Therefore, fixup_exception() is added to fix up such exception.

NAK because:

1) you're using /dev/mem which requires privileges - you're holding
   the gun, pointing it at your foot.

2) you're performing an unaligned access to a device which is
   architecturally not permitted - you're pulling the trigger.

It's not surprising that the result is you've shot yourself in the
foot!

If you access /dev/mem, then you need to know what you're doing and
you must access it according to the requirements of the memory space
you are accessing, otherwise undefined behaviour will occur - not
only architecturally, but also by the kernel.

-- 
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTP is here! 80Mbps down 10Mbps up. Decent connectivity at last!



More information about the linux-arm-kernel mailing list