Merge tag 'x86_mm_for_6.2_v2' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
[linux-2.6-microblaze.git] / arch / x86 / kernel / alternative.c
index e240351..7d8c3cb 100644 (file)
@@ -116,6 +116,7 @@ static void __init_or_module add_nops(void *insns, unsigned int len)
 
 extern s32 __retpoline_sites[], __retpoline_sites_end[];
 extern s32 __return_sites[], __return_sites_end[];
+extern s32 __cfi_sites[], __cfi_sites_end[];
 extern s32 __ibt_endbr_seal[], __ibt_endbr_seal_end[];
 extern struct alt_instr __alt_instructions[], __alt_instructions_end[];
 extern s32 __smp_locks[], __smp_locks_end[];
@@ -377,6 +378,56 @@ 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];
+       int i = 0;
+
+       /*
+        * Clang does 'weird' Jcc __x86_indirect_thunk_r11 conditional
+        * tail-calls. Deal with them.
+        */
+       if (is_jcc32(insn)) {
+               bytes[i++] = op;
+               op = insn->opcode.bytes[1];
+               goto clang_jcc;
+       }
+
+       if (insn->length == 6)
+               bytes[i++] = 0x2e; /* CS-prefix */
+
+       switch (op) {
+       case CALL_INSN_OPCODE:
+               __text_gen_insn(bytes+i, op, addr+i,
+                               __x86_indirect_call_thunk_array[reg],
+                               CALL_INSN_SIZE);
+               i += CALL_INSN_SIZE;
+               break;
+
+       case JMP32_INSN_OPCODE:
+clang_jcc:
+               __text_gen_insn(bytes+i, op, addr+i,
+                               __x86_indirect_jump_thunk_array[reg],
+                               JMP32_INSN_SIZE);
+               i += JMP32_INSN_SIZE;
+               break;
+
+       default:
+               WARN(1, "%pS %px %*ph\n", addr, addr, 6, addr);
+               return -1;
+       }
+
+       WARN_ON_ONCE(i != insn->length);
+
+       return i;
+}
+
 /*
  * Rewrite the compiler generated retpoline thunk calls.
  *
@@ -409,8 +460,12 @@ static int patch_retpoline(void *addr, struct insn *insn, u8 *bytes)
        BUG_ON(reg == 4);
 
        if (cpu_feature_enabled(X86_FEATURE_RETPOLINE) &&
-           !cpu_feature_enabled(X86_FEATURE_RETPOLINE_LFENCE))
+           !cpu_feature_enabled(X86_FEATURE_RETPOLINE_LFENCE)) {
+               if (cpu_feature_enabled(X86_FEATURE_CALL_DEPTH))
+                       return emit_call_track_retpoline(addr, insn, reg, bytes);
+
                return -1;
+       }
 
        op = insn->opcode.bytes[0];
 
@@ -427,8 +482,7 @@ static int patch_retpoline(void *addr, struct insn *insn, u8 *bytes)
         *   [ NOP ]
         * 1:
         */
-       /* Jcc.d32 second opcode byte is in the range: 0x80-0x8f */
-       if (op == 0x0f && (insn->opcode.bytes[1] & 0xf0) == 0x80) {
+       if (is_jcc32(insn)) {
                cc = insn->opcode.bytes[1] & 0xf;
                cc ^= 1; /* invert condition */
 
@@ -518,6 +572,11 @@ void __init_or_module noinline apply_retpolines(s32 *start, s32 *end)
 }
 
 #ifdef CONFIG_RETHUNK
+
+#ifdef CONFIG_CALL_THUNKS
+void (*x86_return_thunk)(void) __ro_after_init = &__x86_return_thunk;
+#endif
+
 /*
  * Rewrite the compiler generated return thunk tail-calls.
  *
@@ -533,14 +592,18 @@ static int patch_return(void *addr, struct insn *insn, u8 *bytes)
 {
        int i = 0;
 
-       if (cpu_feature_enabled(X86_FEATURE_RETHUNK))
-               return -1;
+       if (cpu_feature_enabled(X86_FEATURE_RETHUNK)) {
+               if (x86_return_thunk == __x86_return_thunk)
+                       return -1;
 
-       bytes[i++] = RET_INSN_OPCODE;
+               i = JMP32_INSN_SIZE;
+               __text_gen_insn(bytes, JMP32_INSN_OPCODE, addr, x86_return_thunk, i);
+       } else {
+               bytes[i++] = RET_INSN_OPCODE;
+       }
 
        for (; i < insn->length;)
                bytes[i++] = INT3_INSN_OPCODE;
-
        return i;
 }
 
@@ -594,6 +657,28 @@ void __init_or_module noinline apply_returns(s32 *start, s32 *end) { }
 
 #ifdef CONFIG_X86_KERNEL_IBT
 
+static void poison_endbr(void *addr, bool warn)
+{
+       u32 endbr, poison = gen_endbr_poison();
+
+       if (WARN_ON_ONCE(get_kernel_nofault(endbr, addr)))
+               return;
+
+       if (!is_endbr(endbr)) {
+               WARN_ON_ONCE(warn);
+               return;
+       }
+
+       DPRINTK("ENDBR at: %pS (%px)", addr, addr);
+
+       /*
+        * When we have IBT, the lack of ENDBR will trigger #CP
+        */
+       DUMP_BYTES(((u8*)addr), 4, "%px: orig: ", addr);
+       DUMP_BYTES(((u8*)&poison), 4, "%px: repl: ", addr);
+       text_poke_early(addr, &poison, 4);
+}
+
 /*
  * Generated by: objtool --ibt
  */
@@ -602,31 +687,391 @@ void __init_or_module noinline apply_ibt_endbr(s32 *start, s32 *end)
        s32 *s;
 
        for (s = start; s < end; s++) {
-               u32 endbr, poison = gen_endbr_poison();
                void *addr = (void *)s + *s;
 
-               if (WARN_ON_ONCE(get_kernel_nofault(endbr, addr)))
+               poison_endbr(addr, true);
+               if (IS_ENABLED(CONFIG_FINEIBT))
+                       poison_endbr(addr - 16, false);
+       }
+}
+
+#else
+
+void __init_or_module apply_ibt_endbr(s32 *start, s32 *end) { }
+
+#endif /* CONFIG_X86_KERNEL_IBT */
+
+#ifdef CONFIG_FINEIBT
+
+enum cfi_mode {
+       CFI_DEFAULT,
+       CFI_OFF,
+       CFI_KCFI,
+       CFI_FINEIBT,
+};
+
+static enum cfi_mode cfi_mode __ro_after_init = CFI_DEFAULT;
+static bool cfi_rand __ro_after_init = true;
+static u32  cfi_seed __ro_after_init;
+
+/*
+ * Re-hash the CFI hash with a boot-time seed while making sure the result is
+ * not a valid ENDBR instruction.
+ */
+static u32 cfi_rehash(u32 hash)
+{
+       hash ^= cfi_seed;
+       while (unlikely(is_endbr(hash) || is_endbr(-hash))) {
+               bool lsb = hash & 1;
+               hash >>= 1;
+               if (lsb)
+                       hash ^= 0x80200003;
+       }
+       return hash;
+}
+
+static __init int cfi_parse_cmdline(char *str)
+{
+       if (!str)
+               return -EINVAL;
+
+       while (str) {
+               char *next = strchr(str, ',');
+               if (next) {
+                       *next = 0;
+                       next++;
+               }
+
+               if (!strcmp(str, "auto")) {
+                       cfi_mode = CFI_DEFAULT;
+               } else if (!strcmp(str, "off")) {
+                       cfi_mode = CFI_OFF;
+                       cfi_rand = false;
+               } else if (!strcmp(str, "kcfi")) {
+                       cfi_mode = CFI_KCFI;
+               } else if (!strcmp(str, "fineibt")) {
+                       cfi_mode = CFI_FINEIBT;
+               } else if (!strcmp(str, "norand")) {
+                       cfi_rand = false;
+               } else {
+                       pr_err("Ignoring unknown cfi option (%s).", str);
+               }
+
+               str = next;
+       }
+
+       return 0;
+}
+early_param("cfi", cfi_parse_cmdline);
+
+/*
+ * kCFI                                                FineIBT
+ *
+ * __cfi_\func:                                        __cfi_\func:
+ *     movl   $0x12345678,%eax         // 5         endbr64                    // 4
+ *     nop                                          subl   $0x12345678,%r10d   // 7
+ *     nop                                          jz     1f                  // 2
+ *     nop                                          ud2                        // 2
+ *     nop                                     1:   nop                        // 1
+ *     nop
+ *     nop
+ *     nop
+ *     nop
+ *     nop
+ *     nop
+ *     nop
+ *
+ *
+ * caller:                                     caller:
+ *     movl    $(-0x12345678),%r10d     // 6        movl   $0x12345678,%r10d   // 6
+ *     addl    $-15(%r11),%r10d         // 4        sub    $16,%r11            // 4
+ *     je      1f                       // 2        nop4                       // 4
+ *     ud2                              // 2
+ * 1:  call    __x86_indirect_thunk_r11 // 5        call   *%r11; nop2;        // 5
+ *
+ */
+
+asm(   ".pushsection .rodata                   \n"
+       "fineibt_preamble_start:                \n"
+       "       endbr64                         \n"
+       "       subl    $0x12345678, %r10d      \n"
+       "       je      fineibt_preamble_end    \n"
+       "       ud2                             \n"
+       "       nop                             \n"
+       "fineibt_preamble_end:                  \n"
+       ".popsection\n"
+);
+
+extern u8 fineibt_preamble_start[];
+extern u8 fineibt_preamble_end[];
+
+#define fineibt_preamble_size (fineibt_preamble_end - fineibt_preamble_start)
+#define fineibt_preamble_hash 7
+
+asm(   ".pushsection .rodata                   \n"
+       "fineibt_caller_start:                  \n"
+       "       movl    $0x12345678, %r10d      \n"
+       "       sub     $16, %r11               \n"
+       ASM_NOP4
+       "fineibt_caller_end:                    \n"
+       ".popsection                            \n"
+);
+
+extern u8 fineibt_caller_start[];
+extern u8 fineibt_caller_end[];
+
+#define fineibt_caller_size (fineibt_caller_end - fineibt_caller_start)
+#define fineibt_caller_hash 2
+
+#define fineibt_caller_jmp (fineibt_caller_size - 2)
+
+static u32 decode_preamble_hash(void *addr)
+{
+       u8 *p = addr;
+
+       /* b8 78 56 34 12          mov    $0x12345678,%eax */
+       if (p[0] == 0xb8)
+               return *(u32 *)(addr + 1);
+
+       return 0; /* invalid hash value */
+}
+
+static u32 decode_caller_hash(void *addr)
+{
+       u8 *p = addr;
+
+       /* 41 ba 78 56 34 12       mov    $0x12345678,%r10d */
+       if (p[0] == 0x41 && p[1] == 0xba)
+               return -*(u32 *)(addr + 2);
+
+       /* e8 0c 78 56 34 12       jmp.d8  +12 */
+       if (p[0] == JMP8_INSN_OPCODE && p[1] == fineibt_caller_jmp)
+               return -*(u32 *)(addr + 2);
+
+       return 0; /* invalid hash value */
+}
+
+/* .retpoline_sites */
+static int cfi_disable_callers(s32 *start, s32 *end)
+{
+       /*
+        * Disable kCFI by patching in a JMP.d8, this leaves the hash immediate
+        * in tact for later usage. Also see decode_caller_hash() and
+        * cfi_rewrite_callers().
+        */
+       const u8 jmp[] = { JMP8_INSN_OPCODE, fineibt_caller_jmp };
+       s32 *s;
+
+       for (s = start; s < end; s++) {
+               void *addr = (void *)s + *s;
+               u32 hash;
+
+               addr -= fineibt_caller_size;
+               hash = decode_caller_hash(addr);
+               if (!hash) /* nocfi callers */
                        continue;
 
-               if (WARN_ON_ONCE(!is_endbr(endbr)))
+               text_poke_early(addr, jmp, 2);
+       }
+
+       return 0;
+}
+
+static int cfi_enable_callers(s32 *start, s32 *end)
+{
+       /*
+        * Re-enable kCFI, undo what cfi_disable_callers() did.
+        */
+       const u8 mov[] = { 0x41, 0xba };
+       s32 *s;
+
+       for (s = start; s < end; s++) {
+               void *addr = (void *)s + *s;
+               u32 hash;
+
+               addr -= fineibt_caller_size;
+               hash = decode_caller_hash(addr);
+               if (!hash) /* nocfi callers */
                        continue;
 
-               DPRINTK("ENDBR at: %pS (%px)", addr, addr);
+               text_poke_early(addr, mov, 2);
+       }
 
-               /*
-                * When we have IBT, the lack of ENDBR will trigger #CP
-                */
-               DUMP_BYTES(((u8*)addr), 4, "%px: orig: ", addr);
-               DUMP_BYTES(((u8*)&poison), 4, "%px: repl: ", addr);
-               text_poke_early(addr, &poison, 4);
+       return 0;
+}
+
+/* .cfi_sites */
+static int cfi_rand_preamble(s32 *start, s32 *end)
+{
+       s32 *s;
+
+       for (s = start; s < end; s++) {
+               void *addr = (void *)s + *s;
+               u32 hash;
+
+               hash = decode_preamble_hash(addr);
+               if (WARN(!hash, "no CFI hash found at: %pS %px %*ph\n",
+                        addr, addr, 5, addr))
+                       return -EINVAL;
+
+               hash = cfi_rehash(hash);
+               text_poke_early(addr + 1, &hash, 4);
+       }
+
+       return 0;
+}
+
+static int cfi_rewrite_preamble(s32 *start, s32 *end)
+{
+       s32 *s;
+
+       for (s = start; s < end; s++) {
+               void *addr = (void *)s + *s;
+               u32 hash;
+
+               hash = decode_preamble_hash(addr);
+               if (WARN(!hash, "no CFI hash found at: %pS %px %*ph\n",
+                        addr, addr, 5, addr))
+                       return -EINVAL;
+
+               text_poke_early(addr, fineibt_preamble_start, fineibt_preamble_size);
+               WARN_ON(*(u32 *)(addr + fineibt_preamble_hash) != 0x12345678);
+               text_poke_early(addr + fineibt_preamble_hash, &hash, 4);
+       }
+
+       return 0;
+}
+
+/* .retpoline_sites */
+static int cfi_rand_callers(s32 *start, s32 *end)
+{
+       s32 *s;
+
+       for (s = start; s < end; s++) {
+               void *addr = (void *)s + *s;
+               u32 hash;
+
+               addr -= fineibt_caller_size;
+               hash = decode_caller_hash(addr);
+               if (hash) {
+                       hash = -cfi_rehash(hash);
+                       text_poke_early(addr + 2, &hash, 4);
+               }
+       }
+
+       return 0;
+}
+
+static int cfi_rewrite_callers(s32 *start, s32 *end)
+{
+       s32 *s;
+
+       for (s = start; s < end; s++) {
+               void *addr = (void *)s + *s;
+               u32 hash;
+
+               addr -= fineibt_caller_size;
+               hash = decode_caller_hash(addr);
+               if (hash) {
+                       text_poke_early(addr, fineibt_caller_start, fineibt_caller_size);
+                       WARN_ON(*(u32 *)(addr + fineibt_caller_hash) != 0x12345678);
+                       text_poke_early(addr + fineibt_caller_hash, &hash, 4);
+               }
+               /* rely on apply_retpolines() */
+       }
+
+       return 0;
+}
+
+static void __apply_fineibt(s32 *start_retpoline, s32 *end_retpoline,
+                           s32 *start_cfi, s32 *end_cfi, bool builtin)
+{
+       int ret;
+
+       if (WARN_ONCE(fineibt_preamble_size != 16,
+                     "FineIBT preamble wrong size: %ld", fineibt_preamble_size))
+               return;
+
+       if (cfi_mode == CFI_DEFAULT) {
+               cfi_mode = CFI_KCFI;
+               if (HAS_KERNEL_IBT && cpu_feature_enabled(X86_FEATURE_IBT))
+                       cfi_mode = CFI_FINEIBT;
+       }
+
+       /*
+        * Rewrite the callers to not use the __cfi_ stubs, such that we might
+        * rewrite them. This disables all CFI. If this succeeds but any of the
+        * later stages fails, we're without CFI.
+        */
+       ret = cfi_disable_callers(start_retpoline, end_retpoline);
+       if (ret)
+               goto err;
+
+       if (cfi_rand) {
+               if (builtin)
+                       cfi_seed = get_random_u32();
+
+               ret = cfi_rand_preamble(start_cfi, end_cfi);
+               if (ret)
+                       goto err;
+
+               ret = cfi_rand_callers(start_retpoline, end_retpoline);
+               if (ret)
+                       goto err;
+       }
+
+       switch (cfi_mode) {
+       case CFI_OFF:
+               if (builtin)
+                       pr_info("Disabling CFI\n");
+               return;
+
+       case CFI_KCFI:
+               ret = cfi_enable_callers(start_retpoline, end_retpoline);
+               if (ret)
+                       goto err;
+
+               if (builtin)
+                       pr_info("Using kCFI\n");
+               return;
+
+       case CFI_FINEIBT:
+               ret = cfi_rewrite_preamble(start_cfi, end_cfi);
+               if (ret)
+                       goto err;
+
+               ret = cfi_rewrite_callers(start_retpoline, end_retpoline);
+               if (ret)
+                       goto err;
+
+               if (builtin)
+                       pr_info("Using FineIBT CFI\n");
+               return;
+
+       default:
+               break;
        }
+
+err:
+       pr_err("Something went horribly wrong trying to rewrite the CFI implementation.\n");
 }
 
 #else
 
