diff options
| -rw-r--r-- | arch/x86/kernel/entry_64.S | 45 | ||||
| -rw-r--r-- | arch/x86/kernel/traps.c | 29 |
2 files changed, 48 insertions, 26 deletions
diff --git a/arch/x86/kernel/entry_64.S b/arch/x86/kernel/entry_64.S index 49a0c1781253..c0226ab54106 100644 --- a/arch/x86/kernel/entry_64.S +++ b/arch/x86/kernel/entry_64.S | |||
| @@ -830,8 +830,13 @@ ENTRY(native_iret) | |||
| 830 | 830 | ||
| 831 | .global native_irq_return_iret | 831 | .global native_irq_return_iret |
| 832 | native_irq_return_iret: | 832 | native_irq_return_iret: |
| 833 | /* | ||
| 834 | * This may fault. Non-paranoid faults on return to userspace are | ||
| 835 | * handled by fixup_bad_iret. These include #SS, #GP, and #NP. | ||
| 836 | * Double-faults due to espfix64 are handled in do_double_fault. | ||
| 837 | * Other faults here are fatal. | ||
| 838 | */ | ||
| 833 | iretq | 839 | iretq |
| 834 | _ASM_EXTABLE(native_irq_return_iret, bad_iret) | ||
| 835 | 840 | ||
| 836 | #ifdef CONFIG_X86_ESPFIX64 | 841 | #ifdef CONFIG_X86_ESPFIX64 |
| 837 | native_irq_return_ldt: | 842 | native_irq_return_ldt: |
| @@ -859,25 +864,6 @@ native_irq_return_ldt: | |||
| 859 | jmp native_irq_return_iret | 864 | jmp native_irq_return_iret |
| 860 | #endif | 865 | #endif |
| 861 | 866 | ||
| 862 | .section .fixup,"ax" | ||
| 863 | bad_iret: | ||
| 864 | /* | ||
| 865 | * The iret traps when the %cs or %ss being restored is bogus. | ||
| 866 | * We've lost the original trap vector and error code. | ||
| 867 | * #GPF is the most likely one to get for an invalid selector. | ||
| 868 | * So pretend we completed the iret and took the #GPF in user mode. | ||
| 869 | * | ||
| 870 | * We are now running with the kernel GS after exception recovery. | ||
| 871 | * But error_entry expects us to have user GS to match the user %cs, | ||
| 872 | * so swap back. | ||
| 873 | */ | ||
| 874 | pushq $0 | ||
| 875 | |||
| 876 | SWAPGS | ||
| 877 | jmp general_protection | ||
| 878 | |||
| 879 | .previous | ||
| 880 | |||
| 881 | /* edi: workmask, edx: work */ | 867 | /* edi: workmask, edx: work */ |
| 882 | retint_careful: | 868 | retint_careful: |
| 883 | CFI_RESTORE_STATE | 869 | CFI_RESTORE_STATE |
| @@ -1369,17 +1355,16 @@ error_sti: | |||
| 1369 | 1355 | ||
| 1370 | /* | 1356 | /* |
| 1371 | * There are two places in the kernel that can potentially fault with | 1357 | * There are two places in the kernel that can potentially fault with |
| 1372 | * usergs. Handle them here. The exception handlers after iret run with | 1358 | * usergs. Handle them here. B stepping K8s sometimes report a |
| 1373 | * kernel gs again, so don't set the user space flag. B stepping K8s | 1359 | * truncated RIP for IRET exceptions returning to compat mode. Check |
| 1374 | * sometimes report an truncated RIP for IRET exceptions returning to | 1360 | * for these here too. |
| 1375 | * compat mode. Check for these here too. | ||
| 1376 | */ | 1361 | */ |
| 1377 | error_kernelspace: | 1362 | error_kernelspace: |
| 1378 | CFI_REL_OFFSET rcx, RCX+8 | 1363 | CFI_REL_OFFSET rcx, RCX+8 |
| 1379 | incl %ebx | 1364 | incl %ebx |
| 1380 | leaq native_irq_return_iret(%rip),%rcx | 1365 | leaq native_irq_return_iret(%rip),%rcx |
| 1381 | cmpq %rcx,RIP+8(%rsp) | 1366 | cmpq %rcx,RIP+8(%rsp) |
| 1382 | je error_swapgs | 1367 | je error_bad_iret |
| 1383 | movl %ecx,%eax /* zero extend */ | 1368 | movl %ecx,%eax /* zero extend */ |
| 1384 | cmpq %rax,RIP+8(%rsp) | 1369 | cmpq %rax,RIP+8(%rsp) |
| 1385 | je bstep_iret | 1370 | je bstep_iret |
| @@ -1390,7 +1375,15 @@ error_kernelspace: | |||
| 1390 | bstep_iret: | 1375 | bstep_iret: |
| 1391 | /* Fix truncated RIP */ | 1376 | /* Fix truncated RIP */ |
| 1392 | movq %rcx,RIP+8(%rsp) | 1377 | movq %rcx,RIP+8(%rsp) |
| 1393 | jmp error_swapgs | 1378 | /* fall through */ |
| 1379 | |||
| 1380 | error_bad_iret: | ||
| 1381 | SWAPGS | ||
| 1382 | mov %rsp,%rdi | ||
| 1383 | call fixup_bad_iret | ||
| 1384 | mov %rax,%rsp | ||
| 1385 | decl %ebx /* Return to usergs */ | ||
| 1386 | jmp error_sti | ||
| 1394 | CFI_ENDPROC | 1387 | CFI_ENDPROC |
| 1395 | END(error_entry) | 1388 | END(error_entry) |
| 1396 | 1389 | ||
diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index 48035e9cdde9..de801f22128a 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c | |||
| @@ -407,6 +407,35 @@ asmlinkage __visible struct pt_regs *sync_regs(struct pt_regs *eregs) | |||
| 407 | return regs; | 407 | return regs; |
| 408 | } | 408 | } |
| 409 | NOKPROBE_SYMBOL(sync_regs); | 409 | NOKPROBE_SYMBOL(sync_regs); |
| 410 | |||
| 411 | struct bad_iret_stack { | ||
| 412 | void *error_entry_ret; | ||
| 413 | struct pt_regs regs; | ||
| 414 | }; | ||
| 415 | |||
| 416 | asmlinkage __visible | ||
| 417 | struct bad_iret_stack *fixup_bad_iret(struct bad_iret_stack *s) | ||
| 418 | { | ||
| 419 | /* | ||
| 420 | * This is called from entry_64.S early in handling a fault | ||
| 421 | * caused by a bad iret to user mode. To handle the fault | ||
| 422 | * correctly, we want move our stack frame to task_pt_regs | ||
| 423 | * and we want to pretend that the exception came from the | ||
| 424 | * iret target. | ||
| 425 | */ | ||
| 426 | struct bad_iret_stack *new_stack = | ||
| 427 | container_of(task_pt_regs(current), | ||
| 428 | struct bad_iret_stack, regs); | ||
| 429 | |||
| 430 | /* Copy the IRET target to the new stack. */ | ||
| 431 | memmove(&new_stack->regs.ip, (void *)s->regs.sp, 5*8); | ||
| 432 | |||
| 433 | /* Copy the remainder of the stack from the current stack. */ | ||
| 434 | memmove(new_stack, s, offsetof(struct bad_iret_stack, regs.ip)); | ||
| 435 | |||
| 436 | BUG_ON(!user_mode_vm(&new_stack->regs)); | ||
| 437 | return new_stack; | ||
| 438 | } | ||
| 410 | #endif | 439 | #endif |
| 411 | 440 | ||
| 412 | /* | 441 | /* |
