Skip to content

Commit f6170f0

Browse files
committed
Merge branch 'x86-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull misc x86 updates from Ingo Molnar: "Misc changes: - Enhance #GP fault printouts by distinguishing between canonical and non-canonical address faults, and also add KASAN fault decoding. - Fix/enhance the x86 NMI handler by putting the duration check into a direct function call instead of an irq_work which we know to be broken in some cases. - Clean up do_general_protection() a bit" * 'x86-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: x86/nmi: Remove irq_work from the long duration NMI handler x86/traps: Cleanup do_general_protection() x86/kasan: Print original address on #GP x86/dumpstack: Introduce die_addr() for die() with #GP fault address x86/traps: Print address on #GP x86/insn-eval: Add support for 64-bit kernel mode
2 parents 6da49d1 + 248ed51 commit f6170f0

File tree

10 files changed

+198
-64
lines changed

10 files changed

+198
-64
lines changed

arch/x86/include/asm/kdebug.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ enum show_regs_mode {
3333
};
3434

3535
extern void die(const char *, struct pt_regs *,long);
36+
void die_addr(const char *str, struct pt_regs *regs, long err, long gp_addr);
3637
extern int __must_check __die(const char *, struct pt_regs *, long);
3738
extern void show_stack_regs(struct pt_regs *regs);
3839
extern void __show_regs(struct pt_regs *regs, enum show_regs_mode);

arch/x86/include/asm/nmi.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ struct nmiaction {
4141
struct list_head list;
4242
nmi_handler_t handler;
4343
u64 max_duration;
44-
struct irq_work irq_work;
4544
unsigned long flags;
4645
const char *name;
4746
};

arch/x86/include/asm/ptrace.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,19 @@ static inline bool user_64bit_mode(struct pt_regs *regs)
159159
#endif
160160
}
161161

162+
/*
163+
* Determine whether the register set came from any context that is running in
164+
* 64-bit mode.
165+
*/
166+
static inline bool any_64bit_mode(struct pt_regs *regs)
167+
{
168+
#ifdef CONFIG_X86_64
169+
return !user_mode(regs) || user_64bit_mode(regs);
170+
#else
171+
return false;
172+
#endif
173+
}
174+
162175
#ifdef CONFIG_X86_64
163176
#define current_user_stack_pointer() current_pt_regs()->sp
164177
#define compat_user_stack_pointer() current_pt_regs()->sp

arch/x86/kernel/dumpstack.c

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,7 @@ void oops_end(unsigned long flags, struct pt_regs *regs, int signr)
365365
}
366366
NOKPROBE_SYMBOL(oops_end);
367367

368-
int __die(const char *str, struct pt_regs *regs, long err)
368+
static void __die_header(const char *str, struct pt_regs *regs, long err)
369369
{
370370
const char *pr = "";
371371

@@ -384,7 +384,11 @@ int __die(const char *str, struct pt_regs *regs, long err)
384384
IS_ENABLED(CONFIG_KASAN) ? " KASAN" : "",
385385
IS_ENABLED(CONFIG_PAGE_TABLE_ISOLATION) ?
386386
(boot_cpu_has(X86_FEATURE_PTI) ? " PTI" : " NOPTI") : "");
387+
}
388+
NOKPROBE_SYMBOL(__die_header);
387389

390+
static int __die_body(const char *str, struct pt_regs *regs, long err)
391+
{
388392
show_regs(regs);
389393
print_modules();
390394

@@ -394,6 +398,13 @@ int __die(const char *str, struct pt_regs *regs, long err)
394398

395399
return 0;
396400
}
401+
NOKPROBE_SYMBOL(__die_body);
402+
403+
int __die(const char *str, struct pt_regs *regs, long err)
404+
{
405+
__die_header(str, regs, err);
406+
return __die_body(str, regs, err);
407+
}
397408
NOKPROBE_SYMBOL(__die);
398409

