From patchwork Tue Aug 8 01:59:08 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kees Cook X-Patchwork-Id: 9886439 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id B0BBB60363 for ; Tue, 8 Aug 2017 01:59:58 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id A0EA72874F for ; Tue, 8 Aug 2017 01:59:58 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 9577F28753; Tue, 8 Aug 2017 01:59:58 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.3 required=2.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_HI, RCVD_IN_SORBS_SPAM, T_DKIM_INVALID autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 086192874F for ; Tue, 8 Aug 2017 01:59:58 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752101AbdHHB7z (ORCPT ); Mon, 7 Aug 2017 21:59:55 -0400 Received: from mail-pg0-f41.google.com ([74.125.83.41]:38014 "EHLO mail-pg0-f41.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751950AbdHHB7N (ORCPT ); Mon, 7 Aug 2017 21:59:13 -0400 Received: by mail-pg0-f41.google.com with SMTP id l64so8423631pge.5 for ; Mon, 07 Aug 2017 18:59:13 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=1Pks5f60RhHJJ1pPiqSTuB9ZZnElKpVvvLUREUJ/qlk=; b=P6125ybSiFbiOPBTN+sit5gPB98A5RbN3s5bYQEN0lt3DvpmiJMzcRjNvaeHK7Nbj8 Ov+r5vLpfNB5zz3RAPiNEAxcuGbO1qn+7/EatE9NDcry5bi1N7aEHJpGIWDuE28nY2FH RuQpGqbbbywJkw+m3WOmZWYjHTgJ/7yL/NvJQ= 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; bh=1Pks5f60RhHJJ1pPiqSTuB9ZZnElKpVvvLUREUJ/qlk=; b=mnbRfCoPobiHQ2Y+yhpksM48vdQxvpGOj8mQuUh/pqsD68QEk9sOpC4BOhxzhUpD80 JAWDkolMC3y0Rmg4l3jO6S5fEn2JoY7Gtmpw9NFi+6adtaXUijy9IvO0VJkU2xlPoPIK epqP5KPkN1JgwJugW6t/HMtiCTB2XcKMRs8N5xaaCuv7SUtpZHS+xIkFWE7MxHl6Kvum kTz9D8ab33RqEr1jR6nxvQjll8z9G+O3J6nfTdTdVREWrAF8KEHcX/bE0TvPxcKec3Nc YgVkJ05gtL2fsxCD8zF5mA3F+GMNTqKyKqLzOxxE+VzqH117JuCRd+uHlsrt2chqU3CR 10ww== X-Gm-Message-State: AHYfb5ghS/5GgG4i/y7KPjRrRbC7lAveJtgAC1f7XTGNon2os8gy/juO DEbYs0jHlcJlBLNS X-Received: by 10.84.139.40 with SMTP id 37mr2849805plq.153.1502157553281; Mon, 07 Aug 2017 18:59:13 -0700 (PDT) Received: from www.outflux.net (173-164-112-133-Oregon.hfc.comcastbusiness.net. [173.164.112.133]) by smtp.gmail.com with ESMTPSA id 206sm202282pfc.61.2017.08.07.18.59.10 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Mon, 07 Aug 2017 18:59:10 -0700 (PDT) From: Kees Cook To: linux-kernel@vger.kernel.org Cc: Kees Cook , Fabricio Voznika , Tyler Hicks , Andy Lutomirski , Will Drewry , Shuah Khan , linux-kselftest@vger.kernel.org, linux-security-module@vger.kernel.org Subject: [PATCH v2 4/4] selftests/seccomp: Test thread vs process killing Date: Mon, 7 Aug 2017 18:59:08 -0700 Message-Id: <1502157548-111843-5-git-send-email-keescook@chromium.org> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1502157548-111843-1-git-send-email-keescook@chromium.org> References: <1502157548-111843-1-git-send-email-keescook@chromium.org> Sender: owner-linux-security-module@vger.kernel.org Precedence: bulk List-ID: X-Virus-Scanned: ClamAV using ClamSMTP SECCOMP_RET_KILL is supposed to kill the current thread (and userspace depends on this), so test for this, distinct from killing the entire process. This also tests killing the entire process with the new SECCOMP_FILTER_FLAG_KILL_PROCESS flag. (This also moves a bunch of defines up earlier in the file to use them earlier.) Signed-off-by: Kees Cook Reviewed-by: Tyler Hicks --- tools/testing/selftests/seccomp/seccomp_bpf.c | 185 ++++++++++++++++++++------ 1 file changed, 144 insertions(+), 41 deletions(-) diff --git a/tools/testing/selftests/seccomp/seccomp_bpf.c b/tools/testing/selftests/seccomp/seccomp_bpf.c index ee78a53da5d1..d0a9bebf21f3 100644 --- a/tools/testing/selftests/seccomp/seccomp_bpf.c +++ b/tools/testing/selftests/seccomp/seccomp_bpf.c @@ -87,6 +87,51 @@ struct seccomp_data { }; #endif +#ifndef __NR_seccomp +# if defined(__i386__) +# define __NR_seccomp 354 +# elif defined(__x86_64__) +# define __NR_seccomp 317 +# elif defined(__arm__) +# define __NR_seccomp 383 +# elif defined(__aarch64__) +# define __NR_seccomp 277 +# elif defined(__hppa__) +# define __NR_seccomp 338 +# elif defined(__powerpc__) +# define __NR_seccomp 358 +# elif defined(__s390__) +# define __NR_seccomp 348 +# else +# warning "seccomp syscall number unknown for this architecture" +# define __NR_seccomp 0xffff +# endif +#endif + +#ifndef SECCOMP_SET_MODE_STRICT +#define SECCOMP_SET_MODE_STRICT 0 +#endif + +#ifndef SECCOMP_SET_MODE_FILTER +#define SECCOMP_SET_MODE_FILTER 1 +#endif + +#ifndef SECCOMP_FILTER_FLAG_TSYNC +#define SECCOMP_FILTER_FLAG_TSYNC 1 +#endif + +#ifndef SECCOMP_FILTER_FLAG_KILL_PROCESS +#define SECCOMP_FILTER_FLAG_KILL_PROCESS 2 +#endif + +#ifndef seccomp +int seccomp(unsigned int op, unsigned int flags, void *args) +{ + errno = 0; + return syscall(__NR_seccomp, op, flags, args); +} +#endif + #if __BYTE_ORDER == __LITTLE_ENDIAN #define syscall_arg(_n) (offsetof(struct seccomp_data, args[_n])) #elif __BYTE_ORDER == __BIG_ENDIAN @@ -520,6 +565,105 @@ TEST_SIGNAL(KILL_one_arg_six, SIGSYS) close(fd); } +/* This is a thread task to die via seccomp filter violation. */ +void *kill_thread(void *data) +{ + bool die = (bool)data; + + if (die) { + prctl(PR_GET_SECCOMP, 0, 0, 0, 0); + return (void *)SIBLING_EXIT_FAILURE; + } + + return (void *)SIBLING_EXIT_UNKILLED; +} + +/* Prepare a thread that will kill itself or both of us. */ +void kill_thread_or_group(struct __test_metadata *_metadata, bool kill_process) +{ + pthread_t thread; + void *status; + unsigned int flags; + /* Kill only when calling __NR_prctl. */ + struct sock_filter filter[] = { + BPF_STMT(BPF_LD|BPF_W|BPF_ABS, + offsetof(struct seccomp_data, nr)), + BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_prctl, 0, 1), + BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_KILL), + BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW), + }; + struct sock_fprog prog = { + .len = (unsigned short)ARRAY_SIZE(filter), + .filter = filter, + }; + + ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { + TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!"); + } + + flags = kill_process ? SECCOMP_FILTER_FLAG_KILL_PROCESS : 0; + ASSERT_EQ(0, seccomp(SECCOMP_SET_MODE_FILTER, flags, &prog)) { + if (kill_process) + TH_LOG("Kernel does not support SECCOMP_FILTER_FLAG_KILL_PROCESS"); + else + TH_LOG("Kernel does not support seccomp syscall"); + } + + /* Start a thread that will exit immediately. */ + ASSERT_EQ(0, pthread_create(&thread, NULL, kill_thread, (void *)false)); + ASSERT_EQ(0, pthread_join(thread, &status)); + ASSERT_EQ(SIBLING_EXIT_UNKILLED, (unsigned long)status); + + /* Start a thread that will die immediately. */ + ASSERT_EQ(0, pthread_create(&thread, NULL, kill_thread, (void *)true)); + ASSERT_EQ(0, pthread_join(thread, &status)); + ASSERT_NE(SIBLING_EXIT_FAILURE, (unsigned long)status); + + /* + * If we get here, only the spawned thread died. Let the parent know + * this entire process (all threads including this one) didn't die. + */ + exit(42); +} + +TEST(KILL_thread) +{ + int status; + pid_t child_pid; + + child_pid = fork(); + ASSERT_LE(0, child_pid); + if (child_pid == 0) { + kill_thread_or_group(_metadata, false); + _exit(38); + } + + ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0)); + + /* If only the thread was killed, we'll see exit 42. */ + ASSERT_TRUE(WIFEXITED(status)); + ASSERT_EQ(42, WEXITSTATUS(status)); +} + +TEST(KILL_process) +{ + int status; + pid_t child_pid; + + child_pid = fork(); + ASSERT_LE(0, child_pid); + if (child_pid == 0) { + kill_thread_or_group(_metadata, true); + _exit(38); + } + + ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0)); + + /* If the entire process was killed, we'll see SIGSYS. */ + ASSERT_TRUE(WIFSIGNALED(status)); + ASSERT_EQ(SIGSYS, WTERMSIG(status)); +} + /* TODO(wad) add 64-bit versus 32-bit arg tests. */ TEST(arg_out_of_range) { @@ -1675,47 +1819,6 @@ TEST_F_SIGNAL(TRACE_syscall, kill_after_ptrace, SIGSYS) EXPECT_NE(self->mypid, syscall(__NR_getpid)); } -#ifndef __NR_seccomp -# if defined(__i386__) -# define __NR_seccomp 354 -# elif defined(__x86_64__) -# define __NR_seccomp 317 -# elif defined(__arm__) -# define __NR_seccomp 383 -# elif defined(__aarch64__) -# define __NR_seccomp 277 -# elif defined(__hppa__) -# define __NR_seccomp 338 -# elif defined(__powerpc__) -# define __NR_seccomp 358 -# elif defined(__s390__) -# define __NR_seccomp 348 -# else -# warning "seccomp syscall number unknown for this architecture" -# define __NR_seccomp 0xffff -# endif -#endif - -#ifndef SECCOMP_SET_MODE_STRICT -#define SECCOMP_SET_MODE_STRICT 0 -#endif - -#ifndef SECCOMP_SET_MODE_FILTER -#define SECCOMP_SET_MODE_FILTER 1 -#endif - -#ifndef SECCOMP_FILTER_FLAG_TSYNC -#define SECCOMP_FILTER_FLAG_TSYNC 1 -#endif - -#ifndef seccomp -int seccomp(unsigned int op, unsigned int flags, void *args) -{ - errno = 0; - return syscall(__NR_seccomp, op, flags, args); -} -#endif - TEST(seccomp_syscall) { struct sock_filter filter[] = {