diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2013-04-15 16:31:13 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2013-04-29 15:41:47 -0400 |
commit | c08c464d6f4136d9e48ffa23c0bcd93442343b2a (patch) | |
tree | 988674bee82e2e7f94a258066899e2e0408cf3eb /drivers/isdn | |
parent | 03feee373f05d5c500dd6198015de83005df902c (diff) |
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 | |||
72 | mISDN_close(struct inode *ino, struct file *filep) | 72 | mISDN_close(struct inode *ino, struct file *filep) |
73 | { | 73 | { |
74 | struct mISDNtimerdev *dev = filep->private_data; | 74 | struct mISDNtimerdev *dev = filep->private_data; |
75 | struct list_head *list = &dev->pending; | ||
75 | struct mISDNtimer *timer, *next; | 76 | struct mISDNtimer *timer, *next; |
76 | 77 | ||
77 | if (*debug & DEBUG_TIMER) | 78 | if (*debug & DEBUG_TIMER) |
78 | printk(KERN_DEBUG "%s(%p,%p)\n", __func__, ino, filep); | 79 | printk(KERN_DEBUG "%s(%p,%p)\n", __func__, ino, filep); |
79 | list_for_each_entry_safe(timer, next, &dev->pending, list) { | 80 | |
80 | del_timer(&timer->tl); | 81 | spin_lock_irq(&dev->lock); |
82 | while (!list_empty(list)) { | ||
83 | timer = list_first_entry(list, struct mISDNtimer, list); | ||
84 | spin_unlock_irq(&dev->lock); | ||
85 | del_timer_sync(&timer->tl); | ||
86 | spin_lock_irq(&dev->lock); | ||
87 | /* it might have been moved to ->expired */ | ||
88 | list_del(&timer->list); | ||
81 | kfree(timer); | 89 | kfree(timer); |
82 | } | 90 | } |
91 | spin_unlock_irq(&dev->lock); | ||
92 | |||
83 | list_for_each_entry_safe(timer, next, &dev->expired, list) { | 93 | list_for_each_entry_safe(timer, next, &dev->expired, list) { |
84 | kfree(timer); | 94 | kfree(timer); |
85 | } | 95 | } |