Skip to content

Commit

Permalink
x86,objtool: Split UNWIND_HINT_EMPTY in two
Browse files Browse the repository at this point in the history
Mark reported that the ORC unwinder incorrectly marks an unwind as
reliable when the unwind terminates prematurely in the dark corners of
return_to_handler() due to lack of information about the next frame.

The problem is UNWIND_HINT_EMPTY is used in two different situations:

  1) The end of the kernel stack unwind before hitting user entry, boot
     code, or fork entry

  2) A blind spot in ORC coverage where the unwinder has to bail due to
     lack of information about the next frame

The ORC unwinder has no way to tell the difference between the two.
When it encounters an undefined stack state with 'end=1', it blindly
marks the stack reliable, which can break the livepatch consistency
model.

Fix it by splitting UNWIND_HINT_EMPTY into UNWIND_HINT_UNDEFINED and
UNWIND_HINT_END_OF_STACK.

Reported-by: Mark Rutland <[email protected]>
Signed-off-by: Josh Poimboeuf <[email protected]>
Signed-off-by: Peter Zijlstra (Intel) <[email protected]>
Acked-by: Steven Rostedt (Google) <[email protected]>
Acked-by: Peter Zijlstra (Intel) <[email protected]>
Link: https://lore.kernel.org/r/fd6212c8b450d3564b855e1cb48404d6277b4d9f.1677683419.git.jpoimboe@kernel.org
  • Loading branch information
jpoimboe authored and Peter Zijlstra committed Mar 23, 2023
1 parent 4708ea1 commit fb79944
Show file tree
Hide file tree
Showing 20 changed files with 116 additions and 95 deletions.
2 changes: 1 addition & 1 deletion Documentation/livepatch/reliable-stacktrace.rst
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ trampoline or return trampoline. For example, considering the x86_64
.. code-block:: none
SYM_CODE_START(return_to_handler)
UNWIND_HINT_EMPTY
UNWIND_HINT_UNDEFINED
subq $24, %rsp
/* Save the return values */
Expand Down
12 changes: 6 additions & 6 deletions arch/x86/entry/entry_64.S
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ syscall_return_via_sysret:
*/
movq %rsp, %rdi
movq PER_CPU_VAR(cpu_tss_rw + TSS_sp0), %rsp
UNWIND_HINT_EMPTY
UNWIND_HINT_END_OF_STACK

pushq RSP-RDI(%rdi) /* RSP */
pushq (%rdi) /* RDI */
Expand Down Expand Up @@ -286,7 +286,7 @@ SYM_FUNC_END(__switch_to_asm)
.pushsection .text, "ax"
__FUNC_ALIGN
SYM_CODE_START_NOALIGN(ret_from_fork)
UNWIND_HINT_EMPTY
UNWIND_HINT_END_OF_STACK
ANNOTATE_NOENDBR // copy_thread
CALL_DEPTH_ACCOUNT
movq %rax, %rdi
Expand All @@ -303,7 +303,7 @@ SYM_CODE_START_NOALIGN(ret_from_fork)

1:
/* kernel thread */
UNWIND_HINT_EMPTY
UNWIND_HINT_END_OF_STACK
movq %r12, %rdi
CALL_NOSPEC rbx
/*
Expand Down Expand Up @@ -643,7 +643,7 @@ SYM_INNER_LABEL(swapgs_restore_regs_and_return_to_usermode, SYM_L_GLOBAL)
*/
movq %rsp, %rdi
movq PER_CPU_VAR(cpu_tss_rw + TSS_sp0), %rsp
UNWIND_HINT_EMPTY
UNWIND_HINT_END_OF_STACK

/* Copy the IRET frame to the trampoline stack. */
pushq 6*8(%rdi) /* SS */
Expand Down Expand Up @@ -869,7 +869,7 @@ SYM_CODE_END(exc_xen_hypervisor_callback)
*/
__FUNC_ALIGN
SYM_CODE_START_NOALIGN(xen_failsafe_callback)
UNWIND_HINT_EMPTY
UNWIND_HINT_UNDEFINED
ENDBR
movl %ds, %ecx
cmpw %cx, 0x10(%rsp)
Expand Down Expand Up @@ -1520,7 +1520,7 @@ SYM_CODE_END(asm_exc_nmi)
* MSRs to fully disable 32-bit SYSCALL.
*/
SYM_CODE_START(ignore_sysret)
UNWIND_HINT_EMPTY
UNWIND_HINT_END_OF_STACK
ENDBR
mov $-ENOSYS, %eax
sysretl
Expand Down
14 changes: 7 additions & 7 deletions arch/x86/include/asm/orc_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,11 @@
#define ORC_REG_SP_INDIRECT 9
#define ORC_REG_MAX 15

