mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-26 19:00:25 +00:00
mISDN: fix mISDN_read()/mISDN_read() race
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
parent
1678ec00a6
commit
ebb06be16b
1 changed files with 14 additions and 9 deletions
|
@ -102,36 +102,41 @@ static ssize_t
|
||||||
mISDN_read(struct file *filep, char __user *buf, size_t count, loff_t *off)
|
mISDN_read(struct file *filep, char __user *buf, size_t count, loff_t *off)
|
||||||
{
|
{
|
||||||
struct mISDNtimerdev *dev = filep->private_data;
|
struct mISDNtimerdev *dev = filep->private_data;
|
||||||
|
struct list_head *list = &dev->expired;
|
||||||
struct mISDNtimer *timer;
|
struct mISDNtimer *timer;
|
||||||
u_long flags;
|
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
if (*debug & DEBUG_TIMER)
|
if (*debug & DEBUG_TIMER)
|
||||||
printk(KERN_DEBUG "%s(%p, %p, %d, %p)\n", __func__,
|
printk(KERN_DEBUG "%s(%p, %p, %d, %p)\n", __func__,
|
||||||
filep, buf, (int)count, off);
|
filep, buf, (int)count, off);
|
||||||
|
|
||||||
if (list_empty(&dev->expired) && (dev->work == 0)) {
|
if (count < sizeof(int))
|
||||||
|
return -ENOSPC;
|
||||||
|
|
||||||
|
spin_lock_irq(&dev->lock);
|
||||||
|
while (list_empty(list) && (dev->work == 0)) {
|
||||||
|
spin_unlock_irq(&dev->lock);
|
||||||
if (filep->f_flags & O_NONBLOCK)
|
if (filep->f_flags & O_NONBLOCK)
|
||||||
return -EAGAIN;
|
return -EAGAIN;
|
||||||
wait_event_interruptible(dev->wait, (dev->work ||
|
wait_event_interruptible(dev->wait, (dev->work ||
|
||||||
!list_empty(&dev->expired)));
|
!list_empty(list)));
|
||||||
if (signal_pending(current))
|
if (signal_pending(current))
|
||||||
return -ERESTARTSYS;
|
return -ERESTARTSYS;
|
||||||
|
spin_lock_irq(&dev->lock);
|
||||||
}
|
}
|
||||||
if (count < sizeof(int))
|
|
||||||
return -ENOSPC;
|
|
||||||
if (dev->work)
|
if (dev->work)
|
||||||
dev->work = 0;
|
dev->work = 0;
|
||||||
if (!list_empty(&dev->expired)) {
|
if (!list_empty(list)) {
|
||||||
spin_lock_irqsave(&dev->lock, flags);
|
timer = list_first_entry(list, struct mISDNtimer, list);
|
||||||
timer = (struct mISDNtimer *)dev->expired.next;
|
|
||||||
list_del(&timer->list);
|
list_del(&timer->list);
|
||||||
spin_unlock_irqrestore(&dev->lock, flags);
|
spin_unlock_irq(&dev->lock);
|
||||||
if (put_user(timer->id, (int __user *)buf))
|
if (put_user(timer->id, (int __user *)buf))
|
||||||
ret = -EFAULT;
|
ret = -EFAULT;
|
||||||
else
|
else
|
||||||
ret = sizeof(int);
|
ret = sizeof(int);
|
||||||
kfree(timer);
|
kfree(timer);
|
||||||
|
} else {
|
||||||
|
spin_unlock_irq(&dev->lock);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue