summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--pkg/sentry/vfs/epoll.go6
-rw-r--r--test/syscalls/linux/BUILD1
-rw-r--r--test/syscalls/linux/epoll.cc44
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