#define ORC_TYPE_CALL 0
#define ORC_TYPE_REGS 1
#define ORC_TYPE_REGS_PARTIAL 2
#define ORC_TYPE_UNDEFINED 0
#define ORC_TYPE_END_OF_STACK 1
#define ORC_TYPE_CALL 2
#define ORC_TYPE_REGS 3
#define ORC_TYPE_REGS_PARTIAL 4

#ifndef __ASSEMBLY__
#include <asm/byteorder.h>
Expand All @@ -60,16 +62,14 @@ struct orc_entry {
#if defined(__LITTLE_ENDIAN_BITFIELD)
unsigned sp_reg:4;
unsigned bp_reg:4;
unsigned type:2;
unsigned type:3;
unsigned signal:1;
unsigned end:1;
#elif defined(__BIG_ENDIAN_BITFIELD)
unsigned bp_reg:4;
unsigned sp_reg:4;
unsigned unused:4;
unsigned end:1;
unsigned signal:1;
unsigned type:2;
unsigned type:3;
#endif
} __packed;

Expand Down
12 changes: 8 additions & 4 deletions arch/x86/include/asm/unwind_hints.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,17 @@

#ifdef __ASSEMBLY__

.macro UNWIND_HINT_EMPTY
UNWIND_HINT type=UNWIND_HINT_TYPE_CALL end=1
.macro UNWIND_HINT_END_OF_STACK
UNWIND_HINT type=UNWIND_HINT_TYPE_END_OF_STACK
.endm

.macro UNWIND_HINT_UNDEFINED
UNWIND_HINT type=UNWIND_HINT_TYPE_UNDEFINED
.endm

.macro UNWIND_HINT_ENTRY
VALIDATE_UNRET_BEGIN
UNWIND_HINT_EMPTY
UNWIND_HINT_END_OF_STACK
.endm

.macro UNWIND_HINT_REGS base=%rsp offset=0 indirect=0 extra=1 partial=0 signal=1
Expand Down Expand Up @@ -73,7 +77,7 @@
#else

#define UNWIND_HINT_FUNC \
UNWIND_HINT(UNWIND_HINT_TYPE_FUNC, ORC_REG_SP, 8, 0, 0)
UNWIND_HINT(UNWIND_HINT_TYPE_FUNC, ORC_REG_SP, 8, 0)

#endif /* __ASSEMBLY__ */

Expand Down
2 changes: 1 addition & 1 deletion arch/x86/kernel/ftrace_64.S
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ STACK_FRAME_NON_STANDARD_FP(__fentry__)

#ifdef CONFIG_FUNCTION_GRAPH_TRACER
SYM_CODE_START(return_to_handler)
UNWIND_HINT_EMPTY
UNWIND_HINT_UNDEFINED
ANNOTATE_NOENDBR
subq $16, %rsp

Expand Down
12 changes: 6 additions & 6 deletions arch/x86/kernel/head_64.S
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ L3_START_KERNEL = pud_index(__START_KERNEL_map)
__HEAD
.code64
SYM_CODE_START_NOALIGN(startup_64)
UNWIND_HINT_EMPTY
UNWIND_HINT_END_OF_STACK
/*
* At this point the CPU runs in 64bit mode CS.L = 1 CS.D = 0,
* and someone has loaded an identity mapped page table
Expand Down Expand Up @@ -105,7 +105,7 @@ SYM_CODE_START_NOALIGN(startup_64)
lretq

.Lon_kernel_cs:
UNWIND_HINT_EMPTY
UNWIND_HINT_END_OF_STACK

/* Sanitize CPU configuration */
call verify_cpu
Expand All @@ -127,7 +127,7 @@ SYM_CODE_START_NOALIGN(startup_64)
SYM_CODE_END(startup_64)

SYM_CODE_START(secondary_startup_64)
UNWIND_HINT_EMPTY
UNWIND_HINT_END_OF_STACK
ANNOTATE_NOENDBR
/*
* At this point the CPU runs in 64bit mode CS.L = 1 CS.D = 0,
Expand Down Expand Up @@ -156,7 +156,7 @@ SYM_CODE_START(secondary_startup_64)
* verify_cpu() above to make sure NX is enabled.
*/
SYM_INNER_LABEL(secondary_startup_64_no_verify, SYM_L_GLOBAL)
UNWIND_HINT_EMPTY
UNWIND_HINT_END_OF_STACK
ANNOTATE_NOENDBR

