From patchwork Thu Feb 11 17:45:43 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Richard Palethorpe X-Patchwork-Id: 1439596 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) 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; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=suse.com header.i=@suse.com header.a=rsa-sha256 header.s=susede1 header.b=fWp9sB9c; dkim-atps=neutral Received: from picard.linux.it (picard.linux.it [213.254.12.146]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4Dc3w96Gpbz9rx8 for ; Fri, 12 Feb 2021 04:46:33 +1100 (AEDT) Received: from picard.linux.it (localhost [IPv6:::1]) by picard.linux.it (Postfix) with ESMTP id 448793C6903 for ; Thu, 11 Feb 2021 18:46:31 +0100 (CET) X-Original-To: ltp@lists.linux.it Delivered-To: ltp@picard.linux.it Received: from in-5.smtp.seeweb.it (in-5.smtp.seeweb.it [IPv6:2001:4b78:1:20::5]) by picard.linux.it (Postfix) with ESMTP id A69F63C5E23 for ; Thu, 11 Feb 2021 18:46:03 +0100 (CET) Received: from mx2.suse.de (mx2.suse.de [195.135.220.15]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by in-5.smtp.seeweb.it (Postfix) with ESMTPS id 4BEFB600C77 for ; Thu, 11 Feb 2021 18:46:03 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.com; s=susede1; t=1613065563; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=xJosqfT/7+4oOfYRNqDZEtXZu9un0l11DYUBPjb+Smo=; b=fWp9sB9ciaCzz6dmaEPC79gUQFq9YUp2UawA98mef7xT4oMNOnyDnWLUhR6whL7HkY0lXX RXXNDnxvP585wtyJww9LeYWfORGKccvEUTkYk00ceze3E6ODl1/DfXGNSm8e3B1kxIKTu4 +tU6KqSXsVfyRLrNZcYhYPmZJgnw9TQ= Received: from relay2.suse.de (unknown [195.135.221.27]) by mx2.suse.de (Postfix) with ESMTP id E530FB130; Thu, 11 Feb 2021 17:46:02 +0000 (UTC) To: ltp@lists.linux.it Date: Thu, 11 Feb 2021 17:45:43 +0000 Message-Id: <20210211174543.25003-6-rpalethorpe@suse.com> X-Mailer: git-send-email 2.30.0 In-Reply-To: <20210211174543.25003-1-rpalethorpe@suse.com> References: <20210211174543.25003-1-rpalethorpe@suse.com> MIME-Version: 1.0 X-Virus-Scanned: clamav-milter 0.102.4 at in-5.smtp.seeweb.it X-Virus-Status: Clean X-Spam-Status: No, score=0.1 required=7.0 tests=DKIM_SIGNED,DKIM_VALID, DKIM_VALID_AU,DKIM_VALID_EF,SPF_HELO_NONE,SPF_PASS autolearn=disabled version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on in-5.smtp.seeweb.it Subject: [LTP] [PATCH v2 5/5] close_range: Add test X-BeenThere: ltp@lists.linux.it X-Mailman-Version: 2.1.29 Precedence: list List-Id: Linux Test Project List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Richard Palethorpe via ltp From: Richard Palethorpe Reply-To: Richard Palethorpe Cc: Richard Palethorpe Errors-To: ltp-bounces+incoming=patchwork.ozlabs.org@lists.linux.it Sender: "ltp" Signed-off-by: Richard Palethorpe --- .../kernel/syscalls/close_range/.gitignore | 1 + .../kernel/syscalls/close_range/Makefile | 10 + .../syscalls/close_range/close_range01.c | 200 ++++++++++++++++++ 3 files changed, 211 insertions(+) create mode 100644 testcases/kernel/syscalls/close_range/.gitignore create mode 100644 testcases/kernel/syscalls/close_range/Makefile create mode 100644 testcases/kernel/syscalls/close_range/close_range01.c diff --git a/testcases/kernel/syscalls/close_range/.gitignore b/testcases/kernel/syscalls/close_range/.gitignore new file mode 100644 index 000000000..291a0379c --- /dev/null +++ b/testcases/kernel/syscalls/close_range/.gitignore @@ -0,0 +1 @@ +close_range01 \ No newline at end of file diff --git a/testcases/kernel/syscalls/close_range/Makefile b/testcases/kernel/syscalls/close_range/Makefile new file mode 100644 index 000000000..dc6413b10 --- /dev/null +++ b/testcases/kernel/syscalls/close_range/Makefile @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (c) 2019-2021 Linux Test Project + +top_srcdir ?= ../../../.. + +include $(top_srcdir)/include/mk/testcases.mk + +CFLAGS += -D_GNU_SOURCE + +include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/testcases/kernel/syscalls/close_range/close_range01.c b/testcases/kernel/syscalls/close_range/close_range01.c new file mode 100644 index 000000000..ed3f6b416 --- /dev/null +++ b/testcases/kernel/syscalls/close_range/close_range01.c @@ -0,0 +1,200 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Taken from the kernel self tests, which in turn were based on + * a Syzkaller reproducer. + * + * Self test author and close_range author: + * Christian Brauner + * + * LTP Author: Richard Palethorpe + * Copyright (c) 2021 SUSE LLC, other copyrights may apply. + * + * - First we test if close_range closes some FDs. + * - Then we test if it UNSHARES some FDs before closing them. + * - Then we test if it sets CLOEXEC (in both cloned process and parent). + * - Finally we test a combination of CLOEXEC and UNSHARE. + * + * The final test is the actual bug reproducer. Note that we call + * clone directly to share the file table. + */ + +#include + +#include "tst_test.h" +#include "tst_clone.h" + +#include "lapi/clone.h" +#include "lapi/close_range.h" + +static int fd[3]; + +static inline void do_close_range(unsigned int fd, unsigned int max_fd, + unsigned int flags) +{ + int ret = close_range(fd, max_fd, flags); + + if (!ret) + return; + + if (errno == EINVAL) { + if (flags & CLOSE_RANGE_UNSHARE) + tst_brk(TCONF | TERRNO, "No CLOSE_RANGE_UNSHARE"); + if (flags & CLOSE_RANGE_CLOEXEC) + tst_brk(TCONF | TERRNO, "No CLOSE_RANGE_CLOEXEC"); + } + + tst_brk(TBROK | TERRNO, "close_range(%d, %d, %d)", fd, max_fd, flags); +} + +static void setup(void) +{ + struct rlimit nfd; + + SAFE_GETRLIMIT(RLIMIT_NOFILE, &nfd); + + if (nfd.rlim_max < 1000) { + tst_brk(TCONF, "NOFILE limit max too low: %lu < 1000", + nfd.rlim_max); + } + + nfd.rlim_cur = nfd.rlim_max; + SAFE_SETRLIMIT(RLIMIT_NOFILE, &nfd); +} + +static void check_cloexec(int i, int expected) +{ + int present = SAFE_FCNTL(fd[i], F_GETFD) & FD_CLOEXEC; + + if (expected && !present) + tst_res(TFAIL, "fd[%d] flags do not contain FD_CLOEXEC", i); + + if (!expected && present) + tst_res(TFAIL, "fd[%d] flags contain FD_CLOEXEC", i); +} + +static void check_closed(int min) +{ + int i; + + for (i = min; i < 3; i++) { + if (fcntl(fd[i], F_GETFD) > -1) + tst_res(TFAIL, "fd[%d] is still open", i); + } +} + +static void child(unsigned int n) +{ + switch (n) { + case 0: + SAFE_DUP2(fd[1], fd[2]); + do_close_range(3, ~0U, 0); + check_closed(0); + break; + case 1: + SAFE_DUP2(fd[1], fd[2]); + do_close_range(3, ~0U, CLOSE_RANGE_UNSHARE); + check_closed(0); + break; + case 2: + do_close_range(3, ~0U, CLOSE_RANGE_CLOEXEC); + check_cloexec(0, 1); + check_cloexec(1, 1); + + SAFE_DUP2(fd[1], fd[2]); + check_cloexec(2, 0); + break; + case 3: + do_close_range(3, ~0U, + CLOSE_RANGE_CLOEXEC | CLOSE_RANGE_UNSHARE); + check_cloexec(0, 1); + check_cloexec(1, 1); + + SAFE_DUP2(fd[1], fd[2]); + check_cloexec(2, 0); + break; + } + + exit(0); +} + +static void run(unsigned int n) +{ + const struct tst_clone_args args = { + .flags = CLONE_FILES, + .exit_signal = SIGCHLD, + }; + + switch (n) { + case 0: + tst_res(TINFO, "Plain close range"); + do_close_range(3, ~0U, 0); + break; + case 1: + tst_res(TINFO, "Set UNSHARE and close range"); + do_close_range(3, ~0U, CLOSE_RANGE_UNSHARE); + break; + case 2: + tst_res(TINFO, "Set CLOEXEC on range"); + do_close_range(3, ~0U, CLOSE_RANGE_CLOEXEC); + break; + case 3: + tst_res(TINFO, "Set UNSHARE and CLOEXEC on range"); + do_close_range(3, ~0U, + CLOSE_RANGE_CLOEXEC | CLOSE_RANGE_UNSHARE); + break; + } + + fd[0] = SAFE_OPEN("mnt/tmpfile", O_RDWR | O_CREAT, 0644); + fd[1] = SAFE_DUP2(fd[0], 1000); + fd[2] = 42; + + if (!SAFE_CLONE(&args)) + child(n); + + tst_reap_children(); + + switch (n) { + case 0: + check_closed(0); + break; + case 1: + check_cloexec(0, 0); + check_cloexec(1, 0); + check_cloexec(2, 0); + break; + case 2: + check_cloexec(0, 1); + check_cloexec(1, 1); + check_cloexec(2, 0); + break; + case 3: + check_cloexec(0, 0); + check_cloexec(1, 0); + check_closed(2); + break; + } + + do_close_range(3, ~0U, 0); + check_closed(0); + + if (tst_taint_check()) + tst_res(TFAIL, "Kernel tainted"); + else + tst_res(TPASS, "No kernel taints"); +} + +static struct tst_test test = { + .tcnt = 4, + .needs_tmpdir = 1, + .forks_child = 1, + .mount_device = 1, + .mntpoint = "mnt", + .all_filesystems = 1, + .test = run, + .taint_check = TST_TAINT_W | TST_TAINT_D, + .setup = setup, + .tags = (const struct tst_tag[]) { + {"linux-git", "fec8a6a691033f2538cd46848f17f337f0739923"}, + {}, + }, +};