diff mbox series

x86/alternatives: Make FineIBT mode Kconfig selectable

Message ID 20240501000218.work.998-kees@kernel.org (mailing list archive)
State Mainlined
Commit d6f635bcaca8d38dfa47ee20658705f9eff156b5
Headers show
Series x86/alternatives: Make FineIBT mode Kconfig selectable | expand

Commit Message

Kees Cook May 1, 2024, 12:02 a.m. UTC
Since FineIBT performs checking at the destination, it is weaker against
attacks that can construct arbitrary executable memory contents. As such,
some system builders want to run with FineIBT disabled by default. Allow
the "cfi=kcfi" boot param mode to be selectable through Kconfig via the
newly introduced CONFIG_CFI_AUTO_DEFAULT.

Signed-off-by: Kees Cook <keescook@chromium.org>
---
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Dave Hansen <dave.hansen@linux.intel.com>
Cc: x86@kernel.org
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Sami Tolvanen <samitolvanen@google.com>
Cc: Nathan Chancellor <nathan@kernel.org>
Cc: Josh Poimboeuf <jpoimboe@kernel.org>
---
 arch/x86/Kconfig              | 9 +++++++++
 arch/x86/include/asm/cfi.h    | 2 +-
 arch/x86/kernel/alternative.c | 8 ++++----
 3 files changed, 14 insertions(+), 5 deletions(-)

Comments

Nathan Chancellor May 1, 2024, 4:33 p.m. UTC | #1
On Tue, Apr 30, 2024 at 05:02:22PM -0700, Kees Cook wrote:
> Since FineIBT performs checking at the destination, it is weaker against
> attacks that can construct arbitrary executable memory contents. As such,
> some system builders want to run with FineIBT disabled by default. Allow
> the "cfi=kcfi" boot param mode to be selectable through Kconfig via the
> newly introduced CONFIG_CFI_AUTO_DEFAULT.
> 
> Signed-off-by: Kees Cook <keescook@chromium.org>

I verified that flipping the configuration does indeed change the
default and that 'cfi=' could still be used to override whatever choice
was made at compile time. This patch was a perfect excuse to put my new
CET enabled test machine to work.

Reviewed-by: Nathan Chancellor <nathan@kernel.org>
Tested-by: Nathan Chancellor <nathan@kernel.org>

CFI_DEFAULT_AUTO reads a little bit better to me personally but I am not
looking to get into painting today :)

