diff mbox series

[v3] Input: uinput - Add UI_SET_PHYS_STR and UI_SET_UNIQ_STR

Message ID 20191204135434.v3.1.Ib53f70556ffe94d9a1903632ee9b0dc929f94557@changeid (mailing list archive)
State Superseded
Headers show
Series [v3] Input: uinput - Add UI_SET_PHYS_STR and UI_SET_UNIQ_STR | expand

Commit Message

Abhishek Pandit-Subedi Dec. 4, 2019, 9:55 p.m. UTC
The ioctl definition for UI_SET_PHYS is ambiguous because it is defined
with size = sizeof(char*) but is expected to be given a variable length
string. Add a deprecation notice for UI_SET_PHYS and provide
UI_SET_PHYS_STR(len) which expects a size from the user.

Also support setting the uniq attribute of the input device. The uniq
attribute is used as a unique identifier for the connected device.

For example, uinput devices created by BlueZ will store the address of
the connected device as the uniq property.

Signed-off-by: Abhishek Pandit-Subedi <abhishekpandit@chromium.org>
---
Hi input maintainers,

Here is an updated patch that refactors the ioctl handlers (properly
allowing the size to be set from userspace). When calling the new
ioctls, the call signature will look like this:

```
ioctl(fd, UI_SET_PHYS_STR(18), "00:11:22:33:44:55");
```

I've tested this on a Chromebook running kernel v4.19 with a sample
program compiled for both 32-bit (i.e. gcc -m32 test.c) and 64-bit.

The final uinput device looks like this:

```
udevadm info -a -p /devices/virtual/input/input18

Udevadm info starts with the device specified by the devpath and then
walks up the chain of parent devices. It prints for every device
found, all possible attributes in the udev rules key format.
A rule to match, can be composed by the attributes of the device
and the attributes from one single parent device.

  looking at device '/devices/virtual/input/input18':
    KERNEL=="input18"
    SUBSYSTEM=="input"
    DRIVER==""
    ATTR{inhibited}=="0"
    ATTR{name}=="Test"
    ATTR{phys}=="00:00:00:33:44:55"
    ATTR{properties}=="0"
    ATTR{uniq}=="00:11:22:00:00:00"

```


Changes in v3:
- Added UI_SET_PHYS_STR(len) and UI_SET_UNIQ_STR(len) and added
  deprecation notice for UI_SET_PHYS

Changes in v2:
- Added compat handling for UI_SET_UNIQ

 drivers/input/misc/uinput.c | 41 ++++++++++++++++++++++++++++++++++++-
 include/uapi/linux/uinput.h |  5 +++++
 2 files changed, 45 insertions(+), 1 deletion(-)

Comments