399410
/*
@@ -410,6 +421,19 @@ void die(const char *str, struct pt_regs *regs, long err)
410421
oops_end(flags, regs, sig);
411422
}
412423

424+
void die_addr(const char *str, struct pt_regs *regs, long err, long gp_addr)
425+
{
426+
unsigned long flags = oops_begin();
427+
int sig = SIGSEGV;
428+
429+
__die_header(str, regs, err);
430+
if (gp_addr)
431+
kasan_non_canonical_hook(gp_addr);
432+
if (__die_body(str, regs, err))
433+
sig = 0;
434+
oops_end(flags, regs, sig);
435+
}
436+
413437
void show_regs(struct pt_regs *regs)
414438
{
415439
show_regs_print_info(KERN_DEFAULT);

arch/x86/kernel/nmi.c

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -104,18 +104,22 @@ static int __init nmi_warning_debugfs(void)
104104
}
105105
fs_initcall(nmi_warning_debugfs);
106106

107-
static void nmi_max_handler(struct irq_work *w)
107+
static void nmi_check_duration(struct nmiaction *action, u64 duration)
108108
{
109-
struct nmiaction *a = container_of(w, struct nmiaction, irq_work);
109+
u64 whole_msecs = READ_ONCE(action->max_duration);
110110
int remainder_ns, decimal_msecs;
111-
u64 whole_msecs = READ_ONCE(a->max_duration);
111+
112+
if (duration < nmi_longest_ns || duration < action->max_duration)
113+
return;
114+
115+
action->max_duration = duration;
112116

113117
remainder_ns = do_div(whole_msecs, (1000 * 1000));
114118
decimal_msecs = remainder_ns / 1000;
115119

116120
printk_ratelimited(KERN_INFO
117121
"INFO: NMI handler (%ps) took too long to run: %lld.%03d msecs\n",
118-
a->handler, whole_msecs, decimal_msecs);
122+
action->handler, whole_msecs, decimal_msecs);
119123
}
120124

121125
static int nmi_handle(unsigned int type, struct pt_regs *regs)
@@ -142,11 +146,7 @@ static int nmi_handle(unsigned int type, struct pt_regs *regs)
142146
delta = sched_clock() - delta;
143147
trace_nmi_handler(a->handler, (int)delta, thishandled);
144148

145-
if (delta < nmi_longest_ns || delta < a->max_duration)
146-
continue;
147-
148-
a->max_duration = delta;
149-
irq_work_queue(&a->irq_work);
149+
nmi_check_duration(a, delta);
150150
}
151151

152152
rcu_read_unlock();
@@ -164,8 +164,6 @@ int __register_nmi_handler(unsigned int type, struct nmiaction *action)
164164
if (!action->handler)
165165
return -EINVAL;
166166

167-
init_irq_work(&action->irq_work, nmi_max_handler);
168-
169167
raw_spin_lock_irqsave(&desc->lock, flags);
170168

171169
/*

arch/x86/kernel/traps.c

Lines changed: 89 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@
5656
#include <asm/mpx.h>
5757
#include <asm/vm86.h>
5858
#include <asm/umip.h>
59+
#include <asm/insn.h>
60+
#include <asm/insn-eval.h>
5961

6062
#ifdef CONFIG_X86_64
6163
#include <asm/x86_init.h>
@@ -518,11 +520,57 @@ dotraplinkage void do_bounds(struct pt_regs *regs, long error_code)
518520
do_trap(X86_TRAP_BR, SIGSEGV, "bounds", regs, error_code, 0, NULL);
519521
}
520522

521-
dotraplinkage void
522-
do_general_protection(struct pt_regs *regs, long error_code)
523+
enum kernel_gp_hint {
524+
GP_NO_HINT,
525+
GP_NON_CANONICAL,
526+
GP_CANONICAL
527+
};
528+
529+
/*
530+
* When an uncaught #GP occurs, try to determine the memory address accessed by
531+
* the instruction and return that address to the caller. Also, try to figure
532+
* out whether any part of the access to that address was non-canonical.
533+
*/
534+
static enum kernel_gp_hint get_kernel_gp_address(struct pt_regs *regs,
535+
unsigned long *addr)
523536
{
524-
const char *desc = "general protection fault";
537+
u8 insn_buf[MAX_INSN_SIZE];
538+
struct insn insn;
539+
540+
if (probe_kernel_read(insn_buf, (void *)regs->ip, MAX_INSN_SIZE))
541+
return GP_NO_HINT;
542+
543+
kernel_insn_init(&insn, insn_buf, MAX_INSN_SIZE);
544+
insn_get_modrm(&insn);
545+
insn_get_sib(&insn);
546+
547+
*addr = (unsigned long)insn_get_addr_ref(&insn, regs);
548+
if (*addr == -1UL)
549+
return GP_NO_HINT;
550+
551+
#ifdef CONFIG_X86_64
552+
/*
553+
* Check that:
554+
* - the operand is not in the kernel half
555+
* - the last byte of the operand is not in the user canonical half
556+
*/
557+
if (*addr < ~__VIRTUAL_MASK &&
558+
*addr + insn.opnd_bytes - 1 > __VIRTUAL_MASK)
559+
return GP_NON_CANONICAL;
560+
#endif
561+
562+
return GP_CANONICAL;
563+
}
564+
565+
#define GPFSTR "general protection fault"
566+
567+
dotraplinkage void do_general_protection(struct pt_regs *regs, long error_code)
568+
{
569+
char desc[sizeof(GPFSTR) + 50 + 2*sizeof(unsigned long) + 1] = GPFSTR;
570+
enum kernel_gp_hint hint = GP_NO_HINT;
525571
struct task_struct *tsk;
572+
unsigned long gp_addr;
573+
int ret;
526574

527575
RCU_LOCKDEP_WARN(!rcu_is_watching(), "entry code didn't wake RCU");
528576
cond_local_irq_enable(regs);
@@ -539,34 +587,56 @@ do_general_protection(struct pt_regs *regs, long error_code)
539587
}
540588

541589
tsk = current;
542-
if (!user_mode(regs)) {
543-
if (fixup_exception(regs, X86_TRAP_GP, error_code, 0))
544-
return;
545590

591+
if (user_mode(regs)) {
546592
tsk->thread.error_code = error_code;
547593
tsk->thread.trap_nr = X86_TRAP_GP;
548594

549-
/*
550-
* To be potentially processing a kprobe fault and to
551-
* trust the result from kprobe_running(), we have to
552-
* be non-preemptible.
553-
*/
554-
if (!preemptible() && kprobe_running() &&
555-
kprobe_fault_handler(regs, X86_TRAP_GP))
556-
return;
595+
show_signal(tsk, SIGSEGV, "", desc, regs, error_code);
596+
force_sig(SIGSEGV);
557597

558-
if (notify_die(DIE_GPF, desc, regs, error_code,
559-
X86_TRAP_GP, SIGSEGV) != NOTIFY_STOP)
560-
die(desc, regs, error_code);
561598
return;
562599
}
563600

601+
if (fixup_exception(regs, X86_TRAP_GP, error_code, 0))
602+
return;
603+
564604
tsk->thread.error_code = error_code;
565605
tsk->thread.trap_nr = X86_TRAP_GP;
566606

567-
show_signal(tsk, SIGSEGV, "", desc, regs, error_code);
607+
/*
608+
* To be potentially processing a kprobe fault and to trust the result
609+
* from kprobe_running(), we have to be non-preemptible.
610+
*/
611+
if (!preemptible() &&
612+
kprobe_running() &&
613+
kprobe_fault_handler(regs, X86_TRAP_GP))
614+
return;
615+
616+
ret = notify_die(DIE_GPF, desc, regs, error_code, X86_TRAP_GP, SIGSEGV);
617+
if (ret == NOTIFY_STOP)
618+
return;
619+
620+
if (error_code)
621+
snprintf(desc, sizeof(desc), "segment-related " GPFSTR);
622+
else
623+
hint = get_kernel_gp_address(regs, &gp_addr);
624+
625+
if (hint != GP_NO_HINT)
626+
snprintf(desc, sizeof(desc), GPFSTR ", %s 0x%lx",
627+
(hint == GP_NON_CANONICAL) ? "probably for non-canonical address"
628+
: "maybe for address",
629+
gp_addr);
630+
631+
/*
632+
* KASAN is interested only in the non-canonical case, clear it
633+
* otherwise.
634+
*/
635+
if (hint != GP_NON_CANONICAL)
636+
gp_addr = 0;
637+
638+
die_addr(desc, regs, error_code, gp_addr);
568639

569-
force_sig(SIGSEGV);
570640
}
571641
NOKPROBE_SYMBOL(do_general_protection);
572642

