2020-06-18 14:56:04

by Peter Zijlstra

[permalink] [raw]
Subject: [PATCH 1/7] x86/entry: Fix #UD vs WARN more

vmlinux.o: warning: objtool: exc_invalid_op()+0x47: call to probe_kernel_read() leaves .noinstr.text section

Since we use UD2 as a short-cut for 'CALL __WARN', treat it as such.
Have the bare exception handler do the report_bug() thing.

Fixes: 15a416e8aaa7 ("x86/entry: Treat BUG/WARN as NMI-like entries")
Signed-off-by: Peter Zijlstra (Intel) <[email protected]>
---
---
arch/x86/kernel/traps.c | 50 +++++++++++++++++++++---------------------------
1 file changed, 22 insertions(+), 28 deletions(-)

--- a/arch/x86/kernel/traps.c
+++ b/arch/x86/kernel/traps.c
@@ -216,40 +216,34 @@ static inline void handle_invalid_op(str
ILL_ILLOPN, error_get_trap_addr(regs));
}

+static noinstr bool handle_bug(struct pt_regs *regs)
+{
+ bool handled = false;
+
+ /*
+ * All lies, just get the WARN/BUG out.
+ */
+ instrumentation_begin();
+ if (report_bug(regs->ip, regs) == BUG_TRAP_TYPE_WARN) {
+ regs->ip += LEN_UD2;
+ handled = true;
+ }
+ instrumentation_end();
+
+ return handled;
+}
+
DEFINE_IDTENTRY_RAW(exc_invalid_op)
{
bool rcu_exit;

/*
- * Handle BUG/WARN like NMIs instead of like normal idtentries:
- * if we bugged/warned in a bad RCU context, for example, the last
- * thing we want is to BUG/WARN again in the idtentry code, ad
- * infinitum.
+ * We use UD2 as a short encoding for 'CALL __WARN', as such
+ * handle it before exception entry to avoid recursive WARN
+ * in case exception entry is the one triggering WARNs.
*/
- if (!user_mode(regs) && is_valid_bugaddr(regs->ip)) {
- enum bug_trap_type type;
-
- nmi_enter();
- instrumentation_begin();
- trace_hardirqs_off_finish();
- type = report_bug(regs->ip, regs);
- if (regs->flags & X86_EFLAGS_IF)
- trace_hardirqs_on_prepare();
- instrumentation_end();
- nmi_exit();
-
- if (type == BUG_TRAP_TYPE_WARN) {
- /* Skip the ud2. */
- regs->ip += LEN_UD2;
- return;
- }
-
- /*
- * Else, if this was a BUG and report_bug returns or if this
- * was just a normal #UD, we want to continue onward and
- * crash.
- */
- }
+ if (!user_mode(regs) && handle_bug(regs))
+ return;

rcu_exit = idtentry_enter_cond_rcu(regs);
instrumentation_begin();



2020-06-18 15:02:17

by Andy Lutomirski

[permalink] [raw]
Subject: Re: [PATCH 1/7] x86/entry: Fix #UD vs WARN more



> On Jun 18, 2020, at 7:50 AM, Peter Zijlstra <[email protected]> wrote:
>
> vmlinux.o: warning: objtool: exc_invalid_op()+0x47: call to probe_kernel_read() leaves .noinstr.text section
>
> Since we use UD2 as a short-cut for 'CALL __WARN', treat it as such.
> Have the bare exception handler do the report_bug() thing.

I think you should consider inlining or noinstr-ifying report_bug() too if you want to make this more bulletproof. I admit the scenario where someone instruments it and it goes wrong is farfetched.

>
> Fixes: 15a416e8aaa7 ("x86/entry: Treat BUG/WARN as NMI-like entries")
> Signed-off-by: Peter Zijlstra (Intel) <[email protected]>
> ---
> ---
> arch/x86/kernel/traps.c | 50 +++++++++++++++++++++---------------------------
> 1 file changed, 22 insertions(+), 28 deletions(-)
>
> --- a/arch/x86/kernel/traps.c
> +++ b/arch/x86/kernel/traps.c
> @@ -216,40 +216,34 @@ static inline void handle_invalid_op(str
> ILL_ILLOPN, error_get_trap_addr(regs));
> }
>
> +static noinstr bool handle_bug(struct pt_regs *regs)
> +{
> + bool handled = false;
> +
> + /*
> + * All lies, just get the WARN/BUG out.
> + */
> + instrumentation_begin();
> + if (report_bug(regs->ip, regs) == BUG_TRAP_TYPE_WARN) {
> + regs->ip += LEN_UD2;
> + handled = true;
> + }
> + instrumentation_end();
> +
> + return handled;
> +}
> +
> DEFINE_IDTENTRY_RAW(exc_invalid_op)
> {
> bool rcu_exit;
>
> /*
> - * Handle BUG/WARN like NMIs instead of like normal idtentries:
> - * if we bugged/warned in a bad RCU context, for example, the last
> - * thing we want is to BUG/WARN again in the idtentry code, ad
> - * infinitum.
> + * We use UD2 as a short encoding for 'CALL __WARN', as such
> + * handle it before exception entry to avoid recursive WARN
> + * in case exception entry is the one triggering WARNs.
> */
> - if (!user_mode(regs) && is_valid_bugaddr(regs->ip)) {
> - enum bug_trap_type type;
> -
> - nmi_enter();
> - instrumentation_begin();
> - trace_hardirqs_off_finish();
> - type = report_bug(regs->ip, regs);
> - if (regs->flags & X86_EFLAGS_IF)
> - trace_hardirqs_on_prepare();
> - instrumentation_end();
> - nmi_exit();
> -
> - if (type == BUG_TRAP_TYPE_WARN) {
> - /* Skip the ud2. */
> - regs->ip += LEN_UD2;
> - return;
> - }
> -
> - /*
> - * Else, if this was a BUG and report_bug returns or if this
> - * was just a normal #UD, we want to continue onward and
> - * crash.
> - */
> - }
> + if (!user_mode(regs) && handle_bug(regs))
> + return;
>
> rcu_exit = idtentry_enter_cond_rcu(regs);
> instrumentation_begin();
>
>

2020-06-18 15:55:49

by Peter Zijlstra

[permalink] [raw]
Subject: Re: [PATCH 1/7] x86/entry: Fix #UD vs WARN more

On Thu, Jun 18, 2020 at 07:57:35AM -0700, Andy Lutomirski wrote:
>
>
> > On Jun 18, 2020, at 7:50 AM, Peter Zijlstra <[email protected]> wrote:
> >
> > vmlinux.o: warning: objtool: exc_invalid_op()+0x47: call to probe_kernel_read() leaves .noinstr.text section
> >
> > Since we use UD2 as a short-cut for 'CALL __WARN', treat it as such.
> > Have the bare exception handler do the report_bug() thing.
>
> I think you should consider inlining or noinstr-ifying report_bug()
> too if you want to make this more bulletproof. I admit the scenario
> where someone instruments it and it goes wrong is farfetched.

How far down that rabbit hole do we go? Because then we need to noinstr
printk, the console drivers, those will very quickly pull in lovely bits
like PCI, USB, DRM :/

At some point we have to just give up.

2020-06-18 19:17:52

by Peter Zijlstra

[permalink] [raw]
Subject: Re: [PATCH 1/7] x86/entry: Fix #UD vs WARN more

On Thu, Jun 18, 2020 at 11:36:53AM -0700, Andy Lutomirski wrote:

> I wasn't imagining going far down the rabbit hole at all -- I think
> that, at most, we should cover the path for when the fault wasn't a
> BUG/WARN in the first place. I admit that, for #UD in particular,
> this isn't a big deal, but if it were a different vector, this could
> matter.

Right, so there's 3 cases for ud2:

- WARN; ud2, bug_entry, recovers
- BUG; ud2, bug_entry, dies
- UBSAN; ud2, !bug_entry, dies

Nothing else should be generating ud2 instructions, any other #UD goes
into handle_invalid_op() -> do_error_trap() -> ... -> die().

[ while there, we should probably restructure do_trap() to have
cond_local_irq_enable() _after_ do_trap_no_signal(). ]

We could probably change is_valid_bugaddr() to not use
probe_kernel_address(), because if it couldn't read the instruction,
we'd not be getting #UD in the first place.

If we've gotten rid of probe_kernel_address() we can noinstr/inline that
and then we can only call into report_bug() IFF ud2.

Does that make things 'better' ? This can only go realy bad if there's a
1 byte instruction that triggers #UD, but I think that was ruled out.


---
diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c
index c26751e303f1..275a621f1aff 100644
--- a/arch/x86/kernel/traps.c
+++ b/arch/x86/kernel/traps.c
@@ -91,10 +91,7 @@ int is_valid_bugaddr(unsigned long addr)
if (addr < TASK_SIZE_MAX)
return 0;

- if (probe_kernel_address((unsigned short *)addr, ud))
- return 0;
-
- return ud == INSN_UD0 || ud == INSN_UD2;
+ return *(unsigned short *)addr == INSN_UD2;
}

