Skip to content

Commit fd9f30d

Browse files
committed
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 <[email protected]> Signed-off-by: Helge Deller <[email protected]> Tested-by: Guenter Roeck <[email protected]> Cc: [email protected] # v6.12+
1 parent 6146a0f commit fd9f30d

1 file changed

Lines changed: 10 additions & 3 deletions

File tree

arch/parisc/kernel/unwind.c

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@
3535

3636
#define KERNEL_START (KERNEL_BINARY_TEXT_START)
3737

38+
#define ALIGNMENT_OK(ptr, type) (((ptr) & (sizeof(type) - 1)) == 0)
39+
3840
extern struct unwind_table_entry __start___unwind[];
3941
extern struct unwind_table_entry __stop___unwind[];
4042

@@ -257,12 +259,15 @@ static int unwind_special(struct unwind_frame_info *info, unsigned long pc, int
257259
if (pc_is_kernel_fn(pc, _switch_to) ||
258260
pc == (unsigned long)&_switch_to_ret) {
259261
info->prev_sp = info->sp - CALLEE_SAVE_FRAME_SIZE;
260-
info->prev_ip = *(unsigned long *)(info->prev_sp - RP_OFFSET);
262+
if (ALIGNMENT_OK(info->prev_sp, long))
263+
info->prev_ip = *(unsigned long *)(info->prev_sp - RP_OFFSET);
264+
else
265+
info->prev_ip = info->prev_sp = 0;
261266
return 1;
262267
}
263268

264269
#ifdef CONFIG_IRQSTACKS
265-
if (pc == (unsigned long)&_call_on_stack) {
270+
if (pc == (unsigned long)&_call_on_stack && ALIGNMENT_OK(info->sp, long)) {
266271
info->prev_sp = *(unsigned long *)(info->sp - FRAME_SIZE - REG_SZ);
267272
info->prev_ip = *(unsigned long *)(info->sp - FRAME_SIZE - RP_OFFSET);
268273
return 1;
@@ -370,8 +375,10 @@ static void unwind_frame_regs(struct unwind_frame_info *info)
370375
info->prev_sp = info->sp - frame_size;
371376
if (e->Millicode)
372377
info->rp = info->r31;
373-
else if (rpoffset)
378+
else if (rpoffset && ALIGNMENT_OK(info->prev_sp, long))
374379
info->rp = *(unsigned long *)(info->prev_sp - rpoffset);
380+
else
381+
info->rp = 0;
375382
info->prev_ip = info->rp;
376383
info->rp = 0;
377384
}

0 commit comments

Comments
 (0)