From owner-svn-src-projects@freebsd.org Tue Sep 3 14:05:48 2019 Return-Path: Delivered-To: svn-src-projects@mailman.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.nyi.freebsd.org (Postfix) with ESMTP id 2E3C8DBEC0 for ; Tue, 3 Sep 2019 14:05:48 +0000 (UTC) (envelope-from yuripv@freebsd.org) Received: from freefall.freebsd.org (freefall.freebsd.org [IPv6:2610:1c1:1:6074::16:84]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) server-signature RSA-PSS (4096 bits) client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "freefall.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 46N7yg688zz4NrH; Tue, 3 Sep 2019 14:05:47 +0000 (UTC) (envelope-from yuripv@freebsd.org) Received: by freefall.freebsd.org (Postfix, from userid 1452) id 5C3EC19B9B; Tue, 3 Sep 2019 14:05:47 +0000 (UTC) X-Original-To: yuripv@localmail.freebsd.org Delivered-To: yuripv@localmail.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (Client CN "mx1.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by freefall.freebsd.org (Postfix) with ESMTPS id D3F1C4B1F; Thu, 28 Mar 2019 01:12:48 +0000 (UTC) (envelope-from owner-src-committers@freebsd.org) Received: from freefall.freebsd.org (freefall.freebsd.org [96.47.72.132]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) server-signature RSA-PSS (4096 bits) client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "freefall.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4F30A72052; Thu, 28 Mar 2019 01:12:48 +0000 (UTC) (envelope-from owner-src-committers@freebsd.org) Received: by freefall.freebsd.org (Postfix, from userid 538) id 214DA4ADB; Thu, 28 Mar 2019 01:12:48 +0000 (UTC) Delivered-To: src-committers@localmail.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (Client CN "mx1.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by freefall.freebsd.org (Postfix) with ESMTPS id 9F26A4AD9 for ; Thu, 28 Mar 2019 01:12:45 +0000 (UTC) (envelope-from asomers@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) server-signature RSA-PSS (4096 bits) client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 477DE7204B; Thu, 28 Mar 2019 01:12:45 +0000 (UTC) (envelope-from asomers@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 1A8FDF948; Thu, 28 Mar 2019 01:12:45 +0000 (UTC) (envelope-from asomers@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id x2S1Ci4Z069610; Thu, 28 Mar 2019 01:12:44 GMT (envelope-from asomers@FreeBSD.org) Received: (from asomers@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id x2S1Ci3S069607; Thu, 28 Mar 2019 01:12:44 GMT (envelope-from asomers@FreeBSD.org) Message-Id: <201903280112.x2S1Ci3S069607@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: asomers set sender to asomers@FreeBSD.org using -f From: Alan Somers To: src-committers@freebsd.org, svn-src-projects@freebsd.org Subject: svn commit: r345624 - projects/fuse2/tests/sys/fs/fusefs X-SVN-Group: projects X-SVN-Commit-Author: asomers X-SVN-Commit-Paths: projects/fuse2/tests/sys/fs/fusefs X-SVN-Commit-Revision: 345624 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Precedence: bulk X-Loop: FreeBSD.org Sender: owner-src-committers@freebsd.org X-Rspamd-Queue-Id: 4F30A72052 X-Spamd-Bar: -- Authentication-Results: mx1.freebsd.org X-Spamd-Result: default: False [-2.97 / 15.00]; local_wl_from(0.00)[freebsd.org]; NEURAL_HAM_MEDIUM(-0.99)[-0.993,0]; NEURAL_HAM_SHORT(-0.98)[-0.975,0]; ASN(0.00)[asn:11403, ipnet:96.47.64.0/20, country:US]; NEURAL_HAM_LONG(-1.00)[-1.000,0] Status: O X-BeenThere: svn-src-projects@freebsd.org X-Mailman-Version: 2.1.29 List-Id: "SVN commit messages for the src " projects" tree" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Date: Tue, 03 Sep 2019 14:05:49 -0000 X-Original-Date: Thu, 28 Mar 2019 01:12:44 +0000 (UTC) X-List-Received-Date: Tue, 03 Sep 2019 14:05:49 -0000 Author: asomers Date: Thu Mar 28 01:12:44 2019 New Revision: 345624 URL: https://svnweb.freebsd.org/changeset/base/345624 Log: fusefs: deduplicate code in the allow_other test Sponsored by: The FreeBSD Foundation Modified: projects/fuse2/tests/sys/fs/fusefs/allow_other.cc projects/fuse2/tests/sys/fs/fusefs/utils.cc projects/fuse2/tests/sys/fs/fusefs/utils.hh Modified: projects/fuse2/tests/sys/fs/fusefs/allow_other.cc ============================================================================== --- projects/fuse2/tests/sys/fs/fusefs/allow_other.cc Thu Mar 28 00:25:57 2019 (r345623) +++ projects/fuse2/tests/sys/fs/fusefs/allow_other.cc Thu Mar 28 01:12:44 2019 (r345624) @@ -35,11 +35,8 @@ extern "C" { #include -#include -#include #include -#include -#include +#include } #include "mockfs.hh" @@ -47,27 +44,9 @@ extern "C" { using namespace testing; -void sighandler(int __unused sig) {} +const static char FULLPATH[] = "mountpoint/some_file.txt"; +const static char RELPATH[] = "some_file.txt"; -static void -get_unprivileged_uid(int *uid) -{ - struct passwd *pw; - - /* - * First try "tests", Kyua's default unprivileged user. XXX after - * GoogleTest gains a proper Kyua wrapper, get this with the Kyua API - */ - pw = getpwnam("tests"); - if (pw == NULL) { - /* Fall back to "nobody" */ - pw = getpwnam("nobody"); - } - if (pw == NULL) - GTEST_SKIP() << "Test requires an unprivileged user"; - *uid = pw->pw_uid; -} - class NoAllowOther: public FuseTest { public: @@ -78,9 +57,6 @@ virtual void SetUp() { if (geteuid() != 0) { GTEST_SKIP() << "This test must be run as root"; } - get_unprivileged_uid(&m_uid); - if (IsSkipped()) - return; FuseTest::SetUp(); } @@ -97,119 +73,42 @@ virtual void SetUp() { TEST_F(AllowOther, allowed) { - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - uint64_t ino = 42; - int fd; - pid_t child; - sem_t *sem; - int mprot = PROT_READ | PROT_WRITE; - int mflags = MAP_ANON | MAP_SHARED; - - sem = (sem_t*)mmap(NULL, sizeof(*sem), mprot, mflags, -1, 0); - ASSERT_NE(MAP_FAILED, sem) << strerror(errno); - ASSERT_EQ(0, sem_init(sem, 1, 0)) << strerror(errno); + fork(true, [&] { + uint64_t ino = 42; - if ((child = fork()) == 0) { - /* In child */ - int err = 0; + expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); + expect_open(ino, 0, 1); + expect_release(ino); + expect_getattr(ino, 0); + }, []() { + int fd; - ASSERT_EQ(0, sem_wait(sem)) << strerror(errno); - - /* Drop privileges before accessing */ - if (0 != setreuid(-1, m_uid)) { - perror("setreuid"); - err = 1; - goto out; + fd = open(FULLPATH, O_RDONLY); + if (fd < 0) { + perror("open"); + return(1); + } + return 0; } - fd = open(FULLPATH, O_RDONLY); - if (fd < 0) { - perror("open"); - err = 1; - } - -out: - sem_destroy(sem); - _exit(err); - /* Deliberately leak fd */ - } else if (child > 0) { - int child_status; - - /* - * In parent. Cleanup must happen here, because it's still - * privileged. - */ - expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); - expect_open(ino, 0, 1); - expect_release(ino); - /* Until the attr cache is working, we may send an additional - * GETATTR */ - expect_getattr(ino, 0); - m_mock->m_child_pid = child; - /* Signal the child process to go */ - ASSERT_EQ(0, sem_post(sem)) << strerror(errno); - - wait(&child_status); - ASSERT_EQ(0, WEXITSTATUS(child_status)); - } else { - FAIL() << strerror(errno); - } - munmap(sem, sizeof(*sem)); + ); } TEST_F(NoAllowOther, disallowed) { - const char FULLPATH[] = "mountpoint/some_file.txt"; - int fd; - pid_t child; - sem_t *sem; - int mprot = PROT_READ | PROT_WRITE; - int mflags = MAP_ANON | MAP_SHARED; + fork(true, [] { + }, []() { + int fd; - sem = (sem_t*)mmap(NULL, sizeof(*sem), mprot, mflags, -1, 0); - ASSERT_NE(MAP_FAILED, sem) << strerror(errno); - ASSERT_EQ(0, sem_init(sem, 1, 0)) << strerror(errno); - - if ((child = fork()) == 0) { - /* In child */ - int err = 0; - - ASSERT_EQ(0, sem_wait(sem)) << strerror(errno); - - /* Drop privileges before accessing */ - if (0 != setreuid(-1, m_uid)) { - perror("setreuid"); - err = 1; - goto out; + fd = open(FULLPATH, O_RDONLY); + if (fd >= 0) { + fprintf(stderr, "open should've failed\n"); + return(1); + } else if (errno != EPERM) { + fprintf(stderr, "Unexpected error: %s\n", + strerror(errno)); + return(1); + } + return 0; } - fd = open(FULLPATH, O_RDONLY); - if (fd >= 0) { - fprintf(stderr, "open should've failed\n"); - err = 1; - } else if (errno != EPERM) { - fprintf(stderr, - "Unexpected error: %s\n", strerror(errno)); - err = 1; - } - -out: - sem_destroy(sem); - _exit(0); - /* Deliberately leak fd */ - } else if (child > 0) { - /* - * In parent. Cleanup must happen here, because it's still - * privileged. - */ - m_mock->m_child_pid = child; - /* Signal the child process to go */ - ASSERT_EQ(0, sem_post(sem)) << strerror(errno); - int child_status; - - wait(&child_status); - ASSERT_EQ(0, WEXITSTATUS(child_status)); - } else { - FAIL() << strerror(errno); - } - munmap(sem, sizeof(*sem)); + ); } Modified: projects/fuse2/tests/sys/fs/fusefs/utils.cc ============================================================================== --- projects/fuse2/tests/sys/fs/fusefs/utils.cc Thu Mar 28 00:25:57 2019 (r345623) +++ projects/fuse2/tests/sys/fs/fusefs/utils.cc Thu Mar 28 01:12:44 2019 (r345624) @@ -28,13 +28,20 @@ * SUCH DAMAGE. */ +extern "C" { #include +#include #include #include +#include -#include +#include +#include #include +} +#include + #include "mockfs.hh" #include "utils.hh" @@ -239,6 +246,86 @@ void FuseTest::expect_write(uint64_t ino, uint64_t off SET_OUT_HEADER_LEN(out, write); out->body.write.size = osize; }))); +} + +static void +get_unprivileged_uid(uid_t *uid) +{ + struct passwd *pw; + + /* + * First try "tests", Kyua's default unprivileged user. XXX after + * GoogleTest gains a proper Kyua wrapper, get this with the Kyua API + */ + pw = getpwnam("tests"); + if (pw == NULL) { + /* Fall back to "nobody" */ + pw = getpwnam("nobody"); + } + if (pw == NULL) + GTEST_SKIP() << "Test requires an unprivileged user"; + *uid = pw->pw_uid; +} + +void +FuseTest::fork(bool drop_privs, std::function parent_func, + std::function child_func) +{ + sem_t *sem; + int mprot = PROT_READ | PROT_WRITE; + int mflags = MAP_ANON | MAP_SHARED; + pid_t child; + uid_t uid; + + if (drop_privs) { + get_unprivileged_uid(&uid); + if (IsSkipped()) + return; + } + + sem = (sem_t*)mmap(NULL, sizeof(*sem), mprot, mflags, -1, 0); + ASSERT_NE(MAP_FAILED, sem) << strerror(errno); + ASSERT_EQ(0, sem_init(sem, 1, 0)) << strerror(errno); + + if ((child = ::fork()) == 0) { + /* In child */ + int err = 0; + + if (sem_wait(sem)) { + perror("sem_wait"); + err = 1; + goto out; + } + + if (drop_privs && 0 != setreuid(-1, uid)) { + perror("setreuid"); + err = 1; + goto out; + } + err = child_func(); + +out: + sem_destroy(sem); + _exit(err); + } else if (child > 0) { + int child_status; + + /* + * In parent. Cleanup must happen here, because it's still + * privileged. + */ + m_mock->m_child_pid = child; + ASSERT_NO_FATAL_FAILURE(parent_func()); + + /* Signal the child process to go */ + ASSERT_EQ(0, sem_post(sem)) << strerror(errno); + + wait(&child_status); + ASSERT_EQ(0, WEXITSTATUS(child_status)); + } else { + FAIL() << strerror(errno); + } + munmap(sem, sizeof(*sem)); } static void usage(char* progname) { Modified: projects/fuse2/tests/sys/fs/fusefs/utils.hh ============================================================================== --- projects/fuse2/tests/sys/fs/fusefs/utils.hh Thu Mar 28 00:25:57 2019 (r345623) +++ projects/fuse2/tests/sys/fs/fusefs/utils.hh Thu Mar 28 01:12:44 2019 (r345624) @@ -124,4 +124,22 @@ class FuseTest : public ::testing::Test { */ void expect_write(uint64_t ino, uint64_t offset, uint64_t isize, uint64_t osize, uint32_t flags, const void *contents); + + /* + * Helper that runs code in a child process. + * + * First, parent_func runs in the parent process. + * Then, child_func runs in the child process, dropping privileges if + * desired. + * Finally, fusetest_fork returns. + * + * # Returns + * + * fusetest_fork will FAIL the test if child_func returns nonzero. + * It may SKIP the test, which the caller should detect with the + * IsSkipped() method. + */ + void fork(bool drop_privs, + std::function parent_func, + std::function child_func); };