linux-pci.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 00/17] Add support for Clang CFI
@ 2021-03-18 17:10 Sami Tolvanen
  2021-03-18 17:10 ` [PATCH v2 01/17] add " Sami Tolvanen
                   ` (16 more replies)
  0 siblings, 17 replies; 35+ messages in thread
From: Sami Tolvanen @ 2021-03-18 17:10 UTC (permalink / raw)
  To: Kees Cook
  Cc: Nathan Chancellor, Nick Desaulniers, Masahiro Yamada,
	Will Deacon, Jessica Yu, Arnd Bergmann, Tejun Heo,
	Paul E. McKenney, Christoph Hellwig, bpf, linux-hardening,
	linux-arch, linux-arm-kernel, linux-kbuild, linux-pci,
	linux-kernel, Sami Tolvanen

This series adds support for Clang's Control-Flow Integrity (CFI)
checking. With CFI, the compiler injects a runtime check before each
indirect function call to ensure the target is a valid function with
the correct static type. This restricts possible call targets and
makes it more difficult for an attacker to exploit bugs that allow the
modification of stored function pointers. For more details, see:

  https://clang.llvm.org/docs/ControlFlowIntegrity.html

The first patch contains build system changes and error handling,
and implements support for cross-module indirect call checking. The
remaining patches address issues caused by the compiler
instrumentation. These include fixing known type mismatches, as well
as issues with address space confusion and cross-module function
address equality.

These patches add support only for arm64, but I'll post patches also
for x86_64 after we address the remaining issues there, including
objtool support.

You can also pull this series from

  https://github.com/samitolvanen/linux.git cfi-v2

---
Changes in v2:
 - Fixed .text merging in module.lds.S.
 - Added WARN_ON_FUNCTION_MISMATCH() and changed kernel/thread.c
   and kernel/workqueue.c to use the macro instead.


Sami Tolvanen (17):
  add support for Clang CFI
  cfi: add __cficanonical
  mm: add generic __va_function and __pa_function macros
  module: ensure __cfi_check alignment
  workqueue: use WARN_ON_FUNCTION_MISMATCH
  kthread: use WARN_ON_FUNCTION_MISMATCH
  kallsyms: strip ThinLTO hashes from static functions
  bpf: disable CFI in dispatcher functions
  lib/list_sort: fix function type mismatches
  lkdtm: use __va_function
  psci: use __pa_function for cpu_resume
  arm64: implement __va_function
  arm64: use __pa_function
  arm64: add __nocfi to functions that jump to a physical address
  arm64: add __nocfi to __apply_alternatives
  KVM: arm64: Disable CFI for nVHE
  arm64: allow CONFIG_CFI_CLANG to be selected

 Makefile                                  |  17 ++
 arch/Kconfig                              |  45 +++
 arch/arm64/Kconfig                        |   1 +
 arch/arm64/include/asm/memory.h           |  15 +
 arch/arm64/include/asm/mmu_context.h      |   4 +-
 arch/arm64/kernel/acpi_parking_protocol.c |   2 +-
 arch/arm64/kernel/alternative.c           |   4 +-
 arch/arm64/kernel/cpu-reset.h             |  10 +-
 arch/arm64/kernel/cpufeature.c            |   4 +-
 arch/arm64/kernel/psci.c                  |   3 +-
 arch/arm64/kernel/smp_spin_table.c        |   2 +-
 arch/arm64/kvm/hyp/nvhe/Makefile          |   6 +-
 drivers/firmware/psci/psci.c              |   4 +-
 drivers/misc/lkdtm/usercopy.c             |   2 +-
 include/asm-generic/bug.h                 |  16 ++
 include/asm-generic/vmlinux.lds.h         |  20 +-
 include/linux/bpf.h                       |   4 +-
 include/linux/cfi.h                       |  41 +++
 include/linux/compiler-clang.h            |   3 +
 include/linux/compiler_types.h            |   8 +
 include/linux/init.h                      |   6 +-
 include/linux/mm.h                        |   8 +
 include/linux/module.h                    |  13 +-
 include/linux/pci.h                       |   4 +-
 init/Kconfig                              |   2 +-
 kernel/Makefile                           |   4 +
 kernel/cfi.c                              | 329 ++++++++++++++++++++++
 kernel/kallsyms.c                         |  54 +++-
 kernel/kthread.c                          |   3 +-
 kernel/module.c                           |  43 +++
 kernel/workqueue.c                        |   2 +-
 lib/list_sort.c                           |   8 +-
 scripts/Makefile.modfinal                 |   2 +-
 scripts/module.lds.S                      |  18 +-
 34 files changed, 663 insertions(+), 44 deletions(-)
 create mode 100644 include/linux/cfi.h
 create mode 100644 kernel/cfi.c


base-commit: 6417f03132a6952cd17ddd8eaddbac92b61b17e0
-- 
2.31.0.291.g576ba9dcdaf-goog


^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH v2 01/17] add support for Clang CFI
  2021-03-18 17:10 [PATCH v2 00/17] Add support for Clang CFI Sami Tolvanen
@ 2021-03-18 17:10 ` Sami Tolvanen
  2021-03-18 22:29   ` Peter Zijlstra
  2021-03-18 17:10 ` [PATCH v2 02/17] cfi: add __cficanonical Sami Tolvanen
                   ` (15 subsequent siblings)
  16 siblings, 1 reply; 35+ messages in thread
From: Sami Tolvanen @ 2021-03-18 17:10 UTC (permalink / raw)
  To: Kees Cook
  Cc: Nathan Chancellor, Nick Desaulniers, Masahiro Yamada,
	Will Deacon, Jessica Yu, Arnd Bergmann, Tejun Heo,
	Paul E. McKenney, Christoph Hellwig, bpf, linux-hardening,
	linux-arch, linux-arm-kernel, linux-kbuild, linux-pci,
	linux-kernel, Sami Tolvanen

This change adds support for Clang’s forward-edge Control Flow
Integrity (CFI) checking. With CONFIG_CFI_CLANG, the compiler
injects a runtime check before each indirect function call to ensure
the target is a valid function with the correct static type. This
restricts possible call targets and makes it more difficult for
an attacker to exploit bugs that allow the modification of stored
function pointers. For more details, see:

  https://clang.llvm.org/docs/ControlFlowIntegrity.html

Clang requires CONFIG_LTO_CLANG to be enabled with CFI to gain
visibility to possible call targets. Kernel modules are supported
with Clang’s cross-DSO CFI mode, which allows checking between
independently compiled components.

With CFI enabled, the compiler injects a __cfi_check() function into
the kernel and each module for validating local call targets. For
cross-module calls that cannot be validated locally, the compiler
calls the global __cfi_slowpath_diag() function, which determines
the target module and calls the correct __cfi_check() function. This
patch includes a slowpath implementation that uses __module_address()
to resolve call targets, and with CONFIG_CFI_CLANG_SHADOW enabled, a
shadow map that speeds up module look-ups by ~3x.

Clang implements indirect call checking using jump tables and
offers two methods of generating them. With canonical jump tables,
the compiler renames each address-taken function to <function>.cfi
and points the original symbol to a jump table entry, which passes
__cfi_check() validation. This isn’t compatible with stand-alone
assembly code, which the compiler doesn’t instrument, and would
result in indirect calls to assembly code to fail. Therefore, we
default to using non-canonical jump tables instead, where the compiler
generates a local jump table entry <function>.cfi_jt for each
address-taken function, and replaces all references to the function
with the address of the jump table entry.

Note that because non-canonical jump table addresses are local
to each component, they break cross-module function address
equality. Specifically, the address of a global function will be
different in each module, as it's replaced with the address of a local
jump table entry. If this address is passed to a different module,
it won’t match the address of the same function taken there. This
may break code that relies on comparing addresses passed from other
components.

CFI checking can be disabled in a function with the __nocfi attribute.
Additionally, CFI can be disabled for an entire compilation unit by
filtering out CC_FLAGS_CFI.

By default, CFI failures result in a kernel panic to stop a potential
exploit. CONFIG_CFI_PERMISSIVE enables a permissive mode, where the
kernel prints out a rate-limited warning instead, and allows execution
to continue. This option is helpful for locating type mismatches, but
should only be enabled during development.

Signed-off-by: Sami Tolvanen <samitolvanen@google.com>
Reviewed-by: Kees Cook <keescook@chromium.org>
---
 Makefile                          |  17 ++
 arch/Kconfig                      |  45 ++++
 include/asm-generic/bug.h         |  16 ++
 include/asm-generic/vmlinux.lds.h |  20 +-
 include/linux/cfi.h               |  41 ++++
 include/linux/compiler-clang.h    |   2 +
 include/linux/compiler_types.h    |   4 +
 include/linux/init.h              |   2 +-
 include/linux/module.h            |  13 +-
 init/Kconfig                      |   2 +-
 kernel/Makefile                   |   4 +
 kernel/cfi.c                      | 329 ++++++++++++++++++++++++++++++
 kernel/module.c                   |  43 ++++
 scripts/Makefile.modfinal         |   2 +-
 14 files changed, 534 insertions(+), 6 deletions(-)
 create mode 100644 include/linux/cfi.h
 create mode 100644 kernel/cfi.c

diff --git a/Makefile b/Makefile
index a28bb374663d..9a9e75b937f9 100644
--- a/Makefile
+++ b/Makefile
@@ -920,6 +920,23 @@ KBUILD_AFLAGS	+= -fno-lto
 export CC_FLAGS_LTO
 endif
 
+ifdef CONFIG_CFI_CLANG
+CC_FLAGS_CFI	:= -fsanitize=cfi \
+		   -fsanitize-cfi-cross-dso \
+		   -fno-sanitize-cfi-canonical-jump-tables \
+		   -fno-sanitize-trap=cfi \
+		   -fno-sanitize-blacklist
+
+ifdef CONFIG_CFI_PERMISSIVE
+CC_FLAGS_CFI	+= -fsanitize-recover=cfi
+endif
+
+# If LTO flags are filtered out, we must also filter out CFI.
+CC_FLAGS_LTO	+= $(CC_FLAGS_CFI)
+KBUILD_CFLAGS	+= $(CC_FLAGS_CFI)
+export CC_FLAGS_CFI
+endif
+
 ifdef CONFIG_DEBUG_FORCE_FUNCTION_ALIGN_32B
 KBUILD_CFLAGS += -falign-functions=32
 endif
diff --git a/arch/Kconfig b/arch/Kconfig
index ecfd3520b676..f6a85ba6cba2 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -692,6 +692,51 @@ config LTO_CLANG_THIN
 	  If unsure, say Y.
 endchoice
 
+config ARCH_SUPPORTS_CFI_CLANG
+	bool
+	help
+	  An architecture should select this option if it can support Clang's
+	  Control-Flow Integrity (CFI) checking.
+
+config CFI_CLANG
+	bool "Use Clang's Control Flow Integrity (CFI)"
+	depends on LTO_CLANG && ARCH_SUPPORTS_CFI_CLANG
+	# Clang >= 12:
+	# - https://bugs.llvm.org/show_bug.cgi?id=46258
+	# - https://bugs.llvm.org/show_bug.cgi?id=47479
+	depends on CLANG_VERSION >= 120000
+	select KALLSYMS
+	help
+	  This option enables Clang’s forward-edge Control Flow Integrity
+	  (CFI) checking, where the compiler injects a runtime check to each
+	  indirect function call to ensure the target is a valid function with
+	  the correct static type. This restricts possible call targets and
+	  makes it more difficult for an attacker to exploit bugs that allow
+	  the modification of stored function pointers. More information can be
+	  found from Clang's documentation:
+
+	    https://clang.llvm.org/docs/ControlFlowIntegrity.html
+
+config CFI_CLANG_SHADOW
+	bool "Use CFI shadow to speed up cross-module checks"
+	default y
+	depends on CFI_CLANG && MODULES
+	help
+	  If you select this option, the kernel builds a fast look-up table of
+	  CFI check functions in loaded modules to reduce performance overhead.
+
+	  If unsure, say Y.
+
+config CFI_PERMISSIVE
+	bool "Use CFI in permissive mode"
+	depends on CFI_CLANG
+	help
+	  When selected, Control Flow Integrity (CFI) violations result in a
+	  warning instead of a kernel panic. This option should only be used
+	  for finding indirect call type mismatches during development.
+
+	  If unsure, say N.
+
 config HAVE_ARCH_WITHIN_STACK_FRAMES
 	bool
 	help
