// Copyright 2018 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 <fcntl.h> #include <sys/resource.h> #include <sys/select.h> #include <sys/time.h> #include <climits> #include <csignal> #include <cstdio> #include "gtest/gtest.h" #include "gtest/gtest.h" #include "absl/time/time.h" #include "test/syscalls/linux/base_poll_test.h" #include "test/util/file_descriptor.h" #include "test/util/multiprocess_util.h" #include "test/util/posix_error.h" #include "test/util/rlimit_util.h" #include "test/util/temp_path.h" #include "test/util/test_util.h" namespace gvisor { namespace testing { namespace { class SelectTest : public BasePollTest { protected: void SetUp() override { BasePollTest::SetUp(); } void TearDown() override { BasePollTest::TearDown(); } }; // See that when there are no FD sets, select behaves like sleep. TEST_F(SelectTest, NullFds) { struct timeval timeout = absl::ToTimeval(absl::Milliseconds(10)); ASSERT_THAT(select(0, nullptr, nullptr, nullptr, &timeout), SyscallSucceeds()); EXPECT_EQ(timeout.tv_sec, 0); EXPECT_EQ(timeout.tv_usec, 0); timeout = absl::ToTimeval(absl::Milliseconds(10)); ASSERT_THAT(select(1, nullptr, nullptr, nullptr, &timeout), SyscallSucceeds()); EXPECT_EQ(timeout.tv_sec, 0); EXPECT_EQ(timeout.tv_usec, 0); } TEST_F(SelectTest, NegativeNfds) { EXPECT_THAT(select(-1, nullptr, nullptr, nullptr, nullptr), SyscallFailsWithErrno(EINVAL)); EXPECT_THAT(select(-100000, nullptr, nullptr, nullptr, nullptr), SyscallFailsWithErrno(EINVAL)); EXPECT_THAT(select(INT_MIN, nullptr, nullptr, nullptr, nullptr), SyscallFailsWithErrno(EINVAL)); } TEST_F(SelectTest, ClosedFds) { auto temp_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(temp_file.path(), O_RDONLY)); // We can't rely on a file descriptor being closed in a multi threaded // application so fork to get a clean process. EXPECT_THAT(InForkedProcess([&] { int fd_num = fd.get(); fd.reset(); fd_set read_set; FD_ZERO(&read_set); FD_SET(fd_num, &read_set); struct timeval timeout = absl::ToTimeval(absl::Milliseconds(10)); TEST_PCHECK(select(fd_num + 1, &read_set, nullptr, nullptr, &timeout) != 0); TEST_PCHECK(errno == EBADF); }), IsPosixErrorOkAndHolds(0)); } TEST_F(SelectTest, ZeroTimeout) { struct timeval timeout = {}; EXPECT_THAT(select(1, nullptr, nullptr, nullptr, &timeout), SyscallSucceeds()); // Ignore timeout as its value is now undefined. } // If random S/R interrupts the select, SIGALRM may be delivered before select // restarts, causing the select to hang forever. TEST_F(SelectTest, NoTimeout_NoRandomSave) { // When there's no timeout, select may never return so set a timer. SetTimer(absl::Milliseconds(100)); // See that we get interrupted by the timer. ASSERT_THAT(select(1, nullptr, nullptr, nullptr, nullptr), SyscallFailsWithErrno(EINTR)); EXPECT_TRUE(TimerFired()); } TEST_F(SelectTest, InvalidTimeoutNegative) { struct timeval timeout = absl::ToTimeval(absl::Microseconds(-1)); EXPECT_THAT(select(1, nullptr, nullptr, nullptr, &timeout), SyscallFailsWithErrno(EINVAL)); // Ignore timeout as its value is now undefined. } // Verify that a signal interrupts select. // // If random S/R interrupts the select, SIGALRM may be delivered before select // restarts, causing the select to hang forever. TEST_F(SelectTest, InterruptedBySignal_NoRandomSave) { absl::Duration duration(absl::Seconds(5)); struct timeval timeout = absl::ToTimeval(duration); SetTimer(absl::Milliseconds(100)); ASSERT_FALSE(TimerFired()); ASSERT_THAT(select(1, nullptr, nullptr, nullptr, &timeout), SyscallFailsWithErrno(EINTR)); EXPECT_TRUE(TimerFired()); // Ignore timeout as its value is now undefined. } TEST_F(SelectTest, IgnoreBitsAboveNfds) { // fd_set is a bit array with at least FD_SETSIZE bits. Test that bits // corresponding to file descriptors above nfds are ignored. fd_set read_set; FD_ZERO(&read_set); constexpr int kNfds = 1; for (int fd = kNfds; fd < FD_SETSIZE; fd++) { FD_SET(fd, &read_set); } // Pass a zero timeout so that select returns immediately. struct timeval timeout = {}; EXPECT_THAT(select(kNfds, &read_set, nullptr, nullptr, &timeout), SyscallSucceedsWithValue(0)); } // This test illustrates Linux's behavior of 'select' calls passing after // setrlimit RLIMIT_NOFILE is called. In particular, versions of sshd rely on // this behavior. TEST_F(SelectTest, SetrlimitCallNOFILE) { fd_set read_set; FD_ZERO(&read_set); timeval timeout = {}; const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE( Open(NewTempAbsPath(), O_RDONLY | O_CREAT, S_IRUSR)); Cleanup reset_rlimit = ASSERT_NO_ERRNO_AND_VALUE(ScopedSetSoftRlimit(RLIMIT_NOFILE, 0)); FD_SET(fd.get(), &read_set); // this call with zero timeout should return immediately EXPECT_THAT(select(fd.get() + 1, &read_set, nullptr, nullptr, &timeout), SyscallSucceeds()); } } // namespace } // namespace testing } // namespace gvisor