From f9be9e8661323fd259a5cc1bec67e3d6bb43ec41 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 6 Dec 2014 00:35:45 +0100 Subject: Bluetooth: Check for force_lesc_support when enabling SMP over BR/EDR The SMP over BR/EDR support for cross-transport pairing should also be enabled when the debugfs setting force_lesc_support has been enabled. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/l2cap_core.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index a8da7ea9c2c0..cc518eb48dc8 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -6967,8 +6967,9 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon) test_bit(HCI_HS_ENABLED, &hcon->hdev->dev_flags)) conn->local_fixed_chan |= L2CAP_FC_A2MP; - if (bredr_sc_enabled(hcon->hdev) && - test_bit(HCI_LE_ENABLED, &hcon->hdev->dev_flags)) + if (test_bit(HCI_LE_ENABLED, &hcon->hdev->dev_flags) && + (bredr_sc_enabled(hcon->hdev) || + test_bit(HCI_FORCE_LESC, &hcon->hdev->dbg_flags))) conn->local_fixed_chan |= L2CAP_FC_SMP_BREDR; mutex_init(&conn->ident_lock); -- cgit v1.2.3 From 08f63cc5026f20b9124828619c9025738ecb5a5e Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 7 Dec 2014 16:19:12 +0100 Subject: Bluetooth: Check for force_lesc_support before rejecting SMP over BR/EDR The SMP over BR/EDR requests for cross-transport pairing should also accepted when the debugfs setting force_lesc_support has been enabled. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/smp.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 96bf16dcd9e9..3e174f94a46b 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -1674,7 +1674,8 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) /* SMP over BR/EDR requires special treatment */ if (conn->hcon->type == ACL_LINK) { /* We must have a BR/EDR SC link */ - if (!test_bit(HCI_CONN_AES_CCM, &conn->hcon->flags)) + if (!test_bit(HCI_CONN_AES_CCM, &conn->hcon->flags) && + !test_bit(HCI_FORCE_LESC, &hdev->dbg_flags)) return SMP_CROSS_TRANSP_NOT_ALLOWED; set_bit(SMP_FLAG_SC, &smp->flags); -- cgit v1.2.3 From 9437d2edc39ed8f7e40536281ee53ec3fd5a14ca Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 7 Dec 2014 20:13:17 +0100 Subject: Bluetooth: Fix generation of non-resolvable private addresses When the host decides to use a non-resolvable private address, it must ensure that this generated address does not match the public address of the controller. Add an extra check to ensure this required behavior. In addition rename the variable from urpa to nrpa and fix all of the comments in the code that use the term unresolvable instead of the term non-resolvable as used in the Bluetooth specification. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/hci_conn.c | 2 +- net/bluetooth/hci_core.c | 24 ++++++++++++++++++------ net/bluetooth/mgmt.c | 2 +- 3 files changed, 20 insertions(+), 8 deletions(-) (limited to 'net') diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 79d84b88b8f0..fe18825cc8a4 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -661,7 +661,7 @@ static void hci_req_add_le_create_conn(struct hci_request *req, memset(&cp, 0, sizeof(cp)); /* Update random address, but set require_privacy to false so - * that we never connect with an unresolvable address. + * that we never connect with an non-resolvable address. */ if (hci_update_random_address(req, false, &own_addr_type)) return; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 93f92a085506..96e7321c57a4 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3959,17 +3959,29 @@ int hci_update_random_address(struct hci_request *req, bool require_privacy, } /* In case of required privacy without resolvable private address, - * use an unresolvable private address. This is useful for active + * use an non-resolvable private address. This is useful for active * scanning and non-connectable advertising. */ if (require_privacy) { - bdaddr_t urpa; + bdaddr_t nrpa; - get_random_bytes(&urpa, 6); - urpa.b[5] &= 0x3f; /* Clear two most significant bits */ + while (true) { + /* The non-resolvable private address is generated + * from random six bytes with the two most significant + * bits cleared. + */ + get_random_bytes(&nrpa, 6); + nrpa.b[5] &= 0x3f; + + /* The non-resolvable private address shall not be + * equal to the public address. + */ + if (bacmp(&hdev->bdaddr, &nrpa)) + break; + } *own_addr_type = ADDR_LE_DEV_RANDOM; - set_random_addr(req, &urpa); + set_random_addr(req, &nrpa); return 0; } @@ -5625,7 +5637,7 @@ void hci_req_add_le_passive_scan(struct hci_request *req) u8 filter_policy; /* Set require_privacy to false since no SCAN_REQ are send - * during passive scanning. Not using an unresolvable address + * during passive scanning. Not using an non-resolvable address * here is important so that peer devices using direct * advertising with our address will be correctly reported * by the controller. diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index a91e484886fe..44b20deb6038 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -3791,7 +3791,7 @@ static bool trigger_discovery(struct hci_request *req, u8 *status) /* All active scans will be done with either a resolvable * private address (when privacy feature has been enabled) - * or unresolvable private address. + * or non-resolvable private address. */ err = hci_update_random_address(req, true, &own_addr_type); if (err < 0) { -- cgit v1.2.3 From fe7007719742e4c0c1255e2f45bdd4ed97afc369 Mon Sep 17 00:00:00 2001 From: Fengguang Wu Date: Mon, 8 Dec 2014 03:04:38 +0800 Subject: Bluetooth: fix err_cast.cocci warnings net/bluetooth/smp.c:2650:9-16: WARNING: ERR_CAST can be used with tfm_aes Use ERR_CAST inlined function instead of ERR_PTR(PTR_ERR(...)) Generated by: scripts/coccinelle/api/err_cast.cocci Signed-off-by: Fengguang Wu Signed-off-by: Marcel Holtmann --- net/bluetooth/smp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 3e174f94a46b..04489856ca68 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -2931,7 +2931,7 @@ static struct l2cap_chan *smp_add_cid(struct hci_dev *hdev, u16 cid) tfm_aes = crypto_alloc_blkcipher("ecb(aes)", 0, 0); if (IS_ERR(tfm_aes)) { BT_ERR("Unable to create crypto context"); - return ERR_PTR(PTR_ERR(tfm_aes)); + return ERR_CAST(tfm_aes); } create_chan: -- cgit v1.2.3 From 3ad675827f4a3623b7fc51ffe8fdb0347b3cbc53 Mon Sep 17 00:00:00 2001 From: Jaganath Kanakkassery Date: Thu, 11 Dec 2014 11:43:12 +0530 Subject: Bluetooth: Fix missing hci_dev_lock/unlock in mgmt req_complete() mgmt_pending_remove() should be called with hci_dev_lock protection and currently the rule to take dev lock is that all mgmt req_complete functions should take dev lock. So this patch fixes the same in the missing functions Without this patch there is a chance of invalid memory access while accessing the mgmt_pending list like below bluetoothd: 392] [0] Backtrace: bluetoothd: 392] [0] [] (pending_eir_or_class+0x0/0x68) from [] (add_uuid+0x34/0x1c4) bluetoothd: 392] [0] [] (add_uuid+0x0/0x1c4) from [] (mgmt_control+0x204/0x274) bluetoothd: 392] [0] [] (mgmt_control+0x0/0x274) from [] (hci_sock_sendmsg+0x80/0x308) bluetoothd: 392] [0] [] (hci_sock_sendmsg+0x0/0x308) from [] (sock_aio_write+0x144/0x174) bluetoothd: 392] [0] r8:00000000 r7 7c1be90 r6 7c1be18 r5:00000017 r4 a90ea80 bluetoothd: 392] [0] [] (sock_aio_write+0x0/0x174) from [] (do_sync_write+0xb0/0xe0) bluetoothd: 392] [0] [] (do_sync_write+0x0/0xe0) from [] (vfs_write+0x134/0x13c) bluetoothd: 392] [0] r8:00000000 r7 7c1bf70 r6:beeca5c8 r5:00000017 r4 7c05900 bluetoothd: 392] [0] [] (vfs_write+0x0/0x13c) from [] (sys_write+0x44/0x70) bluetoothd: 392] [0] r8:00000000 r7:00000004 r6:00000017 r5:beeca5c8 r4 7c05900 bluetoothd: 392] [0] [] (sys_write+0x0/0x70) from [] (ret_fast_syscall+0x0/0x30) bluetoothd: 392] [0] r9 7c1a000 r8:c000e568 r6:400b5f10 r5:403896d8 r4:beeca604 bluetoothd: 392] [0] Code: e28cc00c e152000c 0a00000f e3a00001 (e1d210b8) bluetoothd: 392] [0] ---[ end trace 67b6ac67435864c4 ]--- bluetoothd: 392] [0] Kernel panic - not syncing: Fatal exception Signed-off-by: Jaganath Kanakkassery Signed-off-by: Marcel Holtmann --- net/bluetooth/hci_core.c | 2 ++ net/bluetooth/mgmt.c | 18 ++++++++++++------ 2 files changed, 14 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 96e7321c57a4..ecd7c01317bc 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3083,7 +3083,9 @@ static void hci_power_on(struct work_struct *work) err = hci_dev_do_open(hdev); if (err < 0) { + hci_dev_lock(hdev); mgmt_set_powered_failed(hdev, err); + hci_dev_unlock(hdev); return; } diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 44b20deb6038..16ac03730f4d 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2199,12 +2199,14 @@ static void le_enable_complete(struct hci_dev *hdev, u8 status) { struct cmd_lookup match = { NULL, hdev }; + hci_dev_lock(hdev); + if (status) { u8 mgmt_err = mgmt_status(status); mgmt_pending_foreach(MGMT_OP_SET_LE, hdev, cmd_status_rsp, &mgmt_err); - return; + goto unlock; } mgmt_pending_foreach(MGMT_OP_SET_LE, hdev, settings_rsp, &match); @@ -2222,17 +2224,16 @@ static void le_enable_complete(struct hci_dev *hdev, u8 status) if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) { struct hci_request req; - hci_dev_lock(hdev); - hci_req_init(&req, hdev); update_adv_data(&req); update_scan_rsp_data(&req); hci_req_run(&req, NULL); hci_update_background_scan(hdev); - - hci_dev_unlock(hdev); } + +unlock: + hci_dev_unlock(hdev); } static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) @@ -4279,12 +4280,14 @@ static void set_advertising_complete(struct hci_dev *hdev, u8 status) { struct cmd_lookup match = { NULL, hdev }; + hci_dev_lock(hdev); + if (status) { u8 mgmt_err = mgmt_status(status); mgmt_pending_foreach(MGMT_OP_SET_ADVERTISING, hdev, cmd_status_rsp, &mgmt_err); - return; + goto unlock; } if (test_bit(HCI_LE_ADV, &hdev->dev_flags)) @@ -4299,6 +4302,9 @@ static void set_advertising_complete(struct hci_dev *hdev, u8 status) if (match.sk) sock_put(match.sk); + +unlock: + hci_dev_unlock(hdev); } static int set_advertising(struct sock *sk, struct hci_dev *hdev, void *data, -- cgit v1.2.3 From 5c1a4c8f28059e592da4fff40c0dc54202fb5cc6 Mon Sep 17 00:00:00 2001 From: Jaganath Kanakkassery Date: Thu, 11 Dec 2014 18:58:15 +0530 Subject: Bluetooth: Fix missing hci_dev_lock/unlock in hci_event mgmt_pending_remove() should be called with hci_dev_lock protection and all hci_event.c functions which calls mgmt_complete() (which eventually calls mgmt_pending_remove()) should hold the lock. So this patch fixes the same Signed-off-by: Jaganath Kanakkassery Signed-off-by: Marcel Holtmann --- net/bluetooth/hci_event.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'net') diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 322abbbbcef9..39a5c8a01726 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -257,6 +257,8 @@ static void hci_cc_write_auth_enable(struct hci_dev *hdev, struct sk_buff *skb) if (!sent) return; + hci_dev_lock(hdev); + if (!status) { __u8 param = *((__u8 *) sent); @@ -268,6 +270,8 @@ static void hci_cc_write_auth_enable(struct hci_dev *hdev, struct sk_buff *skb) if (test_bit(HCI_MGMT, &hdev->dev_flags)) mgmt_auth_enable_complete(hdev, status); + + hci_dev_unlock(hdev); } static void hci_cc_write_encrypt_mode(struct hci_dev *hdev, struct sk_buff *skb) @@ -443,6 +447,8 @@ static void hci_cc_write_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb) if (!sent) return; + hci_dev_lock(hdev); + if (!status) { if (sent->mode) hdev->features[1][0] |= LMP_HOST_SSP; @@ -458,6 +464,8 @@ static void hci_cc_write_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb) else clear_bit(HCI_SSP_ENABLED, &hdev->dev_flags); } + + hci_dev_unlock(hdev); } static void hci_cc_write_sc_support(struct hci_dev *hdev, struct sk_buff *skb) @@ -471,6 +479,8 @@ static void hci_cc_write_sc_support(struct hci_dev *hdev, struct sk_buff *skb) if (!sent) return; + hci_dev_lock(hdev); + if (!status) { if (sent->support) hdev->features[1][0] |= LMP_HOST_SC; @@ -486,6 +496,8 @@ static void hci_cc_write_sc_support(struct hci_dev *hdev, struct sk_buff *skb) else clear_bit(HCI_SC_ENABLED, &hdev->dev_flags); } + + hci_dev_unlock(hdev); } static void hci_cc_read_local_version(struct hci_dev *hdev, struct sk_buff *skb) @@ -1135,6 +1147,8 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev, if (!cp) return; + hci_dev_lock(hdev); + switch (cp->enable) { case LE_SCAN_ENABLE: set_bit(HCI_LE_SCAN, &hdev->dev_flags); @@ -1184,6 +1198,8 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev, BT_ERR("Used reserved LE_Scan_Enable param %d", cp->enable); break; } + + hci_dev_unlock(hdev); } static void hci_cc_le_read_white_list_size(struct hci_dev *hdev, @@ -1278,6 +1294,8 @@ static void hci_cc_write_le_host_supported(struct hci_dev *hdev, if (!sent) return; + hci_dev_lock(hdev); + if (sent->le) { hdev->features[1][0] |= LMP_HOST_LE; set_bit(HCI_LE_ENABLED, &hdev->dev_flags); @@ -1291,6 +1309,8 @@ static void hci_cc_write_le_host_supported(struct hci_dev *hdev, hdev->features[1][0] |= LMP_HOST_LE_BREDR; else hdev->features[1][0] &= ~LMP_HOST_LE_BREDR; + + hci_dev_unlock(hdev); } static void hci_cc_set_adv_param(struct hci_dev *hdev, struct sk_buff *skb) -- cgit v1.2.3 From 417287de88bfbb724bab2f50782395d2606b28e1 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 11 Dec 2014 20:21:54 +0100 Subject: Bluetooth: Fix check for support for page scan related commands The Read Page Scan Activity and Read Page Scan Type commands are not supported by all controllers. Move the execution of both commands into the 3rd phase of the init procedure. And then check the bit mask of supported commands before adding them to the init sequence. With this re-ordering of the init sequence, the extra check for AVM BlueFritz! controllers is no longer needed. They will report that these two commands are not supported. This fixes an issue with the Microsoft Corp. Wireless Transceiver for Bluetooth 2.0 (ID 045e:009c). Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/hci_core.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'net') diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index ecd7c01317bc..cc60465ea774 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1373,8 +1373,6 @@ static void hci_init1_req(struct hci_request *req, unsigned long opt) static void bredr_setup(struct hci_request *req) { - struct hci_dev *hdev = req->hdev; - __le16 param; __u8 flt_type; @@ -1403,14 +1401,6 @@ static void bredr_setup(struct hci_request *req) /* Connection accept timeout ~20 secs */ param = cpu_to_le16(0x7d00); hci_req_add(req, HCI_OP_WRITE_CA_TIMEOUT, 2, ¶m); - - /* AVM Berlin (31), aka "BlueFRITZ!", reports version 1.2, - * but it does not support page scan related HCI commands. - */ - if (hdev->manufacturer != 31 && hdev->hci_ver > BLUETOOTH_VER_1_1) { - hci_req_add(req, HCI_OP_READ_PAGE_SCAN_ACTIVITY, 0, NULL); - hci_req_add(req, HCI_OP_READ_PAGE_SCAN_TYPE, 0, NULL); - } } static void le_setup(struct hci_request *req) @@ -1718,6 +1708,16 @@ static void hci_init3_req(struct hci_request *req, unsigned long opt) if (hdev->commands[5] & 0x10) hci_setup_link_policy(req); + if (hdev->commands[8] & 0x01) + hci_req_add(req, HCI_OP_READ_PAGE_SCAN_ACTIVITY, 0, NULL); + + /* Some older Broadcom based Bluetooth 1.2 controllers do not + * support the Read Page Scan Type command. Check support for + * this command in the bit mask of supported commands. + */ + if (hdev->commands[13] & 0x01) + hci_req_add(req, HCI_OP_READ_PAGE_SCAN_TYPE, 0, NULL); + if (lmp_le_capable(hdev)) { u8 events[8]; -- cgit v1.2.3 From 15013aeb63fb4df7ff809d63246c8398e9703736 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 11 Dec 2014 21:45:44 +0200 Subject: Bluetooth: Fix calling hci_conn_put too early The pairing_complete() function relies on a hci_conn reference to be able to access the hci_conn object. It should therefore only release this reference once it's done accessing the object, i.e. at the end of the function. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/mgmt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 16ac03730f4d..34da65ccc888 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -3115,14 +3115,14 @@ static void pairing_complete(struct pending_cmd *cmd, u8 status) conn->disconn_cfm_cb = NULL; hci_conn_drop(conn); - hci_conn_put(conn); - mgmt_pending_remove(cmd); /* The device is paired so there is no need to remove * its connection parameters anymore. */ clear_bit(HCI_CONN_PARAM_REMOVAL_PEND, &conn->flags); + + hci_conn_put(conn); } void mgmt_smp_complete(struct hci_conn *conn, bool complete) -- cgit v1.2.3 From a511b35ba4f02cca526c7239723c48445a8b127d Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 11 Dec 2014 21:45:45 +0200 Subject: Bluetooth: Fix incorrect pending cmd removal in pairing_complete() The pairing_complete() function is used as a pending mgmt command cmd_complete callback. The expectation of such functions is that they are not responsible themselves for calling mgmt_pending_remove(). This patch fixes the incorrect mgmt_pending_remove() call in pairing_complete() and adds it to the appropriate changes. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/mgmt.c | 38 +++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-) (limited to 'net') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 34da65ccc888..827107d5a424 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -3115,7 +3115,6 @@ static void pairing_complete(struct pending_cmd *cmd, u8 status) conn->disconn_cfm_cb = NULL; hci_conn_drop(conn); - mgmt_pending_remove(cmd); /* The device is paired so there is no need to remove * its connection parameters anymore. @@ -3131,8 +3130,10 @@ void mgmt_smp_complete(struct hci_conn *conn, bool complete) struct pending_cmd *cmd; cmd = find_pairing(conn); - if (cmd) + if (cmd) { cmd->cmd_complete(cmd, status); + mgmt_pending_remove(cmd); + } } static void pairing_complete_cb(struct hci_conn *conn, u8 status) @@ -3142,10 +3143,13 @@ static void pairing_complete_cb(struct hci_conn *conn, u8 status) BT_DBG("status %u", status); cmd = find_pairing(conn); - if (!cmd) + if (!cmd) { BT_DBG("Unable to find a pending command"); - else - cmd->cmd_complete(cmd, mgmt_status(status)); + return; + } + + cmd->cmd_complete(cmd, mgmt_status(status)); + mgmt_pending_remove(cmd); } static void le_pairing_complete_cb(struct hci_conn *conn, u8 status) @@ -3158,10 +3162,13 @@ static void le_pairing_complete_cb(struct hci_conn *conn, u8 status) return; cmd = find_pairing(conn); - if (!cmd) + if (!cmd) { BT_DBG("Unable to find a pending command"); - else - cmd->cmd_complete(cmd, mgmt_status(status)); + return; + } + + cmd->cmd_complete(cmd, mgmt_status(status)); + mgmt_pending_remove(cmd); } static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data, @@ -3275,8 +3282,10 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data, cmd->user_data = hci_conn_get(conn); if ((conn->state == BT_CONNECTED || conn->state == BT_CONFIG) && - hci_conn_security(conn, sec_level, auth_type, true)) - pairing_complete(cmd, 0); + hci_conn_security(conn, sec_level, auth_type, true)) { + cmd->cmd_complete(cmd, 0); + mgmt_pending_remove(cmd); + } err = 0; @@ -3318,7 +3327,8 @@ static int cancel_pair_device(struct sock *sk, struct hci_dev *hdev, void *data, goto unlock; } - pairing_complete(cmd, MGMT_STATUS_CANCELLED); + cmd->cmd_complete(cmd, MGMT_STATUS_CANCELLED); + mgmt_pending_remove(cmd); err = cmd_complete(sk, hdev->id, MGMT_OP_CANCEL_PAIR_DEVICE, 0, addr, sizeof(*addr)); @@ -6687,8 +6697,10 @@ void mgmt_auth_failed(struct hci_conn *conn, u8 hci_status) mgmt_event(MGMT_EV_AUTH_FAILED, conn->hdev, &ev, sizeof(ev), cmd ? cmd->sk : NULL); - if (cmd) - pairing_complete(cmd, status); + if (cmd) { + cmd->cmd_complete(cmd, status); + mgmt_pending_remove(cmd); + } } void mgmt_auth_enable_complete(struct hci_dev *hdev, u8 status) -- cgit v1.2.3 From 1aeb9c651c06282e84c33eb838d1069582879af8 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 11 Dec 2014 21:45:46 +0200 Subject: Bluetooth: Fix notifying mgmt power off before flushing connection list This patch moves the mgmt_powered() notification earlier in the hci_dev_do_close() function. This way the correct "not powered" error gets passed to any pending mgmt commands. Without the patch the pending commands would instead get a misleading "disconnected" response when powering down the adapter. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/hci_core.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'net') diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index cc60465ea774..5dcacf9607e4 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2634,6 +2634,12 @@ static int hci_dev_do_close(struct hci_dev *hdev) drain_workqueue(hdev->workqueue); hci_dev_lock(hdev); + + if (!test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags)) { + if (hdev->dev_type == HCI_BREDR) + mgmt_powered(hdev, 0); + } + hci_inquiry_cache_flush(hdev); hci_pend_le_actions_clear(hdev); hci_conn_hash_flush(hdev); @@ -2681,14 +2687,6 @@ static int hci_dev_do_close(struct hci_dev *hdev) hdev->flags &= BIT(HCI_RAW); hdev->dev_flags &= ~HCI_PERSISTENT_MASK; - if (!test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags)) { - if (hdev->dev_type == HCI_BREDR) { - hci_dev_lock(hdev); - mgmt_powered(hdev, 0); - hci_dev_unlock(hdev); - } - } - /* Controller radio is available but is currently powered down */ hdev->amp_status = AMP_STATUS_POWERED_DOWN; -- cgit v1.2.3 From 7e6225a1604d0c6aa4140289bf5761868ffc9c83 Mon Sep 17 00:00:00 2001 From: Jes Sorensen Date: Wed, 10 Dec 2014 14:14:07 -0500 Subject: mac80211: avoid using uninitialized stack data Avoid a case where we would access uninitialized stack data if the AP advertises HT support without 40MHz channel support. Cc: stable@vger.kernel.org Fixes: f3000e1b43f1 ("mac80211: fix broken use of VHT/20Mhz with some APs") Signed-off-by: Jes Sorensen Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 1 + 1 file changed, 1 insertion(+) (limited to 'net') diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 75a9bf50207e..2c36c4765f47 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -174,6 +174,7 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata, if (!(ht_cap->cap_info & cpu_to_le16(IEEE80211_HT_CAP_SUP_WIDTH_20_40))) { ret = IEEE80211_STA_DISABLE_40MHZ; + vht_chandef = *chandef; goto out; } -- cgit v1.2.3 From d025933e29872cb1fe19fc54d80e4dfa4ee5779c Mon Sep 17 00:00:00 2001 From: Andreas Müller Date: Fri, 12 Dec 2014 12:11:11 +0100 Subject: mac80211: fix multicast LED blinking and counter As multicast-frames can't be fragmented, "dot11MulticastReceivedFrameCount" stopped being incremented after the use-after-free fix. Furthermore, the RX-LED will be triggered by every multicast frame (which wouldn't happen before) which wouldn't allow the LED to rest at all. Fixes https://bugzilla.kernel.org/show_bug.cgi?id=89431 which also had the patch. Cc: stable@vger.kernel.org Fixes: b8fff407a180 ("mac80211: fix use-after-free in defragmentation") Signed-off-by: Andreas Müller [rewrite commit message] Signed-off-by: Johannes Berg --- net/mac80211/rx.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'net') diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 49c23bdf08bb..683b10f46505 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -1761,14 +1761,14 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx) sc = le16_to_cpu(hdr->seq_ctrl); frag = sc & IEEE80211_SCTL_FRAG; - if (likely(!ieee80211_has_morefrags(fc) && frag == 0)) - goto out; - if (is_multicast_ether_addr(hdr->addr1)) { rx->local->dot11MulticastReceivedFrameCount++; - goto out; + goto out_no_led; } + if (likely(!ieee80211_has_morefrags(fc) && frag == 0)) + goto out; + I802_DEBUG_INC(rx->local->rx_handlers_fragments); if (skb_linearize(rx->skb)) @@ -1859,9 +1859,10 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx) status->rx_flags |= IEEE80211_RX_FRAGMENTED; out: + ieee80211_led_rx(rx->local); + out_no_led: if (rx->sta) rx->sta->rx_packets++; - ieee80211_led_rx(rx->local); return RX_CONTINUE; } -- cgit v1.2.3 From 08f6f147773b23b765b94633a8eaa82e7defcf4c Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Thu, 11 Dec 2014 23:48:55 +0200 Subject: cfg80211: Fix 160 MHz channels with 80+80 and 160 MHz drivers The VHT supported channel width field is a two bit integer, not a bitfield. cfg80211_chandef_usable() was interpreting it incorrectly and ended up rejecting 160 MHz channel width if the driver indicated support for both 160 and 80+80 MHz channels. Cc: stable@vger.kernel.org (3.16+) Fixes: 3d9d1d6656a73 ("nl80211/cfg80211: support VHT channel configuration") (however, no real drivers had 160 MHz support it until 3.16) Signed-off-by: Jouni Malinen Signed-off-by: Johannes Berg --- net/wireless/chan.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/wireless/chan.c b/net/wireless/chan.c index 85506f1d0789..7aaf7415dc4c 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -603,7 +603,7 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy, { struct ieee80211_sta_ht_cap *ht_cap; struct ieee80211_sta_vht_cap *vht_cap; - u32 width, control_freq; + u32 width, control_freq, cap; if (WARN_ON(!cfg80211_chandef_valid(chandef))) return false; @@ -643,7 +643,8 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy, return false; break; case NL80211_CHAN_WIDTH_80P80: - if (!(vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ)) + cap = vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK; + if (cap != IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ) return false; case NL80211_CHAN_WIDTH_80: if (!vht_cap->vht_supported) @@ -654,7 +655,9 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy, case NL80211_CHAN_WIDTH_160: if (!vht_cap->vht_supported) return false; - if (!(vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ)) + cap = vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK; + if (cap != IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ && + cap != IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ) return false; prohibited_flags |= IEEE80211_CHAN_NO_160MHZ; width = 160; -- cgit v1.2.3 From 34f05f543f02350e920bddb7660ffdd4697aaf60 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Thu, 4 Dec 2014 12:22:16 +0200 Subject: cfg80211: avoid mem leak on driver hint set In the already-set and intersect case of a driver-hint, the previous wiphy regdomain was not freed before being reset with a copy of the cfg80211 regdomain. Cc: stable@vger.kernel.org Signed-off-by: Arik Nemtsov Acked-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/reg.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 47be6163381c..7ddd16a51adf 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1907,7 +1907,7 @@ static enum reg_request_treatment reg_process_hint_driver(struct wiphy *wiphy, struct regulatory_request *driver_request) { - const struct ieee80211_regdomain *regd; + const struct ieee80211_regdomain *regd, *tmp; enum reg_request_treatment treatment; treatment = __reg_process_hint_driver(driver_request); @@ -1927,7 +1927,10 @@ reg_process_hint_driver(struct wiphy *wiphy, reg_free_request(driver_request); return REG_REQ_IGNORE; } + + tmp = get_wiphy_regdom(wiphy); rcu_assign_pointer(wiphy->regd, regd); + rcu_free_regdom(tmp); } -- cgit v1.2.3 From f89f46cf3a23d8d7c98f924a461fd931e1331746 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Mon, 1 Dec 2014 11:32:09 +0200 Subject: nl80211: check matches array length before acessing it If the userspace passes a malformed sched scan request (or a net detect wowlan configuration) by adding a NL80211_ATTR_SCHED_SCAN_MATCH attribute without any nested matchsets, a NULL pointer dereference will occur. Fix this by checking that we do have matchsets in our array before trying to access it. BUG: unable to handle kernel NULL pointer dereference at 0000000000000024 IP: [] nl80211_parse_sched_scan.part.67+0x6e9/0x900 [cfg80211] PGD 865c067 PUD 865b067 PMD 0 Oops: 0002 [#1] SMP Modules linked in: iwlmvm(O) iwlwifi(O) mac80211(O) cfg80211(O) compat(O) [last unloaded: compat] CPU: 2 PID: 2442 Comm: iw Tainted: G O 3.17.2 #31 Hardware name: Bochs Bochs, BIOS Bochs 01/01/2011 task: ffff880013800790 ti: ffff880008d80000 task.ti: ffff880008d80000 RIP: 0010:[] [] nl80211_parse_sched_scan.part.67+0x6e9/0x900 [cfg80211] RSP: 0018:ffff880008d838d0 EFLAGS: 00010293 RAX: 0000000000000000 RBX: 0000000000000000 RCX: 0000000000000000 RDX: 000000000000143c RSI: 0000000000000000 RDI: ffff880008ee8dd0 RBP: ffff880008d83948 R08: 0000000000000002 R09: 0000000000000019 R10: ffff88001d1b3c40 R11: 0000000000000002 R12: ffff880019e85e00 R13: 00000000fffffed4 R14: ffff880009757800 R15: 0000000000001388 FS: 00007fa3b6d13700(0000) GS:ffff88003e200000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000000000000024 CR3: 0000000008670000 CR4: 00000000000006e0 Stack: ffff880009757800 ffff880000000001 0000000000000000 ffff880008ee84e0 0000000000000000 ffff880009757800 00000000fffffed4 ffff880008d83948 ffffffff814689c9 ffff880009757800 ffff880008ee8000 0000000000000000 Call Trace: [] ? nla_parse+0xb9/0x120 [] nl80211_set_wowlan+0x75e/0x960 [cfg80211] [] ? mark_held_locks+0x75/0xa0 [] genl_family_rcv_msg+0x18b/0x360 [] ? trace_hardirqs_on+0xd/0x10 [] genl_rcv_msg+0x84/0xc0 [] ? genl_family_rcv_msg+0x360/0x360 [] netlink_rcv_skb+0xa9/0xd0 [] genl_rcv+0x28/0x40 [] netlink_unicast+0x105/0x180 [] netlink_sendmsg+0x34f/0x7a0 [] ? kvm_clock_read+0x27/0x40 [] sock_sendmsg+0x8d/0xc0 [] ? might_fault+0xb9/0xc0 [] ? might_fault+0x5e/0xc0 [] ? verify_iovec+0x56/0xe0 [] ___sys_sendmsg+0x3d0/0x3e0 [] ? sched_clock_cpu+0x98/0xd0 [] ? __do_page_fault+0x254/0x580 [] ? up_read+0x1f/0x40 [] ? __do_page_fault+0x254/0x580 [] ? __fget_light+0x13d/0x160 [] __sys_sendmsg+0x42/0x80 [] SyS_sendmsg+0x12/0x20 [] system_call_fastpath+0x16/0x1b Fixes: ea73cbce4e1f ("nl80211: fix scheduled scan RSSI matchset attribute confusion") Cc: stable@vger.kernel.org [3.15+] Signed-off-by: Luciano Coelho Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index a17d6bc6b22c..7ca4b5133123 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -6002,7 +6002,7 @@ nl80211_parse_sched_scan(struct wiphy *wiphy, struct wireless_dev *wdev, } /* there was no other matchset, so the RSSI one is alone */ - if (i == 0) + if (i == 0 && n_match_sets) request->match_sets[0].rssi_thold = default_match_rssi; request->min_rssi_thold = INT_MAX; -- cgit v1.2.3 From 722ddb0dab29b666881d74067c2b92d42074351f Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Sun, 30 Nov 2014 17:17:26 +0200 Subject: mac80211: update the channel context after channel switch When the channel switch has been made, a vif is now using the channel context which was reserved. When that happens, we need to update the channel context since its parameters may change. I hit a case in which I switched to a 40Mhz channel but the reserved channel context was still on 20Mhz. The rate control would try to send 40Mhz packets on a 20Mhz channel context and that made iwlwifi's firmware unhappy. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- net/mac80211/chan.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'net') diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index 5d6dae9e4aac..da1c12c34487 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c @@ -1011,6 +1011,10 @@ ieee80211_vif_use_reserved_reassign(struct ieee80211_sub_if_data *sdata) ieee80211_vif_update_chandef(sdata, &sdata->reserved_chandef); + ieee80211_recalc_smps_chanctx(local, new_ctx); + ieee80211_recalc_radar_chanctx(local, new_ctx); + ieee80211_recalc_chanctx_min_def(local, new_ctx); + if (changed) ieee80211_bss_info_change_notify(sdata, changed); -- cgit v1.2.3 From ec6f99b807e4bd50566c48fff8994da2fb1bf9fe Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 12 Dec 2014 13:30:11 +0200 Subject: Bluetooth: Fix enabling BR/EDR SC when powering on If we're in the AUTO_OFF stage the powered_update_hci() function is responsible for doing the updates to the HCI state that were not done during the actual mgmt command handlers. One of the updates needing done is for BR/EDR SC support. This patch adds the missing HCI command for SC support to the powered_update_hci() function. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/mgmt.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'net') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 827107d5a424..1e33880ed562 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -6097,6 +6097,11 @@ static int powered_update_hci(struct hci_dev *hdev) hci_req_add(&req, HCI_OP_WRITE_SSP_MODE, 1, &ssp); } + if (bredr_sc_enabled(hdev) && !lmp_host_sc_capable(hdev)) { + u8 sc = 0x01; + hci_req_add(&req, HCI_OP_WRITE_SC_SUPPORT, sizeof(sc), &sc); + } + if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags) && lmp_bredr_capable(hdev)) { struct hci_cp_write_le_host_supported cp; -- cgit v1.2.3 From 9845904fd489288bcf693642c1b31cc463c0b660 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 12 Dec 2014 11:15:21 +0200 Subject: Bluetooth: Fix mgmt response status when removing adapter When an adapter is removed (hci_unregister_dev) any pending mgmt commands for that adapter should get the appropriate INVALID_INDEX response. Since hci_unregister_dev() calls hci_dev_do_close() first that'd so far have caused "not powered" responses to be sent. Skipping the HCI_UNREGISTER case in mgmt_powered() is also not a solution since before reaching the mgmt_index_removed() stage any hci_conn callbacks (e.g. used by pairing) will get called, thereby causing "disconnected" status responses to be sent. The fix that covers all scenarios is to handle both INVALID_INDEX and NOT_POWERED responses through the mgmt_powered() function. The INVALID_INDEX response sending from mgmt_index_removed() is left untouched since there are a couple of places not related to powering off or removing an adapter that call it (e.g. configuring a new bdaddr). Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/mgmt.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 1e33880ed562..23a0ca5a4737 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -6151,8 +6151,7 @@ static int powered_update_hci(struct hci_dev *hdev) int mgmt_powered(struct hci_dev *hdev, u8 powered) { struct cmd_lookup match = { NULL, hdev }; - u8 status_not_powered = MGMT_STATUS_NOT_POWERED; - u8 zero_cod[] = { 0, 0, 0 }; + u8 status, zero_cod[] = { 0, 0, 0 }; int err; if (!test_bit(HCI_MGMT, &hdev->dev_flags)) @@ -6168,7 +6167,20 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered) } mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match); - mgmt_pending_foreach(0, hdev, cmd_complete_rsp, &status_not_powered); + + /* If the power off is because of hdev unregistration let + * use the appropriate INVALID_INDEX status. Otherwise use + * NOT_POWERED. We cover both scenarios here since later in + * mgmt_index_removed() any hci_conn callbacks will have already + * been triggered, potentially causing misleading DISCONNECTED + * status responses. + */ + if (test_bit(HCI_UNREGISTER, &hdev->dev_flags)) + status = MGMT_STATUS_INVALID_INDEX; + else + status = MGMT_STATUS_NOT_POWERED; + + mgmt_pending_foreach(0, hdev, cmd_complete_rsp, &status); if (memcmp(hdev->dev_class, zero_cod, sizeof(zero_cod)) != 0) mgmt_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev, -- cgit v1.2.3 From 70dcec5a488a7b81779190ac8089475fe4b8b962 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 2 Dec 2014 09:53:25 +0200 Subject: cfg80211: don't WARN about two consecutive Country IE hint This can happen and there is no point in added more detection code lower in the stack. Catching these in one single point (cfg80211) is enough. Stop WARNING about this case. This fixes: https://bugzilla.kernel.org/show_bug.cgi?id=89001 Cc: stable@vger.kernel.org Fixes: 2f1c6c572d7b ("cfg80211: process non country IE conflicting first") Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- net/wireless/reg.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'net') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 7ddd16a51adf..c9f5ad5d5d94 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1989,11 +1989,8 @@ __reg_process_hint_country_ie(struct wiphy *wiphy, return REG_REQ_IGNORE; return REG_REQ_ALREADY_SET; } - /* - * Two consecutive Country IE hints on the same wiphy. - * This should be picked up early by the driver/stack - */ - if (WARN_ON(regdom_changes(country_ie_request->alpha2))) + + if (regdom_changes(country_ie_request->alpha2)) return REG_REQ_OK; return REG_REQ_ALREADY_SET; } -- cgit v1.2.3 From 185076d6db7b0320fd7669732923179f9a4d068b Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Wed, 3 Dec 2014 18:08:17 +0200 Subject: cfg80211: correctly check ad-hoc channels Ad-hoc requires beaconing for regulatory purposes. Validate that the channel is valid for beaconing, and not only enabled. Signed-off-by: Arik Nemtsov Reviewed-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/reg.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index c9f5ad5d5d94..7b8309840d4e 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1546,12 +1546,18 @@ static bool reg_wdev_chan_valid(struct wiphy *wiphy, struct wireless_dev *wdev) if (!wdev->beacon_interval) goto out; + ret = cfg80211_reg_can_beacon(wiphy, + &wdev->chandef, wdev->iftype); + break; + case NL80211_IFTYPE_ADHOC: + if (!wdev->ssid_len) + goto out; + ret = cfg80211_reg_can_beacon(wiphy, &wdev->chandef, wdev->iftype); break; case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_P2P_CLIENT: - case NL80211_IFTYPE_ADHOC: if (!wdev->current_bss || !wdev->current_bss->pub.channel) goto out; -- cgit v1.2.3 From 8a0033a947403569caeca45fa5e6f7ba60d51974 Mon Sep 17 00:00:00 2001 From: Timo Teräs Date: Mon, 15 Dec 2014 09:24:13 +0200 Subject: gre: fix the inner mac header in nbma tunnel xmit path The NBMA GRE tunnels temporarily push GRE header that contain the per-packet NBMA destination on the skb via header ops early in xmit path. It is the later pulled before the real GRE header is constructed. The inner mac was thus set differently in nbma case: the GRE header has been pushed by neighbor layer, and mac header points to beginning of the temporary gre header (set by dev_queue_xmit). Now that the offloads expect mac header to point to the gre payload, fix the xmit patch to: - pull first the temporary gre header away - and reset mac header to point to gre payload This fixes tso to work again with nbma tunnels. Fixes: 14051f0452a2 ("gre: Use inner mac length when computing tunnel length") Signed-off-by: Timo Teräs Cc: Tom Herbert Cc: Alexander Duyck Signed-off-by: David S. Miller --- net/ipv4/ip_gre.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index ac8491245e5b..4f4bf5b99686 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -252,10 +252,6 @@ static netdev_tx_t ipgre_xmit(struct sk_buff *skb, struct ip_tunnel *tunnel = netdev_priv(dev); const struct iphdr *tnl_params; - skb = gre_handle_offloads(skb, !!(tunnel->parms.o_flags&TUNNEL_CSUM)); - if (IS_ERR(skb)) - goto out; - if (dev->header_ops) { /* Need space for new headers */ if (skb_cow_head(skb, dev->needed_headroom - @@ -268,6 +264,7 @@ static netdev_tx_t ipgre_xmit(struct sk_buff *skb, * to gre header. */ skb_pull(skb, tunnel->hlen + sizeof(struct iphdr)); + skb_reset_mac_header(skb); } else { if (skb_cow_head(skb, dev->needed_headroom)) goto free_skb; @@ -275,6 +272,10 @@ static netdev_tx_t ipgre_xmit(struct sk_buff *skb, tnl_params = &tunnel->parms.iph; } + skb = gre_handle_offloads(skb, !!(tunnel->parms.o_flags&TUNNEL_CSUM)); + if (IS_ERR(skb)) + goto out; + __gre_xmit(skb, dev, tnl_params, skb->protocol); return NETDEV_TX_OK; -- cgit v1.2.3 From 6ff4a8ad4b6eae5171754fb60418bc81834aa09b Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 15 Dec 2014 13:21:42 +0100 Subject: rds: Fix min() warning in rds_message_inc_copy_to_user() net/rds/message.c: In function ‘rds_message_inc_copy_to_user’: net/rds/message.c:328: warning: comparison of distinct pointer types lacks a cast Use min_t(unsigned long, ...) like is done in rds_message_copy_from_user(). Signed-off-by: Geert Uytterhoeven Signed-off-by: David S. Miller --- net/rds/message.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/rds/message.c b/net/rds/message.c index ff2202218187..5a21e6f5986f 100644 --- a/net/rds/message.c +++ b/net/rds/message.c @@ -325,7 +325,8 @@ int rds_message_inc_copy_to_user(struct rds_incoming *inc, struct iov_iter *to) copied = 0; while (iov_iter_count(to) && copied < len) { - to_copy = min(iov_iter_count(to), sg->length - vec_off); + to_copy = min_t(unsigned long, iov_iter_count(to), + sg->length - vec_off); to_copy = min_t(unsigned long, to_copy, len - copied); rds_stats_add(s_copy_to_user, to_copy); -- cgit v1.2.3 From bb1553c800227f33063fe96561af881619447b83 Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Tue, 16 Dec 2014 21:05:20 +0100 Subject: ip_tunnel: Add sanity checks to ip_tunnel_encap_add_ops() The symbols are exported and could be used by external modules. Fixes: a8c5f9 ("ip_tunnel: Ops registration for secondary encap (fou, gue)") Signed-off-by: Thomas Graf Signed-off-by: David S. Miller --- net/ipv4/ip_tunnel.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'net') diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c index 63e745aadab6..2f498f87e3d8 100644 --- a/net/ipv4/ip_tunnel.c +++ b/net/ipv4/ip_tunnel.c @@ -514,6 +514,9 @@ const struct ip_tunnel_encap_ops __rcu * int ip_tunnel_encap_add_ops(const struct ip_tunnel_encap_ops *ops, unsigned int num) { + if (num >= MAX_IPTUN_ENCAP_OPS) + return -ERANGE; + return !cmpxchg((const struct ip_tunnel_encap_ops **) &iptun_encaps[num], NULL, ops) ? 0 : -1; @@ -525,6 +528,9 @@ int ip_tunnel_encap_del_ops(const struct ip_tunnel_encap_ops *ops, { int ret; + if (num >= MAX_IPTUN_ENCAP_OPS) + return -ERANGE; + ret = (cmpxchg((const struct ip_tunnel_encap_ops **) &iptun_encaps[num], ops, NULL) == ops) ? 0 : -1; -- cgit v1.2.3 From f1fb521f7d94c35e278d76a9198f078223f26799 Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Tue, 16 Dec 2014 21:05:21 +0100 Subject: ip_tunnel: Add missing validation of encap type to ip_tunnel_encap_setup() The encap->type comes straight from Netlink. Validate it against max supported encap types just like ip_encap_hlen() already does. Fixes: a8c5f9 ("ip_tunnel: Ops registration for secondary encap (fou, gue)") Signed-off-by: Thomas Graf Signed-off-by: David S. Miller --- net/ipv4/ip_tunnel.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'net') diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c index 2f498f87e3d8..d3e447936720 100644 --- a/net/ipv4/ip_tunnel.c +++ b/net/ipv4/ip_tunnel.c @@ -573,6 +573,9 @@ int ip_tunnel_encap(struct sk_buff *skb, struct ip_tunnel *t, if (t->encap.type == TUNNEL_ENCAP_NONE) return 0; + if (t->encap.type >= MAX_IPTUN_ENCAP_OPS) + return -EINVAL; + rcu_read_lock(); ops = rcu_dereference(iptun_encaps[t->encap.type]); if (likely(ops && ops->build_header)) -- cgit v1.2.3 From 65891feac27e26115dc4cce881743a1ac33372df Mon Sep 17 00:00:00 2001 From: Or Gerlitz Date: Sun, 14 Dec 2014 18:19:05 +0200 Subject: net: Disallow providing non zero VLAN ID for NIC drivers FDB add flow The current implementations all use dev_uc_add_excl() and such whose API doesn't support vlans, so we can't make it with NICs HW for now. Fixes: f6f6424ba773 ('net: make vid as a parameter for ndo_fdb_add/ndo_fdb_del') Signed-off-by: Or Gerlitz Reviewed-by: Jiri Pirko Acked-by: Jeff Kirsher Signed-off-by: David S. Miller --- drivers/net/ethernet/intel/i40e/i40e_main.c | 5 +++++ net/core/rtnetlink.c | 5 +++++ 2 files changed, 10 insertions(+) (limited to 'net') diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 0a7ea4c5f9d3..a5f2660d552d 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -7549,6 +7549,11 @@ static int i40e_ndo_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], if (!(pf->flags & I40E_FLAG_SRIOV_ENABLED)) return -EOPNOTSUPP; + if (vid) { + pr_info("%s: vlans aren't supported yet for dev_uc|mc_add()\n", dev->name); + return -EINVAL; + } + /* Hardware does not support aging addresses so if a * ndm_state is given only allow permanent addresses */ diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index d06107d36ec8..9cf6fe9ddc0c 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -2368,6 +2368,11 @@ int ndo_dflt_fdb_add(struct ndmsg *ndm, return err; } + if (vid) { + pr_info("%s: vlans aren't supported yet for dev_uc|mc_add()\n", dev->name); + return err; + } + if (is_unicast_ether_addr(addr) || is_link_local_ether_addr(addr)) err = dev_uc_add_excl(dev, addr); else if (is_multicast_ether_addr(addr)) -- cgit v1.2.3 From 28a9bc68124c319b2b3dc861e80828a8865fd1ba Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 17 Dec 2014 13:55:49 +0100 Subject: mac80211: free management frame keys when removing station When writing the code to allow per-station GTKs, I neglected to take into account the management frame keys (index 4 and 5) when freeing the station and only added code to free the first four data frame keys. Fix this by iterating the array of keys over the right length. Cc: stable@vger.kernel.org Fixes: e31b82136d1a ("cfg80211/mac80211: allow per-station GTKs") Signed-off-by: Johannes Berg --- net/mac80211/key.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/mac80211/key.c b/net/mac80211/key.c index 434a91ad12c8..0bb7038121ac 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -656,7 +656,7 @@ void ieee80211_free_sta_keys(struct ieee80211_local *local, int i; mutex_lock(&local->key_mtx); - for (i = 0; i < NUM_DEFAULT_KEYS; i++) { + for (i = 0; i < ARRAY_SIZE(sta->gtk); i++) { key = key_mtx_dereference(local, sta->gtk[i]); if (!key) continue; -- cgit v1.2.3 From ea8ae2516ac43028a01c40b58ffa80d3b0afb802 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 17 Dec 2014 18:18:08 +0100 Subject: Bluetooth: Fix bug with filter in service discovery optimization The optimization for filtering out extended inquiry results, advertising reports or scan response data based on provided UUID list has a logic bug. In case no match is found in the advertising data, the scan response is ignored and not checked against the filter. This will lead to events being filtered wrongly. Change the code to actually only drop the events when the scan response data is not present. If it is present, it needs to be checked against the provided filter. The patch is a bit more complex than it needs to be. That is because it also fixes this compiler warning that some gcc versions produce. CC net/bluetooth/mgmt.o net/bluetooth/mgmt.c: In function ‘mgmt_device_found’: net/bluetooth/mgmt.c:7028:7: warning: ‘match’ may be used uninitialized in this function [-Wmaybe-uninitialized] bool match; ^ It seems that gcc can not clearly figure out the context of the match variable. So just change the branches for the extended inquiry response and advertising data around so that it is clear. Reported-by: Geert Uytterhoeven Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/mgmt.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'net') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 06c2e652e4b6..693ce8bcd06e 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -7081,13 +7081,15 @@ void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, * kept and checking possible scan response data * will be skipped. */ - if (hdev->discovery.uuid_count > 0) { + if (hdev->discovery.uuid_count > 0) match = eir_has_uuids(eir, eir_len, hdev->discovery.uuid_count, hdev->discovery.uuids); - if (!match) - return; - } + else + match = true; + + if (!match && !scan_rsp_len) + return; /* Copy EIR or advertising data into event */ memcpy(ev->eir, eir, eir_len); @@ -7096,8 +7098,10 @@ void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, * provided, results with empty EIR or advertising data * should be dropped since they do not match any UUID. */ - if (hdev->discovery.uuid_count > 0) + if (hdev->discovery.uuid_count > 0 && !scan_rsp_len) return; + + match = false; } if (dev_class && !eir_has_data_type(ev->eir, eir_len, EIR_CLASS_OF_DEV)) -- cgit v1.2.3 From 4682a0358639b29cf69437ed909c6221f8c89847 Mon Sep 17 00:00:00 2001 From: David Miller Date: Tue, 16 Dec 2014 17:58:17 -0500 Subject: netlink: Always copy on mmap TX. Checking the file f_count and the nlk->mapped count is not completely sufficient to prevent the mmap'd area contents from changing from under us during netlink mmap sendmsg() operations. Be careful to sample the header's length field only once, because this could change from under us as well. Fixes: 5fd96123ee19 ("netlink: implement memory mapped sendmsg()") Signed-off-by: David S. Miller Acked-by: Daniel Borkmann Acked-by: Thomas Graf --- net/netlink/af_netlink.c | 52 +++++++++++++++--------------------------------- 1 file changed, 16 insertions(+), 36 deletions(-) (limited to 'net') diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index ef5f77b44ec7..a64680a3e782 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -525,14 +525,14 @@ out: return err; } -static void netlink_frame_flush_dcache(const struct nl_mmap_hdr *hdr) +static void netlink_frame_flush_dcache(const struct nl_mmap_hdr *hdr, unsigned int nm_len) { #if ARCH_IMPLEMENTS_FLUSH_DCACHE_PAGE == 1 struct page *p_start, *p_end; /* First page is flushed through netlink_{get,set}_status */ p_start = pgvec_to_page(hdr + PAGE_SIZE); - p_end = pgvec_to_page((void *)hdr + NL_MMAP_HDRLEN + hdr->nm_len - 1); + p_end = pgvec_to_page((void *)hdr + NL_MMAP_HDRLEN + nm_len - 1); while (p_start <= p_end) { flush_dcache_page(p_start); p_start++; @@ -714,24 +714,16 @@ static int netlink_mmap_sendmsg(struct sock *sk, struct msghdr *msg, struct nl_mmap_hdr *hdr; struct sk_buff *skb; unsigned int maxlen; - bool excl = true; int err = 0, len = 0; - /* Netlink messages are validated by the receiver before processing. - * In order to avoid userspace changing the contents of the message - * after validation, the socket and the ring may only be used by a - * single process, otherwise we fall back to copying. - */ - if (atomic_long_read(&sk->sk_socket->file->f_count) > 1 || - atomic_read(&nlk->mapped) > 1) - excl = false; - mutex_lock(&nlk->pg_vec_lock); ring = &nlk->tx_ring; maxlen = ring->frame_size - NL_MMAP_HDRLEN; do { + unsigned int nm_len; + hdr = netlink_current_frame(ring, NL_MMAP_STATUS_VALID); if (hdr == NULL) { if (!(msg->msg_flags & MSG_DONTWAIT) && @@ -739,35 +731,23 @@ static int netlink_mmap_sendmsg(struct sock *sk, struct msghdr *msg, schedule(); continue; } - if (hdr->nm_len > maxlen) { + + nm_len = ACCESS_ONCE(hdr->nm_len); + if (nm_len > maxlen) { err = -EINVAL; goto out; } - netlink_frame_flush_dcache(hdr); + netlink_frame_flush_dcache(hdr, nm_len); - if (likely(dst_portid == 0 && dst_group == 0 && excl)) { - skb = alloc_skb_head(GFP_KERNEL); - if (skb == NULL) { - err = -ENOBUFS; - goto out; - } - sock_hold(sk); - netlink_ring_setup_skb(skb, sk, ring, hdr); - NETLINK_CB(skb).flags |= NETLINK_SKB_TX; - __skb_put(skb, hdr->nm_len); - netlink_set_status(hdr, NL_MMAP_STATUS_RESERVED); - atomic_inc(&ring->pending); - } else { - skb = alloc_skb(hdr->nm_len, GFP_KERNEL); - if (skb == NULL) { - err = -ENOBUFS; - goto out; - } - __skb_put(skb, hdr->nm_len); - memcpy(skb->data, (void *)hdr + NL_MMAP_HDRLEN, hdr->nm_len); - netlink_set_status(hdr, NL_MMAP_STATUS_UNUSED); + skb = alloc_skb(nm_len, GFP_KERNEL); + if (skb == NULL) { + err = -ENOBUFS; + goto out; } + __skb_put(skb, nm_len); + memcpy(skb->data, (void *)hdr + NL_MMAP_HDRLEN, nm_len); + netlink_set_status(hdr, NL_MMAP_STATUS_UNUSED); netlink_increment_head(ring); @@ -813,7 +793,7 @@ static void netlink_queue_mmaped_skb(struct sock *sk, struct sk_buff *skb) hdr->nm_pid = NETLINK_CB(skb).creds.pid; hdr->nm_uid = from_kuid(sk_user_ns(sk), NETLINK_CB(skb).creds.uid); hdr->nm_gid = from_kgid(sk_user_ns(sk), NETLINK_CB(skb).creds.gid); - netlink_frame_flush_dcache(hdr); + netlink_frame_flush_dcache(hdr, hdr->nm_len); netlink_set_status(hdr, NL_MMAP_STATUS_VALID); NETLINK_CB(skb).flags |= NETLINK_SKB_DELIVERED; -- cgit v1.2.3 From a18e6a186f53af06937a2c268c72443336f4ab56 Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Thu, 18 Dec 2014 10:30:26 +0000 Subject: netlink: Don't reorder loads/stores before marking mmap netlink frame as available Each mmap Netlink frame contains a status field which indicates whether the frame is unused, reserved, contains data or needs to be skipped. Both loads and stores may not be reordeded and must complete before the status field is changed and another CPU might pick up the frame for use. Use an smp_mb() to cover needs of both types of callers to netlink_set_status(), callers which have been reading data frame from the frame, and callers which have been filling or releasing and thus writing to the frame. - Example code path requiring a smp_rmb(): memcpy(skb->data, (void *)hdr + NL_MMAP_HDRLEN, hdr->nm_len); netlink_set_status(hdr, NL_MMAP_STATUS_UNUSED); - Example code path requiring a smp_wmb(): hdr->nm_uid = from_kuid(sk_user_ns(sk), NETLINK_CB(skb).creds.uid); hdr->nm_gid = from_kgid(sk_user_ns(sk), NETLINK_CB(skb).creds.gid); netlink_frame_flush_dcache(hdr); netlink_set_status(hdr, NL_MMAP_STATUS_VALID); Fixes: f9c228 ("netlink: implement memory mapped recvmsg()") Reported-by: Eric Dumazet Signed-off-by: Thomas Graf Signed-off-by: David S. Miller --- net/netlink/af_netlink.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index a64680a3e782..074cf3e91c6f 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -550,9 +550,9 @@ static enum nl_mmap_status netlink_get_status(const struct nl_mmap_hdr *hdr) static void netlink_set_status(struct nl_mmap_hdr *hdr, enum nl_mmap_status status) { + smp_mb(); hdr->nm_status = status; flush_dcache_page(pgvec_to_page(hdr)); - smp_wmb(); } static struct nl_mmap_hdr * -- cgit v1.2.3 From 7ed767f73192d6daf673c6d885cd02d5f280ac1f Mon Sep 17 00:00:00 2001 From: Jesse Gross Date: Tue, 16 Dec 2014 18:25:31 -0800 Subject: geneve: Remove socket and offload handlers at destruction. Sockets aren't currently removed from the the global list when they are destroyed. In addition, offload handlers need to be cleaned up as well. Fixes: 0b5e8b8e ("net: Add Geneve tunneling protocol driver") CC: Andy Zhou Signed-off-by: Jesse Gross Acked-by: Thomas Graf Signed-off-by: David S. Miller --- net/ipv4/geneve.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'net') diff --git a/net/ipv4/geneve.c b/net/ipv4/geneve.c index a457232f0131..5a47188b01e9 100644 --- a/net/ipv4/geneve.c +++ b/net/ipv4/geneve.c @@ -159,6 +159,15 @@ static void geneve_notify_add_rx_port(struct geneve_sock *gs) } } +static void geneve_notify_del_rx_port(struct geneve_sock *gs) +{ + struct sock *sk = gs->sock->sk; + sa_family_t sa_family = sk->sk_family; + + if (sa_family == AF_INET) + udp_del_offload(&gs->udp_offloads); +} + /* Callback from net/ipv4/udp.c to receive packets */ static int geneve_udp_encap_recv(struct sock *sk, struct sk_buff *skb) { @@ -312,9 +321,17 @@ EXPORT_SYMBOL_GPL(geneve_sock_add); void geneve_sock_release(struct geneve_sock *gs) { + struct net *net = sock_net(gs->sock->sk); + struct geneve_net *gn = net_generic(net, geneve_net_id); + if (!atomic_dec_and_test(&gs->refcnt)) return; + spin_lock(&gn->sock_lock); + hlist_del_rcu(&gs->hlist); + geneve_notify_del_rx_port(gs); + spin_unlock(&gn->sock_lock); + queue_work(geneve_wq, &gs->del_work); } EXPORT_SYMBOL_GPL(geneve_sock_release); -- cgit v1.2.3 From 12069401d895ff84076a50189ca842c0696b84b2 Mon Sep 17 00:00:00 2001 From: Jesse Gross Date: Tue, 16 Dec 2014 18:25:32 -0800 Subject: geneve: Fix races between socket add and release. Currently, searching for a socket to add a reference to is not synchronized with deletion of sockets. This can result in use after free if there is another operation that is removing a socket at the same time. Solving this requires both holding the appropriate lock and checking the refcount to ensure that it has not already hit zero. Inspired by a related (but not exactly the same) issue in the VXLAN driver. Fixes: 0b5e8b8e ("net: Add Geneve tunneling protocol driver") CC: Andy Zhou Signed-off-by: Jesse Gross Acked-by: Thomas Graf Signed-off-by: David S. Miller --- net/ipv4/geneve.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/net/ipv4/geneve.c b/net/ipv4/geneve.c index 5a47188b01e9..95e47c97585e 100644 --- a/net/ipv4/geneve.c +++ b/net/ipv4/geneve.c @@ -296,6 +296,7 @@ struct geneve_sock *geneve_sock_add(struct net *net, __be16 port, geneve_rcv_t *rcv, void *data, bool no_share, bool ipv6) { + struct geneve_net *gn = net_generic(net, geneve_net_id); struct geneve_sock *gs; gs = geneve_socket_create(net, port, rcv, data, ipv6); @@ -305,15 +306,15 @@ struct geneve_sock *geneve_sock_add(struct net *net, __be16 port, if (no_share) /* Return error if sharing is not allowed. */ return ERR_PTR(-EINVAL); + spin_lock(&gn->sock_lock); gs = geneve_find_sock(net, port); - if (gs) { - if (gs->rcv == rcv) - atomic_inc(&gs->refcnt); - else + if (gs && ((gs->rcv != rcv) || + !atomic_add_unless(&gs->refcnt, 1, 0))) gs = ERR_PTR(-EBUSY); - } else { + spin_unlock(&gn->sock_lock); + + if (!gs) gs = ERR_PTR(-EINVAL); - } return gs; } -- cgit v1.2.3