mirror of
https://github.com/systemd/systemd-stable.git
synced 2024-12-22 13:33:56 +03:00
tree-wide: use recvmsg_safe() at various places
Let's be extra careful whenever we return from recvmsg() and see MSG_CTRUNC set. This generally means we ran into a programming error, as we didn't size the control buffer large enough. It's an error condition we should at least log about, or propagate up. Hence do that. This is particularly important when receiving fds, since for those the control data can be of any size. In particular on stream sockets that's nasty, because if we miss an fd because of control data truncation we cannot recover, we might not even realize that we are one off. (Also, when failing early, if there's any chance the socket might be AF_UNIX let's close all received fds, all the time. We got this right most of the time, but there were a few cases missing. God, UNIX is hard to use)
This commit is contained in:
parent
47eae6ce0c
commit
3691bcf3c5
@ -901,9 +901,9 @@ ssize_t receive_one_fd_iov(
|
||||
* combination with send_one_fd().
|
||||
*/
|
||||
|
||||
k = recvmsg(transport_fd, &mh, MSG_CMSG_CLOEXEC | flags);
|
||||
k = recvmsg_safe(transport_fd, &mh, MSG_CMSG_CLOEXEC | flags);
|
||||
if (k < 0)
|
||||
return (ssize_t) -errno;
|
||||
return k;
|
||||
|
||||
CMSG_FOREACH(cmsg, &mh) {
|
||||
if (cmsg->cmsg_level == SOL_SOCKET &&
|
||||
@ -915,12 +915,13 @@ ssize_t receive_one_fd_iov(
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
if (!found) {
|
||||
cmsg_close_all(&mh);
|
||||
|
||||
/* If didn't receive an FD or any data, return an error. */
|
||||
if (k == 0 && !found)
|
||||
return -EIO;
|
||||
/* If didn't receive an FD or any data, return an error. */
|
||||
if (k == 0)
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (found)
|
||||
*ret_fd = *(int*) CMSG_DATA(found);
|
||||
|
@ -2360,20 +2360,20 @@ static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t
|
||||
return 0;
|
||||
}
|
||||
|
||||
n = recvmsg(m->notify_fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC|MSG_TRUNC);
|
||||
if (n < 0) {
|
||||
if (IN_SET(errno, EAGAIN, EINTR))
|
||||
return 0; /* Spurious wakeup, try again */
|
||||
|
||||
/* If this is any other, real error, then let's stop processing this socket. This of course means we
|
||||
* won't take notification messages anymore, but that's still better than busy looping around this:
|
||||
* being woken up over and over again but being unable to actually read the message off the socket. */
|
||||
return log_error_errno(errno, "Failed to receive notification message: %m");
|
||||
}
|
||||
n = recvmsg_safe(m->notify_fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC|MSG_TRUNC);
|
||||
if (IN_SET(n, -EAGAIN, -EINTR))
|
||||
return 0; /* Spurious wakeup, try again */
|
||||
if (n < 0)
|
||||
/* If this is any other, real error, then let's stop processing this socket. This of course
|
||||
* means we won't take notification messages anymore, but that's still better than busy
|
||||
* looping around this: being woken up over and over again but being unable to actually read
|
||||
* the message off the socket. */
|
||||
return log_error_errno(n, "Failed to receive notification message: %m");
|
||||
|
||||
CMSG_FOREACH(cmsg, &msghdr) {
|
||||
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
|
||||
|
||||
assert(!fd_array);
|
||||
fd_array = (int*) CMSG_DATA(cmsg);
|
||||
n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
|
||||
|
||||
@ -2381,6 +2381,7 @@ static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t
|
||||
cmsg->cmsg_type == SCM_CREDENTIALS &&
|
||||
cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) {
|
||||
|
||||
assert(!ucred);
|
||||
ucred = (struct ucred*) CMSG_DATA(cmsg);
|
||||
}
|
||||
}
|
||||
|
@ -911,10 +911,10 @@ static int process_socket(int fd) {
|
||||
|
||||
mh.msg_iov = &iovec;
|
||||
|
||||
n = recvmsg(fd, &mh, MSG_CMSG_CLOEXEC);
|
||||
n = recvmsg_safe(fd, &mh, MSG_CMSG_CLOEXEC);
|
||||
if (n < 0) {
|
||||
free(iovec.iov_base);
|
||||
r = log_error_errno(errno, "Failed to receive datagram: %m");
|
||||
r = log_error_errno(n, "Failed to receive datagram: %m");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
@ -935,15 +935,17 @@ static int process_socket(int fd) {
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
log_error("Coredump file descriptor missing.");
|
||||
r = -EBADMSG;
|
||||
cmsg_close_all(&mh);
|
||||
r = log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
|
||||
"Coredump file descriptor missing.");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
assert(input_fd < 0);
|
||||
input_fd = *(int*) CMSG_DATA(found);
|
||||
break;
|
||||
}
|
||||
} else
|
||||
cmsg_close_all(&mh);
|
||||
|
||||
/* Add trailing NUL byte, in case these are strings */
|
||||
((char*) iovec.iov_base)[n] = 0;
|
||||
@ -952,8 +954,6 @@ static int process_socket(int fd) {
|
||||
r = iovw_put(&iovw, iovec.iov_base, iovec.iov_len);
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
|
||||
cmsg_close_all(&mh);
|
||||
}
|
||||
|
||||
/* Make sure we got all data we really need */
|
||||
|
@ -981,9 +981,9 @@ static ssize_t read_datagram(int fd, struct ucred *ret_sender, void **ret) {
|
||||
.msg_controllen = sizeof(control),
|
||||
};
|
||||
|
||||
m = recvmsg(fd, &mh, MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
|
||||
m = recvmsg_safe(fd, &mh, MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
|
||||
if (m < 0)
|
||||
return -errno;
|
||||
return m;
|
||||
|
||||
cmsg_close_all(&mh);
|
||||
|
||||
|
@ -566,13 +566,11 @@ static int manager_on_notify(sd_event_source *s, int fd, uint32_t revents, void
|
||||
ssize_t n;
|
||||
int r;
|
||||
|
||||
n = recvmsg(fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
|
||||
if (n < 0) {
|
||||
if (IN_SET(errno, EAGAIN, EINTR))
|
||||
return 0;
|
||||
|
||||
return -errno;
|
||||
}
|
||||
n = recvmsg_safe(fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
|
||||
if (IN_SET(n, -EAGAIN, -EINTR))
|
||||
return 0;
|
||||
if (n < 0)
|
||||
return (int) n;
|
||||
|
||||
cmsg_close_all(&msghdr);
|
||||
|
||||
|
@ -1317,29 +1317,35 @@ int server_process_datagram(
|
||||
|
||||
iovec = IOVEC_MAKE(s->buffer, s->buffer_size - 1); /* Leave room for trailing NUL we add later */
|
||||
|
||||
n = recvmsg(fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
|
||||
if (n < 0) {
|
||||
if (IN_SET(errno, EINTR, EAGAIN))
|
||||
return 0;
|
||||
|
||||
return log_error_errno(errno, "recvmsg() failed: %m");
|
||||
n = recvmsg_safe(fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
|
||||
if (IN_SET(n, -EINTR, -EAGAIN))
|
||||
return 0;
|
||||
if (n == -EXFULL) {
|
||||
log_warning("Got message with truncated control data (too many fds sent?), ignoring.");
|
||||
return 0;
|
||||
}
|
||||
if (n < 0)
|
||||
return log_error_errno(n, "recvmsg() failed: %m");
|
||||
|
||||
CMSG_FOREACH(cmsg, &msghdr)
|
||||
if (cmsg->cmsg_level == SOL_SOCKET &&
|
||||
cmsg->cmsg_type == SCM_CREDENTIALS &&
|
||||
cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred)))
|
||||
cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) {
|
||||
assert(!ucred);
|
||||
ucred = (struct ucred*) CMSG_DATA(cmsg);
|
||||
else if (cmsg->cmsg_level == SOL_SOCKET &&
|
||||
} else if (cmsg->cmsg_level == SOL_SOCKET &&
|
||||
cmsg->cmsg_type == SCM_SECURITY) {
|
||||
assert(!label);
|
||||
label = (char*) CMSG_DATA(cmsg);
|
||||
label_len = cmsg->cmsg_len - CMSG_LEN(0);
|
||||
} else if (cmsg->cmsg_level == SOL_SOCKET &&
|
||||
cmsg->cmsg_type == SO_TIMESTAMP &&
|
||||
cmsg->cmsg_len == CMSG_LEN(sizeof(struct timeval)))
|
||||
cmsg->cmsg_len == CMSG_LEN(sizeof(struct timeval))) {
|
||||
assert(!tv);
|
||||
tv = (struct timeval*) CMSG_DATA(cmsg);
|
||||
else if (cmsg->cmsg_level == SOL_SOCKET &&
|
||||
} else if (cmsg->cmsg_level == SOL_SOCKET &&
|
||||
cmsg->cmsg_type == SCM_RIGHTS) {
|
||||
assert(!fds);
|
||||
fds = (int*) CMSG_DATA(cmsg);
|
||||
n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
|
||||
}
|
||||
|
@ -545,6 +545,7 @@ static int stdout_stream_process(sd_event_source *es, int fd, uint32_t revents,
|
||||
if (cmsg->cmsg_level == SOL_SOCKET &&
|
||||
cmsg->cmsg_type == SCM_CREDENTIALS &&
|
||||
cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) {
|
||||
assert(!ucred);
|
||||
ucred = (struct ucred *)CMSG_DATA(cmsg);
|
||||
break;
|
||||
}
|
||||
|
@ -557,17 +557,24 @@ static int bus_socket_read_auth(sd_bus *b) {
|
||||
mh.msg_control = &control;
|
||||
mh.msg_controllen = sizeof(control);
|
||||
|
||||
k = recvmsg(b->input_fd, &mh, MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
|
||||
if (k < 0 && errno == ENOTSOCK) {
|
||||
k = recvmsg_safe(b->input_fd, &mh, MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
|
||||
if (k == -ENOTSOCK) {
|
||||
b->prefer_readv = true;
|
||||
k = readv(b->input_fd, &iov, 1);
|
||||
if (k < 0)
|
||||
k = -errno;
|
||||
} else
|
||||
handle_cmsg = true;
|
||||
}
|
||||
if (k == -EAGAIN)
|
||||
return 0;
|
||||
if (k < 0)
|
||||
return errno == EAGAIN ? 0 : -errno;
|
||||
if (k == 0)
|
||||
return (int) k;
|
||||
if (k == 0) {
|
||||
if (handle_cmsg)
|
||||
cmsg_close_all(&mh); /* paranoia, we shouldn't have gotten any fds on EOF */
|
||||
return -ECONNRESET;
|
||||
}
|
||||
|
||||
b->rbuffer_size += k;
|
||||
|
||||
@ -1193,17 +1200,24 @@ int bus_socket_read_message(sd_bus *bus) {
|
||||
mh.msg_control = &control;
|
||||
mh.msg_controllen = sizeof(control);
|
||||
|
||||
k = recvmsg(bus->input_fd, &mh, MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
|
||||
if (k < 0 && errno == ENOTSOCK) {
|
||||
k = recvmsg_safe(bus->input_fd, &mh, MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
|
||||
if (k == -ENOTSOCK) {
|
||||
bus->prefer_readv = true;
|
||||
k = readv(bus->input_fd, &iov, 1);
|
||||
if (k < 0)
|
||||
k = -errno;
|
||||
} else
|
||||
handle_cmsg = true;
|
||||
}
|
||||
if (k == -EAGAIN)
|
||||
return 0;
|
||||
if (k < 0)
|
||||
return errno == EAGAIN ? 0 : -errno;
|
||||
if (k == 0)
|
||||
return (int) k;
|
||||
if (k == 0) {
|
||||
if (handle_cmsg)
|
||||
cmsg_close_all(&mh); /* On EOF we shouldn't have gotten an fd, but let's make sure */
|
||||
return -ECONNRESET;
|
||||
}
|
||||
|
||||
bus->rbuffer_size += k;
|
||||
|
||||
|
@ -3716,13 +3716,12 @@ static int nspawn_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t r
|
||||
return 0;
|
||||
}
|
||||
|
||||
n = recvmsg(fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
|
||||
if (n < 0) {
|
||||
if (IN_SET(errno, EAGAIN, EINTR))
|
||||
return 0;
|
||||
n = recvmsg_safe(fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
|
||||
if (IN_SET(n, -EAGAIN, -EINTR))
|
||||
return 0;
|
||||
if (n < 0)
|
||||
return log_warning_errno(n, "Couldn't read notification socket: %m");
|
||||
|
||||
return log_warning_errno(errno, "Couldn't read notification socket: %m");
|
||||
}
|
||||
cmsg_close_all(&msghdr);
|
||||
|
||||
CMSG_FOREACH(cmsg, &msghdr) {
|
||||
|
@ -190,9 +190,9 @@ static int recv_item(
|
||||
assert(ret_name);
|
||||
assert(ret_fd);
|
||||
|
||||
n = recvmsg(socket_fd, &mh, MSG_CMSG_CLOEXEC);
|
||||
n = recvmsg_safe(socket_fd, &mh, MSG_CMSG_CLOEXEC);
|
||||
if (n < 0)
|
||||
return -errno;
|
||||
return (int) n;
|
||||
|
||||
CMSG_FOREACH(cmsg, &mh) {
|
||||
if (cmsg->cmsg_level == SOL_SOCKET &&
|
||||
|
@ -775,17 +775,14 @@ int manager_recv(Manager *m, int fd, DnsProtocol protocol, DnsPacket **ret) {
|
||||
|
||||
iov = IOVEC_MAKE(DNS_PACKET_DATA(p), p->allocated);
|
||||
|
||||
l = recvmsg(fd, &mh, 0);
|
||||
if (l < 0) {
|
||||
if (IN_SET(errno, EAGAIN, EINTR))
|
||||
return 0;
|
||||
|
||||
return -errno;
|
||||
}
|
||||
l = recvmsg_safe(fd, &mh, 0);
|
||||
if (IN_SET(l, -EAGAIN, -EINTR))
|
||||
return 0;
|
||||
if (l < 0)
|
||||
return l;
|
||||
if (l == 0)
|
||||
return 0;
|
||||
|
||||
assert(!(mh.msg_flags & MSG_CTRUNC));
|
||||
assert(!(mh.msg_flags & MSG_TRUNC));
|
||||
|
||||
p->size = (size_t) l;
|
||||
|
@ -925,12 +925,11 @@ int ask_password_agent(
|
||||
msghdr.msg_control = &control;
|
||||
msghdr.msg_controllen = sizeof(control);
|
||||
|
||||
n = recvmsg(socket_fd, &msghdr, 0);
|
||||
n = recvmsg_safe(socket_fd, &msghdr, 0);
|
||||
if (IN_SET(n, -EAGAIN, -EINTR))
|
||||
continue;
|
||||
if (n < 0) {
|
||||
if (IN_SET(errno, EAGAIN, EINTR))
|
||||
continue;
|
||||
|
||||
r = -errno;
|
||||
r = (int) n;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
|
@ -438,12 +438,11 @@ static int manager_receive_response(sd_event_source *source, int fd, uint32_t re
|
||||
return manager_connect(m);
|
||||
}
|
||||
|
||||
len = recvmsg(fd, &msghdr, MSG_DONTWAIT);
|
||||
len = recvmsg_safe(fd, &msghdr, MSG_DONTWAIT);
|
||||
if (len == -EAGAIN)
|
||||
return 0;
|
||||
if (len < 0) {
|
||||
if (errno == EAGAIN)
|
||||
return 0;
|
||||
|
||||
log_warning("Error receiving message. Disconnecting.");
|
||||
log_warning_errno(len, "Error receiving message, disconnecting: %m");
|
||||
return manager_connect(m);
|
||||
}
|
||||
|
||||
|
@ -212,13 +212,11 @@ static int udev_ctrl_connection_event_handler(sd_event_source *s, int fd, uint32
|
||||
if (size == 0)
|
||||
return 0; /* Client disconnects? */
|
||||
|
||||
size = recvmsg(fd, &smsg, 0);
|
||||
if (size < 0) {
|
||||
if (errno != EINTR)
|
||||
return log_error_errno(errno, "Failed to receive ctrl message: %m");
|
||||
|
||||
size = recvmsg_safe(fd, &smsg, 0);
|
||||
if (size == -EINTR)
|
||||
return 0;
|
||||
}
|
||||
if (size < 0)
|
||||
return log_error_errno(size, "Failed to receive ctrl message: %m");
|
||||
|
||||
cmsg_close_all(&smsg);
|
||||
|
||||
|
@ -921,16 +921,18 @@ static int on_worker(sd_event_source *s, int fd, uint32_t revents, void *userdat
|
||||
struct ucred *ucred = NULL;
|
||||
struct worker *worker;
|
||||
|
||||
size = recvmsg(fd, &msghdr, MSG_DONTWAIT);
|
||||
if (size < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
else if (errno == EAGAIN)
|
||||
/* nothing more to read */
|
||||
break;
|
||||
size = recvmsg_safe(fd, &msghdr, MSG_DONTWAIT);
|
||||
if (size == -EINTR)
|
||||
continue;
|
||||
if (size == -EAGAIN)
|
||||
/* nothing more to read */
|
||||
break;
|
||||
if (size < 0)
|
||||
return log_error_errno(size, "Failed to receive message: %m");
|
||||
|
||||
return log_error_errno(errno, "Failed to receive message: %m");
|
||||
} else if (size != sizeof(struct worker_message)) {
|
||||
cmsg_close_all(&msghdr);
|
||||
|
||||
if (size != sizeof(struct worker_message)) {
|
||||
log_warning("Ignoring worker message with invalid size %zi bytes", size);
|
||||
continue;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user