aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2013-04-15 17:18:17 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2013-04-29 15:41:49 -0400
commitebb06be16bc9a1e66a010ca50c75c5128bafb4b1 (patch)
tree3a9212bb7f267519242b3f66420cb98527edee41
parent1678ec00a632f8b9204e28e5c506128881171604 (diff)
mISDN: fix mISDN_read()/mISDN_read() race
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r--drivers/isdn/mISDN/timerdev.c23
1 files changed, 14 insertions, 9 deletions
diff --git a/drivers/isdn/mISDN/timerdev.c b/drivers/isdn/mISDN/timerdev.c
index ddb8adcd5fbb..da2aa376a3a3 100644
--- a/drivers/isdn/mISDN/timerdev.c
+++ b/drivers/isdn/mISDN/timerdev.c
@@ -102,36 +102,41 @@ static ssize_t
102mISDN_read(struct file *filep, char __user *buf, size_t count, loff_t *off) 102mISDN_read(struct file *filep, char __user *buf, size_t count, loff_t *off)
103{ 103{
104 struct mISDNtimerdev *dev = filep->private_data; 104 struct mISDNtimerdev *dev = filep->private_data;
105 struct list_head *list = &dev->expired;
105 struct mISDNtimer *timer; 106 struct mISDNtimer *timer;
106 u_long flags;
107 int ret = 0; 107 int ret = 0;
108 108
109 if (*debug & DEBUG_TIMER) 109 if (*debug & DEBUG_TIMER)
110 printk(KERN_DEBUG "%s(%p, %p, %d, %p)\n", __func__, 110 printk(KERN_DEBUG "%s(%p, %p, %d, %p)\n", __func__,
111 filep, buf, (int)count, off); 111 filep, buf, (int)count, off);
112 112
113 if (list_empty(&dev->expired) && (dev->work == 0)) { 113 if (count < sizeof(int))
114 return -ENOSPC;
115
116 spin_lock_irq(&dev->lock);
117 while (list_empty(list) && (dev->work == 0)) {
118 spin_unlock_irq(&dev->lock);
114 if (filep->f_flags & O_NONBLOCK) 119 if (filep->f_flags & O_NONBLOCK)
115 return -EAGAIN; 120 return -EAGAIN;
116 wait_event_interruptible(dev->wait, (dev->work || 121 wait_event_interruptible(dev->wait, (dev->work ||
117 !list_empty(&dev->expired))); 122 !list_empty(list)));
118 if (signal_pending(current)) 123 if (signal_pending(current))
119 return -ERESTARTSYS; 124 return -ERESTARTSYS;
125 spin_lock_irq(&dev->lock);
120 } 126 }
121 if (count < sizeof(int))
122 return -ENOSPC;
123 if (dev->work) 127 if (dev->work)
124 dev->work = 0; 128 dev->work = 0;
125 if (!list_empty(&dev->expired)) { 129 if (!list_empty(list)) {
126 spin_lock_irqsave(&dev->lock, flags); 130 timer = list_first_entry(list, struct mISDNtimer, list);
127 timer = (struct mISDNtimer *)dev->expired.next;
128 list_del(&timer->list); 131 list_del(&timer->list);
129 spin_unlock_irqrestore(&dev->lock, flags); 132 spin_unlock_irq(&dev->lock);
130 if (put_user(timer->id, (int __user *)buf)) 133 if (put_user(timer->id, (int __user *)buf))
131 ret = -EFAULT; 134 ret = -EFAULT;
132 else 135 else
133 ret = sizeof(int); 136 ret = sizeof(int);
134 kfree(timer); 137 kfree(timer);
138 } else {
139 spin_unlock_irq(&dev->lock);
135 } 140 }
136 return ret; 141 return ret;
137} 142}