linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [GIT PULL] locking fixes
@ 2017-02-28  7:57 Ingo Molnar
  2017-02-28 18:37 ` Linus Torvalds
  2017-05-03 23:21 ` [GIT PULL] locking fixes Linus Torvalds
  0 siblings, 2 replies; 8+ messages in thread
From: Ingo Molnar @ 2017-02-28  7:57 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: linux-kernel, Peter Zijlstra, Thomas Gleixner, Paul E. McKenney,
	Andrew Morton

Linus,

Please pull the latest locking-urgent-for-linus git tree from:

   git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git locking-urgent-for-linus

   # HEAD: 318b1dedcd39012624f466d281627553e9fa2570 locking/refcounts: Add missing kernel.h header to have UINT_MAX defined

The main change is the uninlining of large refcount_t APIs, plus a header 
dependency fix.

Note that the uninlining allowed us to enable the underflow/overflow warnings 
unconditionally and remove the debug Kconfig switch: this might trigger new 
warnings in buggy code and turn crashes/use-after-free bugs into less harmful 
memory leaks.

 Thanks,

	Ingo

------------------>
Elena Reshetova (1):
      locking/refcounts: Add missing kernel.h header to have UINT_MAX defined

Peter Zijlstra (1):
      locking/refcounts: Out-of-line everything


 include/linux/refcount.h | 278 +++--------------------------------------------
 lib/Kconfig.debug        |  13 ---
 lib/Makefile             |   2 +-
 lib/refcount.c           | 267 +++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 281 insertions(+), 279 deletions(-)
 create mode 100644 lib/refcount.c

diff --git a/include/linux/refcount.h b/include/linux/refcount.h
index 600aadf9cca4..0023fee4bbbc 100644
--- a/include/linux/refcount.h
+++ b/include/linux/refcount.h
@@ -1,54 +1,10 @@
 #ifndef _LINUX_REFCOUNT_H
 #define _LINUX_REFCOUNT_H
 
-/*
- * Variant of atomic_t specialized for reference counts.
- *
- * The interface matches the atomic_t interface (to aid in porting) but only
- * provides the few functions one should use for reference counting.
- *
- * It differs in that the counter saturates at UINT_MAX and will not move once
- * there. This avoids wrapping the counter and causing 'spurious'
- * use-after-free issues.
- *
- * Memory ordering rules are slightly relaxed wrt regular atomic_t functions
- * and provide only what is strictly required for refcounts.
- *
- * The increments are fully relaxed; these will not provide ordering. The
- * rationale is that whatever is used to obtain the object we're increasing the
- * reference count on will provide the ordering. For locked data structures,
- * its the lock acquire, for RCU/lockless data structures its the dependent
- * load.
- *
- * Do note that inc_not_zero() provides a control dependency which will order
- * future stores against the inc, this ensures we'll never modify the object
- * if we did not in fact acquire a reference.
- *
- * The decrements will provide release order, such that all the prior loads and
- * stores will be issued before, it also provides a control dependency, which
- * will order us against the subsequent free().
- *
- * The control dependency is against the load of the cmpxchg (ll/sc) that
- * succeeded. This means the stores aren't fully ordered, but this is fine
- * because the 1->0 transition indicates no concurrency.
- *
- * Note that the allocator is responsible for ordering things between free()
- * and alloc().
- *
- */
-
 #include <linux/atomic.h>
-#include <linux/bug.h>
 #include <linux/mutex.h>
 #include <linux/spinlock.h>
-
-#ifdef CONFIG_DEBUG_REFCOUNT
-#define REFCOUNT_WARN(cond, str) WARN_ON(cond)
-#define __refcount_check	__must_check
-#else
-#define REFCOUNT_WARN(cond, str) (void)(cond)
-#define __refcount_check
-#endif
+#include <linux/kernel.h>
 
 typedef struct refcount_struct {
 	atomic_t refs;
@@ -66,229 +22,21 @@ static inline unsigned int refcount_read(const refcount_t *r)
 	return atomic_read(&r->refs);
 }
 
