[PATCH 2/3] ARM: Move thread_info into task_struct (v7 only)
flat view
STALE1863d
From: Keith Packard <keithp@keithp.com>
Date: 2021-09-04 06:09:27
Also in:
lkml
Subsystem:
arm port, arm/cirrus logic ep93xx arm architecture, arm/vfp support, the rest · Maintainers:
Russell King, Hartley Sweeten, Alexander Sverdlin, Nikita Shubin, Linus Torvalds
This avoids many stack overflow attacks which modified the thread_info structure by moving that into the task_struct as is done is almost all other architectures. The task_struct is now referenced by a per-cpu variable, 'current_task', allowing the current one on each cpu to be located from both C and assembly. This also involved removing the 'cpu' member from the thread_info struct and using the one added to the task_struct instead by the THREAD_INFO_IN_TASK code. This code is currently enabled only for v7 hardware as other ARM architectures do not make the c13 register in the p15 co-processor available for general OS use, leaving identifying the current cpu to the 'cpu' field in the thread_info located off the stack pointer. Signed-off-by: Keith Packard <redacted> --- arch/arm/Kconfig | 1 + arch/arm/Makefile | 8 ++++ arch/arm/include/asm/assembler.h | 66 ++++++++++++++++++++++++++---- arch/arm/include/asm/current.h | 41 +++++++++++++++++++ arch/arm/include/asm/smp.h | 18 ++++++++ arch/arm/include/asm/thread_info.h | 17 ++++++++ arch/arm/kernel/asm-offsets.c | 4 ++ arch/arm/kernel/entry-armv.S | 17 ++++---- arch/arm/kernel/entry-common.S | 6 +-- arch/arm/kernel/entry-v7m.S | 7 +++- arch/arm/kernel/iwmmxt.S | 2 +- arch/arm/kernel/smp.c | 12 ++++++ arch/arm/lib/copy_from_user.S | 2 +- arch/arm/lib/copy_to_user.S | 2 +- arch/arm/mach-ep93xx/crunch-bits.S | 2 +- arch/arm/mm/proc-macros.S | 2 +- arch/arm/vfp/entry.S | 4 ++ arch/arm/vfp/vfpmodule.c | 25 +++++++---- 18 files changed, 204 insertions(+), 32 deletions(-) create mode 100644 arch/arm/include/asm/current.h
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 24804f11302d..1c1ded500a2b 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig@@ -128,6 +128,7 @@ config ARM select RTC_LIB select SET_FS select SYS_SUPPORTS_APM_EMULATION + select THREAD_INFO_IN_TASK if CPU_V7 # Above selects are sorted alphabetically; please add new ones # according to that. Thanks. help
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index 415c3514573a..71a2ba4549d3 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile@@ -284,6 +284,14 @@ stack_protector_prepare: prepare0 $(eval GCC_PLUGINS_CFLAGS += $(SSP_PLUGIN_CFLAGS)) endif +ifdef CONFIG_SMP +prepare: task_cpu_prepare + +PHONY += task_cpu_prepare +task_cpu_prepare: prepare0 + $(eval KBUILD_CFLAGS += -D_TSK_CPU=$(shell awk '{if ($$2 == "TSK_CPU") print $$3;}' include/generated/asm-offsets.h)) +endif + all: $(notdir $(KBUILD_IMAGE))
diff --git a/arch/arm/include/asm/assembler.h b/arch/arm/include/asm/assembler.h
index e2b1fd558bf3..2ce7403f9298 100644
--- a/arch/arm/include/asm/assembler.h
+++ b/arch/arm/include/asm/assembler.h@@ -199,16 +199,68 @@ .endm .endr +#ifdef CONFIG_THREAD_INFO_IN_TASK /* - * Get current thread_info. + * Get per-CPU offset + * rd: Destination register */ - .macro get_thread_info, rd - ARM( mov \rd, sp, lsr #THREAD_SIZE_ORDER + PAGE_SHIFT ) - THUMB( mov \rd, sp ) - THUMB( lsr \rd, \rd, #THREAD_SIZE_ORDER + PAGE_SHIFT ) - mov \rd, \rd, lsl #THREAD_SIZE_ORDER + PAGE_SHIFT + .macro this_cpu_offset rd:req + mrc p15, 0, \rd, c13, c0, 4 .endm +/* + * Load a per-cpu variable + * @dst: Destination register to receive value + * @sym: The name of the per-cpu variable + * @tmp: scratch register + */ + .macro ldr_this_cpu dst : req, sym : req, tmp : req + movw \dst, #:lower16:\sym + movt \dst, #:upper16:\sym + this_cpu_offset \tmp + ldr \dst, [\dst, \tmp] + .endm + +/* + * Store a value in a per-cpu variable + * @src: Source register with value + * @sym: The name of the per-cpu variable + * @t1: scratch register 1 + * @t2: scratch register 2 + */ + .macro str_this_cpu src : req, sym : req, t1 : req, t2 : req + movw \t1, #:lower16:\sym + movt \t1, #:upper16:\sym + this_cpu_offset \t2 + str \src, [\t1, \t2] + .endm +#endif + +/* + * Get current thread_info + * @dst: Destination register to receive current thread info + * @tmp: scratch register + */ + .macro get_current, dst : req, tmp : req +#ifdef CONFIG_THREAD_INFO_IN_TASK + ldr_this_cpu \dst, current_task, \tmp +#else + ARM( mov \dst, sp, lsr #THREAD_SIZE_ORDER + PAGE_SHIFT ) + THUMB( mov \dst, sp ) + THUMB( lsr \dst, \dst, #THREAD_SIZE_ORDER + PAGE_SHIFT ) + mov \dst, \dst, lsl #THREAD_SIZE_ORDER + PAGE_SHIFT +#endif + .endm + +#ifdef CONFIG_THREAD_INFO_IN_TASK +/* + * Set current task info + */ + .macro set_current_task rs : req, t1 : req, t2 : req + str_this_cpu \rs, current_task, \t1, \t2 + .endm +#endif + /* * Increment/decrement the preempt count. */
@@ -226,7 +278,7 @@ .endm .macro dec_preempt_count_ti, ti, tmp - get_thread_info \ti + get_current \ti, \tmp dec_preempt_count \ti, \tmp .endm #else
diff --git a/arch/arm/include/asm/current.h b/arch/arm/include/asm/current.h
new file mode 100644
index 000000000000..7160315eb569
--- /dev/null
+++ b/arch/arm/include/asm/current.h@@ -0,0 +1,41 @@ +/* + * Copyright © 2021 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef _ASM_ARM_CURRENT_H +#define _ASM_ARM_CURRENT_H + +#include <linux/compiler.h> +#include <linux/thread_info.h> +#include <asm/percpu.h> + +#ifndef __ASSEMBLY__ + +#ifdef CONFIG_THREAD_INFO_IN_TASK +struct task_struct; + +DECLARE_PER_CPU(struct task_struct *, current_task); + +static __always_inline struct task_struct *get_current(void) +{ + return raw_cpu_read(current_task); +} + +#define current get_current() +#else +#include <asm-generic/current.h> +#endif + +#endif /* __ASSEMBLY__ */ + +#endif /* _ASM_ARM_CURRENT_H */
diff --git a/arch/arm/include/asm/smp.h b/arch/arm/include/asm/smp.h
index 86a7fd721556..1c38d1fde641 100644
--- a/arch/arm/include/asm/smp.h
+++ b/arch/arm/include/asm/smp.h@@ -15,7 +15,25 @@ # error "<asm/smp.h> included in non-SMP build" #endif +#ifdef CONFIG_THREAD_INFO_IN_TASK +/* + * This is particularly ugly: it appears we can't actually get the definition + * of task_struct here, but we need access to the CPU this task is running on. + * Instead of using task_struct we're using TSK_CPU which is extracted from + * asm-offsets.h by kbuild to get the current processor ID. + * + * This also needs to be safeguarded when building asm-offsets.s because at + * that time TSK_CPU is not defined yet. + */ +#ifndef _TSK_CPU +#define raw_smp_processor_id() (0) +#else +#define raw_smp_processor_id() (*(unsigned int *)((void *)current + _TSK_CPU)) +#endif + +#else #define raw_smp_processor_id() (current_thread_info()->cpu) +#endif struct seq_file;
diff --git a/arch/arm/include/asm/thread_info.h b/arch/arm/include/asm/thread_info.h
index 70d4cbc49ae1..cbcd476a095b 100644
--- a/arch/arm/include/asm/thread_info.h
+++ b/arch/arm/include/asm/thread_info.h@@ -55,8 +55,10 @@ struct thread_info { unsigned long flags; /* low level flags */ int preempt_count; /* 0 => preemptable, <0 => bug */ mm_segment_t addr_limit; /* address limit */ +#ifndef CONFIG_THREAD_INFO_IN_TASK struct task_struct *task; /* main task structure */ __u32 cpu; /* cpu */ +#endif __u32 cpu_domain; /* cpu domain */ #ifdef CONFIG_STACKPROTECTOR_PER_TASK unsigned long stack_canary;
@@ -75,6 +77,17 @@ struct thread_info { #endif }; +#ifdef CONFIG_THREAD_INFO_IN_TASK + +#define INIT_THREAD_INFO(tsk) \ +{ \ + .flags = 0, \ + .preempt_count = INIT_PREEMPT_COUNT, \ + .addr_limit = KERNEL_DS, \ +} + +#else + #define INIT_THREAD_INFO(tsk) \ { \ .task = &tsk, \
@@ -83,6 +96,9 @@ struct thread_info { .addr_limit = KERNEL_DS, \ } +#endif + +#ifndef CONFIG_THREAD_INFO_IN_TASK /* * how to get the thread information struct from C */
@@ -93,6 +109,7 @@ static inline struct thread_info *current_thread_info(void) return (struct thread_info *) (current_stack_pointer & ~(THREAD_SIZE - 1)); } +#endif #define thread_saved_pc(tsk) \ ((unsigned long)(task_thread_info(tsk)->cpu_context.pc))
diff --git a/arch/arm/kernel/asm-offsets.c b/arch/arm/kernel/asm-offsets.c
index 70993af22d80..c91dcfe34bdd 100644
--- a/arch/arm/kernel/asm-offsets.c
+++ b/arch/arm/kernel/asm-offsets.c@@ -44,8 +44,12 @@ int main(void) DEFINE(TI_FLAGS, offsetof(struct thread_info, flags)); DEFINE(TI_PREEMPT, offsetof(struct thread_info, preempt_count)); DEFINE(TI_ADDR_LIMIT, offsetof(struct thread_info, addr_limit)); +#ifdef CONFIG_THREAD_INFO_IN_TASK + DEFINE(TSK_CPU, offsetof(struct task_struct, cpu)); +#else DEFINE(TI_TASK, offsetof(struct thread_info, task)); DEFINE(TI_CPU, offsetof(struct thread_info, cpu)); +#endif DEFINE(TI_CPU_DOMAIN, offsetof(struct thread_info, cpu_domain)); DEFINE(TI_CPU_SAVE, offsetof(struct thread_info, cpu_context)); DEFINE(TI_USED_CP, offsetof(struct thread_info, used_cp));
diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S
index 0ea8529a4872..a5d71b0d8897 100644
--- a/arch/arm/kernel/entry-armv.S
+++ b/arch/arm/kernel/entry-armv.S@@ -179,7 +179,7 @@ ENDPROC(__und_invalid) @ stmia r7, {r2 - r6} - get_thread_info tsk + get_current tsk, r0 uaccess_entry tsk, r0, r1, r2, \uaccess .if \trace
@@ -260,7 +260,7 @@ __und_svc: bl __und_fault __und_svc_finish: - get_thread_info tsk + get_current tsk, r5 ldr r5, [sp, #S_PSR] @ Get SVC cpsr svc_exit r5 @ return from exception UNWIND(.fnend )
@@ -428,7 +428,7 @@ __irq_usr: usr_entry kuser_cmpxchg_check irq_handler - get_thread_info tsk + get_current tsk, why mov why, #0 b ret_to_user_from_irq UNWIND(.fnend )
@@ -572,12 +572,12 @@ ENDPROC(__und_usr) @ Fall-through from Thumb-2 __und_usr @ #ifdef CONFIG_NEON - get_thread_info r10 @ get current thread + get_current r10, r6 @ get current thread adr r6, .LCneon_thumb_opcodes b 2f #endif call_fpe: - get_thread_info r10 @ get current thread + get_current r10, r6 @ get current thread #ifdef CONFIG_NEON adr r6, .LCneon_arm_opcodes 2: ldr r5, [r6], #4 @ mask value
@@ -722,7 +722,7 @@ __pabt_usr: ENTRY(ret_from_exception) UNWIND(.fnstart ) UNWIND(.cantunwind ) - get_thread_info tsk + get_current tsk, why mov why, #0 b ret_to_user UNWIND(.fnend )
@@ -735,7 +735,7 @@ __fiq_usr: kuser_cmpxchg_check mov r0, sp @ struct pt_regs *regs bl handle_fiq_as_nmi - get_thread_info tsk + get_current tsk, r0 restore_user_regs fast = 0, offset = 0 UNWIND(.fnend ) ENDPROC(__fiq_usr)
@@ -771,6 +771,9 @@ ENTRY(__switch_to) #endif #ifdef CONFIG_CPU_USE_DOMAINS mcr p15, 0, r6, c3, c0, 0 @ Set domain register +#endif +#ifdef CONFIG_THREAD_INFO_IN_TASK + set_current_task r2, r4, r5 #endif mov r5, r0 add r4, r2, #TI_CPU_SAVE
diff --git a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-common.S
index 7f0b7aba1498..52580ee463fe 100644
--- a/arch/arm/kernel/entry-common.S
+++ b/arch/arm/kernel/entry-common.S@@ -156,7 +156,7 @@ ENTRY(ret_from_fork) movne r0, r4 badrne lr, 1f retne r5 -1: get_thread_info tsk +1: get_current tsk, r1 b ret_slow_syscall ENDPROC(ret_from_fork)
@@ -243,7 +243,7 @@ ENTRY(vector_swi) bic scno, scno, #0xff000000 @ mask off SWI op-code eor scno, scno, #__NR_SYSCALL_BASE @ check OS number #endif - get_thread_info tsk + get_current tsk, r10 /* * Reload the registers that may have been corrupted on entry to * the syscall assembly (by tracing or context tracking.)
@@ -278,7 +278,7 @@ local_restart: 9001: sub lr, saved_pc, #4 str lr, [sp, #S_PC] - get_thread_info tsk + get_current tsk, r1 b ret_fast_syscall #endif ENDPROC(vector_swi)
diff --git a/arch/arm/kernel/entry-v7m.S b/arch/arm/kernel/entry-v7m.S
index d0e898608d30..f36a7a876085 100644
--- a/arch/arm/kernel/entry-v7m.S
+++ b/arch/arm/kernel/entry-v7m.S@@ -57,7 +57,7 @@ __irq_entry: tst r0, V7M_SCB_ICSR_RETTOBASE beq 2f - get_thread_info tsk + get_current tsk, r2 ldr r2, [tsk, #TI_FLAGS] movs r2, r2, lsl #16 beq 2f @ no work pending
@@ -83,7 +83,7 @@ __pendsv_entry: str r0, [r1, V7M_SCB_ICSR] @ clear PendSV @ execute the pending work, including reschedule - get_thread_info tsk + get_current tsk, why mov why, #0 b ret_to_user_from_irq ENDPROC(__pendsv_entry)
@@ -107,6 +107,9 @@ ENTRY(__switch_to) bl atomic_notifier_call_chain mov ip, r4 mov r0, r5 +#ifdef CONFIG_THREAD_INFO_IN_TASK + set_current_task r2, r4, r5 +#endif ldmia ip!, {r4 - r11} @ Load all regs saved previously ldr sp, [ip] ldr pc, [ip, #4]!
diff --git a/arch/arm/kernel/iwmmxt.S b/arch/arm/kernel/iwmmxt.S
index d2b4ac06e4ed..781f7c7fca90 100644
--- a/arch/arm/kernel/iwmmxt.S
+++ b/arch/arm/kernel/iwmmxt.S@@ -96,7 +96,7 @@ ENTRY(iwmmxt_task_enable) bl concan_save #ifdef CONFIG_PREEMPT_COUNT - get_thread_info r10 + get_current r10, r3 #endif 4: dec_preempt_count r10, r3 ret r9 @ normal exit from exception
diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c
index 55cb1689a4b3..be0ede16dbb1 100644
--- a/arch/arm/kernel/smp.c
+++ b/arch/arm/kernel/smp.c@@ -51,6 +51,13 @@ #define CREATE_TRACE_POINTS #include <trace/events/ipi.h> +#ifdef CONFIG_THREAD_INFO_IN_TASK +DEFINE_PER_CPU(struct task_struct *, current_task) ____cacheline_aligned = + &init_task; +EXPORT_PER_CPU_SYMBOL(current_task); + +#endif + /* * as from 2.5, kernels no longer have an init_tasks structure * so we need some other way of telling a new secondary core
@@ -156,6 +163,10 @@ int __cpu_up(unsigned int cpu, struct task_struct *idle) secondary_data.cpu = cpu; sync_cache_w(&secondary_data); +#ifdef CONFIG_THREAD_INFO_IN_TASK + per_cpu(current_task, cpu) = idle; +#endif + /* * Now bring the CPU into our world. */
@@ -509,6 +520,7 @@ void __init smp_prepare_cpus(unsigned int max_cpus) */ if (max_cpus > ncores) max_cpus = ncores; + if (ncores > 1 && max_cpus) { /* * Initialise the present map, which describes the set of CPUs
diff --git a/arch/arm/lib/copy_from_user.S b/arch/arm/lib/copy_from_user.S
index f8016e3db65d..5320950100a2 100644
--- a/arch/arm/lib/copy_from_user.S
+++ b/arch/arm/lib/copy_from_user.S@@ -109,7 +109,7 @@ ENTRY(arm_copy_from_user) #ifdef CONFIG_CPU_SPECTRE - get_thread_info r3 + get_current r3, ip ldr r3, [r3, #TI_ADDR_LIMIT] uaccess_mask_range_ptr r1, r2, r3, ip #endif
diff --git a/arch/arm/lib/copy_to_user.S b/arch/arm/lib/copy_to_user.S
index ebfe4cb3d912..e7e61c87893a 100644
--- a/arch/arm/lib/copy_to_user.S
+++ b/arch/arm/lib/copy_to_user.S@@ -109,7 +109,7 @@ ENTRY(__copy_to_user_std) WEAK(arm_copy_to_user) #ifdef CONFIG_CPU_SPECTRE - get_thread_info r3 + get_current r3, ip ldr r3, [r3, #TI_ADDR_LIMIT] uaccess_mask_range_ptr r0, r2, r3, ip #endif
diff --git a/arch/arm/mach-ep93xx/crunch-bits.S b/arch/arm/mach-ep93xx/crunch-bits.S
index fb2dbf76f09e..d0bb34b3d973 100644
--- a/arch/arm/mach-ep93xx/crunch-bits.S
+++ b/arch/arm/mach-ep93xx/crunch-bits.S@@ -192,7 +192,7 @@ crunch_load: 1: #ifdef CONFIG_PREEMPT_COUNT - get_thread_info r10 + get_current r10, r3 #endif 2: dec_preempt_count r10, r3 ret lr
diff --git a/arch/arm/mm/proc-macros.S b/arch/arm/mm/proc-macros.S
index e2c743aa2eb2..a782c025fdf3 100644
--- a/arch/arm/mm/proc-macros.S
+++ b/arch/arm/mm/proc-macros.S@@ -30,7 +30,7 @@ * act_mm - get current->active_mm */ .macro act_mm, rd - get_thread_info \rd + get_current \rd, unused @ won't build if THREAD_INFO_IN_TASK ldr \rd, [\rd, #TI_TASK] .if (TSK_ACTIVE_MM > IMM12_MASK) add \rd, \rd, #TSK_ACTIVE_MM & ~IMM12_MASK
diff --git a/arch/arm/vfp/entry.S b/arch/arm/vfp/entry.S
index 27b0a1f27fbd..48cb40a3b72d 100644
--- a/arch/arm/vfp/entry.S
+++ b/arch/arm/vfp/entry.S@@ -24,7 +24,11 @@ ENTRY(do_vfp) inc_preempt_count r10, r4 ldr r4, .LCvfp +#ifdef CONFIG_THREAD_INFO_IN_TASK + ldr r11, [r10, #TSK_CPU] @ CPU number +#else ldr r11, [r10, #TI_CPU] @ CPU number +#endif add r10, r10, #TI_VFPSTATE @ r10 = workspace ldr pc, [r4] @ call VFP entry point ENDPROC(do_vfp)
diff --git a/arch/arm/vfp/vfpmodule.c b/arch/arm/vfp/vfpmodule.c
index 2cb355c1b5b7..f929a26a05a5 100644
--- a/arch/arm/vfp/vfpmodule.c
+++ b/arch/arm/vfp/vfpmodule.c@@ -143,22 +143,27 @@ static void vfp_thread_copy(struct thread_info *thread) * THREAD_NOFTIFY_SWTICH: * - the previously running thread will not be scheduled onto another CPU. * - the next thread to be run (v) will not be running on another CPU. - * - thread->cpu is the local CPU number + * - tsk->cpu is the local CPU number * - not preemptible as we're called in the middle of a thread switch * THREAD_NOTIFY_FLUSH: * - the thread (v) will be running on the local CPU, so - * v === current_thread_info() - * - thread->cpu is the local CPU number at the time it is accessed, + * v === current + * - tsk->cpu is the local CPU number at the time it is accessed, * but may change at any time. * - we could be preempted if tree preempt rcu is enabled, so - * it is unsafe to use thread->cpu. + * it is unsafe to use tsk->cpu. * THREAD_NOTIFY_EXIT * - we could be preempted if tree preempt rcu is enabled, so - * it is unsafe to use thread->cpu. + * it is unsafe to use tsk->cpu. */ static int vfp_notifier(struct notifier_block *self, unsigned long cmd, void *v) { +#ifdef CONFIG_THREAD_INFO_IN_TASK + struct task_struct *tsk = v; + struct thread_info *thread = &tsk->thread_info; +#else struct thread_info *thread = v; +#endif u32 fpexc; #ifdef CONFIG_SMP unsigned int cpu;
@@ -169,7 +174,11 @@ static int vfp_notifier(struct notifier_block *self, unsigned long cmd, void *v) fpexc = fmrx(FPEXC); #ifdef CONFIG_SMP +#ifdef CONFIG_THREAD_INFO_IN_TASK + cpu = tsk->cpu; +#else cpu = thread->cpu; +#endif /* * On SMP, if VFP is enabled, save the old state in
@@ -458,16 +467,16 @@ static int vfp_pm_suspend(void) /* disable, just in case */ fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_EN); - } else if (vfp_current_hw_state[ti->cpu]) { + } else if (vfp_current_hw_state[smp_processor_id()]) { #ifndef CONFIG_SMP fmxr(FPEXC, fpexc | FPEXC_EN); - vfp_save_state(vfp_current_hw_state[ti->cpu], fpexc); + vfp_save_state(vfp_current_hw_state[smp_processor_id()], fpexc); fmxr(FPEXC, fpexc); #endif } /* clear any information we had about last context state */ - vfp_current_hw_state[ti->cpu] = NULL; + vfp_current_hw_state[smp_processor_id()] = NULL; return 0; }
--
2.33.0
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel