From 5402a7349d26875f69c184badf87b88541b1cf6c Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 5 Aug 2008 11:42:42 -0400 Subject: Input: evdev - split EVIOCGBIT handlig into a separate function Signed-off-by: Linus Torvalds Signed-off-by: Dmitry Torokhov --- drivers/input/evdev.c | 44 ++++++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 20 deletions(-) (limited to 'drivers/input/evdev.c') diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index 2d65411f6763..ef8c2ed792c3 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -647,6 +647,28 @@ static int str_to_user(const char *str, unsigned int maxlen, void __user *p) return copy_to_user(p, str, len) ? -EFAULT : len; } +static int handle_eviocgbit(struct input_dev *dev, unsigned int cmd, void __user *p, int compat_mode) +{ + unsigned long *bits; + int len; + + switch (_IOC_NR(cmd) & EV_MAX) { + + case 0: bits = dev->evbit; len = EV_MAX; break; + case EV_KEY: bits = dev->keybit; len = KEY_MAX; break; + case EV_REL: bits = dev->relbit; len = REL_MAX; break; + case EV_ABS: bits = dev->absbit; len = ABS_MAX; break; + case EV_MSC: bits = dev->mscbit; len = MSC_MAX; break; + case EV_LED: bits = dev->ledbit; len = LED_MAX; break; + case EV_SND: bits = dev->sndbit; len = SND_MAX; break; + case EV_FF: bits = dev->ffbit; len = FF_MAX; break; + case EV_SW: bits = dev->swbit; len = SW_MAX; break; + default: return -EINVAL; + } + return bits_to_user(bits, len, _IOC_SIZE(cmd), p, compat_mode); +} + + static long evdev_do_ioctl(struct file *file, unsigned int cmd, void __user *p, int compat_mode) { @@ -733,26 +755,8 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd, if (_IOC_DIR(cmd) == _IOC_READ) { - if ((_IOC_NR(cmd) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0, 0))) { - - unsigned long *bits; - int len; - - switch (_IOC_NR(cmd) & EV_MAX) { - - case 0: bits = dev->evbit; len = EV_MAX; break; - case EV_KEY: bits = dev->keybit; len = KEY_MAX; break; - case EV_REL: bits = dev->relbit; len = REL_MAX; break; - case EV_ABS: bits = dev->absbit; len = ABS_MAX; break; - case EV_MSC: bits = dev->mscbit; len = MSC_MAX; break; - case EV_LED: bits = dev->ledbit; len = LED_MAX; break; - case EV_SND: bits = dev->sndbit; len = SND_MAX; break; - case EV_FF: bits = dev->ffbit; len = FF_MAX; break; - case EV_SW: bits = dev->swbit; len = SW_MAX; break; - default: return -EINVAL; - } - return bits_to_user(bits, len, _IOC_SIZE(cmd), p, compat_mode); - } + if ((_IOC_NR(cmd) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0, 0))) + return handle_eviocgbit(dev, cmd, p, compat_mode); if (_IOC_NR(cmd) == _IOC_NR(EVIOCGKEY(0))) return bits_to_user(dev->key, KEY_MAX, _IOC_SIZE(cmd), -- cgit v1.2.2 From f2afa7711f8585ffc088ba538b9a510e0d5dca12 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 8 Aug 2008 11:46:53 -0400 Subject: Input: paper over a bug in Synaptics X driver Signed-off-by: Dmitry Torokhov --- drivers/input/evdev.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) (limited to 'drivers/input/evdev.c') diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index ef8c2ed792c3..a92d81567559 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -647,8 +647,10 @@ static int str_to_user(const char *str, unsigned int maxlen, void __user *p) return copy_to_user(p, str, len) ? -EFAULT : len; } +#define OLD_KEY_MAX 0x1ff static int handle_eviocgbit(struct input_dev *dev, unsigned int cmd, void __user *p, int compat_mode) { + static unsigned long keymax_warn_time; unsigned long *bits; int len; @@ -665,9 +667,26 @@ static int handle_eviocgbit(struct input_dev *dev, unsigned int cmd, void __user case EV_SW: bits = dev->swbit; len = SW_MAX; break; default: return -EINVAL; } + + /* + * Work around bugs in userspace programs that like to do + * EVIOCGBIT(EV_KEY, KEY_MAX) and not realize that 'len' + * should be in bytes, not in bits. + */ + if ((_IOC_NR(cmd) & EV_MAX) == EV_KEY && _IOC_SIZE(cmd) == OLD_KEY_MAX) { + len = OLD_KEY_MAX; + if (printk_timed_ratelimit(&keymax_warn_time, 10 * 1000)) + printk(KERN_WARNING + "evdev.c(EVIOCGBIT): Suspicious buffer size %d, " + "limiting output to %d bytes. See " + "http://userweb.kernel.org/~dtor/eviocgbit-bug.html\n", + OLD_KEY_MAX, + BITS_TO_LONGS(OLD_KEY_MAX) * sizeof(long)); + } + return bits_to_user(bits, len, _IOC_SIZE(cmd), p, compat_mode); } - +#undef OLD_KEY_MAX static long evdev_do_ioctl(struct file *file, unsigned int cmd, void __user *p, int compat_mode) -- cgit v1.2.2 From c85e2031eb55381a5e2f0f66c2e1d62ecd58eb85 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 19 Aug 2008 11:28:23 -0400 Subject: Input: evdev - fix printf() format for sizeof commit f2afa7711f8585ffc088ba538b9a510e0d5dca12 ("Input: paper over a bug in Synaptics X driver") introduced a compiler warning on 64-bit platforms, as sizeof() returns a size_t, not an (unsigned) int: | drivers/input/evdev.c: In function 'handle_eviocgbit': | drivers/input/evdev.c:684: warning: format '%d' expects type 'int', but argument 3 has type 'long unsigned int' Use the proper `z' modifier for size_t, and make the printf() formats for the sizes unsigned while we're at it. Signed-off-by: Geert Uytterhoeven Signed-off-by: Dmitry Torokhov --- drivers/input/evdev.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/input/evdev.c') diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index a92d81567559..3524bef62be6 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -677,8 +677,8 @@ static int handle_eviocgbit(struct input_dev *dev, unsigned int cmd, void __user len = OLD_KEY_MAX; if (printk_timed_ratelimit(&keymax_warn_time, 10 * 1000)) printk(KERN_WARNING - "evdev.c(EVIOCGBIT): Suspicious buffer size %d, " - "limiting output to %d bytes. See " + "evdev.c(EVIOCGBIT): Suspicious buffer size %u, " + "limiting output to %zu bytes. See " "http://userweb.kernel.org/~dtor/eviocgbit-bug.html\n", OLD_KEY_MAX, BITS_TO_LONGS(OLD_KEY_MAX) * sizeof(long)); -- cgit v1.2.2