git: 62d3f57c22ee - main - capsicum: add tests for copy_file_range

From: Mariusz Zaborski <oshogbo_at_FreeBSD.org>
Date: Thu, 28 Sep 2023 14:07:39 UTC
The branch main has been updated by oshogbo:

URL: https://cgit.FreeBSD.org/src/commit/?id=62d3f57c22eea353dcc3c4a2f70ef72a42a8cd83

commit 62d3f57c22eea353dcc3c4a2f70ef72a42a8cd83
Author:     Mariusz Zaborski <oshogbo@FreeBSD.org>
AuthorDate: 2023-09-28 13:27:27 +0000
Commit:     Mariusz Zaborski <oshogbo@FreeBSD.org>
CommitDate: 2023-09-28 13:48:32 +0000

    capsicum: add tests for copy_file_range
    
    Reviewed by:    emaste, theraven, kib, markj (all previous version)
    Differential Revision:  https://reviews.freebsd.org/D41967
---
 contrib/capsicum-test/copy_file_range.cc | 228 +++++++++++++++++++++++++++++++
 contrib/capsicum-test/makefile           |   2 +-
 tests/sys/capsicum/Makefile              |   1 +
 3 files changed, 230 insertions(+), 1 deletion(-)

diff --git a/contrib/capsicum-test/copy_file_range.cc b/contrib/capsicum-test/copy_file_range.cc
new file mode 100644
index 000000000000..b785eb7f1e97
--- /dev/null
+++ b/contrib/capsicum-test/copy_file_range.cc
@@ -0,0 +1,228 @@
+#include <sys/types.h>
+#include <fcntl.h>
+
+#include <string>
+
+#include "capsicum.h"
+#include "capsicum-test.h"
+#include "syscalls.h"
+
+#define TOPDIR "cap_copy_file_range"
+#define INFILE "infile"
+#define OUTFILE "outfile"
+
+/* Test that copy_file_range() checks capabilities correctly.
+ * When used without offset arguments, copy_file_range() should
+ * require only CAP_READ on the source and CAP_WRITE on the destination
+ * file descriptors, respectively.
+ * When used with offset arguments, copy_file_range() should
+ * additionally require CAP_SEEK.
+ */
+class CopyFileRangeTest : public ::testing::Test {
+ public:
+  CopyFileRangeTest() {
+    int rc = mkdir(TmpFile(TOPDIR), 0755);
+    EXPECT_OK(rc);
+    if (rc < 0) {
+      EXPECT_EQ(EEXIST, errno);
+    }
+    wd_ = open(TmpFile(TOPDIR), O_DIRECTORY);
+    EXPECT_OK(wd_);
+    CreateFile(TmpFile(TOPDIR "/" INFILE));
+    CreateFile(TmpFile(TOPDIR "/" OUTFILE));
+  }
+  ~CopyFileRangeTest() {
+    close(wd_);
+    unlink(TmpFile(TOPDIR "/" INFILE));
+    unlink(TmpFile(TOPDIR "/" OUTFILE));
+    rmdir(TmpFile(TOPDIR));
+  }
+
+ private:
+  void CreateFile(const char *filename) {
+    int fd = open(filename, O_CREAT|O_RDWR, 0644);
+    const char *contents = "lorem ipsum dolor sit amet";
+    EXPECT_OK(fd);
+    for (int i = 0; i < 100; i++) {
+      EXPECT_OK(write(fd, contents, strlen(contents)));
+    }
+    close(fd);
+  }
+
+ protected:
+  int wd_;
+
+  int openInFile(cap_rights_t *rights) {
+    int fd = openat(wd_, INFILE, O_RDONLY);
+    EXPECT_OK(fd);
+    EXPECT_OK(cap_rights_limit(fd, rights));
+    return fd;
+  }
+  int openOutFile(cap_rights_t *rights) {
+    int fd = openat(wd_, OUTFILE, O_WRONLY);
+    EXPECT_OK(fd);
+    EXPECT_OK(cap_rights_limit(fd, rights));
+    return fd;
+  }
+};
+
+TEST_F(CopyFileRangeTest, WriteReadNeg) {
+  cap_rights_t rights_in, rights_out;
+
+  cap_rights_init(&rights_in, CAP_WRITE);
+  cap_rights_init(&rights_out, CAP_READ);
+
+  int fd_in = openInFile(&rights_in);
+  int fd_out = openOutFile(&rights_out);
+  off_t off_in = 0, off_out = 0;
+
+  EXPECT_NOTCAPABLE(copy_file_range(fd_in, NULL, fd_out, NULL, 8, 0));
+  EXPECT_NOTCAPABLE(copy_file_range(fd_in, &off_in, fd_out, NULL, 8, 0));
+  EXPECT_NOTCAPABLE(copy_file_range(fd_in, NULL, fd_out, &off_out, 8, 0));
+  EXPECT_NOTCAPABLE(copy_file_range(fd_in, &off_in, fd_out, &off_out, 8, 0));
+  off_in = 20;
+  off_out = 20;
+  EXPECT_NOTCAPABLE(copy_file_range(fd_in, &off_in, fd_out, NULL, 8, 0));
+  EXPECT_NOTCAPABLE(copy_file_range(fd_in, NULL, fd_out, &off_out, 8, 0));
+  EXPECT_NOTCAPABLE(copy_file_range(fd_in, &off_in, fd_out, &off_out, 8, 0));
+  close(fd_in);
+  close(fd_out);
+}
+
+TEST_F(CopyFileRangeTest, ReadReadNeg) {
+  cap_rights_t rights_in, rights_out;
+
+  cap_rights_init(&rights_in, CAP_READ);
+  cap_rights_init(&rights_out, CAP_READ);
+
+  int fd_in = openInFile(&rights_in);
+  int fd_out = openOutFile(&rights_out);
+  off_t off_in = 0, off_out = 0;
+
+  EXPECT_NOTCAPABLE(copy_file_range(fd_in, NULL, fd_out, NULL, 8, 0));
+  EXPECT_NOTCAPABLE(copy_file_range(fd_in, &off_in, fd_out, NULL, 8, 0));
+  EXPECT_NOTCAPABLE(copy_file_range(fd_in, NULL, fd_out, &off_out, 8, 0));
+  EXPECT_NOTCAPABLE(copy_file_range(fd_in, &off_in, fd_out, &off_out, 8, 0));
+  off_in = 20;
+  off_out = 20;
+  EXPECT_NOTCAPABLE(copy_file_range(fd_in, &off_in, fd_out, NULL, 8, 0));
+  EXPECT_NOTCAPABLE(copy_file_range(fd_in, NULL, fd_out, &off_out, 8, 0));
+  EXPECT_NOTCAPABLE(copy_file_range(fd_in, &off_in, fd_out, &off_out, 8, 0));
+  close(fd_in);
+  close(fd_out);
+}
+
+TEST_F(CopyFileRangeTest, WriteWriteNeg) {
+  cap_rights_t rights_in, rights_out;
+
+  cap_rights_init(&rights_in, CAP_WRITE);
+  cap_rights_init(&rights_out, CAP_WRITE);
+
+  int fd_in = openInFile(&rights_in);
+  int fd_out = openOutFile(&rights_out);
+  off_t off_in = 0, off_out = 0;
+
+  EXPECT_NOTCAPABLE(copy_file_range(fd_in, NULL, fd_out, NULL, 8, 0));
+  EXPECT_NOTCAPABLE(copy_file_range(fd_in, &off_in, fd_out, NULL, 8, 0));
+  EXPECT_NOTCAPABLE(copy_file_range(fd_in, NULL, fd_out, &off_out, 8, 0));
+  EXPECT_NOTCAPABLE(copy_file_range(fd_in, &off_in, fd_out, &off_out, 8, 0));
+  off_in = 20;
+  off_out = 20;
+  EXPECT_NOTCAPABLE(copy_file_range(fd_in, &off_in, fd_out, NULL, 8, 0));
+  EXPECT_NOTCAPABLE(copy_file_range(fd_in, NULL, fd_out, &off_out, 8, 0));
+  EXPECT_NOTCAPABLE(copy_file_range(fd_in, &off_in, fd_out, &off_out, 8, 0));
+  close(fd_in);
+  close(fd_out);
+}
+
+TEST_F(CopyFileRangeTest, ReadWrite) {
+  cap_rights_t rights_in, rights_out;
+
+  cap_rights_init(&rights_in, CAP_READ);
+  cap_rights_init(&rights_out, CAP_WRITE);
+
+  int fd_in = openInFile(&rights_in);
+  int fd_out = openOutFile(&rights_out);
+  off_t off_in = 0, off_out = 0;
+
+  EXPECT_OK(copy_file_range(fd_in, NULL, fd_out, NULL, 8, 0));
+  EXPECT_NOTCAPABLE(copy_file_range(fd_in, &off_in, fd_out, NULL, 8, 0));
+  EXPECT_NOTCAPABLE(copy_file_range(fd_in, NULL, fd_out, &off_out, 8, 0));
+  EXPECT_NOTCAPABLE(copy_file_range(fd_in, &off_in, fd_out, &off_out, 8, 0));
+  off_in = 20;
+  off_out = 20;
+  EXPECT_NOTCAPABLE(copy_file_range(fd_in, &off_in, fd_out, NULL, 8, 0));
+  EXPECT_NOTCAPABLE(copy_file_range(fd_in, NULL, fd_out, &off_out, 8, 0));
+  EXPECT_NOTCAPABLE(copy_file_range(fd_in, &off_in, fd_out, &off_out, 8, 0));
+  close(fd_in);
+  close(fd_out);
+}
+
+TEST_F(CopyFileRangeTest, ReadSeekWrite) {
+  cap_rights_t rights_in, rights_out;
+
+  cap_rights_init(&rights_in, CAP_READ, CAP_SEEK);
+  cap_rights_init(&rights_out, CAP_WRITE);
+
+  int fd_in = openInFile(&rights_in);
+  int fd_out = openOutFile(&rights_out);
+  off_t off_in = 0, off_out = 0;
+
+  EXPECT_OK(copy_file_range(fd_in, NULL, fd_out, NULL, 8, 0));
+  EXPECT_OK(copy_file_range(fd_in, &off_in, fd_out, NULL, 8, 0));
+  EXPECT_NOTCAPABLE(copy_file_range(fd_in, NULL, fd_out, &off_out, 8, 0));
+  EXPECT_NOTCAPABLE(copy_file_range(fd_in, &off_in, fd_out, &off_out, 8, 0));
+  off_in = 20;
+  off_out = 20;
+  EXPECT_OK(copy_file_range(fd_in, &off_in, fd_out, NULL, 8, 0));
+  EXPECT_NOTCAPABLE(copy_file_range(fd_in, NULL, fd_out, &off_out, 8, 0));
+  EXPECT_NOTCAPABLE(copy_file_range(fd_in, &off_in, fd_out, &off_out, 8, 0));
+  close(fd_in);
+  close(fd_out);
+}
+
+TEST_F(CopyFileRangeTest, ReadWriteSeek) {
+  cap_rights_t rights_in, rights_out;
+
+  cap_rights_init(&rights_in, CAP_READ);
+  cap_rights_init(&rights_out, CAP_WRITE, CAP_SEEK);
+
+  int fd_in = openInFile(&rights_in);
+  int fd_out = openOutFile(&rights_out);
+  off_t off_in = 0, off_out = 0;
+
+  EXPECT_OK(copy_file_range(fd_in, NULL, fd_out, NULL, 8, 0));
+  EXPECT_OK(copy_file_range(fd_in, NULL, fd_out, &off_out, 8, 0));
+  EXPECT_NOTCAPABLE(copy_file_range(fd_in, &off_in, fd_out, NULL, 8, 0));
+  EXPECT_NOTCAPABLE(copy_file_range(fd_in, &off_in, fd_out, &off_out, 8, 0));
+  off_in = 20;
+  off_out = 20;
+  EXPECT_OK(copy_file_range(fd_in, NULL, fd_out, &off_out, 8, 0));
+  EXPECT_NOTCAPABLE(copy_file_range(fd_in, &off_in, fd_out, NULL, 8, 0));
+  EXPECT_NOTCAPABLE(copy_file_range(fd_in, &off_in, fd_out, &off_out, 8, 0));
+  close(fd_in);
+  close(fd_out);
+}
+
+TEST_F(CopyFileRangeTest, ReadSeekWriteSeek) {
+  cap_rights_t rights_in, rights_out;
+
+  cap_rights_init(&rights_in, CAP_READ, CAP_SEEK);
+  cap_rights_init(&rights_out, CAP_WRITE, CAP_SEEK);
+
+  int fd_in = openInFile(&rights_in);
+  int fd_out = openOutFile(&rights_out);
+  off_t off_in = 0, off_out = 0;
+
+  EXPECT_OK(copy_file_range(fd_in, NULL, fd_out, NULL, 8, 0));
+  EXPECT_OK(copy_file_range(fd_in, &off_in, fd_out, NULL, 8, 0));
+  EXPECT_OK(copy_file_range(fd_in, NULL, fd_out, &off_out, 8, 0));
+  EXPECT_OK(copy_file_range(fd_in, &off_in, fd_out, &off_out, 8, 0));
+  off_in = 20;
+  off_out = 20;
+  EXPECT_OK(copy_file_range(fd_in, NULL, fd_out, &off_out, 8, 0));
+  EXPECT_OK(copy_file_range(fd_in, &off_in, fd_out, NULL, 8, 0));
+  EXPECT_OK(copy_file_range(fd_in, &off_in, fd_out, &off_out, 8, 0));
+  close(fd_in);
+  close(fd_out);
+}
diff --git a/contrib/capsicum-test/makefile b/contrib/capsicum-test/makefile
index ad697f160e2e..e55393bbf131 100644
--- a/contrib/capsicum-test/makefile
+++ b/contrib/capsicum-test/makefile
@@ -1,5 +1,5 @@
 all: capsicum-test smoketest mini-me mini-me.noexec mini-me.setuid $(EXTRA_PROGS)