static nokprobe_inline int
@@ -220,15 +217,17 @@ static noinstr bool handle_bug(struct pt_regs *regs)
{
bool handled = false;

- /*
- * All lies, just get the WARN/BUG out.
- */
- instrumentation_begin();
- if (report_bug(regs->ip, regs) == BUG_TRAP_TYPE_WARN) {
- regs->ip += LEN_UD2;
- handled = true;
+ if (is_valid_bugaddr(regs->ip)) {
+ /*
+ * All lies, just get the WARN/BUG out.
+ */
+ instrumentation_begin();
+ if (report_bug(regs->ip, regs) == BUG_TRAP_TYPE_WARN) {
+ regs->ip += LEN_UD2;
+ handled = true;
+ }
+ instrumentation_end();
}
- instrumentation_end();

return handled;
}

2020-06-18 19:32:38

by Andy Lutomirski

[permalink] [raw]
Subject: Re: [PATCH 1/7] x86/entry: Fix #UD vs WARN more


> On Jun 18, 2020, at 12:02 PM, Peter Zijlstra <[email protected]> wrote:
>
> On Thu, Jun 18, 2020 at 11:36:53AM -0700, Andy Lutomirski wrote:
>
>> I wasn't imagining going far down the rabbit hole at all -- I think
>> that, at most, we should cover the path for when the fault wasn't a
>> BUG/WARN in the first place. I admit that, for #UD in particular,
>> this isn't a big deal, but if it were a different vector, this could
>> matter.
>
> Right, so there's 3 cases for ud2:
>
> - WARN; ud2, bug_entry, recovers
> - BUG; ud2, bug_entry, dies
> - UBSAN; ud2, !bug_entry, dies

4. The #UD matches an extable entry. I don’t know whether this ever happens for real.

The failure is still a bit farfetched: we’d need an extable to hit in an inconsistent state where we blow up due to a lack of entry handling.

>
> Nothing else should be generating ud2 instructions, any other #UD goes
> into handle_invalid_op() -> do_error_trap() -> ... -> die().
>
> [ while there, we should probably restructure do_trap() to have
> cond_local_irq_enable() _after_ do_trap_no_signal(). ]
>
> We could probably change is_valid_bugaddr() to not use
> probe_kernel_address(), because if it couldn't read the instruction,
> we'd not be getting #UD in the first place.
>
> If we've gotten rid of probe_kernel_address() we can noinstr/inline that
> and then we can only call into report_bug() IFF ud2.
>
> Does that make things 'better' ? This can only go realy bad if there's a
> 1 byte instruction that triggers #UD, but I think that was ruled out.
>
>
> ---
> diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c
> index c26751e303f1..275a621f1aff 100644
> --- a/arch/x86/kernel/traps.c
> +++ b/arch/x86/kernel/traps.c
> @@ -91,10 +91,7 @@ int is_valid_bugaddr(unsigned long addr)
> if (addr < TASK_SIZE_MAX)
> return 0;
>
> - if (probe_kernel_address((unsigned short *)addr, ud))
> - return 0;
> -
> - return ud == INSN_UD0 || ud == INSN_UD2;
> + return *(unsigned short *)addr == INSN_UD2;
> }

I’m okay with this, at least until we get PKRS or kernel XO memory. But probe_kernel_addr() would be wrong then, too. We need probe_kernel_text().

But I think you might need some IRQ fiddling. With your patch, a WARN with IRQs on will execute the printk code with IRQs off without lockstep handling, and an appropriately configured debugging kernel may get a recursive splat. Or if irq tracing somehow notices that IRQs got turned off, the warning recovery might return back to an IF=1 context with IRQs traced as off.

So maybe also do an untraced cond_local_irq_enable()? After all, if we’re trying to report a bug from IRQs on, it should be okay to have IRQs on while reporting it. It might even work better than having IRQs off.

>
> static nokprobe_inline int
> @@ -220,15 +217,17 @@ static noinstr bool handle_bug(struct pt_regs *regs)
> {
> bool handled = false;
>
> - /*
> - * All lies, just get the WARN/BUG out.
> - */
> - instrumentation_begin();
> - if (report_bug(regs->ip, regs) == BUG_TRAP_TYPE_WARN) {
> - regs->ip += LEN_UD2;
> - handled = true;
> + if (is_valid_bugaddr(regs->ip)) {
> + /*
> + * All lies, just get the WARN/BUG out.
> + */
> + instrumentation_begin();
> + if (report_bug(regs->ip, regs) == BUG_TRAP_TYPE_WARN) {
> + regs->ip += LEN_UD2;
> + handled = true;
> + }
> + instrumentation_end();
> }
> - instrumentation_end();
>
> return handled;
> }

2020-06-18 21:53:46

by Andy Lutomirski

[permalink] [raw]
Subject: Re: [PATCH 1/7] x86/entry: Fix #UD vs WARN more

On Thu, Jun 18, 2020 at 8:50 AM Peter Zijlstra <[email protected]> wrote:
>
> On Thu, Jun 18, 2020 at 07:57:35AM -0700, Andy Lutomirski wrote:
> >
> >
> > > On Jun 18, 2020, at 7:50 AM, Peter Zijlstra <[email protected]> wrote:
> > >
> > > vmlinux.o: warning: objtool: exc_invalid_op()+0x47: call to probe_kernel_read() leaves .noinstr.text section
> > >
> > > Since we use UD2 as a short-cut for 'CALL __WARN', treat it as such.
> > > Have the bare exception handler do the report_bug() thing.
> >
> > I think you should consider inlining or noinstr-ifying report_bug()
> > too if you want to make this more bulletproof. I admit the scenario
> > where someone instruments it and it goes wrong is farfetched.
>
> How far down that rabbit hole do we go? Because then we need to noinstr
> printk, the console drivers, those will very quickly pull in lovely bits
> like PCI, USB, DRM :/
>
> At some point we have to just give up.

I wasn't imagining going far down the rabbit hole at all -- I think
that, at most, we should cover the path for when the fault wasn't a
BUG/WARN in the first place. I admit that, for #UD in particular,
this isn't a big deal, but if it were a different vector, this could
matter.

I suppose report_bug() could be split into lookup_bug() and
handle_bug(), and just lookup_bug() could be noinstr.

--Andy

2020-06-19 00:28:22

by Peter Zijlstra

[permalink] [raw]
Subject: Re: [PATCH 1/7] x86/entry: Fix #UD vs WARN more

On Thu, Jun 18, 2020 at 12:29:50PM -0700, Andy Lutomirski wrote:
>
> > On Jun 18, 2020, at 12:02 PM, Peter Zijlstra <[email protected]> wrote:
> >
> > On Thu, Jun 18, 2020 at 11:36:53AM -0700, Andy Lutomirski wrote:
> >
> >> I wasn't imagining going far down the rabbit hole at all -- I think
> >> that, at most, we should cover the path for when the fault wasn't a
> >> BUG/WARN in the first place. I admit that, for #UD in particular,
> >> this isn't a big deal, but if it were a different vector, this could
> >> matter.
> >
> > Right, so there's 3 cases for ud2:
> >
> > - WARN; ud2, bug_entry, recovers
> > - BUG; ud2, bug_entry, dies
> > - UBSAN; ud2, !bug_entry, dies
>
> 4. The #UD matches an extable entry. I don’t know whether this ever happens for real.

#UD yes, ud2 instruction, not so much.

> The failure is still a bit farfetched: we’d need an extable to hit in
> an inconsistent state where we blow up due to a lack of entry
> handling.

Right, by noinstr checking the instruction is actually ud2 I think we
mostly good. There really aren't that many places that emit ud2.

> But I think you might need some IRQ fiddling. With your patch, a WARN
> with IRQs on will execute the printk code with IRQs off without
> lockstep handling, and an appropriately configured debugging kernel
> may get a recursive splat. Or if irq tracing somehow notices that
> IRQs got turned off, the warning recovery might return back to an IF=1
> context with IRQs traced as off.
>
> So maybe also do an untraced cond_local_irq_enable()? After all, if
> we’re trying to report a bug from IRQs on, it should be okay to have
> IRQs on while reporting it. It might even work better than having IRQs
> off.

Yes, very good point. Now I want to go look at the old code... I'll frob
something tomorrow, brain is pretty fried by now.

2020-06-22 11:50:06

by Peter Zijlstra

[permalink] [raw]
Subject: Re: [PATCH 1/7] x86/entry: Fix #UD vs WARN more

On Thu, Jun 18, 2020 at 11:18:23PM +0200, Peter Zijlstra wrote:

> > So maybe also do an untraced cond_local_irq_enable()? After all, if
> > we’re trying to report a bug from IRQs on, it should be okay to have
> > IRQs on while reporting it. It might even work better than having IRQs
> > off.
>
> Yes, very good point. Now I want to go look at the old code... I'll frob
> something tomorrow, brain is pretty fried by now.

How's this then?

---
Subject: x86/entry: Fix #UD vs WARN more
From: Peter Zijlstra <[email protected]>
Date: Tue Jun 16 13:28:36 CEST 2020

vmlinux.o: warning: objtool: exc_invalid_op()+0x47: call to probe_kernel_read() leaves .noinstr.text section

Since we use UD2 as a short-cut for 'CALL __WARN', treat it as such.
Have the bare exception handler do the report_bug() thing.

Fixes: 15a416e8aaa7 ("x86/entry: Treat BUG/WARN as NMI-like entries")
Signed-off-by: Peter Zijlstra (Intel) <[email protected]>
---
arch/x86/kernel/traps.c | 72 +++++++++++++++++++++++++-----------------------
1 file changed, 38 insertions(+), 34 deletions(-)

--- a/arch/x86/kernel/traps.c
+++ b/arch/x86/kernel/traps.c
@@ -86,15 +86,14 @@ static inline void cond_local_irq_disabl

int is_valid_bugaddr(unsigned long addr)
{
- unsigned short ud;
-
if (addr < TASK_SIZE_MAX)
return 0;

- if (probe_kernel_address((unsigned short *)addr, ud))
- return 0;
-
- return ud == INSN_UD0 || ud == INSN_UD2;
+ /*
+ * We got #UD, if the text isn't readable we'd have gotten
+ * a different exception.
+ */
+ return *(unsigned short)addr == INSN_UD2;
}

static nokprobe_inline int
@@ -216,40 +215,45 @@ static inline void handle_invalid_op(str
ILL_ILLOPN, error_get_trap_addr(regs));
}

-DEFINE_IDTENTRY_RAW(exc_invalid_op)
+static noinstr bool handle_bug(struct pt_regs *regs)
{
- bool rcu_exit;
+ bool handled = false;
+
+ if (!is_valid_bugaddr(regs->ip))
+ return handled;

/*
- * Handle BUG/WARN like NMIs instead of like normal idtentries:
- * if we bugged/warned in a bad RCU context, for example, the last
- * thing we want is to BUG/WARN again in the idtentry code, ad
- * infinitum.
+ * All lies, just get the WARN/BUG out.
+ */
+ instrumentation_begin();
+ /*
+ * Since we're emulating a CALL with exceptions, restore the interrupt
+ * state to what it was at the exception site.
*/
- if (!user_mode(regs) && is_valid_bugaddr(regs->ip)) {
- enum bug_trap_type type;
+ if (regs->flags & X86_EFLAGS_IF)
+ raw_local_irq_enable();
+ if (report_bug(regs->ip, regs) == BUG_TRAP_TYPE_WARN) {
+ regs->ip += LEN_UD2;
+ handled = true;
+ }
+ if (regs->flags & X86_EFLAGS_IF)
+ raw_local_irq_disable();
+ instrumentation_end();

- nmi_enter();
- instrumentation_begin();
- trace_hardirqs_off_finish();
- type = report_bug(regs->ip, regs);
- if (regs->flags & X86_EFLAGS_IF)
- trace_hardirqs_on_prepare();
- instrumentation_end();
- nmi_exit();
-
- if (type == BUG_TRAP_TYPE_WARN) {
- /* Skip the ud2. */
- regs->ip += LEN_UD2;
- return;
- }
+ return handled;
+}

- /*
- * Else, if this was a BUG and report_bug returns or if this
- * was just a normal #UD, we want to continue onward and
- * crash.
- */
- }
+DEFINE_IDTENTRY_RAW(exc_invalid_op)
+{
+ bool rcu_exit;
+
+ /*
+ * We use UD2 as a short encoding for 'CALL __WARN', as such
+ * handle it before exception entry to avoid recursive WARN
+ * in case exception entry is the one triggering WARNs.
+ */
+ if (!user_mode(regs) && handle_bug(regs))
+ return;

rcu_exit = idtentry_enter_cond_rcu(regs);
instrumentation_begin();

2020-06-24 22:40:22

by Andy Lutomirski

[permalink] [raw]
Subject: Re: [PATCH 1/7] x86/entry: Fix #UD vs WARN more

On Mon, Jun 22, 2020 at 4:47 AM Peter Zijlstra <[email protected]> wrote:
>
> On Thu, Jun 18, 2020 at 11:18:23PM +0200, Peter Zijlstra wrote:
>
> > > So maybe also do an untraced cond_local_irq_enable()? After all, if
> > > we’re trying to report a bug from IRQs on, it should be okay to have
> > > IRQs on while reporting it. It might even work better than having IRQs
> > > off.
> >
> > Yes, very good point. Now I want to go look at the old code... I'll frob
> > something tomorrow, brain is pretty fried by now.
>
> How's this then?
>
> ---
> Subject: x86/entry: Fix #UD vs WARN more
> From: Peter Zijlstra <[email protected]>
> Date: Tue Jun 16 13:28:36 CEST 2020
>
> vmlinux.o: warning: objtool: exc_invalid_op()+0x47: call to probe_kernel_read() leaves .noinstr.text section
>
> Since we use UD2 as a short-cut for 'CALL __WARN', treat it as such.
> Have the bare exception handler do the report_bug() thing.
>
> Fixes: 15a416e8aaa7 ("x86/entry: Treat BUG/WARN as NMI-like entries")

Acked-by: Andy Lutomirski <[email protected]>

Subject: [tip: x86/entry] x86/entry: Fix #UD vs WARN more

The following commit has been merged into the x86/entry branch of tip:

Commit-ID: 145a773aef83181d47ebab21bb33c89233aadb1e
Gitweb: https://git.kernel.org/tip/145a773aef83181d47ebab21bb33c89233aadb1e
Author: Peter Zijlstra <[email protected]>
AuthorDate: Tue, 16 Jun 2020 13:28:36 +02:00
Committer: Peter Zijlstra <[email protected]>
CommitterDate: Thu, 25 Jun 2020 13:45:40 +02:00

x86/entry: Fix #UD vs WARN more

vmlinux.o: warning: objtool: exc_invalid_op()+0x47: call to probe_kernel_read() leaves .noinstr.text section

Since we use UD2 as a short-cut for 'CALL __WARN', treat it as such.
Have the bare exception handler do the report_bug() thing.

Fixes: 15a416e8aaa7 ("x86/entry: Treat BUG/WARN as NMI-like entries")
Signed-off-by: Peter Zijlstra (Intel) <[email protected]>
Acked-by: Andy Lutomirski <[email protected]>
Link: https://lkml.kernel.org/r/[email protected]
---
arch/x86/kernel/traps.c | 72 +++++++++++++++++++++-------------------
1 file changed, 38 insertions(+), 34 deletions(-)

diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c
index a7d1570..1d9ea21 100644
--- a/arch/x86/kernel/traps.c
+++ b/arch/x86/kernel/traps.c
@@ -84,17 +84,16 @@ static inline void cond_local_irq_disable(struct pt_regs *regs)
local_irq_disable();
}

-int is_valid_bugaddr(unsigned long addr)
+__always_inline int is_valid_bugaddr(unsigned long addr)
{
- unsigned short ud;
-
if (addr < TASK_SIZE_MAX)
return 0;

- if (probe_kernel_address((unsigned short *)addr, ud))
- return 0;
-
- return ud == INSN_UD0 || ud == INSN_UD2;
+ /*
+ * We got #UD, if the text isn't readable we'd have gotten
+ * a different exception.
+ */
+ return *(unsigned short *)addr == INSN_UD2;
}

static nokprobe_inline int
@@ -216,40 +215,45 @@ static inline void handle_invalid_op(struct pt_regs *regs)
ILL_ILLOPN, error_get_trap_addr(regs));
}

-DEFINE_IDTENTRY_RAW(exc_invalid_op)
+static noinstr bool handle_bug(struct pt_regs *regs)
{
- bool rcu_exit;
+ bool handled = false;
+
+ if (!is_valid_bugaddr(regs->ip))
+ return handled;

/*
- * Handle BUG/WARN like NMIs instead of like normal idtentries:
- * if we bugged/warned in a bad RCU context, for example, the last
- * thing we want is to BUG/WARN again in the idtentry code, ad
- * infinitum.
+ * All lies, just get the WARN/BUG out.
*/
- if (!user_mode(regs) && is_valid_bugaddr(regs->ip)) {
- enum bug_trap_type type;
+ instrumentation_begin();
+ /*
+ * Since we're emulating a CALL with exceptions, restore the interrupt
+ * state to what it was at the exception site.
+ */
+ if (regs->flags & X86_EFLAGS_IF)
+ raw_local_irq_enable();
+ if (report_bug(regs->ip, regs) == BUG_TRAP_TYPE_WARN) {
+ regs->ip += LEN_UD2;
+ handled = true;
+ }
+ if (regs->flags & X86_EFLAGS_IF)
+ raw_local_irq_disable();
+ instrumentation_end();

- nmi_enter();
- instrumentation_begin();
- trace_hardirqs_off_finish();
- type = report_bug(regs->ip, regs);
- if (regs->flags & X86_EFLAGS_IF)
- trace_hardirqs_on_prepare();
- instrumentation_end();
- nmi_exit();
+ return handled;
+}

- if (type == BUG_TRAP_TYPE_WARN) {
- /* Skip the ud2. */
- regs->ip += LEN_UD2;
- return;
- }
+DEFINE_IDTENTRY_RAW(exc_invalid_op)
+{
+ bool rcu_exit;

- /*
- * Else, if this was a BUG and report_bug returns or if this
- * was just a normal #UD, we want to continue onward and
- * crash.
- */
- }
+ /*
+ * We use UD2 as a short encoding for 'CALL __WARN', as such
+ * handle it before exception entry to avoid recursive WARN
+ * in case exception entry is the one triggering WARNs.
+ */
+ if (!user_mode(regs) && handle_bug(regs))
+ return;

rcu_exit = idtentry_enter_cond_rcu(regs);
instrumentation_begin();