summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDenis Kenzior <denkenz@gmail.com>2016-06-27 11:01:12 -0500
committerMarcel Holtmann <marcel@holtmann.org>2016-07-08 12:20:57 +0200
commitb5f34f9420b50c9b5876b9a2b68e96be6d629054 (patch)
tree8bf538e462539a67a35a6b052935d6419cb530e3
parent1c5bf998b3dca0599a2cce885619ffc06fc594df (diff)
downloadlinux-stable-b5f34f9420b50c9b5876b9a2b68e96be6d629054.tar.gz
linux-stable-b5f34f9420b50c9b5876b9a2b68e96be6d629054.tar.bz2
linux-stable-b5f34f9420b50c9b5876b9a2b68e96be6d629054.zip
Bluetooth: Fix bt_sock_recvmsg return value
If recvmsg is called with a destination buffer that is too small to receive the contents of skb in its entirety, the return value from recvmsg was inconsistent with common SOCK_SEQPACKET or SOCK_DGRAM semantics. If destination buffer provided by userspace is too small (e.g. len < copied), then MSG_TRUNC flag is set and copied is returned. Instead, it should return the length of the message, which is consistent with how other datagram based sockets act. Quoting 'man recv': "All three calls return the length of the message on successful compleā€ tion. If a message is too long to fit in the supplied buffer, excess bytes may be discarded depending on the type of socket the message is received from." and "MSG_TRUNC (since Linux 2.2) For raw (AF_PACKET), Internet datagram (since Linux 2.4.27/2.6.8), netlink (since Linux 2.6.22), and UNIX datagram (since Linux 3.4) sockets: return the real length of the packet or datagram, even when it was longer than the passed buffer." Signed-off-by: Denis Kenzior <denkenz@gmail.com> Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
-rw-r--r--net/bluetooth/af_bluetooth.c5
1 files changed, 5 insertions, 0 deletions
diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c
index 3df7aefb7663..ece45e0683fd 100644
--- a/net/bluetooth/af_bluetooth.c
+++ b/net/bluetooth/af_bluetooth.c
@@ -215,6 +215,7 @@ int bt_sock_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
struct sock *sk = sock->sk;
struct sk_buff *skb;
size_t copied;
+ size_t skblen;
int err;
BT_DBG("sock %p sk %p len %zu", sock, sk, len);
@@ -230,6 +231,7 @@ int bt_sock_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
return err;
}
+ skblen = skb->len;
copied = skb->len;
if (len < copied) {
msg->msg_flags |= MSG_TRUNC;
@@ -248,6 +250,9 @@ int bt_sock_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
skb_free_datagram(sk, skb);
+ if (msg->msg_flags & MSG_TRUNC)
+ copied = skblen;
+
return err ? : copied;
}
EXPORT_SYMBOL(bt_sock_recvmsg);