Unverified Commit 7f7d3ea6 authored by Palmer Dabbelt's avatar Palmer Dabbelt
Browse files

Merge patch series "riscv: KCFI support"

Sami Tolvanen <samitolvanen@google.com> says:

This series adds KCFI support for RISC-V. KCFI is a fine-grained
forward-edge control-flow integrity scheme supported in Clang >=16,
which ensures indirect calls in instrumented code can only branch to
functions whose type matches the function pointer type, thus making
code reuse attacks more difficult.

Patch 1 implements a pt_regs based syscall wrapper to address
function pointer type mismatches in syscall handling. Patches 2 and 3
annotate indirectly called assembly functions with CFI types. Patch 4
implements error handling for indirect call checks. Patch 5 disables
CFI for arch/riscv/purgatory. Patch 6 finally allows CONFIG_CFI_CLANG
to be enabled for RISC-V.

Note that Clang 16 has a generic architecture-agnostic KCFI
implementation, which does work with the kernel, but doesn't produce
a stable code sequence for indirect call checks, which means
potential failures just trap and won't result in informative error
messages. Clang 17 includes a RISC-V specific back-end implementation
for KCFI, which emits a predictable code sequence for the checks and a
.kcfi_traps section with locations of the traps, which patch 5 uses to
produce more useful errors.

The type mismatch fixes and annotations in the first three patches
also become necessary in future if the kernel decides to support
fine-grained CFI implemented using the hardware landing pad
feature proposed in the in-progress Zicfisslp extension. Once the
specification is ratified and hardware support emerges, implementing
runtime patching support that replaces KCFI instrumentation with
Zicfisslp landing pads might also be feasible (similarly to KCFI to
FineIBT patching on x86_64), allowing distributions to ship a unified
kernel binary for all devices.

* b4-shazam-merge:
  riscv: Allow CONFIG_CFI_CLANG to be selected
  riscv/purgatory: Disable CFI
  riscv: Add CFI error handling
  riscv: Add ftrace_stub_graph
  riscv: Add types to indirectly called assembly functions
  riscv: Implement syscall wrappers

Link: https://lore.kernel.org/r/20230710183544.999540-8-samitolvanen@google.com


Signed-off-by: default avatarPalmer Dabbelt <palmer@rivosinc.com>
parents 9bdd9248 74f8fc31
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ config RISCV
	select ARCH_HAS_SET_MEMORY if MMU
	select ARCH_HAS_STRICT_KERNEL_RWX if MMU && !XIP_KERNEL
	select ARCH_HAS_STRICT_MODULE_RWX if MMU && !XIP_KERNEL
	select ARCH_HAS_SYSCALL_WRAPPER
	select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST
	select ARCH_HAS_UBSAN_SANITIZE_ALL
	select ARCH_HAS_VDSO_DATA
@@ -42,12 +43,14 @@ config RISCV
	select ARCH_OPTIONAL_KERNEL_RWX_DEFAULT
	select ARCH_STACKWALK
	select ARCH_SUPPORTS_ATOMIC_RMW
	select ARCH_SUPPORTS_CFI_CLANG
	select ARCH_SUPPORTS_DEBUG_PAGEALLOC if MMU
	select ARCH_SUPPORTS_HUGETLBFS if MMU
	select ARCH_SUPPORTS_PAGE_TABLE_CHECK if MMU
	select ARCH_SUPPORTS_PER_VMA_LOCK if MMU
	select ARCH_USE_MEMTEST
	select ARCH_USE_QUEUED_RWLOCKS
	select ARCH_USES_CFI_TRAPS if CFI_CLANG
	select ARCH_WANT_DEFAULT_TOPDOWN_MMAP_LAYOUT if MMU
	select ARCH_WANT_FRAME_POINTERS
	select ARCH_WANT_GENERAL_HUGETLB if !RISCV_ISA_SVNAPOT
+22 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _ASM_RISCV_CFI_H
#define _ASM_RISCV_CFI_H

