gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/syscalls/linux/pselect.cc (about) 1 // Copyright 2018 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #include <signal.h> 16 #include <sys/select.h> 17 18 #include "gtest/gtest.h" 19 #include "absl/time/time.h" 20 #include "test/syscalls/linux/base_poll_test.h" 21 #include "test/util/signal_util.h" 22 #include "test/util/test_util.h" 23 24 namespace gvisor { 25 namespace testing { 26 namespace { 27 28 struct MaskWithSize { 29 sigset_t* mask; 30 size_t mask_size; 31 }; 32 33 // Linux and glibc have a different idea of the sizeof sigset_t. When calling 34 // the syscall directly, use what the kernel expects. 35 unsigned kSigsetSize = SIGRTMAX / 8; 36 37 // Linux pselect(2) differs from the glibc wrapper function in that Linux 38 // updates the timeout with the amount of time remaining. In order to test this 39 // behavior we need to use the syscall directly. 40 int syscallPselect6(int nfds, fd_set* readfds, fd_set* writefds, 41 fd_set* exceptfds, struct timespec* timeout, 42 const MaskWithSize* mask_with_size) { 43 return syscall(SYS_pselect6, nfds, readfds, writefds, exceptfds, timeout, 44 mask_with_size); 45 } 46 47 class PselectTest : public BasePollTest { 48 protected: 49 void SetUp() override { BasePollTest::SetUp(); } 50 void TearDown() override { BasePollTest::TearDown(); } 51 }; 52 53 // See that when there are no FD sets, pselect behaves like sleep. 54 TEST_F(PselectTest, NullFds) { 55 struct timespec timeout = absl::ToTimespec(absl::Milliseconds(10)); 56 ASSERT_THAT(syscallPselect6(0, nullptr, nullptr, nullptr, &timeout, nullptr), 57 SyscallSucceeds()); 58 EXPECT_EQ(timeout.tv_sec, 0); 59 EXPECT_EQ(timeout.tv_nsec, 0); 60 61 timeout = absl::ToTimespec(absl::Milliseconds(10)); 62 ASSERT_THAT(syscallPselect6(1, nullptr, nullptr, nullptr, &timeout, nullptr), 63 SyscallSucceeds()); 64 EXPECT_EQ(timeout.tv_sec, 0); 65 EXPECT_EQ(timeout.tv_nsec, 0); 66 } 67 68 TEST_F(PselectTest, ClosedFds) { 69 fd_set read_set; 70 FD_ZERO(&read_set); 71 int fd; 72 ASSERT_THAT(fd = dup(1), SyscallSucceeds()); 73 ASSERT_THAT(close(fd), SyscallSucceeds()); 74 FD_SET(fd, &read_set); 75 struct timespec timeout = absl::ToTimespec(absl::Milliseconds(10)); 76 EXPECT_THAT( 77 syscallPselect6(fd + 1, &read_set, nullptr, nullptr, &timeout, nullptr), 78 SyscallFailsWithErrno(EBADF)); 79 } 80 81 TEST_F(PselectTest, ZeroTimeout) { 82 struct timespec timeout = {}; 83 ASSERT_THAT(syscallPselect6(1, nullptr, nullptr, nullptr, &timeout, nullptr), 84 SyscallSucceeds()); 85 EXPECT_EQ(timeout.tv_sec, 0); 86 EXPECT_EQ(timeout.tv_nsec, 0); 87 } 88 89 // If random S/R interrupts the pselect, SIGALRM may be delivered before pselect 90 // restarts, causing the pselect to hang forever. 91 TEST_F(PselectTest, NoTimeout) { 92 // When there's no timeout, pselect may never return so set a timer. 93 SetTimer(absl::Milliseconds(100)); 94 // See that we get interrupted by the timer. 95 ASSERT_THAT(syscallPselect6(1, nullptr, nullptr, nullptr, nullptr, nullptr), 96 SyscallFailsWithErrno(EINTR)); 97 EXPECT_TRUE(TimerFired()); 98 } 99 100 TEST_F(PselectTest, InvalidTimeoutNegative) { 101 struct timespec timeout = absl::ToTimespec(absl::Seconds(-1)); 102 ASSERT_THAT(syscallPselect6(1, nullptr, nullptr, nullptr, &timeout, nullptr), 103 SyscallFailsWithErrno(EINVAL)); 104 EXPECT_EQ(timeout.tv_sec, -1); 105 EXPECT_EQ(timeout.tv_nsec, 0); 106 } 107 108 TEST_F(PselectTest, InvalidTimeoutNotNormalized) { 109 struct timespec timeout = {0, 1000000001}; 110 ASSERT_THAT(syscallPselect6(1, nullptr, nullptr, nullptr, &timeout, nullptr), 111 SyscallFailsWithErrno(EINVAL)); 112 EXPECT_EQ(timeout.tv_sec, 0); 113 EXPECT_EQ(timeout.tv_nsec, 1000000001); 114 } 115 116 TEST_F(PselectTest, EmptySigMaskInvalidMaskSize) { 117 struct timespec timeout = {}; 118 MaskWithSize invalid = {nullptr, 7}; 119 EXPECT_THAT(syscallPselect6(0, nullptr, nullptr, nullptr, &timeout, &invalid), 120 SyscallSucceeds()); 121 } 122 123 TEST_F(PselectTest, EmptySigMaskValidMaskSize) { 124 struct timespec timeout = {}; 125 MaskWithSize invalid = {nullptr, 8}; 126 EXPECT_THAT(syscallPselect6(0, nullptr, nullptr, nullptr, &timeout, &invalid), 127 SyscallSucceeds()); 128 } 129 130 TEST_F(PselectTest, InvalidMaskSize) { 131 struct timespec timeout = {}; 132 sigset_t sigmask; 133 ASSERT_THAT(sigemptyset(&sigmask), SyscallSucceeds()); 134 MaskWithSize invalid = {&sigmask, 7}; 135 EXPECT_THAT(syscallPselect6(1, nullptr, nullptr, nullptr, &timeout, &invalid), 136 SyscallFailsWithErrno(EINVAL)); 137 } 138 139 // Verify that signals blocked by the pselect mask (that would otherwise be 140 // allowed) do not interrupt pselect. 141 TEST_F(PselectTest, SignalMaskBlocksSignal) { 142 absl::Duration duration(absl::Seconds(30)); 143 struct timespec timeout = absl::ToTimespec(duration); 144 absl::Duration timer_duration(absl::Seconds(10)); 145 146 // Call with a mask that blocks SIGALRM. See that pselect is not interrupted 147 // (i.e. returns 0) and that upon completion, the timer has fired. 148 sigset_t mask; 149 ASSERT_THAT(sigprocmask(0, nullptr, &mask), SyscallSucceeds()); 150 ASSERT_THAT(sigaddset(&mask, SIGALRM), SyscallSucceeds()); 151 MaskWithSize mask_with_size = {&mask, kSigsetSize}; 152 SetTimer(timer_duration); 153 MaybeSave(); 154 ASSERT_FALSE(TimerFired()); 155 ASSERT_THAT( 156 syscallPselect6(1, nullptr, nullptr, nullptr, &timeout, &mask_with_size), 157 SyscallSucceeds()); 158 EXPECT_TRUE(TimerFired()); 159 EXPECT_EQ(absl::DurationFromTimespec(timeout), absl::Duration()); 160 } 161 162 // Verify that signals allowed by the pselect mask (that would otherwise be 163 // blocked) interrupt pselect. 164 TEST_F(PselectTest, SignalMaskAllowsSignal) { 165 absl::Duration duration = absl::Seconds(30); 166 struct timespec timeout = absl::ToTimespec(duration); 167 absl::Duration timer_duration = absl::Seconds(10); 168 169 sigset_t mask; 170 ASSERT_THAT(sigprocmask(0, nullptr, &mask), SyscallSucceeds()); 171 172 // Block SIGALRM. 173 auto cleanup = 174 ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_BLOCK, SIGALRM)); 175 176 // Call with a mask that unblocks SIGALRM. See that pselect is interrupted. 177 MaskWithSize mask_with_size = {&mask, kSigsetSize}; 178 SetTimer(timer_duration); 179 MaybeSave(); 180 ASSERT_FALSE(TimerFired()); 181 ASSERT_THAT( 182 syscallPselect6(1, nullptr, nullptr, nullptr, &timeout, &mask_with_size), 183 SyscallFailsWithErrno(EINTR)); 184 EXPECT_TRUE(TimerFired()); 185 EXPECT_GT(absl::DurationFromTimespec(timeout), absl::Duration()); 186 } 187 188 } // namespace 189 } // namespace testing 190 } // namespace gvisor