Merge tag 'x86_alternatives_for_v6.3_rc1' of git://git.kernel.org/pub/scm/linux/kerne...
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 21 Feb 2023 16:27:47 +0000 (08:27 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 21 Feb 2023 16:27:47 +0000 (08:27 -0800)
Pull x86 asm alternatives updates from Borislav Petkov:

 - Teach the static_call patching infrastructure to handle conditional
   tall calls properly which can be static calls too

 - Add proper struct alt_instr.flags which controls different aspects of
   insn patching behavior

* tag 'x86_alternatives_for_v6.3_rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  x86/static_call: Add support for Jcc tail-calls
  x86/alternatives: Teach text_poke_bp() to patch Jcc.d32 instructions
  x86/alternatives: Introduce int3_emulate_jcc()
  x86/alternatives: Add alt_instr.flags

arch/x86/include/asm/alternative.h
arch/x86/include/asm/text-patching.h
arch/x86/kernel/alternative.c
arch/x86/kernel/kprobes/core.c
arch/x86/kernel/static_call.c
tools/objtool/arch/x86/include/arch/special.h

index 7659217..e2975a3 100644 (file)
@@ -6,8 +6,10 @@
 #include <linux/stringify.h>
 #include <asm/asm.h>
 
-#define ALTINSTR_FLAG_INV      (1 << 15)
-#define ALT_NOT(feat)          ((feat) | ALTINSTR_FLAG_INV)
+#define ALT_FLAGS_SHIFT                16
+
+#define ALT_FLAG_NOT           BIT(0)
+#define ALT_NOT(feature)       ((ALT_FLAG_NOT << ALT_FLAGS_SHIFT) | (feature))
 
 #ifndef __ASSEMBLY__
 
        ".long 999b - .\n\t"                                    \
        ".popsection\n\t"
 
+/*
+ * The patching flags are part of the upper bits of the @ft_flags parameter when
+ * specifying them. The split is currently like this:
+ *
+ * [31... flags ...16][15... CPUID feature bit ...0]
+ *
+ * but since this is all hidden in the macros argument being split, those fields can be
+ * extended in the future to fit in a u64 or however the need arises.
+ */
 struct alt_instr {
        s32 instr_offset;       /* original instruction */
        s32 repl_offset;        /* offset to replacement instruction */
-       u16 cpuid;              /* cpuid bit set for replacement */
+
+       union {
+               struct {
+                       u32 cpuid: 16;  /* CPUID bit set for replacement */
+                       u32 flags: 16;  /* patching control flags */
+               };
+               u32 ft_flags;
+       };
+
        u8  instrlen;           /* length of original instruction */
        u8  replacementlen;     /* length of new instruction */
 } __packed;
@@ -182,10 +201,10 @@ static inline int alternatives_text_reserved(void *start, void *end)
                " - (" alt_slen ")), 0x90\n"                                                    \
        alt_end_marker ":\n"
 
-#define ALTINSTR_ENTRY(feature, num)                                         \
+#define ALTINSTR_ENTRY(ft_flags, num)                                        \
        " .long 661b - .\n"                             /* label           */ \
        " .long " b_replacement(num)"f - .\n"           /* new instruction */ \
-       " .word " __stringify(feature) "\n"             /* feature bit     */ \
+       " .4byte " __stringify(ft_flags) "\n"           /* feature + flags */ \
        " .byte " alt_total_slen "\n"                   /* source len      */ \
        " .byte " alt_rlen(num) "\n"                    /* replacement len */
 
@@ -194,20 +213,20 @@ static inline int alternatives_text_reserved(void *start, void *end)
        b_replacement(num)":\n\t" newinstr "\n" e_replacement(num) ":\n"
 
 /* alternative assembly primitive: */
-#define ALTERNATIVE(oldinstr, newinstr, feature)                       \
+#define ALTERNATIVE(oldinstr, newinstr, ft_flags)                      \
        OLDINSTR(oldinstr, 1)                                           \
        ".pushsection .altinstructions,\"a\"\n"                         \
-       ALTINSTR_ENTRY(feature, 1)                                      \
+       ALTINSTR_ENTRY(ft_flags, 1)                                     \
        ".popsection\n"                                                 \
        ".pushsection .altinstr_replacement, \"ax\"\n"                  \
        ALTINSTR_REPLACEMENT(newinstr, 1)                               \
        ".popsection\n"
 
-#define ALTERNATIVE_2(oldinstr, newinstr1, feature1, newinstr2, feature2)\
+#define ALTERNATIVE_2(oldinstr, newinstr1, ft_flags1, newinstr2, ft_flags2) \
        OLDINSTR_2(oldinstr, 1, 2)                                      \
        ".pushsection .altinstructions,\"a\"\n"                         \
-       ALTINSTR_ENTRY(feature1, 1)                                     \
-       ALTINSTR_ENTRY(feature2, 2)                                     \
+       ALTINSTR_ENTRY(ft_flags1, 1)                                    \
+       ALTINSTR_ENTRY(ft_flags2, 2)                                    \
        ".popsection\n"                                                 \
        ".pushsection .altinstr_replacement, \"ax\"\n"                  \
        ALTINSTR_REPLACEMENT(newinstr1, 1)                              \
@@ -215,21 +234,22 @@ static inline int alternatives_text_reserved(void *start, void *end)
        ".popsection\n"
 
 /* If @feature is set, patch in @newinstr_yes, otherwise @newinstr_no. */
-#define ALTERNATIVE_TERNARY(oldinstr, feature, newinstr_yes, newinstr_no) \
+#define ALTERNATIVE_TERNARY(oldinstr, ft_flags, newinstr_yes, newinstr_no) \
        ALTERNATIVE_2(oldinstr, newinstr_no, X86_FEATURE_ALWAYS,        \
-                     newinstr_yes, feature)
-
-#define ALTERNATIVE_3(oldinsn, newinsn1, feat1, newinsn2, feat2, newinsn3, feat3) \
-       OLDINSTR_3(oldinsn, 1, 2, 3)                                            \
-       ".pushsection .altinstructions,\"a\"\n"                                 \
-       ALTINSTR_ENTRY(feat1, 1)                                                \
-       ALTINSTR_ENTRY(feat2, 2)                                                \
-       ALTINSTR_ENTRY(feat3, 3)                                                \
-       ".popsection\n"                                                         \
-       ".pushsection .altinstr_replacement, \"ax\"\n"                          \
-       ALTINSTR_REPLACEMENT(newinsn1, 1)                                       \
-       ALTINSTR_REPLACEMENT(newinsn2, 2)                                       \
-       ALTINSTR_REPLACEMENT(newinsn3, 3)                                       \
+                     newinstr_yes, ft_flags)
+
+#define ALTERNATIVE_3(oldinsn, newinsn1, ft_flags1, newinsn2, ft_flags2, \
+                       newinsn3, ft_flags3)                            \
+       OLDINSTR_3(oldinsn, 1, 2, 3)                                    \
+       ".pushsection .altinstructions,\"a\"\n"                         \
+       ALTINSTR_ENTRY(ft_flags1, 1)                                    \
+       ALTINSTR_ENTRY(ft_flags2, 2)                                    \
+       ALTINSTR_ENTRY(ft_flags3, 3)                                    \
+       ".popsection\n"                                                 \
+       ".pushsection .altinstr_replacement, \"ax\"\n"                  \
+       ALTINSTR_REPLACEMENT(newinsn1, 1)                               \
+       ALTINSTR_REPLACEMENT(newinsn2, 2)                               \
+       ALTINSTR_REPLACEMENT(newinsn3, 3)                               \
        ".popsection\n"
 
 /*
@@ -244,14 +264,14 @@ static inline int alternatives_text_reserved(void *start, void *end)
  * For non barrier like inlines please define new variants
  * without volatile and memory clobber.
  */
