Received: by 2002:a05:6a10:9848:0:0:0:0 with SMTP id x8csp144375pxf; Wed, 10 Mar 2021 02:44:23 -0800 (PST) X-Google-Smtp-Source: ABdhPJwzuKy/24+T9/9/24GnqC2IeYgSCiv2X1pGCUJVMHXIr4t6+9WiHxV98pEirO7Tf+ouEUQs X-Received: by 2002:a17:906:f1c8:: with SMTP id gx8mr2973756ejb.385.1615373063281; Wed, 10 Mar 2021 02:44:23 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1615373063; cv=none; d=google.com; s=arc-20160816; b=og1DgRS83MNCo8Vw+KgpmT67Riq6w60KQu+xC7ReSulWtJ0x40SbZPZn48HakwtelX VXhsvUKT2K8gDGAM1LzLOdDeVTwUpYdJUCQY4HhA3YQvD4O/qVEMVzvXmJ+b2M8BgMEA Ltq8+Khf1zzlRG4fGpMdXJk00+BjL1W2NqXnLApzAsMFpVFv4SrQxv9aBklTmyjXYTpQ RvOUxqfy+MR1zXzHgjh/En/mqyGp1uXZ2wAV+s9fKJbB31hFu3gCFwC0LjwkXoUWTDif grte+lCePCf14zdyrbSExjlTm5rdySKIBcKp9MSAxBJd3rfFRlEFRU+AIdKamt4JhGCx eZ8Q== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:cc:to:from:subject:references:mime-version :message-id:in-reply-to:date:dkim-signature; bh=7/IRp5vkLAkmWPwXwPAt2lcWrbp37U2bBtUBzeSgBmg=; b=Sir7po73/6dfDgbbMWIFJn16m+n/rYcFjXr+qCid3GhfMlIv2N8XhVI0qhwjflAbxW QTXkAWD6gWzlsAEW1mIss3mEkHr+m+XPI4KtP8DYnKLntp/aBzivYBId6CNxKZbWhGgF TxqWZrcWbTW5SeynznnMO0UKBMyWNDSQakzTM7dZz2GUGj7wpCjwDi9q3r3CZUYRlmqL vz7LABOiG29nxBB/ns5BC4gM0G9r0MpZEk7Nx9sa5w5EgOz+gzXBEdInBPoZxyyFNTJb 9dcHmftBCmLpBEVNRlswpTaOuzhJ+IU329Zx1Rm+oxA/CvhfK0FNfW0ME19v4gmTE2qe qCyA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@google.com header.s=20161025 header.b=oT8UbYzH; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=google.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id c23si12062249edv.312.2021.03.10.02.44.00; Wed, 10 Mar 2021 02:44:23 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; dkim=pass header.i=@google.com header.s=20161025 header.b=oT8UbYzH; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=google.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232718AbhCJKmg (ORCPT + 99 others); Wed, 10 Mar 2021 05:42:36 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:38694 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232629AbhCJKmL (ORCPT ); Wed, 10 Mar 2021 05:42:11 -0500 Received: from mail-wr1-x44a.google.com (mail-wr1-x44a.google.com [IPv6:2a00:1450:4864:20::44a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id A5718C061761 for ; Wed, 10 Mar 2021 02:42:10 -0800 (PST) Received: by mail-wr1-x44a.google.com with SMTP id z6so7772899wrh.11 for ; Wed, 10 Mar 2021 02:42:10 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=7/IRp5vkLAkmWPwXwPAt2lcWrbp37U2bBtUBzeSgBmg=; b=oT8UbYzHn9gNsaJlosymdC745FCCb5j1NpqCi6y6XGk2LCpINk9uNwnZGGUJkKV6Vb BvjKaIo38nsHgSw8NnUG0skzxM69CaULuVSdFd5vlWXaT6sH0tT0jdlSCtSlKeYnesIU MeptW1dQMsxpCJH+Le+g51q/hy3d7tAUn2IuhPjQus7f6at98xHDrkr2gRdno0xXLXdR WrkerH8tDwFNYLyDwIp3QopJLccGoHWCUbj+5x7yPB6+2vI63wsXZfx1VM3J7t5GxXAU koogE7GC4rcE4UZWoGEojxOM/6FLPltTUum3LORIo/0pvDz9lSg0kFfiCcw8U45f72pE yjmA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=7/IRp5vkLAkmWPwXwPAt2lcWrbp37U2bBtUBzeSgBmg=; b=YTXnpOrA2JOJhO3AkZ8Ust7NNs2dwPewfE6g4ZpLP2J6WElr7yLFv0wSg9QOASts4X aNszWfFBiLPbRvXoVpW3E3Mi408NR9jU4VGRCa5tBVp4vpdrVyMIi2HPkfdECq75j3wm wAz13JTBadqtjuBd1F5x7a1bRe4TUzM4RibfdIo+/vR5CDYVbnBCBttI9LWYfgEXvUnO yi+KC2e50bpYFpnqRuksGXfE7rwEbpzgOQ4qJQysCF8Yukvci7UaZjo1oDOybbkRhcim pIOkdbK7onX/YJCDd7atRsOqJZKxhyI6j55k4sAeFQ1ijtNzyZl62N0NQBDUTPZTjmII iYaw== X-Gm-Message-State: AOAM531dX5fXYDByBDNhxKveewBu88KGMr50OxSqZMc9FkWQjMHDpn7m +z/G/cD8WcBIJJZNjBok8q/jvP2m0Q== X-Received: from elver.muc.corp.google.com ([2a00:79e0:15:13:e995:ac0b:b57c:49a4]) (user=elver job=sendgmr) by 2002:a1c:4c17:: with SMTP id z23mr2721408wmf.17.1615372929385; Wed, 10 Mar 2021 02:42:09 -0800 (PST) Date: Wed, 10 Mar 2021 11:41:39 +0100 In-Reply-To: <20210310104139.679618-1-elver@google.com> Message-Id: <20210310104139.679618-9-elver@google.com> Mime-Version: 1.0 References: <20210310104139.679618-1-elver@google.com> X-Mailer: git-send-email 2.30.1.766.gb4fecdf3b7-goog Subject: [PATCH RFC v2 8/8] selftests/perf: Add kselftest for remove_on_exec From: Marco Elver To: elver@google.com, peterz@infradead.org, alexander.shishkin@linux.intel.com, acme@kernel.org, mingo@redhat.com, jolsa@redhat.com, mark.rutland@arm.com, namhyung@kernel.org, tglx@linutronix.de Cc: glider@google.com, viro@zeniv.linux.org.uk, arnd@arndb.de, christian@brauner.io, dvyukov@google.com, jannh@google.com, axboe@kernel.dk, mascasa@google.com, pcc@google.com, irogers@google.com, kasan-dev@googlegroups.com, linux-arch@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, x86@kernel.org, linux-kselftest@vger.kernel.org Content-Type: text/plain; charset="UTF-8" Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Add kselftest to test that remove_on_exec removes inherited events from child tasks. Signed-off-by: Marco Elver --- v2: * Add patch to series. --- .../testing/selftests/perf_events/.gitignore | 1 + tools/testing/selftests/perf_events/Makefile | 2 +- .../selftests/perf_events/remove_on_exec.c | 256 ++++++++++++++++++ 3 files changed, 258 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/perf_events/remove_on_exec.c diff --git a/tools/testing/selftests/perf_events/.gitignore b/tools/testing/selftests/perf_events/.gitignore index 4dc43e1bd79c..790c47001e77 100644 --- a/tools/testing/selftests/perf_events/.gitignore +++ b/tools/testing/selftests/perf_events/.gitignore @@ -1,2 +1,3 @@ # SPDX-License-Identifier: GPL-2.0-only sigtrap_threads +remove_on_exec diff --git a/tools/testing/selftests/perf_events/Makefile b/tools/testing/selftests/perf_events/Makefile index 973a2c39ca83..fcafa5f0d34c 100644 --- a/tools/testing/selftests/perf_events/Makefile +++ b/tools/testing/selftests/perf_events/Makefile @@ -2,5 +2,5 @@ CFLAGS += -Wl,-no-as-needed -Wall -I../../../../usr/include LDFLAGS += -lpthread -TEST_GEN_PROGS := sigtrap_threads +TEST_GEN_PROGS := sigtrap_threads remove_on_exec include ../lib.mk diff --git a/tools/testing/selftests/perf_events/remove_on_exec.c b/tools/testing/selftests/perf_events/remove_on_exec.c new file mode 100644 index 000000000000..e176b3a74d55 --- /dev/null +++ b/tools/testing/selftests/perf_events/remove_on_exec.c @@ -0,0 +1,256 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Test for remove_on_exec. + * + * Copyright (C) 2021, Google LLC. + */ + +#define _GNU_SOURCE +#include + +/* We need the latest siginfo from the kernel repo. */ +#include +#define __have_siginfo_t 1 +#define __have_sigval_t 1 +#define __have_sigevent_t 1 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../kselftest_harness.h" + +static volatile int signal_count; + +static struct perf_event_attr make_event_attr(void) +{ + struct perf_event_attr attr = { + .type = PERF_TYPE_HARDWARE, + .size = sizeof(attr), + .config = PERF_COUNT_HW_INSTRUCTIONS, + .sample_period = 1000, + .exclude_kernel = 1, + .exclude_hv = 1, + .disabled = 1, + .inherit = 1, + /* + * Children normally retain their inherited event on exec; with + * remove_on_exec, we'll remove their event, but the parent and + * any other non-exec'd children will keep their events. + */ + .remove_on_exec = 1, + .sigtrap = 1, + }; + return attr; +} + +static void sigtrap_handler(int signum, siginfo_t *info, void *ucontext) +{ + if (info->si_code != TRAP_PERF) { + fprintf(stderr, "%s: unexpected si_code %d\n", __func__, info->si_code); + return; + } + + signal_count++; +} + +FIXTURE(remove_on_exec) +{ + struct sigaction oldact; + int fd; +}; + +FIXTURE_SETUP(remove_on_exec) +{ + struct perf_event_attr attr = make_event_attr(); + struct sigaction action = {}; + + signal_count = 0; + + /* Initialize sigtrap handler. */ + action.sa_flags = SA_SIGINFO | SA_NODEFER; + action.sa_sigaction = sigtrap_handler; + sigemptyset(&action.sa_mask); + ASSERT_EQ(sigaction(SIGTRAP, &action, &self->oldact), 0); + + /* Initialize perf event. */ + self->fd = syscall(__NR_perf_event_open, &attr, 0, -1, -1, PERF_FLAG_FD_CLOEXEC); + ASSERT_NE(self->fd, -1); +} + +FIXTURE_TEARDOWN(remove_on_exec) +{ + close(self->fd); + sigaction(SIGTRAP, &self->oldact, NULL); +} + +/* Verify event propagates to fork'd child. */ +TEST_F(remove_on_exec, fork_only) +{ + int status; + pid_t pid = fork(); + + if (pid == 0) { + ASSERT_EQ(signal_count, 0); + ASSERT_EQ(ioctl(self->fd, PERF_EVENT_IOC_ENABLE, 0), 0); + while (!signal_count); + _exit(42); + } + + while (!signal_count); /* Child enables event. */ + EXPECT_EQ(waitpid(pid, &status, 0), pid); + EXPECT_EQ(WEXITSTATUS(status), 42); +} + +/* + * Verify that event does _not_ propagate to fork+exec'd child; event enabled + * after fork+exec. + */ +TEST_F(remove_on_exec, fork_exec_then_enable) +{ + pid_t pid_exec, pid_only_fork; + int pipefd[2]; + int tmp; + + /* + * Non-exec child, to ensure exec does not affect inherited events of + * other children. + */ + pid_only_fork = fork(); + if (pid_only_fork == 0) { + /* Block until parent enables event. */ + while (!signal_count); + _exit(42); + } + + ASSERT_NE(pipe(pipefd), -1); + pid_exec = fork(); + if (pid_exec == 0) { + ASSERT_NE(dup2(pipefd[1], STDOUT_FILENO), -1); + close(pipefd[0]); + execl("/proc/self/exe", "exec_child", NULL); + _exit((perror("exec failed"), 1)); + } + close(pipefd[1]); + + ASSERT_EQ(waitpid(pid_exec, &tmp, WNOHANG), 0); /* Child is running. */ + /* Wait for exec'd child to start spinning. */ + EXPECT_EQ(read(pipefd[0], &tmp, sizeof(int)), sizeof(int)); + EXPECT_EQ(tmp, 42); + close(pipefd[0]); + /* Now we can enable the event, knowing the child is doing work. */ + EXPECT_EQ(ioctl(self->fd, PERF_EVENT_IOC_ENABLE, 0), 0); + /* If the event propagated to the exec'd child, it will exit normally... */ + usleep(100000); /* ... give time for event to trigger (in case of bug). */ + EXPECT_EQ(waitpid(pid_exec, &tmp, WNOHANG), 0); /* Should still be running. */ + EXPECT_EQ(kill(pid_exec, SIGKILL), 0); + + /* Verify removal from child did not affect this task's event. */ + tmp = signal_count; + while (signal_count == tmp); /* Should not hang! */ + /* Nor should it have affected the first child. */ + EXPECT_EQ(waitpid(pid_only_fork, &tmp, 0), pid_only_fork); + EXPECT_EQ(WEXITSTATUS(tmp), 42); +} + +/* + * Verify that event does _not_ propagate to fork+exec'd child; event enabled + * before fork+exec. + */ +TEST_F(remove_on_exec, enable_then_fork_exec) +{ + pid_t pid_exec; + int tmp; + + EXPECT_EQ(ioctl(self->fd, PERF_EVENT_IOC_ENABLE, 0), 0); + + pid_exec = fork(); + if (pid_exec == 0) { + execl("/proc/self/exe", "exec_child", NULL); + _exit((perror("exec failed"), 1)); + } + + /* + * The child may exit abnormally at any time if the event propagated and + * a SIGTRAP is sent before the handler was set up. + */ + usleep(100000); /* ... give time for event to trigger (in case of bug). */ + EXPECT_EQ(waitpid(pid_exec, &tmp, WNOHANG), 0); /* Should still be running. */ + EXPECT_EQ(kill(pid_exec, SIGKILL), 0); + + /* Verify removal from child did not affect this task's event. */ + tmp = signal_count; + while (signal_count == tmp); /* Should not hang! */ +} + +TEST_F(remove_on_exec, exec_stress) +{ + pid_t pids[30]; + int i, tmp; + + for (i = 0; i < sizeof(pids) / sizeof(pids[0]); i++) { + pids[i] = fork(); + if (pids[i] == 0) { + execl("/proc/self/exe", "exec_child", NULL); + _exit((perror("exec failed"), 1)); + } + + /* Some forked with event disabled, rest with enabled. */ + if (i > 10) + EXPECT_EQ(ioctl(self->fd, PERF_EVENT_IOC_ENABLE, 0), 0); + } + + usleep(100000); /* ... give time for event to trigger (in case of bug). */ + + for (i = 0; i < sizeof(pids) / sizeof(pids[0]); i++) { + /* All children should still be running. */ + EXPECT_EQ(waitpid(pids[i], &tmp, WNOHANG), 0); + EXPECT_EQ(kill(pids[i], SIGKILL), 0); + } + + /* Verify event is still alive. */ + tmp = signal_count; + while (signal_count == tmp); +} + +/* For exec'd child. */ +static void exec_child(void) +{ + struct sigaction action = {}; + const int val = 42; + + /* Set up sigtrap handler in case we erroneously receive a trap. */ + action.sa_flags = SA_SIGINFO | SA_NODEFER; + action.sa_sigaction = sigtrap_handler; + sigemptyset(&action.sa_mask); + if (sigaction(SIGTRAP, &action, NULL)) + _exit((perror("sigaction failed"), 1)); + + /* Signal parent that we're starting to spin. */ + if (write(STDOUT_FILENO, &val, sizeof(int)) == -1) + _exit((perror("write failed"), 1)); + + /* Should hang here until killed. */ + while (!signal_count); +} + +#define main test_main +TEST_HARNESS_MAIN +#undef main +int main(int argc, char *argv[]) +{ + if (!strcmp(argv[0], "exec_child")) { + exec_child(); + return 1; + } + + return test_main(argc, argv); +} -- 2.30.1.766.gb4fecdf3b7-goog