[PATCH] ARM: save and reset the address limit when entering an exception
From: Russell King <hidden>
Date: 2016-07-06 19:10:49
Subsystem:
arm port, ptrace support, the rest · Maintainers:
Russell King, Oleg Nesterov, Linus Torvalds
When we enter an exception, the current address limit should not apply to the exception context: if the exception context wishes to access kernel space via the user accessors (eg, perf code), it must explicitly request such access. Doing otherwise allows the parent context settings to leak into the exception handler, which may inadvertently take advantage of raised privileges of the parent context. Acked-by: Will Deacon <redacted> Signed-off-by: Russell King <redacted> --- This patch relies on cleanups previously posted which are currently in linux-next. arch/arm/include/asm/ptrace.h | 2 +- arch/arm/kernel/asm-offsets.c | 1 + arch/arm/kernel/entry-armv.S | 7 ++++++- arch/arm/kernel/entry-header.S | 4 ++++ arch/arm/kernel/process.c | 12 ++++++++---- 5 files changed, 20 insertions(+), 6 deletions(-)
diff --git a/arch/arm/include/asm/ptrace.h b/arch/arm/include/asm/ptrace.h
index 0ef0093800f2..e9c9a117bd25 100644
--- a/arch/arm/include/asm/ptrace.h
+++ b/arch/arm/include/asm/ptrace.h@@ -22,7 +22,7 @@ struct pt_regs { struct svc_pt_regs { struct pt_regs regs; u32 dacr; - u32 unused; + u32 addr_limit; }; #define to_svc_pt_regs(r) container_of(r, struct svc_pt_regs, regs)
diff --git a/arch/arm/kernel/asm-offsets.c b/arch/arm/kernel/asm-offsets.c
index 9a8ce342cd82..608008229c7d 100644
--- a/arch/arm/kernel/asm-offsets.c
+++ b/arch/arm/kernel/asm-offsets.c@@ -109,6 +109,7 @@ int main(void) DEFINE(S_OLD_R0, offsetof(struct pt_regs, ARM_ORIG_r0)); DEFINE(PT_REGS_SIZE, sizeof(struct pt_regs)); DEFINE(SVC_DACR, offsetof(struct svc_pt_regs, dacr)); + DEFINE(SVC_ADDR_LIMIT, offsetof(struct svc_pt_regs, addr_limit)); DEFINE(SVC_REGS_SIZE, sizeof(struct svc_pt_regs)); BLANK(); #ifdef CONFIG_CACHE_L2X0
diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S
index 0d6f5413be18..bc5f50799d75 100644
--- a/arch/arm/kernel/entry-armv.S
+++ b/arch/arm/kernel/entry-armv.S@@ -185,6 +185,12 @@ ENDPROC(__und_invalid) @ stmia r7, {r2 - r6} + get_thread_info tsk + ldr r0, [tsk, #TI_ADDR_LIMIT] + mov r1, #TASK_SIZE + str r1, [tsk, #TI_ADDR_LIMIT] + str r0, [sp, #SVC_ADDR_LIMIT] + uaccess_save r0 .if \uaccess uaccess_disable r0
@@ -213,7 +219,6 @@ __irq_svc: irq_handler #ifdef CONFIG_PREEMPT - get_thread_info tsk ldr r8, [tsk, #TI_PREEMPT] @ get preempt count ldr r0, [tsk, #TI_FLAGS] @ get flags teq r8, #0 @ if preempt count != 0
diff --git a/arch/arm/kernel/entry-header.S b/arch/arm/kernel/entry-header.S
index 5e1d029147cb..6391728c8f03 100644
--- a/arch/arm/kernel/entry-header.S
+++ b/arch/arm/kernel/entry-header.S@@ -215,7 +215,9 @@ blne trace_hardirqs_off #endif .endif + ldr r1, [sp, #SVC_ADDR_LIMIT] uaccess_restore + str r1, [tsk, #TI_ADDR_LIMIT] #ifndef CONFIG_THUMB2_KERNEL @ ARM mode SVC restore
@@ -259,7 +261,9 @@ @ on the stack remains correct). @ .macro svc_exit_via_fiq + ldr r1, [sp, #SVC_ADDR_LIMIT] uaccess_restore + str r1, [tsk, #TI_ADDR_LIMIT] #ifndef CONFIG_THUMB2_KERNEL @ ARM mode restore mov r0, sp
diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c
index f1c720c0d568..612eb530f33f 100644
--- a/arch/arm/kernel/process.c
+++ b/arch/arm/kernel/process.c@@ -96,19 +96,23 @@ void __show_regs(struct pt_regs *regs) unsigned long flags; char buf[64]; #ifndef CONFIG_CPU_V7M - unsigned int domain; + unsigned int domain, fs; #ifdef CONFIG_CPU_SW_DOMAIN_PAN /* * Get the domain register for the parent context. In user * mode, we don't save the DACR, so lets use what it should * be. For other modes, we place it after the pt_regs struct. */ - if (user_mode(regs)) + if (user_mode(regs)) { domain = DACR_UACCESS_ENABLE; - else + fs = get_fs(); + } else { domain = to_svc_pt_regs(regs)->dacr; + fs = to_svc_pt_regs(regs)->addr_limit; + } #else domain = get_domain(); + fs = get_fs(); #endif #endif
@@ -144,7 +148,7 @@ void __show_regs(struct pt_regs *regs) if ((domain & domain_mask(DOMAIN_USER)) == domain_val(DOMAIN_USER, DOMAIN_NOACCESS)) segment = "none"; - else if (get_fs() == get_ds()) + else if (fs == get_ds()) segment = "kernel"; else segment = "user";
--
2.1.0