diff --git a/src/basic/iovec-wrapper.c b/src/basic/iovec-wrapper.c index b335acd108..5cc3cc2f93 100644 --- a/src/basic/iovec-wrapper.c +++ b/src/basic/iovec-wrapper.c @@ -9,22 +9,27 @@ struct iovec_wrapper *iovw_new(void) { return new0(struct iovec_wrapper, 1); } -void iovw_free_contents(struct iovec_wrapper *iovw, bool free_vectors) { +void iovw_done(struct iovec_wrapper *iovw) { assert(iovw); - if (free_vectors) - for (size_t i = 0; i < iovw->count; i++) - free(iovw->iovec[i].iov_base); - iovw->iovec = mfree(iovw->iovec); iovw->count = 0; } +void iovw_done_free(struct iovec_wrapper *iovw) { + assert(iovw); + + FOREACH_ARRAY(i, iovw->iovec, iovw->count) + iovec_done(i); + + iovw_done(iovw); +} + struct iovec_wrapper *iovw_free_free(struct iovec_wrapper *iovw) { if (!iovw) return NULL; - iovw_free_contents(iovw, /* free_vectors= */ true); + iovw_done_free(iovw); return mfree(iovw); } @@ -32,7 +37,7 @@ struct iovec_wrapper *iovw_free(struct iovec_wrapper *iovw) { if (!iovw) return NULL; - iovw_free_contents(iovw, /* free_vectors= */ false); + iovw_done(iovw); return mfree(iovw); } @@ -124,7 +129,7 @@ int iovw_append(struct iovec_wrapper *target, const struct iovec_wrapper *source rollback: for (size_t i = original_count; i < target->count; i++) - free(target->iovec[i].iov_base); + iovec_done(target->iovec + i); target->count = original_count; return r; diff --git a/src/basic/iovec-wrapper.h b/src/basic/iovec-wrapper.h index 05e220c1d0..4778fa6b27 100644 --- a/src/basic/iovec-wrapper.h +++ b/src/basic/iovec-wrapper.h @@ -17,7 +17,8 @@ struct iovec_wrapper *iovw_free_free(struct iovec_wrapper *iovw); DEFINE_TRIVIAL_CLEANUP_FUNC(struct iovec_wrapper*, iovw_free_free); -void iovw_free_contents(struct iovec_wrapper *iovw, bool free_vectors); +void iovw_done_free(struct iovec_wrapper *iovw); +void iovw_done(struct iovec_wrapper *iovw); int iovw_put(struct iovec_wrapper *iovw, void *data, size_t len); static inline int iovw_consume(struct iovec_wrapper *iovw, void *data, size_t len) { diff --git a/src/basic/signal-util.c b/src/basic/signal-util.c index 670040f1ad..ad9ed049f1 100644 --- a/src/basic/signal-util.c +++ b/src/basic/signal-util.c @@ -301,3 +301,19 @@ const struct sigaction sigaction_default = { .sa_handler = SIG_DFL, .sa_flags = SA_RESTART, }; + +int parse_signo(const char *s, int *ret) { + int sig, r; + + r = safe_atoi(s, &sig); + if (r < 0) + return r; + + if (!SIGNAL_VALID(sig)) + return -EINVAL; + + if (ret) + *ret = sig; + + return 0; +} diff --git a/src/basic/signal-util.h b/src/basic/signal-util.h index 5739fe0559..559ce42949 100644 --- a/src/basic/signal-util.h +++ b/src/basic/signal-util.h @@ -68,3 +68,5 @@ void propagate_signal(int sig, siginfo_t *siginfo); extern const struct sigaction sigaction_ignore; extern const struct sigaction sigaction_default; + +int parse_signo(const char *s, int *ret); diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c index dc725d1b22..c3e97325e0 100644 --- a/src/coredump/coredump.c +++ b/src/coredump/coredump.c @@ -140,15 +140,28 @@ static const char * const meta_field_names[_META_MAX] = { }; typedef struct Context { - const char *meta[_META_MAX]; - size_t meta_size[_META_MAX]; - pid_t pid; + PidRef pidref; uid_t uid; gid_t gid; + int signo; + uint64_t rlimit; bool is_pid1; bool is_journald; + int mount_tree_fd; + + /* These point into external memory, are not owned by this object */ + const char *meta[_META_MAX]; + size_t meta_size[_META_MAX]; } Context; +#define CONTEXT_NULL \ + (Context) { \ + .pidref = PIDREF_NULL, \ + .uid = UID_INVALID, \ + .gid = GID_INVALID, \ + .mount_tree_fd = -EBADF, \ + } + typedef enum CoredumpStorage { COREDUMP_STORAGE_NONE, COREDUMP_STORAGE_EXTERNAL, @@ -177,6 +190,13 @@ static uint64_t arg_max_use = UINT64_MAX; static bool arg_enter_namespace = false; #endif +static void context_done(Context *c) { + assert(c); + + pidref_done(&c->pidref); + c->mount_tree_fd = safe_close(c->mount_tree_fd); +} + static int parse_config(void) { static const ConfigTableItem items[] = { { "Coredump", "Storage", config_parse_coredump_storage, 0, &arg_storage }, @@ -436,7 +456,7 @@ static int save_external_coredump( _cleanup_(unlink_and_freep) char *tmp = NULL; _cleanup_free_ char *fn = NULL; _cleanup_close_ int fd = -EBADF; - uint64_t rlimit, process_limit, max_size; + uint64_t process_limit, max_size; bool truncated, storage_on_tmpfs; struct stat st; int r; @@ -449,11 +469,7 @@ static int save_external_coredump( assert(ret_compressed_size); assert(ret_truncated); - r = safe_atou64(context->meta[META_ARGV_RLIMIT], &rlimit); - if (r < 0) - return log_error_errno(r, "Failed to parse resource limit '%s': %m", - context->meta[META_ARGV_RLIMIT]); - if (rlimit < page_size()) + if (context->rlimit < page_size()) /* Is coredumping disabled? Then don't bother saving/processing the * coredump. Anything below PAGE_SIZE cannot give a readable coredump * (the kernel uses ELF_EXEC_PAGESIZE which is not easily accessible, but @@ -468,7 +484,7 @@ static int save_external_coredump( "Limits for coredump processing and storage are both 0, not dumping core."); /* Never store more than the process configured, or than we actually shall keep or process */ - max_size = MIN(rlimit, process_limit); + max_size = MIN(context->rlimit, process_limit); r = make_filename(context, &fn); if (r < 0) @@ -773,9 +789,12 @@ static int get_process_container_parent_cmdline(pid_t pid, char** cmdline) { } static int change_uid_gid(const Context *context) { + int r; + + assert(context); + uid_t uid = context->uid; gid_t gid = context->gid; - int r; if (uid_is_system(uid)) { const char *user = "systemd-coredump"; @@ -821,8 +840,7 @@ static int attach_mount_tree(int mount_tree_fd) { static int submit_coredump( const Context *context, struct iovec_wrapper *iovw, - int input_fd, - int mount_tree_fd) { + int input_fd) { _cleanup_(sd_json_variant_unrefp) sd_json_variant *json_metadata = NULL; _cleanup_close_ int coredump_fd = -EBADF, coredump_node_fd = -EBADF; @@ -863,7 +881,7 @@ static int submit_coredump( (void) coredump_vacuum(coredump_node_fd >= 0 ? coredump_node_fd : coredump_fd, arg_keep_free, arg_max_use); } - if (mount_tree_fd >= 0 && attach_mount_tree(mount_tree_fd) >= 0) + if (context->mount_tree_fd >= 0 && attach_mount_tree(context->mount_tree_fd) >= 0) root = MOUNT_TREE_ROOT; /* Now, let's drop privileges to become the user who owns the segfaulted process and allocate the @@ -996,7 +1014,7 @@ static int submit_coredump( return 0; } -static int save_context(Context *context, const struct iovec_wrapper *iovw) { +static int context_parse_iovw(Context *context, struct iovec_wrapper *iovw) { const char *unit; int r; @@ -1004,33 +1022,46 @@ static int save_context(Context *context, const struct iovec_wrapper *iovw) { assert(iovw); assert(iovw->count >= _META_ARGV_MAX); - /* The context does not allocate any memory on its own */ - - for (size_t n = 0; n < iovw->count; n++) { - struct iovec *iovec = iovw->iovec + n; + /* Converts the data in the iovec array iovw into separate fields. Fills in context->meta[] (for + * which no memory is allocated, it just contains direct pointers into the iovec array memory). */ + bool have_signal_name = false; + FOREACH_ARRAY(iovec, iovw->iovec, iovw->count) { for (size_t i = 0; i < ELEMENTSOF(meta_field_names); i++) { /* 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 gather_pid_metadata_*()) */ assert(((char*) iovec->iov_base)[iovec->iov_len] == 0); - const char *p = startswith(iovec->iov_base, meta_field_names[i]); + const char *p = memory_startswith(iovec->iov_base, iovec->iov_len, meta_field_names[i]); if (p) { context->meta[i] = p; context->meta_size[i] = iovec->iov_len - strlen(meta_field_names[i]); break; } } + + have_signal_name = have_signal_name || + memory_startswith(iovec->iov_base, iovec->iov_len, "COREDUMP_SIGNAL_NAME="); } - if (!context->meta[META_ARGV_PID]) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "Failed to find the PID of crashing process"); + /* The basic fields from argv[] should always be there, refuse early if not */ + for (int i = 0; i < _META_ARGV_MAX; i++) + if (!context->meta[i]) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "A required (%s) has not been sent, aborting.", meta_field_names[i]); - r = parse_pid(context->meta[META_ARGV_PID], &context->pid); + pid_t parsed_pid; + r = parse_pid(context->meta[META_ARGV_PID], &parsed_pid); if (r < 0) return log_error_errno(r, "Failed to parse PID \"%s\": %m", context->meta[META_ARGV_PID]); + if (pidref_is_set(&context->pidref)) { + if (context->pidref.pid != parsed_pid) + return log_error_errno(r, "Passed PID " PID_FMT " does not match passed " PID_FMT ": %m", parsed_pid, context->pidref.pid); + } else { + r = pidref_set_pid(&context->pidref, parsed_pid); + if (r < 0) + return log_error_errno(r, "Failed to initialize pidref from pid " PID_FMT ": %m", parsed_pid); + } r = parse_uid(context->meta[META_ARGV_UID], &context->uid); if (r < 0) @@ -1040,25 +1071,42 @@ static int save_context(Context *context, const struct iovec_wrapper *iovw) { if (r < 0) return log_error_errno(r, "Failed to parse GID \"%s\": %m", context->meta[META_ARGV_GID]); + r = parse_signo(context->meta[META_ARGV_SIGNAL], &context->signo); + if (r < 0) + log_warning_errno(r, "Failed to parse signal number \"%s\", ignoring: %m", context->meta[META_ARGV_SIGNAL]); + + r = safe_atou64(context->meta[META_ARGV_RLIMIT], &context->rlimit); + if (r < 0) + log_warning_errno(r, "Failed to parse resource limit \"%s\", ignoring: %m", context->meta[META_ARGV_RLIMIT]); + unit = context->meta[META_UNIT]; context->is_pid1 = streq(context->meta[META_ARGV_PID], "1") || streq_ptr(unit, SPECIAL_INIT_SCOPE); context->is_journald = streq_ptr(unit, SPECIAL_JOURNALD_SERVICE); + /* After parsing everything, let's also synthesize a new iovw field for the textual signal name if it + * isn't already set. */ + if (SIGNAL_VALID(context->signo) && !have_signal_name) + (void) iovw_put_string_field(iovw, "COREDUMP_SIGNAL_NAME=SIG", signal_to_string(context->signo)); + return 0; } static int process_socket(int fd) { - _cleanup_close_ int input_fd = -EBADF, mount_tree_fd = -EBADF; - Context context = {}; - struct iovec_wrapper iovw = {}; - bool first = true; + _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 on stdin..."); + log_debug("Processing coredump received via socket..."); for (;;) { CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(int))) control; @@ -1070,103 +1118,95 @@ static int process_socket(int fd) { ssize_t n, l; l = next_datagram_size_fd(fd); - if (l < 0) { - r = log_error_errno(l, "Failed to determine datagram size to read: %m"); - goto finish; - } + 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) { - r = log_oom(); - goto finish; - } + if (!iovec.iov_base) + return log_oom(); mh.msg_iov = &iovec; n = recvmsg_safe(fd, &mh, MSG_CMSG_CLOEXEC); - if (n < 0) { - r = log_error_errno(n, "Failed to receive datagram: %m"); - goto finish; - } + if (n < 0) + return log_error_errno(n, "Failed to receive datagram: %m"); - /* The final zero-length datagram carries the file descriptors and tells us - * that we're done. */ + /* 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; - if (first) { - found = cmsg_find(&mh, SOL_SOCKET, SCM_RIGHTS, CMSG_LEN(sizeof(int))); - if (found) { - /* This is the first message that carries file descriptors. Maybe - * there will be one more that actually contains array of two - * descriptors. */ - assert(input_fd < 0); + 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. */ - input_fd = *CMSG_TYPED_DATA(found, int); - first = false; + 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; - continue; - } - - /* 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. */ cmsg_close_all(&mh); - r = log_error_errno(SYNTHETIC_ERRNO(EBADMSG), - "Received zero length message with zero or more than one file descriptor(s)."); - goto finish; + return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), + "Received zero length message with zero or more than one file descriptor(s), expected one."); } - /* Second zero length message might carry two file descriptors, coredump fd and mount tree fd. */ - found = cmsg_find(&mh, SOL_SOCKET, SCM_RIGHTS, CMSG_LEN(sizeof(int) * 2)); - if (found) { - int fds[2] = EBADF_PAIR; + switch (state) { - memcpy(fds, CMSG_TYPED_DATA(found, int), sizeof(int) * 2); + case STATE_PAYLOAD: + assert(input_fd < 0); + input_fd = *CMSG_TYPED_DATA(found, int); + state = STATE_INPUT_FD_DONE; + continue; - assert(mount_tree_fd < 0); - assert(input_fd >= 0); + case STATE_INPUT_FD_DONE: + assert(!pidref_is_set(&context.pidref)); - /* Let's close input fd we got in the previous iteration. */ - safe_close(input_fd); + 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"); - input_fd = fds[0]; - mount_tree_fd = fds[1]; + state = STATE_PID_FD_DONE; + continue; - /* We have all FDs we need so let's take a shortcut here. */ + 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; } - /* This is second iteration and we didn't find array of two FDs. */ - found = cmsg_find(&mh, SOL_SOCKET, SCM_RIGHTS, (socklen_t) -1); - cmsg_close_all(&mh); - - if (!found) - /* Hence we either have no FDs which is OK and we can break. */ - break; - - /* Or we have some other number of FDs and somebody is playing games with us. */ - r = log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Received unexpected file descriptors."); - goto finish; - + break; } + cmsg_close_all(&mh); /* Only zero length messages are allowed after the first message that carried a file descriptor. */ - if (!first) { - r = log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Received unexpected message with non zero length."); - goto finish; - } + 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; - r = iovw_put(&iovw, iovec.iov_base, iovec.iov_len); - if (r < 0) - goto finish; + if (iovw_put(&iovw, iovec.iov_base, iovec.iov_len) < 0) + return log_oom(); TAKE_STRUCT(iovec); } @@ -1174,27 +1214,19 @@ static int process_socket(int fd) { /* Make sure we got all data we really need */ assert(input_fd >= 0); - r = save_context(&context, &iovw); + r = context_parse_iovw(&context, &iovw); if (r < 0) - goto finish; + return r; /* Make sure we received at least all fields we need. */ for (int i = 0; i < _META_MANDATORY_MAX; i++) - if (!context.meta[i]) { - r = log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "A mandatory argument (%i) has not been sent, aborting.", - i); - goto finish; - } + if (!context.meta[i]) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "A mandatory argument (%i) has not been sent, aborting.", i); - r = submit_coredump(&context, &iovw, input_fd, mount_tree_fd); - -finish: - iovw_free_contents(&iovw, true); - return r; + return submit_coredump(&context, &iovw, input_fd); } -static int send_iovec(const struct iovec_wrapper *iovw, int input_fd, int mounts_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; @@ -1233,7 +1265,7 @@ static int send_iovec(const struct iovec_wrapper *iovw, int input_fd, int mounts * what we want to send, and the second one contains * the trailing dots. */ copy[0] = iovw->iovec[i]; - copy[1] = IOVEC_MAKE(((char[]){'.', '.', '.'}), 3); + copy[1] = IOVEC_MAKE(((const char[]){'.', '.', '.'}), 3); mh.msg_iov = copy; mh.msg_iovlen = 2; @@ -1247,15 +1279,26 @@ static int send_iovec(const struct iovec_wrapper *iovw, int input_fd, int mounts } } + /* First sentinel: the coredump fd */ r = send_one_fd(fd, input_fd, 0); if (r < 0) return log_error_errno(r, "Failed to send coredump fd: %m"); - if (mounts_fd >= 0) { - r = send_many_fds(fd, (int[]) { input_fd, mounts_fd }, 2, 0); - if (r < 0) - return log_error_errno(r, "Failed to send coredump fds: %m"); - } + /* The optional second sentinel: the pidfd */ + if (!pidref_is_set(pidref) || pidref->fd < 0) /* If we have no pidfd, stop now */ + return 0; + + r = send_one_fd(fd, pidref->fd, 0); + if (r < 0) + return log_error_errno(r, "Failed to send pidfd: %m"); + + /* The optional third sentinel: the mount tree fd */ + if (mount_tree_fd < 0) /* If we have no mount tree, stop now */ + return 0; + + r = send_one_fd(fd, mount_tree_fd, 0); + if (r < 0) + return log_error_errno(r, "Failed to send mount tree fd: %m"); return 0; } @@ -1265,9 +1308,7 @@ static int gather_pid_metadata_from_argv( Context *context, int argc, char **argv) { - _cleanup_free_ char *free_timestamp = NULL; - int r, signo; - char *t; + int r; assert(iovw); assert(context); @@ -1281,30 +1322,19 @@ static int gather_pid_metadata_from_argv( argc, _META_ARGV_MAX); for (int i = 0; i < _META_ARGV_MAX; i++) { + _cleanup_free_ char *buf = NULL; + const char *t = argv[i]; - t = argv[i]; - - switch (i) { - - case META_ARGV_TIMESTAMP: + if (i == META_ARGV_TIMESTAMP) { /* The journal fields contain the timestamp padded with six * zeroes, so that the kernel-supplied 1s granularity timestamps * becomes 1μs granularity, i.e. the granularity systemd usually * operates in. */ - t = free_timestamp = strjoin(argv[i], "000000"); - if (!t) + buf = strjoin(argv[i], "000000"); + if (!buf) return log_oom(); - break; - case META_ARGV_SIGNAL: - /* For signal, record its pretty name too */ - if (safe_atoi(argv[i], &signo) >= 0 && SIGNAL_VALID(signo)) - (void) iovw_put_string_field(iovw, "COREDUMP_SIGNAL_NAME=SIG", - signal_to_string(signo)); - break; - - default: - break; + t = buf; } r = iovw_put_string_field(iovw, meta_field_names[i], t); @@ -1314,7 +1344,7 @@ static int gather_pid_metadata_from_argv( /* Cache some of the process metadata we collected so far and that we'll need to * access soon */ - return save_context(context, iovw); + return context_parse_iovw(context, iovw); } static int gather_pid_metadata_from_procfs(struct iovec_wrapper *iovw, Context *context) { @@ -1331,10 +1361,10 @@ static int gather_pid_metadata_from_procfs(struct iovec_wrapper *iovw, Context * /* Note that if we fail on oom later on, we do not roll-back changes to the iovec * structure. (It remains valid, with the first iovec fields initialized.) */ - pid = context->pid; + pid = context->pidref.pid; /* The following is mandatory */ - r = pid_get_comm(pid, &t); + r = pidref_get_comm(&context->pidref, &t); if (r < 0) return log_error_errno(r, "Failed to get COMM: %m"); @@ -1349,7 +1379,7 @@ static int gather_pid_metadata_from_procfs(struct iovec_wrapper *iovw, Context * if (r < 0) log_warning_errno(r, "Failed to get EXE, ignoring: %m"); - if (cg_pid_get_unit(pid, &t) >= 0) + if (cg_pidref_get_unit(&context->pidref, &t) >= 0) (void) iovw_put_string_field_free(iovw, "COREDUMP_UNIT=", t); if (cg_pid_get_user_unit(pid, &t) >= 0) @@ -1367,7 +1397,7 @@ static int gather_pid_metadata_from_procfs(struct iovec_wrapper *iovw, Context * if (sd_pid_get_slice(pid, &t) >= 0) (void) iovw_put_string_field_free(iovw, "COREDUMP_SLICE=", t); - if (pid_get_cmdline(pid, SIZE_MAX, PROCESS_CMDLINE_QUOTE_POSIX, &t) >= 0) + if (pidref_get_cmdline(&context->pidref, SIZE_MAX, PROCESS_CMDLINE_QUOTE_POSIX, &t) >= 0) (void) iovw_put_string_field_free(iovw, "COREDUMP_CMDLINE=", t); if (cg_pid_get_path_shifted(pid, NULL, &t) >= 0) @@ -1401,7 +1431,7 @@ static int gather_pid_metadata_from_procfs(struct iovec_wrapper *iovw, Context * if (read_full_virtual_file(p, &t, &size) >= 0) { char *buf = malloc(strlen("COREDUMP_PROC_AUXV=") + size + 1); if (buf) { - /* Add a dummy terminator to make save_context() happy. */ + /* Add a dummy terminator to make context_parse_iovw() happy. */ *mempcpy_typesafe(stpcpy(buf, "COREDUMP_PROC_AUXV="), t, size) = '\0'; (void) iovw_consume(iovw, buf, size + strlen("COREDUMP_PROC_AUXV=")); } @@ -1429,10 +1459,10 @@ static int gather_pid_metadata_from_procfs(struct iovec_wrapper *iovw, Context * (void) iovw_put_string_field_free(iovw, "COREDUMP_ENVIRON=", t); /* we successfully acquired all metadata */ - return save_context(context, iovw); + return context_parse_iovw(context, iovw); } -static int send_ucred(int transport_fd, struct ucred *ucred) { +static int send_ucred(int transport_fd, const struct ucred *ucred) { CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred))) control = {}; struct msghdr mh = { .msg_control = &control, @@ -1441,6 +1471,7 @@ static int send_ucred(int transport_fd, struct ucred *ucred) { struct cmsghdr *cmsg; assert(transport_fd >= 0); + assert(ucred); cmsg = CMSG_FIRSTHDR(&mh); *cmsg = (struct cmsghdr) { @@ -1463,6 +1494,7 @@ static int receive_ucred(int transport_fd, struct ucred *ret_ucred) { struct ucred *ucred = NULL; ssize_t n; + assert(transport_fd >= 0); assert(ret_ucred); n = recvmsg_safe(transport_fd, &mh, 0); @@ -1519,19 +1551,21 @@ static int can_forward_coredump(pid_t pid) { static int forward_coredump_to_container(Context *context) { _cleanup_close_ int pidnsfd = -EBADF, mntnsfd = -EBADF, netnsfd = -EBADF, usernsfd = -EBADF, rootfd = -EBADF; _cleanup_close_pair_ int pair[2] = EBADF_PAIR; - pid_t pid, child; + pid_t leader_pid, child; struct ucred ucred = { - .pid = context->pid, + .pid = context->pidref.pid, .uid = context->uid, .gid = context->gid, }; int r; - r = namespace_get_leader(context->pid, NAMESPACE_PID, &pid); + assert(context); + + r = namespace_get_leader(context->pidref.pid, NAMESPACE_PID, &leader_pid); if (r < 0) return log_debug_errno(r, "Failed to get namespace leader: %m"); - r = can_forward_coredump(pid); + r = can_forward_coredump(leader_pid); if (r < 0) return log_debug_errno(r, "Failed to check if coredump can be forwarded: %m"); if (r == 0) @@ -1546,19 +1580,16 @@ static int forward_coredump_to_container(Context *context) { if (r < 0) return log_debug_errno(r, "Failed to set SO_PASSCRED: %m"); - r = namespace_open(pid, &pidnsfd, &mntnsfd, &netnsfd, &usernsfd, &rootfd); + r = namespace_open(leader_pid, &pidnsfd, &mntnsfd, &netnsfd, &usernsfd, &rootfd); if (r < 0) - return log_debug_errno(r, "Failed to join namespaces of PID " PID_FMT ": %m", pid); + return log_debug_errno(r, "Failed to join namespaces of PID " PID_FMT ": %m", leader_pid); r = namespace_fork("(sd-coredumpns)", "(sd-coredump)", NULL, 0, FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM, pidnsfd, mntnsfd, netnsfd, usernsfd, rootfd, &child); if (r < 0) - return log_debug_errno(r, "Failed to fork into namespaces of PID " PID_FMT ": %m", pid); + return log_debug_errno(r, "Failed to fork into namespaces of PID " PID_FMT ": %m", leader_pid); if (r == 0) { - _cleanup_(iovw_free_freep) struct iovec_wrapper *iovw = NULL; - Context child_context = {}; - pair[0] = safe_close(pair[0]); r = access_nofollow("/run/systemd/coredump", W_OK); @@ -1573,7 +1604,7 @@ static int forward_coredump_to_container(Context *context) { _exit(EXIT_FAILURE); } - iovw = iovw_new(); + _cleanup_(iovw_free_freep) struct iovec_wrapper *iovw = iovw_new(); if (!iovw) { log_oom(); _exit(EXIT_FAILURE); @@ -1584,16 +1615,15 @@ static int forward_coredump_to_container(Context *context) { (void) iovw_put_string_field(iovw, "COREDUMP_FORWARDED=", "1"); for (int i = 0; i < _META_ARGV_MAX; i++) { - int signo; char buf[DECIMAL_STR_MAX(pid_t)]; const char *t = context->meta[i]; + /* Patch some of the fields with the translated ucred data */ switch (i) { case META_ARGV_PID: xsprintf(buf, PID_FMT, ucred.pid); t = buf; - break; case META_ARGV_UID: @@ -1606,13 +1636,6 @@ static int forward_coredump_to_container(Context *context) { t = buf; break; - case META_ARGV_SIGNAL: - if (safe_atoi(t, &signo) >= 0 && SIGNAL_VALID(signo)) - (void) iovw_put_string_field(iovw, - "COREDUMP_SIGNAL_NAME=SIG", - signal_to_string(signo)); - break; - default: break; } @@ -1624,7 +1647,8 @@ static int forward_coredump_to_container(Context *context) { } } - r = save_context(&child_context, iovw); + _cleanup_(context_done) Context child_context = CONTEXT_NULL; + r = context_parse_iovw(&child_context, iovw); if (r < 0) { log_debug_errno(r, "Failed to save context: %m"); _exit(EXIT_FAILURE); @@ -1636,7 +1660,7 @@ static int forward_coredump_to_container(Context *context) { _exit(EXIT_FAILURE); } - r = send_iovec(iovw, STDIN_FILENO, -EBADF); + r = send_iovec(iovw, STDIN_FILENO, &context->pidref, /* mount_tree_fd= */ -EBADF); if (r < 0) { log_debug_errno(r, "Failed to send iovec to coredump socket: %m"); _exit(EXIT_FAILURE); @@ -1686,7 +1710,7 @@ static int gather_pid_mount_tree_fd(const Context *context, int *ret_fd) { if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, pair) < 0) return log_error_errno(errno, "Failed to create socket pair: %m"); - r = namespace_open(context->pid, + r = namespace_open(context->pidref.pid, /* ret_pidns_fd= */ NULL, &mntns_fd, /* ret_netns_fd= */ NULL, @@ -1739,9 +1763,8 @@ static int gather_pid_mount_tree_fd(const Context *context, int *ret_fd) { static int process_kernel(int argc, char* argv[]) { _cleanup_(iovw_free_freep) struct iovec_wrapper *iovw = NULL; - _cleanup_close_ int mount_tree_fd = -EBADF; - Context context = {}; - int r, signo; + _cleanup_(context_done) Context context = CONTEXT_NULL; + int r; /* When we're invoked by the kernel, stdout/stderr are closed which is dangerous because the fds * could get reallocated. To avoid hard to debug issues, let's instead bind stdout/stderr to @@ -1772,11 +1795,11 @@ static int process_kernel(int argc, char* argv[]) { /* Log minimal metadata now, so it is not lost if the system is about to shut down. */ log_info("Process %s (%s) of user %s terminated abnormally with signal %s/%s, processing...", - context.meta[META_ARGV_PID], context.meta[META_COMM], - context.meta[META_ARGV_UID], context.meta[META_ARGV_SIGNAL], - strna(safe_atoi(context.meta[META_ARGV_SIGNAL], &signo) >= 0 ? signal_to_string(signo) : NULL)); + context.meta[META_ARGV_PID], context.meta[META_COMM], + context.meta[META_ARGV_UID], context.meta[META_ARGV_SIGNAL], + signal_to_string(context.signo)); - r = in_same_namespace(getpid_cached(), context.pid, NAMESPACE_PID); + r = in_same_namespace(getpid_cached(), context.pidref.pid, NAMESPACE_PID); if (r < 0) log_debug_errno(r, "Failed to check pidns of crashing process, ignoring: %m"); if (r == 0) { @@ -1786,7 +1809,7 @@ static int process_kernel(int argc, char* argv[]) { if (r >= 0) return 0; - r = gather_pid_mount_tree_fd(&context, &mount_tree_fd); + r = gather_pid_mount_tree_fd(&context, &context.mount_tree_fd); if (r < 0) log_warning_errno(r, "Failed to access the mount tree of a container, ignoring: %m"); } @@ -1806,18 +1829,20 @@ static int process_kernel(int argc, char* argv[]) { (void) iovw_put_string_field(iovw, "PRIORITY=", STRINGIFY(LOG_CRIT)); if (context.is_journald || context.is_pid1) - return submit_coredump(&context, iovw, STDIN_FILENO, mount_tree_fd); + return submit_coredump(&context, iovw, STDIN_FILENO); - return send_iovec(iovw, STDIN_FILENO, mount_tree_fd); + return send_iovec(iovw, STDIN_FILENO, &context.pidref, context.mount_tree_fd); } static int process_backtrace(int argc, char *argv[]) { _cleanup_(journal_importer_cleanup) JournalImporter importer = JOURNAL_IMPORTER_INIT(STDIN_FILENO); _cleanup_(iovw_free_freep) struct iovec_wrapper *iovw = NULL; - Context context = {}; + _cleanup_(context_done) Context context = CONTEXT_NULL; char *message; int r; + assert(argc >= 2); + log_debug("Processing backtrace on stdin..."); iovw = iovw_new(); diff --git a/src/shared/journal-importer.c b/src/shared/journal-importer.c index 4dc0b8f662..9264638303 100644 --- a/src/shared/journal-importer.c +++ b/src/shared/journal-importer.c @@ -33,7 +33,7 @@ void journal_importer_cleanup(JournalImporter *imp) { free(imp->name); free(imp->buf); - iovw_free_contents(&imp->iovw, false); + iovw_done(&imp->iovw); } static char* realloc_buffer(JournalImporter *imp, size_t size) { @@ -452,7 +452,7 @@ void journal_importer_drop_iovw(JournalImporter *imp) { /* This function drops processed data that along with the iovw that points at it */ - iovw_free_contents(&imp->iovw, false); + iovw_done(&imp->iovw); /* possibly reset buffer position */ remain = imp->filled - imp->offset;