From: Oliver Hartkopp socketcan@hartkopp.net
[ Upstream commit 42bf50a1795a1854d48717b7361dbdbce496b16b ]
When providing the MSG_TRUNC flag via recvmsg() syscall the return value provides the real length of the packet or datagram, even when it was longer than the passed buffer.
Fixes: e057dd3fc20f ("can: add ISO 15765-2:2016 transport protocol") Link: https://github.com/linux-can/can-utils/issues/347#issuecomment-1065932671 Link: https://lore.kernel.org/all/20220316164258.54155-3-socketcan@hartkopp.net Suggested-by: Derek Will derekrobertwill@gmail.com Signed-off-by: Oliver Hartkopp socketcan@hartkopp.net Signed-off-by: Marc Kleine-Budde mkl@pengutronix.de Signed-off-by: Sasha Levin sashal@kernel.org --- net/can/isotp.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-)
diff --git a/net/can/isotp.c b/net/can/isotp.c index cb5546c186bc..518014506fff 100644 --- a/net/can/isotp.c +++ b/net/can/isotp.c @@ -1004,29 +1004,28 @@ static int isotp_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, struct sock *sk = sock->sk; struct sk_buff *skb; struct isotp_sock *so = isotp_sk(sk); - int err = 0; - int noblock; + int noblock = flags & MSG_DONTWAIT; + int ret = 0;
- noblock = flags & MSG_DONTWAIT; - flags &= ~MSG_DONTWAIT; + if (flags & ~(MSG_DONTWAIT | MSG_TRUNC)) + return -EINVAL;
if (!so->bound) return -EADDRNOTAVAIL;
- skb = skb_recv_datagram(sk, flags, noblock, &err); + flags &= ~MSG_DONTWAIT; + skb = skb_recv_datagram(sk, flags, noblock, &ret); if (!skb) - return err; + return ret;
if (size < skb->len) msg->msg_flags |= MSG_TRUNC; else size = skb->len;
- err = memcpy_to_msg(msg, skb->data, size); - if (err < 0) { - skb_free_datagram(sk, skb); - return err; - } + ret = memcpy_to_msg(msg, skb->data, size); + if (ret < 0) + goto out_err;
sock_recv_timestamp(msg, sk, skb);
@@ -1036,9 +1035,13 @@ static int isotp_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, memcpy(msg->msg_name, skb->cb, msg->msg_namelen); }
+ /* set length of return value */ + ret = (flags & MSG_TRUNC) ? skb->len : size; + +out_err: skb_free_datagram(sk, skb);
- return size; + return ret; }
static int isotp_release(struct socket *sock)