Commit 73d20564 authored by Sebastian Andrzej Siewior's avatar Sebastian Andrzej Siewior Committed by Thomas Gleixner
Browse files

hrtimer: Don't dereference the hrtimer pointer after the callback



A hrtimer can be released in its callback, but lockdep_hrtimer_exit()
dereferences the pointer after the callback returns, i.e. a potential use
after free.

Retrieve the context in which the hrtimer expires before the callback is
invoked and use it in lockdep_hrtimer_exit().

Fixes: 40db1739 ("lockdep: Add hrtimer context tracing bits")
Reported-by: default avatar <syzbot+62c155c276e580cfb606@syzkaller.appspotmail.com>
Signed-off-by: default avatarSebastian Andrzej Siewior <bigeasy@linutronix.de>
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
Link: https://lkml.kernel.org/r/20200331201849.fkp2siy3vcdqvqlz@linutronix.de
parent 42595ce9
Loading
Loading
Loading
Loading
+16 −11
Original line number Diff line number Diff line
@@ -58,14 +58,19 @@ do { \
} while (0)

# define lockdep_hrtimer_enter(__hrtimer)		\
	  do {						\
		  if (!__hrtimer->is_hard)		\
({							\
	bool __expires_hardirq = true;			\
							\
	if (!__hrtimer->is_hard) {			\
		current->irq_config = 1;		\
	  } while (0)
		__expires_hardirq = false;		\
	}						\
	__expires_hardirq;				\
})

# define lockdep_hrtimer_exit(__hrtimer)		\
# define lockdep_hrtimer_exit(__expires_hardirq)	\
	do {						\
		  if (!__hrtimer->is_hard)		\
		if (!__expires_hardirq)			\
			current->irq_config = 0;	\
	} while (0)

@@ -102,8 +107,8 @@ do { \
# define lockdep_hardirq_exit()		do { } while (0)
# define lockdep_softirq_enter()	do { } while (0)
# define lockdep_softirq_exit()		do { } while (0)
# define lockdep_hrtimer_enter(__hrtimer)		do { } while (0)
# define lockdep_hrtimer_exit(__hrtimer)		do { } while (0)
# define lockdep_hrtimer_enter(__hrtimer)	false
# define lockdep_hrtimer_exit(__context)	do { } while (0)
# define lockdep_posixtimer_enter()		do { } while (0)
# define lockdep_posixtimer_exit()		do { } while (0)
# define lockdep_irq_work_enter(__work)		do { } while (0)
+3 −2
Original line number Diff line number Diff line
@@ -1480,6 +1480,7 @@ static void __run_hrtimer(struct hrtimer_cpu_base *cpu_base,
			  unsigned long flags) __must_hold(&cpu_base->lock)
{
	enum hrtimer_restart (*fn)(struct hrtimer *);
	bool expires_in_hardirq;
	int restart;

	lockdep_assert_held(&cpu_base->lock);
@@ -1514,11 +1515,11 @@ static void __run_hrtimer(struct hrtimer_cpu_base *cpu_base,
	 */
	raw_spin_unlock_irqrestore(&cpu_base->lock, flags);
	trace_hrtimer_expire_entry(timer, now);
	lockdep_hrtimer_enter(timer);
	expires_in_hardirq = lockdep_hrtimer_enter(timer);

	restart = fn(timer);

	lockdep_hrtimer_exit(timer);
	lockdep_hrtimer_exit(expires_in_hardirq);
	trace_hrtimer_expire_exit(timer);
	raw_spin_lock_irq(&cpu_base->lock);