Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 57f4959b authored by James Morse's avatar James Morse Committed by Catalin Marinas
Browse files

arm64: kernel: Add support for User Access Override



'User Access Override' is a new ARMv8.2 feature which allows the
unprivileged load and store instructions to be overridden to behave in
the normal way.

This patch converts {get,put}_user() and friends to use ldtr*/sttr*
instructions - so that they can only access EL0 memory, then enables
UAO when fs==KERNEL_DS so that these functions can access kernel memory.

This allows user space's read/write permissions to be checked against the
page tables, instead of testing addr<USER_DS, then using the kernel's
read/write permissions.

Signed-off-by: default avatarJames Morse <james.morse@arm.com>
[catalin.marinas@arm.com: move uao_thread_switch() above dsb()]
Signed-off-by: default avatarCatalin Marinas <catalin.marinas@arm.com>
parent 406e3087
Loading
Loading
Loading
Loading
+21 −0
Original line number Diff line number Diff line
@@ -756,6 +756,27 @@ config ARM64_LSE_ATOMICS

endmenu

config ARM64_UAO
	bool "Enable support for User Access Override (UAO)"
	default y
	help
	  User Access Override (UAO; part of the ARMv8.2 Extensions)
	  causes the 'unprivileged' variant of the load/store instructions to
	  be overriden to be privileged.

	  This option changes get_user() and friends to use the 'unprivileged'
	  variant of the load/store instructions. This ensures that user-space
	  really did have access to the supplied memory. When addr_limit is
	  set to kernel memory the UAO bit will be set, allowing privileged
	  access to kernel memory.

	  Choosing this option will cause copy_to_user() et al to use user-space
	  memory permissions.

	  The feature is detected at runtime, the kernel will use the
	  regular load/store instructions if the cpu does not implement the
	  feature.

endmenu

menu "Boot options"
+72 −0
Original line number Diff line number Diff line
#ifndef __ASM_ALTERNATIVE_H
#define __ASM_ALTERNATIVE_H

#include <asm/cpufeature.h>

#ifndef __ASSEMBLY__

#include <linux/init.h>
@@ -63,6 +65,8 @@ void apply_alternatives(void *start, size_t length);

#else

#include <asm/assembler.h>

.macro altinstruction_entry orig_offset alt_offset feature orig_len alt_len
	.word \orig_offset - .
	.word \alt_offset - .
@@ -136,6 +140,74 @@ void apply_alternatives(void *start, size_t length);
	alternative_insn insn1, insn2, cap, IS_ENABLED(cfg)


/*
 * Generate the assembly for UAO alternatives with exception table entries.
 * This is complicated as there is no post-increment or pair versions of the
 * unprivileged instructions, and USER() only works for single instructions.
 */
#ifdef CONFIG_ARM64_UAO
	.macro uao_ldp l, reg1, reg2, addr, post_inc
		alternative_if_not ARM64_HAS_UAO
8888:			ldp	\reg1, \reg2, [\addr], \post_inc;
8889:			nop;
			nop;
		alternative_else
			ldtr	\reg1, [\addr];
			ldtr	\reg2, [\addr, #8];
			add	\addr, \addr, \post_inc;
		alternative_endif

		.section __ex_table,"a";
		.align	3;
		.quad	8888b,\l;
		.quad	8889b,\l;
		.previous;
	.endm

	.macro uao_stp l, reg1, reg2, addr, post_inc
		alternative_if_not ARM64_HAS_UAO
8888:			stp	\reg1, \reg2, [\addr], \post_inc;
8889:			nop;
			nop;
		alternative_else
			sttr	\reg1, [\addr];
			sttr	\reg2, [\addr, #8];
			add	\addr, \addr, \post_inc;
		alternative_endif

		.section __ex_table,"a";
		.align	3;
		.quad	8888b,\l;
		.quad	8889b,\l;
		.previous
	.endm

	.macro uao_user_alternative l, inst, alt_inst, reg, addr, post_inc
		alternative_if_not ARM64_HAS_UAO
8888:			\inst	\reg, [\addr], \post_inc;
			nop;
		alternative_else
			\alt_inst	\reg, [\addr];
			add		\addr, \addr, \post_inc;
		alternative_endif

		.section __ex_table,"a";
		.align	3;
		.quad	8888b,\l;
		.previous
	.endm
#else
	.macro uao_ldp l, reg1, reg2, addr, post_inc
		USER(\l, ldp \reg1, \reg2, [\addr], \post_inc)
	.endm
	.macro uao_stp l, reg1, reg2, addr, post_inc
		USER(\l, stp \reg1, \reg2, [\addr], \post_inc)
	.endm
	.macro uao_user_alternative l, inst, alt_inst, reg, addr, post_inc
		USER(\l, \inst \reg, [\addr], \post_inc)
	.endm
#endif

#endif  /*  __ASSEMBLY__  */

/*
+2 −1
Original line number Diff line number Diff line
@@ -31,8 +31,9 @@
#define ARM64_WORKAROUND_CAVIUM_23154		6
#define ARM64_WORKAROUND_834220			7
#define ARM64_HAS_NO_HW_PREFETCH		8
#define ARM64_HAS_UAO				9

#define ARM64_NCAPS				9
#define ARM64_NCAPS				10

#ifndef __ASSEMBLY__

+1 −0
Original line number Diff line number Diff line
@@ -191,5 +191,6 @@ static inline void spin_lock_prefetch(const void *ptr)
#endif

void cpu_enable_pan(void *__unused);
void cpu_enable_uao(void *__unused);

#endif /* __ASM_PROCESSOR_H */
+3 −0
Original line number Diff line number Diff line
@@ -79,9 +79,12 @@
#define SYS_DCZID_EL0			sys_reg(3, 3, 0, 0, 7)

#define REG_PSTATE_PAN_IMM		sys_reg(0, 0, 4, 0, 4)
#define REG_PSTATE_UAO_IMM		sys_reg(0, 0, 4, 0, 3)

#define SET_PSTATE_PAN(x) __inst_arm(0xd5000000 | REG_PSTATE_PAN_IMM |\
				     (!!x)<<8 | 0x1f)
#define SET_PSTATE_UAO(x) __inst_arm(0xd5000000 | REG_PSTATE_UAO_IMM |\
				     (!!x)<<8 | 0x1f)

/* SCTLR_EL1 */
#define SCTLR_EL1_CP15BEN	(0x1 << 5)
Loading