Received: by 2002:a25:4158:0:0:0:0:0 with SMTP id o85csp5118732yba; Tue, 30 Apr 2019 09:25:11 -0700 (PDT) X-Google-Smtp-Source: APXvYqwJ0PL/gUiQffAPbGTTAV9d5axnekcoLajEJOPEqium02Yt7JmPbo/AIWcSYufDbZqf3X+Q X-Received: by 2002:a63:5a4b:: with SMTP id k11mr67043742pgm.119.1556641511882; Tue, 30 Apr 2019 09:25:11 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1556641511; cv=none; d=google.com; s=arc-20160816; b=wPQPHr28LSgdlD6UZC47LSBB6lJwQaVFlHqWx1+rvlwhiZrF9OkhxcKaajHDbWvV3k NlpK3oBHQn8ZOWukieL7JYwcbp5L9fcTOo4Othn2CR96/oyVlcw4YMt3I6M3TwtHtaTV jhGItCIQm3+J2Vz0kNxr9evif6ig1QXDLtdaudPiNduS3hXF8uevu/kc23qm2VO7DjXH 3bPYP0GNoMv+wty39SQqVwnvihQ5zvuVDCeoyJtNKHnDC8fTJYVA9f88uG6KkZeZEfFi 0rB5VYevcRl5gEFGDJ90K7+Q8Pylo5r92kyj4zSQXzE084szzvn6e3TCnyldop1Horjk QStQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=+Y43YbmIOB0Qk1pxLpeQ+huaB4E29PSD97QFlwu/9zQ=; b=aZoZbP/dY/HjFlZ3NUAl+z9xM9FD25m5hJB86moKugN/2Q1diaANmfLbwFZdBzgOuE 7UFR/wAruo66IDn4ZzVSq9mXH79lE+XzfyYmVhz99KM3oE+WAyFEfy8ooTp7pl8+UqBb OAKnHMnUMApowtSQ5QQ5tx0RwuFGAdN81cu9FW+PqnrRS6tOXCx8v5UArTr6TtxtSIUu 42+YpPJM90ypjDkbI/LhT7ZJxknwQhoosZIbqznjdRTolFQMVNvWIIbTl/LLgX9OHTlz LGhM/y/2WodN7s0IZjzwTVT6CUIwKyumN+esvi5ECTR6td5HxdXTcLQXIH359fU2nyWo YqWw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@joelfernandes.org header.s=google header.b=kv2kWG3T; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id m184si20227712pfb.166.2019.04.30.09.24.55; Tue, 30 Apr 2019 09:25:11 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@joelfernandes.org header.s=google header.b=kv2kWG3T; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726603AbfD3QWY (ORCPT + 99 others); Tue, 30 Apr 2019 12:22:24 -0400 Received: from mail-pl1-f194.google.com ([209.85.214.194]:32890 "EHLO mail-pl1-f194.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726412AbfD3QWY (ORCPT ); Tue, 30 Apr 2019 12:22:24 -0400 Received: by mail-pl1-f194.google.com with SMTP id y3so6124981plp.0 for ; Tue, 30 Apr 2019 09:22:23 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=joelfernandes.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=+Y43YbmIOB0Qk1pxLpeQ+huaB4E29PSD97QFlwu/9zQ=; b=kv2kWG3TEBOd8W/Tcco64TZP9JSGUKVbLS9QLAezRBpzlddDt4+nTS1OoitiwNCBew fqJeEQVf5co62HnUNsiqmLbbO3G7ZljHGK7NJWw4MEFLoPA1NWuwJFBKtLlidx97W47j hRtZDnkdmFH28/lEAjCpcdoPxLpbJKG9iPKNU= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=+Y43YbmIOB0Qk1pxLpeQ+huaB4E29PSD97QFlwu/9zQ=; b=fe3CoTlUoX43YIwyt59TZiSZXbzD9930q4Sy2v7Bn1deEqafNONMRVAtbxefwC02ja GS2BGy01McrXbifIkJ4ObCkZsZzEi4xZzn+xSbRezxr1h1xNtRXbMc+N0E1ReJowTg2q p2fNsfyFLZi9Iz5/hYZCMAMB8bl/LKLJxjXYOTnjvLrbvae1V0mPhG0vUGASxDYCs+34 k5XbSm+XlYAsnokmBZ+8bZL4y+VpHMUfA7lx7bBdPTl9fEL0OjugiVEbp9kgU/O+5WN6 RYQ5HZy1UzFhVcc/VMrcGATnGYrxV0rUsEY5UZB0KFbxv8R8DRD4cHoZYeQpHCHEKJSK zNwA== X-Gm-Message-State: APjAAAXH//MJbKoJtrHJMMWchtfz0tPqY6OO8mgG0/YdVFDiWeW+D52c DjCsQTwE74N2i5RA3+8h9dmWK9xjXQc= X-Received: by 2002:a17:902:822:: with SMTP id 31mr69673738plk.41.1556641342684; Tue, 30 Apr 2019 09:22:22 -0700 (PDT) Received: from joelaf.cam.corp.google.com ([2620:15c:6:12:9c46:e0da:efbf:69cc]) by smtp.gmail.com with ESMTPSA id y68sm2201059pfy.28.2019.04.30.09.22.19 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 30 Apr 2019 09:22:21 -0700 (PDT) From: "Joel Fernandes (Google)" To: linux-kernel@vger.kernel.org Cc: "Joel Fernandes (Google)" , Al Viro , Andrew Morton , Andy Lutomirski , Arnd Bergmann , Christian Brauner , Daniel Colascione , David Howells , "Eric W. Biederman" , Greg Kroah-Hartman , Ingo Molnar , Jann Horn , Jonathan Kowalski , Kees Cook , kernel-team@android.com, KJ Tsanaktsidis , Linus Torvalds , linux-kselftest@vger.kernel.org, Michal Hocko , Nadav Amit , Oleg Nesterov , "Peter Zijlstra (Intel)" , Serge Hallyn , Shuah Khan , Steven Rostedt , Thomas Gleixner , Tim Murray , Tycho Andersen Subject: [PATCH v2 2/2] Add selftests for pidfd polling Date: Tue, 30 Apr 2019 12:21:54 -0400 Message-Id: <20190430162154.61314-2-joel@joelfernandes.org> X-Mailer: git-send-email 2.21.0.593.g511ec345e18-goog In-Reply-To: <20190430162154.61314-1-joel@joelfernandes.org> References: <20190430162154.61314-1-joel@joelfernandes.org> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Other than verifying pidfd based polling, the tests make sure that wait semantics are preserved with the pidfd poll. Notably the 2 cases: 1. If a thread group leader exits while threads still there, then no pidfd poll notifcation should happen. 2. If a non-thread group leader does an execve, then the thread group leader is signaled to exit and is replaced with the execing thread as the new leader, however the parent is not notified in this case. Signed-off-by: Joel Fernandes (Google) --- tools/testing/selftests/pidfd/Makefile | 2 +- tools/testing/selftests/pidfd/pidfd_test.c | 210 +++++++++++++++++++++ 2 files changed, 211 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/pidfd/Makefile b/tools/testing/selftests/pidfd/Makefile index deaf8073bc06..4b31c14f273c 100644 --- a/tools/testing/selftests/pidfd/Makefile +++ b/tools/testing/selftests/pidfd/Makefile @@ -1,4 +1,4 @@ -CFLAGS += -g -I../../../../usr/include/ +CFLAGS += -g -I../../../../usr/include/ -lpthread TEST_GEN_PROGS := pidfd_test diff --git a/tools/testing/selftests/pidfd/pidfd_test.c b/tools/testing/selftests/pidfd/pidfd_test.c index d59378a93782..8b404ccbc4ff 100644 --- a/tools/testing/selftests/pidfd/pidfd_test.c +++ b/tools/testing/selftests/pidfd/pidfd_test.c @@ -4,18 +4,47 @@ #include #include #include +#include #include #include #include #include #include #include +#include +#include #include #include +#include #include #include "../kselftest.h" +#define str(s) _str(s) +#define _str(s) #s +#define CHILD_THREAD_MIN_WAIT 3 /* seconds */ + +#define MAX_EVENTS 5 +#ifndef __NR_pidfd_send_signal +#define __NR_pidfd_send_signal 424 +#endif + +#ifndef CLONE_PIDFD +#define CLONE_PIDFD 0x00001000 +#endif + +static pid_t pidfd_clone(int flags, int *pidfd, int (*fn)(void *)) +{ + size_t stack_size = 1024; + char *stack[1024] = { 0 }; + +#ifdef __ia64__ + return __clone2(fn, stack, stack_size, flags | SIGCHLD, NULL, pidfd); +#else + return clone(fn, stack + stack_size, flags | SIGCHLD, NULL, pidfd); +#endif +} + static inline int sys_pidfd_send_signal(int pidfd, int sig, siginfo_t *info, unsigned int flags) { @@ -368,10 +397,191 @@ static int test_pidfd_send_signal_syscall_support(void) return 0; } +static void *test_pidfd_poll_exec_thread(void *priv) +{ + ksft_print_msg("Child Thread: starting. pid %d tid %d ; and sleeping\n", + getpid(), syscall(SYS_gettid)); + ksft_print_msg("Child Thread: doing exec of sleep\n"); + + execl("/bin/sleep", "sleep", str(CHILD_THREAD_MIN_WAIT), (char *)NULL); + + ksft_print_msg("Child Thread: DONE. pid %d tid %d\n", + getpid(), syscall(SYS_gettid)); + return NULL; +} + +static void poll_pidfd(const char *test_name, int pidfd) +{ + int c; + int epoll_fd = epoll_create1(EPOLL_CLOEXEC); + struct epoll_event event, events[MAX_EVENTS]; + + if (epoll_fd == -1) + ksft_exit_fail_msg("%s test: Failed to create epoll file descriptor " + "(errno %d)\n", + test_name, errno); + + event.events = EPOLLIN; + event.data.fd = pidfd; + + if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, pidfd, &event)) { + ksft_exit_fail_msg("%s test: Failed to add epoll file descriptor " + "(errno %d)\n", + test_name, errno); + } + + c = epoll_wait(epoll_fd, events, MAX_EVENTS, 5000); + if (c != 1 || !(events[0].events & EPOLLIN)) + ksft_exit_fail_msg("%s test: Unexpected epoll_wait result (c=%d, events=%x) ", + "(errno %d)\n", + test_name, c, events[0].events, errno); + + close(epoll_fd); + return; + +} + +static int child_poll_exec_test(void *args) +{ + pthread_t t1; + + ksft_print_msg("Child (pidfd): starting. pid %d tid %d\n", getpid(), + syscall(SYS_gettid)); + pthread_create(&t1, NULL, test_pidfd_poll_exec_thread, NULL); + /* + * Exec in the non-leader thread will destroy the leader immediately. + * If the wait in the parent returns too soon, the test fails. + */ + while (1) + sleep(1); +} + +static int test_pidfd_poll_exec(int use_waitpid) +{ + int pid, pidfd = 0; + int status, ret; + pthread_t t1; + time_t prog_start = time(NULL); + const char *test_name = "pidfd_poll check for premature notification on child thread exec"; + + ksft_print_msg("Parent: pid: %d\n", getpid()); + pid = pidfd_clone(CLONE_PIDFD, &pidfd, child_poll_exec_test); + if (pid < 0) + ksft_exit_fail_msg("%s test: pidfd_clone failed (ret %d, errno %d)\n", + test_name, pid, errno); + + ksft_print_msg("Parent: Waiting for Child (%d) to complete.\n", pid); + + if (use_waitpid) { + ret = waitpid(pid, &status, 0); + if (ret == -1) + ksft_print_msg("Parent: error\n"); + + if (ret == pid) + ksft_print_msg("Parent: Child process waited for.\n"); + } else { + poll_pidfd(test_name, pidfd); + } + + time_t prog_time = time(NULL) - prog_start; + + ksft_print_msg("Time waited for child: %lu\n", prog_time); + + close(pidfd); + + if (prog_time < CHILD_THREAD_MIN_WAIT || prog_time > CHILD_THREAD_MIN_WAIT + 2) + ksft_exit_fail_msg("%s test: Failed\n", test_name); + else + ksft_test_result_pass("%s test: Passed\n", test_name); +} + +static void *test_pidfd_poll_leader_exit_thread(void *priv) +{ + ksft_print_msg("Child Thread: starting. pid %d tid %d ; and sleeping\n", + getpid(), syscall(SYS_gettid)); + sleep(CHILD_THREAD_MIN_WAIT); + ksft_print_msg("Child Thread: DONE. pid %d tid %d\n", getpid(), syscall(SYS_gettid)); + return NULL; +} + +static time_t *child_exit_secs; +static int child_poll_leader_exit_test(void *args) +{ + pthread_t t1, t2; + + ksft_print_msg("Child: starting. pid %d tid %d\n", getpid(), syscall(SYS_gettid)); + pthread_create(&t1, NULL, test_pidfd_poll_leader_exit_thread, NULL); + pthread_create(&t2, NULL, test_pidfd_poll_leader_exit_thread, NULL); + + /* + * glibc exit calls exit_group syscall, so explicity call exit only + * so that only the group leader exits, leaving the threads alone. + */ + *child_exit_secs = time(NULL); + syscall(SYS_exit, 0); +} + +static int test_pidfd_poll_leader_exit(int use_waitpid) +{ + int pid, pidfd = 0; + int status, ret; + time_t prog_start = time(NULL); + const char *test_name = "pidfd_poll check for premature notification on non-empty" + "group leader exit"; + + child_exit_secs = mmap(NULL, sizeof *child_exit_secs, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS, -1, 0); + + if (child_exit_secs == MAP_FAILED) + ksft_exit_fail_msg("%s test: mmap failed (errno %d)\n", + test_name, errno); + + ksft_print_msg("Parent: pid: %d\n", getpid()); + pid = pidfd_clone(CLONE_PIDFD, &pidfd, child_poll_leader_exit_test); + if (pid < 0) + ksft_exit_fail_msg("%s test: pidfd_clone failed (ret %d, errno %d)\n", + test_name, pid, errno); + + ksft_print_msg("Parent: Waiting for Child (%d) to complete.\n", pid); + + if (use_waitpid) { + ret = waitpid(pid, &status, 0); + if (ret == -1) + ksft_print_msg("Parent: error\n"); + } else { + /* + * This sleep tests for the case where if the child exits, and is in + * EXIT_ZOMBIE, but the thread group leader is non-empty, then the poll + * doesn't prematurely return even though there are active threads + */ + sleep(1); + poll_pidfd(test_name, pidfd); + } + + if (ret == pid) + ksft_print_msg("Parent: Child process waited for.\n"); + + time_t since_child_exit = time(NULL) - *child_exit_secs; + + ksft_print_msg("Time since child exit: %lu\n", since_child_exit); + + close(pidfd); + + if (since_child_exit < CHILD_THREAD_MIN_WAIT || + since_child_exit > CHILD_THREAD_MIN_WAIT + 2) + ksft_exit_fail_msg("%s test: Failed\n", test_name); + else + ksft_test_result_pass("%s test: Passed\n", test_name); +} + int main(int argc, char **argv) { ksft_print_header(); + test_pidfd_poll_exec(0); + test_pidfd_poll_exec(1); + test_pidfd_poll_leader_exit(0); + test_pidfd_poll_leader_exit(1); test_pidfd_send_signal_syscall_support(); test_pidfd_send_signal_simple_success(); test_pidfd_send_signal_exited_fail(); -- 2.21.0.593.g511ec345e18-goog