> ---
> Cc: Peter Zijlstra <peterz@infradead.org>
> Cc: Thomas Gleixner <tglx@linutronix.de>
> Cc: Ingo Molnar <mingo@redhat.com>
> Cc: Borislav Petkov <bp@alien8.de>
> Cc: Dave Hansen <dave.hansen@linux.intel.com>
> Cc: x86@kernel.org
> Cc: "H. Peter Anvin" <hpa@zytor.com>
> Cc: Alexei Starovoitov <ast@kernel.org>
> Cc: Sami Tolvanen <samitolvanen@google.com>
> Cc: Nathan Chancellor <nathan@kernel.org>
> Cc: Josh Poimboeuf <jpoimboe@kernel.org>
> ---
>  arch/x86/Kconfig              | 9 +++++++++
>  arch/x86/include/asm/cfi.h    | 2 +-
>  arch/x86/kernel/alternative.c | 8 ++++----
>  3 files changed, 14 insertions(+), 5 deletions(-)
> 
> diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
> index 4fff6ed46e90..d5cf52d2f6a8 100644
> --- a/arch/x86/Kconfig
> +++ b/arch/x86/Kconfig
> @@ -2424,6 +2424,15 @@ config STRICT_SIGALTSTACK_SIZE
>  
>  	  Say 'N' unless you want to really enforce this check.
>  
> +config CFI_AUTO_DEFAULT
> +	bool "Attempt to use FineIBT by default at boot time"
> +	depends on FINEIBT
> +	default y
> +	help
> +	  Attempt to use FineIBT by default at boot time. If enabled,
> +	  this is the same as booting with "cfi=auto". If disabled,
> +	  this is the same as booting with "cfi=kcfi".
> +
>  source "kernel/livepatch/Kconfig"
>  
>  endmenu
> diff --git a/arch/x86/include/asm/cfi.h b/arch/x86/include/asm/cfi.h
> index 7cd752557905..31d19c815f99 100644
> --- a/arch/x86/include/asm/cfi.h
> +++ b/arch/x86/include/asm/cfi.h
> @@ -93,7 +93,7 @@
>   *
>   */
>  enum cfi_mode {
> -	CFI_DEFAULT,	/* FineIBT if hardware has IBT, otherwise kCFI */
> +	CFI_AUTO,	/* FineIBT if hardware has IBT, otherwise kCFI */
>  	CFI_OFF,	/* Taditional / IBT depending on .config */
>  	CFI_KCFI,	/* Optionally CALL_PADDING, IBT, RETPOLINE */
>  	CFI_FINEIBT,	/* see arch/x86/kernel/alternative.c */
> diff --git a/arch/x86/kernel/alternative.c b/arch/x86/kernel/alternative.c
> index 45a280f2161c..e8d0892d89cf 100644
> --- a/arch/x86/kernel/alternative.c
> +++ b/arch/x86/kernel/alternative.c
> @@ -902,8 +902,8 @@ void __init_or_module apply_seal_endbr(s32 *start, s32 *end) { }
>  
>  #endif /* CONFIG_X86_KERNEL_IBT */
>  
> -#ifdef CONFIG_FINEIBT
> -#define __CFI_DEFAULT	CFI_DEFAULT
> +#ifdef CONFIG_CFI_AUTO_DEFAULT
> +#define __CFI_DEFAULT	CFI_AUTO
>  #elif defined(CONFIG_CFI_CLANG)
>  #define __CFI_DEFAULT	CFI_KCFI
>  #else
> @@ -1011,7 +1011,7 @@ static __init int cfi_parse_cmdline(char *str)
>  		}
>  
>  		if (!strcmp(str, "auto")) {
> -			cfi_mode = CFI_DEFAULT;
> +			cfi_mode = CFI_AUTO;
>  		} else if (!strcmp(str, "off")) {
>  			cfi_mode = CFI_OFF;
>  			cfi_rand = false;
> @@ -1271,7 +1271,7 @@ static void __apply_fineibt(s32 *start_retpoline, s32 *end_retpoline,
>  		      "FineIBT preamble wrong size: %ld", fineibt_preamble_size))
>  		return;
>  
> -	if (cfi_mode == CFI_DEFAULT) {
> +	if (cfi_mode == CFI_AUTO) {
>  		cfi_mode = CFI_KCFI;
>  		if (HAS_KERNEL_IBT && cpu_feature_enabled(X86_FEATURE_IBT))
>  			cfi_mode = CFI_FINEIBT;
> -- 
> 2.34.1
>
Kees Cook May 1, 2024, 8:13 p.m. UTC | #2
On Wed, May 01, 2024 at 09:33:14AM -0700, Nathan Chancellor wrote:
> On Tue, Apr 30, 2024 at 05:02:22PM -0700, Kees Cook wrote:
> > Since FineIBT performs checking at the destination, it is weaker against
> > attacks that can construct arbitrary executable memory contents. As such,
> > some system builders want to run with FineIBT disabled by default. Allow
> > the "cfi=kcfi" boot param mode to be selectable through Kconfig via the
> > newly introduced CONFIG_CFI_AUTO_DEFAULT.
> > 
> > Signed-off-by: Kees Cook <keescook@chromium.org>
> 
> I verified that flipping the configuration does indeed change the
> default and that 'cfi=' could still be used to override whatever choice
> was made at compile time. This patch was a perfect excuse to put my new
> CET enabled test machine to work.

Heh, yeah. I have my one lonely CET system that is only powered on for
this sort of testing too. ;)

> 
> Reviewed-by: Nathan Chancellor <nathan@kernel.org>
> Tested-by: Nathan Chancellor <nathan@kernel.org>

Thanks!

> CFI_DEFAULT_AUTO reads a little bit better to me personally but I am not
> looking to get into painting today :)