-#define alternative(oldinstr, newinstr, feature)                       \
-       asm_inline volatile (ALTERNATIVE(oldinstr, newinstr, feature) : : : "memory")
+#define alternative(oldinstr, newinstr, ft_flags)                      \
+       asm_inline volatile (ALTERNATIVE(oldinstr, newinstr, ft_flags) : : : "memory")
 
-#define alternative_2(oldinstr, newinstr1, feature1, newinstr2, feature2) \
-       asm_inline volatile(ALTERNATIVE_2(oldinstr, newinstr1, feature1, newinstr2, feature2) ::: "memory")
+#define alternative_2(oldinstr, newinstr1, ft_flags1, newinstr2, ft_flags2) \
+       asm_inline volatile(ALTERNATIVE_2(oldinstr, newinstr1, ft_flags1, newinstr2, ft_flags2) ::: "memory")
 
-#define alternative_ternary(oldinstr, feature, newinstr_yes, newinstr_no) \
-       asm_inline volatile(ALTERNATIVE_TERNARY(oldinstr, feature, newinstr_yes, newinstr_no) ::: "memory")
+#define alternative_ternary(oldinstr, ft_flags, newinstr_yes, newinstr_no) \
+       asm_inline volatile(ALTERNATIVE_TERNARY(oldinstr, ft_flags, newinstr_yes, newinstr_no) ::: "memory")
 
 /*
  * Alternative inline assembly with input.
@@ -261,8 +281,8 @@ static inline int alternatives_text_reserved(void *start, void *end)
  * Argument numbers start with 1.
  * Leaving an unused argument 0 to keep API compatibility.
  */
