Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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+

+10 -3
+10 -3
arch/parisc/kernel/unwind.c
··· 35 35 36 36 #define KERNEL_START (KERNEL_BINARY_TEXT_START) 37 37 38 + #define ALIGNMENT_OK(ptr, type) (((ptr) & (sizeof(type) - 1)) == 0) 39 + 38 40 extern struct unwind_table_entry __start___unwind[]; 39 41 extern struct unwind_table_entry __stop___unwind[]; 40 42 ··· 259 257 if (pc_is_kernel_fn(pc, _switch_to) || 260 258 pc == (unsigned long)&_switch_to_ret) { 261 259 info->prev_sp = info->sp - CALLEE_SAVE_FRAME_SIZE; 262 - info->prev_ip = *(unsigned long *)(info->prev_sp - RP_OFFSET); 260 + if (ALIGNMENT_OK(info->prev_sp, long)) 261 + info->prev_ip = *(unsigned long *)(info->prev_sp - RP_OFFSET); 262 + else 263 + info->prev_ip = info->prev_sp = 0; 263 264 return 1; 264 265 } 265 266 266 267 #ifdef CONFIG_IRQSTACKS 267 - if (pc == (unsigned long)&_call_on_stack) { 268 + if (pc == (unsigned long)&_call_on_stack && ALIGNMENT_OK(info->sp, long)) { 268 269 info->prev_sp = *(unsigned long *)(info->sp - FRAME_SIZE - REG_SZ); 269 270 info->prev_ip = *(unsigned long *)(info->sp - FRAME_SIZE - RP_OFFSET); 270 271 return 1; ··· 375 370 info->prev_sp = info->sp - frame_size; 376 371 if (e->Millicode) 377 372 info->rp = info->r31; 378 - else if (rpoffset) 373 + else if (rpoffset && ALIGNMENT_OK(info->prev_sp, long)) 379 374 info->rp = *(unsigned long *)(info->prev_sp - rpoffset); 375 + else 376 + info->rp = 0; 380 377 info->prev_ip = info->rp; 381 378 info->rp = 0; 382 379 }