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:
parent
03feee373f
commit
c08c464d6f
1 changed files with 12 additions and 2 deletions
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue