Showing error 565

User: Jiri Slaby
Error type: Double Lock
Error type description: Some lock is locked twice unintentionally in a sequence
File location: drivers/hid/hidraw.c
Line in file: 52
Project: Linux Kernel
Project version: 2.6.28
Confirmation: Fixed by b0e14951ee0f6c29abc64b92ec7075a159ede37c
Tools: Stanse (1.2)
Clang Static Analyzer (3.0)
Entered: 2011-11-07 22:19:59 UTC


Source:

  1/*
  2 * HID raw devices, giving access to raw HID events.
  3 *
  4 * In comparison to hiddev, this device does not process the
  5 * hid events at all (no parsing, no lookups). This lets applications
  6 * to work on raw hid events as they want to, and avoids a need to
  7 * use a transport-specific userspace libhid/libusb libraries.
  8 *
  9 *  Copyright (c) 2007 Jiri Kosina
 10 */
 11
 12/*
 13 * This program is free software; you can redistribute it and/or modify it
 14 * under the terms and conditions of the GNU General Public License,
 15 * version 2, as published by the Free Software Foundation.
 16 *
 17 * You should have received a copy of the GNU General Public License along with
 18 * this program; if not, write to the Free Software Foundation, Inc.,
 19 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
 20 */
 21
 22#include <linux/fs.h>
 23#include <linux/module.h>
 24#include <linux/errno.h>
 25#include <linux/kernel.h>
 26#include <linux/init.h>
 27#include <linux/cdev.h>
 28#include <linux/poll.h>
 29#include <linux/device.h>
 30#include <linux/major.h>
 31#include <linux/hid.h>
 32#include <linux/mutex.h>
 33#include <linux/smp_lock.h>
 34
 35#include <linux/hidraw.h>
 36
 37static int hidraw_major;
 38static struct cdev hidraw_cdev;
 39static struct class *hidraw_class;
 40static struct hidraw *hidraw_table[HIDRAW_MAX_DEVICES];
 41static DEFINE_MUTEX(minors_lock);
 42
 43static ssize_t hidraw_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
 44{
 45        struct hidraw_list *list = file->private_data;
 46        int ret = 0, len;
 47        char *report;
 48        DECLARE_WAITQUEUE(wait, current);
 49
 50        while (ret == 0) {
 51
 52                mutex_lock(&list->read_mutex);
 53
 54                if (list->head == list->tail) {
 55                        add_wait_queue(&list->hidraw->wait, &wait);
 56                        set_current_state(TASK_INTERRUPTIBLE);
 57
 58                        while (list->head == list->tail) {
 59                                if (file->f_flags & O_NONBLOCK) {
 60                                        ret = -EAGAIN;
 61                                        break;
 62                                }
 63                                if (signal_pending(current)) {
 64                                        ret = -ERESTARTSYS;
 65                                        break;
 66                                }
 67                                if (!list->hidraw->exist) {
 68                                        ret = -EIO;
 69                                        break;
 70                                }
 71
 72                                /* allow O_NONBLOCK to work well from other threads */
 73                                mutex_unlock(&list->read_mutex);
 74                                schedule();
 75                                mutex_lock(&list->read_mutex);
 76                                set_current_state(TASK_INTERRUPTIBLE);
 77                        }
 78
 79                        set_current_state(TASK_RUNNING);
 80                        remove_wait_queue(&list->hidraw->wait, &wait);
 81                }
 82
 83                if (ret)
 84                        goto out;
 85
 86                report = list->buffer[list->tail].value;
 87                len = list->buffer[list->tail].len > count ?
 88                        count : list->buffer[list->tail].len;
 89
 90                if (copy_to_user(buffer, list->buffer[list->tail].value, len)) {
 91                        ret = -EFAULT;
 92                        goto out;
 93                }
 94                ret += len;
 95
 96                kfree(list->buffer[list->tail].value);
 97                list->tail = (list->tail + 1) & (HIDRAW_BUFFER_SIZE - 1);
 98        }
 99out:
100        mutex_unlock(&list->read_mutex);
101        return ret;
102}
103
104/* the first byte is expected to be a report number */
105static ssize_t hidraw_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
106{
107        unsigned int minor = iminor(file->f_path.dentry->d_inode);
108        /* FIXME: What stops hidraw_table going NULL */
109        struct hid_device *dev = hidraw_table[minor]->hid;
110        __u8 *buf;
111        int ret = 0;
112
113        if (!dev->hid_output_raw_report)
114                return -ENODEV;
115
116        if (count > HID_MAX_BUFFER_SIZE) {
117                printk(KERN_WARNING "hidraw: pid %d passed too large report\n",
118                                task_pid_nr(current));
119                return -EINVAL;
120        }
121
122        if (count < 2) {
123                printk(KERN_WARNING "hidraw: pid %d passed too short report\n",
124                                task_pid_nr(current));
125                return -EINVAL;
126        }
127
128        buf = kmalloc(count * sizeof(__u8), GFP_KERNEL);
129        if (!buf)
130                return -ENOMEM;
131
132        if (copy_from_user(buf, buffer, count)) {
133                ret = -EFAULT;
134                goto out;
135        }
136
137        ret = dev->hid_output_raw_report(dev, buf, count);
138out:
139        kfree(buf);
140        return ret;
141}
142
143static unsigned int hidraw_poll(struct file *file, poll_table *wait)
144{
145        struct hidraw_list *list = file->private_data;
146
147        poll_wait(file, &list->hidraw->wait, wait);
148        if (list->head != list->tail)
149                return POLLIN | POLLRDNORM;
150        if (!list->hidraw->exist)
151                return POLLERR | POLLHUP;
152        return 0;
153}
154
155static int hidraw_open(struct inode *inode, struct file *file)
156{
157        unsigned int minor = iminor(inode);
158        struct hidraw *dev;
159        struct hidraw_list *list;
160        int err = 0;
161
162        if (!(list = kzalloc(sizeof(struct hidraw_list), GFP_KERNEL))) {
163                err = -ENOMEM;
164                goto out;
165        }
166
167        lock_kernel();
168        mutex_lock(&minors_lock);
169        if (!hidraw_table[minor]) {
170                printk(KERN_EMERG "hidraw device with minor %d doesn't exist\n",
171                                minor);
172                kfree(list);
173                err = -ENODEV;
174                goto out_unlock;
175        }
176
177        list->hidraw = hidraw_table[minor];
178        mutex_init(&list->read_mutex);
179        list_add_tail(&list->node, &hidraw_table[minor]->list);
180        file->private_data = list;
181
182        dev = hidraw_table[minor];
183        if (!dev->open++) {
184                err = dev->hid->ll_driver->open(dev->hid);
185                if (err < 0)
186                        dev->open--;
187        }
188
189out_unlock:
190        mutex_unlock(&minors_lock);
191        unlock_kernel();
192out:
193        return err;
194
195}
196
197static int hidraw_release(struct inode * inode, struct file * file)
198{
199        unsigned int minor = iminor(inode);
200        struct hidraw *dev;
201        struct hidraw_list *list = file->private_data;
202
203        if (!hidraw_table[minor]) {
204                printk(KERN_EMERG "hidraw device with minor %d doesn't exist\n",
205                                minor);
206                return -ENODEV;
207        }
208
209        list_del(&list->node);
210        dev = hidraw_table[minor];
211        if (!dev->open--) {
212                if (list->hidraw->exist)
213                        dev->hid->ll_driver->close(dev->hid);
214                else
215                        kfree(list->hidraw);
216        }
217
218        kfree(list);
219
220        return 0;
221}
222
223static long hidraw_ioctl(struct file *file, unsigned int cmd,
224                                                        unsigned long arg)
225{
226        struct inode *inode = file->f_path.dentry->d_inode;
227        unsigned int minor = iminor(inode);
228        long ret = 0;
229        /* FIXME: What stops hidraw_table going NULL */
230        struct hidraw *dev = hidraw_table[minor];
231        void __user *user_arg = (void __user*) arg;
232
233        lock_kernel();
234        switch (cmd) {
235                case HIDIOCGRDESCSIZE:
236                        if (put_user(dev->hid->rsize, (int __user *)arg))
237                                ret = -EFAULT;
238                        break;
239
240                case HIDIOCGRDESC:
241                        {
242                                __u32 len;
243
244                                if (get_user(len, (int __user *)arg))
245                                        ret = -EFAULT;
246                                else if (len > HID_MAX_DESCRIPTOR_SIZE - 1)
247                                        ret = -EINVAL;
248                                else if (copy_to_user(user_arg + offsetof(
249                                        struct hidraw_report_descriptor,
250                                        value[0]),
251                                        dev->hid->rdesc,
252                                        min(dev->hid->rsize, len)))
253                                        ret = -EFAULT;
254                                break;
255                        }
256                case HIDIOCGRAWINFO:
257                        {
258                                struct hidraw_devinfo dinfo;
259
260                                dinfo.bustype = dev->hid->bus;
261                                dinfo.vendor = dev->hid->vendor;
262                                dinfo.product = dev->hid->product;
263                                if (copy_to_user(user_arg, &dinfo, sizeof(dinfo)))
264                                        ret = -EFAULT;
265                                break;
266                        }
267                default:
268                        ret = -ENOTTY;
269        }
270        unlock_kernel();
271        return ret;
272}
273
274static const struct file_operations hidraw_ops = {
275        .owner =        THIS_MODULE,
276        .read =         hidraw_read,
277        .write =        hidraw_write,
278        .poll =         hidraw_poll,
279        .open =         hidraw_open,
280        .release =      hidraw_release,
281        .unlocked_ioctl = hidraw_ioctl,
282};
283
284void hidraw_report_event(struct hid_device *hid, u8 *data, int len)
285{
286        struct hidraw *dev = hid->hidraw;
287        struct hidraw_list *list;
288
289        list_for_each_entry(list, &dev->list, node) {
290                list->buffer[list->head].value = kmemdup(data, len, GFP_ATOMIC);
291                list->buffer[list->head].len = len;
292                list->head = (list->head + 1) & (HIDRAW_BUFFER_SIZE - 1);
293                kill_fasync(&list->fasync, SIGIO, POLL_IN);
294        }
295
296        wake_up_interruptible(&dev->wait);
297}
298EXPORT_SYMBOL_GPL(hidraw_report_event);
299
300int hidraw_connect(struct hid_device *hid)
301{
302        int minor, result;
303        struct hidraw *dev;
304
305        /* TODO currently we accept any HID device. This should later
306         * probably be fixed to accept only those devices which provide
307         * non-input applications
308         */
309
310        dev = kzalloc(sizeof(struct hidraw), GFP_KERNEL);
311        if (!dev)
312                return -ENOMEM;
313
314        result = -EINVAL;
315
316        mutex_lock(&minors_lock);
317
318        for (minor = 0; minor < HIDRAW_MAX_DEVICES; minor++) {
319                if (hidraw_table[minor])
320                        continue;
321                hidraw_table[minor] = dev;
322                result = 0;
323                break;
324        }
325
326        if (result) {
327                mutex_unlock(&minors_lock);
328                kfree(dev);
329                goto out;
330        }
331
332        dev->dev = device_create(hidraw_class, NULL, MKDEV(hidraw_major, minor),
333                                 NULL, "%s%d", "hidraw", minor);
334
335        if (IS_ERR(dev->dev)) {
336                hidraw_table[minor] = NULL;
337                mutex_unlock(&minors_lock);
338                result = PTR_ERR(dev->dev);
339                kfree(dev);
340                goto out;
341        }
342
343        mutex_unlock(&minors_lock);
344        init_waitqueue_head(&dev->wait);
345        INIT_LIST_HEAD(&dev->list);
346
347        dev->hid = hid;
348        dev->minor = minor;
349
350        dev->exist = 1;
351        hid->hidraw = dev;
352
353out:
354        return result;
355
356}
357EXPORT_SYMBOL_GPL(hidraw_connect);
358
359void hidraw_disconnect(struct hid_device *hid)
360{
361        struct hidraw *hidraw = hid->hidraw;
362
363        hidraw->exist = 0;
364
365        mutex_lock(&minors_lock);
366        hidraw_table[hidraw->minor] = NULL;
367        mutex_unlock(&minors_lock);
368
369        device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor));
370
371        if (hidraw->open) {
372                hid->ll_driver->close(hid);
373                wake_up_interruptible(&hidraw->wait);
374        } else {
375                kfree(hidraw);
376        }
377}
378EXPORT_SYMBOL_GPL(hidraw_disconnect);
379
380int __init hidraw_init(void)
381{
382        int result;
383        dev_t dev_id;
384
385        result = alloc_chrdev_region(&dev_id, HIDRAW_FIRST_MINOR,
386                        HIDRAW_MAX_DEVICES, "hidraw");
387
388        hidraw_major = MAJOR(dev_id);
389
390        if (result < 0) {
391                printk(KERN_WARNING "hidraw: can't get major number\n");
392                result = 0;
393                goto out;
394        }
395
396        hidraw_class = class_create(THIS_MODULE, "hidraw");
397        if (IS_ERR(hidraw_class)) {
398                result = PTR_ERR(hidraw_class);
399                unregister_chrdev(hidraw_major, "hidraw");
400                goto out;
401        }
402
403        cdev_init(&hidraw_cdev, &hidraw_ops);
404        cdev_add(&hidraw_cdev, dev_id, HIDRAW_MAX_DEVICES);
405out:
406        return result;
407}
408
409void hidraw_exit(void)
410{
411        dev_t dev_id = MKDEV(hidraw_major, 0);
412
413        cdev_del(&hidraw_cdev);
414        class_destroy(hidraw_class);
415        unregister_chrdev_region(dev_id, HIDRAW_MAX_DEVICES);
416
417}