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.orgpull/1354/merge
parent
28d11e4548
commit
894af4a1cd
|
|
@ -453,6 +453,10 @@ void __nocfi machine_kexec(struct kimage *image)
|
|||
|
||||
__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 */
|
||||
|
||||
|
|
|
|||
|
|
@ -361,6 +361,10 @@ SYM_FUNC_END(vmread_error_trampoline)
|
|||
|
||||
.section .text, "ax"
|
||||
|
||||
#ifndef CONFIG_X86_FRED
|
||||
|
||||
SYM_FUNC_START(vmx_do_interrupt_irqoff)
|
||||
VMX_DO_EVENT_IRQOFF CALL_NOSPEC _ASM_ARG1
|
||||
SYM_FUNC_END(vmx_do_interrupt_irqoff)
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -11,6 +11,10 @@
|
|||
#include <asm/nospec-branch.h>
|
||||
|
||||
SYM_FUNC_START(__efi_call)
|
||||
/*
|
||||
* The EFI code doesn't have any CFI, annotate away the CFI violation.
|
||||
*/
|
||||
ANNOTATE_NOCFI_SYM
|
||||
pushq %rbp
|
||||
movq %rsp, %rbp
|
||||
and $~0xf, %rsp
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#include <linux/vmalloc.h>
|
||||
#include <linux/mman.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/objtool.h>
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/sections.h>
|
||||
|
||||
|
|
@ -86,6 +87,10 @@ static noinline __nocfi void execute_location(void *dst, bool write)
|
|||
func();
|
||||
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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -184,6 +184,15 @@
|
|||
* WARN using UD2.
|
||||
*/
|
||||
#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
|
||||
#define ANNOTATE_NOENDBR ANNOTATE type=ANNOTYPE_NOENDBR
|
||||
|
|
@ -194,6 +203,7 @@
|
|||
#define ANNOTATE_INTRA_FUNCTION_CALL ANNOTATE type=ANNOTYPE_INTRA_FUNCTION_CALL
|
||||
#define ANNOTATE_UNRET_BEGIN ANNOTATE type=ANNOTYPE_UNRET_BEGIN
|
||||
#define ANNOTATE_REACHABLE ANNOTATE type=ANNOTYPE_REACHABLE
|
||||
#define ANNOTATE_NOCFI_SYM ANNOTATE type=ANNOTYPE_NOCFI
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_NOINSTR_VALIDATION) && \
|
||||
|
|
|
|||
|
|
@ -65,5 +65,6 @@ struct unwind_hint {
|
|||
#define ANNOTYPE_IGNORE_ALTS 6
|
||||
#define ANNOTYPE_INTRA_FUNCTION_CALL 7
|
||||
#define ANNOTYPE_REACHABLE 8
|
||||
#define ANNOTYPE_NOCFI 9
|
||||
|
||||
#endif /* _LINUX_OBJTOOL_TYPES_H */
|
||||
|
|
|
|||
|
|
@ -65,5 +65,6 @@ struct unwind_hint {
|
|||
#define ANNOTYPE_IGNORE_ALTS 6
|
||||
#define ANNOTYPE_INTRA_FUNCTION_CALL 7
|
||||
#define ANNOTYPE_REACHABLE 8
|
||||
#define ANNOTYPE_NOCFI 9
|
||||
|
||||
#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)
|
||||
{
|
||||
struct symbol *sym;
|
||||
|
||||
switch (type) {
|
||||
case ANNOTYPE_NOENDBR:
|
||||
/* early */
|
||||
|
|
@ -2433,6 +2435,15 @@ static int __annotate_late(struct objtool_file *file, int type, struct instructi
|
|||
insn->dead_end = false;
|
||||
break;
|
||||
|
||||
case ANNOTYPE_NOCFI:
|
||||
sym = insn->sym;
|
||||
if (!sym) {
|
||||
ERROR_INSN(insn, "dodgy NOCFI annotation");
|
||||
return -1;
|
||||
}
|
||||
insn->sym->nocfi = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
ERROR_INSN(insn, "Unknown annotation type: %d", type);
|
||||
return -1;
|
||||
|
|
@ -4002,6 +4013,37 @@ static int validate_retpoline(struct objtool_file *file)
|
|||
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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ struct symbol {
|
|||
u8 local_label : 1;
|
||||
u8 frame_pointer : 1;
|
||||
u8 ignore : 1;
|
||||
u8 nocfi : 1;
|
||||
struct list_head pv_target;
|
||||
struct reloc *relocs;
|
||||
struct section *group_sec;
|
||||
|
|
|
|||
Loading…
Reference in New Issue