/*
 * Clang Control Flow Integrity (CFI) support.
 *
 * Copyright (C) 2023 Google LLC
 */

#include <linux/cfi.h>

#ifdef CONFIG_CFI_CLANG
enum bug_trap_type handle_cfi_failure(struct pt_regs *regs);
#else
static inline enum bug_trap_type handle_cfi_failure(struct pt_regs *regs)
{
	return BUG_TRAP_TYPE_NONE;
}
#endif /* CONFIG_CFI_CLANG */

#endif /* _ASM_RISCV_CFI_H */
+10 −0
Original line number Diff line number Diff line
@@ -63,6 +63,7 @@
#define RVG_RS1_OPOFF		15
#define RVG_RS2_OPOFF		20
#define RVG_RD_OPOFF		7
#define RVG_RS1_MASK		GENMASK(4, 0)
#define RVG_RD_MASK		GENMASK(4, 0)

/* The bit field of immediate value in RVC J instruction */
@@ -129,6 +130,7 @@
#define RVC_C2_RS1_OPOFF	7
#define RVC_C2_RS2_OPOFF	2
#define RVC_C2_RD_OPOFF		7
#define RVC_C2_RS1_MASK		GENMASK(4, 0)

/* parts of opcode for RVG*/
#define RVG_OPCODE_FENCE	0x0f
@@ -278,6 +280,10 @@ static __always_inline bool riscv_insn_is_branch(u32 code)
#define RV_X(X, s, mask)  (((X) >> (s)) & (mask))
#define RVC_X(X, s, mask) RV_X(X, s, mask)

#define RV_EXTRACT_RS1_REG(x) \
	({typeof(x) x_ = (x); \
	(RV_X(x_, RVG_RS1_OPOFF, RVG_RS1_MASK)); })

#define RV_EXTRACT_RD_REG(x) \
	({typeof(x) x_ = (x); \
	(RV_X(x_, RVG_RD_OPOFF, RVG_RD_MASK)); })
@@ -305,6 +311,10 @@ static __always_inline bool riscv_insn_is_branch(u32 code)
	(RV_X(x_, RV_B_IMM_11_OPOFF, RV_B_IMM_11_MASK) << RV_B_IMM_11_OFF) | \
	(RV_IMM_SIGN(x_) << RV_B_IMM_SIGN_OFF); })

#define RVC_EXTRACT_C2_RS1_REG(x) \
	({typeof(x) x_ = (x); \
	(RV_X(x_, RVC_C2_RS1_OPOFF, RVC_C2_RS1_MASK)); })

#define RVC_EXTRACT_JTYPE_IMM(x) \
	({typeof(x) x_ = (x); \
	(RVC_X(x_, RVC_J_IMM_3_1_OPOFF, RVC_J_IMM_3_1_MASK) << RVC_J_IMM_3_1_OFF) | \
+2 −3
Original line number Diff line number Diff line
@@ -75,7 +75,7 @@ static inline int syscall_get_arch(struct task_struct *task)
#endif
}

typedef long (*syscall_t)(ulong, ulong, ulong, ulong, ulong, ulong, ulong);
typedef long (*syscall_t)(const struct pt_regs *);
static inline void syscall_handler(struct pt_regs *regs, ulong syscall)
{
	syscall_t fn;
@@ -87,8 +87,7 @@ static inline void syscall_handler(struct pt_regs *regs, ulong syscall)
#endif
		fn = sys_call_table[syscall];

	regs->a0 = fn(regs->orig_a0, regs->a1, regs->a2,
		      regs->a3, regs->a4, regs->a5, regs->a6);
	regs->a0 = fn(regs);
}

static inline bool arch_syscall_is_vdso_sigreturn(struct pt_regs *regs)
+87 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0 */
/*
 * syscall_wrapper.h - riscv specific wrappers to syscall definitions
 *
 * Based on arch/arm64/include/syscall_wrapper.h
 */

#ifndef __ASM_SYSCALL_WRAPPER_H
#define __ASM_SYSCALL_WRAPPER_H

#include <asm/ptrace.h>

asmlinkage long __riscv_sys_ni_syscall(const struct pt_regs *);

#define SC_RISCV_REGS_TO_ARGS(x, ...)				\
	__MAP(x,__SC_ARGS					\
	      ,,regs->orig_a0,,regs->a1,,regs->a2		\
	      ,,regs->a3,,regs->a4,,regs->a5,,regs->a6)

#ifdef CONFIG_COMPAT

#define COMPAT_SYSCALL_DEFINEx(x, name, ...)						\
	asmlinkage long __riscv_compat_sys##name(const struct pt_regs *regs);		\
	ALLOW_ERROR_INJECTION(__riscv_compat_sys##name, ERRNO);				\
	static long __se_compat_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__));		\
	static inline long __do_compat_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__));	\
	asmlinkage long __riscv_compat_sys##name(const struct pt_regs *regs)		\
	{										\
		return __se_compat_sys##name(SC_RISCV_REGS_TO_ARGS(x,__VA_ARGS__));	\
	}										\
	static long __se_compat_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__))		\
	{										\
		return __do_compat_sys##name(__MAP(x,__SC_DELOUSE,__VA_ARGS__));	\
	}										\
	static inline long __do_compat_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__))

