gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/syscalls/linux/ppoll.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 <poll.h>
    16  #include <signal.h>
    17  #include <sys/syscall.h>
    18  #include <sys/time.h>
    19  #include <unistd.h>
    20  
    21  #include "gtest/gtest.h"
    22  #include "absl/time/time.h"
    23  #include "test/syscalls/linux/base_poll_test.h"
    24  #include "test/util/signal_util.h"
    25  #include "test/util/test_util.h"
    26  
    27  namespace gvisor {
    28  namespace testing {
    29  namespace {
    30  
    31  // Linux and glibc have a different idea of the sizeof sigset_t. When calling
    32  // the syscall directly, use what the kernel expects.
    33  unsigned kSigsetSize = SIGRTMAX / 8;
    34  
    35  // Linux ppoll(2) differs from the glibc wrapper function in that Linux updates
    36  // the timeout with the amount of time remaining. In order to test this behavior
    37  // we need to use the syscall directly.
    38  int syscallPpoll(struct pollfd* fds, nfds_t nfds, struct timespec* timeout_ts,
    39                   const sigset_t* sigmask, unsigned mask_size) {
    40    return syscall(SYS_ppoll, fds, nfds, timeout_ts, sigmask, mask_size);
    41  }
    42  
    43  class PpollTest : public BasePollTest {
    44   protected:
    45    void SetUp() override { BasePollTest::SetUp(); }
    46    void TearDown() override { BasePollTest::TearDown(); }
    47  };
    48  
    49  TEST_F(PpollTest, InvalidFds) {
    50    // fds is invalid because it's null, but we tell ppoll the length is non-zero.
    51    struct timespec timeout = {};
    52    sigset_t sigmask;
    53    TEST_PCHECK(sigemptyset(&sigmask) == 0);
    54    EXPECT_THAT(syscallPpoll(nullptr, 1, &timeout, &sigmask, kSigsetSize),
    55                SyscallFailsWithErrno(EFAULT));
    56    EXPECT_THAT(syscallPpoll(nullptr, -1, &timeout, &sigmask, kSigsetSize),
    57                SyscallFailsWithErrno(EINVAL));
    58  }
    59  
    60  // See that when fds is null, ppoll behaves like sleep.
    61  TEST_F(PpollTest, NullFds) {
    62    struct timespec timeout = absl::ToTimespec(absl::Milliseconds(10));
    63    ASSERT_THAT(syscallPpoll(nullptr, 0, &timeout, nullptr, 0),
    64                SyscallSucceeds());
    65    EXPECT_EQ(timeout.tv_sec, 0);
    66    EXPECT_EQ(timeout.tv_nsec, 0);
    67  }
    68  
    69  TEST_F(PpollTest, ZeroTimeout) {
    70    struct timespec timeout = {};
    71    ASSERT_THAT(syscallPpoll(nullptr, 0, &timeout, nullptr, 0),
    72                SyscallSucceeds());
    73    EXPECT_EQ(timeout.tv_sec, 0);
    74    EXPECT_EQ(timeout.tv_nsec, 0);
    75  }
    76  
    77  // If random S/R interrupts the ppoll, SIGALRM may be delivered before ppoll
    78  // restarts, causing the ppoll to hang forever.
    79  TEST_F(PpollTest, NoTimeout) {
    80    // When there's no timeout, ppoll may never return so set a timer.
    81    SetTimer(absl::Milliseconds(100));
    82    // See that we get interrupted by the timer.
    83    ASSERT_THAT(syscallPpoll(nullptr, 0, nullptr, nullptr, 0),
    84                SyscallFailsWithErrno(EINTR));
    85    EXPECT_TRUE(TimerFired());
    86  }
    87  
    88  TEST_F(PpollTest, InvalidTimeoutNegative) {
    89    struct timespec timeout = absl::ToTimespec(absl::Nanoseconds(-1));
    90    EXPECT_THAT(syscallPpoll(nullptr, 0, &timeout, nullptr, 0),
    91                SyscallFailsWithErrno(EINVAL));
    92  }
    93  
    94  TEST_F(PpollTest, InvalidTimeoutNotNormalized) {
    95    struct timespec timeout = {0, 1000000001};
    96    EXPECT_THAT(syscallPpoll(nullptr, 0, &timeout, nullptr, 0),
    97                SyscallFailsWithErrno(EINVAL));
    98  }
    99  
   100  TEST_F(PpollTest, InvalidMaskSize) {
   101    struct timespec timeout = {};
   102    sigset_t sigmask;
   103    TEST_PCHECK(sigemptyset(&sigmask) == 0);
   104    EXPECT_THAT(syscallPpoll(nullptr, 0, &timeout, &sigmask, 128),
   105                SyscallFailsWithErrno(EINVAL));
   106  }
   107  
   108  // Verify that signals blocked by the ppoll mask (that would otherwise be
   109  // allowed) do not interrupt ppoll.
   110  TEST_F(PpollTest, SignalMaskBlocksSignal) {
   111    absl::Duration duration(absl::Seconds(30));
   112    struct timespec timeout = absl::ToTimespec(duration);
   113    absl::Duration timer_duration(absl::Seconds(10));
   114  
   115    // Call with a mask that blocks SIGALRM. See that ppoll is not interrupted
   116    // (i.e. returns 0) and that upon completion, the timer has fired.
   117    sigset_t mask;
   118    ASSERT_THAT(sigprocmask(0, nullptr, &mask), SyscallSucceeds());
   119    TEST_PCHECK(sigaddset(&mask, SIGALRM) == 0);
   120    SetTimer(timer_duration);
   121    MaybeSave();
   122    ASSERT_FALSE(TimerFired());
   123    ASSERT_THAT(syscallPpoll(nullptr, 0, &timeout, &mask, kSigsetSize),
   124                SyscallSucceeds());
   125    EXPECT_TRUE(TimerFired());
   126    EXPECT_EQ(absl::DurationFromTimespec(timeout), absl::Duration());
   127  }
   128  
   129  // Verify that signals allowed by the ppoll mask (that would otherwise be
   130  // blocked) interrupt ppoll.
   131  TEST_F(PpollTest, SignalMaskAllowsSignal) {
   132    absl::Duration duration(absl::Seconds(30));
   133    struct timespec timeout = absl::ToTimespec(duration);
   134    absl::Duration timer_duration(absl::Seconds(10));
   135  
   136    sigset_t mask;
   137    ASSERT_THAT(sigprocmask(0, nullptr, &mask), SyscallSucceeds());
   138  
   139    // Block SIGALRM.
   140    auto cleanup =
   141        ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_BLOCK, SIGALRM));
   142  
   143    // Call with a mask that unblocks SIGALRM. See that ppoll is interrupted.
   144    SetTimer(timer_duration);
   145    MaybeSave();
   146    ASSERT_FALSE(TimerFired());
   147    ASSERT_THAT(syscallPpoll(nullptr, 0, &timeout, &mask, kSigsetSize),
   148                SyscallFailsWithErrno(EINTR));
   149    EXPECT_TRUE(TimerFired());
   150    EXPECT_GT(absl::DurationFromTimespec(timeout), absl::Duration());
   151  }
   152  
   153  }  // namespace
   154  }  // namespace testing
   155  }  // namespace gvisor