From 3a6827db2592b8b562d651b573fb146aaa962fba Mon Sep 17 00:00:00 2001 From: Axel Le Bourhis Date: Wed, 21 Aug 2024 14:02:43 +0200 Subject: [PATCH] net: sockets: Update msg_controllen in recvmsg properly According to recvmsg man page, msg_controllen should be set to the sum of the length of all control messages in the buffer. This is missing from the current recvmsg implementation. This commit aims to fix this by updating msg_controllen each time control data are added to the buffer. This commit also fixes cases where the msg_controllen is cleared incorrectly. Fixes #77303 Signed-off-by: Axel Le Bourhis --- subsys/net/ip/net_context.c | 11 +++++++++++ subsys/net/ip/net_private.h | 6 ++++++ subsys/net/lib/sockets/sockets.c | 31 +++++++++++++++++++++++-------- 3 files changed, 40 insertions(+), 8 deletions(-) diff --git a/subsys/net/ip/net_context.c b/subsys/net/ip/net_context.c index 800c6b7efcf8d2c..53a3452c58daacc 100644 --- a/subsys/net/ip/net_context.c +++ b/subsys/net/ip/net_context.c @@ -128,6 +128,17 @@ bool net_context_is_recv_pktinfo_set(struct net_context *context) #endif } +bool net_context_is_timestamping_set(struct net_context *context) +{ +#if defined(CONFIG_NET_CONTEXT_TIMESTAMPING) + return (bool)(context->options.timestamping > 0); +#else + ARG_UNUSED(context); + + return false; +#endif +} + #if defined(CONFIG_NET_UDP) || defined(CONFIG_NET_TCP) static inline bool is_in_tcp_listen_state(struct net_context *context) { diff --git a/subsys/net/ip/net_private.h b/subsys/net/ip/net_private.h index cb4c32c3a28ffdd..0bbcbb231649e38 100644 --- a/subsys/net/ip/net_private.h +++ b/subsys/net/ip/net_private.h @@ -84,6 +84,7 @@ extern bool net_context_is_reuseaddr_set(struct net_context *context); extern bool net_context_is_reuseport_set(struct net_context *context); extern bool net_context_is_v6only_set(struct net_context *context); extern bool net_context_is_recv_pktinfo_set(struct net_context *context); +extern bool net_context_is_timestamping_set(struct net_context *context); extern void net_pkt_init(void); extern void net_tc_tx_init(void); extern void net_tc_rx_init(void); @@ -115,6 +116,11 @@ static inline bool net_context_is_recv_pktinfo_set(struct net_context *context) ARG_UNUSED(context); return false; } +static inline bool net_context_is_timestamping_set(struct net_context *context) +{ + ARG_UNUSED(context); + return false; +} static inline int net_context_get_local_addr(struct net_context *context, struct sockaddr *addr, diff --git a/subsys/net/lib/sockets/sockets.c b/subsys/net/lib/sockets/sockets.c index dc74a229665b259..ae7bb8634ebf3a8 100644 --- a/subsys/net/lib/sockets/sockets.c +++ b/subsys/net/lib/sockets/sockets.c @@ -1428,6 +1428,21 @@ static int add_pktinfo(struct net_context *ctx, return ret; } +static int update_msg_controllen(struct msghdr *msg) { + struct cmsghdr *cmsg; + size_t cmsg_space = 0; + + for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; cmsg = CMSG_NXTHDR(msg, cmsg)) { + if (cmsg->cmsg_len == 0) { + break; + } + cmsg_space += cmsg->cmsg_len; + } + msg->msg_controllen = cmsg_space; + + return 0; +} + static inline ssize_t zsock_recv_dgram(struct net_context *ctx, struct msghdr *msg, void *buf, @@ -1572,10 +1587,8 @@ static inline ssize_t zsock_recv_dgram(struct net_context *ctx, if (msg != NULL) { if (msg->msg_control != NULL) { if (msg->msg_controllen > 0) { - bool clear_controllen = true; - - if (IS_ENABLED(CONFIG_NET_CONTEXT_TIMESTAMPING)) { - clear_controllen = false; + if (IS_ENABLED(CONFIG_NET_CONTEXT_TIMESTAMPING) && + net_context_is_timestamping_set(ctx)) { if (add_timestamping(ctx, pkt, msg) < 0) { msg->msg_flags |= ZSOCK_MSG_CTRUNC; } @@ -1583,15 +1596,17 @@ static inline ssize_t zsock_recv_dgram(struct net_context *ctx, if (IS_ENABLED(CONFIG_NET_CONTEXT_RECV_PKTINFO) && net_context_is_recv_pktinfo_set(ctx)) { - clear_controllen = false; if (add_pktinfo(ctx, pkt, msg) < 0) { msg->msg_flags |= ZSOCK_MSG_CTRUNC; } } - if (clear_controllen) { - msg->msg_controllen = 0; - } + /* msg_controllen must be updated to reflect the total length of all control + * messages in the buffer + * If there are no control data, msg_controllen will be cleared as expected + * It will also take into account pre-existing control data + */ + update_msg_controllen(msg); } } else { msg->msg_controllen = 0U;