tree-wide: check POLLNVAL everywhere

poll() sets POLLNVAL inside of the poll structures if an invalid fd is
passed. So far we generally didn't check for that, thus not taking
notice of the error. Given that this specific kind of error is generally
indication of a programming error, and given that our code is embedded
into our projects via NSS or because people link against our library,
let's explicitly check for this and convert it to EBADF.

(I ran into a busy loop because of this missing check when some of my
test code accidentally closed an fd it shouldn't close, so this is a
real thing)
This commit is contained in:
Lennart Poettering 2020-06-09 13:40:25 +02:00 committed by Zbigniew Jędrzejewski-Szmek
parent 45204921be
commit dad28bffd6
17 changed files with 111 additions and 27 deletions

View File

@ -33,10 +33,13 @@ int flush_fd(int fd) {
continue;
return -errno;
} else if (r == 0)
}
if (r == 0)
return count;
if (pollfd.revents & POLLNVAL)
return -EBADF;
l = read(fd, buf, sizeof(buf));
if (l < 0) {
@ -169,6 +172,9 @@ int pipe_eof(int fd) {
if (r == 0)
return 0;
if (pollfd.revents & POLLNVAL)
return -EBADF;
return pollfd.revents & POLLHUP;
}
@ -188,6 +194,9 @@ int fd_wait_for_event(int fd, int event, usec_t t) {
if (r == 0)
return 0;
if (pollfd.revents & POLLNVAL)
return -EBADF;
return pollfd.revents;
}

View File

@ -1002,6 +1002,9 @@ int flush_accept(int fd) {
if (r == 0)
return 0;
if (pollfd.revents & POLLNVAL)
return -EBADF;
if (iteration >= MAX_FLUSH_ITERATIONS)
return log_debug_errno(SYNTHETIC_ERRNO(EBUSY),
"Failed to flush connections within " STRINGIFY(MAX_FLUSH_ITERATIONS) " iterations.");

View File

@ -2093,10 +2093,13 @@ static int wait_for_change(sd_journal *j, int poll_fd) {
return log_error_errno(errno, "Couldn't wait for journal event: %m");
}
if (pollfds[1].revents & (POLLHUP|POLLERR)) /* STDOUT has been closed? */
if (pollfds[1].revents & (POLLHUP|POLLERR|POLLNVAL)) /* STDOUT has been closed? */
return log_debug_errno(SYNTHETIC_ERRNO(ECANCELED),
"Standard output has been closed.");
if (pollfds[0].revents & POLLNVAL)
return log_debug_errno(SYNTHETIC_ERRNO(EBADF), "Change fd closed?");
r = sd_journal_process(j);
if (r < 0)
return log_error_errno(r, "Failed to process journal events: %m");

View File

@ -1275,6 +1275,8 @@ int bus_socket_process_opening(sd_bus *b) {
r = poll(&p, 1, 0);
if (r < 0)
return -errno;
if (p.revents & POLLNVAL)
return -EBADF;
if (!(p.revents & (POLLOUT|POLLERR|POLLHUP)))
return 0;

View File

@ -3121,8 +3121,15 @@ static int bus_poll(sd_bus *bus, bool need_more, uint64_t timeout_usec) {
r = ppoll(p, n, m == USEC_INFINITY ? NULL : timespec_store(&ts, m), NULL);
if (r < 0)
return -errno;
if (r == 0)
return 0;
return r > 0 ? 1 : 0;
if (p[0].revents & POLLNVAL)
return -EBADF;
if (n >= 2 && (p[1].revents & POLLNVAL))
return -EBADF;
return 1;
}
_public_ int sd_bus_wait(sd_bus *bus, uint64_t timeout_usec) {

View File

@ -577,6 +577,8 @@ _public_ int sd_notify_barrier(int unset_environment, uint64_t timeout) {
return -errno;
if (r == 0)
return -ETIMEDOUT;
if (pfd.revents & POLLNVAL)
return -EBADF;
return 1;
}

View File

@ -462,7 +462,6 @@ static usec_t calc_elapse(uint64_t usec) {
}
static int rtnl_poll(sd_netlink *rtnl, bool need_more, uint64_t timeout_usec) {
struct pollfd p[1] = {};
struct timespec ts;
usec_t m = USEC_INFINITY;
int r, e;
@ -495,14 +494,21 @@ static int rtnl_poll(sd_netlink *rtnl, bool need_more, uint64_t timeout_usec) {
if (timeout_usec != (uint64_t) -1 && (m == (uint64_t) -1 || timeout_usec < m))
m = timeout_usec;
p[0].fd = rtnl->fd;
p[0].events = e;
struct pollfd p = {
.fd = rtnl->fd,
.events = e,
};
r = ppoll(p, 1, m == (uint64_t) -1 ? NULL : timespec_store(&ts, m), NULL);
r = ppoll(&p, 1, m == (uint64_t) -1 ? NULL : timespec_store(&ts, m), NULL);
if (r < 0)
return -errno;
if (r == 0)
return 0;
return r > 0 ? 1 : 0;
if (p.revents & POLLNVAL)
return -EBADF;
return 1;
}
int sd_netlink_wait(sd_netlink *nl, uint64_t timeout_usec) {

View File

@ -216,8 +216,11 @@ static int udev_monitor_receive_sd_device(struct udev_monitor *udev_monitor, sd_
continue;
return -errno;
} else if (r == 0)
}
if (r == 0)
return -EAGAIN;
if (pfd.revents & POLLNVAL)
return -EBADF;
/* receive next message */
break;

View File

@ -305,6 +305,12 @@ int ask_password_plymouth(
goto finish;
}
if (pollfd[POLL_SOCKET].revents & POLLNVAL ||
(notify >= 0 && pollfd[POLL_INOTIFY].revents & POLLNVAL)) {
r = -EBADF;
goto finish;
}
if (notify >= 0 && pollfd[POLL_INOTIFY].revents != 0)
(void) flush_fd(notify);
@ -541,6 +547,12 @@ int ask_password_tty(
goto finish;
}
if ((pollfd[POLL_TTY].revents & POLLNVAL) ||
(notify >= 0 && (pollfd[POLL_INOTIFY].revents & POLLNVAL))) {
r = -EBADF;
goto finish;
}
if (notify >= 0 && pollfd[POLL_INOTIFY].revents != 0 && keyname) {
(void) flush_fd(notify);
@ -888,6 +900,13 @@ int ask_password_agent(
goto finish;
}
if (pollfd[FD_SOCKET].revents & POLLNVAL ||
pollfd[FD_SIGNAL].revents & POLLNVAL ||
(notify >= 0 && pollfd[FD_INOTIFY].revents & POLLNVAL)) {
r = -EBADF;
goto finish;
}
if (pollfd[FD_SIGNAL].revents & POLLIN) {
r = -EINTR;
goto finish;

View File

@ -218,10 +218,14 @@ static bool barrier_read(Barrier *b, int64_t comp) {
uint64_t buf;
int r;
r = poll(pfd, 2, -1);
if (r < 0 && IN_SET(errno, EAGAIN, EINTR))
continue;
else if (r < 0)
r = poll(pfd, ELEMENTSOF(pfd), -1);
if (r < 0) {
if (IN_SET(errno, EAGAIN, EINTR))
continue;
goto error;
}
if (pfd[0].revents & POLLNVAL ||
pfd[1].revents & POLLNVAL)
goto error;
if (pfd[1].revents) {

View File

@ -334,9 +334,10 @@ static int write_to_terminal(const char *tty, const char *message) {
k = poll(&pollfd, 1, (end - t) / USEC_PER_MSEC);
if (k < 0)
return -errno;
if (k == 0)
return -ETIME;
if (pollfd.revents & POLLNVAL)
return -EBADF;
n = write(fd, p, left);
if (n < 0) {

View File

@ -1048,10 +1048,14 @@ int varlink_wait(Varlink *v, usec_t timeout) {
NULL);
if (r < 0)
return -errno;
if (r == 0)
return 0;
if (pfd.revents & POLLNVAL)
return -EBADF;
handle_revents(v, pfd.revents);
return r > 0 ? 1 : 0;
return 1;
}
int varlink_get_fd(Varlink *v) {
@ -1139,9 +1143,14 @@ int varlink_flush(Varlink *v) {
.events = POLLOUT,
};
if (poll(&pfd, 1, -1) < 0)
r = poll(&pfd, 1, -1);
if (r < 0)
return -errno;
assert(r > 0);
if (pfd.revents & POLLNVAL)
return -EBADF;
handle_revents(v, pfd.revents);
}

View File

@ -271,6 +271,9 @@ static int execute_s2h(const SleepConfig *sleep_config) {
tfd = safe_close(tfd);
if (FLAGS_SET(fds.revents, POLLNVAL))
return log_error_errno(SYNTHETIC_ERRNO(EBADF), "Invalid timer fd to sleep on?");
if (!FLAGS_SET(fds.revents, POLLIN)) /* We woke up before the alarm time, we are done. */
return 0;

View File

@ -238,17 +238,19 @@ static int run(int argc, char *argv[]) {
ts = timespec_store(&_ts, t);
}
{
struct pollfd p[3] = {
{.fd = fd, .events = events_a },
{.fd = STDIN_FILENO, .events = events_b & POLLIN },
{.fd = STDOUT_FILENO, .events = events_b & POLLOUT },
};
struct pollfd p[3] = {
{ .fd = fd, .events = events_a },
{ .fd = STDIN_FILENO, .events = events_b & POLLIN },
{ .fd = STDOUT_FILENO, .events = events_b & POLLOUT },
};
r = ppoll(p, ELEMENTSOF(p), ts, NULL);
}
r = ppoll(p, ELEMENTSOF(p), ts, NULL);
if (r < 0)
return log_error_errno(errno, "ppoll() failed: %m");
if (p[0].revents & POLLNVAL ||
p[1].revents & POLLNVAL ||
p[2].revents & POLLNVAL)
return log_error_errno(SYNTHETIC_ERRNO(EBADF), "Invalid file descriptor to poll on?");
}
return 0;

View File

@ -395,6 +395,10 @@ static int process_and_watch_password_files(bool watch) {
return -errno;
}
if (pollfd[FD_SIGNAL].revents & POLLNVAL ||
pollfd[FD_INOTIFY].revents & POLLNVAL)
return -EBADF;
if (pollfd[FD_INOTIFY].revents != 0)
(void) flush_fd(notify);

View File

@ -202,7 +202,12 @@ int settle_main(int argc, char *argv[], void *userdata) {
return -ETIMEDOUT;
/* wake up when queue becomes empty */
if (poll(&pfd, 1, MSEC_PER_SEC) > 0 && pfd.revents & POLLIN) {
r = poll(&pfd, 1, MSEC_PER_SEC);
if (r < 0)
return -errno;
if (pfd.revents & POLLNVAL)
return -EBADF;
if (r > 0 && pfd.revents & POLLIN) {
r = udev_queue_flush(queue);
if (r < 0)
return log_error_errno(r, "Failed to flush queue: %m");

View File

@ -757,6 +757,8 @@ static int run(int argc, char *argv[]) {
if (poll(&pfd, 1, 0) < 0)
return log_error_errno(errno, "Failed to test for POLLIN on listening socket: %m");
if (FLAGS_SET(pfd.revents, POLLNVAL))
return log_error_errno(SYNTHETIC_ERRNO(EBADF), "Listening socket dead?");
if (FLAGS_SET(pfd.revents, POLLIN)) {
pid_t parent;