diff options
Diffstat (limited to 'test')
-rw-r--r-- | test/e2e/exec_test.go | 22 | ||||
-rw-r--r-- | test/iptables/filter_input.go | 14 | ||||
-rw-r--r-- | test/iptables/iptables_util.go | 2 | ||||
-rw-r--r-- | test/packetdrill/fin_wait2_timeout.pkt | 2 | ||||
-rw-r--r-- | test/packetimpact/tests/defs.bzl | 4 | ||||
-rw-r--r-- | test/perf/linux/futex_benchmark.cc | 144 | ||||
-rw-r--r-- | test/runner/runner.go | 27 | ||||
-rw-r--r-- | test/syscalls/linux/proc_net.cc | 53 | ||||
-rw-r--r-- | test/syscalls/linux/seccomp.cc | 40 | ||||
-rw-r--r-- | test/syscalls/linux/stat.cc | 11 | ||||
-rw-r--r-- | test/syscalls/linux/sticky.cc | 16 | ||||
-rw-r--r-- | test/syscalls/linux/sysret.cc | 9 |
12 files changed, 191 insertions, 153 deletions
diff --git a/test/e2e/exec_test.go b/test/e2e/exec_test.go index 4074d2285..594c8e752 100644 --- a/test/e2e/exec_test.go +++ b/test/e2e/exec_test.go @@ -240,17 +240,7 @@ func TestExecEnvHasHome(t *testing.T) { } d := dockerutil.MakeDocker("exec-env-home-test") - // We will check that HOME is set for root user, and also for a new - // non-root user we will create. - newUID := 1234 - newHome := "/foo/bar" - - // Create a new user with a home directory, and then sleep. - script := fmt.Sprintf(` - mkdir -p -m 777 %s && \ - adduser foo -D -u %d -h %s && \ - sleep 1000`, newHome, newUID, newHome) - if err := d.Run("alpine", "/bin/sh", "-c", script); err != nil { + if err := d.Run("alpine", "sleep", "1000"); err != nil { t.Fatalf("docker run failed: %v", err) } defer d.CleanUp() @@ -264,7 +254,15 @@ func TestExecEnvHasHome(t *testing.T) { t.Errorf("wanted exec output to contain %q, got %q", want, got) } - // Execute the same as uid 123 and expect newHome. + // Create a new user with a home directory. + newUID := 1234 + newHome := "/foo/bar" + cmd := fmt.Sprintf("mkdir -p -m 777 %q && adduser foo -D -u %d -h %q", newHome, newUID, newHome) + if _, err := d.Exec("/bin/sh", "-c", cmd); err != nil { + t.Fatalf("docker exec failed: %v", err) + } + + // Execute the same as the new user and expect newHome. got, err = d.ExecAsUser(strconv.Itoa(newUID), "/bin/sh", "-c", "echo $HOME") if err != nil { t.Fatalf("docker exec failed: %v", err) diff --git a/test/iptables/filter_input.go b/test/iptables/filter_input.go index 05647de33..41e0cfa8d 100644 --- a/test/iptables/filter_input.go +++ b/test/iptables/filter_input.go @@ -194,8 +194,11 @@ func (FilterInputDropTCPDestPort) ContainerAction(ip net.IP) error { // LocalAction implements TestCase.LocalAction. func (FilterInputDropTCPDestPort) LocalAction(ip net.IP) error { - if err := connectTCP(ip, dropPort, sendloopDuration); err == nil { - return fmt.Errorf("connection destined to port %d should not be accepted, but got accepted", dropPort) + // Ensure we cannot connect to the container. + for start := time.Now(); time.Since(start) < sendloopDuration; { + if err := connectTCP(ip, dropPort, sendloopDuration-time.Since(start)); err == nil { + return fmt.Errorf("expected not to connect, but was able to connect on port %d", dropPort) + } } return nil @@ -226,8 +229,11 @@ func (FilterInputDropTCPSrcPort) ContainerAction(ip net.IP) error { // LocalAction implements TestCase.LocalAction. func (FilterInputDropTCPSrcPort) LocalAction(ip net.IP) error { - if err := connectTCP(ip, acceptPort, sendloopDuration); err == nil { - return fmt.Errorf("connection should not be accepted, but was") + // Ensure we cannot connect to the container. + for start := time.Now(); time.Since(start) < sendloopDuration; { + if err := connectTCP(ip, acceptPort, sendloopDuration-time.Since(start)); err == nil { + return fmt.Errorf("expected not to connect, but was able to connect on port %d", acceptPort) + } } return nil diff --git a/test/iptables/iptables_util.go b/test/iptables/iptables_util.go index e8ae65c5a..134391e8d 100644 --- a/test/iptables/iptables_util.go +++ b/test/iptables/iptables_util.go @@ -144,7 +144,7 @@ func connectTCP(ip net.IP, port int, timeout time.Duration) error { // The container may not be listening when we first connect, so retry // upon error. callback := func() error { - conn, err := net.DialTCP("tcp4", nil, &contAddr) + conn, err := net.DialTimeout("tcp", contAddr.String(), timeout) if conn != nil { conn.Close() } diff --git a/test/packetdrill/fin_wait2_timeout.pkt b/test/packetdrill/fin_wait2_timeout.pkt index 613f0bec9..93ab08575 100644 --- a/test/packetdrill/fin_wait2_timeout.pkt +++ b/test/packetdrill/fin_wait2_timeout.pkt @@ -19,5 +19,5 @@ +0 > F. 1:1(0) ack 1 <...> +0 < . 1:1(0) ack 2 win 257 -+1.1 < . 1:1(0) ack 2 win 257 ++2 < . 1:1(0) ack 2 win 257 +0 > R 2:2(0) win 0 diff --git a/test/packetimpact/tests/defs.bzl b/test/packetimpact/tests/defs.bzl index 3baac567a..1b4213d9b 100644 --- a/test/packetimpact/tests/defs.bzl +++ b/test/packetimpact/tests/defs.bzl @@ -71,7 +71,7 @@ def packetimpact_linux_test(name, testbench_binary, **kwargs): name = name + "_linux_test", testbench_binary = testbench_binary, flags = ["--dut_platform", "linux"], - tags = PACKETIMPACT_TAGS, + tags = PACKETIMPACT_TAGS + ["packetimpact"], **kwargs ) @@ -89,7 +89,7 @@ def packetimpact_netstack_test(name, testbench_binary, **kwargs): # This is the default runtime unless # "--test_arg=--runtime=OTHER_RUNTIME" is used to override the value. flags = ["--dut_platform", "netstack", "--runtime=runsc-d"], - tags = PACKETIMPACT_TAGS, + tags = PACKETIMPACT_TAGS + ["packetimpact"], **kwargs ) diff --git a/test/perf/linux/futex_benchmark.cc b/test/perf/linux/futex_benchmark.cc index b349d50bf..241f39896 100644 --- a/test/perf/linux/futex_benchmark.cc +++ b/test/perf/linux/futex_benchmark.cc @@ -33,24 +33,24 @@ namespace testing { namespace { inline int FutexWait(std::atomic<int32_t>* v, int32_t val) { - return syscall(SYS_futex, v, FUTEX_BITSET_MATCH_ANY, nullptr); + return syscall(SYS_futex, v, FUTEX_WAIT_PRIVATE, val, nullptr); } -inline int FutexWaitRelativeTimeout(std::atomic<int32_t>* v, int32_t val, - const struct timespec* reltime) { - return syscall(SYS_futex, v, FUTEX_WAIT_PRIVATE, reltime); +inline int FutexWaitMonotonicTimeout(std::atomic<int32_t>* v, int32_t val, + const struct timespec* timeout) { + return syscall(SYS_futex, v, FUTEX_WAIT_PRIVATE, val, timeout); } -inline int FutexWaitAbsoluteTimeout(std::atomic<int32_t>* v, int32_t val, - const struct timespec* abstime) { - return syscall(SYS_futex, v, FUTEX_BITSET_MATCH_ANY, abstime); +inline int FutexWaitMonotonicDeadline(std::atomic<int32_t>* v, int32_t val, + const struct timespec* deadline) { + return syscall(SYS_futex, v, FUTEX_WAIT_BITSET_PRIVATE, val, deadline, + nullptr, FUTEX_BITSET_MATCH_ANY); } -inline int FutexWaitBitsetAbsoluteTimeout(std::atomic<int32_t>* v, int32_t val, - int32_t bits, - const struct timespec* abstime) { +inline int FutexWaitRealtimeDeadline(std::atomic<int32_t>* v, int32_t val, + const struct timespec* deadline) { return syscall(SYS_futex, v, FUTEX_WAIT_BITSET_PRIVATE | FUTEX_CLOCK_REALTIME, - val, abstime, nullptr, bits); + val, deadline, nullptr, FUTEX_BITSET_MATCH_ANY); } inline int FutexWake(std::atomic<int32_t>* v, int32_t count) { @@ -62,11 +62,11 @@ void BM_FutexWakeNop(benchmark::State& state) { std::atomic<int32_t> v(0); for (auto _ : state) { - EXPECT_EQ(0, FutexWake(&v, 1)); + TEST_PCHECK(FutexWake(&v, 1) == 0); } } -BENCHMARK(BM_FutexWakeNop); +BENCHMARK(BM_FutexWakeNop)->MinTime(5); // This just uses FUTEX_WAIT on an address whose value has changed, i.e., the // syscall won't wait. @@ -74,43 +74,63 @@ void BM_FutexWaitNop(benchmark::State& state) { std::atomic<int32_t> v(0); for (auto _ : state) { - EXPECT_EQ(-EAGAIN, FutexWait(&v, 1)); + TEST_PCHECK(FutexWait(&v, 1) == -1 && errno == EAGAIN); } } -BENCHMARK(BM_FutexWaitNop); +BENCHMARK(BM_FutexWaitNop)->MinTime(5); // This uses FUTEX_WAIT with a timeout on an address whose value never // changes, such that it always times out. Timeout overhead can be estimated by // timer overruns for short timeouts. -void BM_FutexWaitTimeout(benchmark::State& state) { +void BM_FutexWaitMonotonicTimeout(benchmark::State& state) { const int timeout_ns = state.range(0); std::atomic<int32_t> v(0); auto ts = absl::ToTimespec(absl::Nanoseconds(timeout_ns)); for (auto _ : state) { - EXPECT_EQ(-ETIMEDOUT, FutexWaitRelativeTimeout(&v, 0, &ts)); + TEST_PCHECK(FutexWaitMonotonicTimeout(&v, 0, &ts) == -1 && + errno == ETIMEDOUT); } } -BENCHMARK(BM_FutexWaitTimeout) +BENCHMARK(BM_FutexWaitMonotonicTimeout) + ->MinTime(5) + ->UseRealTime() ->Arg(1) ->Arg(10) ->Arg(100) ->Arg(1000) ->Arg(10000); -// This calls FUTEX_WAIT_BITSET with CLOCK_REALTIME. -void BM_FutexWaitBitset(benchmark::State& state) { +// This uses FUTEX_WAIT_BITSET with a deadline that is in the past. This allows +// estimation of the overhead of setting up a timer for a deadline (as opposed +// to a timeout as specified for FUTEX_WAIT). +void BM_FutexWaitMonotonicDeadline(benchmark::State& state) { std::atomic<int32_t> v(0); - int timeout_ns = state.range(0); - auto ts = absl::ToTimespec(absl::Nanoseconds(timeout_ns)); + struct timespec ts = {}; + for (auto _ : state) { - EXPECT_EQ(-ETIMEDOUT, FutexWaitBitsetAbsoluteTimeout(&v, 0, 1, &ts)); + TEST_PCHECK(FutexWaitMonotonicDeadline(&v, 0, &ts) == -1 && + errno == ETIMEDOUT); } } -BENCHMARK(BM_FutexWaitBitset)->Range(0, 100000); +BENCHMARK(BM_FutexWaitMonotonicDeadline)->MinTime(5); + +// This is equivalent to BM_FutexWaitMonotonicDeadline, but uses CLOCK_REALTIME +// instead of CLOCK_MONOTONIC for the deadline. +void BM_FutexWaitRealtimeDeadline(benchmark::State& state) { + std::atomic<int32_t> v(0); + struct timespec ts = {}; + + for (auto _ : state) { + TEST_PCHECK(FutexWaitRealtimeDeadline(&v, 0, &ts) == -1 && + errno == ETIMEDOUT); + } +} + +BENCHMARK(BM_FutexWaitRealtimeDeadline)->MinTime(5); int64_t GetCurrentMonotonicTimeNanos() { struct timespec ts; @@ -130,11 +150,10 @@ void SpinNanos(int64_t delay_ns) { // Each iteration of FutexRoundtripDelayed involves a thread sending a futex // wakeup to another thread, which spins for delay_us and then sends a futex -// wakeup back. The time per iteration is 2* (delay_us + kBeforeWakeDelayNs + +// wakeup back. The time per iteration is 2 * (delay_us + kBeforeWakeDelayNs + // futex/scheduling overhead). void BM_FutexRoundtripDelayed(benchmark::State& state) { const int delay_us = state.range(0); - const int64_t delay_ns = delay_us * 1000; // Spin for an extra kBeforeWakeDelayNs before invoking FUTEX_WAKE to reduce // the probability that the wakeup comes before the wait, preventing the wait @@ -165,83 +184,14 @@ void BM_FutexRoundtripDelayed(benchmark::State& state) { } BENCHMARK(BM_FutexRoundtripDelayed) + ->MinTime(5) + ->UseRealTime() ->Arg(0) ->Arg(10) ->Arg(20) ->Arg(50) ->Arg(100); -// FutexLock is a simple, dumb futex based lock implementation. -// It will try to acquire the lock by atomically incrementing the -// lock word. If it did not increment the lock from 0 to 1, someone -// else has the lock, so it will FUTEX_WAIT until it is woken in -// the unlock path. -class FutexLock { - public: - FutexLock() : lock_word_(0) {} - - void lock(struct timespec* deadline) { - int32_t val; - while ((val = lock_word_.fetch_add(1, std::memory_order_acquire) + 1) != - 1) { - // If we didn't get the lock by incrementing from 0 to 1, - // do a FUTEX_WAIT with the desired current value set to - // val. If val is no longer what the atomic increment returned, - // someone might have set it to 0 so we can try to acquire - // again. - int ret = FutexWaitAbsoluteTimeout(&lock_word_, val, deadline); - if (ret == 0 || ret == -EWOULDBLOCK || ret == -EINTR) { - continue; - } else { - FAIL() << "unexpected FUTEX_WAIT return: " << ret; - } - } - } - - void unlock() { - // Store 0 into the lock word and wake one waiter. We intentionally - // ignore the return value of the FUTEX_WAKE here, since there may be - // no waiters to wake anyway. - lock_word_.store(0, std::memory_order_release); - (void)FutexWake(&lock_word_, 1); - } - - private: - std::atomic<int32_t> lock_word_; -}; - -FutexLock* test_lock; // Used below. - -void FutexContend(benchmark::State& state, int thread_index, - struct timespec* deadline) { - int counter = 0; - if (thread_index == 0) { - test_lock = new FutexLock(); - } - for (auto _ : state) { - test_lock->lock(deadline); - counter++; - test_lock->unlock(); - } - if (thread_index == 0) { - delete test_lock; - } - state.SetItemsProcessed(state.iterations()); -} - -void BM_FutexContend(benchmark::State& state) { - FutexContend(state, state.thread_index, nullptr); -} - -BENCHMARK(BM_FutexContend)->ThreadRange(1, 1024)->UseRealTime(); - -void BM_FutexDeadlineContend(benchmark::State& state) { - auto deadline = absl::ToTimespec(absl::Now() + absl::Minutes(10)); - FutexContend(state, state.thread_index, &deadline); -} - -BENCHMARK(BM_FutexDeadlineContend)->ThreadRange(1, 1024)->UseRealTime(); - } // namespace } // namespace testing diff --git a/test/runner/runner.go b/test/runner/runner.go index a78ef38e0..0d3742f71 100644 --- a/test/runner/runner.go +++ b/test/runner/runner.go @@ -300,6 +300,7 @@ func runTestCaseRunsc(testBin string, tc gtest.TestCase, t *testing.T) { // Test spec comes with pre-defined mounts that we don't want. Reset it. spec.Mounts = nil + testTmpDir := "/tmp" if *useTmpfs { // Forces '/tmp' to be mounted as tmpfs, otherwise test that rely on // features only available in gVisor's internal tmpfs may fail. @@ -325,11 +326,19 @@ func runTestCaseRunsc(testBin string, tc gtest.TestCase, t *testing.T) { t.Fatalf("could not chmod temp dir: %v", err) } - spec.Mounts = append(spec.Mounts, specs.Mount{ - Type: "bind", - Destination: "/tmp", - Source: tmpDir, - }) + // "/tmp" is not replaced with a tmpfs mount inside the sandbox + // when it's not empty. This ensures that testTmpDir uses gofer + // in exclusive mode. + testTmpDir = tmpDir + if *fileAccess == "shared" { + // All external mounts except the root mount are shared. + spec.Mounts = append(spec.Mounts, specs.Mount{ + Type: "bind", + Destination: "/tmp", + Source: tmpDir, + }) + testTmpDir = "/tmp" + } } // Set environment variables that indicate we are @@ -349,12 +358,8 @@ func runTestCaseRunsc(testBin string, tc gtest.TestCase, t *testing.T) { // Set TEST_TMPDIR to /tmp, as some of the syscall tests require it to // be backed by tmpfs. - for i, kv := range env { - if strings.HasPrefix(kv, "TEST_TMPDIR=") { - env[i] = "TEST_TMPDIR=/tmp" - break - } - } + env = filterEnv(env, []string{"TEST_TMPDIR"}) + env = append(env, fmt.Sprintf("TEST_TMPDIR=%s", testTmpDir)) spec.Process.Env = env diff --git a/test/syscalls/linux/proc_net.cc b/test/syscalls/linux/proc_net.cc index 05c952b99..4e23d1e78 100644 --- a/test/syscalls/linux/proc_net.cc +++ b/test/syscalls/linux/proc_net.cc @@ -92,6 +92,59 @@ TEST(ProcSysNetIpv4Sack, CanReadAndWrite) { EXPECT_EQ(buf, to_write); } +// DeviceEntry is an entry in /proc/net/dev +struct DeviceEntry { + std::string name; + uint64_t stats[16]; +}; + +PosixErrorOr<std::vector<DeviceEntry>> GetDeviceMetricsFromProc( + const std::string dev) { + std::vector<std::string> lines = absl::StrSplit(dev, '\n'); + std::vector<DeviceEntry> entries; + + // /proc/net/dev prints 2 lines of headers followed by a line of metrics for + // each network interface. + for (unsigned i = 2; i < lines.size(); i++) { + // Ignore empty lines. + if (lines[i].empty()) { + continue; + } + + std::vector<std::string> values = + absl::StrSplit(lines[i], ' ', absl::SkipWhitespace()); + + // Interface name + 16 values. + if (values.size() != 17) { + return PosixError(EINVAL, "invalid line: " + lines[i]); + } + + DeviceEntry entry; + entry.name = values[0]; + // Skip the interface name and read only the values. + for (unsigned j = 1; j < 17; j++) { + uint64_t num; + if (!absl::SimpleAtoi(values[j], &num)) { + return PosixError(EINVAL, "invalid value: " + values[j]); + } + entry.stats[j - 1] = num; + } + + entries.push_back(entry); + } + + return entries; +} + +// TEST(ProcNetDev, Format) tests that /proc/net/dev is parsable and +// contains at least one entry. +TEST(ProcNetDev, Format) { + auto dev = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/net/dev")); + auto entries = ASSERT_NO_ERRNO_AND_VALUE(GetDeviceMetricsFromProc(dev)); + + EXPECT_GT(entries.size(), 0); +} + PosixErrorOr<uint64_t> GetSNMPMetricFromProc(const std::string snmp, const std::string& type, const std::string& item) { diff --git a/test/syscalls/linux/seccomp.cc b/test/syscalls/linux/seccomp.cc index 8e0fc9acc..ce88d90dd 100644 --- a/test/syscalls/linux/seccomp.cc +++ b/test/syscalls/linux/seccomp.cc @@ -70,20 +70,27 @@ void ApplySeccompFilter(uint32_t sysno, uint32_t filtered_result, MaybeSave(); struct sock_filter filter[] = { - // A = seccomp_data.arch - BPF_STMT(BPF_LD | BPF_ABS | BPF_W, 4), - // if (A != AUDIT_ARCH_X86_64) goto kill - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, AUDIT_ARCH_X86_64, 0, 4), - // A = seccomp_data.nr - BPF_STMT(BPF_LD | BPF_ABS | BPF_W, 0), - // if (A != sysno) goto allow - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, sysno, 0, 1), - // return filtered_result - BPF_STMT(BPF_RET | BPF_K, filtered_result), - // allow: return SECCOMP_RET_ALLOW - BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), - // kill: return SECCOMP_RET_KILL - BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL), + // A = seccomp_data.arch + BPF_STMT(BPF_LD | BPF_ABS | BPF_W, 4), +#if defined(__x86_64__) + // if (A != AUDIT_ARCH_X86_64) goto kill + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, AUDIT_ARCH_X86_64, 0, 4), +#elif defined(__aarch64__) + // if (A != AUDIT_ARCH_AARCH64) goto kill + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, AUDIT_ARCH_AARCH64, 0, 4), +#else +#error "Unknown architecture" +#endif + // A = seccomp_data.nr + BPF_STMT(BPF_LD | BPF_ABS | BPF_W, 0), + // if (A != sysno) goto allow + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, sysno, 0, 1), + // return filtered_result + BPF_STMT(BPF_RET | BPF_K, filtered_result), + // allow: return SECCOMP_RET_ALLOW + BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), + // kill: return SECCOMP_RET_KILL + BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL), }; struct sock_fprog prog; prog.len = ABSL_ARRAYSIZE(filter); @@ -179,9 +186,12 @@ TEST(SeccompTest, RetTrapCausesSIGSYS) { TEST_CHECK(info->si_errno == kTrapValue); TEST_CHECK(info->si_call_addr != nullptr); TEST_CHECK(info->si_syscall == kFilteredSyscall); -#ifdef __x86_64__ +#if defined(__x86_64__) TEST_CHECK(info->si_arch == AUDIT_ARCH_X86_64); TEST_CHECK(uc->uc_mcontext.gregs[REG_RAX] == kFilteredSyscall); +#elif defined(__aarch64__) + TEST_CHECK(info->si_arch == AUDIT_ARCH_AARCH64); + TEST_CHECK(uc->uc_mcontext.regs[8] == kFilteredSyscall); #endif // defined(__x86_64__) _exit(0); }); diff --git a/test/syscalls/linux/stat.cc b/test/syscalls/linux/stat.cc index 513b9cd1c..2503960f3 100644 --- a/test/syscalls/linux/stat.cc +++ b/test/syscalls/linux/stat.cc @@ -34,6 +34,13 @@ #include "test/util/temp_path.h" #include "test/util/test_util.h" +#ifndef AT_STATX_FORCE_SYNC +#define AT_STATX_FORCE_SYNC 0x2000 +#endif +#ifndef AT_STATX_DONT_SYNC +#define AT_STATX_DONT_SYNC 0x4000 +#endif + namespace gvisor { namespace testing { @@ -700,8 +707,10 @@ TEST_F(StatTest, StatxInvalidFlags) { struct kernel_statx stx; EXPECT_THAT(statx(AT_FDCWD, test_file_name_.c_str(), 12345, 0, &stx), SyscallFailsWithErrno(EINVAL)); + + // Sync flags are mutually exclusive. EXPECT_THAT(statx(AT_FDCWD, test_file_name_.c_str(), - 0x6000 /* AT_STATX_SYNC_TYPE */, 0, &stx), + AT_STATX_FORCE_SYNC | AT_STATX_DONT_SYNC, 0, &stx), SyscallFailsWithErrno(EINVAL)); } diff --git a/test/syscalls/linux/sticky.cc b/test/syscalls/linux/sticky.cc index 7e73325bf..92eec0449 100644 --- a/test/syscalls/linux/sticky.cc +++ b/test/syscalls/linux/sticky.cc @@ -42,8 +42,9 @@ TEST(StickyTest, StickyBitPermDenied) { auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); EXPECT_THAT(chmod(dir.path().c_str(), 0777 | S_ISVTX), SyscallSucceeds()); - std::string path = JoinPath(dir.path(), "NewDir"); - ASSERT_THAT(mkdir(path.c_str(), 0755), SyscallSucceeds()); + const FileDescriptor dirfd = + ASSERT_NO_ERRNO_AND_VALUE(Open(dir.path(), O_DIRECTORY)); + ASSERT_THAT(mkdirat(dirfd.get(), "NewDir", 0755), SyscallSucceeds()); // Drop privileges and change IDs only in child thread, or else this parent // thread won't be able to open some log files after the test ends. @@ -61,7 +62,8 @@ TEST(StickyTest, StickyBitPermDenied) { syscall(SYS_setresuid, -1, absl::GetFlag(FLAGS_scratch_uid), -1), SyscallSucceeds()); - EXPECT_THAT(rmdir(path.c_str()), SyscallFailsWithErrno(EPERM)); + EXPECT_THAT(unlinkat(dirfd.get(), "NewDir", AT_REMOVEDIR), + SyscallFailsWithErrno(EPERM)); }); } @@ -96,8 +98,9 @@ TEST(StickyTest, StickyBitCapFOWNER) { auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); EXPECT_THAT(chmod(dir.path().c_str(), 0777 | S_ISVTX), SyscallSucceeds()); - std::string path = JoinPath(dir.path(), "NewDir"); - ASSERT_THAT(mkdir(path.c_str(), 0755), SyscallSucceeds()); + const FileDescriptor dirfd = + ASSERT_NO_ERRNO_AND_VALUE(Open(dir.path(), O_DIRECTORY)); + ASSERT_THAT(mkdirat(dirfd.get(), "NewDir", 0755), SyscallSucceeds()); // Drop privileges and change IDs only in child thread, or else this parent // thread won't be able to open some log files after the test ends. @@ -114,7 +117,8 @@ TEST(StickyTest, StickyBitCapFOWNER) { SyscallSucceeds()); EXPECT_NO_ERRNO(SetCapability(CAP_FOWNER, true)); - EXPECT_THAT(rmdir(path.c_str()), SyscallSucceeds()); + EXPECT_THAT(unlinkat(dirfd.get(), "NewDir", AT_REMOVEDIR), + SyscallSucceeds()); }); } } // namespace diff --git a/test/syscalls/linux/sysret.cc b/test/syscalls/linux/sysret.cc index 569190a59..19ffbd85b 100644 --- a/test/syscalls/linux/sysret.cc +++ b/test/syscalls/linux/sysret.cc @@ -58,7 +58,8 @@ class SysretTest : public ::testing::Test { iov.iov_base = ®s_; iov.iov_len = sizeof(regs_); - ASSERT_THAT(ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &iov), SyscallSucceeds()); + ASSERT_THAT(ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &iov), + SyscallSucceeds()); child_ = pid; } @@ -75,7 +76,8 @@ class SysretTest : public ::testing::Test { #else #error "Unknown architecture" #endif - ASSERT_THAT(ptrace(PTRACE_SETREGSET, child_, NT_PRSTATUS, &iov), SyscallSucceeds()); + ASSERT_THAT(ptrace(PTRACE_SETREGSET, child_, NT_PRSTATUS, &iov), + SyscallSucceeds()); } void SetRsp(uint64_t newrsp) { @@ -86,7 +88,8 @@ class SysretTest : public ::testing::Test { #else #error "Unknown architecture" #endif - ASSERT_THAT(ptrace(PTRACE_SETREGSET, child_, NT_PRSTATUS, &iov), SyscallSucceeds()); + ASSERT_THAT(ptrace(PTRACE_SETREGSET, child_, NT_PRSTATUS, &iov), + SyscallSucceeds()); } // Wait waits for the child pid and returns the exit status. |