All of lore.kernel.org
 help / color / mirror / Atom feed
* re: locking/atomic: Introduce atomic_try_cmpxchg()
@ 2017-03-24 12:44 Dmitry Vyukov
  2017-03-24 14:21 ` Peter Zijlstra
  2017-03-27 12:16 ` Peter Zijlstra
  0 siblings, 2 replies; 37+ messages in thread
From: Dmitry Vyukov @ 2017-03-24 12:44 UTC (permalink / raw)
  To: Peter Zijlstra, Andrew Morton, Andy Lutomirski, Borislav Petkov,
	Brian Gerst, Denys Vlasenko, H. Peter Anvin, Josh Poimboeuf,
	Linus Torvalds, Paul McKenney, Thomas Gleixner, Ingo Molnar,
	LKML

Hi,

I've come across:

commit a9ebf306f52c756c4f9e50ee9a60cd6389d71344
Author: Peter Zijlstra
Date:   Wed Feb 1 16:39:38 2017 +0100
    locking/atomic: Introduce atomic_try_cmpxchg()

The primitive has subtle difference with all other implementation that
I know of, and can lead to very subtle bugs. Some time ago I've spent
several days debugging a memory corruption caused by similar
implementation. Consider a classical lock-free stack push:

node->next = atomic_read(&head);
do {
} while (!atomic_try_cmpxchg(&head, &node->next, node));

This code is broken with the current implementation, the problem is
with unconditional update of *__po here:

#define __atomic64_try_cmpxchg(type, _p, _po, _n)    \
({
                \
        typeof(_po) __po = (_po);                                       \
        typeof(*(_po)) __o = *__po;                                     \
        *__po = atomic64_cmpxchg##type((_p), __o, (_n)); \
        (*__po == __o);
         \
})

In case of success it writes the same value back into *__po, but in
case of cmpxchg success we might have lose ownership of some memory
locations and potentially over what __po has pointed to. The same
holds for the re-read of *__po.
The problem is very easy to overlook in user code (not saying about
the case when developer is already familiar with different semantics
of similar functions (e.g. C/C++ atomic operations, or gcc/clang
atomic builtins)). For this reason cmpxchg implementations usually
update comparand only in case of failure. So I would suggest to change
it to a safer and less surprising alternative:

diff --git a/arch/x86/include/asm/cmpxchg.h b/arch/x86/include/asm/cmpxchg.h
index fb961db51a2a..81fb985f51f4 100644
--- a/arch/x86/include/asm/cmpxchg.h
+++ b/arch/x86/include/asm/cmpxchg.h
@@ -212,7 +212,8 @@ extern void __add_wrong_size(void)
        default:                                                        \
                __cmpxchg_wrong_size();                                 \
        }                                                               \
-       *_old = __old;                                                  \
+       if (!success)                                                   \
+               *_old = __old;                                          \
        success;                                                        \
 })

diff --git a/include/linux/atomic.h b/include/linux/atomic.h
index aae5953817d6..f8098157f7c8 100644
--- a/include/linux/atomic.h
+++ b/include/linux/atomic.h
@@ -1023,8 +1023,11 @@ static inline int atomic_dec_if_positive(atomic_t *v)
 ({                                                                     \
        typeof(_po) __po = (_po);                                       \
        typeof(*(_po)) __o = *__po;                                     \
-       *__po = atomic64_cmpxchg##type((_p), __o, (_n));                \
-       (*__po == __o);                                                 \
+       typeof(*(_po)) __v = atomic64_cmpxchg##type((_p), __o, (_n));   \
+       if (__v == __o)                                                 \
+               return true;                                            \
+       *__po = __v;                                                    \
+       return false;                                                   \
 })

^ permalink raw reply related	[flat|nested] 37+ messages in thread

end of thread, other threads:[~2017-03-27 13:45 UTC | newest]

Thread overview: 37+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-03-24 12:44 locking/atomic: Introduce atomic_try_cmpxchg() Dmitry Vyukov
2017-03-24 14:21 ` Peter Zijlstra
2017-03-24 14:23   ` Dmitry Vyukov
2017-03-24 16:41   ` Peter Zijlstra
2017-03-24 16:54     ` Andy Lutomirski
2017-03-24 17:23       ` Peter Zijlstra
2017-03-24 17:51         ` Dmitry Vyukov
2017-03-24 18:08           ` Peter Zijlstra
2017-03-24 18:13             ` Peter Zijlstra
2017-03-24 19:16               ` Andy Lutomirski
2017-03-24 19:20                 ` Linus Torvalds
2017-03-24 19:27                   ` Andy Lutomirski
2017-03-24 20:15                   ` Peter Zijlstra
2017-03-24 20:14                 ` Peter Zijlstra
2017-03-24 20:21                   ` Andy Lutomirski
2017-03-24 18:16             ` Dmitry Vyukov
2017-03-24 18:00         ` Peter Zijlstra
2017-03-24 18:04           ` Peter Zijlstra
2017-03-24 18:45         ` Andy Lutomirski
2017-03-24 19:17           ` Linus Torvalds
2017-03-24 21:23             ` Peter Zijlstra
2017-03-25  7:51               ` Peter Zijlstra
2017-03-25 18:00                 ` Linus Torvalds
2017-03-25 18:20                   ` Peter Zijlstra
2017-03-25 18:28                     ` Linus Torvalds
2017-03-25 18:34                       ` Linus Torvalds
2017-03-25 21:13                         ` Peter Zijlstra
2017-03-25 22:08                           ` Linus Torvalds
2017-03-27  9:48                             ` Peter Zijlstra
2017-03-24 20:22           ` Peter Zijlstra
2017-03-24 20:27             ` Andy Lutomirski
2017-03-24 21:07               ` Peter Zijlstra
2017-03-24 19:08         ` Linus Torvalds
2017-03-24 20:46           ` Peter Zijlstra
2017-03-24 20:58             ` Linus Torvalds
2017-03-27 12:16 ` Peter Zijlstra
2017-03-27 13:45   ` Dmitry Vyukov

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.