-static inline __refcount_check
-bool refcount_add_not_zero(unsigned int i, refcount_t *r)
-{
-	unsigned int old, new, val = atomic_read(&r->refs);
-
-	for (;;) {
-		if (!val)
-			return false;
-
-		if (unlikely(val == UINT_MAX))
-			return true;
-
-		new = val + i;
-		if (new < val)
-			new = UINT_MAX;
-		old = atomic_cmpxchg_relaxed(&r->refs, val, new);
-		if (old == val)
-			break;
-
-		val = old;
-	}
-
-	REFCOUNT_WARN(new == UINT_MAX, "refcount_t: saturated; leaking memory.\n");
-
-	return true;
-}
-
-static inline void refcount_add(unsigned int i, refcount_t *r)
-{
-	REFCOUNT_WARN(!refcount_add_not_zero(i, r), "refcount_t: addition on 0; use-after-free.\n");
-}
-
-/*
- * Similar to atomic_inc_not_zero(), will saturate at UINT_MAX and WARN.
- *
- * Provides no memory ordering, it is assumed the caller has guaranteed the
- * object memory to be stable (RCU, etc.). It does provide a control dependency
- * and thereby orders future stores. See the comment on top.
- */
-static inline __refcount_check
-bool refcount_inc_not_zero(refcount_t *r)
-{
-	unsigned int old, new, val = atomic_read(&r->refs);
-
-	for (;;) {
-		new = val + 1;
-
-		if (!val)
-			return false;
-
-		if (unlikely(!new))
-			return true;
-
-		old = atomic_cmpxchg_relaxed(&r->refs, val, new);
-		if (old == val)
-			break;
-
-		val = old;
-	}
-
-	REFCOUNT_WARN(new == UINT_MAX, "refcount_t: saturated; leaking memory.\n");
-
-	return true;
-}
-
-/*
- * Similar to atomic_inc(), will saturate at UINT_MAX and WARN.
- *
- * Provides no memory ordering, it is assumed the caller already has a
- * reference on the object, will WARN when this is not so.
- */
-static inline void refcount_inc(refcount_t *r)
-{
-	REFCOUNT_WARN(!refcount_inc_not_zero(r), "refcount_t: increment on 0; use-after-free.\n");
-}
-
-/*
- * Similar to atomic_dec_and_test(), it will WARN on underflow and fail to
- * decrement when saturated at UINT_MAX.
- *
- * Provides release memory ordering, such that prior loads and stores are done
- * before, and provides a control dependency such that free() must come after.
- * See the comment on top.
- */
-static inline __refcount_check
-bool refcount_sub_and_test(unsigned int i, refcount_t *r)
-{
-	unsigned int old, new, val = atomic_read(&r->refs);
-
-	for (;;) {
-		if (unlikely(val == UINT_MAX))
-			return false;
-
-		new = val - i;
-		if (new > val) {
-			REFCOUNT_WARN(new > val, "refcount_t: underflow; use-after-free.\n");
-			return false;
-		}
-
-		old = atomic_cmpxchg_release(&r->refs, val, new);
-		if (old == val)
-			break;
-
-		val = old;
-	}
-
-	return !new;
-}
-
-static inline __refcount_check
-bool refcount_dec_and_test(refcount_t *r)
-{
-	return refcount_sub_and_test(1, r);
-}
+extern __must_check bool refcount_add_not_zero(unsigned int i, refcount_t *r);
+extern void refcount_add(unsigned int i, refcount_t *r);
 
-/*
- * Similar to atomic_dec(), it will WARN on underflow and fail to decrement
- * when saturated at UINT_MAX.
- *
- * Provides release memory ordering, such that prior loads and stores are done
- * before.
- */
-static inline
-void refcount_dec(refcount_t *r)
-{
-	REFCOUNT_WARN(refcount_dec_and_test(r), "refcount_t: decrement hit 0; leaking memory.\n");
-}
-
-/*
- * No atomic_t counterpart, it attempts a 1 -> 0 transition and returns the
- * success thereof.
- *
- * Like all decrement operations, it provides release memory order and provides
- * a control dependency.
- *
- * It can be used like a try-delete operator; this explicit case is provided
- * and not cmpxchg in generic, because that would allow implementing unsafe
- * operations.
- */
-static inline __refcount_check
-bool refcount_dec_if_one(refcount_t *r)
-{
-	return atomic_cmpxchg_release(&r->refs, 1, 0) == 1;
-}
-
-/*
- * No atomic_t counterpart, it decrements unless the value is 1, in which case
- * it will return false.
- *
- * Was often done like: atomic_add_unless(&var, -1, 1)
- */
-static inline __refcount_check
-bool refcount_dec_not_one(refcount_t *r)
-{
-	unsigned int old, new, val = atomic_read(&r->refs);
+extern __must_check bool refcount_inc_not_zero(refcount_t *r);
+extern void refcount_inc(refcount_t *r);
 
-	for (;;) {
-		if (unlikely(val == UINT_MAX))
-			return true;
+extern __must_check bool refcount_sub_and_test(unsigned int i, refcount_t *r);
+extern void refcount_sub(unsigned int i, refcount_t *r);
 
-		if (val == 1)
-			return false;
+extern __must_check bool refcount_dec_and_test(refcount_t *r);
+extern void refcount_dec(refcount_t *r);
 
-		new = val - 1;
-		if (new > val) {
-			REFCOUNT_WARN(new > val, "refcount_t: underflow; use-after-free.\n");
-			return true;
-		}
-
-		old = atomic_cmpxchg_release(&r->refs, val, new);
-		if (old == val)
-			break;
-
-		val = old;
-	}
-
-	return true;
-}
-
-/*
- * Similar to atomic_dec_and_mutex_lock(), it will WARN on underflow and fail
- * to decrement when saturated at UINT_MAX.
- *
- * Provides release memory ordering, such that prior loads and stores are done
- * before, and provides a control dependency such that free() must come after.
- * See the comment on top.
- */
-static inline __refcount_check
-bool refcount_dec_and_mutex_lock(refcount_t *r, struct mutex *lock)
-{
-	if (refcount_dec_not_one(r))
-		return false;
-
-	mutex_lock(lock);
-	if (!refcount_dec_and_test(r)) {
-		mutex_unlock(lock);
-		return false;
-	}
-
-	return true;
-}
-
-/*
- * Similar to atomic_dec_and_lock(), it will WARN on underflow and fail to
- * decrement when saturated at UINT_MAX.
- *
- * Provides release memory ordering, such that prior loads and stores are done
- * before, and provides a control dependency such that free() must come after.
- * See the comment on top.
- */
-static inline __refcount_check
-bool refcount_dec_and_lock(refcount_t *r, spinlock_t *lock)
-{
-	if (refcount_dec_not_one(r))
-		return false;
-
-	spin_lock(lock);
-	if (!refcount_dec_and_test(r)) {
-		spin_unlock(lock);
-		return false;
-	}
-
-	return true;
-}
+extern __must_check bool refcount_dec_if_one(refcount_t *r);
+extern __must_check bool refcount_dec_not_one(refcount_t *r);
+extern __must_check bool refcount_dec_and_mutex_lock(refcount_t *r, struct mutex *lock);
+extern __must_check bool refcount_dec_and_lock(refcount_t *r, spinlock_t *lock);
 
 #endif /* _LINUX_REFCOUNT_H */
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index acedbe626d47..0dbce99d8433 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -716,19 +716,6 @@ source "lib/Kconfig.kmemcheck"
 
 source "lib/Kconfig.kasan"
 
