Commit | Line | Data |
---|---|---|
eda3dc90 | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
d871f7b5 RG |
2 | #include <string.h> |
3 | ||
7786032e VG |
4 | #include <objtool/special.h> |
5 | #include <objtool/builtin.h> | |
eda3dc90 JT |
6 | |
7 | #define X86_FEATURE_POPCNT (4 * 32 + 23) | |
8 | #define X86_FEATURE_SMAP (9 * 32 + 20) | |
9 | ||
10 | void arch_handle_alternative(unsigned short feature, struct special_alt *alt) | |
11 | { | |
12 | switch (feature) { | |
13 | case X86_FEATURE_SMAP: | |
14 | /* | |
15 | * If UACCESS validation is enabled; force that alternative; | |
16 | * otherwise force it the other way. | |
17 | * | |
18 | * What we want to avoid is having both the original and the | |
19 | * alternative code flow at the same time, in that case we can | |
20 | * find paths that see the STAC but take the NOP instead of | |
21 | * CLAC and the other way around. | |
22 | */ | |
2daf7fab | 23 | if (opts.uaccess) |
eda3dc90 JT |
24 | alt->skip_orig = true; |
25 | else | |
26 | alt->skip_alt = true; | |
27 | break; | |
28 | case X86_FEATURE_POPCNT: | |
29 | /* | |
30 | * It has been requested that we don't validate the !POPCNT | |
31 | * feature path which is a "very very small percentage of | |
32 | * machines". | |
33 | */ | |
34 | alt->skip_orig = true; | |
35 | break; | |
36 | default: | |
37 | break; | |
38 | } | |
39 | } | |
45245f51 JT |
40 | |
41 | bool arch_support_alt_relocation(struct special_alt *special_alt, | |
42 | struct instruction *insn, | |
43 | struct reloc *reloc) | |
44 | { | |
270a69c4 | 45 | return true; |
45245f51 | 46 | } |
d871f7b5 RG |
47 | |
48 | /* | |
49 | * There are 3 basic jump table patterns: | |
50 | * | |
51 | * 1. jmpq *[rodata addr](,%reg,8) | |
52 | * | |
53 | * This is the most common case by far. It jumps to an address in a simple | |
54 | * jump table which is stored in .rodata. | |
55 | * | |
56 | * 2. jmpq *[rodata addr](%rip) | |
57 | * | |
58 | * This is caused by a rare GCC quirk, currently only seen in three driver | |
59 | * functions in the kernel, only with certain obscure non-distro configs. | |
60 | * | |
61 | * As part of an optimization, GCC makes a copy of an existing switch jump | |
62 | * table, modifies it, and then hard-codes the jump (albeit with an indirect | |
63 | * jump) to use a single entry in the table. The rest of the jump table and | |
64 | * some of its jump targets remain as dead code. | |
65 | * | |
66 | * In such a case we can just crudely ignore all unreachable instruction | |
67 | * warnings for the entire object file. Ideally we would just ignore them | |
68 | * for the function, but that would require redesigning the code quite a | |
69 | * bit. And honestly that's just not worth doing: unreachable instruction | |
70 | * warnings are of questionable value anyway, and this is such a rare issue. | |
71 | * | |
72 | * 3. mov [rodata addr],%reg1 | |
73 | * ... some instructions ... | |
74 | * jmpq *(%reg1,%reg2,8) | |
75 | * | |
76 | * This is a fairly uncommon pattern which is new for GCC 6. As of this | |
77 | * writing, there are 11 occurrences of it in the allmodconfig kernel. | |
78 | * | |
79 | * As of GCC 7 there are quite a few more of these and the 'in between' code | |
80 | * is significant. Esp. with KASAN enabled some of the code between the mov | |
81 | * and jmpq uses .rodata itself, which can confuse things. | |
82 | * | |
83 | * TODO: Once we have DWARF CFI and smarter instruction decoding logic, | |
84 | * ensure the same register is used in the mov and jump instructions. | |
85 | * | |
aefb2f2e | 86 | * NOTE: MITIGATION_RETPOLINE made it harder still to decode dynamic jumps. |
d871f7b5 RG |
87 | */ |
88 | struct reloc *arch_find_switch_table(struct objtool_file *file, | |
89 | struct instruction *insn) | |
90 | { | |
91 | struct reloc *text_reloc, *rodata_reloc; | |
92 | struct section *table_sec; | |
93 | unsigned long table_offset; | |
94 | ||
95 | /* look for a relocation which references .rodata */ | |
96 | text_reloc = find_reloc_by_dest_range(file->elf, insn->sec, | |
97 | insn->offset, insn->len); | |
98 | if (!text_reloc || text_reloc->sym->type != STT_SECTION || | |
99 | !text_reloc->sym->sec->rodata) | |
100 | return NULL; | |
101 | ||
0696b6e3 | 102 | table_offset = reloc_addend(text_reloc); |
d871f7b5 RG |
103 | table_sec = text_reloc->sym->sec; |
104 | ||
fcee899d | 105 | if (reloc_type(text_reloc) == R_X86_64_PC32) |
d871f7b5 RG |
106 | table_offset += 4; |
107 | ||
108 | /* | |
109 | * Make sure the .rodata address isn't associated with a | |
110 | * symbol. GCC jump tables are anonymous data. | |
111 | * | |
112 | * Also support C jump tables which are in the same format as | |
113 | * switch jump tables. For objtool to recognize them, they | |
114 | * need to be placed in the C_JUMP_TABLE_SECTION section. They | |
115 | * have symbols associated with them. | |
116 | */ | |
117 | if (find_symbol_containing(table_sec, table_offset) && | |
118 | strcmp(table_sec->name, C_JUMP_TABLE_SECTION)) | |
119 | return NULL; | |
120 | ||
121 | /* | |
122 | * Each table entry has a rela associated with it. The rela | |
123 | * should reference text in the same function as the original | |
124 | * instruction. | |
125 | */ | |
126 | rodata_reloc = find_reloc_by_dest(file->elf, table_sec, table_offset); | |
127 | if (!rodata_reloc) | |
128 | return NULL; | |
129 | ||
130 | /* | |
131 | * Use of RIP-relative switch jumps is quite rare, and | |
132 | * indicates a rare GCC quirk/bug which can leave dead | |
133 | * code behind. | |
134 | */ | |
fcee899d | 135 | if (reloc_type(text_reloc) == R_X86_64_PC32) |
d871f7b5 RG |
136 | file->ignore_unreachables = true; |
137 | ||
138 | return rodata_reloc; | |
139 | } |