diff --git a/include/asm-generic/bug.h b/include/asm-generic/bug.h
index 76a10e0dca9f..b402494883b6 100644
--- a/include/asm-generic/bug.h
+++ b/include/asm-generic/bug.h
@@ -241,6 +241,22 @@ void __warn(const char *file, int line, void *caller, unsigned taint,
 # define WARN_ON_SMP(x)			({0;})
 #endif
 
+/*
+ * WARN_ON_FUNCTION_MISMATCH() warns if a value doesn't match a
+ * function address, and can be useful for catching issues with
+ * callback functions, for example.
+ *
+ * With CONFIG_CFI_CLANG, the warning is disabled because the
+ * compiler replaces function addresses taken in C code with
+ * local jump table addresses, which breaks cross-module function
+ * address equality.
+ */
+#if defined(CONFIG_CFI_CLANG) && defined(CONFIG_MODULES)
+# define WARN_ON_FUNCTION_MISMATCH(x, fn) ({ 0; })
+#else
+# define WARN_ON_FUNCTION_MISMATCH(x, fn) WARN_ON_ONCE((x) != (fn))
+#endif
+
 #endif /* __ASSEMBLY__ */
 
 #endif
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index 0331d5d49551..40a9c101565e 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -544,6 +544,22 @@
 	. = ALIGN((align));						\
 	__end_rodata = .;
 
+
+/*
+ * .text..L.cfi.jumptable.* contain Control-Flow Integrity (CFI)
+ * jump table entries.
+ */
+#ifdef CONFIG_CFI_CLANG
+#define TEXT_CFI_JT							\
+		. = ALIGN(PMD_SIZE);					\
+		__cfi_jt_start = .;					\
+		*(.text..L.cfi.jumptable .text..L.cfi.jumptable.*)	\
+		. = ALIGN(PMD_SIZE);					\
+		__cfi_jt_end = .;
+#else
+#define TEXT_CFI_JT
+#endif
+
 /*
  * Non-instrumentable text section
  */
@@ -570,6 +586,7 @@
 		NOINSTR_TEXT						\
 		*(.text..refcount)					\
 		*(.ref.text)						\
+		TEXT_CFI_JT						\
 	MEM_KEEP(init.text*)						\
 	MEM_KEEP(exit.text*)						\
 
@@ -974,7 +991,8 @@
  * keep any .init_array.* sections.
  * https://bugs.llvm.org/show_bug.cgi?id=46478
  */
-#if defined(CONFIG_GCOV_KERNEL) || defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KCSAN)
+#if defined(CONFIG_GCOV_KERNEL) || defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KCSAN) || \
+	defined(CONFIG_CFI_CLANG)
 # ifdef CONFIG_CONSTRUCTORS
 #  define SANITIZER_DISCARDS						\
 	*(.eh_frame)
diff --git a/include/linux/cfi.h b/include/linux/cfi.h
new file mode 100644
index 000000000000..879744aaa6e0
--- /dev/null
+++ b/include/linux/cfi.h
@@ -0,0 +1,41 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Clang Control Flow Integrity (CFI) support.
+ *
+ * Copyright (C) 2021 Google LLC
+ */
+#ifndef _LINUX_CFI_H
+#define _LINUX_CFI_H
+
+#ifdef CONFIG_CFI_CLANG
+typedef void (*cfi_check_fn)(uint64_t id, void *ptr, void *diag);
+
+/* Compiler-generated function in each module, and the kernel */
+extern void __cfi_check(uint64_t id, void *ptr, void *diag);
+
+/*
+ * Force the compiler to generate a CFI jump table entry for a function
+ * and store the jump table address to __cfi_jt_<function>.
+ */
+#define __CFI_ADDRESSABLE(fn, __attr) \
+	const void *__cfi_jt_ ## fn __visible __attr = (void *)&fn
+
+#ifdef CONFIG_CFI_CLANG_SHADOW
+
+extern void cfi_module_add(struct module *mod, unsigned long base_addr);
+extern void cfi_module_remove(struct module *mod, unsigned long base_addr);
+
+#else
+
+static inline void cfi_module_add(struct module *mod, unsigned long base_addr) {}
+static inline void cfi_module_remove(struct module *mod, unsigned long base_addr) {}
+
+#endif /* CONFIG_CFI_CLANG_SHADOW */
+
+#else /* !CONFIG_CFI_CLANG */
+
+#define __CFI_ADDRESSABLE(fn, __attr)
+
+#endif /* CONFIG_CFI_CLANG */
+
+#endif /* _LINUX_CFI_H */
diff --git a/include/linux/compiler-clang.h b/include/linux/compiler-clang.h
index d217c382b02d..6de9d0c9377e 100644
--- a/include/linux/compiler-clang.h
+++ b/include/linux/compiler-clang.h
@@ -61,3 +61,5 @@
 #if __has_feature(shadow_call_stack)
 # define __noscs	__attribute__((__no_sanitize__("shadow-call-stack")))
 #endif
+
+#define __nocfi		__attribute__((__no_sanitize__("cfi")))
diff --git a/include/linux/compiler_types.h b/include/linux/compiler_types.h
index e5dd5a4ae946..796935a37e37 100644
--- a/include/linux/compiler_types.h
+++ b/include/linux/compiler_types.h
@@ -242,6 +242,10 @@ struct ftrace_likely_data {
 # define __noscs
 #endif
 
+#ifndef __nocfi
+# define __nocfi
+#endif
+
 #ifndef asm_volatile_goto
 #define asm_volatile_goto(x...) asm goto(x)
 #endif
diff --git a/include/linux/init.h b/include/linux/init.h
index 31f54de58429..b3ea15348fbd 100644
--- a/include/linux/init.h
+++ b/include/linux/init.h
@@ -47,7 +47,7 @@
 
 /* These are for everybody (although not all archs will actually
    discard it in modules) */
-#define __init		__section(".init.text") __cold  __latent_entropy __noinitretpoline
+#define __init		__section(".init.text") __cold  __latent_entropy __noinitretpoline __nocfi
 #define __initdata	__section(".init.data")
 #define __initconst	__section(".init.rodata")
 #define __exitdata	__section(".exit.data")
diff --git a/include/linux/module.h b/include/linux/module.h
index da4b6fbe8ebe..8100bb477d86 100644
--- a/include/linux/module.h
+++ b/include/linux/module.h
@@ -26,6 +26,7 @@
 #include <linux/tracepoint-defs.h>
 #include <linux/srcu.h>
 #include <linux/static_call_types.h>
+#include <linux/cfi.h>
 
 #include <linux/percpu.h>
 #include <asm/module.h>
@@ -128,13 +129,17 @@ extern void cleanup_module(void);
 #define module_init(initfn)					\
 	static inline initcall_t __maybe_unused __inittest(void)		\
 	{ return initfn; }					\
-	int init_module(void) __copy(initfn) __attribute__((alias(#initfn)));
+	int init_module(void) __copy(initfn)			\
+		__attribute__((alias(#initfn)));		\
+	__CFI_ADDRESSABLE(init_module, __initdata);
 
 /* This is only required if you want to be unloadable. */
 #define module_exit(exitfn)					\
 	static inline exitcall_t __maybe_unused __exittest(void)		\
 	{ return exitfn; }					\
-	void cleanup_module(void) __copy(exitfn) __attribute__((alias(#exitfn)));
+	void cleanup_module(void) __copy(exitfn)		\
+		__attribute__((alias(#exitfn)));		\
+	__CFI_ADDRESSABLE(cleanup_module, __exitdata);
 
 #endif
 
@@ -376,6 +381,10 @@ struct module {
 	const s32 *crcs;
 	unsigned int num_syms;
 
+#ifdef CONFIG_CFI_CLANG
+	cfi_check_fn cfi_check;
+#endif
+
 	/* Kernel parameters. */
 #ifdef CONFIG_SYSFS
 	struct mutex param_lock;
diff --git a/init/Kconfig b/init/Kconfig
index 5f5c776ef192..5babea38e346 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -2296,7 +2296,7 @@ endif # MODULES
 
 config MODULES_TREE_LOOKUP
 	def_bool y
-	depends on PERF_EVENTS || TRACING
+	depends on PERF_EVENTS || TRACING || CFI_CLANG
 
 config INIT_ALL_POSSIBLE
 	bool
diff --git a/kernel/Makefile b/kernel/Makefile
index 320f1f3941b7..e8a6715f38dc 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -41,6 +41,9 @@ KCSAN_SANITIZE_kcov.o := n
 UBSAN_SANITIZE_kcov.o := n
 CFLAGS_kcov.o := $(call cc-option, -fno-conserve-stack) -fno-stack-protector
 
+# Don't instrument error handlers
+CFLAGS_REMOVE_cfi.o := $(CC_FLAGS_CFI)
+
 obj-y += sched/
 obj-y += locking/
 obj-y += power/
@@ -111,6 +114,7 @@ obj-$(CONFIG_BPF) += bpf/
 obj-$(CONFIG_KCSAN) += kcsan/
 obj-$(CONFIG_SHADOW_CALL_STACK) += scs.o
 obj-$(CONFIG_HAVE_STATIC_CALL_INLINE) += static_call.o
+obj-$(CONFIG_CFI_CLANG) += cfi.o
 
 obj-$(CONFIG_PERF_EVENTS) += events/
 
diff --git a/kernel/cfi.c b/kernel/cfi.c
new file mode 100644
index 000000000000..8c9ed3f7058a
--- /dev/null
+++ b/kernel/cfi.c
@@ -0,0 +1,329 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Clang Control Flow Integrity (CFI) error and slowpath handling.
+ *
+ * Copyright (C) 2021 Google LLC
+ */
+
+#include <linux/hardirq.h>
+#include <linux/kallsyms.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/printk.h>
+#include <linux/ratelimit.h>
+#include <linux/rcupdate.h>
+#include <linux/vmalloc.h>
+#include <asm/cacheflush.h>
+#include <asm/set_memory.h>
+
+/* Compiler-defined handler names */
+#ifdef CONFIG_CFI_PERMISSIVE
+#define cfi_failure_handler	__ubsan_handle_cfi_check_fail
+#else
+#define cfi_failure_handler	__ubsan_handle_cfi_check_fail_abort
+#endif
+
+static inline void handle_cfi_failure(void *ptr)
+{
+	if (IS_ENABLED(CONFIG_CFI_PERMISSIVE))
+		WARN_RATELIMIT(1, "CFI failure (target: %pS):\n", ptr);
+	else
+		panic("CFI failure (target: %pS)\n", ptr);
+}
+
+#ifdef CONFIG_MODULES
+#ifdef CONFIG_CFI_CLANG_SHADOW
+/*
+ * Index type. A 16-bit index can address at most (2^16)-2 pages (taking
+ * into account SHADOW_INVALID), i.e. ~256M with 4k pages.
+ */
+typedef u16 shadow_t;
+#define SHADOW_INVALID		((shadow_t)~0UL)
+
+struct cfi_shadow {
+	/* Page index for the beginning of the shadow */
+	unsigned long base;
+	/* An array of __cfi_check locations (as indices to the shadow) */
+	shadow_t shadow[1];
+} __packed;
+
+/*
+ * The shadow covers ~128M from the beginning of the module region. If
+ * the region is larger, we fall back to __module_address for the rest.
+ */
+#define __SHADOW_RANGE		(_UL(SZ_128M) >> PAGE_SHIFT)
+
+/* The in-memory size of struct cfi_shadow, always at least one page */
+#define __SHADOW_PAGES		((__SHADOW_RANGE * sizeof(shadow_t)) >> PAGE_SHIFT)
+#define SHADOW_PAGES		max(1UL, __SHADOW_PAGES)
+#define SHADOW_SIZE		(SHADOW_PAGES << PAGE_SHIFT)
+
+/* The actual size of the shadow array, minus metadata */
+#define SHADOW_ARR_SIZE		(SHADOW_SIZE - offsetof(struct cfi_shadow, shadow))
+#define SHADOW_ARR_SLOTS	(SHADOW_ARR_SIZE / sizeof(shadow_t))
+
+static DEFINE_MUTEX(shadow_update_lock);
+static struct cfi_shadow __rcu *cfi_shadow __read_mostly;
+
+/* Returns the index in the shadow for the given address */
+static inline int ptr_to_shadow(const struct cfi_shadow *s, unsigned long ptr)
+{
+	unsigned long index;
+	unsigned long page = ptr >> PAGE_SHIFT;
+
+	if (unlikely(page < s->base))
+		return -1; /* Outside of module area */
+
+	index = page - s->base;
+
+	if (index >= SHADOW_ARR_SLOTS)
+		return -1; /* Cannot be addressed with shadow */
+
+	return (int)index;
+}
+
+/* Returns the page address for an index in the shadow */
+static inline unsigned long shadow_to_ptr(const struct cfi_shadow *s,
+	int index)
+{
+	if (unlikely(index < 0 || index >= SHADOW_ARR_SLOTS))
+		return 0;
+
+	return (s->base + index) << PAGE_SHIFT;
+}
+
+/* Returns the __cfi_check function address for the given shadow location */
+static inline unsigned long shadow_to_check_fn(const struct cfi_shadow *s,
+	int index)
+{
+	if (unlikely(index < 0 || index >= SHADOW_ARR_SLOTS))
+		return 0;
+
+	if (unlikely(s->shadow[index] == SHADOW_INVALID))
+		return 0;
+
+	/* __cfi_check is always page aligned */
+	return (s->base + s->shadow[index]) << PAGE_SHIFT;
+}
+
+static void prepare_next_shadow(const struct cfi_shadow __rcu *prev,
+		struct cfi_shadow *next)
+{
+	int i, index, check;
+
+	/* Mark everything invalid */
+	memset(next->shadow, 0xFF, SHADOW_ARR_SIZE);
+
+	if (!prev)
+		return; /* No previous shadow */
+
+	/* If the base address didn't change, an update is not needed */
+	if (prev->base == next->base) {
+		memcpy(next->shadow, prev->shadow, SHADOW_ARR_SIZE);
+		return;
+	}
+
+	/* Convert the previous shadow to the new address range */
+	for (i = 0; i < SHADOW_ARR_SLOTS; ++i) {
+		if (prev->shadow[i] == SHADOW_INVALID)
+			continue;
+
+		index = ptr_to_shadow(next, shadow_to_ptr(prev, i));
+		if (index < 0)
+			continue;
+
+		check = ptr_to_shadow(next,
+				shadow_to_check_fn(prev, prev->shadow[i]));
+		if (check < 0)
+			continue;
+
+		next->shadow[index] = (shadow_t)check;
+	}
+}
+
+static void add_module_to_shadow(struct cfi_shadow *s, struct module *mod,
+			unsigned long min_addr, unsigned long max_addr)
+{
+	int check_index;
+	unsigned long check = (unsigned long)mod->cfi_check;
+	unsigned long ptr;
+
+	if (unlikely(!PAGE_ALIGNED(check))) {
+		pr_warn("cfi: not using shadow for module %s\n", mod->name);
+		return;
+	}
+
+	check_index = ptr_to_shadow(s, check);
+	if (check_index < 0)
+		return; /* Module not addressable with shadow */
+
+	/* For each page, store the check function index in the shadow */
+	for (ptr = min_addr; ptr <= max_addr; ptr += PAGE_SIZE) {
+		int index = ptr_to_shadow(s, ptr);
+
+		if (index >= 0) {
+			/* Each page must only contain one module */
+			WARN_ON_ONCE(s->shadow[index] != SHADOW_INVALID);
+			s->shadow[index] = (shadow_t)check_index;
+		}
+	}
+}
+
+static void remove_module_from_shadow(struct cfi_shadow *s, struct module *mod,
+		unsigned long min_addr, unsigned long max_addr)
+{
+	unsigned long ptr;
+
+	for (ptr = min_addr; ptr <= max_addr; ptr += PAGE_SIZE) {
+		int index = ptr_to_shadow(s, ptr);
+
+		if (index >= 0)
+			s->shadow[index] = SHADOW_INVALID;
+	}
+}
+
+typedef void (*update_shadow_fn)(struct cfi_shadow *, struct module *,
+			unsigned long min_addr, unsigned long max_addr);
+
+static void update_shadow(struct module *mod, unsigned long base_addr,
+		update_shadow_fn fn)
+{
+	struct cfi_shadow *prev;
+	struct cfi_shadow *next;
+	unsigned long min_addr, max_addr;
+
+	next = vmalloc(SHADOW_SIZE);
+
+	mutex_lock(&shadow_update_lock);
+	prev = rcu_dereference_protected(cfi_shadow,
+					 mutex_is_locked(&shadow_update_lock));
+
+	if (next) {
+		next->base = base_addr >> PAGE_SHIFT;
+		prepare_next_shadow(prev, next);
+
+		min_addr = (unsigned long)mod->core_layout.base;
+		max_addr = min_addr + mod->core_layout.text_size;
+		fn(next, mod, min_addr & PAGE_MASK, max_addr & PAGE_MASK);
+
+		set_memory_ro((unsigned long)next, SHADOW_PAGES);
+	}
+
+	rcu_assign_pointer(cfi_shadow, next);
+	mutex_unlock(&shadow_update_lock);
+	synchronize_rcu_expedited();
+
+	if (prev) {
+		set_memory_rw((unsigned long)prev, SHADOW_PAGES);
+		vfree(prev);
+	}
+}
+
+void cfi_module_add(struct module *mod, unsigned long base_addr)
+{
+	update_shadow(mod, base_addr, add_module_to_shadow);
+}
+
+void cfi_module_remove(struct module *mod, unsigned long base_addr)
+{
+	update_shadow(mod, base_addr, remove_module_from_shadow);
+}
+
+static inline cfi_check_fn ptr_to_check_fn(const struct cfi_shadow __rcu *s,
+	unsigned long ptr)
+{
+	int index;
+
+	if (unlikely(!s))
+		return NULL; /* No shadow available */
+
+	index = ptr_to_shadow(s, ptr);
+	if (index < 0)
+		return NULL; /* Cannot be addressed with shadow */
+
+	return (cfi_check_fn)shadow_to_check_fn(s, index);
+}
+
+static inline cfi_check_fn find_shadow_check_fn(unsigned long ptr)
+{
+	cfi_check_fn fn;
+
+	rcu_read_lock_sched();
+	fn = ptr_to_check_fn(rcu_dereference_sched(cfi_shadow), ptr);
+	rcu_read_unlock_sched();
+
+	return fn;
+}
+
+#else /* !CONFIG_CFI_CLANG_SHADOW */
+
+static inline cfi_check_fn find_shadow_check_fn(unsigned long ptr)
+{
+	return NULL;
+}
+
+#endif /* CONFIG_CFI_CLANG_SHADOW */
+
+static inline cfi_check_fn find_module_check_fn(unsigned long ptr)
+{
+	cfi_check_fn fn = NULL;
+	struct module *mod;
+
+	rcu_read_lock_sched();
+	mod = __module_address(ptr);
+	if (mod)
+		fn = mod->cfi_check;
+	rcu_read_unlock_sched();
+
+	return fn;
+}
+
+static inline cfi_check_fn find_check_fn(unsigned long ptr)
+{
+	cfi_check_fn fn = NULL;
+
+	if (is_kernel_text(ptr))
+		return __cfi_check;
+
+	/*
+	 * Indirect call checks can happen when RCU is not watching. Both
+	 * the shadow and __module_address use RCU, so we need to wake it
+	 * up if necessary.
+	 */
+	RCU_NONIDLE({
+		if (IS_ENABLED(CONFIG_CFI_CLANG_SHADOW))
+			fn = find_shadow_check_fn(ptr);
+
+		if (!fn)
+			fn = find_module_check_fn(ptr);
+	});
+
+	return fn;
+}
+
+void __cfi_slowpath_diag(uint64_t id, void *ptr, void *diag)
+{
+	cfi_check_fn fn = find_check_fn((unsigned long)ptr);
+
+	if (likely(fn))
+		fn(id, ptr, diag);
+	else /* Don't allow unchecked modules */
+		handle_cfi_failure(ptr);
+}
+EXPORT_SYMBOL(__cfi_slowpath_diag);
+
+#else /* !CONFIG_MODULES */
+
+void __cfi_slowpath_diag(uint64_t id, void *ptr, void *diag)
+{
+	handle_cfi_failure(ptr); /* No modules */
+}
+EXPORT_SYMBOL(__cfi_slowpath_diag);
+
+#endif /* CONFIG_MODULES */
+
+void cfi_failure_handler(void *data, void *ptr, void *vtable)
+{
+	handle_cfi_failure(ptr);
+}
+EXPORT_SYMBOL(cfi_failure_handler);
diff --git a/kernel/module.c b/kernel/module.c
index 30479355ab85..20fb004e7d8d 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -2146,6 +2146,8 @@ void __weak module_arch_freeing_init(struct module *mod)
 {
 }
 
+static void cfi_cleanup(struct module *mod);
+
 /* Free a module, remove from lists, etc. */
 static void free_module(struct module *mod)
 {
@@ -2187,6 +2189,9 @@ static void free_module(struct module *mod)
 	synchronize_rcu();
 	mutex_unlock(&module_mutex);
 
+	/* Clean up CFI for the module. */
+	cfi_cleanup(mod);
+
 	/* This may be empty, but that's OK */
 	module_arch_freeing_init(mod);
 	module_memfree(mod->init_layout.base);
@@ -3866,6 +3871,8 @@ static int unknown_module_param_cb(char *param, char *val, const char *modname,
 	return 0;
 }
 
+static void cfi_init(struct module *mod);
+
 /*
  * Allocate and load the module: note that size of section 0 is always
  * zero, and we rely on this for optional sections.
@@ -3997,6 +4004,9 @@ static int load_module(struct load_info *info, const char __user *uargs,
 
 	flush_module_icache(mod);
 
+	/* Setup CFI for the module. */
+	cfi_init(mod);
+
 	/* Now copy in args */
 	mod->args = strndup_user(uargs, ~0UL >> 1);
 	if (IS_ERR(mod->args)) {
@@ -4070,6 +4080,7 @@ static int load_module(struct load_info *info, const char __user *uargs,
 	synchronize_rcu();
 	kfree(mod->args);
  free_arch_cleanup:
+	cfi_cleanup(mod);
 	module_arch_cleanup(mod);
  free_modinfo:
 	free_modinfo(mod);
@@ -4415,6 +4426,38 @@ int module_kallsyms_on_each_symbol(int (*fn)(void *, const char *,
 #endif /* CONFIG_LIVEPATCH */
 #endif /* CONFIG_KALLSYMS */
 
+static void cfi_init(struct module *mod)
+{
+#ifdef CONFIG_CFI_CLANG
+	initcall_t *init;
+	exitcall_t *exit;
+
+	rcu_read_lock_sched();
+	mod->cfi_check = (cfi_check_fn)
+		find_kallsyms_symbol_value(mod, "__cfi_check");
+	init = (initcall_t *)
+		find_kallsyms_symbol_value(mod, "__cfi_jt_init_module");
+	exit = (exitcall_t *)
+		find_kallsyms_symbol_value(mod, "__cfi_jt_cleanup_module");
+	rcu_read_unlock_sched();
+
+	/* Fix init/exit functions to point to the CFI jump table */
+	if (init)
+		mod->init = *init;
+	if (exit)
+		mod->exit = *exit;
+
+	cfi_module_add(mod, module_addr_min);
+#endif
+}
+
+static void cfi_cleanup(struct module *mod)
+{
+#ifdef CONFIG_CFI_CLANG
+	cfi_module_remove(mod, module_addr_min);
+#endif
+}
+
 /* Maximum number of characters written by module_flags() */
 #define MODULE_FLAGS_BUF_SIZE (TAINT_FLAGS_COUNT + 4)
 
diff --git a/scripts/Makefile.modfinal b/scripts/Makefile.modfinal
index 735e11e9041b..dd87cea9fba7 100644
--- a/scripts/Makefile.modfinal
+++ b/scripts/Makefile.modfinal
@@ -23,7 +23,7 @@ modname = $(notdir $(@:.mod.o=))
 part-of-module = y
 
 quiet_cmd_cc_o_c = CC [M]  $@
-      cmd_cc_o_c = $(CC) $(c_flags) -c -o $@ $<
+      cmd_cc_o_c = $(CC) $(filter-out $(CC_FLAGS_CFI), $(c_flags)) -c -o $@ $<
 
 %.mod.o: %.mod.c FORCE
 	$(call if_changed_dep,cc_o_c)
-- 
2.31.0.291.g576ba9dcdaf-goog


^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH v2 02/17] cfi: add __cficanonical
  2021-03-18 17:10 [PATCH v2 00/17] Add support for Clang CFI Sami Tolvanen
  2021-03-18 17:10 ` [PATCH v2 01/17] add " Sami Tolvanen
@ 2021-03-18 17:10 ` Sami Tolvanen
  2021-03-18 17:49   ` Nick Desaulniers
  2021-03-18 17:10 ` [PATCH v2 03/17] mm: add generic __va_function and __pa_function macros Sami Tolvanen
                   ` (14 subsequent siblings)
  16 siblings, 1 reply; 35+ messages in thread
From: Sami Tolvanen @ 2021-03-18 17:10 UTC (permalink / raw)
  To: Kees Cook
  Cc: Nathan Chancellor, Nick Desaulniers, Masahiro Yamada,
	Will Deacon, Jessica Yu, Arnd Bergmann, Tejun Heo,
	Paul E. McKenney, Christoph Hellwig, bpf, linux-hardening,
	linux-arch, linux-arm-kernel, linux-kbuild, linux-pci,
	linux-kernel, Sami Tolvanen

With CONFIG_CFI_CLANG, the compiler replaces a function address taken
in C code with the address of a local jump table entry, which passes
runtime indirect call checks. However, the compiler won't replace
addresses taken in assembly code, which will result in a CFI failure
if we later jump to such an address in instrumented C code. The code
generated for the non-canonical jump table looks this:

  <noncanonical.cfi_jt>: /* In C, &noncanonical points here */
	jmp noncanonical
  ...
  <noncanonical>:        /* function body */
	...

This change adds the __cficanonical attribute, which tells the
compiler to use a canonical jump table for the function instead. This
means the compiler will rename the actual function to <function>.cfi
and points the original symbol to the jump table entry instead:

  <canonical>:           /* jump table entry */
	jmp canonical.cfi
  ...
  <canonical.cfi>:       /* function body */
	...

As a result, the address taken in assembly, or other non-instrumented
code always points to the jump table and therefore, can be used for
indirect calls in instrumented code without tripping CFI checks.

Signed-off-by: Sami Tolvanen <samitolvanen@google.com>
Reviewed-by: Kees Cook <keescook@chromium.org>
Acked-by: Bjorn Helgaas <bhelgaas@google.com>   # pci.h
---
 include/linux/compiler-clang.h | 1 +
 include/linux/compiler_types.h | 4 ++++
 include/linux/init.h           | 4 ++--
 include/linux/pci.h            | 4 ++--
 4 files changed, 9 insertions(+), 4 deletions(-)

diff --git a/include/linux/compiler-clang.h b/include/linux/compiler-clang.h
index 6de9d0c9377e..adbe76b203e2 100644
--- a/include/linux/compiler-clang.h
+++ b/include/linux/compiler-clang.h
@@ -63,3 +63,4 @@
 #endif
 
 #define __nocfi		__attribute__((__no_sanitize__("cfi")))
+#define __cficanonical	__attribute__((__cfi_canonical_jump_table__))
diff --git a/include/linux/compiler_types.h b/include/linux/compiler_types.h
index 796935a37e37..d29bda7f6ebd 100644
--- a/include/linux/compiler_types.h
+++ b/include/linux/compiler_types.h
@@ -246,6 +246,10 @@ struct ftrace_likely_data {
 # define __nocfi
 #endif
 
+#ifndef __cficanonical
+# define __cficanonical
+#endif
+
 #ifndef asm_volatile_goto
 #define asm_volatile_goto(x...) asm goto(x)
 #endif
diff --git a/include/linux/init.h b/include/linux/init.h
index b3ea15348fbd..045ad1650ed1 100644
--- a/include/linux/init.h
+++ b/include/linux/init.h
@@ -220,8 +220,8 @@ extern bool initcall_debug;
 	__initcall_name(initstub, __iid, id)
 
 #define __define_initcall_stub(__stub, fn)			\
-	int __init __stub(void);				\
-	int __init __stub(void)					\
+	int __init __cficanonical __stub(void);			\
+	int __init __cficanonical __stub(void)			\
 	{ 							\
 		return fn();					\
 	}							\
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 86c799c97b77..39684b72db91 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -1944,8 +1944,8 @@ enum pci_fixup_pass {
 #ifdef CONFIG_LTO_CLANG
 #define __DECLARE_PCI_FIXUP_SECTION(sec, name, vendor, device, class,	\
 				  class_shift, hook, stub)		\
-	void stub(struct pci_dev *dev);					\
-	void stub(struct pci_dev *dev)					\
+	void __cficanonical stub(struct pci_dev *dev);			\
+	void __cficanonical stub(struct pci_dev *dev)			\
 	{ 								\
 		hook(dev); 						\
 	}								\
-- 
2.31.0.291.g576ba9dcdaf-goog


^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH v2 03/17] mm: add generic __va_function and __pa_function macros
  2021-03-18 17:10 [PATCH v2 00/17] Add support for Clang CFI Sami Tolvanen
  2021-03-18 17:10 ` [PATCH v2 01/17] add " Sami Tolvanen
  2021-03-18 17:10 ` [PATCH v2 02/17] cfi: add __cficanonical Sami Tolvanen
@ 2021-03-18 17:10 ` Sami Tolvanen
  2021-03-18 17:10 ` [PATCH v2 04/17] module: ensure __cfi_check alignment Sami Tolvanen
                   ` (13 subsequent siblings)
  16 siblings, 0 replies; 35+ messages in thread
From: Sami Tolvanen @ 2021-03-18 17:10 UTC (permalink / raw)
  To: Kees Cook
  Cc: Nathan Chancellor, Nick Desaulniers, Masahiro Yamada,
	Will Deacon, Jessica Yu, Arnd Bergmann, Tejun Heo,
	Paul E. McKenney, Christoph Hellwig, bpf, linux-hardening,
	linux-arch, linux-arm-kernel, linux-kbuild, linux-pci,
	linux-kernel, Sami Tolvanen

With CONFIG_CFI_CLANG, the compiler replaces function addresses
in instrumented C code with jump table addresses. This means that
__pa_symbol(function) returns the physical address of the jump table
entry instead of the actual function, which may not work as the jump
table code will immediately jump to a virtual address that may not be
mapped.

To avoid this address space confusion, this change adds generic
definitions for __va_function and __pa_function, which architectures
that support CFI can override. The typical implementation of the
__va_function macro would use inline assembly to take the function
address, which avoids compiler instrumentation.

Signed-off-by: Sami Tolvanen <samitolvanen@google.com>
Reviewed-by: Kees Cook <keescook@chromium.org>
---
 include/linux/mm.h | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/include/linux/mm.h b/include/linux/mm.h
index 64a71bf20536..a0d285cd59ce 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -116,6 +116,14 @@ extern int mmap_rnd_compat_bits __read_mostly;
 #define __pa_symbol(x)  __pa(RELOC_HIDE((unsigned long)(x), 0))
 #endif
 
+#ifndef __va_function
+#define __va_function(x) (x)
+#endif
+
+#ifndef __pa_function
+#define __pa_function(x) __pa_symbol(__va_function(x))
+#endif
+
 #ifndef page_to_virt
 #define page_to_virt(x)	__va(PFN_PHYS(page_to_pfn(x)))
 #endif
-- 
2.31.0.291.g576ba9dcdaf-goog


^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH v2 04/17] module: ensure __cfi_check alignment
  2021-03-18 17:10 [PATCH v2 00/17] Add support for Clang CFI Sami Tolvanen
                   ` (2 preceding siblings ...)
  2021-03-18 17:10 ` [PATCH v2 03/17] mm: add generic __va_function and __pa_function macros Sami Tolvanen
@ 2021-03-18 17:10 ` Sami Tolvanen
  2021-03-18 19:27   ` Nick Desaulniers
  2021-03-18 17:10 ` [PATCH v2 05/17] workqueue: use WARN_ON_FUNCTION_MISMATCH Sami Tolvanen
                   ` (12 subsequent siblings)
  16 siblings, 1 reply; 35+ messages in thread
From: Sami Tolvanen @ 2021-03-18 17:10 UTC (permalink / raw)
  To: Kees Cook
  Cc: Nathan Chancellor, Nick Desaulniers, Masahiro Yamada,
	Will Deacon, Jessica Yu, Arnd Bergmann, Tejun Heo,
	Paul E. McKenney, Christoph Hellwig, bpf, linux-hardening,
	linux-arch, linux-arm-kernel, linux-kbuild, linux-pci,
	linux-kernel, Sami Tolvanen

CONFIG_CFI_CLANG_SHADOW assumes the __cfi_check() function is page
aligned and at the beginning of the .text section. While Clang would
normally align the function correctly, it fails to do so for modules
with no executable code.

This change ensures the correct __cfi_check() location and
alignment. It also discards the .eh_frame section, which Clang can
generate with certain sanitizers, such as CFI.

Link: https://bugs.llvm.org/show_bug.cgi?id=46293
Signed-off-by: Sami Tolvanen <samitolvanen@google.com>
---
 scripts/module.lds.S | 18 +++++++++++++++++-
 1 file changed, 17 insertions(+), 1 deletion(-)

diff --git a/scripts/module.lds.S b/scripts/module.lds.S
index 168cd27e6122..93518579cf5d 100644
--- a/scripts/module.lds.S
+++ b/scripts/module.lds.S
@@ -3,10 +3,19 @@
  * Archs are free to supply their own linker scripts.  ld will
  * combine them automatically.
  */
+#include <asm/page.h>
+
+#ifdef CONFIG_CFI_CLANG
+# define ALIGN_CFI ALIGN(PAGE_SIZE)
+#else
+# define ALIGN_CFI
+#endif
+
 SECTIONS {
 	/DISCARD/ : {
 		*(.discard)
 		*(.discard.*)
+		*(.eh_frame)
 	}
 
 	__ksymtab		0 : { *(SORT(___ksymtab+*)) }
@@ -40,7 +49,14 @@ SECTIONS {
 		*(.rodata..L*)
 	}
 
-	.text : { *(.text .text.[0-9a-zA-Z_]*) }
+	/*
+	 * With CONFIG_CFI_CLANG, we assume __cfi_check is at the beginning
+	 * of the .text section, and is aligned to PAGE_SIZE.
+	 */
+	.text : ALIGN_CFI {
+		*(.text.__cfi_check)
+		*(.text .text.[0-9a-zA-Z_]* .text..L.cfi*)
+	}
 }
 
 /* bring in arch-specific sections */
-- 
2.31.0.291.g576ba9dcdaf-goog


^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH v2 05/17] workqueue: use WARN_ON_FUNCTION_MISMATCH
  2021-03-18 17:10 [PATCH v2 00/17] Add support for Clang CFI Sami Tolvanen
                   ` (3 preceding siblings ...)
  2021-03-18 17:10 ` [PATCH v2 04/17] module: ensure __cfi_check alignment Sami Tolvanen
@ 2021-03-18 17:10 ` Sami Tolvanen
  2021-03-18 18:50   ` Nick Desaulniers
  2021-03-18 17:11 ` [PATCH v2 06/17] kthread: " Sami Tolvanen
                   ` (11 subsequent siblings)
  16 siblings, 1 reply; 35+ messages in thread
From: Sami Tolvanen @ 2021-03-18 17:10 UTC (permalink / raw)
  To: Kees Cook
  Cc: Nathan Chancellor, Nick Desaulniers, Masahiro Yamada,
	Will Deacon, Jessica Yu, Arnd Bergmann, Tejun Heo,
	Paul E. McKenney, Christoph Hellwig, bpf, linux-hardening,
	linux-arch, linux-arm-kernel, linux-kbuild, linux-pci,
	linux-kernel, Sami Tolvanen

With CONFIG_CFI_CLANG, a callback function passed to
__queue_delayed_work from a module points to a jump table entry
defined in the module instead of the one used in the core kernel,
which breaks function address equality in this check:

  WARN_ON_ONCE(timer->function != delayed_work_timer_fn);

Use WARN_ON_FUNCTION_MISMATCH() instead to disable the warning
when CFI and modules are both enabled.

Signed-off-by: Sami Tolvanen <samitolvanen@google.com>
---
 kernel/workqueue.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index 0d150da252e8..03fe07d2f39f 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -1630,7 +1630,7 @@ static void __queue_delayed_work(int cpu, struct workqueue_struct *wq,
 	struct work_struct *work = &dwork->work;
 
 	WARN_ON_ONCE(!wq);
-	WARN_ON_ONCE(timer->function != delayed_work_timer_fn);
+	WARN_ON_FUNCTION_MISMATCH(timer->function, delayed_work_timer_fn);
 	WARN_ON_ONCE(timer_pending(timer));
 	WARN_ON_ONCE(!list_empty(&work->entry));
 
-- 
2.31.0.291.g576ba9dcdaf-goog


^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH v2 06/17] kthread: use WARN_ON_FUNCTION_MISMATCH
  2021-03-18 17:10 [PATCH v2 00/17] Add support for Clang CFI Sami Tolvanen
                   ` (4 preceding siblings ...)
  2021-03-18 17:10 ` [PATCH v2 05/17] workqueue: use WARN_ON_FUNCTION_MISMATCH Sami Tolvanen
@ 2021-03-18 17:11 ` Sami Tolvanen
  2021-03-18 17:11 ` [PATCH v2 07/17] kallsyms: strip ThinLTO hashes from static functions Sami Tolvanen
                   ` (10 subsequent siblings)
  16 siblings, 0 replies; 35+ messages in thread
From: Sami Tolvanen @ 2021-03-18 17:11 UTC (permalink / raw)
  To: Kees Cook
  Cc: Nathan Chancellor, Nick Desaulniers, Masahiro Yamada,
	Will Deacon, Jessica Yu, Arnd Bergmann, Tejun Heo,
	Paul E. McKenney, Christoph Hellwig, bpf, linux-hardening,
	linux-arch, linux-arm-kernel, linux-kbuild, linux-pci,
	linux-kernel, Sami Tolvanen

With CONFIG_CFI_CLANG, a callback function passed to
__kthread_queue_delayed_work from a module points to a jump table
entry defined in the module instead of the one used in the core
kernel, which breaks function address equality in this check:

  WARN_ON_ONCE(timer->function != ktead_delayed_work_timer_fn);

Use WARN_ON_FUNCTION_MISMATCH() instead to disable the warning
when CFI and modules are both enabled.

Signed-off-by: Sami Tolvanen <samitolvanen@google.com>
---
 kernel/kthread.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/kernel/kthread.c b/kernel/kthread.c
index 1578973c5740..a1972eba2917 100644
--- a/kernel/kthread.c
+++ b/kernel/kthread.c
@@ -963,7 +963,8 @@ static void __kthread_queue_delayed_work(struct kthread_worker *worker,
 	struct timer_list *timer = &dwork->timer;
 	struct kthread_work *work = &dwork->work;
 
-	WARN_ON_ONCE(timer->function != kthread_delayed_work_timer_fn);
+	WARN_ON_FUNCTION_MISMATCH(timer->function,
+				  kthread_delayed_work_timer_fn);
 
 	/*
 	 * If @delay is 0, queue @dwork->work immediately.  This is for
-- 
2.31.0.291.g576ba9dcdaf-goog


^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH v2 07/17] kallsyms: strip ThinLTO hashes from static functions
  2021-03-18 17:10 [PATCH v2 00/17] Add support for Clang CFI Sami Tolvanen
                   ` (5 preceding siblings ...)
  2021-03-18 17:11 ` [PATCH v2 06/17] kthread: " Sami Tolvanen
@ 2021-03-18 17:11 ` Sami Tolvanen
  2021-03-18 19:00   ` Nick Desaulniers
  2021-03-18 17:11 ` [PATCH v2 08/17] bpf: disable CFI in dispatcher functions Sami Tolvanen
                   ` (9 subsequent siblings)
  16 siblings, 1 reply; 35+ messages in thread
From: Sami Tolvanen @ 2021-03-18 17:11 UTC (permalink / raw)
  To: Kees Cook
  Cc: Nathan Chancellor, Nick Desaulniers, Masahiro Yamada,
	Will Deacon, Jessica Yu, Arnd Bergmann, Tejun Heo,
	Paul E. McKenney, Christoph Hellwig, bpf, linux-hardening,
	linux-arch, linux-arm-kernel, linux-kbuild, linux-pci,
	linux-kernel, Sami Tolvanen

With CONFIG_CFI_CLANG and ThinLTO, Clang appends a hash to the names
of all static functions not marked __used. This can break userspace
tools that don't expect the function name to change, so strip out the
hash from the output.

Suggested-by: Jack Pham <jackp@codeaurora.org>
Signed-off-by: Sami Tolvanen <samitolvanen@google.com>
Reviewed-by: Kees Cook <keescook@chromium.org>
---
 kernel/kallsyms.c | 54 ++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 49 insertions(+), 5 deletions(-)

diff --git a/kernel/kallsyms.c b/kernel/kallsyms.c
index 8043a90aa50e..17d3a704bafa 100644
--- a/kernel/kallsyms.c
+++ b/kernel/kallsyms.c
@@ -161,6 +161,26 @@ static unsigned long kallsyms_sym_address(int idx)
 	return kallsyms_relative_base - 1 - kallsyms_offsets[idx];
 }
 
+#if defined(CONFIG_CFI_CLANG) && defined(CONFIG_LTO_CLANG_THIN)
+/*
+ * LLVM appends a hash to static function names when ThinLTO and CFI are
+ * both enabled, which causes confusion and potentially breaks user space
+ * tools, so we will strip the postfix from expanded symbol names.
+ */
+static inline char *cleanup_symbol_name(char *s)
+{
+	char *res = NULL;
+
+	res = strrchr(s, '$');
+	if (res)
+		*res = '\0';
+
+	return res;
+}
+#else
+static inline char *cleanup_symbol_name(char *s) { return NULL; }
+#endif
+
 /* Lookup the address for this symbol. Returns 0 if not found. */
 unsigned long kallsyms_lookup_name(const char *name)
 {
@@ -173,6 +193,9 @@ unsigned long kallsyms_lookup_name(const char *name)
 
 		if (strcmp(namebuf, name) == 0)
 			return kallsyms_sym_address(i);
+
+		if (cleanup_symbol_name(namebuf) && strcmp(namebuf, name) == 0)
+			return kallsyms_sym_address(i);
 	}
 	return module_kallsyms_lookup_name(name);
 }
@@ -303,7 +326,9 @@ const char *kallsyms_lookup(unsigned long addr,
 				       namebuf, KSYM_NAME_LEN);
 		if (modname)
 			*modname = NULL;
-		return namebuf;
+
+		ret = namebuf;
+		goto found;
 	}
 
 	/* See if it's in a module or a BPF JITed image. */
@@ -316,11 +341,16 @@ const char *kallsyms_lookup(unsigned long addr,
 	if (!ret)
 		ret = ftrace_mod_address_lookup(addr, symbolsize,
 						offset, modname, namebuf);
+
+found:
+	cleanup_symbol_name(namebuf);
 	return ret;
 }
 
 int lookup_symbol_name(unsigned long addr, char *symname)
 {
+	int res;
+
 	symname[0] = '\0';
 	symname[KSYM_NAME_LEN - 1] = '\0';
 
@@ -331,15 +361,23 @@ int lookup_symbol_name(unsigned long addr, char *symname)
 		/* Grab name */
 		kallsyms_expand_symbol(get_symbol_offset(pos),
 				       symname, KSYM_NAME_LEN);
-		return 0;
+		goto found;
 	}
 	/* See if it's in a module. */
-	return lookup_module_symbol_name(addr, symname);
+	res = lookup_module_symbol_name(addr, symname);
+	if (res)
+		return res;
+
+found:
+	cleanup_symbol_name(symname);
+	return 0;
 }
 
 int lookup_symbol_attrs(unsigned long addr, unsigned long *size,
 			unsigned long *offset, char *modname, char *name)
 {
+	int res;
+
 	name[0] = '\0';
 	name[KSYM_NAME_LEN - 1] = '\0';
 
@@ -351,10 +389,16 @@ int lookup_symbol_attrs(unsigned long addr, unsigned long *size,
 		kallsyms_expand_symbol(get_symbol_offset(pos),
 				       name, KSYM_NAME_LEN);
 		modname[0] = '\0';
-		return 0;
+		goto found;
 	}
 	/* See if it's in a module. */
-	return lookup_module_symbol_attrs(addr, size, offset, modname, name);
+	res = lookup_module_symbol_attrs(addr, size, offset, modname, name);
+	if (res)
+		return res;
+
+found:
+	cleanup_symbol_name(name);
+	return 0;
 }
 
 /* Look up a kernel symbol and return it in a text buffer. */
-- 
2.31.0.291.g576ba9dcdaf-goog


^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH v2 08/17] bpf: disable CFI in dispatcher functions
  2021-03-18 17:10 [PATCH v2 00/17] Add support for Clang CFI Sami Tolvanen
                   ` (6 preceding siblings ...)
  2021-03-18 17:11 ` [PATCH v2 07/17] kallsyms: strip ThinLTO hashes from static functions Sami Tolvanen
@ 2021-03-18 17:11 ` Sami Tolvanen
  2021-03-18 17:11 ` [PATCH v2 09/17] lib/list_sort: fix function type mismatches Sami Tolvanen
                   ` (8 subsequent siblings)
  16 siblings, 0 replies; 35+ messages in thread
From: Sami Tolvanen @ 2021-03-18 17:11 UTC (permalink / raw)
  To: Kees Cook
  Cc: Nathan Chancellor, Nick Desaulniers, Masahiro Yamada,
	Will Deacon, Jessica Yu, Arnd Bergmann, Tejun Heo,
	Paul E. McKenney, Christoph Hellwig, bpf, linux-hardening,
	linux-arch, linux-arm-kernel, linux-kbuild, linux-pci,
	linux-kernel, Sami Tolvanen

BPF dispatcher functions are patched at runtime to perform direct
instead of indirect calls. Disable CFI for the dispatcher functions to
avoid conflicts.

Signed-off-by: Sami Tolvanen <samitolvanen@google.com>
Reviewed-by: Kees Cook <keescook@chromium.org>
---
 include/linux/bpf.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index cccaef1088ea..9acdca574527 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -635,7 +635,7 @@ struct bpf_dispatcher {
 	struct bpf_ksym ksym;
 };
 
-static __always_inline unsigned int bpf_dispatcher_nop_func(
+static __always_inline __nocfi unsigned int bpf_dispatcher_nop_func(
 	const void *ctx,
 	const struct bpf_insn *insnsi,
 	unsigned int (*bpf_func)(const void *,
@@ -663,7 +663,7 @@ void bpf_trampoline_put(struct bpf_trampoline *tr);
 }
 
 #define DEFINE_BPF_DISPATCHER(name)					\
-	noinline unsigned int bpf_dispatcher_##name##_func(		\
+	noinline __nocfi unsigned int bpf_dispatcher_##name##_func(	\
 		const void *ctx,					\
 		const struct bpf_insn *insnsi,				\
 		unsigned int (*bpf_func)(const void *,			\
-- 
2.31.0.291.g576ba9dcdaf-goog


^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH v2 09/17] lib/list_sort: fix function type mismatches
  2021-03-18 17:10 [PATCH v2 00/17] Add support for Clang CFI Sami Tolvanen
                   ` (7 preceding siblings ...)
  2021-03-18 17:11 ` [PATCH v2 08/17] bpf: disable CFI in dispatcher functions Sami Tolvanen
@ 2021-03-18 17:11 ` Sami Tolvanen
  2021-03-18 18:31   ` Nick Desaulniers
  2021-03-18 17:11 ` [PATCH v2 10/17] lkdtm: use __va_function Sami Tolvanen
                   ` (7 subsequent siblings)
  16 siblings, 1 reply; 35+ messages in thread
From: Sami Tolvanen @ 2021-03-18 17:11 UTC (permalink / raw)
  To: Kees Cook
  Cc: Nathan Chancellor, Nick Desaulniers, Masahiro Yamada,
	Will Deacon, Jessica Yu, Arnd Bergmann, Tejun Heo,
	Paul E. McKenney, Christoph Hellwig, bpf, linux-hardening,
	linux-arch, linux-arm-kernel, linux-kbuild, linux-pci,
	linux-kernel, Sami Tolvanen

Casting the comparison function to a different type trips indirect
call Control-Flow Integrity (CFI) checking. Remove the additional
consts from cmp_func, and the now unneeded casts.

Fixes: 043b3f7b6388 ("lib/list_sort: simplify and remove MAX_LIST_LENGTH_BITS")
Signed-off-by: Sami Tolvanen <samitolvanen@google.com>
Reviewed-by: Kees Cook <keescook@chromium.org>
---
 lib/list_sort.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/lib/list_sort.c b/lib/list_sort.c
index 52f0c258c895..b14accf4ef83 100644
--- a/lib/list_sort.c
+++ b/lib/list_sort.c
@@ -8,7 +8,7 @@
 #include <linux/list.h>
 
 typedef int __attribute__((nonnull(2,3))) (*cmp_func)(void *,
-		struct list_head const *, struct list_head const *);
+		struct list_head *, struct list_head *);
 
 /*
  * Returns a list organized in an intermediate format suited
@@ -227,7 +227,7 @@ void list_sort(void *priv, struct list_head *head,
 		if (likely(bits)) {
 			struct list_head *a = *tail, *b = a->prev;
 
-			a = merge(priv, (cmp_func)cmp, b, a);
+			a = merge(priv, cmp, b, a);
 			/* Install the merged result in place of the inputs */
 			a->prev = b->prev;
 			*tail = a;
@@ -249,10 +249,10 @@ void list_sort(void *priv, struct list_head *head,
 
 		if (!next)
 			break;
-		list = merge(priv, (cmp_func)cmp, pending, list);
+		list = merge(priv, cmp, pending, list);
 		pending = next;
 	}
 	/* The final merge, rebuilding prev links */
-	merge_final(priv, (cmp_func)cmp, head, pending, list);
+	merge_final(priv, cmp, head, pending, list);
 }
 EXPORT_SYMBOL(list_sort);
-- 
2.31.0.291.g576ba9dcdaf-goog


^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH v2 10/17] lkdtm: use __va_function
  2021-03-18 17:10 [PATCH v2 00/17] Add support for Clang CFI Sami Tolvanen
                   ` (8 preceding siblings ...)
  2021-03-18 17:11 ` [PATCH v2 09/17] lib/list_sort: fix function type mismatches Sami Tolvanen
@ 2021-03-18 17:11 ` Sami Tolvanen
  2021-03-18 18:43   ` Nick Desaulniers
  2021-03-18 17:11 ` [PATCH v2 11/17] psci: use __pa_function for cpu_resume Sami Tolvanen
                   ` (6 subsequent siblings)
  16 siblings, 1 reply; 35+ messages in thread
From: Sami Tolvanen @ 2021-03-18 17:11 UTC (permalink / raw)
  To: Kees Cook
  Cc: Nathan Chancellor, Nick Desaulniers, Masahiro Yamada,
	Will Deacon, Jessica Yu, Arnd Bergmann, Tejun Heo,
	Paul E. McKenney, Christoph Hellwig, bpf, linux-hardening,
	linux-arch, linux-arm-kernel, linux-kbuild, linux-pci,
	linux-kernel, Sami Tolvanen

To ensure we take the actual address of a function in kernel text, use
__va_function. Otherwise, with CONFIG_CFI_CLANG, the compiler replaces
the address with a pointer to the CFI jump table, which is actually in
the module when compiled with CONFIG_LKDTM=m.

Signed-off-by: Sami Tolvanen <samitolvanen@google.com>
Acked-by: Kees Cook <keescook@chromium.org>
---
 drivers/misc/lkdtm/usercopy.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/misc/lkdtm/usercopy.c b/drivers/misc/lkdtm/usercopy.c
index 109e8d4302c1..d173d6175c87 100644
--- a/drivers/misc/lkdtm/usercopy.c
+++ b/drivers/misc/lkdtm/usercopy.c
@@ -314,7 +314,7 @@ void lkdtm_USERCOPY_KERNEL(void)
 
 	pr_info("attempting bad copy_to_user from kernel text: %px\n",
 		vm_mmap);
-	if (copy_to_user((void __user *)user_addr, vm_mmap,
+	if (copy_to_user((void __user *)user_addr, __va_function(vm_mmap),
 			 unconst + PAGE_SIZE)) {
 		pr_warn("copy_to_user failed, but lacked Oops\n");
 		goto free_user;
-- 
2.31.0.291.g576ba9dcdaf-goog


^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH v2 11/17] psci: use __pa_function for cpu_resume
  2021-03-18 17:10 [PATCH v2 00/17] Add support for Clang CFI Sami Tolvanen
                   ` (9 preceding siblings ...)
  2021-03-18 17:11 ` [PATCH v2 10/17] lkdtm: use __va_function Sami Tolvanen
@ 2021-03-18 17:11 ` Sami Tolvanen
  2021-03-18 17:11 ` [PATCH v2 12/17] arm64: implement __va_function Sami Tolvanen
                   ` (5 subsequent siblings)
  16 siblings, 0 replies; 35+ messages in thread
From: Sami Tolvanen @ 2021-03-18 17:11 UTC (permalink / raw)
  To: Kees Cook
  Cc: Nathan Chancellor, Nick Desaulniers, Masahiro Yamada,
	Will Deacon, Jessica Yu, Arnd Bergmann, Tejun Heo,
	Paul E. McKenney, Christoph Hellwig, bpf, linux-hardening,
	linux-arch, linux-arm-kernel, linux-kbuild, linux-pci,
	linux-kernel, Sami Tolvanen

With CONFIG_CFI_CLANG, the compiler replaces function pointers with
jump table addresses, which results in __pa_symbol returning the
physical address of the jump table entry. As the jump table contains
an immediate jump to an EL1 virtual address, this typically won't
work as intended. Use __pa_function instead to get the address to
cpu_resume.

Signed-off-by: Sami Tolvanen <samitolvanen@google.com>
Reviewed-by: Kees Cook <keescook@chromium.org>
---
 drivers/firmware/psci/psci.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/firmware/psci/psci.c b/drivers/firmware/psci/psci.c
index f5fc429cae3f..facd3cce3244 100644
--- a/drivers/firmware/psci/psci.c
+++ b/drivers/firmware/psci/psci.c
@@ -326,7 +326,7 @@ static int psci_suspend_finisher(unsigned long state)
 {
 	u32 power_state = state;
 
-	return psci_ops.cpu_suspend(power_state, __pa_symbol(cpu_resume));
+	return psci_ops.cpu_suspend(power_state, __pa_function(cpu_resume));
 }
 
 int psci_cpu_suspend_enter(u32 state)
@@ -345,7 +345,7 @@ int psci_cpu_suspend_enter(u32 state)
 static int psci_system_suspend(unsigned long unused)
 {
 	return invoke_psci_fn(PSCI_FN_NATIVE(1_0, SYSTEM_SUSPEND),
-			      __pa_symbol(cpu_resume), 0, 0);
+			      __pa_function(cpu_resume), 0, 0);
 }
 
 static int psci_system_suspend_enter(suspend_state_t state)
-- 
2.31.0.291.g576ba9dcdaf-goog


^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH v2 12/17] arm64: implement __va_function
  2021-03-18 17:10 [PATCH v2 00/17] Add support for Clang CFI Sami Tolvanen
                   ` (10 preceding siblings ...)
  2021-03-18 17:11 ` [PATCH v2 11/17] psci: use __pa_function for cpu_resume Sami Tolvanen
@ 2021-03-18 17:11 ` Sami Tolvanen
  2021-03-18 17:11 ` [PATCH v2 13/17] arm64: use __pa_function Sami Tolvanen
                   ` (4 subsequent siblings)
  16 siblings, 0 replies; 35+ messages in thread
From: Sami Tolvanen @ 2021-03-18 17:11 UTC (permalink / raw)
  To: Kees Cook
  Cc: Nathan Chancellor, Nick Desaulniers, Masahiro Yamada,
	Will Deacon, Jessica Yu, Arnd Bergmann, Tejun Heo,
	Paul E. McKenney, Christoph Hellwig, bpf, linux-hardening,
	linux-arch, linux-arm-kernel, linux-kbuild, linux-pci,
	linux-kernel, Sami Tolvanen

With CONFIG_CFI_CLANG, the compiler replaces function addresses in
instrumented C code with jump table addresses. This change implements
the __va_function() macro, which returns the actual function address
instead.

Signed-off-by: Sami Tolvanen <samitolvanen@google.com>
Reviewed-by: Kees Cook <keescook@chromium.org>
---
 arch/arm64/include/asm/memory.h | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/arch/arm64/include/asm/memory.h b/arch/arm64/include/asm/memory.h
index 0aabc3be9a75..9a4887808681 100644
--- a/arch/arm64/include/asm/memory.h
+++ b/arch/arm64/include/asm/memory.h
@@ -321,6 +321,21 @@ static inline void *phys_to_virt(phys_addr_t x)
 #define virt_to_pfn(x)		__phys_to_pfn(__virt_to_phys((unsigned long)(x)))
 #define sym_to_pfn(x)		__phys_to_pfn(__pa_symbol(x))
 
+#ifdef CONFIG_CFI_CLANG
+/*
+ * With CONFIG_CFI_CLANG, the compiler replaces function address
+ * references with the address of the function's CFI jump table
+ * entry. The __va_function macro always returns the address of the
+ * actual function instead.
+ */
+#define __va_function(x) ({						\
+	void *addr;							\
+	asm("adrp %0, " __stringify(x) "\n\t"				\
+	    "add  %0, %0, :lo12:" __stringify(x) : "=r" (addr));	\
+	addr;								\
+})
+#endif
+
 /*
  *  virt_to_page(x)	convert a _valid_ virtual address to struct page *
  *  virt_addr_valid(x)	indicates whether a virtual address is valid
-- 
2.31.0.291.g576ba9dcdaf-goog


^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH v2 13/17] arm64: use __pa_function
  2021-03-18 17:10 [PATCH v2 00/17] Add support for Clang CFI Sami Tolvanen
                   ` (11 preceding siblings ...)
  2021-03-18 17:11 ` [PATCH v2 12/17] arm64: implement __va_function Sami Tolvanen
@ 2021-03-18 17:11 ` Sami Tolvanen
  2021-03-18 17:11 ` [PATCH v2 14/17] arm64: add __nocfi to functions that jump to a physical address Sami Tolvanen
                   ` (3 subsequent siblings)
  16 siblings, 0 replies; 35+ messages in thread
From: Sami Tolvanen @ 2021-03-18 17:11 UTC (permalink / raw)
  To: Kees Cook
  Cc: Nathan Chancellor, Nick Desaulniers, Masahiro Yamada,
	Will Deacon, Jessica Yu, Arnd Bergmann, Tejun Heo,
	Paul E. McKenney, Christoph Hellwig, bpf, linux-hardening,
	linux-arch, linux-arm-kernel, linux-kbuild, linux-pci,
	linux-kernel, Sami Tolvanen

With CONFIG_CFI_CLANG, the compiler replaces function address
references with the address of the function's CFI jump table
entry. This means that __pa_symbol(function) returns the physical
address of the jump table entry, which can lead to address space
confusion as the jump table points to the function's virtual
address. Therefore, use the __pa_function() macro to ensure we are
always taking the address of the actual function instead.

Signed-off-by: Sami Tolvanen <samitolvanen@google.com>
Reviewed-by: Kees Cook <keescook@chromium.org>
---
 arch/arm64/include/asm/mmu_context.h      | 2 +-
 arch/arm64/kernel/acpi_parking_protocol.c | 2 +-
 arch/arm64/kernel/cpu-reset.h             | 2 +-
 arch/arm64/kernel/cpufeature.c            | 2 +-
 arch/arm64/kernel/psci.c                  | 3 ++-
 arch/arm64/kernel/smp_spin_table.c        | 2 +-
 6 files changed, 7 insertions(+), 6 deletions(-)

diff --git a/arch/arm64/include/asm/mmu_context.h b/arch/arm64/include/asm/mmu_context.h
index bd02e99b1a4c..16cc9a694bb2 100644
--- a/arch/arm64/include/asm/mmu_context.h
+++ b/arch/arm64/include/asm/mmu_context.h
@@ -140,7 +140,7 @@ static inline void cpu_replace_ttbr1(pgd_t *pgdp)
 		ttbr1 |= TTBR_CNP_BIT;
 	}
 
-	replace_phys = (void *)__pa_symbol(idmap_cpu_replace_ttbr1);
+	replace_phys = (void *)__pa_function(idmap_cpu_replace_ttbr1);
 
 	cpu_install_idmap();
 	replace_phys(ttbr1);
diff --git a/arch/arm64/kernel/acpi_parking_protocol.c b/arch/arm64/kernel/acpi_parking_protocol.c
index e7c941d8340d..e7f3af6043c5 100644
--- a/arch/arm64/kernel/acpi_parking_protocol.c
+++ b/arch/arm64/kernel/acpi_parking_protocol.c
@@ -99,7 +99,7 @@ static int acpi_parking_protocol_cpu_boot(unsigned int cpu)
 	 * that read this address need to convert this address to the
 	 * Boot-Loader's endianness before jumping.
 	 */
-	writeq_relaxed(__pa_symbol(secondary_entry), &mailbox->entry_point);
+	writeq_relaxed(__pa_function(secondary_entry), &mailbox->entry_point);
 	writel_relaxed(cpu_entry->gic_cpu_id, &mailbox->cpu_id);
 
 	arch_send_wakeup_ipi_mask(cpumask_of(cpu));
diff --git a/arch/arm64/kernel/cpu-reset.h b/arch/arm64/kernel/cpu-reset.h
index ed50e9587ad8..dfba8cf921e5 100644
--- a/arch/arm64/kernel/cpu-reset.h
+++ b/arch/arm64/kernel/cpu-reset.h
@@ -22,7 +22,7 @@ static inline void __noreturn cpu_soft_restart(unsigned long entry,
 
 	unsigned long el2_switch = !is_kernel_in_hyp_mode() &&
 		is_hyp_mode_available();
-	restart = (void *)__pa_symbol(__cpu_soft_restart);
+	restart = (void *)__pa_function(__cpu_soft_restart);
 
 	cpu_install_idmap();
 	restart(el2_switch, entry, arg0, arg1, arg2);
diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index 066030717a4c..7ec1c2ccdc0b 100644
--- a/arch/arm64/kernel/cpufeature.c
+++ b/arch/arm64/kernel/cpufeature.c
@@ -1460,7 +1460,7 @@ kpti_install_ng_mappings(const struct arm64_cpu_capabilities *__unused)
 	if (arm64_use_ng_mappings)
 		return;
 
-	remap_fn = (void *)__pa_symbol(idmap_kpti_install_ng_mappings);
+	remap_fn = (void *)__pa_function(idmap_kpti_install_ng_mappings);
 
 	cpu_install_idmap();
 	remap_fn(cpu, num_online_cpus(), __pa_symbol(swapper_pg_dir));
diff --git a/arch/arm64/kernel/psci.c b/arch/arm64/kernel/psci.c
index 62d2bda7adb8..bfb1a6f8282d 100644
--- a/arch/arm64/kernel/psci.c
+++ b/arch/arm64/kernel/psci.c
@@ -38,7 +38,8 @@ static int __init cpu_psci_cpu_prepare(unsigned int cpu)
 
 static int cpu_psci_cpu_boot(unsigned int cpu)
 {
-	int err = psci_ops.cpu_on(cpu_logical_map(cpu), __pa_symbol(secondary_entry));
+	int err = psci_ops.cpu_on(cpu_logical_map(cpu),
+				  __pa_function(secondary_entry));
 	if (err)
 		pr_err("failed to boot CPU%d (%d)\n", cpu, err);
 
diff --git a/arch/arm64/kernel/smp_spin_table.c b/arch/arm64/kernel/smp_spin_table.c
index 056772c26098..a80ff9092e86 100644
--- a/arch/arm64/kernel/smp_spin_table.c
+++ b/arch/arm64/kernel/smp_spin_table.c
@@ -88,7 +88,7 @@ static int smp_spin_table_cpu_prepare(unsigned int cpu)
 	 * boot-loader's endianness before jumping. This is mandated by
 	 * the boot protocol.
 	 */
-	writeq_relaxed(__pa_symbol(secondary_holding_pen), release_addr);
+	writeq_relaxed(__pa_function(secondary_holding_pen), release_addr);
 	__flush_dcache_area((__force void *)release_addr,
 			    sizeof(*release_addr));
 
-- 
2.31.0.291.g576ba9dcdaf-goog


^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH v2 14/17] arm64: add __nocfi to functions that jump to a physical address
  2021-03-18 17:10 [PATCH v2 00/17] Add support for Clang CFI Sami Tolvanen
                   ` (12 preceding siblings ...)
  2021-03-18 17:11 ` [PATCH v2 13/17] arm64: use __pa_function Sami Tolvanen
@ 2021-03-18 17:11 ` Sami Tolvanen
  2021-03-18 17:11 ` [PATCH v2 15/17] arm64: add __nocfi to __apply_alternatives Sami Tolvanen
                   ` (2 subsequent siblings)
  16 siblings, 0 replies; 35+ messages in thread
From: Sami Tolvanen @ 2021-03-18 17:11 UTC (permalink / raw)
  To: Kees Cook
  Cc: Nathan Chancellor, Nick Desaulniers, Masahiro Yamada,
	Will Deacon, Jessica Yu, Arnd Bergmann, Tejun Heo,
	Paul E. McKenney, Christoph Hellwig, bpf, linux-hardening,
	linux-arch, linux-arm-kernel, linux-kbuild, linux-pci,
	linux-kernel, Sami Tolvanen

Disable CFI checking for functions that switch to linear mapping and
make an indirect call to a physical address, since the compiler only
understands virtual addresses and the CFI check for such indirect calls
would always fail.

Signed-off-by: Sami Tolvanen <samitolvanen@google.com>
Reviewed-by: Kees Cook <keescook@chromium.org>
---
 arch/arm64/include/asm/mmu_context.h | 2 +-
 arch/arm64/kernel/cpu-reset.h        | 8 ++++----
 arch/arm64/kernel/cpufeature.c       | 2 +-
 3 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/arch/arm64/include/asm/mmu_context.h b/arch/arm64/include/asm/mmu_context.h
index 16cc9a694bb2..270ba8761a23 100644
--- a/arch/arm64/include/asm/mmu_context.h
+++ b/arch/arm64/include/asm/mmu_context.h
@@ -119,7 +119,7 @@ static inline void cpu_install_idmap(void)
  * Atomically replaces the active TTBR1_EL1 PGD with a new VA-compatible PGD,
  * avoiding the possibility of conflicting TLB entries being allocated.
  */
-static inline void cpu_replace_ttbr1(pgd_t *pgdp)
+static inline void __nocfi cpu_replace_ttbr1(pgd_t *pgdp)
 {
 	typedef void (ttbr_replace_func)(phys_addr_t);
 	extern ttbr_replace_func idmap_cpu_replace_ttbr1;
diff --git a/arch/arm64/kernel/cpu-reset.h b/arch/arm64/kernel/cpu-reset.h
index dfba8cf921e5..a05bda363272 100644
--- a/arch/arm64/kernel/cpu-reset.h
+++ b/arch/arm64/kernel/cpu-reset.h
@@ -13,10 +13,10 @@
 void __cpu_soft_restart(unsigned long el2_switch, unsigned long entry,
 	unsigned long arg0, unsigned long arg1, unsigned long arg2);
 
-static inline void __noreturn cpu_soft_restart(unsigned long entry,
-					       unsigned long arg0,
-					       unsigned long arg1,
-					       unsigned long arg2)
+static inline void __noreturn __nocfi cpu_soft_restart(unsigned long entry,
+						       unsigned long arg0,
+						       unsigned long arg1,
+						       unsigned long arg2)
 {
 	typeof(__cpu_soft_restart) *restart;
 
diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index 7ec1c2ccdc0b..473212ff4d70 100644
--- a/arch/arm64/kernel/cpufeature.c
+++ b/arch/arm64/kernel/cpufeature.c
@@ -1443,7 +1443,7 @@ static bool unmap_kernel_at_el0(const struct arm64_cpu_capabilities *entry,
 }
 
 #ifdef CONFIG_UNMAP_KERNEL_AT_EL0
-static void
+static void __nocfi
 kpti_install_ng_mappings(const struct arm64_cpu_capabilities *__unused)
 {
 	typedef void (kpti_remap_fn)(int, int, phys_addr_t);
-- 
2.31.0.291.g576ba9dcdaf-goog


^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH v2 15/17] arm64: add __nocfi to __apply_alternatives
  2021-03-18 17:10 [PATCH v2 00/17] Add support for Clang CFI Sami Tolvanen
                   ` (13 preceding siblings ...)
  2021-03-18 17:11 ` [PATCH v2 14/17] arm64: add __nocfi to functions that jump to a physical address Sami Tolvanen
@ 2021-03-18 17:11 ` Sami Tolvanen
  2021-03-18 17:11 ` [PATCH v2 16/17] KVM: arm64: Disable CFI for nVHE Sami Tolvanen
  2021-03-18 17:11 ` [PATCH v2 17/17] arm64: allow CONFIG_CFI_CLANG to be selected Sami Tolvanen
  16 siblings, 0 replies; 35+ messages in thread
From: Sami Tolvanen @ 2021-03-18 17:11 UTC (permalink / raw)
  To: Kees Cook
  Cc: Nathan Chancellor, Nick Desaulniers, Masahiro Yamada,
	Will Deacon, Jessica Yu, Arnd Bergmann, Tejun Heo,
	Paul E. McKenney, Christoph Hellwig, bpf, linux-hardening,
	linux-arch, linux-arm-kernel, linux-kbuild, linux-pci,
	linux-kernel, Sami Tolvanen

__apply_alternatives makes indirect calls to functions whose address
is taken in assembly code using the alternative_cb macro. With
non-canonical CFI, the compiler won't replace these function
references with the jump table addresses, which trips CFI. Disable CFI
checking in the function to work around the issue.

Signed-off-by: Sami Tolvanen <samitolvanen@google.com>
Reviewed-by: Kees Cook <keescook@chromium.org>
---
 arch/arm64/kernel/alternative.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/arch/arm64/kernel/alternative.c b/arch/arm64/kernel/alternative.c
index 1184c44ea2c7..abc84636af07 100644
--- a/arch/arm64/kernel/alternative.c
+++ b/arch/arm64/kernel/alternative.c
@@ -133,8 +133,8 @@ static void clean_dcache_range_nopatch(u64 start, u64 end)
 	} while (cur += d_size, cur < end);
 }
 
-static void __apply_alternatives(void *alt_region,  bool is_module,
-				 unsigned long *feature_mask)
+static void __nocfi __apply_alternatives(void *alt_region,  bool is_module,
+					 unsigned long *feature_mask)
 {
 	struct alt_instr *alt;
 	struct alt_region *region = alt_region;
-- 
2.31.0.291.g576ba9dcdaf-goog


^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH v2 16/17] KVM: arm64: Disable CFI for nVHE
  2021-03-18 17:10 [PATCH v2 00/17] Add support for Clang CFI Sami Tolvanen
                   ` (14 preceding siblings ...)
  2021-03-18 17:11 ` [PATCH v2 15/17] arm64: add __nocfi to __apply_alternatives Sami Tolvanen
@ 2021-03-18 17:11 ` Sami Tolvanen
  2021-03-18 17:11 ` [PATCH v2 17/17] arm64: allow CONFIG_CFI_CLANG to be selected Sami Tolvanen
  16 siblings, 0 replies; 35+ messages in thread
From: Sami Tolvanen @ 2021-03-18 17:11 UTC (permalink / raw)
  To: Kees Cook
  Cc: Nathan Chancellor, Nick Desaulniers, Masahiro Yamada,
	Will Deacon, Jessica Yu, Arnd Bergmann, Tejun Heo,
	Paul E. McKenney, Christoph Hellwig, bpf, linux-hardening,
	linux-arch, linux-arm-kernel, linux-kbuild, linux-pci,
	linux-kernel, Sami Tolvanen

Disable CFI for the nVHE code to avoid address space confusion.

Signed-off-by: Sami Tolvanen <samitolvanen@google.com>
Reviewed-by: Kees Cook <keescook@chromium.org>
---
 arch/arm64/kvm/hyp/nvhe/Makefile | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/arch/arm64/kvm/hyp/nvhe/Makefile b/arch/arm64/kvm/hyp/nvhe/Makefile
index a6707df4f6c0..fb24a0f022ad 100644
--- a/arch/arm64/kvm/hyp/nvhe/Makefile
+++ b/arch/arm64/kvm/hyp/nvhe/Makefile
@@ -75,9 +75,9 @@ quiet_cmd_hyprel = HYPREL  $@
 quiet_cmd_hypcopy = HYPCOPY $@
       cmd_hypcopy = $(OBJCOPY) --prefix-symbols=__kvm_nvhe_ $< $@
 
-# Remove ftrace and Shadow Call Stack CFLAGS.
-# This is equivalent to the 'notrace' and '__noscs' annotations.
-KBUILD_CFLAGS := $(filter-out $(CC_FLAGS_FTRACE) $(CC_FLAGS_SCS), $(KBUILD_CFLAGS))
+# Remove ftrace, Shadow Call Stack, and CFI CFLAGS.
+# This is equivalent to the 'notrace', '__noscs', and '__nocfi' annotations.
+KBUILD_CFLAGS := $(filter-out $(CC_FLAGS_FTRACE) $(CC_FLAGS_SCS) $(CC_FLAGS_CFI), $(KBUILD_CFLAGS))
 
 # KVM nVHE code is run at a different exception code with a different map, so
 # compiler instrumentation that inserts callbacks or checks into the code may
-- 
2.31.0.291.g576ba9dcdaf-goog


^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH v2 17/17] arm64: allow CONFIG_CFI_CLANG to be selected
  2021-03-18 17:10 [PATCH v2 00/17] Add support for Clang CFI Sami Tolvanen
                   ` (15 preceding siblings ...)
  2021-03-18 17:11 ` [PATCH v2 16/17] KVM: arm64: Disable CFI for nVHE Sami Tolvanen
@ 2021-03-18 17:11 ` Sami Tolvanen
  16 siblings, 0 replies; 35+ messages in thread
From: Sami Tolvanen @ 2021-03-18 17:11 UTC (permalink / raw)
  To: Kees Cook
  Cc: Nathan Chancellor, Nick Desaulniers, Masahiro Yamada,
	Will Deacon, Jessica Yu, Arnd Bergmann, Tejun Heo,
	Paul E. McKenney, Christoph Hellwig, bpf, linux-hardening,
	linux-arch, linux-arm-kernel, linux-kbuild, linux-pci,
	linux-kernel, Sami Tolvanen

Select ARCH_SUPPORTS_CFI_CLANG to allow CFI to be enabled.

Signed-off-by: Sami Tolvanen <samitolvanen@google.com>
Reviewed-by: Kees Cook <keescook@chromium.org>
---
 arch/arm64/Kconfig | 1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 5656e7aacd69..2eefdbc3e3c9 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -75,6 +75,7 @@ config ARM64
 	select ARCH_SUPPORTS_SHADOW_CALL_STACK if CC_HAVE_SHADOW_CALL_STACK
 	select ARCH_SUPPORTS_LTO_CLANG if CPU_LITTLE_ENDIAN
 	select ARCH_SUPPORTS_LTO_CLANG_THIN
+	select ARCH_SUPPORTS_CFI_CLANG
 	select ARCH_SUPPORTS_ATOMIC_RMW
 	select ARCH_SUPPORTS_INT128 if CC_HAS_INT128 && (GCC_VERSION >= 50000 || CC_IS_CLANG)
 	select ARCH_SUPPORTS_NUMA_BALANCING
-- 
2.31.0.291.g576ba9dcdaf-goog


^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v2 02/17] cfi: add __cficanonical
  2021-03-18 17:10 ` [PATCH v2 02/17] cfi: add __cficanonical Sami Tolvanen
@ 2021-03-18 17:49   ` Nick Desaulniers
  0 siblings, 0 replies; 35+ messages in thread
From: Nick Desaulniers @ 2021-03-18 17:49 UTC (permalink / raw)
  To: Sami Tolvanen
  Cc: Kees Cook, Nathan Chancellor, Masahiro Yamada, Will Deacon,
	Jessica Yu, Arnd Bergmann, Tejun Heo, Paul E. McKenney,
	Christoph Hellwig, bpf, linux-hardening, linux-arch, Linux ARM,
	Linux Kbuild mailing list, PCI, LKML

On Thu, Mar 18, 2021 at 10:11 AM Sami Tolvanen <samitolvanen@google.com> wrote:
>
> With CONFIG_CFI_CLANG, the compiler replaces a function address taken
> in C code with the address of a local jump table entry, which passes
> runtime indirect call checks. However, the compiler won't replace
> addresses taken in assembly code, which will result in a CFI failure
> if we later jump to such an address in instrumented C code. The code
> generated for the non-canonical jump table looks this:
>
>   <noncanonical.cfi_jt>: /* In C, &noncanonical points here */
>         jmp noncanonical
>   ...
>   <noncanonical>:        /* function body */
>         ...
>
> This change adds the __cficanonical attribute, which tells the
> compiler to use a canonical jump table for the function instead. This
> means the compiler will rename the actual function to <function>.cfi
> and points the original symbol to the jump table entry instead:
>
>   <canonical>:           /* jump table entry */
>         jmp canonical.cfi
>   ...
>   <canonical.cfi>:       /* function body */
>         ...
>
> As a result, the address taken in assembly, or other non-instrumented
> code always points to the jump table and therefore, can be used for
> indirect calls in instrumented code without tripping CFI checks.
>
> Signed-off-by: Sami Tolvanen <samitolvanen@google.com>
> Reviewed-by: Kees Cook <keescook@chromium.org>
> Acked-by: Bjorn Helgaas <bhelgaas@google.com>   # pci.h

Irrelevant to this series, but I checked when the FN attr was first
available in clang; clang-10. (That's the minimum supported version of
clang for the kernel, and this series depends on LTO which depends on
clang-12, so no additional guards are necessary).

Reviewed-by: Nick Desaulniers <ndesaulniers@google.com>

> ---
>  include/linux/compiler-clang.h | 1 +
>  include/linux/compiler_types.h | 4 ++++
>  include/linux/init.h           | 4 ++--
>  include/linux/pci.h            | 4 ++--
>  4 files changed, 9 insertions(+), 4 deletions(-)
>
> diff --git a/include/linux/compiler-clang.h b/include/linux/compiler-clang.h
> index 6de9d0c9377e..adbe76b203e2 100644
> --- a/include/linux/compiler-clang.h
> +++ b/include/linux/compiler-clang.h
> @@ -63,3 +63,4 @@
>  #endif
>
>  #define __nocfi                __attribute__((__no_sanitize__("cfi")))
> +#define __cficanonical __attribute__((__cfi_canonical_jump_table__))
> diff --git a/include/linux/compiler_types.h b/include/linux/compiler_types.h
> index 796935a37e37..d29bda7f6ebd 100644
> --- a/include/linux/compiler_types.h
> +++ b/include/linux/compiler_types.h
> @@ -246,6 +246,10 @@ struct ftrace_likely_data {
>  # define __nocfi
>  #endif
>
> +#ifndef __cficanonical
> +# define __cficanonical
> +#endif
> +
>  #ifndef asm_volatile_goto
>  #define asm_volatile_goto(x...) asm goto(x)
>  #endif
> diff --git a/include/linux/init.h b/include/linux/init.h
> index b3ea15348fbd..045ad1650ed1 100644
> --- a/include/linux/init.h
> +++ b/include/linux/init.h
> @@ -220,8 +220,8 @@ extern bool initcall_debug;
>         __initcall_name(initstub, __iid, id)
>
>  #define __define_initcall_stub(__stub, fn)                     \
> -       int __init __stub(void);                                \
> -       int __init __stub(void)                                 \
> +       int __init __cficanonical __stub(void);                 \
> +       int __init __cficanonical __stub(void)                  \
>         {                                                       \
>                 return fn();                                    \
>         }                                                       \
> diff --git a/include/linux/pci.h b/include/linux/pci.h
> index 86c799c97b77..39684b72db91 100644
> --- a/include/linux/pci.h
> +++ b/include/linux/pci.h
> @@ -1944,8 +1944,8 @@ enum pci_fixup_pass {
>  #ifdef CONFIG_LTO_CLANG
>  #define __DECLARE_PCI_FIXUP_SECTION(sec, name, vendor, device, class,  \
>                                   class_shift, hook, stub)              \
> -       void stub(struct pci_dev *dev);                                 \
> -       void stub(struct pci_dev *dev)                                  \
> +       void __cficanonical stub(struct pci_dev *dev);                  \
> +       void __cficanonical stub(struct pci_dev *dev)                   \
>         {                                                               \
>                 hook(dev);                                              \
>         }                                                               \
> --
> 2.31.0.291.g576ba9dcdaf-goog
>


-- 
Thanks,
~Nick Desaulniers

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v2 09/17] lib/list_sort: fix function type mismatches
  2021-03-18 17:11 ` [PATCH v2 09/17] lib/list_sort: fix function type mismatches Sami Tolvanen
@ 2021-03-18 18:31   ` Nick Desaulniers
  2021-03-18 21:31     ` Sami Tolvanen
  0 siblings, 1 reply; 35+ messages in thread
From: Nick Desaulniers @ 2021-03-18 18:31 UTC (permalink / raw)
  To: Sami Tolvanen
  Cc: Kees Cook, Nathan Chancellor, Masahiro Yamada, Will Deacon,
	Jessica Yu, Arnd Bergmann, Tejun Heo, Paul E. McKenney,
	Christoph Hellwig, bpf, linux-hardening, linux-arch, Linux ARM,
	Linux Kbuild mailing list, PCI, LKML

On Thu, Mar 18, 2021 at 10:11 AM Sami Tolvanen <samitolvanen@google.com> wrote:
>
> Casting the comparison function to a different type trips indirect
> call Control-Flow Integrity (CFI) checking. Remove the additional
> consts from cmp_func, and the now unneeded casts.
>
> Fixes: 043b3f7b6388 ("lib/list_sort: simplify and remove MAX_LIST_LENGTH_BITS")
> Signed-off-by: Sami Tolvanen <samitolvanen@google.com>
> Reviewed-by: Kees Cook <keescook@chromium.org>
> ---
>  lib/list_sort.c | 8 ++++----
>  1 file changed, 4 insertions(+), 4 deletions(-)
>
> diff --git a/lib/list_sort.c b/lib/list_sort.c
> index 52f0c258c895..b14accf4ef83 100644
> --- a/lib/list_sort.c
> +++ b/lib/list_sort.c
> @@ -8,7 +8,7 @@
>  #include <linux/list.h>
>
>  typedef int __attribute__((nonnull(2,3))) (*cmp_func)(void *,
> -               struct list_head const *, struct list_head const *);
> +               struct list_head *, struct list_head *);
>
>  /*
>   * Returns a list organized in an intermediate format suited
> @@ -227,7 +227,7 @@ void list_sort(void *priv, struct list_head *head,

There's definitely some const confusion going on around here.
Comparison functions that modify their in/out parameters are a code
smell, and I wonder if any exist in tree?

I think it would be better to enforce one signature for cmp_func
throughout lib/list_sort.c and the tree, either const or not, but not
a mix of both.  I know `const` is messy because it tends to propagate
everywhere, so I don't care which is preferred (making cmp_func have
const qualified params or not, though if we're already being pedantic
about which params are non-null...), but something like this might be
nicer:

```
diff --git a/block/blk-mq-sched.c b/block/blk-mq-sched.c
index 5132f64a5cee..d475b3cfd06f 100644
--- a/block/blk-mq-sched.c
+++ b/block/blk-mq-sched.c
@@ -75,7 +75,8 @@ void blk_mq_sched_restart(struct blk_mq_hw_ctx *hctx)
        blk_mq_run_hw_queue(hctx, true);
 }

-static int sched_rq_cmp(void *priv, struct list_head *a, struct list_head *b)
+static int sched_rq_cmp(void *priv, const struct list_head *a,
+               const struct list_head *b)
 {
        struct request *rqa = container_of(a, struct request, queuelist);
        struct request *rqb = container_of(b, struct request, queuelist);
diff --git a/block/blk-mq.c b/block/blk-mq.c
index 2e825a7a3606..9ed063ffdb27 100644
--- a/block/blk-mq.c
+++ b/block/blk-mq.c
@@ -1905,7 +1905,8 @@ void blk_mq_insert_requests(struct blk_mq_hw_ctx
*hctx, struct blk_mq_ctx *ctx,
        spin_unlock(&ctx->lock);
 }

-static int plug_rq_cmp(void *priv, struct list_head *a, struct list_head *b)
+static int plug_rq_cmp(void *priv, const struct list_head *a,
+               const struct list_head *b)
 {
        struct request *rqa = container_of(a, struct request, queuelist);
        struct request *rqb = container_of(b, struct request, queuelist);
diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c
index 1ac67d4505e0..5a3a343499f6 100644
--- a/drivers/gpu/drm/drm_modes.c
+++ b/drivers/gpu/drm/drm_modes.c
@@ -1290,7 +1290,8 @@ EXPORT_SYMBOL(drm_mode_prune_invalid);
  * Negative if @lh_a is better than @lh_b, zero if they're equivalent, or
  * positive if @lh_b is better than @lh_a.
  */
-static int drm_mode_compare(void *priv, struct list_head *lh_a,
struct list_head *lh_b)
+static int drm_mode_compare(void *priv, const struct list_head *lh_a,
+               const struct list_head *lh_b)
 {
        struct drm_display_mode *a = list_entry(lh_a, struct
drm_display_mode, head);
        struct drm_display_mode *b = list_entry(lh_b, struct
drm_display_mode, head);
diff --git a/drivers/gpu/drm/i915/gt/intel_engine_user.c
b/drivers/gpu/drm/i915/gt/intel_engine_user.c
index 34e6096f196e..7586dffd27d3 100644
--- a/drivers/gpu/drm/i915/gt/intel_engine_user.c
+++ b/drivers/gpu/drm/i915/gt/intel_engine_user.c
@@ -49,7 +49,8 @@ static const u8 uabi_classes[] = {
        [VIDEO_ENHANCEMENT_CLASS] = I915_ENGINE_CLASS_VIDEO_ENHANCE,
 };

-static int engine_cmp(void *priv, struct list_head *A, struct list_head *B)
+static int engine_cmp(void *priv, const struct list_head *A,
+               const struct list_head *B)
 {
        const struct intel_engine_cs *a =
                container_of((struct rb_node *)A, typeof(*a), uabi_node);
diff --git a/fs/ext4/fsmap.c b/fs/ext4/fsmap.c
index 4c2a9fe30067..4493ef0c715e 100644
--- a/fs/ext4/fsmap.c
+++ b/fs/ext4/fsmap.c
@@ -354,8 +354,8 @@ static unsigned int ext4_getfsmap_find_sb(struct
super_block *sb,

 /* Compare two fsmap items. */
 static int ext4_getfsmap_compare(void *priv,
-                                struct list_head *a,
-                                struct list_head *b)
+                                const struct list_head *a,
+                                const struct list_head *b)
 {
        struct ext4_fsmap *fa;
        struct ext4_fsmap *fb;
diff --git a/fs/iomap/buffered-io.c b/fs/iomap/buffered-io.c
index 414769a6ad11..0129e6bab985 100644
--- a/fs/iomap/buffered-io.c
+++ b/fs/iomap/buffered-io.c
@@ -1155,7 +1155,8 @@ iomap_ioend_try_merge(struct iomap_ioend *ioend,
struct list_head *more_ioends,
 EXPORT_SYMBOL_GPL(iomap_ioend_try_merge);

 static int
-iomap_ioend_compare(void *priv, struct list_head *a, struct list_head *b)
+iomap_ioend_compare(void *priv, const struct list_head *a,
+               const struct list_head *b)
 {
        struct iomap_ioend *ia = container_of(a, struct iomap_ioend, io_list);
        struct iomap_ioend *ib = container_of(b, struct iomap_ioend, io_list);
diff --git a/include/linux/list_sort.h b/include/linux/list_sort.h
index 20f178c24e9d..4fe9cb94d0d1 100644
--- a/include/linux/list_sort.h
+++ b/include/linux/list_sort.h
@@ -6,8 +6,9 @@

 struct list_head;

+typedef int __attribute__((nonnull(2,3))) (*cmp_func)(void *,
+               struct list_head const *, struct list_head const *);
+
 __attribute__((nonnull(2,3)))
-void list_sort(void *priv, struct list_head *head,
-              int (*cmp)(void *priv, struct list_head *a,
-                         struct list_head *b));
+void list_sort(void *priv, struct list_head *head, cmp_func cmp);
 #endif
diff --git a/lib/list_sort.c b/lib/list_sort.c
index 52f0c258c895..6cfac649c4a6 100644
--- a/lib/list_sort.c
+++ b/lib/list_sort.c
@@ -7,9 +7,6 @@
 #include <linux/list_sort.h>
 #include <linux/list.h>

-typedef int __attribute__((nonnull(2,3))) (*cmp_func)(void *,
-               struct list_head const *, struct list_head const *);
-
 /*
  * Returns a list organized in an intermediate format suited
  * to chaining of merge() calls: null-terminated, no reserved or
@@ -185,9 +182,7 @@ static void merge_final(void *priv, cmp_func cmp,
struct list_head *head,
  * 2^(k+1) - 1 (second merge of case 5 when x == 2^(k-1) - 1).
  */
 __attribute__((nonnull(2,3)))
-void list_sort(void *priv, struct list_head *head,
-               int (*cmp)(void *priv, struct list_head *a,
-                       struct list_head *b))
+void list_sort(void *priv, struct list_head *head, cmp_func cmp)
 {
        struct list_head *list = head->next, *pending = NULL;
        size_t count = 0;       /* Count of pending */
```
There are probably more instances in the tree to clean up, but that
compiles with x86_64 defconfig, and I'm sure it doesn't suffer the CFI
issue from the cast.

>                 if (likely(bits)) {
>                         struct list_head *a = *tail, *b = a->prev;
>
> -                       a = merge(priv, (cmp_func)cmp, b, a);
> +                       a = merge(priv, cmp, b, a);
>                         /* Install the merged result in place of the inputs */
>                         a->prev = b->prev;
>                         *tail = a;
> @@ -249,10 +249,10 @@ void list_sort(void *priv, struct list_head *head,
>
>                 if (!next)
>                         break;
> -               list = merge(priv, (cmp_func)cmp, pending, list);
> +               list = merge(priv, cmp, pending, list);
>                 pending = next;
>         }
>         /* The final merge, rebuilding prev links */
> -       merge_final(priv, (cmp_func)cmp, head, pending, list);
> +       merge_final(priv, cmp, head, pending, list);
>  }
>  EXPORT_SYMBOL(list_sort);
> --
> 2.31.0.291.g576ba9dcdaf-goog
>


-- 
Thanks,
~Nick Desaulniers

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v2 10/17] lkdtm: use __va_function
  2021-03-18 17:11 ` [PATCH v2 10/17] lkdtm: use __va_function Sami Tolvanen
@ 2021-03-18 18:43   ` Nick Desaulniers
  2021-03-18 18:45     ` Nick Desaulniers
  0 siblings, 1 reply; 35+ messages in thread
From: Nick Desaulniers @ 2021-03-18 18:43 UTC (permalink / raw)
  To: Sami Tolvanen
  Cc: Kees Cook, Nathan Chancellor, Masahiro Yamada, Will Deacon,
	Jessica Yu, Arnd Bergmann, Tejun Heo, Paul E. McKenney,
	Christoph Hellwig, bpf, linux-hardening, linux-arch, Linux ARM,
	Linux Kbuild mailing list, PCI, LKML

On Thu, Mar 18, 2021 at 10:11 AM Sami Tolvanen <samitolvanen@google.com> wrote:
>
> To ensure we take the actual address of a function in kernel text, use
> __va_function. Otherwise, with CONFIG_CFI_CLANG, the compiler replaces
> the address with a pointer to the CFI jump table, which is actually in
> the module when compiled with CONFIG_LKDTM=m.

Should patch 10 and 12 be reordered against one another? Otherwise it
looks like 12 defines __va_function while 10 uses it?


>
> Signed-off-by: Sami Tolvanen <samitolvanen@google.com>
> Acked-by: Kees Cook <keescook@chromium.org>
> ---
>  drivers/misc/lkdtm/usercopy.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/misc/lkdtm/usercopy.c b/drivers/misc/lkdtm/usercopy.c
> index 109e8d4302c1..d173d6175c87 100644
> --- a/drivers/misc/lkdtm/usercopy.c
> +++ b/drivers/misc/lkdtm/usercopy.c
> @@ -314,7 +314,7 @@ void lkdtm_USERCOPY_KERNEL(void)
>
>         pr_info("attempting bad copy_to_user from kernel text: %px\n",
>                 vm_mmap);
> -       if (copy_to_user((void __user *)user_addr, vm_mmap,
> +       if (copy_to_user((void __user *)user_addr, __va_function(vm_mmap),
>                          unconst + PAGE_SIZE)) {
>                 pr_warn("copy_to_user failed, but lacked Oops\n");
>                 goto free_user;
> --
> 2.31.0.291.g576ba9dcdaf-goog
>


--
Thanks,
~Nick Desaulniers

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v2 10/17] lkdtm: use __va_function
  2021-03-18 18:43   ` Nick Desaulniers
@ 2021-03-18 18:45     ` Nick Desaulniers
  0 siblings, 0 replies; 35+ messages in thread
From: Nick Desaulniers @ 2021-03-18 18:45 UTC (permalink / raw)
  To: Sami Tolvanen
  Cc: Kees Cook, Nathan Chancellor, Masahiro Yamada, Will Deacon,
	Jessica Yu, Arnd Bergmann, Tejun Heo, Paul E. McKenney,
	Christoph Hellwig, bpf, linux-hardening, linux-arch, Linux ARM,
	Linux Kbuild mailing list, PCI, LKML

On Thu, Mar 18, 2021 at 11:43 AM Nick Desaulniers
<ndesaulniers@google.com> wrote:
>
> On Thu, Mar 18, 2021 at 10:11 AM Sami Tolvanen <samitolvanen@google.com> wrote:
> >
> > To ensure we take the actual address of a function in kernel text, use
> > __va_function. Otherwise, with CONFIG_CFI_CLANG, the compiler replaces
> > the address with a pointer to the CFI jump table, which is actually in
> > the module when compiled with CONFIG_LKDTM=m.
>
> Should patch 10 and 12 be reordered against one another? Otherwise it
> looks like 12 defines __va_function while 10 uses it?

Ah, nvm patch 3 defines a generic version, I see.

>
>
> >
> > Signed-off-by: Sami Tolvanen <samitolvanen@google.com>
> > Acked-by: Kees Cook <keescook@chromium.org>
> > ---
> >  drivers/misc/lkdtm/usercopy.c | 2 +-
> >  1 file changed, 1 insertion(+), 1 deletion(-)
> >
> > diff --git a/drivers/misc/lkdtm/usercopy.c b/drivers/misc/lkdtm/usercopy.c
> > index 109e8d4302c1..d173d6175c87 100644
> > --- a/drivers/misc/lkdtm/usercopy.c
> > +++ b/drivers/misc/lkdtm/usercopy.c
> > @@ -314,7 +314,7 @@ void lkdtm_USERCOPY_KERNEL(void)
> >
> >         pr_info("attempting bad copy_to_user from kernel text: %px\n",
> >                 vm_mmap);
> > -       if (copy_to_user((void __user *)user_addr, vm_mmap,
> > +       if (copy_to_user((void __user *)user_addr, __va_function(vm_mmap),
> >                          unconst + PAGE_SIZE)) {
> >                 pr_warn("copy_to_user failed, but lacked Oops\n");
> >                 goto free_user;
> > --
> > 2.31.0.291.g576ba9dcdaf-goog
> >
>
>
> --
> Thanks,
> ~Nick Desaulniers



-- 
Thanks,
~Nick Desaulniers

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v2 05/17] workqueue: use WARN_ON_FUNCTION_MISMATCH
  2021-03-18 17:10 ` [PATCH v2 05/17] workqueue: use WARN_ON_FUNCTION_MISMATCH Sami Tolvanen
@ 2021-03-18 18:50   ` Nick Desaulniers
  2021-03-18 21:38     ` Sami Tolvanen
  0 siblings, 1 reply; 35+ messages in thread
From: Nick Desaulniers @ 2021-03-18 18:50 UTC (permalink / raw)
  To: Sami Tolvanen
  Cc: Kees Cook, Nathan Chancellor, Masahiro Yamada, Will Deacon,
	Jessica Yu, Arnd Bergmann, Tejun Heo, Paul E. McKenney,
	Christoph Hellwig, bpf, linux-hardening, linux-arch, Linux ARM,
	Linux Kbuild mailing list, PCI, LKML

On Thu, Mar 18, 2021 at 10:11 AM Sami Tolvanen <samitolvanen@google.com> wrote:
>
> With CONFIG_CFI_CLANG, a callback function passed to
> __queue_delayed_work from a module points to a jump table entry
> defined in the module instead of the one used in the core kernel,
> which breaks function address equality in this check:
>
>   WARN_ON_ONCE(timer->function != delayed_work_timer_fn);
>
> Use WARN_ON_FUNCTION_MISMATCH() instead to disable the warning
> when CFI and modules are both enabled.

Does __cficanonical help with such comparisons? Or would that be a
very invasive change, if the concern was to try to keep these checks
in place for CONFIG_CFI_CLANG?

>
> Signed-off-by: Sami Tolvanen <samitolvanen@google.com>
> ---
>  kernel/workqueue.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/kernel/workqueue.c b/kernel/workqueue.c
> index 0d150da252e8..03fe07d2f39f 100644
> --- a/kernel/workqueue.c
> +++ b/kernel/workqueue.c
> @@ -1630,7 +1630,7 @@ static void __queue_delayed_work(int cpu, struct workqueue_struct *wq,
>         struct work_struct *work = &dwork->work;
>
>         WARN_ON_ONCE(!wq);
> -       WARN_ON_ONCE(timer->function != delayed_work_timer_fn);
> +       WARN_ON_FUNCTION_MISMATCH(timer->function, delayed_work_timer_fn);
>         WARN_ON_ONCE(timer_pending(timer));
>         WARN_ON_ONCE(!list_empty(&work->entry));
>
> --
> 2.31.0.291.g576ba9dcdaf-goog
>


--
Thanks,
~Nick Desaulniers

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v2 07/17] kallsyms: strip ThinLTO hashes from static functions
  2021-03-18 17:11 ` [PATCH v2 07/17] kallsyms: strip ThinLTO hashes from static functions Sami Tolvanen
@ 2021-03-18 19:00   ` Nick Desaulniers
  2021-03-18 21:41     ` Sami Tolvanen
  0 siblings, 1 reply; 35+ messages in thread
From: Nick Desaulniers @ 2021-03-18 19:00 UTC (permalink / raw)
  To: Sami Tolvanen
  Cc: Kees Cook, Nathan Chancellor, Masahiro Yamada, Will Deacon,
	Jessica Yu, Arnd Bergmann, Tejun Heo, Paul E. McKenney,
	Christoph Hellwig, bpf, linux-hardening, linux-arch, Linux ARM,
	Linux Kbuild mailing list, PCI, LKML

On Thu, Mar 18, 2021 at 10:11 AM Sami Tolvanen <samitolvanen@google.com> wrote:
>
> With CONFIG_CFI_CLANG and ThinLTO, Clang appends a hash to the names
> of all static functions not marked __used. This can break userspace
> tools that don't expect the function name to change, so strip out the
> hash from the output.
>
> Suggested-by: Jack Pham <jackp@codeaurora.org>
> Signed-off-by: Sami Tolvanen <samitolvanen@google.com>
> Reviewed-by: Kees Cook <keescook@chromium.org>
> ---
>  kernel/kallsyms.c | 54 ++++++++++++++++++++++++++++++++++++++++++-----
>  1 file changed, 49 insertions(+), 5 deletions(-)
>
> diff --git a/kernel/kallsyms.c b/kernel/kallsyms.c
> index 8043a90aa50e..17d3a704bafa 100644
> --- a/kernel/kallsyms.c
> +++ b/kernel/kallsyms.c
> @@ -161,6 +161,26 @@ static unsigned long kallsyms_sym_address(int idx)
>         return kallsyms_relative_base - 1 - kallsyms_offsets[idx];
>  }
>
> +#if defined(CONFIG_CFI_CLANG) && defined(CONFIG_LTO_CLANG_THIN)
> +/*
> + * LLVM appends a hash to static function names when ThinLTO and CFI are
> + * both enabled, which causes confusion and potentially breaks user space

Might be nice to add an example, something along the lines of:
ie. foo() becomes foo$asfdasdfasdfasdf()

> + * tools, so we will strip the postfix from expanded symbol names.

s/postfix/suffix/ ?

> + */
> +static inline char *cleanup_symbol_name(char *s)
> +{
> +       char *res = NULL;
> +
> +       res = strrchr(s, '$');
> +       if (res)
> +               *res = '\0';
> +
> +       return res;
> +}
> +#else
> +static inline char *cleanup_symbol_name(char *s) { return NULL; }
> +#endif

Might be nicer to return a `bool` and have the larger definition
`return res != NULL`).  Not sure what a caller would do with `res` if
it was not `NULL`?

> +
>  /* Lookup the address for this symbol. Returns 0 if not found. */
>  unsigned long kallsyms_lookup_name(const char *name)
>  {
> @@ -173,6 +193,9 @@ unsigned long kallsyms_lookup_name(const char *name)
>
>                 if (strcmp(namebuf, name) == 0)
>                         return kallsyms_sym_address(i);
> +
> +               if (cleanup_symbol_name(namebuf) && strcmp(namebuf, name) == 0)
> +                       return kallsyms_sym_address(i);
>         }
>         return module_kallsyms_lookup_name(name);
>  }
> @@ -303,7 +326,9 @@ const char *kallsyms_lookup(unsigned long addr,
>                                        namebuf, KSYM_NAME_LEN);
>                 if (modname)
>                         *modname = NULL;
> -               return namebuf;
> +
> +               ret = namebuf;
> +               goto found;
>         }
>
>         /* See if it's in a module or a BPF JITed image. */
> @@ -316,11 +341,16 @@ const char *kallsyms_lookup(unsigned long addr,
>         if (!ret)
>                 ret = ftrace_mod_address_lookup(addr, symbolsize,
>                                                 offset, modname, namebuf);
> +
> +found:
> +       cleanup_symbol_name(namebuf);
>         return ret;
>  }
>
>  int lookup_symbol_name(unsigned long addr, char *symname)
>  {
> +       int res;
> +
>         symname[0] = '\0';
>         symname[KSYM_NAME_LEN - 1] = '\0';
>
> @@ -331,15 +361,23 @@ int lookup_symbol_name(unsigned long addr, char *symname)
>                 /* Grab name */
>                 kallsyms_expand_symbol(get_symbol_offset(pos),
>                                        symname, KSYM_NAME_LEN);
> -               return 0;
> +               goto found;
>         }
>         /* See if it's in a module. */
> -       return lookup_module_symbol_name(addr, symname);
> +       res = lookup_module_symbol_name(addr, symname);
> +       if (res)
> +               return res;
> +
> +found:
> +       cleanup_symbol_name(symname);
> +       return 0;
>  }
>
>  int lookup_symbol_attrs(unsigned long addr, unsigned long *size,
>                         unsigned long *offset, char *modname, char *name)
>  {
> +       int res;
> +
>         name[0] = '\0';
>         name[KSYM_NAME_LEN - 1] = '\0';
>
> @@ -351,10 +389,16 @@ int lookup_symbol_attrs(unsigned long addr, unsigned long *size,
>                 kallsyms_expand_symbol(get_symbol_offset(pos),
>                                        name, KSYM_NAME_LEN);
>                 modname[0] = '\0';
> -               return 0;
> +               goto found;
>         }
>         /* See if it's in a module. */
> -       return lookup_module_symbol_attrs(addr, size, offset, modname, name);
> +       res = lookup_module_symbol_attrs(addr, size, offset, modname, name);
> +       if (res)
> +               return res;
> +
> +found:
> +       cleanup_symbol_name(name);
> +       return 0;
>  }
>
>  /* Look up a kernel symbol and return it in a text buffer. */
> --
> 2.31.0.291.g576ba9dcdaf-goog
>


-- 
Thanks,
~Nick Desaulniers

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v2 04/17] module: ensure __cfi_check alignment
  2021-03-18 17:10 ` [PATCH v2 04/17] module: ensure __cfi_check alignment Sami Tolvanen
@ 2021-03-18 19:27   ` Nick Desaulniers
  2021-03-18 21:43     ` Sami Tolvanen
  0 siblings, 1 reply; 35+ messages in thread
From: Nick Desaulniers @ 2021-03-18 19:27 UTC (permalink / raw)
  To: Sami Tolvanen
  Cc: Kees Cook, Nathan Chancellor, Masahiro Yamada, Will Deacon,
	Jessica Yu, Arnd Bergmann, Tejun Heo, Paul E. McKenney,
	Christoph Hellwig, bpf, linux-hardening, linux-arch, Linux ARM,
	Linux Kbuild mailing list, PCI, LKML

On Thu, Mar 18, 2021 at 10:11 AM Sami Tolvanen <samitolvanen@google.com> wrote:
>
> CONFIG_CFI_CLANG_SHADOW assumes the __cfi_check() function is page
> aligned and at the beginning of the .text section. While Clang would
> normally align the function correctly, it fails to do so for modules
> with no executable code.
>
> This change ensures the correct __cfi_check() location and
> alignment. It also discards the .eh_frame section, which Clang can
> generate with certain sanitizers, such as CFI.
>
> Link: https://bugs.llvm.org/show_bug.cgi?id=46293
> Signed-off-by: Sami Tolvanen <samitolvanen@google.com>
> ---
>  scripts/module.lds.S | 18 +++++++++++++++++-
>  1 file changed, 17 insertions(+), 1 deletion(-)
>
> diff --git a/scripts/module.lds.S b/scripts/module.lds.S
> index 168cd27e6122..93518579cf5d 100644
> --- a/scripts/module.lds.S
> +++ b/scripts/module.lds.S
> @@ -3,10 +3,19 @@
>   * Archs are free to supply their own linker scripts.  ld will
>   * combine them automatically.
>   */
> +#include <asm/page.h>
> +
> +#ifdef CONFIG_CFI_CLANG
> +# define ALIGN_CFI ALIGN(PAGE_SIZE)
> +#else
> +# define ALIGN_CFI
> +#endif
> +
>  SECTIONS {
>         /DISCARD/ : {
>                 *(.discard)
>                 *(.discard.*)
> +               *(.eh_frame)

Do we want to unconditionally discard this section from modules for
all arches/configs?  I like how we conditionally do so on
SANITIZER_DISCARDS in include/asm-generic/vmlinux.lds.h for example.

>         }
>
>         __ksymtab               0 : { *(SORT(___ksymtab+*)) }
> @@ -40,7 +49,14 @@ SECTIONS {
>                 *(.rodata..L*)
>         }
>
> -       .text : { *(.text .text.[0-9a-zA-Z_]*) }
> +       /*
> +        * With CONFIG_CFI_CLANG, we assume __cfi_check is at the beginning
> +        * of the .text section, and is aligned to PAGE_SIZE.
> +        */
> +       .text : ALIGN_CFI {
> +               *(.text.__cfi_check)
> +               *(.text .text.[0-9a-zA-Z_]* .text..L.cfi*)
> +       }
>  }
>
>  /* bring in arch-specific sections */
> --
> 2.31.0.291.g576ba9dcdaf-goog
>


-- 
Thanks,
~Nick Desaulniers

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v2 09/17] lib/list_sort: fix function type mismatches
  2021-03-18 18:31   ` Nick Desaulniers
@ 2021-03-18 21:31     ` Sami Tolvanen
  0 siblings, 0 replies; 35+ messages in thread
From: Sami Tolvanen @ 2021-03-18 21:31 UTC (permalink / raw)
  To: Nick Desaulniers
  Cc: Kees Cook, Nathan Chancellor, Masahiro Yamada, Will Deacon,
	Jessica Yu, Arnd Bergmann, Tejun Heo, Paul E. McKenney,
	Christoph Hellwig, bpf, linux-hardening, linux-arch, Linux ARM,
	Linux Kbuild mailing list, PCI, LKML

On Thu, Mar 18, 2021 at 11:31 AM Nick Desaulniers
<ndesaulniers@google.com> wrote:
>
> On Thu, Mar 18, 2021 at 10:11 AM Sami Tolvanen <samitolvanen@google.com> wrote:
> >
> > Casting the comparison function to a different type trips indirect
> > call Control-Flow Integrity (CFI) checking. Remove the additional
> > consts from cmp_func, and the now unneeded casts.
> >
> > Fixes: 043b3f7b6388 ("lib/list_sort: simplify and remove MAX_LIST_LENGTH_BITS")
> > Signed-off-by: Sami Tolvanen <samitolvanen@google.com>
> > Reviewed-by: Kees Cook <keescook@chromium.org>
> > ---
> >  lib/list_sort.c | 8 ++++----
> >  1 file changed, 4 insertions(+), 4 deletions(-)
> >
> > diff --git a/lib/list_sort.c b/lib/list_sort.c
> > index 52f0c258c895..b14accf4ef83 100644
> > --- a/lib/list_sort.c
> > +++ b/lib/list_sort.c
> > @@ -8,7 +8,7 @@
> >  #include <linux/list.h>
> >
> >  typedef int __attribute__((nonnull(2,3))) (*cmp_func)(void *,
> > -               struct list_head const *, struct list_head const *);
> > +               struct list_head *, struct list_head *);
> >
> >  /*
> >   * Returns a list organized in an intermediate format suited
> > @@ -227,7 +227,7 @@ void list_sort(void *priv, struct list_head *head,
>
> There's definitely some const confusion going on around here.
> Comparison functions that modify their in/out parameters are a code
> smell, and I wonder if any exist in tree?
>
> I think it would be better to enforce one signature for cmp_func
> throughout lib/list_sort.c and the tree, either const or not, but not
> a mix of both.  I know `const` is messy because it tends to propagate
> everywhere, so I don't care which is preferred (making cmp_func have
> const qualified params or not, though if we're already being pedantic
> about which params are non-null...), but something like this might be
> nicer:

Sure, an alternative to removing the internal casts is to actually
change all the callers to use a comparison function with const
parameters. That's quite a bit more invasive, but I'm fine with that
approach too.

>
> ```
> diff --git a/block/blk-mq-sched.c b/block/blk-mq-sched.c
> index 5132f64a5cee..d475b3cfd06f 100644
> --- a/block/blk-mq-sched.c
> +++ b/block/blk-mq-sched.c
> @@ -75,7 +75,8 @@ void blk_mq_sched_restart(struct blk_mq_hw_ctx *hctx)
>         blk_mq_run_hw_queue(hctx, true);
>  }
>
> -static int sched_rq_cmp(void *priv, struct list_head *a, struct list_head *b)
> +static int sched_rq_cmp(void *priv, const struct list_head *a,
> +               const struct list_head *b)
>  {
>         struct request *rqa = container_of(a, struct request, queuelist);
>         struct request *rqb = container_of(b, struct request, queuelist);
> diff --git a/block/blk-mq.c b/block/blk-mq.c
> index 2e825a7a3606..9ed063ffdb27 100644
> --- a/block/blk-mq.c
> +++ b/block/blk-mq.c
> @@ -1905,7 +1905,8 @@ void blk_mq_insert_requests(struct blk_mq_hw_ctx
> *hctx, struct blk_mq_ctx *ctx,
>         spin_unlock(&ctx->lock);
>  }
>
> -static int plug_rq_cmp(void *priv, struct list_head *a, struct list_head *b)
> +static int plug_rq_cmp(void *priv, const struct list_head *a,
> +               const struct list_head *b)
>  {
>         struct request *rqa = container_of(a, struct request, queuelist);
>         struct request *rqb = container_of(b, struct request, queuelist);
> diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c
> index 1ac67d4505e0..5a3a343499f6 100644
> --- a/drivers/gpu/drm/drm_modes.c
> +++ b/drivers/gpu/drm/drm_modes.c
> @@ -1290,7 +1290,8 @@ EXPORT_SYMBOL(drm_mode_prune_invalid);
>   * Negative if @lh_a is better than @lh_b, zero if they're equivalent, or
>   * positive if @lh_b is better than @lh_a.
>   */
> -static int drm_mode_compare(void *priv, struct list_head *lh_a,
> struct list_head *lh_b)
> +static int drm_mode_compare(void *priv, const struct list_head *lh_a,
> +               const struct list_head *lh_b)
>  {
>         struct drm_display_mode *a = list_entry(lh_a, struct
> drm_display_mode, head);
>         struct drm_display_mode *b = list_entry(lh_b, struct
> drm_display_mode, head);
> diff --git a/drivers/gpu/drm/i915/gt/intel_engine_user.c
> b/drivers/gpu/drm/i915/gt/intel_engine_user.c
> index 34e6096f196e..7586dffd27d3 100644
> --- a/drivers/gpu/drm/i915/gt/intel_engine_user.c
> +++ b/drivers/gpu/drm/i915/gt/intel_engine_user.c
> @@ -49,7 +49,8 @@ static const u8 uabi_classes[] = {
>         [VIDEO_ENHANCEMENT_CLASS] = I915_ENGINE_CLASS_VIDEO_ENHANCE,
>  };
>
> -static int engine_cmp(void *priv, struct list_head *A, struct list_head *B)
> +static int engine_cmp(void *priv, const struct list_head *A,
> +               const struct list_head *B)
>  {
>         const struct intel_engine_cs *a =
>                 container_of((struct rb_node *)A, typeof(*a), uabi_node);
> diff --git a/fs/ext4/fsmap.c b/fs/ext4/fsmap.c
> index 4c2a9fe30067..4493ef0c715e 100644
> --- a/fs/ext4/fsmap.c
> +++ b/fs/ext4/fsmap.c
> @@ -354,8 +354,8 @@ static unsigned int ext4_getfsmap_find_sb(struct
> super_block *sb,
>
>  /* Compare two fsmap items. */
>  static int ext4_getfsmap_compare(void *priv,
> -                                struct list_head *a,
> -                                struct list_head *b)
> +                                const struct list_head *a,
> +                                const struct list_head *b)
>  {
>         struct ext4_fsmap *fa;
>         struct ext4_fsmap *fb;
> diff --git a/fs/iomap/buffered-io.c b/fs/iomap/buffered-io.c
> index 414769a6ad11..0129e6bab985 100644
> --- a/fs/iomap/buffered-io.c
> +++ b/fs/iomap/buffered-io.c
> @@ -1155,7 +1155,8 @@ iomap_ioend_try_merge(struct iomap_ioend *ioend,
> struct list_head *more_ioends,
>  EXPORT_SYMBOL_GPL(iomap_ioend_try_merge);
>
>  static int
> -iomap_ioend_compare(void *priv, struct list_head *a, struct list_head *b)
> +iomap_ioend_compare(void *priv, const struct list_head *a,
> +               const struct list_head *b)
>  {
>         struct iomap_ioend *ia = container_of(a, struct iomap_ioend, io_list);
>         struct iomap_ioend *ib = container_of(b, struct iomap_ioend, io_list);
> diff --git a/include/linux/list_sort.h b/include/linux/list_sort.h
> index 20f178c24e9d..4fe9cb94d0d1 100644
> --- a/include/linux/list_sort.h
> +++ b/include/linux/list_sort.h
> @@ -6,8 +6,9 @@
>
>  struct list_head;
>
> +typedef int __attribute__((nonnull(2,3))) (*cmp_func)(void *,
> +               struct list_head const *, struct list_head const *);
> +

Note that we already have cmp_func_t in linux/types.h, so something
like list_cmp_func_t would probably be more appropriate here.

>  __attribute__((nonnull(2,3)))
> -void list_sort(void *priv, struct list_head *head,
> -              int (*cmp)(void *priv, struct list_head *a,
> -                         struct list_head *b));
> +void list_sort(void *priv, struct list_head *head, cmp_func cmp);
>  #endif
> diff --git a/lib/list_sort.c b/lib/list_sort.c
> index 52f0c258c895..6cfac649c4a6 100644
> --- a/lib/list_sort.c
> +++ b/lib/list_sort.c
> @@ -7,9 +7,6 @@
>  #include <linux/list_sort.h>
>  #include <linux/list.h>
>
> -typedef int __attribute__((nonnull(2,3))) (*cmp_func)(void *,
> -               struct list_head const *, struct list_head const *);
> -
>  /*
>   * Returns a list organized in an intermediate format suited
>   * to chaining of merge() calls: null-terminated, no reserved or
> @@ -185,9 +182,7 @@ static void merge_final(void *priv, cmp_func cmp,
> struct list_head *head,
>   * 2^(k+1) - 1 (second merge of case 5 when x == 2^(k-1) - 1).
>   */
>  __attribute__((nonnull(2,3)))
> -void list_sort(void *priv, struct list_head *head,
> -               int (*cmp)(void *priv, struct list_head *a,
> -                       struct list_head *b))
> +void list_sort(void *priv, struct list_head *head, cmp_func cmp)
>  {
>         struct list_head *list = head->next, *pending = NULL;
>         size_t count = 0;       /* Count of pending */
> ```
> There are probably more instances in the tree to clean up, but that
> compiles with x86_64 defconfig, and I'm sure it doesn't suffer the CFI
> issue from the cast.

You indeed missed some callers, but after fixing all the remaining
ones too, arm64 and x86_64 allyesconfigs still seem to build just fine
for me:

 arch/arm64/kvm/vgic/vgic-its.c                         |    8 ++++----
 arch/arm64/kvm/vgic/vgic.c                             |    2 +-
 block/blk-mq-sched.c                                   |    2 +-
 block/blk-mq.c                                         |    2 +-
 drivers/acpi/nfit/core.c                               |    2 +-
 drivers/acpi/numa/hmat.c                               |    2 +-
 drivers/clk/keystone/sci-clk.c                         |    4 ++--
 drivers/gpu/drm/drm_modes.c                            |    2 +-
 drivers/gpu/drm/i915/gt/intel_engine_user.c            |    2 +-
 drivers/gpu/drm/i915/gvt/debugfs.c                     |    2 +-
 drivers/gpu/drm/i915/selftests/i915_gem_gtt.c          |    2 +-
 drivers/gpu/drm/radeon/radeon_cs.c                     |    4 ++--
 drivers/infiniband/hw/usnic/usnic_uiom_interval_tree.c |    2 +-
 drivers/interconnect/qcom/bcm-voter.c                  |    2 +-
 drivers/md/raid5.c                                     |    2 +-
 drivers/misc/sram.c                                    |    4 ++--
 drivers/nvme/host/core.c                               |    2 +-
 drivers/pci/controller/cadence/pcie-cadence-host.c     |    3 ++-
 drivers/spi/spi-loopback-test.c                        |    2 +-
 fs/btrfs/raid56.c                                      |    2 +-
 fs/btrfs/tree-log.c                                    |    2 +-
 fs/btrfs/volumes.c                                     |    2 +-
 fs/ext4/fsmap.c                                        |    4 ++--
 fs/gfs2/glock.c                                        |    2 +-
 fs/gfs2/log.c                                          |    2 +-
 fs/gfs2/lops.c                                         |    2 +-
 fs/iomap/buffered-io.c                                 |    2 +-
 fs/ubifs/gc.c                                          |    6 +++---
 fs/ubifs/replay.c                                      |    4 ++--
 fs/xfs/scrub/bitmap.c                                  |    4 ++--
 fs/xfs/xfs_bmap_item.c                                 |    4 ++--
 fs/xfs/xfs_buf.c                                       |    4 ++--
 fs/xfs/xfs_extent_busy.c                               |    4 ++--
 fs/xfs/xfs_extent_busy.h                               |    2 +-
 fs/xfs/xfs_extfree_item.c                              |    4 ++--
 fs/xfs/xfs_refcount_item.c                             |    4 ++--
 fs/xfs/xfs_rmap_item.c                                 |    4 ++--
 include/linux/list_sort.h                              |    7 ++++---
 lib/list_sort.c                                        |   17 ++++++-----------
 lib/test_list_sort.c                                   |    2 +-
 net/tipc/name_table.c                                  |    4 ++--
 41 files changed, 68 insertions(+), 71 deletions(-)

I'll have to double check that all the affected code is actually
included in these builds, but if nobody objects and I don't run into
any issues, I'll include the patch in v3.

Sami

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v2 05/17] workqueue: use WARN_ON_FUNCTION_MISMATCH
  2021-03-18 18:50   ` Nick Desaulniers
@ 2021-03-18 21:38     ` Sami Tolvanen
  0 siblings, 0 replies; 35+ messages in thread
From: Sami Tolvanen @ 2021-03-18 21:38 UTC (permalink / raw)
  To: Nick Desaulniers
  Cc: Kees Cook, Nathan Chancellor, Masahiro Yamada, Will Deacon,
	Jessica Yu, Arnd Bergmann, Tejun Heo, Paul E. McKenney,
	Christoph Hellwig, bpf, linux-hardening, linux-arch, Linux ARM,
	Linux Kbuild mailing list, PCI, LKML

On Thu, Mar 18, 2021 at 11:50 AM Nick Desaulniers
<ndesaulniers@google.com> wrote:
>
> On Thu, Mar 18, 2021 at 10:11 AM Sami Tolvanen <samitolvanen@google.com> wrote:
> >
> > With CONFIG_CFI_CLANG, a callback function passed to
> > __queue_delayed_work from a module points to a jump table entry
> > defined in the module instead of the one used in the core kernel,
> > which breaks function address equality in this check:
> >
> >   WARN_ON_ONCE(timer->function != delayed_work_timer_fn);
> >
> > Use WARN_ON_FUNCTION_MISMATCH() instead to disable the warning
> > when CFI and modules are both enabled.
>
> Does __cficanonical help with such comparisons? Or would that be a
> very invasive change, if the concern was to try to keep these checks
> in place for CONFIG_CFI_CLANG?

The last time I checked, Clang ignored the __cficanonical attribute in
header files, which means it would still generate a local jump table
entry in each module for such functions, and the comparison here would
fail. We could avoid the issue by using __cficanonical for the
callback function *and* using __va_function() when we take the
function address in modules, but that feels way too invasive for this
particular use case.

Sami

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v2 07/17] kallsyms: strip ThinLTO hashes from static functions
  2021-03-18 19:00   ` Nick Desaulniers
@ 2021-03-18 21:41     ` Sami Tolvanen
  0 siblings, 0 replies; 35+ messages in thread
From: Sami Tolvanen @ 2021-03-18 21:41 UTC (permalink / raw)
  To: Nick Desaulniers
  Cc: Kees Cook, Nathan Chancellor, Masahiro Yamada, Will Deacon,
	Jessica Yu, Arnd Bergmann, Tejun Heo, Paul E. McKenney,
	Christoph Hellwig, bpf, linux-hardening, linux-arch, Linux ARM,
	Linux Kbuild mailing list, PCI, LKML

On Thu, Mar 18, 2021 at 12:00 PM Nick Desaulniers
<ndesaulniers@google.com> wrote:
>
> On Thu, Mar 18, 2021 at 10:11 AM Sami Tolvanen <samitolvanen@google.com> wrote:
> >
> > With CONFIG_CFI_CLANG and ThinLTO, Clang appends a hash to the names
> > of all static functions not marked __used. This can break userspace
> > tools that don't expect the function name to change, so strip out the
> > hash from the output.
> >
> > Suggested-by: Jack Pham <jackp@codeaurora.org>
> > Signed-off-by: Sami Tolvanen <samitolvanen@google.com>
> > Reviewed-by: Kees Cook <keescook@chromium.org>
> > ---
> >  kernel/kallsyms.c | 54 ++++++++++++++++++++++++++++++++++++++++++-----
> >  1 file changed, 49 insertions(+), 5 deletions(-)
> >
> > diff --git a/kernel/kallsyms.c b/kernel/kallsyms.c
> > index 8043a90aa50e..17d3a704bafa 100644
> > --- a/kernel/kallsyms.c
> > +++ b/kernel/kallsyms.c
> > @@ -161,6 +161,26 @@ static unsigned long kallsyms_sym_address(int idx)
> >         return kallsyms_relative_base - 1 - kallsyms_offsets[idx];
> >  }
> >
> > +#if defined(CONFIG_CFI_CLANG) && defined(CONFIG_LTO_CLANG_THIN)
> > +/*
> > + * LLVM appends a hash to static function names when ThinLTO and CFI are
> > + * both enabled, which causes confusion and potentially breaks user space
>
> Might be nice to add an example, something along the lines of:
> ie. foo() becomes foo$asfdasdfasdfasdf()

Agreed, I'll update the comment in v3.

>
> > + * tools, so we will strip the postfix from expanded symbol names.
>
> s/postfix/suffix/ ?

Ack.

>
> > + */
> > +static inline char *cleanup_symbol_name(char *s)
> > +{
> > +       char *res = NULL;
> > +
> > +       res = strrchr(s, '$');
> > +       if (res)
> > +               *res = '\0';
> > +
> > +       return res;
> > +}
> > +#else
> > +static inline char *cleanup_symbol_name(char *s) { return NULL; }
> > +#endif
>
> Might be nicer to return a `bool` and have the larger definition
> `return res != NULL`).  Not sure what a caller would do with `res` if
> it was not `NULL`?

Sure, I'll change this to bool.

Sami

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v2 04/17] module: ensure __cfi_check alignment
  2021-03-18 19:27   ` Nick Desaulniers
@ 2021-03-18 21:43     ` Sami Tolvanen
  0 siblings, 0 replies; 35+ messages in thread
From: Sami Tolvanen @ 2021-03-18 21:43 UTC (permalink / raw)
  To: Nick Desaulniers
  Cc: Kees Cook, Nathan Chancellor, Masahiro Yamada, Will Deacon,
	Jessica Yu, Arnd Bergmann, Tejun Heo, Paul E. McKenney,
	Christoph Hellwig, bpf, linux-hardening, linux-arch, Linux ARM,
	Linux Kbuild mailing list, PCI, LKML

On Thu, Mar 18, 2021 at 12:27 PM Nick Desaulniers
<ndesaulniers@google.com> wrote:
>
> On Thu, Mar 18, 2021 at 10:11 AM Sami Tolvanen <samitolvanen@google.com> wrote:
> >
> > CONFIG_CFI_CLANG_SHADOW assumes the __cfi_check() function is page
> > aligned and at the beginning of the .text section. While Clang would
> > normally align the function correctly, it fails to do so for modules
> > with no executable code.
> >
> > This change ensures the correct __cfi_check() location and
> > alignment. It also discards the .eh_frame section, which Clang can
> > generate with certain sanitizers, such as CFI.
> >
> > Link: https://bugs.llvm.org/show_bug.cgi?id=46293
> > Signed-off-by: Sami Tolvanen <samitolvanen@google.com>
> > ---
> >  scripts/module.lds.S | 18 +++++++++++++++++-
> >  1 file changed, 17 insertions(+), 1 deletion(-)
> >
> > diff --git a/scripts/module.lds.S b/scripts/module.lds.S
> > index 168cd27e6122..93518579cf5d 100644
> > --- a/scripts/module.lds.S
> > +++ b/scripts/module.lds.S
> > @@ -3,10 +3,19 @@
> >   * Archs are free to supply their own linker scripts.  ld will
> >   * combine them automatically.
> >   */
> > +#include <asm/page.h>
> > +
> > +#ifdef CONFIG_CFI_CLANG
> > +# define ALIGN_CFI ALIGN(PAGE_SIZE)
> > +#else
> > +# define ALIGN_CFI
> > +#endif
> > +
> >  SECTIONS {
> >         /DISCARD/ : {
> >                 *(.discard)
> >                 *(.discard.*)
> > +               *(.eh_frame)
>
> Do we want to unconditionally discard this section from modules for
> all arches/configs?  I like how we conditionally do so on
> SANITIZER_DISCARDS in include/asm-generic/vmlinux.lds.h for example.

I think nothing should generate this section unless one of the
sanitizers is enabled, but I'm fine with using the same logic as
vmlinux.lds.h here. I'll change this in v3.

Sami

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v2 01/17] add support for Clang CFI
  2021-03-18 17:10 ` [PATCH v2 01/17] add " Sami Tolvanen
@ 2021-03-18 22:29   ` Peter Zijlstra
  2021-03-18 23:48     ` Sami Tolvanen
  0 siblings, 1 reply; 35+ messages in thread
From: Peter Zijlstra @ 2021-03-18 22:29 UTC (permalink / raw)
  To: Sami Tolvanen
  Cc: Kees Cook, Nathan Chancellor, Nick Desaulniers, Masahiro Yamada,
	Will Deacon, Jessica Yu, Arnd Bergmann, Tejun Heo,
	Paul E. McKenney, Christoph Hellwig, bpf, linux-hardening,
	linux-arch, linux-arm-kernel, linux-kbuild, linux-pci,
	linux-kernel

On Thu, Mar 18, 2021 at 10:10:55AM -0700, Sami Tolvanen wrote:
> +static void update_shadow(struct module *mod, unsigned long base_addr,
> +		update_shadow_fn fn)
> +{
> +	struct cfi_shadow *prev;
> +	struct cfi_shadow *next;
> +	unsigned long min_addr, max_addr;
> +
> +	next = vmalloc(SHADOW_SIZE);
> +
> +	mutex_lock(&shadow_update_lock);
> +	prev = rcu_dereference_protected(cfi_shadow,
> +					 mutex_is_locked(&shadow_update_lock));
> +
> +	if (next) {
> +		next->base = base_addr >> PAGE_SHIFT;
> +		prepare_next_shadow(prev, next);
> +
> +		min_addr = (unsigned long)mod->core_layout.base;
> +		max_addr = min_addr + mod->core_layout.text_size;
> +		fn(next, mod, min_addr & PAGE_MASK, max_addr & PAGE_MASK);
> +
> +		set_memory_ro((unsigned long)next, SHADOW_PAGES);
> +	}
> +
> +	rcu_assign_pointer(cfi_shadow, next);
> +	mutex_unlock(&shadow_update_lock);
> +	synchronize_rcu_expedited();

expedited is BAD(tm), why is it required and why doesn't it have a
comment?

> +
> +	if (prev) {
> +		set_memory_rw((unsigned long)prev, SHADOW_PAGES);
> +		vfree(prev);
> +	}
> +}

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v2 01/17] add support for Clang CFI
  2021-03-18 22:29   ` Peter Zijlstra
@ 2021-03-18 23:48     ` Sami Tolvanen
  2021-03-19 12:26       ` Peter Zijlstra
  0 siblings, 1 reply; 35+ messages in thread
From: Sami Tolvanen @ 2021-03-18 23:48 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Kees Cook, Nathan Chancellor, Nick Desaulniers, Masahiro Yamada,
	Will Deacon, Jessica Yu, Arnd Bergmann, Tejun Heo,
	Paul E. McKenney, Christoph Hellwig, bpf, linux-hardening,
	linux-arch, linux-arm-kernel, linux-kbuild, PCI, LKML

On Thu, Mar 18, 2021 at 3:29 PM Peter Zijlstra <peterz@infradead.org> wrote:
>
> On Thu, Mar 18, 2021 at 10:10:55AM -0700, Sami Tolvanen wrote:
> > +static void update_shadow(struct module *mod, unsigned long base_addr,
> > +             update_shadow_fn fn)
> > +{
> > +     struct cfi_shadow *prev;
> > +     struct cfi_shadow *next;
> > +     unsigned long min_addr, max_addr;
> > +
> > +     next = vmalloc(SHADOW_SIZE);
> > +
> > +     mutex_lock(&shadow_update_lock);
> > +     prev = rcu_dereference_protected(cfi_shadow,
> > +                                      mutex_is_locked(&shadow_update_lock));
> > +
> > +     if (next) {
> > +             next->base = base_addr >> PAGE_SHIFT;
> > +             prepare_next_shadow(prev, next);
> > +
> > +             min_addr = (unsigned long)mod->core_layout.base;
> > +             max_addr = min_addr + mod->core_layout.text_size;
> > +             fn(next, mod, min_addr & PAGE_MASK, max_addr & PAGE_MASK);
> > +
> > +             set_memory_ro((unsigned long)next, SHADOW_PAGES);
> > +     }
> > +
> > +     rcu_assign_pointer(cfi_shadow, next);
> > +     mutex_unlock(&shadow_update_lock);
> > +     synchronize_rcu_expedited();
>
> expedited is BAD(tm), why is it required and why doesn't it have a
> comment?

Ah, this uses synchronize_rcu_expedited() because we have a case where
synchronize_rcu() hangs here with a specific SoC family after the
vendor's cpu_pm driver powers down CPU cores. Would you say expedited
is bad enough that we should avoid it here? The function is called
only when kernel modules are loaded or unloaded, so not very
frequently.

Sami

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v2 01/17] add support for Clang CFI
  2021-03-18 23:48     ` Sami Tolvanen
@ 2021-03-19 12:26       ` Peter Zijlstra
  2021-03-19 13:52         ` Paul E. McKenney
  0 siblings, 1 reply; 35+ messages in thread
From: Peter Zijlstra @ 2021-03-19 12:26 UTC (permalink / raw)
  To: Sami Tolvanen
  Cc: Kees Cook, Nathan Chancellor, Nick Desaulniers, Masahiro Yamada,
	Will Deacon, Jessica Yu, Arnd Bergmann, Tejun Heo,
	Paul E. McKenney, Christoph Hellwig, bpf, linux-hardening,
	linux-arch, linux-arm-kernel, linux-kbuild, PCI, LKML

On Thu, Mar 18, 2021 at 04:48:43PM -0700, Sami Tolvanen wrote:
> On Thu, Mar 18, 2021 at 3:29 PM Peter Zijlstra <peterz@infradead.org> wrote:
> >
> > On Thu, Mar 18, 2021 at 10:10:55AM -0700, Sami Tolvanen wrote:
> > > +static void update_shadow(struct module *mod, unsigned long base_addr,
> > > +             update_shadow_fn fn)
> > > +{
> > > +     struct cfi_shadow *prev;
> > > +     struct cfi_shadow *next;
> > > +     unsigned long min_addr, max_addr;
> > > +
> > > +     next = vmalloc(SHADOW_SIZE);
> > > +
> > > +     mutex_lock(&shadow_update_lock);
> > > +     prev = rcu_dereference_protected(cfi_shadow,
> > > +                                      mutex_is_locked(&shadow_update_lock));
> > > +
> > > +     if (next) {
> > > +             next->base = base_addr >> PAGE_SHIFT;
> > > +             prepare_next_shadow(prev, next);
> > > +
> > > +             min_addr = (unsigned long)mod->core_layout.base;
> > > +             max_addr = min_addr + mod->core_layout.text_size;
> > > +             fn(next, mod, min_addr & PAGE_MASK, max_addr & PAGE_MASK);
> > > +
> > > +             set_memory_ro((unsigned long)next, SHADOW_PAGES);
> > > +     }
> > > +
> > > +     rcu_assign_pointer(cfi_shadow, next);
> > > +     mutex_unlock(&shadow_update_lock);
> > > +     synchronize_rcu_expedited();
> >
> > expedited is BAD(tm), why is it required and why doesn't it have a
> > comment?
> 
> Ah, this uses synchronize_rcu_expedited() because we have a case where
> synchronize_rcu() hangs here with a specific SoC family after the
> vendor's cpu_pm driver powers down CPU cores.

Broken vendor drivers seem like an exceedingly poor reason for this.

> Would you say expedited is bad enough that we should avoid it here?
> The function is called only when kernel modules are loaded or
> unloaded, so not very frequently.

Module unload is pretty crap (it has stop_machine), so an expedited
would not really be noticable, but module load isn't nearly as bad.

Also, getting the vendor to fix their driver seems like a good thing :-)

So please consider using regular synchronize_rcu() here.

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v2 01/17] add support for Clang CFI
  2021-03-19 12:26       ` Peter Zijlstra
@ 2021-03-19 13:52         ` Paul E. McKenney
  2021-03-19 16:17           ` Sami Tolvanen
  0 siblings, 1 reply; 35+ messages in thread
From: Paul E. McKenney @ 2021-03-19 13:52 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Sami Tolvanen, Kees Cook, Nathan Chancellor, Nick Desaulniers,
	Masahiro Yamada, Will Deacon, Jessica Yu, Arnd Bergmann,
	Tejun Heo, Christoph Hellwig, bpf, linux-hardening, linux-arch,
	linux-arm-kernel, linux-kbuild, PCI, LKML

On Fri, Mar 19, 2021 at 01:26:59PM +0100, Peter Zijlstra wrote:
> On Thu, Mar 18, 2021 at 04:48:43PM -0700, Sami Tolvanen wrote:
> > On Thu, Mar 18, 2021 at 3:29 PM Peter Zijlstra <peterz@infradead.org> wrote:
> > >
> > > On Thu, Mar 18, 2021 at 10:10:55AM -0700, Sami Tolvanen wrote:
> > > > +static void update_shadow(struct module *mod, unsigned long base_addr,
> > > > +             update_shadow_fn fn)
> > > > +{
> > > > +     struct cfi_shadow *prev;
> > > > +     struct cfi_shadow *next;
> > > > +     unsigned long min_addr, max_addr;
> > > > +
> > > > +     next = vmalloc(SHADOW_SIZE);
> > > > +
> > > > +     mutex_lock(&shadow_update_lock);
> > > > +     prev = rcu_dereference_protected(cfi_shadow,
> > > > +                                      mutex_is_locked(&shadow_update_lock));
> > > > +
> > > > +     if (next) {
> > > > +             next->base = base_addr >> PAGE_SHIFT;
> > > > +             prepare_next_shadow(prev, next);
> > > > +
> > > > +             min_addr = (unsigned long)mod->core_layout.base;
> > > > +             max_addr = min_addr + mod->core_layout.text_size;
> > > > +             fn(next, mod, min_addr & PAGE_MASK, max_addr & PAGE_MASK);
> > > > +
> > > > +             set_memory_ro((unsigned long)next, SHADOW_PAGES);
> > > > +     }
> > > > +
> > > > +     rcu_assign_pointer(cfi_shadow, next);
> > > > +     mutex_unlock(&shadow_update_lock);
> > > > +     synchronize_rcu_expedited();
> > >
> > > expedited is BAD(tm), why is it required and why doesn't it have a
> > > comment?
> > 
> > Ah, this uses synchronize_rcu_expedited() because we have a case where
> > synchronize_rcu() hangs here with a specific SoC family after the
> > vendor's cpu_pm driver powers down CPU cores.
> 
> Broken vendor drivers seem like an exceedingly poor reason for this.

The vendor is supposed to make sure that RCU sees the CPU cores as either
deep idle or offline before powering them down.  My guess is that the
CPU is powered down, but RCU (and probably much else in the system)
thinks that the CPU is still up and running.  So I bet that you are
seeing other issues as well.

I take it that the IPIs from synchronize_rcu_expedited() have the effect
of momentarily powering up those CPUs?

							Thanx, Paul

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v2 01/17] add support for Clang CFI
  2021-03-19 13:52         ` Paul E. McKenney
@ 2021-03-19 16:17           ` Sami Tolvanen
  2021-03-19 17:03             ` Paul E. McKenney
  0 siblings, 1 reply; 35+ messages in thread
From: Sami Tolvanen @ 2021-03-19 16:17 UTC (permalink / raw)
  To: Paul E. McKenney
  Cc: Peter Zijlstra, Kees Cook, Nathan Chancellor, Nick Desaulniers,
	Masahiro Yamada, Will Deacon, Jessica Yu, Arnd Bergmann,
	Tejun Heo, Christoph Hellwig, bpf, linux-hardening, linux-arch,
	linux-arm-kernel, linux-kbuild, PCI, LKML

On Fri, Mar 19, 2021 at 6:52 AM Paul E. McKenney <paulmck@kernel.org> wrote:
>
> On Fri, Mar 19, 2021 at 01:26:59PM +0100, Peter Zijlstra wrote:
> > On Thu, Mar 18, 2021 at 04:48:43PM -0700, Sami Tolvanen wrote:
> > > On Thu, Mar 18, 2021 at 3:29 PM Peter Zijlstra <peterz@infradead.org> wrote:
> > > >
> > > > On Thu, Mar 18, 2021 at 10:10:55AM -0700, Sami Tolvanen wrote:
> > > > > +static void update_shadow(struct module *mod, unsigned long base_addr,
> > > > > +             update_shadow_fn fn)
> > > > > +{
> > > > > +     struct cfi_shadow *prev;
> > > > > +     struct cfi_shadow *next;
> > > > > +     unsigned long min_addr, max_addr;
> > > > > +
> > > > > +     next = vmalloc(SHADOW_SIZE);
> > > > > +
> > > > > +     mutex_lock(&shadow_update_lock);
> > > > > +     prev = rcu_dereference_protected(cfi_shadow,
> > > > > +                                      mutex_is_locked(&shadow_update_lock));
> > > > > +
> > > > > +     if (next) {
> > > > > +             next->base = base_addr >> PAGE_SHIFT;
> > > > > +             prepare_next_shadow(prev, next);
> > > > > +
> > > > > +             min_addr = (unsigned long)mod->core_layout.base;
> > > > > +             max_addr = min_addr + mod->core_layout.text_size;
> > > > > +             fn(next, mod, min_addr & PAGE_MASK, max_addr & PAGE_MASK);
> > > > > +
> > > > > +             set_memory_ro((unsigned long)next, SHADOW_PAGES);
> > > > > +     }
> > > > > +
> > > > > +     rcu_assign_pointer(cfi_shadow, next);
> > > > > +     mutex_unlock(&shadow_update_lock);
> > > > > +     synchronize_rcu_expedited();
> > > >
> > > > expedited is BAD(tm), why is it required and why doesn't it have a
> > > > comment?
> > >
> > > Ah, this uses synchronize_rcu_expedited() because we have a case where
> > > synchronize_rcu() hangs here with a specific SoC family after the
> > > vendor's cpu_pm driver powers down CPU cores.
> >
> > Broken vendor drivers seem like an exceedingly poor reason for this.
>
> The vendor is supposed to make sure that RCU sees the CPU cores as either
> deep idle or offline before powering them down.  My guess is that the
> CPU is powered down, but RCU (and probably much else in the system)
> thinks that the CPU is still up and running.  So I bet that you are
> seeing other issues as well.
>
> I take it that the IPIs from synchronize_rcu_expedited() have the effect
> of momentarily powering up those CPUs?

I suspect you're correct. I'll change this to use synchronize_rcu() in v3.

Sami

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v2 01/17] add support for Clang CFI
  2021-03-19 16:17           ` Sami Tolvanen
@ 2021-03-19 17:03             ` Paul E. McKenney
  0 siblings, 0 replies; 35+ messages in thread
From: Paul E. McKenney @ 2021-03-19 17:03 UTC (permalink / raw)
  To: Sami Tolvanen
  Cc: Peter Zijlstra, Kees Cook, Nathan Chancellor, Nick Desaulniers,
	Masahiro Yamada, Will Deacon, Jessica Yu, Arnd Bergmann,
	Tejun Heo, Christoph Hellwig, bpf, linux-hardening, linux-arch,
	linux-arm-kernel, linux-kbuild, PCI, LKML

On Fri, Mar 19, 2021 at 09:17:14AM -0700, Sami Tolvanen wrote:
> On Fri, Mar 19, 2021 at 6:52 AM Paul E. McKenney <paulmck@kernel.org> wrote:
> >
> > On Fri, Mar 19, 2021 at 01:26:59PM +0100, Peter Zijlstra wrote:
> > > On Thu, Mar 18, 2021 at 04:48:43PM -0700, Sami Tolvanen wrote:
> > > > On Thu, Mar 18, 2021 at 3:29 PM Peter Zijlstra <peterz@infradead.org> wrote:
> > > > >
> > > > > On Thu, Mar 18, 2021 at 10:10:55AM -0700, Sami Tolvanen wrote:
> > > > > > +static void update_shadow(struct module *mod, unsigned long base_addr,
> > > > > > +             update_shadow_fn fn)
> > > > > > +{
> > > > > > +     struct cfi_shadow *prev;
> > > > > > +     struct cfi_shadow *next;
> > > > > > +     unsigned long min_addr, max_addr;
> > > > > > +
> > > > > > +     next = vmalloc(SHADOW_SIZE);
> > > > > > +
> > > > > > +     mutex_lock(&shadow_update_lock);
> > > > > > +     prev = rcu_dereference_protected(cfi_shadow,
> > > > > > +                                      mutex_is_locked(&shadow_update_lock));
> > > > > > +
> > > > > > +     if (next) {
> > > > > > +             next->base = base_addr >> PAGE_SHIFT;
> > > > > > +             prepare_next_shadow(prev, next);
> > > > > > +
> > > > > > +             min_addr = (unsigned long)mod->core_layout.base;
> > > > > > +             max_addr = min_addr + mod->core_layout.text_size;
> > > > > > +             fn(next, mod, min_addr & PAGE_MASK, max_addr & PAGE_MASK);
> > > > > > +
> > > > > > +             set_memory_ro((unsigned long)next, SHADOW_PAGES);
> > > > > > +     }
> > > > > > +
> > > > > > +     rcu_assign_pointer(cfi_shadow, next);
> > > > > > +     mutex_unlock(&shadow_update_lock);
> > > > > > +     synchronize_rcu_expedited();
> > > > >
> > > > > expedited is BAD(tm), why is it required and why doesn't it have a
> > > > > comment?
> > > >
> > > > Ah, this uses synchronize_rcu_expedited() because we have a case where
> > > > synchronize_rcu() hangs here with a specific SoC family after the
> > > > vendor's cpu_pm driver powers down CPU cores.
> > >
> > > Broken vendor drivers seem like an exceedingly poor reason for this.
> >
> > The vendor is supposed to make sure that RCU sees the CPU cores as either
> > deep idle or offline before powering them down.  My guess is that the
> > CPU is powered down, but RCU (and probably much else in the system)
> > thinks that the CPU is still up and running.  So I bet that you are
> > seeing other issues as well.
> >
> > I take it that the IPIs from synchronize_rcu_expedited() have the effect
> > of momentarily powering up those CPUs?
> 
> I suspect you're correct. I'll change this to use synchronize_rcu() in v3.

You might also suggest to the vendor that they look for a missing
rcu_idle_enter(), rcu_irq_exit(), or similar on the code path that the
outgoing CPUs follow before getting powered down.  That way, they won't
be wasting power from irrelevant IPIs.  You see, RCU will eventually
send IPIs to non-responding CPUs for normal grace periods.

							Thanx, Paul

^ permalink raw reply	[flat|nested] 35+ messages in thread

end of thread, other threads:[~2021-03-19 17:04 UTC | newest]

Thread overview: 35+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-03-18 17:10 [PATCH v2 00/17] Add support for Clang CFI Sami Tolvanen
2021-03-18 17:10 ` [PATCH v2 01/17] add " Sami Tolvanen
2021-03-18 22:29   ` Peter Zijlstra
2021-03-18 23:48     ` Sami Tolvanen
2021-03-19 12:26       ` Peter Zijlstra
2021-03-19 13:52         ` Paul E. McKenney
2021-03-19 16:17           ` Sami Tolvanen
2021-03-19 17:03             ` Paul E. McKenney
2021-03-18 17:10 ` [PATCH v2 02/17] cfi: add __cficanonical Sami Tolvanen
2021-03-18 17:49   ` Nick Desaulniers
2021-03-18 17:10 ` [PATCH v2 03/17] mm: add generic __va_function and __pa_function macros Sami Tolvanen
2021-03-18 17:10 ` [PATCH v2 04/17] module: ensure __cfi_check alignment Sami Tolvanen
2021-03-18 19:27   ` Nick Desaulniers
2021-03-18 21:43     ` Sami Tolvanen
2021-03-18 17:10 ` [PATCH v2 05/17] workqueue: use WARN_ON_FUNCTION_MISMATCH Sami Tolvanen
2021-03-18 18:50   ` Nick Desaulniers
2021-03-18 21:38     ` Sami Tolvanen
2021-03-18 17:11 ` [PATCH v2 06/17] kthread: " Sami Tolvanen
2021-03-18 17:11 ` [PATCH v2 07/17] kallsyms: strip ThinLTO hashes from static functions Sami Tolvanen
2021-03-18 19:00   ` Nick Desaulniers
2021-03-18 21:41     ` Sami Tolvanen
2021-03-18 17:11 ` [PATCH v2 08/17] bpf: disable CFI in dispatcher functions Sami Tolvanen
2021-03-18 17:11 ` [PATCH v2 09/17] lib/list_sort: fix function type mismatches Sami Tolvanen
2021-03-18 18:31   ` Nick Desaulniers
2021-03-18 21:31     ` Sami Tolvanen
2021-03-18 17:11 ` [PATCH v2 10/17] lkdtm: use __va_function Sami Tolvanen
2021-03-18 18:43   ` Nick Desaulniers
2021-03-18 18:45     ` Nick Desaulniers
2021-03-18 17:11 ` [PATCH v2 11/17] psci: use __pa_function for cpu_resume Sami Tolvanen
2021-03-18 17:11 ` [PATCH v2 12/17] arm64: implement __va_function Sami Tolvanen
2021-03-18 17:11 ` [PATCH v2 13/17] arm64: use __pa_function Sami Tolvanen
2021-03-18 17:11 ` [PATCH v2 14/17] arm64: add __nocfi to functions that jump to a physical address Sami Tolvanen
2021-03-18 17:11 ` [PATCH v2 15/17] arm64: add __nocfi to __apply_alternatives Sami Tolvanen
2021-03-18 17:11 ` [PATCH v2 16/17] KVM: arm64: Disable CFI for nVHE Sami Tolvanen
2021-03-18 17:11 ` [PATCH v2 17/17] arm64: allow CONFIG_CFI_CLANG to be selected Sami Tolvanen

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).