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>
---
Changes in v4:
- Removed third phase of initialization (it was using Xen's RNG)
- remove stack-protector.h because it is not required anymore
- Reworded comments
- __stack_chk_fail() now dumps execution state before calling panic()
- "Compiler option" Kconfig entry renamed to "Other hardening"
Changes in v3:
- Fixed coding style in stack-protector.h
- Extended panic() message
- Included missed random.h
- Renamed Kconfig option
- Used Andrew's suggestion for the Kconfig help text
- Added "asmlinkage" attribute to __stack_chk_fail() to make Eclair
happy
- Initial stack guard value is random
- Added LCG to generate stack guard value at early boot stages
- Added comment to asm-generic/random.h about dependencies
- Extended the commit message
Changes in v2:
- Moved changes to EMBEDDED_EXTRA_CFLAGS into separate patch
- Renamed stack_protector.c to stack-protector.c
- Renamed stack_protector.h to stack-protector.h
- Removed #ifdef CONFIG_X86 in stack-protector.h
- Updated comment in stack-protector.h
(also, we can't call boot_stack_chk_guard_setup() from asm code in
general case, because it calls get_random() and get_random() may
depend in per_cpu infrastructure, which is initialized later)
- Fixed coding style
- Moved CONFIG_STACK_PROTECTOR into newly added "Compiler options"
submenu
- Marked __stack_chk_guard as __ro_after_init
---
xen/Makefile | 4 +++
xen/common/Kconfig | 15 +++++++++++
xen/common/Makefile | 1 +
xen/common/stack-protector.c | 51 ++++++++++++++++++++++++++++++++++++
4 files changed, 71 insertions(+)
create mode 100644 xen/common/stack-protector.c
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..8fa9f6147f
--- /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");
+}
--
2.47.1
On 14/01/2025 4:25 am, Volodymyr Babchuk wrote: > diff --git a/xen/common/stack-protector.c b/xen/common/stack-protector.c > new file mode 100644 > index 0000000000..8fa9f6147f > --- /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; Indentation. Can be fixed on commit. Everything else LGTM. ~Andrew
© 2016 - 2025 Red Hat, Inc.