On Thu, 2015-03-19 at 03:07 -0700, Keith Packard wrote: Hi,
unfortunately there are a few issues with this driver. Please see the comments in the quoted code. Regards Oliver > +#define CK_DEBUG(mask, fmt, args...) do { \ > + if (unlikely(debug & (mask))) \ > + printk(KERN_INFO " chaoskey: %s: %d " fmt, __func__, > __LINE__, ##args); \ > + } while (0) > + > +#define CK_ENTER(mask, what) CK_DEBUG(mask, "Enter " what) > +#define CK_LEAVE(mask, what,fmt,ret) CK_DEBUG(mask, "Leave " what ":" fmt, > ret) > +#define CK_LEAVE_INT(mask, what,ret) CK_LEAVE(mask, what,"%d",ret) > +#define CK_LEAVE_SIZE(mask, what,ret) CK_LEAVE(mask, what,"%zu",ret) > +#define CK_LEAVE_SSIZE(mask, what,ret) CK_LEAVE(mask, what,"%zd",ret) > +#define CK_LEAVE_VOID(mask, what) CK_DEBUG(mask, "Leave " what) Do we really need yet another version of home grown debug macros? > +/* Driver-local specific stuff */ > +struct chaoskey { > + struct usb_device *udev; > + struct usb_interface *interface; > + char in_ep; > + struct mutex lock; > + struct mutex rng_lock; > + int open; /* open count */ > + int present; /* device not disconnected */ > + int size; /* size of buf */ > + int valid; /* bytes of buf read */ > + int used; /* bytes of buf consumed */ > + char *name; /* product + serial */ > + struct hwrng hwrng; /* Embedded struct for hwrng */ > + int hwrng_registered; /* registered with hwrng API */ > + wait_queue_head_t wait_q; /* for timeouts */ > + char buf[0]; That is a violation of the DMA rules on non-coherent architectures. The buffer must be allocated separately. > +}; > +static int chaoskey_probe(struct usb_interface *interface, > + const struct usb_device_id *id) > +{ > + struct usb_device *udev = interface_to_usbdev(interface); > + struct usb_host_interface *altsetting = interface->cur_altsetting; > + int i; > + int in_ep = -1; > + struct chaoskey *dev; > + int result; > + int size; > + > + CK_ENTER(DEBUG_INIT, "probe"); > + > + /* Find the first bulk IN endpoint and its packet size */ > + for (i = 0; i < altsetting->desc.bNumEndpoints; i++) { > + if (usb_endpoint_is_bulk_in(&altsetting->endpoint[i].desc)) { > + in_ep = altsetting->endpoint[i].desc.bEndpointAddress; > + size = altsetting->endpoint[i].desc.wMaxPacketSize; > + break; > + } > + } > + > + /* Validate endpoint and size */ > + if (in_ep == -1) { > + CK_LEAVE_INT(DEBUG_INIT, "probe (ep)", -ENODEV); > + return -ENODEV; > + } > + if (size <= 0) { > + CK_LEAVE_INT(DEBUG_INIT, "probe (size)", -ENODEV); > + return -ENODEV; > + } > + > + /* Looks good, allocate and initialize */ > + CK_DEBUG(DEBUG_INIT, "New chaoskey, in_ep %d size %d\n", in_ep, size); > + > + dev = kzalloc (sizeof (struct chaoskey) + size, GFP_KERNEL); > + You do the test for the plausibility of size after you've used it. > + if (dev == NULL) { > + CK_LEAVE_INT(DEBUG_INIT, "probe (dev)", -ENOMEM); > + return -ENOMEM; > + } > + > + dev->udev = udev; This can be easily deduced from the interface. > + dev->interface = interface; > + > + dev->in_ep = in_ep; > + > + if (size > CHAOSKEY_BUF_LEN) { > + CK_DEBUG(DEBUG_INIT, "chaoskey size %d reduced to %d\n", > size, > + CHAOSKEY_BUF_LEN); > + size = CHAOSKEY_BUF_LEN; > + } > + > + dev->size = size; > + dev->present = 1; > + > + init_waitqueue_head(&dev->wait_q); > + > + usb_set_intfdata(interface, dev); > + > + result = usb_register_dev(interface, &chaoskey_class); > + if (result) { > + dev_err(&interface->dev, "Unable to allocate minor number.\n"); > + usb_set_intfdata(interface, NULL); > + chaoskey_free(dev); > + CK_LEAVE_INT(DEBUG_INIT, "probe (register)", result); > + return result; > + } > + That is a race condition. At this point the device can be opened and open() uses the mutexes. > + mutex_init(&dev->lock); > + mutex_init(&dev->rng_lock); Shift this. > + > + /* Construct a name using the product and serial values. Each > + * device needs a unique name for the hwrng code > + */ > + dev->name = NULL; > + > + if (udev->product && udev->serial) { > + dev->name = kmalloc(strlen(udev->product) + 1 + > strlen(udev->serial) + 1, GFP_KERNEL); > + if (dev->name) { > + strcpy(dev->name, udev->product); > + strcat(dev->name, "-"); > + strcat(dev->name, udev->serial); > + } > + } > + > + dev->hwrng.name = dev->name ? dev->name : chaoskey_driver.name; > + dev->hwrng.read = chaoskey_rng_read; > + > + /* Set the 'quality' metric. Quality is measured in units of > + * 1/1024's of a bit ("mills"). This should be set to 1024, > + * but there is a bug in the hwrng core which masks it with > + * 1023. > + * > + * The patch that has been merged to the crypto development > + * tree for that bug limits the value to 1024 at most, so by > + * setting this to 1024 + 1023, we get 1023 before the fix is > + * merged and 1024 afterwards. We'll patch this driver once > + * both bits of code are in the same tree. > + */ > + dev->hwrng.quality = 1024 + 1023; > + > + dev->hwrng_registered = (hwrng_register(&dev->hwrng) == 0); > + if (!dev->hwrng_registered) > + CK_DEBUG(DEBUG_INIT, "probe hwrng register failed: %d\n", > result); > + > + usb_enable_autosuspend(udev); > + > + CK_LEAVE_INT(DEBUG_INIT, "probe", 0); > + return 0; > +} > + > +static ssize_t chaoskey_read(struct file *file, > + char __user *buffer, > + size_t count, > + loff_t * ppos) > +{ > + struct chaoskey *dev; > + int intr; > + ssize_t read_count = 0; > + int this_time; > + int result; > + > + CK_ENTER(DEBUG_DEV, "read"); > + dev = file->private_data; > + > + if (dev == NULL || !dev->present) { > + CK_LEAVE_INT(DEBUG_DEV, "read (present)", -ENODEV); > + return -ENODEV; > + } > + > + > + CK_DEBUG(DEBUG_DEV, "read %zu\n", count); > + > + while (count > 0) { > + > + /* Grab the rng_lock briefly to ensure that the hwrng interface > + * gets priority over other user access > + */ > + intr = mutex_lock_interruptible(&dev->rng_lock); > + if (intr) { > + CK_LEAVE_INT(DEBUG_DEV, "read (rng_lock)", -EINTR); > + return -EINTR; At this point you may have already copied data to user space. A signal should cause a short read in that case, not -EINTR. Also the mutex is still held. > + } > + mutex_unlock(&dev->rng_lock); > + > + intr = mutex_lock_interruptible(&dev->lock); > + if (intr) { > + CK_LEAVE_INT(DEBUG_DEV, "read (lock)", -EINTR); The same issue as above. > + return -EINTR; > + } > + if (dev->valid == dev->used) { > + result = _chaoskey_fill(dev); > + if (result) { > + if (read_count) { > + CK_LEAVE_SIZE(DEBUG_DEV, "read (count)", > + read_count); > + return read_count; The mutex is still held. > + } > + CK_LEAVE_INT(DEBUG_DEV, "read (result)", > result); > + return result; The mutex is still held. > + } > + > + /* Read returned zero bytes */ > + if (dev->used == dev->valid) { > + CK_LEAVE_SSIZE(DEBUG_DEV, "read (zero)", > read_count); > + return read_count; > + } > + } > + > + this_time = dev->valid - dev->used; > + if (this_time > count) > + this_time = count; > + > + if (copy_to_user(buffer, dev->buf + dev->used, this_time)) { > + mutex_unlock(&dev->lock); > + CK_LEAVE_INT(DEBUG_DEV, "read (copyout)", -EFAULT); > + return -EFAULT; > + } > + > + count -= this_time; > + read_count += this_time; > + buffer += this_time; > + dev->used += this_time; > + mutex_unlock(&dev->lock); > + } > + > + CK_LEAVE_SSIZE(DEBUG_DEV, "read", read_count); > + return read_count; > +} > + -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html