mISDN: fix the races with timers going off just as they are deleted

timer callback in timerdev.c both accesses struct mISDNtimer it's
called for *and* moves it to dev->expired.  We need del_timer_sync(),
or we risk kfree() freeing it right under dev_expire_timer() *and*
dev->expired getting corrupted.

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
Al Viro 2013-04-15 16:31:13 -04:00
parent 03feee373f
commit c08c464d6f

View file

@ -72,14 +72,24 @@ static int
mISDN_close(struct inode *ino, struct file *filep) mISDN_close(struct inode *ino, struct file *filep)
{ {
struct mISDNtimerdev *dev = filep->private_data; struct mISDNtimerdev *dev = filep->private_data;
struct list_head *list = &dev->pending;
struct mISDNtimer *timer, *next; struct mISDNtimer *timer, *next;
if (*debug & DEBUG_TIMER) if (*debug & DEBUG_TIMER)
printk(KERN_DEBUG "%s(%p,%p)\n", __func__, ino, filep); printk(KERN_DEBUG "%s(%p,%p)\n", __func__, ino, filep);
list_for_each_entry_safe(timer, next, &dev->pending, list) {
del_timer(&timer->tl); spin_lock_irq(&dev->lock);
while (!list_empty(list)) {
timer = list_first_entry(list, struct mISDNtimer, list);
spin_unlock_irq(&dev->lock);
del_timer_sync(&timer->tl);
spin_lock_irq(&dev->lock);
/* it might have been moved to ->expired */
list_del(&timer->list);
kfree(timer); kfree(timer);
} }
spin_unlock_irq(&dev->lock);
list_for_each_entry_safe(timer, next, &dev->expired, list) { list_for_each_entry_safe(timer, next, &dev->expired, list) {
kfree(timer); kfree(timer);
} }