Skip to content

Commit

Permalink
x86/fault: Correct a few user vs kernel checks wrt WRUSS
Browse files Browse the repository at this point in the history
In general, page fault errors for WRUSS should be just like get_user(),
etc.  Fix three bugs in this area:

We have a comment that says that, if we can't handle a page fault on a user
address due to OOM, we will skip the OOM-kill-and-retry logic.  The code
checked kernel *privilege*, not kernel mode, so it missed WRUSS.  This
means that we would malfunction if we got OOM on a WRUSS fault -- this
would be a kernel-mode, user-privilege fault, and we would invoke the OOM
killer and retry.

A failed user access from kernel while a fatal signal is pending should
fail even if the instruction in question was WRUSS.

do_sigbus() should not send SIGBUS for WRUSS -- it should handle it like
any other kernel mode failure.

Cc: Dave Hansen <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Signed-off-by: Andy Lutomirski <[email protected]>
  • Loading branch information
amluto authored and intel-lab-lkp committed Jan 31, 2021
1 parent ca9fd73 commit 5085e22
Showing 1 changed file with 11 additions and 4 deletions.
15 changes: 11 additions & 4 deletions arch/x86/mm/fault.c
Original file line number Diff line number Diff line change
Expand Up @@ -908,7 +908,7 @@ do_sigbus(struct pt_regs *regs, unsigned long error_code, unsigned long address,
vm_fault_t fault)
{
/* Kernel mode? Handle exceptions or die: */
if (!(error_code & X86_PF_USER)) {
if (!user_mode(regs)) {
no_context(regs, error_code, address, SIGBUS, BUS_ADRERR);
return;
}
Expand Down Expand Up @@ -1180,7 +1180,14 @@ do_kern_addr_fault(struct pt_regs *regs, unsigned long hw_error_code,
}
NOKPROBE_SYMBOL(do_kern_addr_fault);

/* Handle faults in the user portion of the address space */
/*
* Handle faults in the user portion of the address space. Nothing in here
* should check X86_PF_USER without a specific justification: for almost
* all purposes, we should treat a normal kernel access to user memory
* (e.g. get_user(), put_user(), etc.) the same as the WRUSS instruction.
* The one exception is AC flag handling, which is, per the x86
* architecture, special for WRUSS.
*/
static inline
void do_user_addr_fault(struct pt_regs *regs,
unsigned long error_code,
Expand Down Expand Up @@ -1369,14 +1376,14 @@ void do_user_addr_fault(struct pt_regs *regs,
if (likely(!(fault & VM_FAULT_ERROR)))
return;

if (fatal_signal_pending(current) && !(error_code & X86_PF_USER)) {
if (fatal_signal_pending(current) && !user_mode(regs)) {
no_context(regs, error_code, address, 0, 0);
return;
}

if (fault & VM_FAULT_OOM) {
/* Kernel mode? Handle exceptions or die: */
if (!(error_code & X86_PF_USER)) {
if (!user_mode(regs)) {
no_context(regs, error_code, address,
SIGSEGV, SEGV_MAPERR);
return;
Expand Down

0 comments on commit 5085e22

Please sign in to comment.