]> Pileus Git - ~andy/linux/commitdiff
s390/cmpxchg: fix sign extension bugs
authorHeiko Carstens <heiko.carstens@de.ibm.com>
Tue, 29 May 2012 09:11:51 +0000 (11:11 +0200)
committerMartin Schwidefsky <schwidefsky@de.ibm.com>
Wed, 30 May 2012 07:07:58 +0000 (09:07 +0200)
For 1 and 2 byte operands for xchg and cmpxchg the old and new values
get or'ed into the larger 4 byte old value before the compare and swap
instruction gets executed. This is done without using the proper byte
mask before or'ing the values.
If the caller passed in negative old or new values these got sign
extended by the caller. Which in turn means that either the old value
never matches, or, even worse, unrelated bytes would be changed in memory.

Luckily there don't seem to be any callers around yet, since that would
have resulted in the specification exception fixed in an earlies patch.

Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
arch/s390/include/asm/cmpxchg.h

index 26f2cb1aa9ff35ae975686e3030774216d6e8289..8d798e962b632c9a8aa426576077a48d1f6f0a38 100644 (file)
@@ -29,7 +29,7 @@ static inline unsigned long __xchg(unsigned long x, void *ptr, int size)
                        "       cs      %0,0,%4\n"
                        "       jl      0b\n"
                        : "=&d" (old), "=Q" (*(int *) addr)
-                       : "d" (x << shift), "d" (~(255 << shift)),
+                       : "d" ((x & 0xff) << shift), "d" (~(0xff << shift)),
                          "Q" (*(int *) addr) : "memory", "cc", "0");
                return old >> shift;
        case 2:
@@ -44,7 +44,7 @@ static inline unsigned long __xchg(unsigned long x, void *ptr, int size)
                        "       cs      %0,0,%4\n"
                        "       jl      0b\n"
                        : "=&d" (old), "=Q" (*(int *) addr)
-                       : "d" (x << shift), "d" (~(65535 << shift)),
+                       : "d" ((x & 0xffff) << shift), "d" (~(0xffff << shift)),
                          "Q" (*(int *) addr) : "memory", "cc", "0");
                return old >> shift;
        case 4:
@@ -114,8 +114,9 @@ static inline unsigned long __cmpxchg(void *ptr, unsigned long old,
                        "       jnz     0b\n"
                        "1:"
                        : "=&d" (prev), "=&d" (tmp), "+Q" (*(int *) addr)
-                       : "d" (old << shift), "d" (new << shift),
-                         "d" (~(255 << shift))
+                       : "d" ((old & 0xff) << shift),
+                         "d" ((new & 0xff) << shift),
+                         "d" (~(0xff << shift))
                        : "memory", "cc");
                return prev >> shift;
        case 2:
@@ -135,8 +136,9 @@ static inline unsigned long __cmpxchg(void *ptr, unsigned long old,
                        "       jnz     0b\n"
                        "1:"
                        : "=&d" (prev), "=&d" (tmp), "+Q" (*(int *) addr)
-                       : "d" (old << shift), "d" (new << shift),
-                         "d" (~(65535 << shift))
+                       : "d" ((old & 0xffff) << shift),
+                         "d" ((new & 0xffff) << shift),
+                         "d" (~(0xffff << shift))
                        : "memory", "cc");
                return prev >> shift;
        case 4: