From: Sean Christopherson <sean.j.christopherson@intel.com>
To: Jann Horn <jannh@google.com>
Cc: Thomas Gleixner <tglx@linutronix.de>,
Ingo Molnar <mingo@redhat.com>, Borislav Petkov <bp@alien8.de>,
Andrew Morton <akpm@linux-foundation.org>,
Josh Poimboeuf <jpoimboe@redhat.com>,
syzbot <syzbot+ca95b2b7aef9e7cbd6ab@syzkaller.appspotmail.com>,
"H. Peter Anvin" <hpa@zytor.com>,
x86@kernel.org, linux-kernel@vger.kernel.org,
Masahiro Yamada <yamada.masahiro@socionext.com>,
Michal Marek <michal.lkml@markovi.net>,
linux-kbuild@vger.kernel.org
Subject: Re: [PATCH 1/2] x86/unwind: handle NULL pointer calls better in frame unwinder
Date: Fri, 1 Mar 2019 07:32:58 -0800 [thread overview]
Message-ID: <20190301153258.GD22584@linux.intel.com> (raw)
In-Reply-To: <20190301031201.7416-1-jannh@google.com>
On Fri, Mar 01, 2019 at 04:12:00AM +0100, Jann Horn wrote:
> When the frame unwinder is invoked for an oops caused by a call to NULL,
> it currently skips the parent function because BP still points to the
> parent's stack frame; the (nonexistent) current function only has the first
> half of a stack frame, and BP doesn't point to it yet.
>
> Add a special case for IP==0 that calculates a fake BP from SP, then uses
> the real BP for the next frame.
>
> Note that this handles first_frame specially: We return information about
> the parent function as long as the saved IP is >=first_frame, even if the
> fake BP points below it.
>
> With an artificially-added NULL call in prctl_set_seccomp(), before this
> patch, the trace is:
>
> Call Trace:
> ? prctl_set_seccomp+0x3a/0x50
> __x64_sys_prctl+0x457/0x6f0
> ? __ia32_sys_prctl+0x750/0x750
> do_syscall_64+0x72/0x160
> entry_SYSCALL_64_after_hwframe+0x44/0xa9
>
> After this patch, the trace is:
>
> Call Trace:
> prctl_set_seccomp+0x3a/0x50
> __x64_sys_prctl+0x457/0x6f0
> ? __ia32_sys_prctl+0x750/0x750
> do_syscall_64+0x72/0x160
> entry_SYSCALL_64_after_hwframe+0x44/0xa9
>
> Signed-off-by: Jann Horn <jannh@google.com>
> ---
> arch/x86/include/asm/unwind.h | 6 ++++++
> arch/x86/kernel/unwind_frame.c | 25 ++++++++++++++++++++++---
> 2 files changed, 28 insertions(+), 3 deletions(-)
>
> diff --git a/arch/x86/include/asm/unwind.h b/arch/x86/include/asm/unwind.h
> index 1f86e1b0a5cd..499578f7e6d7 100644
> --- a/arch/x86/include/asm/unwind.h
> +++ b/arch/x86/include/asm/unwind.h
> @@ -23,6 +23,12 @@ struct unwind_state {
> #elif defined(CONFIG_UNWINDER_FRAME_POINTER)
> bool got_irq;
> unsigned long *bp, *orig_sp, ip;
> + /*
> + * If non-NULL: The current frame is incomplete and doesn't contain a
> + * valid BP. When looking for the next frame, use this instead of the
> + * non-existent saved BP.
> + */
> + unsigned long *next_bp;
> struct pt_regs *regs;
> #else
> unsigned long *sp;
> diff --git a/arch/x86/kernel/unwind_frame.c b/arch/x86/kernel/unwind_frame.c
> index 3dc26f95d46e..9b9fd4826e7a 100644
> --- a/arch/x86/kernel/unwind_frame.c
> +++ b/arch/x86/kernel/unwind_frame.c
> @@ -320,10 +320,14 @@ bool unwind_next_frame(struct unwind_state *state)
> }
>
> /* Get the next frame pointer: */
> - if (state->regs)
> + if (state->next_bp) {
> + next_bp = state->next_bp;
> + state->next_bp = NULL;
> + } else if (state->regs) {
> next_bp = (unsigned long *)state->regs->bp;
> - else
> + } else {
> next_bp = (unsigned long *)READ_ONCE_TASK_STACK(state->task, *state->bp);
> + }
>
> /* Move to the next frame if it's safe: */
> if (!update_stack_state(state, next_bp))
> @@ -398,6 +402,21 @@ void __unwind_start(struct unwind_state *state, struct task_struct *task,
>
> bp = get_frame_pointer(task, regs);
>
> + /*
> + * If we crash with IP==0, the last successfully executed instruction
> + * was probably an indirect function call with a NULL function pointer.
> + * That means that SP points into the middle of an incomplete frame:
> + * *SP is a return pointer, and *(SP-sizeof(unsigned long)) is where we
> + * would have written a frame pointer if we hadn't crashed.
> + * Pretend that the frame is complete and that BP points to it, but save
> + * the real BP so that we can use it when looking for the next frame.
> + */
> + if (regs && regs->ip == 0 &&
Would it make sense to do 'regs->ip < PAGE_SIZE', a la show_fault_oops()?
E.g. to handle bugs where a function pointer gets loaded with NULL+offset.
> + (unsigned long *)kernel_stack_pointer(regs) >= first_frame) {
> + state->next_bp = bp;
> + bp = ((unsigned long *)kernel_stack_pointer(regs)) - 1;
> + }
> +
> /* Initialize stack info and make sure the frame data is accessible: */
> get_stack_info(bp, state->task, &state->stack_info,
> &state->stack_mask);
> @@ -410,7 +429,7 @@ void __unwind_start(struct unwind_state *state, struct task_struct *task,
> */
> while (!unwind_done(state) &&
> (!on_stack(&state->stack_info, first_frame, sizeof(long)) ||
> - state->bp < first_frame))
> + (state->next_bp == NULL && state->bp < first_frame)))
> unwind_next_frame(state);
> }
> EXPORT_SYMBOL_GPL(__unwind_start);
> --
> 2.21.0.352.gf09ad66450-goog
>
next prev parent reply other threads:[~2019-03-01 15:32 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-03-01 3:12 [PATCH 1/2] x86/unwind: handle NULL pointer calls better in frame unwinder Jann Horn
2019-03-01 3:12 ` [PATCH 2/2] x86/unwind: add hardcoded ORC entry for NULL Jann Horn
2019-03-01 16:24 ` Josh Poimboeuf
2019-03-01 16:29 ` Josh Poimboeuf
2019-03-01 16:55 ` Jann Horn
2019-03-06 22:31 ` [tip:x86/urgent] x86/unwind: Add " tip-bot for Jann Horn
2019-03-01 4:22 ` [PATCH 1/2] x86/unwind: handle NULL pointer calls better in frame unwinder Josh Poimboeuf
2019-03-01 15:32 ` Sean Christopherson [this message]
2019-03-01 15:59 ` Jann Horn
2019-03-01 16:19 ` Josh Poimboeuf
2019-03-06 22:31 ` [tip:x86/urgent] x86/unwind: Handle " tip-bot for Jann Horn
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20190301153258.GD22584@linux.intel.com \
--to=sean.j.christopherson@intel.com \
--cc=akpm@linux-foundation.org \
--cc=bp@alien8.de \
--cc=hpa@zytor.com \
--cc=jannh@google.com \
--cc=jpoimboe@redhat.com \
--cc=linux-kbuild@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=michal.lkml@markovi.net \
--cc=mingo@redhat.com \
--cc=syzbot+ca95b2b7aef9e7cbd6ab@syzkaller.appspotmail.com \
--cc=tglx@linutronix.de \
--cc=x86@kernel.org \
--cc=yamada.masahiro@socionext.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.