From: Yu Watanabe Date: Wed, 8 Oct 2025 01:01:06 +0000 (+0900) Subject: coredump: split out process_socket() to coredump-receive.[ch] X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=ab2e98b43c32a570eb0787d842fd0ef414be94dd;p=thirdparty%2Fsystemd.git coredump: split out process_socket() to coredump-receive.[ch] Then, rename to coredump_receive(). --- diff --git a/src/coredump/coredump-context.c b/src/coredump/coredump-context.c index 8134d75cbc4..6e62c4615f4 100644 --- a/src/coredump/coredump-context.c +++ b/src/coredump/coredump-context.c @@ -275,7 +275,7 @@ int context_parse_iovw(Context *context, struct iovec_wrapper *iovw) { bool have_signal_name = false; FOREACH_ARRAY(iovec, iovw->iovec, iovw->count) { /* Note that these strings are NUL-terminated, because we made sure that a trailing NUL byte - * is in the buffer, though not included in the iov_len count. See process_socket() and + * is in the buffer, though not included in the iov_len count. See coredump_receive() and * gather_pid_metadata_*(). */ assert(((char*) iovec->iov_base)[iovec->iov_len] == 0); diff --git a/src/coredump/coredump-receive.c b/src/coredump/coredump-receive.c new file mode 100644 index 00000000000..db9987b3ff6 --- /dev/null +++ b/src/coredump/coredump-receive.c @@ -0,0 +1,162 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include + +#include "coredump-context.h" +#include "coredump-receive.h" +#include "coredump-submit.h" +#include "iovec-util.h" +#include "iovec-wrapper.h" +#include "fd-util.h" +#include "log.h" +#include "socket-util.h" + +int coredump_receive(int fd) { + _cleanup_(iovw_done_free) struct iovec_wrapper iovw = {}; + _cleanup_(context_done) Context context = CONTEXT_NULL; + _cleanup_close_ int input_fd = -EBADF; + enum { + STATE_PAYLOAD, + STATE_INPUT_FD_DONE, + STATE_PID_FD_DONE, + } state = STATE_PAYLOAD; + int r; + + assert(fd >= 0); + + log_setup(); + + log_debug("Processing coredump received via socket..."); + + for (;;) { + CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(int))) control; + struct msghdr mh = { + .msg_control = &control, + .msg_controllen = sizeof(control), + .msg_iovlen = 1, + }; + ssize_t n, l; + + l = next_datagram_size_fd(fd); + if (l < 0) + return log_error_errno(l, "Failed to determine datagram size to read: %m"); + + _cleanup_(iovec_done) struct iovec iovec = { + .iov_len = l, + .iov_base = malloc(l + 1), + }; + if (!iovec.iov_base) + return log_oom(); + + mh.msg_iov = &iovec; + + n = recvmsg_safe(fd, &mh, MSG_CMSG_CLOEXEC); + if (n < 0) + return log_error_errno(n, "Failed to receive datagram: %m"); + + /* The final zero-length datagrams ("sentinels") carry file descriptors and tell us that + * we're done. There are three sentinels: one with just the coredump fd, followed by one with + * the pidfd, and finally one with the mount tree fd. The latter two or the last one may be + * omitted (which is supported for compatibility with older systemd version, in particular to + * facilitate cross-container coredumping). */ + if (n == 0) { + struct cmsghdr *found; + + found = cmsg_find(&mh, SOL_SOCKET, SCM_RIGHTS, CMSG_LEN(sizeof(int))); + if (!found) { + /* This is zero length message but it either doesn't carry a single + * descriptor, or it has more than one. This is a protocol violation so let's + * bail out. + * + * Well, not quite! In practice there's one more complication: EOF on + * SOCK_SEQPACKET is not distinguishable from a zero length datagram. Hence + * if we get a zero length datagram without fds we consider it EOF, and + * that's permissible for the final two fds. Hence let's be strict on the + * first fd, but lenient on the other two. */ + + if (!cmsg_find(&mh, SOL_SOCKET, SCM_RIGHTS, (socklen_t) -1) && state != STATE_PAYLOAD) + /* No fds, and already got the first fd → we are done. */ + break; + + cmsg_close_all(&mh); + return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), + "Received zero length message with zero or more than one file descriptor(s), expected one."); + } + + switch (state) { + + case STATE_PAYLOAD: + assert(input_fd < 0); + input_fd = *CMSG_TYPED_DATA(found, int); + state = STATE_INPUT_FD_DONE; + continue; + + case STATE_INPUT_FD_DONE: + assert(!pidref_is_set(&context.pidref)); + + r = pidref_set_pidfd_consume(&context.pidref, *CMSG_TYPED_DATA(found, int)); + if (r < 0) + return log_error_errno(r, "Failed to initialize pidref: %m"); + + state = STATE_PID_FD_DONE; + continue; + + case STATE_PID_FD_DONE: + assert(context.mount_tree_fd < 0); + context.mount_tree_fd = *CMSG_TYPED_DATA(found, int); + /* We have all FDs we need so we are done. */ + break; + } + + break; + } + + cmsg_close_all(&mh); + + /* Only zero length messages are allowed after the first message that carried a file descriptor. */ + if (state != STATE_PAYLOAD) + return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Received unexpected message with non-zero length."); + + /* Payload messages should not carry fds */ + if (cmsg_find(&mh, SOL_SOCKET, SCM_RIGHTS, (socklen_t) -1)) + return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), + "Received payload message with file descriptor(s), expected none."); + + /* Add trailing NUL byte, in case these are strings */ + ((char*) iovec.iov_base)[n] = 0; + iovec.iov_len = (size_t) n; + + if (iovw_put(&iovw, iovec.iov_base, iovec.iov_len) < 0) + return log_oom(); + + TAKE_STRUCT(iovec); + } + + /* Make sure we got all data we really need */ + assert(input_fd >= 0); + + r = context_parse_iovw(&context, &iovw); + if (r < 0) + return r; + + /* Make sure we received all the expected fields. We support being called by an *older* + * systemd-coredump from the outside, so we require only the basic set of fields that + * was being sent when the support for sending to containers over a socket was added + * in a108c43e36d3ceb6e34efe37c014fc2cda856000. */ + meta_argv_t i; + FOREACH_ARGUMENT(i, + META_ARGV_PID, + META_ARGV_UID, + META_ARGV_GID, + META_ARGV_SIGNAL, + META_ARGV_TIMESTAMP, + META_ARGV_RLIMIT, + META_ARGV_HOSTNAME, + META_COMM) + if (!context.meta[i]) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Mandatory argument %s not received on socket, aborting.", + meta_field_names[i]); + + return coredump_submit(&context, &iovw, input_fd); +} diff --git a/src/coredump/coredump-receive.h b/src/coredump/coredump-receive.h new file mode 100644 index 00000000000..2476887e198 --- /dev/null +++ b/src/coredump/coredump-receive.h @@ -0,0 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +int coredump_receive(int fd); diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c index e34b9a8d85b..df85ef447bb 100644 --- a/src/coredump/coredump.c +++ b/src/coredump/coredump.c @@ -25,6 +25,7 @@ #include "coredump-backtrace.h" #include "coredump-config.h" #include "coredump-context.h" +#include "coredump-receive.h" #include "coredump-submit.h" #include "coredump-util.h" #include "coredump-vacuum.h" @@ -60,156 +61,6 @@ #include "uid-classification.h" #include "user-util.h" -static int process_socket(int fd) { - _cleanup_(iovw_done_free) struct iovec_wrapper iovw = {}; - _cleanup_(context_done) Context context = CONTEXT_NULL; - _cleanup_close_ int input_fd = -EBADF; - enum { - STATE_PAYLOAD, - STATE_INPUT_FD_DONE, - STATE_PID_FD_DONE, - } state = STATE_PAYLOAD; - int r; - - assert(fd >= 0); - - log_setup(); - - log_debug("Processing coredump received via socket..."); - - for (;;) { - CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(int))) control; - struct msghdr mh = { - .msg_control = &control, - .msg_controllen = sizeof(control), - .msg_iovlen = 1, - }; - ssize_t n, l; - - l = next_datagram_size_fd(fd); - if (l < 0) - return log_error_errno(l, "Failed to determine datagram size to read: %m"); - - _cleanup_(iovec_done) struct iovec iovec = { - .iov_len = l, - .iov_base = malloc(l + 1), - }; - if (!iovec.iov_base) - return log_oom(); - - mh.msg_iov = &iovec; - - n = recvmsg_safe(fd, &mh, MSG_CMSG_CLOEXEC); - if (n < 0) - return log_error_errno(n, "Failed to receive datagram: %m"); - - /* The final zero-length datagrams ("sentinels") carry file descriptors and tell us that - * we're done. There are three sentinels: one with just the coredump fd, followed by one with - * the pidfd, and finally one with the mount tree fd. The latter two or the last one may be - * omitted (which is supported for compatibility with older systemd version, in particular to - * facilitate cross-container coredumping). */ - if (n == 0) { - struct cmsghdr *found; - - found = cmsg_find(&mh, SOL_SOCKET, SCM_RIGHTS, CMSG_LEN(sizeof(int))); - if (!found) { - /* This is zero length message but it either doesn't carry a single - * descriptor, or it has more than one. This is a protocol violation so let's - * bail out. - * - * Well, not quite! In practice there's one more complication: EOF on - * SOCK_SEQPACKET is not distinguishable from a zero length datagram. Hence - * if we get a zero length datagram without fds we consider it EOF, and - * that's permissible for the final two fds. Hence let's be strict on the - * first fd, but lenient on the other two. */ - - if (!cmsg_find(&mh, SOL_SOCKET, SCM_RIGHTS, (socklen_t) -1) && state != STATE_PAYLOAD) - /* No fds, and already got the first fd → we are done. */ - break; - - cmsg_close_all(&mh); - return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), - "Received zero length message with zero or more than one file descriptor(s), expected one."); - } - - switch (state) { - - case STATE_PAYLOAD: - assert(input_fd < 0); - input_fd = *CMSG_TYPED_DATA(found, int); - state = STATE_INPUT_FD_DONE; - continue; - - case STATE_INPUT_FD_DONE: - assert(!pidref_is_set(&context.pidref)); - - r = pidref_set_pidfd_consume(&context.pidref, *CMSG_TYPED_DATA(found, int)); - if (r < 0) - return log_error_errno(r, "Failed to initialize pidref: %m"); - - state = STATE_PID_FD_DONE; - continue; - - case STATE_PID_FD_DONE: - assert(context.mount_tree_fd < 0); - context.mount_tree_fd = *CMSG_TYPED_DATA(found, int); - /* We have all FDs we need so we are done. */ - break; - } - - break; - } - - cmsg_close_all(&mh); - - /* Only zero length messages are allowed after the first message that carried a file descriptor. */ - if (state != STATE_PAYLOAD) - return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Received unexpected message with non-zero length."); - - /* Payload messages should not carry fds */ - if (cmsg_find(&mh, SOL_SOCKET, SCM_RIGHTS, (socklen_t) -1)) - return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), - "Received payload message with file descriptor(s), expected none."); - - /* Add trailing NUL byte, in case these are strings */ - ((char*) iovec.iov_base)[n] = 0; - iovec.iov_len = (size_t) n; - - if (iovw_put(&iovw, iovec.iov_base, iovec.iov_len) < 0) - return log_oom(); - - TAKE_STRUCT(iovec); - } - - /* Make sure we got all data we really need */ - assert(input_fd >= 0); - - r = context_parse_iovw(&context, &iovw); - if (r < 0) - return r; - - /* Make sure we received all the expected fields. We support being called by an *older* - * systemd-coredump from the outside, so we require only the basic set of fields that - * was being sent when the support for sending to containers over a socket was added - * in a108c43e36d3ceb6e34efe37c014fc2cda856000. */ - meta_argv_t i; - FOREACH_ARGUMENT(i, - META_ARGV_PID, - META_ARGV_UID, - META_ARGV_GID, - META_ARGV_SIGNAL, - META_ARGV_TIMESTAMP, - META_ARGV_RLIMIT, - META_ARGV_HOSTNAME, - META_COMM) - if (!context.meta[i]) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "Mandatory argument %s not received on socket, aborting.", - meta_field_names[i]); - - return coredump_submit(&context, &iovw, input_fd); -} - static int send_iovec(const struct iovec_wrapper *iovw, int input_fd, PidRef *pidref, int mount_tree_fd) { _cleanup_close_ int fd = -EBADF; int r; @@ -624,7 +475,7 @@ static int run(int argc, char *argv[]) { else return process_kernel(argc, argv); } else if (r == 1) - return process_socket(SD_LISTEN_FDS_START); + return coredump_receive(SD_LISTEN_FDS_START); return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Received unexpected number of file descriptors."); diff --git a/src/coredump/meson.build b/src/coredump/meson.build index c1d26b4ac62..a14a4464de3 100644 --- a/src/coredump/meson.build +++ b/src/coredump/meson.build @@ -9,6 +9,7 @@ systemd_coredump_sources = files( 'coredump-backtrace.c', 'coredump-config.c', 'coredump-context.c', + 'coredump-receive.c', 'coredump-submit.c', ) systemd_coredump_extract_sources = files(