mirror of
https://github.com/raspberrypi/linux.git
synced 2025-12-06 01:49:46 +00:00
parisc: Avoid crash due to unaligned access in unwinder
Guenter Roeck reported this kernel crash on his emulated B160L machine: Starting network: udhcpc: started, v1.36.1 Backtrace: [<104320d4>] unwind_once+0x1c/0x5c [<10434a00>] walk_stackframe.isra.0+0x74/0xb8 [<10434a6c>] arch_stack_walk+0x28/0x38 [<104e5efc>] stack_trace_save+0x48/0x5c [<105d1bdc>] set_track_prepare+0x44/0x6c [<105d9c80>] ___slab_alloc+0xfc4/0x1024 [<105d9d38>] __slab_alloc.isra.0+0x58/0x90 [<105dc80c>] kmem_cache_alloc_noprof+0x2ac/0x4a0 [<105b8e54>] __anon_vma_prepare+0x60/0x280 [<105a823c>] __vmf_anon_prepare+0x68/0x94 [<105a8b34>] do_wp_page+0x8cc/0xf10 [<105aad88>] handle_mm_fault+0x6c0/0xf08 [<10425568>] do_page_fault+0x110/0x440 [<10427938>] handle_interruption+0x184/0x748 [<11178398>] schedule+0x4c/0x190 BUG: spinlock recursion on CPU#0, ifconfig/2420 lock: terminate_lock.2+0x0/0x1c, .magic: dead4ead, .owner: ifconfig/2420, .owner_cpu: 0 While creating the stack trace, the unwinder uses the stack pointer to guess the previous frame to read the previous stack pointer from memory. The crash happens, because the unwinder tries to read from unaligned memory and as such triggers the unalignment trap handler which then leads to the spinlock recursion and finally to a deadlock. Fix it by checking the alignment before accessing the memory. Reported-by: Guenter Roeck <linux@roeck-us.net> Signed-off-by: Helge Deller <deller@gmx.de> Tested-by: Guenter Roeck <linux@roeck-us.net> Cc: stable@vger.kernel.org # v6.12+
This commit is contained in:
@@ -35,6 +35,8 @@
|
||||
|
||||
#define KERNEL_START (KERNEL_BINARY_TEXT_START)
|
||||
|
||||
#define ALIGNMENT_OK(ptr, type) (((ptr) & (sizeof(type) - 1)) == 0)
|
||||
|
||||
extern struct unwind_table_entry __start___unwind[];
|
||||
extern struct unwind_table_entry __stop___unwind[];
|
||||
|
||||
@@ -257,12 +259,15 @@ static int unwind_special(struct unwind_frame_info *info, unsigned long pc, int
|
||||
if (pc_is_kernel_fn(pc, _switch_to) ||
|
||||
pc == (unsigned long)&_switch_to_ret) {
|
||||
info->prev_sp = info->sp - CALLEE_SAVE_FRAME_SIZE;
|
||||
info->prev_ip = *(unsigned long *)(info->prev_sp - RP_OFFSET);
|
||||
if (ALIGNMENT_OK(info->prev_sp, long))
|
||||
info->prev_ip = *(unsigned long *)(info->prev_sp - RP_OFFSET);
|
||||
else
|
||||
info->prev_ip = info->prev_sp = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_IRQSTACKS
|
||||
if (pc == (unsigned long)&_call_on_stack) {
|
||||
if (pc == (unsigned long)&_call_on_stack && ALIGNMENT_OK(info->sp, long)) {
|
||||
info->prev_sp = *(unsigned long *)(info->sp - FRAME_SIZE - REG_SZ);
|
||||
info->prev_ip = *(unsigned long *)(info->sp - FRAME_SIZE - RP_OFFSET);
|
||||
return 1;
|
||||
@@ -370,8 +375,10 @@ static void unwind_frame_regs(struct unwind_frame_info *info)
|
||||
info->prev_sp = info->sp - frame_size;
|
||||
if (e->Millicode)
|
||||
info->rp = info->r31;
|
||||
else if (rpoffset)
|
||||
else if (rpoffset && ALIGNMENT_OK(info->prev_sp, long))
|
||||
info->rp = *(unsigned long *)(info->prev_sp - rpoffset);
|
||||
else
|
||||
info->rp = 0;
|
||||
info->prev_ip = info->rp;
|
||||
info->rp = 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user