#define COMPAT_SYSCALL_DEFINE0(sname)							\
	asmlinkage long __riscv_compat_sys_##sname(const struct pt_regs *__unused);	\
	ALLOW_ERROR_INJECTION(__riscv_compat_sys_##sname, ERRNO);			\
	asmlinkage long __riscv_compat_sys_##sname(const struct pt_regs *__unused)

#define COND_SYSCALL_COMPAT(name) 							\
	asmlinkage long __weak __riscv_compat_sys_##name(const struct pt_regs *regs);	\
	asmlinkage long __weak __riscv_compat_sys_##name(const struct pt_regs *regs)	\
	{										\
		return sys_ni_syscall();						\
	}

#define COMPAT_SYS_NI(name) \
	SYSCALL_ALIAS(__riscv_compat_sys_##name, sys_ni_posix_timers);

#endif /* CONFIG_COMPAT */

#define __SYSCALL_DEFINEx(x, name, ...)						\
	asmlinkage long __riscv_sys##name(const struct pt_regs *regs);		\
	ALLOW_ERROR_INJECTION(__riscv_sys##name, ERRNO);			\
	static long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__));		\
	static inline long __do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__));	\
	asmlinkage long __riscv_sys##name(const struct pt_regs *regs)		\
	{									\
		return __se_sys##name(SC_RISCV_REGS_TO_ARGS(x,__VA_ARGS__));	\
	}									\
	static long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__))		\
	{									\
		long ret = __do_sys##name(__MAP(x,__SC_CAST,__VA_ARGS__));	\
		__MAP(x,__SC_TEST,__VA_ARGS__);					\
		__PROTECT(x, ret,__MAP(x,__SC_ARGS,__VA_ARGS__));		\
		return ret;							\
	}									\
	static inline long __do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__))

#define SYSCALL_DEFINE0(sname)							\
	SYSCALL_METADATA(_##sname, 0);						\
	asmlinkage long __riscv_sys_##sname(const struct pt_regs *__unused);	\
	ALLOW_ERROR_INJECTION(__riscv_sys_##sname, ERRNO);			\
	asmlinkage long __riscv_sys_##sname(const struct pt_regs *__unused)

#define COND_SYSCALL(name)							\
	asmlinkage long __weak __riscv_sys_##name(const struct pt_regs *regs);	\
	asmlinkage long __weak __riscv_sys_##name(const struct pt_regs *regs)	\
	{									\
		return sys_ni_syscall();					\
	}

#define SYS_NI(name) SYSCALL_ALIAS(__riscv_sys_##name, sys_ni_posix_timers);

#endif /* __ASM_SYSCALL_WRAPPER_H */
Loading