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
24 const char *data_file = "/sys/kernel/tracing/user_events_data";
25 const char *enable_file = "/sys/kernel/tracing/events/user_events/__abi_event/enable";
26
event_exists(void)27 static 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
change_event(bool enable)39 static 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
event_delete(void)62 static 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
reg_enable_flags(void * enable,int size,int bit,int flags)77 static 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
reg_enable(void * enable,int size,int bit)100 static int reg_enable(void *enable, int size, int bit)
101 {
102 return reg_enable_flags(enable, size, bit, 0);
103 }
104
reg_disable(void * enable,int bit)105 static 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
FIXTURE(user)125 FIXTURE(user) {
126 int check;
127 long check_long;
128 bool umount;
129 };
130
FIXTURE_SETUP(user)131 FIXTURE_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
FIXTURE_TEARDOWN(user)139 FIXTURE_TEARDOWN(user) {
140 USER_EVENT_FIXTURE_TEARDOWN(self->umount);
141 }
142
TEST_F(user,enablement)143 TEST_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
TEST_F(user,flags)164 TEST_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
TEST_F(user,bit_sizes)184 TEST_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
TEST_F(user,forks)210 TEST_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 */
clone_check(void * check)251 static 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
TEST_F(user,clones)265 TEST_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
main(int argc,char ** argv)286 int main(int argc, char **argv)
287 {
288 return test_harness_run(argc, argv);
289 }
290