Skip to content

Commit 098e07e

Browse files
Vudentzgregkh
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 raspberrypi#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: stable@kernel.org Reported-by: Lee Jones <lee.jones@linaro.org> Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com> Tested-by: Lee Jones <lee.jones@linaro.org> Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 77ba2b9 commit 098e07e

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
@@ -802,6 +802,7 @@ enum {
802802
};
803803

804804
void l2cap_chan_hold(struct l2cap_chan *c);
805+
struct l2cap_chan *l2cap_chan_hold_unless_zero(struct l2cap_chan *c);
805806
void l2cap_chan_put(struct l2cap_chan *c);
806807

807808
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
@@ -110,23 +110,28 @@ static struct l2cap_chan *__l2cap_get_chan_by_scid(struct l2cap_conn *conn,
110110
}
111111

112112
/* Find channel with given SCID.
113-
* Returns locked channel. */
113+
* Returns a reference locked channel.
114+
*/
114115
static struct l2cap_chan *l2cap_get_chan_by_scid(struct l2cap_conn *conn,
115116
u16 cid)
116117
{
117118
struct l2cap_chan *c;
118119

119120
mutex_lock(&conn->chan_lock);
120121
c = __l2cap_get_chan_by_scid(conn, cid);
121-
if (c)
122-
l2cap_chan_lock(c);
122+
if (c) {
123+
/* Only lock if chan reference is not 0 */
124+
c = l2cap_chan_hold_unless_zero(c);
125+
if (c)
126+
l2cap_chan_lock(c);
127+
}
123128
mutex_unlock(&conn->chan_lock);
124129

125130
return c;
126131
}
127132

128133
/* Find channel with given DCID.
129-
* Returns locked channel.
134+
* Returns a reference locked channel.
130135
*/
131136
static struct l2cap_chan *l2cap_get_chan_by_dcid(struct l2cap_conn *conn,
132137
u16 cid)
@@ -135,8 +140,12 @@ static struct l2cap_chan *l2cap_get_chan_by_dcid(struct l2cap_conn *conn,
135140

136141
mutex_lock(&conn->chan_lock);
137142
c = __l2cap_get_chan_by_dcid(conn, cid);
138-
if (c)
139-
l2cap_chan_lock(c);
143+
if (c) {
144+
/* Only lock if chan reference is not 0 */
145+
c = l2cap_chan_hold_unless_zero(c);
146+
if (c)
147+
l2cap_chan_lock(c);
148+
}
140149
mutex_unlock(&conn->chan_lock);
141150

142151
return c;
@@ -161,8 +170,12 @@ static struct l2cap_chan *l2cap_get_chan_by_ident(struct l2cap_conn *conn,
161170

162171
mutex_lock(&conn->chan_lock);
163172
c = __l2cap_get_chan_by_ident(conn, ident);
164-
if (c)
165-
l2cap_chan_lock(c);
173+
if (c) {
174+
/* Only lock if chan reference is not 0 */
175+
c = l2cap_chan_hold_unless_zero(c);
176+
if (c)
177+
l2cap_chan_lock(c);
178+
}
166179
mutex_unlock(&conn->chan_lock);
167180

168181
return c;
@@ -496,6 +509,16 @@ void l2cap_chan_hold(struct l2cap_chan *c)
496509
kref_get(&c->kref);
497510
}
498511

512+
struct l2cap_chan *l2cap_chan_hold_unless_zero(struct l2cap_chan *c)
513+
{
514+
BT_DBG("chan %p orig refcnt %u", c, kref_read(&c->kref));
515+
516+
if (!kref_get_unless_zero(&c->kref))
517+
return NULL;
518+
519+
return c;
520+
}
521+
499522
void l2cap_chan_put(struct l2cap_chan *c)
500523
{
501524
BT_DBG("chan %p orig refcnt %d", c, kref_read(&c->kref));
@@ -1812,7 +1835,10 @@ static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm,
18121835
src_match = !bacmp(&c->src, src);
18131836
dst_match = !bacmp(&c->dst, dst);
18141837
if (src_match && dst_match) {
1815-
l2cap_chan_hold(c);
1838+
c = l2cap_chan_hold_unless_zero(c);
1839+
if (!c)
1840+
continue;
1841+
18161842
read_unlock(&chan_list_lock);
18171843
return c;
18181844
}
@@ -1827,7 +1853,7 @@ static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm,
18271853
}
18281854

18291855
if (c1)
1830-
l2cap_chan_hold(c1);
1856+
c1 = l2cap_chan_hold_unless_zero(c1);
18311857

18321858
read_unlock(&chan_list_lock);
18331859

@@ -4221,6 +4247,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn,
42214247

42224248
unlock:
42234249
l2cap_chan_unlock(chan);
4250+
l2cap_chan_put(chan);
42244251
return err;
42254252
}
42264253