I went with CFI_AUTO_DEFAULT since this seems to be our current pattern
for setting these kinds of boot param defaults. A selection of examples:

arch/Kconfig:config RANDOMIZE_KSTACK_OFFSET_DEFAULT
lib/Kconfig.debug:config CONSOLE_LOGLEVEL_DEFAULT
mm/Kconfig.debug:config DEBUG_PAGEALLOC_ENABLE_DEFAULT
Sami Tolvanen May 1, 2024, 8:18 p.m. UTC | #3
On Tue, Apr 30, 2024 at 5:02 PM Kees Cook <keescook@chromium.org> wrote:
>
> Since FineIBT performs checking at the destination, it is weaker against
> attacks that can construct arbitrary executable memory contents. As such,
> some system builders want to run with FineIBT disabled by default. Allow
> the "cfi=kcfi" boot param mode to be selectable through Kconfig via the
> newly introduced CONFIG_CFI_AUTO_DEFAULT.
>
> Signed-off-by: Kees Cook <keescook@chromium.org>
> ---

Reviewed-by: Sami Tolvanen <samitolvanen@google.com>

Sami
jvoisin July 29, 2024, 12:35 p.m. UTC | #4
> Since FineIBT performs checking at the destination, it is weaker against
> attacks that can construct arbitrary executable memory contents. As such,
> some system builders want to run with FineIBT disabled by default. Allow
> the "cfi=kcfi" boot param mode to be selectable through Kconfig via the
> newly introduced CONFIG_CFI_AUTO_DEFAULT.

