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