Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests/perf_events: Add kselftest for remove_on_exec

Add kselftest to test that remove_on_exec removes inherited events from
child tasks.

Signed-off-by: Marco Elver <elver@google.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lkml.kernel.org/r/20210408103605.1676875-9-elver@google.com

authored by

Marco Elver and committed by
Peter Zijlstra
6216798b f2c3c32f

+262 -1
+1
tools/testing/selftests/perf_events/.gitignore
··· 1 1 # SPDX-License-Identifier: GPL-2.0-only 2 2 sigtrap_threads 3 + remove_on_exec
+1 -1
tools/testing/selftests/perf_events/Makefile
··· 2 2 CFLAGS += -Wl,-no-as-needed -Wall -I../../../../usr/include 3 3 LDFLAGS += -lpthread 4 4 5 - TEST_GEN_PROGS := sigtrap_threads 5 + TEST_GEN_PROGS := sigtrap_threads remove_on_exec 6 6 include ../lib.mk
+260
tools/testing/selftests/perf_events/remove_on_exec.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Test for remove_on_exec. 4 + * 5 + * Copyright (C) 2021, Google LLC. 6 + */ 7 + 8 + #define _GNU_SOURCE 9 + 10 + /* We need the latest siginfo from the kernel repo. */ 11 + #include <sys/types.h> 12 + #include <asm/siginfo.h> 13 + #define __have_siginfo_t 1 14 + #define __have_sigval_t 1 15 + #define __have_sigevent_t 1 16 + #define __siginfo_t_defined 17 + #define __sigval_t_defined 18 + #define __sigevent_t_defined 19 + #define _BITS_SIGINFO_CONSTS_H 1 20 + #define _BITS_SIGEVENT_CONSTS_H 1 21 + 22 + #include <stdbool.h> 23 + #include <stddef.h> 24 + #include <stdint.h> 25 + #include <stdio.h> 26 + #include <linux/perf_event.h> 27 + #include <pthread.h> 28 + #include <signal.h> 29 + #include <sys/ioctl.h> 30 + #include <sys/syscall.h> 31 + #include <unistd.h> 32 + 33 + #include "../kselftest_harness.h" 34 + 35 + static volatile int signal_count; 36 + 37 + static struct perf_event_attr make_event_attr(void) 38 + { 39 + struct perf_event_attr attr = { 40 + .type = PERF_TYPE_HARDWARE, 41 + .size = sizeof(attr), 42 + .config = PERF_COUNT_HW_INSTRUCTIONS, 43 + .sample_period = 1000, 44 + .exclude_kernel = 1, 45 + .exclude_hv = 1, 46 + .disabled = 1, 47 + .inherit = 1, 48 + /* 49 + * Children normally retain their inherited event on exec; with 50 + * remove_on_exec, we'll remove their event, but the parent and 51 + * any other non-exec'd children will keep their events. 52 + */ 53 + .remove_on_exec = 1, 54 + .sigtrap = 1, 55 + }; 56 + return attr; 57 + } 58 + 59 + static void sigtrap_handler(int signum, siginfo_t *info, void *ucontext) 60 + { 61 + if (info->si_code != TRAP_PERF) { 62 + fprintf(stderr, "%s: unexpected si_code %d\n", __func__, info->si_code); 63 + return; 64 + } 65 + 66 + signal_count++; 67 + } 68 + 69 + FIXTURE(remove_on_exec) 70 + { 71 + struct sigaction oldact; 72 + int fd; 73 + }; 74 + 75 + FIXTURE_SETUP(remove_on_exec) 76 + { 77 + struct perf_event_attr attr = make_event_attr(); 78 + struct sigaction action = {}; 79 + 80 + signal_count = 0; 81 + 82 + /* Initialize sigtrap handler. */ 83 + action.sa_flags = SA_SIGINFO | SA_NODEFER; 84 + action.sa_sigaction = sigtrap_handler; 85 + sigemptyset(&action.sa_mask); 86 + ASSERT_EQ(sigaction(SIGTRAP, &action, &self->oldact), 0); 87 + 88 + /* Initialize perf event. */ 89 + self->fd = syscall(__NR_perf_event_open, &attr, 0, -1, -1, PERF_FLAG_FD_CLOEXEC); 90 + ASSERT_NE(self->fd, -1); 91 + } 92 + 93 + FIXTURE_TEARDOWN(remove_on_exec) 94 + { 95 + close(self->fd); 96 + sigaction(SIGTRAP, &self->oldact, NULL); 97 + } 98 + 99 + /* Verify event propagates to fork'd child. */ 100 + TEST_F(remove_on_exec, fork_only) 101 + { 102 + int status; 103 + pid_t pid = fork(); 104 + 105 + if (pid == 0) { 106 + ASSERT_EQ(signal_count, 0); 107 + ASSERT_EQ(ioctl(self->fd, PERF_EVENT_IOC_ENABLE, 0), 0); 108 + while (!signal_count); 109 + _exit(42); 110 + } 111 + 112 + while (!signal_count); /* Child enables event. */ 113 + EXPECT_EQ(waitpid(pid, &status, 0), pid); 114 + EXPECT_EQ(WEXITSTATUS(status), 42); 115 + } 116 + 117 + /* 118 + * Verify that event does _not_ propagate to fork+exec'd child; event enabled 119 + * after fork+exec. 120 + */ 121 + TEST_F(remove_on_exec, fork_exec_then_enable) 122 + { 123 + pid_t pid_exec, pid_only_fork; 124 + int pipefd[2]; 125 + int tmp; 126 + 127 + /* 128 + * Non-exec child, to ensure exec does not affect inherited events of 129 + * other children. 130 + */ 131 + pid_only_fork = fork(); 132 + if (pid_only_fork == 0) { 133 + /* Block until parent enables event. */ 134 + while (!signal_count); 135 + _exit(42); 136 + } 137 + 138 + ASSERT_NE(pipe(pipefd), -1); 139 + pid_exec = fork(); 140 + if (pid_exec == 0) { 141 + ASSERT_NE(dup2(pipefd[1], STDOUT_FILENO), -1); 142 + close(pipefd[0]); 143 + execl("/proc/self/exe", "exec_child", NULL); 144 + _exit((perror("exec failed"), 1)); 145 + } 146 + close(pipefd[1]); 147 + 148 + ASSERT_EQ(waitpid(pid_exec, &tmp, WNOHANG), 0); /* Child is running. */ 149 + /* Wait for exec'd child to start spinning. */ 150 + EXPECT_EQ(read(pipefd[0], &tmp, sizeof(int)), sizeof(int)); 151 + EXPECT_EQ(tmp, 42); 152 + close(pipefd[0]); 153 + /* Now we can enable the event, knowing the child is doing work. */ 154 + EXPECT_EQ(ioctl(self->fd, PERF_EVENT_IOC_ENABLE, 0), 0); 155 + /* If the event propagated to the exec'd child, it will exit normally... */ 156 + usleep(100000); /* ... give time for event to trigger (in case of bug). */ 157 + EXPECT_EQ(waitpid(pid_exec, &tmp, WNOHANG), 0); /* Should still be running. */ 158 + EXPECT_EQ(kill(pid_exec, SIGKILL), 0); 159 + 160 + /* Verify removal from child did not affect this task's event. */ 161 + tmp = signal_count; 162 + while (signal_count == tmp); /* Should not hang! */ 163 + /* Nor should it have affected the first child. */ 164 + EXPECT_EQ(waitpid(pid_only_fork, &tmp, 0), pid_only_fork); 165 + EXPECT_EQ(WEXITSTATUS(tmp), 42); 166 + } 167 + 168 + /* 169 + * Verify that event does _not_ propagate to fork+exec'd child; event enabled 170 + * before fork+exec. 171 + */ 172 + TEST_F(remove_on_exec, enable_then_fork_exec) 173 + { 174 + pid_t pid_exec; 175 + int tmp; 176 + 177 + EXPECT_EQ(ioctl(self->fd, PERF_EVENT_IOC_ENABLE, 0), 0); 178 + 179 + pid_exec = fork(); 180 + if (pid_exec == 0) { 181 + execl("/proc/self/exe", "exec_child", NULL); 182 + _exit((perror("exec failed"), 1)); 183 + } 184 + 185 + /* 186 + * The child may exit abnormally at any time if the event propagated and 187 + * a SIGTRAP is sent before the handler was set up. 188 + */ 189 + usleep(100000); /* ... give time for event to trigger (in case of bug). */ 190 + EXPECT_EQ(waitpid(pid_exec, &tmp, WNOHANG), 0); /* Should still be running. */ 191 + EXPECT_EQ(kill(pid_exec, SIGKILL), 0); 192 + 193 + /* Verify removal from child did not affect this task's event. */ 194 + tmp = signal_count; 195 + while (signal_count == tmp); /* Should not hang! */ 196 + } 197 + 198 + TEST_F(remove_on_exec, exec_stress) 199 + { 200 + pid_t pids[30]; 201 + int i, tmp; 202 + 203 + for (i = 0; i < sizeof(pids) / sizeof(pids[0]); i++) { 204 + pids[i] = fork(); 205 + if (pids[i] == 0) { 206 + execl("/proc/self/exe", "exec_child", NULL); 207 + _exit((perror("exec failed"), 1)); 208 + } 209 + 210 + /* Some forked with event disabled, rest with enabled. */ 211 + if (i > 10) 212 + EXPECT_EQ(ioctl(self->fd, PERF_EVENT_IOC_ENABLE, 0), 0); 213 + } 214 + 215 + usleep(100000); /* ... give time for event to trigger (in case of bug). */ 216 + 217 + for (i = 0; i < sizeof(pids) / sizeof(pids[0]); i++) { 218 + /* All children should still be running. */ 219 + EXPECT_EQ(waitpid(pids[i], &tmp, WNOHANG), 0); 220 + EXPECT_EQ(kill(pids[i], SIGKILL), 0); 221 + } 222 + 223 + /* Verify event is still alive. */ 224 + tmp = signal_count; 225 + while (signal_count == tmp); 226 + } 227 + 228 + /* For exec'd child. */ 229 + static void exec_child(void) 230 + { 231 + struct sigaction action = {}; 232 + const int val = 42; 233 + 234 + /* Set up sigtrap handler in case we erroneously receive a trap. */ 235 + action.sa_flags = SA_SIGINFO | SA_NODEFER; 236 + action.sa_sigaction = sigtrap_handler; 237 + sigemptyset(&action.sa_mask); 238 + if (sigaction(SIGTRAP, &action, NULL)) 239 + _exit((perror("sigaction failed"), 1)); 240 + 241 + /* Signal parent that we're starting to spin. */ 242 + if (write(STDOUT_FILENO, &val, sizeof(int)) == -1) 243 + _exit((perror("write failed"), 1)); 244 + 245 + /* Should hang here until killed. */ 246 + while (!signal_count); 247 + } 248 + 249 + #define main test_main 250 + TEST_HARNESS_MAIN 251 + #undef main 252 + int main(int argc, char *argv[]) 253 + { 254 + if (!strcmp(argv[0], "exec_child")) { 255 + exec_child(); 256 + return 1; 257 + } 258 + 259 + return test_main(argc, argv); 260 + }