xref: /linux/tools/perf/bench/sched-seccomp-notify.c (revision 06d07429858317ded2db7986113a9e0129cd599b)
17d5cb68aSAndrei Vagin // SPDX-License-Identifier: GPL-2.0
27d5cb68aSAndrei Vagin #include <subcmd/parse-options.h>
37d5cb68aSAndrei Vagin #include "bench.h"
47d5cb68aSAndrei Vagin 
57d5cb68aSAndrei Vagin #include <uapi/linux/filter.h>
67d5cb68aSAndrei Vagin #include <sys/types.h>
77d5cb68aSAndrei Vagin #include <sys/time.h>
87d5cb68aSAndrei Vagin #include <linux/unistd.h>
97d5cb68aSAndrei Vagin #include <sys/syscall.h>
107d5cb68aSAndrei Vagin #include <sys/ioctl.h>
117d5cb68aSAndrei Vagin #include <linux/time64.h>
12678ddf73SArnaldo Carvalho de Melo #include <uapi/linux/seccomp.h>
137d5cb68aSAndrei Vagin #include <sys/prctl.h>
147d5cb68aSAndrei Vagin 
157d5cb68aSAndrei Vagin #include <unistd.h>
167d5cb68aSAndrei Vagin #include <limits.h>
177d5cb68aSAndrei Vagin #include <stddef.h>
187d5cb68aSAndrei Vagin #include <stdint.h>
197d5cb68aSAndrei Vagin #include <stdio.h>
207d5cb68aSAndrei Vagin #include <stdlib.h>
217d5cb68aSAndrei Vagin #include <signal.h>
227d5cb68aSAndrei Vagin #include <sys/wait.h>
237d5cb68aSAndrei Vagin #include <string.h>
247d5cb68aSAndrei Vagin #include <errno.h>
257d5cb68aSAndrei Vagin #include <err.h>
267d5cb68aSAndrei Vagin #include <inttypes.h>
277d5cb68aSAndrei Vagin 
287d5cb68aSAndrei Vagin #define LOOPS_DEFAULT 1000000UL
297d5cb68aSAndrei Vagin static uint64_t loops = LOOPS_DEFAULT;
307d5cb68aSAndrei Vagin static bool sync_mode;
317d5cb68aSAndrei Vagin 
327d5cb68aSAndrei Vagin static const struct option options[] = {
337d5cb68aSAndrei Vagin 	OPT_U64('l', "loop",	&loops,		"Specify number of loops"),
347d5cb68aSAndrei Vagin 	OPT_BOOLEAN('s', "sync-mode", &sync_mode,
35*018b0424SColin Ian King 		    "Enable the synchronous mode for seccomp notifications"),
367d5cb68aSAndrei Vagin 	OPT_END()
377d5cb68aSAndrei Vagin };
387d5cb68aSAndrei Vagin 
397d5cb68aSAndrei Vagin static const char * const bench_seccomp_usage[] = {
407d5cb68aSAndrei Vagin 	"perf bench sched secccomp-notify <options>",
417d5cb68aSAndrei Vagin 	NULL
427d5cb68aSAndrei Vagin };
437d5cb68aSAndrei Vagin 
seccomp(unsigned int op,unsigned int flags,void * args)447d5cb68aSAndrei Vagin static int seccomp(unsigned int op, unsigned int flags, void *args)
457d5cb68aSAndrei Vagin {
467d5cb68aSAndrei Vagin 	return syscall(__NR_seccomp, op, flags, args);
477d5cb68aSAndrei Vagin }
487d5cb68aSAndrei Vagin 
user_notif_syscall(int nr,unsigned int flags)497d5cb68aSAndrei Vagin static int user_notif_syscall(int nr, unsigned int flags)
507d5cb68aSAndrei Vagin {
517d5cb68aSAndrei Vagin 	struct sock_filter filter[] = {
527d5cb68aSAndrei Vagin 		BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
537d5cb68aSAndrei Vagin 			offsetof(struct seccomp_data, nr)),
547d5cb68aSAndrei Vagin 		BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, nr, 0, 1),
557d5cb68aSAndrei Vagin 		BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_USER_NOTIF),
567d5cb68aSAndrei Vagin 		BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
577d5cb68aSAndrei Vagin 	};
587d5cb68aSAndrei Vagin 
597d5cb68aSAndrei Vagin 	struct sock_fprog prog = {
607d5cb68aSAndrei Vagin 		.len = (unsigned short)ARRAY_SIZE(filter),
617d5cb68aSAndrei Vagin 		.filter = filter,
627d5cb68aSAndrei Vagin 	};
637d5cb68aSAndrei Vagin 
647d5cb68aSAndrei Vagin 	return seccomp(SECCOMP_SET_MODE_FILTER, flags, &prog);
657d5cb68aSAndrei Vagin }
667d5cb68aSAndrei Vagin 
677d5cb68aSAndrei Vagin #define USER_NOTIF_MAGIC INT_MAX
user_notification_sync_loop(int listener)687d5cb68aSAndrei Vagin static void user_notification_sync_loop(int listener)
697d5cb68aSAndrei Vagin {
707d5cb68aSAndrei Vagin 	struct seccomp_notif_resp resp;
717d5cb68aSAndrei Vagin 	struct seccomp_notif req;
727d5cb68aSAndrei Vagin 	uint64_t nr;
737d5cb68aSAndrei Vagin 
747d5cb68aSAndrei Vagin 	for (nr = 0; nr < loops; nr++) {
757d5cb68aSAndrei Vagin 		memset(&req, 0, sizeof(req));
767d5cb68aSAndrei Vagin 		if (ioctl(listener, SECCOMP_IOCTL_NOTIF_RECV, &req))
777d5cb68aSAndrei Vagin 			err(EXIT_FAILURE, "SECCOMP_IOCTL_NOTIF_RECV failed");
787d5cb68aSAndrei Vagin 
797d5cb68aSAndrei Vagin 		if (req.data.nr != __NR_gettid)
807d5cb68aSAndrei Vagin 			errx(EXIT_FAILURE, "unexpected syscall: %d", req.data.nr);
817d5cb68aSAndrei Vagin 
827d5cb68aSAndrei Vagin 		resp.id = req.id;
837d5cb68aSAndrei Vagin 		resp.error = 0;
847d5cb68aSAndrei Vagin 		resp.val = USER_NOTIF_MAGIC;
857d5cb68aSAndrei Vagin 		resp.flags = 0;
867d5cb68aSAndrei Vagin 		if (ioctl(listener, SECCOMP_IOCTL_NOTIF_SEND, &resp))
877d5cb68aSAndrei Vagin 			err(EXIT_FAILURE, "SECCOMP_IOCTL_NOTIF_SEND failed");
887d5cb68aSAndrei Vagin 	}
897d5cb68aSAndrei Vagin }
907d5cb68aSAndrei Vagin 
917d5cb68aSAndrei Vagin #ifndef SECCOMP_USER_NOTIF_FD_SYNC_WAKE_UP
927d5cb68aSAndrei Vagin #define SECCOMP_USER_NOTIF_FD_SYNC_WAKE_UP (1UL << 0)
937d5cb68aSAndrei Vagin #define SECCOMP_IOCTL_NOTIF_SET_FLAGS  SECCOMP_IOW(4, __u64)
947d5cb68aSAndrei Vagin #endif
bench_sched_seccomp_notify(int argc,const char ** argv)957d5cb68aSAndrei Vagin int bench_sched_seccomp_notify(int argc, const char **argv)
967d5cb68aSAndrei Vagin {
977d5cb68aSAndrei Vagin 	struct timeval start, stop, diff;
987d5cb68aSAndrei Vagin 	unsigned long long result_usec = 0;
997d5cb68aSAndrei Vagin 	int status, listener;
1007d5cb68aSAndrei Vagin 	pid_t pid;
1017d5cb68aSAndrei Vagin 	long ret;
1027d5cb68aSAndrei Vagin 
1037d5cb68aSAndrei Vagin 	argc = parse_options(argc, argv, options, bench_seccomp_usage, 0);
1047d5cb68aSAndrei Vagin 
1057d5cb68aSAndrei Vagin 	gettimeofday(&start, NULL);
1067d5cb68aSAndrei Vagin 
1077d5cb68aSAndrei Vagin 	prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
1087d5cb68aSAndrei Vagin 	listener = user_notif_syscall(__NR_gettid,
1097d5cb68aSAndrei Vagin 				      SECCOMP_FILTER_FLAG_NEW_LISTENER);
1107d5cb68aSAndrei Vagin 	if (listener < 0)
1117d5cb68aSAndrei Vagin 		err(EXIT_FAILURE, "can't create a notification descriptor");
1127d5cb68aSAndrei Vagin 
1137d5cb68aSAndrei Vagin 	pid = fork();
1147d5cb68aSAndrei Vagin 	if (pid < 0)
1157d5cb68aSAndrei Vagin 		err(EXIT_FAILURE, "fork");
1167d5cb68aSAndrei Vagin 	if (pid == 0) {
1177d5cb68aSAndrei Vagin 		if (prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0))
1187d5cb68aSAndrei Vagin 			err(EXIT_FAILURE, "can't set the parent death signal");
1197d5cb68aSAndrei Vagin 		while (1) {
1207d5cb68aSAndrei Vagin 			ret = syscall(__NR_gettid);
1217d5cb68aSAndrei Vagin 			if (ret == USER_NOTIF_MAGIC)
1227d5cb68aSAndrei Vagin 				continue;
1237d5cb68aSAndrei Vagin 			break;
1247d5cb68aSAndrei Vagin 		}
1257d5cb68aSAndrei Vagin 		_exit(1);
1267d5cb68aSAndrei Vagin 	}
1277d5cb68aSAndrei Vagin 
1287d5cb68aSAndrei Vagin 	if (sync_mode) {
1297d5cb68aSAndrei Vagin 		if (ioctl(listener, SECCOMP_IOCTL_NOTIF_SET_FLAGS,
1307d5cb68aSAndrei Vagin 			     SECCOMP_USER_NOTIF_FD_SYNC_WAKE_UP, 0))
1317d5cb68aSAndrei Vagin 			err(EXIT_FAILURE,
1327d5cb68aSAndrei Vagin 			    "can't set SECCOMP_USER_NOTIF_FD_SYNC_WAKE_UP");
1337d5cb68aSAndrei Vagin 	}
1347d5cb68aSAndrei Vagin 	user_notification_sync_loop(listener);
1357d5cb68aSAndrei Vagin 
1367d5cb68aSAndrei Vagin 	kill(pid, SIGKILL);
1377d5cb68aSAndrei Vagin 	if (waitpid(pid, &status, 0) != pid)
1387d5cb68aSAndrei Vagin 		err(EXIT_FAILURE, "waitpid(%d) failed", pid);
1397d5cb68aSAndrei Vagin 	if (!WIFSIGNALED(status) || WTERMSIG(status) != SIGKILL)
1407d5cb68aSAndrei Vagin 		errx(EXIT_FAILURE, "unexpected exit code: %d", status);
1417d5cb68aSAndrei Vagin 
1427d5cb68aSAndrei Vagin 	gettimeofday(&stop, NULL);
1437d5cb68aSAndrei Vagin 	timersub(&stop, &start, &diff);
1447d5cb68aSAndrei Vagin 
1457d5cb68aSAndrei Vagin 	switch (bench_format) {
1467d5cb68aSAndrei Vagin 	case BENCH_FORMAT_DEFAULT:
1477d5cb68aSAndrei Vagin 		printf("# Executed %" PRIu64 " system calls\n\n",
1487d5cb68aSAndrei Vagin 			loops);
1497d5cb68aSAndrei Vagin 
1507d5cb68aSAndrei Vagin 		result_usec = diff.tv_sec * USEC_PER_SEC;
1517d5cb68aSAndrei Vagin 		result_usec += diff.tv_usec;
1527d5cb68aSAndrei Vagin 
1537d5cb68aSAndrei Vagin 		printf(" %14s: %lu.%03lu [sec]\n\n", "Total time",
1547d5cb68aSAndrei Vagin 		       (unsigned long) diff.tv_sec,
1557d5cb68aSAndrei Vagin 		       (unsigned long) (diff.tv_usec / USEC_PER_MSEC));
1567d5cb68aSAndrei Vagin 
1577d5cb68aSAndrei Vagin 		printf(" %14lf usecs/op\n",
1587d5cb68aSAndrei Vagin 		       (double)result_usec / (double)loops);
1597d5cb68aSAndrei Vagin 		printf(" %14d ops/sec\n",
1607d5cb68aSAndrei Vagin 		       (int)((double)loops /
1617d5cb68aSAndrei Vagin 			     ((double)result_usec / (double)USEC_PER_SEC)));
1627d5cb68aSAndrei Vagin 		break;
1637d5cb68aSAndrei Vagin 
1647d5cb68aSAndrei Vagin 	case BENCH_FORMAT_SIMPLE:
1657d5cb68aSAndrei Vagin 		printf("%lu.%03lu\n",
1667d5cb68aSAndrei Vagin 		       (unsigned long) diff.tv_sec,
1677d5cb68aSAndrei Vagin 		       (unsigned long) (diff.tv_usec / USEC_PER_MSEC));
1687d5cb68aSAndrei Vagin 		break;
1697d5cb68aSAndrei Vagin 
1707d5cb68aSAndrei Vagin 	default:
1717d5cb68aSAndrei Vagin 		/* reaching here is something disaster */
1727d5cb68aSAndrei Vagin 		fprintf(stderr, "Unknown format:%d\n", bench_format);
1737d5cb68aSAndrei Vagin 		exit(1);
1747d5cb68aSAndrei Vagin 		break;
1757d5cb68aSAndrei Vagin 	}
1767d5cb68aSAndrei Vagin 
1777d5cb68aSAndrei Vagin 	return 0;
1787d5cb68aSAndrei Vagin }
179