Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 #include <string.h>
0003 
0004 #include <objtool/special.h>
0005 #include <objtool/builtin.h>
0006 
0007 #define X86_FEATURE_POPCNT (4 * 32 + 23)
0008 #define X86_FEATURE_SMAP   (9 * 32 + 20)
0009 
0010 void arch_handle_alternative(unsigned short feature, struct special_alt *alt)
0011 {
0012     switch (feature) {
0013     case X86_FEATURE_SMAP:
0014         /*
0015          * If UACCESS validation is enabled; force that alternative;
0016          * otherwise force it the other way.
0017          *
0018          * What we want to avoid is having both the original and the
0019          * alternative code flow at the same time, in that case we can
0020          * find paths that see the STAC but take the NOP instead of
0021          * CLAC and the other way around.
0022          */
0023         if (opts.uaccess)
0024             alt->skip_orig = true;
0025         else
0026             alt->skip_alt = true;
0027         break;
0028     case X86_FEATURE_POPCNT:
0029         /*
0030          * It has been requested that we don't validate the !POPCNT
0031          * feature path which is a "very very small percentage of
0032          * machines".
0033          */
0034         alt->skip_orig = true;
0035         break;
0036     default:
0037         break;
0038     }
0039 }
0040 
0041 bool arch_support_alt_relocation(struct special_alt *special_alt,
0042                  struct instruction *insn,
0043                  struct reloc *reloc)
0044 {
0045     /*
0046      * The x86 alternatives code adjusts the offsets only when it
0047      * encounters a branch instruction at the very beginning of the
0048      * replacement group.
0049      */
0050     return insn->offset == special_alt->new_off &&
0051            (insn->type == INSN_CALL || is_jump(insn));
0052 }
0053 
0054 /*
0055  * There are 3 basic jump table patterns:
0056  *
0057  * 1. jmpq *[rodata addr](,%reg,8)
0058  *
0059  *    This is the most common case by far.  It jumps to an address in a simple
0060  *    jump table which is stored in .rodata.
0061  *
0062  * 2. jmpq *[rodata addr](%rip)
0063  *
0064  *    This is caused by a rare GCC quirk, currently only seen in three driver
0065  *    functions in the kernel, only with certain obscure non-distro configs.
0066  *
0067  *    As part of an optimization, GCC makes a copy of an existing switch jump
0068  *    table, modifies it, and then hard-codes the jump (albeit with an indirect
0069  *    jump) to use a single entry in the table.  The rest of the jump table and
0070  *    some of its jump targets remain as dead code.
0071  *
0072  *    In such a case we can just crudely ignore all unreachable instruction
0073  *    warnings for the entire object file.  Ideally we would just ignore them
0074  *    for the function, but that would require redesigning the code quite a
0075  *    bit.  And honestly that's just not worth doing: unreachable instruction
0076  *    warnings are of questionable value anyway, and this is such a rare issue.
0077  *
0078  * 3. mov [rodata addr],%reg1
0079  *    ... some instructions ...
0080  *    jmpq *(%reg1,%reg2,8)
0081  *
0082  *    This is a fairly uncommon pattern which is new for GCC 6.  As of this
0083  *    writing, there are 11 occurrences of it in the allmodconfig kernel.
0084  *
0085  *    As of GCC 7 there are quite a few more of these and the 'in between' code
0086  *    is significant. Esp. with KASAN enabled some of the code between the mov
0087  *    and jmpq uses .rodata itself, which can confuse things.
0088  *
0089  *    TODO: Once we have DWARF CFI and smarter instruction decoding logic,
0090  *    ensure the same register is used in the mov and jump instructions.
0091  *
0092  *    NOTE: RETPOLINE made it harder still to decode dynamic jumps.
0093  */
0094 struct reloc *arch_find_switch_table(struct objtool_file *file,
0095                     struct instruction *insn)
0096 {
0097     struct reloc  *text_reloc, *rodata_reloc;
0098     struct section *table_sec;
0099     unsigned long table_offset;
0100 
0101     /* look for a relocation which references .rodata */
0102     text_reloc = find_reloc_by_dest_range(file->elf, insn->sec,
0103                           insn->offset, insn->len);
0104     if (!text_reloc || text_reloc->sym->type != STT_SECTION ||
0105         !text_reloc->sym->sec->rodata)
0106         return NULL;
0107 
0108     table_offset = text_reloc->addend;
0109     table_sec = text_reloc->sym->sec;
0110 
0111     if (text_reloc->type == R_X86_64_PC32)
0112         table_offset += 4;
0113 
0114     /*
0115      * Make sure the .rodata address isn't associated with a
0116      * symbol.  GCC jump tables are anonymous data.
0117      *
0118      * Also support C jump tables which are in the same format as
0119      * switch jump tables.  For objtool to recognize them, they
0120      * need to be placed in the C_JUMP_TABLE_SECTION section.  They
0121      * have symbols associated with them.
0122      */
0123     if (find_symbol_containing(table_sec, table_offset) &&
0124         strcmp(table_sec->name, C_JUMP_TABLE_SECTION))
0125         return NULL;
0126 
0127     /*
0128      * Each table entry has a rela associated with it.  The rela
0129      * should reference text in the same function as the original
0130      * instruction.
0131      */
0132     rodata_reloc = find_reloc_by_dest(file->elf, table_sec, table_offset);
0133     if (!rodata_reloc)
0134         return NULL;
0135 
0136     /*
0137      * Use of RIP-relative switch jumps is quite rare, and
0138      * indicates a rare GCC quirk/bug which can leave dead
0139      * code behind.
0140      */
0141     if (text_reloc->type == R_X86_64_PC32)
0142         file->ignore_unreachables = true;
0143 
0144     return rodata_reloc;
0145 }