Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Input: uinput - rework ABS validation

Rework the uinput ABS validation to check passed absinfo data immediately,
but do ABS initialization as last step in UI_DEV_CREATE. The behavior
observed by user-space is not changed, as ABS initialization was never
checked for errors.

With this in place, the order of device initialization and abs
configuration is no longer fixed. Userspace can initialize the device and
afterwards set absinfo just fine.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
Reviewed-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Tested-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>

authored by

David Herrmann and committed by
Dmitry Torokhov
fbae10db 052876f8

+58 -60
+45 -44
drivers/input/misc/uinput.c
··· 256 256 static int uinput_create_device(struct uinput_device *udev) 257 257 { 258 258 struct input_dev *dev = udev->dev; 259 - int error; 259 + int error, nslot; 260 260 261 261 if (udev->state != UIST_SETUP_COMPLETE) { 262 262 printk(KERN_DEBUG "%s: write device info first\n", UINPUT_NAME); 263 263 return -EINVAL; 264 + } 265 + 266 + if (test_bit(ABS_MT_SLOT, dev->absbit)) { 267 + nslot = input_abs_get_max(dev, ABS_MT_SLOT) + 1; 268 + error = input_mt_init_slots(dev, nslot, 0); 269 + if (error) 270 + goto fail1; 271 + } else if (test_bit(ABS_MT_POSITION_X, dev->absbit)) { 272 + input_set_events_per_packet(dev, 60); 264 273 } 265 274 266 275 if (udev->ff_effects_max) { ··· 317 308 return 0; 318 309 } 319 310 311 + static int uinput_validate_absinfo(struct input_dev *dev, unsigned int code, 312 + const struct input_absinfo *abs) 313 + { 314 + int min, max; 315 + 316 + min = abs->minimum; 317 + max = abs->maximum; 318 + 319 + if ((min != 0 || max != 0) && max <= min) { 320 + printk(KERN_DEBUG 321 + "%s: invalid abs[%02x] min:%d max:%d\n", 322 + UINPUT_NAME, code, min, max); 323 + return -EINVAL; 324 + } 325 + 326 + if (abs->flat > max - min) { 327 + printk(KERN_DEBUG 328 + "%s: abs_flat #%02x out of range: %d (min:%d/max:%d)\n", 329 + UINPUT_NAME, code, abs->flat, min, max); 330 + return -EINVAL; 331 + } 332 + 333 + return 0; 334 + } 335 + 320 336 static int uinput_validate_absbits(struct input_dev *dev) 321 337 { 322 338 unsigned int cnt; 323 - int nslot; 339 + int error; 324 340 325 341 if (!test_bit(EV_ABS, dev->evbit)) 326 342 return 0; ··· 355 321 */ 356 322 357 323 for_each_set_bit(cnt, dev->absbit, ABS_CNT) { 358 - int min, max; 359 - 360 - min = input_abs_get_min(dev, cnt); 361 - max = input_abs_get_max(dev, cnt); 362 - 363 - if ((min != 0 || max != 0) && max <= min) { 364 - printk(KERN_DEBUG 365 - "%s: invalid abs[%02x] min:%d max:%d\n", 366 - UINPUT_NAME, cnt, 367 - input_abs_get_min(dev, cnt), 368 - input_abs_get_max(dev, cnt)); 324 + if (!dev->absinfo) 369 325 return -EINVAL; 370 - } 371 326 372 - if (input_abs_get_flat(dev, cnt) > 373 - input_abs_get_max(dev, cnt) - input_abs_get_min(dev, cnt)) { 374 - printk(KERN_DEBUG 375 - "%s: abs_flat #%02x out of range: %d " 376 - "(min:%d/max:%d)\n", 377 - UINPUT_NAME, cnt, 378 - input_abs_get_flat(dev, cnt), 379 - input_abs_get_min(dev, cnt), 380 - input_abs_get_max(dev, cnt)); 381 - return -EINVAL; 382 - } 383 - } 384 - 385 - if (test_bit(ABS_MT_SLOT, dev->absbit)) { 386 - nslot = input_abs_get_max(dev, ABS_MT_SLOT) + 1; 387 - input_mt_init_slots(dev, nslot, 0); 388 - } else if (test_bit(ABS_MT_POSITION_X, dev->absbit)) { 389 - input_set_events_per_packet(dev, 60); 327 + error = uinput_validate_absinfo(dev, cnt, &dev->absinfo[cnt]); 328 + if (error) 329 + return error; 390 330 } 391 331 392 332 return 0; ··· 383 375 { 384 376 struct uinput_setup setup; 385 377 struct input_dev *dev; 386 - int retval; 387 378 388 379 if (udev->state == UIST_CREATED) 389 380 return -EINVAL; ··· 402 395 if (!dev->name) 403 396 return -ENOMEM; 404 397 405 - retval = uinput_validate_absbits(dev); 406 - if (retval < 0) 407 - return retval; 408 - 409 398 udev->state = UIST_SETUP_COMPLETE; 410 399 return 0; 411 400 } ··· 411 408 { 412 409 struct uinput_abs_setup setup = {}; 413 410 struct input_dev *dev; 411 + int error; 414 412 415 413 if (size > sizeof(setup)) 416 414 return -E2BIG; ··· 427 423 428 424 dev = udev->dev; 429 425 426 + error = uinput_validate_absinfo(dev, setup.code, &setup.absinfo); 427 + if (error) 428 + return error; 429 + 430 430 input_alloc_absinfo(dev); 431 431 if (!dev->absinfo) 432 432 return -ENOMEM; 433 433 434 434 set_bit(setup.code, dev->absbit); 435 435 dev->absinfo[setup.code] = setup.absinfo; 436 - 437 - /* 438 - * We restore the state to UIST_NEW_DEVICE because the user has to call 439 - * UI_DEV_SETUP in the last place before UI_DEV_CREATE to check the 440 - * validity of the absbits. 441 - */ 442 - udev->state = UIST_NEW_DEVICE; 443 436 return 0; 444 437 } 445 438
+13 -16
include/uapi/linux/uinput.h
··· 72 72 /** 73 73 * UI_DEV_SETUP - Set device parameters for setup 74 74 * 75 - * This ioctl sets parameters for the input device to be created. It must be 76 - * issued *before* calling UI_DEV_CREATE or it will fail. This ioctl supersedes 77 - * the old "struct uinput_user_dev" method, which wrote this data via write(). 78 - * To actually set the absolute axes, you also need to call the ioctl 79 - * UI_ABS_SETUP *before* calling this ioctl. 75 + * This ioctl sets parameters for the input device to be created. It 76 + * supersedes the old "struct uinput_user_dev" method, which wrote this data 77 + * via write(). To actually set the absolute axes UI_ABS_SETUP should be 78 + * used. 80 79 * 81 - * This ioctl takes a "struct uinput_setup" object as argument. The fields of 80 + * The ioctl takes a "struct uinput_setup" object as argument. The fields of 82 81 * this object are as follows: 83 82 * id: See the description of "struct input_id". This field is 84 83 * copied unchanged into the new device. ··· 86 87 * See below for a description of FF with uinput. 87 88 * 88 89 * This ioctl can be called multiple times and will overwrite previous values. 89 - * If this ioctl fails with -EINVAL, you're recommended to use the old 90 - * "uinput_user_dev" method via write() as fallback, in case you run on an old 91 - * kernel that does not support this ioctl. 90 + * If this ioctl fails with -EINVAL, it is recommended to use the old 91 + * "uinput_user_dev" method via write() as a fallback, in case you run on an 92 + * old kernel that does not support this ioctl. 92 93 * 93 94 * This ioctl may fail with -EINVAL if it is not supported or if you passed 94 95 * incorrect values, -ENOMEM if the kernel runs out of memory or -EFAULT if the ··· 108 109 * UI_ABS_SETUP - Set absolute axis information for the device to setup 109 110 * 110 111 * This ioctl sets one absolute axis information for the input device to be 111 - * created. It must be issued *before* calling UI_DEV_SETUP and UI_DEV_CREATE 112 - * for every absolute axis the device exports. 113 - * This ioctl supersedes the old "struct uinput_user_dev" method, which wrote 112 + * created. It supersedes the old "struct uinput_user_dev" method, which wrote 114 113 * part of this data and the content of UI_DEV_SETUP via write(). 115 114 * 116 - * This ioctl takes a "struct uinput_abs_setup" object as argument. The fields 115 + * The ioctl takes a "struct uinput_abs_setup" object as argument. The fields 117 116 * of this object are as follows: 118 117 * code: The corresponding input code associated with this axis 119 118 * (ABS_X, ABS_Y, etc...) ··· 121 124 * UI_SET_ABSBIT, this ioctl will enable it. 122 125 * 123 126 * This ioctl can be called multiple times and will overwrite previous values. 124 - * If this ioctl fails with -EINVAL, you're recommended to use the old 125 - * "uinput_user_dev" method via write() as fallback, in case you run on an old 126 - * kernel that does not support this ioctl. 127 + * If this ioctl fails with -EINVAL, it is recommended to use the old 128 + * "uinput_user_dev" method via write() as a fallback, in case you run on an 129 + * old kernel that does not support this ioctl. 127 130 * 128 131 * This ioctl may fail with -EINVAL if it is not supported or if you passed 129 132 * incorrect values, -ENOMEM if the kernel runs out of memory or -EFAULT if the