I'm confused as why you think that KCFI is stronger/better than FineIBT.
The latter is compatible with execute-only memory, makes use of hardware
support, doesn't need LTO, is faster, … moreover, I don't see why an
attacker able to "construct arbitrary executable memory contents"
wouldn't be able to bypass KCFI as well, since its threat model
(https://github.com/kcfi/docs/blob/master/kCFI_whitepaper.pdf)
explicitly says "We assume an OS that fully implements the W^X policy
[56,58,106] preventing direct code injection in kernel space."
Kees Cook July 30, 2024, 12:41 a.m. UTC | #5
On Mon, Jul 29, 2024 at 02:35:02PM +0200, jvoisin wrote:
> > Since FineIBT performs checking at the destination, it is weaker against
> > attacks that can construct arbitrary executable memory contents. As such,
> > some system builders want to run with FineIBT disabled by default. Allow
> > the "cfi=kcfi" boot param mode to be selectable through Kconfig via the
> > newly introduced CONFIG_CFI_AUTO_DEFAULT.
> 
> I'm confused as why you think that KCFI is stronger/better than FineIBT.

Sure, can I try to explain this more.

> The latter is compatible with execute-only memory,

Yes, and since Linux doesn't have kernel execute-only memory (and likely
won't for some time), it doesn't make sense to use FineIBT over KCFI for
that reason.

> makes use of hardware support,

Hm? KCFI does too. IBT is still enabled with KCFI (when the hardware
supports it).

> doesn't need LTO,

KCFI doesn't need LTO either.

> is faster,

What? Measured how? I feel like you're thinking about the old Clang CFI,
not the modern KCFI implementation.

> … moreover, I don't see why an
> attacker able to "construct arbitrary executable memory contents"
> wouldn't be able to bypass KCFI as well,

To bypass KCFI, the attacker additionally needs a targeted memory
exposure to get the correct function hash that they must include before
the malicious function they construct. With FineIBT, no such exposure is
needed.

> since its threat model
> (https://github.com/kcfi/docs/blob/master/kCFI_whitepaper.pdf)
> explicitly says "We assume an OS that fully implements the W^X policy
> [56,58,106] preventing direct code injection in kernel space."

I mean, a whitepaper's threat model is nice and all, but this just isn't
the reality. Linux certainly tries to maintain W^X, but there are bugs
and things like BPF, which can be manipulated to gain attacker-controlled
executable code injected into the kernel address space. (e.g. BPF will
flip a writable region from RW to RX, so W^X is maintained spatially but
not temporally.)

So without execute-only memory, some deployments prefer to not weaken
the CFI implementation to allow for hash checking bypasses. Once X-O
exists, FineIBT is a slam-dunk over KCFI. :)

-Kees
diff mbox series

Patch

diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 4fff6ed46e90..d5cf52d2f6a8 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -2424,6 +2424,15 @@  config STRICT_SIGALTSTACK_SIZE
 
 	  Say 'N' unless you want to really enforce this check.
 
+config CFI_AUTO_DEFAULT
+	bool "Attempt to use FineIBT by default at boot time"
+	depends on FINEIBT
+	default y
+	help
+	  Attempt to use FineIBT by default at boot time. If enabled,
+	  this is the same as booting with "cfi=auto". If disabled,
+	  this is the same as booting with "cfi=kcfi".
+
 source "kernel/livepatch/Kconfig"
 
 endmenu
diff --git a/arch/x86/include/asm/cfi.h b/arch/x86/include/asm/cfi.h
index 7cd752557905..31d19c815f99 100644
--- a/arch/x86/include/asm/cfi.h
+++ b/arch/x86/include/asm/cfi.h
@@ -93,7 +93,7 @@ 
  *
  */
 enum cfi_mode {
-	CFI_DEFAULT,	/* FineIBT if hardware has IBT, otherwise kCFI */
+	CFI_AUTO,	/* FineIBT if hardware has IBT, otherwise kCFI */
 	CFI_OFF,	/* Taditional / IBT depending on .config */
 	CFI_KCFI,	/* Optionally CALL_PADDING, IBT, RETPOLINE */
 	CFI_FINEIBT,	/* see arch/x86/kernel/alternative.c */
diff --git a/arch/x86/kernel/alternative.c b/arch/x86/kernel/alternative.c
index 45a280f2161c..e8d0892d89cf 100644
--- a/arch/x86/kernel/alternative.c
+++ b/arch/x86/kernel/alternative.c
@@ -902,8 +902,8 @@  void __init_or_module apply_seal_endbr(s32 *start, s32 *end) { }
 
 #endif /* CONFIG_X86_KERNEL_IBT */
 
-#ifdef CONFIG_FINEIBT
-#define __CFI_DEFAULT	CFI_DEFAULT
+#ifdef CONFIG_CFI_AUTO_DEFAULT
+#define __CFI_DEFAULT	CFI_AUTO
 #elif defined(CONFIG_CFI_CLANG)
 #define __CFI_DEFAULT	CFI_KCFI
 #else
@@ -1011,7 +1011,7 @@  static __init int cfi_parse_cmdline(char *str)
 		}
 
 		if (!strcmp(str, "auto")) {
-			cfi_mode = CFI_DEFAULT;
+			cfi_mode = CFI_AUTO;
 		} else if (!strcmp(str, "off")) {
 			cfi_mode = CFI_OFF;
 			cfi_rand = false;
@@ -1271,7 +1271,7 @@  static void __apply_fineibt(s32 *start_retpoline, s32 *end_retpoline,
 		      "FineIBT preamble wrong size: %ld", fineibt_preamble_size))
 		return;
 
-	if (cfi_mode == CFI_DEFAULT) {
+	if (cfi_mode == CFI_AUTO) {
 		cfi_mode = CFI_KCFI;
 		if (HAS_KERNEL_IBT && cpu_feature_enabled(X86_FEATURE_IBT))
 			cfi_mode = CFI_FINEIBT;