-config DEBUG_REFCOUNT
-	bool "Verbose refcount checks"
-	help
-	  Say Y here if you want reference counters (refcount_t and kref) to
-	  generate WARNs on dubious usage. Without this refcount_t will still
-	  be a saturating counter and avoid Use-After-Free by turning it into
-	  a resource leak Denial-Of-Service.
-
-	  Use of this option will increase kernel text size but will alert the
-	  admin of potential abuse.
-
-	  If in doubt, say "N".
-
 endmenu # "Memory Debugging"
 
 config ARCH_HAS_KCOV
diff --git a/lib/Makefile b/lib/Makefile
index 19ea76149a37..192e4d03caf9 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -36,7 +36,7 @@ obj-y += bcd.o div64.o sort.o parser.o debug_locks.o random32.o \
 	 gcd.o lcm.o list_sort.o uuid.o flex_array.o iov_iter.o clz_ctz.o \
 	 bsearch.o find_bit.o llist.o memweight.o kfifo.o \
 	 percpu-refcount.o percpu_ida.o rhashtable.o reciprocal_div.o \
-	 once.o
+	 once.o refcount.o
 obj-y += string_helpers.o
 obj-$(CONFIG_TEST_STRING_HELPERS) += test-string_helpers.o
 obj-y += hexdump.o
