udp: Handle more error conditions in udp_sock_errs()

udp_sock_errs() reads out everything in the socket error queue.  However
we've seen some cases[0] where an EPOLLERR event is active, but there isn't
anything in the queue.

One possibility is that the error is reported instead by the SO_ERROR
sockopt.  Check for that case and report it as best we can.  If we still
get an EPOLLERR without visible error, we have no way to clear the error
state, so treat it as an unrecoverable error.

[0] https://github.com/containers/podman/issues/23686#issuecomment-2324945010

Link: https://bugs.passt.top/show_bug.cgi?id=95
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
This commit is contained in:
David Gibson 2024-09-06 15:17:10 +10:00 committed by Stefano Brivio
parent bd99f02a64
commit aff5a49b0e

21
udp.c
View file

@ -450,7 +450,8 @@ static int udp_sock_recverr(int s)
static int udp_sock_errs(const struct ctx *c, int s, uint32_t events) static int udp_sock_errs(const struct ctx *c, int s, uint32_t events)
{ {
unsigned n_err = 0; unsigned n_err = 0;
int rc; socklen_t errlen;
int rc, err;
ASSERT(!c->no_udp); ASSERT(!c->no_udp);
@ -464,6 +465,24 @@ static int udp_sock_errs(const struct ctx *c, int s, uint32_t events)
if (rc < 0) if (rc < 0)
return -1; /* error reading error, unrecoverable */ return -1; /* error reading error, unrecoverable */
errlen = sizeof(err);
if (getsockopt(s, SOL_SOCKET, SO_ERROR, &err, &errlen) < 0 ||
errlen != sizeof(err)) {
err_perror("Error reading SO_ERROR");
return -1; /* error reading error, unrecoverable */
}
if (err) {
debug("Unqueued error on UDP socket %i: %s", s, strerror(err));
n_err++;
}
if (!n_err) {
/* EPOLLERR, but no errors to clear !? */
err("EPOLLERR event without reported errors on socket %i", s);
return -1; /* no way to clear, unrecoverable */
}
return n_err; return n_err;
} }