mirror of
https://github.com/torvalds/linux.git
synced 2026-05-25 23:52:08 +02:00
objtool: Validate kCFI calls
Validate that all indirect calls adhere to kCFI rules. Notably doing nocfi indirect call to a cfi function is broken. Apparently some Rust 'core' code violates this and explodes when ran with FineIBT. All the ANNOTATE_NOCFI_SYM sites are prime targets for attackers. - runtime EFI is especially henous because it also needs to disable IBT. Basically calling unknown code without CFI protection at runtime is a massice security issue. - Kexec image handover; if you can exploit this, you get to keep it :-) Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Acked-by: Josh Poimboeuf <jpoimboe@kernel.org> Acked-by: Sean Christopherson <seanjc@google.com> Link: https://lkml.kernel.org/r/20250714103441.496787279@infradead.org
This commit is contained in:
parent
28d11e4548
commit
894af4a1cd
|
|
@ -453,6 +453,10 @@ void __nocfi machine_kexec(struct kimage *image)
|
||||||
|
|
||||||
__ftrace_enabled_restore(save_ftrace_enabled);
|
__ftrace_enabled_restore(save_ftrace_enabled);
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
* Handover to the next kernel, no CFI concern.
|
||||||
|
*/
|
||||||
|
ANNOTATE_NOCFI_SYM(machine_kexec);
|
||||||
|
|
||||||
/* arch-dependent functionality related to kexec file-based syscall */
|
/* arch-dependent functionality related to kexec file-based syscall */
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -361,6 +361,10 @@ SYM_FUNC_END(vmread_error_trampoline)
|
||||||
|
|
||||||
.section .text, "ax"
|
.section .text, "ax"
|
||||||
|
|
||||||
|
#ifndef CONFIG_X86_FRED
|
||||||
|
|
||||||
SYM_FUNC_START(vmx_do_interrupt_irqoff)
|
SYM_FUNC_START(vmx_do_interrupt_irqoff)
|
||||||
VMX_DO_EVENT_IRQOFF CALL_NOSPEC _ASM_ARG1
|
VMX_DO_EVENT_IRQOFF CALL_NOSPEC _ASM_ARG1
|
||||||
SYM_FUNC_END(vmx_do_interrupt_irqoff)
|
SYM_FUNC_END(vmx_do_interrupt_irqoff)
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,10 @@
|
||||||
#include <asm/nospec-branch.h>
|
#include <asm/nospec-branch.h>
|
||||||
|
|
||||||
SYM_FUNC_START(__efi_call)
|
SYM_FUNC_START(__efi_call)
|
||||||
|
/*
|
||||||
|
* The EFI code doesn't have any CFI, annotate away the CFI violation.
|
||||||
|
*/
|
||||||
|
ANNOTATE_NOCFI_SYM
|
||||||
pushq %rbp
|
pushq %rbp
|
||||||
movq %rsp, %rbp
|
movq %rsp, %rbp
|
||||||
and $~0xf, %rsp
|
and $~0xf, %rsp
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
#include <linux/vmalloc.h>
|
#include <linux/vmalloc.h>
|
||||||
#include <linux/mman.h>
|
#include <linux/mman.h>
|
||||||
#include <linux/uaccess.h>
|
#include <linux/uaccess.h>
|
||||||
|
#include <linux/objtool.h>
|
||||||
#include <asm/cacheflush.h>
|
#include <asm/cacheflush.h>
|
||||||
#include <asm/sections.h>
|
#include <asm/sections.h>
|
||||||
|
|
||||||
|
|
@ -86,6 +87,10 @@ static noinline __nocfi void execute_location(void *dst, bool write)
|
||||||
func();
|
func();
|
||||||
pr_err("FAIL: func returned\n");
|
pr_err("FAIL: func returned\n");
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
* Explicitly doing the wrong thing for testing.
|
||||||
|
*/
|
||||||
|
ANNOTATE_NOCFI_SYM(execute_location);
|
||||||
|
|
||||||
static void execute_user_location(void *dst)
|
static void execute_user_location(void *dst)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -184,6 +184,15 @@
|
||||||
* WARN using UD2.
|
* WARN using UD2.
|
||||||
*/
|
*/
|
||||||
#define ANNOTATE_REACHABLE(label) __ASM_ANNOTATE(label, ANNOTYPE_REACHABLE)
|
#define ANNOTATE_REACHABLE(label) __ASM_ANNOTATE(label, ANNOTYPE_REACHABLE)
|
||||||
|
/*
|
||||||
|
* This should not be used; it annotates away CFI violations. There are a few
|
||||||
|
* valid use cases like kexec handover to the next kernel image, and there is
|
||||||
|
* no security concern there.
|
||||||
|
*
|
||||||
|
* There are also a few real issues annotated away, like EFI because we can't
|
||||||
|
* control the EFI code.
|
||||||
|
*/
|
||||||
|
#define ANNOTATE_NOCFI_SYM(sym) asm(__ASM_ANNOTATE(sym, ANNOTYPE_NOCFI))
|
||||||
|
|
||||||
#else
|
#else
|
||||||
#define ANNOTATE_NOENDBR ANNOTATE type=ANNOTYPE_NOENDBR
|
#define ANNOTATE_NOENDBR ANNOTATE type=ANNOTYPE_NOENDBR
|
||||||
|
|
@ -194,6 +203,7 @@
|
||||||
#define ANNOTATE_INTRA_FUNCTION_CALL ANNOTATE type=ANNOTYPE_INTRA_FUNCTION_CALL
|
#define ANNOTATE_INTRA_FUNCTION_CALL ANNOTATE type=ANNOTYPE_INTRA_FUNCTION_CALL
|
||||||
#define ANNOTATE_UNRET_BEGIN ANNOTATE type=ANNOTYPE_UNRET_BEGIN
|
#define ANNOTATE_UNRET_BEGIN ANNOTATE type=ANNOTYPE_UNRET_BEGIN
|
||||||
#define ANNOTATE_REACHABLE ANNOTATE type=ANNOTYPE_REACHABLE
|
#define ANNOTATE_REACHABLE ANNOTATE type=ANNOTYPE_REACHABLE
|
||||||
|
#define ANNOTATE_NOCFI_SYM ANNOTATE type=ANNOTYPE_NOCFI
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(CONFIG_NOINSTR_VALIDATION) && \
|
#if defined(CONFIG_NOINSTR_VALIDATION) && \
|
||||||
|
|
|
||||||
|
|
@ -65,5 +65,6 @@ struct unwind_hint {
|
||||||
#define ANNOTYPE_IGNORE_ALTS 6
|
#define ANNOTYPE_IGNORE_ALTS 6
|
||||||
#define ANNOTYPE_INTRA_FUNCTION_CALL 7
|
#define ANNOTYPE_INTRA_FUNCTION_CALL 7
|
||||||
#define ANNOTYPE_REACHABLE 8
|
#define ANNOTYPE_REACHABLE 8
|
||||||
|
#define ANNOTYPE_NOCFI 9
|
||||||
|
|
||||||
#endif /* _LINUX_OBJTOOL_TYPES_H */
|
#endif /* _LINUX_OBJTOOL_TYPES_H */
|
||||||
|
|
|
||||||
|
|
@ -65,5 +65,6 @@ struct unwind_hint {
|
||||||
#define ANNOTYPE_IGNORE_ALTS 6
|
#define ANNOTYPE_IGNORE_ALTS 6
|
||||||
#define ANNOTYPE_INTRA_FUNCTION_CALL 7
|
#define ANNOTYPE_INTRA_FUNCTION_CALL 7
|
||||||
#define ANNOTYPE_REACHABLE 8
|
#define ANNOTYPE_REACHABLE 8
|
||||||
|
#define ANNOTYPE_NOCFI 9
|
||||||
|
|
||||||
#endif /* _LINUX_OBJTOOL_TYPES_H */
|
#endif /* _LINUX_OBJTOOL_TYPES_H */
|
||||||
|
|
|
||||||
|
|
@ -2392,6 +2392,8 @@ static int __annotate_ifc(struct objtool_file *file, int type, struct instructio
|
||||||
|
|
||||||
static int __annotate_late(struct objtool_file *file, int type, struct instruction *insn)
|
static int __annotate_late(struct objtool_file *file, int type, struct instruction *insn)
|
||||||
{
|
{
|
||||||
|
struct symbol *sym;
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case ANNOTYPE_NOENDBR:
|
case ANNOTYPE_NOENDBR:
|
||||||
/* early */
|
/* early */
|
||||||
|
|
@ -2433,6 +2435,15 @@ static int __annotate_late(struct objtool_file *file, int type, struct instructi
|
||||||
insn->dead_end = false;
|
insn->dead_end = false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case ANNOTYPE_NOCFI:
|
||||||
|
sym = insn->sym;
|
||||||
|
if (!sym) {
|
||||||
|
ERROR_INSN(insn, "dodgy NOCFI annotation");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
insn->sym->nocfi = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
ERROR_INSN(insn, "Unknown annotation type: %d", type);
|
ERROR_INSN(insn, "Unknown annotation type: %d", type);
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -4002,6 +4013,37 @@ static int validate_retpoline(struct objtool_file *file)
|
||||||
warnings++;
|
warnings++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!opts.cfi)
|
||||||
|
return warnings;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* kCFI call sites look like:
|
||||||
|
*
|
||||||
|
* movl $(-0x12345678), %r10d
|
||||||
|
* addl -4(%r11), %r10d
|
||||||
|
* jz 1f
|
||||||
|
* ud2
|
||||||
|
* 1: cs call __x86_indirect_thunk_r11
|
||||||
|
*
|
||||||
|
* Verify all indirect calls are kCFI adorned by checking for the
|
||||||
|
* UD2. Notably, doing __nocfi calls to regular (cfi) functions is
|
||||||
|
* broken.
|
||||||
|
*/
|
||||||
|
list_for_each_entry(insn, &file->retpoline_call_list, call_node) {
|
||||||
|
struct symbol *sym = insn->sym;
|
||||||
|
|
||||||
|
if (sym && (sym->type == STT_NOTYPE ||
|
||||||
|
sym->type == STT_FUNC) && !sym->nocfi) {
|
||||||
|
struct instruction *prev =
|
||||||
|
prev_insn_same_sym(file, insn);
|
||||||
|
|
||||||
|
if (!prev || prev->type != INSN_BUG) {
|
||||||
|
WARN_INSN(insn, "no-cfi indirect call!");
|
||||||
|
warnings++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return warnings;
|
return warnings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,7 @@ struct symbol {
|
||||||
u8 local_label : 1;
|
u8 local_label : 1;
|
||||||
u8 frame_pointer : 1;
|
u8 frame_pointer : 1;
|
||||||
u8 ignore : 1;
|
u8 ignore : 1;
|
||||||
|
u8 nocfi : 1;
|
||||||
struct list_head pv_target;
|
struct list_head pv_target;
|
||||||
struct reloc *relocs;
|
struct reloc *relocs;
|
||||||
struct section *group_sec;
|
struct section *group_sec;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user