From 55cb20dd90482967e9a234f5a20192f565050bc7 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 (cherry picked from commit 5d643f4b00da1572cb10494357197548c6f28883) --- subsys/net/ip/net_context.c | 11 +++++++++++ subsys/net/ip/net_private.h | 6 ++++++ subsys/net/lib/sockets/sockets.c | 32 ++++++++++++++++++++++++-------- 3 files changed, 41 insertions(+), 8 deletions(-) diff --git a/subsys/net/ip/net_context.c b/subsys/net/ip/net_context.c index 800c6b7efcf8d2..53a3452c58daac 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 cb4c32c3a28ffd..0bbcbb231649e3 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 2532893945795b..9ed8dce238f621 100644 --- a/subsys/net/lib/sockets/sockets.c +++ b/subsys/net/lib/sockets/sockets.c @@ -1426,6 +1426,22 @@ 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, @@ -1570,10 +1586,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; } @@ -1581,15 +1595,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;