Skip to content

Commit 58cd186

Browse files
Vudentzcodewalkerster
authored andcommitted
Bluetooth: L2CAP: Fix use-after-free caused by l2cap_chan_put
commit d0be834 upstream. This fixes the following trace which is caused by hci_rx_work starting up *after* the final channel reference has been put() during sock_close() but *before* the references to the channel have been destroyed, so instead the code now rely on kref_get_unless_zero/l2cap_chan_hold_unless_zero to prevent referencing a channel that is about to be destroyed. refcount_t: increment on 0; use-after-free. BUG: KASAN: use-after-free in refcount_dec_and_test+0x20/0xd0 Read of size 4 at addr ffffffc114f5bf18 by task kworker/u17:14/705 CPU: 4 PID: 705 Comm: kworker/u17:14 Tainted: G S W 4.14.234-00003-g1fb6d0bd49a4-dirty #28 Hardware name: Qualcomm Technologies, Inc. SM8150 V2 PM8150 Google Inc. MSM sm8150 Flame DVT (DT) Workqueue: hci0 hci_rx_work Call trace: dump_backtrace+0x0/0x378 show_stack+0x20/0x2c dump_stack+0x124/0x148 print_address_description+0x80/0x2e8 __kasan_report+0x168/0x188 kasan_report+0x10/0x18 __asan_load4+0x84/0x8c refcount_dec_and_test+0x20/0xd0 l2cap_chan_put+0x48/0x12c l2cap_recv_frame+0x4770/0x6550 l2cap_recv_acldata+0x44c/0x7a4 hci_acldata_packet+0x100/0x188 hci_rx_work+0x178/0x23c process_one_work+0x35c/0x95c worker_thread+0x4cc/0x960 kthread+0x1a8/0x1c4 ret_from_fork+0x10/0x18 Cc: [email protected] Reported-by: Lee Jones <[email protected]> Signed-off-by: Luiz Augusto von Dentz <[email protected]> Tested-by: Lee Jones <[email protected]> Signed-off-by: Luiz Augusto von Dentz <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 5d8d527 commit 58cd186

File tree

2 files changed

+49
-13
lines changed

2 files changed

+49
-13
lines changed

include/net/bluetooth/l2cap.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -798,6 +798,7 @@ enum {
798798
};
799799

800800
void l2cap_chan_hold(struct l2cap_chan *c);
801+
struct l2cap_chan *l2cap_chan_hold_unless_zero(struct l2cap_chan *c);
801802
void l2cap_chan_put(struct l2cap_chan *c);
802803

803804
static inline void l2cap_chan_lock(struct l2cap_chan *chan)

net/bluetooth/l2cap_core.c

Lines changed: 48 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -113,23 +113,28 @@ static struct l2cap_chan *__l2cap_get_chan_by_scid(struct l2cap_conn *conn,
113113
}
114114

115115
/* Find channel with given SCID.
116-
* Returns locked channel. */
116+
* Returns a reference locked channel.
117+
*/
117118
static struct l2cap_chan *l2cap_get_chan_by_scid(struct l2cap_conn *conn,
118119
u16 cid)
119120
{
120121
struct l2cap_chan *c;
121122

122123
mutex_lock(&conn->chan_lock);
123124
c = __l2cap_get_chan_by_scid(conn, cid);
124-
if (c)
125-
l2cap_chan_lock(c);
125+
if (c) {
126+
/* Only lock if chan reference is not 0 */
127+
c = l2cap_chan_hold_unless_zero(c);
128+
if (c)
129+
l2cap_chan_lock(c);
130+
}
126131
mutex_unlock(&conn->chan_lock);
127132

128133
return c;
129134
}
130135

131136
/* Find channel with given DCID.
132-
* Returns locked channel.
137+
* Returns a reference locked channel.
133138
*/
134139
static struct l2cap_chan *l2cap_get_chan_by_dcid(struct l2cap_conn *conn,
135140
u16 cid)
@@ -138,8 +143,12 @@ static struct l2cap_chan *l2cap_get_chan_by_dcid(struct l2cap_conn *conn,
138143

139144
mutex_lock(&conn->chan_lock);
140145
c = __l2cap_get_chan_by_dcid(conn, cid);
141-
if (c)
142-
l2cap_chan_lock(c);
146+
if (c) {
147+
/* Only lock if chan reference is not 0 */
148+
c = l2cap_chan_hold_unless_zero(c);
149+
if (c)
150+
l2cap_chan_lock(c);
151+
}
143152
mutex_unlock(&conn->chan_lock);
144153

145154
return c;
@@ -164,8 +173,12 @@ static struct l2cap_chan *l2cap_get_chan_by_ident(struct l2cap_conn *conn,
164173

165174
mutex_lock(&conn->chan_lock);
166175
c = __l2cap_get_chan_by_ident(conn, ident);
167-
if (c)
168-
l2cap_chan_lock(c);
176+
if (c) {
177+
/* Only lock if chan reference is not 0 */
178+
c = l2cap_chan_hold_unless_zero(c);
179+
if (c)
180+
l2cap_chan_lock(c);
181+
}
169182
mutex_unlock(&conn->chan_lock);
170183

171184
return c;
@@ -491,6 +504,16 @@ void l2cap_chan_hold(struct l2cap_chan *c)
491504
kref_get(&c->kref);
492505
}
493506

507+
struct l2cap_chan *l2cap_chan_hold_unless_zero(struct l2cap_chan *c)
508+
{
509+
BT_DBG("chan %p orig refcnt %u", c, kref_read(&c->kref));
510+
511+
if (!kref_get_unless_zero(&c->kref))
512+
return NULL;
513+
514+
return c;
515+
}
516+
494517
void l2cap_chan_put(struct l2cap_chan *c)
495518
{
496519
BT_DBG("chan %p orig refcnt %d", c, atomic_read(&c->kref.refcount));
@@ -1803,7 +1826,10 @@ static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm,
18031826
src_match = !bacmp(&c->src, src);
18041827
dst_match = !bacmp(&c->dst, dst);
18051828
if (src_match && dst_match) {
1806-
l2cap_chan_hold(c);
1829+
c = l2cap_chan_hold_unless_zero(c);
1830+
if (!c)
1831+
continue;
1832+
18071833
read_unlock(&chan_list_lock);
18081834
return c;
18091835
}
@@ -1818,7 +1844,7 @@ static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm,
18181844
}
18191845

18201846
if (c1)
1821-
l2cap_chan_hold(c1);
1847+
c1 = l2cap_chan_hold_unless_zero(c1);
18221848

18231849
read_unlock(&chan_list_lock);
18241850

@@ -4194,6 +4220,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn,
41944220

41954221
unlock:
41964222
l2cap_chan_unlock(chan);
4223+
l2cap_chan_put(chan);
41974224
return err;
41984225
}
41994226