diff --git a/lib/refcount.c b/lib/refcount.c
new file mode 100644
index 000000000000..1d33366189d1
--- /dev/null
+++ b/lib/refcount.c
@@ -0,0 +1,267 @@
+/*
+ * Variant of atomic_t specialized for reference counts.
+ *
+ * The interface matches the atomic_t interface (to aid in porting) but only
+ * provides the few functions one should use for reference counting.
+ *
+ * It differs in that the counter saturates at UINT_MAX and will not move once
+ * there. This avoids wrapping the counter and causing 'spurious'
+ * use-after-free issues.
+ *
+ * Memory ordering rules are slightly relaxed wrt regular atomic_t functions
+ * and provide only what is strictly required for refcounts.
+ *
+ * The increments are fully relaxed; these will not provide ordering. The
+ * rationale is that whatever is used to obtain the object we're increasing the
+ * reference count on will provide the ordering. For locked data structures,
+ * its the lock acquire, for RCU/lockless data structures its the dependent
+ * load.
+ *
+ * Do note that inc_not_zero() provides a control dependency which will order
+ * future stores against the inc, this ensures we'll never modify the object
+ * if we did not in fact acquire a reference.
+ *
+ * The decrements will provide release order, such that all the prior loads and
+ * stores will be issued before, it also provides a control dependency, which
+ * will order us against the subsequent free().
+ *
+ * The control dependency is against the load of the cmpxchg (ll/sc) that
+ * succeeded. This means the stores aren't fully ordered, but this is fine
+ * because the 1->0 transition indicates no concurrency.
+ *
+ * Note that the allocator is responsible for ordering things between free()
+ * and alloc().
+ *
+ */
+
+#include <linux/refcount.h>
+#include <linux/bug.h>
+
+bool refcount_add_not_zero(unsigned int i, refcount_t *r)
+{
+	unsigned int old, new, val = atomic_read(&r->refs);
+
+	for (;;) {
+		if (!val)
+			return false;
+
+		if (unlikely(val == UINT_MAX))
+			return true;
+
+		new = val + i;
+		if (new < val)
+			new = UINT_MAX;
+		old = atomic_cmpxchg_relaxed(&r->refs, val, new);
+		if (old == val)
+			break;
+
+		val = old;
+	}
+
+	WARN(new == UINT_MAX, "refcount_t: saturated; leaking memory.\n");
+
+	return true;
+}
+EXPORT_SYMBOL_GPL(refcount_add_not_zero);
+
+void refcount_add(unsigned int i, refcount_t *r)
+{
+	WARN(!refcount_add_not_zero(i, r), "refcount_t: addition on 0; use-after-free.\n");
+}
+EXPORT_SYMBOL_GPL(refcount_add);
+
+/*
+ * Similar to atomic_inc_not_zero(), will saturate at UINT_MAX and WARN.
+ *
+ * Provides no memory ordering, it is assumed the caller has guaranteed the
+ * object memory to be stable (RCU, etc.). It does provide a control dependency
+ * and thereby orders future stores. See the comment on top.
+ */
+bool refcount_inc_not_zero(refcount_t *r)
+{
+	unsigned int old, new, val = atomic_read(&r->refs);
+
+	for (;;) {
+		new = val + 1;
+
+		if (!val)
+			return false;
+
+		if (unlikely(!new))
+			return true;
+
+		old = atomic_cmpxchg_relaxed(&r->refs, val, new);
+		if (old == val)
+			break;
+
+		val = old;
+	}
+
+	WARN(new == UINT_MAX, "refcount_t: saturated; leaking memory.\n");
+
+	return true;
+}
+EXPORT_SYMBOL_GPL(refcount_inc_not_zero);
+
+/*
+ * Similar to atomic_inc(), will saturate at UINT_MAX and WARN.
+ *
+ * Provides no memory ordering, it is assumed the caller already has a
+ * reference on the object, will WARN when this is not so.
+ */
+void refcount_inc(refcount_t *r)
+{
+	WARN(!refcount_inc_not_zero(r), "refcount_t: increment on 0; use-after-free.\n");
+}
+EXPORT_SYMBOL_GPL(refcount_inc);
+
+bool refcount_sub_and_test(unsigned int i, refcount_t *r)
+{
+	unsigned int old, new, val = atomic_read(&r->refs);
+
+	for (;;) {
+		if (unlikely(val == UINT_MAX))
+			return false;
+
+		new = val - i;
+		if (new > val) {
+			WARN(new > val, "refcount_t: underflow; use-after-free.\n");
+			return false;
+		}
+
+		old = atomic_cmpxchg_release(&r->refs, val, new);
+		if (old == val)
+			break;
+
+		val = old;
+	}
+
+	return !new;
+}
+EXPORT_SYMBOL_GPL(refcount_sub_and_test);
+
+/*
+ * Similar to atomic_dec_and_test(), it will WARN on underflow and fail to
+ * decrement when saturated at UINT_MAX.
+ *
+ * Provides release memory ordering, such that prior loads and stores are done
+ * before, and provides a control dependency such that free() must come after.
+ * See the comment on top.
+ */
+bool refcount_dec_and_test(refcount_t *r)
+{
+	return refcount_sub_and_test(1, r);
+}
+EXPORT_SYMBOL_GPL(refcount_dec_and_test);
+
+/*
+ * Similar to atomic_dec(), it will WARN on underflow and fail to decrement
+ * when saturated at UINT_MAX.
+ *
+ * Provides release memory ordering, such that prior loads and stores are done
+ * before.
+ */
+
+void refcount_dec(refcount_t *r)
+{
+	WARN(refcount_dec_and_test(r), "refcount_t: decrement hit 0; leaking memory.\n");
+}
+EXPORT_SYMBOL_GPL(refcount_dec);
+
+/*
+ * No atomic_t counterpart, it attempts a 1 -> 0 transition and returns the
+ * success thereof.
+ *
+ * Like all decrement operations, it provides release memory order and provides
+ * a control dependency.
+ *
+ * It can be used like a try-delete operator; this explicit case is provided
+ * and not cmpxchg in generic, because that would allow implementing unsafe
+ * operations.
+ */
+bool refcount_dec_if_one(refcount_t *r)
+{
+	return atomic_cmpxchg_release(&r->refs, 1, 0) == 1;
+}
+EXPORT_SYMBOL_GPL(refcount_dec_if_one);
+
+/*
+ * No atomic_t counterpart, it decrements unless the value is 1, in which case
+ * it will return false.
+ *
+ * Was often done like: atomic_add_unless(&var, -1, 1)
+ */
+bool refcount_dec_not_one(refcount_t *r)
+{
+	unsigned int old, new, val = atomic_read(&r->refs);
+
+	for (;;) {
+		if (unlikely(val == UINT_MAX))
+			return true;
+
+		if (val == 1)
+			return false;
+
+		new = val - 1;
+		if (new > val) {
+			WARN(new > val, "refcount_t: underflow; use-after-free.\n");
+			return true;
+		}
+
+		old = atomic_cmpxchg_release(&r->refs, val, new);
+		if (old == val)
+			break;
+
+		val = old;
+	}
+
+	return true;
+}
+EXPORT_SYMBOL_GPL(refcount_dec_not_one);
+
+/*
+ * Similar to atomic_dec_and_mutex_lock(), it will WARN on underflow and fail
+ * to decrement when saturated at UINT_MAX.
+ *
+ * Provides release memory ordering, such that prior loads and stores are done
+ * before, and provides a control dependency such that free() must come after.
+ * See the comment on top.
+ */
+bool refcount_dec_and_mutex_lock(refcount_t *r, struct mutex *lock)
+{
+	if (refcount_dec_not_one(r))
+		return false;
+
+	mutex_lock(lock);
+	if (!refcount_dec_and_test(r)) {
+		mutex_unlock(lock);
+		return false;
+	}
+
+	return true;
+}
+EXPORT_SYMBOL_GPL(refcount_dec_and_mutex_lock);
+
+/*
+ * Similar to atomic_dec_and_lock(), it will WARN on underflow and fail to
+ * decrement when saturated at UINT_MAX.
+ *
+ * Provides release memory ordering, such that prior loads and stores are done
+ * before, and provides a control dependency such that free() must come after.
+ * See the comment on top.
+ */
+bool refcount_dec_and_lock(refcount_t *r, spinlock_t *lock)
+{
+	if (refcount_dec_not_one(r))
+		return false;
+
+	spin_lock(lock);
+	if (!refcount_dec_and_test(r)) {
+		spin_unlock(lock);
+		return false;
+	}
+
+	return true;
+}
+EXPORT_SYMBOL_GPL(refcount_dec_and_lock);
+

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

* Re: [GIT PULL] locking fixes
  2017-02-28  7:57 [GIT PULL] locking fixes Ingo Molnar
@ 2017-02-28 18:37 ` Linus Torvalds
  2017-03-01  8:30   ` [PATCH] locking/refcounts: Change WARN() to WARN_ONCE() Ingo Molnar
  2017-03-01  8:33   ` [tip:locking/urgent] " tip-bot for Ingo Molnar
  2017-05-03 23:21 ` [GIT PULL] locking fixes Linus Torvalds
  1 sibling, 2 replies; 8+ messages in thread
From: Linus Torvalds @ 2017-02-28 18:37 UTC (permalink / raw)
  To: Ingo Molnar
  Cc: Linux Kernel Mailing List, Peter Zijlstra, Thomas Gleixner,
	Paul E. McKenney, Andrew Morton

On Mon, Feb 27, 2017 at 11:57 PM, Ingo Molnar <mingo@kernel.org> wrote:
>
> Note that the uninlining allowed us to enable the underflow/overflow warnings
> unconditionally and remove the debug Kconfig switch: this might trigger new
> warnings in buggy code and turn crashes/use-after-free bugs into less harmful
> memory leaks.

