core: relax gpiod_chip_open() for symbolic links
authorAndy Shevchenko <andriy.shevchenko@linux.intel.com>
Thu, 6 Feb 2020 18:13:58 +0000 (20:13 +0200)
committerBartosz Golaszewski <bgolaszewski@baylibre.com>
Fri, 7 Feb 2020 12:47:56 +0000 (13:47 +0100)
User may ask device helper tool, for example, udev, to create a specific
symbolic link to a device node. GPIO chip character device node is not
exceptional. However, libgpiod in the commit d9b1c1f14c6b
("core: harden gpiod_chip_open()") went way too far in the hardening device
node check.

Relax that hardening for symbolic link to fix the regression.

Reproducer:

  % gpioinfo /dev/gpiochip5
  gpiochip5 - 16 lines:
      line   0:  "MUX33_DIR" "uart1-rx-oe" output active-high [used]
      ...

  % ln -sf /dev/gpiochip5 /dev/MyGPIO_5

  % gpioinfo /dev/MyGPIO_5
  gpioinfo: looking up chip /dev/MyGPIO_5: Inappropriate ioctl for device

Link: https://stackoverflow.com/questions/60057494/gpio-issue-with-sym-link
Fixes: d9b1c1f14c6b ("core: harden gpiod_chip_open()")
Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
lib/core.c

index 8352e18feda8ac28a0402d7639020bf2dbcaa79f..ed6301202784361cd388ec237af19f822102ff2c 100644 (file)
@@ -10,6 +10,7 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <gpiod.h>
+#include <limits.h>
 #include <linux/gpio.h>
 #include <poll.h>
 #include <stdint.h>
@@ -75,7 +76,7 @@ struct gpiod_chip {
 
 static bool is_gpiochip_cdev(const char *path)
 {
-       char *name, *pathcpy, *sysfsp, sysfsdev[16], devstr[16];
+       char *name, *realname, *sysfsp, sysfsdev[16], devstr[16];
        struct stat statbuf;
        bool ret = false;
        int rv, fd;
@@ -85,6 +86,19 @@ static bool is_gpiochip_cdev(const char *path)
        if (rv)
                goto out;
 
+       /*
+        * Is it a symbolic link? We have to resolve symbolic link before
+        * checking the rest.
+        */
+       realname = S_ISLNK(statbuf.st_mode) ? realpath(path, NULL)
+                                           : strdup(path);
+       if (realname == NULL)
+               goto out;
+
+       rv = stat(realname, &statbuf);
+       if (rv)
+               goto out_free_realname;
+
        /* Is it a character device? */
        if (!S_ISCHR(statbuf.st_mode)) {
                /*
@@ -94,20 +108,16 @@ static bool is_gpiochip_cdev(const char *path)
                 * libgpiod from before the introduction of this routine.
                 */
                errno = ENOTTY;
-               goto out;
+               goto out_free_realname;
        }
 
        /* Get the basename. */
-       pathcpy = strdup(path);
-       if (!pathcpy)
-               goto out;
-
-       name = basename(pathcpy);
+       name = basename(realname);
 
        /* Do we have a corresponding sysfs attribute? */
        rv = asprintf(&sysfsp, "/sys/bus/gpio/devices/%s/dev", name);
        if (rv < 0)
-               goto out_free_pathcpy;
+               goto out_free_realname;
 
        if (access(sysfsp, R_OK) != 0) {
                /*
@@ -149,8 +159,8 @@ static bool is_gpiochip_cdev(const char *path)
 
 out_free_sysfsp:
        free(sysfsp);
-out_free_pathcpy:
-       free(pathcpy);
+out_free_realname:
+       free(realname);
 out:
        return ret;
 }