Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/x86/kernel/cfi.c
26424 views
1
// SPDX-License-Identifier: GPL-2.0
2
/*
3
* Clang Control Flow Integrity (CFI) support.
4
*
5
* Copyright (C) 2022 Google LLC
6
*/
7
#include <linux/string.h>
8
#include <linux/cfi.h>
9
#include <asm/insn.h>
10
#include <asm/insn-eval.h>
11
12
/*
13
* Returns the target address and the expected type when regs->ip points
14
* to a compiler-generated CFI trap.
15
*/
16
static bool decode_cfi_insn(struct pt_regs *regs, unsigned long *target,
17
u32 *type)
18
{
19
char buffer[MAX_INSN_SIZE];
20
struct insn insn;
21
int offset = 0;
22
23
*target = *type = 0;
24
25
/*
26
* The compiler generates the following instruction sequence
27
* for indirect call checks:
28
*
29
*   movl -<id>, %r10d ; 6 bytes
30
* addl -4(%reg), %r10d ; 4 bytes
31
* je .Ltmp1 ; 2 bytes
32
* ud2 ; <- regs->ip
33
* .Ltmp1:
34
*
35
* We can decode the expected type and the target address from the
36
* movl/addl instructions.
37
*/
38
if (copy_from_kernel_nofault(buffer, (void *)regs->ip - 12, MAX_INSN_SIZE))
39
return false;
40
if (insn_decode_kernel(&insn, &buffer[offset]))
41
return false;
42
if (insn.opcode.value != 0xBA)
43
return false;
44
45
*type = -(u32)insn.immediate.value;
46
47
if (copy_from_kernel_nofault(buffer, (void *)regs->ip - 6, MAX_INSN_SIZE))
48
return false;
49
if (insn_decode_kernel(&insn, &buffer[offset]))
50
return false;
51
if (insn.opcode.value != 0x3)
52
return false;
53
54
/* Read the target address from the register. */
55
offset = insn_get_modrm_rm_off(&insn, regs);
56
if (offset < 0)
57
return false;
58
59
*target = *(unsigned long *)((void *)regs + offset);
60
61
return true;
62
}
63
64
/*
65
* Checks if a ud2 trap is because of a CFI failure, and handles the trap
66
* if needed. Returns a bug_trap_type value similarly to report_bug.
67
*/
68
enum bug_trap_type handle_cfi_failure(struct pt_regs *regs)
69
{
70
unsigned long target, addr = regs->ip;
71
u32 type;
72
73
switch (cfi_mode) {
74
case CFI_KCFI:
75
if (!is_cfi_trap(addr))
76
return BUG_TRAP_TYPE_NONE;
77
78
if (!decode_cfi_insn(regs, &target, &type))
79
return report_cfi_failure_noaddr(regs, addr);
80
81
break;
82
83
case CFI_FINEIBT:
84
if (!decode_fineibt_insn(regs, &target, &type))
85
return BUG_TRAP_TYPE_NONE;
86
87
break;
88
89
default:
90
return BUG_TRAP_TYPE_NONE;
91
}
92
93
return report_cfi_failure(regs, addr, &target, type);
94
}
95
96
/*
97
* Ensure that __kcfi_typeid_ symbols are emitted for functions that may
98
* not be indirectly called with all configurations.
99
*/
100
__ADDRESSABLE(__memcpy)
101
102