I'm ok with this, but that WARN() really needs to be a WARN_ON_ONCE().

Because once an underflow (or overflow) is happening, it tends to
_keep_ happening. And you may just have essentially DoS'ed the machine
that is now spending all its time writing those logs to disk.

Yes, yes, quiet independently of this we should limit WARN printouts
(and do the reverse: turn a "once" to mean "once in a blue moon"
rather than actually just once), but particularly for this kind of
"never happens" thing, it really is better to just warn once.

                   Linus

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

* [PATCH] locking/refcounts: Change WARN() to WARN_ONCE()
  2017-02-28 18:37 ` Linus Torvalds
@ 2017-03-01  8:30   ` Ingo Molnar
  2017-03-01  8:33   ` [tip:locking/urgent] " tip-bot for Ingo Molnar
  1 sibling, 0 replies; 8+ messages in thread
From: Ingo Molnar @ 2017-03-01  8:30 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: Linux Kernel Mailing List, Peter Zijlstra, Thomas Gleixner,
	Paul E. McKenney, Andrew Morton


* Linus Torvalds <torvalds@linux-foundation.org> wrote:

> On Mon, Feb 27, 2017 at 11:57 PM, Ingo Molnar <mingo@kernel.org> wrote:
> >
> > Note that the uninlining allowed us to enable the underflow/overflow warnings
> > unconditionally and remove the debug Kconfig switch: this might trigger new
> > warnings in buggy code and turn crashes/use-after-free bugs into less harmful
> > memory leaks.
> 
> I'm ok with this, but that WARN() really needs to be a WARN_ON_ONCE().
> 
> Because once an underflow (or overflow) is happening, it tends to
> _keep_ happening. And you may just have essentially DoS'ed the machine
> that is now spending all its time writing those logs to disk.
> 
> Yes, yes, quiet independently of this we should limit WARN printouts
> (and do the reverse: turn a "once" to mean "once in a blue moon"
> rather than actually just once), but particularly for this kind of
> "never happens" thing, it really is better to just warn once.

Good point - I've done this in the attached patch.

Thanks,

	Ingo

=================>
>From 9dcfe2c75b51f454f39c2de4756e841228865b47 Mon Sep 17 00:00:00 2001
From: Ingo Molnar <mingo@kernel.org>
Date: Wed, 1 Mar 2017 09:25:55 +0100
Subject: [PATCH] locking/refcounts: Change WARN() to WARN_ONCE()

Linus noticed that the new refcount.h APIs used WARN(), which would turn
into a dmesg DoS if it triggers frequently on some buggy driver.

So make sure we only warn once. These warnings are never supposed to happen,
so it's typically not a problem to lose subsequent warnings.

Suggested-by: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra (Intel) <peterz@infradead.org>
Cc: Elena Reshetova <elena.reshetova@intel.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Link: http://lkml.kernel.org/r/CA+55aFzbYUTZ=oqZ2YgDjY0C2_n6ODhTfqj6V+m5xWmDxsuB0w@mail.gmail.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
---
 lib/refcount.c | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/lib/refcount.c b/lib/refcount.c
index 1d33366189d1..aa09ad3c30b0 100644
--- a/lib/refcount.c
+++ b/lib/refcount.c
@@ -58,7 +58,7 @@ bool refcount_add_not_zero(unsigned int i, refcount_t *r)
 		val = old;
 	}
 
-	WARN(new == UINT_MAX, "refcount_t: saturated; leaking memory.\n");
+	WARN_ONCE(new == UINT_MAX, "refcount_t: saturated; leaking memory.\n");
 
 	return true;
 }
@@ -66,7 +66,7 @@ EXPORT_SYMBOL_GPL(refcount_add_not_zero);
 
 void refcount_add(unsigned int i, refcount_t *r)
 {
-	WARN(!refcount_add_not_zero(i, r), "refcount_t: addition on 0; use-after-free.\n");
+	WARN_ONCE(!refcount_add_not_zero(i, r), "refcount_t: addition on 0; use-after-free.\n");
 }
 EXPORT_SYMBOL_GPL(refcount_add);
 
@@ -97,7 +97,7 @@ bool refcount_inc_not_zero(refcount_t *r)
 		val = old;
 	}
 
-	WARN(new == UINT_MAX, "refcount_t: saturated; leaking memory.\n");
+	WARN_ONCE(new == UINT_MAX, "refcount_t: saturated; leaking memory.\n");
 
 	return true;
 }
@@ -111,7 +111,7 @@ EXPORT_SYMBOL_GPL(refcount_inc_not_zero);
  */
 void refcount_inc(refcount_t *r)
 {
-	WARN(!refcount_inc_not_zero(r), "refcount_t: increment on 0; use-after-free.\n");
+	WARN_ONCE(!refcount_inc_not_zero(r), "refcount_t: increment on 0; use-after-free.\n");
 }
 EXPORT_SYMBOL_GPL(refcount_inc);
 
@@ -125,7 +125,7 @@ bool refcount_sub_and_test(unsigned int i, refcount_t *r)
 
 		new = val - i;
 		if (new > val) {
-			WARN(new > val, "refcount_t: underflow; use-after-free.\n");
+			WARN_ONCE(new > val, "refcount_t: underflow; use-after-free.\n");
 			return false;
 		}
 
@@ -164,7 +164,7 @@ EXPORT_SYMBOL_GPL(refcount_dec_and_test);
 
 void refcount_dec(refcount_t *r)
 {
-	WARN(refcount_dec_and_test(r), "refcount_t: decrement hit 0; leaking memory.\n");
+	WARN_ONCE(refcount_dec_and_test(r), "refcount_t: decrement hit 0; leaking memory.\n");
 }
 EXPORT_SYMBOL_GPL(refcount_dec);
 
@@ -204,7 +204,7 @@ bool refcount_dec_not_one(refcount_t *r)
 
 		new = val - 1;
 		if (new > val) {
-			WARN(new > val, "refcount_t: underflow; use-after-free.\n");
+			WARN_ONCE(new > val, "refcount_t: underflow; use-after-free.\n");
 			return true;
 		}
 

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

* [tip:locking/urgent] locking/refcounts: Change WARN() to WARN_ONCE()
  2017-02-28 18:37 ` Linus Torvalds
  2017-03-01  8:30   ` [PATCH] locking/refcounts: Change WARN() to WARN_ONCE() Ingo Molnar
@ 2017-03-01  8:33   ` tip-bot for Ingo Molnar
  1 sibling, 0 replies; 8+ messages in thread
