gpiolib: improve the robustness of watch/unwatch ioctl()
authorBartosz Golaszewski <bgolaszewski@baylibre.com>
Wed, 15 Apr 2020 08:23:59 +0000 (10:23 +0200)
committerBartosz Golaszewski <bgolaszewski@baylibre.com>
Thu, 23 Apr 2020 15:55:27 +0000 (17:55 +0200)
This makes the new ioctl() a bit more robust - we now check if a line
is already being watched and return -EBUSY if the user-space tries to
start watching it again. Same for unwatch - return -EBUSY if user-space
tries to unwatch a line that's not being watched.

Fixes: 51c1064e82e7 ("gpiolib: add new ioctl() for monitoring changes in line info")
Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
drivers/gpio/gpiolib.c

index 40f2d7f..29f6abe 100644 (file)
@@ -1227,6 +1227,7 @@ static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
        void __user *ip = (void __user *)arg;
        struct gpio_desc *desc;
        __u32 offset;
+       int hwgpio;
 
        /* We fail any subsequent ioctl():s when the chip is gone */
        if (!gc)
@@ -1259,13 +1260,19 @@ static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
                if (IS_ERR(desc))
                        return PTR_ERR(desc);
 
+               hwgpio = gpio_chip_hwgpio(desc);
+
+               if (cmd == GPIO_GET_LINEINFO_WATCH_IOCTL &&
+                   test_bit(hwgpio, priv->watched_lines))
+                       return -EBUSY;
+
                gpio_desc_to_lineinfo(desc, &lineinfo);
 
                if (copy_to_user(ip, &lineinfo, sizeof(lineinfo)))
                        return -EFAULT;
 
                if (cmd == GPIO_GET_LINEINFO_WATCH_IOCTL)
-                       set_bit(gpio_chip_hwgpio(desc), priv->watched_lines);
+                       set_bit(hwgpio, priv->watched_lines);
 
                return 0;
        } else if (cmd == GPIO_GET_LINEHANDLE_IOCTL) {
@@ -1280,7 +1287,12 @@ static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
                if (IS_ERR(desc))
                        return PTR_ERR(desc);
 
-               clear_bit(gpio_chip_hwgpio(desc), priv->watched_lines);
+               hwgpio = gpio_chip_hwgpio(desc);
+
+               if (!test_bit(hwgpio, priv->watched_lines))
+                       return -EBUSY;
+
+               clear_bit(hwgpio, priv->watched_lines);
                return 0;
        }
        return -EINVAL;