xref: /linux/tools/testing/selftests/gpio/gpio-cdev-uaf.c (revision 1334d2a3b3235d062e5e1f51aebe7a64ed57cf72)
1*c7f92042STzung-Bi Shih // SPDX-License-Identifier: GPL-2.0-or-later
2*c7f92042STzung-Bi Shih /*
3*c7f92042STzung-Bi Shih  * GPIO character device helper for UAF tests.
4*c7f92042STzung-Bi Shih  *
5*c7f92042STzung-Bi Shih  * Copyright 2026 Google LLC
6*c7f92042STzung-Bi Shih  */
7*c7f92042STzung-Bi Shih 
8*c7f92042STzung-Bi Shih #include <errno.h>
9*c7f92042STzung-Bi Shih #include <fcntl.h>
10*c7f92042STzung-Bi Shih #include <linux/gpio.h>
11*c7f92042STzung-Bi Shih #include <poll.h>
12*c7f92042STzung-Bi Shih #include <stdio.h>
13*c7f92042STzung-Bi Shih #include <stdlib.h>
14*c7f92042STzung-Bi Shih #include <string.h>
15*c7f92042STzung-Bi Shih #include <sys/ioctl.h>
16*c7f92042STzung-Bi Shih #include <sys/stat.h>
17*c7f92042STzung-Bi Shih #include <sys/types.h>
18*c7f92042STzung-Bi Shih #include <unistd.h>
19*c7f92042STzung-Bi Shih 
20*c7f92042STzung-Bi Shih #define CONFIGFS_DIR "/sys/kernel/config/gpio-sim"
21*c7f92042STzung-Bi Shih #define PROCFS_DIR "/proc"
22*c7f92042STzung-Bi Shih 
print_usage(void)23*c7f92042STzung-Bi Shih static void print_usage(void)
24*c7f92042STzung-Bi Shih {
25*c7f92042STzung-Bi Shih 	printf("usage:\n");
26*c7f92042STzung-Bi Shih 	printf("  gpio-cdev-uaf [chip|handle|event|req] [poll|read|ioctl]\n");
27*c7f92042STzung-Bi Shih }
28*c7f92042STzung-Bi Shih 
_create_chip(const char * name,int create)29*c7f92042STzung-Bi Shih static int _create_chip(const char *name, int create)
30*c7f92042STzung-Bi Shih {
31*c7f92042STzung-Bi Shih 	char path[64];
32*c7f92042STzung-Bi Shih 
33*c7f92042STzung-Bi Shih 	snprintf(path, sizeof(path), CONFIGFS_DIR "/%s", name);
34*c7f92042STzung-Bi Shih 
35*c7f92042STzung-Bi Shih 	if (create)
36*c7f92042STzung-Bi Shih 		return mkdir(path, 0755);
37*c7f92042STzung-Bi Shih 	else
38*c7f92042STzung-Bi Shih 		return rmdir(path);
39*c7f92042STzung-Bi Shih }
40*c7f92042STzung-Bi Shih 
create_chip(const char * name)41*c7f92042STzung-Bi Shih static int create_chip(const char *name)
42*c7f92042STzung-Bi Shih {
43*c7f92042STzung-Bi Shih 	return _create_chip(name, 1);
44*c7f92042STzung-Bi Shih }
45*c7f92042STzung-Bi Shih 
remove_chip(const char * name)46*c7f92042STzung-Bi Shih static void remove_chip(const char *name)
47*c7f92042STzung-Bi Shih {
48*c7f92042STzung-Bi Shih 	_create_chip(name, 0);
49*c7f92042STzung-Bi Shih }
50*c7f92042STzung-Bi Shih 
_create_bank(const char * chip_name,const char * name,int create)51*c7f92042STzung-Bi Shih static int _create_bank(const char *chip_name, const char *name, int create)
52*c7f92042STzung-Bi Shih {
53*c7f92042STzung-Bi Shih 	char path[64];
54*c7f92042STzung-Bi Shih 
55*c7f92042STzung-Bi Shih 	snprintf(path, sizeof(path), CONFIGFS_DIR "/%s/%s", chip_name, name);
56*c7f92042STzung-Bi Shih 
57*c7f92042STzung-Bi Shih 	if (create)
58*c7f92042STzung-Bi Shih 		return mkdir(path, 0755);
59*c7f92042STzung-Bi Shih 	else
60*c7f92042STzung-Bi Shih 		return rmdir(path);
61*c7f92042STzung-Bi Shih }
62*c7f92042STzung-Bi Shih 
create_bank(const char * chip_name,const char * name)63*c7f92042STzung-Bi Shih static int create_bank(const char *chip_name, const char *name)
64*c7f92042STzung-Bi Shih {
65*c7f92042STzung-Bi Shih 	return _create_bank(chip_name, name, 1);
66*c7f92042STzung-Bi Shih }
67*c7f92042STzung-Bi Shih 
remove_bank(const char * chip_name,const char * name)68*c7f92042STzung-Bi Shih static void remove_bank(const char *chip_name, const char *name)
69*c7f92042STzung-Bi Shih {
70*c7f92042STzung-Bi Shih 	_create_bank(chip_name, name, 0);
71*c7f92042STzung-Bi Shih }
72*c7f92042STzung-Bi Shih 
_enable_chip(const char * name,int enable)73*c7f92042STzung-Bi Shih static int _enable_chip(const char *name, int enable)
74*c7f92042STzung-Bi Shih {
75*c7f92042STzung-Bi Shih 	char path[64];
76*c7f92042STzung-Bi Shih 	int fd, ret;
77*c7f92042STzung-Bi Shih 
78*c7f92042STzung-Bi Shih 	snprintf(path, sizeof(path), CONFIGFS_DIR "/%s/live", name);
79*c7f92042STzung-Bi Shih 
80*c7f92042STzung-Bi Shih 	fd = open(path, O_WRONLY);
81*c7f92042STzung-Bi Shih 	if (fd == -1)
82*c7f92042STzung-Bi Shih 		return fd;
83*c7f92042STzung-Bi Shih 
84*c7f92042STzung-Bi Shih 	if (enable)
85*c7f92042STzung-Bi Shih 		ret = write(fd, "1", 1);
86*c7f92042STzung-Bi Shih 	else
87*c7f92042STzung-Bi Shih 		ret = write(fd, "0", 1);
88*c7f92042STzung-Bi Shih 
89*c7f92042STzung-Bi Shih 	close(fd);
90*c7f92042STzung-Bi Shih 	return ret == 1 ? 0 : -1;
91*c7f92042STzung-Bi Shih }
92*c7f92042STzung-Bi Shih 
enable_chip(const char * name)93*c7f92042STzung-Bi Shih static int enable_chip(const char *name)
94*c7f92042STzung-Bi Shih {
95*c7f92042STzung-Bi Shih 	return _enable_chip(name, 1);
96*c7f92042STzung-Bi Shih }
97*c7f92042STzung-Bi Shih 
disable_chip(const char * name)98*c7f92042STzung-Bi Shih static void disable_chip(const char *name)
99*c7f92042STzung-Bi Shih {
100*c7f92042STzung-Bi Shih 	_enable_chip(name, 0);
101*c7f92042STzung-Bi Shih }
102*c7f92042STzung-Bi Shih 
open_chip(const char * chip_name,const char * bank_name)103*c7f92042STzung-Bi Shih static int open_chip(const char *chip_name, const char *bank_name)
104*c7f92042STzung-Bi Shih {
105*c7f92042STzung-Bi Shih 	char path[64], dev_name[32];
106*c7f92042STzung-Bi Shih 	int ret, fd;
107*c7f92042STzung-Bi Shih 
108*c7f92042STzung-Bi Shih 	ret = create_chip(chip_name);
109*c7f92042STzung-Bi Shih 	if (ret) {
110*c7f92042STzung-Bi Shih 		fprintf(stderr, "failed to create chip\n");
111*c7f92042STzung-Bi Shih 		return ret;
112*c7f92042STzung-Bi Shih 	}
113*c7f92042STzung-Bi Shih 
114*c7f92042STzung-Bi Shih 	ret = create_bank(chip_name, bank_name);
115*c7f92042STzung-Bi Shih 	if (ret) {
116*c7f92042STzung-Bi Shih 		fprintf(stderr, "failed to create bank\n");
117*c7f92042STzung-Bi Shih 		goto err_remove_chip;
118*c7f92042STzung-Bi Shih 	}
119*c7f92042STzung-Bi Shih 
120*c7f92042STzung-Bi Shih 	ret = enable_chip(chip_name);
121*c7f92042STzung-Bi Shih 	if (ret) {
122*c7f92042STzung-Bi Shih 		fprintf(stderr, "failed to enable chip\n");
123*c7f92042STzung-Bi Shih 		goto err_remove_bank;
124*c7f92042STzung-Bi Shih 	}
125*c7f92042STzung-Bi Shih 
126*c7f92042STzung-Bi Shih 	snprintf(path, sizeof(path), CONFIGFS_DIR "/%s/%s/chip_name",
127*c7f92042STzung-Bi Shih 		 chip_name, bank_name);
128*c7f92042STzung-Bi Shih 
129*c7f92042STzung-Bi Shih 	fd = open(path, O_RDONLY);
130*c7f92042STzung-Bi Shih 	if (fd == -1) {
131*c7f92042STzung-Bi Shih 		ret = fd;
132*c7f92042STzung-Bi Shih 		fprintf(stderr, "failed to open %s\n", path);
133*c7f92042STzung-Bi Shih 		goto err_disable_chip;
134*c7f92042STzung-Bi Shih 	}
135*c7f92042STzung-Bi Shih 
136*c7f92042STzung-Bi Shih 	ret = read(fd, dev_name, sizeof(dev_name) - 1);
137*c7f92042STzung-Bi Shih 	close(fd);
138*c7f92042STzung-Bi Shih 	if (ret == -1) {
139*c7f92042STzung-Bi Shih 		fprintf(stderr, "failed to read %s\n", path);
140*c7f92042STzung-Bi Shih 		goto err_disable_chip;
141*c7f92042STzung-Bi Shih 	}
142*c7f92042STzung-Bi Shih 	dev_name[ret] = '\0';
143*c7f92042STzung-Bi Shih 	if (ret && dev_name[ret - 1] == '\n')
144*c7f92042STzung-Bi Shih 		dev_name[ret - 1] = '\0';
145*c7f92042STzung-Bi Shih 
146*c7f92042STzung-Bi Shih 	snprintf(path, sizeof(path), "/dev/%s", dev_name);
147*c7f92042STzung-Bi Shih 
148*c7f92042STzung-Bi Shih 	fd = open(path, O_RDWR);
149*c7f92042STzung-Bi Shih 	if (fd == -1) {
150*c7f92042STzung-Bi Shih 		ret = fd;
151*c7f92042STzung-Bi Shih 		fprintf(stderr, "failed to open %s\n", path);
152*c7f92042STzung-Bi Shih 		goto err_disable_chip;
153*c7f92042STzung-Bi Shih 	}
154*c7f92042STzung-Bi Shih 
155*c7f92042STzung-Bi Shih 	return fd;
156*c7f92042STzung-Bi Shih err_disable_chip:
157*c7f92042STzung-Bi Shih 	disable_chip(chip_name);
158*c7f92042STzung-Bi Shih err_remove_bank:
159*c7f92042STzung-Bi Shih 	remove_bank(chip_name, bank_name);
160*c7f92042STzung-Bi Shih err_remove_chip:
161*c7f92042STzung-Bi Shih 	remove_chip(chip_name);
162*c7f92042STzung-Bi Shih 	return ret;
163*c7f92042STzung-Bi Shih }
164*c7f92042STzung-Bi Shih 
close_chip(const char * chip_name,const char * bank_name)165*c7f92042STzung-Bi Shih static void close_chip(const char *chip_name, const char *bank_name)
166*c7f92042STzung-Bi Shih {
167*c7f92042STzung-Bi Shih 	disable_chip(chip_name);
168*c7f92042STzung-Bi Shih 	remove_bank(chip_name, bank_name);
169*c7f92042STzung-Bi Shih 	remove_chip(chip_name);
170*c7f92042STzung-Bi Shih }
171*c7f92042STzung-Bi Shih 
test_poll(int fd)172*c7f92042STzung-Bi Shih static int test_poll(int fd)
173*c7f92042STzung-Bi Shih {
174*c7f92042STzung-Bi Shih 	struct pollfd pfds;
175*c7f92042STzung-Bi Shih 
176*c7f92042STzung-Bi Shih 	pfds.fd = fd;
177*c7f92042STzung-Bi Shih 	pfds.events = POLLIN;
178*c7f92042STzung-Bi Shih 	pfds.revents = 0;
179*c7f92042STzung-Bi Shih 
180*c7f92042STzung-Bi Shih 	if (poll(&pfds, 1, 0) == -1)
181*c7f92042STzung-Bi Shih 		return -1;
182*c7f92042STzung-Bi Shih 
183*c7f92042STzung-Bi Shih 	return (pfds.revents & ~(POLLHUP | POLLERR)) ? -1 : 0;
184*c7f92042STzung-Bi Shih }
185*c7f92042STzung-Bi Shih 
test_read(int fd)186*c7f92042STzung-Bi Shih static int test_read(int fd)
187*c7f92042STzung-Bi Shih {
188*c7f92042STzung-Bi Shih 	char data;
189*c7f92042STzung-Bi Shih 
190*c7f92042STzung-Bi Shih 	if (read(fd, &data, 1) == -1 && errno == ENODEV)
191*c7f92042STzung-Bi Shih 		return 0;
192*c7f92042STzung-Bi Shih 	return -1;
193*c7f92042STzung-Bi Shih }
194*c7f92042STzung-Bi Shih 
test_ioctl(int fd)195*c7f92042STzung-Bi Shih static int test_ioctl(int fd)
196*c7f92042STzung-Bi Shih {
197*c7f92042STzung-Bi Shih 	if (ioctl(fd, 0, NULL) == -1 && errno == ENODEV)
198*c7f92042STzung-Bi Shih 		return 0;
199*c7f92042STzung-Bi Shih 	return -1;
200*c7f92042STzung-Bi Shih }
201*c7f92042STzung-Bi Shih 
main(int argc,char ** argv)202*c7f92042STzung-Bi Shih int main(int argc, char **argv)
203*c7f92042STzung-Bi Shih {
204*c7f92042STzung-Bi Shih 	int cfd, fd, ret;
205*c7f92042STzung-Bi Shih 	int (*test_func)(int);
206*c7f92042STzung-Bi Shih 
207*c7f92042STzung-Bi Shih 	if (argc != 3) {
208*c7f92042STzung-Bi Shih 		print_usage();
209*c7f92042STzung-Bi Shih 		return EXIT_FAILURE;
210*c7f92042STzung-Bi Shih 	}
211*c7f92042STzung-Bi Shih 
212*c7f92042STzung-Bi Shih 	if (strcmp(argv[1], "chip") == 0 ||
213*c7f92042STzung-Bi Shih 	    strcmp(argv[1], "event") == 0 ||
214*c7f92042STzung-Bi Shih 	    strcmp(argv[1], "req") == 0) {
215*c7f92042STzung-Bi Shih 		if (strcmp(argv[2], "poll") &&
216*c7f92042STzung-Bi Shih 		    strcmp(argv[2], "read") &&
217*c7f92042STzung-Bi Shih 		    strcmp(argv[2], "ioctl")) {
218*c7f92042STzung-Bi Shih 			fprintf(stderr, "unknown command: %s\n", argv[2]);
219*c7f92042STzung-Bi Shih 			return EXIT_FAILURE;
220*c7f92042STzung-Bi Shih 		}
221*c7f92042STzung-Bi Shih 	} else if (strcmp(argv[1], "handle") == 0) {
222*c7f92042STzung-Bi Shih 		if (strcmp(argv[2], "ioctl")) {
223*c7f92042STzung-Bi Shih 			fprintf(stderr, "unknown command: %s\n", argv[2]);
224*c7f92042STzung-Bi Shih 			return EXIT_FAILURE;
225*c7f92042STzung-Bi Shih 		}
226*c7f92042STzung-Bi Shih 	} else {
227*c7f92042STzung-Bi Shih 		fprintf(stderr, "unknown command: %s\n", argv[1]);
228*c7f92042STzung-Bi Shih 		return EXIT_FAILURE;
229*c7f92042STzung-Bi Shih 	}
230*c7f92042STzung-Bi Shih 
231*c7f92042STzung-Bi Shih 	if (strcmp(argv[2], "poll") == 0)
232*c7f92042STzung-Bi Shih 		test_func = test_poll;
233*c7f92042STzung-Bi Shih 	else if (strcmp(argv[2], "read") == 0)
234*c7f92042STzung-Bi Shih 		test_func = test_read;
235*c7f92042STzung-Bi Shih 	else	/* strcmp(argv[2], "ioctl") == 0 */
236*c7f92042STzung-Bi Shih 		test_func = test_ioctl;
237*c7f92042STzung-Bi Shih 
238*c7f92042STzung-Bi Shih 	cfd = open_chip("chip", "bank");
239*c7f92042STzung-Bi Shih 	if (cfd == -1) {
240*c7f92042STzung-Bi Shih 		fprintf(stderr, "failed to open chip\n");
241*c7f92042STzung-Bi Shih 		return EXIT_FAILURE;
242*c7f92042STzung-Bi Shih 	}
243*c7f92042STzung-Bi Shih 
244*c7f92042STzung-Bi Shih 	/* Step 1: Hold a FD to the test target. */
245*c7f92042STzung-Bi Shih 	if (strcmp(argv[1], "chip") == 0) {
246*c7f92042STzung-Bi Shih 		fd = cfd;
247*c7f92042STzung-Bi Shih 	} else if (strcmp(argv[1], "handle") == 0) {
248*c7f92042STzung-Bi Shih 		struct gpiohandle_request req = {0};
249*c7f92042STzung-Bi Shih 
250*c7f92042STzung-Bi Shih 		req.lines = 1;
251*c7f92042STzung-Bi Shih 		if (ioctl(cfd, GPIO_GET_LINEHANDLE_IOCTL, &req) == -1) {
252*c7f92042STzung-Bi Shih 			fprintf(stderr, "failed to get handle FD\n");
253*c7f92042STzung-Bi Shih 			goto err_close_chip;
254*c7f92042STzung-Bi Shih 		}
255*c7f92042STzung-Bi Shih 
256*c7f92042STzung-Bi Shih 		close(cfd);
257*c7f92042STzung-Bi Shih 		fd = req.fd;
258*c7f92042STzung-Bi Shih 	} else if (strcmp(argv[1], "event") == 0) {
259*c7f92042STzung-Bi Shih 		struct gpioevent_request req = {0};
260*c7f92042STzung-Bi Shih 
261*c7f92042STzung-Bi Shih 		if (ioctl(cfd, GPIO_GET_LINEEVENT_IOCTL, &req) == -1) {
262*c7f92042STzung-Bi Shih 			fprintf(stderr, "failed to get event FD\n");
263*c7f92042STzung-Bi Shih 			goto err_close_chip;
264*c7f92042STzung-Bi Shih 		}
265*c7f92042STzung-Bi Shih 
266*c7f92042STzung-Bi Shih 		close(cfd);
267*c7f92042STzung-Bi Shih 		fd = req.fd;
268*c7f92042STzung-Bi Shih 	} else {	/* strcmp(argv[1], "req") == 0 */
269*c7f92042STzung-Bi Shih 		struct gpio_v2_line_request req = {0};
270*c7f92042STzung-Bi Shih 
271*c7f92042STzung-Bi Shih 		req.num_lines = 1;
272*c7f92042STzung-Bi Shih 		if (ioctl(cfd, GPIO_V2_GET_LINE_IOCTL, &req) == -1) {
273*c7f92042STzung-Bi Shih 			fprintf(stderr, "failed to get req FD\n");
274*c7f92042STzung-Bi Shih 			goto err_close_chip;
275*c7f92042STzung-Bi Shih 		}
276*c7f92042STzung-Bi Shih 
277*c7f92042STzung-Bi Shih 		close(cfd);
278*c7f92042STzung-Bi Shih 		fd = req.fd;
279*c7f92042STzung-Bi Shih 	}
280*c7f92042STzung-Bi Shih 
281*c7f92042STzung-Bi Shih 	/* Step 2: Free the chip. */
282*c7f92042STzung-Bi Shih 	close_chip("chip", "bank");
283*c7f92042STzung-Bi Shih 
284*c7f92042STzung-Bi Shih 	/* Step 3: Access the dangling FD to trigger UAF. */
285*c7f92042STzung-Bi Shih 	ret = test_func(fd);
286*c7f92042STzung-Bi Shih 	close(fd);
287*c7f92042STzung-Bi Shih 	return ret ? EXIT_FAILURE : EXIT_SUCCESS;
288*c7f92042STzung-Bi Shih err_close_chip:
289*c7f92042STzung-Bi Shih 	close(cfd);
290*c7f92042STzung-Bi Shih 	close_chip("chip", "bank");
291*c7f92042STzung-Bi Shih 	return EXIT_FAILURE;
292*c7f92042STzung-Bi Shih }
293