Commit 63f0c603 authored by Catalin Marinas's avatar Catalin Marinas Committed by Will Deacon
Browse files

arm64: Introduce prctl() options to control the tagged user addresses ABI



It is not desirable to relax the ABI to allow tagged user addresses into
the kernel indiscriminately. This patch introduces a prctl() interface
for enabling or disabling the tagged ABI with a global sysctl control
for preventing applications from enabling the relaxed ABI (meant for
testing user-space prctl() return error checking without reconfiguring
the kernel). The ABI properties are inherited by threads of the same
application and fork()'ed children but cleared on execve(). A Kconfig
option allows the overall disabling of the relaxed ABI.

The PR_SET_TAGGED_ADDR_CTRL will be expanded in the future to handle
MTE-specific settings like imprecise vs precise exceptions.

Reviewed-by: default avatarKees Cook <keescook@chromium.org>
Signed-off-by: default avatarCatalin Marinas <catalin.marinas@arm.com>
Signed-off-by: default avatarAndrey Konovalov <andreyknvl@google.com>
Signed-off-by: default avatarWill Deacon <will@kernel.org>
parent 2b835e24
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -1110,6 +1110,15 @@ config ARM64_SW_TTBR0_PAN
	  zeroed area and reserved ASID. The user access routines
	  restore the valid TTBR0_EL1 temporarily.

config ARM64_TAGGED_ADDR_ABI
	bool "Enable the tagged user addresses syscall ABI"
	default y
	help
	  When this option is enabled, user applications can opt in to a
	  relaxed ABI via prctl() allowing tagged addresses to be passed
	  to system calls as pointer arguments. For details, see
	  Documentation/arm64/tagged-address-abi.txt.

menuconfig COMPAT
	bool "Kernel support for 32-bit EL0"
	depends on ARM64_4K_PAGES || EXPERT
+8 −0
Original line number Diff line number Diff line
@@ -306,6 +306,14 @@ extern void __init minsigstksz_setup(void);
/* PR_PAC_RESET_KEYS prctl */
#define PAC_RESET_KEYS(tsk, arg)	ptrauth_prctl_reset_keys(tsk, arg)

#ifdef CONFIG_ARM64_TAGGED_ADDR_ABI
/* PR_{SET,GET}_TAGGED_ADDR_CTRL prctl */
long set_tagged_addr_ctrl(unsigned long arg);
long get_tagged_addr_ctrl(void);
#define SET_TAGGED_ADDR_CTRL(arg)	set_tagged_addr_ctrl(arg)
#define GET_TAGGED_ADDR_CTRL()		get_tagged_addr_ctrl()
#endif

/*
 * For CONFIG_GCC_PLUGIN_STACKLEAK
 *
+1 −0
Original line number Diff line number Diff line
@@ -90,6 +90,7 @@ void arch_release_task_struct(struct task_struct *tsk);
#define TIF_SVE			23	/* Scalable Vector Extension in use */
#define TIF_SVE_VL_INHERIT	24	/* Inherit sve_vl_onexec across exec */
#define TIF_SSBD		25	/* Wants SSB mitigation */
#define TIF_TAGGED_ADDR		26	/* Allow tagged user addresses */

#define _TIF_SIGPENDING		(1 << TIF_SIGPENDING)
#define _TIF_NEED_RESCHED	(1 << TIF_NEED_RESCHED)
+3 −1
Original line number Diff line number Diff line
@@ -62,6 +62,8 @@ static inline unsigned long __range_ok(const void __user *addr, unsigned long si
{
	unsigned long ret, limit = current_thread_info()->addr_limit;

	if (IS_ENABLED(CONFIG_ARM64_TAGGED_ADDR_ABI) &&
	    test_thread_flag(TIF_TAGGED_ADDR))
		addr = untagged_addr(addr);

	__chk_user_ptr(addr);
+73 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/stddef.h>
#include <linux/sysctl.h>
#include <linux/unistd.h>
#include <linux/user.h>
#include <linux/delay.h>
@@ -38,6 +39,7 @@
#include <trace/events/power.h>
#include <linux/percpu.h>
#include <linux/thread_info.h>
#include <linux/prctl.h>

#include <asm/alternative.h>
#include <asm/arch_gicv3.h>
@@ -307,11 +309,18 @@ static void tls_thread_flush(void)
	}
}

static void flush_tagged_addr_state(void)
{
	if (IS_ENABLED(CONFIG_ARM64_TAGGED_ADDR_ABI))
		clear_thread_flag(TIF_TAGGED_ADDR);
}

void flush_thread(void)
{
	fpsimd_flush_thread();
	tls_thread_flush();
	flush_ptrace_hw_breakpoint(current);
	flush_tagged_addr_state();
}

void release_thread(struct task_struct *dead_task)
@@ -565,3 +574,67 @@ void arch_setup_new_exec(void)

	ptrauth_thread_init_user(current);
}

#ifdef CONFIG_ARM64_TAGGED_ADDR_ABI
/*
 * Control the relaxed ABI allowing tagged user addresses into the kernel.
 */
static unsigned int tagged_addr_prctl_allowed = 1;

long set_tagged_addr_ctrl(unsigned long arg)
{
	if (!tagged_addr_prctl_allowed)
		return -EINVAL;
	if (is_compat_task())
		return -EINVAL;
	if (arg & ~PR_TAGGED_ADDR_ENABLE)
		return -EINVAL;

	update_thread_flag(TIF_TAGGED_ADDR, arg & PR_TAGGED_ADDR_ENABLE);

	return 0;
}

long get_tagged_addr_ctrl(void)
{
	if (!tagged_addr_prctl_allowed)
		return -EINVAL;
	if (is_compat_task())
		return -EINVAL;

	if (test_thread_flag(TIF_TAGGED_ADDR))
		return PR_TAGGED_ADDR_ENABLE;

	return 0;
}

/*
 * Global sysctl to disable the tagged user addresses support. This control
 * only prevents the tagged address ABI enabling via prctl() and does not
 * disable it for tasks that already opted in to the relaxed ABI.
 */
static int zero;
static int one = 1;

static struct ctl_table tagged_addr_sysctl_table[] = {
	{
		.procname	= "tagged_addr",
		.mode		= 0644,
		.data		= &tagged_addr_prctl_allowed,
		.maxlen		= sizeof(int),
		.proc_handler	= proc_dointvec_minmax,
		.extra1		= &zero,
		.extra2		= &one,
	},
	{ }
};

static int __init tagged_addr_init(void)
{
	if (!register_sysctl("abi", tagged_addr_sysctl_table))
		return -EINVAL;
	return 0;
}

core_initcall(tagged_addr_init);
#endif	/* CONFIG_ARM64_TAGGED_ADDR_ABI */
Loading