summaryrefslogtreecommitdiff
path: root/net
diff options
context:
space:
mode:
Diffstat (limited to 'net')
-rw-r--r--net/batman-adv/hard-interface.c2
-rw-r--r--net/bluetooth/hci_core.c4
-rw-r--r--net/bluetooth/hci_sync.c3
-rw-r--r--net/bluetooth/l2cap_core.c33
-rw-r--r--net/bluetooth/msft.c2
-rw-r--r--net/bridge/br_multicast.c13
-rw-r--r--net/ceph/messenger.c1
-rw-r--r--net/core/dev.c25
-rw-r--r--net/core/gro_cells.c2
-rw-r--r--net/core/page_pool.c3
-rw-r--r--net/core/sock.c3
-rw-r--r--net/devlink/netlink.c2
-rw-r--r--net/devlink/param.c7
-rw-r--r--net/ipv4/fib_semantics.c2
-rw-r--r--net/ipv4/igmp.c210
-rw-r--r--net/ipv4/tcp.c32
-rw-r--r--net/ipv4/tcp_cong.c4
-rw-r--r--net/ipv4/tcp_dctcp.c4
-rw-r--r--net/ipv4/tcp_ipv4.c4
-rw-r--r--net/ipv4/tcp_minisocks.c2
-rw-r--r--net/ipv4/tcp_offload.c22
-rw-r--r--net/ipv4/tcp_output.c6
-rw-r--r--net/ipv4/tcp_timer.c6
-rw-r--r--net/ipv4/udp.c15
-rw-r--r--net/ipv6/exthdrs.c4
-rw-r--r--net/ipv6/ip6_gre.c6
-rw-r--r--net/ipv6/mcast.c148
-rw-r--r--net/ipv6/route.c2
-rw-r--r--net/ipv6/tcpv6_offload.c15
-rw-r--r--net/ipv6/udp.c13
-rw-r--r--net/iucv/af_iucv.c42
-rw-r--r--net/mac80211/nan.c3
-rw-r--r--net/mac802154/ieee802154_i.h7
-rw-r--r--net/mac802154/main.c1
-rw-r--r--net/mac802154/scan.c51
-rw-r--r--net/mctp/test/route-test.c2
-rw-r--r--net/mctp/test/utils.c2
-rw-r--r--net/mptcp/protocol.c3
-rw-r--r--net/mptcp/protocol.h2
-rw-r--r--net/netfilter/nf_tables_api.c2
-rw-r--r--net/netfilter/nfnetlink_cttimeout.c2
-rw-r--r--net/packet/af_packet.c5
-rw-r--r--net/qrtr/af_qrtr.c66
-rw-r--r--net/qrtr/ns.c35
-rw-r--r--net/rds/connection.c89
-rw-r--r--net/rds/ib_recv.c9
-rw-r--r--net/rds/info.c2
-rw-r--r--net/rds/send.c14
-rw-r--r--net/rds/tcp.c101
-rw-r--r--net/rds/tcp_listen.c6
-rw-r--r--net/rxrpc/key.c2
-rw-r--r--net/sched/act_api.c37
-rw-r--r--net/sched/act_gate.c4
-rw-r--r--net/sched/act_tunnel_key.c2
-rw-r--r--net/sched/cls_flower.c5
-rw-r--r--net/sched/cls_u32.c32
-rw-r--r--net/sctp/inqueue.c6
-rw-r--r--net/sctp/sm_make_chunk.c14
-rw-r--r--net/sctp/sm_sideeffect.c11
-rw-r--r--net/sunrpc/auth_gss/gss_krb5_crypto.c5
-rw-r--r--net/sunrpc/svcauth_unix.c4
-rw-r--r--net/sunrpc/xdr.c3
-rw-r--r--net/tipc/link.c6
-rw-r--r--net/tipc/name_table.c30
-rw-r--r--net/tipc/node.c2
-rw-r--r--net/unix/af_unix.c4
-rw-r--r--net/vmw_vsock/af_vsock.c32
-rw-r--r--net/vmw_vsock/virtio_transport_common.c3
-rw-r--r--net/vmw_vsock/vmci_transport.c34
-rw-r--r--net/wireless/core.c5
-rw-r--r--net/wireless/nl80211.c9
71 files changed, 872 insertions, 417 deletions
diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c
index e7ad295504e4..c5bc60d16e40 100644
--- a/net/batman-adv/hard-interface.c
+++ b/net/batman-adv/hard-interface.c
@@ -981,7 +981,7 @@ batadv_wifi_net_device_insert(struct net_device *net_dev, u32 wifi_flags)
ASSERT_RTNL();
- device_state = kzalloc_obj(*device_state, GFP_KERNEL);
+ device_state = kzalloc_obj(*device_state);
if (!device_state)
return -ENOMEM;
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 35a1be57e386..d7355c73f93e 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -2632,11 +2632,11 @@ int hci_register_dev(struct hci_dev *hdev)
if (error)
BT_WARN("register suspend notifier failed error:%d\n", error);
- queue_work(hdev->req_workqueue, &hdev->power_on);
-
idr_init(&hdev->adv_monitors_idr);
msft_register(hdev);
+ queue_work(hdev->req_workqueue, &hdev->power_on);
+
return id;
err_wqueue:
diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
index ffd7b37e7401..3f121099eb22 100644
--- a/net/bluetooth/hci_sync.c
+++ b/net/bluetooth/hci_sync.c
@@ -6423,8 +6423,7 @@ static int hci_update_event_filter_sync(struct hci_dev *hdev)
goto update_scan;
}
- accept_list = kmalloc_array(num_entries, sizeof(*accept_list),
- GFP_KERNEL);
+ accept_list = kmalloc_objs(*accept_list, num_entries);
if (!accept_list) {
hci_dev_unlock(hdev);
return -ENOMEM;
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index ee459dd411f5..644e31160d55 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -1337,7 +1337,7 @@ static void l2cap_le_connect(struct l2cap_chan *chan)
struct l2cap_ecred_conn_data {
struct {
struct l2cap_ecred_conn_req_hdr req;
- __le16 scid[5];
+ __le16 scid[L2CAP_ECRED_CONN_SCID_MAX];
} __packed pdu;
struct l2cap_chan *chan;
struct pid *pid;
@@ -1352,7 +1352,7 @@ static void l2cap_ecred_defer_connect(struct l2cap_chan *chan, void *data)
if (chan == conn->chan)
return;
- if (!test_and_clear_bit(FLAG_DEFER_SETUP, &chan->flags))
+ if (!test_bit(FLAG_DEFER_SETUP, &chan->flags))
return;
pid = chan->ops->get_peer_pid(chan);
@@ -1362,9 +1362,16 @@ static void l2cap_ecred_defer_connect(struct l2cap_chan *chan, void *data)
chan->mode != L2CAP_MODE_EXT_FLOWCTL || chan->state != BT_CONNECT)
return;
+ if (!test_and_clear_bit(FLAG_DEFER_SETUP, &chan->flags))
+ return;
+
if (test_and_set_bit(FLAG_ECRED_CONN_REQ_SENT, &chan->flags))
return;
+ /* Unreachable, checked in l2cap_connect (+timer drops it if reached) */
+ if (WARN_ON_ONCE(conn->count >= ARRAY_SIZE(conn->pdu.scid)))
+ return;
+
l2cap_ecred_init(chan, 0);
/* Set the same ident so we can match on the rsp */
@@ -3894,6 +3901,9 @@ static void l2cap_ecred_rsp_defer(struct l2cap_chan *chan, void *data)
struct l2cap_ecred_conn_rsp *rsp_flex =
container_of(&rsp->pdu.rsp, struct l2cap_ecred_conn_rsp, hdr);
+ if (chan->mode != L2CAP_MODE_EXT_FLOWCTL)
+ return;
+
/* Check if channel for outgoing connection or if it wasn't deferred
* since in those cases it must be skipped.
*/
@@ -3904,6 +3914,10 @@ static void l2cap_ecred_rsp_defer(struct l2cap_chan *chan, void *data)
/* Reset ident so only one response is sent */
chan->ident = 0;
+ /* Unreachable, check in l2cap_ecred_conn_req. If reached, drop rest */
+ if (WARN_ON_ONCE(rsp->count >= ARRAY_SIZE(rsp->pdu.scid)))
+ rsp->pdu.rsp.result = cpu_to_le16(L2CAP_CR_LE_NO_MEM);
+
/* Include all channels pending with the same ident */
if (!rsp->pdu.rsp.result)
rsp_flex->dcid[rsp->count++] = cpu_to_le16(chan->scid);
@@ -5063,6 +5077,7 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn,
__set_chan_timer(chan, chan->ops->get_sndtimeo(chan));
chan->ident = cmd->ident;
+ chan->mode = L2CAP_MODE_LE_FLOWCTL;
if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) {
l2cap_state_change(chan, BT_CONNECT2);
@@ -7369,6 +7384,9 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
goto done;
}
+ mutex_lock(&conn->lock);
+ l2cap_chan_lock(chan);
+
if (chan->mode == L2CAP_MODE_EXT_FLOWCTL) {
struct l2cap_chan_data data;
@@ -7376,19 +7394,20 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
data.pid = chan->ops->get_peer_pid(chan);
data.count = 1;
- l2cap_chan_list(conn, l2cap_chan_by_pid, &data);
+ __l2cap_chan_list(conn, l2cap_chan_by_pid, &data);
+
+ /* Leave room for non-deferred channel that ends the group. */
+ if (test_bit(FLAG_DEFER_SETUP, &chan->flags))
+ data.count += 1;
/* Check if there isn't too many channels being connected */
if (data.count > L2CAP_ECRED_CONN_SCID_MAX) {
hci_conn_drop(hcon);
err = -EPROTO;
- goto done;
+ goto chan_unlock;
}
}
- mutex_lock(&conn->lock);
- l2cap_chan_lock(chan);
-
if (cid && __l2cap_get_chan_by_dcid(conn, cid)) {
hci_conn_drop(hcon);
err = -EBUSY;
diff --git a/net/bluetooth/msft.c b/net/bluetooth/msft.c
index ded68568e6c9..d9dd722db3eb 100644
--- a/net/bluetooth/msft.c
+++ b/net/bluetooth/msft.c
@@ -769,8 +769,8 @@ void msft_register(struct hci_dev *hdev)
INIT_LIST_HEAD(&msft->handle_map);
INIT_LIST_HEAD(&msft->address_filters);
- hdev->msft_data = msft;
mutex_init(&msft->filter_lock);
+ hdev->msft_data = msft;
}
void msft_release(struct hci_dev *hdev)
diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c
index 3ef5d8bbf552..3e9b10f8abf1 100644
--- a/net/bridge/br_multicast.c
+++ b/net/bridge/br_multicast.c
@@ -808,7 +808,11 @@ void br_multicast_del_pg(struct net_bridge_mdb_entry *mp,
struct hlist_node *tmp;
rcu_assign_pointer(*pp, pg->next);
- hlist_del_init(&pg->mglist);
+ /* Keep ->next (held under multicast_lock, freed later by the GC work):
+ * a port->mglist teardown walk may have latched this node as its next,
+ * and deleting other groups of the same port must not truncate it.
+ */
+ hlist_del_init_rcu(&pg->mglist);
br_multicast_eht_clean_sets(pg);
hlist_for_each_entry_safe(ent, tmp, &pg->src_list, node)
br_multicast_del_group_src(ent, false);
@@ -835,6 +839,13 @@ static void br_multicast_find_del_pg(struct net_bridge *br,
struct net_bridge_mdb_entry *mp;
struct net_bridge_port_group *p;
+ /* A teardown walk over port->mglist can reach a group that an earlier
+ * iteration already deleted as a side effect. It is off mp->ports by
+ * now, so skip it instead of falling through to the WARN_ON() below.
+ */
+ if (hlist_unhashed(&pg->mglist))
+ return;
+
mp = br_mdb_ip_get(br, &pg->key.addr);
if (WARN_ON(!mp))
return;
diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c
index 9c1b6cf8c36f..212e7797f9e4 100644
--- a/net/ceph/messenger.c
+++ b/net/ceph/messenger.c
@@ -1003,7 +1003,6 @@ static struct page *ceph_msg_data_iter_next(struct ceph_msg_data_cursor *cursor,
* we'll get an iov_iter_get_pages2 variant that doesn't take
* page refs. Until then, just put the page ref.
*/
- VM_BUG_ON_PAGE(!PageWriteback(page) && page_count(page) < 2, page);
put_page(page);
*length = min_t(size_t, len, cursor->resid);
diff --git a/net/core/dev.c b/net/core/dev.c
index 38336858c168..290e0f099e6b 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -12703,7 +12703,7 @@ int __dev_change_net_namespace(struct net_device *dev, struct net *net,
const char *pat, int new_ifindex,
struct netlink_ext_ack *extack)
{
- struct netdev_name_node *name_node;
+ struct netdev_name_node *name_node, *tmp;
struct net *net_old = dev_net(dev);
char new_name[IFNAMSIZ] = {};
int err, new_nsid;
@@ -12749,13 +12749,19 @@ int __dev_change_net_namespace(struct net_device *dev, struct net *net,
}
/* Check that none of the altnames conflicts. */
err = -EEXIST;
- netdev_for_each_altname(dev, name_node) {
- if (netdev_name_in_use(net, name_node->name)) {
- NL_SET_ERR_MSG_FMT(extack,
- "An interface with the altname %s exists in the target netns",
- name_node->name);
- goto out;
+ netdev_for_each_altname_safe(dev, name_node, tmp) {
+ if (!netdev_name_in_use(net, name_node->name))
+ continue;
+
+ if (!check_net(net_old)) {
+ __netdev_name_node_alt_destroy(name_node);
+ continue;
}
+
+ NL_SET_ERR_MSG_FMT(extack,
+ "An interface with the altname %s exists in the target netns",
+ name_node->name);
+ goto out;
}
/* Check that new_ifindex isn't used yet. */
@@ -13210,7 +13216,6 @@ static struct pernet_operations __net_initdata netdev_net_ops = {
static void __net_exit default_device_exit_net(struct net *net)
{
- struct netdev_name_node *name_node, *tmp;
struct net_device *dev, *aux;
/*
* Push all migratable network devices back to the
@@ -13234,10 +13239,6 @@ static void __net_exit default_device_exit_net(struct net *net)
if (netdev_name_in_use(&init_net, fb_name))
snprintf(fb_name, IFNAMSIZ, "dev%%d");
- netdev_for_each_altname_safe(dev, name_node, tmp)
- if (netdev_name_in_use(&init_net, name_node->name))
- __netdev_name_node_alt_destroy(name_node);
-
err = dev_change_net_namespace(dev, &init_net, fb_name);
if (err) {
pr_emerg("%s: failed to move %s to init_net: %d\n",
diff --git a/net/core/gro_cells.c b/net/core/gro_cells.c
index 1b84385c04bd..d8c0a2867120 100644
--- a/net/core/gro_cells.c
+++ b/net/core/gro_cells.c
@@ -22,6 +22,8 @@ int gro_cells_receive(struct gro_cells *gcells, struct sk_buff *skb)
if (unlikely(!(dev->flags & IFF_UP)))
goto drop;
+ skb_unset_transport_header(skb);
+
if (!gcells->cells || skb_cloned(skb) || netif_elide_gro(dev)) {
res = netif_rx(skb);
goto unlock;
diff --git a/net/core/page_pool.c b/net/core/page_pool.c
index 8f8956fb061b..08d7f35cf608 100644
--- a/net/core/page_pool.c
+++ b/net/core/page_pool.c
@@ -1073,7 +1073,8 @@ netmem_ref page_pool_alloc_frag_netmem(struct page_pool *pool,
if (WARN_ON(size > max_size))
return 0;
- size = ALIGN(size, dma_get_cache_alignment());
+ size = ALIGN(size, max_t(unsigned int, dma_get_cache_alignment(),
+ __alignof__(struct skb_shared_info)));
*offset = pool->frag_offset;
if (netmem && *offset + size > max_size) {
diff --git a/net/core/sock.c b/net/core/sock.c
index 1ad41904db25..fa60b7494c58 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -2494,6 +2494,9 @@ struct sock *sk_clone(const struct sock *sk, const gfp_t priority,
#ifdef CONFIG_BPF_SYSCALL
RCU_INIT_POINTER(newsk->sk_bpf_storage, NULL);
#endif
+#if IS_ENABLED(CONFIG_INET_PSP)
+ RCU_INIT_POINTER(newsk->psp_assoc, NULL);
+#endif
/* SANITY */
if (likely(newsk->sk_net_refcnt)) {
diff --git a/net/devlink/netlink.c b/net/devlink/netlink.c
index 300580c1a217..a8eda727bff1 100644
--- a/net/devlink/netlink.c
+++ b/net/devlink/netlink.c
@@ -251,7 +251,7 @@ devlink_get_parent_from_attrs_lock(struct net *net, struct nlattr **attrs)
if (!attrs[DEVLINK_ATTR_PARENT_DEV])
return ERR_PTR(-EINVAL);
- tb = kcalloc(maxtype + 1, sizeof(*tb), GFP_KERNEL);
+ tb = kzalloc_objs(*tb, maxtype + 1);
if (!tb)
return ERR_PTR(-ENOMEM);
diff --git a/net/devlink/param.c b/net/devlink/param.c
index 8ca0f3ed646c..95ac9db8c993 100644
--- a/net/devlink/param.c
+++ b/net/devlink/param.c
@@ -330,13 +330,12 @@ static int devlink_nl_param_fill(struct sk_buff *msg, struct devlink *devlink,
int err;
int i;
- default_value = kcalloc(DEVLINK_PARAM_CMODE_MAX + 1,
- sizeof(*default_value), GFP_KERNEL);
+ default_value = kzalloc_objs(*default_value,
+ DEVLINK_PARAM_CMODE_MAX + 1);
if (!default_value)
return -ENOMEM;
- param_value = kcalloc(DEVLINK_PARAM_CMODE_MAX + 1,
- sizeof(*param_value), GFP_KERNEL);
+ param_value = kzalloc_objs(*param_value, DEVLINK_PARAM_CMODE_MAX + 1);
if (!param_value) {
kfree(default_value);
return -ENOMEM;
diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c
index 0483519b7fb0..7a362f2e2c2b 100644
--- a/net/ipv4/fib_semantics.c
+++ b/net/ipv4/fib_semantics.c
@@ -874,7 +874,7 @@ static void fib_rebalance(struct fib_info *fi)
change_nexthops(fi) {
int upper_bound;
- if (nexthop_nh->fib_nh_flags & RTNH_F_DEAD) {
+ if (!total || nexthop_nh->fib_nh_flags & RTNH_F_DEAD) {
upper_bound = -1;
} else if (ip_ignore_linkdown(nexthop_nh->fib_nh_dev) &&
nexthop_nh->fib_nh_flags & RTNH_F_LINKDOWN) {
diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c
index b80b8a92f46e..d56355aca797 100644
--- a/net/ipv4/igmp.c
+++ b/net/ipv4/igmp.c
@@ -188,6 +188,10 @@ static void ip_ma_put(struct ip_mc_list *im)
}
}
+#define pmc_dereference(e, pmc) \
+ rcu_dereference_protected(e, lockdep_is_held(&(pmc)->lock) || \
+ lockdep_is_held(&(pmc)->interface->mc_tomb_lock))
+
#define for_each_pmc_rcu(in_dev, pmc) \
for (pmc = rcu_dereference(in_dev->mc_list); \
pmc != NULL; \
@@ -198,13 +202,28 @@ static void ip_ma_put(struct ip_mc_list *im)
pmc != NULL; \
pmc = rtnl_dereference(pmc->next_rcu))
+#define for_each_psf_mclock(pmc, psf) \
+ for (psf = pmc_dereference((pmc)->sources, pmc); \
+ psf; \
+ psf = pmc_dereference(psf->sf_next, pmc))
+
+#define for_each_psf_rcu(im, psf) \
+ for (psf = rcu_dereference((im)->sources); \
+ psf; \
+ psf = rcu_dereference(psf->sf_next))
+
+#define for_each_psf_tomb(pmc, psf) \
+ for (psf = pmc_dereference((pmc)->tomb, pmc); \
+ psf; \
+ psf = pmc_dereference(psf->sf_next, pmc))
+
static void ip_sf_list_clear_all(struct ip_sf_list *psf)
{
struct ip_sf_list *next;
while (psf) {
- next = psf->sf_next;
- kfree(psf);
+ next = rcu_dereference_protected(psf->sf_next, 1);
+ kfree_rcu(psf, rcu);
psf = next;
}
}
@@ -349,7 +368,7 @@ igmp_scount(struct ip_mc_list *pmc, int type, int gdeleted, int sdeleted)
struct ip_sf_list *psf;
int scount = 0;
- for (psf = pmc->sources; psf; psf = psf->sf_next) {
+ for_each_psf_mclock(pmc, psf) {
if (!is_in(pmc, psf, type, gdeleted, sdeleted))
continue;
scount++;
@@ -494,7 +513,8 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ip_mc_list *pmc,
struct net *net = dev_net(dev);
struct igmpv3_report *pih;
struct igmpv3_grec *pgr = NULL;
- struct ip_sf_list *psf, *psf_next, *psf_prev, **psf_list;
+ struct ip_sf_list *psf, *psf_next, *psf_prev;
+ struct ip_sf_list __rcu **psf_list;
int scount, stotal, first, isquery, truncate;
unsigned int mtu;
@@ -517,7 +537,7 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ip_mc_list *pmc,
psf_list = sdeleted ? &pmc->tomb : &pmc->sources;
- if (!*psf_list)
+ if (!rcu_access_pointer(*psf_list))
goto empty_source;
pih = skb ? igmpv3_report_hdr(skb) : NULL;
@@ -533,10 +553,12 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ip_mc_list *pmc,
}
first = 1;
psf_prev = NULL;
- for (psf = *psf_list; psf; psf = psf_next) {
+ for (psf = pmc_dereference(*psf_list, pmc);
+ psf;
+ psf = psf_next) {
__be32 *psrc;
- psf_next = psf->sf_next;
+ psf_next = pmc_dereference(psf->sf_next, pmc);
if (!is_in(pmc, psf, type, gdeleted, sdeleted)) {
psf_prev = psf;
@@ -583,10 +605,12 @@ decrease_sf_crcount:
psf->sf_crcount--;
if ((sdeleted || gdeleted) && psf->sf_crcount == 0) {
if (psf_prev)
- psf_prev->sf_next = psf->sf_next;
+ rcu_assign_pointer(psf_prev->sf_next,
+ psf_next);
else
- *psf_list = psf->sf_next;
- kfree(psf);
+ rcu_assign_pointer(*psf_list,
+ psf_next);
+ kfree_rcu(psf, rcu);
continue;
}
}
@@ -655,28 +679,29 @@ static int igmpv3_send_report(struct in_device *in_dev, struct ip_mc_list *pmc)
/*
* remove zero-count source records from a source filter list
*/
-static void igmpv3_clear_zeros(struct ip_sf_list **ppsf)
+static void igmpv3_clear_zeros(struct ip_sf_list __rcu **ppsf)
{
struct ip_sf_list *psf_prev, *psf_next, *psf;
psf_prev = NULL;
- for (psf = *ppsf; psf; psf = psf_next) {
- psf_next = psf->sf_next;
+ for (psf = rcu_dereference_protected(*ppsf, 1); psf; psf = psf_next) {
+ psf_next = rcu_dereference_protected(psf->sf_next, 1);
if (psf->sf_crcount == 0) {
if (psf_prev)
- psf_prev->sf_next = psf->sf_next;
+ rcu_assign_pointer(psf_prev->sf_next, psf_next);
else
- *ppsf = psf->sf_next;
- kfree(psf);
- } else
+ rcu_assign_pointer(*ppsf, psf_next);
+ kfree_rcu(psf, rcu);
+ } else {
psf_prev = psf;
+ }
}
}
static void kfree_pmc(struct ip_mc_list *pmc)
{
- ip_sf_list_clear_all(pmc->sources);
- ip_sf_list_clear_all(pmc->tomb);
+ ip_sf_list_clear_all(rcu_dereference_protected(pmc->sources, 1));
+ ip_sf_list_clear_all(rcu_dereference_protected(pmc->tomb, 1));
kfree(pmc);
}
@@ -710,7 +735,8 @@ static void igmpv3_send_cr(struct in_device *in_dev)
igmpv3_clear_zeros(&pmc->sources);
}
}
- if (pmc->crcount == 0 && !pmc->tomb && !pmc->sources) {
+ if (pmc->crcount == 0 && !rcu_access_pointer(pmc->tomb) &&
+ !rcu_access_pointer(pmc->sources)) {
if (pmc_prev)
pmc_prev->next = pmc_next;
else
@@ -896,7 +922,7 @@ static int igmp_xmarksources(struct ip_mc_list *pmc, int nsrcs, __be32 *srcs)
int i, scount;
scount = 0;
- for (psf = pmc->sources; psf; psf = psf->sf_next) {
+ for_each_psf_mclock(pmc, psf) {
if (scount == nsrcs)
break;
for (i = 0; i < nsrcs; i++) {
@@ -927,7 +953,7 @@ static int igmp_marksources(struct ip_mc_list *pmc, int nsrcs, __be32 *srcs)
/* mark INCLUDE-mode sources */
scount = 0;
- for (psf = pmc->sources; psf; psf = psf->sf_next) {
+ for_each_psf_mclock(pmc, psf) {
if (scount == nsrcs)
break;
for (i = 0; i < nsrcs; i++)
@@ -1228,11 +1254,12 @@ static void igmpv3_add_delrec(struct in_device *in_dev, struct ip_mc_list *im,
if (pmc->sfmode == MCAST_INCLUDE) {
struct ip_sf_list *psf;
+ for_each_psf_mclock(im, psf)
+ psf->sf_crcount = pmc->crcount;
pmc->tomb = im->tomb;
pmc->sources = im->sources;
- im->tomb = im->sources = NULL;
- for (psf = pmc->sources; psf; psf = psf->sf_next)
- psf->sf_crcount = pmc->crcount;
+ RCU_INIT_POINTER(im->tomb, NULL);
+ RCU_INIT_POINTER(im->sources, NULL);
}
spin_unlock_bh(&im->lock);
@@ -1271,9 +1298,18 @@ static void igmpv3_del_delrec(struct in_device *in_dev, struct ip_mc_list *im)
if (pmc) {
im->interface = pmc->interface;
if (im->sfmode == MCAST_INCLUDE) {
- swap(im->tomb, pmc->tomb);
- swap(im->sources, pmc->sources);
- for (psf = im->sources; psf; psf = psf->sf_next)
+ struct ip_sf_list *sources, *tomb;
+
+ tomb = rcu_replace_pointer(im->tomb,
+ rcu_dereference_protected(pmc->tomb, 1),
+ lockdep_is_held(&im->lock));
+ rcu_assign_pointer(pmc->tomb, tomb);
+
+ sources = rcu_replace_pointer(im->sources,
+ rcu_dereference_protected(pmc->sources, 1),
+ lockdep_is_held(&im->lock));
+ rcu_assign_pointer(pmc->sources, sources);
+ for_each_psf_mclock(im, psf)
psf->sf_crcount = in_dev->mr_qrv ?:
READ_ONCE(net->ipv4.sysctl_igmp_qrv);
} else {
@@ -1310,8 +1346,8 @@ static void igmpv3_clear_delrec(struct in_device *in_dev)
struct ip_sf_list *psf;
spin_lock_bh(&pmc->lock);
- psf = pmc->tomb;
- pmc->tomb = NULL;
+ psf = pmc_dereference(pmc->tomb, pmc);
+ RCU_INIT_POINTER(pmc->tomb, NULL);
spin_unlock_bh(&pmc->lock);
ip_sf_list_clear_all(psf);
}
@@ -1990,7 +2026,7 @@ static int ip_mc_del1_src(struct ip_mc_list *pmc, int sfmode,
int rv = 0;
psf_prev = NULL;
- for (psf = pmc->sources; psf; psf = psf->sf_next) {
+ for_each_psf_mclock(pmc, psf) {
if (psf->sf_inaddr == *psfsrc)
break;
psf_prev = psf;
@@ -1999,7 +2035,7 @@ static int ip_mc_del1_src(struct ip_mc_list *pmc, int sfmode,
/* source filter not found, or count wrong => bug */
return -ESRCH;
}
- psf->sf_count[sfmode]--;
+ WRITE_ONCE(psf->sf_count[sfmode], psf->sf_count[sfmode] - 1);
if (psf->sf_count[sfmode] == 0) {
ip_rt_multicast_event(pmc->interface);
}
@@ -2011,19 +2047,28 @@ static int ip_mc_del1_src(struct ip_mc_list *pmc, int sfmode,
/* no more filters for this source */
if (psf_prev)
- psf_prev->sf_next = psf->sf_next;
+ rcu_assign_pointer(psf_prev->sf_next,
+ pmc_dereference(psf->sf_next, pmc));
else
- pmc->sources = psf->sf_next;
+ rcu_assign_pointer(pmc->sources,
+ pmc_dereference(psf->sf_next, pmc));
#ifdef CONFIG_IP_MULTICAST
if (psf->sf_oldin &&
!IGMP_V1_SEEN(in_dev) && !IGMP_V2_SEEN(in_dev)) {
- psf->sf_crcount = in_dev->mr_qrv ?: READ_ONCE(net->ipv4.sysctl_igmp_qrv);
- psf->sf_next = pmc->tomb;
- pmc->tomb = psf;
- rv = 1;
- } else
+ struct ip_sf_list *dpsf = kmalloc_obj(*dpsf, GFP_ATOMIC);
+
+ if (dpsf) {
+ *dpsf = *psf;
+ dpsf->sf_crcount = in_dev->mr_qrv ?:
+ READ_ONCE(net->ipv4.sysctl_igmp_qrv);
+ rcu_assign_pointer(dpsf->sf_next,
+ pmc_dereference(pmc->tomb, pmc));
+ rcu_assign_pointer(pmc->tomb, dpsf);
+ rv = 1;
+ }
+ }
#endif
- kfree(psf);
+ kfree_rcu(psf, rcu);
}
return rv;
}
@@ -2060,7 +2105,7 @@ static int ip_mc_del_src(struct in_device *in_dev, __be32 *pmca, int sfmode,
err = -EINVAL;
if (!pmc->sfcount[sfmode])
goto out_unlock;
- pmc->sfcount[sfmode]--;
+ WRITE_ONCE(pmc->sfcount[sfmode], pmc->sfcount[sfmode] - 1);
}
err = 0;
for (i = 0; i < sfcount; i++) {
@@ -2083,7 +2128,7 @@ static int ip_mc_del_src(struct in_device *in_dev, __be32 *pmca, int sfmode,
#ifdef CONFIG_IP_MULTICAST
pmc->crcount = in_dev->mr_qrv ?: READ_ONCE(net->ipv4.sysctl_igmp_qrv);
WRITE_ONCE(in_dev->mr_ifc_count, pmc->crcount);
- for (psf = pmc->sources; psf; psf = psf->sf_next)
+ for_each_psf_mclock(pmc, psf)
psf->sf_crcount = 0;
igmp_ifc_event(pmc->interface);
} else if (sf_setstate(pmc) || changerec) {
@@ -2104,7 +2149,7 @@ static int ip_mc_add1_src(struct ip_mc_list *pmc, int sfmode,
struct ip_sf_list *psf, *psf_prev;
psf_prev = NULL;
- for (psf = pmc->sources; psf; psf = psf->sf_next) {
+ for_each_psf_mclock(pmc, psf) {
if (psf->sf_inaddr == *psfsrc)
break;
psf_prev = psf;
@@ -2114,12 +2159,12 @@ static int ip_mc_add1_src(struct ip_mc_list *pmc, int sfmode,
if (!psf)
return -ENOBUFS;
psf->sf_inaddr = *psfsrc;
- if (psf_prev) {
- psf_prev->sf_next = psf;
- } else
- pmc->sources = psf;
+ if (psf_prev)
+ rcu_assign_pointer(psf_prev->sf_next, psf);
+ else
+ rcu_assign_pointer(pmc->sources, psf);
}
- psf->sf_count[sfmode]++;
+ WRITE_ONCE(psf->sf_count[sfmode], psf->sf_count[sfmode] + 1);
if (psf->sf_count[sfmode] == 1) {
ip_rt_multicast_event(pmc->interface);
}
@@ -2132,13 +2177,15 @@ static void sf_markstate(struct ip_mc_list *pmc)
struct ip_sf_list *psf;
int mca_xcount = pmc->sfcount[MCAST_EXCLUDE];
- for (psf = pmc->sources; psf; psf = psf->sf_next)
+ for_each_psf_mclock(pmc, psf) {
if (pmc->sfcount[MCAST_EXCLUDE]) {
psf->sf_oldin = mca_xcount ==
psf->sf_count[MCAST_EXCLUDE] &&
!psf->sf_count[MCAST_INCLUDE];
- } else
+ } else {
psf->sf_oldin = psf->sf_count[MCAST_INCLUDE] != 0;
+ }
+ }
}
static int sf_setstate(struct ip_mc_list *pmc)
@@ -2149,27 +2196,31 @@ static int sf_setstate(struct ip_mc_list *pmc)
int new_in, rv;
rv = 0;
- for (psf = pmc->sources; psf; psf = psf->sf_next) {
+ for_each_psf_mclock(pmc, psf) {
if (pmc->sfcount[MCAST_EXCLUDE]) {
new_in = mca_xcount == psf->sf_count[MCAST_EXCLUDE] &&
!psf->sf_count[MCAST_INCLUDE];
- } else
+ } else {
new_in = psf->sf_count[MCAST_INCLUDE] != 0;
+ }
if (new_in) {
if (!psf->sf_oldin) {
struct ip_sf_list *prev = NULL;
- for (dpsf = pmc->tomb; dpsf; dpsf = dpsf->sf_next) {
+ for_each_psf_tomb(pmc, dpsf) {
if (dpsf->sf_inaddr == psf->sf_inaddr)
break;
prev = dpsf;
}
if (dpsf) {
+ struct ip_sf_list *dpsf_next;
+
+ dpsf_next = pmc_dereference(dpsf->sf_next, pmc);
if (prev)
- prev->sf_next = dpsf->sf_next;
+ rcu_assign_pointer(prev->sf_next, dpsf_next);
else
- pmc->tomb = dpsf->sf_next;
- kfree(dpsf);
+ rcu_assign_pointer(pmc->tomb, dpsf_next);
+ kfree_rcu(dpsf, rcu);
}
psf->sf_crcount = qrv;
rv++;
@@ -2181,17 +2232,19 @@ static int sf_setstate(struct ip_mc_list *pmc)
* add or update "delete" records if an active filter
* is now inactive
*/
- for (dpsf = pmc->tomb; dpsf; dpsf = dpsf->sf_next)
+ for_each_psf_tomb(pmc, dpsf) {
if (dpsf->sf_inaddr == psf->sf_inaddr)
break;
+ }
if (!dpsf) {
dpsf = kmalloc_obj(*dpsf, GFP_ATOMIC);
if (!dpsf)
continue;
*dpsf = *psf;
/* pmc->lock held by callers */
- dpsf->sf_next = pmc->tomb;
- pmc->tomb = dpsf;
+ rcu_assign_pointer(dpsf->sf_next,
+ pmc_dereference(pmc->tomb, pmc));
+ rcu_assign_pointer(pmc->tomb, dpsf);
}
dpsf->sf_crcount = qrv;
rv++;
@@ -2231,7 +2284,7 @@ static int ip_mc_add_src(struct in_device *in_dev, __be32 *pmca, int sfmode,
#endif
isexclude = pmc->sfmode == MCAST_EXCLUDE;
if (!delta)
- pmc->sfcount[sfmode]++;
+ WRITE_ONCE(pmc->sfcount[sfmode], pmc->sfcount[sfmode] + 1);
err = 0;
for (i = 0; i < sfcount; i++) {
err = ip_mc_add1_src(pmc, sfmode, &psfsrc[i]);
@@ -2242,7 +2295,7 @@ static int ip_mc_add_src(struct in_device *in_dev, __be32 *pmca, int sfmode,
int j;
if (!delta)
- pmc->sfcount[sfmode]--;
+ WRITE_ONCE(pmc->sfcount[sfmode], pmc->sfcount[sfmode] - 1);
for (j = 0; j < i; j++)
(void) ip_mc_del1_src(pmc, sfmode, &psfsrc[j]);
} else if (isexclude != (pmc->sfcount[MCAST_EXCLUDE] != 0)) {
@@ -2262,7 +2315,7 @@ static int ip_mc_add_src(struct in_device *in_dev, __be32 *pmca, int sfmode,
pmc->crcount = in_dev->mr_qrv ?: READ_ONCE(net->ipv4.sysctl_igmp_qrv);
WRITE_ONCE(in_dev->mr_ifc_count, pmc->crcount);
- for (psf = pmc->sources; psf; psf = psf->sf_next)
+ for_each_psf_mclock(pmc, psf)
psf->sf_crcount = 0;
igmp_ifc_event(in_dev);
} else if (sf_setstate(pmc)) {
@@ -2278,13 +2331,13 @@ static void ip_mc_clear_src(struct ip_mc_list *pmc)
struct ip_sf_list *tomb, *sources;
spin_lock_bh(&pmc->lock);
- tomb = pmc->tomb;
- pmc->tomb = NULL;
- sources = pmc->sources;
- pmc->sources = NULL;
+ tomb = pmc_dereference(pmc->tomb, pmc);
+ RCU_INIT_POINTER(pmc->tomb, NULL);
+ sources = pmc_dereference(pmc->sources, pmc);
+ RCU_INIT_POINTER(pmc->sources, NULL);
pmc->sfmode = MCAST_EXCLUDE;
- pmc->sfcount[MCAST_INCLUDE] = 0;
- pmc->sfcount[MCAST_EXCLUDE] = 1;
+ WRITE_ONCE(pmc->sfcount[MCAST_INCLUDE], 0);
+ WRITE_ONCE(pmc->sfcount[MCAST_EXCLUDE], 1);
spin_unlock_bh(&pmc->lock);
ip_sf_list_clear_all(tomb);
@@ -2866,20 +2919,19 @@ int ip_check_mc_rcu(struct in_device *in_dev, __be32 mc_addr, __be32 src_addr, u
rv = 1;
} else if (im) {
if (src_addr) {
- spin_lock_bh(&im->lock);
- for (psf = im->sources; psf; psf = psf->sf_next) {
+ for_each_psf_rcu(im, psf) {
if (psf->sf_inaddr == src_addr)
break;
}
if (psf)
- rv = psf->sf_count[MCAST_INCLUDE] ||
- psf->sf_count[MCAST_EXCLUDE] !=
- im->sfcount[MCAST_EXCLUDE];
+ rv = READ_ONCE(psf->sf_count[MCAST_INCLUDE]) ||
+ READ_ONCE(psf->sf_count[MCAST_EXCLUDE]) !=
+ READ_ONCE(im->sfcount[MCAST_EXCLUDE]);
else
- rv = im->sfcount[MCAST_EXCLUDE] != 0;
- spin_unlock_bh(&im->lock);
- } else
+ rv = READ_ONCE(im->sfcount[MCAST_EXCLUDE]) != 0;
+ } else {
rv = 1; /* unspecified source; tentatively allow */
+ }
}
return rv;
}
@@ -3043,7 +3095,7 @@ static inline struct ip_sf_list *igmp_mcf_get_first(struct seq_file *seq)
im = rcu_dereference(idev->mc_list);
if (likely(im)) {
spin_lock_bh(&im->lock);
- psf = im->sources;
+ psf = pmc_dereference(im->sources, im);
if (likely(psf)) {
state->im = im;
state->idev = idev;
@@ -3059,7 +3111,7 @@ static struct ip_sf_list *igmp_mcf_get_next(struct seq_file *seq, struct ip_sf_l
{
struct igmp_mcf_iter_state *state = igmp_mcf_seq_private(seq);
- psf = psf->sf_next;
+ psf = pmc_dereference(psf->sf_next, state->im);
while (!psf) {
spin_unlock_bh(&state->im->lock);
state->im = state->im->next;
@@ -3075,7 +3127,7 @@ static struct ip_sf_list *igmp_mcf_get_next(struct seq_file *seq, struct ip_sf_l
state->im = rcu_dereference(state->idev->mc_list);
}
spin_lock_bh(&state->im->lock);
- psf = state->im->sources;
+ psf = pmc_dereference(state->im->sources, state->im);
}
out:
return psf;
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index b4237d0e994d..1c867a302444 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -3182,8 +3182,7 @@ void __tcp_close(struct sock *sk, long timeout)
/* Unread data was tossed, zap the connection. */
NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPABORTONCLOSE);
tcp_set_state(sk, TCP_CLOSE);
- tcp_send_active_reset(sk, sk->sk_allocation,
- SK_RST_REASON_TCP_ABORT_ON_CLOSE);
+ tcp_send_active_reset(sk, SK_RST_REASON_TCP_ABORT_ON_CLOSE);
} else if (sock_flag(sk, SOCK_LINGER) && !sk->sk_lingertime) {
/* Check zero linger _after_ checking for unread data. */
sk->sk_prot->disconnect(sk, 0);
@@ -3257,7 +3256,7 @@ adjudge_to_death:
struct tcp_sock *tp = tcp_sk(sk);
if (READ_ONCE(tp->linger2) < 0) {
tcp_set_state(sk, TCP_CLOSE);
- tcp_send_active_reset(sk, GFP_ATOMIC,
+ tcp_send_active_reset(sk,
SK_RST_REASON_TCP_ABORT_ON_LINGER);
__NET_INC_STATS(sock_net(sk),
LINUX_MIB_TCPABORTONLINGER);
@@ -3276,7 +3275,7 @@ adjudge_to_death:
if (sk->sk_state != TCP_CLOSE) {
if (tcp_check_oom(sk, 0)) {
tcp_set_state(sk, TCP_CLOSE);
- tcp_send_active_reset(sk, GFP_ATOMIC,
+ tcp_send_active_reset(sk,
SK_RST_REASON_TCP_ABORT_ON_MEMORY);
__NET_INC_STATS(sock_net(sk),
LINUX_MIB_TCPABORTONMEMORY);
@@ -3377,14 +3376,14 @@ int tcp_disconnect(struct sock *sk, int flags)
} else if (unlikely(tp->repair)) {
WRITE_ONCE(sk->sk_err, ECONNABORTED);
} else if (tcp_need_reset(old_state)) {
- tcp_send_active_reset(sk, gfp_any(), SK_RST_REASON_TCP_STATE);
+ tcp_send_active_reset(sk, SK_RST_REASON_TCP_STATE);
WRITE_ONCE(sk->sk_err, ECONNRESET);
} else if (tp->snd_nxt != tp->write_seq &&
(1 << old_state) & (TCPF_CLOSING | TCPF_LAST_ACK)) {
/* The last check adjusts for discrepancy of Linux wrt. RFC
* states
*/
- tcp_send_active_reset(sk, gfp_any(),
+ tcp_send_active_reset(sk,
SK_RST_REASON_TCP_DISCONNECT_WITH_DATA);
WRITE_ONCE(sk->sk_err, ECONNRESET);
} else if (old_state == TCP_SYN_SENT)
@@ -4562,9 +4561,11 @@ int do_tcp_getsockopt(struct sock *sk, int level,
if (copy_from_sockptr(&len, optlen, sizeof(int)))
return -EFAULT;
- ca_ops = icsk->icsk_ca_ops;
+ rcu_read_lock();
+ ca_ops = READ_ONCE(icsk->icsk_ca_ops);
if (ca_ops && ca_ops->get_info)
sz = ca_ops->get_info(sk, ~0U, &attr, &info);
+ rcu_read_unlock();
len = min_t(unsigned int, len, sz);
if (copy_to_sockptr(optlen, &len, sizeof(int)))
@@ -4577,16 +4578,24 @@ int do_tcp_getsockopt(struct sock *sk, int level,
val = !inet_csk_in_pingpong_mode(sk);
break;
- case TCP_CONGESTION:
+ case TCP_CONGESTION: {
+ char ca_name[TCP_CA_NAME_MAX] = {};
+
if (copy_from_sockptr(&len, optlen, sizeof(int)))
return -EFAULT;
len = min_t(unsigned int, len, TCP_CA_NAME_MAX);
if (copy_to_sockptr(optlen, &len, sizeof(int)))
return -EFAULT;
- if (copy_to_sockptr(optval, icsk->icsk_ca_ops->name, len))
+
+ rcu_read_lock();
+ memcpy(ca_name, READ_ONCE(icsk->icsk_ca_ops)->name,
+ sizeof(ca_name));
+ rcu_read_unlock();
+
+ if (copy_to_sockptr(optval, ca_name, len))
return -EFAULT;
return 0;
-
+ }
case TCP_ULP:
if (copy_from_sockptr(&len, optlen, sizeof(int)))
return -EFAULT;
@@ -5147,8 +5156,7 @@ int tcp_abort(struct sock *sk, int err)
bh_lock_sock(sk);
if (tcp_need_reset(sk->sk_state))
- tcp_send_active_reset(sk, GFP_ATOMIC,
- SK_RST_REASON_TCP_STATE);
+ tcp_send_active_reset(sk, SK_RST_REASON_TCP_STATE);
tcp_done_with_error(sk, err);
bh_unlock_sock(sk);
diff --git a/net/ipv4/tcp_cong.c b/net/ipv4/tcp_cong.c
index e9f6c77e0631..8e83ef81fc18 100644
--- a/net/ipv4/tcp_cong.c
+++ b/net/ipv4/tcp_cong.c
@@ -223,7 +223,7 @@ void tcp_assign_congestion_control(struct sock *sk)
ca = rcu_dereference(net->ipv4.tcp_congestion_control);
if (unlikely(!bpf_try_module_get(ca, ca->owner)))
ca = &tcp_reno;
- icsk->icsk_ca_ops = ca;
+ WRITE_ONCE(icsk->icsk_ca_ops, ca);
rcu_read_unlock();
memset(icsk->icsk_ca_priv, 0, sizeof(icsk->icsk_ca_priv));
@@ -253,7 +253,7 @@ static void tcp_reinit_congestion_control(struct sock *sk,
struct inet_connection_sock *icsk = inet_csk(sk);
tcp_cleanup_congestion_control(sk);
- icsk->icsk_ca_ops = ca;
+ WRITE_ONCE(icsk->icsk_ca_ops, ca);
icsk->icsk_ca_setsockopt = 1;
memset(icsk->icsk_ca_priv, 0, sizeof(icsk->icsk_ca_priv));
diff --git a/net/ipv4/tcp_dctcp.c b/net/ipv4/tcp_dctcp.c
index 274e628e7cf8..5b457f68a581 100644
--- a/net/ipv4/tcp_dctcp.c
+++ b/net/ipv4/tcp_dctcp.c
@@ -111,7 +111,7 @@ __bpf_kfunc static void dctcp_init(struct sock *sk)
/* No ECN support? Fall back to Reno. Also need to clear
* ECT from sk since it is set during 3WHS for DCTCP.
*/
- inet_csk(sk)->icsk_ca_ops = &dctcp_reno;
+ WRITE_ONCE(inet_csk(sk)->icsk_ca_ops, &dctcp_reno);
INET_ECN_dontxmit(sk);
}
@@ -228,7 +228,7 @@ static size_t dctcp_get_info(struct sock *sk, u32 ext, int *attr,
if (ext & (1 << (INET_DIAG_DCTCPINFO - 1)) ||
ext & (1 << (INET_DIAG_VEGASINFO - 1))) {
memset(&info->dctcp, 0, sizeof(info->dctcp));
- if (inet_csk(sk)->icsk_ca_ops != &dctcp_reno) {
+ if (READ_ONCE(inet_csk(sk)->icsk_ca_ops) != &dctcp_reno) {
info->dctcp.dctcp_enabled = 1;
info->dctcp.dctcp_ce_state = (u16) ca->ce_state;
info->dctcp.dctcp_alpha = ca->dctcp_alpha;
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 9f053eb8b46e..04dbb2babbcd 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -2931,8 +2931,8 @@ static int bpf_iter_tcp_realloc_batch(struct bpf_tcp_iter_state *iter,
{
union bpf_tcp_iter_batch_item *new_batch;
- new_batch = kvmalloc_array(new_batch_sz, sizeof(*new_batch),
- flags | __GFP_NOWARN);
+ new_batch = kvmalloc_objs(*new_batch, new_batch_sz,
+ flags | __GFP_NOWARN);
if (!new_batch)
return -ENOMEM;
diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c
index f3fa0b18eda0..0ddfd5af6e58 100644
--- a/net/ipv4/tcp_minisocks.c
+++ b/net/ipv4/tcp_minisocks.c
@@ -507,7 +507,7 @@ void tcp_ca_openreq_child(struct sock *sk, const struct dst_entry *dst)
ca = tcp_ca_find_key(ca_key);
if (likely(ca && bpf_try_module_get(ca, ca->owner))) {
icsk->icsk_ca_dst_locked = tcp_ca_dst_locked(dst);
- icsk->icsk_ca_ops = ca;
+ WRITE_ONCE(icsk->icsk_ca_ops, ca);
ca_got_dst = true;
}
rcu_read_unlock();
diff --git a/net/ipv4/tcp_offload.c b/net/ipv4/tcp_offload.c
index 3b1fdcd3cb29..e74d99ca9fac 100644
--- a/net/ipv4/tcp_offload.c
+++ b/net/ipv4/tcp_offload.c
@@ -332,6 +332,7 @@ struct sk_buff *tcp_gro_receive(struct list_head *head, struct sk_buff *skb,
flush |= skb->ip_summed != p->ip_summed;
flush |= skb->csum_level != p->csum_level;
flush |= NAPI_GRO_CB(p)->count >= 64;
+ flush |= NAPI_GRO_CB(p)->is_flist != NAPI_GRO_CB(skb)->is_flist;
skb_set_network_header(skb, skb_gro_receive_network_offset(skb));
if (flush || skb_gro_receive_list(p, skb))
@@ -395,12 +396,20 @@ static void tcp4_check_fraglist_gro(struct list_head *head, struct sk_buff *skb,
struct net *net;
int iif, sdif;
- if (likely(!(skb->dev->features & NETIF_F_GRO_FRAGLIST)))
- return;
-
p = tcp_gro_lookup(head, th);
if (p) {
- NAPI_GRO_CB(skb)->is_flist = NAPI_GRO_CB(p)->is_flist;
+ /* flist GRO applies to consecutive non-GSO skbs */
+ if (!skb_is_gso(skb) || !NAPI_GRO_CB(p)->is_flist) {
+ NAPI_GRO_CB(skb)->is_flist = NAPI_GRO_CB(p)->is_flist;
+ return;
+ }
+
+ /* Fall back to the regular GRO path */
+ if (NAPI_GRO_CB(p)->count == 1)
+ NAPI_GRO_CB(p)->is_flist = 0;
+
+ NAPI_GRO_CB(skb)->is_flist = 0;
+
return;
}
@@ -410,7 +419,7 @@ static void tcp4_check_fraglist_gro(struct list_head *head, struct sk_buff *skb,
sk = __inet_lookup_established(net, iph->saddr, th->source,
iph->daddr, ntohs(th->dest),
iif, sdif);
- NAPI_GRO_CB(skb)->is_flist = !sk;
+ NAPI_GRO_CB(skb)->is_flist = !sk && !skb_is_gso(skb);
if (sk)
sock_gen_put(sk);
}
@@ -430,7 +439,8 @@ struct sk_buff *tcp4_gro_receive(struct list_head *head, struct sk_buff *skb)
if (!th)
goto flush;
- tcp4_check_fraglist_gro(head, skb, th);
+ if (unlikely(skb->dev->features & NETIF_F_GRO_FRAGLIST))
+ tcp4_check_fraglist_gro(head, skb, th);
return tcp_gro_receive(head, skb, th);
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 6f4dca4a4de9..d960e3de7d50 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -3849,9 +3849,9 @@ void tcp_send_fin(struct sock *sk)
* was unread data in the receive queue. This behavior is recommended
* by RFC 2525, section 2.17. -DaveM
*/
-void tcp_send_active_reset(struct sock *sk, gfp_t priority,
- enum sk_rst_reason reason)
+void tcp_send_active_reset(struct sock *sk, enum sk_rst_reason reason)
{
+ gfp_t priority = sk_gfp_mask(sk, GFP_ATOMIC | __GFP_NOWARN);
struct sk_buff *skb;
TCP_INC_STATS(sock_net(sk), TCP_MIB_OUTRSTS);
@@ -4092,7 +4092,7 @@ static void tcp_ca_dst_init(struct sock *sk, const struct dst_entry *dst)
if (likely(ca && bpf_try_module_get(ca, ca->owner))) {
bpf_module_put(icsk->icsk_ca_ops, icsk->icsk_ca_ops->owner);
icsk->icsk_ca_dst_locked = tcp_ca_dst_locked(dst);
- icsk->icsk_ca_ops = ca;
+ WRITE_ONCE(icsk->icsk_ca_ops, ca);
}
rcu_read_unlock();
}
diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c
index 1038e7ba9c2e..e56eae4bc341 100644
--- a/net/ipv4/tcp_timer.c
+++ b/net/ipv4/tcp_timer.c
@@ -126,7 +126,7 @@ static int tcp_out_of_resources(struct sock *sk, bool do_reset)
(!tp->snd_wnd && !tp->packets_out))
do_reset = true;
if (do_reset)
- tcp_send_active_reset(sk, GFP_ATOMIC,
+ tcp_send_active_reset(sk,
SK_RST_REASON_TCP_ABORT_ON_MEMORY);
tcp_done(sk);
__NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPABORTONMEMORY);
@@ -809,7 +809,7 @@ static void tcp_keepalive_timer(struct timer_list *t)
goto out;
}
}
- tcp_send_active_reset(sk, GFP_ATOMIC, SK_RST_REASON_TCP_STATE);
+ tcp_send_active_reset(sk, SK_RST_REASON_TCP_STATE);
goto death;
}
@@ -836,7 +836,7 @@ static void tcp_keepalive_timer(struct timer_list *t)
icsk->icsk_probes_out > 0) ||
(user_timeout == 0 &&
icsk->icsk_probes_out >= keepalive_probes(tp))) {
- tcp_send_active_reset(sk, GFP_ATOMIC,
+ tcp_send_active_reset(sk,
SK_RST_REASON_TCP_KEEPALIVE_TIMEOUT);
tcp_write_err(sk);
goto out;
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index af9603217444..bb8cfc62cb00 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -900,6 +900,15 @@ out:
return sk;
}
+static void udp_err_update_exception(struct net *net, struct sk_buff *skb,
+ int type, int code, u32 info)
+{
+ if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED)
+ ipv4_update_pmtu(skb, net, info, 0, IPPROTO_UDP);
+ else if (type == ICMP_REDIRECT)
+ ipv4_redirect(skb, net, 0, IPPROTO_UDP);
+}
+
/*
* This routine is called by the ICMP module when it gets some
* sort of error condition. If err < 0 then the socket should
@@ -923,6 +932,8 @@ int udp_err(struct sk_buff *skb, u32 info)
int harderr;
int err;
+ udp_err_update_exception(net, skb, type, code, info);
+
uh = (struct udphdr *)(skb->data + (iph->ihl << 2));
sk = __udp4_lib_lookup(net, iph->daddr, uh->dest,
iph->saddr, uh->source, skb->dev->ifindex,
@@ -2166,10 +2177,10 @@ int __udp_disconnect(struct sock *sk, int flags)
*/
sk->sk_state = TCP_CLOSE;
- inet->inet_daddr = 0;
+ WRITE_ONCE(inet->inet_daddr, 0);
inet->inet_dport = 0;
sock_rps_reset_rxhash(sk);
- sk->sk_bound_dev_if = 0;
+ WRITE_ONCE(sk->sk_bound_dev_if, 0);
if (!(sk->sk_userlocks & SOCK_BINDADDR_LOCK)) {
inet_reset_saddr(sk);
if (sk->sk_prot->rehash &&
diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c
index 51941ad656a3..09a4552f7f08 100644
--- a/net/ipv6/exthdrs.c
+++ b/net/ipv6/exthdrs.c
@@ -445,7 +445,7 @@ looped_back:
hdr->segments_left--;
addr = hdr->segments + hdr->segments_left;
- skb_push(skb, sizeof(struct ipv6hdr));
+ skb_push(skb, -skb_network_offset(skb));
if (skb->ip_summed == CHECKSUM_COMPLETE)
seg6_update_csum(skb);
@@ -469,7 +469,7 @@ looped_back:
}
ipv6_hdr(skb)->hop_limit--;
- skb_pull(skb, sizeof(struct ipv6hdr));
+ skb_pull(skb, skb_transport_offset(skb));
goto looped_back;
}
diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c
index 69c51f1a5bf0..8ebda0b6a78b 100644
--- a/net/ipv6/ip6_gre.c
+++ b/net/ipv6/ip6_gre.c
@@ -878,6 +878,7 @@ static int ip6gre_xmit_other(struct sk_buff *skb, struct net_device *dev)
static netdev_tx_t ip6gre_tunnel_xmit(struct sk_buff *skb,
struct net_device *dev)
{
+ struct ip_tunnel_info *tun_info = NULL;
struct ip6_tnl *t = netdev_priv(dev);
__be16 payload_protocol;
int ret;
@@ -888,6 +889,9 @@ static netdev_tx_t ip6gre_tunnel_xmit(struct sk_buff *skb,
if (!ip6_tnl_xmit_ctl(t, &t->parms.laddr, &t->parms.raddr))
goto tx_err;
+ if (t->parms.collect_md)
+ tun_info = skb_tunnel_info_txcheck(skb);
+
payload_protocol = skb_protocol(skb, true);
switch (payload_protocol) {
case htons(ETH_P_IP):
@@ -907,7 +911,7 @@ static netdev_tx_t ip6gre_tunnel_xmit(struct sk_buff *skb,
return NETDEV_TX_OK;
tx_err:
- if (!t->parms.collect_md || !IS_ERR(skb_tunnel_info_txcheck(skb)))
+ if (!IS_ERR(tun_info))
DEV_STATS_INC(dev, tx_errors);
DEV_STATS_INC(dev, tx_dropped);
kfree_skb(skb);
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
index aaba4c2aae23..ecef55f26189 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -240,7 +240,8 @@ static int __ipv6_sock_mc_join(struct sock *sk, int ifindex,
return err;
}
- mc_lst->next = np->ipv6_mc_list;
+ rcu_assign_pointer(mc_lst->next,
+ sock_dereference(np->ipv6_mc_list, sk));
rcu_assign_pointer(np->ipv6_mc_list, mc_lst);
return 0;
@@ -300,7 +301,8 @@ int ipv6_sock_mc_drop(struct sock *sk, int ifindex, const struct in6_addr *addr)
lnk = &mc_lst->next) {
if ((ifindex == 0 || mc_lst->ifindex == ifindex) &&
ipv6_addr_equal(&mc_lst->addr, addr)) {
- *lnk = mc_lst->next;
+ rcu_assign_pointer(*lnk,
+ sock_dereference(mc_lst->next, sk));
__ipv6_sock_mc_drop(sk, mc_lst);
return 0;
}
@@ -333,7 +335,8 @@ void __ipv6_sock_mc_close(struct sock *sk)
struct ipv6_mc_socklist *mc_lst;
while ((mc_lst = sock_dereference(np->ipv6_mc_list, sk)) != NULL) {
- np->ipv6_mc_list = mc_lst->next;
+ rcu_assign_pointer(np->ipv6_mc_list,
+ sock_dereference(mc_lst->next, sk));
__ipv6_sock_mc_drop(sk, mc_lst);
}
}
@@ -355,12 +358,12 @@ int ip6_mc_source(int add, int omode, struct sock *sk,
{
struct ipv6_pinfo *inet6 = inet6_sk(sk);
struct in6_addr *source, *group;
+ struct ip6_sf_socklist *newpsl, *psl;
struct net *net = sock_net(sk);
struct ipv6_mc_socklist *pmc;
- struct ip6_sf_socklist *psl;
struct inet6_dev *idev;
int leavegroup = 0;
- int i, j, rv;
+ int i, j;
int err;
source = &((struct sockaddr_in6 *)&pgsr->gsr_source)->sin6_addr;
@@ -409,13 +412,11 @@ int ip6_mc_source(int add, int omode, struct sock *sk,
if (!add) {
if (!psl)
goto done; /* err = -EADDRNOTAVAIL */
- rv = !0;
for (i = 0; i < psl->sl_count; i++) {
- rv = !ipv6_addr_equal(&psl->sl_addr[i], source);
- if (rv == 0)
+ if (ipv6_addr_equal(&psl->sl_addr[i], source))
break;
}
- if (rv) /* source not found */
+ if (i == psl->sl_count) /* source not found */
goto done; /* err = -EADDRNOTAVAIL */
/* special case - (INCLUDE, empty) == LEAVE_GROUP */
@@ -424,58 +425,74 @@ int ip6_mc_source(int add, int omode, struct sock *sk,
goto done;
}
+ atomic_sub(struct_size(psl, sl_addr, psl->sl_max),
+ &sk->sk_omem_alloc);
+
+ if (psl->sl_count == 1) {
+ newpsl = NULL;
+ } else {
+ newpsl = sock_kmalloc(sk, struct_size(newpsl, sl_addr,
+ psl->sl_count - 1),
+ GFP_KERNEL);
+ if (!newpsl) {
+ atomic_add(struct_size(psl, sl_addr, psl->sl_max),
+ &sk->sk_omem_alloc);
+ err = -ENOBUFS;
+ goto done;
+ }
+ newpsl->sl_max = psl->sl_count - 1;
+ newpsl->sl_count = psl->sl_count - 1;
+ for (j = 0; j < i; j++)
+ newpsl->sl_addr[j] = psl->sl_addr[j];
+ for (j = i + 1; j < psl->sl_count; j++)
+ newpsl->sl_addr[j - 1] = psl->sl_addr[j];
+ }
+
/* update the interface filter */
ip6_mc_del_src(idev, group, omode, 1, source, 1);
- for (j = i+1; j < psl->sl_count; j++)
- psl->sl_addr[j-1] = psl->sl_addr[j];
- psl->sl_count--;
+ rcu_assign_pointer(pmc->sflist, newpsl);
+ kfree_rcu(psl, rcu);
err = 0;
goto done;
}
/* else, add a new source to the filter */
- if (psl && psl->sl_count >= sysctl_mld_max_msf) {
+ if (psl && psl->sl_count >= READ_ONCE(sysctl_mld_max_msf)) {
err = -ENOBUFS;
goto done;
}
- if (!psl || psl->sl_count == psl->sl_max) {
- struct ip6_sf_socklist *newpsl;
- int count = IP6_SFBLOCK;
-
- if (psl)
- count += psl->sl_max;
- newpsl = sock_kmalloc(sk, struct_size(newpsl, sl_addr, count),
- GFP_KERNEL);
- if (!newpsl) {
- err = -ENOBUFS;
- goto done;
- }
- newpsl->sl_max = count;
- newpsl->sl_count = count - IP6_SFBLOCK;
- if (psl) {
- for (i = 0; i < psl->sl_count; i++)
- newpsl->sl_addr[i] = psl->sl_addr[i];
- atomic_sub(struct_size(psl, sl_addr, psl->sl_max),
- &sk->sk_omem_alloc);
+ if (psl) {
+ for (i = 0; i < psl->sl_count; i++) {
+ if (ipv6_addr_equal(&psl->sl_addr[i], source))
+ goto done; /* err = -EADDRNOTAVAIL */
}
- rcu_assign_pointer(pmc->sflist, newpsl);
- kfree_rcu(psl, rcu);
- psl = newpsl;
}
- rv = 1; /* > 0 for insert logic below if sl_count is 0 */
- for (i = 0; i < psl->sl_count; i++) {
- rv = !ipv6_addr_equal(&psl->sl_addr[i], source);
- if (rv == 0) /* There is an error in the address. */
- goto done;
+
+ i = psl ? psl->sl_count + 1 : 1;
+ newpsl = sock_kmalloc(sk, struct_size(newpsl, sl_addr, i),
+ GFP_KERNEL);
+ if (!newpsl) {
+ err = -ENOBUFS;
+ goto done;
}
- for (j = psl->sl_count-1; j >= i; j--)
- psl->sl_addr[j+1] = psl->sl_addr[j];
- psl->sl_addr[i] = *source;
- psl->sl_count++;
- err = 0;
+ newpsl->sl_max = i;
+ newpsl->sl_count = i;
+ if (psl) {
+ for (j = 0; j < psl->sl_count; j++)
+ newpsl->sl_addr[j] = psl->sl_addr[j];
+ }
+ newpsl->sl_addr[i - 1] = *source;
+
/* update the interface list */
ip6_mc_add_src(idev, group, omode, 1, source, 1);
+
+ if (psl)
+ atomic_sub(struct_size(psl, sl_addr, psl->sl_max),
+ &sk->sk_omem_alloc);
+ rcu_assign_pointer(pmc->sflist, newpsl);
+ kfree_rcu(psl, rcu);
+ err = 0;
done:
mutex_unlock(&idev->mc_lock);
in6_dev_put(idev);
@@ -784,9 +801,11 @@ static void mld_del_delrec(struct inet6_dev *idev, struct ifmcaddr6 *im)
if (!pmc)
return;
if (pmc_prev)
- rcu_assign_pointer(pmc_prev->next, pmc->next);
+ rcu_assign_pointer(pmc_prev->next,
+ mc_dereference(pmc->next, idev));
else
- rcu_assign_pointer(idev->mc_tomb, pmc->next);
+ rcu_assign_pointer(idev->mc_tomb,
+ mc_dereference(pmc->next, idev));
im->idev = pmc->idev;
if (im->mca_sfmode == MCAST_INCLUDE) {
@@ -966,7 +985,7 @@ static int __ipv6_dev_mc_inc(struct net_device *dev,
return -ENOMEM;
}
- rcu_assign_pointer(mc->next, idev->mc_list);
+ rcu_assign_pointer(mc->next, mc_dereference(idev->mc_list, idev));
rcu_assign_pointer(idev->mc_list, mc);
mld_del_delrec(idev, mc);
@@ -1000,7 +1019,8 @@ int __ipv6_dev_mc_dec(struct inet6_dev *idev, const struct in6_addr *addr)
WRITE_ONCE(ma->mca_users, new_users);
if (new_users == 0) {
- *map = ma->next;
+ rcu_assign_pointer(*map,
+ mc_dereference(ma->next, idev));
igmp6_group_dropped(ma);
inet6_ifmcaddr_notify(idev->dev, ma,
@@ -2351,14 +2371,18 @@ static int ip6_mc_del1_src(struct ifmcaddr6 *pmc, int sfmode,
if (psf->sf_oldin && !(pmc->mca_flags & MAF_NOREPORT) &&
!mld_in_v1_mode(idev)) {
- psf->sf_crcount = idev->mc_qrv;
- rcu_assign_pointer(psf->sf_next,
- mc_dereference(pmc->mca_tomb, idev));
- rcu_assign_pointer(pmc->mca_tomb, psf);
- rv = 1;
- } else {
- kfree_rcu(psf, rcu);
+ struct ip6_sf_list *dpsf = kmalloc_obj(*dpsf);
+
+ if (dpsf) {
+ *dpsf = *psf;
+ dpsf->sf_crcount = idev->mc_qrv;
+ rcu_assign_pointer(dpsf->sf_next,
+ mc_dereference(pmc->mca_tomb, idev));
+ rcu_assign_pointer(pmc->mca_tomb, dpsf);
+ rv = 1;
+ }
}
+ kfree_rcu(psf, rcu);
}
return rv;
}
@@ -2621,7 +2645,7 @@ static void ip6_mc_clear_src(struct ifmcaddr6 *pmc)
static void igmp6_join_group(struct ifmcaddr6 *ma)
{
- unsigned long delay;
+ unsigned long delay, interval;
mc_assert_locked(ma->idev);
@@ -2630,13 +2654,17 @@ static void igmp6_join_group(struct ifmcaddr6 *ma)
igmp6_send(&ma->mca_addr, ma->idev->dev, ICMPV6_MGM_REPORT);
- delay = get_random_u32_below(unsolicited_report_interval(ma->idev));
+ interval = unsolicited_report_interval(ma->idev);
+ delay = interval;
if (cancel_delayed_work(&ma->mca_work)) {
refcount_dec(&ma->mca_refcnt);
delay = ma->mca_work.timer.expires - jiffies;
}
+ if (delay >= interval)
+ delay = get_random_u32_below(interval);
+
if (!mod_delayed_work(mld_wq, &ma->mca_work, delay))
refcount_inc(&ma->mca_refcnt);
WRITE_ONCE(ma->mca_flags, ma->mca_flags |
@@ -3001,7 +3029,7 @@ static int igmp6_mc_seq_show(struct seq_file *seq, void *v)
struct ifmcaddr6 *im = (struct ifmcaddr6 *)v;
struct igmp6_mc_iter_state *state = igmp6_mc_seq_private(seq);
unsigned int mca_flags = READ_ONCE(im->mca_flags);
- unsigned long expires = READ_ONCE(im->mca_work.timer.expires);
+ long delta = READ_ONCE(im->mca_work.timer.expires) - jiffies;
seq_printf(seq,
"%-4d %-15s %pi6 %5d %08X %ld\n",
@@ -3009,7 +3037,7 @@ static int igmp6_mc_seq_show(struct seq_file *seq, void *v)
&im->mca_addr,
READ_ONCE(im->mca_users), mca_flags,
(mca_flags & MAF_TIMER_RUNNING) ?
- jiffies_to_clock_t(expires - jiffies) : 0);
+ jiffies_delta_to_clock_t(delta) : 0);
return 0;
}
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index 6a40c5074543..9658939511e0 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -3255,7 +3255,7 @@ void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif)
void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk)
{
- ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if,
+ ip6_redirect(skb, sock_net(sk), skb->dev->ifindex,
READ_ONCE(sk->sk_mark), sk_uid(sk));
}
diff --git a/net/ipv6/tcpv6_offload.c b/net/ipv6/tcpv6_offload.c
index f2a659cd6183..eec3778855eb 100644
--- a/net/ipv6/tcpv6_offload.c
+++ b/net/ipv6/tcpv6_offload.c
@@ -26,7 +26,18 @@ static void tcp6_check_fraglist_gro(struct list_head *head, struct sk_buff *skb,
p = tcp_gro_lookup(head, th);
if (p) {
- NAPI_GRO_CB(skb)->is_flist = NAPI_GRO_CB(p)->is_flist;
+ /* flist GRO applies to consecutive non-GSO skbs */
+ if (!skb_is_gso(skb) || !NAPI_GRO_CB(p)->is_flist) {
+ NAPI_GRO_CB(skb)->is_flist = NAPI_GRO_CB(p)->is_flist;
+ return;
+ }
+
+ /* Fall back to the regular GRO path */
+ if (NAPI_GRO_CB(p)->count == 1)
+ NAPI_GRO_CB(p)->is_flist = 0;
+
+ NAPI_GRO_CB(skb)->is_flist = 0;
+
return;
}
@@ -36,7 +47,7 @@ static void tcp6_check_fraglist_gro(struct list_head *head, struct sk_buff *skb,
sk = __inet6_lookup_established(net, &hdr->saddr, th->source,
&hdr->daddr, ntohs(th->dest),
iif, sdif);
- NAPI_GRO_CB(skb)->is_flist = !sk;
+ NAPI_GRO_CB(skb)->is_flist = !sk && !skb_is_gso(skb);
if (sk)
sock_gen_put(sk);
#endif /* IS_ENABLED(CONFIG_IPV6) */
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index fd875908ac0c..93478d1ad576 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -690,6 +690,17 @@ out:
return sk;
}
+static void udpv6_err_update_exception(struct net *net, struct sk_buff *skb,
+ u8 type, __be32 info)
+{
+ if (type == ICMPV6_PKT_TOOBIG)
+ ip6_update_pmtu(skb, net, info, skb->dev->ifindex, 0,
+ sock_net_uid(net, NULL));
+ else if (type == NDISC_REDIRECT)
+ ip6_redirect(skb, net, skb->dev->ifindex, 0,
+ sock_net_uid(net, NULL));
+}
+
static int udpv6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
u8 type, u8 code, int offset, __be32 info)
{
@@ -703,6 +714,8 @@ static int udpv6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
int harderr;
int err;
+ udpv6_err_update_exception(net, skb, type, info);
+
daddr = seg6_get_daddr(skb, opt) ? : &hdr->daddr;
saddr = &hdr->saddr;
sk = __udp6_lib_lookup(net, daddr, uh->dest, saddr, uh->source,
diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c
index 4e5cc9da6e06..db261ecd19af 100644
--- a/net/iucv/af_iucv.c
+++ b/net/iucv/af_iucv.c
@@ -210,12 +210,6 @@ static int afiucv_hs_send(struct iucv_message *imsg, struct sock *sock,
phs_hdr->flags = flags;
if (flags == AF_IUCV_FLAG_SYN)
phs_hdr->window = iucv->msglimit;
- else if ((flags == AF_IUCV_FLAG_WIN) || !flags) {
- confirm_recv = atomic_read(&iucv->msg_recv);
- phs_hdr->window = confirm_recv;
- if (confirm_recv)
- phs_hdr->flags = phs_hdr->flags | AF_IUCV_FLAG_WIN;
- }
memcpy(phs_hdr->destUserID, iucv->dst_user_id, 8);
memcpy(phs_hdr->destAppName, iucv->dst_name, 8);
memcpy(phs_hdr->srcUserID, iucv->src_user_id, 8);
@@ -250,13 +244,22 @@ static int afiucv_hs_send(struct iucv_message *imsg, struct sock *sock,
}
skb->protocol = cpu_to_be16(ETH_P_AF_IUCV);
+ /* Claim the receive credit here, not while building the header: every
+ * way this frame can be dropped has now been ruled out, so the window
+ * is zeroed only for as long as the transmit itself takes.
+ */
+ if (flags == AF_IUCV_FLAG_WIN || !flags) {
+ confirm_recv = atomic_xchg(&iucv->msg_recv, 0);
+ phs_hdr->window = confirm_recv;
+ if (confirm_recv)
+ phs_hdr->flags = phs_hdr->flags | AF_IUCV_FLAG_WIN;
+ }
+
atomic_inc(&iucv->skbs_in_xmit);
err = dev_queue_xmit(skb);
if (net_xmit_eval(err)) {
atomic_dec(&iucv->skbs_in_xmit);
- } else {
- atomic_sub(confirm_recv, &iucv->msg_recv);
- WARN_ON(atomic_read(&iucv->msg_recv) < 0);
+ atomic_add(confirm_recv, &iucv->msg_recv);
}
return net_xmit_eval(err);
@@ -1241,6 +1244,7 @@ static int iucv_sock_recvmsg(struct socket *sock, struct msghdr *msg,
struct iucv_sock *iucv = iucv_sk(sk);
unsigned int copied, rlen;
struct sk_buff *skb, *rskb, *cskb;
+ bool send_win = false;
int err = 0;
u32 offset;
@@ -1331,16 +1335,20 @@ static int iucv_sock_recvmsg(struct socket *sock, struct msghdr *msg,
if (skb_queue_empty(&iucv->backlog_skb_q)) {
if (!list_empty(&iucv->message_q.list))
iucv_process_message_q(sk);
- if (atomic_read(&iucv->msg_recv) >=
- iucv->msglimit / 2) {
- err = iucv_send_ctrl(sk, AF_IUCV_FLAG_WIN);
- if (err) {
- sk->sk_state = IUCV_DISCONN;
- sk->sk_state_change(sk);
- }
- }
+ if (iucv->transport == AF_IUCV_TRANS_HIPER &&
+ atomic_read(&iucv->msg_recv) >=
+ iucv->msglimit / 2)
+ send_win = true;
}
spin_unlock_bh(&iucv->message_q.lock);
+
+ if (send_win) {
+ err = iucv_send_ctrl(sk, AF_IUCV_FLAG_WIN);
+ if (err) {
+ sk->sk_state = IUCV_DISCONN;
+ sk->sk_state_change(sk);
+ }
+ }
}
done:
diff --git a/net/mac80211/nan.c b/net/mac80211/nan.c
index 19e08661be43..c7769da9617b 100644
--- a/net/mac80211/nan.c
+++ b/net/mac80211/nan.c
@@ -659,8 +659,7 @@ int ieee80211_nan_set_peer_sched(struct ieee80211_sub_if_data *sdata,
if (!sta)
return -ENOENT;
- new_sched = kzalloc(struct_size(new_sched, channels, sched->n_channels),
- GFP_KERNEL);
+ new_sched = kzalloc_flex(*new_sched, channels, sched->n_channels);
if (!new_sched)
return -ENOMEM;
diff --git a/net/mac802154/ieee802154_i.h b/net/mac802154/ieee802154_i.h
index 8f2bff268392..c53aa293a222 100644
--- a/net/mac802154/ieee802154_i.h
+++ b/net/mac802154/ieee802154_i.h
@@ -76,7 +76,12 @@ struct ieee802154_local {
struct work_struct rx_mac_cmd_work;
/* Association */
- struct ieee802154_pan_device *assoc_dev;
+ /* assoc_lock protects assoc_dev_extended_addr, assoc_addr,
+ * assoc_status, the assoc_done reinit/complete pairing and the
+ * IEEE802154_IS_ASSOCIATING bit in @ongoing.
+ */
+ spinlock_t assoc_lock;
+ __le64 assoc_dev_extended_addr;
struct completion assoc_done;
__le16 assoc_addr;
u8 assoc_status;
diff --git a/net/mac802154/main.c b/net/mac802154/main.c
index ea1efef3572a..63e89bd586e3 100644
--- a/net/mac802154/main.c
+++ b/net/mac802154/main.c
@@ -104,6 +104,7 @@ ieee802154_alloc_hw(size_t priv_data_len, const struct ieee802154_ops *ops)
INIT_WORK(&local->rx_mac_cmd_work, mac802154_rx_mac_cmd_worker);
init_completion(&local->assoc_done);
+ spin_lock_init(&local->assoc_lock);
/* init supported flags with 802.15.4 default ranges */
phy->supported.max_minbe = 8;
diff --git a/net/mac802154/scan.c b/net/mac802154/scan.c
index 005338f89b75..dd156c01ac49 100644
--- a/net/mac802154/scan.c
+++ b/net/mac802154/scan.c
@@ -536,7 +536,9 @@ int mac802154_perform_association(struct ieee802154_sub_if_data *sdata,
struct ieee802154_association_req_frame frame = {};
struct ieee802154_local *local = sdata->local;
struct wpan_dev *wpan_dev = &sdata->wpan_dev;
+ __le16 resp_short_addr;
struct sk_buff *skb;
+ u8 resp_status;
int ret;
frame.mhr.fc.type = IEEE802154_FC_TYPE_MAC_CMD;
@@ -578,9 +580,11 @@ int mac802154_perform_association(struct ieee802154_sub_if_data *sdata,
return ret;
}
- local->assoc_dev = coord;
+ spin_lock(&local->assoc_lock);
reinit_completion(&local->assoc_done);
+ local->assoc_dev_extended_addr = coord->extended_addr;
set_bit(IEEE802154_IS_ASSOCIATING, &local->ongoing);
+ spin_unlock(&local->assoc_lock);
ret = ieee802154_mlme_tx_one_locked(local, sdata, skb);
if (ret) {
@@ -599,25 +603,37 @@ int mac802154_perform_association(struct ieee802154_sub_if_data *sdata,
goto clear_assoc;
}
- if (local->assoc_status != IEEE802154_ASSOCIATION_SUCCESSFUL) {
- if (local->assoc_status == IEEE802154_PAN_AT_CAPACITY)
+ /* The association is complete: mac802154_process_association_resp()
+ * cleared the associating bit before waking us, so a second (e.g.
+ * malicious) ASSOC RESP can no longer pass the recheck and overwrite
+ * the result. Snapshot assoc_status/assoc_addr under the lock.
+ */
+ spin_lock(&local->assoc_lock);
+ resp_status = local->assoc_status;
+ resp_short_addr = local->assoc_addr;
+ spin_unlock(&local->assoc_lock);
+
+ if (resp_status != IEEE802154_ASSOCIATION_SUCCESSFUL) {
+ if (resp_status == IEEE802154_PAN_AT_CAPACITY)
ret = -ERANGE;
else
ret = -EPERM;
dev_warn(&sdata->dev->dev,
"Negative ASSOC RESP received from %8phC: %s\n", &ceaddr,
- local->assoc_status == IEEE802154_PAN_AT_CAPACITY ?
+ resp_status == IEEE802154_PAN_AT_CAPACITY ?
"PAN at capacity" : "access denied");
- goto clear_assoc;
+ return ret;
}
- ret = 0;
- *short_addr = local->assoc_addr;
+ *short_addr = resp_short_addr;
+
+ return 0;
clear_assoc:
+ spin_lock(&local->assoc_lock);
clear_bit(IEEE802154_IS_ASSOCIATING, &local->ongoing);
- local->assoc_dev = NULL;
+ spin_unlock(&local->assoc_lock);
return ret;
}
@@ -639,19 +655,28 @@ int mac802154_process_association_resp(struct ieee802154_sub_if_data *sdata,
dest->mode != IEEE802154_EXTENDED_ADDRESSING))
return -EINVAL;
- if (unlikely(dest->extended_addr != wpan_dev->extended_addr ||
- src->extended_addr != local->assoc_dev->extended_addr))
+ spin_lock(&local->assoc_lock);
+ if (unlikely(!test_bit(IEEE802154_IS_ASSOCIATING, &local->ongoing) ||
+ dest->extended_addr != wpan_dev->extended_addr ||
+ src->extended_addr != local->assoc_dev_extended_addr)) {
+ spin_unlock(&local->assoc_lock);
return -ENODEV;
+ }
memcpy(&resp_pl, skb->data, sizeof(resp_pl));
local->assoc_addr = resp_pl.short_addr;
local->assoc_status = resp_pl.status;
+ /* Clear the associating bit before waking the waiter: once the result
+ * is saved, any subsequent (e.g. malicious) ASSOC RESP must fail the
+ * test_bit() recheck above and can no longer overwrite the result.
+ */
+ clear_bit(IEEE802154_IS_ASSOCIATING, &local->ongoing);
+ complete(&local->assoc_done);
+ spin_unlock(&local->assoc_lock);
dev_dbg(&skb->dev->dev,
"ASSOC RESP 0x%x received from %8phC, getting short address %04x\n",
- local->assoc_status, &deaddr, local->assoc_addr);
-
- complete(&local->assoc_done);
+ resp_pl.status, &deaddr, resp_pl.short_addr);
return 0;
}
diff --git a/net/mctp/test/route-test.c b/net/mctp/test/route-test.c
index c92e3abb40d7..f05f75bf93e0 100644
--- a/net/mctp/test/route-test.c
+++ b/net/mctp/test/route-test.c
@@ -193,7 +193,7 @@ static void __mctp_route_test_init(struct kunit *test,
if (netid != MCTP_NET_ANY)
WRITE_ONCE(dev->mdev->net, netid);
- dev->mdev->addrs = kmalloc_objs(u8, 1, GFP_KERNEL);
+ dev->mdev->addrs = kmalloc_objs(u8, 1);
dev->mdev->num_addrs = 1;
dev->mdev->addrs[0] = 8;
diff --git a/net/mctp/test/utils.c b/net/mctp/test/utils.c
index 6eef8d485c25..6b131084a249 100644
--- a/net/mctp/test/utils.c
+++ b/net/mctp/test/utils.c
@@ -88,7 +88,7 @@ struct mctp_test_dev *mctp_test_create_dev_with_addr(mctp_eid_t addr)
if (!dev)
return NULL;
- dev->mdev->addrs = kmalloc_objs(u8, 1, GFP_KERNEL);
+ dev->mdev->addrs = kmalloc_objs(u8, 1);
if (!dev->mdev->addrs) {
mctp_test_destroy_dev(dev);
return NULL;
diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
index b474d03620a7..e1f08f71cdb1 100644
--- a/net/mptcp/protocol.c
+++ b/net/mptcp/protocol.c
@@ -3109,8 +3109,7 @@ static void mptcp_do_fastclose(struct sock *sk)
*/
inet_csk(ssk)->icsk_ack.rcv_mss = TCP_MIN_MSS;
- tcp_send_active_reset(ssk, ssk->sk_allocation,
- SK_RST_REASON_TCP_ABORT_ON_CLOSE);
+ tcp_send_active_reset(ssk, SK_RST_REASON_TCP_ABORT_ON_CLOSE);
unlock:
release_sock(ssk);
}
diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h
index 06a107d4e839..87ccb84e9927 100644
--- a/net/mptcp/protocol.h
+++ b/net/mptcp/protocol.h
@@ -690,7 +690,7 @@ mptcp_send_active_reset_reason(struct sock *sk)
enum sk_rst_reason reason;
reason = sk_rst_convert_mptcp_reason(subflow->reset_reason);
- tcp_send_active_reset(sk, GFP_ATOMIC, reason);
+ tcp_send_active_reset(sk, reason);
}
/* Made the fwd mem carried by the given skb available to the msk,
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index 765a92fa90d6..31fbd5a28937 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -3215,7 +3215,7 @@ static int nft_trans_delhook(struct nft_hook *hook,
{
struct nft_trans_hook *trans_hook;
- trans_hook = kmalloc_obj(*trans_hook, GFP_KERNEL);
+ trans_hook = kmalloc_obj(*trans_hook);
if (!trans_hook)
return -ENOMEM;
diff --git a/net/netfilter/nfnetlink_cttimeout.c b/net/netfilter/nfnetlink_cttimeout.c
index 66c2016f6049..f43bf768b31c 100644
--- a/net/netfilter/nfnetlink_cttimeout.c
+++ b/net/netfilter/nfnetlink_cttimeout.c
@@ -150,7 +150,7 @@ static int cttimeout_new_timeout(struct sk_buff *skb,
goto err_proto_put;
}
- timeout = kzalloc(sizeof(*timeout), GFP_KERNEL);
+ timeout = kzalloc_obj(*timeout);
if (timeout == NULL) {
ret = -ENOMEM;
goto err_proto_put;
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index b22cda322136..76bde7906d49 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -2675,7 +2675,8 @@ static int tpacket_parse_header(struct packet_sock *po, void *frame,
int size_max, void **data)
{
union tpacket_uhdr ph;
- int tp_len, off;
+ u32 tp_len;
+ int off;
ph.raw = frame;
@@ -2695,7 +2696,7 @@ static int tpacket_parse_header(struct packet_sock *po, void *frame,
break;
}
if (unlikely(tp_len > size_max)) {
- pr_err("packet size is too long (%d > %d)\n", tp_len, size_max);
+ pr_err("packet size is too long (%u > %d)\n", tp_len, size_max);
return -EMSGSIZE;
}
diff --git a/net/qrtr/af_qrtr.c b/net/qrtr/af_qrtr.c
index a30fa56e6aa3..78347c937af7 100644
--- a/net/qrtr/af_qrtr.c
+++ b/net/qrtr/af_qrtr.c
@@ -9,6 +9,7 @@
#include <linux/termios.h> /* For TIOCINQ/OUTQ */
#include <linux/spinlock.h>
#include <linux/wait.h>
+#include <linux/workqueue.h>
#include <net/sock.h>
@@ -120,8 +121,10 @@ static DEFINE_XARRAY_ALLOC(qrtr_ports);
* @nid: node id
* @qrtr_tx_flow: xarray of qrtr_tx_flow, keyed by node << 32 | port
* @qrtr_tx_lock: lock for qrtr_tx_flow inserts
+ * @hello_sent: hello packet send successful
* @rx_queue: receive queue
* @item: list item for broadcast list
+ * @say_hello: delayed work for sending hello packet
*/
struct qrtr_node {
struct mutex ep_lock;
@@ -132,8 +135,11 @@ struct qrtr_node {
struct xarray qrtr_tx_flow;
struct mutex qrtr_tx_lock; /* for qrtr_tx_flow */
+ bool hello_sent;
+
struct sk_buff_head rx_queue;
struct list_head item;
+ struct delayed_work say_hello;
};
/**
@@ -187,6 +193,8 @@ static void __qrtr_node_release(struct kref *kref)
list_del(&node->item);
mutex_unlock(&qrtr_node_lock);
+ cancel_delayed_work_sync(&node->say_hello);
+
skb_queue_purge(&node->rx_queue);
/* Free tx flow counters */
@@ -341,6 +349,14 @@ static int qrtr_node_enqueue(struct qrtr_node *node, struct sk_buff *skb,
size_t len = skb->len;
int rc, confirm_rx;
+ mutex_lock(&node->ep_lock);
+ if (!node->hello_sent && type != QRTR_TYPE_HELLO) {
+ mutex_unlock(&node->ep_lock);
+ kfree_skb(skb);
+ return -EAGAIN;
+ }
+ mutex_unlock(&node->ep_lock);
+
confirm_rx = qrtr_tx_wait(node, to->sq_node, to->sq_port, type);
if (confirm_rx < 0) {
kfree_skb(skb);
@@ -353,7 +369,7 @@ static int qrtr_node_enqueue(struct qrtr_node *node, struct sk_buff *skb,
hdr->src_node_id = cpu_to_le32(from->sq_node);
hdr->src_port_id = cpu_to_le32(from->sq_port);
if (to->sq_port == QRTR_PORT_CTRL) {
- hdr->dst_node_id = cpu_to_le32(node->nid);
+ hdr->dst_node_id = cpu_to_le32(READ_ONCE(node->nid));
hdr->dst_port_id = cpu_to_le32(QRTR_PORT_CTRL);
} else {
hdr->dst_node_id = cpu_to_le32(to->sq_node);
@@ -372,6 +388,8 @@ static int qrtr_node_enqueue(struct qrtr_node *node, struct sk_buff *skb,
rc = node->ep->xmit(node->ep, skb);
else
kfree_skb(skb);
+ if (!rc && type == QRTR_TYPE_HELLO)
+ node->hello_sent = true;
mutex_unlock(&node->ep_lock);
}
/* Need to ensure that a subsequent message carries the otherwise lost
@@ -379,6 +397,9 @@ static int qrtr_node_enqueue(struct qrtr_node *node, struct sk_buff *skb,
if (rc && confirm_rx)
qrtr_tx_flow_failed(node, to->sq_node, to->sq_port);
+ if (rc == -EAGAIN && type == QRTR_TYPE_HELLO)
+ schedule_delayed_work(&node->say_hello, msecs_to_jiffies(100));
+
return rc;
}
@@ -416,7 +437,7 @@ static void qrtr_node_assign(struct qrtr_node *node, unsigned int nid)
spin_lock_irqsave(&qrtr_nodes_lock, flags);
radix_tree_insert(&qrtr_nodes, nid, node);
if (node->nid == QRTR_EP_NID_AUTO)
- node->nid = nid;
+ WRITE_ONCE(node->nid, nid);
spin_unlock_irqrestore(&qrtr_nodes_lock, flags);
}
@@ -570,6 +591,38 @@ static struct sk_buff *qrtr_alloc_ctrl_packet(struct qrtr_ctrl_pkt **pkt,
return skb;
}
+static void qrtr_hello_work(struct work_struct *work)
+{
+ struct sockaddr_qrtr from = {AF_QIPCRTR, 0, QRTR_PORT_CTRL};
+ struct sockaddr_qrtr to = {AF_QIPCRTR, 0, QRTR_PORT_CTRL};
+ struct qrtr_ctrl_pkt *pkt;
+ struct qrtr_node *node;
+ struct qrtr_sock *ctrl;
+ struct sk_buff *skb;
+
+ node = container_of(to_delayed_work(work), struct qrtr_node, say_hello);
+
+ /* NS must be bound before we can send; retry with backoff if not ready */
+ ctrl = qrtr_port_lookup(QRTR_PORT_CTRL);
+ if (!ctrl) {
+ schedule_delayed_work(&node->say_hello, msecs_to_jiffies(100));
+ return;
+ }
+
+ skb = qrtr_alloc_ctrl_packet(&pkt, GFP_KERNEL);
+ if (!skb) {
+ qrtr_port_put(ctrl);
+ schedule_delayed_work(&node->say_hello, msecs_to_jiffies(100));
+ return;
+ }
+
+ pkt->cmd = cpu_to_le32(QRTR_TYPE_HELLO);
+ from.sq_node = qrtr_local_nid;
+ to.sq_node = node->nid;
+ qrtr_node_enqueue(node, skb, QRTR_TYPE_HELLO, &from, &to);
+ qrtr_port_put(ctrl);
+}
+
/**
* qrtr_endpoint_register() - register a new endpoint
* @ep: endpoint to register
@@ -595,6 +648,9 @@ int qrtr_endpoint_register(struct qrtr_endpoint *ep, unsigned int nid)
node->nid = QRTR_EP_NID_AUTO;
node->ep = ep;
+ node->hello_sent = false;
+ INIT_DELAYED_WORK(&node->say_hello, qrtr_hello_work);
+
xa_init(&node->qrtr_tx_flow);
mutex_init(&node->qrtr_tx_lock);
@@ -605,6 +661,9 @@ int qrtr_endpoint_register(struct qrtr_endpoint *ep, unsigned int nid)
mutex_unlock(&qrtr_node_lock);
ep->node = node;
+ /* Initiate HELLO handshake from the core layer */
+ schedule_delayed_work(&node->say_hello, 0);
+
return 0;
}
EXPORT_SYMBOL_GPL(qrtr_endpoint_register);
@@ -879,6 +938,9 @@ static int qrtr_bcast_enqueue(struct qrtr_node *node, struct sk_buff *skb,
mutex_lock(&qrtr_node_lock);
list_for_each_entry(node, &qrtr_all_nodes, item) {
+ /* Skip nodes with no assigned node ID yet. */
+ if (READ_ONCE(node->nid) == QRTR_EP_NID_AUTO)
+ continue;
skbn = pskb_copy(skb, GFP_KERNEL);
if (!skbn)
break;
diff --git a/net/qrtr/ns.c b/net/qrtr/ns.c
index c5e7e01db249..bcb090ee79d4 100644
--- a/net/qrtr/ns.c
+++ b/net/qrtr/ns.c
@@ -212,6 +212,7 @@ static void lookup_notify(struct sockaddr_qrtr *to, struct qrtr_server *srv,
pr_err("failed to send lookup notification\n");
}
+/* Announce the list of servers registered on the local node */
static int announce_servers(struct sockaddr_qrtr *sq)
{
struct qrtr_server *srv;
@@ -326,38 +327,8 @@ static int server_del(struct qrtr_node *node, unsigned int port, bool bcast)
return 0;
}
-static int say_hello(struct sockaddr_qrtr *dest)
-{
- struct qrtr_ctrl_pkt pkt;
- struct msghdr msg = { };
- struct kvec iv;
- int ret;
-
- iv.iov_base = &pkt;
- iv.iov_len = sizeof(pkt);
-
- memset(&pkt, 0, sizeof(pkt));
- pkt.cmd = cpu_to_le32(QRTR_TYPE_HELLO);
-
- msg.msg_name = (struct sockaddr *)dest;
- msg.msg_namelen = sizeof(*dest);
-
- ret = kernel_sendmsg(qrtr_ns.sock, &msg, &iv, 1, sizeof(pkt));
- if (ret < 0)
- pr_err("failed to send hello msg\n");
-
- return ret;
-}
-
-/* Announce the list of servers registered on the local node */
static int ctrl_cmd_hello(struct sockaddr_qrtr *sq)
{
- int ret;
-
- ret = say_hello(sq);
- if (ret < 0)
- return ret;
-
return announce_servers(sq);
}
@@ -774,10 +745,6 @@ int qrtr_ns_init(void)
qrtr_ns.bcast_sq.sq_node = QRTR_NODE_BCAST;
qrtr_ns.bcast_sq.sq_port = QRTR_PORT_CTRL;
- ret = say_hello(&qrtr_ns.bcast_sq);
- if (ret < 0)
- goto err_wq;
-
/* As the qrtr ns socket owner and creator is the same module, we have
* to decrease the qrtr module reference count to guarantee that it
* remains zero after the ns socket is created, otherwise, executing
diff --git a/net/rds/connection.c b/net/rds/connection.c
index 7c8ab8e973e1..b6c4beb50eaf 100644
--- a/net/rds/connection.c
+++ b/net/rds/connection.c
@@ -106,10 +106,12 @@ static struct rds_connection *rds_conn_lookup(struct net *net,
}
/*
- * This is called by transports as they're bringing down a connection.
- * It clears partial message state so that the transport can start sending
- * and receiving over this connection again in the future. It is up to
- * the transport to have serialized this call with its send and recv.
+ * This is called by rds_conn_shutdown() once the transport has brought
+ * a path down. It clears partial message state so that the transport
+ * can start sending and receiving over this path again in the future.
+ * The caller owns RDS_IN_XMIT and RDS_RECV_REFILL across this call,
+ * which is what serializes it against the send and receive-refill
+ * paths.
*/
static void rds_conn_path_reset(struct rds_conn_path *cp)
{
@@ -120,7 +122,16 @@ static void rds_conn_path_reset(struct rds_conn_path *cp)
rds_stats_inc(s_conn_reset);
rds_send_path_reset(cp);
- cp->cp_flags = 0;
+
+ /* Clear the bits the reset is responsible for individually: a
+ * blanket cp_flags = 0 is a plain store that can clobber a
+ * concurrent atomic read-modify-write on the same word.
+ * RDS_IN_XMIT and RDS_RECV_REFILL are held as locks by the
+ * caller, rds_conn_shutdown(), which releases them once the
+ * teardown is complete.
+ */
+ clear_bit(RDS_LL_SEND_FULL, &cp->cp_flags);
+ clear_bit(RDS_RECONNECT_PENDING, &cp->cp_flags);
/* Do not clear next_rx_seq here, else we cannot distinguish
* retransmitted packets from new packets, and will hand all
@@ -406,28 +417,70 @@ void rds_conn_shutdown(struct rds_conn_path *cp)
}
mutex_unlock(&cp->cp_cm_lock);
+ /* Quiesce the transmit and receive-refill paths by
+ * acquiring their bit locks, not merely waiting for
+ * them to be released: with a plain wait, either path
+ * can re-take its lock the instant after we sample it
+ * clear and then run concurrently with the transport
+ * shutdown and the path reset below. Holding both
+ * locks across the teardown makes that structurally
+ * impossible.
+ */
wait_event(cp->cp_waitq,
- !test_bit(RDS_IN_XMIT, &cp->cp_flags));
+ !test_and_set_bit_lock(RDS_IN_XMIT, &cp->cp_flags));
wait_event(cp->cp_waitq,
- !test_bit(RDS_RECV_REFILL, &cp->cp_flags));
+ !test_and_set_bit(RDS_RECV_REFILL, &cp->cp_flags));
conn->c_trans->conn_path_shutdown(cp);
rds_conn_path_reset(cp);
+ /* Release the two locks and wake any waiter (e.g.
+ * rds_tcp_reset_callbacks()) that blocked on them while
+ * we held them. The unlock orders the transport's ring
+ * re-initialization and the path reset above before
+ * either bit is seen clear. rds_conn_path_reset() leaves
+ * both bits alone: ownership ends here, not inside the
+ * reset.
+ */
+ clear_bit_unlock(RDS_IN_XMIT, &cp->cp_flags);
+ clear_bit_unlock(RDS_RECV_REFILL, &cp->cp_flags);
+ wake_up_all(&cp->cp_waitq);
+
if (!rds_conn_path_transition(cp, RDS_CONN_DISCONNECTING,
- RDS_CONN_DOWN) &&
- !rds_conn_path_transition(cp, RDS_CONN_ERROR,
RDS_CONN_DOWN)) {
- /* This can happen - eg when we're in the middle of tearing
- * down the connection, and someone unloads the rds module.
- * Quite reproducible with loopback connections.
- * Mostly harmless.
+ /* The path was dropped again while we tore it
+ * down: by a socket state-change callback in
+ * irq context on receipt of a FIN, or by an
+ * accept that claimed the path just before a
+ * drop put it back to RDS_CONN_ERROR and then
+ * installed a fresh socket on it. Unless a
+ * pending destroy suppressed it, the drop also
+ * queued another shutdown pass, and that pass
+ * must run, because it is what tears down
+ * whatever attached to the path after the
+ * transport shutdown above sampled its state.
+ * Consuming the RDS_CONN_ERROR here would turn
+ * that pass into a no-op: leave the state
+ * alone, and let the pass finish the job.
*
- * Note that this also happens with rds-tcp because
- * we could have triggered rds_conn_path_drop in irq
- * mode from rds_tcp_state change on the receipt of
- * a FIN, thus we need to recheck for RDS_CONN_ERROR
- * here.
+ * Quiesce the reconnect timer before bailing
+ * out, though. When a pending destroy did
+ * suppress the queue, no later pass runs, and
+ * rds_conn_path_destroy() is about to flush
+ * cp_down_w and free the path: it must not
+ * find cp_conn_w still armed. A successor
+ * pass, when there is one, re-arms the
+ * reconnect from its own tail.
+ */
+ cancel_delayed_work_sync(&cp->cp_conn_w);
+ clear_bit(RDS_RECONNECT_PENDING, &cp->cp_flags);
+
+ if (rds_conn_path_state(cp) == RDS_CONN_ERROR)
+ return;
+ /* No current cp_state writer leaves a
+ * DISCONNECTING path in any state but
+ * RDS_CONN_ERROR; report loudly if one ever
+ * does.
*/
rds_conn_path_error(cp, "%s: failed to transition "
"to state DOWN, current state "
diff --git a/net/rds/ib_recv.c b/net/rds/ib_recv.c
index 357128d34a54..bd6cb3ffaa57 100644
--- a/net/rds/ib_recv.c
+++ b/net/rds/ib_recv.c
@@ -363,15 +363,14 @@ static int acquire_refill(struct rds_connection *conn)
static void release_refill(struct rds_connection *conn)
{
- clear_bit(RDS_RECV_REFILL, &conn->c_flags);
- smp_mb__after_atomic();
+ clear_bit_unlock(RDS_RECV_REFILL, &conn->c_flags);
/* We don't use wait_on_bit()/wake_up_bit() because our waking is in a
* hot path and finding waiters is very rare. We don't want to walk
* the system-wide hashed waitqueue buckets in the fast path only to
* almost never find waiters.
*/
- if (waitqueue_active(&conn->c_waitq))
+ if (wq_has_sleeper(&conn->c_waitq))
wake_up_all(&conn->c_waitq);
}
@@ -392,7 +391,9 @@ void rds_ib_recv_refill(struct rds_connection *conn, int prefill, gfp_t gfp)
/* the goal here is to just make sure that someone, somewhere
* is posting buffers. If we can't get the refill lock,
- * let them do their thing
+ * let them do their thing. The holder may also be
+ * rds_conn_shutdown() tearing the path down, in which case
+ * there is nothing to post.
*/
if (!acquire_refill(conn))
return;
diff --git a/net/rds/info.c b/net/rds/info.c
index 31e7ad108459..a57f81a05c87 100644
--- a/net/rds/info.c
+++ b/net/rds/info.c
@@ -205,7 +205,7 @@ int rds_info_getsockopt(struct socket *sock, int optname, sockopt_t *opt)
* iterator code to allocate and hand it back.
*/
npages = iov_iter_npages(&opt->iter_out, INT_MAX);
- pages = kvmalloc_array(npages, sizeof(*pages), GFP_KERNEL);
+ pages = kvmalloc_objs(*pages, npages);
if (!pages) {
ret = -ENOMEM;
goto out;
diff --git a/net/rds/send.c b/net/rds/send.c
index 15a1b97f13e7..1afa981e5c06 100644
--- a/net/rds/send.c
+++ b/net/rds/send.c
@@ -114,8 +114,13 @@ static void release_in_xmit(struct rds_conn_path *cp)
* hot path and finding waiters is very rare. We don't want to walk
* the system-wide hashed waitqueue buckets in the fast path only to
* almost never find waiters.
+ *
+ * wq_has_sleeper() supplies the full barrier that orders the wait
+ * queue read after the bit clear; clear_bit_unlock() alone is only
+ * a release and would let this check read a stale empty queue,
+ * losing the wake-up.
*/
- if (waitqueue_active(&cp->cp_waitq))
+ if (wq_has_sleeper(&cp->cp_waitq))
wake_up_all(&cp->cp_waitq);
}
@@ -239,8 +244,11 @@ restart:
WRITE_ONCE(cp->cp_send_gen, send_gen);
/*
- * rds_conn_shutdown() sets the conn state and then tests RDS_IN_XMIT,
- * we do the opposite to avoid races.
+ * rds_conn_shutdown() sets the conn state and then acquires
+ * RDS_IN_XMIT; we take the lock first and then check the state.
+ * Ownership is decided by the atomic RMW on the cp_flags word:
+ * if the teardown won the bit we back off here, and if we won
+ * it the teardown waits until we release it.
*/
if (!rds_conn_path_up(cp)) {
release_in_xmit(cp);
diff --git a/net/rds/tcp.c b/net/rds/tcp.c
index b263634ac750..774a71f88d37 100644
--- a/net/rds/tcp.c
+++ b/net/rds/tcp.c
@@ -115,46 +115,90 @@ void rds_tcp_restore_callbacks(struct socket *sock,
}
/*
- * rds_tcp_reset_callbacks() switches the to the new sock and
- * returns the existing tc->t_sock.
+ * rds_tcp_reset_callbacks() switches a path to a new socket and
+ * releases the old one it finds in tc->t_sock, resolving a duelling
+ * SYN.
*
- * The only functions that set tc->t_sock are rds_tcp_set_callbacks
- * and rds_tcp_reset_callbacks. Send and receive trust that
- * it is set. The absence of RDS_CONN_UP bit protects those paths
- * from being called while it isn't set.
+ * tc->t_sock is set by rds_tcp_set_callbacks() and cleared by
+ * rds_tcp_restore_callbacks(). Four paths write it: the active
+ * connect in rds_tcp_conn_path_connect(), which sets it and clears it
+ * again on failure; the accept path in rds_tcp_accept_one(), which
+ * sets it for a path with no socket yet; the teardown in
+ * rds_tcp_conn_path_shutdown(), which clears it; and the swap done
+ * here, which does both. The connect and accept paths are serialized
+ * against each other by t_conn_path_lock. Send and receive trust
+ * that it is set: the absence of RDS_CONN_UP protects those paths
+ * from being called while it isn't, and the swap done here runs under
+ * RDS_IN_XMIT so that it cannot interleave with a sender already
+ * inside rds_send_xmit().
*/
void rds_tcp_reset_callbacks(struct socket *sock,
struct rds_conn_path *cp)
{
struct rds_tcp_connection *tc = cp->cp_transport_data;
- struct socket *osock = tc->t_sock;
-
- if (!osock)
- goto newsock;
+ struct socket *osock;
/* Need to resolve a duelling SYN between peers.
* We have an outstanding SYN to this peer, which may
* potentially have transitioned to the RDS_CONN_UP state,
* so we must quiesce any send threads before resetting
- * cp_transport_data. We quiesce these threads by setting
- * cp_state to something other than RDS_CONN_UP, and then
- * waiting for any existing threads in rds_send_xmit to
- * complete release_in_xmit(). (Subsequent threads entering
- * rds_send_xmit() will bail on !rds_conn_up().
+ * cp_transport_data. Setting cp_state to something other
+ * than RDS_CONN_UP stops new senders, and owning RDS_IN_XMIT
+ * excludes any thread already inside rds_send_xmit() - or a
+ * teardown in rds_conn_shutdown(), which holds the same lock
+ * for the duration of the transport shutdown - for the whole
+ * socket swap and the rds_send_path_reset() below.
*
- * However an incoming syn-ack at this point would end up
- * marking the conn as RDS_CONN_UP, and would again permit
- * rds_send_xmi() threads through, so ideally we would
- * synchronize on RDS_CONN_UP after lock_sock(), but cannot
- * do that: waiting on !RDS_IN_XMIT after lock_sock() may
- * end up deadlocking with tcp_sendmsg(), and the RDS_IN_XMIT
- * would not get set. As a result, we set c_state to
- * RDS_CONN_RESETTTING, to ensure that rds_tcp_state_change
- * cannot mark rds_conn_path_up() in the window before lock_sock()
+ * An incoming syn-ack at this point would end up marking the
+ * conn as RDS_CONN_UP, and would again permit rds_send_xmit()
+ * threads through, so ideally we would synchronize on
+ * RDS_CONN_UP after lock_sock(), but cannot do that: acquiring
+ * RDS_IN_XMIT after lock_sock() may end up deadlocking with
+ * tcp_sendmsg(), which takes the socket lock while holding
+ * RDS_IN_XMIT. As a result, we set c_state to
+ * RDS_CONN_RESETTING, to ensure that rds_tcp_state_change
+ * cannot mark rds_conn_path_up() in the window before
+ * lock_sock().
+ *
+ * Only make that transition if the path is still connecting
+ * (or already resetting from an earlier duel). A path in any
+ * other state - typically RDS_CONN_DISCONNECTING or
+ * RDS_CONN_ERROR with a shutdown in flight - is dropped
+ * instead. That still replaces its state, with RDS_CONN_ERROR,
+ * and, unless a pending destroy is about to reap the whole
+ * connection anyway, queues one more shutdown pass. A shutdown
+ * already in flight leaves that RDS_CONN_ERROR alone when it
+ * finishes; the queued pass then completes the transition to
+ * RDS_CONN_DOWN and tears down anything that attached to the
+ * path in the meantime.
+ */
+ if (!rds_conn_path_transition(cp, RDS_CONN_CONNECTING,
+ RDS_CONN_RESETTING) &&
+ !rds_conn_path_transition(cp, RDS_CONN_RESETTING,
+ RDS_CONN_RESETTING))
+ rds_conn_path_drop(cp, 0);
+ wait_event(cp->cp_waitq,
+ !test_and_set_bit_lock(RDS_IN_XMIT, &cp->cp_flags));
+
+ /* Read t_sock only while owning RDS_IN_XMIT, never before the
+ * wait: the teardown in rds_conn_shutdown() releases the old
+ * socket and clears t_sock, so a pointer sampled earlier can
+ * be stale by the time we wake up. The teardown holds the
+ * same lock while it does so, so what we read here cannot
+ * change under us until we release it.
+ */
+ osock = tc->t_sock;
+ if (!osock)
+ goto newsock;
+
+ /* reset receive side state for rds_tcp_data_recv() for osock.
+ *
+ * The sync cancels while owning RDS_IN_XMIT rely on cp_wq
+ * being ordered: a teardown blocked on the bit occupies
+ * cp_wq's only execution slot, so cp_send_w and cp_recv_w are
+ * pending at most and the cancels never flush. Nothing here
+ * may flush or wait on cp_wq itself.
*/
- atomic_set(&cp->cp_state, RDS_CONN_RESETTING);
- wait_event(cp->cp_waitq, !test_bit(RDS_IN_XMIT, &cp->cp_flags));
- /* reset receive side state for rds_tcp_data_recv() for osock */
cancel_delayed_work_sync(&cp->cp_send_w);
cancel_delayed_work_sync(&cp->cp_recv_w);
lock_sock(osock->sk);
@@ -172,6 +216,9 @@ newsock:
lock_sock(sock->sk);
rds_tcp_set_callbacks(sock, cp);
release_sock(sock->sk);
+
+ clear_bit_unlock(RDS_IN_XMIT, &cp->cp_flags);
+ wake_up_all(&cp->cp_waitq);
}
/* Add tc to rds_tcp_tc_list and set tc->t_sock. See comments
diff --git a/net/rds/tcp_listen.c b/net/rds/tcp_listen.c
index a3db9b057084..13fa60c1985b 100644
--- a/net/rds/tcp_listen.c
+++ b/net/rds/tcp_listen.c
@@ -295,7 +295,11 @@ int rds_tcp_accept_one(struct rds_tcp_net *rtn)
if (rs_tcp->t_sock) {
/* Duelling SYN has been handled in rds_tcp_accept_one() */
rds_tcp_reset_callbacks(new_sock, cp);
- /* rds_connect_path_complete() marks RDS_CONN_UP */
+ /* rds_connect_path_complete() marks RDS_CONN_UP, or,
+ * if a concurrent shutdown won the duel, drops the
+ * path again and the pass that drop queues reaps the
+ * socket installed above.
+ */
rds_connect_path_complete(cp, RDS_CONN_RESETTING);
} else {
rds_tcp_set_callbacks(new_sock, cp);
diff --git a/net/rxrpc/key.c b/net/rxrpc/key.c
index a0aa78d89289..cbd26da44951 100644
--- a/net/rxrpc/key.c
+++ b/net/rxrpc/key.c
@@ -210,7 +210,7 @@ static int rxrpc_preparse_xdr_yfs_rxgk(struct key_preparsed_payload *prep,
if (!token)
goto nomem;
- token->rxgk = kzalloc(struct_size_t(struct rxgk_key, _key, raw_keylen), GFP_KERNEL);
+ token->rxgk = kzalloc_flex(struct rxgk_key, _key, raw_keylen);
if (!token->rxgk)
goto nomem_token;
diff --git a/net/sched/act_api.c b/net/sched/act_api.c
index b4415d358c91..37eced84dfa5 100644
--- a/net/sched/act_api.c
+++ b/net/sched/act_api.c
@@ -443,12 +443,21 @@ static size_t tcf_action_shared_attrs_size(const struct tc_action *act)
+ nla_total_size(IFNAMSIZ) /* TCA_ACT_KIND */
+ cookie_len /* TCA_ACT_COOKIE */
+ nla_total_size(sizeof(struct nla_bitfield32)) /* TCA_ACT_HW_STATS */
+ /* TCA_ACT_USED_HW_STATS */
+ + nla_total_size(sizeof(struct nla_bitfield32))
+ + nla_total_size(sizeof(u32)) /* TCA_ACT_IN_HW_COUNT */
+ nla_total_size(0) /* TCA_ACT_STATS nested */
+ nla_total_size(sizeof(struct nla_bitfield32)) /* TCA_ACT_FLAGS */
/* TCA_STATS_BASIC */
+ nla_total_size_64bit(sizeof(struct gnet_stats_basic))
- /* TCA_STATS_PKT64 */
- + nla_total_size_64bit(sizeof(u64))
+ /* TCA_STATS_BASIC_HW */
+ + nla_total_size_64bit(sizeof(struct gnet_stats_basic))
+ /* TCA_STATS_PKT64, emitted by both of the basic copies above */
+ + 2 * nla_total_size_64bit(sizeof(u64))
+ /* TCA_STATS_RATE_EST */
+ + nla_total_size_64bit(sizeof(struct gnet_stats_rate_est))
+ /* TCA_STATS_RATE_EST64 */
+ + nla_total_size_64bit(sizeof(struct gnet_stats_rate_est64))
/* TCA_STATS_QUEUE */
+ nla_total_size_64bit(sizeof(struct gnet_stats_queue))
+ nla_total_size(0) /* TCA_ACT_OPTIONS nested */
@@ -1688,12 +1697,12 @@ out_nlmsg_trim:
static int
tcf_get_notify(struct net *net, u32 portid, struct nlmsghdr *n,
- struct tc_action *actions[], int event,
+ struct tc_action *actions[], size_t attr_size, int event,
struct netlink_ext_ack *extack)
{
struct sk_buff *skb;
- skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
+ skb = alloc_skb(max(attr_size, NLMSG_GOODSIZE), GFP_KERNEL);
if (!skb)
return -ENOBUFS;
if (tca_get_fill(skb, actions, portid, n->nlmsg_seq, 0, event,
@@ -1858,11 +1867,13 @@ static int tcf_action_delete(struct net *net, struct tc_action *actions[])
static struct sk_buff *tcf_reoffload_del_notify_msg(struct net *net,
struct tc_action *action)
{
- size_t attr_size = tcf_action_fill_size(action);
struct tc_action *actions[TCA_ACT_MAX_PRIO] = {
[0] = action,
};
struct sk_buff *skb;
+ size_t attr_size;
+
+ attr_size = tcf_action_full_attrs_size(tcf_action_fill_size(action));
skb = alloc_skb(max(attr_size, NLMSG_GOODSIZE), GFP_KERNEL);
if (!skb)
@@ -1879,15 +1890,18 @@ static struct sk_buff *tcf_reoffload_del_notify_msg(struct net *net,
static int tcf_reoffload_del_notify(struct net *net, struct tc_action *action)
{
const struct tc_action_ops *ops = action->ops;
- struct sk_buff *skb;
+ struct sk_buff *skb = NULL;
int ret;
- if (!rtnl_notify_needed(net, 0, RTNLGRP_TC)) {
- skb = NULL;
- } else {
+ if (rtnl_notify_needed(net, 0, RTNLGRP_TC)) {
skb = tcf_reoffload_del_notify_msg(net, action);
+ /* The action has already lost its hardware instance and is
+ * skip_sw, so it must be released whether or not the
+ * notification can be built. Drop the notification rather
+ * than leave an action behind that processes no packets.
+ */
if (IS_ERR(skb))
- return PTR_ERR(skb);
+ skb = NULL;
}
ret = tcf_idr_release_unsafe(action);
@@ -2044,7 +2058,8 @@ tca_action_gd(struct net *net, struct nlattr *nla, struct nlmsghdr *n,
attr_size = tcf_action_full_attrs_size(attr_size);
if (event == RTM_GETACTION)
- ret = tcf_get_notify(net, portid, n, actions, event, extack);
+ ret = tcf_get_notify(net, portid, n, actions, attr_size, event,
+ extack);
else { /* delete */
ret = tcf_del_notify(net, n, actions, portid, attr_size, extack);
if (ret)
diff --git a/net/sched/act_gate.c b/net/sched/act_gate.c
index fdbfcaa3e2ab..5d228a402204 100644
--- a/net/sched/act_gate.c
+++ b/net/sched/act_gate.c
@@ -240,7 +240,7 @@ static int tcf_gate_copy_entries(struct tcf_gate_params *dst,
list_for_each_entry(entry, &src->entries, list) {
struct tcfg_gate_entry *new;
- new = kzalloc(sizeof(*new), GFP_ATOMIC);
+ new = kzalloc_obj(*new, GFP_ATOMIC);
if (!new) {
NL_SET_ERR_MSG(extack, "Not enough memory for entry");
return -ENOMEM;
@@ -415,7 +415,7 @@ static int tcf_gate_init(struct net *net, struct nlattr *nla,
if (err < 0)
goto release_idr;
- p = kzalloc(sizeof(*p), GFP_KERNEL);
+ p = kzalloc_obj(*p);
if (!p) {
err = -ENOMEM;
goto chain_put;
diff --git a/net/sched/act_tunnel_key.c b/net/sched/act_tunnel_key.c
index ff401ace4f3d..503834853306 100644
--- a/net/sched/act_tunnel_key.c
+++ b/net/sched/act_tunnel_key.c
@@ -526,7 +526,7 @@ static int tunnel_key_init(struct net *net, struct nlattr *nla,
}
t = to_tunnel_key(*a);
- params_new = kzalloc(sizeof(*params_new), GFP_KERNEL);
+ params_new = kzalloc_obj(*params_new);
if (unlikely(!params_new)) {
NL_SET_ERR_MSG(extack, "Cannot allocate tunnel key parameters");
ret = -ENOMEM;
diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c
index 0e275b58151c..1cefea571efd 100644
--- a/net/sched/cls_flower.c
+++ b/net/sched/cls_flower.c
@@ -1703,6 +1703,11 @@ static int fl_set_enc_opt(struct nlattr **tb, struct fl_flow_key *key,
return -EINVAL;
}
nla_opt_msk = nla_next(nla_opt_msk, &msk_depth);
+
+ if (msk_depth && !nla_ok(nla_opt_msk, msk_depth)) {
+ NL_SET_ERR_MSG(extack, "A mask attribute is invalid");
+ return -EINVAL;
+ }
}
return 0;
diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c
index ac6d0fa5a40e..a3e65c8cf29e 100644
--- a/net/sched/cls_u32.c
+++ b/net/sched/cls_u32.c
@@ -370,6 +370,10 @@ static int u32_init(struct tcf_proto *tp)
refcount_set(&root_ht->refcnt, 1);
root_ht->handle = tp_c ? gen_new_htid(tp_c, root_ht) : id2handle(0);
+ if (root_ht->handle == 0) {
+ kfree(root_ht);
+ return -ENOMEM;
+ }
root_ht->prio = tp->prio;
root_ht->is_root = true;
idr_init(&root_ht->handle_idr);
@@ -695,21 +699,33 @@ out:
return ret;
}
-static u32 gen_new_kid(struct tc_u_hnode *ht, u32 htid)
+static u32 gen_new_kid(struct tc_u_hnode *ht, u32 htid, int *err)
{
u32 index = htid | 0x800;
u32 max = htid | 0xFFF;
+ *err = 0;
+
if (idr_alloc_u32(&ht->handle_idr, NULL, &index, max, GFP_KERNEL)) {
index = htid + 1;
- if (idr_alloc_u32(&ht->handle_idr, NULL, &index, max,
- GFP_KERNEL))
- index = max;
+ *err = idr_alloc_u32(&ht->handle_idr, NULL, &index, max,
+ GFP_KERNEL);
+ if (*err)
+ return 0;
}
return index;
}
+static int u32_kid_extack(int err, struct netlink_ext_ack *extack)
+{
+ if (err == -ENOSPC)
+ NL_SET_ERR_MSG_MOD(extack, "Hash table node ID pool exhausted");
+ else
+ NL_SET_ERR_MSG_MOD(extack, "Failed to allocate node ID");
+ return err;
+}
+
static const struct nla_policy u32_policy[TCA_U32_MAX + 1] = {
[TCA_U32_CLASSID] = { .type = NLA_U32 },
[TCA_U32_HASH] = { .type = NLA_U32 },
@@ -1079,7 +1095,9 @@ static int u32_change(struct net *net, struct sk_buff *in_skb,
* handle which is used to uniquely identify the match entry.
*/
if (!TC_U32_NODE(handle)) {
- handle = gen_new_kid(ht, htid);
+ handle = gen_new_kid(ht, htid, &err);
+ if (err)
+ return u32_kid_extack(err, extack);
} else {
handle = htid | TC_U32_NODE(handle);
err = idr_alloc_u32(&ht->handle_idr, NULL, &handle,
@@ -1091,7 +1109,9 @@ static int u32_change(struct net *net, struct sk_buff *in_skb,
/* The user did not give us a handle; lets just generate one
* from the table's pool of nodeids.
*/
- handle = gen_new_kid(ht, htid);
+ handle = gen_new_kid(ht, htid, &err);
+ if (err)
+ return u32_kid_extack(err, extack);
}
if (tb[TCA_U32_SEL] == NULL) {
diff --git a/net/sctp/inqueue.c b/net/sctp/inqueue.c
index 5f988b3a8814..d666cec6b194 100644
--- a/net/sctp/inqueue.c
+++ b/net/sctp/inqueue.c
@@ -212,8 +212,10 @@ new_skb:
chunk->chunk_end = ((__u8 *)ch) + SCTP_PAD4(ntohs(ch->length));
skb_pull(chunk->skb, sizeof(*ch));
chunk->subh.v = NULL; /* Subheader is no longer valid. */
-
- if (chunk->chunk_end + sizeof(*ch) <= skb_tail_pointer(chunk->skb)) {
+ if (unlikely(ntohs(ch->length) < sizeof(*ch))) {
+ chunk->pdiscard = 1;
+ } else if (chunk->chunk_end + sizeof(*ch) <=
+ skb_tail_pointer(chunk->skb)) {
/* This is not a singleton */
chunk->singleton = 0;
} else if (chunk->chunk_end > skb_tail_pointer(chunk->skb)) {
diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c
index 236e25abc7a4..84a4c97d0f75 100644
--- a/net/sctp/sm_make_chunk.c
+++ b/net/sctp/sm_make_chunk.c
@@ -3215,6 +3215,9 @@ bool sctp_verify_asconf(const struct sctp_association *asoc,
*errp = param.p;
switch (param.p->type) {
case SCTP_PARAM_ERR_CAUSE:
+ if (length < sizeof(struct sctp_addip_param) +
+ sizeof(struct sctp_errhdr))
+ return false;
break;
case SCTP_PARAM_IPV4_ADDRESS:
if (length != sizeof(struct sctp_ipv4addr_param))
@@ -3448,20 +3451,15 @@ static __be16 sctp_get_asconf_response(struct sctp_chunk *asconf_ack,
case SCTP_PARAM_ERR_CAUSE:
length = sizeof(*asconf_ack_param);
err_param = (void *)asconf_ack_param + length;
- asconf_ack_len -= length;
- if (asconf_ack_len > 0)
- return err_param->cause;
- else
- return SCTP_ERROR_INV_PARAM;
- break;
+ return err_param->cause;
default:
return SCTP_ERROR_INV_PARAM;
}
}
length = ntohs(asconf_ack_param->param_hdr.length);
- asconf_ack_param = (void *)asconf_ack_param + length;
- asconf_ack_len -= length;
+ asconf_ack_param = (void *)asconf_ack_param + SCTP_PAD4(length);
+ asconf_ack_len -= SCTP_PAD4(length);
}
return err_code;
diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c
index 94716406d602..0d99b7e8c082 100644
--- a/net/sctp/sm_sideeffect.c
+++ b/net/sctp/sm_sideeffect.c
@@ -1545,17 +1545,8 @@ static int sctp_cmd_interpreter(enum sctp_event_type event_type,
timeout = asoc->timeouts[cmd->obj.to];
BUG_ON(!timeout);
- /*
- * SCTP has a hard time with timer starts. Because we process
- * timer starts as side effects, it can be hard to tell if we
- * have already started a timer or not, which leads to BUG
- * halts when we call add_timer. So here, instead of just starting
- * a timer, if the timer is already started, and just mod
- * the timer with the shorter of the two expiration times
- */
- if (!timer_pending(timer))
+ if (!timer_reduce(timer, jiffies + timeout))
sctp_association_hold(asoc);
- timer_reduce(timer, jiffies + timeout);
break;
case SCTP_CMD_TIMER_RESTART:
diff --git a/net/sunrpc/auth_gss/gss_krb5_crypto.c b/net/sunrpc/auth_gss/gss_krb5_crypto.c
index cf461ebcdde5..09a7c97e87da 100644
--- a/net/sunrpc/auth_gss/gss_krb5_crypto.c
+++ b/net/sunrpc/auth_gss/gss_krb5_crypto.c
@@ -282,9 +282,8 @@ int gss_krb5_mic_build_sg(const struct xdr_buf *body,
unsigned int overflow_nents =
nsg - XDR_BUF_TO_SG_NENTS + 1;
- *sg_overflow = kmalloc_array(overflow_nents,
- sizeof(**sg_overflow),
- GFP_NOFS);
+ *sg_overflow = kmalloc_objs(**sg_overflow, overflow_nents,
+ GFP_NOFS);
if (!*sg_overflow)
return -ENOMEM;
diff --git a/net/sunrpc/svcauth_unix.c b/net/sunrpc/svcauth_unix.c
index aebd97e7f66c..31a1bc60a5f6 100644
--- a/net/sunrpc/svcauth_unix.c
+++ b/net/sunrpc/svcauth_unix.c
@@ -625,7 +625,7 @@ int sunrpc_nl_unix_gid_get_reqs_dumpit(struct sk_buff *skb,
if (!cnt)
return 0;
- items = kcalloc(cnt, sizeof(*items), GFP_KERNEL);
+ items = kzalloc_objs(*items, cnt);
seqnos = kcalloc(cnt, sizeof(*seqnos), GFP_KERNEL);
if (!items || !seqnos) {
ret = -ENOMEM;
@@ -1326,7 +1326,7 @@ int sunrpc_nl_ip_map_get_reqs_dumpit(struct sk_buff *skb,
if (!cnt)
return 0;
- items = kcalloc(cnt, sizeof(*items), GFP_KERNEL);
+ items = kzalloc_objs(*items, cnt);
seqnos = kcalloc(cnt, sizeof(*seqnos), GFP_KERNEL);
if (!items || !seqnos) {
ret = -ENOMEM;
diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c
index cb2ef428651f..c42fd338c607 100644
--- a/net/sunrpc/xdr.c
+++ b/net/sunrpc/xdr.c
@@ -371,8 +371,7 @@ int xdr_buf_to_sg_alloc(const struct xdr_buf *buf, unsigned int offset,
unsigned int overflow_nents = nsg - sg_head_nents + 1;
struct scatterlist *overflow;
- overflow = kmalloc_array(overflow_nents, sizeof(*overflow),
- gfp);
+ overflow = kmalloc_objs(*overflow, overflow_nents, gfp);
if (!overflow)
return -ENOMEM;
diff --git a/net/tipc/link.c b/net/tipc/link.c
index 49dfc098d89b..6427c69f8929 100644
--- a/net/tipc/link.c
+++ b/net/tipc/link.c
@@ -504,7 +504,7 @@ bool tipc_link_create(struct net *net, char *if_name, int bearer_id,
snprintf(l->name, sizeof(l->name), "%s:%s-%s:unknown",
self_str, if_name, peer_str);
- strcpy(l->if_name, if_name);
+ strscpy(l->if_name, if_name);
l->addr = peer;
l->peer_caps = peer_caps;
l->net = net;
@@ -574,7 +574,7 @@ bool tipc_link_bc_create(struct net *net, u32 ownnode, u32 peer, u8 *peer_id,
snprintf(l->name, sizeof(l->name), "%s:%s", tipc_bclink_name,
peer_str);
} else {
- strcpy(l->name, tipc_bclink_name);
+ strscpy(l->name, tipc_bclink_name);
}
trace_tipc_link_reset(l, TIPC_DUMP_ALL, "bclink created!");
tipc_link_reset(l);
@@ -1898,7 +1898,7 @@ static void tipc_link_build_proto_msg(struct tipc_link *l, int mtyp, bool probe,
msg_set_dest_session(hdr, l->peer_session);
}
msg_set_max_pkt(hdr, l->advertised_mtu);
- strcpy(data, l->if_name);
+ memcpy(data, l->if_name, TIPC_MAX_IF_NAME);
msg_set_size(hdr, INT_H_SIZE + TIPC_MAX_IF_NAME);
skb_trim(skb, INT_H_SIZE + TIPC_MAX_IF_NAME);
}
diff --git a/net/tipc/name_table.c b/net/tipc/name_table.c
index 253c72d1366e..6fda36ab1766 100644
--- a/net/tipc/name_table.c
+++ b/net/tipc/name_table.c
@@ -763,21 +763,40 @@ struct publication *tipc_nametbl_publish(struct net *net, struct tipc_uaddr *ua,
struct tipc_socket_addr *sk, u32 key)
{
struct name_table *nt = tipc_name_table(net);
+ u32 max_user_pub = TIPC_MAX_PUBL - 1;
struct tipc_net *tn = tipc_net(net);
struct publication *p = NULL;
struct sk_buff *skb = NULL;
+ bool protocol_type = false;
u32 rc_dests;
+ if (ua->sr.type == TIPC_NODE_STATE || ua->sr.type == TIPC_LINK_STATE ||
+ ua->sr.type == TIPC_TOP_SRV)
+ protocol_type = true;
+
spin_lock_bh(&tn->nametbl_lock);
+ if (protocol_type)
+ goto insert;
- if (nt->local_publ_count >= TIPC_MAX_PUBL) {
- pr_warn("Bind failed, max limit %u reached\n", TIPC_MAX_PUBL);
+ /* Reserve one entry for node state service type because it has cluster
+ * scope and it is distributed in bulk. So, the maximum number of user's
+ * publications is (TIPC_MAX_PUBL - 1).
+ */
+ if (nt->local_publ_count >= max_user_pub) {
+ pr_warn("Bind failed, max limit %u reached\n", max_user_pub);
goto exit;
}
+insert:
p = tipc_nametbl_insert_publ(net, ua, sk, key);
if (p) {
- nt->local_publ_count++;
+ /* Not count node state, link state and topology server types
+ * so that maximum nt->local_publ_count does not prevent
+ * protocol service types from being inserted into the name
+ * table.
+ */
+ if (!protocol_type)
+ nt->local_publ_count++;
skb = tipc_named_publish(net, p);
}
rc_dests = nt->rc_dests;
@@ -810,7 +829,10 @@ void tipc_nametbl_withdraw(struct net *net, struct tipc_uaddr *ua,
p = tipc_nametbl_remove_publ(net, ua, sk, key);
if (p) {
- nt->local_publ_count--;
+ if (p->sr.type != TIPC_NODE_STATE &&
+ p->sr.type != TIPC_LINK_STATE &&
+ p->sr.type != TIPC_TOP_SRV)
+ nt->local_publ_count--;
skb = tipc_named_withdraw(net, p);
list_del_init(&p->binding_sock);
kfree_rcu(p, rcu);
diff --git a/net/tipc/node.c b/net/tipc/node.c
index 683a136e53ef..bd91378b7540 100644
--- a/net/tipc/node.c
+++ b/net/tipc/node.c
@@ -1333,7 +1333,9 @@ static void tipc_node_reset_links(struct tipc_node *n)
pr_warn("Resetting all links to %x\n", n->addr);
+ tipc_node_write_lock(n);
trace_tipc_node_reset_links(n, true, " ");
+ tipc_node_write_unlock_fast(n);
for (i = 0; i < MAX_BEARERS; i++) {
tipc_node_link_down(n, i, false);
}
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index 13f9926bf205..79c3921c583c 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -3667,8 +3667,8 @@ static int bpf_iter_unix_realloc_batch(struct bpf_unix_iter_state *iter,
{
struct sock **new_batch;
- new_batch = kvmalloc_array(new_batch_sz, sizeof(*new_batch),
- GFP_USER | __GFP_NOWARN);
+ new_batch = kvmalloc_objs(*new_batch, new_batch_sz,
+ GFP_USER | __GFP_NOWARN);
if (!new_batch)
return -ENOMEM;
diff --git a/net/vmw_vsock/af_vsock.c b/net/vmw_vsock/af_vsock.c
index a33b2a2d381d..f840498b58af 100644
--- a/net/vmw_vsock/af_vsock.c
+++ b/net/vmw_vsock/af_vsock.c
@@ -438,6 +438,38 @@ struct sock *vsock_find_connected_socket(struct sockaddr_vm *src,
}
EXPORT_SYMBOL_GPL(vsock_find_connected_socket);
+/**
+ * vsock_check_source - validate a packet source against a socket peer
+ * @vsk: socket receiving the packet
+ * @transport: transport receiving the packet
+ * @src: source address from the packet
+ *
+ * Return: true if the packet arrived on the socket's assigned transport and
+ * its source matches the stored peer. Loopback packets are generated
+ * internally and always use the local CID as their source, including
+ * connections using a valid CID alias.
+ *
+ * The caller must hold the socket lock and must not call this for listening
+ * sockets, which accept packets from any source and have no assigned
+ * transport.
+ */
+bool vsock_check_source(const struct vsock_sock *vsk,
+ const struct vsock_transport *transport,
+ const struct sockaddr_vm *src)
+{
+ if (vsk->transport != transport)
+ return false;
+
+ if (src->svm_port != vsk->remote_addr.svm_port)
+ return false;
+
+ if (src->svm_cid == vsk->remote_addr.svm_cid)
+ return true;
+
+ return transport->get_local_cid() == VMADDR_CID_LOCAL;
+}
+EXPORT_SYMBOL_GPL(vsock_check_source);
+
void vsock_remove_sock(struct vsock_sock *vsk)
{
/* Transport reassignment must not remove the binding. */
diff --git a/net/vmw_vsock/virtio_transport_common.c b/net/vmw_vsock/virtio_transport_common.c
index 88df82364f77..f225f53ed4ba 100644
--- a/net/vmw_vsock/virtio_transport_common.c
+++ b/net/vmw_vsock/virtio_transport_common.c
@@ -1836,7 +1836,8 @@ void virtio_transport_recv_pkt(struct virtio_transport *t,
* lock_sock (note: listener sockets are not assigned to any transport)
*/
if (sock_flag(sk, SOCK_DONE) ||
- (sk->sk_state != TCP_LISTEN && vsk->transport != &t->transport)) {
+ (sk->sk_state != TCP_LISTEN &&
+ !vsock_check_source(vsk, &t->transport, &src))) {
(void)virtio_transport_reset_no_sock(t, skb, net);
release_sock(sk);
sock_put(sk);
diff --git a/net/vmw_vsock/vmci_transport.c b/net/vmw_vsock/vmci_transport.c
index 1c4ee039c166..1f186e8f8364 100644
--- a/net/vmw_vsock/vmci_transport.c
+++ b/net/vmw_vsock/vmci_transport.c
@@ -680,11 +680,13 @@ static int vmci_transport_recv_stream_cb(void *data, struct vmci_datagram *dg)
struct vmci_transport_packet *pkt;
struct vsock_sock *vsk;
bool bh_process_pkt;
+ bool drop_pkt;
int err;
sk = NULL;
err = VMCI_SUCCESS;
bh_process_pkt = false;
+ drop_pkt = false;
/* Ignore incoming packets from resources that aren't vsock
* implementations.
@@ -765,17 +767,29 @@ static int vmci_transport_recv_stream_cb(void *data, struct vmci_datagram *dg)
bh_lock_sock(sk);
if (!sock_owned_by_user(sk)) {
- /* The local context ID may be out of date, update it. */
- vsk->local_addr.svm_cid = dst.svm_cid;
+ if (sk->sk_state != TCP_LISTEN &&
+ !vsock_check_source(vsk, &vmci_transport, &src)) {
+ drop_pkt = true;
+ err = VMCI_ERROR_NO_ACCESS;
+ } else {
+ /* The local context ID may be out of date, update it. */
+ vsk->local_addr.svm_cid = dst.svm_cid;
- if (sk->sk_state == TCP_ESTABLISHED)
- vmci_trans(vsk)->notify_ops->handle_notify_pkt(
- sk, pkt, true, &dst, &src,
- &bh_process_pkt);
+ if (sk->sk_state == TCP_ESTABLISHED)
+ vmci_trans(vsk)->notify_ops->handle_notify_pkt(sk, pkt, true,
+ &dst, &src,
+ &bh_process_pkt);
+ }
}
bh_unlock_sock(sk);
+ if (drop_pkt) {
+ if (vmci_transport_send_reset_bh(&dst, &src, pkt) < 0)
+ pr_err("unable to send reset\n");
+ goto out;
+ }
+
if (!bh_process_pkt) {
struct vmci_transport_recv_pkt_info *recv_pkt_info;
@@ -900,6 +914,7 @@ static void vmci_transport_recv_pkt_work(struct work_struct *work)
{
struct vmci_transport_recv_pkt_info *recv_pkt_info;
struct vmci_transport_packet *pkt;
+ struct sockaddr_vm src;
struct sock *sk;
recv_pkt_info =
@@ -908,6 +923,12 @@ static void vmci_transport_recv_pkt_work(struct work_struct *work)
pkt = &recv_pkt_info->pkt;
lock_sock(sk);
+ vsock_addr_init(&src, pkt->dg.src.context, pkt->src_port);
+ if (sk->sk_state != TCP_LISTEN &&
+ !vsock_check_source(vsock_sk(sk), &vmci_transport, &src)) {
+ vmci_transport_reply_reset(pkt);
+ goto out;
+ }
/* The local context ID may be out of date. */
vsock_sk(sk)->local_addr.svm_cid = pkt->dg.dst.context;
@@ -937,6 +958,7 @@ static void vmci_transport_recv_pkt_work(struct work_struct *work)
break;
}
+out:
release_sock(sk);
kfree(recv_pkt_info);
/* Release reference obtained in the stream callback when we fetched
diff --git a/net/wireless/core.c b/net/wireless/core.c
index d13310fef691..3032993ba5dc 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -311,9 +311,8 @@ int cfg80211_nan_set_local_schedule(struct cfg80211_registered_device *rdev,
if (!sched->n_channels)
return 0;
- wdev->u.nan.chandefs = kcalloc(sched->n_channels,
- sizeof(*wdev->u.nan.chandefs),
- GFP_KERNEL);
+ wdev->u.nan.chandefs = kzalloc_objs(*wdev->u.nan.chandefs,
+ sched->n_channels);
if (!wdev->u.nan.chandefs)
return -ENOMEM;
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 44f2bad08670..899b6374c550 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -17330,8 +17330,7 @@ static int nl80211_parse_nan_channel(struct cfg80211_registered_device *rdev,
u8 n_rx_nss;
int ret;
- channel_parsed = kcalloc(NL80211_ATTR_MAX + 1, sizeof(*channel_parsed),
- GFP_KERNEL);
+ channel_parsed = kzalloc_objs(*channel_parsed, NL80211_ATTR_MAX + 1);
if (!channel_parsed)
return -ENOMEM;
@@ -17554,8 +17553,7 @@ static int nl80211_nan_set_peer_sched(struct sk_buff *skb,
}
if (n_channels) {
- nan_channels = kcalloc(n_channels, sizeof(*nan_channels),
- GFP_KERNEL);
+ nan_channels = kzalloc_objs(*nan_channels, n_channels);
if (!nan_channels)
return -ENOMEM;
}
@@ -17693,8 +17691,7 @@ static int nl80211_nan_set_local_sched(struct sk_buff *skb,
info->nlhdr, GENL_HDRLEN, rem)
n_channels++;
- sched = kzalloc(struct_size(sched, nan_channels, n_channels),
- GFP_KERNEL);
+ sched = kzalloc_flex(*sched, nan_channels, n_channels);
if (!sched)
return -ENOMEM;