@@ -4334,6 +4361,7 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn,
43344361

43354362
done:
43364363
l2cap_chan_unlock(chan);
4364+
l2cap_chan_put(chan);
43374365
return err;
43384366
}
43394367

@@ -5062,6 +5090,7 @@ static inline int l2cap_move_channel_req(struct l2cap_conn *conn,
50625090
l2cap_send_move_chan_rsp(chan, result);
50635091

50645092
l2cap_chan_unlock(chan);
5093+
l2cap_chan_put(chan);
50655094

50665095
return 0;
50675096
}
@@ -5154,6 +5183,7 @@ static void l2cap_move_continue(struct l2cap_conn *conn, u16 icid, u16 result)
51545183
}
51555184

51565185
l2cap_chan_unlock(chan);
5186+
l2cap_chan_put(chan);
51575187
}
51585188

51595189
static void l2cap_move_fail(struct l2cap_conn *conn, u8 ident, u16 icid,
@@ -5183,6 +5213,7 @@ static void l2cap_move_fail(struct l2cap_conn *conn, u8 ident, u16 icid,
51835213
l2cap_send_move_chan_cfm(chan, L2CAP_MC_UNCONFIRMED);
51845214

51855215
l2cap_chan_unlock(chan);
5216+
l2cap_chan_put(chan);
51865217
}
51875218

51885219
static int l2cap_move_channel_rsp(struct l2cap_conn *conn,
@@ -5246,6 +5277,7 @@ static int l2cap_move_channel_confirm(struct l2cap_conn *conn,
52465277
l2cap_send_move_chan_cfm_rsp(conn, cmd->ident, icid);
52475278

52485279
l2cap_chan_unlock(chan);
5280+
l2cap_chan_put(chan);
52495281

52505282
return 0;
52515283
}
@@ -5281,6 +5313,7 @@ static inline int l2cap_move_channel_confirm_rsp(struct l2cap_conn *conn,
52815313
}
52825314

52835315
l2cap_chan_unlock(chan);
5316+
l2cap_chan_put(chan);
52845317

52855318
return 0;
52865319
}
@@ -5653,12 +5686,11 @@ static inline int l2cap_le_credits(struct l2cap_conn *conn,
56535686
if (credits > max_credits) {
56545687
BT_ERR("LE credits overflow");
56555688
l2cap_send_disconn_req(chan, ECONNRESET);
5656-
l2cap_chan_unlock(chan);
56575689

56585690
/* Return 0 so that we don't trigger an unnecessary
56595691
* command reject packet.
56605692
*/
5661-
return 0;
5693+
goto unlock;
56625694
}
56635695

56645696
chan->tx_credits += credits;
@@ -5669,7 +5701,9 @@ static inline int l2cap_le_credits(struct l2cap_conn *conn,
56695701
if (chan->tx_credits)
56705702
chan->ops->resume(chan);
56715703

5704+
unlock:
56725705
l2cap_chan_unlock(chan);
5706+
l2cap_chan_put(chan);
56735707

56745708
return 0;
56755709
}
@@ -6983,6 +7017,7 @@ static void l2cap_data_channel(struct l2cap_conn *conn, u16 cid,
69837017

69847018
done:
69857019
l2cap_chan_unlock(chan);
7020+
l2cap_chan_put(chan);
69867021
}
69877022

69887023
static void l2cap_conless_channel(struct l2cap_conn *conn, __le16 psm,
@@ -7386,7 +7421,7 @@ static struct l2cap_chan *l2cap_global_fixed_chan(struct l2cap_chan *c,
73867421
if (src_type != c->src_type)
73877422
continue;
73887423

7389-
l2cap_chan_hold(c);
7424+
c = l2cap_chan_hold_unless_zero(c);
73907425
read_unlock(&chan_list_lock);
73917426
return c;
73927427
}

0 commit comments

Comments
 (0)