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

Commit c254dc7b authored by Mark Rutland's avatar Mark Rutland Committed by Srinivasa Rao Kuppala
Browse files

BACKPORT: arm64: split thread_info from task stack



This patch moves arm64's struct thread_info from the task stack into
task_struct. This protects thread_info from corruption in the case of
stack overflows, and makes its address harder to determine if stack
addresses are leaked, making a number of attacks more difficult. Precise
detection and handling of overflow is left for subsequent patches.

Largely, this involves changing code to store the task_struct in sp_el0,
and acquire the thread_info from the task struct. Core code now
implements current_thread_info(), and as noted in <linux/sched.h> this
relies on offsetof(task_struct, thread_info) == 0, enforced by core
code.

This change means that the 'tsk' register used in entry.S now points to
a task_struct, rather than a thread_info as it used to. To make this
clear, the TI_* field offsets are renamed to TSK_TI_*, with asm-offsets
appropriately updated to account for the structural change.

Userspace clobbers sp_el0, and we can no longer restore this from the
stack. Instead, the current task is cached in a per-cpu variable that we
can safely access from early assembly as interrupts are disabled (and we
are thus not preemptible).

Both secondary entry and idle are updated to stash the sp and task
pointer separately.

Signed-off-by: default avatarMark Rutland <mark.rutland@arm.com>
Tested-by: default avatarLaura Abbott <labbott@redhat.com>
Cc: AKASHI Takahiro <takahiro.akashi@linaro.org>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Cc: James Morse <james.morse@arm.com>
Cc: Kees Cook <keescook@chromium.org>
Cc: Suzuki K Poulose <suzuki.poulose@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Signed-off-by: default avatarCatalin Marinas <catalin.marinas@arm.com>

This is a modification of Mark Rutland's original patch. Guards to check
if CONFIG_THREAD_INFO_IN_TASK is used has been inserted. get_current()
for when CONFIG_THREAD_INFO_IN_TASK is not used has been added to
arch/arm64/include/asm/current.h.

Bug: 64672701
Change-Id: Ic5eae344a7c2baea0864f6ae16be1e9c60c0a74a
Signed-off-by: default avatarZubin Mithra <zsm@google.com>
Git-repo: https://android.googlesource.com/kernel/msm


Git-commit: fe5800b60e30df67caed77da246150c8d478538b
Signed-off-by: default avatarChetan C R <cravin@codeaurora.org>
Signed-off-by: default avatarSrinivasa Rao Kuppala <srkupp@codeaurora.org>
parent 3368b3c3
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -103,6 +103,7 @@ config ARM64
	select SYSCTL_EXCEPTION_TRACE
	select SYSCTL_EXCEPTION_TRACE
	select HAVE_CONTEXT_TRACKING
	select HAVE_CONTEXT_TRACKING
	select HAVE_ARM_SMCCC
	select HAVE_ARM_SMCCC
	select THREAD_INFO_IN_TASK
	help
	help
	  ARM 64-bit (AArch64) Linux support.
	  ARM 64-bit (AArch64) Linux support.


+27 −0
Original line number Original line Diff line number Diff line
#ifndef __ASM_CURRENT_H
#define __ASM_CURRENT_H

#include <linux/compiler.h>

#include <asm/sysreg.h>

#ifndef __ASSEMBLY__

#ifdef CONFIG_THREAD_INFO_IN_TASK
struct task_struct;

static __always_inline struct task_struct *get_current(void)
{
	return (struct task_struct *)read_sysreg(sp_el0);
}
#define current get_current()
#else
#include <linux/thread_info.h>
#define get_current() (current_thread_info()->task)
#define current get_current()
#endif

#endif /* __ASSEMBLY__ */

#endif /* __ASM_CURRENT_H */
+3 −0
Original line number Original line Diff line number Diff line
@@ -57,6 +57,9 @@ asmlinkage void secondary_start_kernel(void);
 */
 */
struct secondary_data {
struct secondary_data {
	void *stack;
	void *stack;
#ifdef CONFIG_THREAD_INFO_IN_TASK
	struct task_struct *task;
#endif
};
};
extern struct secondary_data secondary_data;
extern struct secondary_data secondary_data;
extern void secondary_entry(void);
extern void secondary_entry(void);
+14 −1
Original line number Original line Diff line number Diff line
@@ -47,14 +47,25 @@ typedef unsigned long mm_segment_t;
struct thread_info {
struct thread_info {
	unsigned long		flags;		/* low level flags */
	unsigned long		flags;		/* low level flags */
	mm_segment_t		addr_limit;	/* address limit */
	mm_segment_t		addr_limit;	/* address limit */
#ifndef CONFIG_THREAD_INFO_IN_TASK
	struct task_struct	*task;		/* main task structure */
	struct task_struct	*task;		/* main task structure */
#endif
#ifdef CONFIG_ARM64_SW_TTBR0_PAN
#ifdef CONFIG_ARM64_SW_TTBR0_PAN
	u64			ttbr0;		/* saved TTBR0_EL1 */
	u64			ttbr0;		/* saved TTBR0_EL1 */
#endif
#endif
	int			preempt_count;	/* 0 => preemptable, <0 => bug */
	int			preempt_count;	/* 0 => preemptable, <0 => bug */
#ifndef CONFIG_THREAD_INFO_IN_TASK
	int			cpu;		/* cpu */
	int			cpu;		/* cpu */
#endif
};
};