From: tip-bot for Ingo Molnar @ 2017-03-01  8:33 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: elena.reshetova, hpa, torvalds, linux-kernel, tglx, mingo, peterz

Commit-ID:  9dcfe2c75b51f454f39c2de4756e841228865b47
Gitweb:     http://git.kernel.org/tip/9dcfe2c75b51f454f39c2de4756e841228865b47
Author:     Ingo Molnar <mingo@kernel.org>
AuthorDate: Wed, 1 Mar 2017 09:25:55 +0100
Committer:  Ingo Molnar <mingo@kernel.org>
CommitDate: Wed, 1 Mar 2017 09:25:55 +0100

locking/refcounts: Change WARN() to WARN_ONCE()

Linus noticed that the new refcount.h APIs used WARN(), which would turn
into a dmesg DoS if it triggers frequently on some buggy driver.

So make sure we only warn once. These warnings are never supposed to happen,
so it's typically not a problem to lose subsequent warnings.

Suggested-by: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra (Intel) <peterz@infradead.org>
Cc: Elena Reshetova <elena.reshetova@intel.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Link: http://lkml.kernel.org/r/CA+55aFzbYUTZ=oqZ2YgDjY0C2_n6ODhTfqj6V+m5xWmDxsuB0w@mail.gmail.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
---
 lib/refcount.c | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/lib/refcount.c b/lib/refcount.c
index 1d33366..aa09ad3 100644
--- a/lib/refcount.c
+++ b/lib/refcount.c
@@ -58,7 +58,7 @@ bool refcount_add_not_zero(unsigned int i, refcount_t *r)
 		val = old;
 	}
 
-	WARN(new == UINT_MAX, "refcount_t: saturated; leaking memory.\n");
+	WARN_ONCE(new == UINT_MAX, "refcount_t: saturated; leaking memory.\n");
 
 	return true;
 }
@@ -66,7 +66,7 @@ EXPORT_SYMBOL_GPL(refcount_add_not_zero);
 
 void refcount_add(unsigned int i, refcount_t *r)
 {
-	WARN(!refcount_add_not_zero(i, r), "refcount_t: addition on 0; use-after-free.\n");
+	WARN_ONCE(!refcount_add_not_zero(i, r), "refcount_t: addition on 0; use-after-free.\n");
 }
 EXPORT_SYMBOL_GPL(refcount_add);
 
@@ -97,7 +97,7 @@ bool refcount_inc_not_zero(refcount_t *r)
 		val = old;
 	}
 
-	WARN(new == UINT_MAX, "refcount_t: saturated; leaking memory.\n");
+	WARN_ONCE(new == UINT_MAX, "refcount_t: saturated; leaking memory.\n");
 
 	return true;
 }
@@ -111,7 +111,7 @@ EXPORT_SYMBOL_GPL(refcount_inc_not_zero);
  */
 void refcount_inc(refcount_t *r)
 {
-	WARN(!refcount_inc_not_zero(r), "refcount_t: increment on 0; use-after-free.\n");
+	WARN_ONCE(!refcount_inc_not_zero(r), "refcount_t: increment on 0; use-after-free.\n");
 }
 EXPORT_SYMBOL_GPL(refcount_inc);
 
@@ -125,7 +125,7 @@ bool refcount_sub_and_test(unsigned int i, refcount_t *r)
 
 		new = val - i;
 		if (new > val) {
-			WARN(new > val, "refcount_t: underflow; use-after-free.\n");
+			WARN_ONCE(new > val, "refcount_t: underflow; use-after-free.\n");
 			return false;
 		}
 
@@ -164,7 +164,7 @@ EXPORT_SYMBOL_GPL(refcount_dec_and_test);
 
 void refcount_dec(refcount_t *r)
 {
-	WARN(refcount_dec_and_test(r), "refcount_t: decrement hit 0; leaking memory.\n");
+	WARN_ONCE(refcount_dec_and_test(r), "refcount_t: decrement hit 0; leaking memory.\n");
 }
 EXPORT_SYMBOL_GPL(refcount_dec);
 
@@ -204,7 +204,7 @@ bool refcount_dec_not_one(refcount_t *r)
 
 		new = val - 1;
 		if (new > val) {
-			WARN(new > val, "refcount_t: underflow; use-after-free.\n");
+			WARN_ONCE(new > val, "refcount_t: underflow; use-after-free.\n");
 			return true;
 		}
 

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

