ARM: Add SWP/SWPB emulation for ARMv7 processors (v2)
PATCH 1/1 ARM: Add SWP/SWPB emulation for ARMv7 processors (v2)
The SWP instruction was deprecated in the ARMv6 architecture, superseded by the LDREX/STREX family of instructions for load-linked/store-conditional operations. The ARMv7 multiprocessing extensions mandate that SWP/SWPB instructions are treated as undefined from reset, with the ability to enable them through the System Control Register SW bit.
This patch adds the alternative solution to emulate the SWP and SWPB instructions using LDREX/STREX sequences, and log statistics to /proc/cpu/swp_emulation. To correctly deal with copy-on-write, it also modifies cpu_v7_set_pte_ext to change the mappings to priviliged RO when user RO.
--- arch/arm/kernel/Makefile | 1 arch/arm/kernel/swp_emulate.c | 282 +++++++++++++++++++++++++++++++++++++++++ arch/arm/mm/Kconfig | 20 +++ arch/arm/mm/proc-v7.S | 6 + 4 files changed, 309 insertions(+), 0 deletions(-) create mode 100644 arch/arm/kernel/swp_emulate.c
arch/arm/kernel/Makefile +1 -0
@@ -38,6 +38,7 @@ obj-$(CONFIG_ARM_THUMBEE) += thumbee.o obj-$(CONFIG_KGDB) += kgdb.o obj-$(CONFIG_ARM_UNWIND) += unwind.o obj-$(CONFIG_HAVE_TCM) += tcm.o +obj-$(CONFIG_SWP_EMULATE) += swp_emulate.o obj-$(CONFIG_CRUNCH) += crunch.o crunch-bits.o AFLAGS_crunch-bits.o := -Wa,-mcpu=ep9312
arch/arm/kernel/swp_emulate.c new file +282 -0
282 lines not on this page · raw message
arch/arm/mm/Kconfig +20 -0
@@ -630,6 +630,26 @@ config ARM_THUMBEE Say Y here if you have a CPU with the ThumbEE extension and code to make use of it. Say N for code that can run on CPUs without ThumbEE. +config SWP_EMULATE + bool "Emulate SWP/SWPB instructions" + depends on CPU_V7 + default y if SMP + help + ARMv6 architecture deprecates use of the SWP/SWPB instructions. + ARMv7 multiprocessing extensions introduce the ability to disable + these instructions, triggering an undefined instruction exception + when executed. Say Y here to enable software emulation of these + instructions for userspace (not kernel) using LDREX/STREX. + Also creates /proc/cpu/swp_emulation for statistics. + + NOTE: when accessing uncached shared regions, LDREX/STREX rely + on an external transaction monitoring block called a global + monitor to maintain update atomicity. If your system does not + implement a global monitor, this option is not safe for programs + that are permitted to map uncached memory (CAP_SYS_RAWIO). + + If unsure, say Y. + config CPU_BIG_ENDIAN bool "Build big-endian kernel" depends on ARCH_SUPPORTS_BIG_ENDIAN
arch/arm/mm/proc-v7.S +6 -0
@@ -147,8 +147,10 @@ ENTRY(cpu_v7_set_pte_ext) tst r1, #L_PTE_USER orrne r3, r3, #PTE_EXT_AP1 +#ifndef CONFIG_SWP_EMULATE tstne r3, #PTE_EXT_APX bicne r3, r3, #PTE_EXT_APX | PTE_EXT_AP0 +#endif tst r1, #L_PTE_EXEC orreq r3, r3, #PTE_EXT_XN
@@ -275,6 +277,10 @@ __v7_setup: #ifdef CONFIG_CPU_ENDIAN_BE8 orr r6, r6, #1 << 25 @ big-endian page tables #endif +#ifdef CONFIG_SWP_EMULATE + orr r5, r5, #(1 << 10) @ set SW bit in "clear" + bic r6, r6, #(1 << 10) @ clear it in "mmuset" +#endif mrc p15, 0, r0, c1, c0, 0 @ read control register bic r0, r0, r5 @ clear bits them orr r0, r0, r6 @ set them
PATCH 1/1 ARM: Add SWP/SWPB emulation for ARMv7 processors (v2)
arch/arm/kernel/swp_emulate.c line 189
Quoted 5 lines · Leif Lindholm
arch/arm/kernel/swp_emulate.c new file +5
-0+static int swp_handler (struct pt_regs *regs,unsigned int instr) +{ +unsigned int address, destreg, data; +unsigned int res =0 ; +long current_pid =sys_getpid ();
Kernel functions calling system calls like this is frowned upon. We know what the current PID is - it's available from current->pid. No function call required.
Quoted 8 lines
arch/arm/kernel/swp_emulate.c new file +8
-0+/* +* Register handler and create status file in /proc/cpu +* Invoked as late_initcall, since not needed before init spawned. +*/ +static int __init swp_emulation_init (void ) +{ +#ifdef CONFIG_PROC_FS +struct proc_dir_entry *res;
arch/arm/kernel/swp_emulate.c line 268
Quoted 10 lines
arch/arm/kernel/swp_emulate.c new file +10
-0+ +#ifndef CONFIG_ALIGNMENT_TRAP + res =proc_mkdir ("cpu" ,NULL ); +if (!res) +return -ENOMEM ; + + res =create_proc_entry ("swp_emulation" ,S_IRUGO , res); +#else + res =create_proc_entry ("cpu/swp_emulation" ,S_IRUGO ,NULL ); +#endif
This needs to be cleaned up - we should probably always create the "cpu" directory so that these don't have to worry about whether it exists or not.
arch/arm/mm/Kconfig config ARM_THUMBEE · line 649
Quoted 5 lines
arch/arm/mm/Kconfig +5
-0+ NOTE: when accessing uncached shared regions, LDREX/STREX rely + on an external transaction monitoring block called a global + monitor to maintain update atomicity. If your system does not + implement a global monitor, this option is not safe for programs + that are permitted to map uncached memory (CAP_SYS_RAWIO).
We can trap this case by looking at the L_PTE_MT_* bits in the pte for the page we're going to be accessing - that's probably a good idea to ensure that such accesses are trapped, rather than going head and possibly risking silent corruption.
PATCH 1/1 ARM: Add SWP/SWPB emulation for ARMv7 processors (v2)
Quoted 10 lines · Russell King - ARM Linux
arch/arm/kernel/swp_emulate.c line 189
Quoted 5 lines · Leif Lindholm
arch/arm/kernel/swp_emulate.c new file +5
-0+static int swp_handler (struct pt_regs *regs,unsigned int instr) +{ +unsigned int address, destreg, data; +unsigned int res =0 ; +long current_pid =sys_getpid ();Kernel functions calling system calls like this is frowned upon. We know what the current PID is - it's available from
current->pid. No function call required.
Quick nitpick in case Leif follows that advice :-)
sys_getpid returns task_tgid_vnr(current).
It is neither ->pid (gettid returns that), nor directly dereferenced because of the vnr part. You'll have to decide whether current->tgid or task_tgid_vnr(current), or current->pid after all are the appropriate value in this situation.
-- Jamie
PATCH 1/1 ARM: Add SWP/SWPB emulation for ARMv7 processors (v2)
Quoted 12 lines · Jamie Lokier
Quoted 10 lines · Russell King - ARM Linux
arch/arm/kernel/swp_emulate.c line 189
Quoted 5 lines · Leif Lindholm
arch/arm/kernel/swp_emulate.c new file +5
-0+static int swp_handler (struct pt_regs *regs,unsigned int instr) +{ +unsigned int address, destreg, data; +unsigned int res =0 ; +long current_pid =sys_getpid ();Kernel functions calling system calls like this is frowned upon. We know what the current PID is - it's available from
current->pid. No function call required.
Quoted 8 lines
Quick nitpick in case Leif follows that advice :-)
sys_getpidreturnstask_tgid_vnr(current).It is neither ->pid (gettid returns that), nor directly dereferenced because of the vnr part. You'll have to decide whether
current->tgidortask_tgid_vnr(current), orcurrent->pidafter all are the appropriate value in this situation.
task_tgid_vnr() returns the "thread group" (aka process) ID, and is what 'ps' spits out for PID. current->pid is effectively the thread ID and is unique to every thread.
If you base this decision on the thread group (sys_getpid()), it can miss threads within a process which may make use of swp - and the use of swp may not be in the binary but a called library.
If the purpose of this is to find where the swp's are, then you want to use current->pid, not sys_getpid().
That aside, the printk is a DoS attack waiting to happen. It needs rate limiting.
PATCH 1/1 ARM: Add SWP/SWPB emulation for ARMv7 processors (v2)
In reply to Russell King - ARM LinuxQuoted 10 lines · Russell King - ARM Linux
arch/arm/mm/Kconfig config ARM_THUMBEE · line 649
Quoted 5 lines
arch/arm/mm/Kconfig +5
-0+ NOTE: when accessing uncached shared regions, LDREX/STREX rely + on an external transaction monitoring block called a global + monitor to maintain update atomicity. If your system does not + implement a global monitor, this option is not safe for programs + that are permitted to map uncached memory (CAP_SYS_RAWIO).We can trap this case by looking at the L_PTE_MT_* bits in the pte for the page we're going to be accessing - that's probably a good idea to ensure that such accesses are trapped, rather than going head and possibly risking silent corruption.
After that test, the kernel could even use the SWP/SWPB instructions if necessary for correct behaviour :-)
Trapping and sending the process a signal would be a good way to ensure code doing that is fixed.
There might be thread synchronisation code using SWP/SWPB on shared memory which gets mapped uncached to prevent aliasing.
So would it be more appropriate to trap when the access is to a non-RAM mapping? Or are there devices which depend on the SWP bus cycle on RAM too, for example a PCI device updating RAM by DMA and using locked cycles?
-- Jamie
PATCH 1/1 ARM: Add SWP/SWPB emulation for ARMv7 processors (v2)
Quoted 2 lines · Jamie Lokier
There might be thread synchronisation code using SWP/SWPB on shared memory which gets mapped uncached to prevent aliasing.
The shared mmap fixup doesn't get triggered on ARMv7 (or any VIPT ARM cached system). Such fixup is only applicable to VIVT ARMs.
PATCH 1/1 ARM: Add SWP/SWPB emulation for ARMv7 processors (v2)
In reply to Russell King - ARM LinuxQuoted 10 lines · Russell King - ARM Linux
arch/arm/kernel/swp_emulate.c line 189
Quoted 5 lines · Leif Lindholm
arch/arm/kernel/swp_emulate.c new file +5
-0+static int swp_handler (struct pt_regs *regs,unsigned int instr) +{ +unsigned int address, destreg, data; +unsigned int res =0 ; +long current_pid =sys_getpid ();Kernel functions calling system calls like this is frowned upon. We know what the current PID is - it's available from
current->pid. No function call required.
Actually, current->pid together with get_task_comm() would be even more useful since the task may die by the time you check the running apps.
Quoted 10 lines
arch/arm/mm/Kconfig config ARM_THUMBEE · line 649
Quoted 5 lines
arch/arm/mm/Kconfig +5
-0+ NOTE: when accessing uncached shared regions, LDREX/STREX rely + on an external transaction monitoring block called a global + monitor to maintain update atomicity. If your system does not + implement a global monitor, this option is not safe for programs + that are permitted to map uncached memory (CAP_SYS_RAWIO).We can trap this case by looking at the L_PTE_MT_* bits in the pte for the page we're going to be accessing - that's probably a good idea to ensure that such accesses are trapped, rather than going head and possibly risking silent corruption.
I wonder if there could be an exploit with Leif's current implementation. The LDREX/STREX pair to uncached or device memory may never complete. This way a user SWP to something like the frame buffer would lock the kernel.
There's also TI's issue with SWP to some memory shared with devices outside the CPU coherency domain where SWP may still be needed. Is this usage in user space or kernel device drivers?
The major issue I see with SWP is that it isn't guaranteed to always work in an SMP shareability domain (bus locking not implemented), hence the LDREX/STREX emulation.
But Leif is away for the rest of the year, so we won't see an updated patch before January.
Signature · 1 line
Catalin
PATCH 1/1 ARM: Add SWP/SWPB emulation for ARMv7 processors (v2)
Quoted 12 lines · Catalin Marinas
Quoted 10 lines · Russell King - ARM Linux
arch/arm/kernel/swp_emulate.c line 189
Quoted 5 lines · Leif Lindholm
arch/arm/kernel/swp_emulate.c new file +5
-0+static int swp_handler (struct pt_regs *regs,unsigned int instr) +{ +unsigned int address, destreg, data; +unsigned int res =0 ; +long current_pid =sys_getpid ();Kernel functions calling system calls like this is frowned upon. We know what the current PID is - it's available from
current->pid. No function call required.
Quoted 2 lines
Actually,
current->pidtogether withget_task_comm()would be even more useful since the task may die by the time you check the running apps.
That's effectively what is being done - current->comm is effectively get_task_comm() - get_task_comm() just copies the string out under a lock.
For debugging of the 'current' task, the additional copy and lock are mere overhead.
Quoted 9 lines
Quoted 4 lines
We can trap this case by looking at the L_PTE_MT_* bits in the pte for the page we're going to be accessing - that's probably a good idea to ensure that such accesses are trapped, rather than going head and possibly risking silent corruption.
I wonder if there could be an exploit with Leif's current implementation. The LDREX/STREX pair to uncached or device memory may never complete. This way a user SWP to something like the frame buffer would lock the kernel.
That means we shouldn't consider applying it until that has been investigated.
PATCH 1/1 ARM: Add SWP/SWPB emulation for ARMv7 processors (v2)
Quoted 10 lines
From: Russell King - ARM Linux [mailto:linux at arm.linux.org.uk]
Sent: 19 December 2009 17:29Quoted 4 lines
I wonder if there could be an exploit with Leif's current implementation. The LDREX/STREX pair to uncached or device memory may never complete. This way a user SWP to something like the frame buffer would lock the kernel.
That means we shouldn't consider applying it until that has been investigated.
What if I modify the patch such that a failed STREX causes the emulation to return success without readjusting the PC? This would result in the SWP instruction being executed again upon returning to the application - removing the potential kernel lockup.
/
Leif
-- IMPORTANT NOTICE: The contents of this email and any attachments are confidential and may also be privileged. If you are not the intended recipient, please notify the sender immediately and do not disclose the contents to any other person, use it for any purpose, or store or copy the information in any medium. Thank you.
PATCH 1/1 ARM: Add SWP/SWPB emulation for ARMv7 processors (v2)
Quoted 8 lines · Leif Lindholm
Quoted 8 lines
From: Russell King - ARM Linux [mailto:linux at arm.linux.org.uk]
Sent: 19 December 2009 17:29Quoted 4 lines
I wonder if there could be an exploit with Leif's current implementation. The LDREX/STREX pair to uncached or device memory may never complete. This way a user SWP to something like the frame buffer would lock the kernel.
Quoted 7 lines
Quoted 2 lines
That means we shouldn't consider applying it until that has been investigated.
What if I modify the patch such that a failed STREX causes the emulation to return success without readjusting the PC? This would result in the SWP instruction being executed again upon returning to the application - removing the potential kernel lockup.
There is no need to return to userspace, as you know that userspace will immediately retry the instruction, or respond to a signal.
So you can loop, as long as you:
1. Allow other tasks to run by calling cond_resched().
2. Break out of the loop if signal_pending().
I think that's sufficient but I hope someone will double check because I'm a little rusty.
If someone executes SWP on uncached or device memory and it's not an intentional DOS (e.g. worked with older ARMs), they won't expect the instruction to loop at that point. Faulting would be better. Is it feasible to detect when this has happened and send a SIGBUS signal instead of looping?
-- Jamie
PATCH 1/1 ARM: Add SWP/SWPB emulation for ARMv7 processors (v2)
Quoted 10 lines
From: Jamie Lokier [mailto:jamie at shareable.org]
Sent: 04 January 2010 19:34There is no need to return to userspace, as you know that userspace will immediately retry the instruction, or respond to a signal.
So you can loop, as long as you:
1. Allow other tasks to run by calling
cond_resched().
2. Break out of the loop ifsignal_pending().
Thanks for these suggestions - I will be posting an updated patch that does this shortly.
Quoted 3 lines
If someone executes SWP on uncached or device memory and it's not an intentional DOS (e.g. worked with older ARMs), they won't expect the instruction to loop at that point.
True, but difficult to solve in a clean way (see below).
Quoted 2 lines
Faulting would be better. Is it feasible to detect when this has happened and send a SIGBUS signal instead of looping?
Only if the processor implements it (the AXI spec explicitly permits a processor to treat an OKAY response, as opposed to EXOKAY, as an error - but it doesn't mandate it). I am not aware of any ARM processors that do this. Otherwise that would certainly be the best solution.
Of course this means that if we want to "detect" an impossible situation in order to generate an abort, we need to resort to counters, and determining an arbitrary "highest possible" number of valid sequential failures...
/
Leif