-void __init_or_module noinline apply_ibt_endbr(s32 *start, s32 *end) { }
+static void __apply_fineibt(s32 *start_retpoline, s32 *end_retpoline,
+                           s32 *start_cfi, s32 *end_cfi, bool builtin)
+{
+}
 
-#endif /* CONFIG_X86_KERNEL_IBT */
+#endif
+
+void apply_fineibt(s32 *start_retpoline, s32 *end_retpoline,
+                  s32 *start_cfi, s32 *end_cfi)
+{
+       return __apply_fineibt(start_retpoline, end_retpoline,
+                              start_cfi, end_cfi,
+                              /* .builtin = */ false);
+}
 
 #ifdef CONFIG_SMP
 static void alternatives_smp_lock(const s32 *start, const s32 *end,
@@ -934,6 +1379,9 @@ void __init alternative_instructions(void)
         */
        apply_paravirt(__parainstructions, __parainstructions_end);
 
+       __apply_fineibt(__retpoline_sites, __retpoline_sites_end,
+                       __cfi_sites, __cfi_sites_end, true);
+
        /*
         * Rewrite the retpolines, must be done before alternatives since
         * those can rewrite the retpoline thunks.
@@ -947,6 +1395,12 @@ void __init alternative_instructions(void)
         */
        apply_alternatives(__alt_instructions, __alt_instructions_end);
 
+       /*
+        * Now all calls are established. Apply the call thunks if
+        * required.
+        */
+       callthunks_patch_builtin_calls();
+
        apply_ibt_endbr(__ibt_endbr_seal, __ibt_endbr_seal_end);
 
 #ifdef CONFIG_SMP
@@ -1236,27 +1690,15 @@ void *text_poke_kgdb(void *addr, const void *opcode, size_t len)
        return __text_poke(text_poke_memcpy, addr, opcode, len);
 }
 
