Skip to content

Commit 894af4a

Browse files
author
Peter Zijlstra
committed
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) <[email protected]> Acked-by: Josh Poimboeuf <[email protected]> Acked-by: Sean Christopherson <[email protected]> Link: https://lkml.kernel.org/r/[email protected]
1 parent 28d11e4 commit 894af4a

File tree

9 files changed

+72
-0
lines changed

9 files changed

+72
-0
lines changed

arch/x86/kernel/machine_kexec_64.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,10 @@ void __nocfi machine_kexec(struct kimage *image)
453453

454454
__ftrace_enabled_restore(save_ftrace_enabled);
455455
}
456+
/*
457+
* Handover to the next kernel, no CFI concern.
458+
*/
459+
ANNOTATE_NOCFI_SYM(machine_kexec);
456460

457461
/* arch-dependent functionality related to kexec file-based syscall */
458462

arch/x86/kvm/vmx/vmenter.S

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,10 @@ SYM_FUNC_END(vmread_error_trampoline)
361361

362362
.section .text, "ax"
363363

364+
#ifndef CONFIG_X86_FRED
365+
364366
SYM_FUNC_START(vmx_do_interrupt_irqoff)
365367
VMX_DO_EVENT_IRQOFF CALL_NOSPEC _ASM_ARG1
366368
SYM_FUNC_END(vmx_do_interrupt_irqoff)
369+
370+
#endif

arch/x86/platform/efi/efi_stub_64.S

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@
1111
#include <asm/nospec-branch.h>
1212

1313
SYM_FUNC_START(__efi_call)
14+
/*
15+
* The EFI code doesn't have any CFI, annotate away the CFI violation.
16+
*/
17+
ANNOTATE_NOCFI_SYM
1418
pushq %rbp
1519
movq %rsp, %rbp
1620
and $~0xf, %rsp

drivers/misc/lkdtm/perms.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <linux/vmalloc.h>
1010
#include <linux/mman.h>
1111
#include <linux/uaccess.h>
12+
#include <linux/objtool.h>
1213
#include <asm/cacheflush.h>
1314
#include <asm/sections.h>
1415

@@ -86,6 +87,10 @@ static noinline __nocfi void execute_location(void *dst, bool write)
8687
func();
8788
pr_err("FAIL: func returned\n");
8889
}
90+
/*
91+
* Explicitly doing the wrong thing for testing.
92+
*/
93+
ANNOTATE_NOCFI_SYM(execute_location);
8994

9095
static void execute_user_location(void *dst)
9196
{

include/linux/objtool.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,15 @@
184184
* WARN using UD2.
185185
*/
186186
#define ANNOTATE_REACHABLE(label) __ASM_ANNOTATE(label, ANNOTYPE_REACHABLE)
187+
/*
188+
* This should not be used; it annotates away CFI violations. There are a few
189+
* valid use cases like kexec handover to the next kernel image, and there is
190+
* no security concern there.
191+
*
192+
* There are also a few real issues annotated away, like EFI because we can't
193+
* control the EFI code.
194+
*/
195+
#define ANNOTATE_NOCFI_SYM(sym) asm(__ASM_ANNOTATE(sym, ANNOTYPE_NOCFI))
187196

188197
#else
189198
#define ANNOTATE_NOENDBR ANNOTATE type=ANNOTYPE_NOENDBR
@@ -194,6 +203,7 @@
194203
#define ANNOTATE_INTRA_FUNCTION_CALL ANNOTATE type=ANNOTYPE_INTRA_FUNCTION_CALL
195204
#define ANNOTATE_UNRET_BEGIN ANNOTATE type=ANNOTYPE_UNRET_BEGIN
196205
#define ANNOTATE_REACHABLE ANNOTATE type=ANNOTYPE_REACHABLE
206+
#define ANNOTATE_NOCFI_SYM ANNOTATE type=ANNOTYPE_NOCFI
197207
#endif
198208

199209
#if defined(CONFIG_NOINSTR_VALIDATION) && \

include/linux/objtool_types.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,5 +65,6 @@ struct unwind_hint {
6565
#define ANNOTYPE_IGNORE_ALTS 6
6666
#define ANNOTYPE_INTRA_FUNCTION_CALL 7
6767
#define ANNOTYPE_REACHABLE 8
68+
#define ANNOTYPE_NOCFI 9
6869

6970
#endif /* _LINUX_OBJTOOL_TYPES_H */

tools/include/linux/objtool_types.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,5 +65,6 @@ struct unwind_hint {
6565
#define ANNOTYPE_IGNORE_ALTS 6
6666
#define ANNOTYPE_INTRA_FUNCTION_CALL 7
6767
#define ANNOTYPE_REACHABLE 8
68+
#define ANNOTYPE_NOCFI 9
6869

6970
#endif /* _LINUX_OBJTOOL_TYPES_H */

tools/objtool/check.c

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2392,6 +2392,8 @@ static int __annotate_ifc(struct objtool_file *file, int type, struct instructio
23922392

23932393
static int __annotate_late(struct objtool_file *file, int type, struct instruction *insn)
23942394
{
2395+
struct symbol *sym;
2396+
23952397
switch (type) {
23962398
case ANNOTYPE_NOENDBR:
23972399
/* early */
@@ -2433,6 +2435,15 @@ static int __annotate_late(struct objtool_file *file, int type, struct instructi
24332435
insn->dead_end = false;
24342436
break;
24352437

2438+
case ANNOTYPE_NOCFI:
2439+
sym = insn->sym;
2440+
if (!sym) {
2441+
ERROR_INSN(insn, "dodgy NOCFI annotation");
2442+
return -1;
2443+
}
2444+
insn->sym->nocfi = 1;
2445+
break;
2446+
24362447
default:
24372448
ERROR_INSN(insn, "Unknown annotation type: %d", type);
24382449
return -1;
@@ -4002,6 +4013,37 @@ static int validate_retpoline(struct objtool_file *file)
40024013
warnings++;
40034014
}
40044015

4016+
if (!opts.cfi)
4017+
return warnings;
4018+
4019+
/*
4020+
* kCFI call sites look like:
4021+
*
4022+
* movl $(-0x12345678), %r10d
4023+
* addl -4(%r11), %r10d
4024+
* jz 1f
4025+
* ud2
4026+
* 1: cs call __x86_indirect_thunk_r11
4027+
*
4028+
* Verify all indirect calls are kCFI adorned by checking for the
4029+
* UD2. Notably, doing __nocfi calls to regular (cfi) functions is
4030+
* broken.
4031+
*/
4032+
list_for_each_entry(insn, &file->retpoline_call_list, call_node) {
4033+
struct symbol *sym = insn->sym;
4034+
4035+
if (sym && (sym->type == STT_NOTYPE ||
4036+
sym->type == STT_FUNC) && !sym->nocfi) {
4037+
struct instruction *prev =
4038+
prev_insn_same_sym(file, insn);
4039+
4040+
if (!prev || prev->type != INSN_BUG) {
4041+
WARN_INSN(insn, "no-cfi indirect call!");
4042+
warnings++;
4043+
}
4044+
}
4045+
}
4046+
40054047
return warnings;
40064048
}
40074049

tools/objtool/include/objtool/elf.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ struct symbol {
7070
u8 local_label : 1;
7171
u8 frame_pointer : 1;
7272
u8 ignore : 1;
73+
u8 nocfi : 1;
7374
struct list_head pv_target;
7475
struct reloc *relocs;
7576
struct section *group_sec;

0 commit comments

Comments
 (0)