diff options
author | Andy Shevchenko <andriy.shevchenko@linux.intel.com> | 2020-02-06 20:13:58 +0200 |
---|---|---|
committer | Bartosz Golaszewski <bgolaszewski@baylibre.com> | 2020-02-07 13:59:36 +0100 |
commit | 56ae2a701144b9a1dd5c2b57c1d4b51d6e3c4e69 (patch) | |
tree | ff15a1618eabe6b2ca5fc78d30cda5d84a07b61c | |
parent | 33adcd100cb6bc4258ae464127213c3bb9f66785 (diff) | |
download | libgpiod-56ae2a701144b9a1dd5c2b57c1d4b51d6e3c4e69.tar.gz |
core: relax gpiod_chip_open() for symbolic links
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>
-rw-r--r-- | lib/core.c | 30 |
1 files changed, 20 insertions, 10 deletions
@@ -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> @@ -62,7 +63,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; @@ -72,6 +73,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)) { /* @@ -81,20 +95,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) { /* @@ -136,8 +146,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; } |