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()") Cc: Bartosz Golaszewski <bartekgola@xxxxxxxxx> Signed-off-by: Andy Shevchenko <andriy.shevchenko@xxxxxxxxxxxxxxx> --- lib/core.c | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/lib/core.c b/lib/core.c index 8352e18..32476ee 100644 --- a/lib/core.c +++ b/lib/core.c @@ -8,6 +8,7 @@ /* Low-level, core library code. */ #include <errno.h> +#include <limits.h> #include <fcntl.h> #include <gpiod.h> #include <linux/gpio.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,21 @@ 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. + */ + if (S_ISLNK(statbuf.st_mode)) + realname = realpath(path, NULL); + else + realname = 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 +110,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 +161,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; } -- 2.25.0