fix(libkernel): clear POLLOUT from a hung-up socket in poll on Linux

FreeBSD's pollscan clears POLLOUT whenever POLLHUP is set, for every
descriptor. 64ca6e332 applied that to pipes, but a stream socket whose
peer has closed and whose own sending side is shut down still reported
POLLOUT next to POLLHUP, because Linux's poll sets both. Linux sets
POLLHUP only once both directions are shut, the same condition as
FreeBSD, so poll now clears POLLOUT from any entry that reports POLLHUP
there.
This commit is contained in:
Yunus Shaikh authored and Adrià Franch committed 2026-10-25 19:01:02 +01:00
1 parent 7a25f87620
commit 7c36d37671
2 files changed
+30

No files matched your search

@@ -1834,6 +1834,9 @@ int APS5_VABI poll_nid_postfix(GuestPollDescriptor* descriptors, std::uint32_t c
if (pipe && (entry.revents & GuestPollError)) entry.revents = static_cast<short>((entry.revents & ~GuestPollError) | GuestPollHangUp);
if (pipe && (entry.revents & GuestPollHangUp))
entry.revents = static_cast<short>((entry.revents & ~GuestPollWritable) | (entry.events & GuestPollReadable));
#ifndef _WIN32
if (entry.revents & GuestPollHangUp) entry.revents = static_cast<short>(entry.revents & ~GuestPollWritable);
#endif
if (entry.revents) ++ready;
};
const auto deadline = std::chrono::steady_clock::now() + std::chrono::milliseconds(timeout < 0 ? 0 : timeout);
+27
View File
@@ -261,9 +261,36 @@ static void PollsAStreamWhosePeerClosed() {
for (const int descriptor : {client, server, listener}) Require(close_nid_postfix(descriptor) == 0);
}
#ifndef _WIN32
static void PollsAHungUpSocketWithoutPollout() {
const int listener = socket_nid_postfix(2, 1, 0);
Require(listener >= 0);
std::array<unsigned char, 16> address{16, 2, 0, 0, 127, 0, 0, 1};
Require(bind_nid_postfix(listener, address.data(), address.size()) == 0);
std::uint32_t size = address.size();
Require(getsockname_nid_postfix(listener, address.data(), &size) == 0 && listen_nid_postfix(listener, 1) == 0);
const int client = socket_nid_postfix(2, 1, 0);
Require(client >= 0 && connect_nid_postfix(client, address.data(), address.size()) == 0);
const int server = accept_nid_postfix(listener, nullptr, nullptr);
Require(server >= 0);
Require(close_nid_postfix(client) == 0);
char byte = 0;
GuestPollDescriptor ended{server, In, 0};
Require(poll_nid_postfix(&ended, 1, 1000) == 1);
Require(recv_nid_postfix(server, &byte, 1, 0) == 0);
Require(shutdown_nid_postfix(server, 1) == 0);
GuestPollDescriptor hungUp{server, static_cast<short>(In | Out), 0};
Require(poll_nid_postfix(&hungUp, 1, 1000) == 1 && hungUp.revents == (In | Hup));
Require(close_nid_postfix(server) == 0 && close_nid_postfix(listener) == 0);
}
#endif
int main() {
PollsUrgentData();
PollsAStreamWhosePeerClosed();
#ifndef _WIN32
PollsAHungUpSocketWithoutPollout();
#endif
const int listener = socket_nid_postfix(2, 1, 0);
Require(listener >= 0 && listener < 1024);
std::array<unsigned char, 16> address{16, 2, 0, 0, 127, 0, 0, 1};