Commit b49c0f24 authored by Nicolas Pitre's avatar Nicolas Pitre Committed by Russell King

[ARM] 4659/1: remove possibilities for spurious false negative with __kuser_cmpxchg

The ARM __kuser_cmpxchg routine is meant to implement an atomic cmpxchg
in user space.  It however can produce spurious false negative if a
processor exception occurs in the middle of the operation.  Normally
this is not a problem since cmpxchg is typically called in a loop until
it succeeds to implement an atomic increment for example.

Some use cases which don't involve a loop require that the operation be
100% reliable though.  This patch changes the implementation so to
reattempt the operation after an exception has occurred in the critical
section rather than abort it.

Here's a simple program to test the fix (don't use CONFIG_NO_HZ in your
kernel as this depends on a sufficiently high interrupt rate):

	#include <stdio.h>

	typedef int (__kernel_cmpxchg_t)(int oldval, int newval, int *ptr);
	#define __kernel_cmpxchg (*(__kernel_cmpxchg_t *)0xffff0fc0)

	int main()
	{
		int i, x = 0;
		for (i = 0; i < 100000000; i++) {
			int v = x;
			if (__kernel_cmpxchg(v, v+1, &x))
				printf("failed at %d: %d vs %d\n", i, v, x);
		}
		printf("done with %d vs %d\n", i, x);
		return 0;
	}
