From patchwork Thu May 23 02:54:00 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yang Xu X-Patchwork-Id: 1103793 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=lists.linux.it (client-ip=213.254.12.146; helo=picard.linux.it; envelope-from=ltp-bounces+incoming=patchwork.ozlabs.org@lists.linux.it; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=cn.fujitsu.com Received: from picard.linux.it (picard.linux.it [213.254.12.146]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 458Yxl6Fgjz9s3Z for ; Thu, 23 May 2019 12:54:35 +1000 (AEST) Received: from picard.linux.it (localhost [IPv6:::1]) by picard.linux.it (Postfix) with ESMTP id 501083EA406 for ; Thu, 23 May 2019 04:54:31 +0200 (CEST) X-Original-To: ltp@lists.linux.it Delivered-To: ltp@picard.linux.it Received: from in-2.smtp.seeweb.it (in-2.smtp.seeweb.it [IPv6:2001:4b78:1:20::2]) by picard.linux.it (Postfix) with ESMTP id 5EAF23EA3BC for ; Thu, 23 May 2019 04:54:28 +0200 (CEST) Received: from heian.cn.fujitsu.com (mail.cn.fujitsu.com [183.91.158.132]) by in-2.smtp.seeweb.it (Postfix) with ESMTP id 2F87C6023AF for ; Thu, 23 May 2019 04:54:22 +0200 (CEST) X-IronPort-AV: E=Sophos;i="5.60,501,1549900800"; d="scan'208";a="64254433" Received: from unknown (HELO cn.fujitsu.com) ([10.167.33.5]) by heian.cn.fujitsu.com with ESMTP; 23 May 2019 10:54:20 +0800 Received: from G08CNEXCHPEKD02.g08.fujitsu.local (unknown [10.167.33.83]) by cn.fujitsu.com (Postfix) with ESMTP id AC57F4CD846F; Thu, 23 May 2019 10:54:19 +0800 (CST) Received: from localhost.localdomain (10.167.215.30) by G08CNEXCHPEKD02.g08.fujitsu.local (10.167.33.89) with Microsoft SMTP Server (TLS) id 14.3.439.0; Thu, 23 May 2019 10:54:17 +0800 From: Yang Xu To: Date: Thu, 23 May 2019 10:54:00 +0800 Message-ID: <1558580040-3074-1-git-send-email-xuyang2018.jy@cn.fujitsu.com> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <20190522123657.GB7912@rei.lan> References: <20190522123657.GB7912@rei.lan> MIME-Version: 1.0 X-Originating-IP: [10.167.215.30] X-yoursite-MailScanner-ID: AC57F4CD846F.A20E5 X-yoursite-MailScanner: Found to be clean X-yoursite-MailScanner-From: xuyang2018.jy@cn.fujitsu.com X-Spam-Status: No, score=0.0 required=7.0 tests=SPF_HELO_NONE,SPF_NONE autolearn=disabled version=3.4.0 X-Virus-Scanned: clamav-milter 0.99.2 at in-2.smtp.seeweb.it X-Virus-Status: Clean X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on in-2.smtp.seeweb.it Cc: ltp@lists.linux.it Subject: [LTP] [PATCH v3] syscalls/prctl04.c: New test for prctl() with PR_{SET, GET}_SECCOMP X-BeenThere: ltp@lists.linux.it X-Mailman-Version: 2.1.18 Precedence: list List-Id: Linux Test Project List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ltp-bounces+incoming=patchwork.ozlabs.org@lists.linux.it Sender: "ltp" Signed-off-by: Yang Xu --- configure.ac | 1 + include/lapi/prctl.h | 5 + include/lapi/seccomp.h | 40 ++++ runtest/syscalls | 1 + testcases/kernel/syscalls/prctl/.gitignore | 1 + testcases/kernel/syscalls/prctl/prctl04.c | 231 +++++++++++++++++++++ 6 files changed, 279 insertions(+) create mode 100644 include/lapi/seccomp.h create mode 100644 testcases/kernel/syscalls/prctl/prctl04.c diff --git a/configure.ac b/configure.ac index 5dc85728a..5a3dc5b62 100644 --- a/configure.ac +++ b/configure.ac @@ -46,6 +46,7 @@ AC_CHECK_HEADERS([ \ linux/mempolicy.h \ linux/module.h \ linux/netlink.h \ + linux/seccomp.h \ linux/userfaultfd.h \ mm.h \ netinet/sctp.h \ diff --git a/include/lapi/prctl.h b/include/lapi/prctl.h index 6db8a6480..f42bd6459 100644 --- a/include/lapi/prctl.h +++ b/include/lapi/prctl.h @@ -9,6 +9,11 @@ #include +#ifndef PR_SET_SECCOMP +# define PR_GET_SECCOMP 21 +# define PR_SET_SECCOMP 22 +#endif + #ifndef PR_SET_CHILD_SUBREAPER # define PR_SET_CHILD_SUBREAPER 36 # define PR_GET_CHILD_SUBREAPER 37 diff --git a/include/lapi/seccomp.h b/include/lapi/seccomp.h new file mode 100644 index 000000000..a80d3d8cd --- /dev/null +++ b/include/lapi/seccomp.h @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2019 FUJITSU LIMITED. All rights reserved. + * Author: Yang Xu + */ +#ifndef LAPI_SECCOMP_H +#define LAPI_SECCOMP_H + +# include + +# ifdef HAVE_LINUX_SECCOMP_H +# include +# else +/* Valid values for seccomp.mode and prctl(PR_SET_SECCOMP, ) */ +# define SECCOMP_MODE_DISABLED 0 +# define SECCOMP_MODE_STRICT 1 +# define SECCOMP_MODE_FILTER 2 + +# define SECCOMP_RET_KILL_THREAD 0x00000000U /* kill the thread */ +# define SECCOMP_RET_KILL SECCOMP_RET_KILL_THREAD +# define SECCOMP_RET_ALLOW 0x7fff0000U /* allow */ + +/** + * struct seccomp_data - the format the BPF program executes over. + * @nr: the system call number + * @arch: indicates system call convention as an AUDIT_ARCH_* value + * as defined in . + * @instruction_pointer: at the time of the system call. + * @args: up to 6 system call arguments always stored as 64-bit values + * regardless of the architecture. + */ +struct seccomp_data { + int nr; + __u32 arch; + __u64 instruction_pointer; + __u64 args[6]; +}; + +# endif /* HAVE_LINUX_SECCOMP_H*/ +#endif /* LAPI_SECCOMP_H */ diff --git a/runtest/syscalls b/runtest/syscalls index 30c132751..04558a580 100644 --- a/runtest/syscalls +++ b/runtest/syscalls @@ -861,6 +861,7 @@ ppoll01 ppoll01 prctl01 prctl01 prctl02 prctl02 prctl03 prctl03 +prctl04 prctl04 pread01 pread01 pread01_64 pread01_64 diff --git a/testcases/kernel/syscalls/prctl/.gitignore b/testcases/kernel/syscalls/prctl/.gitignore index 2f46a9a12..1c3da3052 100644 --- a/testcases/kernel/syscalls/prctl/.gitignore +++ b/testcases/kernel/syscalls/prctl/.gitignore @@ -1,3 +1,4 @@ /prctl01 /prctl02 /prctl03 +/prctl04 diff --git a/testcases/kernel/syscalls/prctl/prctl04.c b/testcases/kernel/syscalls/prctl/prctl04.c new file mode 100644 index 000000000..585274a8a --- /dev/null +++ b/testcases/kernel/syscalls/prctl/prctl04.c @@ -0,0 +1,231 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2019 FUJITSU LIMITED. All rights reserved. + * Author: Yang Xu + * + * Test PR_GET_SECCOMP and PR_SET_SECCOMP of prctl(2). + * 1) If PR_SET_SECCOMP sets the SECCOMP_MODE_STRICT for the calling thread, + * the only system call that the thread is permitted to make are read(2), + * write(2),_exit(2)(but not exit_group(2)), and sigreturn(2). Other + * system calls result in the delivery of a SIGKILL signal. This operation + * is available only if the kernel is configured with CONFIG_SECCOMP enabled. + * 2) If PR_SET_SECCOMP sets the SECCOMP_MODE_FILTER for the calling thread, + * the system calls allowed are defined by a pointer to a Berkeley Packet + * Filter. Other system calls result int the delivery of a SIGSYS signal + * with SECCOMP_RET_KILL. The SECCOMP_SET_MODE_FILTER operation is available + * only if the kernel is configured with CONFIG_SECCOMP_FILTER enabled. + * 3) If SECCOMP_MODE_FILTER filters permit fork(2), then the seccomp mode + * is inherited by children created by fork(2). + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "tst_test.h" +#include "lapi/syscalls.h" +#include "lapi/prctl.h" +#include "config.h" +#include "lapi/seccomp.h" + +#define FNAME "filename" + +static const struct sock_filter strict_filter[] = { + BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (offsetof (struct seccomp_data, nr))), + + BPF_JUMP(BPF_JMP | BPF_JEQ, __NR_close, 5, 0), + BPF_JUMP(BPF_JMP | BPF_JEQ, __NR_exit, 4, 0), + BPF_JUMP(BPF_JMP | BPF_JEQ, __NR_wait4, 3, 0), + BPF_JUMP(BPF_JMP | BPF_JEQ, __NR_write, 2, 0), + BPF_JUMP(BPF_JMP | BPF_JEQ, __NR_clone, 1, 0), + + BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL), + BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW) +}; + +static const struct sock_fprog strict = { + .len = (unsigned short)ARRAY_SIZE(strict_filter), + .filter = (struct sock_filter *)strict_filter +}; + +static void check_strict_mode(int); +static void check_filter_mode(int); + +static struct tcase { + void (*func_check)(); + int pass_flag; + int val; + int exp_signal; + char *message; +} tcases[] = { + {check_strict_mode, 1, 1, SIGKILL, + "SECCOMP_MODE_STRICT doesn't permit GET_SECCOMP call"}, + + {check_strict_mode, 0, 2, SIGKILL, + "SECCOMP_MODE_STRICT doesn't permit read(2) write(2) and _exit(2)"}, + + {check_strict_mode, 1, 3, SIGKILL, + "SECCOMP_MODE_STRICT doesn't permit close(2)"}, + + {check_filter_mode, 1, 1, SIGSYS, + "SECCOMP_MODE_FILTER doestn't permit GET_SECCOMP call"}, + + {check_filter_mode, 0, 2, SIGSYS, + "SECCOMP_MODE_FILTER doesn't permit close(2)"}, + + {check_filter_mode, 2, 3, SIGSYS, + "SECCOMP_MODE_FILTER doesn't permit exit()"}, + + {check_filter_mode, 0, 4, SIGSYS, + "SECCOMP_MODE_FILTER doesn't permit exit()"} +}; + +static void check_filter_mode_inherit(void) +{ + int childpid; + int childstatus; + + childpid = SAFE_FORK(); + if (childpid == 0) { + tst_res(TPASS, "SECCOMP_MODE_FILTER permits fork(2)"); + exit(0); + } + + wait4(childpid, &childstatus, 0, NULL); + if (WIFSIGNALED(childstatus) && WTERMSIG(childstatus) == SIGSYS) + tst_res(TPASS, + "SECCOMP_MODE_FILTER has been inherited by child"); + else + tst_res(TFAIL, + "SECCOMP_MODE_FILTER isn't been inherited by child"); +} + +static void check_strict_mode(int val) +{ + int fd; + char buf[2]; + + fd = SAFE_OPEN(FNAME, O_RDWR | O_CREAT, 0666); + + TEST(prctl(PR_SET_SECCOMP, SECCOMP_MODE_STRICT)); + if (TST_RET == -1) { + tst_res(TFAIL | TTERRNO, + "prctl(PR_SET_SECCOMP) sets SECCOMP_MODE_STRICT failed"); + return; + } + + switch (val) { + case 1: + tst_res(TPASS, + "prctl(PR_SET_SECCOMP) sets SECCOMP_MODE_STRICT succeed"); + prctl(PR_GET_SECCOMP); + tst_res(TFAIL, "prctl(PR_GET_SECCOMP) succeed unexpectedly"); + break; + case 2: + SAFE_WRITE(1, fd, "a", 1); + SAFE_READ(0, fd, buf, 1); + tst_res(TPASS, + "SECCOMP_MODE_STRICT permits read(2) write(2) and _exit(2)"); + break; + case 3: + close(fd); + tst_res(TFAIL, + "SECCOMP_MODE_STRICT permits close(2) unexpectdly"); + break; + } + + tst_syscall(__NR_exit, 0); +} + +static void check_filter_mode(int val) +{ + int fd; + + fd = SAFE_OPEN(FNAME, O_RDWR | O_CREAT, 0666); + + TEST(prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &strict)); + if (TST_RET == -1) { + if (TST_ERR == EINVAL) + tst_res(TCONF, + "kernel doesn't support SECCOMP_MODE_FILTER"); + else + tst_res(TFAIL | TERRNO, + "prctl(PR_SET_SECCOMP) sets SECCOMP_MODE_FILTER failed"); + return; + } + + switch (val) { + case 1: + tst_res(TPASS, + "prctl(PR_SET_SECCOMP) sets SECCOMP_MODE_FILTER succeed"); + prctl(PR_GET_SECCOMP); + tst_res(TFAIL, "prctl(PR_GET_SECCOMP) succeed unexpectedly"); + break; + case 2: + close(fd); + tst_res(TPASS, "SECCOMP_MODE_FILTER permits close(2)"); + break; + case 3: + exit(0); + break; + case 4: + check_filter_mode_inherit(); + break; + } + + tst_syscall(__NR_exit, 0); +} + +static void verify_prctl(unsigned int n) +{ + int pid; + int status; + struct tcase *tc = &tcases[n]; + + pid = SAFE_FORK(); + if (pid == 0) { + tc->func_check(tc->val); + } else { + SAFE_WAITPID(pid, &status, 0); + if (WIFSIGNALED(status) && WTERMSIG(status) == tc->exp_signal) { + if (tc->pass_flag) + tst_res(TPASS, "%s", tc->message); + else + tst_res(TFAIL, "%s", tc->message); + return; + } + + if (tc->pass_flag == 2) + tst_res(TFAIL, + "SECCOMP_MODE_FILTER permits exit() unexpectedly"); + } +} + +static void setup(void) +{ + TEST(prctl(PR_GET_SECCOMP)); + if (TST_RET == 0) { + tst_res(TINFO, "kernel support PR_GET/SET_SECCOMP"); + return; + } + + if (TST_ERR == EINVAL) + tst_brk(TCONF, "kernel doesn't support PR_GET/SET_SECCOMP"); + + tst_brk(TBROK | TTERRNO, + "current environment doesn't permit PR_GET/SET_SECCOMP"); +} + +static struct tst_test test = { + .setup = setup, + .test = verify_prctl, + .tcnt = ARRAY_SIZE(tcases), + .forks_child = 1, + .needs_tmpdir = 1, + .needs_root = 1, +};