Merge pull request #24992 from yuwata/sd-device-monitor-receive-buffer
sd-device-monitor: dynamically allocate receive buffer
This commit is contained in:
@@ -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");
|
||||
|
||||
|
||||
Executable
+70
@@ -0,0 +1,70 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
set -ex
|
||||
set -o pipefail
|
||||
|
||||
# shellcheck source=test/units/assert.sh
|
||||
. "$(dirname "$0")"/assert.sh
|
||||
|
||||
# This is a test for issue #24987.
|
||||
|
||||
mkdir -p /run/udev/rules.d/
|
||||
cat >/run/udev/rules.d/50-testsuite.rules <<EOF
|
||||
SUBSYSTEM!="mem", GOTO="test-end"
|
||||
KERNEL!="null", GOTO="test-end"
|
||||
ACTION=="remove", GOTO="test-end"
|
||||
|
||||
# add 100 * 100byte of properties
|
||||
$(for ((i = 0; i < 100; i++)); do printf 'ENV{XXX%03i}="0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"\n' "$i"; done)
|
||||
|
||||
LABEL="test-end"
|
||||
EOF
|
||||
|
||||
udevadm control --reload
|
||||
|
||||
TMPDIR=$(mktemp -d -p /tmp udev-tests.XXXXXX)
|
||||
SYSTEMD_LOG_LEVEL=debug udevadm monitor --udev --property --subsystem-match=mem >"$TMPDIR"/monitor.txt 2>&1 &
|
||||
KILL_PID="$!"
|
||||
|
||||
FOUND=
|
||||
for _ in {1..40}; do
|
||||
if grep -F 'UDEV - the event which udev sends out after rule processing' "$TMPDIR"/monitor.txt; then
|
||||
FOUND=1
|
||||
break
|
||||
fi
|
||||
sleep .5
|
||||
done
|
||||
[[ -n "$FOUND" ]]
|
||||
|
||||
udevadm trigger --verbose --settle --action add /dev/null
|
||||
|
||||
FOUND=
|
||||
for _ in {1..40}; do
|
||||
if ! grep -e 'UDEV *\[[0-9.]*\] *add *\/devices\/virtual\/mem\/null (mem)' "$TMPDIR"/monitor.txt; then
|
||||
sleep .5
|
||||
continue
|
||||
fi
|
||||
|
||||
FOUND=1
|
||||
for ((i = 0; i < 100; i++)); do
|
||||
if ! grep -F "$(printf 'XXX%03i=0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789' "$i")" "$TMPDIR"/monitor.txt; then
|
||||
FOUND=
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [[ -n "$FOUND" ]]; then
|
||||
break;
|
||||
fi
|
||||
|
||||
sleep .5
|
||||
done
|
||||
[[ -n "$FOUND" ]]
|
||||
|
||||
# cleanup
|
||||
rm -f /run/udev/rules.d/50-testsuite.rules
|
||||
udevadm control --reload
|
||||
|
||||
kill "$KILL_PID"
|
||||
rm -rf "$TMPDIR"
|
||||
|
||||
exit 0
|
||||
Reference in New Issue
Block a user