objtool: Add IBT/ENDBR decoding
authorPeter Zijlstra <peterz@infradead.org>
Tue, 8 Mar 2022 15:30:53 +0000 (16:30 +0100)
committerPeter Zijlstra <peterz@infradead.org>
Tue, 15 Mar 2022 09:32:46 +0000 (10:32 +0100)
Intel IBT requires the target of any indirect CALL or JMP instruction
to be the ENDBR instruction; optionally it allows those two
instructions to have a NOTRACK prefix in order to avoid this
requirement.

The kernel will not enable the use of NOTRACK, as such any occurence
of it in compiler generated code should be flagged.

Teach objtool to Decode ENDBR instructions and WARN about NOTRACK
prefixes.

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>
Link: https://lore.kernel.org/r/20220308154319.645963517@infradead.org
tools/objtool/arch/x86/decode.c
tools/objtool/include/objtool/arch.h

index 479e769ca3241f397086f00c9c91fc144fd39f98..943cb41cddf7cd8e75f199c5b58335b06d05afc8 100644 (file)
@@ -103,6 +103,18 @@ unsigned long arch_jump_destination(struct instruction *insn)
 #define rm_is_mem(reg) (mod_is_mem() && !is_RIP() && rm_is(reg))
 #define rm_is_reg(reg) (mod_is_reg() && modrm_rm == (reg))
 
+static bool has_notrack_prefix(struct insn *insn)
+{
+       int i;
+
+       for (i = 0; i < insn->prefixes.nbytes; i++) {
+               if (insn->prefixes.bytes[i] == 0x3e)
+                       return true;
+       }
+
+       return false;
+}
+
 int arch_decode_instruction(struct objtool_file *file, const struct section *sec,
                            unsigned long offset, unsigned int maxlen,
                            unsigned int *len, enum insn_type *type,
@@ -112,7 +124,7 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
        const struct elf *elf = file->elf;
        struct insn insn;
        int x86_64, ret;
-       unsigned char op1, op2, op3,
+       unsigned char op1, op2, op3, prefix,
                      rex = 0, rex_b = 0, rex_r = 0, rex_w = 0, rex_x = 0,
                      modrm = 0, modrm_mod = 0, modrm_rm = 0, modrm_reg = 0,
                      sib = 0, /* sib_scale = 0, */ sib_index = 0, sib_base = 0;
@@ -137,6 +149,8 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
        if (insn.vex_prefix.nbytes)
                return 0;
 
+       prefix = insn.prefixes.bytes[0];
+
        op1 = insn.opcode.bytes[0];
        op2 = insn.opcode.bytes[1];
        op3 = insn.opcode.bytes[2];
@@ -492,6 +506,12 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
                        /* nopl/nopw */
                        *type = INSN_NOP;
 
+               } else if (op2 == 0x1e) {
+
+                       if (prefix == 0xf3 && (modrm == 0xfa || modrm == 0xfb))
+                               *type = INSN_ENDBR;
+
+
                } else if (op2 == 0x38 && op3 == 0xf8) {
                        if (insn.prefixes.nbytes == 1 &&
                            insn.prefixes.bytes[0] == 0xf2) {
@@ -636,20 +656,24 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
                break;
 
        case 0xff:
-               if (modrm_reg == 2 || modrm_reg == 3)
+               if (modrm_reg == 2 || modrm_reg == 3) {
 
                        *type = INSN_CALL_DYNAMIC;
+                       if (has_notrack_prefix(&insn))
+                               WARN("notrack prefix found at %s:0x%lx", sec->name, offset);
 
-               else if (modrm_reg == 4)
+               } else if (modrm_reg == 4) {
 
                        *type = INSN_JUMP_DYNAMIC;
+                       if (has_notrack_prefix(&insn))
+                               WARN("notrack prefix found at %s:0x%lx", sec->name, offset);
 
-               else if (modrm_reg == 5)
+               } else if (modrm_reg == 5) {
 
                        /* jmpf */
                        *type = INSN_CONTEXT_SWITCH;
 
-               else if (modrm_reg == 6) {
+               else if (modrm_reg == 6) {
 
                        /* push from mem */
                        ADD_OP(op) {
index 76bae3078286f1546cbbd8103068d1a1a1777403..9b19cc3041955bab4375a280e8d209b5e1054d42 100644 (file)
@@ -27,6 +27,7 @@ enum insn_type {
        INSN_STD,
        INSN_CLD,
        INSN_TRAP,
+       INSN_ENDBR,
        INSN_OTHER,
 };