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(full_args, sizeof(full_args), "__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, 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(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(&self->check, 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