diff options
| -rw-r--r-- | arch/x86/hyperv/hv_init.c | 7 | ||||
| -rw-r--r-- | arch/x86/include/asm/page_64_types.h | 4 | ||||
| -rw-r--r-- | arch/x86/include/asm/unwind.h | 6 | ||||
| -rw-r--r-- | arch/x86/kernel/unwind_frame.c | 25 | ||||
| -rw-r--r-- | arch/x86/kernel/unwind_orc.c | 17 | ||||
| -rw-r--r-- | arch/x86/mm/pageattr.c | 4 | ||||
| -rw-r--r-- | include/asm-generic/vmlinux.lds.h | 2 |
7 files changed, 55 insertions, 10 deletions
diff --git a/arch/x86/hyperv/hv_init.c b/arch/x86/hyperv/hv_init.c index 7f2eed1fc81b..6461a16b4559 100644 --- a/arch/x86/hyperv/hv_init.c +++ b/arch/x86/hyperv/hv_init.c | |||
| @@ -407,6 +407,13 @@ void hyperv_cleanup(void) | |||
| 407 | /* Reset our OS id */ | 407 | /* Reset our OS id */ |
| 408 | wrmsrl(HV_X64_MSR_GUEST_OS_ID, 0); | 408 | wrmsrl(HV_X64_MSR_GUEST_OS_ID, 0); |
| 409 | 409 | ||
| 410 | /* | ||
| 411 | * Reset hypercall page reference before reset the page, | ||
| 412 | * let hypercall operations fail safely rather than | ||
| 413 | * panic the kernel for using invalid hypercall page | ||
| 414 | */ | ||
| 415 | hv_hypercall_pg = NULL; | ||
| 416 | |||
| 410 | /* Reset the hypercall page */ | 417 | /* Reset the hypercall page */ |
| 411 | hypercall_msr.as_uint64 = 0; | 418 | hypercall_msr.as_uint64 = 0; |
| 412 | wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64); | 419 | wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64); |
diff --git a/arch/x86/include/asm/page_64_types.h b/arch/x86/include/asm/page_64_types.h index 0ce558a8150d..8f657286d599 100644 --- a/arch/x86/include/asm/page_64_types.h +++ b/arch/x86/include/asm/page_64_types.h | |||
| @@ -7,11 +7,7 @@ | |||
| 7 | #endif | 7 | #endif |
| 8 | 8 | ||
| 9 | #ifdef CONFIG_KASAN | 9 | #ifdef CONFIG_KASAN |
| 10 | #ifdef CONFIG_KASAN_EXTRA | ||
| 11 | #define KASAN_STACK_ORDER 2 | ||
| 12 | #else | ||
| 13 | #define KASAN_STACK_ORDER 1 | 10 | #define KASAN_STACK_ORDER 1 |
| 14 | #endif | ||
| 15 | #else | 11 | #else |
| 16 | #define KASAN_STACK_ORDER 0 | 12 | #define KASAN_STACK_ORDER 0 |
| 17 | #endif | 13 | #endif |
diff --git a/arch/x86/include/asm/unwind.h b/arch/x86/include/asm/unwind.h index 1f86e1b0a5cd..499578f7e6d7 100644 --- a/arch/x86/include/asm/unwind.h +++ b/arch/x86/include/asm/unwind.h | |||
| @@ -23,6 +23,12 @@ struct unwind_state { | |||
| 23 | #elif defined(CONFIG_UNWINDER_FRAME_POINTER) | 23 | #elif defined(CONFIG_UNWINDER_FRAME_POINTER) |
| 24 | bool got_irq; | 24 | bool got_irq; |
| 25 | unsigned long *bp, *orig_sp, ip; | 25 | unsigned long *bp, *orig_sp, ip; |
| 26 | /* | ||
| 27 | * If non-NULL: The current frame is incomplete and doesn't contain a | ||
| 28 | * valid BP. When looking for the next frame, use this instead of the | ||
| 29 | * non-existent saved BP. | ||
| 30 | */ | ||
| 31 | unsigned long *next_bp; | ||
| 26 | struct pt_regs *regs; | 32 | struct pt_regs *regs; |
| 27 | #else | 33 | #else |
| 28 | unsigned long *sp; | 34 | unsigned long *sp; |
diff --git a/arch/x86/kernel/unwind_frame.c b/arch/x86/kernel/unwind_frame.c index 3dc26f95d46e..9b9fd4826e7a 100644 --- a/arch/x86/kernel/unwind_frame.c +++ b/arch/x86/kernel/unwind_frame.c | |||
| @@ -320,10 +320,14 @@ bool unwind_next_frame(struct unwind_state *state) | |||
| 320 | } | 320 | } |
| 321 | 321 | ||
| 322 | /* Get the next frame pointer: */ | 322 | /* Get the next frame pointer: */ |
| 323 | if (state->regs) | 323 | if (state->next_bp) { |
| 324 | next_bp = state->next_bp; | ||
| 325 | state->next_bp = NULL; | ||
| 326 | } else if (state->regs) { | ||
| 324 | next_bp = (unsigned long *)state->regs->bp; | 327 | next_bp = (unsigned long *)state->regs->bp; |
| 325 | else | 328 | } else { |
| 326 | next_bp = (unsigned long *)READ_ONCE_TASK_STACK(state->task, *state->bp); | 329 | next_bp = (unsigned long *)READ_ONCE_TASK_STACK(state->task, *state->bp); |
| 330 | } | ||
| 327 | 331 | ||
| 328 | /* Move to the next frame if it's safe: */ | 332 | /* Move to the next frame if it's safe: */ |
| 329 | if (!update_stack_state(state, next_bp)) | 333 | if (!update_stack_state(state, next_bp)) |
| @@ -398,6 +402,21 @@ void __unwind_start(struct unwind_state *state, struct task_struct *task, | |||
| 398 | 402 | ||
| 399 | bp = get_frame_pointer(task, regs); | 403 | bp = get_frame_pointer(task, regs); |
| 400 | 404 | ||
| 405 | /* | ||
| 406 | * If we crash with IP==0, the last successfully executed instruction | ||
| 407 | * was probably an indirect function call with a NULL function pointer. | ||
| 408 | * That means that SP points into the middle of an incomplete frame: | ||
| 409 | * *SP is a return pointer, and *(SP-sizeof(unsigned long)) is where we | ||
| 410 | * would have written a frame pointer if we hadn't crashed. | ||
| 411 | * Pretend that the frame is complete and that BP points to it, but save | ||
| 412 | * the real BP so that we can use it when looking for the next frame. | ||
| 413 | */ | ||
| 414 | if (regs && regs->ip == 0 && | ||
| 415 | (unsigned long *)kernel_stack_pointer(regs) >= first_frame) { | ||
| 416 | state->next_bp = bp; | ||
| 417 | bp = ((unsigned long *)kernel_stack_pointer(regs)) - 1; | ||
| 418 | } | ||
| 419 | |||
| 401 | /* Initialize stack info and make sure the frame data is accessible: */ | 420 | /* Initialize stack info and make sure the frame data is accessible: */ |
| 402 | get_stack_info(bp, state->task, &state->stack_info, | 421 | get_stack_info(bp, state->task, &state->stack_info, |
| 403 | &state->stack_mask); | 422 | &state->stack_mask); |
| @@ -410,7 +429,7 @@ void __unwind_start(struct unwind_state *state, struct task_struct *task, | |||
| 410 | */ | 429 | */ |
| 411 | while (!unwind_done(state) && | 430 | while (!unwind_done(state) && |
| 412 | (!on_stack(&state->stack_info, first_frame, sizeof(long)) || | 431 | (!on_stack(&state->stack_info, first_frame, sizeof(long)) || |
| 413 | state->bp < first_frame)) | 432 | (state->next_bp == NULL && state->bp < first_frame))) |
| 414 | unwind_next_frame(state); | 433 | unwind_next_frame(state); |
| 415 | } | 434 | } |
| 416 | EXPORT_SYMBOL_GPL(__unwind_start); | 435 | EXPORT_SYMBOL_GPL(__unwind_start); |
diff --git a/arch/x86/kernel/unwind_orc.c b/arch/x86/kernel/unwind_orc.c index 26038eacf74a..89be1be1790c 100644 --- a/arch/x86/kernel/unwind_orc.c +++ b/arch/x86/kernel/unwind_orc.c | |||
| @@ -113,6 +113,20 @@ static struct orc_entry *orc_ftrace_find(unsigned long ip) | |||
| 113 | } | 113 | } |
| 114 | #endif | 114 | #endif |
| 115 | 115 | ||
| 116 | /* | ||
| 117 | * If we crash with IP==0, the last successfully executed instruction | ||
| 118 | * was probably an indirect function call with a NULL function pointer, | ||
| 119 | * and we don't have unwind information for NULL. | ||
| 120 | * This hardcoded ORC entry for IP==0 allows us to unwind from a NULL function | ||
| 121 | * pointer into its parent and then continue normally from there. | ||
| 122 | */ | ||
| 123 | static struct orc_entry null_orc_entry = { | ||
| 124 | .sp_offset = sizeof(long), | ||
| 125 | .sp_reg = ORC_REG_SP, | ||
| 126 | .bp_reg = ORC_REG_UNDEFINED, | ||
| 127 | .type = ORC_TYPE_CALL | ||
| 128 | }; | ||
| 129 | |||
| 116 | static struct orc_entry *orc_find(unsigned long ip) | 130 | static struct orc_entry *orc_find(unsigned long ip) |
| 117 | { | 131 | { |
| 118 | static struct orc_entry *orc; | 132 | static struct orc_entry *orc; |
| @@ -120,6 +134,9 @@ static struct orc_entry *orc_find(unsigned long ip) | |||
| 120 | if (!orc_init) | 134 | if (!orc_init) |
| 121 | return NULL; | 135 | return NULL; |
| 122 | 136 | ||
| 137 | if (ip == 0) | ||
| 138 | return &null_orc_entry; | ||
| 139 | |||
| 123 | /* For non-init vmlinux addresses, use the fast lookup table: */ | 140 | /* For non-init vmlinux addresses, use the fast lookup table: */ |
| 124 | if (ip >= LOOKUP_START_IP && ip < LOOKUP_STOP_IP) { | 141 | if (ip >= LOOKUP_START_IP && ip < LOOKUP_STOP_IP) { |
| 125 | unsigned int idx, start, stop; | 142 | unsigned int idx, start, stop; |
diff --git a/arch/x86/mm/pageattr.c b/arch/x86/mm/pageattr.c index 14e6119838a6..4c570612e24e 100644 --- a/arch/x86/mm/pageattr.c +++ b/arch/x86/mm/pageattr.c | |||
| @@ -738,7 +738,7 @@ static int __should_split_large_page(pte_t *kpte, unsigned long address, | |||
| 738 | { | 738 | { |
| 739 | unsigned long numpages, pmask, psize, lpaddr, pfn, old_pfn; | 739 | unsigned long numpages, pmask, psize, lpaddr, pfn, old_pfn; |
| 740 | pgprot_t old_prot, new_prot, req_prot, chk_prot; | 740 | pgprot_t old_prot, new_prot, req_prot, chk_prot; |
| 741 | pte_t new_pte, old_pte, *tmp; | 741 | pte_t new_pte, *tmp; |
| 742 | enum pg_level level; | 742 | enum pg_level level; |
| 743 | 743 | ||
| 744 | /* | 744 | /* |
| @@ -781,7 +781,7 @@ static int __should_split_large_page(pte_t *kpte, unsigned long address, | |||
| 781 | * Convert protection attributes to 4k-format, as cpa->mask* are set | 781 | * Convert protection attributes to 4k-format, as cpa->mask* are set |
| 782 | * up accordingly. | 782 | * up accordingly. |
| 783 | */ | 783 | */ |
| 784 | old_pte = *kpte; | 784 | |
| 785 | /* Clear PSE (aka _PAGE_PAT) and move PAT bit to correct position */ | 785 | /* Clear PSE (aka _PAGE_PAT) and move PAT bit to correct position */ |
| 786 | req_prot = pgprot_large_2_4k(old_prot); | 786 | req_prot = pgprot_large_2_4k(old_prot); |
| 787 | 787 | ||
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index 3d7a6a9c2370..f8f6f04c4453 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h | |||
| @@ -733,7 +733,7 @@ | |||
| 733 | KEEP(*(.orc_unwind_ip)) \ | 733 | KEEP(*(.orc_unwind_ip)) \ |
| 734 | __stop_orc_unwind_ip = .; \ | 734 | __stop_orc_unwind_ip = .; \ |
| 735 | } \ | 735 | } \ |
| 736 | . = ALIGN(6); \ | 736 | . = ALIGN(2); \ |
| 737 | .orc_unwind : AT(ADDR(.orc_unwind) - LOAD_OFFSET) { \ | 737 | .orc_unwind : AT(ADDR(.orc_unwind) - LOAD_OFFSET) { \ |
| 738 | __start_orc_unwind = .; \ | 738 | __start_orc_unwind = .; \ |
| 739 | KEEP(*(.orc_unwind)) \ | 739 | KEEP(*(.orc_unwind)) \ |
