aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBartosz Golaszewski <bartekgola@gmail.com>2018-11-05 03:57:17 +0100
committerBartosz Golaszewski <bartekgola@gmail.com>2018-11-05 11:01:27 +0100
commitd9b1c1f14c6bebab3c931086bbbdd31c2a8a3f67 (patch)
treec176e944859fd913d122952cd1d9375742b40b2f
parent3a8aa70c54edc146ccfc595f072085d29b495b91 (diff)
core: harden gpiod_chip_open()
Verify that the file we were asked to use is really a GPIO character device. Signed-off-by: Bartosz Golaszewski <bartekgola@gmail.com>
-rw-r--r--src/lib/core.c90
1 files changed, 90 insertions, 0 deletions
diff --git a/src/lib/core.c b/src/lib/core.c
index ed80273..1902639 100644
--- a/src/lib/core.c
+++ b/src/lib/core.c
@@ -9,6 +9,7 @@
#include <gpiod.h>
+#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>
@@ -59,6 +60,86 @@ struct gpiod_chip {
char label[32];
};
+static bool is_gpiochip_cdev(const char *path)
+{
+ char *name, *pathcpy, *sysfsp, sysfsdev[16], devstr[16];
+ struct stat statbuf;
+ bool ret = false;
+ int rv, fd;
+ ssize_t rd;
+
+ rv = lstat(path, &statbuf);
+ if (rv)
+ goto out;
+
+ /* Is it a character device? */
+ if (!S_ISCHR(statbuf.st_mode)) {
+ /*
+ * Passing a file descriptor not associated with a character
+ * device to ioctl() makes it set errno to ENOTTY. Let's do
+ * the same in order to stay compatible with the versions of
+ * libgpiod from before the introduction of this routine.
+ */
+ errno = ENOTTY;
+ goto out;
+ }
+
+ /* Get the basename. */
+ pathcpy = strdup(path);
+ if (!pathcpy)
+ goto out;
+
+ name = basename(pathcpy);
+
+ /* Do we have a corresponding sysfs attribute? */
+ rv = asprintf(&sysfsp, "/sys/bus/gpio/devices/%s/dev", name);
+ if (rv < 0)
+ goto out_free_pathcpy;
+
+ if (access(sysfsp, R_OK) != 0) {
+ /*
+ * This is a character device but not the one we're after.
+ * Before the introduction of this function, we'd fail with
+ * ENOTTY on the first GPIO ioctl() call for this file
+ * descriptor. Let's stay compatible here and keep returning
+ * the same error code.
+ */
+ errno = ENOTTY;
+ goto out_free_sysfsp;
+ }
+
+ /*
+ * Make sure the major and minor numbers of the character device
+ * correspond with the ones in the dev attribute in sysfs.
+ */
+ snprintf(devstr, sizeof(devstr), "%u:%u",
+ major(statbuf.st_rdev), minor(statbuf.st_rdev));
+
+ fd = open(sysfsp, O_RDONLY);
+ if (fd < 0)
+ goto out_free_sysfsp;
+
+ memset(sysfsdev, 0, sizeof(sysfsdev));
+ rd = read(fd, sysfsdev, strlen(devstr));
+ close(fd);
+ if (rd < 0)
+ goto out_free_sysfsp;
+
+ if (strcmp(sysfsdev, devstr) != 0) {
+ errno = ENODEV;
+ goto out_free_sysfsp;
+ }
+
+ ret = true;
+
+out_free_sysfsp:
+ free(sysfsp);
+out_free_pathcpy:
+ free(pathcpy);
+out:
+ return ret;
+}
+
struct gpiod_chip *gpiod_chip_open(const char *path)
{
struct gpiochip_info info;
@@ -69,6 +150,15 @@ struct gpiod_chip *gpiod_chip_open(const char *path)
if (fd < 0)
return NULL;
+ /*
+ * We were able to open the file but is it really a gpiochip character
+ * device?
+ */
+ if (!is_gpiochip_cdev(path)) {
+ close(fd);
+ return NULL;
+ }
+
chip = malloc(sizeof(*chip));
if (!chip)
goto err_close_fd;