~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/tools/testing/selftests/perf_events/remove_on_exec.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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 }
261 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php