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

Commit f077ee26 authored by Catalin Marinas's avatar Catalin Marinas Committed by Alistair Delva
Browse files

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



(Upstream commit 63f0c60379650d82250f22e4cf4137ef3dc4f43d).

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>
Change-Id: I2d52c5589b05415faab315c116245f1058d64750
Signed-off-by: default avatarAndrey Konovalov <andreyknvl@google.com>
Bug: 135692346
parent 14d0bfec
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -964,6 +964,15 @@ config ARM64_SSBD

	  If unsure, say Y.

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 ARMV8_DEPRECATED
	bool "Emulate deprecated/obsolete ARMv8 instructions"
	depends on COMPAT
+8 −0
Original line number Diff line number Diff line
@@ -274,6 +274,14 @@ extern void __init minsigstksz_setup(void);
#define SVE_SET_VL(arg)	sve_set_current_vl(arg)
#define SVE_GET_VL()	sve_get_current_vl()

#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
@@ -89,6 +89,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
@@ -76,6 +76,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
@@ -30,6 +30,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>
@@ -49,6 +50,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/compat.h>
@@ -269,11 +271,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)
@@ -515,3 +524,67 @@ void __used stackleak_check_alloca(unsigned long size)
}
EXPORT_SYMBOL(stackleak_check_alloca);
#endif

#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