From: Yu Watanabe Date: Thu, 13 Oct 2022 16:18:47 +0000 (+0900) Subject: sd-device-monitor: dynamically allocate receive buffer X-Git-Tag: v252-rc2~9^2~1 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=efbd4b3ca84c0426b6ff98d6352f82f3b7c090b2;p=thirdparty%2Fsystemd.git sd-device-monitor: dynamically allocate receive buffer If udevd broadcasts a processed device with huge amount of properties, then clients cannot receive the device. Fixes #24987. --- diff --git a/src/libsystemd/sd-device/device-monitor.c b/src/libsystemd/sd-device/device-monitor.c index 48f425396ac..5c8e043fc6f 100644 --- a/src/libsystemd/sd-device/device-monitor.c +++ b/src/libsystemd/sd-device/device-monitor.c @@ -482,14 +482,13 @@ static bool check_sender_uid(sd_device_monitor *m, uid_t uid) { int device_monitor_receive_device(sd_device_monitor *m, sd_device **ret) { _cleanup_(sd_device_unrefp) sd_device *device = NULL; + _cleanup_free_ uint8_t *buf_alloc = NULL; union { - monitor_netlink_header nlh; - char raw[8192]; - } buf; - struct iovec iov = { - .iov_base = &buf, - .iov_len = sizeof(buf) - }; + monitor_netlink_header *nlh; + char *nulstr; + uint8_t *buf; + } message; + struct iovec iov; CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred))) control; union sockaddr_union snl; struct msghdr smsg = { @@ -502,22 +501,45 @@ int device_monitor_receive_device(sd_device_monitor *m, sd_device **ret) { }; struct cmsghdr *cmsg; struct ucred *cred; - ssize_t buflen, bufpos; + size_t offset; + ssize_t n; bool is_initialized = false; int r; assert(m); assert(ret); - buflen = recvmsg(m->sock, &smsg, 0); - if (buflen < 0) { + n = next_datagram_size_fd(m->sock); + if (n < 0) { + if (!ERRNO_IS_TRANSIENT(n)) + log_monitor_errno(m, n, "Failed to get the received message size: %m"); + return n; + } + + if ((size_t) n < ALLOCA_MAX / sizeof(uint8_t) / 2) + message.buf = newa(uint8_t, n); + else { + buf_alloc = new(uint8_t, n); + if (!buf_alloc) + return log_oom_debug(); + + message.buf = buf_alloc; + } + + iov = IOVEC_MAKE(message.buf, n); + + n = recvmsg(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 (buflen < 32 || (smsg.msg_flags & MSG_TRUNC)) - return log_monitor_errno(m, SYNTHETIC_ERRNO(EINVAL), "Invalid message length."); + 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); if (snl.nl.nl_groups == MONITOR_GROUP_NONE) { /* unicast message, check if we trust the sender */ @@ -543,37 +565,37 @@ int device_monitor_receive_device(sd_device_monitor *m, sd_device **ret) { return log_monitor_errno(m, SYNTHETIC_ERRNO(EAGAIN), "Sender uid="UID_FMT", message ignored.", cred->uid); - if (streq(buf.raw, "libudev")) { + if (!memchr(message.buf, 0, n)) + return log_monitor_errno(m, SYNTHETIC_ERRNO(EAGAIN), "Received message without NUL, ignoring message."); + + if (streq(message.nulstr, "libudev")) { /* udev message needs proper version magic */ - if (buf.nlh.magic != htobe32(UDEV_MONITOR_MAGIC)) + if (message.nlh->magic != htobe32(UDEV_MONITOR_MAGIC)) return log_monitor_errno(m, SYNTHETIC_ERRNO(EAGAIN), "Invalid message signature (%x != %x).", - buf.nlh.magic, htobe32(UDEV_MONITOR_MAGIC)); + message.nlh->magic, htobe32(UDEV_MONITOR_MAGIC)); - if (buf.nlh.properties_off+32 > (size_t) buflen) + if (message.nlh->properties_off + 32 > (size_t) n) return log_monitor_errno(m, SYNTHETIC_ERRNO(EAGAIN), - "Invalid message length (%u > %zd).", - buf.nlh.properties_off+32, buflen); + "Invalid offset for properties (%u > %zi).", + message.nlh->properties_off + 32, n); - bufpos = buf.nlh.properties_off; + offset = message.nlh->properties_off; /* devices received from udev are always initialized */ is_initialized = true; } else { - /* kernel message with header */ - bufpos = strlen(buf.raw) + 1; - if ((size_t) bufpos < sizeof("a@/d") || bufpos >= buflen) - return log_monitor_errno(m, SYNTHETIC_ERRNO(EAGAIN), - "Invalid message length."); + /* check kernel message header */ + if (!strstr(message.nulstr, "@/")) + return log_monitor_errno(m, SYNTHETIC_ERRNO(EAGAIN), "Invalid message header."); - /* check message header */ - if (!strstr(buf.raw, "@/")) - return log_monitor_errno(m, SYNTHETIC_ERRNO(EAGAIN), - "Invalid message header."); + offset = strlen(message.nulstr) + 1; + if (offset >= (size_t) n) + return log_monitor_errno(m, SYNTHETIC_ERRNO(EAGAIN), "Invalid message length."); } - r = device_new_from_nulstr(&device, &buf.raw[bufpos], buflen - bufpos); + r = device_new_from_nulstr(&device, message.nulstr + offset, n - offset); if (r < 0) return log_monitor_errno(m, r, "Failed to create device from received message: %m");