Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * User Events ABI Test Program
4 *
5 * Copyright (c) 2022 Beau Belgrave <beaub@linux.microsoft.com>
6 */
7
8#define _GNU_SOURCE
9#include <sched.h>
10
11#include <errno.h>
12#include <linux/user_events.h>
13#include <stdio.h>
14#include <stdlib.h>
15#include <fcntl.h>
16#include <sys/ioctl.h>
17#include <sys/stat.h>
18#include <unistd.h>
19#include <asm/unistd.h>
20
21#include "../kselftest_harness.h"
22#include "user_events_selftests.h"
23
24const char *data_file = "/sys/kernel/tracing/user_events_data";
25const char *enable_file = "/sys/kernel/tracing/events/user_events/__abi_event/enable";
26
27static bool event_exists(void)
28{
29 int fd = open(enable_file, O_RDWR);
30
31 if (fd < 0)
32 return false;
33
34 close(fd);
35
36 return true;
37}
38
39static int change_event(bool enable)
40{
41 int fd = open(enable_file, O_RDWR);
42 int ret;
43
44 if (fd < 0)
45 return -1;
46
47 if (enable)
48 ret = write(fd, "1", 1);
49 else
50 ret = write(fd, "0", 1);
51
52 close(fd);
53
54 if (ret == 1)
55 ret = 0;
56 else
57 ret = -1;
58
59 return ret;
60}
61
62static int event_delete(void)
63{
64 int fd = open(data_file, O_RDWR);
65 int ret;
66
67 if (fd < 0)
68 return -1;
69
70 ret = ioctl(fd, DIAG_IOCSDEL, "__abi_event");
71
72 close(fd);
73
74 return ret;
75}
76
77static int reg_enable_flags(void *enable, int size, int bit, int flags)
78{
79 struct user_reg reg = {0};
80 int fd = open(data_file, O_RDWR);
81 int ret;
82
83 if (fd < 0)
84 return -1;
85
86 reg.size = sizeof(reg);
87 reg.name_args = (__u64)"__abi_event";
88 reg.flags = flags;
89 reg.enable_bit = bit;
90 reg.enable_addr = (__u64)enable;
91 reg.enable_size = size;
92
93 ret = ioctl(fd, DIAG_IOCSREG, ®);
94
95 close(fd);
96
97 return ret;
98}
99
100static int reg_enable(void *enable, int size, int bit)
101{
102 return reg_enable_flags(enable, size, bit, 0);
103}
104
105static int reg_disable(void *enable, int bit)
106{
107 struct user_unreg reg = {0};
108 int fd = open(data_file, O_RDWR);
109 int ret;
110
111 if (fd < 0)
112 return -1;
113
114 reg.size = sizeof(reg);
115 reg.disable_bit = bit;
116 reg.disable_addr = (__u64)enable;
117
118 ret = ioctl(fd, DIAG_IOCSUNREG, ®);
119
120 close(fd);
121
122 return ret;
123}
124
125FIXTURE(user) {
126 int check;
127 long check_long;
128 bool umount;
129};
130
131FIXTURE_SETUP(user) {
132 USER_EVENT_FIXTURE_SETUP(return, self->umount);
133
134 change_event(false);
135 self->check = 0;
136 self->check_long = 0;
137}
138
139FIXTURE_TEARDOWN(user) {
140 USER_EVENT_FIXTURE_TEARDOWN(self->umount);
141}
142
143TEST_F(user, enablement) {
144 /* Changes should be reflected immediately */
145 ASSERT_EQ(0, self->check);
146 ASSERT_EQ(0, reg_enable(&self->check, sizeof(int), 0));
147 ASSERT_EQ(0, change_event(true));
148 ASSERT_EQ(1, self->check);
149 ASSERT_EQ(0, change_event(false));
150 ASSERT_EQ(0, self->check);
151
152 /* Ensure kernel clears bit after disable */
153 ASSERT_EQ(0, change_event(true));
154 ASSERT_EQ(1, self->check);
155 ASSERT_EQ(0, reg_disable(&self->check, 0));
156 ASSERT_EQ(0, self->check);
157
158 /* Ensure doesn't change after unreg */
159 ASSERT_EQ(0, change_event(true));
160 ASSERT_EQ(0, self->check);
161 ASSERT_EQ(0, change_event(false));
162}
163
164TEST_F(user, flags) {
165 /* USER_EVENT_REG_PERSIST is allowed */
166 ASSERT_EQ(0, reg_enable_flags(&self->check, sizeof(int), 0,
167 USER_EVENT_REG_PERSIST));
168 ASSERT_EQ(0, reg_disable(&self->check, 0));
169
170 /* Ensure it exists after close and disable */
171 ASSERT_TRUE(event_exists());
172
173 /* Ensure we can delete it */
174 ASSERT_EQ(0, event_delete());
175
176 /* USER_EVENT_REG_MAX or above is not allowed */
177 ASSERT_EQ(-1, reg_enable_flags(&self->check, sizeof(int), 0,
178 USER_EVENT_REG_MAX));
179
180 /* Ensure it does not exist after invalid flags */
181 ASSERT_FALSE(event_exists());
182}
183
184TEST_F(user, bit_sizes) {
185 /* Allow 0-31 bits for 32-bit */
186 ASSERT_EQ(0, reg_enable(&self->check, sizeof(int), 0));
187 ASSERT_EQ(0, reg_enable(&self->check, sizeof(int), 31));
188 ASSERT_NE(0, reg_enable(&self->check, sizeof(int), 32));
189 ASSERT_EQ(0, reg_disable(&self->check, 0));
190 ASSERT_EQ(0, reg_disable(&self->check, 31));
191
192#if BITS_PER_LONG == 8
193 /* Allow 0-64 bits for 64-bit */
194 ASSERT_EQ(0, reg_enable(&self->check_long, sizeof(long), 63));
195 ASSERT_NE(0, reg_enable(&self->check_long, sizeof(long), 64));
196 ASSERT_EQ(0, reg_disable(&self->check_long, 63));
197#endif
198
199 /* Disallowed sizes (everything beside 4 and 8) */
200 ASSERT_NE(0, reg_enable(&self->check, 1, 0));
201 ASSERT_NE(0, reg_enable(&self->check, 2, 0));
202 ASSERT_NE(0, reg_enable(&self->check, 3, 0));
203 ASSERT_NE(0, reg_enable(&self->check, 5, 0));
204 ASSERT_NE(0, reg_enable(&self->check, 6, 0));
205 ASSERT_NE(0, reg_enable(&self->check, 7, 0));
206 ASSERT_NE(0, reg_enable(&self->check, 9, 0));
207 ASSERT_NE(0, reg_enable(&self->check, 128, 0));
208}
209
210TEST_F(user, forks) {
211 int i;
212
213 /* Ensure COW pages get updated after fork */
214 ASSERT_EQ(0, reg_enable(&self->check, sizeof(int), 0));
215 ASSERT_EQ(0, self->check);
216
217 if (fork() == 0) {
218 /* Force COW */
219 self->check = 0;
220
221 /* Up to 1 sec for enablement */
222 for (i = 0; i < 10; ++i) {
223 usleep(100000);
224
225 if (self->check)
226 exit(0);
227 }
228
229 exit(1);
230 }
231
232 /* Allow generous time for COW, then enable */
233 usleep(100000);
234 ASSERT_EQ(0, change_event(true));
235
236 ASSERT_NE(-1, wait(&i));
237 ASSERT_EQ(0, WEXITSTATUS(i));
238
239 /* Ensure child doesn't disable parent */
240 if (fork() == 0)
241 exit(reg_disable(&self->check, 0));
242
243 ASSERT_NE(-1, wait(&i));
244 ASSERT_EQ(0, WEXITSTATUS(i));
245 ASSERT_EQ(1, self->check);
246 ASSERT_EQ(0, change_event(false));
247 ASSERT_EQ(0, self->check);
248}
249
250/* Waits up to 1 sec for enablement */
251static int clone_check(void *check)
252{
253 int i;
254
255 for (i = 0; i < 10; ++i) {
256 usleep(100000);
257
258 if (*(int *)check)
259 return 0;
260 }
261
262 return 1;
263}
264
265TEST_F(user, clones) {
266 int i, stack_size = 4096;
267 void *stack = mmap(NULL, stack_size, PROT_READ | PROT_WRITE,
268 MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK,
269 -1, 0);
270
271 ASSERT_NE(MAP_FAILED, stack);
272 ASSERT_EQ(0, reg_enable(&self->check, sizeof(int), 0));
273 ASSERT_EQ(0, self->check);
274
275 /* Shared VM should see enablements */
276 ASSERT_NE(-1, clone(&clone_check, stack + stack_size,
277 CLONE_VM | SIGCHLD, &self->check));
278
279 ASSERT_EQ(0, change_event(true));
280 ASSERT_NE(-1, wait(&i));
281 ASSERT_EQ(0, WEXITSTATUS(i));
282 munmap(stack, stack_size);
283 ASSERT_EQ(0, change_event(false));
284}
285
286int main(int argc, char **argv)
287{
288 return test_harness_run(argc, argv);
289}