diff mbox series

[v5,2/4] xen: common: add ability to enable stack protector

Message ID 20250213220021.2897526-3-volodymyr_babchuk@epam.com (mailing list archive)
State Superseded
Headers show
Series Add/enable stack protector | expand

Commit Message

Volodymyr Babchuk Feb. 13, 2025, 10 p.m. UTC
Both GCC and Clang support -fstack-protector feature, which add stack
canaries to functions where stack corruption is possible. This patch
makes general preparations to enable this feature on different
supported architectures:

 - Added CONFIG_HAS_STACK_PROTECTOR option so each architecture
   can enable this feature individually
 - Added user-selectable CONFIG_STACK_PROTECTOR option
 - Implemented code that sets up random stack canary and a basic
   handler for stack protector failures

Stack guard value is initialized in two phases:

1. Pre-defined randomly-selected value.

2. Own implementation linear congruent random number generator. It
relies on get_cycles() being available very early. If get_cycles()
returns zero, it would leave pre-defined value from the previous
step.

Signed-off-by: Volodymyr Babchuk <volodymyr_babchuk@epam.com>
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
---

Changes in v5:

- Fixed indentation
- Added stack-protector.h
---
 xen/Makefile                      |  4 +++
 xen/common/Kconfig                | 15 +++++++++
 xen/common/Makefile               |  1 +
 xen/common/stack-protector.c      | 51 +++++++++++++++++++++++++++++++
 xen/include/xen/stack-protector.h | 14 +++++++++
 5 files changed, 85 insertions(+)
 create mode 100644 xen/common/stack-protector.c
 create mode 100644 xen/include/xen/stack-protector.h

Comments

Jan Beulich Feb. 14, 2025, 8:05 a.m. UTC | #1
On 13.02.2025 23:00, Volodymyr Babchuk wrote:
> Both GCC and Clang support -fstack-protector feature, which add stack
> canaries to functions where stack corruption is possible. This patch
> makes general preparations to enable this feature on different
> supported architectures:
> 
>  - Added CONFIG_HAS_STACK_PROTECTOR option so each architecture
>    can enable this feature individually
>  - Added user-selectable CONFIG_STACK_PROTECTOR option
>  - Implemented code that sets up random stack canary and a basic
>    handler for stack protector failures
> 
> Stack guard value is initialized in two phases:
> 
> 1. Pre-defined randomly-selected value.
> 
> 2. Own implementation linear congruent random number generator. It
> relies on get_cycles() being available very early. If get_cycles()
> returns zero, it would leave pre-defined value from the previous
> step.
> 
> Signed-off-by: Volodymyr Babchuk <volodymyr_babchuk@epam.com>
> Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
> ---
> 
> Changes in v5:
> 
> - Fixed indentation
> - Added stack-protector.h

With this I question the retaining of Andrew's R-b.

> --- /dev/null
> +++ b/xen/common/stack-protector.c
> @@ -0,0 +1,51 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +#include <xen/init.h>
> +#include <xen/lib.h>
> +#include <xen/random.h>
> +#include <xen/time.h>
> +
> +/*
> + * Initial value is chosen by a fair dice roll.
> + * It will be updated during boot process.
> + */
> +#if BITS_PER_LONG == 32
> +unsigned long __ro_after_init __stack_chk_guard = 0xdd2cc927UL;
> +#else
> +unsigned long __ro_after_init __stack_chk_guard = 0x2d853605a4d9a09cUL;
> +#endif
> +
> +/*
> + * This function should be called from early asm or from a C function
> + * that escapes stack canary tracking (by calling
> + * reset_stack_and_jump() for example).
> + */

This comment is now stale, and ...

> +void __init asmlinkage boot_stack_chk_guard_setup(void)

... I question the asmlinkage linkage here now, too.

Based on how things are moving, I don't think this should be rushed into
4.20 (anymore).

Jan
Julien Grall Feb. 15, 2025, 9:38 a.m. UTC | #2
Hi Volodymyr,