/*
Expand Down Expand Up @@ -238,7 +238,7 @@ SYM_INNER_LABEL(secondary_startup_64_no_verify, SYM_L_GLOBAL)
ANNOTATE_RETPOLINE_SAFE
jmp *%rax
1:
UNWIND_HINT_EMPTY
UNWIND_HINT_END_OF_STACK
ANNOTATE_NOENDBR // above

/*
Expand Down Expand Up @@ -371,7 +371,7 @@ SYM_CODE_END(secondary_startup_64)
*/
SYM_CODE_START(start_cpu0)
ANNOTATE_NOENDBR
UNWIND_HINT_EMPTY
UNWIND_HINT_END_OF_STACK
movq initial_stack(%rip), %rsp
jmp .Ljump_to_C_code
SYM_CODE_END(start_cpu0)
Expand Down
10 changes: 5 additions & 5 deletions arch/x86/kernel/relocate_kernel_64.S
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
.code64
SYM_CODE_START_NOALIGN(relocate_range)
SYM_CODE_START_NOALIGN(relocate_kernel)
UNWIND_HINT_EMPTY
UNWIND_HINT_END_OF_STACK
ANNOTATE_NOENDBR
/*
* %rdi indirection_page
Expand Down Expand Up @@ -113,7 +113,7 @@ SYM_CODE_START_NOALIGN(relocate_kernel)
SYM_CODE_END(relocate_kernel)

SYM_CODE_START_LOCAL_NOALIGN(identity_mapped)
UNWIND_HINT_EMPTY
UNWIND_HINT_END_OF_STACK
/* set return address to 0 if not preserving context */
pushq $0
/* store the start address on the stack */
Expand Down Expand Up @@ -231,7 +231,7 @@ SYM_CODE_START_LOCAL_NOALIGN(identity_mapped)
SYM_CODE_END(identity_mapped)

SYM_CODE_START_LOCAL_NOALIGN(virtual_mapped)
UNWIND_HINT_EMPTY
UNWIND_HINT_END_OF_STACK
ANNOTATE_NOENDBR // RET target, above
movq RSP(%r8), %rsp
movq CR4(%r8), %rax
Expand All @@ -256,8 +256,8 @@ SYM_CODE_END(virtual_mapped)

/* Do the copies */
SYM_CODE_START_LOCAL_NOALIGN(swap_pages)
UNWIND_HINT_EMPTY
movq %rdi, %rcx /* Put the page_list in %rcx */
UNWIND_HINT_END_OF_STACK
movq %rdi, %rcx /* Put the page_list in %rcx */
xorl %edi, %edi
xorl %esi, %esi
jmp 1f
Expand Down
15 changes: 6 additions & 9 deletions arch/x86/kernel/unwind_orc.c
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,6 @@ static struct orc_entry orc_fp_entry = {
.sp_offset = 16,
.bp_reg = ORC_REG_PREV_SP,
.bp_offset = -16,
.end = 0,
};

static struct orc_entry *orc_find(unsigned long ip)
Expand Down Expand Up @@ -250,13 +249,13 @@ static int orc_sort_cmp(const void *_a, const void *_b)
return -1;

/*
* The "weak" section terminator entries need to always be on the left
* The "weak" section terminator entries need to always be first
* to ensure the lookup code skips them in favor of real entries.
* These terminator entries exist to handle any gaps created by
* whitelisted .o files which didn't get objtool generation.
*/
orc_a = cur_orc_table + (a - cur_orc_ip_table);
return orc_a->sp_reg == ORC_REG_UNDEFINED && !orc_a->end ? -1 : 1;
return orc_a->type == ORC_TYPE_UNDEFINED ? -1 : 1;
}

void unwind_module_init(struct module *mod, void *_orc_ip, size_t orc_ip_size,
Expand Down Expand Up @@ -474,14 +473,12 @@ bool unwind_next_frame(struct unwind_state *state)
*/
orc = &orc_fp_entry;
state->error = true;
}

/* End-of-stack check for kernel threads: */
if (orc->sp_reg == ORC_REG_UNDEFINED) {
if (!orc->end)
} else {
if (orc->type == ORC_TYPE_UNDEFINED)
goto err;

goto the_end;
if (orc->type == ORC_TYPE_END_OF_STACK)
goto the_end;
}

state->signal = orc->signal;
Expand Down
6 changes: 3 additions & 3 deletions arch/x86/lib/retpoline.S
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@

.align RETPOLINE_THUNK_SIZE
SYM_INNER_LABEL(__x86_indirect_thunk_\reg, SYM_L_GLOBAL)
UNWIND_HINT_EMPTY
UNWIND_HINT_UNDEFINED
ANNOTATE_NOENDBR

ALTERNATIVE_2 __stringify(RETPOLINE \reg), \
Expand Down Expand Up @@ -75,7 +75,7 @@ SYM_CODE_END(__x86_indirect_thunk_array)
.align RETPOLINE_THUNK_SIZE

SYM_INNER_LABEL(__x86_indirect_call_thunk_\reg, SYM_L_GLOBAL)
UNWIND_HINT_EMPTY
UNWIND_HINT_UNDEFINED
ANNOTATE_NOENDBR

