Path: blob/master/tools/testing/selftests/gpio/gpio-cdev-uaf.c
170953 views
// SPDX-License-Identifier: GPL-2.0-or-later1/*2* GPIO character device helper for UAF tests.3*4* Copyright 2026 Google LLC5*/67#include <errno.h>8#include <fcntl.h>9#include <linux/gpio.h>10#include <poll.h>11#include <stdio.h>12#include <stdlib.h>13#include <string.h>14#include <sys/ioctl.h>15#include <sys/stat.h>16#include <sys/types.h>17#include <unistd.h>1819#define CONFIGFS_DIR "/sys/kernel/config/gpio-sim"20#define PROCFS_DIR "/proc"2122static void print_usage(void)23{24printf("usage:\n");25printf(" gpio-cdev-uaf [chip|handle|event|req] [poll|read|ioctl]\n");26}2728static int _create_chip(const char *name, int create)29{30char path[64];3132snprintf(path, sizeof(path), CONFIGFS_DIR "/%s", name);3334if (create)35return mkdir(path, 0755);36else37return rmdir(path);38}3940static int create_chip(const char *name)41{42return _create_chip(name, 1);43}4445static void remove_chip(const char *name)46{47_create_chip(name, 0);48}4950static int _create_bank(const char *chip_name, const char *name, int create)51{52char path[64];5354snprintf(path, sizeof(path), CONFIGFS_DIR "/%s/%s", chip_name, name);5556if (create)57return mkdir(path, 0755);58else59return rmdir(path);60}6162static int create_bank(const char *chip_name, const char *name)63{64return _create_bank(chip_name, name, 1);65}6667static void remove_bank(const char *chip_name, const char *name)68{69_create_bank(chip_name, name, 0);70}7172static int _enable_chip(const char *name, int enable)73{74char path[64];75int fd, ret;7677snprintf(path, sizeof(path), CONFIGFS_DIR "/%s/live", name);7879fd = open(path, O_WRONLY);80if (fd == -1)81return fd;8283if (enable)84ret = write(fd, "1", 1);85else86ret = write(fd, "0", 1);8788close(fd);89return ret == 1 ? 0 : -1;90}9192static int enable_chip(const char *name)93{94return _enable_chip(name, 1);95}9697static void disable_chip(const char *name)98{99_enable_chip(name, 0);100}101102static int open_chip(const char *chip_name, const char *bank_name)103{104char path[64], dev_name[32];105int ret, fd;106107ret = create_chip(chip_name);108if (ret) {109fprintf(stderr, "failed to create chip\n");110return ret;111}112113ret = create_bank(chip_name, bank_name);114if (ret) {115fprintf(stderr, "failed to create bank\n");116goto err_remove_chip;117}118119ret = enable_chip(chip_name);120if (ret) {121fprintf(stderr, "failed to enable chip\n");122goto err_remove_bank;123}124125snprintf(path, sizeof(path), CONFIGFS_DIR "/%s/%s/chip_name",126chip_name, bank_name);127128fd = open(path, O_RDONLY);129if (fd == -1) {130ret = fd;131fprintf(stderr, "failed to open %s\n", path);132goto err_disable_chip;133}134135ret = read(fd, dev_name, sizeof(dev_name) - 1);136close(fd);137if (ret == -1) {138fprintf(stderr, "failed to read %s\n", path);139goto err_disable_chip;140}141dev_name[ret] = '\0';142if (ret && dev_name[ret - 1] == '\n')143dev_name[ret - 1] = '\0';144145snprintf(path, sizeof(path), "/dev/%s", dev_name);146147fd = open(path, O_RDWR);148if (fd == -1) {149ret = fd;150fprintf(stderr, "failed to open %s\n", path);151goto err_disable_chip;152}153154return fd;155err_disable_chip:156disable_chip(chip_name);157err_remove_bank:158remove_bank(chip_name, bank_name);159err_remove_chip:160remove_chip(chip_name);161return ret;162}163164static void close_chip(const char *chip_name, const char *bank_name)165{166disable_chip(chip_name);167remove_bank(chip_name, bank_name);168remove_chip(chip_name);169}170171static int test_poll(int fd)172{173struct pollfd pfds;174175pfds.fd = fd;176pfds.events = POLLIN;177pfds.revents = 0;178179if (poll(&pfds, 1, 0) == -1)180return -1;181182return (pfds.revents & ~(POLLHUP | POLLERR)) ? -1 : 0;183}184185static int test_read(int fd)186{187char data;188189if (read(fd, &data, 1) == -1 && errno == ENODEV)190return 0;191return -1;192}193194static int test_ioctl(int fd)195{196if (ioctl(fd, 0, NULL) == -1 && errno == ENODEV)197return 0;198return -1;199}200201int main(int argc, char **argv)202{203int cfd, fd, ret;204int (*test_func)(int);205206if (argc != 3) {207print_usage();208return EXIT_FAILURE;209}210211if (strcmp(argv[1], "chip") == 0 ||212strcmp(argv[1], "event") == 0 ||213strcmp(argv[1], "req") == 0) {214if (strcmp(argv[2], "poll") &&215strcmp(argv[2], "read") &&216strcmp(argv[2], "ioctl")) {217fprintf(stderr, "unknown command: %s\n", argv[2]);218return EXIT_FAILURE;219}220} else if (strcmp(argv[1], "handle") == 0) {221if (strcmp(argv[2], "ioctl")) {222fprintf(stderr, "unknown command: %s\n", argv[2]);223return EXIT_FAILURE;224}225} else {226fprintf(stderr, "unknown command: %s\n", argv[1]);227return EXIT_FAILURE;228}229230if (strcmp(argv[2], "poll") == 0)231test_func = test_poll;232else if (strcmp(argv[2], "read") == 0)233test_func = test_read;234else /* strcmp(argv[2], "ioctl") == 0 */235test_func = test_ioctl;236237cfd = open_chip("chip", "bank");238if (cfd == -1) {239fprintf(stderr, "failed to open chip\n");240return EXIT_FAILURE;241}242243/* Step 1: Hold a FD to the test target. */244if (strcmp(argv[1], "chip") == 0) {245fd = cfd;246} else if (strcmp(argv[1], "handle") == 0) {247struct gpiohandle_request req = {0};248249req.lines = 1;250if (ioctl(cfd, GPIO_GET_LINEHANDLE_IOCTL, &req) == -1) {251fprintf(stderr, "failed to get handle FD\n");252goto err_close_chip;253}254255close(cfd);256fd = req.fd;257} else if (strcmp(argv[1], "event") == 0) {258struct gpioevent_request req = {0};259260if (ioctl(cfd, GPIO_GET_LINEEVENT_IOCTL, &req) == -1) {261fprintf(stderr, "failed to get event FD\n");262goto err_close_chip;263}264265close(cfd);266fd = req.fd;267} else { /* strcmp(argv[1], "req") == 0 */268struct gpio_v2_line_request req = {0};269270req.num_lines = 1;271if (ioctl(cfd, GPIO_V2_GET_LINE_IOCTL, &req) == -1) {272fprintf(stderr, "failed to get req FD\n");273goto err_close_chip;274}275276close(cfd);277fd = req.fd;278}279280/* Step 2: Free the chip. */281close_chip("chip", "bank");282283/* Step 3: Access the dangling FD to trigger UAF. */284ret = test_func(fd);285close(fd);286return ret ? EXIT_FAILURE : EXIT_SUCCESS;287err_close_chip:288close(cfd);289close_chip("chip", "bank");290return EXIT_FAILURE;291}292293294