Skip to contentSkip to conversation

ARM: Add SWP/SWPB emulation for ARMv7 processors (v2)

PATCH New Leif Lindholm 4 participants 11 messages Dec 18, 2009 – Jan 5, 2010 Arm (32-bit)

lore
Share & tools
lore
#0 lore raw

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.

Cc Catalin Marinas <[email protected]>
---
 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
index dd00f74..d1befbc 100644
@@ -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
index dd4698c..1d40bd9 100644
@@ -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
index 3a28521..cb85aeb 100644
@@ -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
#1 lore raw

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.

#2 lore raw

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

#3 lore raw

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_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.

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.

#4 lore raw

PATCH 1/1 ARM: Add SWP/SWPB emulation for ARMv7 processors (v2)

In reply to Russell King - ARM Linux
Quoted 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

#5 lore raw

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.

#6 lore raw

PATCH 1/1 ARM: Add SWP/SWPB emulation for ARMv7 processors (v2)

In reply to Russell King - ARM Linux
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.

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
#7 lore raw

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->pid together with get_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.

#8 lore raw

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:29

Quoted 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.

#9 lore raw

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:29

Quoted 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

#10 lore raw

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:34

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().

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