diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2013-04-15 17:18:17 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2013-04-29 15:41:49 -0400 |
commit | ebb06be16bc9a1e66a010ca50c75c5128bafb4b1 (patch) | |
tree | 3a9212bb7f267519242b3f66420cb98527edee41 | |
parent | 1678ec00a632f8b9204e28e5c506128881171604 (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.c | 23 |
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 | |||
102 | mISDN_read(struct file *filep, char __user *buf, size_t count, loff_t *off) | 102 | mISDN_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 | } |