Signed-off-by: default avatarNicolas Pitre <nico@marvell.com>
Signed-off-by: default avatarRussell King <rmk+kernel@arm.linux.org.uk>
parent aeb747af
...@@ -339,16 +339,6 @@ __pabt_svc: ...@@ -339,16 +339,6 @@ __pabt_svc:
str r1, [sp] @ save the "real" r0 copied str r1, [sp] @ save the "real" r0 copied
@ from the exception stack @ from the exception stack
#if __LINUX_ARM_ARCH__ < 6 && !defined(CONFIG_NEEDS_SYSCALL_FOR_CMPXCHG)
#ifndef CONFIG_MMU
#warning "NPTL on non MMU needs fixing"
#else
@ make sure our user space atomic helper is aborted
cmp r2, #TASK_SIZE
bichs r3, r3, #PSR_Z_BIT
#endif
#endif
@ @
@ We are now ready to fill in the remaining blanks on the stack: @ We are now ready to fill in the remaining blanks on the stack:
@ @
...@@ -372,9 +362,25 @@ __pabt_svc: ...@@ -372,9 +362,25 @@ __pabt_svc:
zero_fp zero_fp
.endm .endm
.macro kuser_cmpxchg_check
#if __LINUX_ARM_ARCH__ < 6 && !defined(CONFIG_NEEDS_SYSCALL_FOR_CMPXCHG)
#ifndef CONFIG_MMU
#warning "NPTL on non MMU needs fixing"
#else
@ Make sure our user space atomic helper is restarted
@ if it was interrupted in a critical region. Here we
@ perform a quick test inline since it should be false
@ 99.9999% of the time. The rest is done out of line.
cmp r2, #TASK_SIZE
blhs kuser_cmpxchg_fixup
#endif
#endif
.endm
.align 5 .align 5
__dabt_usr: __dabt_usr:
usr_entry usr_entry
kuser_cmpxchg_check
@ @
@ Call the processor-specific abort handler: @ Call the processor-specific abort handler:
...@@ -404,6 +410,7 @@ __dabt_usr: ...@@ -404,6 +410,7 @@ __dabt_usr:
.align 5 .align 5
__irq_usr: __irq_usr:
usr_entry usr_entry
kuser_cmpxchg_check
#ifdef CONFIG_TRACE_IRQFLAGS #ifdef CONFIG_TRACE_IRQFLAGS
bl trace_hardirqs_off bl trace_hardirqs_off
...@@ -669,7 +676,7 @@ __kuser_helper_start: ...@@ -669,7 +676,7 @@ __kuser_helper_start:
* *
* Clobbered: * Clobbered:
* *
* the Z flag might be lost * none
* *
* Definition and user space usage example: * Definition and user space usage example:
* *
...@@ -730,9 +737,6 @@ __kuser_memory_barrier: @ 0xffff0fa0 ...@@ -730,9 +737,6 @@ __kuser_memory_barrier: @ 0xffff0fa0
* *
* - This routine already includes memory barriers as needed. * - This routine already includes memory barriers as needed.
* *
* - A failure might be transient, i.e. it is possible, although unlikely,
* that "failure" be returned even if *ptr == oldval.
*
* For example, a user space atomic_add implementation could look like this: * For example, a user space atomic_add implementation could look like this:
* *
* #define atomic_add(ptr, val) \ * #define atomic_add(ptr, val) \
...@@ -769,46 +773,62 @@ __kuser_cmpxchg: @ 0xffff0fc0 ...@@ -769,46 +773,62 @@ __kuser_cmpxchg: @ 0xffff0fc0
#elif __LINUX_ARM_ARCH__ < 6 #elif __LINUX_ARM_ARCH__ < 6
#ifdef CONFIG_MMU
/* /*
* Theory of operation: * The only thing that can break atomicity in this cmpxchg
* * implementation is either an IRQ or a data abort exception
* We set the Z flag before loading oldval. If ever an exception * causing another process/thread to be scheduled in the middle
* occurs we can not be sure the loaded value will still be the same * of the critical sequence. To prevent this, code is added to
* when the exception returns, therefore the user exception handler * the IRQ and data abort exception handlers to set the pc back
* will clear the Z flag whenever the interrupted user code was * to the beginning of the critical section if it is found to be
* actually from the kernel address space (see the usr_entry macro). * within that critical section (see kuser_cmpxchg_fixup).
*
* The post-increment on the str is used to prevent a race with an
* exception happening just after the str instruction which would
* clear the Z flag although the exchange was done.
*/ */
#ifdef CONFIG_MMU 1: ldr r3, [r2] @ load current val
teq ip, ip @ set Z flag subs r3, r3, r0 @ compare with oldval
ldr ip, [r2] @ load current val 2: streq r1, [r2] @ store newval if eq
add r3, r2, #1 @ prepare store ptr rsbs r0, r3, #0 @ set return val and C flag
teqeq ip, r0 @ compare with oldval if still allowed usr_ret lr
streq r1, [r3, #-1]! @ store newval if still allowed
subs r0, r2, r3 @ if r2 == r3 the str occured .text
kuser_cmpxchg_fixup:
@ Called from kuser_cmpxchg_check macro.
@ r2 = address of interrupted insn (must be preserved).
@ sp = saved regs. r7 and r8 are clobbered.
@ 1b = first critical insn, 2b = last critical insn.
@ If r2 >= 1b and r2 <= 2b then saved pc_usr is set to 1b.
mov r7, #0xffff0fff
sub r7, r7, #(0xffff0fff - (0xffff0fc0 + (1b - __kuser_cmpxchg)))
subs r8, r2, r7
rsbcss r8, r8, #(2b - 1b)
strcs r7, [sp, #S_PC]
mov pc, lr
.previous
#else #else
#warning "NPTL on non MMU needs fixing" #warning "NPTL on non MMU needs fixing"
mov r0, #-1 mov r0, #-1
adds r0, r0, #0 adds r0, r0, #0
#endif
usr_ret lr usr_ret lr
#endif
#else #else
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
mcr p15, 0, r0, c7, c10, 5 @ dmb mcr p15, 0, r0, c7, c10, 5 @ dmb
#endif #endif
ldrex r3, [r2] 1: ldrex r3, [r2]
subs r3, r3, r0 subs r3, r3, r0
strexeq r3, r1, [r2] strexeq r3, r1, [r2]
teqeq r3, #1
beq 1b
rsbs r0, r3, #0 rsbs r0, r3, #0
/* beware -- each __kuser slot must be 8 instructions max */
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
mcr p15, 0, r0, c7, c10, 5 @ dmb b __kuser_memory_barrier
#endif #else
usr_ret lr usr_ret lr
#endif
#endif #endif
...@@ -829,7 +849,7 @@ __kuser_cmpxchg: @ 0xffff0fc0 ...@@ -829,7 +849,7 @@ __kuser_cmpxchg: @ 0xffff0fc0
* *
* Clobbered: * Clobbered:
* *
* the Z flag might be lost * none
* *
* Definition and user space usage example: * Definition and user space usage example:
* *
......
...@@ -509,7 +509,7 @@ asmlinkage int arm_syscall(int no, struct pt_regs *regs) ...@@ -509,7 +509,7 @@ asmlinkage int arm_syscall(int no, struct pt_regs *regs)
* existence. Don't ever use this from user code. * existence. Don't ever use this from user code.
*/ */
case 0xfff0: case 0xfff0:
{ for (;;) {
extern void do_DataAbort(unsigned long addr, unsigned int fsr, extern void do_DataAbort(unsigned long addr, unsigned int fsr,
struct pt_regs *regs); struct pt_regs *regs);
unsigned long val; unsigned long val;
...@@ -545,7 +545,6 @@ asmlinkage int arm_syscall(int no, struct pt_regs *regs) ...@@ -545,7 +545,6 @@ asmlinkage int arm_syscall(int no, struct pt_regs *regs)
up_read(&mm->mmap_sem); up_read(&mm->mmap_sem);
/* simulate a write access fault */ /* simulate a write access fault */
do_DataAbort(addr, 15 + (1 << 11), regs); do_DataAbort(addr, 15 + (1 << 11), regs);
return -1;
} }
#endif #endif
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment