diff options
Diffstat (limited to 'test')
-rw-r--r-- | test/runner/defs.bzl | 2 | ||||
-rw-r--r-- | test/runtimes/exclude/python3.7.3.csv | 1 | ||||
-rw-r--r-- | test/syscalls/BUILD | 4 | ||||
-rw-r--r-- | test/syscalls/linux/BUILD | 22 | ||||
-rw-r--r-- | test/syscalls/linux/ptrace.cc | 43 | ||||
-rw-r--r-- | test/syscalls/linux/semaphore.cc | 115 | ||||
-rw-r--r-- | test/syscalls/linux/socket_inet_loopback.cc | 10 | ||||
-rw-r--r-- | test/syscalls/linux/socket_ip_unbound.cc | 18 | ||||
-rw-r--r-- | test/syscalls/linux/socket_ip_unbound_netlink.cc | 104 | ||||
-rw-r--r-- | test/syscalls/linux/socket_netlink_route.cc | 2 |
10 files changed, 290 insertions, 31 deletions
diff --git a/test/runner/defs.bzl b/test/runner/defs.bzl index 9b5994d59..4992147d4 100644 --- a/test/runner/defs.bzl +++ b/test/runner/defs.bzl @@ -97,10 +97,10 @@ def _syscall_test( # we figure out how to request ipv4 sockets on Guitar machines. if network == "host": tags.append("noguitar") - tags.append("block-network") # Disable off-host networking. tags.append("requires-net:loopback") + tags.append("block-network") # gotsan makes sense only if tests are running in gVisor. if platform == "native": diff --git a/test/runtimes/exclude/python3.7.3.csv b/test/runtimes/exclude/python3.7.3.csv index 911f22855..e9fef03b7 100644 --- a/test/runtimes/exclude/python3.7.3.csv +++ b/test/runtimes/exclude/python3.7.3.csv @@ -4,7 +4,6 @@ test_asyncore,b/162973328, test_epoll,b/162983393, test_fcntl,b/162978767,fcntl invalid argument -- artificial test to make sure something works in 64 bit mode. test_httplib,b/163000009,OSError: [Errno 98] Address already in use -test_imaplib,b/162979661, test_logging,b/162980079, test_multiprocessing_fork,,Flaky. Sometimes times out. test_multiprocessing_forkserver,,Flaky. Sometimes times out. diff --git a/test/syscalls/BUILD b/test/syscalls/BUILD index f66a9ceb4..b5a4ef4df 100644 --- a/test/syscalls/BUILD +++ b/test/syscalls/BUILD @@ -695,6 +695,10 @@ syscall_test( ) syscall_test( + test = "//test/syscalls/linux:socket_ip_unbound_netlink_test", +) + +syscall_test( test = "//test/syscalls/linux:socket_netdevice_test", ) diff --git a/test/syscalls/linux/BUILD b/test/syscalls/linux/BUILD index c94c1d5bd..99f286bfc 100644 --- a/test/syscalls/linux/BUILD +++ b/test/syscalls/linux/BUILD @@ -1802,10 +1802,14 @@ cc_binary( "@com_google_absl//absl/flags:flag", "@com_google_absl//absl/time", gtest, + "//test/util:file_descriptor", + "//test/util:fs_util", "//test/util:logging", + "//test/util:memory_util", "//test/util:multiprocess_util", "//test/util:platform_util", "//test/util:signal_util", + "//test/util:temp_path", "//test/util:test_util", "//test/util:thread_util", "//test/util:time_util", @@ -2880,6 +2884,24 @@ cc_binary( ) cc_binary( + name = "socket_ip_unbound_netlink_test", + testonly = 1, + srcs = [ + "socket_ip_unbound_netlink.cc", + ], + linkstatic = 1, + deps = [ + ":ip_socket_test_util", + ":socket_netlink_route_util", + ":socket_test_util", + "//test/util:capability_util", + gtest, + "//test/util:test_main", + "//test/util:test_util", + ], +) + +cc_binary( name = "socket_domain_test", testonly = 1, srcs = [ diff --git a/test/syscalls/linux/ptrace.cc b/test/syscalls/linux/ptrace.cc index 926690eb8..13c19d4a8 100644 --- a/test/syscalls/linux/ptrace.cc +++ b/test/syscalls/linux/ptrace.cc @@ -30,10 +30,13 @@ #include "absl/flags/flag.h" #include "absl/time/clock.h" #include "absl/time/time.h" +#include "test/util/fs_util.h" #include "test/util/logging.h" +#include "test/util/memory_util.h" #include "test/util/multiprocess_util.h" #include "test/util/platform_util.h" #include "test/util/signal_util.h" +#include "test/util/temp_path.h" #include "test/util/test_util.h" #include "test/util/thread_util.h" #include "test/util/time_util.h" @@ -113,10 +116,21 @@ TEST(PtraceTest, AttachParent_PeekData_PokeData_SignalSuppression) { // except disabled. SKIP_IF(ASSERT_NO_ERRNO_AND_VALUE(YamaPtraceScope()) > 0); - constexpr long kBeforePokeDataValue = 10; - constexpr long kAfterPokeDataValue = 20; + // Test PTRACE_POKE/PEEKDATA on both anonymous and file mappings. + const auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + ASSERT_NO_ERRNO(Truncate(file.path(), kPageSize)); + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR)); + const auto file_mapping = ASSERT_NO_ERRNO_AND_VALUE(Mmap( + nullptr, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd.get(), 0)); - volatile long word = kBeforePokeDataValue; + constexpr long kBeforePokeDataAnonValue = 10; + constexpr long kAfterPokeDataAnonValue = 20; + constexpr long kBeforePokeDataFileValue = 0; // implicit, due to truncate() + constexpr long kAfterPokeDataFileValue = 30; + + volatile long anon_word = kBeforePokeDataAnonValue; + auto* file_word_ptr = static_cast<volatile long*>(file_mapping.ptr()); pid_t const child_pid = fork(); if (child_pid == 0) { @@ -134,12 +148,22 @@ TEST(PtraceTest, AttachParent_PeekData_PokeData_SignalSuppression) { MaybeSave(); TEST_CHECK(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP); - // Replace the value of word in the parent process with kAfterPokeDataValue. - long const parent_word = ptrace(PTRACE_PEEKDATA, parent_pid, &word, 0); + // Replace the value of anon_word in the parent process with + // kAfterPokeDataAnonValue. + long parent_word = ptrace(PTRACE_PEEKDATA, parent_pid, &anon_word, 0); + MaybeSave(); + TEST_CHECK(parent_word == kBeforePokeDataAnonValue); + TEST_PCHECK(ptrace(PTRACE_POKEDATA, parent_pid, &anon_word, + kAfterPokeDataAnonValue) == 0); + MaybeSave(); + + // Replace the value pointed to by file_word_ptr in the mapped file with + // kAfterPokeDataFileValue, via the parent process' mapping. + parent_word = ptrace(PTRACE_PEEKDATA, parent_pid, file_word_ptr, 0); MaybeSave(); - TEST_CHECK(parent_word == kBeforePokeDataValue); - TEST_PCHECK( - ptrace(PTRACE_POKEDATA, parent_pid, &word, kAfterPokeDataValue) == 0); + TEST_CHECK(parent_word == kBeforePokeDataFileValue); + TEST_PCHECK(ptrace(PTRACE_POKEDATA, parent_pid, file_word_ptr, + kAfterPokeDataFileValue) == 0); MaybeSave(); // Detach from the parent and suppress the SIGSTOP. If the SIGSTOP is not @@ -160,7 +184,8 @@ TEST(PtraceTest, AttachParent_PeekData_PokeData_SignalSuppression) { << " status " << status; // Check that the child's PTRACE_POKEDATA was effective. - EXPECT_EQ(kAfterPokeDataValue, word); + EXPECT_EQ(kAfterPokeDataAnonValue, anon_word); + EXPECT_EQ(kAfterPokeDataFileValue, *file_word_ptr); } TEST(PtraceTest, GetSigMask) { diff --git a/test/syscalls/linux/semaphore.cc b/test/syscalls/linux/semaphore.cc index ed6a1c2aa..1534c15c7 100644 --- a/test/syscalls/linux/semaphore.cc +++ b/test/syscalls/linux/semaphore.cc @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include <signal.h> #include <sys/ipc.h> #include <sys/sem.h> #include <sys/types.h> @@ -542,6 +543,120 @@ TEST(SemaphoreTest, SemCtlIpcStat) { SyscallFailsWithErrno(EACCES)); } +// The funcion keeps calling semctl's GETZCNT command until +// the return value is not less than target. +int WaitSemzcnt(int semid, int target) { + constexpr absl::Duration timeout = absl::Seconds(10); + int semcnt = 0; + for (auto start = absl::Now(); absl::Now() - start < timeout;) { + semcnt = semctl(semid, 0, GETZCNT); + if (semcnt >= target) { + break; + } + absl::SleepFor(absl::Milliseconds(10)); + } + return semcnt; +} + +TEST(SemaphoreTest, SemopGetzcnt) { + // Drop CAP_IPC_OWNER which allows us to bypass semaphore permissions. + ASSERT_NO_ERRNO(SetCapability(CAP_IPC_OWNER, false)); + // Create a write only semaphore set. + AutoSem sem(semget(IPC_PRIVATE, 1, 0200 | IPC_CREAT)); + ASSERT_THAT(sem.get(), SyscallSucceeds()); + + // No read permission to retrieve semzcnt. + EXPECT_THAT(semctl(sem.get(), 0, GETZCNT), SyscallFailsWithErrno(EACCES)); + + // Remove the calling thread's read permission. + struct semid_ds ds = {}; + ds.sem_perm.uid = getuid(); + ds.sem_perm.gid = getgid(); + ds.sem_perm.mode = 0600; + ASSERT_THAT(semctl(sem.get(), 0, IPC_SET, &ds), SyscallSucceeds()); + + std::vector<pid_t> children; + ASSERT_THAT(semctl(sem.get(), 0, SETVAL, 1), SyscallSucceeds()); + + struct sembuf buf = {}; + buf.sem_num = 0; + buf.sem_op = 0; + constexpr size_t kLoops = 10; + for (auto i = 0; i < kLoops; i++) { + auto child_pid = fork(); + if (child_pid == 0) { + ASSERT_THAT(RetryEINTR(semop)(sem.get(), &buf, 1), SyscallSucceeds()); + _exit(0); + } + children.push_back(child_pid); + } + EXPECT_THAT(WaitSemzcnt(sem.get(), kLoops), SyscallSucceedsWithValue(kLoops)); + // Set semval to 0, which wakes up children that sleep on the semop. + ASSERT_THAT(semctl(sem.get(), 0, SETVAL, 0), SyscallSucceeds()); + for (const auto& child_pid : children) { + int status; + ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), + SyscallSucceedsWithValue(child_pid)); + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0); + } + EXPECT_EQ(semctl(sem.get(), 0, GETZCNT), 0); +} + +TEST(SemaphoreTest, SemopGetzcntOnSetRemoval) { + auto semid = semget(IPC_PRIVATE, 1, 0600 | IPC_CREAT); + ASSERT_THAT(semid, SyscallSucceeds()); + ASSERT_THAT(semctl(semid, 0, SETVAL, 1), SyscallSucceeds()); + ASSERT_EQ(semctl(semid, 0, GETZCNT), 0); + + auto child_pid = fork(); + if (child_pid == 0) { + struct sembuf buf = {}; + buf.sem_num = 0; + buf.sem_op = 0; + + ASSERT_THAT(RetryEINTR(semop)(semid, &buf, 1), SyscallFails()); + // Ensure that wait will only unblock when the semaphore is removed. On + // EINTR retry it may race with deletion and return EINVAL. + ASSERT_TRUE(errno == EIDRM || errno == EINVAL) << "errno=" << errno; + _exit(0); + } + + EXPECT_THAT(WaitSemzcnt(semid, 1), SyscallSucceedsWithValue(1)); + // Remove the semaphore set, which fails the sleep semop. + ASSERT_THAT(semctl(semid, 0, IPC_RMID), SyscallSucceeds()); + int status; + ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), + SyscallSucceedsWithValue(child_pid)); + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0); + EXPECT_THAT(semctl(semid, 0, GETZCNT), SyscallFailsWithErrno(EINVAL)); +} + +TEST(SemaphoreTest, SemopGetzcntOnSignal) { + AutoSem sem(semget(IPC_PRIVATE, 1, 0600 | IPC_CREAT)); + ASSERT_THAT(sem.get(), SyscallSucceeds()); + ASSERT_THAT(semctl(sem.get(), 0, SETVAL, 1), SyscallSucceeds()); + ASSERT_EQ(semctl(sem.get(), 0, GETZCNT), 0); + + auto child_pid = fork(); + if (child_pid == 0) { + signal(SIGHUP, [](int sig) -> void {}); + struct sembuf buf = {}; + buf.sem_num = 0; + buf.sem_op = 0; + + ASSERT_THAT(semop(sem.get(), &buf, 1), SyscallFailsWithErrno(EINTR)); + _exit(0); + } + EXPECT_THAT(WaitSemzcnt(sem.get(), 1), SyscallSucceedsWithValue(1)); + // Send a signal to the child, which fails the sleep semop. + ASSERT_EQ(kill(child_pid, SIGHUP), 0); + int status; + ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), + SyscallSucceedsWithValue(child_pid)); + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0); + EXPECT_EQ(semctl(sem.get(), 0, GETZCNT), 0); +} + } // namespace } // namespace testing } // namespace gvisor diff --git a/test/syscalls/linux/socket_inet_loopback.cc b/test/syscalls/linux/socket_inet_loopback.cc index 39a68c5a5..e19a83413 100644 --- a/test/syscalls/linux/socket_inet_loopback.cc +++ b/test/syscalls/linux/socket_inet_loopback.cc @@ -940,7 +940,7 @@ void setupTimeWaitClose(const TestAddress* listener, } // shutdown to trigger TIME_WAIT. - ASSERT_THAT(shutdown(active_closefd.get(), SHUT_RDWR), SyscallSucceeds()); + ASSERT_THAT(shutdown(active_closefd.get(), SHUT_WR), SyscallSucceeds()); { const int kTimeout = 10000; struct pollfd pfd = { @@ -950,7 +950,8 @@ void setupTimeWaitClose(const TestAddress* listener, ASSERT_THAT(poll(&pfd, 1, kTimeout), SyscallSucceedsWithValue(1)); ASSERT_EQ(pfd.revents, POLLIN); } - ScopedThread t([&]() { + ASSERT_THAT(shutdown(passive_closefd.get(), SHUT_WR), SyscallSucceeds()); + { constexpr int kTimeout = 10000; constexpr int16_t want_events = POLLHUP; struct pollfd pfd = { @@ -958,11 +959,8 @@ void setupTimeWaitClose(const TestAddress* listener, .events = want_events, }; ASSERT_THAT(poll(&pfd, 1, kTimeout), SyscallSucceedsWithValue(1)); - }); + } - passive_closefd.reset(); - t.Join(); - active_closefd.reset(); // This sleep is needed to reduce flake to ensure that the passive-close // ensures the state transitions to CLOSE from LAST_ACK. absl::SleepFor(absl::Seconds(1)); diff --git a/test/syscalls/linux/socket_ip_unbound.cc b/test/syscalls/linux/socket_ip_unbound.cc index 8f7ccc868..029f1e872 100644 --- a/test/syscalls/linux/socket_ip_unbound.cc +++ b/test/syscalls/linux/socket_ip_unbound.cc @@ -454,23 +454,15 @@ TEST_P(IPUnboundSocketTest, SetReuseAddr) { INSTANTIATE_TEST_SUITE_P( IPUnboundSockets, IPUnboundSocketTest, - ::testing::ValuesIn(VecCat<SocketKind>(VecCat<SocketKind>( + ::testing::ValuesIn(VecCat<SocketKind>( ApplyVec<SocketKind>(IPv4UDPUnboundSocket, - AllBitwiseCombinations(List<int>{SOCK_DGRAM}, - List<int>{0, - SOCK_NONBLOCK})), + std::vector<int>{0, SOCK_NONBLOCK}), ApplyVec<SocketKind>(IPv6UDPUnboundSocket, - AllBitwiseCombinations(List<int>{SOCK_DGRAM}, - List<int>{0, - SOCK_NONBLOCK})), + std::vector<int>{0, SOCK_NONBLOCK}), ApplyVec<SocketKind>(IPv4TCPUnboundSocket, - AllBitwiseCombinations(List<int>{SOCK_STREAM}, - List<int>{0, - SOCK_NONBLOCK})), + std::vector{0, SOCK_NONBLOCK}), ApplyVec<SocketKind>(IPv6TCPUnboundSocket, - AllBitwiseCombinations(List<int>{SOCK_STREAM}, - List<int>{ - 0, SOCK_NONBLOCK})))))); + std::vector{0, SOCK_NONBLOCK})))); } // namespace testing } // namespace gvisor diff --git a/test/syscalls/linux/socket_ip_unbound_netlink.cc b/test/syscalls/linux/socket_ip_unbound_netlink.cc new file mode 100644 index 000000000..6036bfcaf --- /dev/null +++ b/test/syscalls/linux/socket_ip_unbound_netlink.cc @@ -0,0 +1,104 @@ +// Copyright 2019 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include <arpa/inet.h> +#include <netinet/in.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <sys/un.h> + +#include <cstdio> +#include <cstring> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "test/syscalls/linux/ip_socket_test_util.h" +#include "test/syscalls/linux/socket_netlink_route_util.h" +#include "test/syscalls/linux/socket_test_util.h" +#include "test/util/capability_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +// Test fixture for tests that apply to pairs of IP sockets. +using IPv6UnboundSocketTest = SimpleSocketTest; + +TEST_P(IPv6UnboundSocketTest, ConnectToBadLocalAddress_NoRandomSave) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN))); + + // TODO(gvisor.dev/issue/4595): Addresses on net devices are not saved + // across save/restore. + DisableSave ds; + + // Delete the loopback address from the loopback interface. + Link loopback_link = ASSERT_NO_ERRNO_AND_VALUE(LoopbackLink()); + EXPECT_NO_ERRNO(LinkDelLocalAddr(loopback_link.index, AF_INET6, + /*prefixlen=*/128, &in6addr_loopback, + sizeof(in6addr_loopback))); + Cleanup defer_addr_removal = + Cleanup([loopback_link = std::move(loopback_link)] { + EXPECT_NO_ERRNO(LinkAddLocalAddr(loopback_link.index, AF_INET6, + /*prefixlen=*/128, &in6addr_loopback, + sizeof(in6addr_loopback))); + }); + + TestAddress addr = V6Loopback(); + reinterpret_cast<sockaddr_in6*>(&addr.addr)->sin6_port = 65535; + auto sock = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); + EXPECT_THAT(connect(sock->get(), reinterpret_cast<sockaddr*>(&addr.addr), + addr.addr_len), + SyscallFailsWithErrno(EADDRNOTAVAIL)); +} + +INSTANTIATE_TEST_SUITE_P(IPUnboundSockets, IPv6UnboundSocketTest, + ::testing::ValuesIn(std::vector<SocketKind>{ + IPv6UDPUnboundSocket(0), + IPv6TCPUnboundSocket(0)})); + +using IPv4UnboundSocketTest = SimpleSocketTest; + +TEST_P(IPv4UnboundSocketTest, ConnectToBadLocalAddress_NoRandomSave) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN))); + + // TODO(gvisor.dev/issue/4595): Addresses on net devices are not saved + // across save/restore. + DisableSave ds; + + // Delete the loopback address from the loopback interface. + Link loopback_link = ASSERT_NO_ERRNO_AND_VALUE(LoopbackLink()); + struct in_addr laddr; + laddr.s_addr = htonl(INADDR_LOOPBACK); + EXPECT_NO_ERRNO(LinkDelLocalAddr(loopback_link.index, AF_INET, + /*prefixlen=*/8, &laddr, sizeof(laddr))); + Cleanup defer_addr_removal = Cleanup( + [loopback_link = std::move(loopback_link), addr = std::move(laddr)] { + EXPECT_NO_ERRNO(LinkAddLocalAddr(loopback_link.index, AF_INET, + /*prefixlen=*/8, &addr, sizeof(addr))); + }); + TestAddress addr = V4Loopback(); + reinterpret_cast<sockaddr_in*>(&addr.addr)->sin_port = 65535; + auto sock = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); + EXPECT_THAT(connect(sock->get(), reinterpret_cast<sockaddr*>(&addr.addr), + addr.addr_len), + SyscallFailsWithErrno(EADDRNOTAVAIL)); +} + +INSTANTIATE_TEST_SUITE_P(IPUnboundSockets, IPv4UnboundSocketTest, + ::testing::ValuesIn(std::vector<SocketKind>{ + IPv4UDPUnboundSocket(0), + IPv4TCPUnboundSocket(0)})); + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/socket_netlink_route.cc b/test/syscalls/linux/socket_netlink_route.cc index e83f0d81f..ee3c08770 100644 --- a/test/syscalls/linux/socket_netlink_route.cc +++ b/test/syscalls/linux/socket_netlink_route.cc @@ -536,7 +536,7 @@ TEST(NetlinkRouteTest, AddAndRemoveAddr) { // Second delete should fail, as address no longer exists. EXPECT_THAT(LinkDelLocalAddr(loopback_link.index, AF_INET, /*prefixlen=*/24, &addr, sizeof(addr)), - PosixErrorIs(EINVAL, ::testing::_)); + PosixErrorIs(EADDRNOTAVAIL, ::testing::_)); }); // Replace an existing address should succeed. |