@@ -456,8 +456,13 @@ static int joydev_ioctl_common(struct joydev *joydev,
unsigned int cmd, void __user *argp)
{
struct input_dev *dev = joydev->handle.dev;
+ size_t len;
int i, j;
+ const char *name;
+ __u8 *tmpabspam;
+ __u16 *tmpkeypam;
+ /* Process fixed-sized commands. */
switch (cmd) {
case JS_SET_CAL:
@@ -499,55 +504,88 @@ static int joydev_ioctl_common(struct joydev *joydev,
return copy_to_user(argp, joydev->corr,
sizeof(joydev->corr[0]) * joydev->nabs) ? -EFAULT : 0;
- case JSIOCSAXMAP:
- if (copy_from_user(joydev->abspam, argp,
- sizeof(__u8) * (ABS_MAX + 1)))
- return -EFAULT;
+ }
+
+ /* Process variable-sized commands (the axis and button map commands
+ are considered variable-sized to decouple them from the values of
+ ABS_MAX and KEY_MAX). */
+ switch (cmd & ~IOCSIZE_MASK) {
+ case (JSIOCSAXMAP & ~IOCSIZE_MASK):
+ len = min_t(size_t, _IOC_SIZE(cmd), sizeof(joydev->abspam));
+
+ /* Validate the map. */
+ tmpabspam = kmalloc(len, GFP_KERNEL);
+ if (!tmpabspam)
+ return -ENOMEM;
+ if (copy_from_user(tmpabspam, argp, len)) {
+ kfree(tmpabspam);
+ return -EFAULT;
+ }
for (i = 0; i < joydev->nabs; i++) {
- if (joydev->abspam[i] > ABS_MAX)
+ if (tmpabspam[i] > ABS_MAX) {
+ kfree(tmpabspam);
return -EINVAL;
+ }
+ }
+
+ memcpy(joydev->abspam, tmpabspam, len);
+ kfree(tmpabspam);
+
+ for (i = 0; i < joydev->nabs; i++) {
joydev->absmap[joydev->abspam[i]] = i;
}
return 0;
- case JSIOCGAXMAP:
- return copy_to_user(argp, joydev->abspam,
- sizeof(__u8) * (ABS_MAX + 1)) ? -EFAULT : 0;
+ case (JSIOCGAXMAP & ~IOCSIZE_MASK):
+ len = min_t(size_t, _IOC_SIZE(cmd), sizeof(joydev->abspam));
+ return copy_to_user(argp, joydev->abspam, len) ? -EFAULT : len;
- case JSIOCSBTNMAP:
- if (copy_from_user(joydev->keypam, argp,
- sizeof(__u16) * (KEY_MAX - BTN_MISC + 1)))
- return -EFAULT;
+ case (JSIOCSBTNMAP & ~IOCSIZE_MASK):
+ len = min_t(size_t, _IOC_SIZE(cmd), sizeof(joydev->keypam));
+ /* Validate the map. */
+ tmpkeypam = kmalloc(len, GFP_KERNEL);
+ if (!tmpkeypam)
+ return -ENOMEM;
+ if (copy_from_user(tmpkeypam, argp, len)) {
+ kfree(tmpkeypam);
+ return -EFAULT;
+ }
for (i = 0; i < joydev->nkey; i++) {
- if (joydev->keypam[i] > KEY_MAX ||
- joydev->keypam[i] < BTN_MISC)
+ if (tmpkeypam[i] > KEY_MAX ||
+ tmpkeypam[i] < BTN_MISC) {
+ kfree(tmpkeypam);
return -EINVAL;
+ }
+ }
+
+ memcpy(joydev->keypam, tmpkeypam, len);
+ kfree(tmpkeypam);
+
+ for (i = 0; i < joydev->nkey; i++) {
joydev->keymap[joydev->keypam[i] - BTN_MISC] = i;
}
return 0;
- case JSIOCGBTNMAP:
- return copy_to_user(argp, joydev->keypam,
- sizeof(__u16) * (KEY_MAX - BTN_MISC + 1)) ? -EFAULT : 0;
+ case (JSIOCGBTNMAP & ~IOCSIZE_MASK):
+ len = min_t(size_t, _IOC_SIZE(cmd), sizeof(joydev->keypam));
+ return copy_to_user(argp, joydev->keypam, len) ? -EFAULT : len;
- default:
- if ((cmd & ~IOCSIZE_MASK) == JSIOCGNAME(0)) {
- int len;
- const char *name = dev->name;
-
- if (!name)
- return 0;
- len = strlen(name) + 1;
- if (len > _IOC_SIZE(cmd))
- len = _IOC_SIZE(cmd);
- if (copy_to_user(argp, name, len))
- return -EFAULT;
- return len;
- }
+ case JSIOCGNAME(0):
+ name = dev->name;
+
+ if (!name)
+ return 0;
+ len = strlen(name) + 1;
+ if (len > _IOC_SIZE(cmd))
+ len = _IOC_SIZE(cmd);
+ if (copy_to_user(argp, name, len))
+ return -EFAULT;
+ return len;
}
+
return -EINVAL;
}