#ifdef CONFIG_THREAD_INFO_IN_TASK
#define INIT_THREAD_INFO(tsk)						\
{									\
	.preempt_count	= INIT_PREEMPT_COUNT,				\
	.addr_limit	= KERNEL_DS,					\
}
#else
#define INIT_THREAD_INFO(tsk)						\
#define INIT_THREAD_INFO(tsk)						\
{									\
{									\
	.task		= &tsk,						\
	.task		= &tsk,						\
@@ -64,7 +75,6 @@ struct thread_info {
}
}


#define init_thread_info	(init_thread_union.thread_info)
#define init_thread_info	(init_thread_union.thread_info)
#define init_stack		(init_thread_union.stack)


/*
/*
 * how to get the current stack pointer from C
 * how to get the current stack pointer from C
@@ -87,6 +97,9 @@ static inline struct thread_info *current_thread_info(void)


	return (struct thread_info *)sp_el0;
	return (struct thread_info *)sp_el0;
}
}
#endif

#define init_stack		(init_thread_union.stack)


#define thread_saved_pc(tsk)	\
#define thread_saved_pc(tsk)	\
	((unsigned long)(tsk->thread.cpu_context.pc))
	((unsigned long)(tsk->thread.cpu_context.pc))
+12 −2
Original line number Original line Diff line number Diff line
@@ -35,11 +35,16 @@ int main(void)
{
{
  DEFINE(TSK_ACTIVE_MM,		offsetof(struct task_struct, active_mm));
  DEFINE(TSK_ACTIVE_MM,		offsetof(struct task_struct, active_mm));
  BLANK();
  BLANK();
#ifdef CONFIG_THREAD_INFO_IN_TASK
  DEFINE(TSK_TI_FLAGS,		offsetof(struct task_struct, thread_info.flags));
  DEFINE(TSK_TI_PREEMPT,	offsetof(struct task_struct, thread_info.preempt_count));
  DEFINE(TSK_TI_ADDR_LIMIT,	offsetof(struct task_struct, thread_info.addr_limit));
  DEFINE(TSK_STACK,		offsetof(struct task_struct, stack));
#else
  DEFINE(TI_FLAGS,		offsetof(struct thread_info, flags));
  DEFINE(TI_FLAGS,		offsetof(struct thread_info, flags));
  DEFINE(TI_PREEMPT,		offsetof(struct thread_info, preempt_count));
  DEFINE(TI_PREEMPT,		offsetof(struct thread_info, preempt_count));
  DEFINE(TI_ADDR_LIMIT,		offsetof(struct thread_info, addr_limit));
  DEFINE(TI_ADDR_LIMIT,		offsetof(struct thread_info, addr_limit));
  DEFINE(TI_TASK,		offsetof(struct thread_info, task));
#endif
  DEFINE(TI_CPU,		offsetof(struct thread_info, cpu));
#ifdef CONFIG_ARM64_SW_TTBR0_PAN
#ifdef CONFIG_ARM64_SW_TTBR0_PAN
  DEFINE(TSK_TI_TTBR0,		offsetof(struct thread_info, ttbr0));
  DEFINE(TSK_TI_TTBR0,		offsetof(struct thread_info, ttbr0));
#endif
#endif
@@ -124,6 +129,11 @@ int main(void)
  DEFINE(TZ_MINWEST,		offsetof(struct timezone, tz_minuteswest));
  DEFINE(TZ_MINWEST,		offsetof(struct timezone, tz_minuteswest));
  DEFINE(TZ_DSTTIME,		offsetof(struct timezone, tz_dsttime));
  DEFINE(TZ_DSTTIME,		offsetof(struct timezone, tz_dsttime));
  BLANK();
  BLANK();
#ifdef CONFIG_THREAD_INFO_IN_TASK
  DEFINE(CPU_BOOT_STACK,	offsetof(struct secondary_data, stack));
  DEFINE(CPU_BOOT_TASK,		offsetof(struct secondary_data, task));
  BLANK();
#endif
#ifdef CONFIG_KVM_ARM_HOST
#ifdef CONFIG_KVM_ARM_HOST
  DEFINE(VCPU_CONTEXT,		offsetof(struct kvm_vcpu, arch.ctxt));
  DEFINE(VCPU_CONTEXT,		offsetof(struct kvm_vcpu, arch.ctxt));
  DEFINE(CPU_GP_REGS,		offsetof(struct kvm_cpu_context, gp_regs));
  DEFINE(CPU_GP_REGS,		offsetof(struct kvm_cpu_context, gp_regs));
Loading