| From 79dec6f5cc0b72d43dfb0469fa68b5cd023fbaf9 Mon Sep 17 00:00:00 2001 |
| From: Lennart Poettering <lennart@poettering.net> |
| Date: Thu, 13 Apr 2023 10:21:31 +0200 |
| Subject: [PATCH 1/3] socket-util: tighten aignment check for CMSG_TYPED_DATA() |
| |
| Apparently CMSG_DATA() alignment is very much undefined. Which is quite |
| an ABI fuck-up, but we need to deal with this. CMSG_TYPED_DATA() already |
| checks alignment of the specified pointer. Let's also check matching |
| alignment of the underlying structures, which we already can do at |
| compile-time. |
| |
| See: #27241 |
| |
| (This does not fix #27241, but should catch such errors already at |
| compile-time instead of runtime) |
| |
| Upstream-Status: Backport [https://github.com/systemd/systemd/pull/27254] |
| Signed-off-by: Khem Raj <raj.khem@gmail.com> |
| --- |
| src/basic/socket-util.h | 7 +++++++ |
| 1 file changed, 7 insertions(+) |
| |
| --- a/src/basic/socket-util.h |
| +++ b/src/basic/socket-util.h |
| @@ -175,9 +175,16 @@ int flush_accept(int fd); |
| #define CMSG_FOREACH(cmsg, mh) \ |
| for ((cmsg) = CMSG_FIRSTHDR(mh); (cmsg); (cmsg) = CMSG_NXTHDR((mh), (cmsg))) |
| |
| +/* Returns the cmsghdr's data pointer, but safely cast to the specified type. Does two alignment checks: one |
| + * at compile time, that the requested type has a smaller or same alignment as 'struct cmsghdr', and one |
| + * during runtime, that the actual pointer matches the alignment too. This is supposed to catch cases such as |
| + * 'struct timeval' is embedded into 'struct cmsghdr' on architectures where the alignment of the former is 8 |
| + * bytes (because of a 64bit time_t), but of the latter is 4 bytes (because size_t is 32bit), such as |
| + * riscv32. */ |
| #define CMSG_TYPED_DATA(cmsg, type) \ |
| ({ \ |
| struct cmsghdr *_cmsg = cmsg; \ |
| + assert_cc(__alignof__(type) <= __alignof__(struct cmsghdr)); \ |
| _cmsg ? CAST_ALIGN_PTR(type, CMSG_DATA(_cmsg)) : (type*) NULL; \ |
| }) |
| |
| --- a/src/basic/socket-util.c |
| +++ b/src/basic/socket-util.c |
| @@ -1047,7 +1047,7 @@ ssize_t receive_one_fd_iov( |
| } |
| |
| if (found) |
| - *ret_fd = *(int*) CMSG_DATA(found); |
| + *ret_fd = *CMSG_TYPED_DATA(found, int); |
| else |
| *ret_fd = -EBADF; |
| |
| --- a/src/core/manager.c |
| +++ b/src/core/manager.c |
| @@ -2503,7 +2503,7 @@ static int manager_dispatch_notify_fd(sd |
| if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { |
| |
| assert(!fd_array); |
| - fd_array = (int*) CMSG_DATA(cmsg); |
| + fd_array = CMSG_TYPED_DATA(cmsg, int); |
| n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int); |
| |
| } else if (cmsg->cmsg_level == SOL_SOCKET && |
| @@ -2511,7 +2511,7 @@ static int manager_dispatch_notify_fd(sd |
| cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) { |
| |
| assert(!ucred); |
| - ucred = (struct ucred*) CMSG_DATA(cmsg); |
| + ucred = CMSG_TYPED_DATA(cmsg, struct ucred); |
| } |
| } |
| |
| --- a/src/coredump/coredump.c |
| +++ b/src/coredump/coredump.c |
| @@ -1163,7 +1163,7 @@ static int process_socket(int fd) { |
| } |
| |
| assert(input_fd < 0); |
| - input_fd = *(int*) CMSG_DATA(found); |
| + input_fd = *CMSG_TYPED_DATA(found, int); |
| break; |
| } else |
| cmsg_close_all(&mh); |
| --- a/src/home/homed-manager.c |
| +++ b/src/home/homed-manager.c |
| @@ -1086,7 +1086,7 @@ static ssize_t read_datagram( |
| cmsg->cmsg_type == SCM_CREDENTIALS && |
| cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) { |
| assert(!sender); |
| - sender = (struct ucred*) CMSG_DATA(cmsg); |
| + sender = CMSG_TYPED_DATA(cmsg, struct ucred); |
| } |
| |
| if (cmsg->cmsg_level == SOL_SOCKET && |
| @@ -1098,7 +1098,7 @@ static ssize_t read_datagram( |
| } |
| |
| assert(passed_fd < 0); |
| - passed_fd = *(int*) CMSG_DATA(cmsg); |
| + passed_fd = *CMSG_TYPED_DATA(cmsg, int); |
| } |
| } |
| |
| --- a/src/journal/journald-server.c |
| +++ b/src/journal/journald-server.c |
| @@ -1454,21 +1454,21 @@ int server_process_datagram( |
| cmsg->cmsg_type == SCM_CREDENTIALS && |
| cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) { |
| assert(!ucred); |
| - ucred = (struct ucred*) CMSG_DATA(cmsg); |
| + ucred = CMSG_TYPED_DATA(cmsg, struct ucred); |
| } else if (cmsg->cmsg_level == SOL_SOCKET && |
| cmsg->cmsg_type == SCM_SECURITY) { |
| assert(!label); |
| - label = (char*) CMSG_DATA(cmsg); |
| + label = CMSG_TYPED_DATA(cmsg, char); |
| 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))) { |
| assert(!tv); |
| - tv = (struct timeval*) CMSG_DATA(cmsg); |
| + tv = CMSG_TYPED_DATA(cmsg, struct timeval); |
| } else if (cmsg->cmsg_level == SOL_SOCKET && |
| cmsg->cmsg_type == SCM_RIGHTS) { |
| assert(!fds); |
| - fds = (int*) CMSG_DATA(cmsg); |
| + fds = CMSG_TYPED_DATA(cmsg, int); |
| n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int); |
| } |
| |
| --- a/src/libsystemd-network/icmp6-util.c |
| +++ b/src/libsystemd-network/icmp6-util.c |
| @@ -192,7 +192,7 @@ int icmp6_receive(int fd, void *buffer, |
| if (cmsg->cmsg_level == SOL_IPV6 && |
| cmsg->cmsg_type == IPV6_HOPLIMIT && |
| cmsg->cmsg_len == CMSG_LEN(sizeof(int))) { |
| - int hops = *(int*) CMSG_DATA(cmsg); |
| + int hops = *CMSG_TYPED_DATA(cmsg, int); |
| |
| if (hops != 255) |
| return -EMULTIHOP; |
| @@ -201,7 +201,7 @@ int icmp6_receive(int fd, void *buffer, |
| if (cmsg->cmsg_level == SOL_SOCKET && |
| cmsg->cmsg_type == SO_TIMESTAMP && |
| cmsg->cmsg_len == CMSG_LEN(sizeof(struct timeval))) |
| - triple_timestamp_from_realtime(&t, timeval_load((struct timeval*) CMSG_DATA(cmsg))); |
| + triple_timestamp_from_realtime(&t, timeval_load(CMSG_TYPED_DATA(cmsg, struct timeval))); |
| } |
| |
| if (!triple_timestamp_is_set(&t)) |
| --- a/src/libsystemd-network/sd-dhcp-client.c |
| +++ b/src/libsystemd-network/sd-dhcp-client.c |
| @@ -1981,7 +1981,7 @@ static int client_receive_message_raw( |
| |
| cmsg = cmsg_find(&msg, SOL_PACKET, PACKET_AUXDATA, CMSG_LEN(sizeof(struct tpacket_auxdata))); |
| if (cmsg) { |
| - struct tpacket_auxdata *aux = (struct tpacket_auxdata*) CMSG_DATA(cmsg); |
| + struct tpacket_auxdata *aux = CMSG_TYPED_DATA(cmsg, struct tpacket_auxdata); |
| checksum = !(aux->tp_status & TP_STATUS_CSUMNOTREADY); |
| } |
| |
| --- a/src/libsystemd-network/sd-dhcp-server.c |
| +++ b/src/libsystemd-network/sd-dhcp-server.c |
| @@ -1310,7 +1310,7 @@ static int server_receive_message(sd_eve |
| if (cmsg->cmsg_level == IPPROTO_IP && |
| cmsg->cmsg_type == IP_PKTINFO && |
| cmsg->cmsg_len == CMSG_LEN(sizeof(struct in_pktinfo))) { |
| - struct in_pktinfo *info = (struct in_pktinfo*)CMSG_DATA(cmsg); |
| + struct in_pktinfo *info = CMSG_TYPED_DATA(cmsg, struct in_pktinfo); |
| |
| /* TODO figure out if this can be done as a filter on |
| * the socket, like for IPv6 */ |
| --- a/src/libsystemd/sd-bus/bus-socket.c |
| +++ b/src/libsystemd/sd-bus/bus-socket.c |
| @@ -604,7 +604,7 @@ static int bus_socket_read_auth(sd_bus * |
| * protocol? Somebody is playing games with |
| * us. Close them all, and fail */ |
| j = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int); |
| - close_many((int*) CMSG_DATA(cmsg), j); |
| + close_many(CMSG_TYPED_DATA(cmsg, int), j); |
| return -EIO; |
| } else |
| log_debug("Got unexpected auxiliary data with level=%d and type=%d", |
| @@ -1270,18 +1270,18 @@ int bus_socket_read_message(sd_bus *bus) |
| * isn't actually enabled? Close them, |
| * and fail */ |
| |
| - close_many((int*) CMSG_DATA(cmsg), n); |
| + close_many(CMSG_TYPED_DATA(cmsg, int), n); |
| return -EIO; |
| } |
| |
| f = reallocarray(bus->fds, bus->n_fds + n, sizeof(int)); |
| if (!f) { |
| - close_many((int*) CMSG_DATA(cmsg), n); |
| + close_many(CMSG_TYPED_DATA(cmsg, int), n); |
| return -ENOMEM; |
| } |
| |
| for (i = 0; i < n; i++) |
| - f[bus->n_fds++] = fd_move_above_stdio(((int*) CMSG_DATA(cmsg))[i]); |
| + f[bus->n_fds++] = fd_move_above_stdio(CMSG_TYPED_DATA(cmsg, int)[i]); |
| bus->fds = f; |
| } else |
| log_debug("Got unexpected auxiliary data with level=%d and type=%d", |
| --- a/src/resolve/resolved-dns-stream.c |
| +++ b/src/resolve/resolved-dns-stream.c |
| @@ -147,7 +147,7 @@ static int dns_stream_identify(DnsStream |
| switch (cmsg->cmsg_type) { |
| |
| case IPV6_PKTINFO: { |
| - struct in6_pktinfo *i = (struct in6_pktinfo*) CMSG_DATA(cmsg); |
| + struct in6_pktinfo *i = CMSG_TYPED_DATA(cmsg, struct in6_pktinfo); |
| |
| if (s->ifindex <= 0) |
| s->ifindex = i->ipi6_ifindex; |
| @@ -155,7 +155,7 @@ static int dns_stream_identify(DnsStream |
| } |
| |
| case IPV6_HOPLIMIT: |
| - s->ttl = *(int *) CMSG_DATA(cmsg); |
| + s->ttl = *CMSG_TYPED_DATA(cmsg, int); |
| break; |
| } |
| |
| @@ -165,7 +165,7 @@ static int dns_stream_identify(DnsStream |
| switch (cmsg->cmsg_type) { |
| |
| case IP_PKTINFO: { |
| - struct in_pktinfo *i = (struct in_pktinfo*) CMSG_DATA(cmsg); |
| + struct in_pktinfo *i = CMSG_TYPED_DATA(cmsg, struct in_pktinfo); |
| |
| if (s->ifindex <= 0) |
| s->ifindex = i->ipi_ifindex; |
| @@ -173,7 +173,7 @@ static int dns_stream_identify(DnsStream |
| } |
| |
| case IP_TTL: |
| - s->ttl = *(int *) CMSG_DATA(cmsg); |
| + s->ttl = *CMSG_TYPED_DATA(cmsg, int); |
| break; |
| } |
| } |
| --- a/src/resolve/resolved-manager.c |
| +++ b/src/resolve/resolved-manager.c |
| @@ -801,7 +801,7 @@ int manager_recv(Manager *m, int fd, Dns |
| switch (cmsg->cmsg_type) { |
| |
| case IPV6_PKTINFO: { |
| - struct in6_pktinfo *i = (struct in6_pktinfo*) CMSG_DATA(cmsg); |
| + struct in6_pktinfo *i = CMSG_TYPED_DATA(cmsg, struct in6_pktinfo); |
| |
| if (p->ifindex <= 0) |
| p->ifindex = i->ipi6_ifindex; |
| @@ -811,11 +811,11 @@ int manager_recv(Manager *m, int fd, Dns |
| } |
| |
| case IPV6_HOPLIMIT: |
| - p->ttl = *(int *) CMSG_DATA(cmsg); |
| + p->ttl = *CMSG_TYPED_DATA(cmsg, int); |
| break; |
| |
| case IPV6_RECVFRAGSIZE: |
| - p->fragsize = *(int *) CMSG_DATA(cmsg); |
| + p->fragsize = *CMSG_TYPED_DATA(cmsg, int); |
| break; |
| } |
| } else if (cmsg->cmsg_level == IPPROTO_IP) { |
| @@ -824,7 +824,7 @@ int manager_recv(Manager *m, int fd, Dns |
| switch (cmsg->cmsg_type) { |
| |
| case IP_PKTINFO: { |
| - struct in_pktinfo *i = (struct in_pktinfo*) CMSG_DATA(cmsg); |
| + struct in_pktinfo *i = CMSG_TYPED_DATA(cmsg, struct in_pktinfo); |
| |
| if (p->ifindex <= 0) |
| p->ifindex = i->ipi_ifindex; |
| @@ -834,11 +834,11 @@ int manager_recv(Manager *m, int fd, Dns |
| } |
| |
| case IP_TTL: |
| - p->ttl = *(int *) CMSG_DATA(cmsg); |
| + p->ttl = *CMSG_TYPED_DATA(cmsg, int); |
| break; |
| |
| case IP_RECVFRAGSIZE: |
| - p->fragsize = *(int *) CMSG_DATA(cmsg); |
| + p->fragsize = *CMSG_TYPED_DATA(cmsg, int); |
| break; |
| } |
| } |
| --- a/src/libsystemd/sd-device/device-monitor.c |
| +++ b/src/libsystemd/sd-device/device-monitor.c |
| @@ -503,7 +503,6 @@ int device_monitor_receive_device(sd_dev |
| .msg_name = &snl, |
| .msg_namelen = sizeof(snl), |
| }; |
| - struct cmsghdr *cmsg; |
| struct ucred *cred; |
| size_t offset; |
| ssize_t n; |
| @@ -559,12 +558,11 @@ int device_monitor_receive_device(sd_dev |
| snl.nl.nl_pid); |
| } |
| |
| - cmsg = CMSG_FIRSTHDR(&smsg); |
| - if (!cmsg || cmsg->cmsg_type != SCM_CREDENTIALS) |
| + cred = CMSG_FIND_DATA(&smsg, SOL_SOCKET, SCM_CREDENTIALS, struct ucred); |
| + if (!cred) |
| return log_monitor_errno(m, SYNTHETIC_ERRNO(EAGAIN), |
| "No sender credentials received, ignoring message."); |
| |
| - cred = (struct ucred*) CMSG_DATA(cmsg); |
| if (!check_sender_uid(m, cred->uid)) |
| return log_monitor_errno(m, SYNTHETIC_ERRNO(EAGAIN), |
| "Sender uid="UID_FMT", message ignored.", cred->uid); |
| --- a/src/udev/udev-ctrl.c |
| +++ b/src/udev/udev-ctrl.c |
| @@ -161,7 +161,6 @@ static int udev_ctrl_connection_event_ha |
| .msg_control = &control, |
| .msg_controllen = sizeof(control), |
| }; |
| - struct cmsghdr *cmsg; |
| struct ucred *cred; |
| ssize_t size; |
| |
| @@ -185,15 +184,12 @@ static int udev_ctrl_connection_event_ha |
| |
| cmsg_close_all(&smsg); |
| |
| - cmsg = CMSG_FIRSTHDR(&smsg); |
| - |
| - if (!cmsg || cmsg->cmsg_type != SCM_CREDENTIALS) { |
| + cred = CMSG_FIND_DATA(&smsg, SOL_SOCKET, SCM_CREDENTIALS, struct ucred); |
| + if (!cred) { |
| log_error("No sender credentials received, ignoring message"); |
| return 0; |
| } |
| |
| - cred = (struct ucred *) CMSG_DATA(cmsg); |
| - |
| if (cred->uid != 0) { |
| log_error("Invalid sender uid "UID_FMT", ignoring message", cred->uid); |
| return 0; |