-#define alternative_input(oldinstr, newinstr, feature, input...)       \
-       asm_inline volatile (ALTERNATIVE(oldinstr, newinstr, feature)   \
+#define alternative_input(oldinstr, newinstr, ft_flags, input...)      \
+       asm_inline volatile (ALTERNATIVE(oldinstr, newinstr, ft_flags)  \
                : : "i" (0), ## input)
 
 /*
@@ -273,20 +293,20 @@ static inline int alternatives_text_reserved(void *start, void *end)
  * Otherwise, if CPU has feature1, newinstr1 is used.
  * Otherwise, oldinstr is used.
  */
-#define alternative_input_2(oldinstr, newinstr1, feature1, newinstr2,       \
-                          feature2, input...)                               \
-       asm_inline volatile(ALTERNATIVE_2(oldinstr, newinstr1, feature1,     \
-               newinstr2, feature2)                                         \
+#define alternative_input_2(oldinstr, newinstr1, ft_flags1, newinstr2,      \
+                          ft_flags2, input...)                              \
+       asm_inline volatile(ALTERNATIVE_2(oldinstr, newinstr1, ft_flags1,     \
+               newinstr2, ft_flags2)                                        \
                : : "i" (0), ## input)
 
 /* Like alternative_input, but with a single output argument */
-#define alternative_io(oldinstr, newinstr, feature, output, input...)  \
-       asm_inline volatile (ALTERNATIVE(oldinstr, newinstr, feature)   \
+#define alternative_io(oldinstr, newinstr, ft_flags, output, input...) \
+       asm_inline volatile (ALTERNATIVE(oldinstr, newinstr, ft_flags)  \
                : output : "i" (0), ## input)
 
 /* Like alternative_io, but for replacing a direct call with another one. */
-#define alternative_call(oldfunc, newfunc, feature, output, input...)  \
-       asm_inline volatile (ALTERNATIVE("call %P[old]", "call %P[new]", feature) \
+#define alternative_call(oldfunc, newfunc, ft_flags, output, input...) \
+       asm_inline volatile (ALTERNATIVE("call %P[old]", "call %P[new]", ft_flags) \
                : output : [old] "i" (oldfunc), [new] "i" (newfunc), ## input)
 
 /*
@@ -295,10 +315,10 @@ static inline int alternatives_text_reserved(void *start, void *end)
  * Otherwise, if CPU has feature1, function1 is used.
  * Otherwise, old function is used.
  */
-#define alternative_call_2(oldfunc, newfunc1, feature1, newfunc2, feature2,   \
+#define alternative_call_2(oldfunc, newfunc1, ft_flags1, newfunc2, ft_flags2,   \
                           output, input...)                                  \
-       asm_inline volatile (ALTERNATIVE_2("call %P[old]", "call %P[new1]", feature1,\
-               "call %P[new2]", feature2)                                    \
+       asm_inline volatile (ALTERNATIVE_2("call %P[old]", "call %P[new1]", ft_flags1,\
+               "call %P[new2]", ft_flags2)                                   \
                : output, ASM_CALL_CONSTRAINT                                 \
                : [old] "i" (oldfunc), [new1] "i" (newfunc1),                 \
                  [new2] "i" (newfunc2), ## input)
@@ -347,10 +367,10 @@ static inline int alternatives_text_reserved(void *start, void *end)
  * enough information for the alternatives patching code to patch an
  * instruction. See apply_alternatives().
  */
-.macro altinstruction_entry orig alt feature orig_len alt_len
+.macro altinstr_entry orig alt ft_flags orig_len alt_len
        .long \orig - .
        .long \alt - .
-       .word \feature
+       .4byte \ft_flags
        .byte \orig_len
        .byte \alt_len
 .endm
@@ -361,7 +381,7 @@ static inline int alternatives_text_reserved(void *start, void *end)
  * @newinstr. ".skip" directive takes care of proper instruction padding
  * in case @newinstr is longer than @oldinstr.
  */
-.macro ALTERNATIVE oldinstr, newinstr, feature
+.macro ALTERNATIVE oldinstr, newinstr, ft_flags
 140:
        \oldinstr
 141:
@@ -369,7 +389,7 @@ static inline int alternatives_text_reserved(void *start, void *end)
 142:
 
        .pushsection .altinstructions,"a"
-       altinstruction_entry 140b,143f,\feature,142b-140b,144f-143f
+       altinstr_entry 140b,143f,\ft_flags,142b-140b,144f-143f
        .popsection
 
        .pushsection .altinstr_replacement,"ax"
@@ -399,7 +419,7 @@ static inline int alternatives_text_reserved(void *start, void *end)
  * has @feature1, it replaces @oldinstr with @newinstr1. If CPU has
  * @feature2, it replaces @oldinstr with @feature2.
  */
-.macro ALTERNATIVE_2 oldinstr, newinstr1, feature1, newinstr2, feature2
+.macro ALTERNATIVE_2 oldinstr, newinstr1, ft_flags1, newinstr2, ft_flags2
 140:
        \oldinstr
 141:
@@ -408,8 +428,8 @@ static inline int alternatives_text_reserved(void *start, void *end)
 142:
 
        .pushsection .altinstructions,"a"
-       altinstruction_entry 140b,143f,\feature1,142b-140b,144f-143f
-       altinstruction_entry 140b,144f,\feature2,142b-140b,145f-144f
+       altinstr_entry 140b,143f,\ft_flags1,142b-140b,144f-143f
+       altinstr_entry 140b,144f,\ft_flags2,142b-140b,145f-144f
        .popsection
 
        .pushsection .altinstr_replacement,"ax"
@@ -421,7 +441,7 @@ static inline int alternatives_text_reserved(void *start, void *end)
        .popsection
 .endm
 
-.macro ALTERNATIVE_3 oldinstr, newinstr1, feature1, newinstr2, feature2, newinstr3, feature3
+.macro ALTERNATIVE_3 oldinstr, newinstr1, ft_flags1, newinstr2, ft_flags2, newinstr3, ft_flags3
 140:
        \oldinstr
 141:
@@ -430,9 +450,9 @@ static inline int alternatives_text_reserved(void *start, void *end)
 142:
 
        .pushsection .altinstructions,"a"
-       altinstruction_entry 140b,143f,\feature1,142b-140b,144f-143f
-       altinstruction_entry 140b,144f,\feature2,142b-140b,145f-144f
-       altinstruction_entry 140b,145f,\feature3,142b-140b,146f-145f
+       altinstr_entry 140b,143f,\ft_flags1,142b-140b,144f-143f
+       altinstr_entry 140b,144f,\ft_flags2,142b-140b,145f-144f
+       altinstr_entry 140b,145f,\ft_flags3,142b-140b,146f-145f
        .popsection
 
        .pushsection .altinstr_replacement,"ax"
@@ -447,9 +467,9 @@ static inline int alternatives_text_reserved(void *start, void *end)
 .endm
 
 /* If @feature is set, patch in @newinstr_yes, otherwise @newinstr_no. */
-#define ALTERNATIVE_TERNARY(oldinstr, feature, newinstr_yes, newinstr_no) \
+#define ALTERNATIVE_TERNARY(oldinstr, ft_flags, newinstr_yes, newinstr_no) \
        ALTERNATIVE_2 oldinstr, newinstr_no, X86_FEATURE_ALWAYS,        \
-       newinstr_yes, feature
+       newinstr_yes, ft_flags
 
 #endif /* __ASSEMBLY__ */
 
index f4b87f0..29832c3 100644 (file)
@@ -184,6 +184,37 @@ void int3_emulate_ret(struct pt_regs *regs)
        unsigned long ip = int3_emulate_pop(regs);
        int3_emulate_jmp(regs, ip);
 }
+
+static __always_inline
+void int3_emulate_jcc(struct pt_regs *regs, u8 cc, unsigned long ip, unsigned long disp)
+{
+       static const unsigned long jcc_mask[6] = {
+               [0] = X86_EFLAGS_OF,
+               [1] = X86_EFLAGS_CF,
+               [2] = X86_EFLAGS_ZF,
+               [3] = X86_EFLAGS_CF | X86_EFLAGS_ZF,
+               [4] = X86_EFLAGS_SF,
+               [5] = X86_EFLAGS_PF,
+       };
+
+       bool invert = cc & 1;
+       bool match;
+
+       if (cc < 0xc) {
+               match = regs->flags & jcc_mask[cc >> 1];
+       } else {
+               match = ((regs->flags & X86_EFLAGS_SF) >> X86_EFLAGS_SF_BIT) ^
+                       ((regs->flags & X86_EFLAGS_OF) >> X86_EFLAGS_OF_BIT);
+               if (cc >= 0xe)
+                       match = match || (regs->flags & X86_EFLAGS_ZF);
+       }
+
+       if ((match && !invert) || (!match && invert))
+               ip += disp;
+
+       int3_emulate_jmp(regs, ip);
+}
+
 #endif /* !CONFIG_UML_X86 */
 
 #endif /* _ASM_X86_TEXT_PATCHING_H */
index 7d8c3cb..f615e0c 100644 (file)
@@ -282,27 +282,25 @@ void __init_or_module noinline apply_alternatives(struct alt_instr *start,
         */
        for (a = start; a < end; a++) {
                int insn_buff_sz = 0;
-               /* Mask away "NOT" flag bit for feature to test. */
-               u16 feature = a->cpuid & ~ALTINSTR_FLAG_INV;
 
                instr = (u8 *)&a->instr_offset + a->instr_offset;
                replacement = (u8 *)&a->repl_offset + a->repl_offset;
                BUG_ON(a->instrlen > sizeof(insn_buff));
-               BUG_ON(feature >= (NCAPINTS + NBUGINTS) * 32);
+               BUG_ON(a->cpuid >= (NCAPINTS + NBUGINTS) * 32);
 
                /*
                 * Patch if either:
                 * - feature is present
-                * - feature not present but ALTINSTR_FLAG_INV is set to mean,
+                * - feature not present but ALT_FLAG_NOT is set to mean,
                 *   patch if feature is *NOT* present.
                 */
-               if (!boot_cpu_has(feature) == !(a->cpuid & ALTINSTR_FLAG_INV))
+               if (!boot_cpu_has(a->cpuid) == !(a->flags & ALT_FLAG_NOT))
                        goto next;
 
                DPRINTK("feat: %s%d*32+%d, old: (%pS (%px) len: %d), repl: (%px, len: %d)",
-                       (a->cpuid & ALTINSTR_FLAG_INV) ? "!" : "",
-                       feature >> 5,
-                       feature & 0x1f,
+                       (a->flags & ALT_FLAG_NOT) ? "!" : "",
+                       a->cpuid >> 5,
+                       a->cpuid & 0x1f,
                        instr, instr, a->instrlen,
                        replacement, a->replacementlen);
 
@@ -340,6 +338,12 @@ next:
        }
 }
 
+static inline bool is_jcc32(struct insn *insn)
+{
+       /* Jcc.d32 second opcode byte is in the range: 0x80-0x8f */
+       return insn->opcode.bytes[0] == 0x0f && (insn->opcode.bytes[1] & 0xf0) == 0x80;
+}
+
 #if defined(CONFIG_RETPOLINE) && defined(CONFIG_OBJTOOL)
 
 /*
@@ -378,12 +382,6 @@ static int emit_indirect(int op, int reg, u8 *bytes)
        return i;
 }
 
-static inline bool is_jcc32(struct insn *insn)
-{
-       /* Jcc.d32 second opcode byte is in the range: 0x80-0x8f */
-       return insn->opcode.bytes[0] == 0x0f && (insn->opcode.bytes[1] & 0xf0) == 0x80;
-}
-
 static int emit_call_track_retpoline(void *addr, struct insn *insn, int reg, u8 *bytes)
 {
        u8 op = insn->opcode.bytes[0];
@@ -1772,6 +1770,11 @@ void text_poke_sync(void)
        on_each_cpu(do_sync_core, NULL, 1);
 }
 
+/*
+ * NOTE: crazy scheme to allow patching Jcc.d32 but not increase the size of
+ * this thing. When len == 6 everything is prefixed with 0x0f and we map
+ * opcode to Jcc.d8, using len to distinguish.
+ */
 struct text_poke_loc {
        /* addr := _stext + rel_addr */
        s32 rel_addr;
@@ -1893,6 +1896,10 @@ noinstr int poke_int3_handler(struct pt_regs *regs)
                int3_emulate_jmp(regs, (long)ip + tp->disp);
                break;
 
+       case 0x70 ... 0x7f: /* Jcc */
+               int3_emulate_jcc(regs, tp->opcode & 0xf, (long)ip, tp->disp);
+               break;
+
        default:
                BUG();
        }
@@ -1966,16 +1973,26 @@ static void text_poke_bp_batch(struct text_poke_loc *tp, unsigned int nr_entries
         * Second step: update all but the first byte of the patched range.
         */
        for (do_sync = 0, i = 0; i < nr_entries; i++) {
-               u8 old[POKE_MAX_OPCODE_SIZE] = { tp[i].old, };
+               u8 old[POKE_MAX_OPCODE_SIZE+1] = { tp[i].old, };
+               u8 _new[POKE_MAX_OPCODE_SIZE+1];
+               const u8 *new = tp[i].text;
                int len = tp[i].len;
 
                if (len - INT3_INSN_SIZE > 0) {
                        memcpy(old + INT3_INSN_SIZE,
                               text_poke_addr(&tp[i]) + INT3_INSN_SIZE,
                               len - INT3_INSN_SIZE);
+
+                       if (len == 6) {
+                               _new[0] = 0x0f;
+                               memcpy(_new + 1, new, 5);
+                               new = _new;
+                       }
+
                        text_poke(text_poke_addr(&tp[i]) + INT3_INSN_SIZE,
-                                 (const char *)tp[i].text + INT3_INSN_SIZE,
+                                 new + INT3_INSN_SIZE,
                                  len - INT3_INSN_SIZE);
+
                        do_sync++;
                }
 
@@ -2003,8 +2020,7 @@ static void text_poke_bp_batch(struct text_poke_loc *tp, unsigned int nr_entries
                 * The old instruction is recorded so that the event can be
                 * processed forwards or backwards.
                 */
-               perf_event_text_poke(text_poke_addr(&tp[i]), old, len,
-                                    tp[i].text, len);
+               perf_event_text_poke(text_poke_addr(&tp[i]), old, len, new, len);
        }
 
        if (do_sync) {
@@ -2021,10 +2037,15 @@ static void text_poke_bp_batch(struct text_poke_loc *tp, unsigned int nr_entries
         * replacing opcode.
         */
        for (do_sync = 0, i = 0; i < nr_entries; i++) {
-               if (tp[i].text[0] == INT3_INSN_OPCODE)
+               u8 byte = tp[i].text[0];
+
+               if (tp[i].len == 6)
+                       byte = 0x0f;
+
+               if (byte == INT3_INSN_OPCODE)
                        continue;
 
-               text_poke(text_poke_addr(&tp[i]), tp[i].text, INT3_INSN_SIZE);
+               text_poke(text_poke_addr(&tp[i]), &byte, INT3_INSN_SIZE);
                do_sync++;
        }
 
@@ -2042,9 +2063,11 @@ static void text_poke_loc_init(struct text_poke_loc *tp, void *addr,
                               const void *opcode, size_t len, const void *emulate)
 {
        struct insn insn;
-       int ret, i;
+       int ret, i = 0;
 
-       memcpy((void *)tp->text, opcode, len);
+       if (len == 6)
+               i = 1;
+       memcpy((void *)tp->text, opcode+i, len-i);
        if (!emulate)
                emulate = opcode;
 
@@ -2055,6 +2078,13 @@ static void text_poke_loc_init(struct text_poke_loc *tp, void *addr,
        tp->len = len;
        tp->opcode = insn.opcode.bytes[0];
 
+       if (is_jcc32(&insn)) {
+               /*
+                * Map Jcc.d32 onto Jcc.d8 and use len to distinguish.
+                */
+               tp->opcode = insn.opcode.bytes[1] - 0x10;
+       }
+
        switch (tp->opcode) {
        case RET_INSN_OPCODE:
        case JMP32_INSN_OPCODE:
@@ -2071,7 +2101,6 @@ static void text_poke_loc_init(struct text_poke_loc *tp, void *addr,
                BUG_ON(len != insn.length);
        }
 
-
        switch (tp->opcode) {
        case INT3_INSN_OPCODE:
        case RET_INSN_OPCODE:
@@ -2080,6 +2109,7 @@ static void text_poke_loc_init(struct text_poke_loc *tp, void *addr,
        case CALL_INSN_OPCODE:
        case JMP32_INSN_OPCODE:
        case JMP8_INSN_OPCODE:
+       case 0x70 ... 0x7f: /* Jcc */
                tp->disp = insn.immediate.value;
                break;
 
index 754da3b..f7f6042 100644 (file)
@@ -464,50 +464,26 @@ static void kprobe_emulate_call(struct kprobe *p, struct pt_regs *regs)
 }
 NOKPROBE_SYMBOL(kprobe_emulate_call);
 
-static nokprobe_inline
-void __kprobe_emulate_jmp(struct kprobe *p, struct pt_regs *regs, bool cond)
+static void kprobe_emulate_jmp(struct kprobe *p, struct pt_regs *regs)
 {
        unsigned long ip = regs->ip - INT3_INSN_SIZE + p->ainsn.size;
 
-       if (cond)
-               ip += p->ainsn.rel32;
+       ip += p->ainsn.rel32;
        int3_emulate_jmp(regs, ip);
 }
-
-static void kprobe_emulate_jmp(struct kprobe *p, struct pt_regs *regs)
-{
-       __kprobe_emulate_jmp(p, regs, true);
-}
 NOKPROBE_SYMBOL(kprobe_emulate_jmp);
 
-static const unsigned long jcc_mask[6] = {
-       [0] = X86_EFLAGS_OF,
-       [1] = X86_EFLAGS_CF,
-       [2] = X86_EFLAGS_ZF,
-       [3] = X86_EFLAGS_CF | X86_EFLAGS_ZF,
-       [4] = X86_EFLAGS_SF,
-       [5] = X86_EFLAGS_PF,
-};
-
 static void kprobe_emulate_jcc(struct kprobe *p, struct pt_regs *regs)
 {
-       bool invert = p->ainsn.jcc.type & 1;
-       bool match;
+       unsigned long ip = regs->ip - INT3_INSN_SIZE + p->ainsn.size;
 
-       if (p->ainsn.jcc.type < 0xc) {
-               match = regs->flags & jcc_mask[p->ainsn.jcc.type >> 1];
-       } else {
-               match = ((regs->flags & X86_EFLAGS_SF) >> X86_EFLAGS_SF_BIT) ^
-                       ((regs->flags & X86_EFLAGS_OF) >> X86_EFLAGS_OF_BIT);
-               if (p->ainsn.jcc.type >= 0xe)
-                       match = match || (regs->flags & X86_EFLAGS_ZF);
-       }
-       __kprobe_emulate_jmp(p, regs, (match && !invert) || (!match && invert));
+       int3_emulate_jcc(regs, p->ainsn.jcc.type, ip, p->ainsn.rel32);
 }
 NOKPROBE_SYMBOL(kprobe_emulate_jcc);
 
 static void kprobe_emulate_loop(struct kprobe *p, struct pt_regs *regs)
 {
+       unsigned long ip = regs->ip - INT3_INSN_SIZE + p->ainsn.size;
        bool match;
 
        if (p->ainsn.loop.type != 3) {  /* LOOP* */
@@ -535,7 +511,9 @@ static void kprobe_emulate_loop(struct kprobe *p, struct pt_regs *regs)
        else if (p->ainsn.loop.type == 1)       /* LOOPE */
                match = match && (regs->flags & X86_EFLAGS_ZF);
 
-       __kprobe_emulate_jmp(p, regs, match);
+       if (match)
+               ip += p->ainsn.rel32;
+       int3_emulate_jmp(regs, ip);
 }
 NOKPROBE_SYMBOL(kprobe_emulate_loop);
 
index 2ebc338..b70670a 100644 (file)
@@ -9,6 +9,7 @@ enum insn_type {
        NOP = 1,  /* site cond-call */
        JMP = 2,  /* tramp / site tail-call */
        RET = 3,  /* tramp / site cond-tail-call */
+       JCC = 4,
 };
 
 /*
@@ -25,12 +26,40 @@ static const u8 xor5rax[] = { 0x2e, 0x2e, 0x2e, 0x31, 0xc0 };
 
 static const u8 retinsn[] = { RET_INSN_OPCODE, 0xcc, 0xcc, 0xcc, 0xcc };
 
+static u8 __is_Jcc(u8 *insn) /* Jcc.d32 */
+{
+       u8 ret = 0;
+
+       if (insn[0] == 0x0f) {
+               u8 tmp = insn[1];
+               if ((tmp & 0xf0) == 0x80)
+                       ret = tmp;
+       }
+
+       return ret;
+}
+
+extern void __static_call_return(void);
+
+asm (".global __static_call_return\n\t"
+     ".type __static_call_return, @function\n\t"
+     ASM_FUNC_ALIGN "\n\t"
+     "__static_call_return:\n\t"
+     ANNOTATE_NOENDBR
+     ANNOTATE_RETPOLINE_SAFE
+     "ret; int3\n\t"
+     ".size __static_call_return, . - __static_call_return \n\t");
+
 static void __ref __static_call_transform(void *insn, enum insn_type type,
                                          void *func, bool modinit)
 {
        const void *emulate = NULL;
        int size = CALL_INSN_SIZE;
        const void *code;
+       u8 op, buf[6];
+
+       if ((type == JMP || type == RET) && (op = __is_Jcc(insn)))
+               type = JCC;
 
        switch (type) {
        case CALL:
@@ -57,6 +86,20 @@ static void __ref __static_call_transform(void *insn, enum insn_type type,
                else
                        code = &retinsn;
                break;
+
+       case JCC:
+               if (!func) {
+                       func = __static_call_return;
+                       if (cpu_feature_enabled(X86_FEATURE_RETHUNK))
+                               func = x86_return_thunk;
+               }
+
+               buf[0] = 0x0f;
+               __text_gen_insn(buf+1, op, insn+1, func, 5);
+               code = buf;
+               size = 6;
+
+               break;
        }
 
        if (memcmp(insn, code, size) == 0)
@@ -68,9 +111,9 @@ static void __ref __static_call_transform(void *insn, enum insn_type type,
        text_poke_bp(insn, code, size, emulate);
 }
 
-static void __static_call_validate(void *insn, bool tail, bool tramp)
+static void __static_call_validate(u8 *insn, bool tail, bool tramp)
 {
-       u8 opcode = *(u8 *)insn;
+       u8 opcode = insn[0];
 
        if (tramp && memcmp(insn+5, tramp_ud, 3)) {
                pr_err("trampoline signature fail");
@@ -79,7 +122,8 @@ static void __static_call_validate(void *insn, bool tail, bool tramp)
 
        if (tail) {
                if (opcode == JMP32_INSN_OPCODE ||
-                   opcode == RET_INSN_OPCODE)
+                   opcode == RET_INSN_OPCODE ||
+                   __is_Jcc(insn))
                        return;
        } else {
                if (opcode == CALL_INSN_OPCODE ||
index f2918f7..ca81313 100644 (file)
 #define JUMP_NEW_OFFSET                4
 #define JUMP_KEY_OFFSET                8
 
-#define ALT_ENTRY_SIZE         12
+#define ALT_ENTRY_SIZE         14
 #define ALT_ORIG_OFFSET                0
 #define ALT_NEW_OFFSET         4
 #define ALT_FEATURE_OFFSET     8
-#define ALT_ORIG_LEN_OFFSET    10
-#define ALT_NEW_LEN_OFFSET     11
+#define ALT_ORIG_LEN_OFFSET    12
+#define ALT_NEW_LEN_OFFSET     13
 
 #endif /* _X86_ARCH_SPECIAL_H */