1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | #include <string.h> |
3 | |
4 | #include <objtool/special.h> |
5 | #include <objtool/builtin.h> |
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 | */ |
23 | if (opts.uaccess) |
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 | } |
40 | |
41 | bool arch_support_alt_relocation(struct special_alt *special_alt, |
42 | struct instruction *insn, |
43 | struct reloc *reloc) |
44 | { |
45 | return true; |
46 | } |
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 | * |
86 | * NOTE: MITIGATION_RETPOLINE made it harder still to decode dynamic jumps. |
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 | |
102 | table_offset = reloc_addend(text_reloc); |
103 | table_sec = text_reloc->sym->sec; |
104 | |
105 | if (reloc_type(text_reloc) == R_X86_64_PC32) |
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 | */ |
135 | if (reloc_type(text_reloc) == R_X86_64_PC32) |
136 | file->ignore_unreachables = true; |
137 | |
138 | return rodata_reloc; |
139 | } |
140 | |