Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 1 Apr 2019 21:24:50 +0000 (UTC)
From:      Enji Cooper <ngie@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r345783 - in head: contrib/capsicum-test tests/sys/capsicum
Message-ID:  <201904012124.x31LOoVg071263@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: ngie
Date: Mon Apr  1 21:24:50 2019
New Revision: 345783
URL: https://svnweb.freebsd.org/changeset/base/345783

Log:
  Integrate capsicum-test into the FreeBSD test suite
  
  This change takes capsicum-test from upstream and applies some local changes to make the
  tests work on FreeBSD when executed via Kyua.
  
  The local modifications are as follows:
  1. Make `OpenatTest.WithFlag` pass with the new dot-dot lookup behavior in FreeBSD 12.x+.
  2. capsicum-test references a set of helper binaries: `mini-me`, `mini-me.noexec`, and
     `mini-me.setuid`, as part of the execve/fexecve tests, via execve, fexecve, and open.
     It achieves this upstream by assuming `mini-me*` is in the current directory, however,
     in order for Kyua to execute `capsicum-test`, it needs to provide a full path to
     `mini-me*`. In order to achieve this, I made `capsicum-test` cache the executable's
     path from argv[0] in main(..) and use the cached value to compute the path to
     `mini-me*` as part of the execve/fexecve testcases.
  3. The capsicum-test test suite assumes that it's always being run on CAPABILITIES enabled
     kernels. However, there's a chance that the test will be run on a host without a
     CAPABILITIES enabled kernel, so we must check for the support before running the tests.
     The way to achieve this is to add the relevant `feature_present("security_capabilities")`
     check to SetupEnvironment::SetUp() and skip the tests when the support is not available.
     While here, add a check for `kern.trap_enotcap` being enabled. As noted by markj@ in
     https://github.com/google/capsicum-test/issues/23, this sysctl being enabled can trigger
     non-deterministic failures. Therefore, the tests should be skipped if this sysctl is
     enabled.
  
  All local changes have been submitted to the capsicum-test project
  (https://github.com/google/capsicum-test) and are in various stages of review.
  Please see the following pull requests for more details:
  1. https://github.com/google/capsicum-test/pull/35
  2. https://github.com/google/capsicum-test/pull/41
  3. https://github.com/google/capsicum-test/pull/42
  
  Reviewed by:	asomers
  Discussed with:	emaste, markj
  Approved by:	emaste (mentor)
  MFC after:	2 months
  Differential Revision: https://reviews.freebsd.org/D19758

Added:
  head/contrib/capsicum-test/
     - copied from r345782, vendor/google/capsicum-test/dist/
Modified:
  head/contrib/capsicum-test/capsicum-test-main.cc
  head/contrib/capsicum-test/capsicum.h
  head/contrib/capsicum-test/fexecve.cc
  head/contrib/capsicum-test/openat.cc
  head/tests/sys/capsicum/Makefile

Modified: head/contrib/capsicum-test/capsicum-test-main.cc
==============================================================================
--- vendor/google/capsicum-test/dist/capsicum-test-main.cc	Mon Apr  1 21:04:13 2019	(r345782)
+++ head/contrib/capsicum-test/capsicum-test-main.cc	Mon Apr  1 21:24:50 2019	(r345783)
@@ -2,16 +2,25 @@
 #ifdef __linux__
 #include <sys/vfs.h>
 #include <linux/magic.h>
+#elif defined(__FreeBSD__)
+#include <sys/sysctl.h>
 #endif
 #include <ctype.h>
 #include <errno.h>
+#include <libgen.h>
 #include <pwd.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <unistd.h>
 #include <iostream>
 #include "gtest/gtest.h"
 #include "capsicum-test.h"
 
+// For versions of googletest that lack GTEST_SKIP.
+#ifndef GTEST_SKIP
+#define GTEST_SKIP GTEST_FAIL
+#endif
+
 std::string tmpdir;
 
 class SetupEnvironment : public ::testing::Environment
@@ -19,6 +28,7 @@ class SetupEnvironment : public ::testing::Environment
 public:
   SetupEnvironment() : teardown_tmpdir_(false) {}
   void SetUp() override {
+    CheckCapsicumSupport();
     if (tmpdir.empty()) {
       std::cerr << "Generating temporary directory root: ";
       CreateTemporaryRoot();
@@ -27,6 +37,31 @@ class SetupEnvironment : public ::testing::Environment
     }
     std::cerr << tmpdir << std::endl;
   }
+  void CheckCapsicumSupport() {
+#ifdef __FreeBSD__
+    int rc;
+    bool trap_enotcap_enabled;
+    size_t trap_enotcap_enabled_len = sizeof(trap_enotcap_enabled);
+
+    if (feature_present("security_capabilities") == 0) {
+      GTEST_SKIP() << "Skipping tests because capsicum support is not "
+                   << "enabled in the kernel.";
+    }
+    // If this OID is enabled, it will send SIGTRAP to the process when
+    // `ENOTCAPABLE` is returned.
+    const char *oid = "kern.trap_enotcap";
+    rc = sysctlbyname(oid, &trap_enotcap_enabled, &trap_enotcap_enabled_len,
+      nullptr, 0);
+    if (rc != 0) {
+      GTEST_FAIL() << "sysctlbyname failed: " << strerror(errno);
+    }
+    if (trap_enotcap_enabled) {
+      GTEST_SKIP() << "Debug sysctl, " << oid << ", enabled. "
+                   << "Skipping tests because its enablement invalidates the "
+                   << "test results.";
+    }
+#endif /* FreeBSD */
+  }
   void CreateTemporaryRoot() {
     char *tmpdir_name = tempnam(nullptr, "cptst");
 
@@ -47,7 +82,27 @@ class SetupEnvironment : public ::testing::Environment
   bool teardown_tmpdir_;
 };
 
+std::string capsicum_test_bindir;
+
 int main(int argc, char* argv[]) {
+  // Set up the test program path, so capsicum-test can find programs, like
+  // mini-me* when executed from an absolute path.
+  {
+    char *new_path, *old_path, *program_name;
+
+    program_name = strdup(argv[0]);
+    assert(program_name);
+    capsicum_test_bindir = std::string(dirname(program_name));
+    free(program_name);
+
+    old_path = getenv("PATH");
+    assert(old_path);
+
+    assert(asprintf(&new_path, "%s:%s", capsicum_test_bindir.c_str(),
+      old_path) > 0);
+    assert(setenv("PATH", new_path, 1) == 0);
+  }
+
   ::testing::InitGoogleTest(&argc, argv);
   for (int ii = 1; ii < argc; ii++) {
     if (strcmp(argv[ii], "-v") == 0) {

Modified: head/contrib/capsicum-test/capsicum.h
==============================================================================
--- vendor/google/capsicum-test/dist/capsicum.h	Mon Apr  1 21:04:13 2019	(r345782)
+++ head/contrib/capsicum-test/capsicum.h	Mon Apr  1 21:24:50 2019	(r345783)
@@ -167,4 +167,9 @@ static inline void cap_rights_describe(const cap_right
 
 #endif  /* new/old style rights manipulation */
 
+#ifdef __cplusplus
+#include <string>
+extern std::string capsicum_test_bindir;
+#endif
+
 #endif /*__CAPSICUM_H__*/

Modified: head/contrib/capsicum-test/fexecve.cc
==============================================================================
--- vendor/google/capsicum-test/dist/fexecve.cc	Mon Apr  1 21:04:13 2019	(r345782)
+++ head/contrib/capsicum-test/fexecve.cc	Mon Apr  1 21:24:50 2019	(r345783)
@@ -1,12 +1,12 @@
-#include <errno.h>
-#include <string.h>
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <sys/stat.h>
+#include <errno.h>
 #include <fcntl.h>
-#include <unistd.h>
 #include <limits.h>
 #include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
 
 #include <sstream>
 
@@ -14,41 +14,76 @@
 #include "capsicum.h"
 #include "capsicum-test.h"
 
-// We need a program to exec(), but for fexecve() to work in capability
-// mode that program needs to be statically linked (otherwise ld.so will
-// attempt to traverse the filesystem to load (e.g.) /lib/libc.so and
-// fail).
-#define EXEC_PROG "./mini-me"
-#define EXEC_PROG_NOEXEC  EXEC_PROG ".noexec"
-#define EXEC_PROG_SETUID  EXEC_PROG ".setuid"
-
 // Arguments to use in execve() calls.
-static char* argv_pass[] = {(char*)EXEC_PROG, (char*)"--pass", NULL};
-static char* argv_fail[] = {(char*)EXEC_PROG, (char*)"--fail", NULL};
-static char* argv_checkroot[] = {(char*)EXEC_PROG, (char*)"--checkroot", NULL};
 static char* null_envp[] = {NULL};
 
 class Execve : public ::testing::Test {
  public:
-  Execve() : exec_fd_(open(EXEC_PROG, O_RDONLY)) {
+  Execve() : exec_fd_(-1) {
+    // We need a program to exec(), but for fexecve() to work in capability
+    // mode that program needs to be statically linked (otherwise ld.so will
+    // attempt to traverse the filesystem to load (e.g.) /lib/libc.so and
+    // fail).
+    exec_prog_ = capsicum_test_bindir + "/mini-me";
+    exec_prog_noexec_ = capsicum_test_bindir + "/mini-me.noexec";
+    exec_prog_setuid_ = capsicum_test_bindir + "/mini-me.setuid";
+
+    exec_fd_ = open(exec_prog_.c_str(), O_RDONLY);
     if (exec_fd_ < 0) {
-      fprintf(stderr, "Error! Failed to open %s\n", EXEC_PROG);
+      fprintf(stderr, "Error! Failed to open %s\n", exec_prog_.c_str());
     }
+    argv_checkroot_[0] = (char*)exec_prog_.c_str();
+    argv_fail_[0] = (char*)exec_prog_.c_str();
+    argv_pass_[0] = (char*)exec_prog_.c_str();
   }
-  ~Execve() { if (exec_fd_ >= 0) close(exec_fd_); }
+  ~Execve() {
+    if (exec_fd_ >= 0) {
+      close(exec_fd_);
+      exec_fd_ = -1;
+    }
+  }
 protected:
+  char* argv_checkroot_[3] = {nullptr, (char*)"--checkroot", nullptr};
+  char* argv_fail_[3] = {nullptr, (char*)"--fail", nullptr};
+  char* argv_pass_[3] = {nullptr, (char*)"--pass", nullptr};
+  std::string exec_prog_, exec_prog_noexec_, exec_prog_setuid_;
   int exec_fd_;
 };
 
+class Fexecve : public Execve {
+ public:
+  Fexecve() : Execve() {}
+};
+
+class FexecveWithScript : public Fexecve {
+ public:
+  FexecveWithScript() :
+    Fexecve(), temp_script_filename_(TmpFile("cap_sh_script")) {}
+
+  void SetUp() override {
+    // First, build an executable shell script
+    int fd = open(temp_script_filename_, O_RDWR|O_CREAT, 0755);
+    EXPECT_OK(fd);
+    const char* contents = "#!/bin/sh\nexit 99\n";
+    EXPECT_OK(write(fd, contents, strlen(contents)));
+    close(fd);
+  }
+  void TearDown() override {
+    (void)::unlink(temp_script_filename_);
+  }
+
+  const char *temp_script_filename_;
+};
+
 FORK_TEST_F(Execve, BasicFexecve) {
-  EXPECT_OK(fexecve_(exec_fd_, argv_pass, null_envp));
+  EXPECT_OK(fexecve_(exec_fd_, argv_pass_, null_envp));
   // Should not reach here, exec() takes over.
   EXPECT_TRUE(!"fexecve() should never return");
 }
 
 FORK_TEST_F(Execve, InCapMode) {
   EXPECT_OK(cap_enter());
-  EXPECT_OK(fexecve_(exec_fd_, argv_pass, null_envp));
+  EXPECT_OK(fexecve_(exec_fd_, argv_pass_, null_envp));
   // Should not reach here, exec() takes over.
   EXPECT_TRUE(!"fexecve() should never return");
 }
@@ -60,7 +95,7 @@ FORK_TEST_F(Execve, FailWithoutCap) {
   cap_rights_t rights;
   cap_rights_init(&rights, 0);
   EXPECT_OK(cap_rights_limit(cap_fd, &rights));
-  EXPECT_EQ(-1, fexecve_(cap_fd, argv_fail, null_envp));
+  EXPECT_EQ(-1, fexecve_(cap_fd, argv_fail_, null_envp));
   EXPECT_EQ(ENOTCAPABLE, errno);
 }
 
@@ -73,59 +108,54 @@ FORK_TEST_F(Execve, SucceedWithCap) {
   // rights -- just CAP_FEXECVE|CAP_READ or CAP_FEXECVE would be preferable.
   cap_rights_init(&rights, CAP_FEXECVE, CAP_LOOKUP, CAP_READ);
   EXPECT_OK(cap_rights_limit(cap_fd, &rights));
-  EXPECT_OK(fexecve_(cap_fd, argv_pass, null_envp));
+  EXPECT_OK(fexecve_(cap_fd, argv_pass_, null_envp));
   // Should not reach here, exec() takes over.
   EXPECT_TRUE(!"fexecve() should have succeeded");
 }
 
-FORK_TEST(Fexecve, ExecutePermissionCheck) {
-  int fd = open(EXEC_PROG_NOEXEC, O_RDONLY);
+FORK_TEST_F(Fexecve, ExecutePermissionCheck) {
+  int fd = open(exec_prog_noexec_.c_str(), O_RDONLY);
   EXPECT_OK(fd);
   if (fd >= 0) {
     struct stat data;
     EXPECT_OK(fstat(fd, &data));
     EXPECT_EQ((mode_t)0, data.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH));
-    EXPECT_EQ(-1, fexecve_(fd, argv_fail, null_envp));
+    EXPECT_EQ(-1, fexecve_(fd, argv_fail_, null_envp));
     EXPECT_EQ(EACCES, errno);
     close(fd);
   }
 }
 
-FORK_TEST(Fexecve, SetuidIgnored) {
+FORK_TEST_F(Fexecve, SetuidIgnored) {
   if (geteuid() == 0) {
     TEST_SKIPPED("requires non-root");
     return;
   }
-  int fd = open(EXEC_PROG_SETUID, O_RDONLY);
+  int fd = open(exec_prog_setuid_.c_str(), O_RDONLY);
   EXPECT_OK(fd);
   EXPECT_OK(cap_enter());
   if (fd >= 0) {
     struct stat data;
     EXPECT_OK(fstat(fd, &data));
     EXPECT_EQ((mode_t)S_ISUID, data.st_mode & S_ISUID);
-    EXPECT_OK(fexecve_(fd, argv_checkroot, null_envp));
+    EXPECT_OK(fexecve_(fd, argv_checkroot_, null_envp));
     // Should not reach here, exec() takes over.
     EXPECT_TRUE(!"fexecve() should have succeeded");
     close(fd);
   }
 }
 
-FORK_TEST(Fexecve, ExecveFailure) {
+FORK_TEST_F(Fexecve, ExecveFailure) {
   EXPECT_OK(cap_enter());
-  EXPECT_EQ(-1, execve(argv_fail[0], argv_fail, null_envp));
+  EXPECT_EQ(-1, execve(argv_fail_[0], argv_fail_, null_envp));
   EXPECT_EQ(ECAPMODE, errno);
 }
 
-FORK_TEST_ON(Fexecve, CapModeScriptFail, TmpFile("cap_sh_script")) {
-  // First, build an executable shell script
-  int fd = open(TmpFile("cap_sh_script"), O_RDWR|O_CREAT, 0755);
-  EXPECT_OK(fd);
-  const char* contents = "#!/bin/sh\nexit 99\n";
-  EXPECT_OK(write(fd, contents, strlen(contents)));
-  close(fd);
+FORK_TEST_F(FexecveWithScript, CapModeScriptFail) {
+  int fd;
 
   // Open the script file, with CAP_FEXECVE rights.
-  fd = open(TmpFile("cap_sh_script"), O_RDONLY);
+  fd = open(temp_script_filename_, O_RDONLY);
   cap_rights_t rights;
   cap_rights_init(&rights, CAP_FEXECVE, CAP_READ, CAP_SEEK);
   EXPECT_OK(cap_rights_limit(fd, &rights));
@@ -133,12 +163,17 @@ FORK_TEST_ON(Fexecve, CapModeScriptFail, TmpFile("cap_
   EXPECT_OK(cap_enter());  // Enter capability mode
 
   // Attempt fexecve; should fail, because "/bin/sh" is inaccessible.
-  EXPECT_EQ(-1, fexecve_(fd, argv_pass, null_envp));
+  EXPECT_EQ(-1, fexecve_(fd, argv_pass_, null_envp));
 }
 
 #ifdef HAVE_EXECVEAT
-TEST(Execveat, NoUpwardTraversal) {
-  char *abspath = realpath(EXEC_PROG, NULL);
+class Execveat : public Execve {
+ public:
+  Execveat() : Execve() {}
+};
+
+TEST_F(Execveat, NoUpwardTraversal) {
+  char *abspath = realpath(exec_prog_, NULL);
   char cwd[1024];
   getcwd(cwd, sizeof(cwd));
 
@@ -148,9 +183,9 @@ TEST(Execveat, NoUpwardTraversal) {
     EXPECT_OK(cap_enter());  // Enter capability mode.
     // Can't execveat() an absolute path, even relative to a dfd.
     EXPECT_SYSCALL_FAIL(ECAPMODE,
-                        execveat(AT_FDCWD, abspath, argv_pass, null_envp, 0));
+                        execveat(AT_FDCWD, abspath, argv_pass_, null_envp, 0));
     EXPECT_SYSCALL_FAIL(E_NO_TRAVERSE_CAPABILITY,
-                        execveat(dfd, abspath, argv_pass, null_envp, 0));
+                        execveat(dfd, abspath, argv_pass_, null_envp, 0));
 
     // Can't execveat() a relative path ("../<dir>/./<exe>").
     char *p = cwd + strlen(cwd);
@@ -158,9 +193,9 @@ TEST(Execveat, NoUpwardTraversal) {
     char buffer[1024] = "../";
     strcat(buffer, ++p);
     strcat(buffer, "/");
-    strcat(buffer, EXEC_PROG);
+    strcat(buffer, exec_prog_);
     EXPECT_SYSCALL_FAIL(E_NO_TRAVERSE_CAPABILITY,
-                        execveat(dfd, buffer, argv_pass, null_envp, 0));
+                        execveat(dfd, buffer, argv_pass_, null_envp, 0));
     exit(HasFailure() ? 99 : 123);
   }
   int status;

Modified: head/contrib/capsicum-test/openat.cc
==============================================================================
--- vendor/google/capsicum-test/dist/openat.cc	Mon Apr  1 21:04:13 2019	(r345782)
+++ head/contrib/capsicum-test/openat.cc	Mon Apr  1 21:24:50 2019	(r345783)
@@ -148,7 +148,7 @@ FORK_TEST(Openat, Relative) {
 }
 
 #define TOPDIR "cap_topdir"
-#define SUBDIR_ABS TOPDIR "/subdir"
+#define SUBDIR TOPDIR "/subdir"
 class OpenatTest : public ::testing::Test {
  public:
   // Build a collection of files, subdirs and symlinks:
@@ -156,20 +156,20 @@ class OpenatTest : public ::testing::Test {
   //                 /topfile
   //                 /subdir/
   //                 /subdir/bottomfile
-  //                 /symlink.samedir       -> topfile
-  //                 /dsymlink.samedir      -> ./
-  //                 /symlink.down          -> subdir/bottomfile
-  //                 /dsymlink.down         -> subdir/
-  //                 /symlink.absolute_in   -> /tmp/cap_topdir/topfile
-  //                 /dsymlink.absolute_in  -> /tmp/cap_topdir/
-  //                 /symlink.absolute_out  -> /etc/passwd
-  //                 /dsymlink.absolute_out -> /etc/
-  //                 /symlink.relative_in   -> ../../tmp/cap_topdir/topfile
-  //                 /dsymlink.relative_in  -> ../../tmp/cap_topdir/
-  //                 /symlink.relative_out  -> ../../etc/passwd
-  //                 /dsymlink.relative_out -> ../../etc/
-  //                 /subdir/symlink.up     -> ../topfile
-  //                 /subdir/dsymlink.up    -> ../
+  //                 /symlink.samedir              -> topfile
+  //                 /dsymlink.samedir             -> ./
+  //                 /symlink.down                 -> subdir/bottomfile
+  //                 /dsymlink.down                -> subdir/
+  //                 /symlink.absolute_out         -> /etc/passwd
+  //                 /dsymlink.absolute_out        -> /etc/
+  //                 /symlink.relative_in          -> ../../tmp/cap_topdir/topfile
+  //                 /dsymlink.relative_in         -> ../../tmp/cap_topdir/
+  //                 /symlink.relative_out         -> ../../etc/passwd
+  //                 /dsymlink.relative_out        -> ../../etc/
+  //                 /subdir/dsymlink.absolute_in  -> /tmp/cap_topdir/
+  //                 /subdir/dsymlink.up           -> ../
+  //                 /subdir/symlink.absolute_in   -> /tmp/cap_topdir/topfile
+  //                 /subdir/symlink.up            -> ../topfile
   // (In practice, this is a little more complicated because tmpdir might
   // not be "/tmp".)
   OpenatTest() {
@@ -179,7 +179,7 @@ class OpenatTest : public ::testing::Test {
     if (rc < 0) {
       EXPECT_EQ(EEXIST, errno);
     }
-    rc = mkdir(TmpFile(SUBDIR_ABS), 0755);
+    rc = mkdir(TmpFile(SUBDIR), 0755);
     EXPECT_OK(rc);
     if (rc < 0) {
       EXPECT_EQ(EEXIST, errno);
@@ -197,34 +197,34 @@ class OpenatTest : public ::testing::Test {
 
     // Create normal files in each.
     CreateFile(TmpFile(TOPDIR "/topfile"), "Top-level file");
-    CreateFile(TmpFile(SUBDIR_ABS "/bottomfile"), "File in subdirectory");
+    CreateFile(TmpFile(SUBDIR "/bottomfile"), "File in subdirectory");
 
     // Create various symlinks to files.
     EXPECT_OK(symlink("topfile", TmpFile(TOPDIR "/symlink.samedir")));
     EXPECT_OK(symlink("subdir/bottomfile", TmpFile(TOPDIR "/symlink.down")));
-    EXPECT_OK(symlink(TmpFile(TOPDIR "/topfile"), TmpFile(TOPDIR "/symlink.absolute_in")));
+    EXPECT_OK(symlink(TmpFile(TOPDIR "/topfile"), TmpFile(SUBDIR "/symlink.absolute_in")));
     EXPECT_OK(symlink("/etc/passwd", TmpFile(TOPDIR "/symlink.absolute_out")));
     std::string dots2top = dots2root + TmpFile(TOPDIR "/topfile");
     EXPECT_OK(symlink(dots2top.c_str(), TmpFile(TOPDIR "/symlink.relative_in")));
     std::string dots2passwd = dots2root + "/etc/passwd";
     EXPECT_OK(symlink(dots2passwd.c_str(), TmpFile(TOPDIR "/symlink.relative_out")));
-    EXPECT_OK(symlink("../topfile", TmpFile(SUBDIR_ABS "/symlink.up")));
+    EXPECT_OK(symlink("../topfile", TmpFile(SUBDIR "/symlink.up")));
 
     // Create various symlinks to directories.
     EXPECT_OK(symlink("./", TmpFile(TOPDIR "/dsymlink.samedir")));
     EXPECT_OK(symlink("subdir/", TmpFile(TOPDIR "/dsymlink.down")));
-    EXPECT_OK(symlink(TmpFile(TOPDIR "/"), TmpFile(TOPDIR "/dsymlink.absolute_in")));
+    EXPECT_OK(symlink(TmpFile(TOPDIR "/"), TmpFile(SUBDIR "/dsymlink.absolute_in")));
     EXPECT_OK(symlink("/etc/", TmpFile(TOPDIR "/dsymlink.absolute_out")));
     std::string dots2cwd = dots2root + tmpdir + "/";
     EXPECT_OK(symlink(dots2cwd.c_str(), TmpFile(TOPDIR "/dsymlink.relative_in")));
     std::string dots2etc = dots2root + "/etc/";
     EXPECT_OK(symlink(dots2etc.c_str(), TmpFile(TOPDIR "/dsymlink.relative_out")));
-    EXPECT_OK(symlink("../", TmpFile(SUBDIR_ABS "/dsymlink.up")));
+    EXPECT_OK(symlink("../", TmpFile(SUBDIR "/dsymlink.up")));
 
     // Open directory FDs for those directories and for cwd.
     dir_fd_ = open(TmpFile(TOPDIR), O_RDONLY);
     EXPECT_OK(dir_fd_);
-    sub_fd_ = open(TmpFile(SUBDIR_ABS), O_RDONLY);
+    sub_fd_ = open(TmpFile(SUBDIR), O_RDONLY);
     EXPECT_OK(sub_fd_);
     cwd_ = openat(AT_FDCWD, ".", O_RDONLY);
     EXPECT_OK(cwd_);
@@ -236,23 +236,23 @@ class OpenatTest : public ::testing::Test {
     close(cwd_);
     close(sub_fd_);
     close(dir_fd_);
-    unlink(TmpFile(SUBDIR_ABS "/symlink.up"));
-    unlink(TmpFile(TOPDIR "/symlink.absolute_in"));
+    unlink(TmpFile(SUBDIR "/symlink.up"));
+    unlink(TmpFile(SUBDIR "/symlink.absolute_in"));
     unlink(TmpFile(TOPDIR "/symlink.absolute_out"));
     unlink(TmpFile(TOPDIR "/symlink.relative_in"));
     unlink(TmpFile(TOPDIR "/symlink.relative_out"));
     unlink(TmpFile(TOPDIR "/symlink.down"));
     unlink(TmpFile(TOPDIR "/symlink.samedir"));
-    unlink(TmpFile(SUBDIR_ABS "/dsymlink.up"));
-    unlink(TmpFile(TOPDIR "/dsymlink.absolute_in"));
+    unlink(TmpFile(SUBDIR "/dsymlink.up"));
+    unlink(TmpFile(SUBDIR "/dsymlink.absolute_in"));
     unlink(TmpFile(TOPDIR "/dsymlink.absolute_out"));
     unlink(TmpFile(TOPDIR "/dsymlink.relative_in"));
     unlink(TmpFile(TOPDIR "/dsymlink.relative_out"));
     unlink(TmpFile(TOPDIR "/dsymlink.down"));
     unlink(TmpFile(TOPDIR "/dsymlink.samedir"));
-    unlink(TmpFile(SUBDIR_ABS "/bottomfile"));
+    unlink(TmpFile(SUBDIR "/bottomfile"));
     unlink(TmpFile(TOPDIR "/topfile"));
-    rmdir(TmpFile(SUBDIR_ABS));
+    rmdir(TmpFile(SUBDIR));
     rmdir(TmpFile(TOPDIR));
   }
 
@@ -281,18 +281,18 @@ class OpenatTest : public ::testing::Test {
     // Should only be able to open symlinks that stay within the directory.
     EXPECT_OPEN_OK(openat(dir_fd_, "symlink.samedir", O_RDONLY|oflag));
     EXPECT_OPEN_OK(openat(dir_fd_, "symlink.down", O_RDONLY|oflag));
-    EXPECT_OPENAT_FAIL_TRAVERSAL(dir_fd_, "symlink.absolute_in", O_RDONLY|oflag);
     EXPECT_OPENAT_FAIL_TRAVERSAL(dir_fd_, "symlink.absolute_out", O_RDONLY|oflag);
     EXPECT_OPENAT_FAIL_TRAVERSAL(dir_fd_, "symlink.relative_in", O_RDONLY|oflag);
     EXPECT_OPENAT_FAIL_TRAVERSAL(dir_fd_, "symlink.relative_out", O_RDONLY|oflag);
+    EXPECT_OPENAT_FAIL_TRAVERSAL(sub_fd_, "symlink.absolute_in", O_RDONLY|oflag);
     EXPECT_OPENAT_FAIL_TRAVERSAL(sub_fd_, "symlink.up", O_RDONLY|oflag);
 
     EXPECT_OPEN_OK(openat(dir_fd_, "dsymlink.samedir/topfile", O_RDONLY|oflag));
     EXPECT_OPEN_OK(openat(dir_fd_, "dsymlink.down/bottomfile", O_RDONLY|oflag));
-    EXPECT_OPENAT_FAIL_TRAVERSAL(dir_fd_, "dsymlink.absolute_in/topfile", O_RDONLY|oflag);
     EXPECT_OPENAT_FAIL_TRAVERSAL(dir_fd_, "dsymlink.absolute_out/passwd", O_RDONLY|oflag);
     EXPECT_OPENAT_FAIL_TRAVERSAL(dir_fd_, "dsymlink.relative_in/topfile", O_RDONLY|oflag);
     EXPECT_OPENAT_FAIL_TRAVERSAL(dir_fd_, "dsymlink.relative_out/passwd", O_RDONLY|oflag);
+    EXPECT_OPENAT_FAIL_TRAVERSAL(sub_fd_, "dsymlink.absolute_in/topfile", O_RDONLY|oflag);
     EXPECT_OPENAT_FAIL_TRAVERSAL(sub_fd_, "dsymlink.up/topfile", O_RDONLY|oflag);
 
     // Although recall that O_NOFOLLOW prevents symlink following in final component.
@@ -310,10 +310,10 @@ TEST_F(OpenatTest, WithCapability) {
   // Any kind of symlink can be opened relative to an ordinary directory FD.
   EXPECT_OPEN_OK(openat(dir_fd_, "symlink.samedir", O_RDONLY));
   EXPECT_OPEN_OK(openat(dir_fd_, "symlink.down", O_RDONLY));
-  EXPECT_OPEN_OK(openat(dir_fd_, "symlink.absolute_in", O_RDONLY));
   EXPECT_OPEN_OK(openat(dir_fd_, "symlink.absolute_out", O_RDONLY));
   EXPECT_OPEN_OK(openat(dir_fd_, "symlink.relative_in", O_RDONLY));
   EXPECT_OPEN_OK(openat(dir_fd_, "symlink.relative_out", O_RDONLY));
+  EXPECT_OPEN_OK(openat(sub_fd_, "symlink.absolute_in", O_RDONLY));
   EXPECT_OPEN_OK(openat(sub_fd_, "symlink.up", O_RDONLY));
 
   // Now make both DFDs into Capsicum capabilities.

Modified: head/tests/sys/capsicum/Makefile
==============================================================================
--- head/tests/sys/capsicum/Makefile	Mon Apr  1 21:04:13 2019	(r345782)
+++ head/tests/sys/capsicum/Makefile	Mon Apr  1 21:24:50 2019	(r345783)
@@ -1,11 +1,56 @@
 # $FreeBSD$
 
+.include <src.opts.mk>
+
 TESTSDIR=	${TESTSBASE}/sys/capsicum
 
 ATF_TESTS_C+=	bindat_connectat
 ATF_TESTS_C+=	ioctls_test
 
 CFLAGS+=	-I${SRCTOP}/tests
+
+.if ${MK_GOOGLETEST} != no
+
+.PATH: ${SRCTOP}/contrib/capsicum-test
+
+GTESTS+=	capsicum-test
+
+SRCS.capsicum-test+=	\
+	capsicum-test-main.cc \
+	capsicum-test.cc \
+	capability-fd.cc \
+	fexecve.cc \
+	procdesc.cc \
+	capmode.cc \
+	fcntl.cc \
+	ioctl.cc \
+	openat.cc \
+	sysctl.cc \
+	select.cc \
+	mqueue.cc \
+	socket.cc \
+	sctp.cc \
+	capability-fd-pair.cc \
+	overhead.cc \
+	rename.cc
+
+LIBADD.capsicum-test+=	gtest pthread
+TEST_METADATA.capsicum-test=	required_user="unprivileged"
+
+.for p in mini-me mini-me.noexec mini-me.setuid
+PROGS+=		$p
+NO_SHARED.$p=
+SRCS.$p=	mini-me.c
+.endfor
+
+BINDIR=	${TESTSDIR}
+
+BINMODE.mini-me.noexec=	${NOBINMODE}
+BINMODE.mini-me.setuid=	4555
+
+WARNS.capsicum-test=	3
+
+.endif
 
 WARNS?=	6
 



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201904012124.x31LOoVg071263>