forked from Minki/linux
s390/cmpxchg: fix sign extension bugs
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>
This commit is contained in:
parent
bf3db85311
commit
1896d256d3
@ -29,7 +29,7 @@ static inline unsigned long __xchg(unsigned long x, void *ptr, int size)
|
|||||||
" cs %0,0,%4\n"
|
" cs %0,0,%4\n"
|
||||||
" jl 0b\n"
|
" jl 0b\n"
|
||||||
: "=&d" (old), "=Q" (*(int *) addr)
|
: "=&d" (old), "=Q" (*(int *) addr)
|
||||||
: "d" (x << shift), "d" (~(255 << shift)),
|
: "d" ((x & 0xff) << shift), "d" (~(0xff << shift)),
|
||||||
"Q" (*(int *) addr) : "memory", "cc", "0");
|
"Q" (*(int *) addr) : "memory", "cc", "0");
|
||||||
return old >> shift;
|
return old >> shift;
|
||||||
case 2:
|
case 2:
|
||||||
@ -44,7 +44,7 @@ static inline unsigned long __xchg(unsigned long x, void *ptr, int size)
|
|||||||
" cs %0,0,%4\n"
|
" cs %0,0,%4\n"
|
||||||
" jl 0b\n"
|
" jl 0b\n"
|
||||||
: "=&d" (old), "=Q" (*(int *) addr)
|
: "=&d" (old), "=Q" (*(int *) addr)
|
||||||
: "d" (x << shift), "d" (~(65535 << shift)),
|
: "d" ((x & 0xffff) << shift), "d" (~(0xffff << shift)),
|
||||||
"Q" (*(int *) addr) : "memory", "cc", "0");
|
"Q" (*(int *) addr) : "memory", "cc", "0");
|
||||||
return old >> shift;
|
return old >> shift;
|
||||||
case 4:
|
case 4:
|
||||||
@ -114,8 +114,9 @@ static inline unsigned long __cmpxchg(void *ptr, unsigned long old,
|
|||||||
" jnz 0b\n"
|
" jnz 0b\n"
|
||||||
"1:"
|
"1:"
|
||||||
: "=&d" (prev), "=&d" (tmp), "+Q" (*(int *) addr)
|
: "=&d" (prev), "=&d" (tmp), "+Q" (*(int *) addr)
|
||||||
: "d" (old << shift), "d" (new << shift),
|
: "d" ((old & 0xff) << shift),
|
||||||
"d" (~(255 << shift))
|
"d" ((new & 0xff) << shift),
|
||||||
|
"d" (~(0xff << shift))
|
||||||
: "memory", "cc");
|
: "memory", "cc");
|
||||||
return prev >> shift;
|
return prev >> shift;
|
||||||
case 2:
|
case 2:
|
||||||
@ -135,8 +136,9 @@ static inline unsigned long __cmpxchg(void *ptr, unsigned long old,
|
|||||||
" jnz 0b\n"
|
" jnz 0b\n"
|
||||||
"1:"
|
"1:"
|
||||||
: "=&d" (prev), "=&d" (tmp), "+Q" (*(int *) addr)
|
: "=&d" (prev), "=&d" (tmp), "+Q" (*(int *) addr)
|
||||||
: "d" (old << shift), "d" (new << shift),
|
: "d" ((old & 0xffff) << shift),
|
||||||
"d" (~(65535 << shift))
|
"d" ((new & 0xffff) << shift),
|
||||||
|
"d" (~(0xffff << shift))
|
||||||
: "memory", "cc");
|
: "memory", "cc");
|
||||||
return prev >> shift;
|
return prev >> shift;
|
||||||
case 4:
|
case 4:
|
||||||
|
Loading…
Reference in New Issue
Block a user