Also, unify MSG_TRUNC handling all across the codebase.
ssize_t recvmsg_safe(int sockfd, struct msghdr *msg, int flags) {
ssize_t n;
- /* A wrapper around recvmsg() that checks for MSG_CTRUNC, and turns it into an error, in a reasonably
- * safe way, closing any SCM_RIGHTS fds in the error path.
+ /* A wrapper around recvmsg() that checks for MSG_CTRUNC and MSG_TRUNC, and turns them into an error,
+ * in a reasonably safe way, closing any received fds in the error path.
*
* Note that unlike our usual coding style this might modify *msg on failure. */
+ assert(sockfd >= 0);
+ assert(msg);
+
n = recvmsg(sockfd, msg, flags);
if (n < 0)
return -errno;
- if (FLAGS_SET(msg->msg_flags, MSG_CTRUNC)) {
+ if (FLAGS_SET(msg->msg_flags, MSG_CTRUNC) ||
+ (!FLAGS_SET(flags, MSG_PEEK) && FLAGS_SET(msg->msg_flags, MSG_TRUNC))) {
cmsg_close_all(msg);
- return -EXFULL; /* a recognizable error code */
+ return FLAGS_SET(msg->msg_flags, MSG_CTRUNC) ? -ECHRNG : -EXFULL;
}
return n;
/* glibc duplicates timespec/timeval on certain 32-bit arches, once in 32-bit and once in 64-bit.
* See __convert_scm_timestamps() in glibc source code. Hence, we need additional buffer space for them
- * to prevent from recvmsg_safe() returning -EXFULL. */
+ * to prevent truncating control msg (recvmsg() MSG_CTRUNC). */
#define CMSG_SPACE_TIMEVAL \
((sizeof(struct timeval) == sizeof(struct timeval_large)) ? \
CMSG_SPACE(sizeof(struct timeval)) : \
return 0;
}
- n = recvmsg_safe(m->notify_fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC|MSG_TRUNC);
+ n = recvmsg_safe(m->notify_fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
if (ERRNO_IS_NEG_TRANSIENT(n))
return 0; /* Spurious wakeup, try again */
- if (n == -EXFULL) {
+ if (n == -ECHRNG) {
log_warning_errno(n, "Got message with truncated control data (too many fds sent?), ignoring.");
return 0;
}
+ if (n == -EXFULL) {
+ log_warning_errno(n, "Got message with truncated payload data, ignoring.");
+ return 0;
+ }
if (n < 0)
/* If this is any other, real error, then stop processing this socket. This of course means
* we won't take notification messages anymore, but that's still better than busy looping:
return 0;
}
- if ((size_t) n >= sizeof(buf) || (msghdr.msg_flags & MSG_TRUNC)) {
- log_warning("Received notify message exceeded maximum size. Ignoring.");
- return 0;
- }
-
/* As extra safety check, let's make sure the string we get doesn't contain embedded NUL bytes.
* We permit one trailing NUL byte in the message, but don't expect it. */
if (n > 1 && memchr(buf, 0, n-1)) {
assert(source);
- n = recvmsg_safe(m->handoff_timestamp_fds[0], &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC|MSG_TRUNC);
+ n = recvmsg_safe(m->handoff_timestamp_fds[0], &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
if (ERRNO_IS_NEG_TRANSIENT(n))
return 0; /* Spurious wakeup, try again */
+ if (n == -ECHRNG) {
+ log_warning_errno(n, "Got message with truncated control data (unexpected fds sent?), ignoring.");
+ return 0;
+ }
if (n == -EXFULL) {
- log_warning("Got message with truncated control, ignoring.");
+ log_warning_errno(n, "Got message with truncated payload data, ignoring.");
return 0;
}
if (n < 0)
cmsg_close_all(&msghdr);
- if (msghdr.msg_flags & MSG_TRUNC) {
- log_warning("Got truncated handoff timestamp message, ignoring.");
- return 0;
- }
if (n != sizeof(ts)) {
log_warning("Got handoff timestamp message of unexpected size %zi (expected %zu), ignoring.", n, sizeof(ts));
return 0;
DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_unref);
static int manager_on_notify(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
-
+ Manager *m = ASSERT_PTR(userdata);
char buf[NOTIFY_BUFFER_MAX+1];
struct iovec iovec = {
.iov_base = buf,
.msg_control = &control,
.msg_controllen = sizeof(control),
};
- struct ucred *ucred;
- Manager *m = userdata;
- Transfer *t;
ssize_t n;
- char *p;
int r;
n = recvmsg_safe(fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
- if (n < 0) {
- if (ERRNO_IS_TRANSIENT(n))
- return 0;
- return (int) n;
+ if (ERRNO_IS_NEG_TRANSIENT(n))
+ return 0;
+ if (n == -ECHRNG) {
+ log_warning_errno(n, "Got message with truncated control data (unexpected fds sent?), ignoring.");
+ return 0;
}
-
- cmsg_close_all(&msghdr);
-
- if (msghdr.msg_flags & MSG_TRUNC) {
- log_warning("Got overly long notification datagram, ignoring.");
+ if (n == -EXFULL) {
+ log_warning_errno(n, "Got message with truncated payload data, ignoring.");
return 0;
}
+ if (n < 0)
+ return (int) n;
- ucred = CMSG_FIND_DATA(&msghdr, SOL_SOCKET, SCM_CREDENTIALS, struct ucred);
+ cmsg_close_all(&msghdr);
+
+ struct ucred *ucred = CMSG_FIND_DATA(&msghdr, SOL_SOCKET, SCM_CREDENTIALS, struct ucred);
if (!ucred || ucred->pid <= 0) {
log_warning("Got notification datagram lacking credential information, ignoring.");
return 0;
}
+ Transfer *t;
HASHMAP_FOREACH(t, m->transfers)
if (ucred->pid == t->pidref.pid)
break;
buf[n] = 0;
- p = find_line_startswith(buf, "X_IMPORT_PROGRESS=");
+ char *p = find_line_startswith(buf, "X_IMPORT_PROGRESS=");
if (!p)
return 0;
iovec = IOVEC_MAKE(s->buffer, MALLOC_ELEMENTSOF(s->buffer) - 1); /* Leave room for trailing NUL we add later */
n = recvmsg_safe(fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
- if (n < 0) {
- if (ERRNO_IS_TRANSIENT(n))
- return 0;
- if (n == -EXFULL) {
- log_ratelimit_warning(JOURNAL_LOG_RATELIMIT,
- "Got message with truncated control data (too many fds sent?), ignoring.");
- return 0;
- }
- return log_ratelimit_error_errno(n, JOURNAL_LOG_RATELIMIT, "recvmsg() failed: %m");
+ if (ERRNO_IS_NEG_TRANSIENT(n))
+ return 0;
+ if (n == -ECHRNG) {
+ log_ratelimit_warning_errno(n, JOURNAL_LOG_RATELIMIT,
+ "Got message with truncated control data (too many fds sent?), ignoring.");
+ return 0;
}
+ if (n == -EXFULL) {
+ log_ratelimit_warning_errno(n, JOURNAL_LOG_RATELIMIT, "Got message with truncated payload data, ignoring.");
+ return 0;
+ }
+ if (n < 0)
+ return log_ratelimit_error_errno(n, JOURNAL_LOG_RATELIMIT, "Failed to receive message: %m");
- CMSG_FOREACH(cmsg, &msghdr)
- if (cmsg->cmsg_level == SOL_SOCKET &&
- cmsg->cmsg_type == SCM_CREDENTIALS &&
+ CMSG_FOREACH(cmsg, &msghdr) {
+ if (cmsg->cmsg_level != SOL_SOCKET)
+ continue;
+
+ if (cmsg->cmsg_type == SCM_CREDENTIALS &&
cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) {
assert(!ucred);
ucred = CMSG_TYPED_DATA(cmsg, struct ucred);
- } else if (cmsg->cmsg_level == SOL_SOCKET &&
- cmsg->cmsg_type == SCM_SECURITY) {
+ } else if (cmsg->cmsg_type == SCM_SECURITY) {
assert(!label);
label = CMSG_TYPED_DATA(cmsg, char);
label_len = cmsg->cmsg_len - CMSG_LEN(0);
- } else if (cmsg->cmsg_level == SOL_SOCKET &&
- cmsg->cmsg_type == SCM_TIMESTAMP &&
+ } else if (cmsg->cmsg_type == SCM_TIMESTAMP &&
cmsg->cmsg_len == CMSG_LEN(sizeof(struct timeval))) {
assert(!tv);
tv = memcpy(&tv_buf, CMSG_DATA(cmsg), sizeof(struct timeval));
- } else if (cmsg->cmsg_level == SOL_SOCKET &&
- cmsg->cmsg_type == SCM_RIGHTS) {
+ } else if (cmsg->cmsg_type == SCM_RIGHTS) {
assert(!fds);
fds = CMSG_TYPED_DATA(cmsg, int);
n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
}
+ }
/* And a trailing NUL, just in case */
s->buffer[n] = 0;
iov = IOVEC_MAKE(message.buf, n);
- n = recvmsg(m->sock, &smsg, 0);
+ n = recvmsg_safe(m->sock, &smsg, 0);
if (n < 0) {
- if (!ERRNO_IS_TRANSIENT(errno))
- log_monitor_errno(m, errno, "Failed to receive message: %m");
- return -errno;
+ if (!ERRNO_IS_NEG_TRANSIENT(n))
+ log_monitor_errno(m, n, "Failed to receive message: %s",
+ n == -ECHRNG ? "got truncated control data" :
+ n == -EXFULL ? "got truncated payload data" :
+ STRERROR((int) n));
+ return n;
}
-
- if (smsg.msg_flags & MSG_TRUNC)
- return log_monitor_errno(m, SYNTHETIC_ERRNO(EINVAL), "Received truncated message, ignoring message.");
-
if (n < 32)
return log_monitor_errno(m, SYNTHETIC_ERRNO(EINVAL), "Invalid message length (%zi), ignoring message.", n);
assert(fd >= 0);
assert(peek || (buf && buf_size > 0));
- n = recvmsg_safe(fd, &msg, MSG_TRUNC | (peek ? MSG_PEEK : 0));
- if (n == -ENOBUFS)
- return log_debug_errno(n, "sd-netlink: kernel receive buffer overrun");
- else if (ERRNO_IS_NEG_TRANSIENT(n)) {
+ n = recvmsg_safe(fd, &msg, peek ? (MSG_PEEK|MSG_TRUNC) : 0);
+ if (ERRNO_IS_NEG_TRANSIENT(n)) {
if (ret_mcast_group)
*ret_mcast_group = 0;
return 0;
- } else if (n < 0)
+ }
+ if (n == -ENOBUFS)
+ return log_debug_errno(n, "sd-netlink: kernel receive buffer overrun");
+ if (n == -ECHRNG)
+ return log_debug_errno(n, "sd-netlink: got truncated control message");
+ if (n == -EXFULL)
+ return log_debug_errno(n, "sd-netlink: got truncated payload message");
+ if (n < 0)
return (int) n;
if (sender.nl.nl_pid != 0) {
return 0;
}
- if (!peek && (size_t) n > buf_size) /* message did not fit in read buffer */
- return -EIO;
-
if (ret_mcast_group) {
struct nl_pktinfo *pi;
iov[0] = IOVEC_MAKE(&family, sizeof(family));
iov[1] = IOVEC_MAKE(&in_addr, sizeof(in_addr));
- n = recvmsg(pair[0], &mh, 0);
+ n = recvmsg_safe(pair[0], &mh, 0);
if (n < 0)
- return -errno;
+ return n;
if ((size_t) n < sizeof(family))
break;
n = recvmsg_safe(fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
if (ERRNO_IS_NEG_TRANSIENT(n))
return 0;
- else if (n == -EXFULL) {
- log_warning("Got message with truncated control data (too many fds sent?), ignoring.");
+ if (n == -ECHRNG) {
+ log_warning_errno(n, "Got message with truncated control data (too many fds sent?), ignoring.");
return 0;
- } else if (n < 0)
+ }
+ if (n == -EXFULL) {
+ log_warning_errno(n, "Got message with truncated payload data, ignoring.");
+ return 0;
+ }
+ if (n < 0)
return log_warning_errno(n, "Couldn't read notification socket: %m");
cmsg_close_all(&msghdr);
return 0;
}
- if ((size_t) n >= sizeof(buf)) {
- log_warning("Received notify message exceeded maximum size. Ignoring.");
- return 0;
- }
-
buf[n] = 0;
tags = strv_split(buf, "\n\r");
if (!tags)
if (l <= 0)
return l;
- assert(!(mh.msg_flags & MSG_TRUNC));
-
p->size = (size_t) l;
p->family = sa.sa.sa_family;
if (l <= 0)
return l;
- assert(!(mh.msg_flags & MSG_TRUNC));
-
p->size = (size_t) l;
p->family = sa.sa.sa_family;
n = recvmsg_safe(socket_fd, &msghdr, 0);
if (ERRNO_IS_NEG_TRANSIENT(n))
continue;
- else if (n == -EXFULL) {
- log_debug("Got message with truncated control data, ignoring.");
+ if (n == -ECHRNG) {
+ log_debug_errno(n, "Got message with truncated control data (unexpected fds sent?), ignoring.");
continue;
- } else if (n < 0) {
+ }
+ if (n == -EXFULL) {
+ log_debug_errno(n, "Got message with truncated payload data, ignoring.");
+ continue;
+ }
+ if (n < 0) {
r = (int) n;
goto finish;
}
int r;
n = recvmsg_safe(fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
- if (n < 0) {
- if (ERRNO_IS_TRANSIENT(n))
- return 0;
- return (int) n;
+ if (ERRNO_IS_NEG_TRANSIENT(n))
+ return 0;
+ if (n == -ECHRNG) {
+ log_warning_errno(n, "Got message with truncated control data (unexpected fds sent?), ignoring.");
+ return 0;
}
-
- cmsg_close_all(&msghdr);
-
- if (msghdr.msg_flags & MSG_TRUNC) {
- log_warning("Got overly long notification datagram, ignoring.");
+ if (n == -EXFULL) {
+ log_warning_errno(n, "Got message with truncated payload data, ignoring.");
return 0;
}
+ if (n < 0)
+ return (int) n;
+
+ cmsg_close_all(&msghdr);
ucred = CMSG_FIND_DATA(&msghdr, SOL_SOCKET, SCM_CREDENTIALS, struct ucred);
if (!ucred || ucred->pid <= 0) {
if (IN_SET(j->type, JOB_UPDATE, JOB_VACUUM) && j->target->busy)
return log_notice_errno(SYNTHETIC_ERRNO(EBUSY), "Target %s busy, ignoring job.", j->target->name);
-
+
stdout_fd = memfd_new("sysupdate-stdout");
if (stdout_fd < 0)
return log_error_errno(stdout_fd, "Failed to create memfd: %m");
char *version, *progress, *errno_str, *ready;
n = recvmsg_safe(fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
- if (n < 0) {
- if (ERRNO_IS_TRANSIENT(n))
- return 0;
- return (int) n;
+ if (ERRNO_IS_NEG_TRANSIENT(n))
+ return 0;
+ if (n == -ECHRNG) {
+ log_warning_errno(n, "Got message with truncated control data (unexpected fds sent?), ignoring.");
+ return 0;
}
-
- cmsg_close_all(&msghdr);
-
- if (msghdr.msg_flags & MSG_TRUNC) {
- log_warning("Got overly long notification datagram, ignoring.");
+ if (n == -EXFULL) {
+ log_warning_errno(n, "Got message with truncated payload data, ignoring.");
return 0;
}
+ if (n < 0)
+ return (int) n;
+
+ cmsg_close_all(&msghdr);
ucred = CMSG_FIND_DATA(&msghdr, SOL_SOCKET, SCM_CREDENTIALS, struct ucred);
if (!ucred || ucred->pid <= 0) {
}
len = recvmsg_safe(fd, &msghdr, MSG_DONTWAIT);
- if (len == -EAGAIN)
+ if (ERRNO_IS_NEG_TRANSIENT(len))
return 0;
if (len < 0) {
- log_warning_errno(len, "Error receiving message, disconnecting: %m");
+ log_warning_errno(len, "Error receiving message, disconnecting: %s",
+ len == -ECHRNG ? "got truncated control data" :
+ len == -EXFULL ? "got truncated payload data" :
+ STRERROR((int) len));
return manager_connect(m);
}
- /* Too short or too long packet? */
- if (iov.iov_len < sizeof(struct ntp_msg) || (msghdr.msg_flags & MSG_TRUNC)) {
+ /* Too short packet? */
+ if (iov.iov_len < sizeof(struct ntp_msg)) {
log_warning("Invalid response from server. Disconnecting.");
return manager_connect(m);
}
uctrl = udev_ctrl_ref(userdata);
size = recvmsg_safe(fd, &smsg, 0);
- if (size == -EINTR)
+ if (ERRNO_IS_NEG_TRANSIENT(size))
return 0;
+ if (size == -ECHRNG) {
+ log_warning_errno(size, "Got message with truncated control data (unexpected fds sent?), ignoring.");
+ return 0;
+ }
+ if (size == -EXFULL) {
+ log_warning_errno(size, "Got message with truncated payload data, ignoring.");
+ return 0;
+ }
if (size < 0)
return log_error_errno(size, "Failed to receive ctrl message: %m");
cmsg_close_all(&smsg);
- if (size != sizeof(msg_wire) || FLAGS_SET(smsg->flags, MSG_TRUNC)) {
+ if (size != sizeof(msg_wire)) {
log_warning("Received message with invalid length, ignoring");
return 0;
}