diff options
-rw-r--r-- | pkg/sentry/vfs/epoll.go | 6 | ||||
-rw-r--r-- | test/syscalls/linux/BUILD | 1 | ||||
-rw-r--r-- | test/syscalls/linux/epoll.cc | 44 |
3 files changed, 50 insertions, 1 deletions
diff --git a/pkg/sentry/vfs/epoll.go b/pkg/sentry/vfs/epoll.go index befe3ca25..23ccc6b66 100644 --- a/pkg/sentry/vfs/epoll.go +++ b/pkg/sentry/vfs/epoll.go @@ -136,12 +136,16 @@ func (ep *EpollInstance) Readiness(mask waiter.EventMask) waiter.EventMask { return 0 } ep.mu.Lock() - for epi := ep.ready.Front(); epi != nil; epi = epi.Next() { + var next *epollInterest + for epi := ep.ready.Front(); epi != nil; epi = next { + next = epi.Next() wmask := waiter.EventMaskFromLinux(epi.mask) if epi.key.file.Readiness(wmask)&wmask != 0 { ep.mu.Unlock() return waiter.ReadableEvents } + ep.ready.Remove(epi) + epi.ready = false } ep.mu.Unlock() return 0 diff --git a/test/syscalls/linux/BUILD b/test/syscalls/linux/BUILD index 71cc9b51d..9c7bb8394 100644 --- a/test/syscalls/linux/BUILD +++ b/test/syscalls/linux/BUILD @@ -583,6 +583,7 @@ cc_binary( "//test/util:posix_error", "//test/util:test_main", "//test/util:test_util", + "//test/util:thread_util", ], ) diff --git a/test/syscalls/linux/epoll.cc b/test/syscalls/linux/epoll.cc index 3ef8b0327..89b16d168 100644 --- a/test/syscalls/linux/epoll.cc +++ b/test/syscalls/linux/epoll.cc @@ -496,6 +496,50 @@ TEST(EpollTest, PipeReaderHupAfterWriterClosed) { EXPECT_EQ(result[0].data.u64, kMagicConstant); } +TEST(EpollTest, DoubleLayerEpoll) { + int listen_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + struct sockaddr_in sa; + sa.sin_family = AF_INET; + inet_aton("127.0.0.1", &sa.sin_addr); + sa.sin_port = htons(8888); + bind(listen_fd, (struct sockaddr *)&sa, sizeof(sa)); + listen(listen_fd, 1); + + auto epfd1 = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD()); + ASSERT_NO_ERRNO(RegisterEpollFD(epfd1.get(), listen_fd, EPOLLIN|EPOLLHUP, listen_fd)); + + auto epfd2 = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD()); + ASSERT_NO_ERRNO(RegisterEpollFD(epfd2.get(), epfd1.get(), EPOLLIN|EPOLLHUP, epfd1.get())); + + struct epoll_event ret_events[2]; + + // first time + ScopedThread thread1([&sa]() { + sleep(1); + int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + ASSERT_THAT(connect(sock, (struct sockaddr *)&sa, sizeof(sa)), SyscallSucceeds()); + }); + ASSERT_THAT(epoll_wait(epfd2.get(), ret_events, 2, 2000), SyscallSucceedsWithValue(1)); + ASSERT_EQ(ret_events[0].data.fd, epfd1.get()); + ASSERT_THAT(epoll_wait(epfd1.get(), ret_events, 2, 2000), SyscallSucceedsWithValue(1)); + ASSERT_EQ(ret_events[0].data.fd, listen_fd); + int server_fd = accept(listen_fd, NULL, NULL); + ASSERT_THAT(close(server_fd), SyscallSucceeds()); + + // second time to check if event in epfd1 is cleaned correctly + ScopedThread thread2([&sa]() { + sleep(1); + int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + ASSERT_THAT(connect(sock, (struct sockaddr *)&sa, sizeof(sa)), SyscallSucceeds()); + }); + ASSERT_THAT(epoll_wait(epfd2.get(), ret_events, 2, 2000), SyscallSucceedsWithValue(1)); + ASSERT_EQ(ret_events[0].data.fd, epfd1.get()); + ASSERT_THAT(epoll_wait(epfd1.get(), ret_events, 2, 2000), SyscallSucceedsWithValue(1)); + ASSERT_EQ(ret_events[0].data.fd, listen_fd); + server_fd = accept(listen_fd, NULL, NULL); + ASSERT_THAT(close(server_fd), SyscallSucceeds()); +} + } // namespace } // namespace testing |