Commit c13ffa62 authored by Gustavo F. Padovan's avatar Gustavo F. Padovan Committed by Marcel Holtmann

Bluetooth: Proper shutdown ERTM when closing the channel

Fix a crash regarding the Monitor Timeout, it was running even after the
shutdown of the ACL connection, which doesn't make sense.

The same code also fixes another issue, before this patch L2CAP was sending
many Disconnections Requests while we have to send only one.

The issues are related to each other, a expired Monitor Timeout can
trigger a Disconnection Request and then we may have a crash if the link
was already deleted.
Signed-off-by: default avatarGustavo F. Padovan <padovan@profusion.mobi>
Signed-off-by: default avatarMarcel Holtmann <marcel@holtmann.org>
parent 51893f88
...@@ -272,6 +272,24 @@ static void l2cap_chan_del(struct sock *sk, int err) ...@@ -272,6 +272,24 @@ static void l2cap_chan_del(struct sock *sk, int err)
parent->sk_data_ready(parent, 0); parent->sk_data_ready(parent, 0);
} else } else
sk->sk_state_change(sk); sk->sk_state_change(sk);
skb_queue_purge(TX_QUEUE(sk));
if (l2cap_pi(sk)->mode == L2CAP_MODE_ERTM) {
struct srej_list *l, *tmp;
del_timer(&l2cap_pi(sk)->retrans_timer);
del_timer(&l2cap_pi(sk)->monitor_timer);
del_timer(&l2cap_pi(sk)->ack_timer);
skb_queue_purge(SREJ_QUEUE(sk));
skb_queue_purge(BUSY_QUEUE(sk));
list_for_each_entry_safe(l, tmp, SREJ_LIST(sk), list) {
list_del(&l->list);
kfree(l);
}
}
} }
/* Service level security */ /* Service level security */
...@@ -345,8 +363,12 @@ static inline void l2cap_send_sframe(struct l2cap_pinfo *pi, u16 control) ...@@ -345,8 +363,12 @@ static inline void l2cap_send_sframe(struct l2cap_pinfo *pi, u16 control)
struct sk_buff *skb; struct sk_buff *skb;
struct l2cap_hdr *lh; struct l2cap_hdr *lh;
struct l2cap_conn *conn = pi->conn; struct l2cap_conn *conn = pi->conn;
struct sock *sk = (struct sock *)pi;
int count, hlen = L2CAP_HDR_SIZE + 2; int count, hlen = L2CAP_HDR_SIZE + 2;
if (sk->sk_state != BT_CONNECTED)
return;
if (pi->fcs == L2CAP_FCS_CRC16) if (pi->fcs == L2CAP_FCS_CRC16)
hlen += 2; hlen += 2;
...@@ -438,10 +460,23 @@ static void l2cap_send_disconn_req(struct l2cap_conn *conn, struct sock *sk) ...@@ -438,10 +460,23 @@ static void l2cap_send_disconn_req(struct l2cap_conn *conn, struct sock *sk)
{ {
struct l2cap_disconn_req req; struct l2cap_disconn_req req;
if (!conn)
return;
skb_queue_purge(TX_QUEUE(sk));
if (l2cap_pi(sk)->mode == L2CAP_MODE_ERTM) {
del_timer(&l2cap_pi(sk)->retrans_timer);
del_timer(&l2cap_pi(sk)->monitor_timer);
del_timer(&l2cap_pi(sk)->ack_timer);
}
req.dcid = cpu_to_le16(l2cap_pi(sk)->dcid); req.dcid = cpu_to_le16(l2cap_pi(sk)->dcid);
req.scid = cpu_to_le16(l2cap_pi(sk)->scid); req.scid = cpu_to_le16(l2cap_pi(sk)->scid);
l2cap_send_cmd(conn, l2cap_get_ident(conn), l2cap_send_cmd(conn, l2cap_get_ident(conn),
L2CAP_DISCONN_REQ, sizeof(req), &req); L2CAP_DISCONN_REQ, sizeof(req), &req);
sk->sk_state = BT_DISCONN;
} }
/* ---- L2CAP connections ---- */ /* ---- L2CAP connections ---- */
...@@ -734,7 +769,6 @@ static void __l2cap_sock_close(struct sock *sk, int reason) ...@@ -734,7 +769,6 @@ static void __l2cap_sock_close(struct sock *sk, int reason)
sk->sk_type == SOCK_STREAM) { sk->sk_type == SOCK_STREAM) {
struct l2cap_conn *conn = l2cap_pi(sk)->conn; struct l2cap_conn *conn = l2cap_pi(sk)->conn;
sk->sk_state = BT_DISCONN;
l2cap_sock_set_timer(sk, sk->sk_sndtimeo); l2cap_sock_set_timer(sk, sk->sk_sndtimeo);
l2cap_send_disconn_req(conn, sk); l2cap_send_disconn_req(conn, sk);
} else } else
...@@ -1415,6 +1449,8 @@ static int l2cap_ertm_send(struct sock *sk) ...@@ -1415,6 +1449,8 @@ static int l2cap_ertm_send(struct sock *sk)
u16 control, fcs; u16 control, fcs;
int nsent = 0; int nsent = 0;
if (sk->sk_state != BT_CONNECTED)
return -ENOTCONN;
while ((skb = sk->sk_send_head) && (!l2cap_tx_window_full(sk))) { while ((skb = sk->sk_send_head) && (!l2cap_tx_window_full(sk))) {
...@@ -3072,7 +3108,6 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr ...@@ -3072,7 +3108,6 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr
} }
default: default:
sk->sk_state = BT_DISCONN;
sk->sk_err = ECONNRESET; sk->sk_err = ECONNRESET;
l2cap_sock_set_timer(sk, HZ * 5); l2cap_sock_set_timer(sk, HZ * 5);
l2cap_send_disconn_req(conn, sk); l2cap_send_disconn_req(conn, sk);
...@@ -3126,16 +3161,6 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd ...@@ -3126,16 +3161,6 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd
sk->sk_shutdown = SHUTDOWN_MASK; sk->sk_shutdown = SHUTDOWN_MASK;
skb_queue_purge(TX_QUEUE(sk));
if (l2cap_pi(sk)->mode == L2CAP_MODE_ERTM) {
skb_queue_purge(SREJ_QUEUE(sk));
skb_queue_purge(BUSY_QUEUE(sk));
del_timer(&l2cap_pi(sk)->retrans_timer);
del_timer(&l2cap_pi(sk)->monitor_timer);
del_timer(&l2cap_pi(sk)->ack_timer);
}
l2cap_chan_del(sk, ECONNRESET); l2cap_chan_del(sk, ECONNRESET);
bh_unlock_sock(sk); bh_unlock_sock(sk);
...@@ -3158,16 +3183,6 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd ...@@ -3158,16 +3183,6 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd
if (!sk) if (!sk)
return 0; return 0;
skb_queue_purge(TX_QUEUE(sk));
if (l2cap_pi(sk)->mode == L2CAP_MODE_ERTM) {
skb_queue_purge(SREJ_QUEUE(sk));
skb_queue_purge(BUSY_QUEUE(sk));
del_timer(&l2cap_pi(sk)->retrans_timer);
del_timer(&l2cap_pi(sk)->monitor_timer);
del_timer(&l2cap_pi(sk)->ack_timer);
}
l2cap_chan_del(sk, 0); l2cap_chan_del(sk, 0);
bh_unlock_sock(sk); bh_unlock_sock(sk);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment