Skip to content

Commit 15a416e

Browse files
amlutoKAGA-KOKO
authored andcommitted
x86/entry: Treat BUG/WARN as NMI-like entries
BUG/WARN are cleverly optimized using UD2 to handle the BUG/WARN out of line in an exception fixup. But if BUG or WARN is issued in a funny RCU context, then the idtentry_enter...() path might helpfully WARN that the RCU context is invalid, which results in infinite recursion. Split the BUG/WARN handling into an nmi_enter()/nmi_exit() path in exc_invalid_op() to increase the chance to survive the experience. [ tglx: Make the declaration match the implementation ] Signed-off-by: Andy Lutomirski <[email protected]> Signed-off-by: Thomas Gleixner <[email protected]> Link: https://lkml.kernel.org/r/f8fe40e0088749734b4435b554f73eee53dcf7a8.1591932307.git.luto@kernel.org
1 parent f0178fc commit 15a416e

File tree

3 files changed

+52
-29
lines changed

3 files changed

+52
-29
lines changed

arch/x86/include/asm/idtentry.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -543,7 +543,6 @@ SYM_CODE_END(spurious_entries_start)
543543
DECLARE_IDTENTRY(X86_TRAP_DE, exc_divide_error);
544544
DECLARE_IDTENTRY(X86_TRAP_OF, exc_overflow);
545545
DECLARE_IDTENTRY(X86_TRAP_BR, exc_bounds);
546-
DECLARE_IDTENTRY(X86_TRAP_UD, exc_invalid_op);
547546
DECLARE_IDTENTRY(X86_TRAP_NM, exc_device_not_available);
548547
DECLARE_IDTENTRY(X86_TRAP_OLD_MF, exc_coproc_segment_overrun);
549548
DECLARE_IDTENTRY(X86_TRAP_SPURIOUS, exc_spurious_interrupt_bug);
@@ -561,6 +560,7 @@ DECLARE_IDTENTRY_ERRORCODE(X86_TRAP_GP, exc_general_protection);
561560
DECLARE_IDTENTRY_ERRORCODE(X86_TRAP_AC, exc_alignment_check);
562561

563562
/* Raw exception entries which need extra work */
563+
DECLARE_IDTENTRY_RAW(X86_TRAP_UD, exc_invalid_op);
564564
DECLARE_IDTENTRY_RAW(X86_TRAP_BP, exc_int3);
565565
DECLARE_IDTENTRY_RAW_ERRORCODE(X86_TRAP_PF, exc_page_fault);
566566

arch/x86/kernel/traps.c

Lines changed: 38 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -97,24 +97,6 @@ int is_valid_bugaddr(unsigned long addr)
9797
return ud == INSN_UD0 || ud == INSN_UD2;
9898
}
9999

100-
int fixup_bug(struct pt_regs *regs, int trapnr)
101-
{
102-
if (trapnr != X86_TRAP_UD)
103-
return 0;
104-
105-
switch (report_bug(regs->ip, regs)) {
106-
case BUG_TRAP_TYPE_NONE:
107-
case BUG_TRAP_TYPE_BUG:
108-
break;
109-
110-
case BUG_TRAP_TYPE_WARN:
111-
regs->ip += LEN_UD2;
112-
return 1;
113-
}
114-
115-
return 0;
116-
}
117-
118100
static nokprobe_inline int
119101
do_trap_no_signal(struct task_struct *tsk, int trapnr, const char *str,
120102
struct pt_regs *regs, long error_code)
@@ -190,13 +172,6 @@ static void do_error_trap(struct pt_regs *regs, long error_code, char *str,
190172
{
191173
RCU_LOCKDEP_WARN(!rcu_is_watching(), "entry code didn't wake RCU");
192174

193-
/*
194-
* WARN*()s end up here; fix them up before we call the
195-
* notifier chain.
196-
*/
197-
if (!user_mode(regs) && fixup_bug(regs, trapnr))
198-
return;
199-
200175
if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr) !=
201176
NOTIFY_STOP) {
202177
cond_local_irq_enable(regs);
@@ -241,9 +216,46 @@ static inline void handle_invalid_op(struct pt_regs *regs)
241216
ILL_ILLOPN, error_get_trap_addr(regs));
242217
}
243218

244-
DEFINE_IDTENTRY(exc_invalid_op)
219+
DEFINE_IDTENTRY_RAW(exc_invalid_op)
245220
{
221+
bool rcu_exit;
222+
223+
/*
224+
* Handle BUG/WARN like NMIs instead of like normal idtentries:
225+
* if we bugged/warned in a bad RCU context, for example, the last
226+
* thing we want is to BUG/WARN again in the idtentry code, ad
227+
* infinitum.
228+
*/
229+
if (!user_mode(regs) && is_valid_bugaddr(regs->ip)) {
230+
enum bug_trap_type type;
231+
232+
nmi_enter();
233+
instrumentation_begin();
234+
trace_hardirqs_off_finish();
235+
type = report_bug(regs->ip, regs);
236+
if (regs->flags & X86_EFLAGS_IF)
237+
trace_hardirqs_on_prepare();
238+
instrumentation_end();
239+
nmi_exit();
240+
241+
if (type == BUG_TRAP_TYPE_WARN) {
242+
/* Skip the ud2. */
243+
regs->ip += LEN_UD2;
244+
return;
245+
}
246+
247+
/*
248+
* Else, if this was a BUG and report_bug returns or if this
249+
* was just a normal #UD, we want to continue onward and
250+
* crash.
251+
*/
252+
}
253+
254+
rcu_exit = idtentry_enter_cond_rcu(regs);
255+
instrumentation_begin();
246256
handle_invalid_op(regs);
257+
instrumentation_end();
258+
idtentry_exit_cond_rcu(regs, rcu_exit);
247259
}
248260

249261
DEFINE_IDTENTRY(exc_coproc_segment_overrun)

arch/x86/mm/extable.c

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -204,8 +204,19 @@ void __init early_fixup_exception(struct pt_regs *regs, int trapnr)
204204
if (fixup_exception(regs, trapnr, regs->orig_ax, 0))
205205
return;
206206

207-
if (fixup_bug(regs, trapnr))
208-
return;
207+
if (trapnr == X86_TRAP_UD) {
208+
if (report_bug(regs->ip, regs) == BUG_TRAP_TYPE_WARN) {
209+
/* Skip the ud2. */
210+
regs->ip += LEN_UD2;
211+
return;
212+
}
213+
214+
/*
215+
* If this was a BUG and report_bug returns or if this
216+
* was just a normal #UD, we want to continue onward and
217+
* crash.
218+
*/
219+
}
209220

210221
fail:
211222
early_printk("PANIC: early exception 0x%02x IP %lx:%lx error %lx cr2 0x%lx\n",

0 commit comments

Comments
 (0)