summaryrefslogtreecommitdiff
path: root/net
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-07-23 12:58:08 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2026-07-23 12:58:08 -0700
commitd326f83e819c53aa05c40d64f5805d6237b6aa1b (patch)
treef3505e4347ebd0244f580e11093033ad7b885f36 /net
parent4539944e515183668109bdf4d0c3d7d228383d88 (diff)
parent6a8da869fa338e008533dd6385a0eb35dee6acf4 (diff)
downloadlinux-next-d326f83e819c53aa05c40d64f5805d6237b6aa1b.tar.gz
linux-next-d326f83e819c53aa05c40d64f5805d6237b6aa1b.zip
Merge tag 'net-7.2-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net
Pull networking fixes from Jakub Kicinski: "Lots of fixes, double the count even for the 'new normal'. Largely due to my time off followed by a networking conference which distracted most maintainers (less so the AI generators). Including fixes from Bluetooth and WiFi. Current release - regressions: - wifi: mt76: fix MAC address for non OF pcie cards Current release - new code bugs: - mptcp: fix BUILD_BUG_ON on legacy ARM config - wifi: cfg80211: guard optional PMSR nominal time Previous releases - regressions: - qrtr: ns: raise node count limit to 512, we arbitrarily picked 256 as a limit, turns out it was too low for real world deployments - vhost-net: fix TX stall when vhost owns virtio-net header - eth: amd-xgbe: fix MAC_AUTO_SW handling in CL37 AN - wifi: ath12k: fix low MLO RX throughput on WCN7850 Previous releases - always broken: - number of random AI fixes for SCTP, RDS and TIPC protocols - more AI-looking fixes for WiFi drivers - number of fixes for missing pointer reloading after skb pull - reject BPF redirect use from qdisc qevent block - tcp: initialize standalone TCP-AO response padding - vsock/virtio: collapse receive queue under memory pressure to avoid client OOMing the host with tiny messages - ipv4: icmp: fill flow parameters in icmp_route_lookup decoy lookup, make sure the ICMP response routing follows the routing policy - gro: fix double aggregation of flush-marked skbs - ovpn: fix various refcount bugs - tls: device: push pending open record on splice EOF - eth: mlx5: - use sender devcom for MPV master-up - fix MCIA register buffer overflow on 32 dword reads" * tag 'net-7.2-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net: (234 commits) drop_monitor: perform u64_stats updates under IRQ-disabled section drop_monitor: fix size calculations for 64-bit attributes net: drop_monitor: fix info leak in NET_DM_ATTR_PAYLOAD mptcp: fix BUILD_BUG_ON on legacy ARM config selftests: mptcp: userspace_pm: fix undefined variable port mptcp: fix stale skb->sk reference on subflow close mptcp: pm: userspace: fix use-after-free in get_local_id mptcp: decrement subflows counter on failed passive join mac802154: hold an interface reference across the scan worker sctp: don't free the ASCONF's own transport in DEL-IP processing phonet: check register_netdevice_notifier() error in phonet_device_init() phonet: pep: fix use-after-free in pep_get_sb() bnge/bng_re: fix ring ID widths tipc: fix integer overflow in tipc_recvmsg() and tipc_recvstream() net: airoha: fix ETS channel derivation in airoha_tc_setup_qdisc_ets() mctp: check register_netdevice_notifier() error in mctp_device_init() ptp: netc: explicitly clear TMR_OFF during initialization rds: tcp: unregister sysctl before tearing down listen socket ipv6: Change allocation flags to match rcu_read_lock section requirements net: slip: serialize receive against buffer reallocation ...
Diffstat (limited to 'net')
-rw-r--r--net/bluetooth/hci_sync.c13
-rw-r--r--net/bluetooth/rfcomm/core.c17
-rw-r--r--net/bluetooth/rfcomm/tty.c7
-rw-r--r--net/bridge/br_netlink_tunnel.c3
-rw-r--r--net/bridge/br_private.h6
-rw-r--r--net/bridge/br_vlan.c10
-rw-r--r--net/bridge/br_vlan_options.c3
-rw-r--r--net/core/drop_monitor.c18
-rw-r--r--net/core/filter.c17
-rw-r--r--net/core/gro.c4
-rw-r--r--net/core/sock.c10
-rw-r--r--net/hsr/hsr_slave.c2
-rw-r--r--net/ipv4/datagram.c4
-rw-r--r--net/ipv4/icmp.c18
-rw-r--r--net/ipv4/ip_gre.c4
-rw-r--r--net/ipv4/nexthop.c2
-rw-r--r--net/ipv4/raw.c23
-rw-r--r--net/ipv4/tcp_input.c56
-rw-r--r--net/ipv4/tcp_ipv4.c8
-rw-r--r--net/ipv4/tcp_minisocks.c12
-rw-r--r--net/ipv6/ila/ila_common.c12
-rw-r--r--net/ipv6/ip6_gre.c4
-rw-r--r--net/ipv6/raw.c2
-rw-r--r--net/ipv6/route.c4
-rw-r--r--net/ipv6/tcp_ipv6.c2
-rw-r--r--net/iucv/af_iucv.c17
-rw-r--r--net/mac80211/cfg.c3
-rw-r--r--net/mac80211/link.c4
-rw-r--r--net/mac80211/rx.c2
-rw-r--r--net/mac802154/llsec.c5
-rw-r--r--net/mac802154/scan.c11
-rw-r--r--net/mctp/device.c4
-rw-r--r--net/mpls/af_mpls.c8
-rw-r--r--net/mptcp/options.c17
-rw-r--r--net/mptcp/pm_userspace.c7
-rw-r--r--net/mptcp/protocol.c34
-rw-r--r--net/openvswitch/actions.c19
-rw-r--r--net/openvswitch/datapath.c25
-rw-r--r--net/openvswitch/datapath.h2
-rw-r--r--net/openvswitch/vport.c2
-rw-r--r--net/packet/af_packet.c6
-rw-r--r--net/phonet/pep.c2
-rw-r--r--net/phonet/pn_dev.c30
-rw-r--r--net/qrtr/af_qrtr.c8
-rw-r--r--net/qrtr/ns.c4
-rw-r--r--net/rds/ib.c4
-rw-r--r--net/rds/ib_cm.c4
-rw-r--r--net/rds/recv.c15
-rw-r--r--net/rds/tcp.c12
-rw-r--r--net/rxrpc/ar-internal.h6
-rw-r--r--net/sched/cls_api.c6
-rw-r--r--net/sched/sch_api.c24
-rw-r--r--net/sched/sch_cake.c2
-rw-r--r--net/sched/sch_drr.c2
-rw-r--r--net/sched/sch_dualpi2.c2
-rw-r--r--net/sched/sch_ets.c2
-rw-r--r--net/sched/sch_fq_codel.c2
-rw-r--r--net/sched/sch_fq_pie.c2
-rw-r--r--net/sched/sch_hfsc.c2
-rw-r--r--net/sched/sch_htb.c2
-rw-r--r--net/sched/sch_multiq.c2
-rw-r--r--net/sched/sch_prio.c2
-rw-r--r--net/sched/sch_qfq.c2
-rw-r--r--net/sched/sch_sfb.c2
-rw-r--r--net/sched/sch_sfq.c2
-rw-r--r--net/sctp/auth.c2
-rw-r--r--net/sctp/protocol.c21
-rw-r--r--net/sctp/sm_make_chunk.c6
-rw-r--r--net/sctp/sm_statefuns.c2
-rw-r--r--net/sctp/stream.c6
-rw-r--r--net/sctp/sysctl.c9
-rw-r--r--net/tipc/netlink.c6
-rw-r--r--net/tipc/netlink_compat.c4
-rw-r--r--net/tipc/socket.c5
-rw-r--r--net/tipc/udp_media.c56
-rw-r--r--net/tls/tls_device.c6
-rw-r--r--net/vmw_vsock/virtio_transport_common.c165
-rw-r--r--net/wireless/pmsr.c5
-rw-r--r--net/x25/af_x25.c8
79 files changed, 649 insertions, 220 deletions
diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
index 532534bc601c..c0b1fc293b49 100644
--- a/net/bluetooth/hci_sync.c
+++ b/net/bluetooth/hci_sync.c
@@ -929,12 +929,16 @@ int hci_update_eir_sync(struct hci_dev *hdev)
memset(&cp, 0, sizeof(cp));
+ hci_dev_lock(hdev);
eir_create(hdev, cp.data);
- if (memcmp(cp.data, hdev->eir, sizeof(cp.data)) == 0)
+ if (memcmp(cp.data, hdev->eir, sizeof(cp.data)) == 0) {
+ hci_dev_unlock(hdev);
return 0;
+ }
memcpy(hdev->eir, cp.data, sizeof(cp.data));
+ hci_dev_unlock(hdev);
return __hci_cmd_sync_status(hdev, HCI_OP_WRITE_EIR, sizeof(cp), &cp,
HCI_CMD_TIMEOUT);
@@ -966,6 +970,7 @@ int hci_update_class_sync(struct hci_dev *hdev)
if (hci_dev_test_flag(hdev, HCI_SERVICE_CACHE))
return 0;
+ hci_dev_lock(hdev);
cod[0] = hdev->minor_class;
cod[1] = hdev->major_class;
cod[2] = get_service_classes(hdev);
@@ -973,8 +978,12 @@ int hci_update_class_sync(struct hci_dev *hdev)
if (hci_dev_test_flag(hdev, HCI_LIMITED_DISCOVERABLE))
cod[1] |= 0x20;
- if (memcmp(cod, hdev->dev_class, 3) == 0)
+ if (memcmp(cod, hdev->dev_class, 3) == 0) {
+ hci_dev_unlock(hdev);
return 0;
+ }
+
+ hci_dev_unlock(hdev);
return __hci_cmd_sync_status(hdev, HCI_OP_WRITE_CLASS_OF_DEV,
sizeof(cod), cod, HCI_CMD_TIMEOUT);
diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c
index ebeae17b71d1..75f7512dec54 100644
--- a/net/bluetooth/rfcomm/core.c
+++ b/net/bluetooth/rfcomm/core.c
@@ -1028,6 +1028,23 @@ int rfcomm_send_rpn(struct rfcomm_session *s, int cr, u8 dlci,
return rfcomm_send_frame(s, buf, ptr - buf);
}
+int rfcomm_dlc_send_rpn(struct rfcomm_dlc *d, u8 bit_rate, u8 data_bits,
+ u8 stop_bits, u8 parity, u8 flow_ctrl_settings,
+ u8 xon_char, u8 xoff_char, u16 param_mask)
+{
+ int err = -ENOTCONN;
+
+ rfcomm_lock();
+ if (d->session)
+ err = rfcomm_send_rpn(d->session, 1, d->dlci, bit_rate,
+ data_bits, stop_bits, parity,
+ flow_ctrl_settings, xon_char, xoff_char,
+ param_mask);
+ rfcomm_unlock();
+
+ return err;
+}
+
static int rfcomm_send_rls(struct rfcomm_session *s, int cr, u8 dlci, u8 status)
{
struct rfcomm_hdr *hdr;
diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c
index 4b9a699ec59b..b2c1060394e6 100644
--- a/net/bluetooth/rfcomm/tty.c
+++ b/net/bluetooth/rfcomm/tty.c
@@ -858,7 +858,7 @@ static void rfcomm_tty_set_termios(struct tty_struct *tty,
BT_DBG("tty %p termios %p", tty, old);
- if (!dev || !dev->dlc || !dev->dlc->session)
+ if (!dev || !dev->dlc)
return;
/* Handle turning off CRTSCTS */
@@ -979,9 +979,8 @@ static void rfcomm_tty_set_termios(struct tty_struct *tty,
}
if (changes)
- rfcomm_send_rpn(dev->dlc->session, 1, dev->dlc->dlci, baud,
- data_bits, stop_bits, parity,
- RFCOMM_RPN_FLOW_NONE, x_on, x_off, changes);
+ rfcomm_dlc_send_rpn(dev->dlc, baud, data_bits, stop_bits, parity,
+ RFCOMM_RPN_FLOW_NONE, x_on, x_off, changes);
}
static void rfcomm_tty_throttle(struct tty_struct *tty)
diff --git a/net/bridge/br_netlink_tunnel.c b/net/bridge/br_netlink_tunnel.c
index 71a12da30004..a713668ea34f 100644
--- a/net/bridge/br_netlink_tunnel.c
+++ b/net/bridge/br_netlink_tunnel.c
@@ -271,7 +271,8 @@ static void __vlan_tunnel_handle_range(const struct net_bridge_port *p,
if (!*v_start)
goto out_init;
- if (v && curr_change && br_vlan_can_enter_range(v, *v_end)) {
+ if (v && curr_change &&
+ br_vlan_can_enter_range(v, *v_end, br_get_pvid(vg))) {
*v_end = v;
return;
}
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index d55ea9516e3e..d3880f31edc4 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -1627,7 +1627,8 @@ void br_vlan_notify(const struct net_bridge *br,
u16 vid, u16 vid_range,
int cmd);
bool br_vlan_can_enter_range(const struct net_bridge_vlan *v_curr,
- const struct net_bridge_vlan *range_end);
+ const struct net_bridge_vlan *range_end,
+ u16 pvid);
void br_vlan_fill_forward_path_pvid(struct net_bridge *br,
struct net_device_path_ctx *ctx,
@@ -1874,7 +1875,8 @@ static inline void br_vlan_notify(const struct net_bridge *br,
}
static inline bool br_vlan_can_enter_range(const struct net_bridge_vlan *v_curr,
- const struct net_bridge_vlan *range_end)
+ const struct net_bridge_vlan *range_end,
+ u16 pvid)
{
return true;
}
diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c
index 5560afcaaca3..31c1b2cf75d9 100644
--- a/net/bridge/br_vlan.c
+++ b/net/bridge/br_vlan.c
@@ -1982,9 +1982,11 @@ out_kfree:
/* check if v_curr can enter a range ending in range_end */
bool br_vlan_can_enter_range(const struct net_bridge_vlan *v_curr,
- const struct net_bridge_vlan *range_end)
+ const struct net_bridge_vlan *range_end,
+ u16 pvid)
{
- return v_curr->vid - range_end->vid == 1 &&
+ return v_curr->vid != pvid && range_end->vid != pvid &&
+ v_curr->vid - range_end->vid == 1 &&
range_end->flags == v_curr->flags &&
br_vlan_opts_eq_range(v_curr, range_end);
}
@@ -2066,8 +2068,8 @@ static int br_vlan_dump_dev(const struct net_device *dev,
idx += range_end->vid - range_start->vid + 1;
range_start = v;
- } else if (dump_stats || v->vid == pvid ||
- !br_vlan_can_enter_range(v, range_end)) {
+ } else if (dump_stats ||
+ !br_vlan_can_enter_range(v, range_end, pvid)) {
u16 vlan_flags = br_vlan_flags(range_start, pvid);
if (!br_vlan_fill_vids(skb, range_start->vid,
diff --git a/net/bridge/br_vlan_options.c b/net/bridge/br_vlan_options.c
index fcc200c3e3da..cb0f556ff40d 100644
--- a/net/bridge/br_vlan_options.c
+++ b/net/bridge/br_vlan_options.c
@@ -350,8 +350,7 @@ int br_vlan_process_options(const struct net_bridge *br,
continue;
}
- if (v->vid == pvid ||
- !br_vlan_can_enter_range(v, curr_end)) {
+ if (!br_vlan_can_enter_range(v, curr_end, pvid)) {
br_vlan_notify(br, p, curr_start->vid,
curr_end->vid, RTM_NEWVLAN);
curr_start = v;
diff --git a/net/core/drop_monitor.c b/net/core/drop_monitor.c
index 2bf3cab5e557..abaf108ac4db 100644
--- a/net/core/drop_monitor.c
+++ b/net/core/drop_monitor.c
@@ -530,10 +530,10 @@ static void net_dm_packet_trace_kfree_skb_hit(void *ignore,
return;
unlock_free:
- spin_unlock_irqrestore(&data->drop_queue.lock, flags);
u64_stats_update_begin(&data->stats.syncp);
u64_stats_inc(&data->stats.dropped);
u64_stats_update_end(&data->stats.syncp);
+ spin_unlock_irqrestore(&data->drop_queue.lock, flags);
consume_skb(nskb);
}
@@ -566,13 +566,13 @@ static size_t net_dm_packet_report_size(size_t payload_len)
/* NET_DM_ATTR_ORIGIN */
nla_total_size(sizeof(u16)) +
/* NET_DM_ATTR_PC */
- nla_total_size(sizeof(u64)) +
+ nla_total_size_64bit(sizeof(u64)) +
/* NET_DM_ATTR_SYMBOL */
nla_total_size(NET_DM_MAX_SYMBOL_LEN + 1) +
/* NET_DM_ATTR_IN_PORT */
net_dm_in_port_size() +
/* NET_DM_ATTR_TIMESTAMP */
- nla_total_size(sizeof(u64)) +
+ nla_total_size_64bit(sizeof(u64)) +
/* NET_DM_ATTR_ORIG_LEN */
nla_total_size(sizeof(u32)) +
/* NET_DM_ATTR_PROTO */
@@ -671,9 +671,7 @@ static int net_dm_packet_report_fill(struct sk_buff *msg, struct sk_buff *skb,
if (nla_put_u16(msg, NET_DM_ATTR_PROTO, be16_to_cpu(skb->protocol)))
goto nla_put_failure;
- attr = skb_put(msg, nla_total_size(payload_len));
- attr->nla_type = NET_DM_ATTR_PAYLOAD;
- attr->nla_len = nla_attr_size(payload_len);
+ attr = __nla_reserve(msg, NET_DM_ATTR_PAYLOAD, payload_len);
if (skb_copy_bits(skb, 0, nla_data(attr), payload_len))
goto nla_put_failure;
@@ -768,7 +766,7 @@ net_dm_hw_packet_report_size(size_t payload_len,
/* NET_DM_ATTR_FLOW_ACTION_COOKIE */
net_dm_flow_action_cookie_size(hw_metadata) +
/* NET_DM_ATTR_TIMESTAMP */
- nla_total_size(sizeof(u64)) +
+ nla_total_size_64bit(sizeof(u64)) +
/* NET_DM_ATTR_ORIG_LEN */
nla_total_size(sizeof(u32)) +
/* NET_DM_ATTR_PROTO */
@@ -831,9 +829,7 @@ static int net_dm_hw_packet_report_fill(struct sk_buff *msg,
if (nla_put_u16(msg, NET_DM_ATTR_PROTO, be16_to_cpu(skb->protocol)))
goto nla_put_failure;
- attr = skb_put(msg, nla_total_size(payload_len));
- attr->nla_type = NET_DM_ATTR_PAYLOAD;
- attr->nla_len = nla_attr_size(payload_len);
+ attr = __nla_reserve(msg, NET_DM_ATTR_PAYLOAD, payload_len);
if (skb_copy_bits(skb, 0, nla_data(attr), payload_len))
goto nla_put_failure;
@@ -1001,10 +997,10 @@ net_dm_hw_trap_packet_probe(void *ignore, const struct devlink *devlink,
return;
unlock_free:
- spin_unlock_irqrestore(&hw_data->drop_queue.lock, flags);
u64_stats_update_begin(&hw_data->stats.syncp);
u64_stats_inc(&hw_data->stats.dropped);
u64_stats_update_end(&hw_data->stats.syncp);
+ spin_unlock_irqrestore(&hw_data->drop_queue.lock, flags);
net_dm_hw_metadata_free(n_hw_metadata);
free:
consume_skb(nskb);
diff --git a/net/core/filter.c b/net/core/filter.c
index b446aa8be5c3..11bb0d236822 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -2552,11 +2552,13 @@ out_drop:
BPF_CALL_2(bpf_redirect, u32, ifindex, u64, flags)
{
- struct bpf_redirect_info *ri = bpf_net_ctx_get_ri();
+ struct bpf_redirect_info *ri;
- if (unlikely(flags & (~(BPF_F_INGRESS) | BPF_F_REDIRECT_INTERNAL)))
+ if (unlikely(!bpf_net_ctx_get() ||
+ (flags & (~(BPF_F_INGRESS) | BPF_F_REDIRECT_INTERNAL))))
return TC_ACT_SHOT;
+ ri = bpf_net_ctx_get_ri();
ri->flags = flags;
ri->tgt_index = ifindex;
@@ -2573,11 +2575,12 @@ static const struct bpf_func_proto bpf_redirect_proto = {
BPF_CALL_2(bpf_redirect_peer, u32, ifindex, u64, flags)
{
- struct bpf_redirect_info *ri = bpf_net_ctx_get_ri();
+ struct bpf_redirect_info *ri;
- if (unlikely(flags))
+ if (unlikely(!bpf_net_ctx_get() || flags))
return TC_ACT_SHOT;
+ ri = bpf_net_ctx_get_ri();
ri->flags = BPF_F_PEER;
ri->tgt_index = ifindex;
@@ -2595,11 +2598,13 @@ static const struct bpf_func_proto bpf_redirect_peer_proto = {
BPF_CALL_4(bpf_redirect_neigh, u32, ifindex, struct bpf_redir_neigh *, params,
int, plen, u64, flags)
{
- struct bpf_redirect_info *ri = bpf_net_ctx_get_ri();
+ struct bpf_redirect_info *ri;
- if (unlikely((plen && plen < sizeof(*params)) || flags))
+ if (unlikely((plen && plen < sizeof(*params)) ||
+ !bpf_net_ctx_get() || flags))
return TC_ACT_SHOT;
+ ri = bpf_net_ctx_get_ri();
ri->flags = BPF_F_NEIGH | (plen ? BPF_F_NEXTHOP : 0);
ri->tgt_index = ifindex;
diff --git a/net/core/gro.c b/net/core/gro.c
index 35f2f708f010..29b4d02bf519 100644
--- a/net/core/gro.c
+++ b/net/core/gro.c
@@ -229,7 +229,9 @@ done:
int skb_gro_receive_list(struct sk_buff *p, struct sk_buff *skb)
{
- if (unlikely(p->len + skb->len >= 65536))
+ /* make sure to check flush flag and to not merge */
+ if (unlikely(p->len + skb->len >= 65536 ||
+ NAPI_GRO_CB(skb)->flush))
return -E2BIG;
if (!pskb_may_pull(skb, skb_gro_offset(skb))) {
diff --git a/net/core/sock.c b/net/core/sock.c
index 498a57f34f5b..ffa73594c13c 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -2547,6 +2547,11 @@ struct sock *sk_clone(const struct sock *sk, const gfp_t priority,
cgroup_sk_clone(&newsk->sk_cgrp_data);
+ RCU_INIT_POINTER(newsk->sk_reuseport_cb, NULL);
+
+ if (sock_needs_netstamp(sk) && newsk->sk_flags & SK_FLAGS_TIMESTAMP)
+ net_enable_timestamp();
+
rcu_read_lock();
filter = rcu_dereference(sk->sk_filter);
if (filter != NULL)
@@ -2569,8 +2574,6 @@ struct sock *sk_clone(const struct sock *sk, const gfp_t priority,
goto free;
}
- RCU_INIT_POINTER(newsk->sk_reuseport_cb, NULL);
-
if (bpf_sk_storage_clone(sk, newsk))
goto free;
@@ -2598,9 +2601,6 @@ struct sock *sk_clone(const struct sock *sk, const gfp_t priority,
if (newsk->sk_prot->sockets_allocated)
sk_sockets_allocated_inc(newsk);
-
- if (sock_needs_netstamp(sk) && newsk->sk_flags & SK_FLAGS_TIMESTAMP)
- net_enable_timestamp();
out:
return newsk;
free:
diff --git a/net/hsr/hsr_slave.c b/net/hsr/hsr_slave.c
index d9af9e65f72f..01c73b4b50dd 100644
--- a/net/hsr/hsr_slave.c
+++ b/net/hsr/hsr_slave.c
@@ -242,6 +242,8 @@ void hsr_del_port(struct hsr_port *port)
netdev_rx_handler_unregister(port->dev);
if (!port->hsr->fwd_offloaded)
dev_set_promiscuity(port->dev, -1);
+ if (port->type == HSR_PT_SLAVE_A || port->type == HSR_PT_SLAVE_B)
+ vlan_vids_del_by_dev(port->dev, master->dev);
netdev_upper_dev_unlink(port->dev, master->dev);
if (hsr->prot_version == PRP_V1 &&
port->type == HSR_PT_SLAVE_B) {
diff --git a/net/ipv4/datagram.c b/net/ipv4/datagram.c
index 1614593b6d72..7d25519a6cdd 100644
--- a/net/ipv4/datagram.c
+++ b/net/ipv4/datagram.c
@@ -63,12 +63,12 @@ int __ip4_datagram_connect(struct sock *sk, struct sockaddr_unsized *uaddr, int
}
/* Update addresses before rehashing */
- inet->inet_daddr = fl4->daddr;
+ WRITE_ONCE(inet->inet_daddr, fl4->daddr);
inet->inet_dport = usin->sin_port;
if (!inet->inet_saddr)
inet->inet_saddr = fl4->saddr;
if (!inet->inet_rcv_saddr) {
- inet->inet_rcv_saddr = fl4->saddr;
+ WRITE_ONCE(inet->inet_rcv_saddr, fl4->saddr);
if (sk->sk_prot->rehash)
sk->sk_prot->rehash(sk);
}
diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c
index 23e921d313b3..0caedfc7ca92 100644
--- a/net/ipv4/icmp.c
+++ b/net/ipv4/icmp.c
@@ -548,11 +548,23 @@ static struct rtable *icmp_route_lookup(struct net *net, struct flowi4 *fl4,
if (IS_ERR(rt2))
err = PTR_ERR(rt2);
} else {
- struct flowi4 fl4_2 = {};
+ struct flowi4 fl4_2 = fl4_dec;
unsigned long orefdst;
- fl4_2.daddr = fl4_dec.saddr;
- rt2 = ip_route_output_key(net, &fl4_2);
+ swap(fl4_2.daddr, fl4_2.saddr);
+ switch (fl4_2.flowi4_proto) {
+ case IPPROTO_TCP:
+ case IPPROTO_UDP:
+ case IPPROTO_SCTP:
+ case IPPROTO_DCCP:
+ swap(fl4_2.fl4_sport, fl4_2.fl4_dport);
+ break;
+ }
+
+ fl4_2.flowi4_oif = l3mdev_master_ifindex(route_lookup_dev);
+ fl4_2.flowi4_flags |= FLOWI_FLAG_ANYSRC;
+
+ rt2 = __ip_route_output_key(net, &fl4_2);
if (IS_ERR(rt2)) {
err = PTR_ERR(rt2);
goto relookup_failed;
diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c
index 3efdfb4ffa21..0ba1e94e9012 100644
--- a/net/ipv4/ip_gre.c
+++ b/net/ipv4/ip_gre.c
@@ -1018,6 +1018,8 @@ static void __gre_tunnel_init(struct net_device *dev)
dev->features |= GRE_FEATURES;
dev->hw_features |= GRE_FEATURES;
+ dev->lltx = true;
+
/* TCP offload with GRE SEQ is not supported, nor can we support 2
* levels of outer headers requiring an update.
*/
@@ -1029,8 +1031,6 @@ static void __gre_tunnel_init(struct net_device *dev)
dev->features |= NETIF_F_GSO_SOFTWARE;
dev->hw_features |= NETIF_F_GSO_SOFTWARE;
-
- dev->lltx = true;
}
static int ipgre_tunnel_init(struct net_device *dev)
diff --git a/net/ipv4/nexthop.c b/net/ipv4/nexthop.c
index 6205bd57aa85..44fe75004cac 100644
--- a/net/ipv4/nexthop.c
+++ b/net/ipv4/nexthop.c
@@ -1788,8 +1788,8 @@ static bool nh_res_bucket_migrate(struct nh_res_table *res_table,
bool notify_nl, bool force)
{
struct nh_res_bucket *bucket = &res_table->nh_buckets[bucket_index];
+ struct netlink_ext_ack extack = {};
struct nh_grp_entry *new_nhge;
- struct netlink_ext_ack extack;
int err;
new_nhge = list_first_entry_or_null(&res_table->uw_nh_entries,
diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c
index e9fbab6ad914..7f74d8b95a37 100644
--- a/net/ipv4/raw.c
+++ b/net/ipv4/raw.c
@@ -118,13 +118,21 @@ bool raw_v4_match(struct net *net, const struct sock *sk, unsigned short num,
__be32 raddr, __be32 laddr, int dif, int sdif)
{
const struct inet_sock *inet = inet_sk(sk);
+ __be32 daddr, rcv_saddr;
- if (net_eq(sock_net(sk), net) && inet->inet_num == num &&
- !(inet->inet_daddr && inet->inet_daddr != raddr) &&
- !(inet->inet_rcv_saddr && inet->inet_rcv_saddr != laddr) &&
- raw_sk_bound_dev_eq(net, sk->sk_bound_dev_if, dif, sdif))
- return true;
- return false;
+ if (!net_eq(sock_net(sk), net) || inet->inet_num != num)
+ return false;
+
+ daddr = READ_ONCE(inet->inet_daddr);
+ if (daddr && daddr != raddr)
+ return false;
+
+ rcv_saddr = READ_ONCE(inet->inet_rcv_saddr);
+ if (rcv_saddr && rcv_saddr != laddr)
+ return false;
+
+ return raw_sk_bound_dev_eq(net, READ_ONCE(sk->sk_bound_dev_if),
+ dif, sdif);
}
EXPORT_SYMBOL_GPL(raw_v4_match);
@@ -722,7 +730,8 @@ static int raw_bind(struct sock *sk, struct sockaddr_unsized *uaddr,
chk_addr_ret))
goto out;
- inet->inet_rcv_saddr = inet->inet_saddr = addr->sin_addr.s_addr;
+ inet->inet_saddr = addr->sin_addr.s_addr;
+ WRITE_ONCE(inet->inet_rcv_saddr, addr->sin_addr.s_addr);
if (chk_addr_ret == RTN_MULTICAST || chk_addr_ret == RTN_BROADCAST)
inet->inet_saddr = 0; /* Use device */
sk_dst_reset(sk);
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 61045a8886e4..daff93d51342 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -4038,24 +4038,17 @@ static void tcp_send_ack_reflect_ect(struct sock *sk, bool accecn_reflector)
__tcp_send_ack(sk, tp->rcv_nxt, flags);
}
-/* RFC 5961 7 [ACK Throttling] */
-static void tcp_send_challenge_ack(struct sock *sk, bool accecn_reflector)
+/* Consume one slot from the per-netns RFC 5961 challenge ACK quota.
+ * Returns true if a challenge ACK may be sent.
+ */
+static bool tcp_challenge_ack_allowed(struct net *net)
{
- struct tcp_sock *tp = tcp_sk(sk);
- struct net *net = sock_net(sk);
u32 count, now, ack_limit;
- /* First check our per-socket dupack rate limit. */
- if (__tcp_oow_rate_limited(net,
- LINUX_MIB_TCPACKSKIPPEDCHALLENGE,
- &tp->last_oow_ack_time))
- return;
-
ack_limit = READ_ONCE(net->ipv4.sysctl_tcp_challenge_ack_limit);
if (ack_limit == INT_MAX)
- goto send_ack;
+ return true;
- /* Then check host-wide RFC 5961 rate limit. */
now = jiffies / HZ;
if (now != READ_ONCE(net->ipv4.tcp_challenge_timestamp)) {
u32 half = (ack_limit + 1) >> 1;
@@ -4067,12 +4060,49 @@ static void tcp_send_challenge_ack(struct sock *sk, bool accecn_reflector)
count = READ_ONCE(net->ipv4.tcp_challenge_count);
if (count > 0) {
WRITE_ONCE(net->ipv4.tcp_challenge_count, count - 1);
-send_ack:
+ return true;
+ }
+ return false;
+}
+
+/* RFC 5961 7 [ACK Throttling] */
+static void tcp_send_challenge_ack(struct sock *sk, bool accecn_reflector)
+{
+ struct tcp_sock *tp = tcp_sk(sk);
+ struct net *net = sock_net(sk);
+
+ /* First check our per-socket dupack rate limit. */
+ if (__tcp_oow_rate_limited(net,
+ LINUX_MIB_TCPACKSKIPPEDCHALLENGE,
+ &tp->last_oow_ack_time))
+ return;
+
+ /* Then check the per-netns RFC 5961 rate limit. */
+ if (tcp_challenge_ack_allowed(net)) {
NET_INC_STATS(net, LINUX_MIB_TCPCHALLENGEACK);
tcp_send_ack_reflect_ect(sk, accecn_reflector);
}
}
+/* Send a challenge ACK from a SYN-RECEIVED request socket. Uses
+ * __tcp_oow_rate_limited() directly so that an RST carrying payload
+ * cannot bypass the per-request rate limit.
+ */
+void tcp_reqsk_send_challenge_ack(struct sock *sk, struct sk_buff *skb,
+ struct request_sock *req)
+{
+ struct net *net = sock_net(sk);
+
+ if (__tcp_oow_rate_limited(net, LINUX_MIB_TCPACKSKIPPEDCHALLENGE,
+ &tcp_rsk(req)->last_oow_ack_time))
+ return;
+
+ if (tcp_challenge_ack_allowed(net)) {
+ NET_INC_STATS(net, LINUX_MIB_TCPCHALLENGEACK);
+ req->rsk_ops->send_ack(sk, skb, req);
+ }
+}
+
static void tcp_store_ts_recent(struct tcp_sock *tp)
{
tp->rx_opt.ts_recent = tp->rx_opt.rcv_tsval;
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 4a46da375043..b8887cdd66c5 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -971,6 +971,9 @@ static void tcp_v4_send_ack(const struct sock *sk,
key->rcv_next);
arg.iov[0].iov_len += tcp_ao_len_aligned(key->ao_key);
rep.th.doff = arg.iov[0].iov_len / 4;
+ memset((u8 *)&rep.opt[offset] + tcp_ao_maclen(key->ao_key),
+ TCPOPT_NOP, tcp_ao_len_aligned(key->ao_key) -
+ tcp_ao_len(key->ao_key));
tcp_ao_hash_hdr(AF_INET, (char *)&rep.opt[offset],
key->ao_key, key->traffic_key,
@@ -3143,8 +3146,11 @@ static struct sock *bpf_iter_tcp_batch(struct seq_file *seq)
bpf_iter_tcp_put_batch(iter);
err = bpf_iter_tcp_realloc_batch(iter, expected * 3 / 2,
GFP_USER);
- if (err)
+ if (err) {
+ iter->cur_sk = 0;
+ iter->end_sk = 0;
return ERR_PTR(err);
+ }
sk = bpf_iter_tcp_resume(seq);
if (!sk)
diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c
index ddc4b17a826b..6ab3e3a0b431 100644
--- a/net/ipv4/tcp_minisocks.c
+++ b/net/ipv4/tcp_minisocks.c
@@ -833,7 +833,7 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb,
* elsewhere and is checked directly against the child socket rather
* than req because user data may have been sent out.
*/
- if ((flg & TCP_FLAG_ACK) && !fastopen &&
+ if ((flg & TCP_FLAG_ACK) && !(flg & TCP_FLAG_RST) && !fastopen &&
(TCP_SKB_CB(skb)->ack_seq !=
tcp_rsk(req)->snt_isn + 1))
return sk;
@@ -872,6 +872,16 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb,
flg &= ~TCP_FLAG_SYN;
}
+ /* RFC 5961 section 3.2, as clarified by RFC 9293 section
+ * 3.10.7.4, requires a challenge ACK for a non-exact
+ * in-window RST in SYN-RECEIVED.
+ */
+ if ((flg & TCP_FLAG_RST) &&
+ TCP_SKB_CB(skb)->seq != tcp_rsk(req)->rcv_nxt) {
+ tcp_reqsk_send_challenge_ack(sk, skb, req);
+ return NULL;
+ }
+
/* RFC793: "second check the RST bit" and
* "fourth, check the SYN bit"
*/
diff --git a/net/ipv6/ila/ila_common.c b/net/ipv6/ila/ila_common.c
index e71571455c8a..b78179bfc4c7 100644
--- a/net/ipv6/ila/ila_common.c
+++ b/net/ipv6/ila/ila_common.c
@@ -85,6 +85,7 @@ static void ila_csum_adjust_transport(struct sk_buff *skb,
struct tcphdr *th = (struct tcphdr *)
(skb_network_header(skb) + nhoff);
+ ip6h = ipv6_hdr(skb);
diff = get_csum_diff(ip6h, p);
inet_proto_csum_replace_by_diff(&th->check, skb,
diff, true, true);
@@ -96,6 +97,7 @@ static void ila_csum_adjust_transport(struct sk_buff *skb,
(skb_network_header(skb) + nhoff);
if (uh->check || skb->ip_summed == CHECKSUM_PARTIAL) {
+ ip6h = ipv6_hdr(skb);
diff = get_csum_diff(ip6h, p);
inet_proto_csum_replace_by_diff(&uh->check, skb,
diff, true, true);
@@ -110,6 +112,7 @@ static void ila_csum_adjust_transport(struct sk_buff *skb,
struct icmp6hdr *ih = (struct icmp6hdr *)
(skb_network_header(skb) + nhoff);
+ ip6h = ipv6_hdr(skb);
diff = get_csum_diff(ip6h, p);
inet_proto_csum_replace_by_diff(&ih->icmp6_cksum, skb,
diff, true, true);
@@ -127,6 +130,15 @@ void ila_update_ipv6_locator(struct sk_buff *skb, struct ila_params *p,
switch (p->csum_mode) {
case ILA_CSUM_ADJUST_TRANSPORT:
ila_csum_adjust_transport(skb, p);
+ /*
+ * ila_csum_adjust_transport() calls pskb_may_pull(), which can
+ * reallocate the skb head and leave ip6h (and the iaddr derived
+ * from it) dangling; reload both before the write below. The
+ * other csum modes do not pull, so their cached pointers stay
+ * valid.
+ */
+ ip6h = ipv6_hdr(skb);
+ iaddr = ila_a2i(&ip6h->daddr);
break;
case ILA_CSUM_NEUTRAL_MAP:
if (sir2ila) {
diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c
index 7c09a269b352..b843116e9b70 100644
--- a/net/ipv6/ip6_gre.c
+++ b/net/ipv6/ip6_gre.c
@@ -1455,6 +1455,8 @@ static void ip6gre_tnl_init_features(struct net_device *dev)
dev->features |= GRE6_FEATURES;
dev->hw_features |= GRE6_FEATURES;
+ dev->lltx = true;
+
/* TCP offload with GRE SEQ is not supported, nor can we support 2
* levels of outer headers requiring an update.
*/
@@ -1466,8 +1468,6 @@ static void ip6gre_tnl_init_features(struct net_device *dev)
dev->features |= NETIF_F_GSO_SOFTWARE;
dev->hw_features |= NETIF_F_GSO_SOFTWARE;
-
- dev->lltx = true;
}
static int ip6gre_tunnel_init_common(struct net_device *dev)
diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c
index 3cc58698cbbd..b88d364e78aa 100644
--- a/net/ipv6/raw.c
+++ b/net/ipv6/raw.c
@@ -349,7 +349,7 @@ void raw6_icmp_error(struct sk_buff *skb, int nexthdr,
const struct ipv6hdr *ip6h = (const struct ipv6hdr *)skb->data;
if (!raw_v6_match(net, sk, nexthdr, &ip6h->saddr, &ip6h->daddr,
- inet6_iif(skb), inet6_iif(skb)))
+ inet6_iif(skb), inet6_sdif(skb)))
continue;
rawv6_err(sk, skb, type, code, inner_offset, info);
}
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index a1301334da48..fc42d67e5822 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -4022,7 +4022,7 @@ static int __ip6_del_rt_siblings(struct fib6_info *rt, struct fib6_config *cfg)
struct fib6_node *fn;
/* prefer to send a single notification with all hops */
- skb = nlmsg_new(rt6_nlmsg_size(rt), gfp_any());
+ skb = nlmsg_new(rt6_nlmsg_size(rt), GFP_ATOMIC);
if (skb) {
u32 seq = info->nlh ? info->nlh->nlmsg_seq : 0;
@@ -4078,7 +4078,7 @@ out_put:
if (skb) {
rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE,
- info->nlh, gfp_any());
+ info->nlh, GFP_ATOMIC);
}
return err;
}
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index 522ba45ce9b7..9e9155b1b3aa 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -923,6 +923,8 @@ static void tcp_v6_send_response(const struct sock *sk, struct sk_buff *skb, u32
(tcp_ao_len(key->ao_key) << 16) |
(key->ao_key->sndid << 8) |
(key->rcv_next));
+ memset((u8 *)topt + tcp_ao_maclen(key->ao_key), TCPOPT_NOP,
+ tcp_ao_len_aligned(key->ao_key) - tcp_ao_len(key->ao_key));
tcp_ao_hash_hdr(AF_INET6, (char *)topt, key->ao_key,
key->traffic_key,
diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c
index b85fb9767dec..ea047bab65e7 100644
--- a/net/iucv/af_iucv.c
+++ b/net/iucv/af_iucv.c
@@ -337,6 +337,7 @@ static void iucv_sever_path(struct sock *sk, int with_user_data)
unsigned char user_data[16];
struct iucv_sock *iucv = iucv_sk(sk);
struct iucv_path *path = iucv->path;
+ struct sock_msg_q *p, *n;
/* Whoever resets the path pointer, must sever and free it. */
if (xchg(&iucv->path, NULL)) {
@@ -348,6 +349,19 @@ static void iucv_sever_path(struct sock *sk, int with_user_data)
} else
pr_iucv->path_sever(path, NULL);
iucv_path_free(path);
+
+ /*
+ * Message notifications queued on message_q still reference
+ * the now freed path; drop them, otherwise a later recvmsg()
+ * would pass the freed iucv_path to message_receive() via
+ * iucv_process_message_q().
+ */
+ spin_lock_bh(&iucv->message_q.lock);
+ list_for_each_entry_safe(p, n, &iucv->message_q.list, list) {
+ list_del(&p->list);
+ kfree(p);
+ }
+ spin_unlock_bh(&iucv->message_q.lock);
}
}
@@ -1872,7 +1886,8 @@ static int afiucv_hs_callback_syn(struct sock *sk, struct sk_buff *skb)
afiucv_swap_src_dest(skb);
trans_hdr->flags = AF_IUCV_FLAG_SYN | AF_IUCV_FLAG_FIN;
err = dev_queue_xmit(skb);
- iucv_sock_kill(nsk);
+ if (nsk)
+ iucv_sock_kill(nsk);
bh_unlock_sock(sk);
goto out;
}
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index b00191e02a63..43f142624d33 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -2611,6 +2611,9 @@ static int sta_apply_parameters(struct ieee80211_local *local,
memcpy(&sta->deflink.pub->supp_rates,
&nmi_sta->deflink.pub->supp_rates,
sizeof(sta->deflink.pub->supp_rates));
+
+ sta->deflink.pub->agg = nmi_sta->deflink.pub->agg;
+ __ieee80211_sta_recalc_aggregates(sta, 0);
}
/* set the STA state after all sta info from usermode has been set */
diff --git a/net/mac80211/link.c b/net/mac80211/link.c
index d0535268962c..dc68144dc363 100644
--- a/net/mac80211/link.c
+++ b/net/mac80211/link.c
@@ -399,6 +399,10 @@ static int ieee80211_vif_update_links(struct ieee80211_sub_if_data *sdata,
memcpy(sdata->link, old_data, sizeof(old_data));
memcpy(sdata->vif.link_conf, old, sizeof(old));
ieee80211_set_vif_links_bitmaps(sdata, old_links, dormant_links);
+ for_each_set_bit(link_id, &add, IEEE80211_MLD_MAX_NUM_LINKS) {
+ ieee80211_link_debugfs_remove(&links[link_id]->data);
+ ieee80211_link_stop(&links[link_id]->data);
+ }
/* and free (only) the newly allocated links */
memset(to_free, 0, sizeof(links));
goto free;
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index d9ea19be075d..5e26be8e27d8 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -1717,6 +1717,8 @@ static void sta_ps_start(struct sta_info *sta)
else
clear_bit(tid, &sta->txq_buffered_tids);
}
+
+ sta_info_recalc_tim(sta);
}
static void sta_ps_end(struct sta_info *sta)
diff --git a/net/mac802154/llsec.c b/net/mac802154/llsec.c
index 5e7cc11fab3a..85452ef9a58c 100644
--- a/net/mac802154/llsec.c
+++ b/net/mac802154/llsec.c
@@ -891,6 +891,11 @@ llsec_do_decrypt_auth(struct sk_buff *skb, const struct mac802154_llsec *sec,
data = skb_mac_header(skb) + skb->mac_len;
datalen = skb_tail_pointer(skb) - data;
+ if (datalen < authlen) {
+ kfree_sensitive(req);
+ return -EBADMSG;
+ }
+
sg_init_one(&sg, skb_mac_header(skb), assoclen + datalen);
if (!(hdr->sec.level & IEEE802154_SCF_SECLEVEL_ENC)) {
diff --git a/net/mac802154/scan.c b/net/mac802154/scan.c
index 300d4584533e..65089826ff59 100644
--- a/net/mac802154/scan.c
+++ b/net/mac802154/scan.c
@@ -179,6 +179,7 @@ void mac802154_scan_worker(struct work_struct *work)
enum nl802154_scan_types scan_req_type;
struct ieee802154_sub_if_data *sdata;
unsigned int scan_duration = 0;
+ netdevice_tracker dev_tracker;
struct wpan_phy *wpan_phy;
u8 scan_req_duration;
u8 page, channel;
@@ -209,6 +210,14 @@ void mac802154_scan_worker(struct work_struct *work)
return;
}
+ /*
+ * sdata->dev is dereferenced below after rcu_read_unlock() and outside
+ * the rtnl, and a concurrent DEL_INTERFACE / PHY teardown can free it
+ * asynchronously from netdev_run_todo(). Pin it with a reference taken
+ * while the RCU read lock is still held, and drop it at every exit.
+ */
+ netdev_hold(sdata->dev, &dev_tracker, GFP_ATOMIC);
+
wpan_phy = scan_req->wpan_phy;
scan_req_type = scan_req->type;
scan_req_duration = scan_req->duration;
@@ -262,12 +271,14 @@ void mac802154_scan_worker(struct work_struct *work)
"Scan page %u channel %u for %ums\n",
page, channel, jiffies_to_msecs(scan_duration));
queue_delayed_work(local->mac_wq, &local->scan_work, scan_duration);
+ netdev_put(sdata->dev, &dev_tracker);
return;
end_scan:
rtnl_lock();
mac802154_scan_cleanup_locked(local, sdata, false);
rtnl_unlock();
+ netdev_put(sdata->dev, &dev_tracker);
}
int mac802154_trigger_scan_locked(struct ieee802154_sub_if_data *sdata,
diff --git a/net/mctp/device.c b/net/mctp/device.c
index 2c84df674669..822120e860c8 100644
--- a/net/mctp/device.c
+++ b/net/mctp/device.c
@@ -536,7 +536,9 @@ int __init mctp_device_init(void)
{
int err;
- register_netdevice_notifier(&mctp_dev_nb);
+ err = register_netdevice_notifier(&mctp_dev_nb);
+ if (err)
+ return err;
err = rtnl_af_register(&mctp_af_ops);
if (err)
diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c
index 318cb7e2ac5f..4406c304b639 100644
--- a/net/mpls/af_mpls.c
+++ b/net/mpls/af_mpls.c
@@ -922,8 +922,7 @@ static int mpls_nh_build_multi(struct mpls_route_config *cfg,
struct nlattr *nla_via, *nla_newdst;
int remaining = cfg->rc_mp_len;
int err = 0;
-
- rt->rt_nhn = 0;
+ u8 nhs = 0;
change_nexthops(rt) {
int attrlen;
@@ -959,12 +958,15 @@ static int mpls_nh_build_multi(struct mpls_route_config *cfg,
rt->rt_nhn_alive--;
rtnh = rtnh_next(rtnh, &remaining);
- rt->rt_nhn++;
+ nhs++;
} endfor_nexthops(rt);
+ rt->rt_nhn = nhs;
+
return 0;
errout:
+ rt->rt_nhn = nhs;
return err;
}
diff --git a/net/mptcp/options.c b/net/mptcp/options.c
index dff3fd5d3b55..c664023d37ba 100644
--- a/net/mptcp/options.c
+++ b/net/mptcp/options.c
@@ -157,17 +157,11 @@ static void mptcp_parse_option(const struct sk_buff *skb,
ptr++;
flags = (*ptr++) & MPTCP_DSS_FLAG_MASK;
- mp_opt->data_fin = (flags & MPTCP_DSS_DATA_FIN) != 0;
mp_opt->dsn64 = (flags & MPTCP_DSS_DSN64) != 0;
mp_opt->use_map = (flags & MPTCP_DSS_HAS_MAP) != 0;
mp_opt->ack64 = (flags & MPTCP_DSS_ACK64) != 0;
mp_opt->use_ack = (flags & MPTCP_DSS_HAS_ACK);
- pr_debug("data_fin=%d dsn64=%d use_map=%d ack64=%d use_ack=%d\n",
- mp_opt->data_fin, mp_opt->dsn64,
- mp_opt->use_map, mp_opt->ack64,
- mp_opt->use_ack);
-
expected_opsize = TCPOLEN_MPTCP_DSS_BASE;
if (mp_opt->use_ack) {
@@ -178,12 +172,18 @@ static void mptcp_parse_option(const struct sk_buff *skb,
}
if (mp_opt->use_map) {
+ mp_opt->data_fin = (flags & MPTCP_DSS_DATA_FIN) != 0;
if (mp_opt->dsn64)
expected_opsize += TCPOLEN_MPTCP_DSS_MAP64;
else
expected_opsize += TCPOLEN_MPTCP_DSS_MAP32;
}
+ pr_debug("data_fin=%d dsn64=%d use_map=%d ack64=%d use_ack=%d\n",
+ mp_opt->data_fin, mp_opt->dsn64,
+ mp_opt->use_map, mp_opt->ack64,
+ mp_opt->use_ack);
+
/* Always parse any csum presence combination, we will enforce
* RFC 8684 Section 3.3.0 checks later in subflow_data_ready
*/
@@ -571,10 +571,7 @@ static bool mptcp_established_options_dss(struct sock *sk, struct sk_buff *skb,
bool ret = false;
/* Zero `use_ack` and `use_map` flags with one shot. */
- BUILD_BUG_ON(sizeof_field(struct mptcp_ext, flags) != sizeof(u16));
- BUILD_BUG_ON(!IS_ALIGNED(offsetof(struct mptcp_ext, flags),
- sizeof(u16)));
- *(u16 *)&opts->ext_copy.flags = 0;
+ memset(&opts->ext_copy.flags, 0, sizeof(opts->ext_copy.flags));
opts->csum_reqd = READ_ONCE(msk->csum_enabled);
mpext = skb ? mptcp_get_ext(skb) : NULL;
diff --git a/net/mptcp/pm_userspace.c b/net/mptcp/pm_userspace.c
index d100867e9202..945aa5afc2dd 100644
--- a/net/mptcp/pm_userspace.c
+++ b/net/mptcp/pm_userspace.c
@@ -132,12 +132,15 @@ int mptcp_userspace_pm_get_local_id(struct mptcp_sock *msk,
__be16 msk_sport = ((struct inet_sock *)
inet_sk((struct sock *)msk))->inet_sport;
struct mptcp_pm_addr_entry *entry;
+ int id;
spin_lock_bh(&msk->pm.lock);
entry = mptcp_userspace_pm_lookup_addr(msk, &skc->addr);
+ id = entry ? entry->addr.id : -1;
spin_unlock_bh(&msk->pm.lock);
- if (entry)
- return entry->addr.id;
+
+ if (id != -1)
+ return id;
if (skc->addr.port == msk_sport)
skc->addr.port = 0;
diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
index cb9515f505aa..ca644ec53eed 100644
--- a/net/mptcp/protocol.c
+++ b/net/mptcp/protocol.c
@@ -2545,6 +2545,22 @@ static void __mptcp_subflow_disconnect(struct sock *ssk,
}
}
+static void mptcp_cleanup_ssk_backlog(struct sock *sk, struct sock *ssk)
+{
+ struct mptcp_sock *msk = mptcp_sk(sk);
+ struct sk_buff *skb;
+
+ mptcp_data_lock(sk);
+ list_for_each_entry(skb, &msk->backlog_list, list) {
+ if (skb->sk != ssk)
+ continue;
+
+ atomic_sub(skb->truesize, &skb->sk->sk_rmem_alloc);
+ skb->sk = NULL;
+ }
+ mptcp_data_unlock(sk);
+}
+
/* subflow sockets can be either outgoing (connect) or incoming
* (accept).
*
@@ -2568,6 +2584,9 @@ static void __mptcp_close_ssk(struct sock *sk, struct sock *ssk,
lock_sock_nested(ssk, SINGLE_DEPTH_NESTING);
subflow->closing = 1;
+ if (flags & MPTCP_CF_PUSH)
+ mptcp_cleanup_ssk_backlog(sk, ssk);
+
/* Borrow the fwd allocated page left-over; fwd memory for the subflow
* could be negative at this point, but will be reach zero soon - when
* the data allocated using such fragment will be freed.
@@ -2659,9 +2678,6 @@ out:
void mptcp_close_ssk(struct sock *sk, struct sock *ssk,
struct mptcp_subflow_context *subflow)
{
- struct mptcp_sock *msk = mptcp_sk(sk);
- struct sk_buff *skb;
-
/* The first subflow can already be closed or disconnected */
if (subflow->close_event_done || READ_ONCE(subflow->local_id) < 0)
return;
@@ -2671,17 +2687,6 @@ void mptcp_close_ssk(struct sock *sk, struct sock *ssk,
if (sk->sk_state == TCP_ESTABLISHED)
mptcp_event(MPTCP_EVENT_SUB_CLOSED, mptcp_sk(sk), ssk, GFP_KERNEL);
- /* Remove any reference from the backlog to this ssk; backlog skbs consume
- * space in the msk receive queue, no need to touch sk->sk_rmem_alloc
- */
- list_for_each_entry(skb, &msk->backlog_list, list) {
- if (skb->sk != ssk)
- continue;
-
- atomic_sub(skb->truesize, &skb->sk->sk_rmem_alloc);
- skb->sk = NULL;
- }
-
/* subflow aborted before reaching the fully_established status
* attempt the creation of the next subflow
*/
@@ -3907,6 +3912,7 @@ bool mptcp_finish_join(struct sock *ssk)
mptcp_data_unlock(parent);
if (!ret) {
+ mptcp_pm_close_subflow(msk);
err_prohibited:
subflow->reset_reason = MPTCP_RST_EPROHIBIT;
return false;
diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c
index 140388a18ae0..513fca6a8e8a 100644
--- a/net/openvswitch/actions.c
+++ b/net/openvswitch/actions.c
@@ -837,12 +837,8 @@ static void do_output(struct datapath *dp, struct sk_buff *skb, int out_port,
u16 mru = OVS_CB(skb)->mru;
u32 cutlen = OVS_CB(skb)->cutlen;
- if (unlikely(cutlen > 0)) {
- if (skb->len - cutlen > ovs_mac_header_len(key))
- pskb_trim(skb, skb->len - cutlen);
- else
- pskb_trim(skb, ovs_mac_header_len(key));
- }
+ if (unlikely(cutlen < skb->len))
+ pskb_trim(skb, max(cutlen, ovs_mac_header_len(key)));
if (likely(!mru ||
(skb->len <= mru + vport->dev->hard_header_len))) {
@@ -1234,7 +1230,7 @@ static void execute_psample(struct datapath *dp, struct sk_buff *skb,
psample_group.net = ovs_dp_get_net(dp);
md.in_ifindex = OVS_CB(skb)->input_vport->dev->ifindex;
- md.trunc_size = skb->len - OVS_CB(skb)->cutlen;
+ md.trunc_size = min(skb->len, OVS_CB(skb)->cutlen);
md.rate_as_probability = 1;
rate = OVS_CB(skb)->probability ? OVS_CB(skb)->probability : U32_MAX;
@@ -1284,22 +1280,21 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
clone = skb_clone(skb, GFP_ATOMIC);
if (clone)
do_output(dp, clone, port, key);
- OVS_CB(skb)->cutlen = 0;
+ OVS_CB(skb)->cutlen = U32_MAX;
break;
}
case OVS_ACTION_ATTR_TRUNC: {
struct ovs_action_trunc *trunc = nla_data(a);
- if (skb->len > trunc->max_len)
- OVS_CB(skb)->cutlen = skb->len - trunc->max_len;
+ OVS_CB(skb)->cutlen = trunc->max_len;
break;
}
case OVS_ACTION_ATTR_USERSPACE:
output_userspace(dp, skb, key, a, attr,
len, OVS_CB(skb)->cutlen);
- OVS_CB(skb)->cutlen = 0;
+ OVS_CB(skb)->cutlen = U32_MAX;
if (nla_is_last(a, rem)) {
consume_skb(skb);
return 0;
@@ -1453,7 +1448,7 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
case OVS_ACTION_ATTR_PSAMPLE:
execute_psample(dp, skb, a);
- OVS_CB(skb)->cutlen = 0;
+ OVS_CB(skb)->cutlen = U32_MAX;
if (nla_is_last(a, rem)) {
consume_skb(skb);
return 0;
diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c
index f0164817d9b7..eaf332b156d7 100644
--- a/net/openvswitch/datapath.c
+++ b/net/openvswitch/datapath.c
@@ -276,7 +276,7 @@ void ovs_dp_process_packet(struct sk_buff *skb, struct sw_flow_key *key)
upcall.portid = ovs_vport_find_upcall_portid(p, skb);
upcall.mru = OVS_CB(skb)->mru;
- error = ovs_dp_upcall(dp, skb, key, &upcall, 0);
+ error = ovs_dp_upcall(dp, skb, key, &upcall, U32_MAX);
switch (error) {
case 0:
case -EAGAIN:
@@ -457,7 +457,8 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb,
struct sk_buff *nskb = NULL;
struct sk_buff *user_skb = NULL; /* to be queued to userspace */
struct nlattr *nla;
- size_t len;
+ size_t msg_size;
+ size_t skb_len;
unsigned int hlen;
int err, dp_ifindex;
u64 hash;
@@ -478,7 +479,8 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb,
skb = nskb;
}
- if (nla_attr_size(skb->len) > USHRT_MAX) {
+ skb_len = min(skb->len, cutlen);
+ if (nla_attr_size(skb_len) > USHRT_MAX) {
err = -EFBIG;
goto out;
}
@@ -493,13 +495,13 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb,
* padding logic. Only perform zerocopy if padding is not required.
*/
if (dp->user_features & OVS_DP_F_UNALIGNED)
- hlen = skb_zerocopy_headlen(skb);
+ hlen = min(skb_zerocopy_headlen(skb), cutlen);
else
- hlen = skb->len;
+ hlen = skb_len;
- len = upcall_msg_size(upcall_info, hlen - cutlen,
- OVS_CB(skb)->acts_origlen);
- user_skb = genlmsg_new(len, GFP_ATOMIC);
+ msg_size = upcall_msg_size(upcall_info, hlen,
+ OVS_CB(skb)->acts_origlen);
+ user_skb = genlmsg_new(msg_size, GFP_ATOMIC);
if (!user_skb) {
err = -ENOMEM;
goto out;
@@ -560,7 +562,7 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb,
}
/* Add OVS_PACKET_ATTR_LEN when packet is truncated */
- if (cutlen > 0 &&
+ if (skb_len < skb->len &&
nla_put_u32(user_skb, OVS_PACKET_ATTR_LEN, skb->len)) {
err = -ENOBUFS;
goto out;
@@ -585,9 +587,9 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb,
err = -ENOBUFS;
goto out;
}
- nla->nla_len = nla_attr_size(skb->len - cutlen);
+ nla->nla_len = nla_attr_size(skb_len);
- err = skb_zerocopy(user_skb, skb, skb->len - cutlen, hlen);
+ err = skb_zerocopy(user_skb, skb, skb_len, hlen);
if (err)
goto out;
@@ -644,6 +646,7 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info)
packet->ignore_df = 1;
}
OVS_CB(packet)->mru = mru;
+ OVS_CB(packet)->cutlen = U32_MAX;
if (a[OVS_PACKET_ATTR_HASH]) {
hash = nla_get_u64(a[OVS_PACKET_ATTR_HASH]);
diff --git a/net/openvswitch/datapath.h b/net/openvswitch/datapath.h
index db0c3e69d66c..696640e88fa7 100644
--- a/net/openvswitch/datapath.h
+++ b/net/openvswitch/datapath.h
@@ -118,7 +118,7 @@ struct datapath {
* @mru: The maximum received fragement size; 0 if the packet is not
* fragmented.
* @acts_origlen: The netlink size of the flow actions applied to this skb.
- * @cutlen: The number of bytes from the packet end to be removed.
+ * @cutlen: The number of bytes in the packet to preserve on output.
* @probability: The sampling probability that was applied to this skb; 0 means
* no sampling has occurred; U32_MAX means 100% probability.
* @upcall_pid: Netlink socket PID to use for sending this packet to userspace;
diff --git a/net/openvswitch/vport.c b/net/openvswitch/vport.c
index 56b2e2d1a749..12741485c939 100644
--- a/net/openvswitch/vport.c
+++ b/net/openvswitch/vport.c
@@ -502,7 +502,7 @@ int ovs_vport_receive(struct vport *vport, struct sk_buff *skb,
OVS_CB(skb)->input_vport = vport;
OVS_CB(skb)->mru = 0;
- OVS_CB(skb)->cutlen = 0;
+ OVS_CB(skb)->cutlen = U32_MAX;
OVS_CB(skb)->probability = 0;
OVS_CB(skb)->upcall_pid = 0;
if (unlikely(dev_net(skb->dev) != ovs_dp_get_net(vport->dp))) {
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index 8e6f3a734ba0..e75d2932475a 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -4561,7 +4561,11 @@ static int packet_set_ring(struct sock *sk, union tpacket_req_u *req_u,
spin_lock(&po->bind_lock);
WRITE_ONCE(po->num, num);
- if (was_running)
+ /*
+ * NETDEV_UNREGISTER may have invalidated the binding while bind_lock
+ * was dropped above. Do not re-add a fanout hook to a dead device.
+ */
+ if (was_running && READ_ONCE(po->ifindex) != -1)
register_prot_hook(sk);
spin_unlock(&po->bind_lock);
diff --git a/net/phonet/pep.c b/net/phonet/pep.c
index 706927139393..31b29e3ca7bc 100644
--- a/net/phonet/pep.c
+++ b/net/phonet/pep.c
@@ -55,6 +55,8 @@ static unsigned char *pep_get_sb(struct sk_buff *skb, u8 *ptype, u8 *plen,
ph = skb_header_pointer(skb, 0, 2, &h);
if (ph == NULL || ph->sb_len < 2 || !pskb_may_pull(skb, ph->sb_len))
return NULL;
+ /* pskb_may_pull() may have reallocated the head; refetch ph. */
+ ph = skb_header_pointer(skb, 0, 2, &h);
ph->sb_len -= 2;
*ptype = ph->sb_type;
*plen = ph->sb_len;
diff --git a/net/phonet/pn_dev.c b/net/phonet/pn_dev.c
index ad44831d6745..1272d49cd038 100644
--- a/net/phonet/pn_dev.c
+++ b/net/phonet/pn_dev.c
@@ -350,16 +350,34 @@ static struct pernet_operations phonet_net_ops = {
/* Initialize Phonet devices list */
int __init phonet_device_init(void)
{
- int err = register_pernet_subsys(&phonet_net_ops);
+ int err;
+
+ err = register_pernet_subsys(&phonet_net_ops);
if (err)
return err;
- proc_create_net("pnresource", 0, init_net.proc_net, &pn_res_seq_ops,
- sizeof(struct seq_net_private));
- register_netdevice_notifier(&phonet_device_notifier);
+ if (!proc_create_net("pnresource", 0, init_net.proc_net,
+ &pn_res_seq_ops, sizeof(struct seq_net_private))) {
+ err = -ENOMEM;
+ goto err_pernet;
+ }
+
+ err = register_netdevice_notifier(&phonet_device_notifier);
+ if (err)
+ goto err_proc;
+
err = phonet_netlink_register();
if (err)
- phonet_device_exit();
+ goto err_notifier;
+
+ return 0;
+
+err_notifier:
+ unregister_netdevice_notifier(&phonet_device_notifier);
+err_proc:
+ remove_proc_entry("pnresource", init_net.proc_net);
+err_pernet:
+ unregister_pernet_subsys(&phonet_net_ops);
return err;
}
@@ -367,8 +385,8 @@ void phonet_device_exit(void)
{
rtnl_unregister_all(PF_PHONET);
unregister_netdevice_notifier(&phonet_device_notifier);
- unregister_pernet_subsys(&phonet_net_ops);
remove_proc_entry("pnresource", init_net.proc_net);
+ unregister_pernet_subsys(&phonet_net_ops);
}
int phonet_route_add(struct net_device *dev, u8 daddr)
diff --git a/net/qrtr/af_qrtr.c b/net/qrtr/af_qrtr.c
index d02ef9a74c3c..a30fa56e6aa3 100644
--- a/net/qrtr/af_qrtr.c
+++ b/net/qrtr/af_qrtr.c
@@ -1263,6 +1263,14 @@ static int qrtr_create(struct net *net, struct socket *sock,
if (sock->type != SOCK_DGRAM)
return -EPROTOTYPE;
+ /* QRTR keeps its port and node state in module-global variables that
+ * are not partitioned per network namespace, and the in-kernel name
+ * service only operates in init_net. Confine the family to init_net so
+ * a socket in another namespace cannot reach the global control plane.
+ */
+ if (!net_eq(net, &init_net))
+ return -EAFNOSUPPORT;
+
sk = sk_alloc(net, AF_QIPCRTR, GFP_KERNEL, &qrtr_proto, kern);
if (!sk)
return -ENOMEM;
diff --git a/net/qrtr/ns.c b/net/qrtr/ns.c
index b3f9bbcf9ab9..e5b2adb161d9 100644
--- a/net/qrtr/ns.c
+++ b/net/qrtr/ns.c
@@ -76,11 +76,11 @@ struct qrtr_node {
* requirements. If the requirement changes in the future, these values can be
* increased.
*/
-#define QRTR_NS_MAX_NODES 64
+#define QRTR_NS_MAX_NODES 512
#define QRTR_NS_MAX_SERVERS 256
#define QRTR_NS_MAX_LOOKUPS 64
-static u8 node_count;
+static u16 node_count;
static struct qrtr_node *node_get(unsigned int node_id)
{
diff --git a/net/rds/ib.c b/net/rds/ib.c
index 39f87272e071..8f9cf491984f 100644
--- a/net/rds/ib.c
+++ b/net/rds/ib.c
@@ -429,6 +429,10 @@ static int rds_ib_laddr_check_cm(struct net *net, const struct in6_addr *addr,
sa = (struct sockaddr *)&sin;
} else {
#if IS_ENABLED(CONFIG_IPV6)
+ if (!ipv6_mod_enabled()) {
+ ret = -EADDRNOTAVAIL;
+ goto out;
+ }
memset(&sin6, 0, sizeof(sin6));
sin6.sin6_family = AF_INET6;
sin6.sin6_addr = *addr;
diff --git a/net/rds/ib_cm.c b/net/rds/ib_cm.c
index 5667f0173b47..d46146887ba4 100644
--- a/net/rds/ib_cm.c
+++ b/net/rds/ib_cm.c
@@ -810,6 +810,10 @@ int rds_ib_cm_handle_connect(struct rdma_cm_id *cm_id,
dp = event->param.conn.private_data;
if (isv6) {
#if IS_ENABLED(CONFIG_IPV6)
+ if (!ipv6_mod_enabled()) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
dp_cmn = &dp->ricp_v6.dp_cmn;
saddr6 = &dp->ricp_v6.dp_saddr;
daddr6 = &dp->ricp_v6.dp_daddr;
diff --git a/net/rds/recv.c b/net/rds/recv.c
index 4b3f9e4a8bfd..cf3884d87931 100644
--- a/net/rds/recv.c
+++ b/net/rds/recv.c
@@ -399,6 +399,21 @@ void rds_recv_incoming(struct rds_connection *conn, struct in6_addr *saddr,
goto out;
}
+ /*
+ * rds_find_bound() uses a global (netns-agnostic) hash table.
+ * An RDS connection created in netns A can match a socket bound
+ * in the init netns, delivering inc cross-netns with inc->i_conn
+ * pointing into netns A. When cleanup_net() then frees that conn,
+ * any subsequent dereference of inc->i_conn is a use-after-free.
+ * Drop the inc if the receiving socket lives in a different netns.
+ */
+ if (!net_eq(sock_net(rds_rs_to_sk(rs)), rds_conn_net(conn))) {
+ rds_stats_inc(s_recv_drop_no_sock);
+ rds_sock_put(rs);
+ rs = NULL;
+ goto out;
+ }
+
/* Process extension headers */
rds_recv_incoming_exthdrs(inc, rs);
diff --git a/net/rds/tcp.c b/net/rds/tcp.c
index a1de114d5e2e..5de35d556f29 100644
--- a/net/rds/tcp.c
+++ b/net/rds/tcp.c
@@ -366,9 +366,11 @@ int rds_tcp_laddr_check(struct net *net, const struct in6_addr *addr,
rcu_read_unlock();
}
#if IS_ENABLED(CONFIG_IPV6)
- ret = ipv6_chk_addr(net, addr, dev, 0);
- if (ret)
- return 0;
+ if (ipv6_mod_enabled()) {
+ ret = ipv6_chk_addr(net, addr, dev, 0);
+ if (ret)
+ return 0;
+ }
#endif
return -EADDRNOTAVAIL;
}
@@ -655,13 +657,13 @@ static void __net_exit rds_tcp_exit_net(struct net *net)
{
struct rds_tcp_net *rtn = net_generic(net, rds_tcp_netid);
- rds_tcp_kill_sock(net);
-
if (rtn->rds_tcp_sysctl)
unregister_net_sysctl_table(rtn->rds_tcp_sysctl);
if (net != &init_net)
kfree(rtn->ctl_table);
+
+ rds_tcp_kill_sock(net);
}
static struct pernet_operations rds_tcp_net_ops = {
diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h
index ce946b0a03e2..865f05fe37ab 100644
--- a/net/rxrpc/ar-internal.h
+++ b/net/rxrpc/ar-internal.h
@@ -1285,9 +1285,11 @@ int rxrpc_io_thread(void *data);
void rxrpc_post_response(struct rxrpc_connection *conn, struct sk_buff *skb);
static inline void rxrpc_wake_up_io_thread(struct rxrpc_local *local)
{
- if (!local->io_thread)
+ struct task_struct *io_thread = READ_ONCE(local->io_thread);
+
+ if (!io_thread)
return;
- wake_up_process(READ_ONCE(local->io_thread));
+ wake_up_process(io_thread);
}
static inline bool rxrpc_protocol_error(struct sk_buff *skb, enum rxrpc_abort_reason why)
diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c
index ffeea6db8337..fee4524adc98 100644
--- a/net/sched/cls_api.c
+++ b/net/sched/cls_api.c
@@ -4045,7 +4045,7 @@ struct sk_buff *tcf_qevent_handle(struct tcf_qevent *qe, struct Qdisc *sch, stru
fl = rcu_dereference_bh(qe->filter_chain);
- switch (tcf_classify(skb, NULL, fl, &cl_res, false)) {
+ switch (tcf_classify_qdisc(skb, fl, &cl_res, false)) {
case TC_ACT_SHOT:
qdisc_qstats_drop(sch);
__qdisc_drop(skb, to_free);
@@ -4057,10 +4057,6 @@ struct sk_buff *tcf_qevent_handle(struct tcf_qevent *qe, struct Qdisc *sch, stru
__qdisc_drop(skb, to_free);
*ret = __NET_XMIT_STOLEN;
return NULL;
- case TC_ACT_REDIRECT:
- skb_do_redirect(skb);
- *ret = __NET_XMIT_STOLEN;
- return NULL;
case TC_ACT_CONSUMED:
*ret = __NET_XMIT_STOLEN;
return NULL;
diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c
index 8a3236456db4..668bcd60d183 100644
--- a/net/sched/sch_api.c
+++ b/net/sched/sch_api.c
@@ -415,12 +415,13 @@ static __u8 __detect_linklayer(struct tc_ratespec *r, __u32 *rtab)
}
static struct qdisc_rate_table *qdisc_rtab_list;
+static DEFINE_SPINLOCK(qdisc_rtab_lock);
struct qdisc_rate_table *qdisc_get_rtab(struct tc_ratespec *r,
struct nlattr *tab,
struct netlink_ext_ack *extack)
{
- struct qdisc_rate_table *rtab;
+ struct qdisc_rate_table *rtab, *new_rtab;
if (tab == NULL || r->rate == 0 ||
r->cell_log == 0 || r->cell_log >= 32 ||
@@ -429,15 +430,20 @@ struct qdisc_rate_table *qdisc_get_rtab(struct tc_ratespec *r,
return NULL;
}
+ new_rtab = kmalloc_obj(*new_rtab);
+
+ spin_lock(&qdisc_rtab_lock);
for (rtab = qdisc_rtab_list; rtab; rtab = rtab->next) {
if (!memcmp(&rtab->rate, r, sizeof(struct tc_ratespec)) &&
!memcmp(&rtab->data, nla_data(tab), TC_RTAB_SIZE)) {
rtab->refcnt++;
+ spin_unlock(&qdisc_rtab_lock);
+ kfree(new_rtab);
return rtab;
}
}
- rtab = kmalloc_obj(*rtab);
+ rtab = new_rtab;
if (rtab) {
rtab->rate = *r;
rtab->refcnt = 1;
@@ -449,6 +455,7 @@ struct qdisc_rate_table *qdisc_get_rtab(struct tc_ratespec *r,
} else {
NL_SET_ERR_MSG(extack, "Failed to allocate new qdisc rate table");
}
+ spin_unlock(&qdisc_rtab_lock);
return rtab;
}
EXPORT_SYMBOL(qdisc_get_rtab);
@@ -457,18 +464,25 @@ void qdisc_put_rtab(struct qdisc_rate_table *tab)
{
struct qdisc_rate_table *rtab, **rtabp;
- if (!tab || --tab->refcnt)
+ if (!tab)
return;
+ spin_lock(&qdisc_rtab_lock);
+ if (--tab->refcnt) {
+ spin_unlock(&qdisc_rtab_lock);
+ return;
+ }
+
for (rtabp = &qdisc_rtab_list;
(rtab = *rtabp) != NULL;
rtabp = &rtab->next) {
if (rtab == tab) {
*rtabp = rtab->next;
- kfree(rtab);
- return;
+ break;
}
}
+ spin_unlock(&qdisc_rtab_lock);
+ kfree(tab);
}
EXPORT_SYMBOL(qdisc_put_rtab);
diff --git a/net/sched/sch_cake.c b/net/sched/sch_cake.c
index f78f8e950776..505f63fecf64 100644
--- a/net/sched/sch_cake.c
+++ b/net/sched/sch_cake.c
@@ -1727,7 +1727,7 @@ static u32 cake_classify(struct Qdisc *sch, struct cake_tin_data **t,
goto hash;
*qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
- result = tcf_classify(skb, NULL, filter, &res, false);
+ result = tcf_classify_qdisc(skb, filter, &res, false);
if (result >= 0) {
#ifdef CONFIG_NET_CLS_ACT
diff --git a/net/sched/sch_drr.c b/net/sched/sch_drr.c
index 020657f959b5..91b1ef824afa 100644
--- a/net/sched/sch_drr.c
+++ b/net/sched/sch_drr.c
@@ -312,7 +312,7 @@ static struct drr_class *drr_classify(struct sk_buff *skb, struct Qdisc *sch,
*qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
fl = rcu_dereference_bh(q->filter_list);
- result = tcf_classify(skb, NULL, fl, &res, false);
+ result = tcf_classify_qdisc(skb, fl, &res, false);
if (result >= 0) {
#ifdef CONFIG_NET_CLS_ACT
switch (result) {
diff --git a/net/sched/sch_dualpi2.c b/net/sched/sch_dualpi2.c
index 27088760eff4..4f678d4ff10e 100644
--- a/net/sched/sch_dualpi2.c
+++ b/net/sched/sch_dualpi2.c
@@ -364,7 +364,7 @@ static int dualpi2_skb_classify(struct dualpi2_sched_data *q,
if (!fl)
return NET_XMIT_SUCCESS;
- result = tcf_classify(skb, NULL, fl, &res, false);
+ result = tcf_classify_qdisc(skb, fl, &res, false);
if (result >= 0) {
#ifdef CONFIG_NET_CLS_ACT
switch (result) {
diff --git a/net/sched/sch_ets.c b/net/sched/sch_ets.c
index cb8cf437ce87..25fcf4079fec 100644
--- a/net/sched/sch_ets.c
+++ b/net/sched/sch_ets.c
@@ -391,7 +391,7 @@ static struct ets_class *ets_classify(struct sk_buff *skb, struct Qdisc *sch,
*qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
if (TC_H_MAJ(skb->priority) != sch->handle) {
fl = rcu_dereference_bh(q->filter_list);
- err = tcf_classify(skb, NULL, fl, &res, false);
+ err = tcf_classify_qdisc(skb, fl, &res, false);
#ifdef CONFIG_NET_CLS_ACT
switch (err) {
case TC_ACT_STOLEN:
diff --git a/net/sched/sch_fq_codel.c b/net/sched/sch_fq_codel.c
index cafd1f943d99..6cce86ba383c 100644
--- a/net/sched/sch_fq_codel.c
+++ b/net/sched/sch_fq_codel.c
@@ -91,7 +91,7 @@ static unsigned int fq_codel_classify(struct sk_buff *skb, struct Qdisc *sch,
return fq_codel_hash(q, skb) + 1;
*qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
- result = tcf_classify(skb, NULL, filter, &res, false);
+ result = tcf_classify_qdisc(skb, filter, &res, false);
if (result >= 0) {
#ifdef CONFIG_NET_CLS_ACT
switch (result) {
diff --git a/net/sched/sch_fq_pie.c b/net/sched/sch_fq_pie.c
index 72f48fa4010b..069e1facd413 100644
--- a/net/sched/sch_fq_pie.c
+++ b/net/sched/sch_fq_pie.c
@@ -96,7 +96,7 @@ static unsigned int fq_pie_classify(struct sk_buff *skb, struct Qdisc *sch,
return fq_pie_hash(q, skb) + 1;
*qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
- result = tcf_classify(skb, NULL, filter, &res, false);
+ result = tcf_classify_qdisc(skb, filter, &res, false);
if (result >= 0) {
#ifdef CONFIG_NET_CLS_ACT
switch (result) {
diff --git a/net/sched/sch_hfsc.c b/net/sched/sch_hfsc.c
index 7e537295b8b6..e87f5021a199 100644
--- a/net/sched/sch_hfsc.c
+++ b/net/sched/sch_hfsc.c
@@ -1143,7 +1143,7 @@ hfsc_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr)
*qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
head = &q->root;
tcf = rcu_dereference_bh(q->root.filter_list);
- while (tcf && (result = tcf_classify(skb, NULL, tcf, &res, false)) >= 0) {
+ while (tcf && (result = tcf_classify_qdisc(skb, tcf, &res, false)) >= 0) {
#ifdef CONFIG_NET_CLS_ACT
switch (result) {
case TC_ACT_QUEUED:
diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c
index 908b9ba9ba2e..fdac0dc8f35a 100644
--- a/net/sched/sch_htb.c
+++ b/net/sched/sch_htb.c
@@ -243,7 +243,7 @@ static struct htb_class *htb_classify(struct sk_buff *skb, struct Qdisc *sch,
}
*qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
- while (tcf && (result = tcf_classify(skb, NULL, tcf, &res, false)) >= 0) {
+ while (tcf && (result = tcf_classify_qdisc(skb, tcf, &res, false)) >= 0) {
#ifdef CONFIG_NET_CLS_ACT
switch (result) {
case TC_ACT_QUEUED:
diff --git a/net/sched/sch_multiq.c b/net/sched/sch_multiq.c
index a467dd122369..66df30939aa5 100644
--- a/net/sched/sch_multiq.c
+++ b/net/sched/sch_multiq.c
@@ -36,7 +36,7 @@ multiq_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr)
int err;
*qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
- err = tcf_classify(skb, NULL, fl, &res, false);
+ err = tcf_classify_qdisc(skb, fl, &res, false);
#ifdef CONFIG_NET_CLS_ACT
switch (err) {
case TC_ACT_STOLEN:
diff --git a/net/sched/sch_prio.c b/net/sched/sch_prio.c
index e4dd56a89072..79437c587e7e 100644
--- a/net/sched/sch_prio.c
+++ b/net/sched/sch_prio.c
@@ -39,7 +39,7 @@ prio_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr)
*qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
if (TC_H_MAJ(skb->priority) != sch->handle) {
fl = rcu_dereference_bh(q->filter_list);
- err = tcf_classify(skb, NULL, fl, &res, false);
+ err = tcf_classify_qdisc(skb, fl, &res, false);
#ifdef CONFIG_NET_CLS_ACT
switch (err) {
case TC_ACT_STOLEN:
diff --git a/net/sched/sch_qfq.c b/net/sched/sch_qfq.c
index cb56787e1d25..6f3b7273cb16 100644
--- a/net/sched/sch_qfq.c
+++ b/net/sched/sch_qfq.c
@@ -709,7 +709,7 @@ static struct qfq_class *qfq_classify(struct sk_buff *skb, struct Qdisc *sch,
*qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
fl = rcu_dereference_bh(q->filter_list);
- result = tcf_classify(skb, NULL, fl, &res, false);
+ result = tcf_classify_qdisc(skb, fl, &res, false);
if (result >= 0) {
#ifdef CONFIG_NET_CLS_ACT
switch (result) {
diff --git a/net/sched/sch_sfb.c b/net/sched/sch_sfb.c
index b1d465094276..ed39869199c0 100644
--- a/net/sched/sch_sfb.c
+++ b/net/sched/sch_sfb.c
@@ -260,7 +260,7 @@ static bool sfb_classify(struct sk_buff *skb, struct tcf_proto *fl,
struct tcf_result res;
int result;
- result = tcf_classify(skb, NULL, fl, &res, false);
+ result = tcf_classify_qdisc(skb, fl, &res, false);
if (result >= 0) {
#ifdef CONFIG_NET_CLS_ACT
switch (result) {
diff --git a/net/sched/sch_sfq.c b/net/sched/sch_sfq.c
index 758b88f21865..77675f9a4c46 100644
--- a/net/sched/sch_sfq.c
+++ b/net/sched/sch_sfq.c
@@ -171,7 +171,7 @@ static unsigned int sfq_classify(struct sk_buff *skb, struct Qdisc *sch,
return sfq_hash(q, skb) + 1;
*qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
- result = tcf_classify(skb, NULL, fl, &res, false);
+ result = tcf_classify_qdisc(skb, fl, &res, false);
if (result >= 0) {
#ifdef CONFIG_NET_CLS_ACT
switch (result) {
diff --git a/net/sctp/auth.c b/net/sctp/auth.c
index be9782760f50..c901d373af80 100644
--- a/net/sctp/auth.c
+++ b/net/sctp/auth.c
@@ -672,7 +672,7 @@ int sctp_auth_ep_add_chunkid(struct sctp_endpoint *ep, __u8 chunk_id)
/* Check if we can add this chunk to the array */
param_len = ntohs(p->param_hdr.length);
nchunks = param_len - sizeof(struct sctp_paramhdr);
- if (nchunks == SCTP_NUM_CHUNK_TYPES)
+ if (nchunks == SCTP_AUTH_MAX_CHUNKS)
return -EINVAL;
p->chunks[nchunks] = chunk_id;
diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c
index cf335494bffe..27c26e12f95d 100644
--- a/net/sctp/protocol.c
+++ b/net/sctp/protocol.c
@@ -1383,10 +1383,6 @@ static int __net_init sctp_defaults_init(struct net *net)
net->sctp.l3mdev_accept = 1;
#endif
- status = sctp_sysctl_net_register(net);
- if (status)
- goto err_sysctl_register;
-
/* Allocate and initialise sctp mibs. */
status = init_sctp_mibs(net);
if (status)
@@ -1420,8 +1416,6 @@ err_init_proc:
cleanup_sctp_mibs(net);
#endif
err_init_mibs:
- sctp_sysctl_net_unregister(net);
-err_sysctl_register:
return status;
}
@@ -1436,7 +1430,6 @@ static void __net_exit sctp_defaults_exit(struct net *net)
net->sctp.proc_net_sctp = NULL;
#endif
cleanup_sctp_mibs(net);
- sctp_sysctl_net_unregister(net);
}
static struct pernet_operations sctp_defaults_ops = {
@@ -1450,16 +1443,28 @@ static int __net_init sctp_ctrlsock_init(struct net *net)
/* Initialize the control inode/socket for handling OOTB packets. */
status = sctp_ctl_sock_init(net);
- if (status)
+ if (status) {
pr_err("Failed to initialize the SCTP control sock\n");
+ return status;
+ }
+
+ status = sctp_sysctl_net_register(net);
+ if (status) {
+ inet_ctl_sock_destroy(net->sctp.ctl_sock);
+ net->sctp.ctl_sock = NULL;
+ }
return status;
}
static void __net_exit sctp_ctrlsock_exit(struct net *net)
{
+ sctp_sysctl_net_unregister(net);
+ sctp_udp_sock_stop(net);
+
/* Free the control endpoint. */
inet_ctl_sock_destroy(net->sctp.ctl_sock);
+ net->sctp.ctl_sock = NULL;
}
static struct pernet_operations sctp_ctrlsock_ops = {
diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c
index 8adac9e0cd66..c02809264075 100644
--- a/net/sctp/sm_make_chunk.c
+++ b/net/sctp/sm_make_chunk.c
@@ -3153,6 +3153,12 @@ static __be16 sctp_process_asconf_param(struct sctp_association *asoc,
if (!peer)
return SCTP_ERROR_DNS_FAILED;
+ /* Don't free asconf->transport; a later wildcard DEL-IP
+ * parameter reuses it.
+ */
+ if (peer == asconf->transport)
+ return SCTP_ERROR_REQ_REFUSED;
+
sctp_assoc_rm_peer(asoc, peer);
break;
case SCTP_PARAM_SET_PRIMARY:
diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c
index 3893b44448b3..708fa07d5fff 100644
--- a/net/sctp/sm_statefuns.c
+++ b/net/sctp/sm_statefuns.c
@@ -643,7 +643,7 @@ static bool sctp_auth_chunk_verify(struct net *net, struct sctp_chunk *chunk,
struct sctp_chunk auth;
if (!chunk->auth_chunk)
- return true;
+ return !sctp_auth_recv_cid(chunk->chunk_hdr->type, asoc);
/* SCTP-AUTH: auth_chunk pointer is only set when the cookie-echo
* is supposed to be authenticated and we have to do delayed
diff --git a/net/sctp/stream.c b/net/sctp/stream.c
index 5c2fdedea088..34ffe6c945a4 100644
--- a/net/sctp/stream.c
+++ b/net/sctp/stream.c
@@ -308,7 +308,8 @@ int sctp_send_reset_streams(struct sctp_association *asoc,
goto out;
param_len += str_nums * sizeof(__u16) +
- sizeof(struct sctp_strreset_inreq);
+ (out ? sizeof(struct sctp_strreset_inreq)
+ : sizeof(struct sctp_strreset_outreq));
}
if (param_len > SCTP_MAX_CHUNK_LEN -
@@ -639,6 +640,9 @@ struct sctp_chunk *sctp_process_strreset_inreq(
nums = (ntohs(param.p->length) - sizeof(*inreq)) / sizeof(__u16);
str_p = inreq->list_of_streams;
+ if (nums * sizeof(__u16) + sizeof(struct sctp_strreset_outreq) >
+ SCTP_MAX_CHUNK_LEN - sizeof(struct sctp_reconf_chunk))
+ goto out;
for (i = 0; i < nums; i++) {
if (ntohs(str_p[i]) >= stream->outcnt) {
result = SCTP_STRRESET_ERR_WRONG_SSN;
diff --git a/net/sctp/sysctl.c b/net/sctp/sysctl.c
index 15e7db9a3ab2..fca840484ebf 100644
--- a/net/sctp/sysctl.c
+++ b/net/sctp/sysctl.c
@@ -615,11 +615,16 @@ int sctp_sysctl_net_register(struct net *net)
void sctp_sysctl_net_unregister(struct net *net)
{
+ struct ctl_table_header *header = net->sctp.sysctl_header;
const struct ctl_table *table;
- table = net->sctp.sysctl_header->ctl_table_arg;
- unregister_net_sysctl_table(net->sctp.sysctl_header);
+ if (!header)
+ return;
+
+ table = header->ctl_table_arg;
+ unregister_net_sysctl_table(header);
kfree(table);
+ net->sctp.sysctl_header = NULL;
}
static struct ctl_table_header *sctp_sysctl_header;
diff --git a/net/tipc/netlink.c b/net/tipc/netlink.c
index 8336a9664703..1307dd1a9613 100644
--- a/net/tipc/netlink.c
+++ b/net/tipc/netlink.c
@@ -113,12 +113,16 @@ const struct nla_policy tipc_nl_node_policy[TIPC_NLA_NODE_MAX + 1] = {
};
/* Properties valid for media, bearer and link */
+static const struct netlink_range_validation tipc_nl_mtu_range = {
+ .max = U16_MAX,
+};
+
const struct nla_policy tipc_nl_prop_policy[TIPC_NLA_PROP_MAX + 1] = {
[TIPC_NLA_PROP_UNSPEC] = { .type = NLA_UNSPEC },
[TIPC_NLA_PROP_PRIO] = { .type = NLA_U32 },
[TIPC_NLA_PROP_TOL] = { .type = NLA_U32 },
[TIPC_NLA_PROP_WIN] = { .type = NLA_U32 },
- [TIPC_NLA_PROP_MTU] = { .type = NLA_U32 },
+ [TIPC_NLA_PROP_MTU] = NLA_POLICY_FULL_RANGE(NLA_U32, &tipc_nl_mtu_range),
[TIPC_NLA_PROP_BROADCAST] = { .type = NLA_U32 },
[TIPC_NLA_PROP_BROADCAST_RATIO] = { .type = NLA_U32 }
};
diff --git a/net/tipc/netlink_compat.c b/net/tipc/netlink_compat.c
index 2a786c56c8c5..d9a4f94ea2d4 100644
--- a/net/tipc/netlink_compat.c
+++ b/net/tipc/netlink_compat.c
@@ -221,6 +221,10 @@ static int __tipc_nl_compat_dumpit(struct tipc_nl_compat_cmd_dump *cmd,
int rem;
len = (*cmd->dumpit)(buf, &cb);
+ if (len < 0) {
+ err = len;
+ goto err_out;
+ }
nlmsg_for_each_msg(nlmsg, nlmsg_hdr(buf), len, rem) {
err = nlmsg_parse_deprecated(nlmsg, GENL_HDRLEN,
diff --git a/net/tipc/socket.c b/net/tipc/socket.c
index e564341e0216..185c24003b82 100644
--- a/net/tipc/socket.c
+++ b/net/tipc/socket.c
@@ -502,6 +502,7 @@ static int tipc_sk_create(struct net *net, struct socket *sock,
tipc_set_sk_state(sk, TIPC_OPEN);
if (tipc_sk_insert(tsk)) {
sk_free(sk);
+ sock->sk = NULL;
pr_warn("Socket create failed; port number exhausted\n");
return -EINVAL;
}
@@ -1935,7 +1936,7 @@ static int tipc_recvmsg(struct socket *sock, struct msghdr *m,
if (likely(!err)) {
int offset = skb_cb->bytes_read;
- copy = min_t(int, dlen - offset, buflen);
+ copy = min_t(size_t, dlen - offset, buflen);
rc = skb_copy_datagram_msg(skb, hlen + offset, m, copy);
if (unlikely(rc))
goto exit;
@@ -2067,7 +2068,7 @@ static int tipc_recvstream(struct socket *sock, struct msghdr *m,
/* Copy data if msg ok, otherwise return error/partial data */
if (likely(!err)) {
offset = skb_cb->bytes_read;
- copy = min_t(int, dlen - offset, buflen - copied);
+ copy = min_t(size_t, dlen - offset, buflen - copied);
rc = skb_copy_datagram_msg(skb, hlen + offset, m, copy);
if (unlikely(rc))
break;
diff --git a/net/tipc/udp_media.c b/net/tipc/udp_media.c
index 62ae7f5b5840..230645cc01c9 100644
--- a/net/tipc/udp_media.c
+++ b/net/tipc/udp_media.c
@@ -94,6 +94,8 @@ struct udp_replicast {
* @ifindex: local address scope
* @work: used to schedule deferred work on a bearer
* @rcast: associated udp_replicast container
+ * @rcast_lock: serialize updates to @rcast.list against concurrent updaters
+ * @disabled: bearer is being torn down; reject further @rcast.list additions
*/
struct udp_bearer {
struct tipc_bearer __rcu *bearer;
@@ -101,6 +103,8 @@ struct udp_bearer {
u32 ifindex;
struct work_struct work;
struct udp_replicast rcast;
+ spinlock_t rcast_lock;
+ bool disabled;
};
static int tipc_udp_is_mcast_addr(struct udp_media_addr *addr)
@@ -278,26 +282,6 @@ out:
return err;
}
-static bool tipc_udp_is_known_peer(struct tipc_bearer *b,
- struct udp_media_addr *addr)
-{
- struct udp_replicast *rcast, *tmp;
- struct udp_bearer *ub;
-
- ub = rcu_dereference_rtnl(b->media_ptr);
- if (!ub) {
- pr_err_ratelimited("UDP bearer instance not found\n");
- return false;
- }
-
- list_for_each_entry_safe(rcast, tmp, &ub->rcast.list, list) {
- if (!memcmp(&rcast->addr, addr, sizeof(struct udp_media_addr)))
- return true;
- }
-
- return false;
-}
-
static int tipc_udp_rcast_add(struct tipc_bearer *b,
struct udp_media_addr *addr)
{
@@ -308,16 +292,34 @@ static int tipc_udp_rcast_add(struct tipc_bearer *b,
if (!ub)
return -ENODEV;
+ spin_lock_bh(&ub->rcast_lock);
+ if (ub->disabled) {
+ spin_unlock_bh(&ub->rcast_lock);
+ return 0;
+ }
+ list_for_each_entry(rcast, &ub->rcast.list, list) {
+ if (!memcmp(&rcast->addr, addr, sizeof(*addr))) {
+ spin_unlock_bh(&ub->rcast_lock);
+ return 0;
+ }
+ }
+
rcast = kmalloc_obj(*rcast, GFP_ATOMIC);
- if (!rcast)
+ if (!rcast) {
+ spin_unlock_bh(&ub->rcast_lock);
return -ENOMEM;
+ }
if (dst_cache_init(&rcast->dst_cache, GFP_ATOMIC)) {
+ spin_unlock_bh(&ub->rcast_lock);
kfree(rcast);
return -ENOMEM;
}
memcpy(&rcast->addr, addr, sizeof(struct udp_media_addr));
+ list_add_rcu(&rcast->list, &ub->rcast.list);
+ b->bcast_addr.broadcast = TIPC_REPLICAST_SUPPORT;
+ spin_unlock_bh(&ub->rcast_lock);
if (ntohs(addr->proto) == ETH_P_IP)
pr_info("New replicast peer: %pI4\n", &rcast->addr.ipv4);
@@ -325,8 +327,6 @@ static int tipc_udp_rcast_add(struct tipc_bearer *b,
else if (ntohs(addr->proto) == ETH_P_IPV6)
pr_info("New replicast peer: %pI6\n", &rcast->addr.ipv6);
#endif
- b->bcast_addr.broadcast = TIPC_REPLICAST_SUPPORT;
- list_add_rcu(&rcast->list, &ub->rcast.list);
return 0;
}
@@ -361,9 +361,6 @@ static int tipc_udp_rcast_disc(struct tipc_bearer *b, struct sk_buff *skb)
return 0;
}
- if (likely(tipc_udp_is_known_peer(b, &src)))
- return 0;
-
return tipc_udp_rcast_add(b, &src);
}
@@ -644,9 +641,6 @@ int tipc_udp_nl_bearer_add(struct tipc_bearer *b, struct nlattr *attr)
return -EINVAL;
}
- if (tipc_udp_is_known_peer(b, &addr))
- return 0;
-
return tipc_udp_rcast_add(b, &addr);
}
@@ -679,6 +673,7 @@ static int tipc_udp_enable(struct net *net, struct tipc_bearer *b,
return -ENOMEM;
INIT_LIST_HEAD(&ub->rcast.list);
+ spin_lock_init(&ub->rcast_lock);
if (!attrs[TIPC_NLA_BEARER_UDP_OPTS])
goto err;
@@ -819,10 +814,13 @@ static void cleanup_bearer(struct work_struct *work)
struct udp_replicast *rcast, *tmp;
struct tipc_net *tn;
+ spin_lock_bh(&ub->rcast_lock);
list_for_each_entry_safe(rcast, tmp, &ub->rcast.list, list) {
list_del_rcu(&rcast->list);
call_rcu_hurry(&rcast->rcu, rcast_free_rcu);
}
+ ub->disabled = true;
+ spin_unlock_bh(&ub->rcast_lock);
tn = tipc_net(sock_net(ub->sk));
diff --git a/net/tls/tls_device.c b/net/tls/tls_device.c
index 741aef09bfd3..37bb06a8e8f5 100644
--- a/net/tls/tls_device.c
+++ b/net/tls/tls_device.c
@@ -595,13 +595,15 @@ void tls_device_splice_eof(struct socket *sock)
struct tls_context *tls_ctx = tls_get_ctx(sk);
struct iov_iter iter = {};
- if (!tls_is_partially_sent_record(tls_ctx))
+ if (!tls_is_partially_sent_record(tls_ctx) &&
+ !tls_is_pending_open_record(tls_ctx))
return;
mutex_lock(&tls_ctx->tx_lock);
lock_sock(sk);
- if (tls_is_partially_sent_record(tls_ctx)) {
+ if (tls_is_partially_sent_record(tls_ctx) ||
+ tls_is_pending_open_record(tls_ctx)) {
iov_iter_bvec(&iter, ITER_SOURCE, NULL, 0, 0);
tls_push_data(sk, &iter, 0, 0, TLS_RECORD_TYPE_DATA);
}
diff --git a/net/vmw_vsock/virtio_transport_common.c b/net/vmw_vsock/virtio_transport_common.c
index 09475007165b..8becad81279c 100644
--- a/net/vmw_vsock/virtio_transport_common.c
+++ b/net/vmw_vsock/virtio_transport_common.c
@@ -26,6 +26,13 @@
/* Threshold for detecting small packets to copy */
#define GOOD_COPY_LEN 128
+/* Max payload that can be collapsed into a single linear skb, using the same
+ * allocation threshold as virtio_vsock_alloc_skb() to avoid adding pressure
+ * on the page allocator.
+ */
+#define MAX_COLLAPSE_LEN \
+ SKB_MAX_ORDER(VIRTIO_VSOCK_SKB_HEADROOM, PAGE_ALLOC_COSTLY_ORDER)
+
static void virtio_transport_cancel_close_work(struct vsock_sock *vsk,
bool cancel_timeout);
static s64 virtio_transport_has_space(struct virtio_vsock_sock *vvs);
@@ -420,6 +427,145 @@ static int virtio_transport_send_pkt_info(struct vsock_sock *vsk,
return ret;
}
+static bool virtio_transport_can_collapse(struct sk_buff *skb)
+{
+ /* skbs that are partially consumed, mark a SEQPACKET message boundary,
+ * or are already large enough should not be collapsed: they either
+ * need special accounting, carry protocol state, or already have a
+ * good data-to-overhead ratio.
+ */
+ if (VIRTIO_VSOCK_SKB_CB(skb)->offset)
+ return false;
+ if (le32_to_cpu(virtio_vsock_hdr(skb)->flags) & VIRTIO_VSOCK_SEQ_EOM)
+ return false;
+ if (skb->len >= MAX_COLLAPSE_LEN)
+ return false;
+ return true;
+}
+
+/* Iterate through the packets in the queue starting from the current skb to
+ * count the number of bytes we can collapse.
+ */
+static unsigned int
+virtio_transport_collapse_size(struct sk_buff *skb, struct sk_buff_head *queue)
+{
+ unsigned int target = skb->len - VIRTIO_VSOCK_SKB_CB(skb)->offset;
+
+ while ((skb = skb_peek_next(skb, queue)) &&
+ virtio_transport_can_collapse(skb)) {
+ unsigned int len = skb->len - VIRTIO_VSOCK_SKB_CB(skb)->offset;
+
+ if (len > MAX_COLLAPSE_LEN - target)
+ return target;
+
+ target += len;
+ }
+
+ return target;
+}
+
+/* Called under lock_sock to compact the receive queue by merging small skbs.
+ * @min_to_free: minimum number of skbs to eliminate from the queue. May free
+ * more to fill each collapsed skb to capacity.
+ */
+static void
+virtio_transport_collapse_rx_queue(struct virtio_vsock_sock *vvs,
+ u32 min_to_free)
+{
+ struct sk_buff *skb, *next_skb, *new_skb = NULL;
+ struct sk_buff_head new_queue;
+ u32 saved = 0;
+
+ __skb_queue_head_init(&new_queue);
+
+ skb_queue_walk_safe(&vvs->rx_queue, skb, next_skb) {
+ struct virtio_vsock_hdr *hdr = virtio_vsock_hdr(skb);
+ u32 src_off = VIRTIO_VSOCK_SKB_CB(skb)->offset;
+ u32 src_len = skb->len - src_off;
+ bool keep;
+
+ keep = !virtio_transport_can_collapse(skb);
+ if (keep) {
+ /* Finalize pending collapsed skb to preserve packet
+ * ordering.
+ */
+ if (new_skb) {
+ __skb_queue_tail(&new_queue, new_skb);
+ new_skb = NULL;
+ saved--;
+ }
+ goto next;
+ }
+
+ /* Finalize if this packet won't fit in the remaining tailroom,
+ * so we can allocate a right-sized new_skb.
+ */
+ if (new_skb && src_len > skb_tailroom(new_skb)) {
+ __skb_queue_tail(&new_queue, new_skb);
+ new_skb = NULL;
+ saved--;
+ }
+
+ if (!new_skb) {
+ unsigned int alloc_size;
+
+ /* Check after finalizing to opportunistically fill
+ * each collapsed skb to capacity, merging more skbs
+ * than strictly required.
+ */
+ if (saved >= min_to_free)
+ break;
+
+ alloc_size = virtio_transport_collapse_size(skb, &vvs->rx_queue);
+
+ /* Only this skb's data is eligible, nothing to merge
+ * with. Keep as-is.
+ */
+ if (alloc_size <= src_len) {
+ keep = true;
+ goto next;
+ }
+
+ new_skb = virtio_vsock_alloc_linear_skb(alloc_size +
+ VIRTIO_VSOCK_SKB_HEADROOM, GFP_KERNEL);
+ if (!new_skb)
+ break;
+
+ memcpy(virtio_vsock_hdr(new_skb), hdr,
+ sizeof(struct virtio_vsock_hdr));
+ virtio_vsock_hdr(new_skb)->len = 0;
+ }
+
+ /* Cannot fail since src_off/src_len are within bounds, but if
+ * it does, discard new_skb to avoid queuing corrupted data.
+ */
+ if (WARN_ON_ONCE(skb_copy_bits(skb, src_off,
+ skb_put(new_skb, src_len),
+ src_len))) {
+ kfree_skb(new_skb);
+ new_skb = NULL;
+ break;
+ }
+
+ le32_add_cpu(&virtio_vsock_hdr(new_skb)->len, src_len);
+ virtio_vsock_hdr(new_skb)->flags |= hdr->flags;
+
+next:
+ __skb_unlink(skb, &vvs->rx_queue);
+ if (keep) {
+ __skb_queue_tail(&new_queue, skb);
+ } else {
+ consume_skb(skb);
+ saved++;
+ }
+ }
+
+ if (new_skb)
+ __skb_queue_tail(&new_queue, new_skb);
+
+ skb_queue_splice(&new_queue, &vvs->rx_queue);
+}
+
static bool virtio_transport_inc_rx_pkt(struct virtio_vsock_sock *vvs,
u32 len)
{
@@ -1354,12 +1500,29 @@ virtio_transport_recv_enqueue(struct vsock_sock *vsk,
{
struct virtio_vsock_sock *vvs = vsk->trans;
bool can_enqueue, free_pkt = false;
+ u32 len, queue_max, queue_len;
struct virtio_vsock_hdr *hdr;
- u32 len;
hdr = virtio_vsock_hdr(skb);
len = le32_to_cpu(hdr->len);
+ /* virtio_transport_inc_rx_pkt() rejects packets when the per-skb
+ * overhead (skb_queue_len * SKB_TRUESIZE(0)) exceeds buf_alloc.
+ * Proactively collapse the queue before that happens.
+ * No rx_lock needed: lock_sock is held by caller, preventing
+ * concurrent enqueue or dequeue.
+ */
+ queue_max = vvs->buf_alloc / SKB_TRUESIZE(0);
+ queue_len = skb_queue_len(&vvs->rx_queue);
+ if (queue_len >= queue_max) {
+ /* Walking a large queue may take a significant amount of time
+ * and cache misses, causing traffic burstiness. Limit the
+ * collapse to freeing room for this packet and the next one.
+ * It may free more to fill each collapsed skb to capacity.
+ */
+ virtio_transport_collapse_rx_queue(vvs, queue_len + 2 - queue_max);
+ }
+
spin_lock_bh(&vvs->rx_lock);
can_enqueue = virtio_transport_inc_rx_pkt(vvs, len);
diff --git a/net/wireless/pmsr.c b/net/wireless/pmsr.c
index 34c3625f7fd5..d1e2fae5bc0e 100644
--- a/net/wireless/pmsr.c
+++ b/net/wireless/pmsr.c
@@ -263,8 +263,9 @@ static int pmsr_parse_ftm(struct cfg80211_registered_device *rdev,
"FTM: nominal time is required for PD NTB ranging");
return -EINVAL;
}
- out->ftm.nominal_time =
- nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_NOMINAL_TIME]);
+ if (tb[NL80211_PMSR_FTM_REQ_ATTR_NOMINAL_TIME])
+ out->ftm.nominal_time =
+ nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_NOMINAL_TIME]);
if (tb[NL80211_PMSR_FTM_REQ_ATTR_MIN_TIME_BETWEEN_MEASUREMENTS])
out->ftm.min_time_between_measurements =
diff --git a/net/x25/af_x25.c b/net/x25/af_x25.c
index c31d2af5dd22..8aae9273b7c1 100644
--- a/net/x25/af_x25.c
+++ b/net/x25/af_x25.c
@@ -1768,15 +1768,19 @@ void x25_kill_by_neigh(struct x25_neigh *nb)
{
struct sock *s;
+again:
write_lock_bh(&x25_list_lock);
sk_for_each(s, &x25_list) {
if (x25_sk(s)->neighbour == nb) {
+ sock_hold(s);
write_unlock_bh(&x25_list_lock);
lock_sock(s);
- x25_disconnect(s, ENETUNREACH, 0, 0);
+ if (x25_sk(s)->neighbour == nb)
+ x25_disconnect(s, ENETUNREACH, 0, 0);
release_sock(s);
- write_lock_bh(&x25_list_lock);
+ sock_put(s);
+ goto again;
}
}
write_unlock_bh(&x25_list_lock);