arch/x86/lib/insn-eval.c

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ static bool check_seg_overrides(struct insn *insn, int regoff)
155155
*/
156156
static int resolve_default_seg(struct insn *insn, struct pt_regs *regs, int off)
157157
{
158-
if (user_64bit_mode(regs))
158+
if (any_64bit_mode(regs))
159159
return INAT_SEG_REG_IGNORE;
160160
/*
161161
* Resolve the default segment register as described in Section 3.7.4
@@ -266,7 +266,7 @@ static int resolve_seg_reg(struct insn *insn, struct pt_regs *regs, int regoff)
266266
* which may be invalid at this point.
267267
*/
268268
if (regoff == offsetof(struct pt_regs, ip)) {
269-
if (user_64bit_mode(regs))
269+
if (any_64bit_mode(regs))
270270
return INAT_SEG_REG_IGNORE;
271271
else
272272
return INAT_SEG_REG_CS;
@@ -289,7 +289,7 @@ static int resolve_seg_reg(struct insn *insn, struct pt_regs *regs, int regoff)
289289
* In long mode, segment override prefixes are ignored, except for
290290
* overrides for FS and GS.
291291
*/
292-
if (user_64bit_mode(regs)) {
292+
if (any_64bit_mode(regs)) {
293293
if (idx != INAT_SEG_REG_FS &&
294294
idx != INAT_SEG_REG_GS)
295295
idx = INAT_SEG_REG_IGNORE;
@@ -646,23 +646,27 @@ unsigned long insn_get_seg_base(struct pt_regs *regs, int seg_reg_idx)
646646
*/
647647
return (unsigned long)(sel << 4);
648648

649-
if (user_64bit_mode(regs)) {
649+
if (any_64bit_mode(regs)) {
650650
/*
651651
* Only FS or GS will have a base address, the rest of
652652
* the segments' bases are forced to 0.
653653
*/
654654
unsigned long base;
655655

656-
if (seg_reg_idx == INAT_SEG_REG_FS)
656+
if (seg_reg_idx == INAT_SEG_REG_FS) {
657657
rdmsrl(MSR_FS_BASE, base);
658-
else if (seg_reg_idx == INAT_SEG_REG_GS)
658+
} else if (seg_reg_idx == INAT_SEG_REG_GS) {
659659
/*
660660
* swapgs was called at the kernel entry point. Thus,
661661
* MSR_KERNEL_GS_BASE will have the user-space GS base.
662662
*/
663-
rdmsrl(MSR_KERNEL_GS_BASE, base);
664-
else
663+
if (user_mode(regs))
664+
rdmsrl(MSR_KERNEL_GS_BASE, base);
665+
else
666+
rdmsrl(MSR_GS_BASE, base);
667+
} else {
665668
base = 0;
669+
}
666670
return base;
667671
}
668672

@@ -703,7 +707,7 @@ static unsigned long get_seg_limit(struct pt_regs *regs, int seg_reg_idx)
703707
if (sel < 0)
704708
return 0;
705709

706-
if (user_64bit_mode(regs) || v8086_mode(regs))
710+
if (any_64bit_mode(regs) || v8086_mode(regs))
707711
return -1L;
708712

709713
if (!sel)
@@ -948,7 +952,7 @@ static int get_eff_addr_modrm(struct insn *insn, struct pt_regs *regs,
948952
* following instruction.
949953
*/
950954
if (*regoff == -EDOM) {
951-
if (user_64bit_mode(regs))
955+
if (any_64bit_mode(regs))
952956
tmp = regs->ip + insn->length;
953957
else
954958
tmp = 0;
@@ -1250,7 +1254,7 @@ static void __user *get_addr_ref_32(struct insn *insn, struct pt_regs *regs)
12501254
* After computed, the effective address is treated as an unsigned
12511255
* quantity.
12521256
*/
1253-
if (!user_64bit_mode(regs) && ((unsigned int)eff_addr > seg_limit))
1257+
if (!any_64bit_mode(regs) && ((unsigned int)eff_addr > seg_limit))
12541258
goto out;
12551259

12561260
/*

0 commit comments

Comments
 (0)