-/**
- * text_poke_copy - Copy instructions into (an unused part of) RX memory
- * @addr: address to modify
- * @opcode: source of the copy
- * @len: length to copy, could be more than 2x PAGE_SIZE
- *
- * Not safe against concurrent execution; useful for JITs to dump
- * new code blocks into unused regions of RX memory. Can be used in
- * conjunction with synchronize_rcu_tasks() to wait for existing
- * execution to quiesce after having made sure no existing functions
- * pointers are live.
- */
-void *text_poke_copy(void *addr, const void *opcode, size_t len)
+void *text_poke_copy_locked(void *addr, const void *opcode, size_t len,
+                           bool core_ok)
 {
        unsigned long start = (unsigned long)addr;
        size_t patched = 0;
 
-       if (WARN_ON_ONCE(core_kernel_text(start)))
+       if (WARN_ON_ONCE(!core_ok && core_kernel_text(start)))
                return NULL;
 
-       mutex_lock(&text_mutex);
        while (patched < len) {
                unsigned long ptr = start + patched;
                size_t s;
@@ -1266,6 +1708,25 @@ void *text_poke_copy(void *addr, const void *opcode, size_t len)
                __text_poke(text_poke_memcpy, (void *)ptr, opcode + patched, s);
                patched += s;
        }
+       return addr;
+}
+
+/**
+ * text_poke_copy - Copy instructions into (an unused part of) RX memory
+ * @addr: address to modify
+ * @opcode: source of the copy
+ * @len: length to copy, could be more than 2x PAGE_SIZE
+ *
+ * Not safe against concurrent execution; useful for JITs to dump
+ * new code blocks into unused regions of RX memory. Can be used in
+ * conjunction with synchronize_rcu_tasks() to wait for existing
+ * execution to quiesce after having made sure no existing functions
+ * pointers are live.
+ */
+void *text_poke_copy(void *addr, const void *opcode, size_t len)
+{
+       mutex_lock(&text_mutex);
+       addr = text_poke_copy_locked(addr, opcode, len, false);
        mutex_unlock(&text_mutex);
        return addr;
 }
@@ -1608,7 +2069,7 @@ static void text_poke_loc_init(struct text_poke_loc *tp, void *addr,
 
        default:
                BUG_ON(len != insn.length);
-       };
+       }
 
 
        switch (tp->opcode) {