[PATCH] bug: Use xchg() to update WARN_ON_ONCE() static variable

From: Steven Rostedt
Date: Tue Oct 15 2013 - 15:58:19 EST


The WARN_ON_ONCE() code is to trigger a waring only once when some
condition happens. But due to the way it is written it is racy.

if (unlikely(condition)) {
if (WARN(!__warned))
__warned = true;
}

The problem is that multiple CPUs could hit the same warning and
produce multiple output dumps of the same warning, or an interrupt could
happen and hit the same warning and do the warning in the middle of a
previous one, especially since the WARN() does a dump of the current
stack.

Even more of a problem, a recent WARN_ON_ONCE() that was in the page
fault handler triggered and the stack dump of the WARN() caused the
same WARN_ON_ONCE() get hit again. Since the __warned = true is not
updated until after the WARN() is completed, each WARN() triggered
another page fault causing the stack to be filled and crashed the box.

The point of WARN_ON() is to warn the user and not to crash the box.

The easy fix is to update the __warned variable with a xchg(). This way
only one WARN_ON_ONCE() will actually happen, and prevents any issues
of the WARN() causing the same WARN() to be hit and crash the system.

[ Fixed the WARN() whitespace offset of a '\' while I was at it ]

Signed-off-by: Steven Rostedt <rostedt@xxxxxxxxxxx>

diff --git a/include/asm-generic/bug.h b/include/asm-generic/bug.h
index 7d10f96..ab7ebdb 100644
--- a/include/asm-generic/bug.h
+++ b/include/asm-generic/bug.h
@@ -11,6 +11,7 @@

#ifndef __ASSEMBLY__
#include <linux/kernel.h>
+#include <asm/cmpxchg.h>

#ifdef CONFIG_BUG

@@ -91,7 +92,7 @@ extern void warn_slowpath_null(const char *file, const int line);
#endif

#ifndef WARN
-#define WARN(condition, format...) ({ \
+#define WARN(condition, format...) ({ \
int __ret_warn_on = !!(condition); \
if (unlikely(__ret_warn_on)) \
__WARN_printf(format); \
@@ -134,32 +135,29 @@ extern void warn_slowpath_null(const char *file, const int line);
#endif

#define WARN_ON_ONCE(condition) ({ \
- static bool __section(.data.unlikely) __warned; \
+ static int __section(.data.unlikely) __warned; \
int __ret_warn_once = !!(condition); \
\
if (unlikely(__ret_warn_once)) \
- if (WARN_ON(!__warned)) \
- __warned = true; \
+ WARN_ON(!xchg(&__warned, 1)); \
unlikely(__ret_warn_once); \
})

#define WARN_ONCE(condition, format...) ({ \
- static bool __section(.data.unlikely) __warned; \
+ static int __section(.data.unlikely) __warned; \
int __ret_warn_once = !!(condition); \
\
if (unlikely(__ret_warn_once)) \
- if (WARN(!__warned, format)) \
- __warned = true; \
+ WARN(!xchg(&__warned, 1), format); \
unlikely(__ret_warn_once); \
})

#define WARN_TAINT_ONCE(condition, taint, format...) ({ \
- static bool __section(.data.unlikely) __warned; \
+ static int __section(.data.unlikely) __warned; \
int __ret_warn_once = !!(condition); \
\
if (unlikely(__ret_warn_once)) \
- if (WARN_TAINT(!__warned, taint, format)) \
- __warned = true; \
+ WARN_TAINT(!xchg(&__warned, 1), taint, format); \
unlikely(__ret_warn_once); \
})

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/