@@ -4306,6 +4333,7 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn,
43064333

43074334
done:
43084335
l2cap_chan_unlock(chan);
4336+
l2cap_chan_put(chan);
43094337
return err;
43104338
}
43114339

@@ -5034,6 +5062,7 @@ static inline int l2cap_move_channel_req(struct l2cap_conn *conn,
50345062
l2cap_send_move_chan_rsp(chan, result);
50355063

50365064
l2cap_chan_unlock(chan);
5065+
l2cap_chan_put(chan);
50375066

50385067
return 0;
50395068
}
@@ -5126,6 +5155,7 @@ static void l2cap_move_continue(struct l2cap_conn *conn, u16 icid, u16 result)
51265155
}
51275156

51285157
l2cap_chan_unlock(chan);
5158+
l2cap_chan_put(chan);
51295159
}
51305160

51315161
static void l2cap_move_fail(struct l2cap_conn *conn, u8 ident, u16 icid,
@@ -5155,6 +5185,7 @@ static void l2cap_move_fail(struct l2cap_conn *conn, u8 ident, u16 icid,
51555185
l2cap_send_move_chan_cfm(chan, L2CAP_MC_UNCONFIRMED);
51565186

51575187
l2cap_chan_unlock(chan);
5188+
l2cap_chan_put(chan);
51585189
}
51595190

51605191
static int l2cap_move_channel_rsp(struct l2cap_conn *conn,
@@ -5218,6 +5249,7 @@ static int l2cap_move_channel_confirm(struct l2cap_conn *conn,
52185249
l2cap_send_move_chan_cfm_rsp(conn, cmd->ident, icid);
52195250

52205251
l2cap_chan_unlock(chan);
5252+
l2cap_chan_put(chan);
52215253

52225254
return 0;
52235255
}
@@ -5253,6 +5285,7 @@ static inline int l2cap_move_channel_confirm_rsp(struct l2cap_conn *conn,
52535285
}
52545286

52555287
l2cap_chan_unlock(chan);
5288+
l2cap_chan_put(chan);
52565289

52575290
return 0;
52585291
}
@@ -5625,12 +5658,11 @@ static inline int l2cap_le_credits(struct l2cap_conn *conn,
56255658
if (credits > max_credits) {
56265659
BT_ERR("LE credits overflow");
56275660
l2cap_send_disconn_req(chan, ECONNRESET);
5628-
l2cap_chan_unlock(chan);
56295661

56305662
/* Return 0 so that we don't trigger an unnecessary
56315663
* command reject packet.
56325664
*/
5633-
return 0;
5665+
goto unlock;
56345666
}
56355667

56365668
chan->tx_credits += credits;
@@ -5643,7 +5675,9 @@ static inline int l2cap_le_credits(struct l2cap_conn *conn,
56435675
if (chan->tx_credits)
56445676
chan->ops->resume(chan);
56455677

5678+
unlock:
56465679
l2cap_chan_unlock(chan);
5680+
l2cap_chan_put(chan);
56475681

56485682
return 0;
56495683
}
@@ -6941,6 +6975,7 @@ static void l2cap_data_channel(struct l2cap_conn *conn, u16 cid,
69416975

69426976
done:
69436977
l2cap_chan_unlock(chan);
6978+
l2cap_chan_put(chan);
69446979
}
69456980

69466981
static void l2cap_conless_channel(struct l2cap_conn *conn, __le16 psm,
@@ -7345,7 +7380,7 @@ static struct l2cap_chan *l2cap_global_fixed_chan(struct l2cap_chan *c,
73457380
if (src_type != c->src_type)
73467381
continue;
73477382

7348-
l2cap_chan_hold(c);
7383+
c = l2cap_chan_hold_unless_zero(c);
73497384
read_unlock(&chan_list_lock);
73507385
return c;
73517386
}

0 commit comments

Comments
 (0)