[PATCH, RFC] fix UP futex code to not generate invalid streqt instruction

Mikael Pettersson mikpe at it.uu.se
Fri Jul 22 13:25:14 EDT 2011


Compiling kernel 3.0 for UP ARM (ARMv5) I see:

kernel/futex.c: In function 'fixup_pi_state_owner':
kernel/futex.c:1549: warning: 'curval' may be used uninitialized in this function
kernel/futex.c: In function 'handle_futex_death':
kernel/futex.c:2454: warning: 'nval' may be used uninitialized in this function
/tmp/ccVDjh0q.s: Assembler messages:
/tmp/ccVDjh0q.s:113: Warning: source register same as write-back base

The corresponding instruction is:

2:	streqt  r3, [r3]

(same as strteq) which the ARMv5 ARM ARM describes as being UNPREDICTABLE.

This code originates from futex_atomic_cmpxchg_inatomic():

static inline int
futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
                              u32 oldval, u32 newval)
{
        int ret = 0;
        u32 val;

        if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32)))
                return -EFAULT;

        __asm__ __volatile__("@futex_atomic_cmpxchg_inatomic\n"
        "1:     " T(ldr) "      %1, [%4]\n"
        "       teq     %1, %2\n"
        "       it      eq      @ explicit IT needed for the 2b label\n"
        "2:     " T(streq) "    %3, [%4]\n"
        __futex_atomic_ex_table("%5")
        : "+r" (ret), "=&r" (val)
        : "r" (oldval), "r" (newval), "r" (uaddr), "Ir" (-EFAULT)
        : "cc", "memory");

        *uval = val;
        return ret;
}

The problem is that in the T(streq) insn, %3 and %4 MUST be different registers,
but nothing in the asm() constrains them to be different.  kernel/futex.c:init_futex()
calls this with uaddr, oldval, and newval all zero, and the compiler may allocate
all three to the same register.  That's exactly what happens for me with a gcc-4.4.6
based compiler and the 2.6.39 and 3.0 kernels. (It didn't happen with 2.6.38.)

One way of fixing this is to make uaddr an input/output register, since that prevents
it from overlapping any other input or output.  The patch below does exactly that;
on the problematic fragment in futex.c it causes the following change:

@@ -104,13 +104,14 @@ futex_init:
        mvnne   r3, #13
        bne     .L6
        mvn     r2, #13
+       mov     r0, r3
 #APP
 @ 97 "/tmp/linux-3.0/arch/arm/include/asm/futex.h" 1
        @futex_atomic_cmpxchg_inatomic
-1:     ldrt    r1, [r3]
+1:     ldrt    r1, [r0]
        teq     r1, r3
        it      eq      @ explicit IT needed for the 2b label
-2:     streqt  r3, [r3]
+2:     streqt  r3, [r0]
 3:
        .pushsection __ex_table,"a"
        .align  3

which is pretty much what we want.

Lightly tested on an n2100 (UP, ARMv5).

Does this seem like a reasonable solution, or is there a better way to force
distinct input registers to an asm()?

/Mikael

--- linux-3.0/arch/arm/include/asm/futex.h.~1~  2011-07-22 12:01:07.000000000 +0200
+++ linux-3.0/arch/arm/include/asm/futex.h      2011-07-22 18:31:08.000000000 +0200
@@ -95,13 +95,13 @@ futex_atomic_cmpxchg_inatomic(u32 *uval,
                return -EFAULT;
 
        __asm__ __volatile__("@futex_atomic_cmpxchg_inatomic\n"
-       "1:     " T(ldr) "      %1, [%4]\n"
-       "       teq     %1, %2\n"
+       "1:     " T(ldr) "      %1, [%2]\n"
+       "       teq     %1, %3\n"
        "       it      eq      @ explicit IT needed for the 2b label\n"
-       "2:     " T(streq) "    %3, [%4]\n"
+       "2:     " T(streq) "    %4, [%2]\n"
        __futex_atomic_ex_table("%5")
-       : "+r" (ret), "=&r" (val)
-       : "r" (oldval), "r" (newval), "r" (uaddr), "Ir" (-EFAULT)
+       : "+r" (ret), "=&r" (val), "+r" (uaddr)
+       : "r" (oldval), "r" (newval), "Ir" (-EFAULT)
        : "cc", "memory");
 
        *uval = val;



More information about the linux-arm-kernel mailing list