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 <glob.h>
20#include <string.h>
21#include <asm/unistd.h>
22
23#include "../kselftest_harness.h"
24#include "user_events_selftests.h"
25
26const char *data_file = "/sys/kernel/tracing/user_events_data";
27const char *enable_file = "/sys/kernel/tracing/events/user_events/__abi_event/enable";
28const char *multi_dir_glob = "/sys/kernel/tracing/events/user_events_multi/__abi_event.*";
29
30static int wait_for_delete(char *dir)
31{
32 struct stat buf;
33 int i;
34
35 for (i = 0; i < 10000; ++i) {
36 if (stat(dir, &buf) == -1 && errno == ENOENT)
37 return 0;
38
39 usleep(1000);
40 }
41
42 return -1;
43}
44
45static int find_multi_event_dir(char *unique_field, char *out_dir, int dir_len)
46{
47 char path[256];
48 glob_t buf;
49 int i, ret;
50
51 ret = glob(multi_dir_glob, GLOB_ONLYDIR, NULL, &buf);
52
53 if (ret)
54 return -1;
55
56 ret = -1;
57
58 for (i = 0; i < buf.gl_pathc; ++i) {
59 FILE *fp;
60
61 snprintf(path, sizeof(path), "%s/format", buf.gl_pathv[i]);
62 fp = fopen(path, "r");
63
64 if (!fp)
65 continue;
66
67 while (fgets(path, sizeof(path), fp) != NULL) {
68 if (strstr(path, unique_field)) {
69 fclose(fp);
70 /* strscpy is not available, use snprintf */
71 snprintf(out_dir, dir_len, "%s", buf.gl_pathv[i]);
72 ret = 0;
73 goto out;
74 }
75 }
76
77 fclose(fp);
78 }
79out:
80 globfree(&buf);
81
82 return ret;
83}
84
85static bool event_exists(void)
86{
87 int fd = open(enable_file, O_RDWR);
88
89 if (fd < 0)
90 return false;
91
92 close(fd);
93
94 return true;
95}
96
97static int change_event(bool enable)
98{
99 int fd = open(enable_file, O_RDWR);
100 int ret;
101
102 if (fd < 0)
103 return -1;
104
105 if (enable)
106 ret = write(fd, "1", 1);
107 else
108 ret = write(fd, "0", 1);
109
110 close(fd);
111
112 if (ret == 1)
113 ret = 0;
114 else
115 ret = -1;
116
117 return ret;
118}
119
120static int event_delete(void)
121{
122 int fd = open(data_file, O_RDWR);
123 int ret;
124
125 if (fd < 0)
126 return -1;
127
128 ret = ioctl(fd, DIAG_IOCSDEL, "__abi_event");
129
130 close(fd);
131
132 return ret;
133}
134
135static int reg_enable_multi(void *enable, int size, int bit, int flags,
136 char *args)
137{
138 struct user_reg reg = {0};
139 char full_args[512] = {0};
140 int fd = open(data_file, O_RDWR);
141 int len;
142 int ret;
143
144 if (fd < 0)
145 return -1;
146
147 len = snprintf(buf: full_args, size: sizeof(full_args), fmt: "__abi_event %s", args);
148
149 if (len > sizeof(full_args)) {
150 ret = -E2BIG;
151 goto out;
152 }
153
154 reg.size = sizeof(reg);
155 reg.name_args = (__u64)full_args;
156 reg.flags = USER_EVENT_REG_MULTI_FORMAT | flags;
157 reg.enable_bit = bit;
158 reg.enable_addr = (__u64)enable;
159 reg.enable_size = size;
160
161 ret = ioctl(fd, DIAG_IOCSREG, &reg);
162out:
163 close(fd);
164
165 return ret;
166}
167
168static int reg_enable_flags(void *enable, int size, int bit, int flags)
169{
170 struct user_reg reg = {0};
171 int fd = open(data_file, O_RDWR);
172 int ret;
173
174 if (fd < 0)
175 return -1;
176
177 reg.size = sizeof(reg);
178 reg.name_args = (__u64)"__abi_event";
179 reg.flags = flags;
180 reg.enable_bit = bit;
181 reg.enable_addr = (__u64)enable;
182 reg.enable_size = size;
183
184 ret = ioctl(fd, DIAG_IOCSREG, &reg);
185
186 close(fd);
187
188 return ret;
189}
190
191static int reg_enable(void *enable, int size, int bit)
192{
193 return reg_enable_flags(enable, size, bit, flags: 0);
194}
195
196static int reg_disable(void *enable, int bit)
197{
198 struct user_unreg reg = {0};
199 int fd = open(data_file, O_RDWR);
200 int ret;
201
202 if (fd < 0)
203 return -1;
204
205 reg.size = sizeof(reg);
206 reg.disable_bit = bit;
207 reg.disable_addr = (__u64)enable;
208
209 ret = ioctl(fd, DIAG_IOCSUNREG, &reg);
210
211 close(fd);
212
213 return ret;
214}
215
216FIXTURE(user) {
217 int check;
218 long check_long;
219 bool umount;
220};
221
222FIXTURE_SETUP(user) {
223 USER_EVENT_FIXTURE_SETUP(return, self->umount);
224
225 change_event(enable: false);
226 self->check = 0;
227 self->check_long = 0;
228}
229
230FIXTURE_TEARDOWN(user) {
231 USER_EVENT_FIXTURE_TEARDOWN(self->umount);
232}
233
234TEST_F(user, enablement) {
235 /* Changes should be reflected immediately */
236 ASSERT_EQ(0, self->check);
237 ASSERT_EQ(0, reg_enable(&self->check, sizeof(int), 0));
238 ASSERT_EQ(0, change_event(true));
239 ASSERT_EQ(1, self->check);
240 ASSERT_EQ(0, change_event(false));
241 ASSERT_EQ(0, self->check);
242
243 /* Ensure kernel clears bit after disable */
244 ASSERT_EQ(0, change_event(true));
245 ASSERT_EQ(1, self->check);
246 ASSERT_EQ(0, reg_disable(&self->check, 0));
247 ASSERT_EQ(0, self->check);
248
249 /* Ensure doesn't change after unreg */
250 ASSERT_EQ(0, change_event(true));
251 ASSERT_EQ(0, self->check);
252 ASSERT_EQ(0, change_event(false));
253}
254
255TEST_F(user, flags) {
256 /* USER_EVENT_REG_PERSIST is allowed */
257 ASSERT_EQ(0, reg_enable_flags(&self->check, sizeof(int), 0,
258 USER_EVENT_REG_PERSIST));
259 ASSERT_EQ(0, reg_disable(&self->check, 0));
260
261 /* Ensure it exists after close and disable */
262 ASSERT_TRUE(event_exists());
263
264 /* Ensure we can delete it */
265 ASSERT_EQ(0, event_delete());
266
267 /* USER_EVENT_REG_MAX or above is not allowed */
268 ASSERT_EQ(-1, reg_enable_flags(&self->check, sizeof(int), 0,
269 USER_EVENT_REG_MAX));
270
271 /* Ensure it does not exist after invalid flags */
272 ASSERT_FALSE(event_exists());
273}
274
275TEST_F(user, bit_sizes) {
276 /* Allow 0-31 bits for 32-bit */
277 ASSERT_EQ(0, reg_enable(&self->check, sizeof(int), 0));
278 ASSERT_EQ(0, reg_enable(&self->check, sizeof(int), 31));
279 ASSERT_NE(0, reg_enable(&self->check, sizeof(int), 32));
280 ASSERT_EQ(0, reg_disable(&self->check, 0));
281 ASSERT_EQ(0, reg_disable(&self->check, 31));
282
283#if BITS_PER_LONG == 8
284 /* Allow 0-64 bits for 64-bit */
285 ASSERT_EQ(0, reg_enable(&self->check_long, sizeof(long), 63));
286 ASSERT_NE(0, reg_enable(&self->check_long, sizeof(long), 64));
287 ASSERT_EQ(0, reg_disable(&self->check_long, 63));
288#endif
289
290 /* Disallowed sizes (everything beside 4 and 8) */
291 ASSERT_NE(0, reg_enable(&self->check, 1, 0));
292 ASSERT_NE(0, reg_enable(&self->check, 2, 0));
293 ASSERT_NE(0, reg_enable(&self->check, 3, 0));
294 ASSERT_NE(0, reg_enable(&self->check, 5, 0));
295 ASSERT_NE(0, reg_enable(&self->check, 6, 0));
296 ASSERT_NE(0, reg_enable(&self->check, 7, 0));
297 ASSERT_NE(0, reg_enable(&self->check, 9, 0));
298 ASSERT_NE(0, reg_enable(&self->check, 128, 0));
299}
300
301TEST_F(user, multi_format) {
302 char first_dir[256];
303 char second_dir[256];
304 struct stat buf;
305
306 /* Multiple formats for the same name should work */
307 ASSERT_EQ(0, reg_enable_multi(&self->check, sizeof(int), 0,
308 0, "u32 multi_first"));
309
310 ASSERT_EQ(0, reg_enable_multi(&self->check, sizeof(int), 1,
311 0, "u64 multi_second"));
312
313 /* Same name with same format should also work */
314 ASSERT_EQ(0, reg_enable_multi(&self->check, sizeof(int), 2,
315 0, "u64 multi_second"));
316
317 ASSERT_EQ(0, find_multi_event_dir("multi_first",
318 first_dir, sizeof(first_dir)));
319
320 ASSERT_EQ(0, find_multi_event_dir("multi_second",
321 second_dir, sizeof(second_dir)));
322
323 /* Should not be found in the same dir */
324 ASSERT_NE(0, strcmp(first_dir, second_dir));
325
326 /* First dir should still exist */
327 ASSERT_EQ(0, stat(first_dir, &buf));
328
329 /* Disabling first register should remove first dir */
330 ASSERT_EQ(0, reg_disable(&self->check, 0));
331 ASSERT_EQ(0, wait_for_delete(first_dir));
332
333 /* Second dir should still exist */
334 ASSERT_EQ(0, stat(second_dir, &buf));
335
336 /* Disabling second register should remove second dir */
337 ASSERT_EQ(0, reg_disable(&self->check, 1));
338 /* Ensure bit 1 and 2 are tied together, should not delete yet */
339 ASSERT_EQ(0, stat(second_dir, &buf));
340 ASSERT_EQ(0, reg_disable(&self->check, 2));
341 ASSERT_EQ(0, wait_for_delete(second_dir));
342}
343
344TEST_F(user, forks) {
345 int i;
346
347 /* Ensure COW pages get updated after fork */
348 ASSERT_EQ(0, reg_enable(&self->check, sizeof(int), 0));
349 ASSERT_EQ(0, self->check);
350
351 if (fork() == 0) {
352 /* Force COW */
353 self->check = 0;
354
355 /* Up to 1 sec for enablement */
356 for (i = 0; i < 10; ++i) {
357 usleep(100000);
358
359 if (self->check)
360 exit(0);
361 }
362
363 exit(1);
364 }
365
366 /* Allow generous time for COW, then enable */
367 usleep(100000);
368 ASSERT_EQ(0, change_event(true));
369
370 ASSERT_NE(-1, wait(&i));
371 ASSERT_EQ(0, WEXITSTATUS(i));
372
373 /* Ensure child doesn't disable parent */
374 if (fork() == 0)
375 exit(reg_disable(enable: &self->check, bit: 0));
376
377 ASSERT_NE(-1, wait(&i));
378 ASSERT_EQ(0, WEXITSTATUS(i));
379 ASSERT_EQ(1, self->check);
380 ASSERT_EQ(0, change_event(false));
381 ASSERT_EQ(0, self->check);
382}
383
384/* Waits up to 1 sec for enablement */
385static int clone_check(void *check)
386{
387 int i;
388
389 for (i = 0; i < 10; ++i) {
390 usleep(100000);
391
392 if (*(int *)check)
393 return 0;
394 }
395
396 return 1;
397}
398
399TEST_F(user, clones) {
400 int i, stack_size = 4096;
401 void *stack = mmap(NULL, stack_size, PROT_READ | PROT_WRITE,
402 MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK,
403 -1, 0);
404
405 ASSERT_NE(MAP_FAILED, stack);
406 ASSERT_EQ(0, reg_enable(&self->check, sizeof(int), 0));
407 ASSERT_EQ(0, self->check);
408
409 /* Shared VM should see enablements */
410 ASSERT_NE(-1, clone(&clone_check, stack + stack_size,
411 CLONE_VM | SIGCHLD, &self->check));
412
413 ASSERT_EQ(0, change_event(true));
414 ASSERT_NE(-1, wait(&i));
415 ASSERT_EQ(0, WEXITSTATUS(i));
416 munmap(stack, stack_size);
417 ASSERT_EQ(0, change_event(false));
418}
419
420int main(int argc, char **argv)
421{
422 return test_harness_run(argc, argv);
423}
424

source code of linux/tools/testing/selftests/user_events/abi_test.c