-OBJECTS=capsicum-test-main.o capsicum-test.o capability-fd.o fexecve.o procdesc.o capmode.o fcntl.o ioctl.o openat.o sysctl.o select.o mqueue.o socket.o sctp.o capability-fd-pair.o linux.o overhead.o rename.o
+OBJECTS=capsicum-test-main.o capsicum-test.o capability-fd.o copy_file_range.o fexecve.o procdesc.o capmode.o fcntl.o ioctl.o openat.o sysctl.o select.o mqueue.o socket.o sctp.o capability-fd-pair.o linux.o overhead.o rename.o
 
 GTEST_DIR=gtest-1.10.0
 GTEST_INCS=-I$(GTEST_DIR)/include -I$(GTEST_DIR)
diff --git a/tests/sys/capsicum/Makefile b/tests/sys/capsicum/Makefile
index 542c6720521c..81cb4fa1ceee 100644
--- a/tests/sys/capsicum/Makefile
+++ b/tests/sys/capsicum/Makefile
@@ -19,6 +19,7 @@ SRCS.capsicum-test+=	\
 	capsicum-test-main.cc \
 	capsicum-test.cc \
 	capability-fd.cc \
+	copy_file_range.cc \
 	fexecve.cc \
 	procdesc.cc \
 	capmode.cc \