diff --git a/src/basic/fd-util.c b/src/basic/fd-util.c index 404361e8c1..61a93fcb4a 100644 --- a/src/basic/fd-util.c +++ b/src/basic/fd-util.c @@ -578,3 +578,40 @@ try_dev_shm_without_o_tmpfile: return -EOPNOTSUPP; } + +int fd_move_above_stdio(int fd) { + int flags, copy; + PROTECT_ERRNO; + + /* Moves the specified file descriptor if possible out of the range [0…2], i.e. the range of + * stdin/stdout/stderr. If it can't be moved outside of this range the original file descriptor is + * returned. This call is supposed to be used for long-lasting file descriptors we allocate in our code that + * might get loaded into foreign code, and where we want ensure our fds are unlikely used accidentally as + * stdin/stdout/stderr of unrelated code. + * + * Note that this doesn't fix any real bugs, it just makes it less likely that our code will be affected by + * buggy code from others that mindlessly invokes 'fprintf(stderr, …' or similar in places where stderr has + * been closed before. + * + * This function is written in a "best-effort" and "least-impact" style. This means whenever we encounter an + * error we simply return the original file descriptor, and we do not touch errno. */ + + if (fd < 0 || fd > 2) + return fd; + + flags = fcntl(fd, F_GETFD, 0); + if (flags < 0) + return fd; + + if (flags & FD_CLOEXEC) + copy = fcntl(fd, F_DUPFD_CLOEXEC, 3); + else + copy = fcntl(fd, F_DUPFD, 3); + if (copy < 0) + return fd; + + assert(copy > 2); + + (void) close(fd); + return copy; +} diff --git a/src/basic/fd-util.h b/src/basic/fd-util.h index 84a0816f03..284856ae6d 100644 --- a/src/basic/fd-util.h +++ b/src/basic/fd-util.h @@ -91,3 +91,5 @@ int acquire_data_fd(const void *data, size_t size, unsigned flags); /* Hint: ENETUNREACH happens if we try to connect to "non-existing" special IP addresses, such as ::5 */ #define ERRNO_IS_DISCONNECT(r) \ IN_SET(r, ENOTCONN, ECONNRESET, ECONNREFUSED, ECONNABORTED, EPIPE, ENETUNREACH) + +int fd_move_above_stdio(int fd); diff --git a/src/basic/log.c b/src/basic/log.c index 12ee65a5c3..b3751299d0 100644 --- a/src/basic/log.c +++ b/src/basic/log.c @@ -105,6 +105,8 @@ static int log_open_console(void) { console_fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC); if (console_fd < 0) return console_fd; + + console_fd = fd_move_above_stdio(console_fd); } return 0; @@ -123,6 +125,7 @@ static int log_open_kmsg(void) { if (kmsg_fd < 0) return -errno; + kmsg_fd = fd_move_above_stdio(kmsg_fd); return 0; } @@ -138,11 +141,11 @@ static int create_log_socket(int type) { if (fd < 0) return -errno; + fd = fd_move_above_stdio(fd); (void) fd_inc_sndbuf(fd, SNDBUF_SIZE); - /* We need a blocking fd here since we'd otherwise lose - messages way too early. However, let's not hang forever in the - unlikely case of a deadlock. */ + /* We need a blocking fd here since we'd otherwise lose messages way too early. However, let's not hang forever + * in the unlikely case of a deadlock. */ if (getpid_cached() == 1) timeval_store(&tv, 10 * USEC_PER_MSEC); else diff --git a/src/libsystemd/sd-bus/bus-container.c b/src/libsystemd/sd-bus/bus-container.c index 16156d8823..477f7053ed 100644 --- a/src/libsystemd/sd-bus/bus-container.c +++ b/src/libsystemd/sd-bus/bus-container.c @@ -54,6 +54,8 @@ int bus_container_connect_socket(sd_bus *b) { if (b->input_fd < 0) return -errno; + b->input_fd = fd_move_above_stdio(b->input_fd); + b->output_fd = b->input_fd; bus_socket_setup(b); diff --git a/src/libsystemd/sd-bus/bus-socket.c b/src/libsystemd/sd-bus/bus-socket.c index d210102841..90132bb87b 100644 --- a/src/libsystemd/sd-bus/bus-socket.c +++ b/src/libsystemd/sd-bus/bus-socket.c @@ -713,6 +713,8 @@ static int bus_socket_inotify_setup(sd_bus *b) { b->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC); if (b->inotify_fd < 0) return -errno; + + b->inotify_fd = fd_move_above_stdio(b->inotify_fd); } /* Make sure the path is NUL terminated */ @@ -879,6 +881,8 @@ int bus_socket_connect(sd_bus *b) { if (b->input_fd < 0) return -errno; + b->input_fd = fd_move_above_stdio(b->input_fd); + b->output_fd = b->input_fd; bus_socket_setup(b); @@ -978,7 +982,7 @@ int bus_socket_exec(sd_bus *b) { } safe_close(s[1]); - b->output_fd = b->input_fd = s[0]; + b->output_fd = b->input_fd = fd_move_above_stdio(s[0]); bus_socket_setup(b); @@ -1206,7 +1210,7 @@ int bus_socket_read_message(sd_bus *bus) { CMSG_FOREACH(cmsg, &mh) if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { - int n, *f; + int n, *f, i; n = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int); @@ -1225,9 +1229,9 @@ int bus_socket_read_message(sd_bus *bus) { return -ENOMEM; } - memcpy_safe(f + bus->n_fds, CMSG_DATA(cmsg), n * sizeof(int)); + for (i = 0; i < n; i++) + f[bus->n_fds++] = fd_move_above_stdio(((int*) CMSG_DATA(cmsg))[i]); bus->fds = f; - bus->n_fds += n; } else log_debug("Got unexpected auxiliary data with level=%d and type=%d", cmsg->cmsg_level, cmsg->cmsg_type); diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c index cb9b3a4545..7355921d30 100644 --- a/src/libsystemd/sd-event/sd-event.c +++ b/src/libsystemd/sd-event/sd-event.c @@ -457,6 +457,8 @@ _public_ int sd_event_new(sd_event** ret) { goto fail; } + e->epoll_fd = fd_move_above_stdio(e->epoll_fd); + if (secure_getenv("SD_EVENT_PROFILE_DELAYS")) { log_debug("Event loop profiling enabled. Logarithmic histogram of event loop iterations in the range 2^0 ... 2^63 us will be logged every 5s."); e->profile_delays = true; @@ -695,7 +697,7 @@ static int event_make_signal_data( return 0; } - d->fd = r; + d->fd = fd_move_above_stdio(r); ev.events = EPOLLIN; ev.data.ptr = d; @@ -1045,6 +1047,8 @@ static int event_setup_timer_fd( if (fd < 0) return -errno; + fd = fd_move_above_stdio(fd); + ev.events = EPOLLIN; ev.data.ptr = d; diff --git a/src/libsystemd/sd-netlink/netlink-socket.c b/src/libsystemd/sd-netlink/netlink-socket.c index e08248c9f6..3474ad9ddb 100644 --- a/src/libsystemd/sd-netlink/netlink-socket.c +++ b/src/libsystemd/sd-netlink/netlink-socket.c @@ -25,6 +25,7 @@ #include "sd-netlink.h" #include "alloc-util.h" +#include "fd-util.h" #include "format-util.h" #include "missing.h" #include "netlink-internal.h" @@ -41,7 +42,7 @@ int socket_open(int family) { if (fd < 0) return -errno; - return fd; + return fd_move_above_stdio(fd); } static int broadcast_groups_get(sd_netlink *nl) { diff --git a/src/libsystemd/sd-resolve/sd-resolve.c b/src/libsystemd/sd-resolve/sd-resolve.c index 787642a7fb..1c68a96fec 100644 --- a/src/libsystemd/sd-resolve/sd-resolve.c +++ b/src/libsystemd/sd-resolve/sd-resolve.c @@ -507,12 +507,15 @@ _public_ int sd_resolve_new(sd_resolve **ret) { goto fail; } - fd_inc_sndbuf(resolve->fds[REQUEST_SEND_FD], QUERIES_MAX * BUFSIZE); - fd_inc_rcvbuf(resolve->fds[REQUEST_RECV_FD], QUERIES_MAX * BUFSIZE); - fd_inc_sndbuf(resolve->fds[RESPONSE_SEND_FD], QUERIES_MAX * BUFSIZE); - fd_inc_rcvbuf(resolve->fds[RESPONSE_RECV_FD], QUERIES_MAX * BUFSIZE); + for (i = 0; i < _FD_MAX; i++) + resolve->fds[i] = fd_move_above_stdio(resolve->fds[i]); - fd_nonblock(resolve->fds[RESPONSE_RECV_FD], true); + (void) fd_inc_sndbuf(resolve->fds[REQUEST_SEND_FD], QUERIES_MAX * BUFSIZE); + (void) fd_inc_rcvbuf(resolve->fds[REQUEST_RECV_FD], QUERIES_MAX * BUFSIZE); + (void) fd_inc_sndbuf(resolve->fds[RESPONSE_SEND_FD], QUERIES_MAX * BUFSIZE); + (void) fd_inc_rcvbuf(resolve->fds[RESPONSE_RECV_FD], QUERIES_MAX * BUFSIZE); + + (void) fd_nonblock(resolve->fds[RESPONSE_RECV_FD], true); *ret = resolve; return 0; diff --git a/src/test/test-fd-util.c b/src/test/test-fd-util.c index e5a0e9d51b..db4a7f8fda 100644 --- a/src/test/test-fd-util.c +++ b/src/test/test-fd-util.c @@ -155,12 +155,31 @@ static void test_acquire_data_fd(void) { test_acquire_data_fd_one(ACQUIRE_NO_DEV_NULL|ACQUIRE_NO_MEMFD|ACQUIRE_NO_PIPE|ACQUIRE_NO_TMPFILE); } +static void test_fd_move_above_stdio(void) { + int original_stdin, new_fd; + + original_stdin = fcntl(0, F_DUPFD, 3); + assert_se(original_stdin >= 3); + assert_se(close_nointr(0) != EBADF); + + new_fd = open("/dev/null", O_RDONLY); + assert_se(new_fd == 0); + + new_fd = fd_move_above_stdio(new_fd); + assert_se(new_fd >= 3); + + assert_se(dup(original_stdin) == 0); + assert_se(close_nointr(original_stdin) != EBADF); + assert_se(close_nointr(new_fd) != EBADF); +} + int main(int argc, char *argv[]) { test_close_many(); test_close_nointr(); test_same_fd(); test_open_serialization_fd(); test_acquire_data_fd(); + test_fd_move_above_stdio(); return 0; }