Dmitry Torokhov Dec. 5, 2019, 1:39 a.m. UTC | #1
On Wed, Dec 04, 2019 at 01:55:35PM -0800, Abhishek Pandit-Subedi wrote:
> The ioctl definition for UI_SET_PHYS is ambiguous because it is defined
> with size = sizeof(char*) but is expected to be given a variable length
> string. Add a deprecation notice for UI_SET_PHYS and provide
> UI_SET_PHYS_STR(len) which expects a size from the user.
> 
> Also support setting the uniq attribute of the input device. The uniq
> attribute is used as a unique identifier for the connected device.
> 
> For example, uinput devices created by BlueZ will store the address of
> the connected device as the uniq property.
> 
> Signed-off-by: Abhishek Pandit-Subedi <abhishekpandit@chromium.org>
> ---
> Hi input maintainers,
> 
> Here is an updated patch that refactors the ioctl handlers (properly
> allowing the size to be set from userspace). When calling the new
> ioctls, the call signature will look like this:
> 
> ```
> ioctl(fd, UI_SET_PHYS_STR(18), "00:11:22:33:44:55");
> ```
> 
> I've tested this on a Chromebook running kernel v4.19 with a sample
> program compiled for both 32-bit (i.e. gcc -m32 test.c) and 64-bit.
> 
> The final uinput device looks like this:
> 
> ```
> udevadm info -a -p /devices/virtual/input/input18
> 
> Udevadm info starts with the device specified by the devpath and then
> walks up the chain of parent devices. It prints for every device
> found, all possible attributes in the udev rules key format.
> A rule to match, can be composed by the attributes of the device
> and the attributes from one single parent device.
> 
>   looking at device '/devices/virtual/input/input18':
>     KERNEL=="input18"
>     SUBSYSTEM=="input"
>     DRIVER==""
>     ATTR{inhibited}=="0"
>     ATTR{name}=="Test"
>     ATTR{phys}=="00:00:00:33:44:55"
>     ATTR{properties}=="0"
>     ATTR{uniq}=="00:11:22:00:00:00"
> 
> ```
> 
> 
> Changes in v3:
> - Added UI_SET_PHYS_STR(len) and UI_SET_UNIQ_STR(len) and added
>   deprecation notice for UI_SET_PHYS
> 
> Changes in v2:
> - Added compat handling for UI_SET_UNIQ
> 
>  drivers/input/misc/uinput.c | 41 ++++++++++++++++++++++++++++++++++++-
>  include/uapi/linux/uinput.h |  5 +++++
>  2 files changed, 45 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c
> index 84051f20b18a..27a750896c71 100644
> --- a/drivers/input/misc/uinput.c
> +++ b/drivers/input/misc/uinput.c
> @@ -20,6 +20,7 @@
>   */
>  #include <uapi/linux/uinput.h>
>  #include <linux/poll.h>
> +#include <linux/printk.h>
>  #include <linux/sched.h>
>  #include <linux/slab.h>
>  #include <linux/module.h>
> @@ -280,7 +281,7 @@ static int uinput_dev_flush(struct input_dev *dev, struct file *file)
>  
>  static void uinput_destroy_device(struct uinput_device *udev)
>  {
> -	const char *name, *phys;
> +	const char *name, *phys, *uniq;
>  	struct input_dev *dev = udev->dev;
>  	enum uinput_state old_state = udev->state;
>  
> @@ -289,6 +290,7 @@ static void uinput_destroy_device(struct uinput_device *udev)
>  	if (dev) {
>  		name = dev->name;
>  		phys = dev->phys;
> +		uniq = dev->uniq;
>  		if (old_state == UIST_CREATED) {
>  			uinput_flush_requests(udev);
>  			input_unregister_device(dev);
> @@ -297,6 +299,7 @@ static void uinput_destroy_device(struct uinput_device *udev)
>  		}
>  		kfree(name);
>  		kfree(phys);
> +		kfree(uniq);
>  		udev->dev = NULL;
>  	}
>  }
> @@ -840,6 +843,7 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
>  	struct uinput_ff_erase  ff_erase;
>  	struct uinput_request   *req;
>  	char			*phys;
> +	char			*uniq;
>  	const char		*name;
>  	unsigned int		size;
>  
> @@ -916,6 +920,8 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
>  		goto out;
>  
>  	case UI_SET_PHYS:
> +		pr_warn_once("uinput: UI_SET_PHYS is deprecated. Use UI_SET_PHYS_STR");
> +
>  		if (udev->state == UIST_CREATED) {
>  			retval = -EINVAL;
>  			goto out;
> @@ -1023,6 +1029,39 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
>  	case UI_ABS_SETUP & ~IOCSIZE_MASK:
>  		retval = uinput_abs_setup(udev, p, size);
>  		goto out;
> +
> +	case UI_SET_PHYS_STR(0):
> +		if (udev->state == UIST_CREATED) {
> +			retval = -EINVAL;
> +			goto out;
> +		}
> +
> +		phys = strndup_user(p, size);
> +		if (IS_ERR(phys)) {
> +			retval = PTR_ERR(phys);
> +			goto out;
> +		}
> +
> +		kfree(udev->dev->phys);
> +		udev->dev->phys = phys;

Could we maybe refactor this into uinput_get_user_str(udev,
&udev->dev->phys, p, size) ?

> +		goto out;
> +
> +	case UI_SET_UNIQ_STR(0):
> +		if (udev->state == UIST_CREATED) {
> +			retval = -EINVAL;
> +			goto out;
> +		}
> +
> +		uniq = strndup_user(p, size);
> +		if (IS_ERR(uniq)) {
> +			retval = PTR_ERR(uniq);
> +			goto out;
> +		}
> +
> +		kfree(udev->dev->uniq);
> +		udev->dev->uniq = uniq;
> +		goto out;
> +
>  	}
>  
>  	retval = -EINVAL;
> diff --git a/include/uapi/linux/uinput.h b/include/uapi/linux/uinput.h
> index c9e677e3af1d..84d4fa142830 100644
> --- a/include/uapi/linux/uinput.h
> +++ b/include/uapi/linux/uinput.h
> @@ -142,9 +142,14 @@ struct uinput_abs_setup {
>  #define UI_SET_LEDBIT		_IOW(UINPUT_IOCTL_BASE, 105, int)
>  #define UI_SET_SNDBIT		_IOW(UINPUT_IOCTL_BASE, 106, int)
>  #define UI_SET_FFBIT		_IOW(UINPUT_IOCTL_BASE, 107, int)
> +
> +/* DEPRECATED: Data size is ambiguous. Use UI_SET_PHYS_STR instead. */
>  #define UI_SET_PHYS		_IOW(UINPUT_IOCTL_BASE, 108, char*)
> +
>  #define UI_SET_SWBIT		_IOW(UINPUT_IOCTL_BASE, 109, int)
>  #define UI_SET_PROPBIT		_IOW(UINPUT_IOCTL_BASE, 110, int)
> +#define UI_SET_PHYS_STR(len)	_IOC(_IOC_WRITE, UINPUT_IOCTL_BASE, 111, len)
> +#define UI_SET_UNIQ_STR(len)	_IOC(_IOC_WRITE, UINPUT_IOCTL_BASE, 112, len)
>  
>  #define UI_BEGIN_FF_UPLOAD	_IOWR(UINPUT_IOCTL_BASE, 200, struct uinput_ff_upload)
>  #define UI_END_FF_UPLOAD	_IOW(UINPUT_IOCTL_BASE, 201, struct uinput_ff_upload)
> -- 
> 2.24.0.393.g34dc348eaf-goog
>
diff mbox series

Patch

diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c
index 84051f20b18a..27a750896c71 100644
--- a/drivers/input/misc/uinput.c
+++ b/drivers/input/misc/uinput.c
@@ -20,6 +20,7 @@ 
  */
 #include <uapi/linux/uinput.h>
 #include <linux/poll.h>
+#include <linux/printk.h>
 #include <linux/sched.h>
 #include <linux/slab.h>
 #include <linux/module.h>
@@ -280,7 +281,7 @@  static int uinput_dev_flush(struct input_dev *dev, struct file *file)
 
 static void uinput_destroy_device(struct uinput_device *udev)
 {
-	const char *name, *phys;
+	const char *name, *phys, *uniq;
 	struct input_dev *dev = udev->dev;
 	enum uinput_state old_state = udev->state;
 
@@ -289,6 +290,7 @@  static void uinput_destroy_device(struct uinput_device *udev)
 	if (dev) {
 		name = dev->name;
 		phys = dev->phys;
+		uniq = dev->uniq;
 		if (old_state == UIST_CREATED) {
 			uinput_flush_requests(udev);
 			input_unregister_device(dev);
@@ -297,6 +299,7 @@  static void uinput_destroy_device(struct uinput_device *udev)
 		}
 		kfree(name);
 		kfree(phys);
+		kfree(uniq);
 		udev->dev = NULL;
 	}
 }
@@ -840,6 +843,7 @@  static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
 	struct uinput_ff_erase  ff_erase;
 	struct uinput_request   *req;
 	char			*phys;
+	char			*uniq;
 	const char		*name;
 	unsigned int		size;
 
@@ -916,6 +920,8 @@  static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
 		goto out;
 
 	case UI_SET_PHYS:
+		pr_warn_once("uinput: UI_SET_PHYS is deprecated. Use UI_SET_PHYS_STR");
+
 		if (udev->state == UIST_CREATED) {
 			retval = -EINVAL;
 			goto out;
@@ -1023,6 +1029,39 @@  static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
 	case UI_ABS_SETUP & ~IOCSIZE_MASK:
 		retval = uinput_abs_setup(udev, p, size);
 		goto out;
+
+	case UI_SET_PHYS_STR(0):
+		if (udev->state == UIST_CREATED) {
+			retval = -EINVAL;
+			goto out;
+		}
+
+		phys = strndup_user(p, size);
+		if (IS_ERR(phys)) {
+			retval = PTR_ERR(phys);
+			goto out;
+		}
+
+		kfree(udev->dev->phys);
+		udev->dev->phys = phys;
+		goto out;
+
+	case UI_SET_UNIQ_STR(0):
+		if (udev->state == UIST_CREATED) {
+			retval = -EINVAL;
+			goto out;
+		}
+
+		uniq = strndup_user(p, size);
+		if (IS_ERR(uniq)) {
+			retval = PTR_ERR(uniq);
+			goto out;
+		}
+
+		kfree(udev->dev->uniq);
+		udev->dev->uniq = uniq;
+		goto out;
+
 	}
 
 	retval = -EINVAL;
diff --git a/include/uapi/linux/uinput.h b/include/uapi/linux/uinput.h
index c9e677e3af1d..84d4fa142830 100644
--- a/include/uapi/linux/uinput.h
+++ b/include/uapi/linux/uinput.h
@@ -142,9 +142,14 @@  struct uinput_abs_setup {
 #define UI_SET_LEDBIT		_IOW(UINPUT_IOCTL_BASE, 105, int)
 #define UI_SET_SNDBIT		_IOW(UINPUT_IOCTL_BASE, 106, int)
 #define UI_SET_FFBIT		_IOW(UINPUT_IOCTL_BASE, 107, int)
+
+/* DEPRECATED: Data size is ambiguous. Use UI_SET_PHYS_STR instead. */
 #define UI_SET_PHYS		_IOW(UINPUT_IOCTL_BASE, 108, char*)
+
 #define UI_SET_SWBIT		_IOW(UINPUT_IOCTL_BASE, 109, int)
 #define UI_SET_PROPBIT		_IOW(UINPUT_IOCTL_BASE, 110, int)
+#define UI_SET_PHYS_STR(len)	_IOC(_IOC_WRITE, UINPUT_IOCTL_BASE, 111, len)
+#define UI_SET_UNIQ_STR(len)	_IOC(_IOC_WRITE, UINPUT_IOCTL_BASE, 112, len)
 
 #define UI_BEGIN_FF_UPLOAD	_IOWR(UINPUT_IOCTL_BASE, 200, struct uinput_ff_upload)
 #define UI_END_FF_UPLOAD	_IOW(UINPUT_IOCTL_BASE, 201, struct uinput_ff_upload)