summaryrefslogtreecommitdiff
path: root/net
diff options
context:
space:
mode:
Diffstat (limited to 'net')
-rw-r--r--net/atm/lec.c72
-rw-r--r--net/atm/lec.h2
-rw-r--r--net/batman-adv/bat_iv_ogm.c3
-rw-r--r--net/bluetooth/hci_conn.c4
-rw-r--r--net/bluetooth/hci_sync.c2
-rw-r--r--net/bluetooth/hidp/core.c16
-rw-r--r--net/bluetooth/l2cap_core.c51
-rw-r--r--net/bluetooth/mgmt.c7
-rw-r--r--net/bluetooth/smp.c2
-rw-r--r--net/bridge/br_cfm.c4
-rw-r--r--net/ethernet/eth.c9
-rw-r--r--net/ipv4/icmp.c4
-rw-r--r--net/ipv4/ip_gre.c3
-rw-r--r--net/ipv6/exthdrs.c4
-rw-r--r--net/ipv6/seg6_hmac.c2
-rw-r--r--net/mac80211/cfg.c12
-rw-r--r--net/mac80211/chan.c6
-rw-r--r--net/mac80211/debugfs.c14
-rw-r--r--net/mac80211/mesh.c3
-rw-r--r--net/mac80211/sta_info.c7
-rw-r--r--net/mac80211/tdls.c2
-rw-r--r--net/mac80211/tx.c4
-rw-r--r--net/mac802154/iface.c4
-rw-r--r--net/mpls/af_mpls.c1
-rw-r--r--net/mptcp/pm_kernel.c2
-rw-r--r--net/netfilter/nf_bpf_link.c2
-rw-r--r--net/netfilter/nf_conntrack_h323_asn1.c4
-rw-r--r--net/netfilter/nf_conntrack_netlink.c28
-rw-r--r--net/netfilter/nf_conntrack_proto_sctp.c3
-rw-r--r--net/netfilter/nf_conntrack_sip.c6
-rw-r--r--net/netfilter/nf_flow_table_ip.c1
-rw-r--r--net/netfilter/nf_tables_api.c26
-rw-r--r--net/netfilter/nfnetlink_osf.c13
-rw-r--r--net/netfilter/nft_ct.c4
-rw-r--r--net/netfilter/nft_dynset.c10
-rw-r--r--net/netfilter/nft_set_rbtree.c71
-rw-r--r--net/netfilter/xt_CT.c4
-rw-r--r--net/netfilter/xt_time.c4
-rw-r--r--net/phonet/af_phonet.c5
-rw-r--r--net/rose/af_rose.c5
-rw-r--r--net/sched/sch_generic.c27
-rw-r--r--net/sched/sch_ingress.c14
-rw-r--r--net/sched/sch_teql.c7
-rw-r--r--net/shaper/shaper.c158
-rw-r--r--net/shaper/shaper_nl_gen.c12
-rw-r--r--net/shaper/shaper_nl_gen.h5
-rw-r--r--net/smc/af_smc.c23
-rw-r--r--net/smc/smc.h5
-rw-r--r--net/smc/smc_close.c2
-rw-r--r--net/unix/af_unix.c2
-rw-r--r--net/unix/af_unix.h1
-rw-r--r--net/unix/garbage.c79
-rw-r--r--net/wireless/pmsr.c1
53 files changed, 439 insertions, 323 deletions
diff --git a/net/atm/lec.c b/net/atm/lec.c
index fb93c6e1c329..10e260acf602 100644
--- a/net/atm/lec.c
+++ b/net/atm/lec.c
@@ -154,10 +154,19 @@ static void lec_handle_bridge(struct sk_buff *skb, struct net_device *dev)
/* 0x01 is topology change */
priv = netdev_priv(dev);
- atm_force_charge(priv->lecd, skb2->truesize);
- sk = sk_atm(priv->lecd);
- skb_queue_tail(&sk->sk_receive_queue, skb2);
- sk->sk_data_ready(sk);
+ struct atm_vcc *vcc;
+
+ rcu_read_lock();
+ vcc = rcu_dereference(priv->lecd);
+ if (vcc) {
+ atm_force_charge(vcc, skb2->truesize);
+ sk = sk_atm(vcc);
+ skb_queue_tail(&sk->sk_receive_queue, skb2);
+ sk->sk_data_ready(sk);
+ } else {
+ dev_kfree_skb(skb2);
+ }
+ rcu_read_unlock();
}
}
#endif /* IS_ENABLED(CONFIG_BRIDGE) */
@@ -216,7 +225,7 @@ static netdev_tx_t lec_start_xmit(struct sk_buff *skb,
int is_rdesc;
pr_debug("called\n");
- if (!priv->lecd) {
+ if (!rcu_access_pointer(priv->lecd)) {
pr_info("%s:No lecd attached\n", dev->name);
dev->stats.tx_errors++;
netif_stop_queue(dev);
@@ -449,10 +458,19 @@ static int lec_atm_send(struct atm_vcc *vcc, struct sk_buff *skb)
break;
skb2->len = sizeof(struct atmlec_msg);
skb_copy_to_linear_data(skb2, mesg, sizeof(*mesg));
- atm_force_charge(priv->lecd, skb2->truesize);
- sk = sk_atm(priv->lecd);
- skb_queue_tail(&sk->sk_receive_queue, skb2);
- sk->sk_data_ready(sk);
+ struct atm_vcc *vcc;
+
+ rcu_read_lock();
+ vcc = rcu_dereference(priv->lecd);
+ if (vcc) {
+ atm_force_charge(vcc, skb2->truesize);
+ sk = sk_atm(vcc);
+ skb_queue_tail(&sk->sk_receive_queue, skb2);
+ sk->sk_data_ready(sk);
+ } else {
+ dev_kfree_skb(skb2);
+ }
+ rcu_read_unlock();
}
}
#endif /* IS_ENABLED(CONFIG_BRIDGE) */
@@ -468,23 +486,16 @@ static int lec_atm_send(struct atm_vcc *vcc, struct sk_buff *skb)
static void lec_atm_close(struct atm_vcc *vcc)
{
- struct sk_buff *skb;
struct net_device *dev = (struct net_device *)vcc->proto_data;
struct lec_priv *priv = netdev_priv(dev);
- priv->lecd = NULL;
+ rcu_assign_pointer(priv->lecd, NULL);
+ synchronize_rcu();
/* Do something needful? */
netif_stop_queue(dev);
lec_arp_destroy(priv);
- if (skb_peek(&sk_atm(vcc)->sk_receive_queue))
- pr_info("%s closing with messages pending\n", dev->name);
- while ((skb = skb_dequeue(&sk_atm(vcc)->sk_receive_queue))) {
- atm_return(vcc, skb->truesize);
- dev_kfree_skb(skb);
- }
-
pr_info("%s: Shut down!\n", dev->name);
module_put(THIS_MODULE);
}
@@ -510,12 +521,14 @@ send_to_lecd(struct lec_priv *priv, atmlec_msg_type type,
const unsigned char *mac_addr, const unsigned char *atm_addr,
struct sk_buff *data)
{
+ struct atm_vcc *vcc;
struct sock *sk;
struct sk_buff *skb;
struct atmlec_msg *mesg;
- if (!priv || !priv->lecd)
+ if (!priv || !rcu_access_pointer(priv->lecd))
return -1;
+
skb = alloc_skb(sizeof(struct atmlec_msg), GFP_ATOMIC);
if (!skb)
return -1;
@@ -532,18 +545,27 @@ send_to_lecd(struct lec_priv *priv, atmlec_msg_type type,
if (atm_addr)
memcpy(&mesg->content.normal.atm_addr, atm_addr, ATM_ESA_LEN);
- atm_force_charge(priv->lecd, skb->truesize);
- sk = sk_atm(priv->lecd);
+ rcu_read_lock();
+ vcc = rcu_dereference(priv->lecd);
+ if (!vcc) {
+ rcu_read_unlock();
+ kfree_skb(skb);
+ return -1;
+ }
+
+ atm_force_charge(vcc, skb->truesize);
+ sk = sk_atm(vcc);
skb_queue_tail(&sk->sk_receive_queue, skb);
sk->sk_data_ready(sk);
if (data != NULL) {
pr_debug("about to send %d bytes of data\n", data->len);
- atm_force_charge(priv->lecd, data->truesize);
+ atm_force_charge(vcc, data->truesize);
skb_queue_tail(&sk->sk_receive_queue, data);
sk->sk_data_ready(sk);
}
+ rcu_read_unlock();
return 0;
}
@@ -618,7 +640,7 @@ static void lec_push(struct atm_vcc *vcc, struct sk_buff *skb)
atm_return(vcc, skb->truesize);
if (*(__be16 *) skb->data == htons(priv->lecid) ||
- !priv->lecd || !(dev->flags & IFF_UP)) {
+ !rcu_access_pointer(priv->lecd) || !(dev->flags & IFF_UP)) {
/*
* Probably looping back, or if lecd is missing,
* lecd has gone down
@@ -753,12 +775,12 @@ static int lecd_attach(struct atm_vcc *vcc, int arg)
priv = netdev_priv(dev_lec[i]);
} else {
priv = netdev_priv(dev_lec[i]);
- if (priv->lecd)
+ if (rcu_access_pointer(priv->lecd))
return -EADDRINUSE;
}
lec_arp_init(priv);
priv->itfnum = i; /* LANE2 addition */
- priv->lecd = vcc;
+ rcu_assign_pointer(priv->lecd, vcc);
vcc->dev = &lecatm_dev;
vcc_insert_socket(sk_atm(vcc));
diff --git a/net/atm/lec.h b/net/atm/lec.h
index be0e2667bd8c..ec85709bf818 100644
--- a/net/atm/lec.h
+++ b/net/atm/lec.h
@@ -91,7 +91,7 @@ struct lec_priv {
*/
spinlock_t lec_arp_lock;
struct atm_vcc *mcast_vcc; /* Default Multicast Send VCC */
- struct atm_vcc *lecd;
+ struct atm_vcc __rcu *lecd;
struct delayed_work lec_arp_work; /* C10 */
unsigned int maximum_unknown_frame_count;
/*
diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c
index b75c2228e69a..f28e9cbf8ad5 100644
--- a/net/batman-adv/bat_iv_ogm.c
+++ b/net/batman-adv/bat_iv_ogm.c
@@ -473,6 +473,9 @@ batadv_iv_ogm_can_aggregate(const struct batadv_ogm_packet *new_bat_ogm_packet,
if (aggregated_bytes > max_bytes)
return false;
+ if (skb_tailroom(forw_packet->skb) < packet_len)
+ return false;
+
if (packet_num >= BATADV_MAX_AGGREGATION_PACKETS)
return false;
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index 4719dac07190..6eb59e9f2aa8 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -1944,6 +1944,8 @@ static bool hci_le_set_cig_params(struct hci_conn *conn, struct bt_iso_qos *qos)
return false;
done:
+ conn->iso_qos = *qos;
+
if (hci_cmd_sync_queue(hdev, set_cig_params_sync,
UINT_PTR(qos->ucast.cig), NULL) < 0)
return false;
@@ -2013,8 +2015,6 @@ struct hci_conn *hci_bind_cis(struct hci_dev *hdev, bdaddr_t *dst,
}
hci_conn_hold(cis);
-
- cis->iso_qos = *qos;
cis->state = BT_BOUND;
return cis;
diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
index 121dbc8208ec..3166914b0d6c 100644
--- a/net/bluetooth/hci_sync.c
+++ b/net/bluetooth/hci_sync.c
@@ -6627,8 +6627,8 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
* state.
*/
if (hci_dev_test_flag(hdev, HCI_LE_SCAN)) {
- hci_scan_disable_sync(hdev);
hci_dev_set_flag(hdev, HCI_LE_SCAN_INTERRUPTED);
+ hci_scan_disable_sync(hdev);
}
/* Update random address, but set require_privacy to false so
diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c
index 6fe815241b01..7bcf8c5ceaee 100644
--- a/net/bluetooth/hidp/core.c
+++ b/net/bluetooth/hidp/core.c
@@ -986,7 +986,8 @@ static void session_free(struct kref *ref)
skb_queue_purge(&session->intr_transmit);
fput(session->intr_sock->file);
fput(session->ctrl_sock->file);
- l2cap_conn_put(session->conn);
+ if (session->conn)
+ l2cap_conn_put(session->conn);
kfree(session);
}
@@ -1164,6 +1165,15 @@ static void hidp_session_remove(struct l2cap_conn *conn,
down_write(&hidp_session_sem);
+ /* Drop L2CAP reference immediately to indicate that
+ * l2cap_unregister_user() shall not be called as it is already
+ * considered removed.
+ */
+ if (session->conn) {
+ l2cap_conn_put(session->conn);
+ session->conn = NULL;
+ }
+
hidp_session_terminate(session);
cancel_work_sync(&session->dev_init);
@@ -1301,7 +1311,9 @@ static int hidp_session_thread(void *arg)
* Instead, this call has the same semantics as if user-space tried to
* delete the session.
*/
- l2cap_unregister_user(session->conn, &session->user);
+ if (session->conn)
+ l2cap_unregister_user(session->conn, &session->user);
+
hidp_session_put(session);
module_put_and_kthread_exit(0);
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index ad98db9632fd..5deb6c4f1e41 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -1678,17 +1678,15 @@ static void l2cap_info_timeout(struct work_struct *work)
int l2cap_register_user(struct l2cap_conn *conn, struct l2cap_user *user)
{
- struct hci_dev *hdev = conn->hcon->hdev;
int ret;
/* We need to check whether l2cap_conn is registered. If it is not, we
- * must not register the l2cap_user. l2cap_conn_del() is unregisters
- * l2cap_conn objects, but doesn't provide its own locking. Instead, it
- * relies on the parent hci_conn object to be locked. This itself relies
- * on the hci_dev object to be locked. So we must lock the hci device
- * here, too. */
+ * must not register the l2cap_user. l2cap_conn_del() unregisters
+ * l2cap_conn objects under conn->lock, and we use the same lock here
+ * to protect access to conn->users and conn->hchan.
+ */
- hci_dev_lock(hdev);
+ mutex_lock(&conn->lock);
if (!list_empty(&user->list)) {
ret = -EINVAL;
@@ -1709,16 +1707,14 @@ int l2cap_register_user(struct l2cap_conn *conn, struct l2cap_user *user)
ret = 0;
out_unlock:
- hci_dev_unlock(hdev);
+ mutex_unlock(&conn->lock);
return ret;
}
EXPORT_SYMBOL(l2cap_register_user);
void l2cap_unregister_user(struct l2cap_conn *conn, struct l2cap_user *user)
{
- struct hci_dev *hdev = conn->hcon->hdev;
-
- hci_dev_lock(hdev);
+ mutex_lock(&conn->lock);
if (list_empty(&user->list))
goto out_unlock;
@@ -1727,7 +1723,7 @@ void l2cap_unregister_user(struct l2cap_conn *conn, struct l2cap_user *user)
user->remove(conn, user);
out_unlock:
- hci_dev_unlock(hdev);
+ mutex_unlock(&conn->lock);
}
EXPORT_SYMBOL(l2cap_unregister_user);
@@ -4616,7 +4612,8 @@ static inline int l2cap_information_rsp(struct l2cap_conn *conn,
switch (type) {
case L2CAP_IT_FEAT_MASK:
- conn->feat_mask = get_unaligned_le32(rsp->data);
+ if (cmd_len >= sizeof(*rsp) + sizeof(u32))
+ conn->feat_mask = get_unaligned_le32(rsp->data);
if (conn->feat_mask & L2CAP_FEAT_FIXED_CHAN) {
struct l2cap_info_req req;
@@ -4635,7 +4632,8 @@ static inline int l2cap_information_rsp(struct l2cap_conn *conn,
break;
case L2CAP_IT_FIXED_CHAN:
- conn->remote_fixed_chan = rsp->data[0];
+ if (cmd_len >= sizeof(*rsp) + sizeof(rsp->data[0]))
+ conn->remote_fixed_chan = rsp->data[0];
conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE;
conn->info_ident = 0;
@@ -5059,7 +5057,7 @@ static inline int l2cap_ecred_conn_req(struct l2cap_conn *conn,
u16 mtu, mps;
__le16 psm;
u8 result, rsp_len = 0;
- int i, num_scid;
+ int i, num_scid = 0;
bool defer = false;
if (!enable_ecred)
@@ -5072,6 +5070,14 @@ static inline int l2cap_ecred_conn_req(struct l2cap_conn *conn,
goto response;
}
+ /* Check if there are no pending channels with the same ident */
+ __l2cap_chan_list_id(conn, cmd->ident, l2cap_ecred_list_defer,
+ &num_scid);
+ if (num_scid) {
+ result = L2CAP_CR_LE_INVALID_PARAMS;
+ goto response;
+ }
+
cmd_len -= sizeof(*req);
num_scid = cmd_len / sizeof(u16);
@@ -5424,7 +5430,7 @@ static inline int l2cap_ecred_reconf_rsp(struct l2cap_conn *conn,
u8 *data)
{
struct l2cap_chan *chan, *tmp;
- struct l2cap_ecred_conn_rsp *rsp = (void *) data;
+ struct l2cap_ecred_reconf_rsp *rsp = (void *)data;
u16 result;
if (cmd_len < sizeof(*rsp))
@@ -5432,7 +5438,7 @@ static inline int l2cap_ecred_reconf_rsp(struct l2cap_conn *conn,
result = __le16_to_cpu(rsp->result);
- BT_DBG("result 0x%4.4x", rsp->result);
+ BT_DBG("result 0x%4.4x", result);
if (!result)
return 0;
@@ -6662,8 +6668,10 @@ static int l2cap_ecred_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb)
return -ENOBUFS;
}
- if (chan->imtu < skb->len) {
- BT_ERR("Too big LE L2CAP PDU");
+ if (skb->len > chan->imtu) {
+ BT_ERR("Too big LE L2CAP PDU: len %u > %u", skb->len,
+ chan->imtu);
+ l2cap_send_disconn_req(chan, ECONNRESET);
return -ENOBUFS;
}
@@ -6689,7 +6697,9 @@ static int l2cap_ecred_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb)
sdu_len, skb->len, chan->imtu);
if (sdu_len > chan->imtu) {
- BT_ERR("Too big LE L2CAP SDU length received");
+ BT_ERR("Too big LE L2CAP SDU length: len %u > %u",
+ skb->len, sdu_len);
+ l2cap_send_disconn_req(chan, ECONNRESET);
err = -EMSGSIZE;
goto failed;
}
@@ -6725,6 +6735,7 @@ static int l2cap_ecred_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb)
if (chan->sdu->len + skb->len > chan->sdu_len) {
BT_ERR("Too much LE L2CAP data received");
+ l2cap_send_disconn_req(chan, ECONNRESET);
err = -EINVAL;
goto failed;
}
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index a7238fd3b03b..d52238ce6a9a 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -2195,10 +2195,7 @@ static void set_mesh_complete(struct hci_dev *hdev, void *data, int err)
sk = cmd->sk;
if (status) {
- mgmt_cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_MESH_RECEIVER,
- status);
- mgmt_pending_foreach(MGMT_OP_SET_MESH_RECEIVER, hdev, true,
- cmd_status_rsp, &status);
+ mgmt_cmd_status(cmd->sk, hdev->id, cmd->opcode, status);
goto done;
}
@@ -5377,7 +5374,7 @@ static void mgmt_add_adv_patterns_monitor_complete(struct hci_dev *hdev,
mgmt_cmd_complete(cmd->sk, cmd->hdev->id, cmd->opcode,
mgmt_status(status), &rp, sizeof(rp));
- mgmt_pending_remove(cmd);
+ mgmt_pending_free(cmd);
hci_dev_unlock(hdev);
bt_dev_dbg(hdev, "add monitor %d complete, status %d",
diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
index e67bf7b34ea7..485e3468bd26 100644
--- a/net/bluetooth/smp.c
+++ b/net/bluetooth/smp.c
@@ -2743,7 +2743,7 @@ static int smp_cmd_public_key(struct l2cap_conn *conn, struct sk_buff *skb)
if (!test_bit(SMP_FLAG_DEBUG_KEY, &smp->flags) &&
!crypto_memneq(key, smp->local_pk, 64)) {
bt_dev_err(hdev, "Remote and local public keys are identical");
- return SMP_UNSPECIFIED;
+ return SMP_DHKEY_CHECK_FAILED;
}
memcpy(smp->remote_pk, key, 64);
diff --git a/net/bridge/br_cfm.c b/net/bridge/br_cfm.c
index 2c70fe47de38..118c7ea48c35 100644
--- a/net/bridge/br_cfm.c
+++ b/net/bridge/br_cfm.c
@@ -576,7 +576,7 @@ static void mep_delete_implementation(struct net_bridge *br,
/* Empty and free peer MEP list */
hlist_for_each_entry_safe(peer_mep, n_store, &mep->peer_mep_list, head) {
- cancel_delayed_work_sync(&peer_mep->ccm_rx_dwork);
+ disable_delayed_work_sync(&peer_mep->ccm_rx_dwork);
hlist_del_rcu(&peer_mep->head);
kfree_rcu(peer_mep, rcu);
}
@@ -732,7 +732,7 @@ int br_cfm_cc_peer_mep_remove(struct net_bridge *br, const u32 instance,
return -ENOENT;
}
- cc_peer_disable(peer_mep);
+ disable_delayed_work_sync(&peer_mep->ccm_rx_dwork);
hlist_del_rcu(&peer_mep->head);
kfree_rcu(peer_mep, rcu);
diff --git a/net/ethernet/eth.c b/net/ethernet/eth.c
index 13a63b48b7ee..d9faadbe9b6c 100644
--- a/net/ethernet/eth.c
+++ b/net/ethernet/eth.c
@@ -193,14 +193,11 @@ __be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev)
}
EXPORT_SYMBOL(eth_type_trans);
-/**
- * eth_header_parse - extract hardware address from packet
- * @skb: packet to extract header from
- * @haddr: destination buffer
- */
-int eth_header_parse(const struct sk_buff *skb, unsigned char *haddr)
+int eth_header_parse(const struct sk_buff *skb, const struct net_device *dev,
+ unsigned char *haddr)
{
const struct ethhdr *eth = eth_hdr(skb);
+
memcpy(haddr, eth->h_source, ETH_ALEN);
return ETH_ALEN;
}
diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c
index a62b4c4033cc..568bd1e95d44 100644
--- a/net/ipv4/icmp.c
+++ b/net/ipv4/icmp.c
@@ -1079,10 +1079,12 @@ out:
static bool icmp_tag_validation(int proto)
{
+ const struct net_protocol *ipprot;
bool ok;
rcu_read_lock();
- ok = rcu_dereference(inet_protos[proto])->icmp_strict_tag_validation;
+ ipprot = rcu_dereference(inet_protos[proto]);
+ ok = ipprot ? ipprot->icmp_strict_tag_validation : false;
rcu_read_unlock();
return ok;
}
diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c
index e13244729ad8..35f0baa99d40 100644
--- a/net/ipv4/ip_gre.c
+++ b/net/ipv4/ip_gre.c
@@ -919,7 +919,8 @@ static int ipgre_header(struct sk_buff *skb, struct net_device *dev,
return -(t->hlen + sizeof(*iph));
}
-static int ipgre_header_parse(const struct sk_buff *skb, unsigned char *haddr)
+static int ipgre_header_parse(const struct sk_buff *skb, const struct net_device *dev,
+ unsigned char *haddr)
{
const struct iphdr *iph = (const struct iphdr *) skb_mac_header(skb);
memcpy(haddr, &iph->saddr, 4);
diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c
index 5e3610a926cf..95558fd6f447 100644
--- a/net/ipv6/exthdrs.c
+++ b/net/ipv6/exthdrs.c
@@ -379,6 +379,10 @@ static int ipv6_srh_rcv(struct sk_buff *skb)
hdr = (struct ipv6_sr_hdr *)skb_transport_header(skb);
idev = __in6_dev_get(skb->dev);
+ if (!idev) {
+ kfree_skb(skb);
+ return -1;
+ }
accept_seg6 = min(READ_ONCE(net->ipv6.devconf_all->seg6_enabled),
READ_ONCE(idev->cnf.seg6_enabled));
diff --git a/net/ipv6/seg6_hmac.c b/net/ipv6/seg6_hmac.c
index ee6bac0160ac..e6964c6b0d38 100644
--- a/net/ipv6/seg6_hmac.c
+++ b/net/ipv6/seg6_hmac.c
@@ -184,6 +184,8 @@ bool seg6_hmac_validate_skb(struct sk_buff *skb)
int require_hmac;
idev = __in6_dev_get(skb->dev);
+ if (!idev)
+ return false;
srh = (struct ipv6_sr_hdr *)skb_transport_header(skb);
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index b92b4a5c2636..b85375ceb575 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -1904,12 +1904,6 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev,
__sta_info_flush(sdata, true, link_id, NULL);
- ieee80211_remove_link_keys(link, &keys);
- if (!list_empty(&keys)) {
- synchronize_net();
- ieee80211_free_key_list(local, &keys);
- }
-
ieee80211_stop_mbssid(sdata);
RCU_INIT_POINTER(link_conf->tx_bss_conf, NULL);
@@ -1921,6 +1915,12 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev,
ieee80211_link_info_change_notify(sdata, link,
BSS_CHANGED_BEACON_ENABLED);
+ ieee80211_remove_link_keys(link, &keys);
+ if (!list_empty(&keys)) {
+ synchronize_net();
+ ieee80211_free_key_list(local, &keys);
+ }
+
if (sdata->wdev.links[link_id].cac_started) {
chandef = link_conf->chanreq.oper;
wiphy_hrtimer_work_cancel(wiphy, &link->dfs_cac_timer_work);
diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
index 4447cf03c41b..05f45e66999b 100644
--- a/net/mac80211/chan.c
+++ b/net/mac80211/chan.c
@@ -561,14 +561,16 @@ static void ieee80211_chan_bw_change(struct ieee80211_local *local,
rcu_read_lock();
list_for_each_entry_rcu(sta, &local->sta_list,
list) {
- struct ieee80211_sub_if_data *sdata = sta->sdata;
+ struct ieee80211_sub_if_data *sdata;
enum ieee80211_sta_rx_bandwidth new_sta_bw;
unsigned int link_id;
if (!ieee80211_sdata_running(sta->sdata))
continue;
- for (link_id = 0; link_id < ARRAY_SIZE(sta->sdata->link); link_id++) {
+ sdata = get_bss_sdata(sta->sdata);
+
+ for (link_id = 0; link_id < ARRAY_SIZE(sdata->link); link_id++) {
struct ieee80211_link_data *link =
rcu_dereference(sdata->link[link_id]);
struct ieee80211_bss_conf *link_conf;
diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c
index d02f07368c51..687a66cd4943 100644
--- a/net/mac80211/debugfs.c
+++ b/net/mac80211/debugfs.c
@@ -320,7 +320,6 @@ static ssize_t aql_enable_read(struct file *file, char __user *user_buf,
static ssize_t aql_enable_write(struct file *file, const char __user *user_buf,
size_t count, loff_t *ppos)
{
- bool aql_disabled = static_key_false(&aql_disable.key);
char buf[3];
size_t len;
@@ -335,15 +334,12 @@ static ssize_t aql_enable_write(struct file *file, const char __user *user_buf,
if (len > 0 && buf[len - 1] == '\n')
buf[len - 1] = 0;
- if (buf[0] == '0' && buf[1] == '\0') {
- if (!aql_disabled)
- static_branch_inc(&aql_disable);
- } else if (buf[0] == '1' && buf[1] == '\0') {
- if (aql_disabled)
- static_branch_dec(&aql_disable);
- } else {
+ if (buf[0] == '0' && buf[1] == '\0')
+ static_branch_enable(&aql_disable);
+ else if (buf[0] == '1' && buf[1] == '\0')
+ static_branch_disable(&aql_disable);
+ else
return -EINVAL;
- }
return count;
}
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index 28624e57aa49..8fdbdf9ba2a9 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -79,6 +79,9 @@ bool mesh_matches_local(struct ieee80211_sub_if_data *sdata,
* - MDA enabled
* - Power management control on fc
*/
+ if (!ie->mesh_config)
+ return false;
+
if (!(ifmsh->mesh_id_len == ie->mesh_id_len &&
memcmp(ifmsh->mesh_id, ie->mesh_id, ie->mesh_id_len) == 0 &&
(ifmsh->mesh_pp_id == ie->mesh_config->meshconf_psel) &&
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index 6dc22f1593be..dd51a578fbc5 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -2782,7 +2782,9 @@ static void sta_set_link_sinfo(struct sta_info *sta,
}
link_sinfo->inactive_time =
- jiffies_to_msecs(jiffies - ieee80211_sta_last_active(sta, link_id));
+ jiffies_delta_to_msecs(jiffies -
+ ieee80211_sta_last_active(sta,
+ link_id));
if (!(link_sinfo->filled & (BIT_ULL(NL80211_STA_INFO_TX_BYTES64) |
BIT_ULL(NL80211_STA_INFO_TX_BYTES)))) {
@@ -3015,7 +3017,8 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo,
sinfo->connected_time = ktime_get_seconds() - sta->last_connected;
sinfo->assoc_at = sta->assoc_at;
sinfo->inactive_time =
- jiffies_to_msecs(jiffies - ieee80211_sta_last_active(sta, -1));
+ jiffies_delta_to_msecs(jiffies -
+ ieee80211_sta_last_active(sta, -1));
if (!(sinfo->filled & (BIT_ULL(NL80211_STA_INFO_TX_BYTES64) |
BIT_ULL(NL80211_STA_INFO_TX_BYTES)))) {
diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c
index dbbfe2d6842f..1dca2fae05a5 100644
--- a/net/mac80211/tdls.c
+++ b/net/mac80211/tdls.c
@@ -1449,7 +1449,7 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
}
sta = sta_info_get(sdata, peer);
- if (!sta)
+ if (!sta || !sta->sta.tdls)
return -ENOLINK;
iee80211_tdls_recalc_chanctx(sdata, sta);
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 8cdbd417d7be..b7aedaab8483 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -1899,8 +1899,10 @@ bool ieee80211_tx_prepare_skb(struct ieee80211_hw *hw,
struct ieee80211_tx_data tx;
struct sk_buff *skb2;
- if (ieee80211_tx_prepare(sdata, &tx, NULL, skb) == TX_DROP)
+ if (ieee80211_tx_prepare(sdata, &tx, NULL, skb) == TX_DROP) {
+ kfree_skb(skb);
return false;
+ }
info->band = band;
info->control.vif = vif;
diff --git a/net/mac802154/iface.c b/net/mac802154/iface.c
index 9e4631fade90..000be60d9580 100644
--- a/net/mac802154/iface.c
+++ b/net/mac802154/iface.c
@@ -469,7 +469,9 @@ static int mac802154_header_create(struct sk_buff *skb,
}
static int
-mac802154_header_parse(const struct sk_buff *skb, unsigned char *haddr)
+mac802154_header_parse(const struct sk_buff *skb,
+ const struct net_device *dev,
+ unsigned char *haddr)
{
struct ieee802154_hdr hdr;
diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c
index ef9e749d5e08..d5417688f69e 100644
--- a/net/mpls/af_mpls.c
+++ b/net/mpls/af_mpls.c
@@ -2854,6 +2854,7 @@ out_unregister_rtnl_af:
rtnl_af_unregister(&mpls_af_ops);
out_unregister_dev_type:
dev_remove_pack(&mpls_packet_type);
+ unregister_netdevice_notifier(&mpls_dev_notifier);
out_unregister_pernet:
unregister_pernet_subsys(&mpls_net_ops);
goto out;
diff --git a/net/mptcp/pm_kernel.c b/net/mptcp/pm_kernel.c
index b2b9df43960e..82e59f9c6dd9 100644
--- a/net/mptcp/pm_kernel.c
+++ b/net/mptcp/pm_kernel.c
@@ -838,7 +838,7 @@ static struct lock_class_key mptcp_keys[2];
static int mptcp_pm_nl_create_listen_socket(struct sock *sk,
struct mptcp_pm_addr_entry *entry)
{
- bool is_ipv6 = sk->sk_family == AF_INET6;
+ bool is_ipv6 = entry->addr.family == AF_INET6;
int addrlen = sizeof(struct sockaddr_in);
struct sockaddr_storage addr;
struct sock *newsk, *ssk;
diff --git a/net/netfilter/nf_bpf_link.c b/net/netfilter/nf_bpf_link.c
index 6f3a6411f4af..c20031891b86 100644
--- a/net/netfilter/nf_bpf_link.c
+++ b/net/netfilter/nf_bpf_link.c
@@ -170,7 +170,7 @@ static int bpf_nf_link_update(struct bpf_link *link, struct bpf_prog *new_prog,
static const struct bpf_link_ops bpf_nf_link_lops = {
.release = bpf_nf_link_release,
- .dealloc = bpf_nf_link_dealloc,
+ .dealloc_deferred = bpf_nf_link_dealloc,
.detach = bpf_nf_link_detach,
.show_fdinfo = bpf_nf_link_show_info,
.fill_link_info = bpf_nf_link_fill_link_info,
diff --git a/net/netfilter/nf_conntrack_h323_asn1.c b/net/netfilter/nf_conntrack_h323_asn1.c
index 62aa22a07876..7b1497ed97d2 100644
--- a/net/netfilter/nf_conntrack_h323_asn1.c
+++ b/net/netfilter/nf_conntrack_h323_asn1.c
@@ -331,6 +331,8 @@ static int decode_int(struct bitstr *bs, const struct field_t *f,
if (nf_h323_error_boundary(bs, 0, 2))
return H323_ERROR_BOUND;
len = get_bits(bs, 2) + 1;
+ if (nf_h323_error_boundary(bs, len, 0))
+ return H323_ERROR_BOUND;
BYTE_ALIGN(bs);
if (base && (f->attr & DECODE)) { /* timeToLive */
unsigned int v = get_uint(bs, len) + f->lb;
@@ -922,6 +924,8 @@ int DecodeQ931(unsigned char *buf, size_t sz, Q931 *q931)
break;
p++;
len--;
+ if (len <= 0)
+ break;
return DecodeH323_UserInformation(buf, p, len,
&q931->UUIE);
}
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
index c9d725fc2d71..c156574e1273 100644
--- a/net/netfilter/nf_conntrack_netlink.c
+++ b/net/netfilter/nf_conntrack_netlink.c
@@ -3212,7 +3212,7 @@ ctnetlink_exp_ct_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
{
struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
struct nf_conn *ct = cb->data;
- struct nf_conn_help *help = nfct_help(ct);
+ struct nf_conn_help *help;
u_int8_t l3proto = nfmsg->nfgen_family;
unsigned long last_id = cb->args[1];
struct nf_conntrack_expect *exp;
@@ -3220,6 +3220,10 @@ ctnetlink_exp_ct_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
if (cb->args[0])
return 0;
+ help = nfct_help(ct);
+ if (!help)
+ return 0;
+
rcu_read_lock();
restart:
@@ -3249,6 +3253,24 @@ out:
return skb->len;
}
+static int ctnetlink_dump_exp_ct_start(struct netlink_callback *cb)
+{
+ struct nf_conn *ct = cb->data;
+
+ if (!refcount_inc_not_zero(&ct->ct_general.use))
+ return -ENOENT;
+ return 0;
+}
+
+static int ctnetlink_dump_exp_ct_done(struct netlink_callback *cb)
+{
+ struct nf_conn *ct = cb->data;
+
+ if (ct)
+ nf_ct_put(ct);
+ return 0;
+}
+
static int ctnetlink_dump_exp_ct(struct net *net, struct sock *ctnl,
struct sk_buff *skb,
const struct nlmsghdr *nlh,
@@ -3264,6 +3286,8 @@ static int ctnetlink_dump_exp_ct(struct net *net, struct sock *ctnl,
struct nf_conntrack_zone zone;
struct netlink_dump_control c = {
.dump = ctnetlink_exp_ct_dump_table,
+ .start = ctnetlink_dump_exp_ct_start,
+ .done = ctnetlink_dump_exp_ct_done,
};
err = ctnetlink_parse_tuple(cda, &tuple, CTA_EXPECT_MASTER,
@@ -3465,7 +3489,7 @@ ctnetlink_change_expect(struct nf_conntrack_expect *x,
#if IS_ENABLED(CONFIG_NF_NAT)
static const struct nla_policy exp_nat_nla_policy[CTA_EXPECT_NAT_MAX+1] = {
- [CTA_EXPECT_NAT_DIR] = { .type = NLA_U32 },
+ [CTA_EXPECT_NAT_DIR] = NLA_POLICY_MAX(NLA_BE32, IP_CT_DIR_REPLY),
[CTA_EXPECT_NAT_TUPLE] = { .type = NLA_NESTED },
};
#endif
diff --git a/net/netfilter/nf_conntrack_proto_sctp.c b/net/netfilter/nf_conntrack_proto_sctp.c
index 7c6f7c9f7332..645d2c43ebf7 100644
--- a/net/netfilter/nf_conntrack_proto_sctp.c
+++ b/net/netfilter/nf_conntrack_proto_sctp.c
@@ -582,7 +582,8 @@ nla_put_failure:
}
static const struct nla_policy sctp_nla_policy[CTA_PROTOINFO_SCTP_MAX+1] = {
- [CTA_PROTOINFO_SCTP_STATE] = { .type = NLA_U8 },
+ [CTA_PROTOINFO_SCTP_STATE] = NLA_POLICY_MAX(NLA_U8,
+ SCTP_CONNTRACK_HEARTBEAT_SENT),
[CTA_PROTOINFO_SCTP_VTAG_ORIGINAL] = { .type = NLA_U32 },
[CTA_PROTOINFO_SCTP_VTAG_REPLY] = { .type = NLA_U32 },
};
diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c
index ca748f8dbff1..4ab5ef71d96d 100644
--- a/net/netfilter/nf_conntrack_sip.c
+++ b/net/netfilter/nf_conntrack_sip.c
@@ -1534,11 +1534,12 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff,
{
struct tcphdr *th, _tcph;
unsigned int dataoff, datalen;
- unsigned int matchoff, matchlen, clen;
+ unsigned int matchoff, matchlen;
unsigned int msglen, origlen;
const char *dptr, *end;
s16 diff, tdiff = 0;
int ret = NF_ACCEPT;
+ unsigned long clen;
bool term;
if (ctinfo != IP_CT_ESTABLISHED &&
@@ -1573,6 +1574,9 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff,
if (dptr + matchoff == end)
break;
+ if (clen > datalen)
+ break;
+
term = false;
for (; end + strlen("\r\n\r\n") <= dptr + datalen; end++) {
if (end[0] == '\r' && end[1] == '\n' &&
diff --git a/net/netfilter/nf_flow_table_ip.c b/net/netfilter/nf_flow_table_ip.c
index 3fdb10d9bf7f..fd56d663cb5b 100644
--- a/net/netfilter/nf_flow_table_ip.c
+++ b/net/netfilter/nf_flow_table_ip.c
@@ -738,6 +738,7 @@ static int nf_flow_encap_push(struct sk_buff *skb,
switch (tuple->encap[i].proto) {
case htons(ETH_P_8021Q):
case htons(ETH_P_8021AD):
+ skb_reset_mac_header(skb);
if (skb_vlan_push(skb, tuple->encap[i].proto,
tuple->encap[i].id) < 0)
return -1;
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index dacec5f8a11c..3922cff1bb3d 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -6744,8 +6744,8 @@ static void __nft_set_elem_expr_destroy(const struct nft_ctx *ctx,
}
}
-static void nft_set_elem_expr_destroy(const struct nft_ctx *ctx,
- struct nft_set_elem_expr *elem_expr)
+void nft_set_elem_expr_destroy(const struct nft_ctx *ctx,
+ struct nft_set_elem_expr *elem_expr)
{
struct nft_expr *expr;
u32 size;
@@ -7156,8 +7156,7 @@ static u32 nft_set_maxsize(const struct nft_set *set)
}
static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
- const struct nlattr *attr, u32 nlmsg_flags,
- bool last)
+ const struct nlattr *attr, u32 nlmsg_flags)
{
struct nft_expr *expr_array[NFT_SET_EXPR_MAX] = {};
struct nlattr *nla[NFTA_SET_ELEM_MAX + 1];
@@ -7444,11 +7443,6 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
if (flags)
*nft_set_ext_flags(ext) = flags;
- if (last)
- elem.flags = NFT_SET_ELEM_INTERNAL_LAST;
- else
- elem.flags = 0;
-
if (obj)
*nft_set_ext_obj(ext) = obj;
@@ -7613,8 +7607,7 @@ static int nf_tables_newsetelem(struct sk_buff *skb,
nft_ctx_init(&ctx, net, skb, info->nlh, family, table, NULL, nla);
nla_for_each_nested(attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS], rem) {
- err = nft_add_set_elem(&ctx, set, attr, info->nlh->nlmsg_flags,
- nla_is_last(attr, rem));
+ err = nft_add_set_elem(&ctx, set, attr, info->nlh->nlmsg_flags);
if (err < 0) {
NL_SET_BAD_ATTR(extack, attr);
return err;
@@ -7738,7 +7731,7 @@ static void nft_trans_elems_destroy_abort(const struct nft_ctx *ctx,
}
static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set,
- const struct nlattr *attr, bool last)
+ const struct nlattr *attr)
{
struct nlattr *nla[NFTA_SET_ELEM_MAX + 1];
struct nft_set_ext_tmpl tmpl;
@@ -7806,11 +7799,6 @@ static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set,
if (flags)
*nft_set_ext_flags(ext) = flags;
- if (last)
- elem.flags = NFT_SET_ELEM_INTERNAL_LAST;
- else
- elem.flags = 0;
-
trans = nft_trans_elem_alloc(ctx, NFT_MSG_DELSETELEM, set);
if (trans == NULL)
goto fail_trans;
@@ -7961,8 +7949,7 @@ static int nf_tables_delsetelem(struct sk_buff *skb,
return nft_set_flush(&ctx, set, genmask);
nla_for_each_nested(attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS], rem) {
- err = nft_del_setelem(&ctx, set, attr,
- nla_is_last(attr, rem));
+ err = nft_del_setelem(&ctx, set, attr);
if (err == -ENOENT &&
NFNL_MSG_TYPE(info->nlh->nlmsg_type) == NFT_MSG_DESTROYSETELEM)
continue;
@@ -9216,6 +9203,7 @@ static int nf_tables_newflowtable(struct sk_buff *skb,
return 0;
err_flowtable_hooks:
+ synchronize_rcu();
nft_trans_destroy(trans);
err_flowtable_trans:
nft_hooks_destroy(&flowtable->hook_list);
diff --git a/net/netfilter/nfnetlink_osf.c b/net/netfilter/nfnetlink_osf.c
index 94e3eac5743a..45d9ad231a92 100644
--- a/net/netfilter/nfnetlink_osf.c
+++ b/net/netfilter/nfnetlink_osf.c
@@ -302,7 +302,9 @@ static int nfnl_osf_add_callback(struct sk_buff *skb,
{
struct nf_osf_user_finger *f;
struct nf_osf_finger *kf = NULL, *sf;
+ unsigned int tot_opt_len = 0;
int err = 0;
+ int i;
if (!capable(CAP_NET_ADMIN))
return -EPERM;
@@ -318,6 +320,17 @@ static int nfnl_osf_add_callback(struct sk_buff *skb,
if (f->opt_num > ARRAY_SIZE(f->opt))
return -EINVAL;
+ for (i = 0; i < f->opt_num; i++) {
+ if (!f->opt[i].length || f->opt[i].length > MAX_IPOPTLEN)
+ return -EINVAL;
+ if (f->opt[i].kind == OSFOPT_MSS && f->opt[i].length < 4)
+ return -EINVAL;
+
+ tot_opt_len += f->opt[i].length;
+ if (tot_opt_len > MAX_IPOPTLEN)
+ return -EINVAL;
+ }
+
if (!memchr(f->genre, 0, MAXGENRELEN) ||
!memchr(f->subtype, 0, MAXGENRELEN) ||
!memchr(f->version, 0, MAXGENRELEN))
diff --git a/net/netfilter/nft_ct.c b/net/netfilter/nft_ct.c
index 47d3ef109a99..128ff8155b5d 100644
--- a/net/netfilter/nft_ct.c
+++ b/net/netfilter/nft_ct.c
@@ -23,6 +23,7 @@
#include <net/netfilter/nf_conntrack_l4proto.h>
#include <net/netfilter/nf_conntrack_expect.h>
#include <net/netfilter/nf_conntrack_seqadj.h>
+#include "nf_internals.h"
struct nft_ct_helper_obj {
struct nf_conntrack_helper *helper4;
@@ -543,6 +544,7 @@ static void __nft_ct_set_destroy(const struct nft_ctx *ctx, struct nft_ct *priv)
#endif
#ifdef CONFIG_NF_CONNTRACK_ZONES
case NFT_CT_ZONE:
+ nf_queue_nf_hook_drop(ctx->net);
mutex_lock(&nft_ct_pcpu_mutex);
if (--nft_ct_pcpu_template_refcnt == 0)
nft_ct_tmpl_put_pcpu();
@@ -1015,6 +1017,7 @@ static void nft_ct_timeout_obj_destroy(const struct nft_ctx *ctx,
struct nft_ct_timeout_obj *priv = nft_obj_data(obj);
struct nf_ct_timeout *timeout = priv->timeout;
+ nf_queue_nf_hook_drop(ctx->net);
nf_ct_untimeout(ctx->net, timeout);
nf_ct_netns_put(ctx->net, ctx->family);
kfree(priv->timeout);
@@ -1147,6 +1150,7 @@ static void nft_ct_helper_obj_destroy(const struct nft_ctx *ctx,
{
struct nft_ct_helper_obj *priv = nft_obj_data(obj);
+ nf_queue_nf_hook_drop(ctx->net);
if (priv->helper4)
nf_conntrack_helper_put(priv->helper4);
if (priv->helper6)
diff --git a/net/netfilter/nft_dynset.c b/net/netfilter/nft_dynset.c
index 7807d8129664..9123277be03c 100644
--- a/net/netfilter/nft_dynset.c
+++ b/net/netfilter/nft_dynset.c
@@ -30,18 +30,26 @@ static int nft_dynset_expr_setup(const struct nft_dynset *priv,
const struct nft_set_ext *ext)
{
struct nft_set_elem_expr *elem_expr = nft_set_ext_expr(ext);
+ struct nft_ctx ctx = {
+ .net = read_pnet(&priv->set->net),
+ .family = priv->set->table->family,
+ };
struct nft_expr *expr;
int i;
for (i = 0; i < priv->num_exprs; i++) {
expr = nft_setelem_expr_at(elem_expr, elem_expr->size);
if (nft_expr_clone(expr, priv->expr_array[i], GFP_ATOMIC) < 0)
- return -1;
+ goto err_out;
elem_expr->size += priv->expr_array[i]->ops->size;
}
return 0;
+err_out:
+ nft_set_elem_expr_destroy(&ctx, elem_expr);
+
+ return -1;
}
struct nft_elem_priv *nft_dynset_new(struct nft_set *set,
diff --git a/net/netfilter/nft_set_rbtree.c b/net/netfilter/nft_set_rbtree.c
index ee3d4f5b9ff7..fe8bd497d74a 100644
--- a/net/netfilter/nft_set_rbtree.c
+++ b/net/netfilter/nft_set_rbtree.c
@@ -304,19 +304,10 @@ static void nft_rbtree_set_start_cookie(struct nft_rbtree *priv,
priv->start_rbe_cookie = (unsigned long)rbe;
}
-static void nft_rbtree_set_start_cookie_open(struct nft_rbtree *priv,
- const struct nft_rbtree_elem *rbe,
- unsigned long open_interval)
-{
- priv->start_rbe_cookie = (unsigned long)rbe | open_interval;
-}
-
-#define NFT_RBTREE_OPEN_INTERVAL 1UL
-
static bool nft_rbtree_cmp_start_cookie(struct nft_rbtree *priv,
const struct nft_rbtree_elem *rbe)
{
- return (priv->start_rbe_cookie & ~NFT_RBTREE_OPEN_INTERVAL) == (unsigned long)rbe;
+ return priv->start_rbe_cookie == (unsigned long)rbe;
}
static bool nft_rbtree_insert_same_interval(const struct net *net,
@@ -346,14 +337,13 @@ static bool nft_rbtree_insert_same_interval(const struct net *net,
static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set,
struct nft_rbtree_elem *new,
- struct nft_elem_priv **elem_priv, u64 tstamp, bool last)
+ struct nft_elem_priv **elem_priv, u64 tstamp)
{
struct nft_rbtree_elem *rbe, *rbe_le = NULL, *rbe_ge = NULL, *rbe_prev;
struct rb_node *node, *next, *parent, **p, *first = NULL;
struct nft_rbtree *priv = nft_set_priv(set);
u8 cur_genmask = nft_genmask_cur(net);
u8 genmask = nft_genmask_next(net);
- unsigned long open_interval = 0;
int d;
/* Descend the tree to search for an existing element greater than the
@@ -459,18 +449,10 @@ static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set,
}
}
- if (nft_rbtree_interval_null(set, new)) {
+ if (nft_rbtree_interval_null(set, new))
+ priv->start_rbe_cookie = 0;
+ else if (nft_rbtree_interval_start(new) && priv->start_rbe_cookie)
priv->start_rbe_cookie = 0;
- } else if (nft_rbtree_interval_start(new) && priv->start_rbe_cookie) {
- if (nft_set_is_anonymous(set)) {
- priv->start_rbe_cookie = 0;
- } else if (priv->start_rbe_cookie & NFT_RBTREE_OPEN_INTERVAL) {
- /* Previous element is an open interval that partially
- * overlaps with an existing non-open interval.
- */
- return -ENOTEMPTY;
- }
- }
/* - new start element matching existing start element: full overlap
* reported as -EEXIST, cleared by caller if NLM_F_EXCL is not given.
@@ -478,27 +460,7 @@ static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set,
if (rbe_ge && !nft_rbtree_cmp(set, new, rbe_ge) &&
nft_rbtree_interval_start(rbe_ge) == nft_rbtree_interval_start(new)) {
*elem_priv = &rbe_ge->priv;
-
- /* - Corner case: new start element of open interval (which
- * comes as last element in the batch) overlaps the start of
- * an existing interval with an end element: partial overlap.
- */
- node = rb_first(&priv->root);
- rbe = __nft_rbtree_next_active(node, genmask);
- if (rbe && nft_rbtree_interval_end(rbe)) {
- rbe = nft_rbtree_next_active(rbe, genmask);
- if (rbe &&
- nft_rbtree_interval_start(rbe) &&
- !nft_rbtree_cmp(set, new, rbe)) {
- if (last)
- return -ENOTEMPTY;
-
- /* Maybe open interval? */
- open_interval = NFT_RBTREE_OPEN_INTERVAL;
- }
- }
- nft_rbtree_set_start_cookie_open(priv, rbe_ge, open_interval);
-
+ nft_rbtree_set_start_cookie(priv, rbe_ge);
return -EEXIST;
}
@@ -553,12 +515,6 @@ static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set,
nft_rbtree_interval_end(rbe_ge) && nft_rbtree_interval_end(new))
return -ENOTEMPTY;
- /* - start element overlaps an open interval but end element is new:
- * partial overlap, reported as -ENOEMPTY.
- */
- if (!rbe_ge && priv->start_rbe_cookie && nft_rbtree_interval_end(new))
- return -ENOTEMPTY;
-
/* Accepted element: pick insertion point depending on key value */
parent = NULL;
p = &priv->root.rb_node;
@@ -668,7 +624,6 @@ static int nft_rbtree_insert(const struct net *net, const struct nft_set *set,
struct nft_elem_priv **elem_priv)
{
struct nft_rbtree_elem *rbe = nft_elem_priv_cast(elem->priv);
- bool last = !!(elem->flags & NFT_SET_ELEM_INTERNAL_LAST);
struct nft_rbtree *priv = nft_set_priv(set);
u64 tstamp = nft_net_tstamp(net);
int err;
@@ -685,12 +640,8 @@ static int nft_rbtree_insert(const struct net *net, const struct nft_set *set,
cond_resched();
write_lock_bh(&priv->lock);
- err = __nft_rbtree_insert(net, set, rbe, elem_priv, tstamp, last);
+ err = __nft_rbtree_insert(net, set, rbe, elem_priv, tstamp);
write_unlock_bh(&priv->lock);
-
- if (nft_rbtree_interval_end(rbe))
- priv->start_rbe_cookie = 0;
-
} while (err == -EAGAIN);
return err;
@@ -778,7 +729,6 @@ nft_rbtree_deactivate(const struct net *net, const struct nft_set *set,
const struct nft_set_elem *elem)
{
struct nft_rbtree_elem *rbe, *this = nft_elem_priv_cast(elem->priv);
- bool last = !!(elem->flags & NFT_SET_ELEM_INTERNAL_LAST);
struct nft_rbtree *priv = nft_set_priv(set);
const struct rb_node *parent = priv->root.rb_node;
u8 genmask = nft_genmask_next(net);
@@ -819,10 +769,9 @@ nft_rbtree_deactivate(const struct net *net, const struct nft_set *set,
continue;
}
- if (nft_rbtree_interval_start(rbe)) {
- if (!last)
- nft_rbtree_set_start_cookie(priv, rbe);
- } else if (!nft_rbtree_deactivate_same_interval(net, priv, rbe))
+ if (nft_rbtree_interval_start(rbe))
+ nft_rbtree_set_start_cookie(priv, rbe);
+ else if (!nft_rbtree_deactivate_same_interval(net, priv, rbe))
return NULL;
nft_rbtree_flush(net, set, &rbe->priv);
diff --git a/net/netfilter/xt_CT.c b/net/netfilter/xt_CT.c
index 3ba94c34297c..498f5871c84a 100644
--- a/net/netfilter/xt_CT.c
+++ b/net/netfilter/xt_CT.c
@@ -16,6 +16,7 @@
#include <net/netfilter/nf_conntrack_ecache.h>
#include <net/netfilter/nf_conntrack_timeout.h>
#include <net/netfilter/nf_conntrack_zones.h>
+#include "nf_internals.h"
static inline int xt_ct_target(struct sk_buff *skb, struct nf_conn *ct)
{
@@ -283,6 +284,9 @@ static void xt_ct_tg_destroy(const struct xt_tgdtor_param *par,
struct nf_conn_help *help;
if (ct) {
+ if (info->helper[0] || info->timeout[0])
+ nf_queue_nf_hook_drop(par->net);
+
help = nfct_help(ct);
xt_ct_put_helper(help);
diff --git a/net/netfilter/xt_time.c b/net/netfilter/xt_time.c
index 00319d2a54da..d9d74011bb64 100644
--- a/net/netfilter/xt_time.c
+++ b/net/netfilter/xt_time.c
@@ -223,13 +223,13 @@ time_mt(const struct sk_buff *skb, struct xt_action_param *par)
localtime_2(&current_time, stamp);
- if (!(info->weekdays_match & (1 << current_time.weekday)))
+ if (!(info->weekdays_match & (1U << current_time.weekday)))
return false;
/* Do not spend time computing monthday if all days match anyway */
if (info->monthdays_match != XT_TIME_ALL_MONTHDAYS) {
localtime_3(&current_time, stamp);
- if (!(info->monthdays_match & (1 << current_time.monthday)))
+ if (!(info->monthdays_match & (1U << current_time.monthday)))
return false;
}
diff --git a/net/phonet/af_phonet.c b/net/phonet/af_phonet.c
index 238a9638d2b0..d89225d6bfd3 100644
--- a/net/phonet/af_phonet.c
+++ b/net/phonet/af_phonet.c
@@ -129,9 +129,12 @@ static int pn_header_create(struct sk_buff *skb, struct net_device *dev,
return 1;
}
-static int pn_header_parse(const struct sk_buff *skb, unsigned char *haddr)
+static int pn_header_parse(const struct sk_buff *skb,
+ const struct net_device *dev,
+ unsigned char *haddr)
{
const u8 *media = skb_mac_header(skb);
+
*haddr = *media;
return 1;
}
diff --git a/net/rose/af_rose.c b/net/rose/af_rose.c
index 841d62481048..ba56213e0a2a 100644
--- a/net/rose/af_rose.c
+++ b/net/rose/af_rose.c
@@ -811,6 +811,11 @@ static int rose_connect(struct socket *sock, struct sockaddr_unsized *uaddr, int
goto out_release;
}
+ if (sk->sk_state == TCP_SYN_SENT) {
+ err = -EALREADY;
+ goto out_release;
+ }
+
sk->sk_state = TCP_CLOSE;
sock->state = SS_UNCONNECTED;
diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c
index 98ffe64de51f..9e726c3bd86b 100644
--- a/net/sched/sch_generic.c
+++ b/net/sched/sch_generic.c
@@ -1288,33 +1288,6 @@ static void dev_deactivate_queue(struct net_device *dev,
}
}
-static void dev_reset_queue(struct net_device *dev,
- struct netdev_queue *dev_queue,
- void *_unused)
-{
- struct Qdisc *qdisc;
- bool nolock;
-
- qdisc = rtnl_dereference(dev_queue->qdisc_sleeping);
- if (!qdisc)
- return;
-
- nolock = qdisc->flags & TCQ_F_NOLOCK;
-
- if (nolock)
- spin_lock_bh(&qdisc->seqlock);
- spin_lock_bh(qdisc_lock(qdisc));
-
- qdisc_reset(qdisc);
-
- spin_unlock_bh(qdisc_lock(qdisc));
- if (nolock) {
- clear_bit(__QDISC_STATE_MISSED, &qdisc->state);
- clear_bit(__QDISC_STATE_DRAINING, &qdisc->state);
- spin_unlock_bh(&qdisc->seqlock);
- }
-}
-
static bool some_qdisc_is_busy(struct net_device *dev)
{
unsigned int i;
diff --git a/net/sched/sch_ingress.c b/net/sched/sch_ingress.c
index cc6051d4f2ef..c3e18bae8fbf 100644
--- a/net/sched/sch_ingress.c
+++ b/net/sched/sch_ingress.c
@@ -113,14 +113,15 @@ static void ingress_destroy(struct Qdisc *sch)
{
struct ingress_sched_data *q = qdisc_priv(sch);
struct net_device *dev = qdisc_dev(sch);
- struct bpf_mprog_entry *entry = rtnl_dereference(dev->tcx_ingress);
+ struct bpf_mprog_entry *entry;
if (sch->parent != TC_H_INGRESS)
return;
tcf_block_put_ext(q->block, sch, &q->block_info);
- if (entry) {
+ if (mini_qdisc_pair_inited(&q->miniqp)) {
+ entry = rtnl_dereference(dev->tcx_ingress);
tcx_miniq_dec(entry);
if (!tcx_entry_is_active(entry)) {
tcx_entry_update(dev, NULL, true);
@@ -290,10 +291,9 @@ static int clsact_init(struct Qdisc *sch, struct nlattr *opt,
static void clsact_destroy(struct Qdisc *sch)
{
+ struct bpf_mprog_entry *ingress_entry, *egress_entry;
struct clsact_sched_data *q = qdisc_priv(sch);
struct net_device *dev = qdisc_dev(sch);
- struct bpf_mprog_entry *ingress_entry = rtnl_dereference(dev->tcx_ingress);
- struct bpf_mprog_entry *egress_entry = rtnl_dereference(dev->tcx_egress);
if (sch->parent != TC_H_CLSACT)
return;
@@ -301,7 +301,8 @@ static void clsact_destroy(struct Qdisc *sch)
tcf_block_put_ext(q->ingress_block, sch, &q->ingress_block_info);
tcf_block_put_ext(q->egress_block, sch, &q->egress_block_info);
- if (ingress_entry) {
+ if (mini_qdisc_pair_inited(&q->miniqp_ingress)) {
+ ingress_entry = rtnl_dereference(dev->tcx_ingress);
tcx_miniq_dec(ingress_entry);
if (!tcx_entry_is_active(ingress_entry)) {
tcx_entry_update(dev, NULL, true);
@@ -309,7 +310,8 @@ static void clsact_destroy(struct Qdisc *sch)
}
}
- if (egress_entry) {
+ if (mini_qdisc_pair_inited(&q->miniqp_egress)) {
+ egress_entry = rtnl_dereference(dev->tcx_egress);
tcx_miniq_dec(egress_entry);
if (!tcx_entry_is_active(egress_entry)) {
tcx_entry_update(dev, NULL, false);
diff --git a/net/sched/sch_teql.c b/net/sched/sch_teql.c
index 783300d8b019..ec4039a201a2 100644
--- a/net/sched/sch_teql.c
+++ b/net/sched/sch_teql.c
@@ -146,15 +146,12 @@ teql_destroy(struct Qdisc *sch)
master->slaves = NEXT_SLAVE(q);
if (q == master->slaves) {
struct netdev_queue *txq;
- spinlock_t *root_lock;
txq = netdev_get_tx_queue(master->dev, 0);
master->slaves = NULL;
- root_lock = qdisc_root_sleeping_lock(rtnl_dereference(txq->qdisc));
- spin_lock_bh(root_lock);
- qdisc_reset(rtnl_dereference(txq->qdisc));
- spin_unlock_bh(root_lock);
+ dev_reset_queue(master->dev,
+ txq, NULL);
}
}
skb_queue_purge(&dat->q);
diff --git a/net/shaper/shaper.c b/net/shaper/shaper.c
index 3fd6629cb999..94bc9c7382ea 100644
--- a/net/shaper/shaper.c
+++ b/net/shaper/shaper.c
@@ -36,29 +36,26 @@ static struct net_shaper_binding *net_shaper_binding_from_ctx(void *ctx)
return &((struct net_shaper_nl_ctx *)ctx)->binding;
}
-static void net_shaper_lock(struct net_shaper_binding *binding)
+static struct net_shaper_hierarchy *
+net_shaper_hierarchy(struct net_shaper_binding *binding)
{
- switch (binding->type) {
- case NET_SHAPER_BINDING_TYPE_NETDEV:
- netdev_lock(binding->netdev);
- break;
- }
-}
+ /* Pairs with WRITE_ONCE() in net_shaper_hierarchy_setup. */
+ if (binding->type == NET_SHAPER_BINDING_TYPE_NETDEV)
+ return READ_ONCE(binding->netdev->net_shaper_hierarchy);
-static void net_shaper_unlock(struct net_shaper_binding *binding)
-{
- switch (binding->type) {
- case NET_SHAPER_BINDING_TYPE_NETDEV:
- netdev_unlock(binding->netdev);
- break;
- }
+ /* No other type supported yet. */
+ return NULL;
}
static struct net_shaper_hierarchy *
-net_shaper_hierarchy(struct net_shaper_binding *binding)
+net_shaper_hierarchy_rcu(struct net_shaper_binding *binding)
{
- /* Pairs with WRITE_ONCE() in net_shaper_hierarchy_setup. */
- if (binding->type == NET_SHAPER_BINDING_TYPE_NETDEV)
+ /* Readers look up the device and take a ref, then take RCU lock
+ * later at which point netdev may have been unregistered and flushed.
+ * READ_ONCE() pairs with WRITE_ONCE() in net_shaper_hierarchy_setup.
+ */
+ if (binding->type == NET_SHAPER_BINDING_TYPE_NETDEV &&
+ READ_ONCE(binding->netdev->reg_state) <= NETREG_REGISTERED)
return READ_ONCE(binding->netdev->net_shaper_hierarchy);
/* No other type supported yet. */
@@ -204,12 +201,49 @@ static int net_shaper_ctx_setup(const struct genl_info *info, int type,
return 0;
}
+/* Like net_shaper_ctx_setup(), but for "write" handlers (never for dumps!)
+ * Acquires the lock protecting the hierarchy (instance lock for netdev).
+ */
+static int net_shaper_ctx_setup_lock(const struct genl_info *info, int type,
+ struct net_shaper_nl_ctx *ctx)
+{
+ struct net *ns = genl_info_net(info);
+ struct net_device *dev;
+ int ifindex;
+
+ if (GENL_REQ_ATTR_CHECK(info, type))
+ return -EINVAL;
+
+ ifindex = nla_get_u32(info->attrs[type]);
+ dev = netdev_get_by_index_lock(ns, ifindex);
+ if (!dev) {
+ NL_SET_BAD_ATTR(info->extack, info->attrs[type]);
+ return -ENOENT;
+ }
+
+ if (!dev->netdev_ops->net_shaper_ops) {
+ NL_SET_BAD_ATTR(info->extack, info->attrs[type]);
+ netdev_unlock(dev);
+ return -EOPNOTSUPP;
+ }
+
+ ctx->binding.type = NET_SHAPER_BINDING_TYPE_NETDEV;
+ ctx->binding.netdev = dev;
+ return 0;
+}
+
static void net_shaper_ctx_cleanup(struct net_shaper_nl_ctx *ctx)
{
if (ctx->binding.type == NET_SHAPER_BINDING_TYPE_NETDEV)
netdev_put(ctx->binding.netdev, &ctx->dev_tracker);
}
+static void net_shaper_ctx_cleanup_unlock(struct net_shaper_nl_ctx *ctx)
+{
+ if (ctx->binding.type == NET_SHAPER_BINDING_TYPE_NETDEV)
+ netdev_unlock(ctx->binding.netdev);
+}
+
static u32 net_shaper_handle_to_index(const struct net_shaper_handle *handle)
{
return FIELD_PREP(NET_SHAPER_SCOPE_MASK, handle->scope) |
@@ -251,9 +285,10 @@ static struct net_shaper *
net_shaper_lookup(struct net_shaper_binding *binding,
const struct net_shaper_handle *handle)
{
- struct net_shaper_hierarchy *hierarchy = net_shaper_hierarchy(binding);
u32 index = net_shaper_handle_to_index(handle);
+ struct net_shaper_hierarchy *hierarchy;
+ hierarchy = net_shaper_hierarchy_rcu(binding);
if (!hierarchy || xa_get_mark(&hierarchy->shapers, index,
NET_SHAPER_NOT_VALID))
return NULL;
@@ -262,7 +297,7 @@ net_shaper_lookup(struct net_shaper_binding *binding,
}
/* Allocate on demand the per device shaper's hierarchy container.
- * Called under the net shaper lock
+ * Called under the lock protecting the hierarchy (instance lock for netdev)
*/
static struct net_shaper_hierarchy *
net_shaper_hierarchy_setup(struct net_shaper_binding *binding)
@@ -681,6 +716,22 @@ void net_shaper_nl_post_doit(const struct genl_split_ops *ops,
net_shaper_generic_post(info);
}
+int net_shaper_nl_pre_doit_write(const struct genl_split_ops *ops,
+ struct sk_buff *skb, struct genl_info *info)
+{
+ struct net_shaper_nl_ctx *ctx = (struct net_shaper_nl_ctx *)info->ctx;
+
+ BUILD_BUG_ON(sizeof(*ctx) > sizeof(info->ctx));
+
+ return net_shaper_ctx_setup_lock(info, NET_SHAPER_A_IFINDEX, ctx);
+}
+
+void net_shaper_nl_post_doit_write(const struct genl_split_ops *ops,
+ struct sk_buff *skb, struct genl_info *info)
+{
+ net_shaper_ctx_cleanup_unlock((struct net_shaper_nl_ctx *)info->ctx);
+}
+
int net_shaper_nl_pre_dumpit(struct netlink_callback *cb)
{
struct net_shaper_nl_ctx *ctx = (struct net_shaper_nl_ctx *)cb->ctx;
@@ -778,17 +829,19 @@ int net_shaper_nl_get_dumpit(struct sk_buff *skb,
/* Don't error out dumps performed before any set operation. */
binding = net_shaper_binding_from_ctx(ctx);
- hierarchy = net_shaper_hierarchy(binding);
- if (!hierarchy)
- return 0;
rcu_read_lock();
+ hierarchy = net_shaper_hierarchy_rcu(binding);
+ if (!hierarchy)
+ goto out_unlock;
+
for (; (shaper = xa_find(&hierarchy->shapers, &ctx->start_index,
U32_MAX, XA_PRESENT)); ctx->start_index++) {
ret = net_shaper_fill_one(skb, binding, shaper, info);
if (ret)
break;
}
+out_unlock:
rcu_read_unlock();
return ret;
@@ -806,45 +859,38 @@ int net_shaper_nl_set_doit(struct sk_buff *skb, struct genl_info *info)
binding = net_shaper_binding_from_ctx(info->ctx);
- net_shaper_lock(binding);
ret = net_shaper_parse_info(binding, info->attrs, info, &shaper,
&exists);
if (ret)
- goto unlock;
+ return ret;
if (!exists)
net_shaper_default_parent(&shaper.handle, &shaper.parent);
hierarchy = net_shaper_hierarchy_setup(binding);
- if (!hierarchy) {
- ret = -ENOMEM;
- goto unlock;
- }
+ if (!hierarchy)
+ return -ENOMEM;
/* The 'set' operation can't create node-scope shapers. */
handle = shaper.handle;
if (handle.scope == NET_SHAPER_SCOPE_NODE &&
- !net_shaper_lookup(binding, &handle)) {
- ret = -ENOENT;
- goto unlock;
- }
+ !net_shaper_lookup(binding, &handle))
+ return -ENOENT;
ret = net_shaper_pre_insert(binding, &handle, info->extack);
if (ret)
- goto unlock;
+ return ret;
ops = net_shaper_ops(binding);
ret = ops->set(binding, &shaper, info->extack);
if (ret) {
net_shaper_rollback(binding);
- goto unlock;
+ return ret;
}
net_shaper_commit(binding, 1, &shaper);
-unlock:
- net_shaper_unlock(binding);
- return ret;
+ return 0;
}
static int __net_shaper_delete(struct net_shaper_binding *binding,
@@ -1072,35 +1118,26 @@ int net_shaper_nl_delete_doit(struct sk_buff *skb, struct genl_info *info)
binding = net_shaper_binding_from_ctx(info->ctx);
- net_shaper_lock(binding);
ret = net_shaper_parse_handle(info->attrs[NET_SHAPER_A_HANDLE], info,
&handle);
if (ret)
- goto unlock;
+ return ret;
hierarchy = net_shaper_hierarchy(binding);
- if (!hierarchy) {
- ret = -ENOENT;
- goto unlock;
- }
+ if (!hierarchy)
+ return -ENOENT;
shaper = net_shaper_lookup(binding, &handle);
- if (!shaper) {
- ret = -ENOENT;
- goto unlock;
- }
+ if (!shaper)
+ return -ENOENT;
if (handle.scope == NET_SHAPER_SCOPE_NODE) {
ret = net_shaper_pre_del_node(binding, shaper, info->extack);
if (ret)
- goto unlock;
+ return ret;
}
- ret = __net_shaper_delete(binding, shaper, info->extack);
-
-unlock:
- net_shaper_unlock(binding);
- return ret;
+ return __net_shaper_delete(binding, shaper, info->extack);
}
static int net_shaper_group_send_reply(struct net_shaper_binding *binding,
@@ -1149,21 +1186,17 @@ int net_shaper_nl_group_doit(struct sk_buff *skb, struct genl_info *info)
if (!net_shaper_ops(binding)->group)
return -EOPNOTSUPP;
- net_shaper_lock(binding);
leaves_count = net_shaper_list_len(info, NET_SHAPER_A_LEAVES);
if (!leaves_count) {
NL_SET_BAD_ATTR(info->extack,
info->attrs[NET_SHAPER_A_LEAVES]);
- ret = -EINVAL;
- goto unlock;
+ return -EINVAL;
}
leaves = kcalloc(leaves_count, sizeof(struct net_shaper) +
sizeof(struct net_shaper *), GFP_KERNEL);
- if (!leaves) {
- ret = -ENOMEM;
- goto unlock;
- }
+ if (!leaves)
+ return -ENOMEM;
old_nodes = (void *)&leaves[leaves_count];
ret = net_shaper_parse_node(binding, info->attrs, info, &node);
@@ -1240,9 +1273,6 @@ int net_shaper_nl_group_doit(struct sk_buff *skb, struct genl_info *info)
free_leaves:
kfree(leaves);
-
-unlock:
- net_shaper_unlock(binding);
return ret;
free_msg:
@@ -1352,14 +1382,12 @@ static void net_shaper_flush(struct net_shaper_binding *binding)
if (!hierarchy)
return;
- net_shaper_lock(binding);
xa_lock(&hierarchy->shapers);
xa_for_each(&hierarchy->shapers, index, cur) {
__xa_erase(&hierarchy->shapers, index);
kfree(cur);
}
xa_unlock(&hierarchy->shapers);
- net_shaper_unlock(binding);
kfree(hierarchy);
}
diff --git a/net/shaper/shaper_nl_gen.c b/net/shaper/shaper_nl_gen.c
index e8cccc4c1180..9b29be3ef19a 100644
--- a/net/shaper/shaper_nl_gen.c
+++ b/net/shaper/shaper_nl_gen.c
@@ -99,27 +99,27 @@ static const struct genl_split_ops net_shaper_nl_ops[] = {
},
{
.cmd = NET_SHAPER_CMD_SET,
- .pre_doit = net_shaper_nl_pre_doit,
+ .pre_doit = net_shaper_nl_pre_doit_write,
.doit = net_shaper_nl_set_doit,
- .post_doit = net_shaper_nl_post_doit,
+ .post_doit = net_shaper_nl_post_doit_write,
.policy = net_shaper_set_nl_policy,
.maxattr = NET_SHAPER_A_IFINDEX,
.flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
},
{
.cmd = NET_SHAPER_CMD_DELETE,
- .pre_doit = net_shaper_nl_pre_doit,
+ .pre_doit = net_shaper_nl_pre_doit_write,
.doit = net_shaper_nl_delete_doit,
- .post_doit = net_shaper_nl_post_doit,
+ .post_doit = net_shaper_nl_post_doit_write,
.policy = net_shaper_delete_nl_policy,
.maxattr = NET_SHAPER_A_IFINDEX,
.flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
},
{
.cmd = NET_SHAPER_CMD_GROUP,
- .pre_doit = net_shaper_nl_pre_doit,
+ .pre_doit = net_shaper_nl_pre_doit_write,
.doit = net_shaper_nl_group_doit,
- .post_doit = net_shaper_nl_post_doit,
+ .post_doit = net_shaper_nl_post_doit_write,
.policy = net_shaper_group_nl_policy,
.maxattr = NET_SHAPER_A_LEAVES,
.flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
diff --git a/net/shaper/shaper_nl_gen.h b/net/shaper/shaper_nl_gen.h
index ec41c90431a4..42c46c52c775 100644
--- a/net/shaper/shaper_nl_gen.h
+++ b/net/shaper/shaper_nl_gen.h
@@ -18,12 +18,17 @@ extern const struct nla_policy net_shaper_leaf_info_nl_policy[NET_SHAPER_A_WEIGH
int net_shaper_nl_pre_doit(const struct genl_split_ops *ops,
struct sk_buff *skb, struct genl_info *info);
+int net_shaper_nl_pre_doit_write(const struct genl_split_ops *ops,
+ struct sk_buff *skb, struct genl_info *info);
int net_shaper_nl_cap_pre_doit(const struct genl_split_ops *ops,
struct sk_buff *skb, struct genl_info *info);
void
net_shaper_nl_post_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
struct genl_info *info);
void
+net_shaper_nl_post_doit_write(const struct genl_split_ops *ops,
+ struct sk_buff *skb, struct genl_info *info);
+void
net_shaper_nl_cap_post_doit(const struct genl_split_ops *ops,
struct sk_buff *skb, struct genl_info *info);
int net_shaper_nl_pre_dumpit(struct netlink_callback *cb);
diff --git a/net/smc/af_smc.c b/net/smc/af_smc.c
index d0119afcc6a1..1a565095376a 100644
--- a/net/smc/af_smc.c
+++ b/net/smc/af_smc.c
@@ -131,7 +131,14 @@ static struct sock *smc_tcp_syn_recv_sock(const struct sock *sk,
struct smc_sock *smc;
struct sock *child;
- smc = smc_clcsock_user_data(sk);
+ rcu_read_lock();
+ smc = smc_clcsock_user_data_rcu(sk);
+ if (!smc || !refcount_inc_not_zero(&smc->sk.sk_refcnt)) {
+ rcu_read_unlock();
+ smc = NULL;
+ goto drop;
+ }
+ rcu_read_unlock();
if (READ_ONCE(sk->sk_ack_backlog) + atomic_read(&smc->queued_smc_hs) >
sk->sk_max_ack_backlog)
@@ -153,11 +160,14 @@ static struct sock *smc_tcp_syn_recv_sock(const struct sock *sk,
if (inet_csk(child)->icsk_af_ops == inet_csk(sk)->icsk_af_ops)
inet_csk(child)->icsk_af_ops = smc->ori_af_ops;
}
+ sock_put(&smc->sk);
return child;
drop:
dst_release(dst);
tcp_listendrop(sk);
+ if (smc)
+ sock_put(&smc->sk);
return NULL;
}
@@ -254,7 +264,7 @@ static void smc_fback_restore_callbacks(struct smc_sock *smc)
struct sock *clcsk = smc->clcsock->sk;
write_lock_bh(&clcsk->sk_callback_lock);
- clcsk->sk_user_data = NULL;
+ rcu_assign_sk_user_data(clcsk, NULL);
smc_clcsock_restore_cb(&clcsk->sk_state_change, &smc->clcsk_state_change);
smc_clcsock_restore_cb(&clcsk->sk_data_ready, &smc->clcsk_data_ready);
@@ -902,7 +912,7 @@ static void smc_fback_replace_callbacks(struct smc_sock *smc)
struct sock *clcsk = smc->clcsock->sk;
write_lock_bh(&clcsk->sk_callback_lock);
- clcsk->sk_user_data = (void *)((uintptr_t)smc | SK_USER_DATA_NOCOPY);
+ __rcu_assign_sk_user_data_with_flags(clcsk, smc, SK_USER_DATA_NOCOPY);
smc_clcsock_replace_cb(&clcsk->sk_state_change, smc_fback_state_change,
&smc->clcsk_state_change);
@@ -2665,8 +2675,8 @@ int smc_listen(struct socket *sock, int backlog)
* smc-specific sk_data_ready function
*/
write_lock_bh(&smc->clcsock->sk->sk_callback_lock);
- smc->clcsock->sk->sk_user_data =
- (void *)((uintptr_t)smc | SK_USER_DATA_NOCOPY);
+ __rcu_assign_sk_user_data_with_flags(smc->clcsock->sk, smc,
+ SK_USER_DATA_NOCOPY);
smc_clcsock_replace_cb(&smc->clcsock->sk->sk_data_ready,
smc_clcsock_data_ready, &smc->clcsk_data_ready);
write_unlock_bh(&smc->clcsock->sk->sk_callback_lock);
@@ -2687,10 +2697,11 @@ int smc_listen(struct socket *sock, int backlog)
write_lock_bh(&smc->clcsock->sk->sk_callback_lock);
smc_clcsock_restore_cb(&smc->clcsock->sk->sk_data_ready,
&smc->clcsk_data_ready);
- smc->clcsock->sk->sk_user_data = NULL;
+ rcu_assign_sk_user_data(smc->clcsock->sk, NULL);
write_unlock_bh(&smc->clcsock->sk->sk_callback_lock);
goto out;
}
+ sock_set_flag(sk, SOCK_RCU_FREE);
sk->sk_max_ack_backlog = backlog;
sk->sk_ack_backlog = 0;
sk->sk_state = SMC_LISTEN;
diff --git a/net/smc/smc.h b/net/smc/smc.h
index 9e6af72784ba..52145df83f6e 100644
--- a/net/smc/smc.h
+++ b/net/smc/smc.h
@@ -346,6 +346,11 @@ static inline struct smc_sock *smc_clcsock_user_data(const struct sock *clcsk)
((uintptr_t)clcsk->sk_user_data & ~SK_USER_DATA_NOCOPY);
}
+static inline struct smc_sock *smc_clcsock_user_data_rcu(const struct sock *clcsk)
+{
+ return (struct smc_sock *)rcu_dereference_sk_user_data(clcsk);
+}
+
/* save target_cb in saved_cb, and replace target_cb with new_cb */
static inline void smc_clcsock_replace_cb(void (**target_cb)(struct sock *),
void (*new_cb)(struct sock *),
diff --git a/net/smc/smc_close.c b/net/smc/smc_close.c
index 10219f55aad1..bb0313ef5f7c 100644
--- a/net/smc/smc_close.c
+++ b/net/smc/smc_close.c
@@ -218,7 +218,7 @@ again:
write_lock_bh(&smc->clcsock->sk->sk_callback_lock);
smc_clcsock_restore_cb(&smc->clcsock->sk->sk_data_ready,
&smc->clcsk_data_ready);
- smc->clcsock->sk->sk_user_data = NULL;
+ rcu_assign_sk_user_data(smc->clcsock->sk, NULL);
write_unlock_bh(&smc->clcsock->sk->sk_callback_lock);
rc = kernel_sock_shutdown(smc->clcsock, SHUT_RDWR);
}
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index 7eaa5b187fef..b23c33df8b46 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -1958,6 +1958,8 @@ static void unix_detach_fds(struct scm_cookie *scm, struct sk_buff *skb)
static void unix_peek_fds(struct scm_cookie *scm, struct sk_buff *skb)
{
scm->fp = scm_fp_dup(UNIXCB(skb).fp);
+
+ unix_peek_fpl(scm->fp);
}
static void unix_destruct_scm(struct sk_buff *skb)
diff --git a/net/unix/af_unix.h b/net/unix/af_unix.h
index c4f1b2da363d..8119dbeef3a3 100644
--- a/net/unix/af_unix.h
+++ b/net/unix/af_unix.h
@@ -29,6 +29,7 @@ void unix_del_edges(struct scm_fp_list *fpl);
void unix_update_edges(struct unix_sock *receiver);
int unix_prepare_fpl(struct scm_fp_list *fpl);
void unix_destroy_fpl(struct scm_fp_list *fpl);
+void unix_peek_fpl(struct scm_fp_list *fpl);
void unix_schedule_gc(struct user_struct *user);
/* SOCK_DIAG */
diff --git a/net/unix/garbage.c b/net/unix/garbage.c
index 816e8fa2b062..a7967a345827 100644
--- a/net/unix/garbage.c
+++ b/net/unix/garbage.c
@@ -318,6 +318,25 @@ void unix_destroy_fpl(struct scm_fp_list *fpl)
unix_free_vertices(fpl);
}
+static bool gc_in_progress;
+static seqcount_t unix_peek_seq = SEQCNT_ZERO(unix_peek_seq);
+
+void unix_peek_fpl(struct scm_fp_list *fpl)
+{
+ static DEFINE_SPINLOCK(unix_peek_lock);
+
+ if (!fpl || !fpl->count_unix)
+ return;
+
+ if (!READ_ONCE(gc_in_progress))
+ return;
+
+ /* Invalidate the final refcnt check in unix_vertex_dead(). */
+ spin_lock(&unix_peek_lock);
+ raw_write_seqcount_barrier(&unix_peek_seq);
+ spin_unlock(&unix_peek_lock);
+}
+
static bool unix_vertex_dead(struct unix_vertex *vertex)
{
struct unix_edge *edge;
@@ -351,6 +370,36 @@ static bool unix_vertex_dead(struct unix_vertex *vertex)
return true;
}
+static LIST_HEAD(unix_visited_vertices);
+static unsigned long unix_vertex_grouped_index = UNIX_VERTEX_INDEX_MARK2;
+
+static bool unix_scc_dead(struct list_head *scc, bool fast)
+{
+ struct unix_vertex *vertex;
+ bool scc_dead = true;
+ unsigned int seq;
+
+ seq = read_seqcount_begin(&unix_peek_seq);
+
+ list_for_each_entry_reverse(vertex, scc, scc_entry) {
+ /* Don't restart DFS from this vertex. */
+ list_move_tail(&vertex->entry, &unix_visited_vertices);
+
+ /* Mark vertex as off-stack for __unix_walk_scc(). */
+ if (!fast)
+ vertex->index = unix_vertex_grouped_index;
+
+ if (scc_dead)
+ scc_dead = unix_vertex_dead(vertex);
+ }
+
+ /* If MSG_PEEK intervened, defer this SCC to the next round. */
+ if (read_seqcount_retry(&unix_peek_seq, seq))
+ return false;
+
+ return scc_dead;
+}
+
static void unix_collect_skb(struct list_head *scc, struct sk_buff_head *hitlist)
{
struct unix_vertex *vertex;
@@ -404,9 +453,6 @@ static bool unix_scc_cyclic(struct list_head *scc)
return false;
}
-static LIST_HEAD(unix_visited_vertices);
-static unsigned long unix_vertex_grouped_index = UNIX_VERTEX_INDEX_MARK2;
-
static unsigned long __unix_walk_scc(struct unix_vertex *vertex,
unsigned long *last_index,
struct sk_buff_head *hitlist)
@@ -474,9 +520,7 @@ prev_vertex:
}
if (vertex->index == vertex->scc_index) {
- struct unix_vertex *v;
struct list_head scc;
- bool scc_dead = true;
/* SCC finalised.
*
@@ -485,18 +529,7 @@ prev_vertex:
*/
__list_cut_position(&scc, &vertex_stack, &vertex->scc_entry);
- list_for_each_entry_reverse(v, &scc, scc_entry) {
- /* Don't restart DFS from this vertex in unix_walk_scc(). */
- list_move_tail(&v->entry, &unix_visited_vertices);
-
- /* Mark vertex as off-stack. */
- v->index = unix_vertex_grouped_index;
-
- if (scc_dead)
- scc_dead = unix_vertex_dead(v);
- }
-
- if (scc_dead) {
+ if (unix_scc_dead(&scc, false)) {
unix_collect_skb(&scc, hitlist);
} else {
if (unix_vertex_max_scc_index < vertex->scc_index)
@@ -550,19 +583,11 @@ static void unix_walk_scc_fast(struct sk_buff_head *hitlist)
while (!list_empty(&unix_unvisited_vertices)) {
struct unix_vertex *vertex;
struct list_head scc;
- bool scc_dead = true;
vertex = list_first_entry(&unix_unvisited_vertices, typeof(*vertex), entry);
list_add(&scc, &vertex->scc_entry);
- list_for_each_entry_reverse(vertex, &scc, scc_entry) {
- list_move_tail(&vertex->entry, &unix_visited_vertices);
-
- if (scc_dead)
- scc_dead = unix_vertex_dead(vertex);
- }
-
- if (scc_dead) {
+ if (unix_scc_dead(&scc, true)) {
cyclic_sccs--;
unix_collect_skb(&scc, hitlist);
}
@@ -577,8 +602,6 @@ static void unix_walk_scc_fast(struct sk_buff_head *hitlist)
cyclic_sccs ? UNIX_GRAPH_CYCLIC : UNIX_GRAPH_NOT_CYCLIC);
}
-static bool gc_in_progress;
-
static void unix_gc(struct work_struct *work)
{
struct sk_buff_head hitlist;
diff --git a/net/wireless/pmsr.c b/net/wireless/pmsr.c
index 44bd88c9ea66..50e8e19aa366 100644
--- a/net/wireless/pmsr.c
+++ b/net/wireless/pmsr.c
@@ -664,6 +664,7 @@ void cfg80211_pmsr_wdev_down(struct wireless_dev *wdev)
}
spin_unlock_bh(&wdev->pmsr_lock);
+ cancel_work_sync(&wdev->pmsr_free_wk);
if (found)
cfg80211_pmsr_process_abort(wdev);