From owner-svn-src-all@freebsd.org Tue Mar 12 01:43:04 2019 Return-Path: Delivered-To: svn-src-all@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id 93BA615394DA; Tue, 12 Mar 2019 01:43:04 +0000 (UTC) (envelope-from ngie@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 B5287685F6; Tue, 12 Mar 2019 01:43:03 +0000 (UTC) (envelope-from ngie@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 A6BDA1B917; Tue, 12 Mar 2019 01:43:03 +0000 (UTC) (envelope-from ngie@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id x2C1h3W6072847; Tue, 12 Mar 2019 01:43:03 GMT (envelope-from ngie@FreeBSD.org) Received: (from ngie@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id x2C1h2lm072839; Tue, 12 Mar 2019 01:43:02 GMT (envelope-from ngie@FreeBSD.org) Message-Id: <201903120143.x2C1h2lm072839@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: ngie set sender to ngie@FreeBSD.org using -f From: Enji Cooper Date: Tue, 12 Mar 2019 01:43:02 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-vendor@freebsd.org Subject: svn commit: r345046 - in vendor/google/capsicum-test: . dist X-SVN-Group: vendor X-SVN-Commit-Author: ngie X-SVN-Commit-Paths: in vendor/google/capsicum-test: . dist X-SVN-Commit-Revision: 345046 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Rspamd-Queue-Id: B5287685F6 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(-1.00)[-0.996,0]; NEURAL_HAM_SHORT(-0.98)[-0.977,0]; NEURAL_HAM_LONG(-1.00)[-1.000,0]; ASN(0.00)[asn:11403, ipnet:2610:1c1:1::/48, country:US] X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 12 Mar 2019 01:43:05 -0000 Author: ngie Date: Tue Mar 12 01:43:01 2019 New Revision: 345046 URL: https://svnweb.freebsd.org/changeset/base/345046 Log: Import capsicum-test into ^/vendor/google/capsicum-test/dist The following change imports google/capsicum-test@9333154 from GitHub, omitting the embedded version of googletest, as well as the incomplete libcasper. This test suite helps verify capsicum(3) support via functional tests written in the GoogleTest test framework. Kernel support for capsicum(4) is tested by side-effect of testing capsicum(3). NB: as discussed in a previous [closed] PR [1], the casper(3) tests are incomplete/buggy and will not pass on FreeBSD. Thus, I have no intention of integrating them into the build/test on FreeBSD as-is. The import command used was: ``` curl -L https://github.com/google/capsicum-test/tarball/9333154 | tar --strip-components=1 -xvzf - -C dist/ rm -Rf dist/*/ ``` 1. https://github.com/google/capsicum-test/pull/26 Reviewed by: emaste (mentor) Differential Revision: https://reviews.freebsd.org/D19261 Added: vendor/google/capsicum-test/ vendor/google/capsicum-test/dist/ vendor/google/capsicum-test/dist/.gitignore vendor/google/capsicum-test/dist/CONTRIBUTING.md vendor/google/capsicum-test/dist/GNUmakefile vendor/google/capsicum-test/dist/LICENSE vendor/google/capsicum-test/dist/README.md vendor/google/capsicum-test/dist/capability-fd-pair.cc (contents, props changed) vendor/google/capsicum-test/dist/capability-fd.cc (contents, props changed) vendor/google/capsicum-test/dist/capmode.cc (contents, props changed) vendor/google/capsicum-test/dist/capsicum-freebsd.h (contents, props changed) vendor/google/capsicum-test/dist/capsicum-linux.h (contents, props changed) vendor/google/capsicum-test/dist/capsicum-rights.h (contents, props changed) vendor/google/capsicum-test/dist/capsicum-test-main.cc (contents, props changed) vendor/google/capsicum-test/dist/capsicum-test.cc (contents, props changed) vendor/google/capsicum-test/dist/capsicum-test.h (contents, props changed) vendor/google/capsicum-test/dist/capsicum.h (contents, props changed) vendor/google/capsicum-test/dist/fcntl.cc (contents, props changed) vendor/google/capsicum-test/dist/fexecve.cc (contents, props changed) vendor/google/capsicum-test/dist/ioctl.cc (contents, props changed) vendor/google/capsicum-test/dist/linux.cc (contents, props changed) vendor/google/capsicum-test/dist/makefile (contents, props changed) vendor/google/capsicum-test/dist/mini-me.c (contents, props changed) vendor/google/capsicum-test/dist/mqueue.cc (contents, props changed) vendor/google/capsicum-test/dist/openat.cc (contents, props changed) vendor/google/capsicum-test/dist/overhead.cc (contents, props changed) vendor/google/capsicum-test/dist/procdesc.cc (contents, props changed) vendor/google/capsicum-test/dist/rename.cc (contents, props changed) vendor/google/capsicum-test/dist/sctp.cc (contents, props changed) vendor/google/capsicum-test/dist/select.cc (contents, props changed) vendor/google/capsicum-test/dist/showrights (contents, props changed) vendor/google/capsicum-test/dist/smoketest.c (contents, props changed) vendor/google/capsicum-test/dist/socket.cc (contents, props changed) vendor/google/capsicum-test/dist/syscalls.h (contents, props changed) vendor/google/capsicum-test/dist/sysctl.cc (contents, props changed) vendor/google/capsicum-test/dist/waittest.c (contents, props changed) Added: vendor/google/capsicum-test/dist/.gitignore ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ vendor/google/capsicum-test/dist/.gitignore Tue Mar 12 01:43:01 2019 (r345046) @@ -0,0 +1,19 @@ +capsicum-test +mini-me +mini-me.noexec +mini-me.setuid +mini-me.32 +mini-me.x32 +mini-me.64 +libgtest.a +smoketest +*.o +libcap*.deb +libcap*.dsc +libcap*.tar.gz +libcap*.changes +casper*.deb +casper*.dsc +casper*.tar.gz +casper*.changes +libcaprights.a \ No newline at end of file Added: vendor/google/capsicum-test/dist/CONTRIBUTING.md ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ vendor/google/capsicum-test/dist/CONTRIBUTING.md Tue Mar 12 01:43:01 2019 (r345046) @@ -0,0 +1,20 @@ +## Contributor License Agreement ## + +Contributions to any Google project must be accompanied by a Contributor +License Agreement. This is not a copyright **assignment**, it simply gives +Google permission to use and redistribute your contributions as part of the +project. + + * If you are an individual writing original source code and you're sure you + own the intellectual property, then you'll need to sign an [individual + CLA][]. + + * If you work for a company that wants to allow you to contribute your work, + then you'll need to sign a [corporate CLA][]. + +You generally only need to submit a CLA once, so if you've already submitted +one (even if it was for a different project), you probably don't need to do it +again. + +[individual CLA]: https://developers.google.com/open-source/cla/individual +[corporate CLA]: https://developers.google.com/open-source/cla/corporate Added: vendor/google/capsicum-test/dist/GNUmakefile ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ vendor/google/capsicum-test/dist/GNUmakefile Tue Mar 12 01:43:01 2019 (r345046) @@ -0,0 +1,78 @@ +OS:=$(shell uname) + +# Set ARCH to 32 or x32 for i386/x32 ABIs +ARCH?=64 +ARCHFLAG=-m$(ARCH) + +ifeq ($(OS),Linux) +PROCESSOR:=$(shell uname -p) + +ifneq ($(wildcard /usr/lib/$(PROCESSOR)-linux-gnu),) +# Can use standard Debian location for static libraries. +PLATFORM_LIBDIR=/usr/lib/$(PROCESSOR)-linux-gnu +else +# Attempt to determine library location from gcc configuration. +PLATFORM_LIBDIR=$(shell gcc -v 2>&1 | grep "Configured with:" | sed 's/.*--libdir=\(\/usr\/[^ ]*\).*/\1/g') +endif + +# Override for explicitly specified ARCHFLAG. +# Use locally compiled libcaprights in this case, on the +# assumption that any installed version is 64-bit. +ifeq ($(ARCHFLAG),-m32) +PROCESSOR=i386 +PLATFORM_LIBDIR=/usr/lib32 +LIBCAPRIGHTS=./libcaprights.a +endif +ifeq ($(ARCHFLAG),-mx32) +PROCESSOR=x32 +PLATFORM_LIBDIR=/usr/libx32 +LIBCAPRIGHTS=./libcaprights.a +endif + +# Detect presence of libsctp in normal Debian location +ifneq ($(wildcard $(PLATFORM_LIBDIR)/libsctp.a),) +LIBSCTP=-lsctp +CXXFLAGS=-DHAVE_SCTP +endif + +ifneq ($(LIBCAPRIGHTS),) +# Build local libcaprights.a (assuming ./configure +# has already been done in libcaprights/) +LOCAL_LIBS=$(LIBCAPRIGHTS) +LIBCAPRIGHTS_OBJS=libcaprights/capsicum.o libcaprights/linux-bpf-capmode.o libcaprights/procdesc.o libcaprights/signal.o +LOCAL_CLEAN=$(LOCAL_LIBS) $(LIBCAPRIGHTS_OBJS) +else +# Detect installed libcaprights static library. +ifneq ($(wildcard $(PLATFORM_LIBDIR)/libcaprights.a),) +LIBCAPRIGHTS=$(PLATFORM_LIBDIR)/libcaprights.a +else +ifneq ($(wildcard /usr/lib/libcaprights.a),) +LIBCAPRIGHTS=/usr/lib/libcaprights.a +endif +endif +endif + +endif + +# Extra test programs for arch-transition tests +EXTRA_PROGS = mini-me.32 mini-me.64 +ifneq ($(wildcard /usr/include/gnu/stubs-x32.h),) +EXTRA_PROGS += mini-me.x32 +endif + +# Chain on to the master makefile +include makefile + +./libcaprights.a: $(LIBCAPRIGHTS_OBJS) + ar cr $@ $^ + +# Small static programs of known architectures +# These may require additional packages to be installed; for example, for Debian: +# - libc6-dev-i386 provides 32-bit headers for a 64-bit system +# - libc6-dev-x32 provides headers for the x32 ABI. +mini-me.32: mini-me.c + $(CC) $(CFLAGS) -m32 -static -o $@ $< +mini-me.x32: mini-me.c + $(CC) $(CFLAGS) -mx32 -static -o $@ $< +mini-me.64: mini-me.c + $(CC) $(CFLAGS) -m64 -static -o $@ $< Added: vendor/google/capsicum-test/dist/LICENSE ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ vendor/google/capsicum-test/dist/LICENSE Tue Mar 12 01:43:01 2019 (r345046) @@ -0,0 +1,26 @@ +Copyright (c) 2009-2011 Robert N. M. Watson +Copyright (c) 2011 Jonathan Anderson +Copyright (C) 2012 The Chromium OS Authors +Copyright (c) 2013-2014 Google Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. Added: vendor/google/capsicum-test/dist/README.md ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ vendor/google/capsicum-test/dist/README.md Tue Mar 12 01:43:01 2019 (r345046) @@ -0,0 +1,62 @@ +# Capsicum User Space Tests + +This directory holds unit tests for [Capsicum](http://www.cl.cam.ac.uk/research/security/capsicum/) +object-capabilities. The tests exercise the syscall interface to a Capsicum-enabled operating system, +currently either [FreeBSD >=10.x](http://www.freebsd.org) or a modified Linux kernel (the +[capsicum-linux](http://github.com/google/capsicum-linux) project). + +The tests are written in C++98, and use the [Google Test](https://code.google.com/p/googletest/) +framework, with some additions to fork off particular tests (because a process that enters capability +mode cannot leave it again). + +## Provenance + +The original basis for these tests was: + + - [unit tests](https://github.com/freebsd/freebsd/tree/master/tools/regression/security/cap_test) + written by Robert Watson and Jonathan Anderson for the original FreeBSD 9.x Capsicum implementation + - [unit tests](http://git.chromium.org/gitweb/?p=chromiumos/third_party/kernel-capsicum.git;a=tree;f=tools/testing/capsicum_tests;hb=refs/heads/capsicum) written by Meredydd Luff for the original Capsicum-Linux port. + +These tests were coalesced and moved into an independent repository to enable +comparative testing across multiple OSes, and then substantially extended. + +## OS Configuration + +### Linux + +The following kernel configuration options are needed to run the tests: + + - `CONFIG_SECURITY_CAPSICUM`: enable the Capsicum framework + - `CONFIG_PROCDESC`: enable Capsicum process-descriptor functionality + - `CONFIG_DEBUG_FS`: enable debug filesystem + - `CONFIG_IP_SCTP`: enable SCTP support + +### FreeBSD (>= 10.x) + +The following kernel configuration options are needed so that all tests can run: + + - `options P1003_1B_MQUEUE`: Enable POSIX message queues (or `kldload mqueuefs`) + +## Other Dependencies + +### Linux + +The following additional development packages are needed to build the full test suite on Linux. + + - `libcaprights`: See below + - `libcap-dev`: Provides headers for POSIX.1e capabilities. + - `libsctp1`: Provides SCTP library functions. + - `libsctp-dev`: Provides headers for SCTP library functions. + + +## Linux libcaprights + +The Capsicum userspace library is held in the `libcaprights/` subdirectory. Ideally, this +library should be built (with `./configure; make` or `dpkg-buildpackage -uc -us`) and +installed (with `make install` or `dpkg -i libcaprights*.deb`) so that the tests will +use behave like a normal Capsicum-aware application. + +However, if no installed copy of the library is found, the `GNUmakefile` will attempt +to use the local `libcaprights/*.c` source; this requires `./configure` to have been +performed in the `libcaprights` subdirectory. The local code is also used for +cross-compiled builds of the test suite (e.g. `make ARCH=32` or `make ARCH=x32`). Added: vendor/google/capsicum-test/dist/capability-fd-pair.cc ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ vendor/google/capsicum-test/dist/capability-fd-pair.cc Tue Mar 12 01:43:01 2019 (r345046) @@ -0,0 +1,188 @@ +// Tests involving 2 capability file descriptors. +#include +#include +#include + +#include "capsicum.h" +#include "syscalls.h" +#include "capsicum-test.h" + +TEST(CapabilityPair, sendfile) { + int in_fd = open(TmpFile("cap_sendfile_in"), O_CREAT|O_RDWR, 0644); + EXPECT_OK(write(in_fd, "1234", 4)); + // Output fd for sendfile must be a stream socket in FreeBSD. + int sock_fds[2]; + EXPECT_OK(socketpair(AF_UNIX, SOCK_STREAM, 0, sock_fds)); + + cap_rights_t r_rs; + cap_rights_init(&r_rs, CAP_READ, CAP_SEEK); + cap_rights_t r_ws; + cap_rights_init(&r_ws, CAP_WRITE, CAP_SEEK); + + int cap_in_ro = dup(in_fd); + EXPECT_OK(cap_in_ro); + EXPECT_OK(cap_rights_limit(cap_in_ro, &r_rs)); + int cap_in_wo = dup(in_fd); + EXPECT_OK(cap_in_wo); + EXPECT_OK(cap_rights_limit(cap_in_wo, &r_ws)); + int cap_out_ro = dup(sock_fds[0]); + EXPECT_OK(cap_out_ro); + EXPECT_OK(cap_rights_limit(cap_out_ro, &r_rs)); + int cap_out_wo = dup(sock_fds[0]); + EXPECT_OK(cap_out_wo); + EXPECT_OK(cap_rights_limit(cap_out_wo, &r_ws)); + + off_t offset = 0; + EXPECT_NOTCAPABLE(sendfile_(cap_out_ro, cap_in_ro, &offset, 4)); + EXPECT_NOTCAPABLE(sendfile_(cap_out_wo, cap_in_wo, &offset, 4)); + EXPECT_OK(sendfile_(cap_out_wo, cap_in_ro, &offset, 4)); + + close(cap_in_ro); + close(cap_in_wo); + close(cap_out_ro); + close(cap_out_wo); + close(in_fd); + close(sock_fds[0]); + close(sock_fds[1]); + unlink(TmpFile("cap_sendfile_in")); +} + +#ifdef HAVE_TEE +TEST(CapabilityPair, tee) { + int pipe1_fds[2]; + EXPECT_OK(pipe2(pipe1_fds, O_NONBLOCK)); + int pipe2_fds[2]; + EXPECT_OK(pipe2(pipe2_fds, O_NONBLOCK)); + + // Put some data into pipe1. + unsigned char buffer[4] = {1, 2, 3, 4}; + EXPECT_OK(write(pipe1_fds[1], buffer, 4)); + + cap_rights_t r_ro; + cap_rights_init(&r_ro, CAP_READ); + cap_rights_t r_wo; + cap_rights_init(&r_wo, CAP_WRITE); + cap_rights_t r_rw; + cap_rights_init(&r_rw, CAP_READ, CAP_WRITE); + + // Various attempts to tee into pipe2. + int cap_in_wo = dup(pipe1_fds[0]); + EXPECT_OK(cap_in_wo); + EXPECT_OK(cap_rights_limit(cap_in_wo, &r_wo)); + int cap_in_rw = dup(pipe1_fds[0]); + EXPECT_OK(cap_in_rw); + EXPECT_OK(cap_rights_limit(cap_in_rw, &r_rw)); + int cap_out_ro = dup(pipe2_fds[1]); + EXPECT_OK(cap_out_ro); + EXPECT_OK(cap_rights_limit(cap_out_ro, &r_ro)); + int cap_out_rw = dup(pipe2_fds[1]); + EXPECT_OK(cap_out_rw); + EXPECT_OK(cap_rights_limit(cap_out_rw, &r_rw)); + + EXPECT_NOTCAPABLE(tee(cap_in_wo, cap_out_rw, 4, SPLICE_F_NONBLOCK)); + EXPECT_NOTCAPABLE(tee(cap_in_rw, cap_out_ro, 4, SPLICE_F_NONBLOCK)); + EXPECT_OK(tee(cap_in_rw, cap_out_rw, 4, SPLICE_F_NONBLOCK)); + + close(cap_in_wo); + close(cap_in_rw); + close(cap_out_ro); + close(cap_out_rw); + close(pipe1_fds[0]); + close(pipe1_fds[1]); + close(pipe2_fds[0]); + close(pipe2_fds[1]); +} +#endif + +#ifdef HAVE_SPLICE +TEST(CapabilityPair, splice) { + int pipe1_fds[2]; + EXPECT_OK(pipe2(pipe1_fds, O_NONBLOCK)); + int pipe2_fds[2]; + EXPECT_OK(pipe2(pipe2_fds, O_NONBLOCK)); + + // Put some data into pipe1. + unsigned char buffer[4] = {1, 2, 3, 4}; + EXPECT_OK(write(pipe1_fds[1], buffer, 4)); + + cap_rights_t r_ro; + cap_rights_init(&r_ro, CAP_READ); + cap_rights_t r_wo; + cap_rights_init(&r_wo, CAP_WRITE); + cap_rights_t r_rs; + cap_rights_init(&r_rs, CAP_READ, CAP_SEEK); + cap_rights_t r_ws; + cap_rights_init(&r_ws, CAP_WRITE, CAP_SEEK); + + // Various attempts to splice. + int cap_in_wo = dup(pipe1_fds[0]); + EXPECT_OK(cap_in_wo); + EXPECT_OK(cap_rights_limit(cap_in_wo, &r_wo)); + int cap_in_ro = dup(pipe1_fds[0]); + EXPECT_OK(cap_in_ro); + EXPECT_OK(cap_rights_limit(cap_in_ro, &r_ro)); + int cap_in_ro_seek = dup(pipe1_fds[0]); + EXPECT_OK(cap_in_ro_seek); + EXPECT_OK(cap_rights_limit(cap_in_ro_seek, &r_rs)); + int cap_out_wo = dup(pipe2_fds[1]); + EXPECT_OK(cap_out_wo); + EXPECT_OK(cap_rights_limit(cap_out_wo, &r_wo)); + int cap_out_ro = dup(pipe2_fds[1]); + EXPECT_OK(cap_out_ro); + EXPECT_OK(cap_rights_limit(cap_out_ro, &r_ro)); + int cap_out_wo_seek = dup(pipe2_fds[1]); + EXPECT_OK(cap_out_wo_seek); + EXPECT_OK(cap_rights_limit(cap_out_wo_seek, &r_ws)); + + EXPECT_NOTCAPABLE(splice(cap_in_ro, NULL, cap_out_wo_seek, NULL, 4, SPLICE_F_NONBLOCK)); + EXPECT_NOTCAPABLE(splice(cap_in_wo, NULL, cap_out_wo_seek, NULL, 4, SPLICE_F_NONBLOCK)); + EXPECT_NOTCAPABLE(splice(cap_in_ro_seek, NULL, cap_out_ro, NULL, 4, SPLICE_F_NONBLOCK)); + EXPECT_NOTCAPABLE(splice(cap_in_ro_seek, NULL, cap_out_wo, NULL, 4, SPLICE_F_NONBLOCK)); + EXPECT_OK(splice(cap_in_ro_seek, NULL, cap_out_wo_seek, NULL, 4, SPLICE_F_NONBLOCK)); + + close(cap_in_wo); + close(cap_in_ro); + close(cap_in_ro_seek); + close(cap_out_wo); + close(cap_out_ro); + close(cap_out_wo_seek); + close(pipe1_fds[0]); + close(pipe1_fds[1]); + close(pipe2_fds[0]); + close(pipe2_fds[1]); +} +#endif + +#ifdef HAVE_VMSPLICE +// Although it only involves a single file descriptor, test vmsplice(2) here too. +TEST(CapabilityPair, vmsplice) { + int pipe_fds[2]; + EXPECT_OK(pipe2(pipe_fds, O_NONBLOCK)); + + cap_rights_t r_ro; + cap_rights_init(&r_ro, CAP_READ); + cap_rights_t r_rw; + cap_rights_init(&r_rw, CAP_READ, CAP_WRITE); + + int cap_ro = dup(pipe_fds[1]); + EXPECT_OK(cap_ro); + EXPECT_OK(cap_rights_limit(cap_ro, &r_ro)); + int cap_rw = dup(pipe_fds[1]); + EXPECT_OK(cap_rw); + EXPECT_OK(cap_rights_limit(cap_rw, &r_rw)); + + unsigned char buffer[4] = {1, 2, 3, 4}; + struct iovec iov; + memset(&iov, 0, sizeof(iov)); + iov.iov_base = buffer; + iov.iov_len = sizeof(buffer); + + EXPECT_NOTCAPABLE(vmsplice(cap_ro, &iov, 1, SPLICE_F_NONBLOCK)); + EXPECT_OK(vmsplice(cap_rw, &iov, 1, SPLICE_F_NONBLOCK)); + + close(cap_ro); + close(cap_rw); + close(pipe_fds[0]); + close(pipe_fds[1]); +} +#endif Added: vendor/google/capsicum-test/dist/capability-fd.cc ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ vendor/google/capsicum-test/dist/capability-fd.cc Tue Mar 12 01:43:01 2019 (r345046) @@ -0,0 +1,1271 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "capsicum.h" +#include "syscalls.h" +#include "capsicum-test.h" + +/* Utilities for printing rights information */ +/* Written in C style to allow for: */ +/* TODO(drysdale): migrate these to somewhere in libcaprights/ */ +#define RIGHTS_INFO(RR) { (RR), #RR} +typedef struct { + uint64_t right; + const char* name; +} right_info; +right_info known_rights[] = { + /* Rights that are common to all versions of Capsicum */ + RIGHTS_INFO(CAP_READ), + RIGHTS_INFO(CAP_WRITE), + RIGHTS_INFO(CAP_SEEK_TELL), + RIGHTS_INFO(CAP_SEEK), + RIGHTS_INFO(CAP_PREAD), + RIGHTS_INFO(CAP_PWRITE), + RIGHTS_INFO(CAP_MMAP), + RIGHTS_INFO(CAP_MMAP_R), + RIGHTS_INFO(CAP_MMAP_W), + RIGHTS_INFO(CAP_MMAP_X), + RIGHTS_INFO(CAP_MMAP_RW), + RIGHTS_INFO(CAP_MMAP_RX), + RIGHTS_INFO(CAP_MMAP_WX), + RIGHTS_INFO(CAP_MMAP_RWX), + RIGHTS_INFO(CAP_CREATE), + RIGHTS_INFO(CAP_FEXECVE), + RIGHTS_INFO(CAP_FSYNC), + RIGHTS_INFO(CAP_FTRUNCATE), + RIGHTS_INFO(CAP_LOOKUP), + RIGHTS_INFO(CAP_FCHDIR), + RIGHTS_INFO(CAP_FCHFLAGS), + RIGHTS_INFO(CAP_CHFLAGSAT), + RIGHTS_INFO(CAP_FCHMOD), + RIGHTS_INFO(CAP_FCHMODAT), + RIGHTS_INFO(CAP_FCHOWN), + RIGHTS_INFO(CAP_FCHOWNAT), + RIGHTS_INFO(CAP_FCNTL), + RIGHTS_INFO(CAP_FLOCK), + RIGHTS_INFO(CAP_FPATHCONF), + RIGHTS_INFO(CAP_FSCK), + RIGHTS_INFO(CAP_FSTAT), + RIGHTS_INFO(CAP_FSTATAT), + RIGHTS_INFO(CAP_FSTATFS), + RIGHTS_INFO(CAP_FUTIMES), + RIGHTS_INFO(CAP_FUTIMESAT), + RIGHTS_INFO(CAP_MKDIRAT), + RIGHTS_INFO(CAP_MKFIFOAT), + RIGHTS_INFO(CAP_MKNODAT), + RIGHTS_INFO(CAP_RENAMEAT_SOURCE), + RIGHTS_INFO(CAP_SYMLINKAT), + RIGHTS_INFO(CAP_UNLINKAT), + RIGHTS_INFO(CAP_ACCEPT), + RIGHTS_INFO(CAP_BIND), + RIGHTS_INFO(CAP_CONNECT), + RIGHTS_INFO(CAP_GETPEERNAME), + RIGHTS_INFO(CAP_GETSOCKNAME), + RIGHTS_INFO(CAP_GETSOCKOPT), + RIGHTS_INFO(CAP_LISTEN), + RIGHTS_INFO(CAP_PEELOFF), + RIGHTS_INFO(CAP_RECV), + RIGHTS_INFO(CAP_SEND), + RIGHTS_INFO(CAP_SETSOCKOPT), + RIGHTS_INFO(CAP_SHUTDOWN), + RIGHTS_INFO(CAP_BINDAT), + RIGHTS_INFO(CAP_CONNECTAT), + RIGHTS_INFO(CAP_LINKAT_SOURCE), + RIGHTS_INFO(CAP_RENAMEAT_TARGET), + RIGHTS_INFO(CAP_SOCK_CLIENT), + RIGHTS_INFO(CAP_SOCK_SERVER), + RIGHTS_INFO(CAP_MAC_GET), + RIGHTS_INFO(CAP_MAC_SET), + RIGHTS_INFO(CAP_SEM_GETVALUE), + RIGHTS_INFO(CAP_SEM_POST), + RIGHTS_INFO(CAP_SEM_WAIT), + RIGHTS_INFO(CAP_EVENT), + RIGHTS_INFO(CAP_KQUEUE_EVENT), + RIGHTS_INFO(CAP_IOCTL), + RIGHTS_INFO(CAP_TTYHOOK), + RIGHTS_INFO(CAP_PDWAIT), + RIGHTS_INFO(CAP_PDGETPID), + RIGHTS_INFO(CAP_PDKILL), + RIGHTS_INFO(CAP_EXTATTR_DELETE), + RIGHTS_INFO(CAP_EXTATTR_GET), + RIGHTS_INFO(CAP_EXTATTR_LIST), + RIGHTS_INFO(CAP_EXTATTR_SET), + RIGHTS_INFO(CAP_ACL_CHECK), + RIGHTS_INFO(CAP_ACL_DELETE), + RIGHTS_INFO(CAP_ACL_GET), + RIGHTS_INFO(CAP_ACL_SET), + RIGHTS_INFO(CAP_KQUEUE_CHANGE), + RIGHTS_INFO(CAP_KQUEUE), + /* Rights that are only present in some version or some OS, and so are #ifdef'ed */ + /* LINKAT got split */ +#ifdef CAP_LINKAT + RIGHTS_INFO(CAP_LINKAT), +#endif +#ifdef CAP_LINKAT_SOURCE + RIGHTS_INFO(CAP_LINKAT_SOURCE), +#endif +#ifdef CAP_LINKAT_TARGET + RIGHTS_INFO(CAP_LINKAT_TARGET), +#endif + /* Linux aliased some FD operations for pdgetpid/pdkill */ +#ifdef CAP_PDGETPID_FREEBSD + RIGHTS_INFO(CAP_PDGETPID_FREEBSD), +#endif +#ifdef CAP_PDKILL_FREEBSD + RIGHTS_INFO(CAP_PDKILL_FREEBSD), +#endif + /* Linux-specific rights */ +#ifdef CAP_FSIGNAL + RIGHTS_INFO(CAP_FSIGNAL), +#endif +#ifdef CAP_EPOLL_CTL + RIGHTS_INFO(CAP_EPOLL_CTL), +#endif +#ifdef CAP_NOTIFY + RIGHTS_INFO(CAP_NOTIFY), +#endif +#ifdef CAP_SETNS + RIGHTS_INFO(CAP_SETNS), +#endif +#ifdef CAP_PERFMON + RIGHTS_INFO(CAP_PERFMON), +#endif +#ifdef CAP_BPF + RIGHTS_INFO(CAP_BPF), +#endif + /* Rights in later versions of FreeBSD (>10.0) */ +}; + +void ShowCapRights(FILE *out, int fd) { + size_t ii; + bool first = true; + cap_rights_t rights; + CAP_SET_NONE(&rights); + if (cap_rights_get(fd, &rights) < 0) { + fprintf(out, "Failed to get rights for fd %d: errno %d\n", fd, errno); + return; + } + + /* First print out all known rights */ + size_t num_known = (sizeof(known_rights)/sizeof(known_rights[0])); + for (ii = 0; ii < num_known; ii++) { + if (cap_rights_is_set(&rights, known_rights[ii].right)) { + if (!first) fprintf(out, ","); + first = false; + fprintf(out, "%s", known_rights[ii].name); + } + } + /* Now repeat the loop, clearing rights we know of; this needs to be + * a separate loop because some named rights overlap. + */ + for (ii = 0; ii < num_known; ii++) { + cap_rights_clear(&rights, known_rights[ii].right); + } + /* The following relies on the internal structure of cap_rights_t to + * try to show rights we don't know about. */ + for (ii = 0; ii < (size_t)CAPARSIZE(&rights); ii++) { + uint64_t bits = (rights.cr_rights[0] & 0x01ffffffffffffffULL); + if (bits != 0) { + uint64_t which = 1; + for (which = 1; which < 0x0200000000000000 ; which <<= 1) { + if (bits & which) { + if (!first) fprintf(out, ","); + fprintf(out, "CAP_RIGHT(%d, 0x%016llxULL)", (int)ii, (long long unsigned)which); + } + } + } + } + fprintf(out, "\n"); +} + +void ShowAllCapRights(FILE *out) { + int fd; + struct rlimit limits; + if (getrlimit(RLIMIT_NOFILE, &limits) != 0) { + fprintf(out, "Failed to getrlimit for max FDs: errno %d\n", errno); + return; + } + for (fd = 0; fd < (int)limits.rlim_cur; fd++) { + if (fcntl(fd, F_GETFD, 0) != 0) { + continue; + } + fprintf(out, "fd %d: ", fd); + ShowCapRights(out, fd); + } +} + +FORK_TEST(Capability, CapNew) { + cap_rights_t r_rws; + cap_rights_init(&r_rws, CAP_READ, CAP_WRITE, CAP_SEEK); + cap_rights_t r_all; + CAP_SET_ALL(&r_all); + + int cap_fd = dup(STDOUT_FILENO); + cap_rights_t rights; + CAP_SET_NONE(&rights); + EXPECT_OK(cap_rights_get(cap_fd, &rights)); + EXPECT_RIGHTS_EQ(&r_all, &rights); + + EXPECT_OK(cap_fd); + EXPECT_OK(cap_rights_limit(cap_fd, &r_rws)); + if (cap_fd < 0) return; + int rc = write(cap_fd, "OK!\n", 4); + EXPECT_OK(rc); + EXPECT_EQ(4, rc); + EXPECT_OK(cap_rights_get(cap_fd, &rights)); + EXPECT_RIGHTS_EQ(&r_rws, &rights); + + // dup/dup2 should preserve rights. + int cap_dup = dup(cap_fd); + EXPECT_OK(cap_dup); + EXPECT_OK(cap_rights_get(cap_dup, &rights)); + EXPECT_RIGHTS_EQ(&r_rws, &rights); + close(cap_dup); + EXPECT_OK(dup2(cap_fd, cap_dup)); + EXPECT_OK(cap_rights_get(cap_dup, &rights)); + EXPECT_RIGHTS_EQ(&r_rws, &rights); + close(cap_dup); +#ifdef HAVE_DUP3 + EXPECT_OK(dup3(cap_fd, cap_dup, 0)); + EXPECT_OK(cap_rights_get(cap_dup, &rights)); + EXPECT_RIGHTS_EQ(&r_rws, &rights); + close(cap_dup); +#endif + + // Try to get a disjoint set of rights in a sub-capability. + cap_rights_t r_rs; + cap_rights_init(&r_rs, CAP_READ, CAP_SEEK); + cap_rights_t r_rsmapchmod; + cap_rights_init(&r_rsmapchmod, CAP_READ, CAP_SEEK, CAP_MMAP, CAP_FCHMOD); + int cap_cap_fd = dup(cap_fd); + EXPECT_OK(cap_cap_fd); + EXPECT_NOTCAPABLE(cap_rights_limit(cap_cap_fd, &r_rsmapchmod)); + + // Dump rights info to stderr (mostly to ensure that Show[All]CapRights() + // is working. + ShowAllCapRights(stderr); + + EXPECT_OK(close(cap_fd)); +} + +FORK_TEST(Capability, CapEnter) { + EXPECT_EQ(0, cap_enter()); +} + +FORK_TEST(Capability, BasicInterception) { + cap_rights_t r_0; + cap_rights_init(&r_0, 0); + int cap_fd = dup(1); + EXPECT_OK(cap_fd); + EXPECT_OK(cap_rights_limit(cap_fd, &r_0)); + + EXPECT_NOTCAPABLE(write(cap_fd, "", 0)); + + EXPECT_OK(cap_enter()); // Enter capability mode + + EXPECT_NOTCAPABLE(write(cap_fd, "", 0)); + + // Create a new capability which does have write permission + cap_rights_t r_ws; + cap_rights_init(&r_ws, CAP_WRITE, CAP_SEEK); + int cap_fd2 = dup(1); + EXPECT_OK(cap_fd2); + EXPECT_OK(cap_rights_limit(cap_fd2, &r_ws)); + EXPECT_OK(write(cap_fd2, "", 0)); + + // Tidy up. + if (cap_fd >= 0) close(cap_fd); + if (cap_fd2 >= 0) close(cap_fd2); +} + +FORK_TEST_ON(Capability, OpenAtDirectoryTraversal, TmpFile("cap_openat_testfile")) { + int dir = open(tmpdir.c_str(), O_RDONLY); + EXPECT_OK(dir); + + cap_enter(); + + int file = openat(dir, "cap_openat_testfile", O_RDONLY|O_CREAT, 0644); + EXPECT_OK(file); + + // Test that we are confined to /tmp, and cannot + // escape using absolute paths or ../. + int new_file = openat(dir, "../dev/null", O_RDONLY); + EXPECT_EQ(-1, new_file); + + new_file = openat(dir, "..", O_RDONLY); + EXPECT_EQ(-1, new_file); + + new_file = openat(dir, "/dev/null", O_RDONLY); + EXPECT_EQ(-1, new_file); + + new_file = openat(dir, "/", O_RDONLY); + EXPECT_EQ(-1, new_file); + + // Tidy up. + close(file); + close(dir); +} + +FORK_TEST_ON(Capability, FileInSync, TmpFile("cap_file_sync")) { + int fd = open(TmpFile("cap_file_sync"), O_RDWR|O_CREAT, 0644); + EXPECT_OK(fd); + const char* message = "Hello capability world"; + EXPECT_OK(write(fd, message, strlen(message))); + + cap_rights_t r_rsstat; + cap_rights_init(&r_rsstat, CAP_READ, CAP_SEEK, CAP_FSTAT); + + int cap_fd = dup(fd); + EXPECT_OK(cap_fd); + EXPECT_OK(cap_rights_limit(cap_fd, &r_rsstat)); + int cap_cap_fd = dup(cap_fd); + EXPECT_OK(cap_cap_fd); + EXPECT_OK(cap_rights_limit(cap_cap_fd, &r_rsstat)); + + EXPECT_OK(cap_enter()); // Enter capability mode. + + // Changes to one file descriptor affect the others. + EXPECT_EQ(1, lseek(fd, 1, SEEK_SET)); + EXPECT_EQ(1, lseek(fd, 0, SEEK_CUR)); + EXPECT_EQ(1, lseek(cap_fd, 0, SEEK_CUR)); + EXPECT_EQ(1, lseek(cap_cap_fd, 0, SEEK_CUR)); + EXPECT_EQ(3, lseek(cap_fd, 3, SEEK_SET)); + EXPECT_EQ(3, lseek(fd, 0, SEEK_CUR)); + EXPECT_EQ(3, lseek(cap_fd, 0, SEEK_CUR)); + EXPECT_EQ(3, lseek(cap_cap_fd, 0, SEEK_CUR)); + EXPECT_EQ(5, lseek(cap_cap_fd, 5, SEEK_SET)); + EXPECT_EQ(5, lseek(fd, 0, SEEK_CUR)); + EXPECT_EQ(5, lseek(cap_fd, 0, SEEK_CUR)); + EXPECT_EQ(5, lseek(cap_cap_fd, 0, SEEK_CUR)); + + close(cap_cap_fd); + close(cap_fd); + close(fd); +} + +// Create a capability on /tmp that does not allow CAP_WRITE, +// and check that this restriction is inherited through openat(). +FORK_TEST_ON(Capability, Inheritance, TmpFile("cap_openat_write_testfile")) { + int dir = open(tmpdir.c_str(), O_RDONLY); + EXPECT_OK(dir); + + cap_rights_t r_rl; + cap_rights_init(&r_rl, CAP_READ, CAP_LOOKUP); + + int cap_dir = dup(dir); + EXPECT_OK(cap_dir); + EXPECT_OK(cap_rights_limit(cap_dir, &r_rl)); + + const char *filename = "cap_openat_write_testfile"; + int file = openat(dir, filename, O_WRONLY|O_CREAT, 0644); + EXPECT_OK(file); + EXPECT_EQ(5, write(file, "TEST\n", 5)); + if (file >= 0) close(file); + + EXPECT_OK(cap_enter()); + file = openat(cap_dir, filename, O_RDONLY); + EXPECT_OK(file); + + cap_rights_t rights; + cap_rights_init(&rights, 0); + EXPECT_OK(cap_rights_get(file, &rights)); + EXPECT_RIGHTS_EQ(&r_rl, &rights); + if (file >= 0) close(file); + + file = openat(cap_dir, filename, O_WRONLY|O_APPEND); + EXPECT_NOTCAPABLE(file); + if (file > 0) close(file); + + if (dir > 0) close(dir); + if (cap_dir > 0) close(cap_dir); +} + + +// Ensure that, if the capability had enough rights for the system call to +// pass, then it did. Otherwise, ensure that the errno is ENOTCAPABLE; +// capability restrictions should kick in before any other error logic. +#define CHECK_RIGHT_RESULT(result, rights, ...) do { \ + cap_rights_t rights_needed; \ + cap_rights_init(&rights_needed, __VA_ARGS__); \ + if (cap_rights_contains(&rights, &rights_needed)) { \ + EXPECT_OK(result) << std::endl \ + << " need: " << rights_needed \ + << std::endl \ + << " got: " << rights; \ + } else { \ + EXPECT_EQ(-1, result) << " need: " << rights_needed \ + << std::endl \ + << " got: "<< rights; \ + EXPECT_EQ(ENOTCAPABLE, errno); \ + } \ +} while (0) + +#define EXPECT_MMAP_NOTCAPABLE(result) do { \ + void *rv = result; \ + EXPECT_EQ(MAP_FAILED, rv); \ + EXPECT_EQ(ENOTCAPABLE, errno); \ + if (rv != MAP_FAILED) munmap(rv, getpagesize()); \ +} while (0) + +#define EXPECT_MMAP_OK(result) do { \ + void *rv = result; \ + EXPECT_NE(MAP_FAILED, rv) << " with errno " << errno; \ + if (rv != MAP_FAILED) munmap(rv, getpagesize()); \ +} while (0) + + +// As above, but for the special mmap() case: unmap after successful mmap(). +#define CHECK_RIGHT_MMAP_RESULT(result, rights, ...) do { \ + cap_rights_t rights_needed; \ + cap_rights_init(&rights_needed, __VA_ARGS__); \ + if (cap_rights_contains(&rights, &rights_needed)) { \ + EXPECT_MMAP_OK(result); \ + } else { \ + EXPECT_MMAP_NOTCAPABLE(result); \ + } \ +} while (0) + +FORK_TEST_ON(Capability, Mmap, TmpFile("cap_mmap_operations")) { + int fd = open(TmpFile("cap_mmap_operations"), O_RDWR | O_CREAT, 0644); + EXPECT_OK(fd); + if (fd < 0) return; + + cap_rights_t r_0; + cap_rights_init(&r_0, 0); + cap_rights_t r_mmap; + cap_rights_init(&r_mmap, CAP_MMAP); + cap_rights_t r_r; + cap_rights_init(&r_r, CAP_PREAD); + cap_rights_t r_rmmap; + cap_rights_init(&r_rmmap, CAP_PREAD, CAP_MMAP); + + // If we're missing a capability, it will fail. + int cap_none = dup(fd); + EXPECT_OK(cap_none); + EXPECT_OK(cap_rights_limit(cap_none, &r_0)); + int cap_mmap = dup(fd); + EXPECT_OK(cap_mmap); + EXPECT_OK(cap_rights_limit(cap_mmap, &r_mmap)); + int cap_read = dup(fd); + EXPECT_OK(cap_read); + EXPECT_OK(cap_rights_limit(cap_read, &r_r)); + int cap_both = dup(fd); + EXPECT_OK(cap_both); + EXPECT_OK(cap_rights_limit(cap_both, &r_rmmap)); + + EXPECT_OK(cap_enter()); // Enter capability mode. + + EXPECT_MMAP_NOTCAPABLE(mmap(NULL, getpagesize(), PROT_READ, MAP_PRIVATE, cap_none, 0)); + EXPECT_MMAP_NOTCAPABLE(mmap(NULL, getpagesize(), PROT_READ, MAP_PRIVATE, cap_mmap, 0)); + EXPECT_MMAP_NOTCAPABLE(mmap(NULL, getpagesize(), PROT_READ, MAP_PRIVATE, cap_read, 0)); + + EXPECT_MMAP_OK(mmap(NULL, getpagesize(), PROT_READ, MAP_PRIVATE, cap_both, 0)); + + // A call with MAP_ANONYMOUS should succeed without any capability requirements. + EXPECT_MMAP_OK(mmap(NULL, getpagesize(), PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0)); + + EXPECT_OK(close(cap_both)); + EXPECT_OK(close(cap_read)); + EXPECT_OK(close(cap_mmap)); + EXPECT_OK(close(cap_none)); + EXPECT_OK(close(fd)); +} + +// Given a file descriptor, create a capability with specific rights and +// make sure only those rights work. +#define TRY_FILE_OPS(fd, ...) do { \ + cap_rights_t rights; \ + cap_rights_init(&rights, __VA_ARGS__); \ + TryFileOps((fd), rights); \ +} while (0) + +static void TryFileOps(int fd, cap_rights_t rights) { + int cap_fd = dup(fd); + EXPECT_OK(cap_fd); + EXPECT_OK(cap_rights_limit(cap_fd, &rights)); + if (cap_fd < 0) return; + cap_rights_t erights; + EXPECT_OK(cap_rights_get(cap_fd, &erights)); + EXPECT_RIGHTS_EQ(&rights, &erights); + + // Check creation of a capability from a capability. + int cap_cap_fd = dup(cap_fd); + EXPECT_OK(cap_cap_fd); + EXPECT_OK(cap_rights_limit(cap_cap_fd, &rights)); + EXPECT_NE(cap_fd, cap_cap_fd); + EXPECT_OK(cap_rights_get(cap_cap_fd, &erights)); + EXPECT_RIGHTS_EQ(&rights, &erights); + close(cap_cap_fd); + + char ch; + CHECK_RIGHT_RESULT(read(cap_fd, &ch, sizeof(ch)), rights, CAP_READ, CAP_SEEK_ASWAS); + + ssize_t len1 = pread(cap_fd, &ch, sizeof(ch), 0); + CHECK_RIGHT_RESULT(len1, rights, CAP_PREAD); + ssize_t len2 = pread(cap_fd, &ch, sizeof(ch), 0); + CHECK_RIGHT_RESULT(len2, rights, CAP_PREAD); + EXPECT_EQ(len1, len2); + + CHECK_RIGHT_RESULT(write(cap_fd, &ch, sizeof(ch)), rights, CAP_WRITE, CAP_SEEK_ASWAS); + CHECK_RIGHT_RESULT(pwrite(cap_fd, &ch, sizeof(ch), 0), rights, CAP_PWRITE); + CHECK_RIGHT_RESULT(lseek(cap_fd, 0, SEEK_SET), rights, CAP_SEEK); + +#ifdef HAVE_CHFLAGS + // Note: this is not expected to work over NFS. + struct statfs sf; + EXPECT_OK(fstatfs(fd, &sf)); + bool is_nfs = (strncmp("nfs", sf.f_fstypename, sizeof(sf.f_fstypename)) == 0); + if (!is_nfs) { + CHECK_RIGHT_RESULT(fchflags(cap_fd, UF_NODUMP), rights, CAP_FCHFLAGS); + } +#endif + + CHECK_RIGHT_MMAP_RESULT(mmap(NULL, getpagesize(), PROT_NONE, MAP_SHARED, cap_fd, 0), + rights, CAP_MMAP); + CHECK_RIGHT_MMAP_RESULT(mmap(NULL, getpagesize(), PROT_READ, MAP_SHARED, cap_fd, 0), + rights, CAP_MMAP_R); + CHECK_RIGHT_MMAP_RESULT(mmap(NULL, getpagesize(), PROT_WRITE, MAP_SHARED, cap_fd, 0), + rights, CAP_MMAP_W); + CHECK_RIGHT_MMAP_RESULT(mmap(NULL, getpagesize(), PROT_EXEC, MAP_SHARED, cap_fd, 0), + rights, CAP_MMAP_X); + CHECK_RIGHT_MMAP_RESULT(mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE, MAP_SHARED, cap_fd, 0), + rights, CAP_MMAP_RW); + CHECK_RIGHT_MMAP_RESULT(mmap(NULL, getpagesize(), PROT_READ | PROT_EXEC, MAP_SHARED, cap_fd, 0), + rights, CAP_MMAP_RX); + CHECK_RIGHT_MMAP_RESULT(mmap(NULL, getpagesize(), PROT_EXEC | PROT_WRITE, MAP_SHARED, cap_fd, 0), + rights, CAP_MMAP_WX); + CHECK_RIGHT_MMAP_RESULT(mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED, cap_fd, 0), + rights, CAP_MMAP_RWX); + + CHECK_RIGHT_RESULT(fsync(cap_fd), rights, CAP_FSYNC); +#ifdef HAVE_SYNC_FILE_RANGE + CHECK_RIGHT_RESULT(sync_file_range(cap_fd, 0, 1, 0), rights, CAP_FSYNC, CAP_SEEK); +#endif + + int rc = fcntl(cap_fd, F_GETFL); + CHECK_RIGHT_RESULT(rc, rights, CAP_FCNTL); + rc = fcntl(cap_fd, F_SETFL, rc); + CHECK_RIGHT_RESULT(rc, rights, CAP_FCNTL); + + CHECK_RIGHT_RESULT(fchown(cap_fd, -1, -1), rights, CAP_FCHOWN); + + CHECK_RIGHT_RESULT(fchmod(cap_fd, 0644), rights, CAP_FCHMOD); + + CHECK_RIGHT_RESULT(flock(cap_fd, LOCK_SH), rights, CAP_FLOCK); + CHECK_RIGHT_RESULT(flock(cap_fd, LOCK_UN), rights, CAP_FLOCK); + + CHECK_RIGHT_RESULT(ftruncate(cap_fd, 0), rights, CAP_FTRUNCATE); + + struct stat sb; + CHECK_RIGHT_RESULT(fstat(cap_fd, &sb), rights, CAP_FSTAT); + + struct statfs cap_sf; + CHECK_RIGHT_RESULT(fstatfs(cap_fd, &cap_sf), rights, CAP_FSTATFS); + +#ifdef HAVE_FPATHCONF + CHECK_RIGHT_RESULT(fpathconf(cap_fd, _PC_NAME_MAX), rights, CAP_FPATHCONF); +#endif + + CHECK_RIGHT_RESULT(futimes(cap_fd, NULL), rights, CAP_FUTIMES); + + struct pollfd pollfd; + pollfd.fd = cap_fd; + pollfd.events = POLLIN | POLLERR | POLLHUP; *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***