mirror of
https://gitlab.com/redhat/centos-stream/src/kernel/centos-stream-10.git
synced 2026-09-09 00:07:04 +08:00
Merge: CVE-2026-64557: Bluetooth: L2CAP: Fix use-after-free in l2cap_sock_new_connection_cb()
MR: https://gitlab.com/redhat/centos-stream/src/kernel/centos-stream-10/-/merge_requests/3224 JIRA: https://redhat.atlassian.net/browse/RHEL-231358 CVE: CVE-2026-64557 * 9707a015fe8f3ba8ec7c270f3b2b8efb38823d6b Bluetooth: 6lowpan: fix cyclic locking warning on netdev unregister [linux] * 6fef032af0092ed5ccb767239a9ac1bc38c08a40 Bluetooth: L2CAP: Fix use-after-free in l2cap_sock_new_connection_cb() [linux] Signed-off-by: CKI Backport Bot <cki-ci-bot+cki-gitlab-backport-bot@redhat.com> [^footer]: Created 2026-08-10 19:08 UTC by backporter - [KWF FAQ](https://red.ht/kernel_workflow_doc) - [Slack #team-kernel-workflow](https://redhat-internal.slack.com/archives/C04LRUPMJQ5) - [Source](https://gitlab.com/cki-project/kernel-workflow/-/blob/main/webhook/utils/backporter.py) - [Documentation](https://gitlab.com/cki-project/kernel-workflow/-/blob/main/docs/README.backporter.md) - [Report an issue](https://redhat.atlassian.net/secure/CreateIssueDetails!init.jspa?pid=11779&issuetype=10016&priority=10001&summary=backporter+webhook+issue&components=66291) [^footer] Approved-by: David Marlin <dmarlin@redhat.com> Approved-by: Charles Mirabile <cmirabil@redhat.com> Approved-by: Tony Camuso <tcamuso@redhat.com> Approved-by: CKI KWF Bot <cki-ci-bot+kwf-gitlab-com@redhat.com> Merged-by: CKI GitLab Kmaint Pipeline Bot <26919896-cki-kmaint-pipeline-bot@users.noreply.gitlab.com>
This commit is contained in:
@@ -620,7 +620,8 @@ struct l2cap_chan {
|
||||
struct l2cap_ops {
|
||||
char *name;
|
||||
|
||||
struct l2cap_chan *(*new_connection) (struct l2cap_chan *chan);
|
||||
int (*new_connection)(struct l2cap_chan *chan,
|
||||
struct l2cap_chan *new_chan);
|
||||
int (*recv) (struct l2cap_chan * chan,
|
||||
struct sk_buff *skb);
|
||||
void (*teardown) (struct l2cap_chan *chan, int err);
|
||||
@@ -885,9 +886,10 @@ static inline __u16 __next_seq(struct l2cap_chan *chan, __u16 seq)
|
||||
return (seq + 1) % (chan->tx_win_max + 1);
|
||||
}
|
||||
|
||||
static inline struct l2cap_chan *l2cap_chan_no_new_connection(struct l2cap_chan *chan)
|
||||
static inline int l2cap_chan_no_new_connection(struct l2cap_chan *chan,
|
||||
struct l2cap_chan *new_chan)
|
||||
{
|
||||
return NULL;
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline int l2cap_chan_no_recv(struct l2cap_chan *chan, struct sk_buff *skb)
|
||||
@@ -964,7 +966,7 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
|
||||
void l2cap_chan_busy(struct l2cap_chan *chan, int busy);
|
||||
void l2cap_chan_rx_avail(struct l2cap_chan *chan, ssize_t rx_avail);
|
||||
int l2cap_chan_check_security(struct l2cap_chan *chan, bool initiator);
|
||||
void l2cap_chan_set_defaults(struct l2cap_chan *chan);
|
||||
void l2cap_chan_set_defaults(struct l2cap_chan *chan, struct l2cap_chan *pchan);
|
||||
int l2cap_ertm_init(struct l2cap_chan *chan);
|
||||
void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan);
|
||||
void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan);
|
||||
|
||||
+20
-15
@@ -632,7 +632,7 @@ static struct l2cap_chan *chan_create(void)
|
||||
if (!chan)
|
||||
return NULL;
|
||||
|
||||
l2cap_chan_set_defaults(chan);
|
||||
l2cap_chan_set_defaults(chan, NULL);
|
||||
|
||||
chan->chan_type = L2CAP_CHAN_CONN_ORIENTED;
|
||||
chan->mode = L2CAP_MODE_LE_FLOWCTL;
|
||||
@@ -745,19 +745,24 @@ static inline void chan_ready_cb(struct l2cap_chan *chan)
|
||||
ifup(dev->netdev);
|
||||
}
|
||||
|
||||
static inline struct l2cap_chan *chan_new_conn_cb(struct l2cap_chan *pchan)
|
||||
static void unregister_dev(struct lowpan_btle_dev *dev)
|
||||
{
|
||||
struct l2cap_chan *chan;
|
||||
struct hci_dev *hdev = READ_ONCE(dev->hdev);
|
||||
|
||||
chan = chan_create();
|
||||
if (!chan)
|
||||
return NULL;
|
||||
|
||||
chan->ops = pchan->ops;
|
||||
|
||||
BT_DBG("chan %p pchan %p", chan, pchan);
|
||||
|
||||
return chan;
|
||||
/* If netdev holds last reference to hci_dev (its parent device), this
|
||||
* leads to theoretical cyclic locking on lowpan_unregister_netdev:
|
||||
*
|
||||
* rtnl_lock -> put_device(parent) -> hci_release_dev ->
|
||||
* destroy_workqueue -> hci_rx_work -> l2cap_recv_acldata ->
|
||||
* chan_ready_cb -> ifup -> rtnl_lock
|
||||
*
|
||||
* However, hci_rx_work is disabled in hci_unregister_dev, so this
|
||||
* should not occur. Make lockdep happy by postponing hdev release after
|
||||
* netdev put.
|
||||
*/
|
||||
hci_dev_hold(hdev);
|
||||
lowpan_unregister_netdev(dev->netdev);
|
||||
hci_dev_put(hdev);
|
||||
}
|
||||
|
||||
static void delete_netdev(struct work_struct *work)
|
||||
@@ -766,7 +771,7 @@ static void delete_netdev(struct work_struct *work)
|
||||
struct lowpan_btle_dev,
|
||||
delete_netdev);
|
||||
|
||||
lowpan_unregister_netdev(entry->netdev);
|
||||
unregister_dev(entry);
|
||||
|
||||
/* The entry pointer is deleted by the netdev destructor. */
|
||||
}
|
||||
@@ -881,7 +886,6 @@ static long chan_get_sndtimeo_cb(struct l2cap_chan *chan)
|
||||
|
||||
static const struct l2cap_ops bt_6lowpan_chan_ops = {
|
||||
.name = "L2CAP 6LoWPAN channel",
|
||||
.new_connection = chan_new_conn_cb,
|
||||
.recv = chan_recv_cb,
|
||||
.close = chan_close_cb,
|
||||
.state_change = chan_state_change_cb,
|
||||
@@ -1252,6 +1256,7 @@ static void disconnect_devices(void)
|
||||
break;
|
||||
|
||||
new_dev->netdev = entry->netdev;
|
||||
new_dev->hdev = entry->hdev;
|
||||
INIT_LIST_HEAD(&new_dev->list);
|
||||
|
||||
list_add_rcu(&new_dev->list, &devices);
|
||||
@@ -1263,7 +1268,7 @@ static void disconnect_devices(void)
|
||||
ifdown(entry->netdev);
|
||||
BT_DBG("Unregistering netdev %s %p",
|
||||
entry->netdev->name, entry->netdev);
|
||||
lowpan_unregister_netdev(entry->netdev);
|
||||
unregister_dev(entry);
|
||||
kfree(entry);
|
||||
}
|
||||
}
|
||||
|
||||
+65
-13
@@ -525,7 +525,10 @@ void l2cap_chan_put(struct l2cap_chan *c)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(l2cap_chan_put);
|
||||
|
||||
void l2cap_chan_set_defaults(struct l2cap_chan *chan)
|
||||
/* Initialise @chan with default values, inheriting from the parent channel
|
||||
* @pchan when it is given.
|
||||
*/
|
||||
void l2cap_chan_set_defaults(struct l2cap_chan *chan, struct l2cap_chan *pchan)
|
||||
{
|
||||
chan->fcs = L2CAP_FCS_CRC16;
|
||||
chan->max_tx = L2CAP_DEFAULT_MAX_TX;
|
||||
@@ -539,6 +542,31 @@ void l2cap_chan_set_defaults(struct l2cap_chan *chan)
|
||||
chan->retrans_timeout = L2CAP_DEFAULT_RETRANS_TO;
|
||||
chan->monitor_timeout = L2CAP_DEFAULT_MONITOR_TO;
|
||||
|
||||
if (pchan) {
|
||||
BT_DBG("chan %p pchan %p", chan, pchan);
|
||||
|
||||
chan->chan_type = pchan->chan_type;
|
||||
chan->imtu = pchan->imtu;
|
||||
chan->omtu = pchan->omtu;
|
||||
chan->mode = pchan->mode;
|
||||
chan->fcs = pchan->fcs;
|
||||
chan->max_tx = pchan->max_tx;
|
||||
chan->tx_win = pchan->tx_win;
|
||||
chan->tx_win_max = pchan->tx_win_max;
|
||||
chan->sec_level = pchan->sec_level;
|
||||
chan->conf_state = pchan->conf_state;
|
||||
chan->flags = pchan->flags;
|
||||
chan->tx_credits = pchan->tx_credits;
|
||||
chan->rx_credits = pchan->rx_credits;
|
||||
|
||||
if (chan->chan_type == L2CAP_CHAN_FIXED) {
|
||||
chan->scid = pchan->scid;
|
||||
chan->dcid = pchan->scid;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
chan->conf_state = 0;
|
||||
set_bit(CONF_NOT_COMPLETE, &chan->conf_state);
|
||||
|
||||
@@ -4013,6 +4041,38 @@ static inline int l2cap_command_rej(struct l2cap_conn *conn,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Allocate and initialise a channel for an incoming connection.
|
||||
*
|
||||
* The channel inherits its configuration from @pchan and is linked into @conn
|
||||
* before ->new_connection() runs, so the conn list reference keeps it alive if
|
||||
* the callback exposes it (e.g. via the socket accept queue) before this
|
||||
* returns. The l2cap_chan_create() reference is taken over by the subsystem on
|
||||
* success and dropped here on failure.
|
||||
*/
|
||||
static struct l2cap_chan *l2cap_new_connection(struct l2cap_conn *conn,
|
||||
struct l2cap_chan *pchan)
|
||||
{
|
||||
struct l2cap_chan *chan;
|
||||
|
||||
chan = l2cap_chan_create();
|
||||
if (!chan)
|
||||
return NULL;
|
||||
|
||||
l2cap_chan_set_defaults(chan, pchan);
|
||||
chan->ops = pchan->ops;
|
||||
|
||||
__l2cap_chan_add(conn, chan);
|
||||
|
||||
if (pchan->ops->new_connection &&
|
||||
pchan->ops->new_connection(pchan, chan) < 0) {
|
||||
l2cap_chan_del(chan, 0);
|
||||
l2cap_chan_put(chan);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return chan;
|
||||
}
|
||||
|
||||
static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd,
|
||||
u8 *data, u8 rsp_code)
|
||||
{
|
||||
@@ -4059,7 +4119,7 @@ static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd,
|
||||
goto response;
|
||||
}
|
||||
|
||||
chan = pchan->ops->new_connection(pchan);
|
||||
chan = l2cap_new_connection(conn, pchan);
|
||||
if (!chan)
|
||||
goto response;
|
||||
|
||||
@@ -4077,8 +4137,6 @@ static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd,
|
||||
chan->psm = psm;
|
||||
chan->dcid = scid;
|
||||
|
||||
__l2cap_chan_add(conn, chan);
|
||||
|
||||
dcid = chan->scid;
|
||||
|
||||
__set_chan_timer(chan, chan->ops->get_sndtimeo(chan));
|
||||
@@ -4961,7 +5019,7 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn,
|
||||
goto response_unlock;
|
||||
}
|
||||
|
||||
chan = pchan->ops->new_connection(pchan);
|
||||
chan = l2cap_new_connection(conn, pchan);
|
||||
if (!chan) {
|
||||
result = L2CAP_CR_LE_NO_MEM;
|
||||
goto response_unlock;
|
||||
@@ -4976,8 +5034,6 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn,
|
||||
chan->omtu = mtu;
|
||||
chan->remote_mps = mps;
|
||||
|
||||
__l2cap_chan_add(conn, chan);
|
||||
|
||||
l2cap_le_flowctl_init(chan, __le16_to_cpu(req->credits));
|
||||
|
||||
dcid = chan->scid;
|
||||
@@ -5185,7 +5241,7 @@ static inline int l2cap_ecred_conn_req(struct l2cap_conn *conn,
|
||||
continue;
|
||||
}
|
||||
|
||||
chan = pchan->ops->new_connection(pchan);
|
||||
chan = l2cap_new_connection(conn, pchan);
|
||||
if (!chan) {
|
||||
result = L2CAP_CR_LE_NO_MEM;
|
||||
continue;
|
||||
@@ -5200,8 +5256,6 @@ static inline int l2cap_ecred_conn_req(struct l2cap_conn *conn,
|
||||
chan->omtu = mtu;
|
||||
chan->remote_mps = mps;
|
||||
|
||||
__l2cap_chan_add(conn, chan);
|
||||
|
||||
l2cap_ecred_init(chan, __le16_to_cpu(req->credits));
|
||||
|
||||
/* Init response */
|
||||
@@ -7476,14 +7530,12 @@ static void l2cap_connect_cfm(struct hci_conn *hcon, u8 status)
|
||||
goto next;
|
||||
|
||||
l2cap_chan_lock(pchan);
|
||||
chan = pchan->ops->new_connection(pchan);
|
||||
chan = l2cap_new_connection(conn, pchan);
|
||||
if (chan) {
|
||||
bacpy(&chan->src, &hcon->src);
|
||||
bacpy(&chan->dst, &hcon->dst);
|
||||
chan->src_type = bdaddr_src_type(hcon);
|
||||
chan->dst_type = dst_type;
|
||||
|
||||
__l2cap_chan_add(conn, chan);
|
||||
}
|
||||
|
||||
l2cap_chan_unlock(pchan);
|
||||
|
||||
+49
-54
@@ -45,7 +45,8 @@ static struct bt_sock_list l2cap_sk_list = {
|
||||
static const struct proto_ops l2cap_sock_ops;
|
||||
static void l2cap_sock_init(struct sock *sk, struct sock *parent);
|
||||
static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock,
|
||||
int proto, gfp_t prio, int kern);
|
||||
int proto, gfp_t prio, int kern,
|
||||
struct l2cap_chan *chan);
|
||||
static void l2cap_sock_cleanup_listen(struct sock *parent);
|
||||
|
||||
bool l2cap_is_socket(struct socket *sock)
|
||||
@@ -1274,6 +1275,23 @@ done:
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Release the sock's ref on chan and clear the pointer so that the ref is
|
||||
* dropped exactly once even if both l2cap_sock_kill() and
|
||||
* l2cap_sock_destruct() run. Setting chan->data to NULL first stops any other
|
||||
* task from dereferencing the now-dead sock pointer.
|
||||
*/
|
||||
static void l2cap_sock_put_chan(struct sock *sk)
|
||||
{
|
||||
struct l2cap_chan *chan = l2cap_pi(sk)->chan;
|
||||
|
||||
if (!chan)
|
||||
return;
|
||||
|
||||
chan->data = NULL;
|
||||
l2cap_pi(sk)->chan = NULL;
|
||||
l2cap_chan_put(chan);
|
||||
}
|
||||
|
||||
/* Kill socket (only if zapped and orphan)
|
||||
* Must be called on unlocked socket, with l2cap channel lock.
|
||||
*/
|
||||
@@ -1284,13 +1302,9 @@ static void l2cap_sock_kill(struct sock *sk)
|
||||
|
||||
BT_DBG("sk %p state %s", sk, state_to_string(sk->sk_state));
|
||||
|
||||
/* Sock is dead, so set chan data to NULL, avoid other task use invalid
|
||||
* sock pointer.
|
||||
*/
|
||||
l2cap_pi(sk)->chan->data = NULL;
|
||||
/* Kill poor orphan */
|
||||
l2cap_sock_put_chan(sk);
|
||||
|
||||
l2cap_chan_put(l2cap_pi(sk)->chan);
|
||||
/* Kill poor orphan */
|
||||
sock_set_flag(sk, SOCK_DEAD);
|
||||
sock_put(sk);
|
||||
}
|
||||
@@ -1533,12 +1547,13 @@ static void l2cap_sock_cleanup_listen(struct sock *parent)
|
||||
}
|
||||
}
|
||||
|
||||
static struct l2cap_chan *l2cap_sock_new_connection_cb(struct l2cap_chan *chan)
|
||||
static int l2cap_sock_new_connection_cb(struct l2cap_chan *chan,
|
||||
struct l2cap_chan *new_chan)
|
||||
{
|
||||
struct sock *sk, *parent = chan->data;
|
||||
|
||||
if (!parent)
|
||||
return NULL;
|
||||
return -EINVAL;
|
||||
|
||||
lock_sock(parent);
|
||||
|
||||
@@ -1546,25 +1561,28 @@ static struct l2cap_chan *l2cap_sock_new_connection_cb(struct l2cap_chan *chan)
|
||||
if (sk_acceptq_is_full(parent)) {
|
||||
BT_DBG("backlog full %d", parent->sk_ack_backlog);
|
||||
release_sock(parent);
|
||||
return NULL;
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
sk = l2cap_sock_alloc(sock_net(parent), NULL, BTPROTO_L2CAP,
|
||||
GFP_ATOMIC, 0);
|
||||
GFP_ATOMIC, 0, new_chan);
|
||||
if (!sk) {
|
||||
release_sock(parent);
|
||||
return NULL;
|
||||
}
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
bt_sock_reclassify_lock(sk, BTPROTO_L2CAP);
|
||||
|
||||
l2cap_sock_init(sk, parent);
|
||||
|
||||
/* The conn list reference taken by l2cap_new_connection() keeps new_chan
|
||||
* alive once release_sock() lets another task free this socket.
|
||||
*/
|
||||
bt_accept_enqueue(parent, sk, false);
|
||||
|
||||
release_sock(parent);
|
||||
|
||||
return l2cap_pi(sk)->chan;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l2cap_sock_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb)
|
||||
@@ -1862,10 +1880,7 @@ static void l2cap_sock_destruct(struct sock *sk)
|
||||
|
||||
BT_DBG("sk %p", sk);
|
||||
|
||||
if (l2cap_pi(sk)->chan) {
|
||||
l2cap_pi(sk)->chan->data = NULL;
|
||||
l2cap_chan_put(l2cap_pi(sk)->chan);
|
||||
}
|
||||
l2cap_sock_put_chan(sk);
|
||||
|
||||
list_for_each_entry_safe(rx_busy, next, &l2cap_pi(sk)->rx_busy, list) {
|
||||
kfree_skb(rx_busy->skb);
|
||||
@@ -1898,30 +1913,12 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent)
|
||||
BT_DBG("sk %p", sk);
|
||||
|
||||
if (parent) {
|
||||
struct l2cap_chan *pchan = l2cap_pi(parent)->chan;
|
||||
|
||||
sk->sk_type = parent->sk_type;
|
||||
bt_sk(sk)->flags = bt_sk(parent)->flags;
|
||||
|
||||
chan->chan_type = pchan->chan_type;
|
||||
chan->imtu = pchan->imtu;
|
||||
chan->omtu = pchan->omtu;
|
||||
chan->conf_state = pchan->conf_state;
|
||||
chan->mode = pchan->mode;
|
||||
chan->fcs = pchan->fcs;
|
||||
chan->max_tx = pchan->max_tx;
|
||||
chan->tx_win = pchan->tx_win;
|
||||
chan->tx_win_max = pchan->tx_win_max;
|
||||
chan->sec_level = pchan->sec_level;
|
||||
chan->flags = pchan->flags;
|
||||
chan->tx_credits = pchan->tx_credits;
|
||||
chan->rx_credits = pchan->rx_credits;
|
||||
|
||||
if (chan->chan_type == L2CAP_CHAN_FIXED) {
|
||||
chan->scid = pchan->scid;
|
||||
chan->dcid = pchan->scid;
|
||||
}
|
||||
|
||||
/* Channel configuration is inherited from the parent by
|
||||
* l2cap_new_connection().
|
||||
*/
|
||||
security_sk_clone(parent, sk);
|
||||
} else {
|
||||
switch (sk->sk_type) {
|
||||
@@ -1947,7 +1944,7 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent)
|
||||
chan->mode = L2CAP_MODE_BASIC;
|
||||
}
|
||||
|
||||
l2cap_chan_set_defaults(chan);
|
||||
l2cap_chan_set_defaults(chan, NULL);
|
||||
}
|
||||
|
||||
/* Default config options */
|
||||
@@ -1966,10 +1963,10 @@ static struct proto l2cap_proto = {
|
||||
};
|
||||
|
||||
static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock,
|
||||
int proto, gfp_t prio, int kern)
|
||||
int proto, gfp_t prio, int kern,
|
||||
struct l2cap_chan *chan)
|
||||
{
|
||||
struct sock *sk;
|
||||
struct l2cap_chan *chan;
|
||||
|
||||
sk = bt_sock_alloc(net, sock, &l2cap_proto, proto, prio, kern);
|
||||
if (!sk)
|
||||
@@ -1980,16 +1977,7 @@ static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock,
|
||||
|
||||
INIT_LIST_HEAD(&l2cap_pi(sk)->rx_busy);
|
||||
|
||||
chan = l2cap_chan_create();
|
||||
if (!chan) {
|
||||
sk_free(sk);
|
||||
if (sock)
|
||||
sock->sk = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
l2cap_chan_hold(chan);
|
||||
|
||||
/* The sock takes ownership of the caller's reference on chan. */
|
||||
l2cap_pi(sk)->chan = chan;
|
||||
|
||||
return sk;
|
||||
@@ -1999,6 +1987,7 @@ static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol,
|
||||
int kern)
|
||||
{
|
||||
struct sock *sk;
|
||||
struct l2cap_chan *chan;
|
||||
|
||||
BT_DBG("sock %p", sock);
|
||||
|
||||
@@ -2013,10 +2002,16 @@ static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol,
|
||||
|
||||
sock->ops = &l2cap_sock_ops;
|
||||
|
||||
sk = l2cap_sock_alloc(net, sock, protocol, GFP_ATOMIC, kern);
|
||||
if (!sk)
|
||||
chan = l2cap_chan_create();
|
||||
if (!chan)
|
||||
return -ENOMEM;
|
||||
|
||||
sk = l2cap_sock_alloc(net, sock, protocol, GFP_ATOMIC, kern, chan);
|
||||
if (!sk) {
|
||||
l2cap_chan_put(chan);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
l2cap_sock_init(sk, NULL);
|
||||
bt_sock_link(&l2cap_sk_list, sk);
|
||||
return 0;
|
||||
|
||||
+6
-21
@@ -3234,34 +3234,19 @@ static const struct l2cap_ops smp_chan_ops = {
|
||||
.get_sndtimeo = l2cap_chan_no_get_sndtimeo,
|
||||
};
|
||||
|
||||
static inline struct l2cap_chan *smp_new_conn_cb(struct l2cap_chan *pchan)
|
||||
static inline int smp_new_conn_cb(struct l2cap_chan *chan,
|
||||
struct l2cap_chan *new_chan)
|
||||
{
|
||||
struct l2cap_chan *chan;
|
||||
|
||||
BT_DBG("pchan %p", pchan);
|
||||
|
||||
chan = l2cap_chan_create();
|
||||
if (!chan)
|
||||
return NULL;
|
||||
|
||||
chan->chan_type = pchan->chan_type;
|
||||
chan->ops = &smp_chan_ops;
|
||||
chan->scid = pchan->scid;
|
||||
chan->dcid = chan->scid;
|
||||
chan->imtu = pchan->imtu;
|
||||
chan->omtu = pchan->omtu;
|
||||
chan->mode = pchan->mode;
|
||||
new_chan->ops = &smp_chan_ops;
|
||||
|
||||
/* Other L2CAP channels may request SMP routines in order to
|
||||
* change the security level. This means that the SMP channel
|
||||
* lock must be considered in its own category to avoid lockdep
|
||||
* warnings.
|
||||
*/
|
||||
atomic_set(&chan->nesting, L2CAP_NESTING_SMP);
|
||||
atomic_set(&new_chan->nesting, L2CAP_NESTING_SMP);
|
||||
|
||||
BT_DBG("created chan %p", chan);
|
||||
|
||||
return chan;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct l2cap_ops smp_root_chan_ops = {
|
||||
@@ -3332,7 +3317,7 @@ create_chan:
|
||||
|
||||
l2cap_add_scid(chan, cid);
|
||||
|
||||
l2cap_chan_set_defaults(chan);
|
||||
l2cap_chan_set_defaults(chan, NULL);
|
||||
|
||||
if (cid == L2CAP_CID_SMP) {
|
||||
u8 bdaddr_type;
|
||||
|
||||
Reference in New Issue
Block a user