aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorJohan Hedberg <johan.hedberg@intel.com>2014-01-27 15:11:35 -0800
committerJohan Hedberg <johan.hedberg@intel.com>2014-02-13 09:51:38 +0200
commit0f1bfe4e5eb8db9841b57ade1384b9a8ffcd38c3 (patch)
tree4cd9320657333f77e9e348f41c7ec8aaa646d713 /net
parentBluetooth: Fix disconnecting L2CAP channel for credits violation (diff)
downloadlinux-dev-0f1bfe4e5eb8db9841b57ade1384b9a8ffcd38c3.tar.xz
linux-dev-0f1bfe4e5eb8db9841b57ade1384b9a8ffcd38c3.zip
Bluetooth: Fix disconnecting L2CAP when a credits overflow occurs
The L2CAP specification requires us to disconnect an L2CAP channel if the remote side gives us credits beyond 65535. This patch makes sure we disconnect the channel in such a situation. Signed-off-by: Johan Hedberg <johan.hedberg@intel.com> Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
Diffstat (limited to 'net')
-rw-r--r--net/bluetooth/l2cap_core.c15
1 files changed, 14 insertions, 1 deletions
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index fbc9709abccf..d2ef49b54aa2 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -42,6 +42,8 @@
#include "amp.h"
#include "6lowpan.h"
+#define LE_FLOWCTL_MAX_CREDITS 65535
+
bool disable_ertm;
static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN | L2CAP_FEAT_UCD;
@@ -5473,7 +5475,7 @@ static inline int l2cap_le_credits(struct l2cap_conn *conn,
{
struct l2cap_le_credits *pkt;
struct l2cap_chan *chan;
- u16 cid, credits;
+ u16 cid, credits, max_credits;
if (cmd_len != sizeof(*pkt))
return -EPROTO;
@@ -5488,6 +5490,17 @@ static inline int l2cap_le_credits(struct l2cap_conn *conn,
if (!chan)
return -EBADSLT;
+ max_credits = LE_FLOWCTL_MAX_CREDITS - chan->tx_credits;
+ if (credits > max_credits) {
+ BT_ERR("LE credits overflow");
+ l2cap_send_disconn_req(chan, ECONNRESET);
+
+ /* Return 0 so that we don't trigger an unnecessary
+ * command reject packet.
+ */
+ return 0;
+ }
+
chan->tx_credits += credits;
while (chan->tx_credits && !skb_queue_empty(&chan->tx_q)) {