* Re: [GIT PULL] locking fixes
  2017-02-28  7:57 [GIT PULL] locking fixes Ingo Molnar
  2017-02-28 18:37 ` Linus Torvalds
@ 2017-05-03 23:21 ` Linus Torvalds
  2017-05-04  5:40   ` Peter Zijlstra
  1 sibling, 1 reply; 8+ messages in thread
From: Linus Torvalds @ 2017-05-03 23:21 UTC (permalink / raw)
  To: Ingo Molnar
  Cc: Linux Kernel Mailing List, Peter Zijlstra, Thomas Gleixner,
	Paul E. McKenney, Andrew Morton

This is from last merge window, and the reason I react now is that
nobody noticed or cared until we had a release..

On Mon, Feb 27, 2017 at 11:57 PM, Ingo Molnar <mingo@kernel.org> wrote:
>
> Peter Zijlstra (1):
>       locking/refcounts: Out-of-line everything

This one is all good generally, but it has one really stupid side
effect: it makes refcounting GPL-only.

That's just silly. These are functions that atomically add and
subtract one. The only thing that making them GPL-only can possibly do
is to make people hack around it, and lose the overflow handling
debugging in the process.

It also breaks any kref uses. Which is what drivers etc are supposed to use.

So that "move from inline to out-of-line" had a big subtle semantic
change that was probably not intentional, and certainly not
documented.

                  Linus

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

* Re: [GIT PULL] locking fixes
  2017-05-03 23:21 ` [GIT PULL] locking fixes Linus Torvalds
@ 2017-05-04  5:40   ` Peter Zijlstra
       [not found]     ` <CA+55aFymvtCAYHdz__3Lj=YqmORB7_A-NXrw=+h+60znJVsDTw@mail.gmail.com>
  0 siblings, 1 reply; 8+ messages in thread
From: Peter Zijlstra @ 2017-05-04  5:40 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: Ingo Molnar, Linux Kernel Mailing List, Thomas Gleixner,
	Paul E. McKenney, Andrew Morton, Greg Kroah-Hartman

On Wed, May 03, 2017 at 04:21:01PM -0700, Linus Torvalds wrote:
> This is from last merge window, and the reason I react now is that
> nobody noticed or cared until we had a release..
> 
> On Mon, Feb 27, 2017 at 11:57 PM, Ingo Molnar <mingo@kernel.org> wrote:
> >
> > Peter Zijlstra (1):
> >       locking/refcounts: Out-of-line everything
> 
> This one is all good generally, but it has one really stupid side
> effect: it makes refcounting GPL-only.
> 
> That's just silly. These are functions that atomically add and
> subtract one. The only thing that making them GPL-only can possibly do
> is to make people hack around it, and lose the overflow handling
> debugging in the process.

These people are out-of-tree dubious licensed modules, right? I really
_really_ don't care about those.

> It also breaks any kref uses. Which is what drivers etc are supposed to use.

Greg KH had this to say:

"As all of the previous kref functions were in a GPL-only header file,
and included directly that way, they were already GPL-only symbols, so
there really was no change here except now the linker checks them.  If
you have questions about using inline GPL-only functions from a .h file,
in a non-GPL codebase, please consult your corporate lawyer to get
clarification."

 https://lkml.kernel.org/r/20170308094810.GB30552@kroah.com

> So that "move from inline to out-of-line" had a big subtle semantic
> change that was probably not intentional, and certainly not
> documented.

I'll take the not documented bit.

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

* Re: [GIT PULL] locking fixes
       [not found]     ` <CA+55aFymvtCAYHdz__3Lj=YqmORB7_A-NXrw=+h+60znJVsDTw@mail.gmail.com>
@ 2017-05-04 22:44       ` Greg Kroah-Hartman
  2017-05-04 22:51         ` refcount: change EXPORT_SYMBOL markings Greg Kroah-Hartman
  0 siblings, 1 reply; 8+ messages in thread
From: Greg Kroah-Hartman @ 2017-05-04 22:44 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: Peter Zijlstra, Paul E. McKenney, Thomas Gleixner, Ingo Molnar,
	Linux Kernel Mailing List, Andrew Morton

On Wed, May 03, 2017 at 11:10:07PM -0700, Linus Torvalds wrote:
> 
> 
> On May 3, 2017 22:40, "Peter Zijlstra" <peterz@infradead.org> wrote:
> 
> 
>     These people are out-of-tree dubious licensed modules, right? I really
>     _really_ don't care about those.
> 
> 
> Mainly Nvidia, I think.

nvidia said it was for "new code" they were still developing, and that
the license issue was on their side, and they fixed it up.  They also
agreed that the change was ok from their point of view.

> But the point is, you broke people's working setups.
> 
> We don't do that.

We have never guaranteed kernel api stability, but yes, this is
different from that.

Moving these from a .h to .c caused the change, I asked for this as the
original symbols were obviously GPL-only being in a .h file.  However I
understand your point, we don't want to have people any grumpier at us
than normal :)

> Perhaps equally importantly, you did it by marking *trivial* functions that are
> definitely meant for drivers as gpl-only. Which only demeans the whole concept
> that we consider the gpl-only thing to be an "internal kernel function".

These are really now low-level kernel functions, and not trivial ones
given the long long email threads on how to get them all working
properly.  This was obviously something that took a lot of time to do.

> So the change actually makes our explicitly stated arguments for why certain
> functions are special less valid, and replaced it with a stupid grandstanding
> and legally dubious "linking means it's a derived work" argument.

There's no "linking" argument here, I didn't make that.

But again, I understand the point, changing api "markings" like this
"mid-stream" isn't the nicest thing to do.  And hey, I'm trying to be
"meaner" as I think someone once told me to be that way, so I
recommended that Peter make this change :)

I'll send a patch to change these back now...

thanks,

greg k-h

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

* refcount: change EXPORT_SYMBOL markings
  2017-05-04 22:44       ` Greg Kroah-Hartman
