summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Gleixner <tglx@linutronix.de>2016-10-24 11:41:56 +0200
committerRunmin Wang <runminw@codeaurora.org>2017-01-05 17:27:10 -0800
commit8965291e3dc17b6f1ba9c336100569215ac4e07d (patch)
tree8b00b7e319b41bf8197d80539a711a531c0f5f15
parent2116b44bd37e0245ac3442be3dd094397ce017c9 (diff)
timers: Plug locking race vs. timer migration
Linus noticed that lock_timer_base() lacks a READ_ONCE() for accessing the timer flags. As a consequence the compiler is allowed to reload the flags between the initial check for TIMER_MIGRATION and the following timer base computation and the spin lock of the base. While this has not been observed (yet), we need to make sure that it never happens. Change-Id: I577327e02ab77b6de951ac2aa936cb5d5a4f477a Fixes: 0eeda71bc30d ("timer: Replace timer base by a cpu index") Reported-by: Linus Torvalds <torvalds@linux-foundation.org> Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Link: http://lkml.kernel.org/r/alpine.DEB.2.20.1610241711220.4983@nanos Cc: stable@vger.kernel.org Cc: Andrew Morton <akpm@linux-foundation.org> Cc: Peter Zijlstra <peterz@infradead.org> Git-commit: b831275a3553c32091222ac619cfddd73a5553fb Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git [runminw@codeaurora.org: resolve trivial merge conflicts] Signed-off-by: Runmin Wang <runminw@codeaurora.org>
-rw-r--r--kernel/time/timer.c9
1 files changed, 8 insertions, 1 deletions
diff --git a/kernel/time/timer.c b/kernel/time/timer.c
index 5ebefc7cfa4f..7d0d2632400d 100644
--- a/kernel/time/timer.c
+++ b/kernel/time/timer.c
@@ -811,8 +811,15 @@ static struct tvec_base *lock_timer_base(struct timer_list *timer,
__acquires(timer->base->lock)
{
for (;;) {
- u32 tf = timer->flags;
struct tvec_base *base;
+ u32 tf;
+
+ /*
+ * We need to use READ_ONCE() here, otherwise the compiler
+ * might re-read @tf between the check for TIMER_MIGRATING
+ * and spin_lock().
+ */
+ tf = READ_ONCE(timer->flags);
if (!(tf & TIMER_MIGRATING)) {
base = get_timer_base(tf);