diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2013-04-16 00:31:13 +0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2013-04-29 23:41:47 +0400 |
commit | c08c464d6f4136d9e48ffa23c0bcd93442343b2a (patch) | |
tree | 988674bee82e2e7f94a258066899e2e0408cf3eb /drivers/isdn | |
parent | 03feee373f05d5c500dd6198015de83005df902c (diff) | |
download | linux-c08c464d6f4136d9e48ffa23c0bcd93442343b2a.tar.xz |
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>
Diffstat (limited to 'drivers/isdn')
-rw-r--r-- | drivers/isdn/mISDN/timerdev.c | 14 |
1 files changed, 12 insertions, 2 deletions
diff --git a/drivers/isdn/mISDN/timerdev.c b/drivers/isdn/mISDN/timerdev.c index 1094667d8f31..5a1a5cadc766 100644 --- a/drivers/isdn/mISDN/timerdev.c +++ b/drivers/isdn/mISDN/timerdev.c @@ -72,14 +72,24 @@ static int mISDN_close(struct inode *ino, struct file *filep) { struct mISDNtimerdev *dev = filep->private_data; + struct list_head *list = &dev->pending; struct mISDNtimer *timer, *next; if (*debug & DEBUG_TIMER) 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); } + spin_unlock_irq(&dev->lock); + list_for_each_entry_safe(timer, next, &dev->expired, list) { kfree(timer); } |