x86/cfi: Boot time selection of CFI scheme
authorPeter Zijlstra <peterz@infradead.org>
Thu, 27 Oct 2022 09:28:15 +0000 (11:28 +0200)
committerPeter Zijlstra <peterz@infradead.org>
Tue, 1 Nov 2022 12:44:11 +0000 (13:44 +0100)
Add the "cfi=" boot parameter to allow people to select a CFI scheme
at boot time. Mostly useful for development / debugging.

Requested-by: Kees Cook <keescook@chromium.org>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: Kees Cook <keescook@chromium.org>
Link: https://lore.kernel.org/r/20221027092842.699804264@infradead.org
arch/x86/kernel/alternative.c

index 91b0e63a6238af98c989bbd90aa7efe7b84c9b8c..9d3b58748ca5311484820bf7e3ccc001b8ae1cd7 100644 (file)
@@ -702,6 +702,47 @@ void __init_or_module noinline 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 __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;
+               } else if (!strcmp(str, "kcfi")) {
+                       cfi_mode = CFI_KCFI;
+               } else if (!strcmp(str, "fineibt")) {
+                       cfi_mode = CFI_FINEIBT;
+               } else {
+                       pr_err("Ignoring unknown cfi option (%s).", str);
+               }
+
+               str = next;
+       }
+
+       return 0;
+}
+early_param("cfi", cfi_parse_cmdline);
+
 /*
  * kCFI                                                FineIBT
  *
@@ -868,30 +909,52 @@ static void __apply_fineibt(s32 *start_retpoline, s32 *end_retpoline,
                      "FineIBT preamble wrong size: %ld", fineibt_preamble_size))
                return;
 
-       if (!HAS_KERNEL_IBT || !cpu_feature_enabled(X86_FEATURE_IBT))
+       if (cfi_mode == CFI_DEFAULT) {
+               cfi_mode = CFI_KCFI;
+               if (HAS_KERNEL_IBT && cpu_feature_enabled(X86_FEATURE_IBT))
+                       cfi_mode = CFI_FINEIBT;
+       }
+
+       switch (cfi_mode) {
+       case CFI_OFF:
+               ret = cfi_disable_callers(start_retpoline, end_retpoline);
+               if (ret)
+                       goto err;
+
+               if (builtin)
+                       pr_info("Disabling CFI\n");
                return;
 
-       /*
-        * 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;
+       case CFI_KCFI:
+               if (builtin)
+                       pr_info("Using kCFI\n");
+               return;
 
-       ret = cfi_rewrite_preamble(start_cfi, end_cfi);
-       if (ret)
-               goto err;
+       case 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;
+
+               ret = cfi_rewrite_preamble(start_cfi, end_cfi);
+               if (ret)
+                       goto err;
 
-       ret = cfi_rewrite_callers(start_retpoline, end_retpoline);
-       if (ret)
-               goto err;
+               ret = cfi_rewrite_callers(start_retpoline, end_retpoline);
+               if (ret)
+                       goto err;
 
-       if (builtin)
-               pr_info("Using FineIBT CFI\n");
+               if (builtin)
+                       pr_info("Using FineIBT CFI\n");
+               return;
 
-       return;
+       default:
+               break;
+       }
 
 err:
        pr_err("Something went horribly wrong trying to rewrite the CFI implementation.\n");