Skip to content

Commit c0ab7ff

Browse files
aeglsuryasaimadhu
authored andcommitted
x86/mce: Recover from poison found while copying from user space
Existing kernel code can only recover from a machine check on code that is tagged in the exception table with a fault handling recovery path. Add two new fields in the task structure to pass information from machine check handler to the "task_work" that is queued to run before the task returns to user mode: + mce_vaddr: will be initialized to the user virtual address of the fault in the case where the fault occurred in the kernel copying data from a user address. This is so that kill_me_maybe() can provide that information to the user SIGBUS handler. + mce_kflags: copy of the struct mce.kflags needed by kill_me_maybe() to determine if mce_vaddr is applicable to this error. Add code to recover from a machine check while copying data from user space to the kernel. Action for this case is the same as if the user touched the poison directly; unmap the page and send a SIGBUS to the task. Use a new helper function to share common code between the "fault in user mode" case and the "fault while copying from user" case. New code paths will be activated by the next patch which sets MCE_IN_KERNEL_COPYIN. Suggested-by: Borislav Petkov <bp@alien8.de> Signed-off-by: Tony Luck <tony.luck@intel.com> Signed-off-by: Borislav Petkov <bp@suse.de> Link: https://lkml.kernel.org/r/20201006210910.21062-6-tony.luck@intel.com
1 parent a2f7340 commit c0ab7ff

2 files changed

Lines changed: 22 additions & 7 deletions

File tree

arch/x86/kernel/cpu/mce/core.c

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1260,6 +1260,21 @@ static void kill_me_maybe(struct callback_head *cb)
12601260
kill_me_now(cb);
12611261
}
12621262

1263+
static void queue_task_work(struct mce *m, int kill_it)
1264+
{
1265+
current->mce_addr = m->addr;
1266+
current->mce_kflags = m->kflags;
1267+
current->mce_ripv = !!(m->mcgstatus & MCG_STATUS_RIPV);
1268+
current->mce_whole_page = whole_page(m);
1269+
1270+
if (kill_it)
1271+
current->mce_kill_me.func = kill_me_now;
1272+
else
1273+
current->mce_kill_me.func = kill_me_maybe;
1274+
1275+
task_work_add(current, &current->mce_kill_me, true);
1276+
}
1277+
12631278
/*
12641279
* The actual machine check handler. This only handles real
12651280
* exceptions when something got corrupted coming in through int 18.
@@ -1401,13 +1416,8 @@ noinstr void do_machine_check(struct pt_regs *regs)
14011416
/* If this triggers there is no way to recover. Die hard. */
14021417
BUG_ON(!on_thread_stack() || !user_mode(regs));
14031418

1404-
current->mce_addr = m.addr;
1405-
current->mce_ripv = !!(m.mcgstatus & MCG_STATUS_RIPV);
1406-
current->mce_whole_page = whole_page(&m);
1407-
current->mce_kill_me.func = kill_me_maybe;
1408-
if (kill_it)
1409-
current->mce_kill_me.func = kill_me_now;
1410-
task_work_add(current, &current->mce_kill_me, true);
1419+
queue_task_work(&m, kill_it);
1420+
14111421
} else {
14121422
/*
14131423
* Handle an MCE which has happened in kernel space but from
@@ -1422,6 +1432,9 @@ noinstr void do_machine_check(struct pt_regs *regs)
14221432
if (!fixup_exception(regs, X86_TRAP_MC, 0, 0))
14231433
mce_panic("Failed kernel mode recovery", &m, msg);
14241434
}
1435+
1436+
if (m.kflags & MCE_IN_KERNEL_COPYIN)
1437+
queue_task_work(&m, kill_it);
14251438
}
14261439
out:
14271440
mce_wrmsrl(MSR_IA32_MCG_STATUS, 0);

include/linux/sched.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1308,6 +1308,8 @@ struct task_struct {
13081308
#endif
13091309

13101310
#ifdef CONFIG_X86_MCE
1311+
void __user *mce_vaddr;
1312+
__u64 mce_kflags;
13111313
u64 mce_addr;
13121314
__u64 mce_ripv : 1,
13131315
mce_whole_page : 1,

0 commit comments

Comments
 (0)