From 0188dae693b1c6e82074368fff27fbb65351ab90 Mon Sep 17 00:00:00 2001 From: Ariel Abreu Date: Thu, 23 Apr 2020 13:58:59 -0400 Subject: [PATCH] Fix `recvmsg`/`sendmsg` alignment issues Fixes #761 Linux CMSGs are aligned to 8 bytes, but BSD CMSgs are aligned to 4 bytes. We were correctly adding 4 to the length of BSD message to account for the extra 4 bytes in the message length field, but we were still aligning the data to 4 bytes. This fixes that by aligning to 8 bytes instead. --- src/kernel/emulation/linux/network/recvmsg.c | 11 ++++++----- src/kernel/emulation/linux/network/recvmsg.h | 10 ++++++++++ src/kernel/emulation/linux/network/sendmsg.c | 10 ++++++---- 3 files changed, 22 insertions(+), 9 deletions(-) diff --git a/src/kernel/emulation/linux/network/recvmsg.c b/src/kernel/emulation/linux/network/recvmsg.c index d78065e13..c4e51cabb 100644 --- a/src/kernel/emulation/linux/network/recvmsg.c +++ b/src/kernel/emulation/linux/network/recvmsg.c @@ -38,9 +38,9 @@ long sys_recvmsg_nocancel(int socket, struct bsd_msghdr* msg, int flags) else if (msg->msg_control != NULL && msg->msg_controllen > 0) { // __simple_printf("controllen=%d\n", msg->msg_controllen); - lchdr = (struct linux_cmsghdr*) malloc(msg->msg_controllen + 4); + lmsg.msg_controllen = LINUX_CMSG_ALIGN(msg->msg_controllen + LINUX_BSD_CMSGHDR_SIZE_DIFFERENCE); // FIXME: there could be multiple control messages in a message + lchdr = (struct linux_cmsghdr*) malloc(lmsg.msg_controllen); lmsg.msg_control = lchdr; - lmsg.msg_controllen = msg->msg_controllen + 4; // FIXME: there could be multiple control messages in a message } else { @@ -86,14 +86,15 @@ long sys_recvmsg_nocancel(int socket, struct bsd_msghdr* msg, int flags) // __simple_printf("Linux msg at %d -> BSD at %d\n", lpos, bpos); - bnext->cmsg_len = lnext->cmsg_len - (sizeof(struct linux_cmsghdr) - sizeof(struct bsd_cmsghdr)); + bnext->cmsg_len = lnext->cmsg_len - LINUX_BSD_CMSGHDR_SIZE_DIFFERENCE; bnext->cmsg_level = socket_level_linux_to_bsd(lnext->cmsg_level); bnext->cmsg_type = lnext->cmsg_type; // __simple_printf("About to copy %d bytes\n", lnext->cmsg_len - sizeof(struct linux_cmsghdr)); - memcpy(bnext->cmsg_data, lnext->cmsg_data, lnext->cmsg_len - sizeof(struct linux_cmsghdr)); - msg->msg_controllen -= 4; + size_t actual_data_size = lnext->cmsg_len - sizeof(struct linux_cmsghdr); + memcpy(bnext->cmsg_data, lnext->cmsg_data, actual_data_size); + msg->msg_controllen -= LINUX_CMSG_SPACE(actual_data_size) - BSD_CMSG_SPACE(actual_data_size); lpos += ALIGN(lnext->cmsg_len, sizeof(unsigned long)); bpos += ALIGN(bnext->cmsg_len, 4); diff --git a/src/kernel/emulation/linux/network/recvmsg.h b/src/kernel/emulation/linux/network/recvmsg.h index 0ac78eed8..e2e94fef3 100644 --- a/src/kernel/emulation/linux/network/recvmsg.h +++ b/src/kernel/emulation/linux/network/recvmsg.h @@ -49,5 +49,15 @@ int socket_level_linux_to_bsd(int level); #define LINUX_SYS_RECVMSG 17 +#define BSD_CMSG_ALIGN(len) (((len) + sizeof(uint32_t) - 1) & (size_t)~(sizeof(uint32_t) - 1)) +#define BSD_CMSG_SPACE(len) (BSD_CMSG_ALIGN(sizeof(struct bsd_cmsghdr)) + BSD_CMSG_ALIGN(len)) +#define BSD_CMSG_LEN(len) (BSD_CMSG_ALIGN(sizeof(struct bsd_cmsghdr)) + (len)) + +#define LINUX_CMSG_ALIGN(len) (((len) + sizeof(unsigned long) - 1) & (size_t)~(sizeof(unsigned long) - 1)) +#define LINUX_CMSG_SPACE(len) (LINUX_CMSG_ALIGN(sizeof(struct linux_cmsghdr)) + LINUX_CMSG_ALIGN(len)) +#define LINUX_CMSG_LEN(len) (LINUX_CMSG_ALIGN(sizeof(struct linux_cmsghdr)) + (len)) + +#define LINUX_BSD_CMSGHDR_SIZE_DIFFERENCE (sizeof(struct linux_cmsghdr) - sizeof(struct bsd_cmsghdr)) + #endif diff --git a/src/kernel/emulation/linux/network/sendmsg.c b/src/kernel/emulation/linux/network/sendmsg.c index 99489c041..470e1b69c 100644 --- a/src/kernel/emulation/linux/network/sendmsg.c +++ b/src/kernel/emulation/linux/network/sendmsg.c @@ -45,17 +45,19 @@ long sys_sendmsg_nocancel(int socket, const struct bsd_msghdr* msg, int flags) struct bsd_cmsghdr* bchdr; bchdr = (struct bsd_cmsghdr*) msg->msg_control; - lchdr = (struct linux_cmsghdr*) malloc(msg->msg_controllen + 4); + + lmsg.msg_controllen = LINUX_CMSG_ALIGN(msg->msg_controllen + LINUX_BSD_CMSGHDR_SIZE_DIFFERENCE); + + lchdr = (struct linux_cmsghdr*)malloc(lmsg.msg_controllen); lmsg.msg_control = lchdr; - lmsg.msg_controllen = msg->msg_controllen + 4; - lchdr->cmsg_len = bchdr->cmsg_len; + lchdr->cmsg_len = bchdr->cmsg_len + LINUX_BSD_CMSGHDR_SIZE_DIFFERENCE; lchdr->cmsg_level = socket_level_bsd_to_linux(bchdr->cmsg_level); lchdr->cmsg_type = bchdr->cmsg_type; memcpy(lchdr->cmsg_data, bchdr->cmsg_data, - lchdr->cmsg_len - sizeof(struct bsd_cmsghdr)); + bchdr->cmsg_len - sizeof(struct bsd_cmsghdr)); } else { -- 2.51.2