CALL_DEPTH_ACCOUNT
Expand Down Expand Up @@ -103,7 +103,7 @@ SYM_CODE_END(__x86_indirect_call_thunk_array)
.align RETPOLINE_THUNK_SIZE

SYM_INNER_LABEL(__x86_indirect_jump_thunk_\reg, SYM_L_GLOBAL)
UNWIND_HINT_EMPTY
UNWIND_HINT_UNDEFINED
ANNOTATE_NOENDBR
POLINE \reg
ANNOTATE_UNRET_SAFE
Expand Down
2 changes: 1 addition & 1 deletion arch/x86/platform/pvh/head.S
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
#define PVH_DS_SEL (PVH_GDT_ENTRY_DS * 8)

SYM_CODE_START_LOCAL(pvh_start_xen)
UNWIND_HINT_EMPTY
UNWIND_HINT_END_OF_STACK
cld

lgdt (_pa(gdt))
Expand Down
4 changes: 2 additions & 2 deletions arch/x86/xen/xen-asm.S
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ xen_pv_trap asm_exc_xen_hypervisor_callback
SYM_CODE_START(xen_early_idt_handler_array)
i = 0
.rept NUM_EXCEPTION_VECTORS
UNWIND_HINT_EMPTY
UNWIND_HINT_UNDEFINED
ENDBR
pop %rcx
pop %r11
Expand Down Expand Up @@ -193,7 +193,7 @@ hypercall_iret = hypercall_page + __HYPERVISOR_iret * 32
* rsp->rax }
*/
SYM_CODE_START(xen_iret)
UNWIND_HINT_EMPTY
UNWIND_HINT_UNDEFINED
ANNOTATE_NOENDBR
pushq $0
jmp hypercall_iret
Expand Down
4 changes: 2 additions & 2 deletions arch/x86/xen/xen-head.S
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ SYM_CODE_END(hypercall_page)
#ifdef CONFIG_XEN_PV
__INIT
SYM_CODE_START(startup_xen)
UNWIND_HINT_EMPTY
UNWIND_HINT_END_OF_STACK
ANNOTATE_NOENDBR
cld

Expand All @@ -71,7 +71,7 @@ SYM_CODE_END(startup_xen)
#ifdef CONFIG_XEN_PV_SMP
.pushsection .text
SYM_CODE_START(asm_cpu_bringup_and_idle)
UNWIND_HINT_EMPTY
UNWIND_HINT_END_OF_STACK
ENDBR

call cpu_bringup_and_idle
Expand Down
10 changes: 4 additions & 6 deletions include/linux/objtool.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

#ifndef __ASSEMBLY__

#define UNWIND_HINT(type, sp_reg, sp_offset, signal, end) \
#define UNWIND_HINT(type, sp_reg, sp_offset, signal) \
"987: \n\t" \
".pushsection .discard.unwind_hints\n\t" \
/* struct unwind_hint */ \
Expand All @@ -19,7 +19,6 @@
".byte " __stringify(sp_reg) "\n\t" \
".byte " __stringify(type) "\n\t" \
".byte " __stringify(signal) "\n\t" \
".byte " __stringify(end) "\n\t" \
".balign 4 \n\t" \
".popsection\n\t"

Expand Down Expand Up @@ -91,7 +90,7 @@
* the debuginfo as necessary. It will also warn if it sees any
* inconsistencies.
*/
.macro UNWIND_HINT type:req sp_reg=0 sp_offset=0 signal=0 end=0
.macro UNWIND_HINT type:req sp_reg=0 sp_offset=0 signal=0
.Lhere_\@:
.pushsection .discard.unwind_hints
/* struct unwind_hint */
Expand All @@ -100,7 +99,6 @@
.byte \sp_reg
.byte \type
.byte \signal
.byte \end
.balign 4
.popsection
.endm
Expand Down Expand Up @@ -153,14 +151,14 @@

#ifndef __ASSEMBLY__

#define UNWIND_HINT(type, sp_reg, sp_offset, signal, end) "\n\t"
#define UNWIND_HINT(type, sp_reg, sp_offset, signal) "\n\t"
#define STACK_FRAME_NON_STANDARD(func)
#define STACK_FRAME_NON_STANDARD_FP(func)
#define ANNOTATE_NOENDBR
#define ASM_REACHABLE
#else
#define ANNOTATE_INTRA_FUNCTION_CALL
.macro UNWIND_HINT type:req sp_reg=0 sp_offset=0 signal=0 end=0
.macro UNWIND_HINT type:req sp_reg=0 sp_offset=0 signal=0
.endm
.macro STACK_FRAME_NON_STANDARD func:req
.endm
Expand Down
Loading

0 comments on commit fb79944

Please sign in to comment.