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