Commit e94693f7 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge branch 'core-objtool-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull objtool updates from Ingo Molnar:
 "This is an extensive rewrite of the objdump tool to track all stack
  pointer modifications through the machine instructions of disassembled
  functions found in kernel .o files.

  This re-design removes the prior dependency on CONFIG_FRAME_POINTERS,
  with the goal to prepare the tool to generate kernel debuginfo data in
  the future. There's also an increase in checking/tracking robustness
  as a side effect as well.

  No (intended) changes to existing functionality"

* 'core-objtool-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  objtool: Silence warnings for functions which use IRET
  objtool: Implement stack validation 2.0
  objtool, x86: Add several functions and files to the objtool whitelist
  objtool: Move checking code to check.c
parents 26d3a77d 2513cbf9
......@@ -2,6 +2,8 @@
# Arch-specific CryptoAPI modules.
#
OBJECT_FILES_NON_STANDARD := y
avx_supported := $(call as-instr,vpxor %xmm0$(comma)%xmm0$(comma)%xmm0,yes,no)
avx2_supported := $(call as-instr,vpgatherdd %ymm0$(comma)(%eax$(comma)%ymm1\
$(comma)4)$(comma)%ymm2,yes,no)
......
......@@ -2,6 +2,8 @@
# Arch-specific CryptoAPI modules.
#
OBJECT_FILES_NON_STANDARD := y
avx2_supported := $(call as-instr,vpgatherdd %ymm0$(comma)(%eax$(comma)%ymm1\
$(comma)4)$(comma)%ymm2,yes,no)
ifeq ($(avx2_supported),yes)
......
......@@ -2,6 +2,8 @@
# Arch-specific CryptoAPI modules.
#
OBJECT_FILES_NON_STANDARD := y
avx2_supported := $(call as-instr,vpgatherdd %ymm0$(comma)(%eax$(comma)%ymm1\
$(comma)4)$(comma)%ymm2,yes,no)
ifeq ($(avx2_supported),yes)
......
......@@ -29,6 +29,7 @@ OBJECT_FILES_NON_STANDARD_head_$(BITS).o := y
OBJECT_FILES_NON_STANDARD_relocate_kernel_$(BITS).o := y
OBJECT_FILES_NON_STANDARD_ftrace_$(BITS).o := y
OBJECT_FILES_NON_STANDARD_test_nx.o := y
OBJECT_FILES_NON_STANDARD_paravirt_patch_$(BITS).o := y
# If instrumentation of this dir is enabled, boot hangs during first second.
# Probably could be more selective here, but note that files related to irqs,
......
OBJECT_FILES_NON_STANDARD_wakeup_$(BITS).o := y
obj-$(CONFIG_ACPI) += boot.o
obj-$(CONFIG_ACPI_SLEEP) += sleep.o wakeup_$(BITS).o
obj-$(CONFIG_ACPI_APEI) += apei.o
......
......@@ -28,6 +28,7 @@
#include <linux/kdebug.h>
#include <linux/kallsyms.h>
#include <linux/ftrace.h>
#include <linux/frame.h>
#include <asm/text-patching.h>
#include <asm/cacheflush.h>
......@@ -94,6 +95,7 @@ static void synthesize_set_arg1(kprobe_opcode_t *addr, unsigned long val)
}
asm (
"optprobe_template_func:\n"
".global optprobe_template_entry\n"
"optprobe_template_entry:\n"
#ifdef CONFIG_X86_64
......@@ -131,7 +133,12 @@ asm (
" popf\n"
#endif
".global optprobe_template_end\n"
"optprobe_template_end:\n");
"optprobe_template_end:\n"
".type optprobe_template_func, @function\n"
".size optprobe_template_func, .-optprobe_template_func\n");
void optprobe_template_func(void);
STACK_FRAME_NON_STANDARD(optprobe_template_func);
#define TMPL_MOVE_IDX \
((long)&optprobe_template_val - (long)&optprobe_template_entry)
......
......@@ -9,6 +9,7 @@
#include <linux/sched.h>
#include <linux/tboot.h>
#include <linux/delay.h>
#include <linux/frame.h>
#include <acpi/reboot.h>
#include <asm/io.h>
#include <asm/apic.h>
......@@ -123,6 +124,7 @@ void __noreturn machine_real_restart(unsigned int type)
#ifdef CONFIG_APM_MODULE
EXPORT_SYMBOL(machine_real_restart);
#endif
STACK_FRAME_NON_STANDARD(machine_real_restart);
/*
* Some Apple MacBook and MacBookPro's needs reboot=p to be able to reboot
......
......@@ -36,6 +36,7 @@
#include <linux/slab.h>
#include <linux/amd-iommu.h>
#include <linux/hashtable.h>
#include <linux/frame.h>
#include <asm/apic.h>
#include <asm/perf_event.h>
......@@ -4906,6 +4907,7 @@ static void svm_vcpu_run(struct kvm_vcpu *vcpu)
mark_all_clean(svm->vmcb);
}
STACK_FRAME_NON_STANDARD(svm_vcpu_run);
static void svm_set_cr3(struct kvm_vcpu *vcpu, unsigned long root)
{
......
......@@ -33,6 +33,7 @@
#include <linux/slab.h>
#include <linux/tboot.h>
#include <linux/hrtimer.h>
#include <linux/frame.h>
#include "kvm_cache_regs.h"
#include "x86.h"
......@@ -8652,6 +8653,7 @@ static void vmx_handle_external_intr(struct kvm_vcpu *vcpu)
);
}
}
STACK_FRAME_NON_STANDARD(vmx_handle_external_intr);
static bool vmx_has_high_real_mode_segbase(void)
{
......@@ -9028,6 +9030,7 @@ static void __noclone vmx_vcpu_run(struct kvm_vcpu *vcpu)
vmx_recover_nmi_blocking(vmx);
vmx_complete_interrupts(vmx);
}
STACK_FRAME_NON_STANDARD(vmx_vcpu_run);
static void vmx_switch_vmcs(struct kvm_vcpu *vcpu, struct loaded_vmcs *vmcs)
{
......
......@@ -13,14 +13,14 @@
.macro op_safe_regs op
ENTRY(\op\()_safe_regs)
pushq %rbx
pushq %rbp
pushq %r12
movq %rdi, %r10 /* Save pointer */
xorl %r11d, %r11d /* Return value */
movl (%rdi), %eax
movl 4(%rdi), %ecx
movl 8(%rdi), %edx
movl 12(%rdi), %ebx
movl 20(%rdi), %ebp
movl 20(%rdi), %r12d
movl 24(%rdi), %esi
movl 28(%rdi), %edi
1: \op
......@@ -29,10 +29,10 @@ ENTRY(\op\()_safe_regs)
movl %ecx, 4(%r10)
movl %edx, 8(%r10)
movl %ebx, 12(%r10)
movl %ebp, 20(%r10)
movl %r12d, 20(%r10)
movl %esi, 24(%r10)
movl %edi, 28(%r10)
popq %rbp
popq %r12
popq %rbx
ret
3:
......
#
# Arch-specific network modules
#
OBJECT_FILES_NON_STANDARD_bpf_jit.o += y
obj-$(CONFIG_BPF_JIT) += bpf_jit.o bpf_jit_comp.o
OBJECT_FILES_NON_STANDARD_efi_thunk_$(BITS).o := y
OBJECT_FILES_NON_STANDARD_efi_stub_$(BITS).o := y
obj-$(CONFIG_EFI) += quirks.o efi.o efi_$(BITS).o efi_stub_$(BITS).o
obj-$(CONFIG_EARLY_PRINTK_EFI) += early_printk.o
......
OBJECT_FILES_NON_STANDARD_hibernate_asm_$(BITS).o := y
# __restore_processor_state() restores %gs after S3 resume and so should not
# itself be stack-protected
nostackp := $(call cc-option, -fno-stack-protector)
......
OBJECT_FILES_NON_STANDARD_xen-asm_$(BITS).o := y
OBJECT_FILES_NON_STANDARD_xen-pvh.o := y
ifdef CONFIG_FUNCTION_TRACER
# Do not profile debug and lowlevel utilities
CFLAGS_REMOVE_spinlock.o = -pg
......
......@@ -38,6 +38,7 @@
#include <linux/syscore_ops.h>
#include <linux/compiler.h>
#include <linux/hugetlb.h>
#include <linux/frame.h>
#include <asm/page.h>
#include <asm/sections.h>
......@@ -874,7 +875,7 @@ int kexec_load_disabled;
* only when panic_cpu holds the current CPU number; this is the only CPU
* which processes crash_kexec routines.
*/
void __crash_kexec(struct pt_regs *regs)
void __noclone __crash_kexec(struct pt_regs *regs)
{
/* Take the kexec_mutex here to prevent sys_kexec_load
* running on one cpu from replacing the crash kernel
......@@ -896,6 +897,7 @@ void __crash_kexec(struct pt_regs *regs)
mutex_unlock(&kexec_mutex);
}
}
STACK_FRAME_NON_STANDARD(__crash_kexec);
void crash_kexec(struct pt_regs *regs)
{
......
objtool-y += arch/$(SRCARCH)/
objtool-y += builtin-check.o
objtool-y += check.o
objtool-y += elf.o
objtool-y += special.o
objtool-y += objtool.o
......
......@@ -127,28 +127,13 @@ b) 100% reliable stack traces for DWARF enabled kernels
c) Higher live patching compatibility rate
(NOTE: This is not yet implemented)
Currently with CONFIG_LIVEPATCH there's a basic live patching
framework which is safe for roughly 85-90% of "security" fixes. But
patches can't have complex features like function dependency or
prototype changes, or data structure changes.
There's a strong need to support patches which have the more complex
features so that the patch compatibility rate for security fixes can
eventually approach something resembling 100%. To achieve that, a
"consistency model" is needed, which allows tasks to be safely
transitioned from an unpatched state to a patched state.
One of the key requirements of the currently proposed livepatch
consistency model [*] is that it needs to walk the stack of each
sleeping task to determine if it can be transitioned to the patched
state. If objtool can ensure that stack traces are reliable, this
consistency model can be used and the live patching compatibility
rate can be improved significantly.
[*] https://lkml.kernel.org/r/cover.1423499826.git.jpoimboe@redhat.com
Livepatch has an optional "consistency model", which is needed for
more complex patches. In order for the consistency model to work,
stack traces need to be reliable (or an unreliable condition needs to
be detectable). Objtool makes that possible.
For more details, see the livepatch documentation in the Linux kernel
source tree at Documentation/livepatch/livepatch.txt.
Rules
-----
......@@ -201,80 +186,84 @@ To achieve the validation, objtool enforces the following rules:
return normally.
Errors in .S files
------------------
Objtool warnings
----------------
If you're getting an error in a compiled .S file which you don't
understand, first make sure that the affected code follows the above
rules.
For asm files, if you're getting an error which doesn't make sense,
first make sure that the affected code follows the above rules.
For C files, the common culprits are inline asm statements and calls to
"noreturn" functions. See below for more details.
Another possible cause for errors in C code is if the Makefile removes
-fno-omit-frame-pointer or adds -fomit-frame-pointer to the gcc options.
Here are some examples of common warnings reported by objtool, what
they mean, and suggestions for how to fix them.
1. asm_file.o: warning: objtool: func()+0x128: call without frame pointer save/setup
1. file.o: warning: objtool: func()+0x128: call without frame pointer save/setup
The func() function made a function call without first saving and/or
updating the frame pointer.
If func() is indeed a callable function, add proper frame pointer
logic using the FRAME_BEGIN and FRAME_END macros. Otherwise, remove
its ELF function annotation by changing ENDPROC to END.
updating the frame pointer, and CONFIG_FRAME_POINTER is enabled.
If you're getting this error in a .c file, see the "Errors in .c
files" section.
If the error is for an asm file, and func() is indeed a callable
function, add proper frame pointer logic using the FRAME_BEGIN and
FRAME_END macros. Otherwise, if it's not a callable function, remove
its ELF function annotation by changing ENDPROC to END, and instead
use the manual CFI hint macros in asm/undwarf.h.
If it's a GCC-compiled .c file, the error may be because the function
uses an inline asm() statement which has a "call" instruction. An
asm() statement with a call instruction must declare the use of the
stack pointer in its output operand. For example, on x86_64:
2. asm_file.o: warning: objtool: .text+0x53: return instruction outside of a callable function
A return instruction was detected, but objtool couldn't find a way
for a callable function to reach the instruction.
register void *__sp asm("rsp");
asm volatile("call func" : "+r" (__sp));
If the return instruction is inside (or reachable from) a callable
function, the function needs to be annotated with the ENTRY/ENDPROC
macros.
Otherwise the stack frame may not get created before the call.
If you _really_ need a return instruction outside of a function, and
are 100% sure that it won't affect stack traces, you can tell
objtool to ignore it. See the "Adding exceptions" section below.
2. file.o: warning: objtool: .text+0x53: unreachable instruction
3. asm_file.o: warning: objtool: func()+0x9: function has unreachable instruction
Objtool couldn't find a code path to reach the instruction.
The instruction lives inside of a callable function, but there's no
possible control flow path from the beginning of the function to the
instruction.
If the error is for an asm file, and the instruction is inside (or
reachable from) a callable function, the function should be annotated
with the ENTRY/ENDPROC macros (ENDPROC is the important one).
Otherwise, the code should probably be annotated with the CFI hint
macros in asm/undwarf.h so objtool and the unwinder can know the
stack state associated with the code.
If the instruction is actually needed, and it's actually in a
callable function, ensure that its function is properly annotated
with ENTRY/ENDPROC.
If you're 100% sure the code won't affect stack traces, or if you're
a just a bad person, you can tell objtool to ignore it. See the
"Adding exceptions" section below.
If it's not actually in a callable function (e.g. kernel entry code),
change ENDPROC to END.
4. asm_file.o: warning: objtool: func(): can't find starting instruction
4. file.o: warning: objtool: func(): can't find starting instruction
or
asm_file.o: warning: objtool: func()+0x11dd: can't decode instruction
file.o: warning: objtool: func()+0x11dd: can't decode instruction
Did you put data in a text section? If so, that can confuse
Does the file have data in a text section? If so, that can confuse
objtool's instruction decoder. Move the data to a more appropriate
section like .data or .rodata.
5. asm_file.o: warning: objtool: func()+0x6: kernel entry/exit from callable instruction
This is a kernel entry/exit instruction like sysenter or sysret.
Such instructions aren't allowed in a callable function, and are most
likely part of the kernel entry code.
5. file.o: warning: objtool: func()+0x6: unsupported instruction in callable function
If the instruction isn't actually in a callable function, change
ENDPROC to END.
This is a kernel entry/exit instruction like sysenter or iret. Such
instructions aren't allowed in a callable function, and are most
likely part of the kernel entry code. They should usually not have
the callable function annotation (ENDPROC) and should always be
annotated with the CFI hint macros in asm/undwarf.h.
6. asm_file.o: warning: objtool: func()+0x26: sibling call from callable instruction with changed frame pointer
6. file.o: warning: objtool: func()+0x26: sibling call from callable instruction with modified stack frame
This is a dynamic jump or a jump to an undefined symbol. Stacktool
This is a dynamic jump or a jump to an undefined symbol. Objtool
assumed it's a sibling call and detected that the frame pointer
wasn't first restored to its original state.
......@@ -282,24 +271,28 @@ they mean, and suggestions for how to fix them.
destination code to the local file.
If the instruction is not actually in a callable function (e.g.
kernel entry code), change ENDPROC to END.
kernel entry code), change ENDPROC to END and annotate manually with
the CFI hint macros in asm/undwarf.h.
7. asm_file: warning: objtool: func()+0x5c: frame pointer state mismatch
7. file: warning: objtool: func()+0x5c: stack state mismatch
The instruction's frame pointer state is inconsistent, depending on
which execution path was taken to reach the instruction.
Make sure the function pushes and sets up the frame pointer (for
x86_64, this means rbp) at the beginning of the function and pops it
at the end of the function. Also make sure that no other code in the
function touches the frame pointer.
Make sure that, when CONFIG_FRAME_POINTER is enabled, the function
pushes and sets up the frame pointer (for x86_64, this means rbp) at
the beginning of the function and pops it at the end of the function.
Also make sure that no other code in the function touches the frame
pointer.
Another possibility is that the code has some asm or inline asm which
does some unusual things to the stack or the frame pointer. In such
cases it's probably appropriate to use the CFI hint macros in
asm/undwarf.h.
Errors in .c files
------------------
1. c_file.o: warning: objtool: funcA() falls through to next function funcB()
8. file.o: warning: objtool: funcA() falls through to next function funcB()
This means that funcA() doesn't end with a return instruction or an
unconditional jump, and that objtool has determined that the function
......@@ -318,22 +311,6 @@ Errors in .c files
might be corrupt due to a gcc bug. For more details, see:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70646
2. If you're getting any other objtool error in a compiled .c file, it
may be because the file uses an asm() statement which has a "call"
instruction. An asm() statement with a call instruction must declare
the use of the stack pointer in its output operand. For example, on
x86_64:
register void *__sp asm("rsp");
asm volatile("call func" : "+r" (__sp));
Otherwise the stack frame may not get created before the call.
3. Another possible cause for errors in C code is if the Makefile removes
-fno-omit-frame-pointer or adds -fomit-frame-pointer to the gcc options.
Also see the above section for .S file errors for more information what
the individual error messages mean.
If the error doesn't seem to make sense, it could be a bug in objtool.
Feel free to ask the objtool maintainer for help.
......
......@@ -25,7 +25,7 @@ OBJTOOL_IN := $(OBJTOOL)-in.o
all: $(OBJTOOL)
INCLUDES := -I$(srctree)/tools/include -I$(srctree)/tools/arch/$(HOSTARCH)/include/uapi
CFLAGS += -Wall -Werror $(EXTRA_WARNINGS) -fomit-frame-pointer -O2 -g $(INCLUDES)
CFLAGS += -Wall -Werror $(EXTRA_WARNINGS) -Wno-switch-default -Wno-switch-enum -fomit-frame-pointer -O2 -g $(INCLUDES)
LDFLAGS += -lelf $(LIBSUBCMD)
# Allow old libelf to be used:
......
......@@ -19,25 +19,63 @@
#define _ARCH_H
#include <stdbool.h>
#include <linux/list.h>
#include "elf.h"
#include "cfi.h"
#define INSN_FP_SAVE 1
#define INSN_FP_SETUP 2
#define INSN_FP_RESTORE 3
#define INSN_JUMP_CONDITIONAL 4
#define INSN_JUMP_UNCONDITIONAL 5
#define INSN_JUMP_DYNAMIC 6
#define INSN_CALL 7
#define INSN_CALL_DYNAMIC 8
#define INSN_RETURN 9
#define INSN_CONTEXT_SWITCH 10
#define INSN_NOP 11
#define INSN_OTHER 12
#define INSN_JUMP_CONDITIONAL 1
#define INSN_JUMP_UNCONDITIONAL 2
#define INSN_JUMP_DYNAMIC 3
#define INSN_CALL 4
#define INSN_CALL_DYNAMIC 5
#define INSN_RETURN 6
#define INSN_CONTEXT_SWITCH 7
#define INSN_STACK 8
#define INSN_NOP 9
#define INSN_OTHER 10
#define INSN_LAST INSN_OTHER
enum op_dest_type {
OP_DEST_REG,
OP_DEST_REG_INDIRECT,
OP_DEST_MEM,
OP_DEST_PUSH,
OP_DEST_LEAVE,
};
struct op_dest {
enum op_dest_type type;
unsigned char reg;
int offset;
};
enum op_src_type {
OP_SRC_REG,
OP_SRC_REG_INDIRECT,
OP_SRC_CONST,
OP_SRC_POP,
OP_SRC_ADD,
OP_SRC_AND,
};
struct op_src {
enum op_src_type type;
unsigned char reg;
int offset;
};
struct stack_op {
struct op_dest dest;
struct op_src src;
};
void arch_initial_func_cfi_state(struct cfi_state *state);
int arch_decode_instruction(struct elf *elf, struct section *sec,
unsigned long offset, unsigned int maxlen,
unsigned int *len, unsigned char *type,
unsigned long *displacement);
unsigned long *immediate, struct stack_op *op);
bool arch_callee_saved_reg(unsigned char reg);
#endif /* _ARCH_H */
......@@ -27,6 +27,17 @@
#include "../../arch.h"
#include "../../warn.h"
static unsigned char op_to_cfi_reg[][2] = {
{CFI_AX, CFI_R8},
{CFI_CX, CFI_R9},
{CFI_DX, CFI_R10},
{CFI_BX, CFI_R11},
{CFI_SP, CFI_R12},
{CFI_BP, CFI_R13},
{CFI_SI, CFI_R14},
{CFI_DI, CFI_R15},
};
static int is_x86_64(struct elf *elf)
{
switch (elf->ehdr.e_machine) {
......@@ -40,24 +51,50 @@ static int is_x86_64(struct elf *elf)
}
}
bool arch_callee_saved_reg(unsigned char reg)
{
switch (reg) {
case CFI_BP:
case CFI_BX:
case CFI_R12:
case CFI_R13:
case CFI_R14:
case CFI_R15:
return true;
case CFI_AX:
case CFI_CX:
case CFI_DX:
case CFI_SI:
case CFI_DI:
case CFI_SP:
case CFI_R8:
case CFI_R9:
case CFI_R10:
case CFI_R11:
case CFI_RA:
default:
return false;
}
}
int arch_decode_instruction(struct elf *elf, struct section *sec,
unsigned long offset, unsigned int maxlen,
unsigned int *len, unsigned char *type,
unsigned long *immediate)
unsigned long *immediate, struct stack_op *op)
{
struct insn insn;
int x86_64;
unsigned char op1, op2, ext;
int x86_64, sign;
unsigned char op1, op2, rex = 0, rex_b = 0, rex_r = 0, rex_w = 0,
modrm = 0, modrm_mod = 0, modrm_rm = 0, modrm_reg = 0,
sib = 0;
x86_64 = is_x86_64(elf);
if (x86_64 == -1)
return -1;
insn_init(&insn, (void *)(sec->data + offset), maxlen, x86_64);
insn_init(&insn, sec->data->d_buf + offset, maxlen, x86_64);
insn_get_length(&insn);
insn_get_opcode(&insn);
insn_get_modrm(&insn);
insn_get_immediate(&insn);
if (!insn_complete(&insn)) {
WARN_FUNC("can't decode instruction", sec, offset);
......@@ -73,67 +110,323 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
op1 = insn.opcode.bytes[0];
op2 = insn.opcode.bytes[1];
if (insn.rex_prefix.nbytes) {
rex = insn.rex_prefix.bytes[0];
rex_w = X86_REX_W(rex) >> 3;
rex_r = X86_REX_R(rex) >> 2;
rex_b = X86_REX_B(rex);
}
if (insn.modrm.nbytes) {
modrm = insn.modrm.bytes[0];
modrm_mod = X86_MODRM_MOD(modrm);
modrm_reg = X86_MODRM_REG(modrm);
modrm_rm = X86_MODRM_RM(modrm);
}
if (insn.sib.nbytes)
sib = insn.sib.bytes[0];
switch (op1) {
case 0x55:
if (!insn.rex_prefix.nbytes)
/* push rbp */
*type = INSN_FP_SAVE;