On 13/02/2025 22:00, Volodymyr Babchuk wrote:
> diff --git a/xen/common/stack-protector.c b/xen/common/stack-protector.c
> new file mode 100644
> index 0000000000..286753a1b1
> --- /dev/null
> +++ b/xen/common/stack-protector.c
> @@ -0,0 +1,51 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +#include <xen/init.h>
> +#include <xen/lib.h>
> +#include <xen/random.h>
> +#include <xen/time.h>
> +
> +/*
> + * Initial value is chosen by a fair dice roll.
> + * It will be updated during boot process.
> + */
> +#if BITS_PER_LONG == 32
> +unsigned long __ro_after_init __stack_chk_guard = 0xdd2cc927UL;
> +#else
> +unsigned long __ro_after_init __stack_chk_guard = 0x2d853605a4d9a09cUL;
> +#endif
> +
> +/*
> + * This function should be called from early asm or from a C function
> + * that escapes stack canary tracking (by calling
> + * reset_stack_and_jump() for example).
> + */
> +void __init asmlinkage boot_stack_chk_guard_setup(void)

I am probably missing something. But what prevent the compiler to insert 
a stack guard when entering this function and checking on exit? Wouldn't 
this fail because __stack_chk_guard would be different?

IOW, shouldn't this function be a static always inline like it used to be?

Cheers,
Volodymyr Babchuk Feb. 17, 2025, 12:59 a.m. UTC | #3
Hi Julien,

Julien Grall <julien@xen.org> writes:

> Hi Volodymyr,
>
> On 13/02/2025 22:00, Volodymyr Babchuk wrote:
>> diff --git a/xen/common/stack-protector.c b/xen/common/stack-protector.c
>> new file mode 100644
>> index 0000000000..286753a1b1
>> --- /dev/null
>> +++ b/xen/common/stack-protector.c
>> @@ -0,0 +1,51 @@
>> +/* SPDX-License-Identifier: GPL-2.0-only */
>> +#include <xen/init.h>
>> +#include <xen/lib.h>
>> +#include <xen/random.h>
>> +#include <xen/time.h>
>> +
>> +/*
>> + * Initial value is chosen by a fair dice roll.
>> + * It will be updated during boot process.
>> + */
>> +#if BITS_PER_LONG == 32
>> +unsigned long __ro_after_init __stack_chk_guard = 0xdd2cc927UL;
>> +#else
>> +unsigned long __ro_after_init __stack_chk_guard = 0x2d853605a4d9a09cUL;
>> +#endif
>> +
>> +/*
>> + * This function should be called from early asm or from a C function
>> + * that escapes stack canary tracking (by calling
>> + * reset_stack_and_jump() for example).
>> + */
>> +void __init asmlinkage boot_stack_chk_guard_setup(void)
>
> I am probably missing something. But what prevent the compiler to
> insert a stack guard when entering this function and checking on exit?
> Wouldn't this fail because __stack_chk_guard would be different?

Yes, you are right. I got carried away a bit this time. It is working
right now only because GCC does not emit stack checking code in this
particular function. With "-fstack-protector-all" it panics, as expected.

> IOW, shouldn't this function be a static always inline like it used to be?

Yes, I am going to make it inline in the next version.
diff mbox series

Patch

diff --git a/xen/Makefile b/xen/Makefile
index a0c774ab7d..48bc17c418 100644
--- a/xen/Makefile
+++ b/xen/Makefile
@@ -435,7 +435,11 @@  else
 CFLAGS_UBSAN :=
 endif
 
+ifeq ($(CONFIG_STACK_PROTECTOR),y)
+CFLAGS += -fstack-protector
+else
 CFLAGS += -fno-stack-protector
+endif
 
 ifeq ($(CONFIG_LTO),y)
 CFLAGS += -flto
diff --git a/xen/common/Kconfig b/xen/common/Kconfig
index 6166327f4d..bd53dae43c 100644
--- a/xen/common/Kconfig
+++ b/xen/common/Kconfig
@@ -83,6 +83,9 @@  config HAS_PMAP
 config HAS_SCHED_GRANULARITY
 	bool
 
