]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/blob - queue-5.0/x86-unwind-handle-null-pointer-calls-better-in-frame-unwinder.patch
Linux 4.19.32
[thirdparty/kernel/stable-queue.git] / queue-5.0 / x86-unwind-handle-null-pointer-calls-better-in-frame-unwinder.patch
1 From f4f34e1b82eb4219d8eaa1c7e2e17ca219a6a2b5 Mon Sep 17 00:00:00 2001
2 From: Jann Horn <jannh@google.com>
3 Date: Fri, 1 Mar 2019 04:12:00 +0100
4 Subject: x86/unwind: Handle NULL pointer calls better in frame unwinder
5
6 From: Jann Horn <jannh@google.com>
7
8 commit f4f34e1b82eb4219d8eaa1c7e2e17ca219a6a2b5 upstream.
9
10 When the frame unwinder is invoked for an oops caused by a call to NULL, it
11 currently skips the parent function because BP still points to the parent's
12 stack frame; the (nonexistent) current function only has the first half of
13 a stack frame, and BP doesn't point to it yet.
14
15 Add a special case for IP==0 that calculates a fake BP from SP, then uses
16 the real BP for the next frame.
17
18 Note that this handles first_frame specially: Return information about the
19 parent function as long as the saved IP is >=first_frame, even if the fake
20 BP points below it.
21
22 With an artificially-added NULL call in prctl_set_seccomp(), before this
23 patch, the trace is:
24
25 Call Trace:
26 ? prctl_set_seccomp+0x3a/0x50
27 __x64_sys_prctl+0x457/0x6f0
28 ? __ia32_sys_prctl+0x750/0x750
29 do_syscall_64+0x72/0x160
30 entry_SYSCALL_64_after_hwframe+0x44/0xa9
31
32 After this patch, the trace is:
33
34 Call Trace:
35 prctl_set_seccomp+0x3a/0x50
36 __x64_sys_prctl+0x457/0x6f0
37 ? __ia32_sys_prctl+0x750/0x750
38 do_syscall_64+0x72/0x160
39 entry_SYSCALL_64_after_hwframe+0x44/0xa9
40
41 Signed-off-by: Jann Horn <jannh@google.com>
42 Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
43 Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>
44 Cc: Borislav Petkov <bp@alien8.de>
45 Cc: Andrew Morton <akpm@linux-foundation.org>
46 Cc: syzbot <syzbot+ca95b2b7aef9e7cbd6ab@syzkaller.appspotmail.com>
47 Cc: "H. Peter Anvin" <hpa@zytor.com>
48 Cc: Masahiro Yamada <yamada.masahiro@socionext.com>
49 Cc: Michal Marek <michal.lkml@markovi.net>
50 Cc: linux-kbuild@vger.kernel.org
51 Link: https://lkml.kernel.org/r/20190301031201.7416-1-jannh@google.com
52 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
53
54 ---
55 arch/x86/include/asm/unwind.h | 6 ++++++
56 arch/x86/kernel/unwind_frame.c | 25 ++++++++++++++++++++++---
57 2 files changed, 28 insertions(+), 3 deletions(-)
58
59 --- a/arch/x86/include/asm/unwind.h
60 +++ b/arch/x86/include/asm/unwind.h
61 @@ -23,6 +23,12 @@ struct unwind_state {
62 #elif defined(CONFIG_UNWINDER_FRAME_POINTER)
63 bool got_irq;
64 unsigned long *bp, *orig_sp, ip;
65 + /*
66 + * If non-NULL: The current frame is incomplete and doesn't contain a
67 + * valid BP. When looking for the next frame, use this instead of the
68 + * non-existent saved BP.
69 + */
70 + unsigned long *next_bp;
71 struct pt_regs *regs;
72 #else
73 unsigned long *sp;
74 --- a/arch/x86/kernel/unwind_frame.c
75 +++ b/arch/x86/kernel/unwind_frame.c
76 @@ -320,10 +320,14 @@ bool unwind_next_frame(struct unwind_sta
77 }
78
79 /* Get the next frame pointer: */
80 - if (state->regs)
81 + if (state->next_bp) {
82 + next_bp = state->next_bp;
83 + state->next_bp = NULL;
84 + } else if (state->regs) {
85 next_bp = (unsigned long *)state->regs->bp;
86 - else
87 + } else {
88 next_bp = (unsigned long *)READ_ONCE_TASK_STACK(state->task, *state->bp);
89 + }
90
91 /* Move to the next frame if it's safe: */
92 if (!update_stack_state(state, next_bp))
93 @@ -398,6 +402,21 @@ void __unwind_start(struct unwind_state
94
95 bp = get_frame_pointer(task, regs);
96
97 + /*
98 + * If we crash with IP==0, the last successfully executed instruction
99 + * was probably an indirect function call with a NULL function pointer.
100 + * That means that SP points into the middle of an incomplete frame:
101 + * *SP is a return pointer, and *(SP-sizeof(unsigned long)) is where we
102 + * would have written a frame pointer if we hadn't crashed.
103 + * Pretend that the frame is complete and that BP points to it, but save
104 + * the real BP so that we can use it when looking for the next frame.
105 + */
106 + if (regs && regs->ip == 0 &&
107 + (unsigned long *)kernel_stack_pointer(regs) >= first_frame) {
108 + state->next_bp = bp;
109 + bp = ((unsigned long *)kernel_stack_pointer(regs)) - 1;
110 + }
111 +
112 /* Initialize stack info and make sure the frame data is accessible: */
113 get_stack_info(bp, state->task, &state->stack_info,
114 &state->stack_mask);
115 @@ -410,7 +429,7 @@ void __unwind_start(struct unwind_state
116 */
117 while (!unwind_done(state) &&
118 (!on_stack(&state->stack_info, first_frame, sizeof(long)) ||
119 - state->bp < first_frame))
120 + (state->next_bp == NULL && state->bp < first_frame)))
121 unwind_next_frame(state);
122 }
123 EXPORT_SYMBOL_GPL(__unwind_start);