@ 2017-05-04 22:51         ` Greg Kroah-Hartman
  0 siblings, 0 replies; 8+ messages in thread
From: Greg Kroah-Hartman @ 2017-05-04 22:51 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Linus Torvalds, Paul E. McKenney, Thomas Gleixner, Ingo Molnar,
	Linux Kernel Mailing List, Andrew Morton

From: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

Now that kref is using the refcount apis, the _GPL markings are getting
exported to places that it previously wasn't.  Now kref.h is GPLv2
licensed, so any non-GPL code using it better be talking to some
lawyers, but changing api markings isn't considered "nice", so let's fix
this up.

Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

diff --git a/lib/refcount.c b/lib/refcount.c
index f42124ccf295..9f906783987e 100644
--- a/lib/refcount.c
+++ b/lib/refcount.c
@@ -76,7 +76,7 @@ bool refcount_add_not_zero(unsigned int i, refcount_t *r)
 
 	return true;
 }
-EXPORT_SYMBOL_GPL(refcount_add_not_zero);
+EXPORT_SYMBOL(refcount_add_not_zero);
 
 /**
  * refcount_add - add a value to a refcount
@@ -98,7 +98,7 @@ void refcount_add(unsigned int i, refcount_t *r)
 {
 	WARN_ONCE(!refcount_add_not_zero(i, r), "refcount_t: addition on 0; use-after-free.\n");
 }
-EXPORT_SYMBOL_GPL(refcount_add);
+EXPORT_SYMBOL(refcount_add);
 
 /**
  * refcount_inc_not_zero - increment a refcount unless it is 0
@@ -131,7 +131,7 @@ bool refcount_inc_not_zero(refcount_t *r)
 
 	return true;
 }
-EXPORT_SYMBOL_GPL(refcount_inc_not_zero);
+EXPORT_SYMBOL(refcount_inc_not_zero);
 
 /**
  * refcount_inc - increment a refcount
@@ -149,7 +149,7 @@ void refcount_inc(refcount_t *r)
 {
 	WARN_ONCE(!refcount_inc_not_zero(r), "refcount_t: increment on 0; use-after-free.\n");
 }
-EXPORT_SYMBOL_GPL(refcount_inc);
+EXPORT_SYMBOL(refcount_inc);
 
 /**
  * refcount_sub_and_test - subtract from a refcount and test if it is 0
@@ -189,7 +189,7 @@ bool refcount_sub_and_test(unsigned int i, refcount_t *r)
 
 	return !new;
 }
-EXPORT_SYMBOL_GPL(refcount_sub_and_test);
+EXPORT_SYMBOL(refcount_sub_and_test);
 
 /**
  * refcount_dec_and_test - decrement a refcount and test if it is 0
@@ -208,7 +208,7 @@ bool refcount_dec_and_test(refcount_t *r)
 {
 	return refcount_sub_and_test(1, r);
 }
-EXPORT_SYMBOL_GPL(refcount_dec_and_test);
+EXPORT_SYMBOL(refcount_dec_and_test);
 
 /**
  * refcount_dec - decrement a refcount
@@ -224,7 +224,7 @@ void refcount_dec(refcount_t *r)
 {
 	WARN_ONCE(refcount_dec_and_test(r), "refcount_t: decrement hit 0; leaking memory.\n");
 }
-EXPORT_SYMBOL_GPL(refcount_dec);
+EXPORT_SYMBOL(refcount_dec);
 
 /**
  * refcount_dec_if_one - decrement a refcount if it is 1
@@ -248,7 +248,7 @@ bool refcount_dec_if_one(refcount_t *r)
 
 	return atomic_try_cmpxchg_release(&r->refs, &val, 0);
 }
-EXPORT_SYMBOL_GPL(refcount_dec_if_one);
+EXPORT_SYMBOL(refcount_dec_if_one);
 
 /**
  * refcount_dec_not_one - decrement a refcount if it is not 1
@@ -282,7 +282,7 @@ bool refcount_dec_not_one(refcount_t *r)
 
 	return true;
 }
-EXPORT_SYMBOL_GPL(refcount_dec_not_one);
+EXPORT_SYMBOL(refcount_dec_not_one);
 
 /**
  * refcount_dec_and_mutex_lock - return holding mutex if able to decrement
@@ -313,7 +313,7 @@ bool refcount_dec_and_mutex_lock(refcount_t *r, struct mutex *lock)
 
 	return true;
 }
-EXPORT_SYMBOL_GPL(refcount_dec_and_mutex_lock);
+EXPORT_SYMBOL(refcount_dec_and_mutex_lock);
 
 /**
  * refcount_dec_and_lock - return holding spinlock if able to decrement
@@ -344,5 +344,5 @@ bool refcount_dec_and_lock(refcount_t *r, spinlock_t *lock)
 
 	return true;
 }
-EXPORT_SYMBOL_GPL(refcount_dec_and_lock);
+EXPORT_SYMBOL(refcount_dec_and_lock);
 

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

end of thread, other threads:[~2017-05-04 22:51 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-02-28  7:57 [GIT PULL] locking fixes Ingo Molnar
2017-02-28 18:37 ` Linus Torvalds
2017-03-01  8:30   ` [PATCH] locking/refcounts: Change WARN() to WARN_ONCE() Ingo Molnar
2017-03-01  8:33   ` [tip:locking/urgent] " tip-bot for Ingo Molnar
2017-05-03 23:21 ` [GIT PULL] locking fixes Linus Torvalds
2017-05-04  5:40   ` Peter Zijlstra
     [not found]     ` <CA+55aFymvtCAYHdz__3Lj=YqmORB7_A-NXrw=+h+60znJVsDTw@mail.gmail.com>
2017-05-04 22:44       ` Greg Kroah-Hartman
2017-05-04 22:51         ` refcount: change EXPORT_SYMBOL markings Greg Kroah-Hartman

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).