+config HAS_STACK_PROTECTOR
+	bool
+
 config HAS_UBSAN
 	bool
 
@@ -216,6 +219,18 @@  config SPECULATIVE_HARDEN_LOCK
 
 endmenu
 
+menu "Other hardening"
+
+config STACK_PROTECTOR
+	bool "Stack protector"
+	depends on HAS_STACK_PROTECTOR
+	help
+	  Enable the Stack Protector compiler hardening option. This inserts a
+	  canary value in the stack frame of functions, and performs an integrity
+	  check on function exit.
+
+endmenu
+
 config DIT_DEFAULT
 	bool "Data Independent Timing default"
 	depends on HAS_DIT
diff --git a/xen/common/Makefile b/xen/common/Makefile
index cba3b32733..8adbf6a3b5 100644
--- a/xen/common/Makefile
+++ b/xen/common/Makefile
@@ -46,6 +46,7 @@  obj-y += shutdown.o
 obj-y += softirq.o
 obj-y += smp.o
 obj-y += spinlock.o
+obj-$(CONFIG_STACK_PROTECTOR) += stack-protector.o
 obj-y += stop_machine.o
 obj-y += symbols.o
 obj-y += tasklet.o
diff --git a/xen/common/stack-protector.c b/xen/common/stack-protector.c
new file mode 100644
index 0000000000..286753a1b1
--- /dev/null
+++ b/xen/common/stack-protector.c
@@ -0,0 +1,51 @@ 
+/* SPDX-License-Identifier: GPL-2.0-only */
+#include <xen/init.h>
+#include <xen/lib.h>
+#include <xen/random.h>
+#include <xen/time.h>
+
+/*
+ * Initial value is chosen by a fair dice roll.
+ * It will be updated during boot process.
+ */
+#if BITS_PER_LONG == 32
+unsigned long __ro_after_init __stack_chk_guard = 0xdd2cc927UL;
+#else
+unsigned long __ro_after_init __stack_chk_guard = 0x2d853605a4d9a09cUL;
+#endif
+
+/*
+ * This function should be called from early asm or from a C function
+ * that escapes stack canary tracking (by calling
+ * reset_stack_and_jump() for example).
+ */
+void __init asmlinkage boot_stack_chk_guard_setup(void)
+{
+    /*
+     * Linear congruent generator (X_n+1 = X_n * a + c).
+     *
+     * Constant is taken from "Tables Of Linear Congruential
+     * Generators Of Different Sizes And Good Lattice Structure" by
+     * Pierre L’Ecuyer.
+     */
+#if BITS_PER_LONG == 32
+    const unsigned long a = 2891336453UL;
+#else
+    const unsigned long a = 2862933555777941757UL;
+#endif
+    const unsigned long c = 1;
+
+    unsigned long cycles = get_cycles();
+
+    /* Use the initial value if we can't generate random one */
+    if ( !cycles )
+        return;
+
+    __stack_chk_guard = cycles * a + c;
+}
+
+void asmlinkage __stack_chk_fail(void)
+{
+    dump_execution_state();
+    panic("Stack Protector integrity violation identified\n");
+}
diff --git a/xen/include/xen/stack-protector.h b/xen/include/xen/stack-protector.h
new file mode 100644
index 0000000000..714116498b
--- /dev/null
+++ b/xen/include/xen/stack-protector.h
@@ -0,0 +1,14 @@ 
+#ifndef __XEN_STACK_PROTECTOR_H__
+#define __XEN_STACK_PROTECTOR_H__
+
+#ifdef CONFIG_STACK_PROTECTOR
+
+void asmlinkage boot_stack_chk_guard_setup(void);
+
+#else
+
+static inline void boot_stack_chk_guard_setup(void) {};
+
+#endif
+
+#endif	/* __XEN_STACK_PROTECTOR_H__ */